기존의 시퀀스 변환 모델들은 RNN/CNN 기반이며 인코더–디코더 구조를 가짐. 이러한 구조는 계산이 느리고 병렬화가 어렵다는 한계가 있다. 따라서 본 논문에서는 이를 해결하기 위해 RNN·CNN 제거하고 오직 Attention만 사용하는 Transformer 구조를 제안한다.
이 구조는 아래와 같은 장점을 지닌다.
어텐션 메커니즘은 입력과 출력 시퀀스 내의 거리와 상관없이 의존 관계를 학습할 수 있도록 해주는 핵심 기술로 자리잡았다.
그러나 대부분의 모델에서는 여전히 어텐션을 RNN과 함께 사용하고 있으며, 이를 완전히 대체한 경우는 극히 드물다.
따라서 이 연구에서는 순환 구조를 완전히 제거하고, 입력과 출력 간의 전역적 의존성을 어텐션 메커니즘만으로 학습하는 새로운 모델 구조인 트랜스포머를 제안한다.
과거 CNN 기반 모델들은 병렬화는 가능했지만 멀리 떨어진 단어 관계를 배우기 어려웠다. 트랜스포머는 이를 Self-Attention으로 한 번에 처리해, RNN·CNN 없이 전역 의존성을 학습하는 최초의 모델이다.

대부분의 최신 신경망 기반 시퀀스 변환 모델은 인코더–디코더 구조를 가진다.
트랜스포머 또한 이러한 전체적인 인코더–디코더 구조를 따른다. 하지만 인코더와 디코더 모두에서 RNN이나 CNN 대신,
Self-Attention과 Point-wise 완전연결층을 사용한다.
트랜스포머의 인코더는 N = 6개의 동일한 층 으로 이루어진 구조이다.
즉, Self-Attention + Feed Forward 블록이 6개 쌓여 있는 형태다.
Encoder Layer 1
Encoder Layer 2
Encoder Layer 3
Encoder Layer 4
Encoder Layer 5
Encoder Layer 6
각 층은 두 개의 서브레이어 로 구성된다:
즉, 하나의 Encoder Layer 안에는 아래 두 개의 미니-구성요소가 들어있단 뜻.
┌───────────────────────┐
│ Encoder Layer i │
│ │
│ ┌─────────────────┐ │
│ │ Self-Attention │ │ ← sublayer #1
│ └─────────────────┘ │
│ ↓ │
│ ┌─────────────────┐ │
│ │ Feed Forward │ │ ← sublayer #2
│ └─────────────────┘ │
└───────────────────────┘
Multi-Head Self-Attention 메커니즘
→ 문장 내 모든 단어들이 서로의 관계를 동시에 학습Position-wise Fully Connected Feed-Forward Network
→ 각 단어마다 독립적으로 비선형 변환을 적용 (각 단어 벡터를 독립적으로 MLP에 통과시켜 더 풍부한 의미 표현으로 바꿔주는 단계)
각 서브레이어에는 잔차 연결이 적용되고, 그 다음 Layer Normalization이 수행된다.
즉, 각 서브레이어의 출력은 다음과 같이 계산된다:
여기서 는 해당 서브레이어가 수행하는 함수
(예: Self-Attention 또는 Feed Forward)를 의미한다.
Residual Connection을 적용하려면 입력()과 출력()의 차원이 동일해야 한다.
따라서 인코더의 모든 서브레이어와 임베딩 레이어는 같은 차원 를 사용한다.
토큰 아이디를 토큰 임베딩층을 통해 여러 숫자의 집합인 토큰 임베딩으로 변환
서브워드 토큰화: 자주 나오는 단어는 단어 단위 그대로 유지하고, 가끔 나오는 단어는 더 작은 단위로 나눠 텍스트의 의미를 최대한 유지
임베딩 층이 단어의 의미를 담기 위해서는 딥러닝 모델이 학습 데이터로 훈련되어야 한다
트랜스포머는 순차적인 처리 방식을 버리고 모든 입력을 동시에 처리하기 때문에 순서 정보가 사라짐 -> 이를 위치 인코딩이 담당

토큰 임베딩에 위치 인코딩을 더해 모델에 입력할 최종 입력 임베딩을 준비한다.
디코더 또한 N = 6개의 동일한 층으로 구성된다.
하지만 인코더의 두 개 서브레이어에 추가로 한 개의 서브레이어가 더 포함된다.
즉, 각 디코더 층은 다음 세 부분으로 구성된다
1. Masked Multi-Head Self-Attention
2. Encoder-Decoder Attention
3. Feed-Forward Layer
디코더 역시 각 서브레이어마다 Residual Connection → Layer Normalization 순서로 적용된다.
또한 디코더의 Self-Attention은 미래 단어를 보지 못하도록 수정되어 있다.즉, 현재 단어를 예측할 때는 오직 이전 단어들만 참고할 수 있다.
이 마스킹과, 출력 임베딩이 한 칸씩 오른쪽으로 이동되어 입력되는 구조를 통해, 디코더의 i번째 단어 예측은 i보다 앞선 단어들에만 의존하도록 보장된다.

어텐션 함수는 쿼리와 key–value 쌍들의 집합을 입력받아 하나의 출력으로 매핑하는 함수이다.
쿼리, 키, 값, 그리고 출력은 모두 벡터 형태이며, 출력은 value 벡터들의 가중합으로 계산된다.
이때 각 값에 부여되는 가중치는 쿼리와 해당 값에 대응하는 키(key) 간의 유사도에 따라 결정된다.
Scaled Dot-Product Attention:
Self-Attention Mechanism 계산 과정
1. 유사도 계산 (Dot Product)
Query와 Key의 내적을 계산하여 유사도를 구한다. 이 점수는 의 각 단어가 의 각 단어와 얼마나 관련 있는지 나타낸다.
2. 스케일링
내적 값이 너무 커지면 Softmax 함수의 기울기가 작아져 학습이 불안정해지고 어려워지는 문제가 발생한다. 이를 방지하기 위해, Key의 차원 크기 의 제곱근으로 나누어 점수를 안정화한다.
이 과정 때문에 이 메커니즘을 "Scaled Dot-Product Attention"이라고 부른다.
3. Softmax 적용
스케일링된 유사도 점수를 확률 분포로 변환하여 어디에 집중할지를 결정한다. Softmax 결과로 나온 값이 최종적으로 Value에 곱해질 가중치가 된다.
4. 가중합
Softmax를 통과한 가중치(확률 분포)를 각 Value에 곱하여 합친다. 가중치가 높은 Value의 정보는 더 많이 반영되어 최종 출력 행렬을 형성한다.
다른 어텐션 방식과 비교
| 종류 | 방식 | 특징 |
|---|---|---|
| Additive Attention | 쿼리와 키를 작은 신경망(feed-forward network)에 넣어 유사도 계산 | 정확하지만 계산 느림 |
| Dot-Product Attention | 단순히 내적(dot product)으로 유사도 계산 | 빠르고 효율적 |
| Scaled Dot-Product Attention | Dot-product에 √dₖ로 나눠 안정화한 버전 | 트랜스포머에서 사용, 빠르고 안정적 |
Multi-Head Attention은 단일 Attention을 여러 개 병렬로 실행하여 모델이 다양한 관점에서 입력 정보를 처리할 수 있도록 하는 메커니즘
단일 어텐션에서 사용하는 차원의 를 서로 다른 개의 선형 변환을 통해 다양한 하위 공간으로 투영한다.
Q). 하위공간으로 투영한다의 의미
A). 같은 문장을 다른 각도(특징)에 맞게 여러 개의 작은 공간으로 나눠 보는 것
각 헤드는 이렇게 만들어진 를 이용해 Scaled Dot-Product Attention을 병렬로 수행
계산과정
1. 를 각각 개의 헤드로 선형 변환
각 헤드 는 입력 에 고유한 가중치 행렬 를 곱하여 자신만의 하위 공간으로 투영
2. 모든 헤드의 출력을 이어붙임 (Concat)
각 헤드에서 계산된 Attention 결과()를 하나의 행렬로 옆으로 이어 붙인다.
3. 최종적으로 선형 변환을 적용하여 모델 차원으로 복원
이어 붙인 결과에 최종 가중치 행렬 를 곱하여 모델의 원래 차원 로 복원하고 최종 출력을 얻는다.
여러개의 head를 쓰는 이유
| 항목 | 내용 |
|---|---|
| 헤드 수 () | |
| 각 헤드의 차원 | |
| 전체 차원 () | |
| 계산 비용 | 로 설정하여, 전체 계산 비용은 단일 헤드를 사용하는 경우와 거의 동일하게 유지됨. |
class MultiheadAttention(nn.Module):
def __init__(self, token_embed_dim, d_model, n_head, is_causal=False):
super().__init__()
self.n_head = n_head
self.is_causal = is_causal
self.weight_q = nn.Linear(token_embed_dim, d_model)
self.weight_k = nn.Linear(token_embed_dim, d_model)
self.weight_v = nn.Linear(token_embed_dim, d_model)
self.concat_linear = nn.Linear(d_model, d_model)
def forward(self, querys, keys, values):
B, T, C = querys.size()
querys = self.weight_q(querys).view(B, T, self.n_head, C // self.n_head).transpose(1, 2)
keys = self.weight_k(keys).view(B, T, self.n_head, C // self.n_head).transpose(1, 2)
values = self.weight_v(values).view(B, T, self.n_head, C // self.n_head).transpose(1, 2)
attention = compute_attention(querys, keys, values, self.is_causal)
output = attention.transpose(1, 2).contiguous().view(B, T, C)
output = self.concat_linear(output)
return output
n_head = 4
mh_attention = MultiheadAttention(embedding_dim, embedding_dim, n_head)
after_attention_embeddings = mh_attention(input_embeddings, input_embeddings, input_embeddings)
after_attention_embeddings.shape
트랜스포머 모델은 Multi-Head Attention 메커니즘을 총 세 가지 방식으로 활용한다.
인코더와 디코더의 각 레이어에는 어텐션 서브레이어 외에도 각 위치마다 독립적으로 동일하게 적용되는 feed-forward 네트워크가 포함되어 있다.
이 네트워크는 두 개의 선형 변환 사이에 ReLU 활성화 함수를 넣은 구조로 되어 있다.
수식은 다음과 같다:

선형 변환은 모든 위치에 대해 같은 가중치를 공유하지만, 레이어가 바뀌면 각 레이어마다 다른 파라미터를 사용한다.
이 구조는 또 다르게 말하면, 커널 크기 1짜리 1D convolution 두 개를 연속으로 적용하는 것과 같다.
입력과 출력의 차원은 d_model = 512, 그리고 가운데 숨겨진 레이어(hidden layer)의 차원은 d_ff = 2048이다
“각 위치별(각 토큰별)로 독립적”이란 말은?
문장 전체를 한 번에 변형하는 게 아니라,
x₁에 대해 FFN(x₁)
x₂에 대해 FFN(x₂)
x₃에 대해 FFN(x₃)
이렇게 모든 토큰에 같은 FFN을 적용한다는 뜻