01 - Mechanismus pozornosti v Transformers: Kompletní průvodce
V roce 2017 dokument Google Brain s názvem „Attention Is All You Need“ změnil věci navždy oblast hlubokého učení. Autoři, Vaswani a kolegové, navrhli architekturu zcela založené na mechanismu tzv Pozor, odstranění sítí rekurentní (RNN) a konvoluční systémy, které do té doby dominovaly. Výsledek byl architektura Transformátory, dnes na základně GPT-4, Claude, Llama 3, BERT, T5, Vision Transformers a téměř každý hraniční model.
Pochopení mechanismu pozornosti není akademické cvičení: je to základ, na kterém je stavějí techniky, jako je jemné ladění LoRA, kvantizace, prořezávání a nasazení okrajová zařízení, všechna témata, kterým se budeme v této sérii věnovat. Bez pochopení solidní pochopení toho, jak funguje pozornost, každá následná optimalizace zůstává černou skříňkou.
V tomto prvním článku seriálu Pokročilé Deep Learning a Edge Deployment, prozkoumáme pozornost do hloubky: od počáteční intuice po matematický vzorec, od implementace v PyTorch po moderní varianty jako Flash Attention 3 a Grouped-Query Pozor.
Přehled série
| # | Položka | Soustředit |
|---|---|---|
| 1 | Jste zde - Mechanismus pozornosti v Transformers | Samostatná pozornost, vícehlavá, kompletní architektura |
| 2 | Jemné doladění pomocí LoRA, QLoRA a adaptérů | Parametrově efektivní jemné doladění |
| 3 | Kvantování modelů | INT8, INT4, GPTQ, AWQ |
| 4 | Prořezávání a komprese | Redukce parametrů, destilace |
| 5 | Destilace znalostí | Učitel-žák, předávání znalostí |
| 6 | Ollama a LLM Local | Lokální inference, optimalizace |
| 7 | Vision Transformer | ViT, DINO, klasifikace obrázků |
| 8 | Edge Deployment | ONNX, TensorRT, mobilní zařízení |
| 9 | NAS a AutoML | Hledání neuronové architektury |
| 10 | Benchmarking a optimalizace | Profilování, metriky, ladění |
Co se naučíte
- protože RNN a LSTM nebyly dostatečné pro dlouhé sekvence
- Intuice za mechanismem pozornosti: dotaz, klíč a hodnota
- Kompletní vzorec Scaled Dot-Product Attention
- Jak funguje Multi-Head Attention a proč potřebujete více hlav
- Rozdíl mezi sebepozorností a křížovou pozorností
- Jak poziční kódování řeší problém s objednávkou
- Kompletní architektura transformátoru: kodér a dekodér
- Praktická implementace v PyTorch, řádek po řádku
- Moderní varianty: Flash Attention 3, GQA, Sliding Window Attention
- Skutečné architektury: GPT (pouze dekodér), BERT (pouze kodér), T5 (kodér-dekodér)
1. Problém sekvence: Před Attention
Abychom pochopili, proč byla pozornost revolucí, musíme vycházet z modelů, které to jsou předcházeli ji. Hluboké učení pro sekvence (text, zvuk, časové řady) bylo Dominují dvě architektury: RNN (rekurentní neuronové sítě) e le LSTM (dlouhá krátkodobá paměť).
1.1 RNN a sekvenční úzké hrdlo
RNN zpracovávají sekvence jeden token po druhém a předávají skrytý stav od jednoho časového kroku k dalšímu. Každý token aktualizuje skrytý stav, ve kterém se chová „paměť“ dosud viděné sekvence.
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.
Toto je problém dlouhodobé závislosti. Ve větě jako „Kočka, která byla před třemi lety adoptovaná z útulku a žila šťastně s rodinou, spal na gauči, RNN musí připojit "kočka" „prospal“ desítky přechodných žetonů. Skrytý stav, komprimovaný do vektoru s pevnou velikostí nevyhnutelně ztratí starší informace.
1.2 LSTM: Zlepšení, nikoli řešení
LSTM zavedly hradlový mechanismus (vstupní brána, zapomenutá brána, výstupní brána). kontrolovat, které informace uchovávat a které zlikvidovat. Tím se situace zlepšila, ale nevyřešilo to. LSTM stále trpí dvěma základními problémy:
Omezení RNN/LSTM
| Problém | Popis | Dopad |
|---|---|---|
| Sekvence | Každý token závisí na předchozím: nelze jej paralelizovat | Velmi pomalý trénink na dlouhých sekvencích |
| Úzké místo | Všechny informace procházejí jediným nosičem | Ztráta informací se sekvencemi > 100-200 tokenů |
| Gradient mizí | Gradienty se během zpětného šíření exponenciálně zmenšují | Modelka se nedokáže naučit vzdálené vztahy |
Potřebovali jsme mechanismus, který by umožnil přístup každému tokenu přímo a jakýkoli jiný token v sekvenci, aniž by bylo nutné procházet mezistavy. Tento mechanismus aPozor.
2. Co je pozornost: Intuice
Pozornost je mechanismus, který modelu umožňuje soustředit své vlastní buďte opatrní na nejdůležitější části vstupu při generování výstupu. Místo toho Chcete-li komprimovat celou sekvenci do jediného vektoru, pozornost vytvoří spojení přímo mezi každou výstupní polohou a všemi vstupními pozicemi.
Analogie: Vyhledávání v knihovně
Představte si, že jste v knihkupectví a hledáte informace o „historii Transformers“. Jednu máš na mysli žádost (Dotazy). Každá kniha má a titul (Klíč), který popisuje jeho obsah. Když název odpovídá vaší otázce, extrahovat obsah (Hodnota) této knihy. Pozornost funguje přesně takhle:
- Dotaz (Q): "Co hledám?" - otázka, kterou klade aktuální token
- Klíč (K): "Co tato položka obsahuje?" - označení každého tokenu v pořadí
- Hodnota (V): „Tady jsou informace“ – skutečný obsah každého tokenu
Mechanismus počítá a skóre kompatibility mezi dotazem a každým klíčem. Toto skóre určuje, jakou pozornost je třeba věnovat odpovídající hodnotě. Skóre přichází normalizováno pomocí softmax, aby se získaly váhy, jejichž součet je 1, a konečný výsledek je jedna vážený průměr hodnot.
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. Scaled Dot-Product Pozor: Vzorec
Matematická formulace pozornosti použitá v Transformers and the Měřítko Dot-Product Attention. Je elegantní ve své jednoduchosti a výpočtech efektivní díky použití maticových operací.
Vzorec pozornosti
Pozor (Q, K, V) = softmax(Q * K^T / sqrt(d_k)) * V
Kde:
- Q (Dotaz): matice velikosti (n x d_k), kde n je počet tokenů a d_k je velikost dotazů/klíčů
- K (Klíč): pole dimenzí (n x d_k)
- V (Value): matice dimenze (n x d_v), kde d_v je dimenze hodnot
- d_k: velikost klíče, používá se jako faktor měřítka
- Q * K^T: skalární součin mezi dotazem a klíčem (matice skóre n x n)
- / sqrt(d_k): faktor měřítka pro stabilizaci gradientů
- softmax: normalizuje skóre na váhy, jejichž součet je 1
3.1 Proč je škálování nezbytné
Bez faktoru sqrt(d_k), bodový součin mezi Q a K vytváří rostoucí hodnoty
úměrně rozměru d_k. S d_k = 512 mohou skalární součiny dosáhnout
velmi velké hodnoty. Když tyto hodnoty skončí v softmaxu, vytvoří distribuce
téměř jednožhavé (jedna závaží blízko 1, všechny ostatní blízko 0), s extrémně malými gradienty.
Škálování tomuto problému předchází.
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 Krok za krokem: Výpočet pozornosti
Podívejme se na konkrétní numerický příklad se sekvencí 3 tokenů a 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]
Pozor na složitost
Matice Q * K^T má rozměr n x n, kde n je délka posloupnosti. Při n = 1000 má matice 1 000 000 prvků. S n = 100 000 má 10 miliard prvků. Tato kvadratická složitost O(n^2) je hlavním úzkým hrdlem Transformers a důvod, proč byly vyvinuty varianty jako Flash Attention a Sliding Window Attention.
4. Pozor na více hlav: Sledujte z více úhlů
Jedna operace pozornosti zachycuje jeden typ vztahu mezi tokeny. Ale vztahy v posloupnosti existuje více: syntaktické vztahy (subjekt-sloveso), sémantické (synonyma, kontext), poziční (sousední tokeny) a mnoho dalších. Tam Vícehlavá pozornost řeší tento problém prováděním pozornosti paralelně s různými projekcemi.
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)
Con h = 8 hlavy a d_model = 512, každá hlava pracuje na jedné
rozměrový prostor d_k = d_v = 512 / 8 = 64. Celkové výpočetní náklady
Je to podobné jako u jediné pozornosti s plnou velikostí, protože hlavy fungují
paralelně na menších podprostorech.
Co se naučí každá hlava
Empirický výzkum ukázal, že různé hlavy se specializují na různé vzory:
- Hlava 1: Mohl se naučit vztahy podmět-sloveso
- Hlava 2: Dokáže se naučit koreferenční vztahy (zájmena a jejich předchůdce)
- Hlava 3: Mohlo by se zaměřit na sousední tokeny (místní n-gramy)
- Hlava 4: Mohlo by to zachytit dlouhodobé vztahy mezi větami
- Další hlavy: Syntaktické vzorce, entity, struktura diskurzu
Formule pro vícehlavou pozornost
MultiHead(Q, K, V) = Concat(head_1, ..., head_h) * W_O
Kde head_i = Pozor (Q * W_Qi, K * W_Ki, V * W_Vi)
Typické parametry v původním článku: d_model = 512, h = 8, d_k = d_v = 64. V moderních modelech: d_model = 4096-8192, h = 32-128.
5. Sebepozornost: Token, který sleduje všechny ostatní
La sebepozornost a konkrétní případ, odkud pochází dotaz, klíč a hodnota všechny ze stejné sekvence. Každý token generuje svůj vlastní dotaz, klíč a hodnotu a používá dotaz "dotazovat se" na klíče všech ostatních tokenů (včetně sebe sama).
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
Sebepozornost je srdcem Transformerů. A co umožňuje model postavit kontextové reprezentace: Reprezentace každého vloženého tokenu informace z celé sekvence, vážené podle relevance. Slovo "banka" bude mít odlišná reprezentace v "bankovním účtu" a "bankovním účtu", protože okolní tokeny ovlivňovat jeho reprezentaci prostřednictvím pozornosti.
Maskovaná sebepozornost v dekodérech
V generativních modelech (dekodérech) je sebepozornost např maškaráda: každý token může vidět pouze předchozí tokeny, nikoli budoucí. Toto je implementováno nastavení skóre budoucích tokenů na -nekonečno před softmaxem, produkování váhy rovné nule. Toto je kauzální pozornost používá se v GPT, Llama a všechny autoregresní modely.
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. Křížová pozornost: Když kodéry a dekodéry komunikují
La křížová pozornost (neboli pozornost kodéru-dekodéru) a mechanismus, který umožňuje dekodéru „sledovat“ výstup kodéru. Na rozdíl od sebepozornosti, kde Q, K a V pocházejí ze stejné sekvence, při křížové pozornosti přicházejí dotazy z dekodéru a klíč/hodnota z kodéru.
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.
Křížová pozornost je v architektuře zásadní kodér-dekodér použitý pro strojový překlad (T5, mBART), sumarizaci textu a podmíněné generování. Například v T5 kodér zpracovává vstupní text a dekodér text generuje výstup pomocí křížové pozornosti ke kontrole kodéru v každém kroku generování.
Tři typy pozornosti v transformátorech
| Typ | Zdroj Q | Zdroj K, V | Kde to použít |
|---|---|---|---|
| Vlastní pozornost (kodér) | Vstupy kodéru | Vstupy kodéru | Kodér BERT, kodér T5 |
| Maskovaná sebepozornost | Vstupní dekodér | Vstupní dekodér | GPT, Llama, dekodér T5 |
| Křížová pozornost | Dekodéry | Výstup kodéru | T5 dekodér, mBART |
7. Poziční kódování: Jak Transformers Know Order
Na rozdíl od RNN, které zpracovávají tokeny v sekvenčním pořadí, sebepozornost e invariantní vzhledem k řádu: výsledek se nemění pokud permutujete vstupní tokeny. „Kočka jí ryby“ a „kočka jí ryby“ produkovaly by stejný výstup bez dalšího mechanismu. The poziční kódování řeší tento problém přidáním informací o umístění každý token.
7.1 Sinusové polohové kódování (originální papír)
Původní článek používá sinusové funkce pro generování polohových kódování:
Vzorce pro sinusové polohové kódování
PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))
Kde poz a pozici tokenu v sekvenci e i a velikost. Sudé pozice používají sinus, liché pozice používají kosinus. U každého jiná frekvence velikost umožňuje modelu naučit se relativní polohové vztahy.
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 Naučené poziční kódování
Alternativa k pozičnímu sinusovému kódování a použití naučená vložení (naučil se): pole trénovatelných parametrů, jeden řádek pro každou pozici. Tento přístup se používá v BERT a GPT-2. Výhodou je, že se model může učit optimální poziční vzory pro konkrétní úkol. Nevýhodou je ta délka maximum sekvence a fixované při tréninku.
Porovnání pozičního kódování
| Typ | Výhody | Nevýhody | Používá se v |
|---|---|---|---|
| Sinusový | Žádné další parametry, zobecňuje se na delší sekvence | Pevné vzory, neoptimalizované pro daný úkol | Originální Transformer |
| Učený | Optimalizováno pro konkrétní úkol | Pevná maximální délka, více parametrů | BERT, GPT-2 |
| RoPE (rotační) | Zachyťte relativní polohy, rozšiřitelné | Větší složitost implementace | Lama, Mistral, GPT-NeoX |
| Alibi | Žádné parametry, dobrá extrapolace | Lineární zkreslení může být omezující | BLOOM, MPT |
8. Kompletní architektura transformátoru
Se všemi dílky skládačky v ruce nyní můžeme sestavit architekturu Transformeru kompletní. Původní Transformer se skládá z a zásobníkové kodéry a a zásobníkový dekodér, z nichž každá se skládá z N stejných vrstev (N = 6 v papíru původní).
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 Zbytková připojení
Každá podvrstva (pozor nebo dopředná) má a zbytkové spojení:
výstup podvrstvy se přidá ke vstupu. The formula e
output = LayerNorm(x + SubLayer(x)). Zbytková připojení řeší problém
problém mizejícího gradientu v hlubokých sítích, který umožňuje gradientům proudit
přímo prostřednictvím zkratkových spojení.
8.2 Dopředná síť
Po pozornosti projde každý žeton a dopředná síť aplikované nezávisle na každé pozici. Skládá se ze dvou lineárních transformací s nelineární aktivací (ReLU v originálním papíru, GELU nebo SwiGLU v modelech moderní):
FFN(x) = W2 * aktivace (W1 * x + b1) + b2
Vnitřní rozměr (d_ff) je typicky 4 krát d_model. S d_model = 512, d_ff = 2048. V moderních modelech, jako je Llama 3, se d_ff zvyšuje na 14 336 s d_model = 4096.
8.3 Normalizace vrstev
La Normalizace vrstev normalizuje aktivace podél dimenze funkcí (nikoli šarže). Stabilizuje trénink a urychluje konvergenci. V původním transformátoru je použit Post-LN (normalizace po zbytkovém připojení), ale většina moderních modelů používá Před LN (normalizace před podvrstvou), která je během tréninku stabilnější.
9. Implementace PyTorch: Vlastní pozornost od nuly
Pojďme od teorie ke kódu. Zavedeme Scaled Dot-Product Attention and Multi-Head Attention od nuly v PyTorch, bez použití předpřipravených modulů.
9.1 Scaled Dot-Product Attention
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 Pozor na více hlav
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 Příklad použití
# 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. Moderní variace pozornosti
O(n^2) kvadratická složitost standardní pozornosti motivovala vývoj četné optimalizované varianty. Tyto variace jsou zásadní pro moderní modely správa kontextů od 100 000 do více než 1 milionu tokenů.
10.1 Flash Attention (v1, v2, v3)
Blesková pozornost, vyvinutý Tri Dao a kolegy, nemění matematiku pozornosti, ale radikálně optimalizuje jeho implementaci na hardwarové úrovni. Myšlenka klíč a vyvarujte se zhmotnění kompletní matice n x n skóre pozornosti v Paměť GPU (HBM), místo toho použijte jeden přístup kachlová kdo pracuje zcela v SRAM (rychlá paměť na čipu).
Evoluce Flash Attention
| Verze | Rok | Klíčová inovace | Výkon |
|---|---|---|---|
| Pozor na blesk 1 | 2022 | Tiling + tavené jádro, IO-awareness | 2-4x zrychlení oproti standardu |
| Pozor na blesk 2 | 2023 | Vylepšený paralelismus, méně komunikace | 2x dále než v1 |
| Pozor na blesk 3 | 2024 | Asynchronie na GPU Hopper, FP8, specializace warp | Až 740 TFLOPS (FP16) na H100, 1,2 PFLOPS s FP8 |
Flash Attention 3 využívá specifické vlastnosti GPU NVIDIA Hopper (H100/H200): asynchronie mezi Tensor Core a TMA (Tensor Memory Accelerator) k překrytí výpočet a přenos dat, warp specializace pro prokládání optimální operace matmul a softmax, např Kvantování bloku FP8 s numerickou chybou 2,6krát nižší než naivní implementace FP8. Flash Pozornost je nyní integrována do PyTorch, Hugging Face Transformers, vLLM a TensorRT-LLM.
10.2 Multi-Query Attention (MQA)
Navrhl Shazeer v roce 2019 Multi-Query Attention drasticky snižuje paměť potřebná pro mezipaměť KV během vyvozování. Namísto samostatné sady klíče a hodnoty pro každou hlavu, sdílí MQA a singl soubor K a V mezi všemi hlavy, udržování různých dotazů.
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 Grouped-Query Attention (GQA)
GQA, představený Ainslie et al. v roce 2023 a kompromis mezi MHA a MQA. Místo sdílení jedné sady K/V mezi všemi hlavami (MQA) nebo mít jednu pro každou head (MHA), GQA seskupuje hlavy do g skupiny, s každou skupinou, která sdílí sadu K/V. Při g = 1 získáme MQA, při 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
Porovnání variant Attention
| Varianta | KV hlavy | KV Cache paměť | kvalitní | Modelky |
|---|---|---|---|---|
| MHA | h (vše) | Maximum | zlepšit | BERT, GPT-2, GPT-3 |
| GQA | g (skupiny) | snížení h/d | Téměř stejné jako MHA | Lama 2/3, Mistral |
| MQA | 1 | Minimální | Mírný pokles | PaLM, Falcon |
10.4 Posuvné okno Pozor
La Posuvné okno Pozor, používané v Mistral a Longformer, limity věnujte pozornost místnímu oknu w tokenů pro každou pozici. Místo počítání pozornost na celou sekvenci (O(n^2)), každý token vidí pouze předchozí w tokeny, snížení složitosti na 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 a PagedAttention
Pro velmi dlouhé kontexty (přes 1 milion tokenů) se objevily další inovace:
- Pozor na prsten: distribuuje výpočet pozornosti mezi více GPU organizovaný v ringu. Každý GPU vypočítává pozornost na segmentu sekvence a předá výsledky dalšímu GPU. RingX (2025) dosahuje 94% účinnosti až 4096 GPU s 1 milionem sekvencí tokenů.
- PagedAttention: inspirované správou virtuální paměti operační systémy, alokuje KV cache do nesouvislých bloků (stránek), čímž eliminuje fragmentace paměti. Je základem vLLM a umožňuje velikosti dávek až 76krát vyšší.
- FlexAttention (PyTorch): jednotné API, které podporuje několik varianty pozornosti (GQA, kauzální, posuvné okno, PagedAttention) s méně než 5% režie ve srovnání s vyhrazenými implementacemi.
11. Aplikace: Transformer Architectures v praxi
Architektura Transformer dala vzniknout třem hlavním rodinám modelů, z nichž každá která využívá pozornost jinak.
11.1 Pouze kodér: BERT a deriváty
Používají se pouze modely s kodérem obousměrná sebepozornost: každý žeton může vidět všechny ostatní žetony v posloupnosti, jak ty předchozí, tak ty následující. Díky tomu jsou ideální pro úkoly s porozuměním jazyku.
BERT (Obousměrné reprezentace kodéru od Transformers)
- Předtrénink: Maskovaný jazykový model (MLM) + predikce další věty
- Pozor: Obousměrná sebepozornost (vidí celou sekvenci)
- úkoly: Klasifikace, rozpoznávání pojmenovaných entit, odpovídání na otázky
- varianty: RoBERTa, ALBERT, DeBERTa, DistilBERT
11.2 Pouze dekodér: GPT a rodina LLM
Používají se pouze modely s dekodérem maskovaná sebepozornost (kauzální): každý žeton vidí pouze předchozí tokeny. Jsou optimalizovány pro autoregresivní generování textu.
Modely pouze s dekodérem
| Model | Parametry | Pozor varianta | Kontextové okno |
|---|---|---|---|
| GPT-3 | 175B | Standardní MHA | 2K-4K tokenů |
| GPT-4 | ~1,8T (MoE) | GQA (odhad) | 128 tisíc tokenů |
| Lama 3 405B | 405B | GQA + RoPE | 128 tisíc tokenů |
| Mistral 7B | 7.3B | GQA + posuvné okno | 32 tisíc tokenů |
| Claude (Antropický) | Nezveřejněno | Nezveřejněno | 200 tisíc tokenů |
11.3 Kodér-dekodér: Modely T5 a Seq2Seq
Modely kodér-dekodér využívají všechny tři typy pozornosti: sebepozornost obousměrný v kodéru, maskovaná sebepozornost v dekodéru e křížová pozornost mezi dekodérem a kodérem. Jsou ideální pro úkoly, které transformují vstup na výstup (překlad, shrnutí, zodpovězení otázek).
Modely kodér-dekodér
- T5: "Text-to-Text Transfer Transformer" - každý úkol je formulován jako text-in-text-out
- BART: Odšumovací autokodéry pro generování a porozumění
- mBART: BART vícejazyčný pro překlad
- Flan-T5: T5 instruován s instrukčním laděním
11.4 Vision Transformer (ViT)
Pozornost se neomezuje pouze na text. THE Vision Transformer aplikovat sebepozorování na obrázky, rozdělení obrázku na pole (např. 16x16 pixelů) např. zacházet s každým patchem jako s "tokenem". To ukázalo, že pozornost je a obecný mechanismus použitelný na jakýkoli typ sekvenčních dat.
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
Závěry a další kroky
V tomto článku jsme pokryli celý oblouk mechanismu pozornosti: od problému dlouhodobých závislostí v RNN, na intuici Query-Key-Value, na vzorci od Scaled Dot-Product Attention přes Multi-Head Attention až po architekturu Kompletní Transformer. Implementovali jsme sebepozorování od nuly v PyTorch e prozkoumal moderní varianty, které umožňují modely s miliony žetonů kontextu.
Pozornost je základní cihla, na které je postaveno veškeré moderní hluboké učení. Pochopení toho, jak to funguje, vám umožní pochopit, proč některé optimalizace fungují, proč jsou některé modely rychlejší než jiné a jak vybrat správnou architekturu pro váš případ použití.
Klíčové pojmy k zapamatování
- Pozor umožňuje přímé spojení mezi libovolným párem tokenů, bez překážek
- Měřítko (sqrt(d_k)) zabraňuje nestabilním sklonům v softmax
- Vícehlavý zachytit různé vztahy paralelně bez dalších nákladů
- Sebepozornost vytváří kontextové reprezentace; Křížová pozornost připojte kodér a dekodér
- Poziční kódování poskytuje informace o objednávce (sinusový, naučený, RoPE)
- Blesková pozornost optimalizovat implementaci hardwaru bez změny matematiky
- GQA a optimální kompromis mezi kvalitou (MHA) a účinností (MQA)
Nel další článek série, prozkoumáme dolaďování transformátorů s LoRA, QLoRA a adaptéry: Jak nasadit předem trénované modely na konkrétní úkoly změnou pouze malého zlomku parametrů, snížením Drastické náklady na GPU a paměť.
Další zdroje
- Původní papír: „Pozornost je vše, co potřebujete“ (Vaswani et al., 2017)
- Flash Attention 3: „Rychlá a přesná pozornost s asynchronií a nízkou přesností“ (Dao et al., 2024)
- Dokumenty GQA: „GQA: Training Generalized Multi-Query Transformer Models“ (Ainslie et al., 2023)
- Ilustrovaný transformátor: Vizuální průvodce od Jaye Alammara
- Dokumentace PyTorch: torch.nn.MultiheadAttention pro optimalizované implementace
- Objímání obličeje: Dokumentace transformátorů s praktickými příklady







