--40.Seq2Seq.ipynb--
Sequeuce-to-Sequence(Seq2Seq) 는 입력된 시퀀스로부터 다른 도메인의 시퀀스를 출력하는 모델
Seq2Seq 는 새로운 모델이 아니다. 기존의 순환신경망 2개를 조합해서 만든다.
Seq2Seq 는 훈련과정과 예측과정의 작동방식이 다릅니다.
훈련과정에선
예측과정에선!
기계번역기를 훈련시키기 위해서는 훈련데이터를 병렬 코퍼스 (parrel corpus) 형태로 구성된 데이터가 필요하다.
예를 들면
| src | tar |
|---|---|
| Watch me | Regradez-moi |
| Go. | Marche. |
import numpy as np
import pandas as pd
import tensorflow as tf
import os
import matplotlib.pyplot as plt
import shutil
import requests
import zipfile
import pandas as pd
import requests
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
def download_zip(url, output_path):
response = requests.get(url, headers=headers, stream=True)
if response.status_code == 200:
with open(output_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
print(f'ZIP 파일 다운로드 완료 -> {output_path}')
else:
print(f'다운로드 실패 -> {response.status_code}')
url = "http://www.manythings.org/anki/fra-eng.zip"
output_path = "fra-eng.zip"
!ls -lh *.zip
path = os.getcwd()
zipfilename = os.path.join(path, output_path)
with zipfile.ZipFile(zipfilename, 'r') as zip_ref:
zip_ref.extractall(path)
!ls -lh *.txt
lines = pd.read_csv('fra.txt', names=['src', 'tar', 'lic'], sep='\t')
lines.head()
lines.sample(10)
lines.shape
del lines['lic']
lines.head()
lines = lines[:60000]
lines.sample(10)
"""
번역 문장에 해당되는 프랑스어 데이터는 앞서 배웠듯이 시작을 의미하는 심볼 과
종료를 의미하는 심볼 을 넣어주어야 합니다.
여기서는 와 대신 \t를 시작 심볼, \n을 종료 심볼로 간주하여 추가하고
다시 데이터를 출력해보겠습니다.
"""
None
lines.tar = lines.tar.apply(lambda x : '\t' + x + '\n')
lines.sample(10)
src_vocab = set()
for line in lines.src:
for char in line:
src_vocab.add(char)
tar_vocab = set()
for line in lines.tar:
for char in line:
tar_vocab.add(char)
print(src_vocab)
print(tar_vocab)
src_vocab_size = len(src_vocab) + 1
tar_vocab_size = len(tar_vocab) + 1
print(src_vocab_size, tar_vocab_size)
src_vocab = sorted(list(src_vocab))
tar_vocab = sorted(list(tar_vocab))
print(src_vocab[45:75])
print(tar_vocab[45:75])
src_to_index = dict([(word, i+1) for i, word in enumerate(src_vocab)])
tar_to_index = dict([(word, i+1) for i, word in enumerate(tar_vocab)])
print(src_to_index)
print(tar_to_index)
encoder_input = []
for line in lines.src:
encoder_input.append([src_to_index[char] for char in line])
print(lines.src.values[48:51])
print(encoder_input[48:51])
decoder_input = []
for line in lines.tar:
decoder_input.append([tar_to_index[char] for char in line])
print(lines.tar.values[48:51])
print(decoder_input[48:51])
decoder_target = []
for line in lines.tar:
timestep = 0
encoded_line = []
for char in line:
if timestep > 0: # 시작토큰 sos '\t' 은 제외
encoded_line.append(tar_to_index[char])
timestep += 1
decoder_target.append(encoded_line)
print(decoder_input[:5])
print(decoder_target[:5])
from tensorflow.keras.preprocessing.sequence import pad_sequences
max_src_len = max([len(line) for line in lines.src])
max_tar_len = max([len(line) for line in lines.tar])
print(max_src_len, max_tar_len)
print(encoder_input[0])
print(decoder_input[0])
print(decoder_target[0])
encoder_input = pad_sequences(encoder_input, maxlen=max_src_len, padding='post')
decoder_input = pad_sequences(decoder_input, maxlen=max_tar_len, padding='post')
decoder_target = pad_sequences(decoder_target, maxlen=max_tar_len, padding='post')
print(encoder_input.shape)
print(decoder_input.shape)
print(decoder_target.shape)
print(lines.src[0])
print(encoder_input[0])
from tensorflow.keras.utils import to_categorical
encoder_input = to_categorical(encoder_input)
decoder_input = to_categorical(decoder_input)
decoder_target = to_categorical(decoder_target)
print(src_vocab_size, tar_vocab_size)
print(encoder_input.shape)
print(decoder_input.shape)
print(decoder_target.shape)
from keras.layers import Input, LSTM, Embedding, Dense
from keras.models import Model
encoder_inputs = Input(shape=(None, src_vocab_size)) # 글자사전의 크기만큼의 원핫 벡터가 입력된다
encoder_lstm = LSTM(units=256, return_state=True)
encoder_outputs, state_h, state_c = encoder_lstm(encoder_inputs)
encoder_states = [state_h, state_c]
디코더의 처음 입력은 <sos> 토큰. (start of sequence)
이 토큰 다음에 등장할 단어 예측
예측한 결과를 다음 타임스텍의 디코더 입력으로 사용
위 과정을 반복한다. <eos> 토큰이 예측될때까지!
결국decoder는 => encoder 에서 받은 context vector 를 활용해 sequence 를 만들어 냄
decoder_inputs = Input(shape = (None, tar_vocab_size))
decoder_lstm = LSTM(units=256, return_state=True, return_sequences=True)
decoderoutputs, , _ = decoder_lstm(decoder_inputs, initial_state=encoder_states)
decoder_softmax_layer = Dense(tar_vocab_size, activation='softmax')
decoder_outputs = decoder_softmax_layer(decoder_outputs)
from keras.models import Model
model = Model([encoder_inputs, decoder_inputs], decoder_outputs)
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['acc'])
base_path = r'/content/drive/MyDrive/dataset'
model.load_weights(os.path.join(base_path, 'eng2fra_ch.keras'))
encoder_model = Model(inputs=encoder_inputs, outputs=encoder_states)
decoder_state_input_h = Input(shape=(256,))
decoder_state_input_c = Input(shape=(256,))
decoder_states_inputs = [decoder_state_input_h, decoder_state_input_c]
decoder_outputs, state_h, state_c = decoder_lstm(decoder_inputs, initial_state=decoder_states_inputs)
decoder_states=[state_h, state_c]
decoder_outputs = decoder_softmax_layer(decoder_outputs)
decoder_model = Model(inputs = [decoder_inputs] + decoder_states_inputs, outputs = [decoder_outputs] + decoder_states)
index_to_src = dict((i, char) for char, i in src_to_index.items() )
index_to_tar = dict((i, char) for char, i in tar_to_index.items() )
def decode_sequence(input_seq):
states_value = encoder_model.predict(input_seq)
target_seq = np.zeros((1, 1, tar_vocab_size))
target_seq[0, 0, tar_to_index['\t']] = 1
stop_condition = False
decoded_sentence = ""
while not stop_condition:
# 이전 시점의 상태 states_value 를 현 시점의 초기 상태로 사용
output_tokens, h, c = decoder_model.predict([target_seq] + states_value)
# 예측 결과를 문자로 변환
sampled_token_index = np.argmax(output_tokens[0, -1, :])
sampled_char = index_to_tar[sampled_token_index]
# 현재 시점의 예측 문자를 예측 문장에 추가
decoded_sentence += sampled_char
# <eos> 에 도달하거나, 최개 길이를 넘으면 중단
if(sampled_char == '\n' or
len(decoded_sentence) > max_tar_len):
stop_condition = True
# 현재 타임스텝의 예측 결과를 다음 시점의 입력으로 사용하기 위해 저장
target_seq = np.zeros((1, 1, tar_vocab_size))
target_seq[0, 0, sampled_token_index] = 1
# 현재 시점의 상태를 다음 시점의 상태로 사용하기 위해 저장
states_value = [h, c]
return decoded_sentence
input_idx = [3, 50, 100, 300, 1001]
for idx in input_idx:
print(lines.src[idx])
for seq_index in [3, 5, 100, 300, 10001]:
input_seq = encoder_input[seq_index:seq_index+1]
decoded_sentence = decode_sequence(input_seq)
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' 를 빼고 출력