MLOps 프로젝트/도서 '케라스 창시자에게 배우는 딥러닝'

1부 3.5 뉴스 기사 분류: 다중 분류 예제

youjin86 2021. 8. 29. 04:30

1부 딥러닝의 기초

3장 신경망 시작하기


3.5 뉴스 기사 분류: 다중 분류 예제

 

로이터 데이터셋 (Reuter)

1986년 로이터에서 공개한 짧은 뉴스 기사와 토픽의 집합인 데이터셋

텍스트 분류를 위해 널리 사용되는 간단한 데이터셋

 

 

1. 데이터셋 로드

rom keras.datasets import reuters

(train_data, train_labels), (test_data, test_labels) = reuters.load_data(num_words=10000)

train_data, test_data : 단어 인덱스의 정수 리스트

train_labels, test_labels : 0~45 사이의 정수

 

2.데이터 준비

숫자 리스트에서 텐서로 변경하는 2가지 방법

(1)같은 길이가 되도록 리스트에 패딩(pading)을 추가하고 (sample, sequence_length)크기의 정수 텐서로 변환

그 다음 정수 텐서를 다룰 수 있는 층을 신경망의 첫 번째 층으로 사용(Embedding 층)

 

(2) 리스트를 원-핫 인코딩(one-hot encoding)하여 0과 1의 벡터로 변환

시퀀스[3, 5]를 인덱스 3과 5의 위치는 1이고 그 외는 모두 0인 10,000차원의 벡터로 각각 변환

그 다음 부동 소수 벡터 데이터를 다룰 수 있는 Dense층을 신경망의 첫 번째 층으로 사용

 

#(2)방법 사용

import numpy as np

def vectorize_sequences(sequences, dimension=10000):
    results = np.zeros((len(sequences), dimension))
    for i, sequence in enumerate(sequences):
        results[i, sequence] = 1.
    return results

# 훈련 데이터 벡터 변환
x_train = vectorize_sequences(train_data)
# 테스트 데이터 벡터 변환
x_test = vectorize_sequences(test_data)

 

레이블을 벡터로 변경하는 2가지 방법

(1) 레이블의 리스트를 정수 텐서로 변환하는 것

 

(2) 원-핫 인코딩(one-hot encoding)을 사용

범주형 데이터에 널리 사용됨.

레이블의 인덱스 자리는 1이고 나머진 모두 0인 벡터

def to_one_hot(labels, dimension=46):
    results = np.zeros((len(labels), dimension))
    for i, label in enumerate(labels):
        results[i, label] = 1.
    return results

# 훈련 레이블 벡터 변환
one_hot_train_labels = to_one_hot(train_labels)
# 테스트 레이블 벡터 변환
one_hot_test_labels = to_one_hot(test_labels)

 

[내장함수 사용해 원-핫 인코딩하는 법]

from keras.utils.np_utils import to_categorical

one_hot_train_labels = to_categorical(train_labels)
one_hot_test_labels = to_categorical(test_labels)

 

3. 신경망 모델 만들기 - 모델 정의하기

relu함수

입력 데이터가 벡터고 레이블은 스칼라(1 또는 0)과 같은 문제에 잘 작동하는 네트워크 종류는 relu 활성화 함수를 사용한 완전 연결층(Dense(16, activation='relu'))을 그냥 쌓은 것

 

Dense층에 전달한 매개변수(16)는 은닉 유닛(hidden unit)의 개수

하나의 은닉 유닛은 층이 나타내는 표현 공간에서 하나의 차원이 됨.

 

output = relu(dot(W,input) + b)

16개의 은닉 유닛이 있다는 것은 가중치 행렬 W의 크기가 (input_dimension, 16)이라는 뜻

입력 데이터와 W를 점곱하면 입력 데이터가 16차원으로 표현된 공간으로 투영됨.

그리고 편향 벡터 b를 더하고 relu연산을 적용

 

출처 : http://aidev.co.kr/deeplearning/6893

 

from keras import models
from keras import layers

model = models.Sequential()
model.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(46, activation='softmax'))

 

 

4. 신경망 모델 만들기 - 모델 컴파일하기

model.compile(optimizer='rmsprop',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

손실 함수에서, 많은 출력 클래스의 갯수가 필요하기에 categorical_crossentropy가 적합

이 함수는 두 확률 분포 사이의 거리를 측정

(네트워크가 출력한 확률 분포와 진짜 레이블의 분포 사이의 거리)

두 분포 사이의 거리를 최소화하면 진짜 레이블에 가능한 가까운 출력을 내도록 모델을 훈련

 

5. 훈련 검증

훈련하는 동안 처음 본 데이터에 대한 모델의 정확도를 측정하기 위해 원본 훈련 데이터에서 10,000개의 샘플을 준비

x_val = x_train[:1000]
partial_x_train = x_train[1000:]

y_val = one_hot_train_labels[:1000]
partial_y_train = one_hot_train_labels[1000:]

 

512개의 샘플씩 미니 배치를 만들어 20번의 에포크 동안 훈련

x_train과 y_train 텐서에 있는 모든 샘플에 대해 20번 반복

동시에 따로 떼어 놓은 1만 개의 샘플에서 손실과 정확도를 측정하기 위해 validation_data 매개변수에 검증 데이터 넣기

history = model.fit(partial_x_train,
                    partial_y_train,
                    epochs=20,
                    batch_size=512,
                    validation_data=(x_val, y_val))

 

훈련 검증 손실 그리기

import matplotlib.pyplot as plt

loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(1, len(loss) + 1)

plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()

plt.show()

결과

 

 

훈련과 검증 정확도 그리기

plt.clf()   # 그래프 초기화

acc = history.history['acc']
val_acc = history.history['val_acc']

plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()

plt.show()

결과

 

9번째 에포크 이후부터 훈련 데이터에 과도하게 최적화된 과대 적합(overfitting)인 걸 확인할 수 있음.

 

따라서, 에포크를 9번으로 줄이고 다시 훈련하기

model = models.Sequential()
model.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(46, activation='softmax'))

model.compile(optimizer='rmsprop',
              loss='categorical_crossentropy',
              metrics=['accuracy'])
model.fit(partial_x_train,
          partial_y_train,
          epochs=9,
          batch_size=512,
          validation_data=(x_val, y_val))
results = model.evaluate(x_test, one_hot_test_labels)

 

최종 결과 : 약 78%의 정확도

>>> results
[1.022498257544459, 0.7756010686194165]

 

6. 훈련된 모델로 새로운 데이터에 대해 예측하기

predictions = model.predict(x_test)

 

레이블과 손실을 다루는 다른 방법

정수 텐서로 변환하는 것

y_train = np.array(train_labels)
y_test = np.array(test_labels)

이 방식을 사용하려면 손실 함수 하나만 바꾸면 됨.

categorical_crossentropy -> sparse_categorical_crossentropy

model.compile(optimizer='rmsprop',
              loss='sparse_categorical_crossentropy',
              metrics=['acc'])

 

중간층을 충분히 크게 둬야 하는 이유

많은 정보를 중간층의 저차원 표현 공간으로 압축하려 하면 정확도가 작게 나오게 됨.