이미 오래전 기술이지만, 간단한 텍스트 생성 예제를 통해 RNN 활용(!)을 이해해본다. 아래 링크와 전적으로 동일한 것이며, 개인의 이해를 위해 정리해 두는 것 뿐임. 링크를 통해 이해하는 것이 더 확실하다.
아래 세 문장으로 학습을 하고, 첫 단어를 입력하면 나머지 문장을 만들어 주는 것이다.
경마장에 있는 말이 뛰고 있다
그의 말이 법이다
가는 말이 고와야 오는 말이 곱다
이전에도 이야기했듯이 수식으로 이해할 생각은 전혀 없고, 활용에만 집중하고 싶다. 위의 그림을 간단히 이해하자면 다음 Step의 결과는 현재의 입력과 직전의 Hidden State가 영향을 준다는 것이다. 얼마나 영향을 주는 지를 나타내는 Weight를 찾아 모델을 구성해 주는 것으로 이는 라이브러리가 해 주니 간단히 넘어가자.
이전 포스팅에서도 이해했듯이 자연어처리는 1) 데이터를 읽어들여 이를 토큰화하고 인덱싱 한뒤, 2) 임베딩하여 모델을 구성하는 단계로 구성된다. 우선 세 문장을 읽어들여 사전을 만든다.
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
import numpy as np
from tensorflow.keras.utils import to_categorical
text="""경마장에 있는 말이 뛰고 있다\n
그의 말이 법이다\n
가는 말이 고와야 오는 말이 곱다\n"""
t = Tokenizer()
vocab_size = len(t.word_index)+1
t.word_index
==>
{'말이': 1,
'경마장에': 2,
'있는': 3,
'뛰고': 4,
'있다': 5,
'그의': 6,
'법이다': 7,
'가는': 8,
'고와야': 9,
'오는': 10,
'곱다': 11}
사전을 만들었으니, 자연어 처리를 위해 각 문장의 단어를 인덱싱하고 모두 길이가 같도록 패딩한다.
sequences = list()
// texts_to_sequences가 단어를 인덱스로 바꿈
// RNN은 문장 내 단어의 순서를 학습하는 것이므로 한 문장이 이루어지기까지의 순서를 X로 활용한다.
// 예) 그의 말이 법이다는 '그의' '말이'와 '그의' '말이' '법이다'의 두 개의 데이터가 된다.
for line in text.split('\n'):
encoded = t.texts_to_sequences([line])[0]
for i in range(1, len(encoded)):
sequence = encoded[:i+1]
sequences.append(sequence)
// 제일 긴 문장의 단어수가 최대 길이
max_len=max(len(l) for l in sequences)
// 패딩
sequences = pad_sequences(sequences, maxlen = max_len, padding='pre')
sequences
==>
array([[ 0, 0, 0, 0, 2, 3],
[ 0, 0, 0, 2, 3, 1],
[ 0, 0, 2, 3, 1, 4],
[ 0, 2, 3, 1, 4, 5],
[ 0, 0, 0, 0, 6, 1],
[ 0, 0, 0, 6, 1, 7],
[ 0, 0, 0, 0, 8, 1],
[ 0, 0, 0, 8, 1, 9],
[ 0, 0, 8, 1, 9, 10],
[ 0, 8, 1, 9, 10, 1],
[ 8, 1, 9, 10, 1, 11]], dtype=int32)
// 마지막 칼럼의 값이 다음에 올 거라 예상되는 단어, 즉 Y값이 된다.
// 예) '그의' '말이' 라는 데이터에서 '말이'가 Y값이 된다.
X = sequences[:,:-1]
y = sequences[:, -1]
// 학습을 위해 Y를 One-Hot Vector로 변경
y = to_categorical(y, num_classes = vocab_size)
==>
array([[0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],
[0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
[0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0.],
[0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]], dtype=float32)
데이터 준비가 되었으므로 Keras를 이용해 모델을 만들어 준다.
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding, Dense, SimpleRNN
model = Sequential()
model.add(Embedding(vocab_size, 10, input_length=max_len-1))
model.add(SimpleRNN(32))
model.add(Dense(vocab_size, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
model.fit(X, y, epochs=200, verbose=2)
학습 문장이 적어 순식간에 학습이 마쳐진다. 학습 결과를 간단히 테스트 해 보자
// 단어를 indexing하고 패딩해서 배열을 만든 후 테스트
encoded = t.texts_to_sequences(['경마장에'])[0] # 현재 단어에 대한 정수 인코딩
encoded = pad_sequences([encoded], maxlen=5, padding='pre') # 데이터에 대한 패딩
model.predict_classes(encoded)
==>
array([3])
위의 사전에 배열 세번째는 '있는'으로 우리가 학습한 '경마장에 있는 말이 뛰고 있다'를 고려하면 '경마장에' 다음의 단어를 제대로 찾았다. 이를 문장으로 구성할 수 있게 함수를 만들어본다.
def sentence_generation(model, token, current_word, n_word):
init_word = current_word
sentence = ''
for _ in range(n_word):
encoded = token.texts_to_sequences([current_word])[0] # 현재 단어에 대한 정수 인코딩
encoded = pad_sequences([encoded], maxlen=5, padding='pre') # 데이터에 대한 패딩
result = model.predict_classes(encoded)
for word, index in token.word_index.items():
if index == result:
break;
// 현재 단어 그룹에 위에서 찾은 단어를 덧붙여 n_word만큼 학습한다.
// '그의' -> '그의', '말이'
current_word = current_word + ' ' + word
sentence = sentence + ' ' + word
sentence = init_word + sentence
return sentence
sentence_generation(model, t, '그의',2)
==> '그의 말이 법이다'
sentence_generation(model, t, '경마장에',4)
==> '경마장에 있는 말이 뛰고 있다'
너무 간단해서 AI를 써야하는 일인지 헷갈리겠지만, 기본 구조만 이해하고 가자.
※ Disclaimer: 본인은 AI알고리즘을 연구하지 않습니다. 의미 부분만 대충 파악하고 싶어서 정리하는 것이고 내용에 다소 때로는 상당히 오류가 있을 수 있으며, 아주아주 단편적 내용만 담고 있습니다. 참고만 하시고 각자 열심히 공부하시길 바랍니다 :D
'AI 빅데이터 > 후려치는 데이터분석과 AI 알고리즘' 카테고리의 다른 글
[데이터분석] 시계열분석 2 - XGBoost (1) | 2020.09.10 |
---|---|
[데이터분석] 시계열 분석 1 - ARIMA (937) | 2020.09.09 |
[자연어 처리] Seq2Seq 로 자연어 번역하기 (0) | 2020.08.05 |
[자연어처리] 간단하게 텍스트 감성 분류하기 (0) | 2020.07.13 |
[영상인식] GAN과 AutoEncoder (1) | 2020.06.14 |
댓글