LSTM(Long Short Term Memory) 실습
Kaggle의 영화 리뷰에 대한 감정 예측 데이터셋에 대해 LSTM으로 자연어차리 실습을 해보도록 하겠습니다.
Kaggle 링크를 통해 실습 데이터셋을 받을 수 있습니다.
Data 탭에서 train.tsv.zip
, test.tsv.zip
을 내려받아 압축을 풀면 각각 train set과 test set의 tsv 파일을 얻을 수 있습니다.
해당 데이터셋에서 감정을 나타내는 label들의 의미는 다음과 같습니다.
- 0 : negative
- 1 : somewhat negative
- 2 : neutral
- 3 : somewhat positive
- 4 : positive
데이터 가져오기
우선 실습에 필요한 library들을 불러옵니다.
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
plt.style.use('dark_background')
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences
from sklearn.model_selection import train_test_split
from keras.utils.np_utils import to_categorical
from keras.models import Sequential
from keras.layers import Dense, Dropout, Embedding, LSTM, GlobalMaxPooling1D, SpatialDropout1D
to_categorical
은 keras의 버전에 따라
from keras.utils.np_utils import to_categorical
혹은
from keras.utils import to_categorical
둘 중 동작하는 것으로 진행하면 됩니다.
df_train = pd.read_csv('train.tsv', sep='\t')
print('train set: {0}'.format(df_train.shape))
df_train.head(10)
우선 train 데이터셋을 불러왔습니다. tsv 파일이므로 sep='\t'
옵션으로 데이터를 불러옵니다.
df_test = pd.read_csv('test.tsv', sep='\t')
print('test set: {0}'.format(df_test.shape))
df_test.head(10)
마찬가지로, test 데이터셋도 불러와줍니다.
데이터 전처리
replace_list = {r"i'm": 'i am',
r"'re": ' are',
r"let’s": 'let us',
r"'s": ' is',
r"'ve": ' have',
r"can't": 'can not',
r"cannot": 'can not',
r"shan’t": 'shall not',
r"n't": ' not',
r"'d": ' would',
r"'ll": ' will',
r"'scuse": 'excuse',
',': ' ,',
'.': ' .',
'!': ' !',
'?': ' ?',
'\s+': ' '}
def clean_text(text):
text = text.lower()
for s in replace_list:
text = text.replace(s, replace_list[s])
text = ' '.join(text.split())
return text
원활한 학습을 위해 전처리를 진행해줍니다. i'm
과 i am
은 같은 단어이지만 다르게 인식될 수 있으므로 형태를 통일해주고, 단어 뒤에 문장 부호가 붙어있는 경우에도 다른 단어로 인식되므로 빈 칸을 추가하여 단어와 문장 부호가 서로 다른 단어로 인식될 수 있게끔 전처리를 해줍니다. 또한, 대문자와 소문자의 차이도 제거하기 위해 모든 단어의 글자들을 .lower()
함수를 통해 소문자로 만들어 줍니다.
아래 코드를 통해 적용해줍니다.
X_train = df_train['Phrase'].apply(lambda p: clean_text(p))
이제, corpus(말뭉치)에 있는 구문의 길이를 시각화해보겠습니다.
phrase_len = X_train.apply(lambda p: len(p.split(' ')))
max_phrase_len = phrase_len.max()
print('max phrase len: {0}'.format(max_phrase_len))
plt.figure(figsize = (10, 8))
plt.hist(phrase_len, alpha = 0.2, density = True)
plt.xlabel('phrase len')
plt.ylabel('probability')
plt.grid(alpha = 0.25)
max phrase len: 53
가장 긴 문장의 길이는 53이며, 단어 길이의 분포는 위와 같습니다.
신경망에 대한 모든 입력은 길이가 같아야합니다. 따라서 가장 긴 길이를 나중에 모델에 대한 입력을 정의하는 데 사용할 변수로 저장합니다.
y_train = df_train['Sentiment']
감정 데이터가 있는 Sentiment
항목으로 train set을 만들어줍니다.
이제 tokenizer
를 통해 토큰화를 진행합니다.
또한, filters=
옵션을 통해 특수문자들을 제거해줍니다.
max_words = 8192
tokenizer = Tokenizer(
num_words = max_words,
filters = '"#$%&()*+-/:;<=>@[\]^_`{|}~'
)
tokenizer.fit_on_texts(X_train)
X_train = tokenizer.texts_to_sequences(X_train)
X_train = pad_sequences(X_train, maxlen = max_phrase_len)
y_train = to_categorical(y_train)
to_categorical
함수를 통해, y_train
값을 원-핫 벡터로 바꿔주어 인코딩 과정도 진행해줍니다.
Training
model_lstm = Sequential()
model_lstm.add(Embedding(input_dim = max_words, output_dim = 256, input_length = max_phrase_len))
model_lstm.add(SpatialDropout1D(0.3))
model_lstm.add(LSTM(256, dropout = 0.3, recurrent_dropout = 0.3))
model_lstm.add(Dense(256, activation = 'relu'))
model_lstm.add(Dropout(0.3))
model_lstm.add(Dense(5, activation = 'softmax'))
model_lstm.compile(
loss='categorical_crossentropy',
optimizer='Adam',
metrics=['accuracy']
)
LSTM 레이어를 만들어 모델을 작성합니다.
history = model_lstm.fit(
X_train,
y_train,
validation_split = 0.1,
epochs = 8,
batch_size = 512
)
epochs
는 8, batch_size
는 512로 설정하고 10%만큼을 validation set으로 이용하게 됩니다.
train이 완료되었습니다. history에는 LSTM fitting 과정에서 각 epoch마다의 loss
와 accuracy
데이터가 저장됩니다.
loss와 accuracy 데이터를 시각화하면 다음과 같습니다.
plt.clf()
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(loss) + 1)
plt.figure(figsize=(10, 8))
plt.plot(epochs, loss, 'g', label='Training loss')
plt.plot(epochs, val_loss, 'y', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()
plt.clf()
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
plt.figure(figsize=(10, 8))
plt.plot(epochs, acc, 'g', label='Training acc')
plt.plot(epochs, val_acc, 'y', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
plt.show()