원핫인코딩 텍스트 생성 딥러닝 학습하기

2020. 12. 16. 07:09

앞에서 텍스트 전처리와 토큰화된 데이터를 가지고 오늘은 블로그 텍스트를 생성하는 모형을 만들어보고자 한다. 우선 가장 쉬운(?) 원-핫 인코딩을 이용한 모델을 만들어 보고자 한다. 실제 활용보다는 학습 차원에서 한 번 해보는 정도라 가볍게(?) 해보고자 한다.

코랩을 사용해보니 몇 가지 불편한 점들이 생겼다. 하나는 코랩을 닫았다가 다시 켜면, 업로드한 파일을 다시 올려야 한다. 앞으로도 계속 사용할 파일들인데 매번 업로드해야 된다고 하니, 조금 피곤하다. 다른 하나는 RAM이 부족하다는 것이다. 데이터를 로드하다가 램이 부족해서 다운되었다. 어차피 테스트 정도이니 앞에 데이터만 잘라서 사용하기로 하였다.

 

원핫 인코딩 딥러닝

 

 

먼저 필요한 모듈들을 import 한다.

import sys
sys.path.append('/content')

import pandas as pd
import pickle
from textprepr import TextPreprocessing
import sqlite3
import numpy as np

 

textprepr은 앞에서 작성한 텍스트 전처리 및 토근화하는 코드이다. 다음으로 블로그 데이터를 불러온다.

con = sqlite3.connect("/content/post_data.db")
df = pd.read_sql("SELECT * FROM total_df", con)
df_content = df["content"]

 

다음 블로그 텍스트를 토큰화하여 리스트에 저장한다.

tp = TextPreprocessing()
tokenized_data = [tp.tagging(x) for x in df_content]

 

원핫 인코딩을 하기 위해서는 텍스트에 사용한 모든 데이터 번호를 매개야 한다. 이 작업은 딕셔너리를 이용하면 사용하기 편리하다. pos_dic은 워드를 번호로 바꾸는 딕셔너리이다. 반대로 번호를 워드로도 바꿔줘야 하기 때문에, 키와 값을 뒤바꾼 num_to_word_dic도 만들었다.

# 형태소 딕셔너리 구축
tot_pos = list()
for x in tokenized_data:
	tot_pos = tot_pos + x 

tot_pos_nodup = list(set(tot_pos))

pos_dic = {pos:idx for idx,pos in enumerate(tot_pos_nodup)}
num_to_word_dic = {pos_dic.get(key):key for key in pos_dic}
dic_size = len(pos_dic)
print("딕셔너리 크기: {}".format(dic_size))

 

생성한 딕셔너리의 크기는 6864개였다. 이제 위에서 만든 딕셔너리를 이용해서 원-핫 인코딩을 하는 함수를 만든다.

def to_onehot(pos_dic, pos):
	"""
		전체 pos 딕셔너리와 pos를 입력받아, 원핫인코딩한 벡터를 return 한다.
	"""
	zero_list=np.zeros(len(pos_dic))
	if pos_dic.get(pos,0)==0:
		pass
	else:
		zero_list[pos_dic.get(pos)]=1

	return zero_list

 

원-핫 인코딩 함수를 이용해서, 딥러닝 모델에 넣을 데이터를 만든다. 19개의 단어로 뒤에 나올 단어(20번째)를 에측하는 모델을 만드려고 한다. 19에 해당하는 숫자는 계속 사용해야 하므로 x_len이라는 변수에 저장하였다.

x_len = 19

def make_train_test(content_pos):
	"""
		train과 test셋으로 나누기
	"""
	data = list()
	for i in range(len(content_pos)-x_len+1):
		data.append(content_pos[i:i+x_len+1])

	tot_list = list()
	for d in data:
		if len(d)==x_len+1:
			temp_list = [to_onehot(pos_dic, pos) for pos in d]
			tot_list.append(temp_list)
		else:
			temp_list = [to_onehot(pos_dic, pos) for pos in d]
			
			for i in range(0,x_len+1-len(d)):
				temp_list.append(to_onehot(pos_dic, ""))

			tot_list.append(temp_list)

	tot_np = np.array(tot_list)

	x_train = tot_np[:, 0:x_len, :]
	y_train = tot_np[:, x_len, :]

	return x_train, y_train

 

글이 여러개 있어 토큰화한 다음에 합쳐야 한다. 그래서 빈 ndarray를 만든 후에 합치기로 했다. 여기서 메모리가 Full로 차면서, 에러가 발생했다. 그래서, 일부 데이터만 사용하기로 하였다. tqdm을 이용해서 진행바도 출력해보았다.

x_train = np.empty(shape=(0, x_len, dic_size))
y_train = np.empty(shape=(0, dic_size))

pbar = tqdm(total=len(tokenized_data))
for idx, x in enumerate(tokenized_data):
	if len(x)<20:
		pass
	else:
		x_temp, y_temp = make_train_test(x)
		x_train = np.vstack((x_train, x_temp))
		y_train = np.vstack((y_train, y_temp))

	if idx>10:
		break

	pbar.update(10)

pbar.close()

 

이제 데이터를 다 만들었으니, 모델을 만들고 학습을 시켜야 한다. 모델은 케라스로 만드는 것이 가장 쉬울 것 같아 케라스로 LSTM모형을 간단하게 만들어 보았따. activation function이나 loss함수, optimizer등에 알아야 할 필요가 있는데, 이는 나중에 따로 내용을 다뤄보도록 하겠다.

from keras.models import Sequential
from keras.layers import LSTM, Dropout, Dense, Activation

model = Sequential()
model.add(LSTM(x_len, input_shape=(x_len,dic_size)))
model.add(Dense(dic_size, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='SGD', metrics='accuracy')
model.summary()

 

summary함수를 호출하면 아래와 같이 모델의 구조를 확인할 수 있다.

케라스 모델 summary

 

이제 데이터도 만들었고, 모형도 만들었으니 학습을 시작해보자.

model.fit(x_train, y_train, batch_size=20, epochs=100)

 

학습중에 epoch마다 결과를 아래와 같이 확인할 수 있다.

학습결과

 

이제 학습도 끝났으니, 예측을 해보도록 하자.

tot = 0
is_right = 0

for sen, y in zip(x_train, y_train):
	for word in sen:
		dic_idx = word.argmax()
		word = num_to_word_dic.get(dic_idx)
		print(word, end=", ")

	predict=model.predict(sen.reshape(1, x_len, dic_size))
	pred_word = num_to_word_dic.get(predict.argmax())
	real_word = num_to_word_dic.get(y.argmax())
	print(" | 예측: {}, 정답: {}".format(pred_word, real_word))

	tot+=1

	if pred_word==real_word:
		is_right+=1

	if tot==20:
		print("최종 맞춘 갯수: {}".format(is_right))
		break

 

예측 결과는 아래와 같다. 조사까지 넣으면 잘 안 나올 것 같아, 명사만 추렸다. 그래서 문장처럼 보이지는 않는다. 예측결과는 별로 좋지 않지만, 이게 목적은 아니기 때문에 여기까지만 진행하고 넘어어가기로 했다. 다음 포스팅에서는 LSTM에 학습시키기 전에, '워드투벡'을 다뤄보도록 하겠다.

예측 결과

 

댓글()