본문 바로가기
카테고리 없음

[Keras] AI-HUB 감성 대화 말뭉치 분류 - LSTM

by 공부하는우니 2024. 5. 28.

- cpu활용시 속도 매우 느림

- Colap T4 사용시 4분가량 학습

- 여전히 val_accuracy < 0.5 정도라서 개선 필요

# 한글 처리
# !sudo apt-get install -y fonts-nanum
# !sudo fc-cache -fv
# !rm ~/.cache/matplotlib -rf

import matplotlib.pyplot as plt

plt.rc('font', family='NanumBarunGothic')

import numpy as np
import pandas as pd

# AI-HUB 감성 대화 말뭉치 활용하여 만든 데이터 읽어오기
final_data = pd.read_csv('https://github.com/ohgzone/file1/raw/main/aihub_coupus.csv' )
print(final_data.head())
print(final_data.info())

# '문장' 컬럼의 내용중에 영문, 특수문자 있는지 확인 : 영문과 특수문자 존재 확인
print(final_data[final_data['문장'].str.contains('[^가-힣 ]')].values[:10]) # 모든 한글 + 공백 + 특수문자를 포함하면 출력

# 특수문자 제거
print('before', final_data['문장'][:10])
# final_data['문장'] = final_data['문장'].str.replace('[^가-힣 ]', '')
final_data['문장'] = final_data['문장'].str.replace('[^가-힣 ]','', regex=True) # regex : regex 문법을 이용 (정규표현식)

print('after', final_data['문장'][:10])
print('sum', final_data[final_data['문장'].str.contains('[^가-힣 ]')].sum())

# 양쪽 빈 공간 처리
final_data['문장'] = final_data['문장'].str.strip()

# null 체크
print(final_data.isnull().sum())

# 중복 체크
print('duplicate : ', final_data['문장'].duplicated().sum())
final_data.drop_duplicates(subset=['문장'], inplace = True)
print('duplicate after: ', final_data['문장'].duplicated().sum())


# 여기까지는 RNN과 동일

# 라벨 분포 확인
print(final_data['감정'].value_counts())
final_data['감정'].value_counts().plot(kind='bar')
# plt.show()

# 라벨 인코딩을 직접 수행
list1 = final_data['감정'].value_counts().index.values
print(list1)
# 라벨와 클래스을 매핑 작업
label2class = {}
class2label = {}
for cl, la in enumerate(list1):
  # print(i, j)
  label2class[la] = cl
  class2label[cl] = la

print(label2class)
print(class2label)

final_data['label'] = final_data['감정'].map(label2class)

print(final_data.tail())

features = final_data['문장'].values # 굳이 values필요한가?
labels = final_data['label'].values


# train-test 분리
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(features, labels , test_size=0.2, stratify=labels, random_state=41)
print(x_train.shape, x_test.shape, y_train.shape, y_test.shape)

# 케라스를 이용한 LSTM
import tensorflow as tf
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences

# Tokenizer 구현 : 단어 사전 만들기(fit_on_texts)
tokenizer = Tokenizer()
tokenizer.fit_on_texts(x_train)

print(tokenizer.word_index) # 단어 -> 숫자 인덱싱
print(tokenizer.index_word) # 숫자 -> 단어 인덱싱
print(tokenizer.word_counts) # 단어별 빈도수 확인

max_words = len(tokenizer.index_word) # 총 단어 갯수 : 47,646
print(max_words)


# 문장을 숫자로 나열 texts_to_sequences
x_train_seq = tokenizer.texts_to_sequences(x_train)
x_test_seq = tokenizer.texts_to_sequences(x_test)
print(len(x_train_seq), len(x_test_seq))

print(x_train[1:3]) # ['혼자가 편하다고 한 게 후회돼' '나 부모님께 너무 죄송한 마음이 들어']
print(x_train_seq[1:3]) # [[1664, 8292, 40, 15, 195], [5, 232, 1, 3601, 59, 24]]

# padding
# 문장의 최대 길이 파악 : 제일 긴 문장 seq 길이는 38개로 구성됨.
print('len', max(len(line) for line in x_train_seq))

# 모든 문장을 최대 문장 Seq 길이 38에 맞춤
x_train_pad = pad_sequences(x_train_seq, maxlen=38)
x_test_pad = pad_sequences(x_test_seq, maxlen=38)

# 패딩 확인
print(x_train_pad[:1])
print(x_train_pad.shape, x_test_pad.shape)


print('x_test_pad.shape', x_test_pad.shape) # (10315, 38)
print('x_test_pad[:1]', x_test_pad[:1].shape) # (1, 38) => 추론시 이 형태로 사용
print('x_test_pad[0]', x_test_pad[0].shape) # (38,)

# LSTM
from tensorflow.keras.layers import Dense, Flatten, Conv1D, MaxPool2D
from tensorflow.keras.layers import Embedding, Bidirectional, LSTM, SimpleRNN, GRU
from tensorflow.keras.models import Sequential
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint

max_words = 47646 + 1    # 총 단어 갯수 + padding 0 번호
max_len = 38             # 최대 문장 길이
embedding_dim = 32      # embedding 차원

model = Sequential()

model.add(Embedding(max_words, embedding_dim, input_length=max_len)) # 단어를 의미있는 32 차원으로 Vector 변경(Embedding)

# LSTM 모델
model.add(LSTM(16, return_sequences=True))
model.add(LSTM(16, return_sequences=True))
model.add(Flatten())
model.add(Dense(128, activation='swish'))
model.add(Dense(32, activation='swish'))
model.add(Dense(6, activation='softmax')) # 최종 6가지로 분류

# 모델 compile
model.compile(loss = 'sparse_categorical_crossentropy',
              optimizer = 'adam',
              metrics = 'accuracy')
model.summary()

# early stopping 및 ckpt
es = EarlyStopping(monitor='val_loss', patience=10, verbose=1)
checkpoint_path = 'tmp_checkpoint.ckpt'
cp = ModelCheckpoint(checkpoint_path, monitor='val_loss', verbose=1, save_best_only=True)

history = model.fit(x_train_pad, y_train, epochs=50, batch_size=512,
                      validation_split=0.2, verbose =1, callbacks=[es, cp])

epochs = range(1, len(history.history['accuracy']) + 1)
plt.plot(epochs, history.history['accuracy'])
plt.plot(epochs, history.history['val_accuracy'])
plt.title('model accuracy')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'valid'], )
# plt.show()

model.evaluate(x_test_pad, y_test)

print(f'문자열 : {x_test[0]}')
print(f'Sequence : {x_test_pad[0]}')

# 모델 예측하기(predict)
print('x_test_pad[:1]', x_test_pad[:1], x_test_pad[:1].shape)
print('x_test_pad[0]', x_test_pad[0], x_test_pad[0].shape)
predict = model.predict(x_test_pad[:1])
# predict = model.predict(x_test_pad[0]) # error

print(f'True : {class2label[y_test[0]]}')
print(f'Predict : {class2label[np.argmax(predict)]}')
plt.show()

댓글