본문 바로가기

AI/아이펠_리서치

Transformer Decoder 구현 및 학습

개요

케창딥 11장에 나오는 Transformer Encoder 코드 학습 내용을 정리 합니다.

https://github.com/gilbutITbook/080315/blob/main/chapter11_part04_sequence-to-sequence-learning.ipynb

 

080315/chapter11_part04_sequence-to-sequence-learning.ipynb at main · gilbutITbook/080315

<케라스 창시자에게 배우는 딥러닝 2판> 도서의 코드 저장소. Contribute to gilbutITbook/080315 development by creating an account on GitHub.

github.com

 

 

TransformerDecoder  클래스 개요

출처 : https://wikidocs.net/31379

 

TransformerDecoder 는 다음과 같은 주요 단계들로 구성되어 있습니다:

  1. Masked Self-Attention 
  2. Position-wise Feedforward Network
  3. Layer Normalization
  4. Encoder-Decoder Attention

Transformer Decoder 의 하나의 블록을 구현하는 방식으로, 입력 시퀀스와 출력 시퀀스 각 위치 간의 관계를 학습하는 데 사용됩니다.

init 메서드

  • embed_dim: 입력 임베딩 차원.
  • dense_dim: 피드포워드 네트워크의 내부 레이어의 차원.
  • num_heads: 멀티 헤드 어텐션의 헤드 수.

레이어 정의

  • 멀티 헤드 어텐션 (attention_1, attention_2)
    • attention_1: 첫 번째 멀티 헤드 어텐션 레이어로, 디코더의 입력에 대한 셀프 어텐션을 수행합니다.
    • attention_2: 두 번째 멀티 헤드 어텐션 레이어로, 인코더의 출력(encoder_outputs)에 대한 Encoder-Decoder Attention 을 수행합니다.
  • Position-wise Feedforward Network (dense_proj)
    • 두 개의 Dense 레이어로 구성된 시퀀스:
      • 첫 번째 Dense 레이어는 dense_dim 크기이며, 활성화 함수로 ReLU를 사용합니다.
      • 두 번째 Dense 레이어는 임베딩 차원으로 출력을 매핑합니다.
  • Layer Normalization (layernorm_1, layernorm_2, layernorm_3)
    • 각 Attention 출력 및 Feedforward Network 출력에 Layer Normalization을 적용하여 학습의 안정성을 높입니다.
  • 마스킹 지원 (supports_masking = True)
    • Decoder 에서 마스킹을 지원하도록 설정합니다. 이는 패딩 토큰이나 미래 시점 예측 방지를 위한 causal masking 을 처리할 수 있게 해줍니다.
  • get_config 메서드
    • 객체를 직렬화하기 위해 필요한 설정 정보를 반환합니다.

call 메서드

  • Decoder 의 입력을 처리하는 핵심적인 메서드입니다.
    • inputs: 디코더의 입력입니다 (보통 이전 단계의 출력을 사용).
    • encoder_outputs: 인코더의 최종 출력입니다.
    • mask: 어텐션에서 특정 위치를 무시하도록 하기 위한 마스크입니다.
  1. Masked Self-Attention (attention_1)
    • 쿼리, 키, 값이 모두 inputs으로 설정되어 Self-Attention을 수행합니다.
    • use_causal_mask=True: 현재 시점 이후의 토큰을 보지 않도록 causal mask 를 사용합니다. 이는 디코더에서 미래 정보를 참조하지 않도록 보장하는 역할을 합니다.
  2. Layer Normalization 1 
    • Residual Connection : inputs 과 attention_output_1 을 더합니다.
    • Layer Normalization을 적용하여 정규화합니다.
  3. Encoder-Decoder Attention (attention_2)
    • 쿼리는 디코더 어텐션의 출력(attention_output_1), 키와 값은 인코더의 출력(encoder_outputs)을 사용하여 Encoder-Decoder Attention 을 수행합니다.
    • 디코더 입력과 인코더 출력 간의 연관성을 학습합니다.
  4. Layer Normalization 2
    • Residual Connection 과 Layer Normalization을 통해 안정적으로 학습합니다.
  5. Position-wise Feedforward Network
    • 두 개의 Dense 레이어로 구성된 Feedforward Network 를 통과시켜 각 위치의 표현을 변환합니다.
  6. Layer Normalization 3
    • Residual Connection 과 Layer Normalization을 마지막으로 적용하여 최종 출력을 반환합니다.
class TransformerDecoder(layers.Layer):
    def __init__(self, embed_dim, dense_dim, num_heads, **kwargs):
        super().__init__(**kwargs)
        # 임베딩 차원, 피드포워드 네트워크 차원, 멀티 헤드 수 초기화
        self.embed_dim = embed_dim
        self.dense_dim = dense_dim
        self.num_heads = num_heads

        # 첫 번째 멀티 헤드 어텐션 레이어 생성 (디코더 셀프 어텐션)
        self.attention_1 = layers.MultiHeadAttention(
            num_heads=num_heads, key_dim=embed_dim)

        # 두 번째 멀티 헤드 어텐션 레이어 생성 (인코더-디코더 어텐션)
        self.attention_2 = layers.MultiHeadAttention(
            num_heads=num_heads, key_dim=embed_dim)

        # 피드포워드 네트워크로 구성된 Dense 레이어 시퀀스 생성
        self.dense_proj = keras.Sequential(
            [layers.Dense(dense_dim, activation="relu"),  # 첫 번째 Dense 레이어 (ReLU 활성화 함수 사용)
             layers.Dense(embed_dim),]  # 두 번째 Dense 레이어 (출력 크기는 embed_dim)
        )

        # 세 개의 Layer Normalization 레이어 생성
        self.layernorm_1 = layers.LayerNormalization()  # 첫 번째 어텐션 후 정규화
        self.layernorm_2 = layers.LayerNormalization()  # 두 번째 어텐션 후 정규화
        self.layernorm_3 = layers.LayerNormalization()  # 피드포워드 네트워크 후 정규화

        # 마스킹 지원 설정 (패딩 마스킹 지원을 위한 설정)
        self.supports_masking = True

    def get_config(self):
        # 객체를 직렬화하기 위해 필요한 구성 정보 반환
        config = super().get_config()
        config.update({
            "embed_dim": self.embed_dim,
            "num_heads": self.num_heads,
            "dense_dim": self.dense_dim,
        })
        return config

    def call(self, inputs, encoder_outputs, mask=None):
        # 첫 번째 멀티 헤드 어텐션 적용 (셀프 어텐션, 인과적 마스크 사용)
        attention_output_1 = self.attention_1(
            query=inputs,
            value=inputs,
            key=inputs,
            use_causal_mask=True)  # 미래 정보를 보지 않도록 인과적 마스크 적용 (디코더의 셀프 어텐션)

        # 첫 번째 어텐션 출력과 입력의 잔차 연결 후 Layer Normalization 적용
        attention_output_1 = self.layernorm_1(inputs + attention_output_1)  # 잔차 연결을 통해 원본 정보 보존 및 정규화

        # 두 번째 멀티 헤드 어텐션 적용 (인코더-디코더 어텐션)
        attention_output_2 = self.attention_2(
            query=attention_output_1,
            value=encoder_outputs,
            key=encoder_outputs
        )  # 인코더의 출력과 디코더의 출력 간의 어텐션을 계산하여 상호 참조

        # 두 번째 어텐션 출력과 이전 출력의 잔차 연결 후 Layer Normalization 적용
        attention_output_2 = self.layernorm_2(
            attention_output_1 + attention_output_2)  # 잔차 연결을 통해 이전의 정보 보존 및 정규화

        # 피드포워드 네트워크 적용 (Dense 레이어 시퀀스를 통해 출력 변환)
        proj_output = self.dense_proj(attention_output_2)

        # 피드포워드 출력과 이전 출력의 잔차 연결 후 Layer Normalization 적용
        return self.layernorm_3(attention_output_2 + proj_output)  # 최종 출력에 대해 잔차 연결 및 정규화 적용

 

TeansformerDcoder 시각화

┌──────────────┐
│  Output      │
└──────▲───────┘
       │
┌──────┴───────┐
│ Add & Norm 3 │
└──────▲───────┘
       │
┌──────┴─────────────┐
│ Feedforward        │
│ Dense Network      │
│ [Dense (ReLU)]     │
│ [Dense (embed_dim)]│
└──────▲─────────────┘
       │
┌──────┴───────┐
│ Add & Norm 2 │
└──────▲───────┘
       │
┌──────┴─────────────┐
│ Encoder-Decoder    │
│ Attention (Attention 2) │
└──────▲─────────────┘
       │
┌──────┴───────┐
│ Add & Norm 1 │
└──────▲───────┘
       │
┌──────┴───────┐
│ Multi-Head   │
│ Self-Attention│
│ (Attention 1)│
└──────▲───────┘
       │
       |
┌──────────────┐
│  Input       │
└──────────────┘

 

각 블록의 설명

  1. Input Tensor
    • Transformer 디코더 블록의 시작점입니다. 입력 데이터는 시퀀스 길이 N과 임베딩 차원 D를 가지는 텐서 형태로 들어옵니다.
  2. Multi-Head Self-Attention (Attention 1)
    • 입력 텐서가 쿼리(Q), 키(K), 값(V)로 분리되어 멀티 헤드 셀프 어텐션 연산이 수행됩니다.
    • 여러 어텐션 헤드를 사용하여 입력 시퀀스의 각 위치가 다른 위치와 어떻게 연관되는지를 학습합니다.
    • causal mask 를 사용하여 미래 시점의 정보가 참조되지 않도록 합니다.
  3. Add & Layer Normalization 1
    • 셀프 어텐션 출력에 원래 입력을 더한 뒤, 레이어 정규화를 수행합니다.
    • 잔차 연결을 통해 원래의 정보를 보존하며 학습의 안정성을 높이고, Layer Normalization을 통해 학습 효율성을 높입니다.
  4. Encoder-Decoder Attention (Attention 2)
    • 첫 번째 어텐션의 출력이 쿼리(Q)로 사용되고, 인코더의 출력이 키(K)와 값(V)으로 사용됩니다.
    • 인코더-디코더 어텐션은 디코더 입력과 인코더 출력 간의 연관성을 학습하여, 인코더의 정보를 활용해 디코더의 출력을 생성하는 데 도움을 줍니다.
  5. Add & Layer Normalization 2
    • 인코더-디코더 어텐션의 출력에 이전 단계의 입력을 더한 뒤, 다시 한 번 레이어 정규화를 수행합니다.
    • 잔차 연결과 Layer Normalization을 통해 모델의 학습을 안정적으로 진행할 수 있도록 합니다.
  6. Feedforward Dense Network
    • 정규화된 출력은 포지션-와이즈 피드포워드 네트워크로 전달됩니다.
    • 이 네트워크는 두 개의 Dense 레이어로 구성됩니다:
      • 첫 번째 Dense 레이어에서 dense_dim 차원으로 확장한 뒤 ReLU 활성화 함수가 적용됩니다.
      • 두 번째 Dense 레이어에서 다시 임베딩 차원 (embed_dim)으로 축소합니다.
  7. Add & Layer Normalization 3
    • 피드포워드 네트워크의 출력에 이전 단계의 입력을 더한 뒤, 마지막으로 레이어 정규화를 수행합니다.
    • 잔차 연결과 Layer Normalization을 통해 모델의 학습을 안정적으로 진행하며, 출력의 품질을 높입니다.
  8. Output
    • 최종 출력입니다. 이 출력은 다음 레이어로 전달되거나, Transformer 구조 내에서 디코더의 다음 단계 입력으로 사용될 수 있습니다.