본문 바로가기

Lecture AI/3장.신경망 시작하기

3. 뉴스 기사 분류 : 다중 분류 문제

반응형

뉴스를 통해 상호 배타적인 토픽으로 분류하는 신경망을 만들어 보겠습니다. 클래스가 많기 때문에 이 문제는 다중 분류예 예입니다. 각 데이터 포인트가 정확히 하나의 범주로 분류되기 때문에 좀 더 정확히 말하면 단일 레이블 다중 분류문제입니다. 각 데이터 포인트가 여러 개의 범주에 속할 수 있다면 이것은 다중 레이블 다중 분류 문제가 됩니다.

 

전체적인 진행은 IMDB(https://nicola-ml.tistory.com/125?category=873773)와 유사합니다. 

#로이터 데이터셋 로드하기
from keras.datasets import reuters
(train_data, train_labels), (test_data, test_labels) = reuters.load_data(num_words=1000)

#로이터 데이터셋을 텍스트로 디코딩하기
word_index = reuters.get_word_index()
#정수 인덱스와 단어를 매핑하도록 뒤집습니다.
reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])

#리뷰를 디코딩합니다. 
decoded_review = ' '.join([reverse_word_index.get(i-3, '?') for i in train_data[0]])

 

원-핫 인코딩을 사용합니다. 범주형우 모두 짧은 텍스트를 분류합니다.  

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)

from keras.utils.np_utils import to_categorical
one_hot_train_labels = to_categorical(train_labels)
one_hot_test_labels = to_categorical(test_labels)

 


모델 구성

 

모델 정의하기                            

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='sigmoid'))

두 경우 모두 짧은 텍스트를 분류합니다.  imdb보다 출력 층이 커진 46개로 늘어난 점입니다. 출력공간의 차원이 훨씬 커졌습니다. 이전에 사용했던 것처럼 Dense층을 쌓으면 각 층은 이전 층의 출력에서 제공한 정보만 사용할 수 있습니다. 한 층이 분류 문제에 필요한 일부 정보를 누락하면 그다음 층에서 이를 복원할 방법이 없습니다. 각 층은 잠재적으로 정보의 병목이 될 수 있습니다. 이전 예제에서 16차원을 가진 중간층을 사용했지만 16차원 공간은 46새의 클래스를 구분하기에 너무 제약이 많을 것 같습니다. 이렇게 규모가 작은 층은 유용한 정보를 완전히 잃게 되는 정보의 병목 지점처럼 동작할 수 있습니다.

 

주의할 점이 두가지 있습니다.

  • 마지막 Dense층의 크기가 46입니다. 각 입력 샘플에 대해서 46차원의 벡터를 출력한다는 뜻입니다. 이 벡터의 각 원소는 각기 다른 출력 클래스가 인코딩된 것입니다.
  • 마지막 층에 softmax활성화 함수가 사용되었습니다. 각 입력 샘플마다 46개의 출력 클래스에 대한 확률 분포를 출력합니다. 즉 46차원의 출력 벡터를 만들며 output[i]는 어떤 샘플이 클래스 i에 속할 확률입니다. 46개의 값을 모두 더하면 1이 됩니다.

 

모델 컴파일하기                            

from keras import optimizers

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

이런 문제의 사용할 최선의 손실 함수는 categorical_crossentropy입니다. 이 함수는 두 확률 분포 사이의 거리를 측정합니다. 여기에서는 네트워크가 출력한 확률 분포와 진짜 레이블의 분포 사이의 거리입니다. 두 분포 사이의 거리를 최소화하면 진짜 레이블에 가능한 가까운 출력을 내도록 모델을 훈련하게 됩니다. 

 


훈련 검증

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:]

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

실행 후 정확도를 확인한다.

results = model.evaluate(x_test, one_hot_test_labels)
history_dict = history.history
history_dict.keys()

 

훈련과 검증 손실 그리기                            

import matplotlib.pyplot as plt

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

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

plt.plot(epochs, loss, 'bo', label='Traing 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()

훈련과 검증 정확도 그리기                            

import matplotlib.pyplot as plt

history_dict = history.history
acc = history_dict['accuracy']
val_acc = history_dict['val_accuracy']

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

plt.plot(epochs, acc, 'bo', label='Traing 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()

 


새로운 데이터에 대해 예측하기

predictions = model.predict(x_test)

 


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

레이블을 인코딩하는 다른 방법은 다음과 같이 정수 텐서로 변환하는 것입니다.

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

이 방식을 사용하려면 손실 함수 하나만 바꾸면 됩니다. 사용된 손실 함수 categorical_crossentropy는 레이블이 범주형 인코딩되어 있을 것이라고 기대합니다. 정수 레이블을 사용할 때는 sparse_categorical_crossentropy를 사용해야 합니다.

from keras import optimizers

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

손실 함수는 인터페이스만 다를 뿐이고 수학적으로 categorical_crossentropy와 동일합니다.

 


정리

  • N개의 클래스로 데이터 포인트를 분류하려면 네트워크의 마지막 Dense층의 크기는 N이어야 합니다.
  • 단일 레이블, 다중 분류 문제에서는 N개의 클래스에 대한 확률 분포를 출력하기 위해 softmax활성화 함수를 사용해야 합니다.
  • 이런 문제에는 항상 범주형 크로스엔트로피를 사용해야 합니다. 이 함수는 모델이 출력한 확률 분포와 타킷 분포 사이의 거리를 최소화합니다.
  • 다중 분류에서 레이블을 다루는 두 가지 방법이 있습니다.
    • 레이블 범주현 인코딩(또는 원-핫 인코딩)으로 인코딩하고 categorical_crossentropy손실함수를 사용합니다.
    • 레이블을 정수로 인코딩하고 spars_categorical_crossentropy손실 함수를 사용합니다.
  • 많은 수의 범주를 분류할 때 중간층의 크기가 너무 작아 네트워크에 정보의 병목이 생기지 않도록 해야 합니다.
반응형