01 - Transformers의 주의 메커니즘: 전체 가이드
2017년에 "Attention Is All You Need"라는 제목의 Google Brain 논문이 상황을 완전히 바꿔 놓았습니다. 딥러닝 분야. 저자인 Vaswani와 동료들은 아키텍처를 제안했습니다. 전적으로 다음과 같은 메커니즘을 기반으로 합니다. 주목, 네트워크 제거 그때까지 지배적이었던 순환(RNN) 및 컨벌루션 시스템. 결과는 건축 트랜스포머, 오늘 GPT-4 기지에서 Claude, Llama 3, BERT, T5, Vision Transformers 및 거의 모든 프론티어 모델.
주의 메커니즘을 이해하는 것은 학술적인 연습이 아닙니다. 그들은 LoRA 미세 조정, 양자화, 가지치기 및 배포와 같은 기술을 구축합니다. 에지 장치, 이 시리즈에서 다룰 모든 주제. 이해도 없이 주의가 어떻게 작동하는지에 대한 확실한 이해를 통해 이후의 각 최적화는 블랙박스로 유지됩니다.
이 시리즈의 첫 번째 기사에서는 고급 딥 러닝 및 엣지 배포, 우리는 초기 직관부터 수학 공식까지, PyTorch 구현부터 Flash Attention 3 및 Grouped-Query와 같은 최신 변형까지 주의.
시리즈 개요
| # | Articolo | 집중하다 |
|---|---|---|
| 1 | 현재 위치 - 변압기의 주의 메커니즘 | Self-attention, 멀티 헤드, 완전한 아키텍처 |
| 2 | LoRA, QLoRA 및 어댑터를 사용한 미세 조정 | 매개변수 효율적인 미세 조정 |
| 3 | 모델의 양자화 | INT8, INT4, GPTQ, AWQ |
| 4 | 가지치기 및 압축 | 매개변수 감소, 증류 |
| 5 | 지식의 증류 | 교사-학생, 지식 이전 |
| 6 | 올라마(Ollama)와 LLM 로컬 | 지역 추론, 최적화 |
| 7 | 비전 트랜스포머 | ViT, DINO, 이미지 분류 |
| 8 | 엣지 배포 | ONNX, TensorRT, 모바일 장치 |
| 9 | NAS와 AutoML | 신경 아키텍처 검색 |
| 10 | 벤치마킹 및 최적화 | 프로파일링, 메트릭, 튜닝 |
무엇을 배울 것인가
- RNN과 LSTM은 긴 시퀀스에 충분하지 않았기 때문입니다.
- 어텐션 메커니즘 이면의 직관: 쿼리, 키, 값
- Scaled Dot-Product Attention의 완전한 공식
- Multi-Head Attention의 작동 방식과 다중 헤드가 필요한 이유
- Self-Attention과 Cross-Attention의 차이점
- 위치 인코딩이 순서 문제를 해결하는 방법
- 완전한 Transformer 아키텍처: 인코더 및 디코더
- PyTorch에서 한 줄씩 실제 구현
- 최신 변형: Flash Attention 3, GQA, Sliding Window Attention
- 실제 아키텍처: GPT(디코더 전용), BERT(인코더 전용), T5(인코더-디코더)
1. 순서 문제: 주의를 기울이기 전
주의가 혁명이었던 이유를 이해하려면 다음과 같은 모델부터 시작해야 합니다. 그들은 그녀보다 앞섰습니다. 시퀀스(텍스트, 오디오, 시계열)에 대한 딥러닝은 두 가지 아키텍처가 지배합니다. RNN(반복 신경망) 그리고 LSTM(장단기 기억).
1.1 RNN과 순차적 병목 현상
RNN은 한 번에 하나의 토큰을 시퀀스하여 숨겨진 상태를 전달합니다. 한 단계에서 다음 단계로. 각 토큰은 숨겨진 상태를 업데이트합니다. 지금까지 본 시퀀스의 "기억"입니다.
Input: x1 -----> x2 -----> x3 -----> x4 -----> x5
| | | | |
v v v v v
Hidden: h1 -----> h2 -----> h3 -----> h4 -----> h5
| |
v v
Output: y1 y5
Problema: h5 deve "ricordare" x1 attraverso 4 passaggi.
Con sequenze di 1000+ token, l'informazione di x1 svanisce.
이것이 문제이다 장거리 의존성. "3년 전 보호소에서 입양되어 생활하고 있던 고양이는 가족과 함께 행복하게, 그는 자고 있었어요 소파에", RNN은 "고양이"를 연결해야 합니다. 수십 개의 중간 토큰을 통해 "잠"을 잤습니다. 벡터로 압축된 숨겨진 상태 크기가 고정되어 있으면 필연적으로 오래된 정보가 손실됩니다.
1.2 LSTM: 해결책이 아닌 개선
LSTM은 게이트 메커니즘(입력 게이트, 망각 게이트, 출력 게이트)을 도입했습니다. 어떤 정보를 보관하고 어떤 정보를 삭제할지 제어합니다. 이로 인해 상황이 개선되었으며, 그러나 그것은 해결되지 않았습니다. LSTM은 여전히 두 가지 근본적인 문제를 안고 있습니다.
RNN/LSTM의 한계
| 문제 | 설명 | 영향 |
|---|---|---|
| 순차성 | 각 토큰은 이전 토큰에 의존하므로 병렬화할 수 없습니다. | 긴 시퀀스에 대한 훈련 속도가 매우 느림 |
| 병목 | 모든 정보는 단일 캐리어를 통해 전달됩니다. | 100-200개 토큰을 초과하는 시퀀스로 인한 정보 손실 |
| 그라데이션 소멸 | 역전파 중에 기울기가 기하급수적으로 줄어듭니다. | 모델이 먼 관계를 학습하지 못함 |
각 토큰에 액세스할 수 있는 메커니즘이 필요했습니다. 곧장 에 중간 상태를 거치지 않고 시퀀스의 다른 토큰. 이 메커니즘과주목.
2. 주의력이란 무엇인가: 직관
Attention은 모델이 다음을 수행할 수 있도록 하는 메커니즘입니다. 자신의 집중 조심해 출력을 생성할 때 입력의 가장 관련성이 높은 부분에 대해 대신 전체 시퀀스를 단일 벡터로 압축하려면 주의가 연결을 생성해야 합니다. 각 출력 위치와 모든 입력 위치 사이에 직접 연결됩니다.
비유: 도서관에서 검색하기
당신이 서점에 있고 "트랜스포머의 역사"에 대한 정보를 찾고 있다고 상상해 보십시오. 당신은 염두에두고있는 것이 있습니다 요구 (쿼리). 각 책에는 제목 (Key)는 해당 내용을 설명합니다. 제목이 질문과 일치하면 추출하다 콘텐츠 (가치) 그 책의. 주의가 작용합니다 정확히 다음과 같습니다:
- 쿼리(Q): "내가 뭘 찾고 있는 거지?" - 현재 토큰이 묻는 질문
- 키(K): "이 아이템에는 무엇이 들어있나요?" - 시퀀스에 있는 각 토큰의 레이블
- 값(V): “여기에 정보가 있습니다” – 각 토큰의 실제 내용
메커니즘은 다음을 계산합니다. 호환성 점수 쿼리와 각 키 사이. 이 점수는 해당 가치에 얼마나 많은 관심을 기울여야 하는지를 결정합니다. 점수가 나오네요 소프트맥스를 통해 정규화하여 합이 1이 되는 가중치를 얻었고 최종 결과는 1입니다. 값의 가중 평균.
Token corrente: "dormiva"
Query di "dormiva": "Chi sta compiendo questa azione?"
Key Score Peso (softmax)
"Il" -----> 0.1 0.02
"gatto" -----> 4.8 0.65 <-- Alta attenzione!
"che" -----> 0.3 0.03
"era" -----> 0.2 0.02
"stato" -----> 0.1 0.02
"adottato" -----> 1.2 0.08
"..." -----> ... ...
"sul" -----> 2.1 0.12
"divano" -----> 0.8 0.06
Output = 0.02 * V("Il") + 0.65 * V("gatto") + 0.03 * V("che") + ...
Il modello ha imparato che "gatto" e il soggetto di "dormiva",
anche se sono separati da molti token.
3. 스케일링된 내적 주의: 공식
트랜스포머에 사용된 주의의 수학적 공식과 확장됨 내적 주의. 단순성과 계산면에서 우아합니다. 행렬 연산을 사용하여 효율적입니다.
주의 공식
어텐션(Q, K, V) = 소프트맥스(Q * K^T / sqrt(d_k)) * V
어디:
- Q (쿼리): 크기(n x d_k)의 행렬, 여기서 n은 토큰 수이고 d_k는 쿼리/키의 크기입니다.
- K (Key): 차원의 배열 (n x d_k)
- V (값): 차원(n x d_v)의 행렬, 여기서 d_v는 값의 차원입니다.
- d_k: 배율 인수로 사용되는 키 크기
- Q * K^T: 쿼리와 키 사이의 스칼라 곱(n x n 점수 행렬)
- /sqrt(d_k): 그라디언트를 안정화하기 위한 배율 인수
- 소프트맥스: 점수를 합이 1이 되는 가중치로 정규화합니다.
3.1 스케일링이 필요한 이유
요인이 없으면 sqrt(d_k), Q와 K 사이의 내적은 증가하는 값을 생성합니다.
치수 d_k에 비례합니다. d_k = 512이면 스칼라 곱은 다음과 같습니다.
매우 큰 값. 이 값이 소프트맥스에 도달하면 분포가 생성됩니다.
거의 1-핫(하나의 가중치는 1에 가깝고 다른 모든 가중치는 0에 가까움), 기울기가 매우 작습니다.
확장을 통해 이 문제를 방지할 수 있습니다.
Senza scaling (d_k = 512):
Score raw: [120.3, 115.8, 2.1, -5.4]
Softmax: [0.989, 0.011, 0.000, 0.000] <-- Quasi one-hot, gradienti ~0
Con scaling (/ sqrt(512) = / 22.6):
Score scaled: [5.32, 5.12, 0.09, -0.24]
Softmax: [0.44, 0.36, 0.10, 0.10] <-- Distribuzione morbida, gradienti sani
3.2 단계별: 주의력 계산
3개의 토큰 시퀀스와 d_k = 4를 사용하는 구체적인 수치 예를 살펴보겠습니다.
Sequenza: ["The", "cat", "sat"]
Step 1: Genera Q, K, V tramite proiezioni lineari
Q = X * W_Q K = X * W_K V = X * W_V
Q = [[1.0, 0.5, 0.3, 0.2], (The)
[0.8, 1.2, 0.1, 0.9], (cat)
[0.3, 0.4, 1.1, 0.6]] (sat)
K = [[0.9, 0.6, 0.4, 0.1],
[0.7, 1.1, 0.2, 0.8],
[0.4, 0.3, 1.0, 0.5]]
V = [[0.2, 0.8, 0.1, 0.5],
[0.9, 0.3, 0.7, 0.2],
[0.4, 0.6, 0.5, 0.8]]
Step 2: Calcola Q * K^T (matrice 3x3 di score)
Score[i][j] = dot(Q[i], K[j])
Scores = [[1.19, 1.37, 0.89],
[1.35, 1.77, 1.10],
[0.98, 1.15, 1.42]]
Step 3: Scala per sqrt(d_k) = sqrt(4) = 2
Scaled = [[0.60, 0.69, 0.45],
[0.68, 0.89, 0.55],
[0.49, 0.58, 0.71]]
Step 4: Applica softmax per riga
Weights = [[0.33, 0.36, 0.31], (The guarda The, cat, sat)
[0.32, 0.40, 0.28], (cat guarda The, cat, sat)
[0.29, 0.32, 0.39]] (sat guarda The, cat, sat)
Step 5: Moltiplica pesi per V
Output[0] = 0.33*V[0] + 0.36*V[1] + 0.31*V[2]
= [0.51, 0.56, 0.39, 0.48]
복잡성에 대한 주의
행렬 Q * K^T에는 차원이 있습니다. nxn, 여기서 n은 시퀀스의 길이입니다. n = 1000인 경우 행렬에는 1,000,000개의 요소가 있습니다. n = 100,000이면 100억 개의 요소가 있습니다. 이 O(n^2) 2차 복잡도는 Transformer의 주요 병목 현상입니다. Flash Attention 및 Sliding Window Attention과 같은 변형이 개발된 이유.
4. Multi-Head Attention: 여러 각도에서 시청
단일 주의 작업은 토큰 간의 한 가지 유형의 관계를 캡처합니다. 하지만 관계 순서대로 여러 가지가 있습니다: 구문 관계(주어-동사), 의미(동의어, 컨텍스트), 위치(인접 토큰) 및 기타 여러 가지. 거기 다중 헤드 주의 다양한 투영을 동시에 수행하여 이 문제를 해결합니다.
Input X (dimensione: n x d_model, es. n x 512)
|
+---> Head 1: Q1=X*Wq1, K1=X*Wk1, V1=X*Wv1 --> Attention(Q1,K1,V1) --> Z1
| (d_k = d_model/h = 64)
+---> Head 2: Q2=X*Wq2, K2=X*Wk2, V2=X*Wv2 --> Attention(Q2,K2,V2) --> Z2
|
+---> Head 3: Q3=X*Wq3, K3=X*Wk3, V3=X*Wv3 --> Attention(Q3,K3,V3) --> Z3
|
+---> ...
|
+---> Head 8: Q8=X*Wq8, K8=X*Wk8, V8=X*Wv8 --> Attention(Q8,K8,V8) --> Z8
|
v
Concatena: [Z1; Z2; Z3; ... Z8] (dimensione: n x d_model)
|
v
Proiezione finale: Concat * W_O (dimensione: n x d_model)
와 함께 h = 8 머리와 d_model = 512, 각 헤드는 하나씩 작동합니다.
차원 공간 d_k = d_v = 512 / 8 = 64. 총 계산 비용
헤드가 작동하기 때문에 풀 사이즈의 싱글 어텐션과 유사합니다.
더 작은 부분 공간에서 병렬로.
모든 머리가 배우는 것
경험적 연구에 따르면 서로 다른 머리는 서로 다른 패턴에 특화되어 있는 것으로 나타났습니다.
- 헤드 1: 주어-동사 관계를 배울 수 있다
- 머리 2: 상호참조관계(대명사와 그 선행사)를 배울 수 있습니다.
- 머리 3: 인접한 토큰(로컬 n-그램)에 집중할 수 있습니다.
- 헤드 4: 문장 간의 장거리 관계를 포착할 수 있습니다.
- 다른 머리: 구문 패턴, 개체, 담화 구조
멀티헤드 어텐션 포뮬러
멀티헤드(Q, K, V) = Concat(head_1, ..., head_h) * W_O
어디 head_i = 주의(Q * W_Qi, K * W_Ki, V * W_Vi)
원본 논문의 일반적인 매개변수: d_model = 512, h = 8, d_k = d_v = 64. 최신 모델에서는 d_model = 4096-8192, h = 32-128입니다.
5. Self-Attention: 다른 모든 사람을 감시하는 토큰
La 자기 관심 쿼리, 키, 값이 나오는 특정 사례 모두 같은 순서에서 나온 것입니다. 각 토큰은 자체 쿼리, 키 및 값을 생성하고 쿼리를 사용합니다. 다른 모든 토큰(자체 포함)의 키를 "쿼리"합니다.
Frase: "The cat sat on the mat"
Attention Matrix (ogni riga somma a 1.0):
The cat sat on the mat
The [0.15 0.25 0.10 0.05 0.15 0.30]
cat [0.10 0.20 0.35 0.05 0.05 0.25]
sat [0.05 0.40 0.15 0.20 0.05 0.15]
on [0.05 0.10 0.30 0.10 0.15 0.30]
the [0.20 0.15 0.05 0.10 0.10 0.40]
mat [0.10 0.15 0.15 0.25 0.15 0.20]
Osservazioni:
- "sat" presta molta attenzione a "cat" (0.40) --> soggetto-verbo
- "on" presta attenzione a "sat" (0.30) e "mat" (0.30) --> relazione spaziale
- "the" (seconda occorrenza) presta molta attenzione a "mat" (0.40) --> articolo-sostantivo
Self-attention은 트랜스포머의 핵심입니다. 그리고 모델을 구축할 수 있게 해주는 것은 무엇입니까? 문맥적 표현: 각 토큰의 표현이 내장되어 있습니다. 관련성에 따라 가중치가 부여된 전체 시퀀스의 정보입니다. "은행"이라는 단어는 "강둑"과 "은행 계좌"의 표현이 다릅니다. 왜냐하면 주변 토큰이 주의를 통해 표현에 영향을 줍니다.
디코더의 Masked Self-Attention
생성 모델(디코더)에서는 self-attention e 가장 무도회: 매 토큰은 이전 토큰만 볼 수 있고 미래 토큰은 볼 수 없습니다. 이것이 구현된다 소프트맥스 이전에 미래 토큰의 점수를 -무한대로 설정하여 생성 가중치는 0과 같습니다. 이것은 인과적인 관심 GPT, 라마에서 사용됨 그리고 모든 자기회귀 모델.
Mask per sequenza di 5 token (0 = visibile, -inf = mascherato):
t1 t2 t3 t4 t5
t1 [ 0 -inf -inf -inf -inf ]
t2 [ 0 0 -inf -inf -inf ]
t3 [ 0 0 0 -inf -inf ]
t4 [ 0 0 0 0 -inf ]
t5 [ 0 0 0 0 0 ]
Dopo la softmax:
t1 vede solo [t1]
t2 vede solo [t1, t2]
t3 vede solo [t1, t2, t3]
...e cosi via
6. Cross-Attention: 인코더와 디코더가 통신할 때
La 교차주의 (또는 인코더-디코더 주의) 및 메커니즘 디코더가 인코더 출력을 "감시"할 수 있습니다. 자기 관심과는 달리, 여기서 Q, K 및 V는 동일한 시퀀스에서 나오며 교차 어텐션에서는 쿼리가 옵니다. 디코더의 키/값과 인코더의 키/값입니다.
ENCODER (processa l'input, es. frase in italiano):
"Il gatto dorme" --> Encoder --> Rappresentazioni encoder (K_enc, V_enc)
DECODER (genera l'output, es. traduzione in inglese):
"The cat" --> Self-Attention mascherata --> Q_dec
CROSS-ATTENTION:
Q = Q_dec (dal decoder: "cosa sto cercando per generare il prossimo token?")
K = K_enc (dall'encoder: "cosa contiene ogni token dell'input?")
V = V_enc (dall'encoder: "ecco le informazioni dell'input")
Il decoder può "guardare" tutta la sequenza dell'encoder
per decidere quale token generare dopo.
교차주의는 아키텍처의 기본입니다. 인코더-디코더 사용됨 기계 번역(T5, mBART), 텍스트 요약 및 조건부 생성용. 예를 들어 T5에서는 인코더가 입력 텍스트를 처리하고 디코더가 텍스트를 생성합니다. 각 생성 단계에서 인코더를 참조하기 위해 교차 주의를 사용하여 출력합니다.
트랜스포머의 세 가지 유형의 주의
| 유형 | Q 소스 | 소스 K, V | 사용처 |
|---|---|---|---|
| 셀프 어텐션(인코더) | 인코더 입력 | 인코더 입력 | BERT 인코더, T5 인코더 |
| 가면을 쓴 자기 주의 | 입력 디코더 | 입력 디코더 | GPT, 라마, T5 디코더 |
| 크로스 어텐션 | 디코더 | 엔코더 출력 | T5 디코더, mBART |
7. 위치 인코딩: 변환기가 순서를 아는 방법
토큰을 순차적으로 처리하는 RNN과 달리 자기 관심 전자 순서와 관련하여 불변: 결과는 변하지 않습니다 입력 토큰을 순열하는 경우. "고양이가 생선을 먹습니다" 그리고 "고양이가 생선을 먹습니다" 추가 메커니즘 없이도 동일한 출력을 생성합니다. 그만큼 위치상의 인코딩 위치에 대한 정보를 추가하여 이 문제를 해결합니다. 각 토큰.
7.1 정현파 위치 인코딩(원본)
원본 논문에서는 정현파 함수를 사용하여 위치 인코딩을 생성합니다.
정현파 위치 인코딩 공식
PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))
어디 포스 시퀀스 e에서 토큰의 위치 i 그리고 크기. 짝수 위치는 사인을 사용하고, 홀수 위치는 코사인을 사용합니다. 주파수마다 다른데 크기를 통해 모델은 상대적인 위치 관계를 학습할 수 있습니다.
Posizione 0: [sin(0), cos(0), sin(0), cos(0), ...] = [0.00, 1.00, 0.00, 1.00, ...]
Posizione 1: [sin(1), cos(1), sin(0.01), cos(0.01)] = [0.84, 0.54, 0.01, 1.00, ...]
Posizione 2: [sin(2), cos(2), sin(0.02), cos(0.02)] = [0.91, -0.42, 0.02, 1.00, ...]
L'embedding finale di ogni token e:
token_embedding = word_embedding + positional_encoding
Le frequenze più basse (dimensioni alte) catturano posizioni globali.
Le frequenze più alte (dimensioni basse) catturano posizioni locali.
7.2 학습된 위치 인코딩
위치 정현파 인코딩 및 사용에 대한 대안 학습된 임베딩 (배운): 훈련 가능한 매개변수의 배열, 각 위치에 대해 한 행. 이 접근 방식은 BERT 및 GPT-2에서 사용됩니다. 장점은 모델이 학습할 수 있다는 것입니다. 특정 작업에 대한 최적의 위치 패턴. 단점은 길이가 길다는거 시퀀스의 최대값이며 훈련 시 고정됩니다.
위치 인코딩 비교
| 유형 | 장점 | 단점 | 다음에서 사용됨 |
|---|---|---|---|
| 정현파 | 추가 매개변수 없음, 더 긴 시퀀스로 일반화 | 작업에 최적화되지 않은 고정 패턴 | 오리지널 트랜스포머 |
| 배운 | 특정 작업에 최적화됨 | 고정된 최대 길이, 다중 매개변수 | BERT, GPT-2 |
| RoPE(로타리) | 상대 위치 캡처, 확장 가능 | 구현 복잡성 증가 | 라마, 미스트랄, GPT-NeoX |
| 구실 | 매개변수 없음, 좋은 추정 | 선형 편향으로 인해 제한될 수 있음 | 블룸, MPT |
8. 완전한 트랜스포머 아키텍처
모든 퍼즐 조각을 손에 넣었으므로 이제 Transformer 아키텍처를 조립할 수 있습니다. 완료. 원래 Transformer는 다음과 같이 구성됩니다. 스택 인코더 그리고 스택 디코더, 각각은 N개의 동일한 레이어로 구성됩니다(논문에서는 N = 6). 원본).
INPUT EMBEDDING + POSITIONAL ENCODING
|
+---------v-----------+
| ENCODER STACK | x N (6 nel paper originale)
| |
| +--Multi-Head-------+
| | Self-Attention |
| +------|------------+
| v
| +--Add & Norm-------+ (residual connection + layer norm)
| +------|------------+
| v
| +--Feed-Forward-----+ (2 layer lineari con ReLU/GELU)
| | Network | (d_model -> d_ff -> d_model)
| +------|------------+ (d_ff = 4 * d_model = 2048)
| v
| +--Add & Norm-------+
| +------|------------+
+---------|-----------+
|
| (K, V per cross-attention)
|
OUTPUT EMBEDDING + POSITIONAL ENCODING
|
+---------v-----------+
| DECODER STACK | x N
| |
| +--Masked Multi-----+
| | Head Self-Attn | (causal mask: vede solo il passato)
| +------|------------+
| v
| +--Add & Norm-------+
| +------|------------+
| v
| +--Cross-Attention--+ (Q dal decoder, K/V dall'encoder)
| +------|------------+
| v
| +--Add & Norm-------+
| +------|------------+
| v
| +--Feed-Forward-----+
| +------|------------+
| v
| +--Add & Norm-------+
| +------|------------+
+---------|-----------+
|
v
Linear + Softmax
|
v
Output Probabilities (vocabulario)
8.1 잔여 연결
각 하위 계층(주의 또는 피드포워드)에는 잔여 연결:
하위 레이어의 출력이 입력에 추가됩니다. 공식 e
output = LayerNorm(x + SubLayer(x)). 잔여 연결이 문제를 해결합니다.
딥 네트워크에서 그래디언트가 흐를 수 있게 하는 그래디언트 소실 문제
바로가기 연결을 통해 직접적으로.
8.2 피드포워드 네트워크
주의를 받은 후 각 토큰은 다음을 통과합니다. 피드포워드 네트워크 각 위치에 독립적으로 적용됩니다. 두 개의 선형변환으로 구성된다. 비선형 활성화(원본 논문에서는 ReLU, 모델에서는 GELU 또는 SwiGLU) 현대):
FFN(x) = W2 * 활성화(W1 * x + b1) + b2
내부 치수(d_ff)는 일반적으로 d_model의 4배입니다. d_model = 512인 경우, d_ff = 2048. Llama 3과 같은 최신 모델에서 d_ff는 d_model = 4096으로 14,336까지 올라갑니다.
8.3 레이어 정규화
La 레이어 정규화 차원에 따라 활성화를 정규화합니다. 기능(배치 아님). 훈련을 안정화하고 수렴을 가속화합니다. 원래 Transformer Post-LN에서는 (잔여 연결 후 정규화), 하지만 대부분의 현대 모델에서는 사전 LN (정규화 하위 계층 이전) 이는 훈련 중에 더 안정적입니다.
9. PyTorch 구현: 처음부터 Self-Attention
이론에서 코드로 넘어가겠습니다. 우리는 Scaled Dot-Product Attention을 구현하고 사전 구축된 모듈을 사용하지 않고 PyTorch에서 처음부터 Multi-Head Attention을 수행합니다.
9.1 스케일링된 내적 어텐션
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
def scaled_dot_product_attention(
query: torch.Tensor,
key: torch.Tensor,
value: torch.Tensor,
mask: torch.Tensor = None,
dropout: nn.Dropout = None
) -> tuple[torch.Tensor, torch.Tensor]:
"""
Scaled Dot-Product Attention.
Args:
query: (batch, heads, seq_len, d_k)
key: (batch, heads, seq_len, d_k)
value: (batch, heads, seq_len, d_v)
mask: (batch, 1, 1, seq_len) o (batch, 1, seq_len, seq_len)
dropout: modulo dropout opzionale
Returns:
output: (batch, heads, seq_len, d_v)
attention_weights: (batch, heads, seq_len, seq_len)
"""
d_k = query.size(-1)
# Step 1: Calcola gli score Q * K^T / sqrt(d_k)
scores = torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(d_k)
# Step 2: Applica la maschera (opzionale)
if mask is not None:
scores = scores.masked_fill(mask == 0, float('-inf'))
# Step 3: Softmax per ottenere i pesi di attention
attention_weights = F.softmax(scores, dim=-1)
# Step 4: Dropout opzionale sui pesi
if dropout is not None:
attention_weights = dropout(attention_weights)
# Step 5: Moltiplica pesi per Value
output = torch.matmul(attention_weights, value)
return output, attention_weights
9.2 다중 헤드 어텐션
class MultiHeadAttention(nn.Module):
"""
Multi-Head Attention implementata da zero.
Parametri:
d_model: dimensione del modello (es. 512)
num_heads: numero di teste di attention (es. 8)
dropout: tasso di dropout (es. 0.1)
"""
def __init__(self, d_model: int, num_heads: int, dropout: float = 0.1):
super().__init__()
assert d_model % num_heads == 0, \
f"d_model ({d_model}) deve essere divisibile per num_heads ({num_heads})"
self.d_model = d_model
self.num_heads = num_heads
self.d_k = d_model // num_heads # dimensione per testa
# Proiezioni lineari per Q, K, V e output
self.w_q = nn.Linear(d_model, d_model, bias=False)
self.w_k = nn.Linear(d_model, d_model, bias=False)
self.w_v = nn.Linear(d_model, d_model, bias=False)
self.w_o = nn.Linear(d_model, d_model, bias=False)
self.dropout = nn.Dropout(dropout)
def split_heads(self, x: torch.Tensor) -> torch.Tensor:
"""
Riorganizza il tensore da (batch, seq_len, d_model)
a (batch, num_heads, seq_len, d_k).
"""
batch_size, seq_len, _ = x.size()
x = x.view(batch_size, seq_len, self.num_heads, self.d_k)
return x.transpose(1, 2) # (batch, heads, seq_len, d_k)
def forward(
self,
query: torch.Tensor,
key: torch.Tensor,
value: torch.Tensor,
mask: torch.Tensor = None
) -> torch.Tensor:
"""
Forward pass.
Per Self-Attention: query = key = value = X
Per Cross-Attention: query = decoder, key = value = encoder
"""
batch_size = query.size(0)
# 1. Proiezioni lineari
q = self.w_q(query) # (batch, seq_len, d_model)
k = self.w_k(key)
v = self.w_v(value)
# 2. Dividi in teste
q = self.split_heads(q) # (batch, heads, seq_len, d_k)
k = self.split_heads(k)
v = self.split_heads(v)
# 3. Scaled Dot-Product Attention
attn_output, attn_weights = scaled_dot_product_attention(
q, k, v, mask=mask, dropout=self.dropout
)
# 4. Concatena le teste
# (batch, heads, seq_len, d_k) -> (batch, seq_len, d_model)
attn_output = attn_output.transpose(1, 2).contiguous()
attn_output = attn_output.view(batch_size, -1, self.d_model)
# 5. Proiezione finale
output = self.w_o(attn_output)
return output
9.3 사용예
# Configurazione
batch_size = 2
seq_len = 10
d_model = 512
num_heads = 8
# Crea il modulo
mha = MultiHeadAttention(d_model=d_model, num_heads=num_heads)
# Input random (simula una sequenza di token embeddings)
x = torch.randn(batch_size, seq_len, d_model)
# Self-Attention (query = key = value)
output = mha(query=x, key=x, value=x)
print(f"Input shape: {x.shape}") # torch.Size([2, 10, 512])
print(f"Output shape: {output.shape}") # torch.Size([2, 10, 512])
# Causal mask per decoder (triangolare inferiore)
causal_mask = torch.tril(torch.ones(seq_len, seq_len))
causal_mask = causal_mask.unsqueeze(0).unsqueeze(0) # (1, 1, seq_len, seq_len)
# Masked Self-Attention
output_masked = mha(query=x, key=x, value=x, mask=causal_mask)
print(f"Masked output shape: {output_masked.shape}")
# Cross-Attention (query dal decoder, key/value dall'encoder)
encoder_output = torch.randn(batch_size, 20, d_model) # sequenza encoder più lunga
decoder_input = torch.randn(batch_size, seq_len, d_model)
cross_attn_output = mha(
query=decoder_input,
key=encoder_output,
value=encoder_output
)
print(f"Cross-attention shape: {cross_attn_output.shape}") # [2, 10, 512]
10. 주목의 현대적 변형
표준 주의력의 O(n^2) 2차 복잡성은 다음의 개발에 동기를 부여했습니다. 수많은 최적화된 변형. 이러한 변형은 현대 모델의 기본입니다 10만 개에서 100만 개가 넘는 토큰까지 컨텍스트를 관리합니다.
10.1 플래시 어텐션(v1, v2, v3)
플래시 주의Tri Dao와 동료들이 개발한 는 수학을 바꾸지 않습니다. 주목을 받지만 하드웨어 수준에서 구현을 근본적으로 최적화합니다. 아이디어 키를 사용하고 관심 점수의 완전한 n x n 행렬을 구체화하는 것을 피하십시오. 대신 한 가지 접근 방식을 사용하는 GPU 메모리(HBM) 타일을 붙인 일하는 사람 완전히 SRAM(고속 온칩 메모리)에 있습니다.
플래시 어텐션의 진화
| 버전 | 년도 | 주요 혁신 | 성능 |
|---|---|---|---|
| 플래시 주의 1 | 2022년 | 타일링 + 융합 커널, IO 인식 | 표준 대비 2~4배 속도 향상 |
| 플래시 어텐션 2 | 2023년 | 병렬성 향상, 의사소통 감소 | v1보다 2배 더 향상됨 |
| 플래시 어텐션 3 | 2024년 | Hopper GPU, FP8, 워프 전문화의 비동기성 | H100에서 최대 740TFLOPS(FP16), FP8에서 1.2PFLOPS |
Flash Attention 3은 NVIDIA Hopper GPU(H100/H200)의 특정 특성을 활용합니다. 비동기 Tensor Core와 TMA(Tensor Memory Accelerator) 간 오버레이 계산 및 데이터 전송, 워프 전문화 인터리빙을 위해 matmul 및 Softmax 작업의 최적 e FP8 블록 양자화 순진한 FP8 구현보다 수치 오류가 2.6배 낮습니다. 플래시 Attention은 이제 PyTorch, Hugging Face Transformers, vLLM 및 TensorRT-LLM에 통합되었습니다.
10.2 다중 쿼리 어텐션(MQA)
2019년 Shazeer가 제안한 다중 쿼리 주의 대폭 감소 추론 중 KV 캐시에 필요한 메모리입니다. 따로 세트로 준비하는 것보다 MQA는 각 헤드의 키와 값을 공유합니다. 하나의 K와 V의 집합 다른 쿼리를 유지하는 헤드.
Multi-Head Attention (MHA) - Standard:
Head 1: Q1, K1, V1 | KV Cache per head: d_k * seq_len * 2
Head 2: Q2, K2, V2 | KV Cache totale: h * d_k * seq_len * 2
... | Con h=32, d_k=128, seq=4096:
Head h: Qh, Kh, Vh | = 32 * 128 * 4096 * 2 = 33.5 MB per layer
Multi-Query Attention (MQA):
Head 1: Q1 \
Head 2: Q2 |--- K_shared, V_shared
... | KV Cache totale: d_k * seq_len * 2
Head h: Qh / = 128 * 4096 * 2 = 1.05 MB per layer (32x meno!)
10.3 그룹 쿼리 어텐션(GQA)
GQA, Ainslie et al.에 의해 소개되었습니다. 2023년에는 MHA와 MQA가 절충됩니다. 모든 헤드(MQA) 간에 단일 K/V 세트를 공유하거나 각 헤드마다 하나씩 보유하는 대신 head(MHA), GQA 그룹이 다음으로 향합니다. g 그룹, 각 그룹과 함께 K/V 세트를 공유합니다. g = 1이면 MQA를 얻고, g = h이면 MHA를 얻습니다.
Esempio: 8 query heads, 2 KV groups (g=2)
Gruppo 1: Q1, Q2, Q3, Q4 condividono K1, V1
Gruppo 2: Q5, Q6, Q7, Q8 condividono K2, V2
KV Cache: g * d_k * seq_len * 2 = 2 * 128 * 4096 * 2 = 2.1 MB
(16x meno di MHA, ma solo 2x più di MQA)
Modelli che usano GQA:
- Llama 2 (70B): 8 KV heads, 64 query heads
- Llama 3: GQA con rapporto 8:1
- Mistral 7B: 8 KV heads, 32 query heads
주의 변형 비교
| 변종 | KV 헤드 | KV 캐시 메모리 | 품질 | 모델 |
|---|---|---|---|---|
| MHA | h (모두) | 최고 | 개선하다 | BERT, GPT-2, GPT-3 |
| GQA | g(그룹) | h/d 감소 | MHA와 거의 동일 | 라마 2/3, 미스트랄 |
| MQA | 1 | 최소한의 | 약간의 감소 | PaLM, 팔콘 |
10.4 슬라이딩 윈도우 주의
La 슬라이딩 윈도우 주의, Mistral 및 Longformer에서 사용됨, 한계 각 위치에 대한 w 토큰의 로컬 창에 주의하세요. 계산하는 대신 전체 시퀀스(O(n^2))에 대한 주의, 각 토큰은 이전 w 토큰만 볼 수 있습니다. 복잡성을 O(n * w)로 줄입니다.
Sequenza: t1 t2 t3 t4 t5 t6 t7 t8
Attention di t5 (window=3): vede solo [t3, t4, t5]
Attention di t8 (window=3): vede solo [t6, t7, t8]
Attention Matrix (1 = visibile, 0 = mascherato):
t1 t2 t3 t4 t5 t6 t7 t8
t1 [ 1 0 0 0 0 0 0 0 ]
t2 [ 1 1 0 0 0 0 0 0 ]
t3 [ 1 1 1 0 0 0 0 0 ]
t4 [ 0 1 1 1 0 0 0 0 ]
t5 [ 0 0 1 1 1 0 0 0 ]
t6 [ 0 0 0 1 1 1 0 0 ]
t7 [ 0 0 0 0 1 1 1 0 ]
t8 [ 0 0 0 0 0 1 1 1 ]
L'informazione NON si perde: attraverso più layer stacked,
l'informazione di t1 può raggiungere t8 per propagazione.
Con L layer e window w, la reception field effettiva e L * w.
10.5 Ring Attention 및 PagedAttention
매우 긴 컨텍스트(1백만 개 이상의 토큰)의 경우 추가 혁신이 나타났습니다.
- 링 주의: 여러 GPU에 주의 계산을 분산합니다. 링으로 구성되었습니다. 각 GPU는 시퀀스의 세그먼트에 대한 주의를 계산합니다. 그리고 그 결과를 다음 GPU로 전달합니다. RingX(2025)는 94% 효율성을 달성합니다. 1백만 개의 토큰 시퀀스를 갖춘 최대 4096개의 GPU.
- 페이지 주의: 가상 메모리 관리에서 영감을 얻었습니다. 운영 체제에서는 KV 캐시를 연속되지 않은 블록(페이지)에 할당하여 메모리 단편화. 이는 vLLM의 기초이며 최대 배치 크기를 허용합니다. 76배 더 높습니다.
- FlexAttention(PyTorch): 여러 가지를 지원하는 통합 API 미만의 관심 변형(GQA, 인과관계, 슬라이딩 윈도우, PagedAttention) 전용 구현에 비해 5% 오버헤드.
11. 응용: 실제 변압기 아키텍처
Transformer 아키텍처는 세 가지 주요 모델 제품군을 생성했습니다. 주의를 다르게 사용하는 것입니다.
11.1 인코더 전용: BERT 및 파생 상품
인코더 전용 모델 사용 양방향 자기 관심: 각 토큰 이전 토큰과 이전 토큰 모두 시퀀스의 다른 모든 토큰을 볼 수 있습니다. 후속 것. 이는 언어 이해 작업에 이상적입니다.
BERT(변압기의 양방향 인코더 표현)
- 사전 훈련: 마스크된 언어 모델(MLM) + 다음 문장 예측
- 주목: 양방향 셀프 어텐션(전체 시퀀스 보기)
- 작업: 분류, 개체명 인식, 질문 답변
- 변형: 로버타, 앨버트, 데버타, 디스틸버트
11.2 디코더 전용: GPT 및 LLM 계열
디코더 전용 모델 사용 가면을 쓴 자기 주의(인과관계): 각 토큰 이전 토큰만 볼 수 있습니다. 자동 회귀 텍스트 생성에 최적화되어 있습니다.
디코더 전용 모델
| 모델 | 매개변수 | 주의 변형 | 컨텍스트 창 |
|---|---|---|---|
| GPT-3 | 175B | 표준 MHA | 2K-4K 토큰 |
| GPT-4 | ~1.8T(MoE) | GQA (추정) | 128K 토큰 |
| 라마 3 405B | 405B | GQA + RoPE | 128K 토큰 |
| 미스트랄 7B | 7.3B | GQA + 슬라이딩 윈도우 | 32K 토큰 |
| 클로드(인류) | 게시되지 않음 | 게시되지 않음 | 200K 토큰 |
11.3 인코더-디코더: T5 및 Seq2Seq 모델
인코더-디코더 모델은 세 가지 유형의 주의를 모두 사용합니다. 자기 관심 인코더의 양방향, 디코더의 마스크드 셀프 어텐션 e 교차주의 디코더와 인코더 사이. 이는 다음과 같은 작업에 이상적입니다. 입력을 출력(번역, 요약, 질문 답변)으로 변환합니다.
인코더-디코더 모델
- T5: "텍스트-텍스트 전송 변환기" - 각 작업은 텍스트-인-텍스트-아웃으로 공식화됩니다.
- 바트: 생성 및 이해를 위한 노이즈 제거 자동 인코더
- 엠바트: 번역을 위한 BART 다국어
- 플랜-T5: T5는 명령어 튜닝을 지시받았다.
11.4 비전 변환기(ViT)
주의는 텍스트에만 국한되지 않습니다. 그만큼 비전 트랜스포머 적용하다 이미지에 대한 self-attention, 이미지를 패치(예: 16x16 픽셀)로 분할 e 각 패치를 "토큰"으로 취급합니다. 이는 주의가 모든 유형의 순차 데이터에 적용할 수 있는 일반적인 메커니즘입니다.
Immagine 224x224 pixel
|
v
Dividi in patch 16x16: (224/16)^2 = 196 patch
|
v
Ogni patch -> flatten -> proiezione lineare -> patch embedding
|
v
[CLS] + 196 patch embeddings + positional encoding
|
v
Transformer Encoder (self-attention su 197 token)
|
v
[CLS] token -> classificazione dell'immagine
결론 및 다음 단계
이 기사에서 우리는 어텐션 메커니즘의 전체 내용을 다루었습니다. RNN의 장기 종속성, 쿼리-키-값의 직관, 공식 Scaled Dot-Product Attention부터 Multi-Head Attention, 아키텍처까지 트랜스포머를 완성하세요. 우리는 PyTorch e에서 처음부터 셀프 어텐션을 구현했습니다. 수백만 개의 토큰이 포함된 모델을 가능하게 하는 현대적인 변형을 탐색했습니다. 맥락의.
Attention은 모든 현대 딥러닝이 구축되는 기본 벽돌입니다. 작동 방식을 이해하면 일부 최적화가 작동하는 이유를 이해할 수 있습니다. 특정 모델이 다른 모델보다 빠른 이유와 올바른 아키텍처를 선택하는 방법 귀하의 사용 사례에 맞게.
기억해야 할 주요 개념
- 주목 병목 현상 없이 토큰 쌍 간의 직접 연결을 허용합니다.
- 스케일링(sqrt(d_k)) Softmax의 불안정한 기울기를 방지합니다.
- 멀티헤드 추가 비용 없이 다양한 관계를 동시에 캡처
- 자기 관심 상황에 맞는 표현을 생성합니다. 크로스 어텐션 인코더와 디코더를 연결
- 위치 인코딩 주문 정보 제공(정현파, 학습, RoPE)
- 플래시 주의 수학을 변경하지 않고 하드웨어 구현을 최적화합니다.
- GQA 품질(MHA)과 효율성(MQA) 간의 최적의 절충안
에서 다음 기사 시리즈 중 우리는 미세 조정 LoRA, QLoRA 및 어댑터를 갖춘 변압기: 사전 훈련된 모델을 피팅하는 방법 매개변수의 작은 부분만 변경하여 특정 작업에 적용 GPU와 메모리 비용이 엄청나게 듭니다.
추가 리소스
- 원본 논문: "주의가 필요한 전부입니다"(Vaswani 외, 2017)
- 플래시 주의 3: “비동시성과 낮은 정밀도를 통한 빠르고 정확한 주의”(Dao et al., 2024)
- GQA 논문: "GQA: 일반화된 다중 쿼리 변환기 모델 훈련"(Ainslie et al., 2023)
- 그림으로 표현된 트랜스포머: Jay Alammar의 비주얼 가이드
- PyTorch 문서: 최적화된 구현을 위한 torch.nn.MultiheadAttention
- 포옹하는 얼굴: 실제 예제가 포함된 Transformers 문서







