✳️ [Attention Is All You Need] 발표
✳️ [Attention Is All You Need] 발표
어려워서 걱정했는데
나름 괜찮게 한 둣!
트랜스포머 정복 (아마 (아닐듯
뉴진스 어텐션 포즈ㅋㅋㅋㅋㅋㅋ
인스타에 올라간대
오...너무웃지말걸ㅋㅋㅋ큐ㅠㅠ
✳️ [Attention Is All You Need] ppt 완성본
✳️ [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 값으로