x_train = np_data[:, 0:19, :]
y_train = np_data[:, 19, :]
np_data = np.empty(shape=(0,20,vec_size))
for x in tqdm(tot_list):
if len(x)>=20:
temp = make_df_vec(x)
np_data=np.vstack((np_data, temp))
else:
pass
딥러닝 프레임워크는 여러가지가 있다. 그 중에서 가장 많이 사용하는 것은 Tensorflow, Keras, Pytorch인 것 같다. Tensorflow와 Keras를 일반적으로 많이 사용하는 듯 하나, 새로운 기술들은 Pytorch로 먼저 공개가 많이 되는 듯 하여 Pytorch를 중점적으로 배워보고자 한다. 오늘은 파이토치를 이용해서, 블로그 텍스트 생성을 해보고자 한다.
코랩을 계속해서 이용하다 보니 불편한 점들이 하나씩 생긴다. 그 중에 하나는 생성해놓은 워드투벡 파일을 사용하기 어렵다는 점이다. 코랩에 업로드하여 사용하고 있는데, 어쩔 때는 파일이 깨져서인지 인코딩 에러가 발생한다. 그래서 zip파일로 압축하여 업로드한 후에 압축을 해제하여 사용해봤는데 다행히 이상 없이 잘 동작한다.
import sys
sys.path.append('/content')
import pandas as pd
import pickle
from textprepr import TextPreprocessing
import sqlite3
import numpy as np
from tqdm import tqdm
con = sqlite3.connect("/content/post_data.db")
df = pd.read_sql("SELECT * FROM total_df", con)
import gensim
class MyWord2Vec:
"""
토큰화된 텍스트를 워드투벡으로 임베딩해서 반환
"""
def __init__(self, file="/content/ko_new.bin"):
self.model = gensim.models.Word2Vec.load(file)
def to_w2v(self, pos):
"""
pos:단어,, 워드투벡으로 임베딩
"""
try:
pos_vec = self.model.wv.get_vector(pos)
except:
pos_vec = np.zeros(200, )
return pos_vec
def get_predict_words(self, predict):
return self.model.wv.most_similar(positive=predict, topn=1)[0][0]
w2v = MyWord2Vec()
x_len = 20
vec_size = 200
def make_df_vec(content_pos, x_len=x_len):
"""
여러 단어를 워드투벡으로 변경하기
"""
data = list()
for i in range(len(content_pos) - x_len):
data.append(content_pos[i:i+x_len])
tot_list = list()
for d in data:
temp_list = np.array([w2v.to_w2v(pos) for pos in d])
tot_list.append(temp_list)
np_data = np.array(tot_list)
return np_data
tp = TextPreprocessing()
tot_list = [tp.tagging(x) for x in df["content"]]
처음에 할 때 메모리 초과로 코랩이 계속 재부팅되어, 가비지 컬렉트 하는 코드를 추가하였다.
del(df)
import gc
gc.collect()
np_data = np.empty(shape=(0,20,vec_size))
for x in tqdm(tot_list):
if len(x)>=20:
temp = make_df_vec(x)
np_data=np.vstack((np_data, temp))
else:
pass
numpy의 random.shuffle함수로 만들어 놓은 데이터셋을 섞었다.
x_train = np_data[:, 0:19, :]
y_train = np_data[:, 19, :]
파이토치는 GPU를 사용하기 위해, 모델과 입력데이터를 GPU에 넣어주어야 한다. GPU를 사용가능한 환경인지 확인하고, 이를 DEVICE라는 변수에 저장하였다.
import torch
USE_CUDA = torch.cuda.is_available()
DEVICE = torch.device("cuda" if USE_CUDA else "cpu")
from torch import nn
class TextGenerator(nn.Module):
def __init__(self, input_size, hidden_size):
super(TextGenerator, self).__init__()
self.lstm = nn.LSTM(200,200,2, batch_first = True, bidirectional = True)
self.dropout1 = nn.Dropout(p=0.5)
self.lstm2 = nn.LSTM(400, 300, 2)
self.dropout2 = nn.Dropout(p=0.3)
self.linear1 = nn.Linear(300, 300)
self.linear2 = nn.Linear(300, 300)
self.dropout3 = nn.Dropout(p=0.1)
self.linear3 = nn.Linear(300, 200)
self.relu = nn.ReLU()
def forward(self, X):
h_t, _ = self.lstm(X)
relu1 = self.relu(h_t)
relu1 = self.dropout1(relu1)
lstm2, _ = self.lstm2(relu1)
relu2 = self.relu(lstm2)
relu2 = self.dropout2(relu2)
linear1 = self.linear1(relu2)
linear1 = self.relu(linear1)
temp = torch.sum(linear1, dim=1)
linear2 = self.linear2(temp)
relu3 = self.relu(linear2)
relu3 = self.dropout3(relu3)
logit = self.linear3(relu3)
output = self.relu(logit)
return output
파이토치도 Sequential로 쌓을 수 있어서 그렇게 하려고 했는데, 에러가 발생한다. 아래와 같이 하면, LSTM에서 튜플을 반환하기 때문에 ' AttributeError: 'tuple' object has no attribute 'dim'' 에러가 발생한다. nn.SelectTable(-1)모듈을 넣어서 문제를 해결하는 코드가 있어서, 추가하려고 했는데 버전이 달라서 그런지 모듈이 없다.
model=nn.Sequential(
nn.LSTM(200,100,2),
nn.Linear(100,200)
)
model 객체를 만들고, 출력해 보았다.
model = TextGenerator(200,100).to(DEVICE)
print(model)
TextGenerator(
(lstm): LSTM(200, 200, num_layers=2, batch_first=True, bidirectional=True)
(dropout1): Dropout(p=0.5, inplace=False)
(lstm2): LSTM(400, 300, num_layers=2)
(dropout2): Dropout(p=0.3, inplace=False)
(linear1): Linear(in_features=300, out_features=300, bias=True)
(linear2): Linear(in_features=300, out_features=300, bias=True)
(dropout3): Dropout(p=0.1, inplace=False)
(linear3): Linear(in_features=300, out_features=200, bias=True)
(relu): ReLU()
)
학습률과 optimizer, 손실함수를 지정하였다.
learning_rate = 0.005
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
criterion = torch.nn.MSELoss()
모델을 train모드로 바꾸고 학습을 시작하였다. 학습데이터를 GPU에 넣어야 하는데, GPU메모리 때문에 에러가 발생한다. 그래서 배치크기를 정하고, 배치크기에 해당하는 데이터만 잘라서 GPU에 넣을 수 있도록 수정하였다.
model.train()
batch_size = 2048
for epoch in range(0,15):
loss_list = list()
run_th = int(x_train.shape[0]/batch_size)+1
for i in range(0, run_th):
optimizer.zero_grad()
x_temp=x_train[i*batch_size:(i+1)*batch_size]
y_temp=y_train[i*batch_size:(i+1)*batch_size]
input_x = torch.FloatTensor(x_temp).to(DEVICE)
input_y = torch.FloatTensor(y_temp).to(DEVICE)
train_output = model(input_x)
loss=criterion(train_output,input_y)
loss.backward()
optimizer.step()
loss_list.append(loss)
gc.collect()
print("loss: {}".format(sum(loss_list)))
모델을 평가 모드로 바꾸고, 예측을 해 보았다.
model.eval()
tot =0
is_right = 0
for idx in range(0, x_train.shape[0]):
word_vec_list = x_train[idx]
y=y_train[idx]
for word_vec in word_vec_list:
word=w2v.get_predict_words([word_vec])
print(word, end=",")
# 예측
temp = torch.FloatTensor(word_vec_list).to(DEVICE)
temp=temp.unsqueeze(0)
pred=model(temp).to("cpu")
pred=pred.detach().numpy()[0]
pre_pos=w2v.get_predict_words([pred])
real_word = w2v.get_predict_words([y])
print("|predict={} | real={}".format(pre_pos, real_word))
tot +=1
if pre_pos==real_word:
is_right+=1
if idx==50:
print("전체 갯수:{}, 최종 맞춘 갯수: {}".format(tot, is_right))
break
예측한 결과는 그다지 좋지 않지만, 이 역시 학습의 목적이 강하므로 우선 패쓰다.
위에 작성한 파이토치 코드는 동작하지만, formal하지 않은 면이 있는 것 같다. 배치크기도 직접 잘랐는데 이렇게 사용하지 않고, dataloader를 이용하는 것이 훨씬 편리할 듯 하다. 학습률도 변경해서 하면, 더욱 효율적일 듯 하다. 다음 포스팅에서는 이 코드를 더욱 디벨롭하는 방향으로 작업을 해보도록 하겠다.
'딥러닝' 카테고리의 다른 글
파이썬, 얼굴 사진을 애니메이션 스타일로 바꾸는 방법은?! (0) | 2021.02.26 |
---|---|
파이토치 torchvision 이미지 딥러닝 모델 알아보기 (0) | 2021.02.17 |
딥러닝 워드투벡 블로그 텍스트 생성 해보기! (0) | 2020.12.30 |
워드투벡 단어 추가 및 학습시키는 방법은?! (1) | 2020.12.24 |
워드투벡 의미와 한글에 적용하는 방법은?! (0) | 2020.12.23 |