You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
RNN은 '우리 연애 그만하고, 결혼하자'을 바르게 해석하지 못한다. 문장을 다 듣기도 전에 번역을 시작하기 시작하기 때문이다.
Seq2Seq은 '끝까지 듣고 말하기' 부분이 보안된 모델이다.
2. Seq2Seq : Encoder - Decoder
Seq2Seq의 대표적인 특징은 Encoder - Decoder 구조이다.
(1) Encoder : input의 모든 문장을 듣는 과정
Encoder는 input의 모든 문장을 듣는 과정이다.
모든 문장을 들은 후 입력된 시퀀스(문장)를 벡터의 형태로 압축한다.
압축된 형태를 Decoder에 전달한다.
(2) Decoder : output으로 답변 문장 만드는 과정
Encoder에서 전달받은 벡터를 Decoder의 첫 셀의 hidden state에 넣어주고, 문장이 시작한다는 플래그와 함께 모델을 시작한다.
첫 셀의 output을 문장의 첫 시작 단어로 만들고, 이 단어를 다시 두 번째 셀에 넣어 이전 셀의 hidden state와 함께 다음 단어를 예측한다.
이런 방식으로 완성된 문장을 만들어 낸다.
3. Seq2Seq Encoder - Decoder 간략하게 알아보기
번역 테스크를 수행하는 Seq2Seq 모델의 Encoder - Decoder를 살펴보자.
raw : 전체 데이터 셋
Source Text : 전체 데이터 셋 중 영어 문장
Target Text : 전체 데이터 셋 중 번역 한 한국어 문장
preprocess : 전체 데이터 셋을 Train과 Test로 나누고, 각각의 단어와 단어의 빈도를 체크한다.
SOURCE_MAX_LENGTH : Source Text의 최대 길이 제한
TARGET_MAX_LENGTH : Target Text의 최대 길이 제한
# 전처리SOURCE_MAX_LENGTH=10TARGET_MAX_LENGTH=12load_pairs, load_source_vocab, load_target_vocab=preprocess(raw, SOURCE_MAX_LENGTH, TARGET_MAX_LENGTH)
print(random.choice(load_pairs))
# Hidden state 정의enc_hidden_size=16# Encoder의 Hidden state 정의dec_hidden_size=enc_hidden_size# Decoder의 Hidden state 정의 (Encoder와 같다)# Encoder - Decoderenc=Encoder(load_source_vocab.n_vocab, enc_hidden_size).to(device) # RNN의 레이어인 Encoder 선언dec=Decoder(dec_hidden_size, load_traget_vocab.n_vocab).to(device) # RNN의 레이어인 Decoder 선언# 학습train(load_pairs, load_source_vocab, load_target_vocab, enc, dec, 5000, print_every=1000) # Encoder의 출력을 Decoder의 입력으로 연결하는 부분 존재# 모델 평가evaluate(load_pairs, load_source_vocab, load_target_vocab, enc, dec, TARGET_MAX_LENGTH)
4. Seq2Seq 전 과정 실습하기
importrandomimporttorchimporttorch.nnasnnimporttorch.optimasoptimtorch.manual_seed(0)
device=torch.device("cuda"iftorch.cuda.is_available() else"cpu")
# 전체 데이터셋raw= ["I feel hungry. 나는 배가 고프다.",
"Pytorch is very easy. 파이토치는 매우 쉽다.",
"Pytorch is a framework for depp learning. 파이토치는 딥러닝을 위한 프레임워크이다.",
"Pytorch is very clear to use. 파이토치는 사용하기 매우 직관적이다."]
# 문장을 시작할 때 EOS_token을 붙여서 문장의 시작을 알려 준다.# 문장이 끝날 때 EOS_token을 붙여서 문장의 종료를 알려 준다.SOS_token=0# Start of sentencesEOS_token=1# End of sentences# preprocess : 전체 데이터 셋을 Train과 Test로 나누고, 그 안의 단어와 단어의 빈도를 체크한다.defpreprocess(corpus, source_max_length, target_max_length):
print("reading corpus...")
paris= []
forlineincorpus:
pairs.append([sforsinline.strip().lower().split("\t")]) # tab으로 split 되어 있는 부분을 나눈다.print("Read {} sentence pairs".format(len(pairs)))
pairs= [pairforpairinpairsiffilter_pair(pair, source_max_length, target_max_length)]
print("Trimmed to {} sentence pairs".format(len(pairs)))
source_vocab=Vocab() # Vocab()으로 단어의 개수, 딕셔너리를 만든다.target_vocab=Vocab()
print("Counting words...")
forpairinpairs:
source_vocab.add_vocab(pair[0])
target_vocab.add_vocab(pair[1])
print("source vocab size =", source_vocab.n_vocab)
print("target vocab size =", target_vocab.n_vocab)
returnpairs, source_vocab, target_vocab# Encoder 모델 만들어 놓기''' input의 문장이 100개의 단어로 구성되어 있다면, 100개의 차원으로 구성된 one-hot encoding으로 변환할 수 있다. 즉, 100개의 단어 중 Pytorch라는 단어는 [0 0 0 0 1 0 ... 0 0 0] 과 같이 표현할 수 있다. 이걸 embedding의 input으로 넣으면 이 차원은 hidden_size 차원 만큼의 벡터로 줄어든다.'''classEncoder(nn.Module):
def__init__(self, input_size, hidden_size):
super(Encoder, self).__init__()
self.hidden_size=hidden_sizeself.embedding=nn.Embedding(input_size, hidden_size) # Embeddingself.gru=nn.GRU(hidden_size, hidden_size) # 파라미터로 input과 output의 차원을 정의한다.defforward(self, x, hidden):
x=self.embedding(x).view(1, 1, -1) # embeddingx, hidden=self.gru(x, hidden) # grureturnx, hidden# Decoder 모델 만들어 놓기''' Encoder의 최종 아웃풋이 Decoder의 셀로 들어온다. One-hot 벡터를 Embedding으로 처리한 값이 셀로 들어가서 아웃풋이 나온다. 이 아웃풋을 Target text의 단어로 복원해야 하는데, 이때 out_size를 Target Text의 차원과 같은 벡터로 바꾸기 위해 Linear를 이용한다.'''classDecoder(nn.Module):
def__init__(self, hidden_size, output_size):
super(Decoder, self).__init__()
self.hidden_size=hidden_sizeself.embedding=nn.Embedding(output_size, hidden_size)
self.gru=nn.GRU(hidden_size, hidden_size)
self.out=nn.Linear(hidden_size, output_size) # Linearself.softmax=nn.LogSoftmax(dim=1)
defforward(self, x, hidden):
x=self.embedding(x).view(1, 1, -1) # embeddingx, hidden=self.gru(x, hidden) # grux=self.softmax(self.out(x[0])) # softmaxreturnx, hidden# sentence를 one-hot encoding으로 바꾸고, 텐서화 한다.deftensorize(vocab, sentence):
indexes= [vocab.vocab2index[word] forwordinsentence.split(" ")]
indexes.append(vocab.vocab2index["<EOS>"])
returntorch.Tensor(indexes).long().to(device).view(-1, 1)
deftrain(pairs, source_vocab, target_vocab, encoder, decoder, n_iter, print_every=1000, learning_rate=0.01):
loss_total=0# optimizerencoder_optimizer=optim.SGD(encoder.parameters(), lr=learning_rate)
decoder_optimizer=optim.SGD(decoder.parameters(), lr=learning_rate)
training_batch= [random.choice(pairs) for_inrange(n_iter)] # 전체 데이터 셋 중 랜덤하게 학습 데이터 추출training_source= [tensorize(source_vocab, pair[0]) forpairintraining_batch] # 배치를 source로 나눔training_target= [tensorize(target_vocab, pair[1]) forpairintraining_batch] # 배치를 target으로 나눔# Losscriterion=nn.NLLLoss()
''' 첫 번째 스텝에서의 input은 문장의 첫 단어이다. 이 단어가 embedding을 거쳐 들어간다. Encoder의 첫 GRU에 들어가는 hidden state가 없는데, Encoder_hidden으로 0 벡터를 넣어 만들어 준다. for 문이 다 끝날 때 까지 반복문을 돌며 encoder에서 hidden state를 꺼내 온다. encoder의 마지막 hidden state를 decoder의 첫 hidden state로 넣어 준다. decoder의 첫 입력 값으로 SOS_token이 들어간다. 그 출력 값이 다음 GRU의 입력값으로 들어갈 수도 있고, 아니면 정답 label로 변환한 값이 다음 GRU의 입력값으로 들어갈 수도 있다. (teacher forcing)'''foriinrange(1, n_iter+1):
source_tensor=training_source[i-1]
target_tensor=training_target[i-1]
encoder_hidden=torch.zeros([1, 1, encoder.hidden_size]).to(device)
encoder_optimizer.zero_grad()
decoder_optimizer.zero_grad()
source_length=source_tensor.size(0)
target_length=target_tensor.size(0)
loss=0forenc_inputinrange(source_length):
_, encoder_hidden=encoder(source_tensor[enc_input], encoder_hidden)
decoder_input=torch.Tensor([[SOS_token]]).long().to(device)
decoder_hidden=encoder_hidden# encoder의 마지막 hidden state를 decoder의 첫 hidden state로 넣어 준다. fordiinrange(target_length):
decoder_output, decoder_hidden=decoder(decoder_input, decoder_hidden)
loss+=criterion(decoder_output, target_tensor[di])
decoder_input=target_tensor[di] # teacher forcingloss.backward()
# 최적화encoder_optimizer.step()
decoder_optimizer.step()
loss_iter=loss.item() /target_lengthloss_total+=loss_iterifi%print_every==0:
loss_avg=loss_total/print_everyloss_total=0print("[{} - {}%] loss = {05.4f}".format(i, i/n_iter*100, loss_avg))