아래는 Transformer 아키텍처의 핵심 개념과 구조를 정리한 설명입니다.
---
## 1. 개요
- **논문 및 동기**
- “Attention Is All You Need” (Vaswani et al., 2017)에서 제안
- RNN/LSTM 기반 시퀀스 모델의 병렬 처리 한계와 장거리 의존성 학습의 어려움을 해결하기 위해 등장
- **핵심 아이디어**
- 순차적으로 처리되지 않고, 전체 시퀀스를 한 번에 처리하면서도 각 위치 간의 의존성을 모델링
- **셀프 어텐션(self-attention)** 메커니즘을 중심으로 설계
---
## 2. 주요 구성 요소
1. **Scaled Dot-Product Attention**
- 입력으로 Query(Q), Key(K), Value(V)를 받아 각각의 스코어를 계산
- 수식:
Attention(Q,K,V)=softmax (QKTdk) V \text{Attention}(Q,K,V) = \mathrm{softmax}\!\Bigl(\frac{QK^T}{\sqrt{d_k}}\Bigr)\,V
- √dk 스케일링을 통해 내적 값의 크기를 조절하여 학습 안정화
2. **Multi-Head Attention**
- 여러 개(헤드)의 어텐션을 병렬로 수행한 뒤 결과를 합쳐 다양한 표현 학습
- 수식:
MultiHead(Q,K,V)=Concat(head1,…,headh) WO \text{MultiHead}(Q,K,V) = \mathrm{Concat}(\text{head}_1, …, \text{head}_h)\,W^O
- 각 헤드는 서로 다른 선형 투영 WiQ,WiK,WiVW_i^Q, W_i^K, W_i^V를 사용
3. **Position-wise Feed-Forward Network (FFN)**
- 각 시퀀스 위치마다 독립적으로 적용되는 2-layer MLP
- 수식:
FFN(x)=max(0,xW1+b1) W2+b2 \mathrm{FFN}(x) = \max(0, xW_1 + b_1)\,W_2 + b_2
4. **Positional Encoding**
- 순서를 알 수 없는 어텐션에 위치 정보를 제공
- 사인·코사인 함수를 이용한 고정 인코딩:
PE(pos,2i)=sin(pos100002i/dmodel),PE(pos,2i+1)=cos(pos100002i/dmodel) \begin{aligned} PE_{(pos,2i)} &= \sin\bigl(\tfrac{pos}{10000^{2i/d_\text{model}}}\bigr),\\ PE_{(pos,2i+1)} &= \cos\bigl(\tfrac{pos}{10000^{2i/d_\text{model}}}\bigr) \end{aligned}
5. **Residual Connection & Layer Normalization**
- 각 서브층(sublayer) 앞뒤로 skip-connection 및 LayerNorm 적용
- 안정된 학습과 그라디언트 소실 방지
---
## 3. 인코더(Encoder) 구조
각 인코더 레이어는 다음이 순차적으로 쌓여 있음(총 N개 레이어):
1. **Multi-Head Self-Attention**
2. **Add & Norm**
3. **Feed-Forward Network**
4. **Add & Norm**

_(이미지 출처: 논문 설명 참조)_
---
## 4. 디코더(Decoder) 구조
인코더와 유사하나, 디코더는 두 종류의 어텐션을 추가로 사용:
1. **Masked Multi-Head Self-Attention**
- 미래 토큰을 보지 않도록 마스킹
2. **Multi-Head Attention over Encoder Outputs**
- 인코더의 출력을 Key/Value로 사용해 디코딩 시 인코더 정보 참조
3. **Feed-Forward Network**
각 레이어마다 Add & Norm 적용, 총 N개 레이어
---
## 5. 장점 및 한계
|장점|한계|
|:--|:--|
|- 완전 병렬 처리 가능 → 학습 속도 대폭 향상- 긴거리 의존성 학습에 강함- 다양한 변형 모델(BERT, GPT 등) 기반|- 시퀀스 길이에 비례해 메모리·연산량 급증- 짧은 입력에도 고비용으로 비효율적일 수 있음|
---
## 6. 활용 사례
- **자연어처리**: 기계번역, 요약, 질문응답 (e.g. BERT, GPT 시리즈)
- **비전**: Vision Transformer(ViT)
- **생물정보학**: 단백질 시퀀스 예측(AlphaFold의 Transformer 모듈)
---
---
## 코드 구현
```python
import math
import torch
import torch.nn as nn
import torch.optim as optim
# 위치 인코딩 모듈
class PositionalEncoding(nn.Module):
def __init__(self, d_model, max_len=5000):
super().__init__()
pe = torch.zeros(max_len, d_model) # (max_len, d_model)
position = torch.arange(0, max_len).unsqueeze(1).float() # (max_len, 1)
div_term = torch.exp(torch.arange(0, d_model, 2).float() *
-(math.log(10000.0) / d_model))
pe[:, 0::2] = torch.sin(position * div_term) # 짝수 인덱스
pe[:, 1::2] = torch.cos(position * div_term) # 홀수 인덱스
pe = pe.unsqueeze(0) # (1, max_len, d_model)
self.register_buffer('pe', pe)
def forward(self, x):
# x: (batch_size, seq_len, d_model)
x = x + self.pe[:, :x.size(1)]
return x
# Transformer 기반 Seq2Seq 모델
class TransformerSeq2Seq(nn.Module):
def __init__(self, src_vocab_size, tgt_vocab_size,
d_model=512, nhead=8, num_encoder_layers=6,
num_decoder_layers=6, dim_feedforward=2048,
dropout=0.1, max_len=5000):
super().__init__()
self.d_model = d_model
# 임베딩 + 위치 인코딩
self.src_tok_emb = nn.Embedding(src_vocab_size, d_model)
self.tgt_tok_emb = nn.Embedding(tgt_vocab_size, d_model)
self.positional_encoding = PositionalEncoding(d_model, max_len)
# PyTorch 제공 Transformer 모듈
self.transformer = nn.Transformer(d_model=d_model,
nhead=nhead,
num_encoder_layers=num_encoder_layers,
num_decoder_layers=num_decoder_layers,
dim_feedforward=dim_feedforward,
dropout=dropout)
# 출력 어휘로 매핑
self.generator = nn.Linear(d_model, tgt_vocab_size)
def forward(self, src, tgt, src_mask=None, tgt_mask=None,
src_padding_mask=None, tgt_padding_mask=None,
memory_key_padding_mask=None):
"""
src: (S, N) 시퀀스 길이 S, 배치 크기 N
tgt: (T, N)
masks: PyTorch Transformer API에 맞춘 mask들
"""
# 1) 임베딩 + 스케일링 + 위치 인코딩
src_emb = self.src_tok_emb(src) * math.sqrt(self.d_model) # (S, N, d_model)
tgt_emb = self.tgt_tok_emb(tgt) * math.sqrt(self.d_model) # (T, N, d_model)
src_emb = self.positional_encoding(src_emb)
tgt_emb = self.positional_encoding(tgt_emb)
# 2) Transformer
memory = self.transformer.encoder(src_emb, src_key_padding_mask=src_padding_mask)
output = self.transformer.decoder(tgt_emb, memory,
tgt_mask=tgt_mask,
tgt_key_padding_mask=tgt_padding_mask,
memory_key_padding_mask=memory_key_padding_mask)
# 3) 출력 생성
logits = self.generator(output) # (T, N, tgt_vocab_size)
return logits
def encode(self, src, src_mask=None, src_padding_mask=None):
src_emb = self.positional_encoding(self.src_tok_emb(src) * math.sqrt(self.d_model))
return self.transformer.encoder(src_emb, src_key_padding_mask=src_padding_mask)
def decode(self, tgt, memory, tgt_mask=None, tgt_padding_mask=None, memory_key_padding_mask=None):
tgt_emb = self.positional_encoding(self.tgt_tok_emb(tgt) * math.sqrt(self.d_model))
return self.transformer.decoder(tgt_emb, memory,
tgt_mask=tgt_mask,
tgt_key_padding_mask=tgt_padding_mask,
memory_key_padding_mask=memory_key_padding_mask)
# --- 사용 예제 스케치 ---
def generate_square_subsequent_mask(sz):
"""Decoder가 미래 토큰을 참조하지 못하도록 마스킹 생성"""
mask = (torch.triu(torch.ones(sz, sz)) == 1).transpose(0, 1)
mask = mask.float().masked_fill(mask == 0, float('-inf')).masked_fill(mask == 1, float(0.0))
return mask
# 하이퍼파라미터
SRC_VOCAB_SIZE = 10000
TGT_VOCAB_SIZE = 10000
SEQ_LEN = 32
BATCH_SIZE = 16
# 모델, 옵티마이저, 손실함수
model = TransformerSeq2Seq(SRC_VOCAB_SIZE, TGT_VOCAB_SIZE)
optimizer = optim.Adam(model.parameters(), lr=1e-4)
criterion = nn.CrossEntropyLoss(ignore_index=0) # 패딩 토큰 0으로 가정
# 더미 데이터
src = torch.randint(1, SRC_VOCAB_SIZE, (SEQ_LEN, BATCH_SIZE))
tgt_input = torch.randint(1, TGT_VOCAB_SIZE, (SEQ_LEN, BATCH_SIZE))
tgt_output = torch.randint(1, TGT_VOCAB_SIZE, (SEQ_LEN, BATCH_SIZE))
# 마스크
tgt_mask = generate_square_subsequent_mask(SEQ_LEN)
# 학습 루프 (단일 스텝 예시)
model.train()
optimizer.zero_grad()
logits = model(src, tgt_input, tgt_mask=tgt_mask)
# (T, N, V) -> (T*N, V) / (T, N) -> (T*N)
loss = criterion(logits.view(-1, TGT_VOCAB_SIZE), tgt_output.view(-1))
loss.backward()
optimizer.step()
print(f"Training step done. Loss: {loss.item():.4f}")
```
---
### 코드의 주요 포인트
1. **PositionalEncoding**: 순서 정보를 부여하기 위해 사인&코사인 함수를 사용했습니다.
2. **nn.Transformer**: PyTorch의 내장 모듈로, 인코더·디코더 블록을 손쉽게 쌓을 수 있습니다.
3. **마스킹**: 디코더가 미래 정보를 보지 않도록 `generate_square_subsequent_mask`를 사용해 인위적 마스크를 생성합니다.
4. **모델 사용 흐름**:
- `src`/`tgt_input` → 임베딩 + 위치 인코딩 → Transformer → `generator` → 로짓 → 손실 계산
- 실제 생산 환경에선 소스·타겟 패딩 마스크도 함께 전달해야 합니다.