diff --git a/wyk/15_transformer.ipynb b/wyk/15_transformer.ipynb new file mode 100644 index 0000000..9131627 --- /dev/null +++ b/wyk/15_transformer.ipynb @@ -0,0 +1,250 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Modele Transformer\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Atencja\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Atencję w modelach Transformer można interpretować jako rodzaj\n", + "„miękkiego” odpytywania swego rodzaju bazy danych, w której\n", + "przechowywane są pary klucz-wartość. Mamy trzy rodzaje wektorów (a\n", + "właściwie macierzy, bo wektory są od razu upakowane w macierze):\n", + "\n", + "- $Q$ - macierz zapytań,\n", + "- $K$ - macierz kluczy,\n", + "- $V$ - macierz wartości odpowiadających kluczom $K$.\n", + "\n", + "W atencji modeli Transformer patrzymy jak bardzo zapytania $Q$ pasują\n", + "do kluczy $K$ i na tej podstawie zwracamy wartości $V$ (im bardziej\n", + "**klucz** pasuje do **zapytania**, tym większy wkład wnosi odpowiednia **wartość**).\n", + "Ten rodzaj odpytywania można zrealizować z pomocą mnożenia macierzy i funkcji softmax:\n", + "\n", + "$$\\operatorname{Atention}(Q,K,V) = \\operatorname{softmax}(QK^T)V$$\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Uproszczony przykład\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Załóżmy, że rozmiar embeddingu wynosi 4, w macierzach rozpatrywać\n", + "będziemy po 3 wektory naraz (możemy sobie wyobrazić, że zdanie zawiera 3 wyrazy).\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([[20.5700, 36.2400, 31.1000],\n", + " [15.1100, 13.9100, 7.9500],\n", + " [ 2.2100, 7.1800, 7.4000]])" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import torch\n", + "\n", + "Q = torch.tensor([\n", + " [0.3, -2.0, 0.4, 6.0],\n", + " [-1.0, 1.5, 0.2, 3.0],\n", + " [0.3, -1.0, 0.2, 1.0]])\n", + "\n", + "K = torch.tensor([\n", + " [-0.5, 1.7, 0.3, 4.0],\n", + " [0.4, -1.5, 0.3, 5.5],\n", + " [-1.0, -3.5, 1.0, 4.0]])\n", + "\n", + "M = Q @ torch.transpose(K, 0, 1)\n", + "M" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Jak widać, najbardziej pierwszy wektor $Q$ pasuje do drugiego wektora $K$.\n", + "Znormalizujmy te wartości używać funkcji softmax.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([[1.5562e-07, 9.9418e-01, 5.8236e-03],\n", + " [7.6807e-01, 2.3134e-01, 5.9683e-04],\n", + " [3.0817e-03, 4.4385e-01, 5.5307e-01]])" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import torch\n", + "\n", + "Mn = torch.softmax(M, 1)\n", + "Mn" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Drugi wektor zapytania najbardziej pasuje do pierwszego klucza, trochę\n", + "mniej do drugiego klucza, o wiele mniej do trzeciego klucza. Te\n", + "wektory to oczywiście wektory atencji (drugie słowo najbardziej\n", + "„patrzy” na pierwsze słowo).\n", + "\n", + "Teraz będziemy przemnażać przez wektory wartości:\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([[ 3.9750e+00, 9.9419e-02, 1.0116e-01, 1.5765e-01, 5.8255e-04],\n", + " [ 9.2517e-01, 6.9357e+00, 2.3313e-02, -3.8112e+00, 9.2174e-01],\n", + " [ 1.6095e+00, 7.2120e-02, 2.1031e-01, 5.5597e+00, 5.9005e-02]])" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import torch\n", + "\n", + "V = torch.tensor([\n", + " [0.0, 9.0, 0.0, -5.0, 1.2],\n", + " [4.0, 0.1, 0.1, 0.1, 0.0],\n", + " [-0.3, 0.0, 0.3, 10.0, 0.1]])\n", + "\n", + "Mn @ V" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Dodatkowa normalizacja\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "W praktyce dobrze jest znormalizować pierwszy iloczyn przez\n", + "$\\sqrt{d_k}$, gdzie $d_k$ to rozmiar wektora klucza.\n", + "\n", + "$$\\operatorname{Atention}(Q,K,V) = \\operatorname{softmax}(\\frac{QK^T}{\\sqrt{d^k}})V$$\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Skąd się biorą Q, K i V?\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Wektory (macierze) $Q$, $K$ i $V$ w pierwszej warstwie pochodzą z\n", + "embeddingów tokenów $E$ (właściwie jednostek BPE).\n", + "\n", + "- $Q$ = $EW^Q$\n", + "- $K$ = $EW^K$\n", + "- $V$ = $EW^V$\n", + "\n", + "W kolejnych warstwach zamiast $E$ wykorzystywane jest wyjście z poprzedniej warstwy.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Literatura\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[https://arxiv.org/pdf/1706.03762.pdf](https://arxiv.org/pdf/1706.03762.pdf)\n", + "\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.9.2" + }, + "org": null + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/wyk/15_transformer.org b/wyk/15_transformer.org new file mode 100644 index 0000000..ea4729d --- /dev/null +++ b/wyk/15_transformer.org @@ -0,0 +1,124 @@ +* Modele Transformer + +** Atencja + +Atencję w modelach Transformer można interpretować jako rodzaj +„miękkiego” odpytywania swego rodzaju bazy danych, w której +przechowywane są pary klucz-wartość. Mamy trzy rodzaje wektorów (a +właściwie macierzy, bo wektory są od razu upakowane w macierze): + +- $Q$ - macierz zapytań, +- $K$ - macierz kluczy, +- $V$ - macierz wartości odpowiadających kluczom $K$. + +W atencji modeli Transformer patrzymy jak bardzo zapytania $Q$ pasują +do kluczy $K$ i na tej podstawie zwracamy wartości $V$ (im bardziej +*klucz* pasuje do *zapytania*, tym większy wkład wnosi odpowiednia *wartość*). +Ten rodzaj odpytywania można zrealizować z pomocą mnożenia macierzy i funkcji softmax: + +$$\operatorname{Atention}(Q,K,V) = \operatorname{softmax}(QK^T)V$$ + +*** Uproszczony przykład + +Załóżmy, że rozmiar embeddingu wynosi 4, w macierzach rozpatrywać +będziemy po 3 wektory naraz (możemy sobie wyobrazić, że zdanie zawiera 3 wyrazy). + + +#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer +import torch + +Q = torch.tensor([ + [0.3, -2.0, 0.4, 6.0], + [-1.0, 1.5, 0.2, 3.0], + [0.3, -1.0, 0.2, 1.0]]) + +K = torch.tensor([ + [-0.5, 1.7, 0.3, 4.0], + [0.4, -1.5, 0.3, 5.5], + [-1.0, -3.5, 1.0, 4.0]]) + +M = Q @ torch.transpose(K, 0, 1) +M + +#+END_SRC + +#+RESULTS: +:results: +# Out[11]: +#+BEGIN_EXAMPLE + tensor([[20.5700, 36.2400, 31.1000], + [15.1100, 13.9100, 7.9500], + [ 2.2100, 7.1800, 7.4000]]) +#+END_EXAMPLE +:end: + +Jak widać, najbardziej pierwszy wektor $Q$ pasuje do drugiego wektora $K$. +Znormalizujmy te wartości używać funkcji softmax. + +#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer +import torch + +Mn = torch.softmax(M, 1) +Mn + +#+END_SRC + +#+RESULTS: +:results: +# Out[12]: +#+BEGIN_EXAMPLE + tensor([[1.5562e-07, 9.9418e-01, 5.8236e-03], + [7.6807e-01, 2.3134e-01, 5.9683e-04], + [3.0817e-03, 4.4385e-01, 5.5307e-01]]) +#+END_EXAMPLE +:end: + +Drugi wektor zapytania najbardziej pasuje do pierwszego klucza, trochę +mniej do drugiego klucza, o wiele mniej do trzeciego klucza. Te +wektory to oczywiście wektory atencji (drugie słowo najbardziej +„patrzy” na pierwsze słowo). + +Teraz będziemy przemnażać przez wektory wartości: + +#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer +import torch + +V = torch.tensor([ + [0.0, 9.0, 0.0, -5.0], + [4.0, 0.1, 0.1, 0.1], + [-0.3, 0.0, 0.3, 10.0]]) + +Mn @ V +#+END_SRC + +#+RESULTS: +:results: +# Out[13]: +#+BEGIN_EXAMPLE + tensor([[ 3.9750, 0.0994, 0.1012, 0.1577], + [ 0.9252, 6.9357, 0.0233, -3.8112], + [ 1.6095, 0.0721, 0.2103, 5.5597]]) +#+END_EXAMPLE +:end: + +*** Dodatkowa normalizacja + +W praktyce dobrze jest znormalizować pierwszy iloczyn przez +$\sqrt{d_k}$, gdzie $d_k$ to rozmiar wektora klucza. + +$$\operatorname{Atention}(Q,K,V) = \operatorname{softmax}(\frac{QK^T}{d^k})V$$ + +*** Skąd się biorą Q, K i V? + +Wektory (macierze) $Q$, $K$ i $V$ w pierwszej warstwie pochodzą z +embeddingów tokenów $E$ (właściwie jednostek BPE). + +- $Q$ = $EW^Q$ +- $K$ = $EW^K$ +- $V$ = $EW^V$ + +W kolejnych warstwach zamiast $E$ wykorzystywane jest wyjście z poprzedniej warstwy. + +** Literatura + +https://arxiv.org/pdf/1706.03762.pdf diff --git a/wyk/transformer.drawio b/wyk/transformer.drawio new file mode 100644 index 0000000..2255972 --- /dev/null +++ b/wyk/transformer.drawio @@ -0,0 +1 @@ +7Ztbb5swFMc/DdL2sCpgLs0jkHbXvjSTqj5NKLgJKuCMOLd9+tnBkGK7TTqtsmVXiiJzMBf/fz7mHGMckFa7z022XNygHJaON8p3Dpg4njf2XfJPDfvWEADQGuZNkbcm92iYFn8gM46YdV3kcDWoiBEqcbEcGmeoruEMD2xZ06DtsNoDKodXXWZzKBims6wUrXdFjhet9dKLjvYvsJgvuiu74bjdU2VdZXaK1SLL0bY1HRoHrhyQNgjhtlTtUlhS7TpdWgWun9nb31gDa3zOAd8m9+PsKs7r9f5r7F9H6Y/q9lPATrPJyjVrMbtbvO8kaNC6ziE9y8gByXZRYDhdZjO6d0uYE9sCVyXZckmRnQ42GO6evVG3bz7pNhBVEDd7UqXrMwz9vusJbHt71D9kbR0tnmgPQmbMGPN5f+qjLKTAlHmFSqJIcZkJOpEW46EYK9ygR5iiEjXEUqOa1EweirLkTFlZzGuyOSMKQWJPqH4F6YQx21EVeU4vI1V/yOc/AHAvhwRAIBLwJQC8t9LfF/SvDJbfA5rJHwjyPyJsMgDd+n8kGaRTc/UHujnApah/kNzE0+9OMDEYg25uMBYwJNmqMHgg8n3NCHRRuSVP4mCkm/6ScN2CkSgIdePgCRwuzJW/z7a0kR9IAiKf/oJ0A2dOlGycaPKBbFEGBSYGmrBFk4+smrGohNTNVY1KzN1OoaqsICVkecpJiWneKVKHNNAGVtp5VfhqVjRjtAAV4J9VylHJkvcTqI4hnQ3EtHMuSbr/TuylcFw5MXFm4D3IkCawqkl5kgT2BKl2jscCWH6gGywxy+Vg/SZcTMficpNwvd7KsJxMqR5twBJxWJR7y1n5k/FYxrp5iyz+TgwGwIXT/XIKZQBk4bTBAPiJHfUAZNGxyQB084CuQ9gCgJ+DUQ9AlnWYDEA7D5BlEgYD4FM59QBkb8wMBsBPfKgHIEvaTAYQ6gZAlp6FJVU7LzakOMeHlg8ytiWdjHL7mae2Orn84AhTEQqZRKQYoS8bxM5C6FmKUMhFlCOUDYNnIQS2ItTOC/95IB1bijB0dUMoWz5gEwDVwYj/+kUBFza8Agt1WxTlS5f+eyPajpR4UXoo0KVT6YiWaJifeuTf5OW4HudOvuQLvR7J4Au9N8N0cgHA7Jdrw9sXLlhwgWL/6eZBXgIztgAM/wRSD0ackbuLb6c/72JzIbg8hLd7upDN45fdh31PPo8HV38B \ No newline at end of file