ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [자연어처리] Text-CNN 구현하기 (코드 위주)
    자연어처리 공부 2024. 1. 2. 12:43

    ▶︎ CNN(Convolutional Neural Network) 란? 

     이름에서 알 수 있듯이 합성곱(새로운 연산)을 활용한 신경망 딥러닝 모델입니다.

     필터가 상하좌우 이동하면서 데이터와 convolution을 하여 특징을 추출합니다. 일반적인 Dense layer보다 적은 수의 파라미터θ로 데이터의 주요 특징을 찾아낼 수 있습니다. 

     필터를 이동시키면서 서로 다른 데이터의 특성을 추출하기 때문에 공간적(지역적, 위치적, 계층적) 패턴을 가지고 있는 이미지, 신호 데이터에 적합합니다.

     

    ▶︎ Text-CNN란? 

    → 용어 그대로 텍스트 데이터에 대한 CNN을 기반으로 하는 모델입니다.

    공간적(지역적, 위치적, 계층적) 특징을 잘 포착하는 CNN의 특징에서 영감을 받아 텍스트 모델에도 적용을 한 것입니다. 이를 통해 텍스트(시퀀스) 데이터의 지역적인 특징을 잘 포착하며 문장 분류, 감성 분석 등에서 자주 사용됩니다.

     

    CNN의 특징을 잘 알고 계시면 Text-CNN에 적용하는 것도 쉬울 것입니다. 또한 쉽게 Text-CNN을 설명하는 곳이 많은데 코드로는 많지 않아서 오늘은 코드 위주의 정리를 해보려고 합니다. 

     

    ▶︎ 텍스트 분류 모델을 사용하기 전에 필요한 단계

    1. 데이터 불러오기

    2. 타겟 변수(y) 라벨 인코딩하기(원핫인코딩을 해도 되지만, 그러면 모델 컴파일할 때 loss='categorical_crossentropy'로 고쳐주세요)

    3. 토큰화하여 불용어 제거한 전처리된 corpus를 준비해주세요! (자연어 모델에서는 특히 전처리에 따라 모델 성능이 더 크게 차이납니다)

    4. 전처리된 corpus로 사전 만들어서 corpus를 숫자로 변환하고 패딩까지 완료시켜 모델에 입력할 데이터를 만들어주세요!

    5. 사전에 담긴 단어들을 Word2Vec을 사용하여 의미를 담은 숫자로 임베딩시켜주세요!

    6. 이제 모델을 만들겠습니다!

     

     

     모델 구축 및 학습

    class RocAucEvaluation(Callback):
        def __init__(self, validation_data=(), interval=1):
            super(Callback, self).__init__()
    
            self.interval = interval
            self.X_val, self.y_val = validation_data
    
        def on_epoch_end(self, epoch, logs={}):
            if epoch % self.interval == 0:
                y_pred = self.model.predict(self.X_val, verbose=0)
                score = roc_auc_score(self.y_val, y_pred, multi_class='ovo') # 'ovr' 또는 'ovo'
                print("\n ROC-AUC - epoch: %d - score: %.6f \n" % (epoch+1, score))
    filter_sizes = [1,2,3,5]
    num_filters = 32
    
    def get_model():    
        inp = Input(shape=(max_len, ))
        x = Embedding(vocab_size, embedding_dim, weights=[embedding_matrix], trainable=True)(inp)
        x = SpatialDropout1D(0.4)(x)
        x = Reshape((max_len, embedding_dim, 1))(x)
        
        conv_0 = Conv2D(num_filters, kernel_size=(filter_sizes[0], embedding_dim), kernel_initializer='normal',
                                                                                        activation='elu')(x)
        conv_1 = Conv2D(num_filters, kernel_size=(filter_sizes[1], embedding_dim), kernel_initializer='normal',
                                                                                        activation='elu')(x)
        conv_2 = Conv2D(num_filters, kernel_size=(filter_sizes[2], embedding_dim), kernel_initializer='normal',
                                                                                        activation='elu')(x)
        conv_3 = Conv2D(num_filters, kernel_size=(filter_sizes[3], embedding_dim), kernel_initializer='normal',
                                                                                        activation='elu')(x)
        
        maxpool_0 = MaxPool2D(pool_size=(max_len - filter_sizes[0] + 1, 1))(conv_0)
        maxpool_1 = MaxPool2D(pool_size=(max_len - filter_sizes[1] + 1, 1))(conv_1)
        maxpool_2 = MaxPool2D(pool_size=(max_len - filter_sizes[2] + 1, 1))(conv_2)
        maxpool_3 = MaxPool2D(pool_size=(max_len - filter_sizes[3] + 1, 1))(conv_3)
            
        z = Concatenate(axis=1)([maxpool_0, maxpool_1, maxpool_2, maxpool_3])   
        z = Flatten()(z)
        z = Dropout(0.1)(z)
            
        outp = Dense(3, activation="softmax")(z)  # 클래스의 갯수 입력
        
        model = Model(inputs=inp, outputs=outp)
        model.compile(loss='sparse_categorical_crossentropy',
                      optimizer='adam',
                      metrics=['accuracy'])
        return model
    model = get_model()
    
    batch_size = 256
    epochs = 7
    
    X_tra, X_val, y_tra, y_val = train_test_split(padded_sequences, train['label_encoded'], train_size=0.95, random_state=233)
    RocAuc = RocAucEvaluation(validation_data=(X_val, y_val), interval=1)
    model = get_model()
    
    batch_size = 256
    epochs = 7
    
    X_tra, X_val, y_tra, y_val = train_test_split(padded_sequences, train['label_encoded'], train_size=0.95, random_state=233)
    RocAuc = RocAucEvaluation(validation_data=(X_val, y_val), interval=1)
    hist = model.fit(X_tra, y_tra, batch_size=batch_size, epochs=epochs, validation_data=(X_val, y_val),
                     callbacks=[RocAuc], verbose=2)

     

     평가 지표 시각화하기

    acc = hist.history['accuracy']
    val_acc = hist.history['val_accuracy']
    
    x_len = np.arange(len(acc))
    
    plt.plot(x_len, acc, marker='.', c='blue', label="Train-set Acc.")
    plt.plot(x_len, val_acc, marker='.', c='red', label="Validation-set Acc.")
    
    plt.legend(loc='upper right')
    plt.grid()
    plt.xlabel('epoch')
    plt.ylabel('Accuracy')
    plt.show()
    loss = hist.history['loss']
    val_loss = hist.history['val_loss']
    
    x_len = np.arange(len(acc))
    
    plt.plot(x_len, loss, marker='.', c='blue', label="Train-set loss.")
    plt.plot(x_len, val_loss, marker='.', c='red', label="Validation-set loss.")
    
    plt.legend(loc='upper right')
    plt.grid()
    plt.xlabel('epoch')
    plt.ylabel('Cross-entropy')
    plt.show()

     

    테스트 데이터 평가하기 (테스트 데이터도 동일한 전처리와 패딩 절차를 밟아야합니다.)

    result = model.evaluate(test_padded_sequences, test['label_encoded'])
    print('loss (cross-entropy) :', result[0])
    print('test accuracy :', result[1])

     

     

     

    참조 자료 : https://www.kaggle.com/code/yekenot/textcnn-2d-convolution/script

Designed by Tistory.