AI/구현

Seq2Seq의 Decoder구현하기: Input Feeding 기반으로~

말하는 감자에요 2025. 5. 29. 17:32
728x90
반응형

앞서 Encoder와 Attention 메커니즘을 직접 구현해보았는데요, 이번에는 Decoder를 구현해봅니다.

 

이 글에서는 특히 Input Feeding 구조를 사용하는 LSTM 기반 Decoder를 소개합니다.


1. Decoder의 구조와 역할은?

Seq2Seq 모델의 Decoder는 번역할 문장의 출력 시퀀스를 생성하는 역할을 합니다.

 

Decoder는 매 타임스텝마다 다음과 같은 과정을 반복합니다:

  1. 이전에 생성한 출력 벡터(h_t_1_tilde)와 현재 입력 단어 임베딩(emb_t)을 결합하여 RNN에 전달
  2. RNN을 통해 현재 시점의 hidden state와 output을 계산
  3. 이 output은 다음 토큰을 예측하거나 다음 time-step의 context로 사용됩니다

2. Input Feeding이란?

기존 RNN 기반 Decoder는 매 시점마다 단순히 단어 임베딩만 입력으로 받았습니다. 하지만 Input Feeding 구조는 다음과 같은 방식으로 개선됩니다:

"이전 time-step에서 생성된 출력 벡터(또는 context vector)를 현재 입력 임베딩과 함께 RNN에 넣어주는 방식입니다."

 

이렇게 하면 Decoder는 직전 예측 결과를 참고하여 더 안정적인 예측을 수행할 수 있고, Attention 정보를 매 시점 반영할 수 있습니다.

 

Luong et al.(2015)의 논문에서 처음 제안되었으며, attention과의 궁합도 매우 좋습니다.

3. Teacher Forcing이란?

학습 시에는 다음 단어를 예측할 때 모델의 예측 결과 대신 정답(ground truth)을 입력으로 사용하는 전략이 자주 사용됩니다. 이를 Teacher Forcing이라고 부릅니다.

 

  • 학습 시: 정답 토큰 → 입력
  • 추론 시: 모델의 예측 토큰 → 입력

이 전략은 학습 속도를 빠르게 하고, 초기에는 더 정확한 문장을 생성하는 데 도움을 줍니다. 하지만 추론 시 입력 분포가 달라지는 문제(Exposure Bias)를 야기할 수 있어 적절한 비율 조절이 중요합니다.

 

👉 이때 Input Feeding을 병행하면 이러한 bias를 어느 정도 완화할 수 있습니다.

Decoder 구현 코드

class Decoder(nn.Module):
    
    def __init__(self, word_vec_size, hidden_size, n_layers=4, dropout_p=.2):
        super().__init__()
        
        self.rnn = nn.LSTM(
            word_vec_size + hidden_size, # It is for input feeding
            hidden_size,
            num_layers=n_layers,
            dropout=dropout_p,
            bidirectional=False,
            batch_first=True,
        )
    

    def forward(self, emb_t, h_t_1_tilde, h_t_1):
        '''
        Decoder은 학습때는 input_feeding & Teacher Forcing, inference 때는 당연히 1-step씩 들어간다.
        '''
        # |emb_t| = (batch_size, 1, word_vec_size)
        # |h_t_1_tilde| = (batch_size, 1, hidden_size)
        # |h_t_1[0]| = (n_layers, batch_size, hidden_size) -> Hidden state at previous time step
        
        batch_size = emb_t.size(0)
        hidden_size = h_t_1[0].size(-1)
        
        if h_t_1_tilde is None:
            # If it is the first time-step,
            h_t_1_tilde = emb_t.new(batch_size, 1, hidden_size).zero_()
        
        # Input Feeding
        x = torch.cat([emb_t, h_t_1_tilde], dim=-1)
        
        # Decoder must take an input for sequentially.
        y, h = self.rnn(x, h_t_1) 
        # |y| = (batch_size, 1, hidden_state)
        # |h[0]]| = (n_layers, batch_size, hidden_state)
        
        return y, h

개념 설명

Input Feeding 이전 출력 벡터를 다음 입력 임베딩과 함께 사용
Teacher Forcing 학습 시 정답 토큰을 다음 입력으로 사용
LSTM 기반 Decoder 매 시점마다 입력을 받아 다음 hidden state를 생성

다음 글 예고

Decoder까지 구현을 마쳤습니다!

 

다음 글에는 Decoder의 Output을 처리하는 Generator 부분을 다뤄보겠습니다.

 

읽어주셔서 감사합니다.

 

728x90
반응형