Up
This commit is contained in:
parent
6da452bc4f
commit
861af85d5e
495
wyk/11_Transformer.ipynb
Normal file
495
wyk/11_Transformer.ipynb
Normal 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
|
||||||
|
}
|
@ -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.$$
|
||||||
|
@ -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",
|
||||||
|
Loading…
Reference in New Issue
Block a user