Sequence-to-Sequence, seq2seq
Ref: 딥러닝을 이용한 자연어 처리 입문, keras
Seq2seq
기본 디자인
인코더
- 모든 단어들을 순차적으로 입력받은 뒤에 마지막에 이 모든 단어 정보들을 압축해서 하나의 벡터로 만듦(컨텍스트 벡터)
- 이 컨텍스트 벡터1를 디코더로 전달
디코더
- 컨텍스트 벡터를 받아서 번역된 단어를 하나씩 순차적으로 출력
내부 확대
- 인코더와 디코더는 두 개의 RNN 아키텍처로 이루어져 있음.
- 인코더: 입력 문장을 받는 RNN 셀
- 디코더: 출력 문장을 받는 RNN 셀
RNN, LSTM, GRU 중 무엇을 사용하느냐는 모델에 따라, 성능에 따라 달리 선택
- 인코더는 단어 토큰화를 통해 단어들을 RNN 셀의 각 시점으로 입력하는데 그 후에 인코더 RNN 셀의 마지막 시점의 은닉 상태(컨텍스트벡터)를 디코더 RNN 셀로 넘겨줌.
컨텍스트 벡터는 디코더의 첫번째 은닉 상태에 사용
- 초기값인 sos가 들어가면 순차적으로 다음에 올 단어를 예측 후 다음 타임스템에 전달, eos가 나올때까지 진행됨
\(\star\) sos
- 디코더의 초기 입력, 문장의 시작을 의미
- 다음 등장할 확률이 높은 단어를 예측
\(\star\) eos
- 디코더의 끝 값, 문장의 끝을 의미
- 각 RNNLM(RNN Language Model)은 단어 토큰을 입력 받을 때 임베딩 과정을 거쳐서 텍스트를 벡터로 바꿈(단, 사이즈는 수백 개의 차원을 가질 수 있음)
하나의 RNN 셀 확대
- 입력
- time step t-1에서의 은닉 상태
- 현재 time-step t에서의 입력 벡터
- 은닉 상태(입력으로 만들어낸, 필요없으면 값 무시 가능)
- time step t에서의 은닉 상태
- 현재 time-step t에서의 은닉 상태
- 의미하는 바
- 현재 시점 t에서의 은닉 상태는 과거 시점의 동일한 RNN 셀에서의 모든 은닉 상태의 값들의 영향을 누적해서 받아온 값
- 입력 문장의 단어 토큰들의 정보를 요약해서 담고 있다고 할 수 있음
실습
import os
import shutil
import zipfile
import pandas as pd
import tensorflow as tf
import urllib3
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.utils import to_categorical
= urllib3.PoolManager()
http ='http://www.manythings.org/anki/fra-eng.zip'
url = 'fra-eng.zip' filename
= os.getcwd() path
path
'/home/csy/Dropbox/md/posts/ml_basic'
= os.path.join(path, filename) zipfilename
zipfilename
'/home/csy/Dropbox/md/posts/ml_basic/fra-eng.zip'
with http.request('GET', url, preload_content=False) as r, open(zipfilename, 'wb') as out_file:
shutil.copyfileobj(r, out_file)
with zipfile.ZipFile(zipfilename, ‘r’) as zip_ref: zip_ref.extractall(path)
= pd.read_csv('./fra.txt', names=['src', 'tar', 'lic'], sep='\t') lines
del lines['lic']
print('전체 샘플의 개수 :',len(lines))
전체 샘플의 개수 : 217975
= lines.loc[:, 'src':'tar']
lines = lines[0:60000] # 6만개만 저장
lines 10) lines.sample(
src | tar | |
---|---|---|
57809 | There are no examples. | Il n'y a pas d'exemples. |
57733 | The problem isn't new. | Le problème n'est pas nouveau. |
39939 | I use worms as bait. | J'utilise des vers comme appât. |
28156 | That's irrelevant. | Ça n'a rien à voir. |
31341 | Everyone has voted. | Tout le monde a voté. |
47860 | I'm feeling fine now. | Maintenant je me sens bien. |
2181 | I'm pooped. | Je suis crevé. |
30174 | You can't just go. | Tu ne peux pas simplement t'en aller comme ça. |
14843 | I didn't scream. | Je n'ai pas hurlé. |
28558 | They're separated. | Ils sont séparés. |
sos, eos 대신 \t
,\n
를 입력
= lines.tar.apply(lambda x : '\t '+ x + ' \n')
lines.tar 10) lines.sample(
src | tar | |
---|---|---|
20466 | I think I got it. | \t Je crois avoir compris. \n |
46024 | His blood is boiling. | \t Son sang bout. \n |
18508 | You're too slow. | \t Tu es trop lente. \n |
31397 | Everything's wrong. | \t Tout va mal. \n |
8888 | Stop shooting. | \t Arrêtez de tirer. \n |
16664 | Spring has come. | \t Le printemps est arrivé. \n |
47044 | I meant the opposite. | \t Je voulais dire le contraire. \n |
11988 | Leave it there. | \t Laissez-le là. \n |
44016 | You are such a liar. | \t Tu es un sacré menteur. \n |
55752 | I'm a biology student. | \t Je suis étudiant en biologie. \n |
# 문자 집합 구축
= set()
src_vocab for line in lines.src: # 1줄씩 읽음
for char in line: # 1개의 문자씩 읽음
src_vocab.add(char)
= set()
tar_vocab for line in lines.tar:
for char in line:
tar_vocab.add(char)
= len(src_vocab)+1
src_vocab_size = len(tar_vocab)+1
tar_vocab_size print('source 문장의 char 집합 :',src_vocab_size)
print('target 문장의 char 집합 :',tar_vocab_size)
source 문장의 char 집합 : 80
target 문장의 char 집합 : 103
sorted 된 상태에서 인덱스 출력해야 함
= sorted(list(src_vocab))
src_vocab = sorted(list(tar_vocab))
tar_vocab print(src_vocab[45:75])
print(tar_vocab[45:75])
['W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
['T', 'U', 'V', 'W', 'X', 'Y', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x']
문장에 인덱스 부여
= dict([(word, i+1) for i, word in enumerate(src_vocab)])
src_to_index = dict([(word, i+1) for i, word in enumerate(tar_vocab)])
tar_to_index print(src_to_index)
print(tar_to_index)
{' ': 1, '!': 2, '"': 3, '$': 4, '%': 5, '&': 6, "'": 7, ',': 8, '-': 9, '.': 10, '/': 11, '0': 12, '1': 13, '2': 14, '3': 15, '4': 16, '5': 17, '6': 18, '7': 19, '8': 20, '9': 21, ':': 22, '?': 23, 'A': 24, 'B': 25, 'C': 26, 'D': 27, 'E': 28, 'F': 29, 'G': 30, 'H': 31, 'I': 32, 'J': 33, 'K': 34, 'L': 35, 'M': 36, 'N': 37, 'O': 38, 'P': 39, 'Q': 40, 'R': 41, 'S': 42, 'T': 43, 'U': 44, 'V': 45, 'W': 46, 'X': 47, 'Y': 48, 'Z': 49, 'a': 50, 'b': 51, 'c': 52, 'd': 53, 'e': 54, 'f': 55, 'g': 56, 'h': 57, 'i': 58, 'j': 59, 'k': 60, 'l': 61, 'm': 62, 'n': 63, 'o': 64, 'p': 65, 'q': 66, 'r': 67, 's': 68, 't': 69, 'u': 70, 'v': 71, 'w': 72, 'x': 73, 'y': 74, 'z': 75, '°': 76, 'é': 77, '’': 78, '€': 79}
{'\t': 1, '\n': 2, ' ': 3, '!': 4, '"': 5, '$': 6, '%': 7, '&': 8, "'": 9, '(': 10, ')': 11, ',': 12, '-': 13, '.': 14, '0': 15, '1': 16, '2': 17, '3': 18, '4': 19, '5': 20, '6': 21, '7': 22, '8': 23, '9': 24, ':': 25, '?': 26, 'A': 27, 'B': 28, 'C': 29, 'D': 30, 'E': 31, 'F': 32, 'G': 33, 'H': 34, 'I': 35, 'J': 36, 'K': 37, 'L': 38, 'M': 39, 'N': 40, 'O': 41, 'P': 42, 'Q': 43, 'R': 44, 'S': 45, 'T': 46, 'U': 47, 'V': 48, 'W': 49, 'X': 50, 'Y': 51, 'a': 52, 'b': 53, 'c': 54, 'd': 55, 'e': 56, 'f': 57, 'g': 58, 'h': 59, 'i': 60, 'j': 61, 'k': 62, 'l': 63, 'm': 64, 'n': 65, 'o': 66, 'p': 67, 'q': 68, 'r': 69, 's': 70, 't': 71, 'u': 72, 'v': 73, 'w': 74, 'x': 75, 'y': 76, 'z': 77, '\xa0': 78, '«': 79, '»': 80, 'À': 81, 'Ç': 82, 'É': 83, 'Ê': 84, 'Ô': 85, 'à': 86, 'â': 87, 'ç': 88, 'è': 89, 'é': 90, 'ê': 91, 'ë': 92, 'î': 93, 'ï': 94, 'ô': 95, 'ù': 96, 'û': 97, 'œ': 98, '\u2009': 99, '‘': 100, '’': 101, '\u202f': 102}
train 데이터의 인코더에 정수 인코딩
= []
encoder_input
# 1개의 문장
for line in lines.src:
= []
encoded_line # 각 줄에서 1개의 char
for char in line:
# 각 char을 정수로 변환
encoded_line.append(src_to_index[char])
encoder_input.append(encoded_line)print('source 문장의 정수 인코딩 :',encoder_input[:5])
source 문장의 정수 인코딩 : [[30, 64, 10], [30, 64, 10], [30, 64, 10], [30, 64, 10], [31, 58, 10]]
디코더에 정수 인코딩
= []
decoder_input for line in lines.tar:
= []
encoded_line for char in line:
encoded_line.append(tar_to_index[char])
decoder_input.append(encoded_line)print('target 문장의 정수 인코딩 :',decoder_input[:5])
target 문장의 정수 인코딩 : [[1, 3, 48, 52, 3, 4, 3, 2], [1, 3, 39, 52, 69, 54, 59, 56, 14, 3, 2], [1, 3, 31, 65, 3, 69, 66, 72, 71, 56, 3, 4, 3, 2], [1, 3, 28, 66, 72, 58, 56, 3, 4, 3, 2], [1, 3, 45, 52, 63, 72, 71, 3, 4, 3, 2]]
디코더를 예측한 후 그 값과 비교하기 위한 실제 값을 위해 위에 추가한 \t
를 없애주는 과정으로서 앞의 1
삭제
= []
decoder_target for line in lines.tar:
= 0
timestep = []
encoded_line for char in line:
if timestep > 0:
encoded_line.append(tar_to_index[char])= timestep + 1
timestep
decoder_target.append(encoded_line)print('target 문장 레이블의 정수 인코딩 :',decoder_target[:5])
target 문장 레이블의 정수 인코딩 : [[3, 48, 52, 3, 4, 3, 2], [3, 39, 52, 69, 54, 59, 56, 14, 3, 2], [3, 31, 65, 3, 69, 66, 72, 71, 56, 3, 4, 3, 2], [3, 28, 66, 72, 58, 56, 3, 4, 3, 2], [3, 45, 52, 63, 72, 71, 3, 4, 3, 2]]
= max([len(line) for line in lines.src])
max_src_len = max([len(line) for line in lines.tar])
max_tar_len print('source 문장의 최대 길이 :',max_src_len)
print('target 문장의 최대 길이 :',max_tar_len)
source 문장의 최대 길이 : 22
target 문장의 최대 길이 : 76
영어 데이터끼리, 프랑스어 데이터끼리 길이를 맞추어 패딩 진행
= pad_sequences(encoder_input, maxlen=max_src_len, padding='post')
encoder_input = pad_sequences(decoder_input, maxlen=max_tar_len, padding='post')
decoder_input = pad_sequences(decoder_target, maxlen=max_tar_len, padding='post') decoder_target
원핫인코딩(문자 단위 번역기라 워드 임베딩은 별도로 사용하지 않음)
= to_categorical(encoder_input)
encoder_input = to_categorical(decoder_input)
decoder_input = to_categorical(decoder_target) decoder_target
Teacher Forcing 교사 강요
- train에서는 이전 시점의 디코더 셀의 출력을 현재 시점의 디코더 셀의 입력으로 넣어주지 않음.
- 이전 시점의 실제값을 현재 시점의 디코더 셀의 입력값으로 하는 방법 사용
- 이전 시점의 디코더 셀의 예측이 틀렸는데 이를 현재 시점의 디코더 셀의 입력으로 사용하면 현재 시점의 디코더 셀의 예측에도 잘못될 가능성이 높음
- 이는 연쇄 작용으로 디코더 전체의 예측을 어렵게 함
- 이런 상황이 반복되면 훈련 시간이 느려질 수 있음
교사 강요
- RNN의 모든 시점에 대해서 이전 시점의 예측값 대신 실제값을 입력으로 주는 방법
seq2seq 기계 번역기 훈련
from tensorflow.keras.layers import Input, LSTM, Embedding, Dense
from tensorflow.keras.models import Model
import numpy as np
encoder_states
가 바로 컨텍스트 벡터
= Input(shape=(None, src_vocab_size))
encoder_inputs = LSTM(units=256, return_state=True)
encoder_lstm
# encoder_outputs은 여기서는 불필요
= encoder_lstm(encoder_inputs)
encoder_outputs, state_h, state_c
# LSTM은 바닐라 RNN과는 달리 상태가 두 개. 은닉 상태와 셀 상태.
= [state_h, state_c] encoder_states
=True return_state
인코더의 내부 상태를 디코더로 넘겨주기 위해, False면 리턴하지 않음
= Input(shape=(None, tar_vocab_size))
decoder_inputs = LSTM(units=256, return_sequences=True, return_state=True)
decoder_lstm
# 디코더에게 인코더의 은닉 상태, 셀 상태를 전달.
= decoder_lstm(decoder_inputs, initial_state=encoder_states)
decoder_outputs, _, _
= Dense(tar_vocab_size, activation='softmax')
decoder_softmax_layer = decoder_softmax_layer(decoder_outputs)
decoder_outputs
= Model([encoder_inputs, decoder_inputs], decoder_outputs)
model compile(optimizer="rmsprop", loss="categorical_crossentropy") model.
디코더는 은닉상태, 셀 상태를 사용하지 않기 때문에 _
로 받아줌
=[encoder_input, decoder_input], y=decoder_target, batch_size=64, epochs=40, validation_split=0.2) model.fit(x
Epoch 1/40
750/750 [==============================] - 100s 131ms/step - loss: 0.8574 - val_loss: 0.7819
Epoch 2/40
750/750 [==============================] - 98s 131ms/step - loss: 0.5758 - val_loss: 0.6667
Epoch 3/40
750/750 [==============================] - 144s 191ms/step - loss: 0.5051 - val_loss: 0.6056
Epoch 4/40
750/750 [==============================] - 171s 229ms/step - loss: 0.4589 - val_loss: 0.5569
Epoch 5/40
750/750 [==============================] - 159s 213ms/step - loss: 0.4232 - val_loss: 0.5200
Epoch 6/40
750/750 [==============================] - 154s 205ms/step - loss: 0.3958 - val_loss: 0.4920
Epoch 7/40
750/750 [==============================] - 109s 145ms/step - loss: 0.3745 - val_loss: 0.4701
Epoch 8/40
750/750 [==============================] - 105s 140ms/step - loss: 0.3572 - val_loss: 0.4552
Epoch 9/40
750/750 [==============================] - 119s 159ms/step - loss: 0.3429 - val_loss: 0.4391
Epoch 10/40
750/750 [==============================] - 163s 218ms/step - loss: 0.3306 - val_loss: 0.4275
Epoch 11/40
750/750 [==============================] - 164s 219ms/step - loss: 0.3201 - val_loss: 0.4184
Epoch 12/40
750/750 [==============================] - 160s 213ms/step - loss: 0.3111 - val_loss: 0.4119
Epoch 13/40
750/750 [==============================] - 133s 177ms/step - loss: 0.3031 - val_loss: 0.4011
Epoch 14/40
750/750 [==============================] - 101s 135ms/step - loss: 0.2959 - val_loss: 0.3956
Epoch 15/40
750/750 [==============================] - 101s 135ms/step - loss: 0.2893 - val_loss: 0.3928
Epoch 16/40
750/750 [==============================] - 141s 188ms/step - loss: 0.2832 - val_loss: 0.3868
Epoch 17/40
750/750 [==============================] - 173s 230ms/step - loss: 0.2778 - val_loss: 0.3798
Epoch 18/40
750/750 [==============================] - 161s 215ms/step - loss: 0.2727 - val_loss: 0.3781
Epoch 19/40
750/750 [==============================] - 152s 202ms/step - loss: 0.2679 - val_loss: 0.3755
Epoch 20/40
750/750 [==============================] - 117s 156ms/step - loss: 0.2635 - val_loss: 0.3704
Epoch 21/40
750/750 [==============================] - 105s 140ms/step - loss: 0.2593 - val_loss: 0.3687
Epoch 22/40
750/750 [==============================] - 123s 164ms/step - loss: 0.2553 - val_loss: 0.3656
Epoch 23/40
750/750 [==============================] - 162s 216ms/step - loss: 0.2517 - val_loss: 0.3626
Epoch 24/40
750/750 [==============================] - 162s 217ms/step - loss: 0.2482 - val_loss: 0.3618
Epoch 25/40
750/750 [==============================] - 157s 209ms/step - loss: 0.2448 - val_loss: 0.3602
Epoch 26/40
750/750 [==============================] - 135s 180ms/step - loss: 0.2414 - val_loss: 0.3577
Epoch 27/40
750/750 [==============================] - 98s 131ms/step - loss: 0.2385 - val_loss: 0.3569
Epoch 28/40
750/750 [==============================] - 98s 131ms/step - loss: 0.2355 - val_loss: 0.3562
Epoch 29/40
750/750 [==============================] - 150s 200ms/step - loss: 0.2325 - val_loss: 0.3536
Epoch 30/40
750/750 [==============================] - 172s 229ms/step - loss: 0.2299 - val_loss: 0.3517
Epoch 31/40
750/750 [==============================] - 161s 214ms/step - loss: 0.2271 - val_loss: 0.3520
Epoch 32/40
750/750 [==============================] - 156s 208ms/step - loss: 0.2246 - val_loss: 0.3518
Epoch 33/40
750/750 [==============================] - 110s 147ms/step - loss: 0.2222 - val_loss: 0.3504
Epoch 34/40
750/750 [==============================] - 107s 143ms/step - loss: 0.2199 - val_loss: 0.3494
Epoch 35/40
750/750 [==============================] - 128s 171ms/step - loss: 0.2177 - val_loss: 0.3502
Epoch 36/40
750/750 [==============================] - 168s 224ms/step - loss: 0.2152 - val_loss: 0.3493
Epoch 37/40
750/750 [==============================] - 166s 221ms/step - loss: 0.2131 - val_loss: 0.3479
Epoch 38/40
750/750 [==============================] - 154s 205ms/step - loss: 0.2111 - val_loss: 0.3492
Epoch 39/40
750/750 [==============================] - 122s 162ms/step - loss: 0.2091 - val_loss: 0.3499
Epoch 40/40
750/750 [==============================] - 98s 131ms/step - loss: 0.2070 - val_loss: 0.3497
<keras.callbacks.History at 0x7fe721864a30>
seq2seq 기계 번역기 동작
STEP 1. 번역하고자 하는 입력 문장이 인코더에 들어가 은닉 상태와 셀 상태를 얻는다.
STEP 2. 상태와 <sos>
에 해당하는 \t
를 디코더로 보낸다.
STEP 3. elzhejrk <eos>
에 해당하는 \n
이 나올 때까지 다음 문자를 예측하는 행동을 반복한다.
= Model(inputs=encoder_inputs, outputs=encoder_states) encoder_model
인코더 정의
# 이전 시점의 상태들을 저장하는 텐서
= Input(shape=(256,))
decoder_state_input_h = Input(shape=(256,))
decoder_state_input_c = [decoder_state_input_h, decoder_state_input_c]
decoder_states_inputs
# 문장의 다음 단어를 예측하기 위해서 초기 상태(initial_state)를 이전 시점의 상태로 사용.
# 뒤의 함수 decode_sequence()에 동작을 구현 예정
= decoder_lstm(decoder_inputs, initial_state=decoder_states_inputs)
decoder_outputs, state_h, state_c
# 훈련 과정에서와 달리 LSTM의 리턴하는 은닉 상태와 셀 상태를 버리지 않음.
= [state_h, state_c]
decoder_states = decoder_softmax_layer(decoder_outputs)
decoder_outputs = Model(inputs=[decoder_inputs] + decoder_states_inputs, outputs=[decoder_outputs] + decoder_states) decoder_model
decoder_inputs
<KerasTensor: shape=(None, None, 103) dtype=float32 (created by layer 'input_2')>
decoder_states_inputs
[<KerasTensor: shape=(None, 256) dtype=float32 (created by layer 'input_5')>,
<KerasTensor: shape=(None, 256) dtype=float32 (created by layer 'input_6')>]
디코더 설계
= dict((i, char) for char, i in src_to_index.items())
index_to_src = dict((i, char) for char, i in tar_to_index.items()) index_to_tar
인덱스에서 단어를 얻을 수 있도록 index_to_~
설계
def decode_sequence(input_seq):
# 입력으로부터 인코더의 상태를 얻음
= encoder_model.predict(input_seq)
states_value
# <SOS>에 해당하는 원-핫 벡터 생성
= np.zeros((1, 1, tar_vocab_size))
target_seq 0, 0, tar_to_index['\t']] = 1.
target_seq[
= False
stop_condition = ""
decoded_sentence
# stop_condition이 True가 될 때까지 루프 반복
while not stop_condition:
# 이점 시점의 상태 states_value를 현 시점의 초기 상태로 사용
= decoder_model.predict([target_seq] + states_value)
output_tokens, h, c
# 예측 결과를 문자로 변환
= np.argmax(output_tokens[0, -1, :])
sampled_token_index = index_to_tar[sampled_token_index]
sampled_char
# 현재 시점의 예측 문자를 예측 문장에 추가
+= sampled_char
decoded_sentence
# <eos>에 도달하거나 최대 길이를 넘으면 중단.
if (sampled_char == '\n' or
len(decoded_sentence) > max_tar_len):
= True
stop_condition
# 현재 시점의 예측 결과를 다음 시점의 입력으로 사용하기 위해 저장
= np.zeros((1, 1, tar_vocab_size))
target_seq 0, 0, sampled_token_index] = 1.
target_seq[
# 현재 시점의 상태를 다음 시점의 상태로 사용하기 위해 저장
= [h, c]
states_value
return decoded_sentence
step by step
= encoder_input[5:10] input_seq
= encoder_model.predict(input_seq) states_value
1/1 [==============================] - 0s 31ms/step
# <SOS>에 해당하는 원-핫 벡터 생성
= np.zeros((1, 1, tar_vocab_size)) target_seq
target_seq
array([[[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0.]]])
0, 0, tar_to_index['\t']] = 1. target_seq[
target_seq
array([[[0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0.]]])
= False stop_condition
= "" decoded_sentence
# Check dimensions of input arrays
print(target_seq.shape) # Shape of target_seq
print(states_value[0].shape) # Shape of states_value[0]
print(states_value[1].shape) # Shape of states_value[1]
print(states_value[0][1,:].reshape(1,-1).shape)
print(states_value[1][1,:].reshape(1,-1).shape)
(1, 1, 103)
(5, 256)
(5, 256)
(1, 256)
(1, 256)
= [states_value[0][1,:].reshape(1,-1), states_value[1][1,:].reshape(1,-1)] states_value_tmp
# stop_condition이 True가 될 때까지 루프 반복
while not stop_condition:
# 이점 시점의 상태 states_value를 현 시점의 초기 상태로 사용
= decoder_model.predict([target_seq] + states_value_tmp) output_tokens, h, c
= decoder_model.predict([target_seq] + states_value_tmp) output_tokens, h, c
# 예측 결과를 문자로 변환
= np.argmax(output_tokens[0, -1, :])
sampled_token_index sampled_token_index
= index_to_tar[sampled_token_index]
sampled_char sampled_char
# 현재 시점의 예측 문자를 예측 문장에 추가
+= sampled_char decoded_sentence
# <eos>에 도달하거나 최대 길이를 넘으면 중단.
if (sampled_char == '\n' or
len(decoded_sentence) > max_tar_len):
= True stop_condition
# 현재 시점의 예측 결과를 다음 시점의 입력으로 사용하기 위해 저장
= np.zeros((1, 1, tar_vocab_size))
target_seq target_seq
0, 0, sampled_token_index] = 1. target_seq[
# 현재 시점의 상태를 다음 시점의 상태로 사용하기 위해 저장
= [h, c] states_value
print('입력 문장:', lines.src[5])
print('정답 문장:', lines.tar[5][2:len(lines.tar[5])-1]) # '\t'와 '\n'을 빼고 출력
print('번역 문장:', decoded_sentence[1:len(decoded_sentence)-1]) # '\n'을 빼고 출력
for seq_index in [3,4]: # 입력 문장의 인덱스
= encoder_input[seq_index:seq_index+1]
input_seq = decode_sequence(input_seq)
decoded_sentence # print(35 * "-")
print('입력 문장:', lines.src[seq_index])
print('정답 문장:', lines.tar[seq_index][2:len(lines.tar[seq_index])-1]) # '\t'와 '\n'을 빼고 출력
print('번역 문장:', decoded_sentence[1:len(decoded_sentence)-1]) # '\n'을 빼고 출력
Footnotes
컨텍스트 벡터는 보통 수백차원 이상↩︎