01 - Mechanizm uwagi w transformatorach: kompletny przewodnik
W 2017 r. artykuł Google Brain zatytułowany „Uwaga to wszystko, czego potrzebujesz” zmienił sytuację na zawsze dziedzinie głębokiego uczenia się. Autorzy, Vaswani i współpracownicy, zaproponowali architekturę w całości oparty na mechanizmie tzw uwaga, eliminując sieci dominujące do tego czasu systemy rekurencyjne (RNN) i splotowe. Rezultat był architektura Transformatory, dzisiaj u podstawy GPT-4, Claude, Lama 3, BERT, T5, Vision Transformers i prawie każdy model z pogranicza.
Zrozumienie mechanizmu uwagi nie jest zadaniem akademickim: jest to podstawa, na której się opiera opracowują techniki takie jak dostrajanie, kwantyzacja, przycinanie i wdrażanie LoRA urządzenia brzegowe, wszystkie tematy omówimy w tej serii. Bez zrozumienia solidnego zrozumienia, jak działa uwaga, każda kolejna optymalizacja pozostaje czarną skrzynką.
W pierwszym artykule z tej serii Zaawansowane głębokie uczenie się i wdrażanie brzegowe, zbadamy uwagę dogłębnie: od początkowej intuicji do wzoru matematycznego, od implementacji w PyTorch po nowoczesne warianty, takie jak Flash Attention 3 i Grouped-Query Uwaga.
Przegląd serii
| # | Przedmiot | Centrum |
|---|---|---|
| 1 | Jesteś tutaj - Mechanizm uwagi w transformatorach | Samouważna, wielogłowicowa, kompletna architektura |
| 2 | Dostrajanie za pomocą LoRA, QLoRA i adapterów | Efektywne dostrajanie parametrów |
| 3 | Kwantyzacja modeli | INT8, INT4, GPTQ, AWQ |
| 4 | Przycinanie i kompresja | Redukcja parametrów, destylacja |
| 5 | Destylacja wiedzy | Nauczyciel-uczeń, transfer wiedzy |
| 6 | Ollama i lokalny LLM | Wnioskowanie lokalne, optymalizacja |
| 7 | Transformator wizji | ViT, DINO, klasyfikacja obrazu |
| 8 | Wdrożenie brzegowe | ONNX, TensorRT, urządzenia mobilne |
| 9 | NAS i AutoML | Wyszukiwanie architektury neuronowej |
| 10 | Benchmarking i optymalizacja | Profilowanie, metryki, tuning |
Czego się nauczysz
- ponieważ RNN i LSTM nie były wystarczające dla długich sekwencji
- Intuicja stojąca za mechanizmem uwagi: zapytanie, klucz i wartość
- Kompletna formuła Scaled Dot-Product Uwaga
- Jak działa funkcja Multi-Head Attention i dlaczego potrzebujesz wielu głowic
- Różnica między samouważnością a krzyżową uwagą
- Jak kodowanie pozycyjne rozwiązuje problem kolejności
- Kompletna architektura Transformera: koder i dekoder
- Praktyczna implementacja w PyTorch, linia po linii
- Nowoczesne warianty: Flash Attention 3, GQA, Sliding Window Uwaga
- Prawdziwe architektury: GPT (tylko dekoder), BERT (tylko koder), T5 (koder-dekoder)
1. Problem sekwencji: przed uwagą
Aby zrozumieć, dlaczego uwaga była rewolucją, musimy zacząć od modeli, które to zrobiły poprzedzili ją. Głębokie uczenie się sekwencji (tekst, dźwięk, szeregi czasowe) było zdominowany przez dwie architektury: RNN (rekurencyjne sieci neuronowe) e le LSTM (pamięć długoterminowa).
1.1 RNN i sekwencyjne wąskie gardło
Sieci RNN przetwarzają sekwencje po jednym tokenie na raz, przekazując stan ukryty z jednego kroku czasowego do następnego. Każdy token aktualizuje stan ukryty, w którym działa „pamięć” sekwencji widzianej dotychczas.
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.
To jest problem zależności dalekiego zasięgu. W zdaniu typu „Kot, który został adoptowany ze schroniska trzy lata temu i żył szczęśliwie z rodziną, spał na kanapie”, RNN musi połączyć „kota” „przespał” dziesiątki żetonów pośrednich. Stan ukryty skompresowany do wektora o stałym rozmiarze, nieuchronnie traci starsze informacje.
1.2 LSTM: ulepszenie, a nie rozwiązanie
LSTM wprowadziły mechanizm bramkowy (bramka wejściowa, bramka zapominająca, bramka wyjściowa). kontrolować, które informacje zachować, a które odrzucić. Poprawiło to sytuację, ale to nie rozwiązało problemu. LSTM nadal borykają się z dwoma podstawowymi problemami:
Ograniczenia RNN/LSTM
| Problem | Opis | Uderzenie |
|---|---|---|
| Sekwencja | Każdy token zależy od poprzedniego: nie można go zrównoleglić | Bardzo powolny trening w długich sekwencjach |
| Szyjka | Wszystkie informacje przechodzą przez jednego przewoźnika | Utrata informacji przy sekwencjach > 100-200 tokenów |
| Zanikanie gradientu | Gradienty kurczą się wykładniczo podczas propagacji wstecznej | Model nie uczy się odległych relacji |
Potrzebowaliśmy mechanizmu, który umożliwiłby dostęp każdemu tokenowi bezpośrednio a dowolny inny żeton w sekwencji, bez konieczności przechodzenia przez stany pośrednie. Ten mechanizm iuwaga.
2. Czym jest uwaga: intuicja
Uwaga jest mechanizmem, który pozwala modelowi na to skoncentruj się bądź ostrożny na najbardziej odpowiednich częściach danych wejściowych podczas generowania danych wyjściowych. Zamiast tego aby skompresować całą sekwencję w jeden wektor, uwaga tworzy połączenie bezpośrednio pomiędzy każdą pozycją wyjściową i wszystkimi pozycjami wejściowymi.
Analogia: Wyszukiwanie w bibliotece
Wyobraź sobie, że jesteś w księgarni i szukasz informacji na temat „historii Transformersów”. Masz jednego na myśli wniosek (Pytania). Każda książka posiada tytuł (Klucz), który opisuje jego zawartość. Jeśli tytuł pasuje do Twojego pytania, wyodrębnij treść (Wartość) tej książki. Uwaga działa dokładnie tak:
- Zapytanie (Q): „Czego szukam?” - pytanie zadawane przez bieżący token
- Klucz (K): „Co zawiera ten przedmiot?” - etykieta każdego żetonu w sekwencji
- Wartość (V): „Oto informacja” – rzeczywista zawartość każdego tokena
Mechanizm oblicza a wynik zgodności pomiędzy zapytaniem a każdym kluczem. Wynik ten określa, ile uwagi należy poświęcić odpowiedniej Wartości. Przychodzą wyniki znormalizowane za pomocą oprogramowania Softmax w celu uzyskania wag, których suma wynosi 1, a wynik końcowy wynosi jeden średnia ważona Wartości.
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. Skalowana uwaga iloczynu skalarnego: wzór
Matematyczne sformułowanie uwagi użyte w Transformersach i Skalowane Uwaga na produkt punktowy. Jest elegancki w swojej prostocie i obliczeniowo efektywne dzięki wykorzystaniu operacji macierzowych.
Formuła uwagi
Uwaga(Q, K, V) = softmax(Q * K^T / sqrt(d_k)) * V
Gdzie:
- Q (Zapytanie): macierz rozmiaru (n x d_k), gdzie n to liczba tokenów, a d_k to rozmiar zapytania/klucza
- K (Klucz): tablica wymiarów (n x d_k)
- V (Wartość): macierz wymiarów (n x d_v), gdzie d_v jest wymiarem wartości
- d_k: rozmiar klucza, używany jako współczynnik skalowania
- Q*K^T: iloczyn skalarny między zapytaniem a kluczem (macierz wyników n x n)
- / sqrt(d_k): współczynnik skalowania stabilizujący gradienty
- softmax: normalizuje wyniki w wagi, których suma wynosi 1
3.1 Dlaczego skalowanie jest konieczne
Bez czynnika sqrt(d_k), iloczyn skalarny między Q i K daje rosnące wartości
proporcjonalnie do wymiaru d_k. Przy d_k = 512 iloczyny skalarne mogą osiągnąć
bardzo duże wartości. Kiedy te wartości znajdą się w softmax, powstają rozkłady
prawie jednogorący (jedna waga bliska 1, wszystkie inne bliskie 0), z wyjątkowo małymi gradientami.
Skalowanie zapobiega temu problemowi.
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 po kroku: Obliczanie uwagi
Zobaczmy konkretny przykład liczbowy z sekwencją 3 tokenów i 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]
Uwaga na złożoność
Macierz Q*K^T ma wymiar n x n, gdzie n jest długością ciągu. Przy n = 1000 macierz ma 1 000 000 elementów. Przy n = 100 000 ma 10 miliardów elementów. Ta złożoność kwadratowa O(n^2) jest głównym wąskim gardłem Transformersów i powód, dla którego opracowano warianty takie jak Flash Attention i Sliding Window Attention.
4. Uwaga wielogłowa: oglądaj pod wieloma kątami
Pojedyncza operacja uwagi przechwytuje jeden typ relacji między tokenami. Ale relacje w ciągu występuje wiele relacji: syntaktycznych (podmiot-czasownik), semantycznych (synonimy, kontekst), pozycyjne (sąsiadujące tokeny) i wiele innych. Tam Uwaga wielogłowa rozwiązuje ten problem, wykonując uwagę równolegle z różnymi projekcjami.
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 głowy i d_model = 512, każda głowa pracuje na jednej
przestrzeń wymiarowa d_k = d_v = 512 / 8 = 64. Całkowity koszt obliczeniowy
Podobnie jest z pojedynczą uwagą w pełnym rozmiarze, ponieważ działają głowy
równolegle na mniejszych podprzestrzeniach.
Czego uczy się każda głowa
Badania empiryczne wykazały, że różni szefowie specjalizują się w różnych wzorach:
- Głowa 1: Mógłby nauczyć się relacji podmiot-orzeczenie
- Głowa 2: Mógłby nauczyć się relacji koreferencyjnych (zaimki i ich poprzedniki)
- Głowa 3: Może skupić się na sąsiednich tokenach (lokalne n-gramy)
- Głowa 4: Może uchwycić długoterminowe relacje między zdaniami
- Inne głowy: Wzory syntaktyczne, podmioty, struktura dyskursu
Formuła uwagi wielogłowicowej
MultiHead(Q, K, V) = Concat(head_1, ..., head_h) * W_O
Gdzie head_i = Uwaga(Q * W_Qi, K * W_Ki, V * W_Vi)
Typowe parametry w pracy oryginalnej: d_model = 512, h = 8, d_k = d_v = 64. W nowoczesnych modelach: d_model = 4096-8192, h = 32-128.
5. Samouważność: znak, który obserwuje wszystkich innych
La samouważność oraz konkretny przypadek, z którego pochodzą Zapytanie, Klucz i Wartość wszystko z tej samej sekwencji. Każdy token generuje własne zapytanie, klucz i wartość oraz wykorzystuje zapytanie do „odpytywania” kluczy wszystkich innych tokenów (w tym samego siebie).
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
Samouważność jest sercem Transformers. I co pozwala na zbudowanie modelu reprezentacje kontekstowe: Reprezentacja każdego osadzonego tokenu informacje z całej sekwencji, ważone według trafności. Słowo „bank” będzie miało inna reprezentacja w „brzegu rzeki” i „rachunku bankowym” ze względu na otaczające tokeny wpływać na jego reprezentację poprzez uwagę.
Zamaskowana samouważność w dekoderach
W modelach generatywnych (dekoderach) samouważność (np maskarada: każdy token może zobaczyć tylko poprzednie tokeny, a nie przyszłe. Jest to realizowane ustawienie wyników przyszłych tokenów na -nieskończoność przed softmaxem, produkcja wagi równe zeru. To jest przyczynowa uwaga używany w GPT, lamie i wszystkie modele autoregresyjne.
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. Wzajemna uwaga: kiedy kodery i dekodery komunikują się
La uwaga krzyżowa (lub uwaga kodera-dekodera) i mechanizm, który umożliwia dekoderowi „obserwowanie” sygnału wyjściowego kodera. W odróżnieniu od samouważności, gdzie Q, K i V pochodzą z tej samej sekwencji, w wyniku wzajemnej uwagi przychodzą Zapytania z dekodera i klucz/wartość z kodera.
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.
W architekturze najważniejsza jest wzajemna uwaga koder-dekoder używany do tłumaczenia maszynowego (T5, mBART), podsumowania tekstu i generowania warunkowego. Na przykład w T5 koder przetwarza tekst wejściowy, a dekoder generuje tekst wyjściowy, korzystając ze wzajemnej uwagi i konsultując się z koderem na każdym etapie generowania.
Trzy rodzaje uwagi w transformatorach
| Typ | Źródło Q | Źródło K, V | Gdzie go używać |
|---|---|---|---|
| Samouważność (koder) | Wejścia enkodera | Wejścia enkodera | Enkoder BERT, enkoder T5 |
| Zamaskowana samouważność | Dekoder wejściowy | Dekoder wejściowy | Dekoder GPT, Lama, T5 |
| Uwaga krzyżowa | Dekodery | Wyjście enkodera | Dekoder T5, mBART |
7. Kodowanie pozycyjne: skąd transformatory znają porządek
W przeciwieństwie do sieci RNN, które przetwarzają tokeny w kolejności sekwencyjnej, samouważność tj niezmienna ze względu na porządek: wynik się nie zmienia jeśli permutujesz tokeny wejściowe. „Kot zjada rybę” i „Kot zjada rybę” zapewniałyby taki sam wynik bez dodatkowego mechanizmu. The pozycyjny kodowanie rozwiązuje ten problem poprzez dodanie informacji o lokalizacji każdy żeton.
7.1 Sinusoidalne kodowanie pozycyjne (papier oryginalny)
W oryginalnej pracy wykorzystano funkcje sinusoidalne do generowania kodowań pozycyjnych:
Wzory sinusoidalnego kodowania pozycyjnego
PE(pos, 2i) = sin(pos / 10000^(2i/d_model))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))
Gdzie poz oraz położenie żetonu w sekwencji e i i rozmiar. W pozycjach parzystych używany jest sinus, w pozycjach nieparzystych cosinus. Dla każdego inna częstotliwość rozmiar pozwala modelowi nauczyć się względnych zależności pozycyjnych.
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 Nauczone kodowanie pozycyjne
Alternatywa dla pozycyjnego kodowania sinusoidalnego i zastosowania wyuczone osadzania (nauczył się): tablica parametrów, które można wytrenować, jeden wiersz dla każdej pozycji. Podejście to jest stosowane w BERT i GPT-2. Zaletą jest to, że model może się uczyć optymalne wzorce pozycji dla konkretnego zadania. Wadą jest długość maksimum sekwencji i ustalane podczas treningu.
Porównanie kodowania pozycyjnego
| Typ | Zalety | Wady | Używany w |
|---|---|---|---|
| Sinusoidalny | Żadnych dodatkowych parametrów, uogólnia się na dłuższe sekwencje | Naprawione wzorce, niezoptymalizowane pod kątem zadania | Oryginalny transformator |
| Uczony | Zoptymalizowany pod kątem konkretnego zadania | Naprawiono maksymalną długość, wiele parametrów | BERT, GPT-2 |
| Lina (obrotowa) | Przechwytuj względne pozycje, z możliwością przedłużenia | Większa złożoność wdrożenia | Lama, Mistral, GPT-NeoX |
| Alibi | Brak parametrów, dobra ekstrapolacja | Odchylenie liniowe może być ograniczające | BLOOM, MPT |
8. Kompletna architektura transformatora
Mając wszystkie elementy układanki w ręku, możemy teraz złożyć architekturę Transformera kompletny. Oryginalny Transformer składa się z: kodery stosowe i a dekoder stosowy, każda składająca się z N identycznych warstw (N = 6 w papierze oryginał).
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 Pozostałe połączenia
Każda podwarstwa (uwaga lub sprzężenie zwrotne) ma resztkowe połączenie:
wyjście podwarstwy jest dodawane do wejścia. Formuła E
output = LayerNorm(x + SubLayer(x)). Pozostałe połączenia rozwiązują problem
problem zanikającego gradientu w głębokich sieciach, umożliwiający przepływ gradientów
bezpośrednio poprzez połączenia skrótowe.
8.2 Sieć przekazująca
Po uwagi, każdy żeton przechodzi przez a sieć wyprzedzająca stosowane niezależnie dla każdego stanowiska. Składa się z dwóch transformacji liniowych z aktywacją nieliniową (ReLU w pracy oryginalnej, GELU lub SwiGLU w modelach nowoczesny):
FFN(x) = W2 * aktywacja(W1 * x + b1) + b2
Wymiar wewnętrzny (d_ff) jest zazwyczaj 4-krotnością d_model. Przy d_model = 512, d_ff = 2048. W nowoczesnych modelach, takich jak Lama 3, d_ff wzrasta do 14 336 przy d_model = 4096.
8.3 Normalizacja warstw
La Normalizacja warstw normalizuje aktywacje wzdłuż wymiaru funkcji (nie partii). Stabilizuje trening i przyspiesza konwergencję. W oryginale zastosowano transformator Post-LN (normalizacja po połączeniu resztkowym), ale większość nowoczesnych modeli używa Przed LN (normalizacja przed podwarstwą), która jest stabilniejsza podczas treningu.
9. Implementacja PyTorch: Samouwaga od podstaw
Przejdźmy od teorii do kodu. Wdrożymy Scaled Dot-Product Attention i Multi-Head Attention od podstaw w PyTorch, bez użycia gotowych modułów.
9.1 Skalowana uwaga iloczynu skalowanego
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 Uwaga wielogłowa
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 Przykład użycia
# 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. Współczesne odmiany uwagi
Motywacją do rozwoju była złożoność kwadratowa O(n^2) standardowej uwagi liczne zoptymalizowane warianty. Te różnice są fundamentalne dla nowoczesnych modeli zarządzanie kontekstami od 100 tys. do ponad 1 miliona tokenów.
10.1 Błysk Uwaga (v1, v2, v3)
Błyskawiczna uwaga, opracowany przez Tri Dao i współpracowników, nie zmienia matematyki uwagi, ale radykalnie optymalizuje jego wdrożenie na poziomie sprzętowym. Pomysł klucz i unikaj materializowania pełnej matrycy n x n wyników uwagi Zamiast tego stosuje się jedno podejście do pamięci GPU (HBM). taflowy kto pracuje w całości w SRAM (szybka pamięć na chipie).
Ewolucja uwagi błyskowej
| Wersja | Rok | Kluczowa innowacja | Wydajność |
|---|---|---|---|
| Błysk Uwaga 1 | 2022 | Kafelkowanie + połączone jądro, świadomość IO | Przyspieszenie 2-4x w porównaniu ze standardem |
| Błysk Uwaga 2 | 2023 | Lepsza równoległość, mniejsza komunikacja | 2x dalej niż v1 |
| Błysk Uwaga 3 | 2024 | Asynchronia na GPU Hopper, FP8, specjalizacja warp | Do 740 TFLOPS (FP16) w H100, 1,2 PFLOPS w FP8 |
Flash Attention 3 wykorzystuje specyficzne cechy procesorów graficznych NVIDIA Hopper (H100/H200): asynchronia pomiędzy Tensor Core i TMA (Tensor Memory Accelerator), aby się nałożyć obliczenia i transfer danych, specjalizacja warpowa do przeplatania optymalne operacje matmul i softmax, np Kwantyzacja bloków FP8 z błędem numerycznym 2,6 razy mniejszym niż w przypadku naiwnej realizacji 8PR. Błysk Attention jest teraz zintegrowany z PyTorch, Hugging Face Transformers, vLLM i TensorRT-LLM.
10.2 Uwaga wielozapytaniowa (MQA)
Zaproponowany przez Shazeera w 2019 r Uwaga dotycząca wielu zapytań drastycznie się zmniejsza pamięć potrzebna dla pamięci podręcznej KV podczas wnioskowania. Zamiast mieć oddzielny zestaw klucza i wartości dla każdej głowy, udziały MQA a pojedynczy zestaw K i V spośród wszystkich głowy, utrzymując różne zapytania.
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 Uwaga dotycząca zapytań grupowych (GQA)
GQA, wprowadzone przez Ainslie i in. w 2023 r. oraz kompromis pomiędzy MHA i MQA. Zamiast dzielić jeden zestaw K/V pomiędzy wszystkimi głowami (MQA) lub mieć po jednym dla każdego head (MHA), w które grupy GQA wchodzą grupy gr, z każdą grupą dzieli zestaw K/V. Przy g = 1 otrzymujemy MQA, przy g = h otrzymujemy 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
Porównanie wariantów Uwaga
| Wariant | Głowice KV | Pamięć podręczna KV | jakość | Modele |
|---|---|---|---|---|
| MHA | hala) | Maksymalny | Poprawić | BERT, GPT-2, GPT-3 |
| GQA | g (grupy) | redukcja h/d | Prawie równy MHA | Lama 2/3, Mistral |
| MQA | 1 | Minimalny | Nieznaczny spadek | PaLM, Sokół |
10.4 Uwaga na okno przesuwne
La Uwaga na przesuwane okno, stosowany w Mistral i Longformer, ogranicza zwróć uwagę na lokalne okno żetonów w dla każdej pozycji. Zamiast kalkulować uwaga na całą sekwencję (O(n^2)), każdy token widzi tylko poprzednie w tokeny, redukując złożoność do 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 Dzwonienie i PagedAttention
W przypadku bardzo długich kontekstów (ponad 1 milion tokenów) pojawiły się kolejne innowacje:
- Uwaga na pierścień: rozdziela obliczenia uwagi na wiele procesorów graficznych zorganizowane w pierścień. Każdy procesor graficzny oblicza uwagę na segmencie sekwencji i przekazuje wyniki do następnego procesora graficznego. RingX (2025) osiąga skuteczność na poziomie 94%. do 4096 procesorów graficznych z 1 milionem sekwencji tokenów.
- PagedUwaga: inspirowany zarządzaniem pamięcią wirtualną systemów operacyjnych, przydziela pamięć podręczną KV do nieciągłych bloków (stron), eliminując fragmentacja pamięci. Stanowi podstawę vLLM i pozwala na wielkość partii do 76 razy więcej.
- FlexAttention (PyTorch): ujednolicony interfejs API obsługujący kilka warianty uwagi (GQA, przyczynowy, przesuwane okno, PagedAttention) z mniej niż 5% narzutu w porównaniu do dedykowanych wdrożeń.
11. Zastosowania: Architektury transformatorów w praktyce
Architektura Transformera dała początek trzech głównym rodzinom modeli, każda który inaczej wykorzystuje uwagę.
11.1 Tylko koder: BERT i instrumenty pochodne
Używane są modele wyposażone wyłącznie w enkoder dwukierunkowa samouważność: każdy żeton widzi wszystkie pozostałe żetony w sekwencji, zarówno poprzednie, jak i te kolejne. Dzięki temu idealnie nadają się do zadań związanych ze zrozumieniem języka.
BERT (Dwukierunkowe reprezentacje enkodera z transformatorów)
- Przed treningiem: Model języka maskowanego (MLM) + przewidywanie następnego zdania
- Uwaga: Dwukierunkowa samouważność (widzi całą sekwencję)
- Zadania: Klasyfikacja, rozpoznawanie podmiotów nazwanych, odpowiadanie na pytania
- Warianty: ROBERTA, ALBERT, DeBERTA, DistilBERT
11.2 Tylko dekoder: GPT i rodzina LLM
Używane są modele wyłącznie z dekoderem zamaskowana samouważność (przyczynowa): każdy żeton widzi tylko poprzednie tokeny. Są zoptymalizowane pod kątem autoregresyjnego generowania tekstu.
Modele wyłącznie z dekoderem
| Model | Parametry | Wariant uwaga | Okno kontekstowe |
|---|---|---|---|
| GPT-3 | 175B | Standardowe MHA | Tokeny 2K-4K |
| GPT-4 | ~1,8T (MoE) | GQA (szacunkowo) | 128 tys. tokenów |
| Lama 3 405B | 405B | GQA + lina | 128 tys. tokenów |
| Mistrala 7B | 7.3B | GQA + okno przesuwne | 32 tys. tokenów |
| Claude (antropiczny) | Nieopublikowane | Nieopublikowane | 200 tys. tokenów |
11.3 Koder-dekoder: modele T5 i Seq2Seq
Modele kodera-dekodera wykorzystują wszystkie trzy typy uwagi: samouważność dwukierunkowy w koderze, zamaskowana samouwaga w dekoderze e uwaga krzyżowa pomiędzy dekoderem a koderem. Idealnie nadają się do zadań, które przekształcają dane wejściowe w dane wyjściowe (tłumaczenie, podsumowanie, udzielenie odpowiedzi na pytania).
Modele kodera-dekodera
- T5: „Transformator transferu tekstu na tekst” – każde zadanie jest sformułowane w formie tekstu w tekście
- BART: Odszumianie autoenkoderów w celu generowania i zrozumienia
- mBART: BART wielojęzyczny do tłumaczenia
- Flan-T5: T5 otrzymał instrukcje dotyczące strojenia instrukcji
11.4 Transformator wizyjny (ViT)
Uwaga nie ogranicza się do tekstu. TO Transformator wizji zastosować samouważność wobec obrazów, dzielenie obrazu na plamy (np. 16x16 pikseli) e traktując każdą łatkę jako „żeton”. To pokazało, że uwaga jest ogólny mechanizm mający zastosowanie do dowolnego typu danych sekwencyjnych.
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
Wnioski i dalsze kroki
W tym artykule omówiliśmy cały mechanizm uwagi: od problemu długoterminowych zależności w RNN, do intuicji Zapytanie-Klucz-Wartość, do wzoru od skalowanej uwagi na produkt punktowy, przez uwagę wielogłowicową, aż po architekturę Kompletny Transformator. Samouważność wdrożyliśmy od podstaw w PyTorch e zbadał nowoczesne odmiany, które umożliwiają tworzenie modeli z milionami tokenów kontekstu.
Uwaga jest podstawową cegłą, na której zbudowane jest całe współczesne głębokie uczenie się. Zrozumienie, jak to działa, pozwala zrozumieć, dlaczego niektóre optymalizacje działają, dlaczego niektóre modele są szybsze od innych i jak wybrać odpowiednią architekturę dla Twojego przypadku użycia.
Kluczowe pojęcia do zapamiętania
- Uwaga umożliwia bezpośrednie połączenia pomiędzy dowolną parą tokenów, bez wąskich gardeł
- Skalowanie (sqrt(d_k)) zapobiega niestabilnym gradientom w softmax
- Wiele głowic uchwyć różne relacje równolegle bez dodatkowych kosztów
- Samouważność tworzy reprezentacje kontekstowe; Uwaga krzyżowa podłącz koder i dekoder
- Kodowanie pozycyjne dostarcza informacji o zamówieniu (sinusoidalny, wyuczony, RoPE)
- Błyskawiczna uwaga zoptymalizuj implementację sprzętu bez zmiany matematyki
- GQA oraz optymalny kompromis pomiędzy jakością (MHA) a wydajnością (MQA)
Nel następny artykuł serii, będziemy badać dostrajanie transformatorów z LoRA, QLoRA i adapterami: Jak dopasować wstępnie wytrenowane modele do konkretnych zadań poprzez zmianę tylko niewielkiej części parametrów, redukując Koszty procesora graficznego i pamięci są drastyczne.
Dodatkowe zasoby
- Oryginalny papier: „Wystarczy uwaga” (Vaswani i in., 2017)
- Błyskawiczna uwaga 3: „Szybka i dokładna uwaga przy asynchronii i niskiej precyzji” (Dao i in., 2024)
- Dokumenty GQA: „GQA: szkolenie uogólnionych modeli transformatorów z wieloma zapytaniami” (Ainslie i in., 2023)
- Ilustrowany transformator: Przewodnik wizualny autorstwa Jaya Alammara
- Dokumentacja PyTorcha: torch.nn.MultiheadUwaga na zoptymalizowane wdrożenia
- Przytulanie twarzy: Dokumentacja transformatorów z praktycznymi przykładami







