Club|Project/Euron | AI,데이터분석 학회

✳️ [Attention Is All You Need] 발표

정람지 2025. 3. 18. 17:01

✳️ [Attention Is All You Need] 발표

어려워서 걱정했는데

나름 괜찮게 한 둣!

트랜스포머 정복 (아마 (아닐듯

뉴진스 어텐션 포즈ㅋㅋㅋㅋㅋㅋ

인스타에 올라간대

오...너무웃지말걸ㅋㅋㅋ큐ㅠㅠ

✳️ [Attention Is All You Need] ppt 완성본

Attention Is All You Need 발표자료.pdf
6.75MB


✳️ [Attention Is All You Need] 발표대본

 

 

Position-wise Feed-Forward Networks (FFN) in Transformer

 

Transformer 모델의 Encoder와 Decoder의 각 레이어에는 Self-Attention 레이어와 함께 피드 포워드 네트워크(FFN)가 포함됨!

 

 Self-Attention은 문맥(Context)을 반영한 가중치를 계산하지만,

개별 단어의 정보를 더욱 강화할 필요가 있음

개별 단어의 표현을 더욱 강화하는 역할을 합니다.

 

FFN의 수식을 자세히 보장

 두 개의 선형 변환 (Linear Transformations)

 첫 번째 선형 변환: 입력 차원을 확장 (hidden_dim → ff_dim)

 ReLU 활성화 함수 적용 (비선형성 부여)

 두 번째 선형 변환: 원래 차원으로 복원 (ff_dim → hidden_dim)

x 입력값 (각 단어의 벡터)
 W_1  첫 번째 선형 변환의 가중치 행렬
 b_1  첫 번째 선형 변환의 편향 벡터
max() ReLU 활성화 함수 (비선형 변환)
W_2 두 번째 선형 변환의 가중치 행렬
b_2 두 번째 선형 변환의 편향 벡터
FFN() 최종 출력값

 


 

Embeddings and Softmax in Transformer

 

Transformer는 다른 시퀀스 변환(Sequence Transduction) 모델과 마찬가지로 임베딩(Embeddings)을 사용하여 입력과 출력을 고정된 차원의 벡터로 변환합니다.

 입력(소스) 토큰  임베딩 레이어  d_model 차원의 벡터

 출력(타겟) 토큰  임베딩 레이어  d_model 차원의 벡터

 

Embedding 과정

 여기서 d_model = 512를 일반적으로 사용

 


 Embedding Layer: 각 단어의 의미를 담은 벡터

 Positional Encoding: 단어의 위치 정보를 담은 벡터

 둘을 더해서 모델이 문맥과 단어 순서를 이해할 수 있도록 함

 

 

Softmax 과정

최종 출력을 위한 Softmax 변환 과정이 필요

이때 멀티헤드에 붙어잇는소프트맥스랑은 완전 다른 애임

 

Transformer에서는 입력 임베딩과 출력 임베딩의 가중치를 공유~~


📌 Positional Encoding in Transformer

 

Transformer 모델은 RNN이나 CNN을 사용하지 않기 때문에 단어의 순서를 자연스럽게 반영하지 못함

입력된 단어의 순서를 모델이 이해할 수 있도록 별도의 위치 정보(Position Information)가 필요

Transformer는 입력된 임베딩(Embedding)에 Positional Encoding을 더하여 위치 정보를 반영

 

 

다양한 주파수를 갖는 사인과 코사인 함수를 이용하여 위치 정보를 부여~~

 

 pos: 현재 단어의 위치 (예: 1번째 단어, 2번째 단어, …)

 i: 임베딩 차원의 인덱스 (각 차원마다 다른 값 사용)

 d_model: 전체 임베딩 차원 수

 

 주파수 변화: 낮은 차원에서는 주기가 긴 신호를, 높은 차원에서는 주기가 짧은 신호를 사용하여 다양한 패턴을 반영

 

 

🔹 4. 왜 사인 & 코사인 함수를 사용하는가?

 

✅ (1) 상대적 위치 정보를 쉽게 학습할 수 있음

 사인 & 코사인 함수는 위치 차이에 따라 선형적인 관계를 유지

 특정 단어의 위치 pos가 변해도, 일정한 패턴을 유지할 수 있음

 예를 들어, pos+k 위치의 Positional Encoding은 기존 pos의 선형 변환으로 나타낼 수 있음

 👉 즉, 모델이 상대적 위치 정보를 학습하기 쉬움!

 

✅ (2) 시퀀스 길이가 늘어나도 일반화 가능

 학습 데이터보다 긴 문장을 처리할 때도 일반화가 가능

 학습 데이터에서 보지 못한 긴 문장에도 모델이 적응할 수 있도록 설계됨

(학습가능한포지셔널임베딩 <->


트랜스포머(Transformer)에서 Self-Attention 중요한 이유에 대해 설명드리겠습니다.

트랜스포머는 딥러닝 기반의 자연어 처리(NLP)에서 매우 중요한 모델이며, 핵심은 바로 Self-Attention 메커니즘입니다.

그렇다면, 트랜스포머는 기존의 RNN이나 CNN 아닌 Self-Attention 선택했을까요?

 

논문에서는 세 가지 핵심 이유를 듬 + 알파

1️⃣ 연산 복잡도(Computational Complexity)

2️⃣ 병렬 처리 가능성(Parallelization Capability)

3️⃣ 장기 의존성(Long-Range Dependency) 학습 효율

 

 

 

 

1️⃣ 연산 복잡도(Computational Complexity)

 

먼저, 연산 복잡도를 비교해보겠습니다.

 

실제 계산량인 Complexity per Layer 표시 (ppt) 

n 단어의 길이.(토큰 개수) / 시퀀스 길이(n)가 표현 차원(d)

N   작을 확률이 크기 때믄에 어텐션이 나음

근데 확신 ㄴㄴ

 

병렬화 측면에서 Sequential Operations 말해 보면,

RNN(Recurrent Neural Network) 같은 순환 신경망은 O(n) 연산량을 요구합니다.

, 시퀀스 길이가 길어질수록 계산량이 선형적으로 증가합니다.

반면, Self-Attention 모든 단어가 동시에 서로를 참조할 있어 O(1) 연산을 유지할 있습니다.

, 시퀀스 길이가 늘어나더라도 영향을 받지 않습니다.

 

차이점 덕분에 Self-Attention 시퀀스 길이가 길더라도 효율적으로 연산할 있습니다.

 

 

 

2️⃣ 병렬 처리 가능성(Parallelization Capability)

RNN 계열 모델들은 이전 시간 단계의 계산이 완료된 후에야 다음 단계 계산이 가능합니다.

, 병렬 연산이 어렵고, 훈련 속도가 느려지는 단점이 있습니다.

반면, Self-Attention 모든 단어가 번에 연산될 있어 GPU 병렬 연산이 가능합니다.

따라서 학습 속도가 빠르며, GPU 성능을 효율적으로 활용할 있습니다.

 

 

 

3️⃣ 장기 의존성(Long-Range Dependency) 학습 효율

RNN 기반 모델은 시퀀스를 순차적으로 처리하기 때문에, 입력 단어 거리가 멀수록 정보 전달이 어려워지는 문제가 있습니다.

이를 장기 의존성(Long-Range Dependency) 문제라고 합니다.

예를 들어, 문장의 앞부분과 뒷부분의 단어가 관계가 깊어도, RNN 모델은 이를 학습하는 어려움을 겪습니다.

 

CNN 계열 모델은 번의 합성곱 연산으로 모든 단어를 보지는 못하며, 여러 계층을 쌓아야 전체 문맥을 있습니다.

반면, Self-Attention 모든 단어가 서로를 직접 참조할 있기 때문에, 장기 의존성을 학습하는 유리합니다.

문장의 앞과 뒤에 있는 단어가 직접 연결되므로, 정보를 효과적으로 전달할 있습니다.

 

, Self-Attention 짧은 경로(Path Length) 장기 의존성을 학습할 있도록 해줍니다.

 

 

국소적으로 제한하는 기법도 쓸 수 있으며

해석 가능성 (소프트맥스 적용한 값을 시각적으로 뽑

 

 

Self-Attention 기존 RNN, CNN 모델과 비교했을 연산량이 적어 효율적)이며 병렬 연산이 가능하여 빠르고, 장기 의존성 문제를 해결하여 성능 향상한 매커니즘

덕분에 트랜스포머는 기존 모델들보다 빠르고 정확한 번역, 문장 생성, 요약 등을 수행할 있습니다.

이러한 이유로 현재 NLP 분야에서 트랜스포머는 표준 모델로 자리 잡았으며, 이후 BERT, GPT, T5 다양한 변형 모델이 등장하는 기반이 되었습니다.

 


 

📢 트랜스포머의 학습 과정 (Training of Transformer)

 

 

1️⃣ 데이터 배칭 (Training Data and Batching)

2️⃣ 하드웨어 학습 일정 (Hardware and Training Schedule)

3️⃣ 최적화 기법 (Optimizer and Learning Rate Scheduling)

4️⃣ 정규화 기법 (Regularization Techniques)

을 각각 말함 

 

 

1️⃣ 데이터 배칭 (Training Data and Batching)

 

트랜스포머는 대규모 병렬 데이터셋을 활용하여 학습됩니다.

WMT 2014 English-German 데이터셋

450 개의 문장 쌍으로 구성

Byte-Pair Encoding(BPE) 사용하여 37,000개의 단어 토큰을 생성

WMT 2014 English-French 데이터셋

3,600 개의 문장

WordPiece 토큰화를 통해 32,000개의 단어 토큰을 사용

 

🔹 배칭(Batching) 기법

문장의 길이에 따라 비슷한 길이의 문장들을 그룹화하여 배치(batch)

배치는 25,000개의 원본(source) 타겟(target) 토큰으로 구성

 

 

2️⃣ 하드웨어 학습 일정 (Hardware and Training Schedule)

 

트랜스포머 모델 학습에는 고성능 GPU 병렬 연산이 필수적입니다.

기본(Base) 모델 학습

8개의 NVIDIA P100 GPU 사용

100,000 스텝 동안 학습 ( 12시간 소요)

대형(Big) 모델 학습

동일한 8개의 P100 GPU 사용

300,000 스텝 동안 학습 ( 3.5 소요)

 

 

 

3️⃣ 최적화 기법 (Optimizer and Learning Rate Scheduling)

 

트랜스포머는 Adam 옵티마이저를 사용하여 학습됩니다.

Adam 옵티마이저 하이퍼파라미터

β₁ = 0.9 (이전 그래디언트 정보 반영)

β₂ = 0.98 (적응형 학습률 조정)

ε = 10⁻⁹ (수치 안정성 확보)

 

🔹 학습률 스케줄링 (Learning Rate Scheduling)

(학습률(learning rate,  \eta )은 모델의 가중치 업데이트 크기를 결정하는 하이퍼파라미터)

학습 초기에는 학습률을 점진적으로 증가시킨 , 이후에는 감소하는 방식을 사용합니다.

학습 초반에는 점진적으로 학습률 증가 (Warmup 단계, 4,000 스텝 적용)

이후 스텝 수의 제곱근에 반비례하여 감소

 

 

 

4️⃣ 정규화 기법 (Regularization Techniques)

 

트랜스포머는 과적합(overfitting) 방지하기 위해 3가지 정규화 기법을 적용합니다.

 

🔹 1) 잔차 드롭아웃 (Residual Dropout)

서브 레이어의 출력에 드롭아웃(dropout) 적용

임베딩과 포지셔널 인코딩에도 드롭아웃 적용

기본(Base) 모델에서는 Dropout Rate = 0.1 사용

 

🔹 2) 라벨 스무딩 (Label Smoothing)

Cross-Entropy Loss 계산할 정답 확률 분포를 부드럽게(ε = 0.1) 조정

모델이 너무 확신하는 (Overconfidence) 방지하여 일반화 성능 향상

 

🔹 3) 정규화 레이어 (Layer Normalization)

레이어의 출력을 정규화하여 학습 안정성을 향상

잔차 연결(Residual Connection) 함께 적용하여 깊은 네트워크에서도 정보 손실 방지

 


 

 

📢 트랜스포머의 성능 결과 (Results of Transformer)

 

 

WMT 2014 영어-독일어(EN-DE) 및 영어-프랑스어(EN-FR) 번역 태스크에서 성능을 평가

 

🔹 영어-독일어 (EN-DE) 번역 성능

 트랜스포머(Big) 모델 BLEU 점수: 28.4

 기존 최고 성능 모델보다 2.0 BLEU 이상 높은 성능

 

🔹 영어-프랑스어 (EN-FR) 번역 성능

 트랜스포머(Big) 모델 BLEU 점수: 41.0

 기존 단일 모델(single model) 대비 가장 높은 BLEU 점수

 

 

 

 

여러 변형 모델을 실험

이를 통해 어텐션 헤드 개수, 키/밸류 차원, 모델 크기 및 정규화 기법이 성능에 미치는 영향을 평가

 

🔹 (A) 어텐션 헤드 개수 변화

 싱글 헤드(Single Head) 어텐션을 사용하면 BLEU 점수가 0.9 감소

 반면, 너무 많은 헤드를 사용할 경우에도 성능 감소

 적절한 어텐션 헤드 개수를 유지하는 것이 중요

 

🔹 (B) 어텐션 키/밸류 크기 감소

 어텐션에서 키(key) 크기를 줄이면 모델 성능이 저하됨

 이는 키/밸류 차원이 작아지면 문맥 정보 매칭이 어려워지기 때문

 

🔹 (C) 모델 크기 확대

 더 큰 피드포워드 네트워크(FFN)와 어텐션 차원을 갖는 모델이 일반적으로 더 높은 BLEU 점수를 기록

 Big 모델은 Base 모델 대비 성능이 더 높음

 

🔹 (D) 드롭아웃(Dropout) 적용

 과적합(overfitting)을 방지하는 드롭아웃(dropout) 기법이 효과적

 드롭아웃을 적용하지 않으면 성능이 하락

 

🔹 (E) 포지셔널 인코딩 변화

 기존 사인 및 코사인 함수 기반 포지셔널 인코딩

학습 가능한 포지셔널 임베딩(learnable positional embedding)으로 대체

 그러나 성능 차이는 거의 없음

 

 

💡 트랜스포머 성능 최적화 결론

➡️ 어텐션 헤드 개수와 키/밸류 크기는 적절한 밸런스가 중요

➡️ 드롭아웃은 과적합 방지에 필수적

➡️ 모델 크기를 늘리면 성능이 향상되지만, 연산 비용도 증가

 


 

 

1️⃣ Multi-Head Attention Layer

 

이 코드는 Transformer 모델의 핵심 개념 중 하나인 Multi-Head Self-Attention 을 구현한 클래스입니다.

 

🔹 Multi-Head Self-Attention이란?

 입력 문장에서 중요한 단어들 간의 관계를 찾는 과정

 여러 개의 어텐션(Attention) 헤드를 사용하여 다양한 문맥 정보를 학습 가능

 Scaled Dot-Product Attention 방식을 사용하여 효율적으로 계산

 

📌 코드 주요 기능

 forward() 함수에서 Query, Key, Value 변환

✅ Scaled Dot-Product Attention 계산

✅ 여러 헤드를 결합하여 최종 출력 생성

 

 

2️⃣ 코드 분석 - 클래스 정의

import torch.nn as nn

class MultiHeadAttentionLayer(nn.Module):
    def __init__(self, hidden_dim, n_heads, dropout_ratio, device):
        super().__init__()

        assert hidden_dim % n_heads == 0  # hidden_dim이 n_heads의 배수여야 함

        self.hidden_dim = hidden_dim # 전체 임베딩 차원 (예: 512)
        self.n_heads = n_heads # 헤드(head) 개수 (예: 8)
        self.head_dim = hidden_dim // n_heads # 각 헤드(head)의 차원 (예: 512 / 8 = 64)

📌 설명

 hidden_dim: 전체 임베딩 차원 (예: 512)

 n_heads: 어텐션 헤드 개수 (예: 8)

 head_dim: 각 헤드의 차원 (hidden_dim // n_heads)

 assert hidden_dim % n_heads == 0: 임베딩 차원은 반드시 헤드 개수의 배수여야 함

 

 

3️⃣ Query, Key, Value 변환

self.fc_q = nn.Linear(hidden_dim, hidden_dim)  # Query 변환
self.fc_k = nn.Linear(hidden_dim, hidden_dim)  # Key 변환
self.fc_v = nn.Linear(hidden_dim, hidden_dim)  # Value 변환
self.fc_o = nn.Linear(hidden_dim, hidden_dim)  # 최종 출력 변환

📌 설명

 fc_q, fc_k, fc_v: 입력 문장을 Query, Key, Value로 변환하는 가중치 행렬

 fc_o: 여러 헤드에서 나온 결과를 다시 하나의 벡터로 합침

 

 

 

4️⃣ Self-Attention 과정

Q = self.fc_q(query)  # [batch_size, query_len, hidden_dim]
K = self.fc_k(key)  # [batch_size, key_len, hidden_dim]
V = self.fc_v(value)  # [batch_size, value_len, hidden_dim]

📌 설명

 Query, Key, Value를 Fully Connected Layer를 통해 변환

 

 

 

5️⃣ Multi-Head Attention: Shape 변환

Q = Q.view(batch_size, -1, self.n_heads, self.head_dim).permute(0, 2, 1, 3)
K = K.view(batch_size, -1, self.n_heads, self.head_dim).permute(0, 2, 1, 3)
V = V.view(batch_size, -1, self.n_heads, self.head_dim).permute(0, 2, 1, 3)

📌 설명

 Q, K, V를 여러 개의 헤드로 나눠서 계산 가능하게 변환

 

 

6️⃣ Scaled Dot-Product Attention

energy = torch.matmul(Q, K.permute(0, 1, 3, 2)) / self.scale
attention = torch.softmax(energy, dim=-1)

📌 설명

 energy = Q × K^T: Query와 Key의 내적(dot product) → 유사도(Attention Score) 계산

 / self.scale: 어텐션 점수를 안정적으로 만들기 위해 스케일링

 

 softmax(energy): 가중치를 확률로 변환 (전체 합이 1이 되도록 정규화)

 

💡 왜 스케일링이 필요한가?

 head_dim이 클 경우, 내적 값이 너무 커져 Softmax의 기울기가 작아질 위험이 있음

 

 

7️⃣ 최종 출력 계산

x = torch.matmul(self.dropout(attention), V)  # Attention 적용
x = x.permute(0, 2, 1, 3).contiguous()  # 차원 변환
x = x.view(batch_size, -1, self.hidden_dim)  # 원래 차원으로 복구
x = self.fc_o(x)  # 최종 출력 변환

📌 설명

 어텐션 스코어를 V에 적용하여 최종 출력 값 생성

 permute(0, 2, 1, 3): 원래 차원으로 되돌리기

 view(batch_size, -1, hidden_dim): 여러 헤드에서 나온 결과를 다시 결합

 fc_o: 최종 Fully Connected Layer를 통과하여 원래 차원으로 변환

 


Position-wise Feedforward Layer 설명

 

각 토큰(단어)마다 독립적으로 적용되는 구조를 가지며, (셀프어텐션은 문장 전체를 봄

비선형 변환을 통해 표현력을 높이는 역할을 합니다~

 

1️⃣ Position-wise Feedforward Layer란?

 Self-Attention Layer만으로는 충분한 변환을 수행하기 어렵기 때문에 추가적으로 사용됨

 각 단어(토큰)별로 독립적인 비선형 변환을 수행

 MLP(다층 퍼셉트론, Multi-Layer Perceptron)와 유사한 구조

 입력 차원을 확장했다가 다시 줄이는 구조 (차원을 키워 학습 능력을 높인 후 다시 축소)

 

 

 

📌 ① 선형 변환 (차원 확장)

self.fc_1 = nn.Linear(hidden_dim, pf_dim)

 입력 차원 hidden_dim을 더 큰 차원 pf_dim으로 변환

 이유? 더 높은 차원에서 정보를 충분히 학습한 후 다시 압축하여 학습 효과 극대화

 

 

📌 ② 활성화 함수 (ReLU) 적용 및 드롭아웃

x = self.dropout(torch.relu(self.fc_1(x)))

 ReLU (Rectified Linear Unit): 비선형성을 추가하여 학습 성능 향상

 드롭아웃(Dropout): 과적합 방지를 위해 일부 뉴런을 랜덤하게 제외

 ReLU를 적용한 후 드롭아웃을 적용하면 학습 안정성이 증가

 

📌 ③ 선형 변환 (차원 축소)

self.fc_2 = nn.Linear(pf_dim, hidden_dim)

 차원을 다시 hidden_dim으로 축소하여 원래 크기로 복구

 FFN을 적용한 후에도 입력과 동일한 차원을 유지해야 Transformer의 구조를 유지할 수 있음

 

 

 Self-Attention이 단어 간 관계를 학습하는 것과 달리, FFN은 각 단어의 표현력을 강화하는 역할


Transformer Encoder 설명 및 코드 분석

.

Self-Attention과 Feedforward Network(FFN)를 조합하여 문장을 인코딩하는 역할을 수행합니당

 

 

📌 Encoder의 입력과 출력

 입력: 문장(단어들의 시퀀스)

 출력: 각 단어의 인코딩 벡터 (다음 레이어 또는 디코더로 전달)

 

 

📌 (1) Multi-Head Self-Attention

_src, _ = self.self_attention(src, src, src, src_mask)

 입력: src (입력 문장의 임베딩)

 Query, Key, Value가 동일한 입력을 사용  Self-Attention 수행

 출력: 문맥 정보를 반영한 _src

 

📌 (2) Residual Connection + Layer Normalization

src = self.self_attn_layer_norm(src + self.dropout(_src))

 Residual Connection: Self-Attention 출력 _src를 원래 src와 더함

 Layer Normalization: 데이터 분포를 정규화하여 안정적 학습 가능

 드롭아웃 적용 → 과적합 방지

 

📌 (3) Position-wise Feedforward Network (FFN)

_src = self.positionwise_feedforward(src)

 Self-Attention으로 문맥 정보를 학습했지만, 단어별 표현력을 높이기 위해 FFN 적용

 FFN은 각 단어별 독립적으로 학습됨

 

📌 (4) Residual Connection + Layer Normalization

src = self.ff_layer_norm(src + self.dropout(_src))

 FFN 출력을 원래 입력과 더함 (Residual Connection)

 Layer Normalization 적용하여 학습 안정성 증가


Encoder

 

📌 (1) 단어 임베딩 + 위치 임베딩

self.tok_embedding = nn.Embedding(input_dim, hidden_dim)
self.pos_embedding = nn.Embedding(max_length, hidden_dim)

 단어 임베딩(Token Embedding): input_dim 개의 단어를 hidden_dim 차원의 벡터로 변환

 위치 임베딩(Position Encoding): 문장 내에서 단어의 순서를 학습하기 위해 추가

(코사인사인안씀

 

📌 (2) 여러 개의 EncoderLayer 쌓기

self.layers = nn.ModuleList([EncoderLayer(hidden_dim, n_heads, pf_dim, dropout_ratio, device) for _ in range(n_layers)])

 Transformer는 여러 개의 인코더 레이어를 쌓아서 성능을 향상

 일반적으로 n_layers = 6을 사용

 

📌 (3) 단어 임베딩 + 위치 임베딩 적용

src = self.dropout((self.tok_embedding(src) * self.scale) + self.pos_embedding(pos))

 단어 임베딩을 스케일링하여 학습 안정성 증가

 위치 임베딩과 합하여 입력으로 사용

 드롭아웃(Dropout) 적용

 

📌 (4) 여러 개의 인코더 레이어 통과

for layer in self.layers:
    src = layer(src, src_mask)

  EncoderLayer를 순차적으로 통과하면서 점진적으로 표현 학습

 

📌 (5) 최종 인코딩된 벡터 반환

return src

 Transformer Encoder에서 인코딩된 벡터가 최종 출력

 이 벡터는 디코더(Decoder) 또는 다른 NLP 태스크에서 활용됨


 

Transformer Decoder 설명 및 코드 분석

 

Decoder는 Encoder의 출력을 참고하여 최종 번역된 문장을 생성하는 역할을 합니당

 

 

 

📌 (1) Masked Multi-Head Self-Attention

_trg, _ = self.self_attention(trg, trg, trg, trg_mask)

 현재까지 생성된 단어들만 이용하여 Self-Attention 수행

 trg_mask를 적용하여 미래 단어를 참조하지 못하도록 설정 (Auto-Regressive 특성 유지)

 

 

📌 (2) Residual Connection + Layer Normalization

trg = self.self_attn_layer_norm(trg + self.dropout(_trg))

 원래 trg에 Self-Attention 출력을 더하고 정규화

 

 

📌 (3) Encoder-Decoder Attention

_trg, attention = self.encoder_attention(trg, enc_src, enc_src, src_mask)

 Query = 디코더의 출력 (trg)

 Key, Value = 인코더의 출력 (enc_src)

 이 과정을 통해 Decoder가 Encoder 출력을 활용하여 의미 있는 번역을 생성

 

 

📌 (4) Residual Connection + Layer Normalization

trg = self.enc_attn_layer_norm(trg + self.dropout(_trg))

 Encoder-Decoder Attention 출력을 trg에 더하고 정규화

 

 

📌 (5) Position-wise Feedforward Network (FFN)

_trg = self.positionwise_feedforward(trg)

 FFN을 사용하여 단어별 독립적으로 표현력을 향상

 

 

📌 (6) Residual Connection + Layer Normalization

trg = self.ff_layer_norm(trg + self.dropout(_trg))

 FFN 출력을 원래 입력과 더하고 정규화


 Decoder

🔍 코드 상세 설명

 

📌 (1) 위치 임베딩 생성

pos = torch.arange(0, trg_len).unsqueeze(0).repeat(batch_size, 1).to(self.device)

 문장의 길이 trg_len에 따라 위치 정보를 부여

 

📌 (2) 단어 임베딩 + 위치 임베딩

trg = self.dropout((self.tok_embedding(trg) * self.scale) + self.pos_embedding(pos))

 단어 임베딩(tok_embedding)과 위치 임베딩(pos_embedding)을 더함

 Dropout을 적용하여 과적합 방지

 

📌 (3) 여러 개의 디코더 레이어 통과

for layer in self.layers:
    trg, attention = layer(trg, enc_src, trg_mask, src_mask)

  DecoderLayer를 순차적으로 통과하며 번역을 생성

 

📌 (4) 최종 출력 단어 예측

output = self.fc_out(trg)

 Fully Connected Layer를 통해 최종 출력 생성

 output_dim 크기의 단어 분포로 변환됨


Transformer 모델 설명 및 코드 분석

 

이 코드는 Transformer 전체 모델을 구현한 것입니당

 

class Transformer(nn.Module):
    def __init__(self, encoder, decoder, src_pad_idx, trg_pad_idx, device):
        super().__init__()

        self.encoder = encoder
        self.decoder = decoder
        self.src_pad_idx = src_pad_idx
        self.trg_pad_idx = trg_pad_idx
        self.device = device

 encoder: Transformer Encoder

 decoder: Transformer Decoder

 src_pad_idx: 입력 문장에서 padding 토큰을 구분하는 값

 trg_pad_idx: 출력 문장에서 padding 토큰을 구분하는 값

 device: GPU/CPU 장치 지정

 

 

🔹 1) 입력 문장 마스크 생성

def make_src_mask(self, src):

    # src: [batch_size, src_len]

    src_mask = (src != self.src_pad_idx).unsqueeze(1).unsqueeze(2)

    # src_mask: [batch_size, 1, 1, src_len]

    return src_mask

📌 (1) 역할

 Encoder에서 Padding Token을 무시하도록 마스크 생성

 self.src_pad_idx 값과 다른 부분은 1, padding 부분은 0으로 설정

 

📌 (2) 결과 형태

batch_size x 1 x 1 x src_len

 어텐션 수행 시, 0이 있는 부분은 가중치를 줄이도록 설정

 

🔹 2) 출력 문장 마스크 생성

def make_trg_mask(self, trg):

    # trg: [batch_size, trg_len]

    trg_pad_mask = (trg != self.trg_pad_idx).unsqueeze(1).unsqueeze(2)

    # trg_pad_mask: [batch_size, 1, 1, trg_len]

    trg_len = trg.shape[1]

    trg_sub_mask = torch.tril(torch.ones((trg_len, trg_len), device = self.device)).bool()

    # trg_sub_mask: [trg_len, trg_len]

    trg_mask = trg_pad_mask & trg_sub_mask

    # trg_mask: [batch_size, 1, trg_len, trg_len]

    return trg_mask

📌 (1) 역할

 Decoder에서 미래 단어를 보지 못하도록 마스킹 적용

 

 

 두 가지 마스크 생성

1️⃣ Padding Mask (trg_pad_mask)  padding 부분을 0으로 설정

2️⃣ Subsequent Mask (trg_sub_mask) → 삼각 행렬을 사용하여 현재 단어까지의 정보만 사용

 

📌 (2) trg_sub_mask 예시

1  0  0  0  0
1  1  0  0  0
1  1  1  0  0
1  1  1  1  0
1  1  1  1  1

 첫 번째 단어는 자기 자신만 볼 수 있음

 두 번째 단어는 첫 번째 단어까지만 볼 수 있음

 

📌 (3) 최종 마스크 (trg_mask)

 trg_pad_mask & trg_sub_mask 연산을 통해 패딩과 미래 단어를 모두 차단

 

🔹 3) Transformer의 Forward 연산

def forward(self, src, trg):

    # src: [batch_size, src_len]
    # trg: [batch_size, trg_len]

    src_mask = self.make_src_mask(src)
    trg_mask = self.make_trg_mask(trg)

    # src_mask: [batch_size, 1, 1, src_len]
    # trg_mask: [batch_size, 1, trg_len, trg_len]

    enc_src = self.encoder(src, src_mask)

    # enc_src: [batch_size, src_len, hidden_dim]

    output, attention = self.decoder(trg, enc_src, trg_mask, src_mask)

    # output: [batch_size, trg_len, output_dim]
    # attention: [batch_size, n_heads, trg_len, src_len]

    return output, attention

📌 (1) 입력 문장 인코딩

enc_src = self.encoder(src, src_mask)

 Encoder를 통과하여 입력 문장을 의미 있는 벡터로 변환

 enc_src 각 단어의 문맥 정보가 포함된 벡터 표현

 

📌 (2) 출력 문장 디코딩

output, attention = self.decoder(trg, enc_src, trg_mask, src_mask)

 enc_src를 참고하여 번역된 문장(output)을 생성

 attention 각 단어가 입력 문장에서 어느 부분을 참고했는지 나타내는 어텐션 행렬

 

 

 

📌 3. 전체 Transformer 흐름

 

1️⃣ 입력 문장을 Encoder에 전달

2️⃣ 입력 문장의 Padding Mask를 적용하여 불필요한 정보 무시

3️⃣ Encoder가 Self-Attention을 수행하여 문맥 벡터를 생성 (enc_src)

4️⃣ Decoder에서 Masked Self-Attention을 수행하여 과거 단어만 사용

5️⃣ Encoder-Decoder Attention을 사용하여 enc_src를 참고

6️⃣ Feed Forward Network(FFN) 적용 후 최종 번역 문장 출력

 

 

 

+

소프트맥스  출력으로  확인해 보기


결론 talk talk

 



Dmodel h값으로 나눈 것이 dk dv 값으로