본문 바로가기
기계학습/자연어 처리 머신러닝

글자 단위 RNN - N:1 모델 LSTM

by tryotto 2020. 2. 18.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# 파일 가져오기
= open('/content/drive/My Drive/train_char.txt''rb')
 
 
 
# 불필요한 내용들 제거
lines = []
 
for s in f:
  # \n, \r 제거
  s = s.strip()
 
  # 길이=0 인 문장 제거
  if len(s)==0:
    continue
 
  # 소문자 변환
  s = s.lower()
 
  # 아스키코드 제거
  s = s.decode('ascii''ignore')
 
  lines.append(s)
 
f.close()
 
 
 
 
# 하나의 text 로 연결지어주기
text = " ".join(lines)
 
 
 
 
# 특정 길이만큼씩 text 를 분할해서 저장하기
len_text = len(text)
 
train_X = []
train_Y = []
 
len_line = 10
for i in range(len_line, len_text-len_line):
  tmp = text[ i : i + len_line]
  train_X.append(tmp)
  train_Y.append(text[i+len_line])
 
 
 
 
# char_to_idx, idx_to_char 만들기
char_list = sorted(list(set(text)))
char_size = len(char_list)
 
char_to_idx = {}
idx_to_char = {}
for idx, char in enumerate(char_list):
  char_to_idx[char]=idx
  idx_to_char[idx]=char
 
 
 
 
# train_X, train_Y 를 정수로 인코딩 하기
seq_X = []
seq_Y = []
tmp = []
 
for line in train_X:
  tmp = [char_to_idx[c] for c in line]
  seq_X.append(tmp)
  tmp = []
 
for c in train_Y:
  seq_Y.append(char_to_idx[c])
 
 
 
 
# 패딩 연산 수행하기
from tensorflow.keras.preprocessing.sequence import pad_sequences
 
pad_X = pad_sequences(seq_X, maxlen=len_line)
 
 
 
 
# X 데이터를 원핫벡터로 변환하기
from tensorflow.keras.utils import to_categorical
 
onehot_X = to_categorical(pad_X)
onehot_Y = to_categorical(seq_Y)
 
 
 
 
# 모델 설계하기
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, LSTM, TimeDistributed
 
model = Sequential()
model.add(LSTM(256, input_shape=(None, char_size)))
model.add(Dense(char_size, activation='softmax'))
 
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['acc'])
model.fit(x=onehot_X, y=onehot_Y, batch_size=100, epochs=5)
cs


N:N 방식과 다를 게 없는 모델이었다.

전처리 하는 방식이 조금 색다르긴 하다



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import numpy as np
 
# 임의의 문장을 출력하는 함수 생성 - 모델, 입력 문장, 희망 문장 길이를 받는다
def sentence_generate(model, seed_sentence, len_make_sentence):
  # seed_sentence : 계속 원문을 유지한 채로, 새로운 char 를 맨 뒤에 덧붙인다. str 자료형.
  seed_sentence = seed_sentence.lower()
  
  for i in range(len_make_sentence):
    # seq_X_encoded : seed_sentence 를 매 반복문마다 정수 인코딩해준 리스트 
    seq_X_encoded = [char_to_idx[c] for c in seed_sentence]
    seq_X_encoded = pad_sequences([seq_X_encoded], maxlen=len_make_sentence, padding='pre')
    onehot_X_encoded = to_categorical(seq_X_encoded, num_classes=char_size)
 
    predict_idx = np.argmax(model.predict(x=onehot_X_encoded))
    tmp_char = idx_to_char[predict_idx]
 
    seed_sentence = tmp_char
  return seed_sentence
    
cs


wlr


>> sentence_generate(model, 'I GET on W', len_line)


이런 식으로, 임의의 str 을 전달한 뒤, 최대 문장 길이인 len_line 을 전달하면

전달받은 seed_sentence 를 기점으로 새로운 문장을 생성해낸다


**

다른 것보다, 차원수를 맞추기 위해 전처리 하는 과정이 꽤 걸렸다.


pad_sequence 를 하기 위해선 최소 2차원 텐서이여야 하며,

to_categorical 에서 num_classes 를 설정해주면, 원핫벡터의 차원 수를 내가 원하는 만큼 설정할 수 있으므로 차원수를 맞출때 용이하다

argmax 의 경우, 최대 value 값을 가진 key 값인 인덱스 값을 출력한다는 점도 기억하자