감성분석과 문서분류

Python을 이용해 텍스트에 나타난 감성과 의견을 분석하고, 문서를 카테고리에 따라 분류합니다.


수강중

4. 다항분류 모형

동영상이 없는 텍스트 강의 자료입니다.

텐서플로를 사용하여 다항 분류 모형을 만듭니다.

데이터 불러오기

데이터를 불러옵니다.

import joblib

x_train, x_test, y_train, y_test, tfidf = joblib.load('newsgroup.pkl')

다항 분류 모형

텐서플로를 이용해 다항 분류 모형을 만듭니다. 다항 분류란 3개 이상, 여러 개로 분류한다는 뜻입니다.

import tensorflow as tf

먼저 모형을 만듭니다.

model = tf.keras.models.Sequential()
  • tf.keras.layers.Dense() 로 층(완전 연결층)을 추가합니다. 세부 옵션은 다음과 같습니다.
  • units: 최종 출력의 개수를 유닛(unit)이라 합니다. 본 예시의 목표는 3가지 다항 분류이므로 최종 출력의 개수가 3 이 됩니다.
  • input_shape: 입력데이터의 크기 설정. 앞서 max_feature 개수를 1000으로 지정하였으므로 (1000,) 과 같이 튜플 형태로 입력합니다.
  • kernel_regularizer: 단어들의 가중치가 지나치게 커지지 않도록 정규화항을 추가해줍니다. l1_l2는 L1과 L2, 2가지 정규화를 모두 해줍니다. L1은 가중치의 절대값을 손실함수에 추가합니다. L2는 가중치의 제곱을 손실함수에 추가합니다. 어느 쪽이든 가중치가 커지면 손실이 커지므로, 가중치가 지나치게 커지는 것을 막아줍니다. l1_l2(0.003, 0.005)는 손실함수에서 L1항에 0.003을 곱하고 L2항 0.005를 곱하라는 뜻입니다. 여기서 0.003, 0.005와 같은 수치는 다양하게 바꿔보면서 성능이 가장 잘 나오는 수치를 선택합니다.
  • activation: 활성화 함수로 소프트맥스 함수를 사용합니다.
model.add(tf.keras.layers.Dense(
    units=3,
    input_shape=(1000,),
    kernel_regularizer=tf.keras.regularizers.l1_l2(0.003, 0.005),
    activation='softmax'))

모형을 model.summary()로 확인합니다.

model.summary()
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_1 (Dense)              (None, 3)                 3003      
=================================================================
Total params: 3,003
Trainable params: 3,003
Non-trainable params: 0
_________________________________________________________________

모형 학습/훈련

다항 분류를 해결하기 위해 최적화 알고리즘을 사용합니다.

  • model.compile:
    • loss: 모형의 손실함수는 sparse_categorical_crossentropy를 사용
    • optimizer: 여기에서 최적화 함수는 Adam (Adaptive Moment Estimation: 적응적 모멘트 추정) 을 사용하였습니다.
    • metrics: 모형 훈련 과정에서 기록할 요소를 정합니다. 여기에서는 정확도'accuracy'를 사용하였습니다.
model.compile(
    loss='sparse_categorical_crossentropy',
    optimizer=tf.keras.optimizers.Adam(),
    metrics=['accuracy'])

모형이 오차로부터 파라미터(매개변수)를 업데이트 시키는 과정을 학습/훈련/적합(fit) 한다고 일컫습니다.

  • model.fit 으로 모형을 학습합니다.
    • x_train.toarray(): 훈련 데이터
    • y_train: 지도 학습(supervised learning)에서 카테고리(label) 데이터
    • epochs: 에포크 횟수. 30일 경우 전체 데이터를 30번 학습합니다.
    • validation_split: 훈련(train)과 검증(validate) 데이터셋을 분리하는 비율. 여기서는 0.3 으로 설정하였고, 이는 1795개 중 30% 인 539개(반올림)를 검증용 데이터로 나눈다는 의미입니다.
    • callbacks: tf.keras.callbacks.EarlyStopping() 옵션은 모형의 validation loss가 3개 에포크 동안 낮아지지 않을 경우 학습을 멈추게 됩니다.
    • verbose: 모형 학습 중 출력되는 텍스트를 조절하는 옵션(verbosity). 0, 1, 2의 3개가 있으며verbose=0 일 경우, 아무런 문구도 출력되지 않습니다. verbose=2 일 경우, 진행상태를 나타내는 상태바(====)가 표시되지 않습니다.
history = model.fit(x_train.toarray(), y_train,
                    epochs=30, callbacks=[tf.keras.callbacks.EarlyStopping()],
                    validation_split=.3, verbose=0)

history 변수에는 30개 에포크 간의 학습 정확도(acc), 검증 정확도(val_acc), 학습 손실(loss), 검증 손실(val_loss)가 저장됩니다.

모형의 손실과 정확도 그래프

모형의 학습(손실과 정확도) 를 알아보기 위해 시각화를 합니다. matplotlib 라이브러리를 사용합니다.

import matplotlib.pyplot as plt
  • 손실 그래프를 그립니다.
    • 모형이름.history['loss']: 학습한 모형의 손실
    • 모형이름.history['val_loss']: 학습한 모형의 검증 손실값(validation loss)
    • .xlabel: x축 제목
    • .ylabel: y축 제목
    • .legend: 각 그래프에 대한 설명 범례
    • .title: 전체 제목
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train_loss', 'val_loss'])
plt.title('LOSS')
plt.show()

손실 그래프는 에포크가 증가함에 따라 일정한 추세로 감소하는 모습을 보입니다.

  • 다음으로 정확도 그래프를 그립니다.
    • 모형이름.history['acc']: 학습한 모형의 정확도
    • 모형이름.history['val_acc']: 학습한 모형의 검증 정확도(validation accuracy)
    • .xlabel: x축 제목
    • .ylabel: y축 제목
    • .legend: 각 그래프에 대한 설명 범례
    • .title: 전체 제목
plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train_accuracy', 'val_accuracy'])
plt.title('ACCURACY')
plt.show()

정확도 그래프는 증가하다 어느 에포크 이상에서는 증가세가 감소하는 모습을 보입니다.

모형 평가

model.evaluate 를 사용하여 모형의 손실값과 정확도를 출력합니다.

model.evaluate(x_test.toarray(), y_test, verbose=2)
1194/1 - 0s - loss: 1.0646 - accuracy: 0.7915
[1.0758204781629732, 0.7914573]

위의 모형 평가 결과를 소숫점 셋째자리에서 반올림하면 [0.44, 0.93] 이 됩니다.

즉, 모형의 손실은 1.07 이고 정확도는 약 79%입니다.

모형 계수

단어별 가중치를 표로 정리합니다.

import pandas as pd

w, _ = model.weights
weights = pd.DataFrame(w.numpy())

결과해석을 위해 다항 분류를 [0, 1, 2] 의 숫자가 아닌 본래 카테고리(라벨) 이름으로 설정합니다.

weights.columns = ['motorcycle', 'baseball', 'hockey']

weights 변수의 word 열에 원 단어(feature)를 저장합니다.

weights['word'] = tfidf.get_feature_names()

첫째, motorcycle 을 기준으로 내림차순 정렬을 하고, 해당 카테고리에 어떤 단어들이 많이 등장하는지 확인합니다.

weights.sort_values('motorcycle', ascending=False).head()
motorcycle baseball hockey word
181 0.745936 -0.374010 -0.343098 bike
256 0.568886 -0.094269 -0.459109 com
318 0.541011 -0.214346 -0.200502 dod
767 0.269240 -0.000391 -0.002543 ride
930 0.252685 -0.013626 -0.003667 uk

둘째, baseball 을 기준으로 내림차순 정렬을 하고, 어떤 단어들이 많이 등장하는지 확인합니다.

weights.sort_values('baseball', ascending=False).head()
motorcycle baseball hockey word
340 -0.124038 0.427687 -0.120957 edu
166 -0.161280 0.357024 -0.000412 baseball
786 -0.005161 0.257479 -0.053414 runs
992 -0.213979 0.212253 0.000080 year
501 -0.004552 0.212245 -0.004203 jewish

마지막으로 세번째 분류인 hockey 을 기준으로 내림차순 정렬을 하고, 어떤 단어들이 많이 등장하는지 확인합니다.

weights.sort_values('hockey', ascending=False).head()
motorcycle baseball hockey word
460 -0.202504 -0.212338 0.496042 hockey
629 -0.067200 -0.083500 0.398874 nhl
888 -0.373876 -0.000127 0.251696 team
396 -0.377219 0.000425 0.230549 game
209 -0.000016 -0.190912 0.202833 ca