diff --git a/wyk/11_Transformer.ipynb b/wyk/11_Transformer.ipynb new file mode 100644 index 0000000..b5e0f8a --- /dev/null +++ b/wyk/11_Transformer.ipynb @@ -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", + "
\n", + "

Modelowanie języka

\n", + "

11. Transformer [wykład]

\n", + "

Filip Graliński (2022)

\n", + "
\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=)" + ] + } + ], + "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 ``. 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 (``). Musimy wzbogacić reprezentację wektorową\n", + " słów i specjalnego tokenu (``).\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 +} diff --git a/wyk/11_Transformer.org b/wyk/11_Transformer.org index bef5f66..bd9be02 100644 --- a/wyk/11_Transformer.org +++ b/wyk/11_Transformer.org @@ -15,7 +15,7 @@ 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 — +Można jednak skomplikować schemat, rozróżniając klucze i wartości — mogą one powstawać przez rzutowanie podstawowych zanurzeń różnymi 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.$$ -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$: $$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: -#+BEGIN_SRC python :session mysession :exports both :results raw drawer +#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer from transformers import AutoTokenizer tokenizer = AutoTokenizer.from_pretrained("gpt2") 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]])} :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]] #+END_SRC @@ -113,7 +113,7 @@ Zwróćmy uwagę, że w GPT-2 tokeny obejmują spacje! 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 model = AutoModelForCausalLM.from_pretrained("gpt2") outputs = model(**encoded_input) @@ -123,7 +123,7 @@ Teraz uruchommy zasadniczy model: :results: :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]) #+END_SRC @@ -135,7 +135,7 @@ Teraz uruchommy zasadniczy model: 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 +#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer logits = outputs[0][0][-1] logits #+END_SRC @@ -146,7 +146,7 @@ tensor([-130.2947, -129.5677, -136.4030, ..., -138.3791, -138.8967, -131.6319], grad_fn=) :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 k = 20 @@ -164,7 +164,7 @@ tensor([-130.2947, -129.5677, -136.4030, ..., -138.3791, -138.8967, *** 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 generator = pipeline('text-generation', model='gpt2') 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: 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 (~~). Musimy wzbogacić reprezentację wektorową słów i specjalnego tokenu (~~). @@ -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 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 +Rozwiązanie polega na tym, że wszystkim tokenom będziemy przypisywać kolejne +zanurzenia skontekstualizowane — zależne od innych tokenó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 (!). @@ -229,11 +229,11 @@ również do samego siebie (!). 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: -$$\vec{\alpha}_{*,1},\dots,\vec{\alpha}_{*,i},\dots,\vec{\alpha}_{*,j}$$ +$$\vec{\alpha}_{*,1},\dots,\vec{\alpha}_{*,i},\dots,\vec{\alpha}_{*,j},$$ gdzie: @@ -251,7 +251,7 @@ 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$. +- $V$ — macierz wartości o rozmiarze $j \times d_v$. **** 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 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 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),$$ 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 +$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$: $$\operatorname{MultiHead}(Q, K, V) = [\operatorname{head}_1,...,\operatorname{head}_n]W^O.$$ diff --git a/wyk/helpers/intro b/wyk/helpers/intro index 6da9943..7d82176 100644 --- a/wyk/helpers/intro +++ b/wyk/helpers/intro @@ -8,7 +8,7 @@ "![Logo 1](https://git.wmi.amu.edu.pl/AITech/Szablon/raw/branch/master/Logotyp_AITech1.jpg)\n", "
\n", "

Modelowanie języka

\n", - "

10. Atencja [wykład]

\n", + "

11. Transformer [wykład]

\n", "

Filip Graliński (2022)

\n", "
\n", "\n",