본문 바로가기
AI/자연어처리

12. 문장 임베딩 | Seq2Seq

by 사라리24 2024. 7. 3.



1. 문장 임베딩

- 2017년 이전의 임베딩 기법들은 대부분 단어 수준의 모델 (Word2Vec, FastText, GloVe)
- 단어 수준의 임베딩 기법은 자연어의 특성인 모호성, 동음이의어를 구분하기 어렵다는 한계가 있음
- 2017년 이후에는 ELMo(Embeddigs from Language Models)와 같은 모델이 발표되고 트랜스포머와 같은 언어 모델에서 문장 수준의 언어 모델링을 고려하면서 한계점들이 해결됨

 

 

 

2. Seq2Seq (Sequence To Sequence)

  • 2014년 구글에서 논문으로 제안한 모델
  • LSTM(Long Short-Term Memory) 또는 GRU(Gated Recurrent Unit) 기반의 구조를 가지고 고정된 길이의 단어 시퀀스를 입력으로 받아, 입력 시퀀스에 알맞은 길이의 시퀀스를 출력해주는 언어 모델
  • 2개의 LSTM을 각각 Encoder와 Decoder로 사용해 가변적인 길이의 입/출력을 처리하고자 했음
  • 기계 번역 작업에서 큰 성능 향상을 가져왔고 특히 긴 문장을 처리하는데 강점이 있음
  • 논문: https://arxiv.org/abs/1409.3215
 

Sequence to Sequence Learning with Neural Networks

Deep Neural Networks (DNNs) are powerful models that have achieved excellent performance on difficult learning tasks. Although DNNs work well whenever large labeled training sets are available, they cannot be used to map sequences to sequences. In this pap

arxiv.org

 

 

1. 등장배경

  • Seq2Seq 모델이 등장하기 전에 DNN(Deep Neural Network) 모델은
    사물 인식, 음성 인식 등에서 꾸준히 성과를 냈음(예: CNN, RNN, LSTM, GRU...)
  • 모델 입/출력의 크기가 고정된다는 한계점이 존재했기 때문에
    자연어처리와 같은 가변적인 길이의 입/출력을 처리하는 문제들을 제대로 해결할 수 없었음
  • RNN은 Seq2Seq가 등장하기 전에 입.출력을 시퀀스 단위로 처리할 수 있는 모델이었음 

2. 인코더

  • 입력 문장을 컨텍스트 벡터에 인코딩(압축)하는 역할을 함
  • 인코더의 LSTM은 입력 문장을 단어 순서대로 처리하여 구정된 크기의 컨텍스트 벡터를 반환
  • 컨텍스트 벡터는 인코더의 마지막 스텝에서 출력된 hidden state와 같음
  • 컨텍스트 벡터는 입력 문장의 정보를 합축하는 벡터이므로, 해당 벡터를 입력 문장에 대한 문장 수준의 벡터로 활용할 수 있음

3. 디코더

  • 입력 문장의 정보가 압축되 컨텍스트 벡터를 사용하여 출력 문장을 디코딩하는 역할
  • 인코더로부터 전달받은 컨텍스트 벡터의 문장을 시작을 뜻하는 코튼을 입력으로 받아서,
    문장의 끝을 뜻하는 토큰이 나올때까지 문장을 생성
  •  LSTM의 첫 셀에서는 토큰과 컨텍스트 벡터를 입력받아서 그 다음에 등장할 확률이 가장 높은 단어를 예측하고,
    다음 스텝에서 예측한 단어를 입력으로 받아서, 그 다음에 등장할 확률이 가장 높은 단어를 예측함
  • 위 과정을 재귀적으로 반복하다가 다음에 등장할 확률이 가장 높은 단어로 토큰이 나오면 생성을 종료

4.  학습방법

  • 모델을 학습할 때 Teacher Forcing이라는 기법을 사용
  • 모델 학습 과정에서는 이전 셀에서 예측한 단어를 다음 셀의 입력으로 넣어주는 대신 실제 정답 단어를 다음 셀의 입력으로 넣음
  • 만약 위 방법으로 학습하지 않으면 이전 셀에서의 오류가 다음 셀로 계속 전파될 것이고, 그러면 학습이 제대로 되지 않음

5.  한계점

  • 가변적인 길이를 입/출력을 처리하는데 효과적인 모델 구조이며, 실제로 기계 번역 작업에서 성능 향상을 거뒀으나 여전히 한계를 가짐
  • 인코더가 출력하는 벡터 사이즈가 고정되어 있기 때문에, 입력으로 들어오는 단어의 수가 매우 많아지면 성능이 떨어짐
  • RNN 구조의 모델에서는 hidden state를 통해 이전 셀의 정보를 다음 셀로 계속 전달하는데 문장의 길이가 길어지면 초기 셀에서 전달했던 정보들이 점차 흐려짐
  • LSTM,GRU 같은 모델들이 제안되긴 했으나 여전히 이전 정보를 계속 압축하는데 한계는 있음

 



1. Seq2Seq 데이터 전처리

 

 

  import


       
          import os
          import re
          import shutil
          import zipfile

          import numpy as np
          import pandas as pd
          import tensorflow as tf
          import unicodedata
          import urllib3
          from tensorflow.keras.layers import Embedding, GRU, Dense
          from tensorflow.keras.preprocessing.sequence import pad_sequences
          from tensorflow.keras.preprocessing.text import Tokenizer


 
 

 

 

 fra-eng.zip  다운로드하고 압축해제

: fra-eng.zip 프랑스어-영어 병렬 코퍼스인  파일
19만개의 병렬 문장 샘플  / 예) Watch me. Regardez-moi !)


       
        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(urloutput_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 file downloaded to {output_path}")
            else:
                print(f"Failed to download. HTTP Response Code: {response.status_code}")

        output_path = "fra-eng.zip"
        download_zip(url, output_path)

        path = os.getcwd()
        zipfilename = os.path.join(path, output_path)

        with zipfile.ZipFile(zipfilename, 'r') as zip_ref:
            zip_ref.extractall(path)


 

 

 약 19만개의 데이터 중 33,000개의 샘플만을 사용


       
        num_samples = 33000


 

 

 

 전처리 함수 :  구두점 제거 / 단어 구분


       
        def to_ascii(s):
          # 프랑스어 악센트(accent) 삭제
          # 예시 : 'déjà diné' -> deja dine
          return ''.join(for c in unicodedata.normalize('NFD', s)
                          if unicodedata.category(c) != 'Mn')

        def preprocess_sentence(sent):
          # 악센트 제거 함수 호출
          sent = to_ascii(sent.lower())

          # 단어와 구두점 사이에 공백 추가.
          # ex) "I am a student." => "I am a student ."
          sent = re.sub(r"([?.!,¿])", r" \1", sent)

          # (a-z, A-Z, ".", "?", "!", ",") 이들을 제외하고는 전부 공백으로 변환.
          sent = re.sub(r"[^a-zA-Z!.?]+", r" ", sent)

          # 다수 개의 공백을 하나의 공백으로 치환
          sent = re.sub(r"\s+", " ", sent)
          return sent

 
 

 

 

  임의의 문장 입력테스트


       
        # 전처리 테스트
        en_sent = u"Have you had dinner?"
        fr_sent = u"Avez-vous déjà diné?"

        print('전처리 전 영어 문장 :', en_sent)
        print('전처리 후 영어 문장 :',preprocess_sentence(en_sent))
        print('전처리 전 프랑스어 문장 :', fr_sent)
        print('전처리 후 프랑스어 문장 :', preprocess_sentence(fr_sent))

전처리 전 영어 문장 : Have you had dinner?
전처리 후 영어 문장 : have you had dinner ?
전처리 전 프랑스어 문장 : Avez-vous déjà diné?
전처리 후 프랑스어 문장 : avez vous deja dine ?

 

 

◼ 전체 데이터에서 33,000개의 샘플에 대해서 전처리를 수행
훈련 과정에서 교사 강요(Teacher Forcing)을 사용
>> 훈련 시 사용할 디코더의 입력 시퀀스와 실제값. 즉, 레이블에 해당되는 출력 시퀀스를 따로 분리하여 저장
>>입력 시퀀스에는 시작을 의미하는 토큰인 <sos>를 추가,
      출력 시퀀스에는 종료를 의미하는 토큰인 <eos>를 추가


       
          def load_preprocessed_data():
            encoder_input, decoder_input, decoder_target = [], [], []

            with open("fra.txt", "r") as lines:
              for i, line in enumerate(lines):
                # source 데이터와 target 데이터 분리
                src_line, tar_line, _ = line.strip().split('\t')

                # source 데이터 전처리
                src_line = [for w in preprocess_sentence(src_line).split()]

                # target 데이터 전처리
                tar_line = preprocess_sentence(tar_line)
                tar_line_in = [for w in ("<sos> " + tar_line).split()]
                tar_line_out = [for w in (tar_line + " <eos>").split()]

                encoder_input.append(src_line)
                decoder_input.append(tar_line_in)
                decoder_target.append(tar_line_out)

                if i == num_samples - 1:
                  break

            return encoder_input, decoder_input, decoder_target


 
 

 

 

  위에서 받은 3개의 데이터셋
     인코더의 입력, 디코더의 입력, 디코더의 레이블을 상위 5개 샘플만 출력


       
        sents_en_in, sents_fra_in, sents_fra_out = load_preprocessed_data()
        print('인코더의 입력 :',sents_en_in[:5])
        print('디코더의 입력 :',sents_fra_in[:5])
        print('디코더의 레이블 :',sents_fra_out[:5])


인코더의 입력 : [['go''.'], ['go''.'], ['go''.'], ['hi''.'], ['hi''.']]
디코더의 입력 : [[
'<sos>''va''!'], ['<sos>''marche''.'], ['<sos>''bouge''!'], ['<sos>''salut''!'], ['<sos>''salut''.']]
디코더의 레이블 : [[
'va''!''<eos>'], ['marche''.''<eos>'], ['bouge''!''<eos>'], ['salut''!''<eos>'], ['salut''.''<eos>']]

 

디코더 셀의 입력은 오직 이전 디코더 셀의 출력을 입력으로 받는다고 설명하였는데
디코더의 입력에 해당하는 데이터인 sents_fra_in이 왜 필요할까요?

훈련 과정에서는 이전 시점의 디코더 셀의 출력을 현재 시점의 디코더 셀의 입력으로 넣어주지 않고,
이전 시점의 실제값을 현재 시점의 디코더 셀의 입력값으로 하는 방법을 사용할 겁니다.
그 이유는 이전 시점의 디코더 셀의 예측이 틀렸는데 이를 현재 시점의 디코더 셀의 입력으로 사용하면
현재 시점의 디코더 셀의 예측도 잘못될 가능성이 높고
이는 연쇄 작용으로 디코더 전체의 예측을 어렵게 합니다.
이런 상황이 반복되면 훈련 시간이 느려집니다.

만약 이 상황을 원하지 않는다면
이전 시점의 디코더 셀의 예측값 대신 실제값을 현재 시점의 디코더 셀의 입력으로 사용하는 방법을 사용할 수 있습니다.
이와 같이 RNN의 모든 시점에 대해서 이전 시점의 예측값 대신 실제값을 입력으로 주는 방법을 교사 강요라고 합니다.

 

 케라스 토크나이저를 통해 단어 집합을 생성,
정수 인코딩을 진행 후 이어서 패딩을 진행


     
        tokenizer_en = Tokenizer(filters="", lower=False)
        tokenizer_en.fit_on_texts(sents_en_in)
        encoder_input = tokenizer_en.texts_to_sequences(sents_en_in)
        encoder_input = pad_sequences(encoder_input, padding="post")

        tokenizer_fra = Tokenizer(filters="", lower=False)
        tokenizer_fra.fit_on_texts(sents_fra_in)
        tokenizer_fra.fit_on_texts(sents_fra_out)

        decoder_input = tokenizer_fra.texts_to_sequences(sents_fra_in)
        decoder_input = pad_sequences(decoder_input, padding="post")

        decoder_target = tokenizer_fra.texts_to_sequences(sents_fra_out)
        decoder_target = pad_sequences(decoder_target, padding="post")

 
 

 

 

 데이터의 크기(shape)를 확인


     
          print('인코더의 입력의 크기(shape) :',encoder_input.shape)
          print('디코더의 입력의 크기(shape) :',decoder_input.shape)
          print('디코더의 레이블의 크기(shape) :',decoder_target.shape)

 
인코더의 입력의 크기(shape) : (330008)
디코더의 입력의 크기(shape) : (
3300016)
디코더의 레이블의 크기(shape) : (
3300016)
샘플은 총 33,000개 존재하며 영어 문장의 길이는 8, 프랑스어 문장의 길이는 16

 

 

 

 단어 집합의 크기를 정의


       
          src_vocab_size = len(tokenizer_en.word_index) + 1
          tar_vocab_size = len(tokenizer_fra.word_index) + 1
          print("영어 단어 집합의 크기 : {:d}, 프랑스어 단어 집합의 크기 : {:d}".format(src_vocab_size, tar_vocab_size))

 
영어 단어 집합의 크기 : 4647, 프랑스어 단어 집합의 크기 : 8022
단어 집합의 크기는 각각 4,647개와 8,022개
단어로부터 정수를 얻는 딕셔너리와 정수로부터 단어를 얻는 딕셔너리를 각각 만들어줍니다.
이들은 훈련을 마치고 예측값과 실제값을 비교하는 단계에서 사용

 

 

 단어로부터 정수를 얻는 딕셔너리와 정수로부터 단어를 얻는 딕셔너리를 각각 만들어주기
  : 훈련을 마치고 예측값과 실제값을 비교하는 단계에서 사용


       

        src_to_index = tokenizer_en.word_index
        index_to_src = tokenizer_en.index_word
        tar_to_index = tokenizer_fra.word_index
        index_to_tar = tokenizer_fra.index_word


 

 

 

 테스트 데이터를 분리하기 전 데이터 섞기
    순서가 섞인 정수 시퀀스 리스트를 만들기


       
        indices = np.arange(encoder_input.shape[0])
        np.random.shuffle(indices)
        print('랜덤 시퀀스 :',indices)


랜덤 시퀀스 : [16412  5374  8832 ...  5652 24040 10002]

 

 

 데이터셋의 순서로 지정해주면 샘플들이 기존 순서와 다른 순서로 섞이게 됨


       
        encoder_input = encoder_input[indices]
        decoder_input = decoder_input[indices]
        decoder_target = decoder_target[indices]

 
 

 


임의로 30,997번째 샘플을 출력
:  decoder_input과 decoder_target은 데이터의 구조상으로
앞에 붙은 <sos> 토큰과 뒤에 붙은 <eos>을 제외하면 동일한 정수 시퀀스를 가져야 합니다.


      
          encoder_input[30997]

          decoder_input[30997]

          decoder_target[30997]

array([ 5763810000], dtype=int32)

---------------------------------------------------------

array([ 21851617310000000000], dtype=int32)

---------------------------------------------------------

array([ 18516173130000000000], dtype=int32)

 18, 5, 16, 173, 1이라는 동일 시퀀스를 확인

 

 

 훈련 데이터의 10%를 테스트 데이터로 분리


       

      n_of_val = int(33000*0.1)
      print('검증 데이터의 개수 :',n_of_val)


 

 

 

 

 33,000개의 10%에 해당되는 3,300개의 데이터를 테스트 데이터로 사용


       
          encoder_input_train = encoder_input[:-n_of_val]
          decoder_input_train = decoder_input[:-n_of_val]
          decoder_target_train = decoder_target[:-n_of_val]

          encoder_input_test = encoder_input[-n_of_val:]
          decoder_input_test = decoder_input[-n_of_val:]
          decoder_target_test = decoder_target[-n_of_val:]

 
 

 


 훈련 데이터와 테스트 데이터의 크기(shape)를 출력


     
        print('훈련 source 데이터의 크기 :',encoder_input_train.shape)
        print('훈련 target 데이터의 크기 :',decoder_input_train.shape)
        print('훈련 target 레이블의 크기 :',decoder_target_train.shape)
        print('테스트 source 데이터의 크기 :',encoder_input_test.shape)
        print('테스트 target 데이터의 크기 :',decoder_input_test.shape)
        print('테스트 target 레이블의 크기 :',decoder_target_test.shape)


훈련 source 데이터의 크기 : (29700, 8)
훈련 target 데이터의 크기 : (29700, 16)
훈련 target 레이블의 크기 : (29700, 16)
테스트 
source 데이터의 크기 : (3300, 8)
테스트 target 데이터의 크기 : (3300, 16)
테스트 target 레이블의 크기 : (3300, 16)
훈련 데이터의 샘플은 29,700개, 테스트 데이터의 샘플은 3,300개가 존재

 



2. Seq2Seq 기계 번역기 만들기

 

 import


       

        from tensorflow.keras.layers import Input, LSTM, Embedding, Dense, Masking
        from tensorflow.keras.models import Model


 

 

 

 임베딩 벡터의 차원과 LSTM의 은닉 상태의 크기를 64로 사용


       
      embedding_dim = 64
      hidden_units = 64


 

 

 

 인코더 설계


       
        # 인코더
        encoder_inputs = Input(shape=(None,))
        enc_emb = Embedding(src_vocab_size, embedding_dim)(encoder_inputs) # 임베딩 층
        enc_masking = Masking(mask_value=0.0)(enc_emb) # 패딩 0은 연산에서 제외
        encoder_lstm = LSTM(hidden_units, return_state=True) # 상태값 리턴을 위해 return_state는 True
        encoder_outputs, state_h, state_c = encoder_lstm(enc_masking) # 은닉 상태와 셀 상태를 리턴
        encoder_states = [state_h, state_c] # 인코더의 은닉 상태와 셀 상태를 저장


 

 

 

 디코더 설계


       
        # 디코더
        decoder_inputs = Input(shape=(None,))
        dec_emb_layer = Embedding(tar_vocab_size, hidden_units) # 임베딩 층
        dec_emb = dec_emb_layer(decoder_inputs) # 패딩 0은 연산에서 제외
        dec_masking = Masking(mask_value=0.0)(dec_emb)

        # 상태값 리턴을 위해 return_state는 True, 모든 시점에 대해서 단어를 예측하기 위해 return_sequences는 True
        decoder_lstm = LSTM(hidden_units, return_sequences=True, return_state=True)

        # 인코더의 은닉 상태를 초기 은닉 상태(initial_state)로 사용
        decoder_outputs, _, _ = decoder_lstm(dec_masking,
                                            initial_state=encoder_states)

        # 모든 시점의 결과에 대해서 소프트맥스 함수를 사용한 출력층을 통해 단어 예측
        decoder_dense = Dense(tar_vocab_size, activation='softmax')
        decoder_outputs = decoder_dense(decoder_outputs)

        # 모델의 입력과 출력을 정의.
        model = Model([encoder_inputs, decoder_inputs], decoder_outputs)

        model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['acc'])


 

 

 

 모델을 훈련 (  128개의 배치 크기로 총 50 에포크 학습 )


     
        model.fit(x=[encoder_input_train, decoder_input_train], y=decoder_target_train, \
                  validation_data=([encoder_input_test, decoder_input_test], decoder_target_test),
                  batch_size=128, epochs=50)
 

Epoch 1/50
233/233 [==============================] - 140s 561ms/step - loss: 3.3757 - acc: 0.6155 - val_loss: 2.0012 - val_acc: 0.6233
Epoch 2/50
233/233 [==============================] - 127s 544ms/step - loss: 1.8427 - acc: 0.6804 - val_loss: 1.7148 - val_acc: 0.7446
Epoch 3/50
233/233 [==============================] - 130s 558ms/step - loss: 1.6393 - acc: 0.7459 - val_loss: 1.5574 - val_acc: 0.7590

.....

Epoch 46/50
233/233 [==============================] - 129s 552ms/step - loss: 0.3386 - acc: 0.9180 - val_loss: 0.7202 - val_acc: 0.8728
Epoch 47/50
233/233 [==============================] - 132s 565ms/step - loss: 0.3304 - acc: 0.9196 - val_loss: 0.7188 - val_acc: 0.8730
Epoch 48/50
233/233 [==============================] - 130s 558ms/step - loss: 0.3231 - acc: 0.9211 - val_loss: 0.7218 - val_acc: 0.8722
Epoch 49/50
233/233 [==============================] - 140s 599ms/step - loss: 0.3165 - acc: 0.9224 - val_loss: 0.7165 - val_acc: 0.8737
Epoch 50/50
233/233 [==============================] - 130s 557ms/step - loss: 0.3092 - acc: 0.9239 - val_loss: 0.7177 - val_acc: 0.8740
<keras.src.callbacks.History at 0x78f3a077db10>
최종 에포크에서 훈련 데이터는 92%의 정확도를, 테스트 데이터에서는 87%의 정확도를 얻었습니다.

 



3. Seq2Seq 기계 번역동작시키기

seq2seq는 훈련 과정(교사 강요)과 테스트 과정에서의 동작 방식이 다릅니다.
그래서 테스트 과정을 위해 모델을 다시 설계해주어야 합니다.
특히 디코더를 수정해야 합니다. 이번에는 번역 단계를 위해 모델을 수정하고 동작시켜보겠습니다.

 모델을 다시 설계


     
          # 인코더
          encoder_model = Model(encoder_inputs, encoder_states)

          # 디코더 설계 시작
          # 이전 시점의 상태를 보관할 텐서
          decoder_state_input_h = Input(shape=(hidden_units,))
          decoder_state_input_c = Input(shape=(hidden_units,))
          decoder_states_inputs = [decoder_state_input_h, decoder_state_input_c]

          # 훈련 때 사용했던 임베딩 층을 재사용
          dec_emb2 = dec_emb_layer(decoder_inputs)

          # 다음 단어 예측을 위해 이전 시점의 상태를 현 시점의 초기 상태로 사용
          decoder_outputs2, state_h2, state_c2 = decoder_lstm(dec_emb2, initial_state=decoder_states_inputs)
          decoder_states2 = [state_h2, state_c2]

          # 모든 시점에 대해서 단어 예측
          decoder_outputs2 = decoder_dense(decoder_outputs2)

          # 수정된 디코더
          decoder_model = Model(
              [decoder_inputs] + decoder_states_inputs,
              [decoder_outputs2] + decoder_states2)



 

 

 

 테스트 단계에서의 동작을 위한 decode_sequence 함수 구현


       

        def decode_sequence(input_seq):
          # 입력으로부터 인코더의 마지막 시점의 상태(은닉 상태, 셀 상태)를 얻음
          states_value = encoder_model.predict(input_seq)

          # <SOS>에 해당하는 정수 생성
          target_seq = np.zeros((1,1))
          target_seq[0, 0] = tar_to_index['<sos>']

          stop_condition = False
          decoded_sentence = ''

          # stop_condition이 True가 될 때까지 루프 반복
          # 구현의 간소화를 위해서 이 함수는 배치 크기를 1로 가정합니다.
          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 == '<eos>' or
                len(decoded_sentence) > 50):
                stop_condition = True

            # 현재 시점의 예측 결과를 다음 시점의 입력으로 사용하기 위해 저장
            target_seq = np.zeros((1,1))
            target_seq[0, 0] = sampled_token_index

            # 현재 시점의 상태를 다음 시점의 상태로 사용하기 위해 저장
            states_value = [h, c]

          return decoded_sentence


 

 

 

 결과 확인을 위한 함수


      
      # 원문의 정수 시퀀스를 텍스트 시퀀스로 변환
      def seq_to_src(input_seq):
        sentence = ''
        for encoded_word in input_seq:
          if(encoded_word != 0):
            sentence = sentence + index_to_src[encoded_word] + ' '
        return sentence

      # 번역문의 정수 시퀀스를 텍스트 시퀀스로 변환
      def seq_to_tar(input_seq):
        sentence = ''
        for encoded_word in input_seq:
          if(encoded_word != 0 and encoded_word != tar_to_index['<sos>'] and encoded_word != tar_to_index['<eos>']):
            sentence = sentence + index_to_tar[encoded_word] + ' '
        return sentence


 

 

 훈련 데이터에 대해서 임의로 선택한 인덱스의 샘플의 결과를 출력


       
          for seq_index in [3, 50, 100, 300, 1001]:
            input_seq = encoder_input_train[seq_index: seq_index + 1]
            decoded_sentence = decode_sequence(input_seq)

            print("입력문장 :",seq_to_src(encoder_input_train[seq_index]))
            print("정답문장 :",seq_to_tar(decoder_input_train[seq_index]))
            print("번역문장 :",decoded_sentence[1:-5])
            print("-"*50)

 
입력문장 : when does it end ?
정답문장 : quand est ce que ca finit ?
번역문장 : quand est ce que ca marche ?
--------------------------------------------------

입력문장 : it s sand .
정답문장 : c est du sable .
번역문장 : c est de l eau .
--------------------------------------------------

입력문장 : i didn t go .
정답문장 : je n y suis pas allee .
번역문장 : je ne suis pas encore .
--------------------------------------------------

입력문장 : it was a mistake .
정답문장 : ce fut une erreur .
번역문장 : il s agit d une blague .
--------------------------------------------------

입력문장 : it boggles my mind .
정답문장 : ca me laisse perplexe .
번역문장 : ca m en femme .
--------------------------------------------------

 


 테스트 데이터에 대해서 임의로 선택한 인덱스의 샘플의 결과를 출력


       
      for seq_index in [3, 50, 100, 300, 1001]:
        input_seq = encoder_input_test[seq_index: seq_index + 1]
        decoded_sentence = decode_sequence(input_seq)

        print("입력문장 :",seq_to_src(encoder_input_test[seq_index]))
        print("정답문장 :",seq_to_tar(decoder_input_test[seq_index]))
        print("번역문장 :",decoded_sentence[1:-5])
        print("-"*50)

 
입력문장 : we are busy men .
정답문장 : nous sommes des hommes occupes .
번역문장 : nous sommes tres vieux .
-------------------------------------------------
입력문장 : it was very ugly .
정답문장 : ce n etait vraiment pas beau a voir .
번역문장 : c etait tres fort .
--------------------------------------------------
입력문장 : tom looks shocked .
정답문장 : tom a l air choque .
번역문장 : tom a l air bien .
--------------------------------------------------
입력문장 : cross the street .
정답문장 : traversez la rue .
번역문장 : la ?
--------------------------------------------------
입력문장 : you nearly died .
정답문장 : tu es presque mort .
번역문장 : tu es presque mort .
--------------------------------------------------

 

ㄴ 출처: https://wikidocs.net/86900

 



 Seq2Seq(sequence-to-sequence) 모델을 구현


       

          class Encoder(nn.Module):
                  def __init__(selfinput_sizehidden_size):
                      super(Encoder, self).__init__()
                      self.input_size = input_size
                      self.hidden_size = hidden_size
                      self.embedding = nn.Embedding(input_size, hidden_size)
                      self.gru = nn.GRU(input_size, hidden_size)
                  def forward(selfinput):
                      embedded = self.embedding(input).view(1, input.size(0), self.hidden_size)
                      output, hidden = self.gru(input, embedded)
                      return output, hidden

          class Decoder(nn.Module):
              def __init__(selfhidden_sizeoutput_size):
                  super(Decoder, self).__init__()
                  self.hidden_size = hidden_size
                  self.output_size = output_size
                  self.embedding = nn.Embedding(output_size, hidden_size)
                  self.gru = nn.GRU(tar_vocab_size, hidden_size)
                  self.out = nn.Linear(hidden_size, output_size)
              def forward(selfinputhidden):
                  output = self.embedding(input).view(1, 1, -1)
                  output = F.relu(output)
                  output, hidden = self.gru(output, hidden)
                  output = self.out(output[0])
                  return output, hidden


 

 

 

 

'AI > 자연어처리' 카테고리의 다른 글

14. 문장 임베딩 | ELmo / Transformer  (1) 2024.07.04
13. 문장 임베딩 | Attention Meshanism  (0) 2024.07.04
11. LSTM과 GRU  (0) 2024.07.02
10. CNN Text Classification  (0) 2024.07.02
09. CBOW Text Classification  (0) 2024.07.01