본문 바로가기
프로그래밍/인공지능

RNN - LSTM 활용

by austag 2023. 7. 31.

일요일에 몸이 너무 피곤해서 결국 4시 반에 일어나지 못했습니다...

알람을 듣긴 했는데 그냥 자버리기

원래 이 글은 지금쯤이면 올라갔어야 하는데 지금 쓰기 시작했네요

챕터가 좀 많은데 하나씩 봐보도록 하겠습니다.


앞 글에서 RNN의 예시를 들었었죠.

예시들 중에는 문장의 의미 파악이 있었는데 이것은 모든 단어를 종합해서 하나의 카테고리로 분류하는 것이라고 할 수 있습니다. '나는 지금 졸려' 같은 문장은 '몸상태' 같은 카테고리로 분류할 수 있습니다. 더 긴 문장을 봐볼까요?

중부와 경북 북부를 중심으로 시간당 30에서 60mm 안팎의 매우 강한 비가 벼락과 돌풍과 함께 쏟아질 수 있겠습니다.(출처: MBC) : 날씨
페르세우스 유성우의 유성들은 태양을 돌고 있는 스위프트터틀혜성의 부스러기들이 지구 대기 중으로 돌진해 들어오는 것이다.(출처: 네이버) : 천문학
베이스 기타는 현악기의 하나로, 보통 4개의 줄을 가지고 있으나, 5현, 6현, 그 이상도 있다.(출처: 위키백과) : 음악

이번엔 이런 긴 문장들을 카테고리로 분류하는 연습을 해 볼 것입니다.

로이터 뉴스 데이터를 사용할 것이고 이 데이터는 총 1만 1,228개의 뉴스 기사가 46개의 카테고리로 나누어진 대용량 텍스트 데이터 입니다.

from tensorflow.keras.datasets import reuters

 

데이터셋을 불러오고

(X_train, y_train), (X_test, y_test) = reuters.load_data(num_words=1000, test_split=0.2)

학습셋과 테스트셋으로 나누어줍니다.

코드를 짜기전 데이터를 확인해 볼까요?

category = np.max(y_train)+1
print(category, '카테고리')
print(len(X_train), '학습용 뉴스 기사')
print(len(X_train), '테스용 뉴스 기사')
print(X_train[0])

카테고리의 개수를 np.max() 알아보고 학습셋과 테스트셋의 개수를 확인해보았습니다.

마지막 줄에서 기사를 출력했을 때, 텍스트로 나오지 않고 숫자로 나온 것을 볼 수 있는데 이것으로 딥러닝이 단어를 그대로 사용하지 않고 숫자로 변환후에 학습한다는 것을 알 수 있습니다. 이 모델에서는 데이터 안에서 해당 단어가 몇 번이나 나타나는지 빈도에 따라 번호를 붙인 것입니다. 3이면 세 번째로 빈도가 많고 10이면 열 번째로 빈도가 많다는 것이죠.

그런데 447번째나 되는 수가 있기 때문에 모든 단어를 다 사용하는 것은 비효율적이므로 빈도가 높은 단어만 사용하도록 하겠습니다. 학습셋과 데이터셋을 나눌 때 쓴 num_words=1000 가 이것을 만듭니다.

 

데이터 전처리를 해주겠습니다.

단어의 수도 맞춰주고

from tensorflow.keras.preprocessing import sequence

X_train = sequence.pad_sequences(X_train, maxlen=100) #100개
X_test = sequence.pad_sequences(X_test, maxlen=100) #100개

원-핫 인코딩도 해줍니다.

y_train = to_categorical(y_train)
y_test = to_categorical(y_test)

 

이제 딥러닝 모델을 만들어 봅시다.

model = Sequential()
model.add(Embedding(1000, 100))
model.add(LSTM(100, activation='tanh'))
model.add(Dense(46, activation='softmax'))

Embedding 층과 LSTM 층이 추가되었습니다.

Embedding 층은 데이터 전처리 과정을 통해 입력된 값을 받아 다음 층이 알 수 있는 형태로 변환하는 역할을 합니다.

Embedding( '불러온 단어의 총 수', '기사당 단어 수' )

LSTM 층은 RNN에서 기억값에 대한 가중치를 제어합니다.

LSTM( 기사당 단어 수, 기타 옵션 )

 

마지막으로 학습 코드를 추가해주면 됩니다.

#모델 컴파일
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

#조기 중단
early_stopping_callback = EarlyStopping(monitor='val_loss', patience=5)

#모델 실행
history = model.fit(X_train, y_train, batch_size=20, epochs=200, 
validation_data=(X_test, y_test), callbacks=[early_stopping_callback])

 

전체코드는 다음과 같고

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, LSTM, Embedding
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.preprocessing import sequence
from tensorflow.keras.datasets import reuters
from tensorflow.keras.callbacks import EarlyStopping

import numpy as np
import matplotlib.pyplot as plt

#학습셋, 테스트셋 구분
(X_train, y_train), (X_test, y_test) = reuters.load_data(num_words=1000, test_split=0.2)

"""
#데이터 확인
category = np.max(y_train)+1
print(category, '카테고리')
print(len(X_train), '학습용 뉴스 기사')
print(len(X_train), '테스용 뉴스 기사')
print(X_train[0])
"""

#단어 수 맞추기
X_train = sequence.pad_sequences(X_train, maxlen=100)
X_test = sequence.pad_sequences(X_test, maxlen=100)

#원-핫 인코딩
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)

#모델 구조 설정
model = Sequential()
model.add(Embedding(1000, 100))
model.add(LSTM(100, activation='tanh'))
model.add(Dense(46, activation='softmax'))

#모델 컴파일
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

#조기 중단
early_stopping_callback = EarlyStopping(monitor='val_loss', patience=5)

#모델 실행
history = model.fit(X_train, y_train, batch_size=20, epochs=200, validation_data=(X_test, y_test), callbacks=[early_stopping_callback])

#정확도 출력
print("\nTest Accuracy: %.4f" %(model.evaluate(X_test, y_test)[1]))

#학습셋과 데이터셋의 오차
y_vloss  = history.history['val_loss']
y_loss = history.history['loss']

#그래프 표현
x_len = np.arange(len(y_loss))
plt.plot(x_len, y_vloss, marker='.', c='red', label='Testset_loss')
plt.plot(x_len, y_loss, marker='.', c='blue', label='Trainset_loss')

plt.legend(loc='upper right')
plt.grid()
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()

출력값은 이렇습니다.

18번째 epoch에서 자동 중단 되었고 accuracy는 0.7088, loss는 1.2843 이 나온 것을 확인할 수 있습니다.


RNN 한 예제를 해보았는데 뭔가 오래 걸렸습니다.

학교에서 수업 받으면서 하다보니까 쉬는시간 안에 학습이 다 되지 않고 끊기더군요...

다음은 LSTM과 CNN의 조합을 이용한 예제를 해보겠습니다.

 

참고: [모두의 딥러닝]