This commit is contained in:
Filip Gralinski 2022-06-11 08:58:47 +02:00
parent 6da452bc4f
commit 861af85d5e
3 changed files with 514 additions and 19 deletions

495
wyk/11_Transformer.ipynb Normal file
View File

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

View File

@ -15,7 +15,7 @@ stopniu, mniej lub bardziej.
W najprostszym ujęciu wartości są tym samym co klucze, czyli z naszej 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. 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 — Można jednak skomplikować schemat, rozróżniając klucze i wartości —
mogą one powstawać przez rzutowanie podstawowych zanurzeń różnymi mogą one powstawać przez rzutowanie podstawowych zanurzeń różnymi
macierzami: macierzami:
@ -55,7 +55,7 @@ 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.$$ $$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 Jeśli $j-1$ wektorów wartości ułożymy 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$: $(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.$$ $$A(w_1,\dots,j-1) = \vec{\alpha}_{*,j}^T V = \operatorname{softmax}(\vec{q_j}^T K)^T V.$$
@ -87,7 +87,7 @@ języka jest używany wprost jako generator.
Dokonajmy najpierw tokenizacji: Dokonajmy najpierw tokenizacji:
#+BEGIN_SRC python :session mysession :exports both :results raw drawer #+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
from transformers import AutoTokenizer from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("gpt2") tokenizer = AutoTokenizer.from_pretrained("gpt2")
text = "The World War III will begin in 2028 in" text = "The World War III will begin in 2028 in"
@ -100,7 +100,7 @@ Dokonajmy najpierw tokenizacji:
{'input_ids': tensor([[ 464, 2159, 1810, 6711, 481, 2221, 287, 1160, 2078, 287]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])} {'input_ids': tensor([[ 464, 2159, 1810, 6711, 481, 2221, 287, 1160, 2078, 287]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])}
:end: :end:
#+BEGIN_SRC python :session mysession :exports both :results raw drawer #+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
[tokenizer.decode(i) for i in encoded_input.input_ids[0]] [tokenizer.decode(i) for i in encoded_input.input_ids[0]]
#+END_SRC #+END_SRC
@ -113,7 +113,7 @@ Zwróćmy uwagę, że w GPT-2 tokeny obejmują spacje!
Teraz uruchommy zasadniczy model: Teraz uruchommy zasadniczy model:
#+BEGIN_SRC python :session mysession :exports both :results raw drawer #+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
from transformers import AutoModelForCausalLM from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("gpt2") model = AutoModelForCausalLM.from_pretrained("gpt2")
outputs = model(**encoded_input) outputs = model(**encoded_input)
@ -123,7 +123,7 @@ Teraz uruchommy zasadniczy model:
:results: :results:
:end: :end:
#+BEGIN_SRC python :session mysession :exports both :results raw drawer #+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
softmax(outputs[0][0][-1]) softmax(outputs[0][0][-1])
#+END_SRC #+END_SRC
@ -135,7 +135,7 @@ Teraz uruchommy zasadniczy model:
Z modelu GPT-2 otrzymamy rozkład prawdopodobieństwa kolejnego wyrazu, najpierw w postaci Z modelu GPT-2 otrzymamy rozkład prawdopodobieństwa kolejnego wyrazu, najpierw w postaci
nieznormalizowanych *logitów*: nieznormalizowanych *logitów*:
#+BEGIN_SRC python :session mysession :exports both :results raw drawer #+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
logits = outputs[0][0][-1] logits = outputs[0][0][-1]
logits logits
#+END_SRC #+END_SRC
@ -146,7 +146,7 @@ tensor([-130.2947, -129.5677, -136.4030, ..., -138.3791, -138.8967,
-131.6319], grad_fn=<SelectBackward0>) -131.6319], grad_fn=<SelectBackward0>)
:end: :end:
#+BEGIN_SRC python :session mysession :exports both :results raw drawer #+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
from torch import softmax, topk from torch import softmax, topk
k = 20 k = 20
@ -164,7 +164,7 @@ tensor([-130.2947, -129.5677, -136.4030, ..., -138.3791, -138.8967,
*** Generowanie tekstu za pomocą GPT-2 *** Generowanie tekstu za pomocą GPT-2
#+BEGIN_SRC python :session mysession :exports both :results raw drawer #+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
from transformers import pipeline from transformers import pipeline
generator = pipeline('text-generation', model='gpt2') generator = pipeline('text-generation', model='gpt2')
generator('Hello, I\'m a language model,', max_length=30, num_return_sequences=1) generator('Hello, I\'m a language model,', max_length=30, num_return_sequences=1)
@ -194,7 +194,7 @@ $$\vec{\alpha}_{*,j}^T V = \operatorname{softmax}(\vec{q_j}^T K)^T V.$$
Samo to byłoby oczywiście zbyt proste: Samo to byłoby oczywiście zbyt proste:
1. Otrzymalibyśmy model (ważonego) worka słów, w dodatku każde słowo 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 miałoby zawsze taką samą wagę! — token $w_j$, który atenduje jest
zawsze ten sam (~<mask>~). Musimy wzbogacić reprezentację wektorową zawsze ten sam (~<mask>~). Musimy wzbogacić reprezentację wektorową
słów i specjalnego tokenu (~<mask>~). słów i specjalnego tokenu (~<mask>~).
@ -219,8 +219,8 @@ Na przykład słowo /mysz/ ma jedno wejściowe (/statyczne/) zanurzenie
peryferyjne, tymczasem dość łatwo ustalić na podstawie kontekstu, o peryferyjne, tymczasem dość łatwo ustalić na podstawie kontekstu, o
które znaczenie chodzi. które znaczenie chodzi.
Rozwiązanie polega na tym, że tokenom będziemy przypisywać kolejne Rozwiązanie polega na tym, że wszystkim tokenom będziemy przypisywać kolejne
zanurzenia skontekstualizowane — zależne od innych wyrazów w zdaniu. W zanurzenia skontekstualizowane — zależne od innych tokenów w zdaniu. W
tym celu zastosujemy atencję wsobną (samo-atencję, /self-attention/). tym celu zastosujemy atencję wsobną (samo-atencję, /self-attention/).
Każdy token będzie atendował potencjalnie do każdego innego tokenu, Każdy token będzie atendował potencjalnie do każdego innego tokenu,
również do samego siebie (!). również do samego siebie (!).
@ -229,11 +229,11 @@ również do samego siebie (!).
Rozpatrywać zatem będziemy nie tylko pojedynczy wektor znormalizowanych atencji Rozpatrywać zatem będziemy nie tylko pojedynczy wektor znormalizowanych atencji
$$\vec{\alpha}_{*,j}^T V = \operatorname{softmax}(\vec{q_j}^T K)^T V$$, $$\vec{\alpha}_{*,j}^T V = \operatorname{softmax}(\vec{q_j}^T K)^T V,$$
lecz całą serię wektorów: lecz całą serię wektorów:
$$\vec{\alpha}_{*,1},\dots,\vec{\alpha}_{*,i},\dots,\vec{\alpha}_{*,j}$$ $$\vec{\alpha}_{*,1},\dots,\vec{\alpha}_{*,i},\dots,\vec{\alpha}_{*,j},$$
gdzie: gdzie:
@ -251,7 +251,7 @@ gdzie:
- $E_1(w_i)$ — skontekstualizowane zanurzenie $i$-tego tokenu; używając indeksu $_1$ - $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 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)$); 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$. - $V$ macierz wartości o rozmiarze $j \times d_v$.
**** Zwarta postać macierzowa atencji wsobnej **** Zwarta postać macierzowa atencji wsobnej
@ -268,18 +268,18 @@ gdzie $Q$ to macierz wszystkich zapytań o rozmiarze $j \times d_k$ (wektory uł
Twórcy modelu Transformer odkryli, że lepsze wyniki daje skalowanie atencji Twórcy modelu Transformer odkryli, że lepsze wyniki daje skalowanie atencji
przez stałą zależną od rozmiaru wektora klucza/zapytania $d_k$: przez stałą zależną od rozmiaru wektora klucza/zapytania $d_k$:
$\operatorname{Attention}(Q, K, V) = \operatorname{softmax}(\frac{QK}{d_k})^T V,$$ $$\operatorname{Attention}(Q, K, V) = \operatorname{softmax}(\frac{QK}{d_k})^T V,$$
** Wielogłowicowa atencja ** Wielogłowicowa atencja
Od samego początku w Transformerze zamiast jednej atencji zaproponowano wiele **głowic atencji** 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}_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),$$ $$\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 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, 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 $W_i^Q$, $W_i^K$, $W_i^V$. Otrzymamy w ten sposób $h$ wektorów, konkatenujemy je po prostu i mnożymy
przez dodatkową wyuczalną macierz $W^O$: przez dodatkową wyuczalną macierz $W^O$:
$$\operatorname{MultiHead}(Q, K, V) = [\operatorname{head}_1,...,\operatorname{head}_n]W^O.$$ $$\operatorname{MultiHead}(Q, K, V) = [\operatorname{head}_1,...,\operatorname{head}_n]W^O.$$

View File

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