프로그래밍/NLP

[ NLP ] 한글 텍스트 분류 (3)

Yanoo 2021. 1. 21. 00:51
728x90
반응형

모델링

전처리한 데이터로 감정 분석을 위한 모델링을 진행한다. 먼저 전처리 결과를 보면, 한글 데이터를 정제하고 각 단어들을 벡터화한 후 넘파이 파일로 저장했다. 벡터화된 데이터는 글자가 숫자로 표현됐기에 영어와 한글에 큰 차이는 없다.

 

실습할 모델 소개

이번엔 딥러닝 방식인 합성곱 신경망(CNN) 하나의 모델만 진행한다. 입력값으로는 전처리한 한글 넘파이 데이터를 쓴다.

 

학습 데이터 불러오기

필요한 라이브러리와 전처리한 데이터를 불러온다.

import os
from datetime import datetime
import tensorflow as tf
import numpy as np
import json
from sklearn.model_selection import train_test_split
DATA_IN_PATH='./data_in/'
DATA_OUT_PATH='./data_out/'
INPUT_TRAIN_DATA='nsmc_train_input.npy'
LABEL_TRAIN_DATA='nsmc_train_label.npy'
DATA_CONFIGS='data_configs.json'

input_data=np.load(open(DATA_IN_PATH+INPUT_TRAIN_DATA,'rb'))
label_data=np.load(open(DATA_IN_PATH+LABEL_TRAIN_DATA,'rb'))
prepro_configs=json.load(open(DATA_IN_PATH+DATA_CONFIGS,'r'))

불러온 데이터를 모델에 적용하는데, 그전에 학습 데이터의 일부를 검증 데이터로 분리해서 모델 성능 측정해야함

학습과 검증 데이터셋 분리

데이터를 불러온 후 데이터의 일부는 모델의 하이퍼파라미터 값을 수정하기 위해 검증 데이터로 따로 둬야 한다. 사이킷런을 사용해 검증 데이터를 구분한다.

TEST_SPLIT=0.1
RNG_SEED=13371447

# 하이퍼 파라미터
VOCAB_SIZE = prepro_configs['vocab_size']+1
EMB_SIZE = 128
BATCH_SIZE = 16
NUM_EPOCHS = 1

input_train, input_eval, label_train, label_eval=train_test_split(input_data, label_data[0], test_size=TEST_SPLIT, random_state=RNG_SEED)

근데 전처리를 잘못했나보다 label_data를 넣어야하는데 label_data[0]으로 넣었다. 일단 진행하면,

학습 데이터의 90퍼센트를 학습으로 나머지 10퍼센트를 검증 데이터로 사용한다. 이제 데이터 입력함수를 만든다.

 

데이터 입력 함수

에스티메이터에 데이터를 적용하기 위해 데이터 입력함수를 정의한다. 학습 함수와 검증 함수를 따로 만들고 이 데이터는 tf.data를 활용해서 처리한다.

def mapping_fn(X,Y):
    input,label={'x':X},Y
    return input, label

def train_input_fn():
    dataset=tf.data.Dataset.from_tensor_slices((input_train,label_train))
    dataset=dataset.shuffle(buffer_size=len(input_train))
    dataset=dataset.batch(BATCH_SIZE)
    dataset=dataset.map(mapping_fn)
    dataset=dataset.repeat(count=NUM_EPOCHS)
    iterator=dataset.make_one_shot_iterator()
    
    return iterator.get_next()

def eval_input_fn():
    dataset=tf.data.Dataset.from_tensor_slices((input_eval, label_eval))
    dataset=dataset.shuffle(buffer_size=len(input_eval))
    dataset=dataset.batch(16)
    dataset=dataset.map(mapping_fn)
    iterator=dataset.make_one_shot_iterator()
    
    return iterator.get_next()

 

모델 함수

CNN 모델을 사용하는데 기존과 대부분 동일하지만 한 가지 다른 점은 여기서는 모델은 깊게 쌓이지 않는다. 이것은 빠른 학습을 위함도 있고 캐글 모델과는 달리 성능향상 목적이 아니고 단순히 다루는 목적이기에 그렇다. 성능을 높이고 싶다면 좀 더 깊이 있는 모델을 만든다.

def model_fn(features, labels, mode, params):
    TRAIN=mode==tf.estimator.ModeKeys.TRAIN
    EVAL=mode==tf.estimator.ModeKeys.EVAL
    PREDICT=mode==tf.estimator.ModeKeys.PREDICT
    
    embedding_layer=tf.keras.layers.Embedding(VOCAB_SIZE, EMB_SIZE)(features['x'])
    
    dropout_emb=tf.keras.layers.Dropout(rate=0.2)(embedding_layer)
    
    conv=tf.keras.layers.Conv1D(filters=32, kernel_size=3, padding='same', activation=tf.nn.relu)(dropout_emb)
    
    pool=tf.keras.layers.GlobalMaxPool1D()(conv)
    
    hidden=tf.keras.layers.Dense(units=250, activation=tf.nn.relu)(pool)
    
    dropout_hidden=tf.keras.layers.Dropout(rate=0.2)(hidden, training=TRAIN)
    logits=tf.keras.layers.Dense(units=1)(dropout_hidden)
    
    if labels is not None:
        labels=tf.reshape(labels,[-1,1])
        
    if TRAIN:
        global_step=tf.train.get_global_step()
        loss=tf.losses.sigmoid_cross_entropy(labels, logits)
        train_op=tf.train.AdamOptimizer(0.001).minimize(loss, global_step)
        
        return tf.estimator.EstimatorSpec(mode=mode, train_op=train_op, loss=loss)
    
    elif EVAL:
        loss=tf.losses.sigmoid_cross_entropy(labels,logits)
        pred=tf.nn.sigmoid(logits)
        accuracy=tf.metrics.accuracy(labels, tf.round(pred))
        return tf.estimator.EstimatorSpec(mode=mode, loss=loss, eval_metric_ops={'acc':accuracy})
    
    elif PREDICT:
        return tf.estimator.EstimatorSpec(mode=mode,predictions={'prob':tf.nn.sigmoid(logits),})

 

모델 학습

에스티메이터 객체를 정의한 후 학습을 시킨다. 우선 객체를 만들어야 하는데, 인자값으로 모델 함수와 학습 체크 포인트 저장 경로를 정한다. 매개 변수는 해당 모델은 안쓰므로 인자로 넣지 않는다.

est=tf.estimator.Estimator(model_fn, model_dir="data_out/checkpoint/cnn_model")

이제 생성한 객체의 train 함수를 호출해서 학습한다. 인자값으로는 학습 입력 함수를 넣으면 된다. 경과 시간 확인을 위해 시간을 출력한다.

time_start=datetime.utcnow()
print("Experiment started at {}".format(time_start.strftime("%H:%M:%S")))
print(".....................................")

est.train(train_input_fn)
time_end=datetime.utcnow()
print(".....................................")
print("Experiment finished at {}".format(time_end.strftime("%H:%M:%S")))
print("")
time_elapsed=time_end-time_start
print("Experiment elapsed time: {} seconds".format(time_elapsed.total_seconds()))

 

모델 검증

분리해 놓은 검증 데이터를 이용해 모델을 평가한다. 모델 함수와 검증 데이터 입력 함수를 이용한다.

valid=est.evaluate(eval_input_fn)

여러 결과 값이 나왔다. 이 값을 참고하여 앞에 정의한 하이퍼파라미터 값 혹은 전처리 방법, 모델을 수정하면 된다. 검증 데이터에 대해 만족한 결과가 나오면 평가를 진행한다.

 

모델 평가

평가는 영어와 다르다 그건 영어는 평가 데이터에 감정에 대한 라벨 값이 없었기 때문에 예측값만 만든 후 캐글에 제출하여 정확도를 확인했지만 한글은 라벨 값이 있기에 검증 과정 처럼 바로 결과를 확인한다.

 

우선 평가 데이터를 불러온다.

INPUT_TEST_DATA = 'nsmc_test_input.npy'
LABEL_TEST_DATA = 'nsmc_test_label.npy'

test_input_data = np.load(open(DATA_IN_PATH + INPUT_TEST_DATA, 'rb'))
test_label_data = np.load(open(DATA_IN_PATH + LABEL_TEST_DATA, 'rb'))

이제 평가 입력 함수만 정의하고 진행하면 된다. 평가 입력 함수에는 반복과 셔플을 수행하는 부분만 제거하고 다음과 같이 정의한다.

def test_input_fn():
    dataset = tf.data.Dataset.from_tensor_slices((test_input_data, test_label_data))
    dataset = dataset.batch(16)
    dataset = dataset.map(mapping_fn)
    iterator = dataset.make_one_shot_iterator()
    
    return iterator.get_next()

이전에는 평가 객체(predict)를 만들었지만 여기서는 평가 데이터가 라벨을 가지고 있으므로 검증 함수를 이용해 확인할 수 있다. 검증과 동일하게 진행한다.

predict=est.evaluate(test_input_fn)

 

약 82.73%의 정확도가 나왔다 높은 수치는 아니지만 영어의 경우 길었고 한글은 단어 평균 10개였지만 괜찮게 나왔다.

 

 

 

 

[ 출처 : 책 ( 텐서플로와 머신러닝으로 시작하는 자연어 처리) ]

 

728x90
반응형