Merge branch 'master' of git.wmi.amu.edu.pl:filipg/aitech-moj

This commit is contained in:
Jakub Pokrywka 2022-06-05 22:32:30 +02:00
commit 4d30d0c2f4
4 changed files with 317 additions and 74 deletions

File diff suppressed because one or more lines are too long

View File

@ -37,15 +37,19 @@ nie tylko od pozycji $k$ czy słowa $w_k$. W naszym uproszczonym przypadku
jako kontekst możemy rozpatrywać słowo bezpośrednio poprzedzające
odgadywane słowa (kontekstem jest $w_{i-1}$).
Wygodnie również przyjąć, że $\sum_{k=1}^j \omega_k = 1$, wówczas mamy do czynienia ze średnią ważoną.
Wygodnie również przyjąć, że $\sum_{k=1}^j \omega_k = 1$ i $\omega_k
\in (0,1)$, wówczas mamy do czynienia ze średnią ważoną.
*** Nieznormalizowane wagi atencji
Będziemy liczyć nieznormalizowane **wagi atencji**
$\hat{\alpha}_{k,j}$. Określają one, jak bardzo słowo $w_j$ „zwraca
uwagę” na poszczególne, inne słowa. Innymi słowy, wagi opisują, jak
uwagę” na poszczególne, inne słowa. Innymi słowy, wagi atencji opisują, jak
bardzo słowo $w_k$ pasuje do naszego kontekstu, czyli słowa $w_j$.
*Uwaga*: (nieznormalizowane czy znormalizowane) wagi atencji nie należą do wyuczalnych
wag (parametrów) modelu.
Najprostszy sposób mierzenia dopasowania to po prostu iloczyn skalarny:
$$\hat{\alpha}_{k,j} = E(w_k)E(w_j),$$
@ -86,67 +90,6 @@ słowo odgadywane.
#+CAPTION: Atencja użyta w prostym neuronowym modelu języka
[[./10_Atencja/simple-attention.drawio.png]]
** Atencja jako „miękka” baza danych
O atencji można myśleć metaforycznie jako o odpytywaniu „miękkiej”, wektorowej
bazy danych. Możemy sobie wyobrazić, że słowa $w_1,\dots,w_{j-1}$ są
naszą bazą danych, a słowo $w_j$ (z którego kierujemy „snop” uwagi)
jest *zapytaniem* (/query/). To zapytanie dopasowujemy do *kluczy*
(/keys/), w najprostszym ujęciu po prostu słów $w_1,\dots,w_{j-1}$ (a
właściwie ich zanurzeń). Jeśli klucz pasuje do zapytania, odpowiednia
wartość (/value/) jest wydobywana z bazy. Nasza baza jest jednak
„miękka”, nie — zerojedynkowa, zapytanie pasuje klucza w pewnym
stopniu, mniej lub bardziej.
W najprostszym ujęciu wartości są tym samym co klucze, czyli z naszej
bazy wydobywamy te same zanurzenia słów, których używamy jako kluczy.
Można jednak skomplikować schemat rozróżniając klucze i wartości —
mogą one powstawać przez rzutowanie podstawowe zanurzenia różnymi
macierzami:
$$\vec{k_i} = W_k E(w_i),$$
$$\vec{v_i} = W_v E(w_i).$$
Również samo zapytanie może powstać przez rzutowanie:
$$\vec{q_i} = W_q E(w_i).$$
Jeśli zanurzenie $E(w_i)$ o rozmiarze $m$ przedstawimy w postaci
kolumnowej, wówczas macierze będą $W_k$ i $W_q$ będą miały rozmiar
$d_k \times m$, gdzie $d_k$ jest rozmiarem kluczy i zapytań (dlaczego
wektory kluczy i zapytań powinny mieć raczej ten sam rozmiar?), zaś macierz
$W_v$ — $d_v \times m$, gdzie $d_v$ to rozmiar zanurzenia wektora wartości.
Zazwyczaj $d_k = d_v = m$, ale nie jest to obligatoryjne.
Teraz nieznormalizowane wagi atencji przyjmą postać:
$$\hat{\alpha}_{i,j} = \vec{q_i}^T\vec{k_j} = (W_q E(w_i))(W_k E(k_j)).$$
Zauważmy, że ciąg $\hat{\alpha}_{1,j},\dots,\hat{\alpha}_{j-1,j}$ można potraktować jako wektor
$\hat{\vec{\alpha}_{*,j}}$ i wyliczać w postaci zwartej:
$$\hat{\vec{\alpha}_{*,j}} = \vec{k_j}^T K$$
gdzie $K$ to macierz kluczy złożona z wektorów
$\vec{k_1},\dots,\vec{k_{j-1}}$, tj. macierz o rozmiarze $d_k \times (j-1)$.
Wektor znormalizowanych wag atencji będzie miał wówczas postać:
$$\vec{\alpha}_{*,j} = \operatorname{softmax}(\vec{k_j}^T K).$$
Dokonajmy teraz agregacji wartości — obliczeniamy średnią wektorów
wartości (\vec{v_i}) ważoną atencją:
$$A(w_1,\dots,j-1) = \alpha_{1,j} \vec{v_1} + \dots + \alpha_{j-1,j} \vec{v_{j-1}} = \sum_{i=1}^{j-1} \alpha_{i,j} v_i.$$
Jeśli $j-1$ wektorów wartości ułożyłem w macierz $V$ (o rozmiarze
$(j-1) \times d_v$), powyższy wzór będziemy mogli zapisać jako iloczyn wektora wag atencji i macierzy $V$:
$$A(w_1,\dots,j-1) = \vec{\alpha}_{*,j}^T V = \operatorname{softmax}(\vec{k_j}^T K)^T V.$$
Sposób patrzenia na atencję przez pryzmat trójki
zapytania-klucze-wartości okaże się niezwykle ważny w wypadku modelu Transformer (zob. kolejny wykład).
** Atencja jako składnik sieci rekurencyjnej
@ -171,7 +114,7 @@ $$P_M(w_j|w_1,\dots,w_{j-1}) = ?.$$
Załóżmy, że mamy pewien początek (*prefiks*) tekstu o długości $p$:
$w_1,\dots,w_p$. Powiedzmy, że naszym celem jest *wygenerowanie*
dokończenia czy kontynuacji tego tekstu (nie określamy z góry tej
dokończenia czy kontynuacji tego tekstu (nie określamy z góry
długości tej kontynuacji).
Najprostszy sposób wygenerowania pierwszego wyrazu dokończenia polega
@ -179,7 +122,7 @@ na wzięciu wyrazu maksymalizującego prawdopodobieństwo według modelu języka
$$w_{p+1} = \operatorname{argmax}_w P_M(w|w_1,\dots,w_p).$$
*Pytanie*: Dlaczego \operatorname{argmax}, a nie \operatorname{max}?
*Pytanie*: Dlaczego $\operatorname{argmax}$, a nie $\operatorname{max}$?
Słowo $w_{p+1}$ możemy dołączyć do prefiksu i powtórzyć procedurę:
@ -205,9 +148,9 @@ przedstawić jako zadanie generowania tekstu. Przykładem jest tłumaczenie masz
W tłumaczeniu maszynowym (tłumaczeniu automatycznym, ang. /machine
translation/) na wejściu podawany jest tekst (na ogół pojedyncze
zdanie) źródłowy (/source sentence/) $S = (u_1,\dots,u_|S|)$, celem
zdanie) źródłowy (/source sentence/) $S = (u_1,\dots,u_{|S|})$, celem
jest uzyskanie tekstu docelowego (/target sentence/)
$T=(w_1,\dots,w_|T|). Zakładamy, że $S$ jest tekstem w pewnym języku
$T=(w_1,\dots,w_{|T|})$. Zakładamy, że $S$ jest tekstem w pewnym języku
źródłowym (/source language/), zaś $T$ — w innym języku, języku
docelowym (/target language/).
@ -229,8 +172,8 @@ The 'new Member States' - actually, the name continues to be sort of conditional
The number of armed attacks also rose by 200% overall. Także liczba ataków zbrojnych wzrosła łącznie o 200 %.
#+END_SRC
Zauważmy, że możemy taki tekst modelować po prostu traktując jako
jeden. Innymi słowy, nie modelujemy tekstu angielskiego ani polskiego,
Zauważmy, że możemy taki (bi)tekst modelować po prostu traktując jako
jeden ciągły tekst. Innymi słowy, nie modelujemy tekstu angielskiego ani polskiego,
tylko angielsko-polską mieszankę, to znaczy uczymy model, który najpierw modeluje prawdopodobieństwo
po stronie źródłowej (powiedzmy — angielskiej):
@ -239,7 +182,7 @@ The number of armed attacks also ?
#+END_SRC
W momencie napotkania specjalnego tokenu końca zdania źródłowego (powiedzmy ~<eoss>~) model
powinien nauczyć się, że musi przerzucić się na modelowanie tekstu w języku docelowym (powiedzmy — polskim):
powinien nauczyć się, że musi przełączyć się na modelowanie tekstu w języku docelowym (powiedzmy — polskim):
#+BEGIN_SRC
The number of armed attacks also rose by 200% overall.<eoss>Także liczba ataków ?
@ -254,10 +197,10 @@ tłumaczone zdanie jako prefiks, doklejając tylko token ~<eoss>~.
**** Neuronowe modele języka jako translatory
Jako że N-gramowego modelu języka ani modelu opartego na worku słów
Jako że n-gramowego modelu języka ani modelu opartego na worku słów
nie da się użyć w omawiany sposób w tłumaczeniu maszynowym
(dlaczego?), jako pierwszych użyto w neuronowym tłumaczeniu maszynowym
sieci LSTM, przy użyciu omawianego wyżej sposobu.
sieci LSTM, przy zastosowaniu omawianego wyżej sposobu.
System tłumaczenia oparte na sieciach LSTM działały zaskakująco
dobrze, zważywszy na to, że cała informacja o zdaniu źródłowym musi

281
wyk/11_Transformer.org Normal file
View File

@ -0,0 +1,281 @@
* Transformer
** Atencja jako „miękka” baza danych
O atencji można myśleć metaforycznie jako o odpytywaniu „miękkiej”, wektorowej
bazy danych. Możemy sobie wyobrazić, że słowa $w_1,\dots,w_{j-1}$ są
naszą bazą danych, a słowo $w_j$ (z którego kierujemy „snop” uwagi)
jest *zapytaniem* (/query/). To zapytanie dopasowujemy do *kluczy*
(/keys/), w najprostszym ujęciu po prostu słów $w_1,\dots,w_{j-1}$ (a
właściwie ich zanurzeń). Jeśli klucz pasuje do zapytania, odpowiednia
wartość (/value/) jest wydobywana z bazy. Nasza baza jest jednak
„miękka”, nie — zerojedynkowa, zapytanie pasuje do klucza w pewnym
stopniu, mniej lub bardziej.
W najprostszym ujęciu wartości są tym samym co klucze, czyli z naszej
bazy wydobywamy te same zanurzenia słów, których używamy jako kluczy.
Można jednak skomplikować schemat rozróżniając klucze i wartości —
mogą one powstawać przez rzutowanie podstawowych zanurzeń różnymi
macierzami:
$$\vec{k_i} = W_k E(w_i),$$
$$\vec{v_i} = W_v E(w_i).$$
Również samo zapytanie może powstać przez rzutowanie:
$$\vec{q_i} = W_q E(w_i).$$
Jeśli zanurzenie $E(w_i)$ o rozmiarze $m$ przedstawimy w postaci
kolumnowej, wówczas macierze będą $W_k$ i $W_q$ będą miały rozmiar
$d_k \times m$, gdzie $d_k$ jest rozmiarem kluczy i zapytań (dlaczego
wektory kluczy i zapytań powinny mieć raczej ten sam rozmiar?), zaś macierz
$W_v$ — $d_v \times m$, gdzie $d_v$ to rozmiar zanurzenia wektora wartości.
Zazwyczaj $d_k = d_v = m$, ale nie jest to obligatoryjne.
Teraz nieznormalizowane wagi atencji przyjmą postać:
$$\hat{\alpha}_{i,j} = \vec{q_i}^T\vec{k_j} = (W_q E(w_i))(W_k E(k_j)).$$
Zauważmy, że ciąg $\hat{\alpha}_{1,j},\dots,\hat{\alpha}_{j-1,j}$ można potraktować jako wektor
$\hat{\vec{\alpha}_{*,j}}$ i wyliczać w postaci zwartej:
$$\hat{\vec{\alpha}_{*,j}} = \vec{q_j}^T K$$
gdzie $K$ to macierz kluczy złożona z wektorów
$\vec{k_1},\dots,\vec{k_{j-1}}$, tj. macierz o rozmiarze $d_k \times (j-1)$.
Wektor znormalizowanych wag atencji będzie miał wówczas postać:
$$\vec{\alpha}_{*,j} = \operatorname{softmax}(\vec{q_j}^T K).$$
Dokonajmy teraz agregacji wartości — obliczamy średnią wektorów
wartości ($\vec{v_i}$) ważoną atencją:
$$A(w_1,\dots,j-1) = \alpha_{1,j} \vec{v_1} + \dots + \alpha_{j-1,j} \vec{v_{j-1}} = \sum_{i=1}^{j-1} \alpha_{i,j} v_i.$$
Jeśli $j-1$ wektorów wartości ułożyłem w macierz $V$ (o rozmiarze
$(j-1) \times d_v$), powyższy wzór będziemy mogli zapisać jako iloczyn wektora wag atencji i macierzy $V$:
$$A(w_1,\dots,j-1) = \vec{\alpha}_{*,j}^T V = \operatorname{softmax}(\vec{q_j}^T K)^T V.$$
Sposób patrzenia na atencję przez pryzmat trójki
zapytania-klucze-wartości okaże się niezwykle ważny w wypadku modelu Transformer.
** Model Transformer — historia
Architekturę Transformer opracowano, pierwotnie, na potrzeby
tłumaczenia automatycznego (rok 2017, artykuł [Attention Is All You
Need](https://arxiv.org/abs/1706.03762)). Szybko okazało się, że
podobnie jak w wypadku modelu ELMo dla sieci LSTM, można *pretrenować*
duże modele Transformer (po prostu na dużych korpusach tekstowych, w
sposób nienadzorowany), a potem dostrajać pod konkretne zadanie
przetwarzania języka naturalnego. Jednym z pierwszych przykładów
takiego podejścia był model BERT (rok 2018, artykuł [BERT:
Pre-training of Deep Bidirectional Transformers for Language
Understanding](https://arxiv.org/abs/1810.04805)). To podejście było
później rozwinięte w postaci różnych modeli Transformer, również dla innych
języków niż angielski (RoBERTa, XLM, Polish RoBERTa itd.).
Na tym wykładzie my skupimy się na innej odnodze modeli Transformer —
modelach generatywnych, takich jak na przykład GPT-2 czy GPT-3. To
podejście jest bliższe duchowi czystego modelowania języka — model
języka jest używany wprost jako generator.
** GPT-2 — przykład działania
Dokonajmy najpierw tokenizacji:
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("gpt2")
text = "The World War III will begin in"
encoded_input = tokenizer(text, return_tensors='pt')
encoded_input
#+END_SRC
#+RESULTS:
:results:
{'input_ids': tensor([[ 464, 2159, 1810, 6711, 481, 2221, 287]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1]])}
:end:
Możemy podejrzeć uzyskane tokeny:
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
[tokenizer.decode(i) for i in encoded_input.input_ids[0]]
#+END_SRC
#+RESULTS:
:results:
['The', ' World', ' War', ' III', ' will', ' begin', ' in']
:end:
Zwróćmy uwagę, że w GPT-2 tokeny obejmują spacje!
Teraz uruchommy zasadniczy model:
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("gpt2")
outputs = model(**encoded_input)
#+END_SRC
#+RESULTS:
:results:
:end:
Z modelu GPT-2 otrzymamy rozkład prawdopodobieństwa kolejnego wyrazu, najpierw w postaci
nieznormalizowanych *logitów*:
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
logits = outputs[0][0][-1]
logits
#+END_SRC
#+RESULTS:
:results:
tensor([-130.2947, -129.5677, -136.4030, ..., -138.3791, -138.8967,
-131.6319], grad_fn=<SelectBackward0>)
:end:
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
from torch import softmax, topk
k = 20
t = topk(softmax(logits, -1), k)
tb = [[tokenizer.decode(t.indices[ix]), t.values[ix].item()] for ix in range(k)]
tb
#+END_SRC
#+RESULTS:
:results:
[[' earnest', 0.07378227263689041], [' the', 0.06698606163263321], [' 1945', 0.043497972190380096], [' September', 0.024068640545010567], [' March', 0.0228887926787138], [' October', 0.02232857048511505], [' Europe', 0.02032744698226452], [' 2020', 0.018564637750387192], [' Japan', 0.018423961475491524], [' December', 0.016560807824134827], [' January', 0.015074416995048523], [' July', 0.014139187522232533], [' April', 0.013183596543967724], [' November', 0.012901309877634048], [' 20', 0.012770282104611397], [' Afghanistan', 0.012765118852257729], [' 1944', 0.01266297698020935], [' June', 0.012072316370904446], [' 1914', 0.011765970848500729], [' May', 0.011659453622996807]]
:end:
*** Generowanie tekstu za pomocą GPT-2
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
from transformers import pipeline
generator = pipeline('text-generation', model='gpt2')
generator('Hello, I\'m a language model,', max_length=30, num_return_sequences=1)
#+END_SRC
#+RESULTS:
:results:
:end:
** Model Transformer — podstawowa idea
Model Transformer sprowadza się właściwie do atencji; nie posiada
żadnego komponentu rekurencyjnego, ani nawet nie stosujemy czegoś w
rodzaju połączenia modelu worka słów i modelu n-gramowego.
W pierwszym przybliżeniu przy obliczaniu rozkładu prawdopodobieństwa
dla kolejnego wyrazu, to jest:
$$P(w_j|w_1\dots w_{j-1})$$
na $j$-tym miejscu (w miejscu przewidywanego wyrazu) doklejamy
specjalny token, powiedzmy ~<mask>~. Token ten będzie „atendował” do
innych wszystkich wcześniejszych tokenów w zdaniu:
$$\vec{\alpha}_{*,j}^T V = \operatorname{softmax}(\vec{q_j}^T K)^T V.$$
Samo to byłoby oczywiście zbyt proste:
1. Otrzymalibyśmy model (ważonego) worka słów, w dodatku każde słowo
miałoby zawsze taką samą wagę! — Token $w_j$, który atenduje jest
zawsze ten sam (~<mask>~). Musimy wzbogacić reprezentację wektorową
słów i specjalnego tokenu (~<mask>~).
2. Model Transformer w swojej podstawowej postaci w ogóle nie jest
wyposażony w pojęcie sekwencji — w przeciwieństwie do sieci
rekurencyjnych, które w sposób inherentny operują krok po kroku, w
sekwencji (w czasie). Musimy pozycję tokenów wprowadzić do sieci
Transformer nie przez modyfikację jej architektury, lecz przez dołączenie
informacji pozycyjnej do początkowych zanurzeń.
3. Model Transformer nie powinien mieć żadnych tokenów OOV/UNK. Musimy
wrócić do kwestii tokenizacji tekstu i wprowadzić podział rzadszych
tokenów na mniejsze, *podwyrazowe* jednostki.
** Atencja wsobna
Jeśli chodzi problem (1), rozwiążemy go przez wprowadzenie
**skontekstualizowanych reprezentacji** tokenów.
Na przykład słowo /mysz/ ma jedno wejściowe (/statyczne/) zanurzenie
(embedding) — bez względu na to, czy chodzi o zwierzę czy urządzenie
peryferyjne, tymczasem dość łatwo ustalić na podstawie kontekstu, o
które znaczenie chodzi.
Rozwiązanie polega na tym, że tokenom będziemy przypisywać kolejne
zanurzenia skontekstualizowane — zależne od innych wyrazów w zdaniu. W
tym celu zastosujemy atencję wsobną (samo-atencję, /self-attention/).
Każdy token będzie atendował potencjalnie do każdego innego tokenu,
również do samego siebie (!).
*** Wzory
Rozpatrywać zatem będziemy nie tylko pojedynczy wektor znormalizowanych atencji
$$\vec{\alpha}_{*,j}^T V = \operatorname{softmax}(\vec{q_j}^T K)^T V$$,
lecz całą serię wektorów:
$$\vec{\alpha}_{*,1},\dots,\vec{\alpha}_{*,i},\dots,\vec{\alpha}_{*,j}$$
gdzie:
$$\vec{\alpha}_{*,i} = \operatorname{softmax}(\vec{q_i}^T K)$$
i $K$ jest macierzą kluczy o rozmiarze $d_k \times j$ (tym razem obejmuje również sam $i$-ty token).
Nowa, skontekstualizowana reprezentacja $i$-tego tokenu będzie po prostu średnią wszystkich
wektorów ważoną atencją:
$$E_1(w_i) = \operatorname{softmax}(\vec{q_i}^T K)^T V,$$
gdzie:
- $E_1(w_i)$ — skontekstualizowane zanurzenie $i$-tego tokenu; używając indeksu $_1$
zaznaczamy, że to jest pierwszy skonstekstualizowany embedding, rekurencyjnie będziemy budowali
kolejne $E_2(w_i)$, $E_3(w_i)$ itd. (zaś wejściowy statyczny embedding możemy oznaczyć przez $E_0(w_i)$);
- $V$ to macierz wartości o rozmiarze $j \times d_v$.
**** Zwarta postać macierzowa atencji wsobnej
Z praktycznych powodów (szybkość obliczeń na kartach graficznych) dużą
zaletą atencji wsobnej jest to, że wyliczenie skonstekstualizowanych zanurzeń dla wszystkich tokenów
w tekście da się zapisać w postaci zwartego wzoru macierzowego:
$$E_1 = \operatorname{Attention}(Q, K, V) = \operatorname{softmax}(QK)^T V,$$
gdzie $Q$ to macierz wszystkich zapytań o rozmiarze $j \times d_k$ (wektory ułożone poziomo).
**** Skalowanie atencji
Twórcy modelu Transformer odkryli, że lepsze wyniki daje skalowanie atencji
przez stałą zależną od rozmiaru wektora klucza/zapytania $d_k$:
$\operatorname{Attention}(Q, K, V) = \operatorname{softmax}(\frac{QK}{d_k})^T V,$$
** Wielogłowicowa atencja
Od samego początku w Transformerze zamiast jednej atencji zaproponowano wiele **głowic atencji**
$(\operatorname{head}_1,\dots,\operatorname{head}_h$, każda głowica atencji działa w następujący sposób:
$$\operatorname{head_i} = \operatorname{Attention}(QW_i^Q, KW_i^K,VW_i^V),$$
to znaczy każda głowica atencji działa tak samo, tylko przed jej zastosowaniem mnożymy
wektory zapytań, kluczy i wartości przez różne wyuczalne macierze, odpowiednio,
W_i^Q, W_i^K,W_i^V. Otrzymamy w ten sposób $h$ wektorów, konkatenujemy po prostu i mnożymy
przez dodatkową wyuczalną macierz $W^O$:
$$\operatorname{MultiHead}(Q, K, V) = [\operatorname{head}_1,...,\operatorname{head}_n]W^O.$$
Przyjmujemy, że $d_k = d_v = m/h$, wtedy rozmiary macierzy $W_i^Q$ i $W_i^K$ będą wynosiły
$m \times d_k$, macierzy $W_i^V$ — $m \times d_v$, $W^O$ — $hd_v \times m$.

View File

@ -1,3 +1,4 @@
{
"cells": [
{
@ -7,7 +8,7 @@
"![Logo 1](https://git.wmi.amu.edu.pl/AITech/Szablon/raw/branch/master/Logotyp_AITech1.jpg)\n",
"<div class=\"alert alert-block alert-info\">\n",
"<h1> Modelowanie języka</h1>\n",
"<h2> 2. <i>Języki</i> [wykład]</h2> \n",
"<h2> 10. <i>Atencja</i> [wykład]</h2> \n",
"<h3> Filip Graliński (2022)</h3>\n",
"</div>\n",
"\n",