diff --git a/wyk/07_Zanurzenia_slow.ipynb b/wyk/07_Zanurzenia_slow.ipynb index bb98b73..720c72d 100644 --- a/wyk/07_Zanurzenia_slow.ipynb +++ b/wyk/07_Zanurzenia_slow.ipynb @@ -1,4 +1,3 @@ - { "cells": [ { @@ -16,4 +15,551 @@ "\n" ] }, -{"cell_type":"markdown","metadata":{},"source":["## Zanurzenia słów\n\n"]},{"cell_type":"markdown","metadata":{},"source":["W praktyce stosowalność słowosieci okazała się zaskakująco\nograniczona. Większy przełom w przetwarzaniu języka naturalnego przyniosły\nwielowymiarowe reprezentacje słów, inaczej: zanurzenia słów.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["### „Wymiary” słów\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Moglibyśmy zanurzyć (ang. *embed*) w wielowymiarowej przestrzeni, tzn. zdefiniować odwzorowanie\n$E \\colon V \\rightarrow \\mathcal{R}^m$ dla pewnego $m$ i określić taki sposób estymowania\nprawdopodobieństw $P(u|v)$, by dla par $E(v)$ i $E(v')$ oraz $E(u)$ i $E(u')$ znajdujących się w pobliżu\n(według jakiejś metryki odległości, na przykład zwykłej odległości euklidesowej):\n\n$$P(u|v) \\approx P(u'|v').$$\n\n$E(u)$ nazywamy zanurzeniem (embeddingiem) słowa.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Wymiary określone z góry?\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Można by sobie wyobrazić, że $m$ wymiarów mogłoby być z góry\nokreślonych przez lingwistę. Wymiary te byłyby związane z typowymi\n„osiami” rozpatrywanymi w językoznawstwie, na przykład:\n\n- czy słowo jest wulgarne, pospolite, potoczne, neutralne czy książkowe?\n- czy słowo jest archaiczne, wychodzące z użycia czy jest neologizmem?\n- czy słowo dotyczy kobiet, czy mężczyzn (w sensie rodzaju gramatycznego i/lub\n socjolingwistycznym)?\n- czy słowo jest w liczbie pojedynczej czy mnogiej?\n- czy słowo jest rzeczownikiem czy czasownikiem?\n- czy słowo jest rdzennym słowem czy zapożyczeniem?\n- czy słowo jest nazwą czy słowem pospolitym?\n- czy słowo opisuje konkretną rzecz czy pojęcie abstrakcyjne?\n- …\n\nW praktyce okazało się jednak, że lepiej, żeby komputer uczył się sam\nmożliwych wymiarów — z góry określamy tylko $m$ (liczbę wymiarów).\n\n"]},{"cell_type":"markdown","metadata":{},"source":["### Bigramowy model języka oparty na zanurzeniach\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Zbudujemy teraz najprostszy model język oparty na zanurzeniach. Będzie to właściwie najprostszy\n**neuronowy model języka**, jako że zbudowany model można traktować jako prostą sieć neuronową.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Słownik\n\n"]},{"cell_type":"markdown","metadata":{},"source":["W typowym neuronowym modelu języka rozmiar słownika musi być z góry\nograniczony. Zazwyczaj jest to liczba rzędu kilkudziesięciu wyrazów —\npo prostu będziemy rozpatrywać $|V|$ najczęstszych wyrazów, pozostałe zamienimy\nna specjalny token `` reprezentujący nieznany (*unknown*) wyraz.\n\nAby utworzyć taki słownik użyjemy gotowej klasy `Vocab` z pakietu torchtext:\n\n"]},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":"16"}],"source":["from itertools import islice\nimport regex as re\nimport sys\nfrom torchtext.vocab import build_vocab_from_iterator\n\n\ndef get_words_from_line(line):\n line = line.rstrip()\n yield ''\n for m in re.finditer(r'[\\p{L}0-9\\*]+|\\p{P}+', line):\n yield m.group(0).lower()\n yield ''\n\n\ndef get_word_lines_from_file(file_name):\n with open(file_name, 'r') as fh:\n for line in fh:\n yield get_words_from_line(line)\n\nvocab_size = 20000\n\nvocab = build_vocab_from_iterator(\n get_word_lines_from_file('opensubtitlesA.pl.txt'),\n max_tokens = vocab_size,\n specials = [''])\n\nvocab['jest']"]},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":"['', '', '', 'w', 'wierzyli']"}],"source":["vocab.lookup_tokens([0, 1, 2, 10, 12345])"]},{"cell_type":"markdown","metadata":{},"source":["#### Definicja sieci\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Naszą prostą sieć neuronową zaimplementujemy używając frameworku PyTorch.\n\n"]},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[],"source":["from torch import nn\nimport torch\n\nembed_size = 100\n\nclass SimpleBigramNeuralLanguageModel(nn.Module):\n def __init__(self, vocabulary_size, embedding_size):\n super(SimpleBigramNeuralLanguageModel, self).__init__()\n self.model = nn.Sequential(\n nn.Embedding(vocabulary_size, embedding_size),\n nn.Linear(embedding_size, vocabulary_size),\n nn.Softmax()\n )\n\n def forward(self, x):\n return self.model(x)\n\nmodel = SimpleBigramNeuralLanguageModel(vocab_size, embed_size)\n\nvocab.set_default_index(vocab[''])\nixs = torch.tensor(vocab.forward(['pies']))\nout[0][vocab['jest']]"]},{"cell_type":"markdown","metadata":{},"source":["Teraz wyuczmy model. Wpierw tylko potasujmy nasz plik:\n\n shuf < opensubtitlesA.pl.txt > opensubtitlesA.pl.shuf.txt\n\n"]},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[],"source":["from torch.utils.data import IterableDataset\nimport itertools\n\ndef look_ahead_iterator(gen):\n prev = None\n for item in gen:\n if prev is not None:\n yield (prev, item)\n prev = item\n\nclass Bigrams(IterableDataset):\n def __init__(self, text_file, vocabulary_size):\n self.vocab = build_vocab_from_iterator(\n get_word_lines_from_file(text_file),\n max_tokens = vocabulary_size,\n specials = [''])\n self.vocab.set_default_index(self.vocab[''])\n self.vocabulary_size = vocabulary_size\n self.text_file = text_file\n\n def __iter__(self):\n return look_ahead_iterator(\n (self.vocab[t] for t in itertools.chain.from_iterable(get_word_lines_from_file(self.text_file))))\n\ntrain_dataset = Bigrams('opensubtitlesA.pl.shuf.txt', vocab_size)"]},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":"(2, 5)"}],"source":["from torch.utils.data import DataLoader\n\nnext(iter(train_dataset))"]},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":"[tensor([ 2, 5, 51, 3481, 231]), tensor([ 5, 51, 3481, 231, 4])]"}],"source":["from torch.utils.data import DataLoader\n\nnext(iter(DataLoader(train_dataset, batch_size=5)))"]},{"cell_type":"markdown","metadata":{},"source":[" device = 'cuda'\n model = SimpleBigramNeuralLanguageModel(vocab_size, embed_size).to(device)\n data = DataLoader(train_dataset, batch_size=5000)\n optimizer = torch.optim.Adam(model.parameters())\n criterion = torch.nn.NLLLoss()\n \n model.train()\n step = 0\n for x, y in data:\n x = x.to(device)\n y = y.to(device)\n optimizer.zero_grad()\n ypredicted = model(x)\n loss = criterion(torch.log(ypredicted), y)\n if step % 100 == 0:\n print(step, loss)\n step += 1\n loss.backward()\n optimizer.step()\n \n torch.save(model.state_dict(), 'model1.bin')\n\nPoliczmy najbardziej prawdopodobne kontynuację dla zadanego słowa:\n\n"]},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":"[('ciebie', 73, 0.1580502986907959), ('mnie', 26, 0.15395283699035645), ('', 0, 0.12862136960029602), ('nas', 83, 0.0410110242664814), ('niego', 172, 0.03281523287296295), ('niej', 245, 0.02104802615940571), ('siebie', 181, 0.020788608118891716), ('którego', 365, 0.019379809498786926), ('was', 162, 0.013852755539119244), ('wszystkich', 235, 0.01381855271756649)]"}],"source":["device = 'cuda'\nmodel = SimpleBigramNeuralLanguageModel(vocab_size, embed_size).to(device)\nmodel.load_state_dict(torch.load('model1.bin'))\nmodel.eval()\n\nixs = torch.tensor(vocab.forward(['dla'])).to(device)\n\nout = model(ixs)\ntop = torch.topk(out[0], 10)\ntop_indices = top.indices.tolist()\ntop_probs = top.values.tolist()\ntop_words = vocab.lookup_tokens(top_indices)\nlist(zip(top_words, top_indices, top_probs))"]},{"cell_type":"markdown","metadata":{},"source":["Teraz zbadajmy najbardziej podobne zanurzenia dla zadanego słowa:\n\n"]},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":"[('.', 3, 0.404473215341568), (',', 4, 0.14222915470600128), ('z', 14, 0.10945753753185272), ('?', 6, 0.09583134204149246), ('w', 10, 0.050338443368673325), ('na', 12, 0.020703863352537155), ('i', 11, 0.016762692481279373), ('', 0, 0.014571071602404118), ('...', 15, 0.01453721895813942), ('', 1, 0.011769450269639492)]"}],"source":["vocab = train_dataset.vocab\nixs = torch.tensor(vocab.forward(['kłopot'])).to(device)\n\nout = model(ixs)\ntop = torch.topk(out[0], 10)\ntop_indices = top.indices.tolist()\ntop_probs = top.values.tolist()\ntop_words = vocab.lookup_tokens(top_indices)\nlist(zip(top_words, top_indices, top_probs))"]},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":"[('poszedł', 1087, 1.0), ('idziesz', 1050, 0.4907470941543579), ('przyjeżdża', 4920, 0.45242372155189514), ('pojechałam', 12784, 0.4342481195926666), ('wrócił', 1023, 0.431664377450943), ('dobrać', 10351, 0.4312002956867218), ('stałeś', 5738, 0.4258835017681122), ('poszła', 1563, 0.41979148983955383), ('trafiłam', 18857, 0.4109022617340088), ('jedzie', 1674, 0.4091658890247345)]"}],"source":["cos = nn.CosineSimilarity(dim=1, eps=1e-6)\n\nembeddings = model.model[0].weight\n\nvec = embeddings[vocab['poszedł']]\n\nsimilarities = cos(vec, embeddings)\n\ntop = torch.topk(similarities, 10)\n\ntop_indices = top.indices.tolist()\ntop_probs = top.values.tolist()\ntop_words = vocab.lookup_tokens(top_indices)\nlist(zip(top_words, top_indices, top_probs))"]},{"cell_type":"markdown","metadata":{},"source":["#### Zapis przy użyciu wzoru matematycznego\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Powyżej zaprogramowaną sieć neuronową można opisać następującym wzorem:\n\n$$\\vec{y} = \\operatorname{softmax}(CE(w_{i-1}),$$\n\ngdzie:\n\n- $w_{i-1}$ to pierwszy wyraz w bigramie (poprzedzający wyraz),\n- $E(w)$ to zanurzenie (embedding) wyrazy $w$ — wektor o rozmiarze $m$,\n- $C$ to macierz o rozmiarze $|V| \\times m$, która rzutuje wektor zanurzenia w wektor o rozmiarze słownika,\n- $\\vec{y}$ to wyjściowy wektor prawdopodobieństw o rozmiarze $|V|$.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["##### Hiperparametry\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Zauważmy, że nasz model ma dwa hiperparametry:\n\n- $m$ — rozmiar zanurzenia,\n- $|V|$ — rozmiar słownika, jeśli zakładamy, że możemy sterować\n rozmiarem słownika (np. przez obcinanie słownika do zadanej liczby\n najczęstszych wyrazów i zamiany pozostałych na specjalny token, powiedzmy, ``.\n\nOczywiście możemy próbować manipulować wartościami $m$ i $|V|$ w celu\npolepszenia wyników naszego modelu.\n\n**Pytanie**: dlaczego nie ma sensu wartość $m \\approx |V|$ ? dlaczego nie ma sensu wartość $m = 1$?\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Diagram sieci\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Jako że mnożenie przez macierz ($C$) oznacza po prostu zastosowanie\nwarstwy liniowej, naszą sieć możemy interpretować jako jednowarstwową\nsieć neuronową, co można zilustrować za pomocą następującego diagramu:\n\n![img](./07_Zanurzenia_slow/bigram1.drawio.png \"Diagram prostego bigramowego neuronowego modelu języka\")\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Zanurzenie jako mnożenie przez macierz\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Uzyskanie zanurzenia ($E(w)$) zazwyczaj realizowane jest na zasadzie\nodpytania (look-up\\_). Co ciekawe, zanurzenie można intepretować jako\nmnożenie przez macierz zanurzeń (embeddingów) $E$ o rozmiarze $m \\times |V|$ — jeśli słowo będziemy na wejściu kodowali przy użyciu\nwektora z gorącą jedynką (one-hot encoding\\_), tzn. słowo $w$ zostanie\npodane na wejściu jako wektor $\\vec{1_V}(w) = [0,\\ldots,0,1,0\\ldots,0]$ o rozmiarze $|V|$\nzłożony z samych zer z wyjątkiem jedynki na pozycji odpowiadającej indeksowi wyrazu $w$ w słowniku $V$.\n\nWówczas wzór przyjmie postać:\n\n$$\\vec{y} = \\operatorname{softmax}(CE\\vec{1_V}(w_{i-1})),$$\n\ngdzie $E$ będzie tym razem macierzą $m \\times |V|$.\n\n**Pytanie**: czy $\\vec{1_V}(w)$ intepretujemy jako wektor wierszowy czy kolumnowy?\n\nW postaci diagramu można tę interpretację zilustrować w następujący sposób:\n\n![img](./07_Zanurzenia_slow/bigram2.drawio.png \"Diagram prostego bigramowego neuronowego modelu języka z wejściem w postaci one-hot\")\n\n"]}],"metadata":{"org":null,"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.5.2"}},"nbformat":4,"nbformat_minor":0} \ No newline at end of file + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Zanurzenia słów\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "W praktyce stosowalność słowosieci okazała się zaskakująco\n", + "ograniczona. Większy przełom w przetwarzaniu języka naturalnego przyniosły\n", + "wielowymiarowe reprezentacje słów, inaczej: zanurzenia słów.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### „Wymiary” słów\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Moglibyśmy zanurzyć (ang. *embed*) w wielowymiarowej przestrzeni, tzn. zdefiniować odwzorowanie\n", + "$E \\colon V \\rightarrow \\mathcal{R}^m$ dla pewnego $m$ i określić taki sposób estymowania\n", + "prawdopodobieństw $P(u|v)$, by dla par $E(v)$ i $E(v')$ oraz $E(u)$ i $E(u')$ znajdujących się w pobliżu\n", + "(według jakiejś metryki odległości, na przykład zwykłej odległości euklidesowej):\n", + "\n", + "$$P(u|v) \\approx P(u'|v').$$\n", + "\n", + "$E(u)$ nazywamy zanurzeniem (embeddingiem) słowa.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Wymiary określone z góry?\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Można by sobie wyobrazić, że $m$ wymiarów mogłoby być z góry\n", + "określonych przez lingwistę. Wymiary te byłyby związane z typowymi\n", + "„osiami” rozpatrywanymi w językoznawstwie, na przykład:\n", + "\n", + "- czy słowo jest wulgarne, pospolite, potoczne, neutralne czy książkowe?\n", + "- czy słowo jest archaiczne, wychodzące z użycia czy jest neologizmem?\n", + "- czy słowo dotyczy kobiet, czy mężczyzn (w sensie rodzaju gramatycznego i/lub\n", + " socjolingwistycznym)?\n", + "- czy słowo jest w liczbie pojedynczej czy mnogiej?\n", + "- czy słowo jest rzeczownikiem czy czasownikiem?\n", + "- czy słowo jest rdzennym słowem czy zapożyczeniem?\n", + "- czy słowo jest nazwą czy słowem pospolitym?\n", + "- czy słowo opisuje konkretną rzecz czy pojęcie abstrakcyjne?\n", + "- …\n", + "\n", + "W praktyce okazało się jednak, że lepiej, żeby komputer uczył się sam\n", + "możliwych wymiarów — z góry określamy tylko $m$ (liczbę wymiarów).\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Bigramowy model języka oparty na zanurzeniach\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Zbudujemy teraz najprostszy model język oparty na zanurzeniach. Będzie to właściwie najprostszy\n", + "**neuronowy model języka**, jako że zbudowany model można traktować jako prostą sieć neuronową.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Słownik\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "W typowym neuronowym modelu języka rozmiar słownika musi być z góry\n", + "ograniczony. Zazwyczaj jest to liczba rzędu kilkudziesięciu wyrazów —\n", + "po prostu będziemy rozpatrywać $|V|$ najczęstszych wyrazów, pozostałe zamienimy\n", + "na specjalny token `` reprezentujący nieznany (*unknown*) wyraz.\n", + "\n", + "Aby utworzyć taki słownik użyjemy gotowej klasy `Vocab` z pakietu torchtext:\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "16" + ] + } + ], + "source": [ + "from itertools import islice\n", + "import regex as re\n", + "import sys\n", + "from torchtext.vocab import build_vocab_from_iterator\n", + "\n", + "\n", + "def get_words_from_line(line):\n", + " line = line.rstrip()\n", + " yield ''\n", + " for m in re.finditer(r'[\\p{L}0-9\\*]+|\\p{P}+', line):\n", + " yield m.group(0).lower()\n", + " yield ''\n", + "\n", + "\n", + "def get_word_lines_from_file(file_name):\n", + " with open(file_name, 'r') as fh:\n", + " for line in fh:\n", + " yield get_words_from_line(line)\n", + "\n", + "vocab_size = 20000\n", + "\n", + "vocab = build_vocab_from_iterator(\n", + " get_word_lines_from_file('opensubtitlesA.pl.txt'),\n", + " max_tokens = vocab_size,\n", + " specials = [''])\n", + "\n", + "vocab['jest']" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['', '', '', 'w', 'wierzyli']" + ] + } + ], + "source": [ + "vocab.lookup_tokens([0, 1, 2, 10, 12345])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Definicja sieci\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Naszą prostą sieć neuronową zaimplementujemy używając frameworku PyTorch.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from torch import nn\n", + "import torch\n", + "\n", + "embed_size = 100\n", + "\n", + "class SimpleBigramNeuralLanguageModel(nn.Module):\n", + " def __init__(self, vocabulary_size, embedding_size):\n", + " super(SimpleBigramNeuralLanguageModel, self).__init__()\n", + " self.model = nn.Sequential(\n", + " nn.Embedding(vocabulary_size, embedding_size),\n", + " nn.Linear(embedding_size, vocabulary_size),\n", + " nn.Softmax()\n", + " )\n", + "\n", + " def forward(self, x):\n", + " return self.model(x)\n", + "\n", + "model = SimpleBigramNeuralLanguageModel(vocab_size, embed_size)\n", + "\n", + "vocab.set_default_index(vocab[''])\n", + "ixs = torch.tensor(vocab.forward(['pies']))\n", + "out[0][vocab['jest']]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Teraz wyuczmy model. Wpierw tylko potasujmy nasz plik:\n", + "\n", + " shuf < opensubtitlesA.pl.txt > opensubtitlesA.pl.shuf.txt\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from torch.utils.data import IterableDataset\n", + "import itertools\n", + "\n", + "def look_ahead_iterator(gen):\n", + " prev = None\n", + " for item in gen:\n", + " if prev is not None:\n", + " yield (prev, item)\n", + " prev = item\n", + "\n", + "class Bigrams(IterableDataset):\n", + " def __init__(self, text_file, vocabulary_size):\n", + " self.vocab = build_vocab_from_iterator(\n", + " get_word_lines_from_file(text_file),\n", + " max_tokens = vocabulary_size,\n", + " specials = [''])\n", + " self.vocab.set_default_index(self.vocab[''])\n", + " self.vocabulary_size = vocabulary_size\n", + " self.text_file = text_file\n", + "\n", + " def __iter__(self):\n", + " return look_ahead_iterator(\n", + " (self.vocab[t] for t in itertools.chain.from_iterable(get_word_lines_from_file(self.text_file))))\n", + "\n", + "train_dataset = Bigrams('opensubtitlesA.pl.shuf.txt', vocab_size)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(2, 5)" + ] + } + ], + "source": [ + "from torch.utils.data import DataLoader\n", + "\n", + "next(iter(train_dataset))" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[tensor([ 2, 5, 51, 3481, 231]), tensor([ 5, 51, 3481, 231, 4])]" + ] + } + ], + "source": [ + "from torch.utils.data import DataLoader\n", + "\n", + "next(iter(DataLoader(train_dataset, batch_size=5)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " device = 'cuda'\n", + " model = SimpleBigramNeuralLanguageModel(vocab_size, embed_size).to(device)\n", + " data = DataLoader(train_dataset, batch_size=5000)\n", + " optimizer = torch.optim.Adam(model.parameters())\n", + " criterion = torch.nn.NLLLoss()\n", + " \n", + " model.train()\n", + " step = 0\n", + " for x, y in data:\n", + " x = x.to(device)\n", + " y = y.to(device)\n", + " optimizer.zero_grad()\n", + " ypredicted = model(x)\n", + " loss = criterion(torch.log(ypredicted), y)\n", + " if step % 100 == 0:\n", + " print(step, loss)\n", + " step += 1\n", + " loss.backward()\n", + " optimizer.step()\n", + " \n", + " torch.save(model.state_dict(), 'model1.bin')\n", + "\n", + "Policzmy najbardziej prawdopodobne kontynuację dla zadanego słowa:\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[('ciebie', 73, 0.1580502986907959), ('mnie', 26, 0.15395283699035645), ('', 0, 0.12862136960029602), ('nas', 83, 0.0410110242664814), ('niego', 172, 0.03281523287296295), ('niej', 245, 0.02104802615940571), ('siebie', 181, 0.020788608118891716), ('którego', 365, 0.019379809498786926), ('was', 162, 0.013852755539119244), ('wszystkich', 235, 0.01381855271756649)]" + ] + } + ], + "source": [ + "device = 'cuda'\n", + "model = SimpleBigramNeuralLanguageModel(vocab_size, embed_size).to(device)\n", + "model.load_state_dict(torch.load('model1.bin'))\n", + "model.eval()\n", + "\n", + "ixs = torch.tensor(vocab.forward(['dla'])).to(device)\n", + "\n", + "out = model(ixs)\n", + "top = torch.topk(out[0], 10)\n", + "top_indices = top.indices.tolist()\n", + "top_probs = top.values.tolist()\n", + "top_words = vocab.lookup_tokens(top_indices)\n", + "list(zip(top_words, top_indices, top_probs))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Teraz zbadajmy najbardziej podobne zanurzenia dla zadanego słowa:\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[('.', 3, 0.404473215341568), (',', 4, 0.14222915470600128), ('z', 14, 0.10945753753185272), ('?', 6, 0.09583134204149246), ('w', 10, 0.050338443368673325), ('na', 12, 0.020703863352537155), ('i', 11, 0.016762692481279373), ('', 0, 0.014571071602404118), ('...', 15, 0.01453721895813942), ('', 1, 0.011769450269639492)]" + ] + } + ], + "source": [ + "vocab = train_dataset.vocab\n", + "ixs = torch.tensor(vocab.forward(['kłopot'])).to(device)\n", + "\n", + "out = model(ixs)\n", + "top = torch.topk(out[0], 10)\n", + "top_indices = top.indices.tolist()\n", + "top_probs = top.values.tolist()\n", + "top_words = vocab.lookup_tokens(top_indices)\n", + "list(zip(top_words, top_indices, top_probs))" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[('poszedł', 1087, 1.0), ('idziesz', 1050, 0.4907470941543579), ('przyjeżdża', 4920, 0.45242372155189514), ('pojechałam', 12784, 0.4342481195926666), ('wrócił', 1023, 0.431664377450943), ('dobrać', 10351, 0.4312002956867218), ('stałeś', 5738, 0.4258835017681122), ('poszła', 1563, 0.41979148983955383), ('trafiłam', 18857, 0.4109022617340088), ('jedzie', 1674, 0.4091658890247345)]" + ] + } + ], + "source": [ + "cos = nn.CosineSimilarity(dim=1, eps=1e-6)\n", + "\n", + "embeddings = model.model[0].weight\n", + "\n", + "vec = embeddings[vocab['poszedł']]\n", + "\n", + "similarities = cos(vec, embeddings)\n", + "\n", + "top = torch.topk(similarities, 10)\n", + "\n", + "top_indices = top.indices.tolist()\n", + "top_probs = top.values.tolist()\n", + "top_words = vocab.lookup_tokens(top_indices)\n", + "list(zip(top_words, top_indices, top_probs))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Zapis przy użyciu wzoru matematycznego\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Powyżej zaprogramowaną sieć neuronową można opisać następującym wzorem:\n", + "\n", + "$$\\vec{y} = \\operatorname{softmax}(CE(w_{i-1}),$$\n", + "\n", + "gdzie:\n", + "\n", + "- $w_{i-1}$ to pierwszy wyraz w bigramie (poprzedzający wyraz),\n", + "- $E(w)$ to zanurzenie (embedding) wyrazy $w$ — wektor o rozmiarze $m$,\n", + "- $C$ to macierz o rozmiarze $|V| \\times m$, która rzutuje wektor zanurzenia w wektor o rozmiarze słownika,\n", + "- $\\vec{y}$ to wyjściowy wektor prawdopodobieństw o rozmiarze $|V|$.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Hiperparametry\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Zauważmy, że nasz model ma dwa hiperparametry:\n", + "\n", + "- $m$ — rozmiar zanurzenia,\n", + "- $|V|$ — rozmiar słownika, jeśli zakładamy, że możemy sterować\n", + " rozmiarem słownika (np. przez obcinanie słownika do zadanej liczby\n", + " najczęstszych wyrazów i zamiany pozostałych na specjalny token, powiedzmy, ``.\n", + "\n", + "Oczywiście możemy próbować manipulować wartościami $m$ i $|V|$ w celu\n", + "polepszenia wyników naszego modelu.\n", + "\n", + "**Pytanie**: dlaczego nie ma sensu wartość $m \\approx |V|$ ? dlaczego nie ma sensu wartość $m = 1$?\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Diagram sieci\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Jako że mnożenie przez macierz ($C$) oznacza po prostu zastosowanie\n", + "warstwy liniowej, naszą sieć możemy interpretować jako jednowarstwową\n", + "sieć neuronową, co można zilustrować za pomocą następującego diagramu:\n", + "\n", + "![img](./07_Zanurzenia_slow/bigram1.drawio.png \"Diagram prostego bigramowego neuronowego modelu języka\")\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Zanurzenie jako mnożenie przez macierz\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Uzyskanie zanurzenia ($E(w)$) zazwyczaj realizowane jest na zasadzie\n", + "odpytania (look-up\\_). Co ciekawe, zanurzenie można intepretować jako\n", + "mnożenie przez macierz zanurzeń (embeddingów) $E$ o rozmiarze $m \\times |V|$ — jeśli słowo będziemy na wejściu kodowali przy użyciu\n", + "wektora z gorącą jedynką (one-hot encoding\\_), tzn. słowo $w$ zostanie\n", + "podane na wejściu jako wektor $\\vec{1_V}(w) = [0,\\ldots,0,1,0\\ldots,0]$ o rozmiarze $|V|$\n", + "złożony z samych zer z wyjątkiem jedynki na pozycji odpowiadającej indeksowi wyrazu $w$ w słowniku $V$.\n", + "\n", + "Wówczas wzór przyjmie postać:\n", + "\n", + "$$\\vec{y} = \\operatorname{softmax}(CE\\vec{1_V}(w_{i-1})),$$\n", + "\n", + "gdzie $E$ będzie tym razem macierzą $m \\times |V|$.\n", + "\n", + "**Pytanie**: czy $\\vec{1_V}(w)$ intepretujemy jako wektor wierszowy czy kolumnowy?\n", + "\n", + "W postaci diagramu można tę interpretację zilustrować w następujący sposób:\n", + "\n", + "![img](./07_Zanurzenia_slow/bigram2.drawio.png \"Diagram prostego bigramowego neuronowego modelu języka z wejściem w postaci one-hot\")\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.2" + }, + "org": null + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/wyk/08_Neuronowy_ngramowy_model.ipynb b/wyk/08_Neuronowy_ngramowy_model.ipynb index 607c5bc..a9a1212 100644 --- a/wyk/08_Neuronowy_ngramowy_model.ipynb +++ b/wyk/08_Neuronowy_ngramowy_model.ipynb @@ -1,4 +1,3 @@ - { "cells": [ { @@ -16,4 +15,728 @@ "\n" ] }, -{"cell_type":"markdown","metadata":{},"source":["## Neuronowy n-gramowy model języka\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Omówiony w poprzedniej części neuronowy bigramowy model języka\nwarunkuje kolejny wyraz jedynie względem bezpośrednio poprzedzającego\n— jak w każdym bigramowym modelu przyjmujemy założenie, że $w_i$\nzależy tylko od $w_{i-1}$. Rzecz jasna jest to bardzo duże\nograniczenie, w rzeczywistości bardzo często prawdopodobieństwo\nkolejnego wyrazu zależy od wyrazu dwie, trzy, cztery itd. pozycje\nwstecz czy w ogólności od wszystkich wyrazów poprzedzających (bez\nwzględu na ich pozycje).\n\n**Pytanie**: Wskaż zależności o zasięgu większym niż 1 wyraz w zdaniu\n/Zatopieni w kłębach dymu cygar i pochyleni nad butelkami z ciemnego\nszkła obywatele tej dzielnicy, jedni zakładali się o wygranę lub\nprzegranę Anglii, drudzy o bankructwo Wokulskiego; jedni nazywali\ngeniuszem Bismarcka, drudzy — awanturnikiem Wokulskiego; jedni\nkrytykowali postępowanie prezydenta MacMahona, inni twierdzili, że\nWokulski jest zdecydowanym wariatem, jeżeli nie czymś gorszym…/\n\n"]},{"cell_type":"markdown","metadata":{},"source":["### Trigramowy neuronowy model języka\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Spróbujmy najpierw rozszerzyć nasz model na trigramy, to znaczy\nbędziemy przewidywać słowo $w_i$ na podstawie słów $w_{i-2}$ i\n$w_{i-1}$.\n\nNajprostsze rozwiązanie polegałoby na zanurzeniu pary $(w_{i-2},\nw_{i-1})$ w całości i postępowaniu jak w przypadku modelu bigramowego.\nByłoby to jednak zupełnie niepraktyczne, jako że:\n\n- liczba zanurzeń do wyuczenia byłaby olbrzymia ($|V|^2$ — byłoby to\n ewentualnie akceptowalne dla modeli operujących na krótszych\n jednostkach niż słowa, np. na znakach),\n- w szczególności zanurzenia dla par $(v, u)$, $(u, v)$, $(u, u)$ i\n $(v, v)$ nie miałyby ze sobą nic wspólnego.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Konketanacja zanurzeń\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Właściwsze rozwiązanie polega na zanurzeniu dalej pojedynczych słów i\nnastępnie ich **konkatenowaniu**.\n\nPrzypomnijmy, że konkatenacja wektorów $\\vec{x_1}$ i $\\vec{x_2}$ to wektor o rozmiarze\n$|\\vec{x_1}| + |\\vec{x_2}|$ powstały ze „sklejania” wektorów $\\vec{x_1}$ i $\\vec{x_2}$.\nKonkatenację wektorów $\\vec{x_1}$ i $\\vec{x_2}$ będziemy oznaczać za pomocą $[\\vec{x_1}, \\vec{x_2}]$.\n\nPrzykład: jeśli $\\vec{x_1} = [-1, 2, 0]$ i $\\vec{x_2} = [3, -3]$,\nwówczas $[\\vec{x_1}, \\vec{x_2}] = [-1, 2, 0, 3, -3]$\n\nOznacza to, że nasza macierz „kontekstowa” $C$ powinna mieć w modelu trigramowym rozmiar nie\n$|V| \\times m$, lecz $|V| \\times (m+m)$ = $|V| \\times 2m$ i wyjście będzie zdefiniowane za pomocą wzoru:\n\n$$\\vec{y} = \\operatorname{softmax}(C[E(w_{i-2}),E(w_{i-1})]),$$\n\nco można przedstawić za pomocą następującego schematu:\n\n![img](./08_Neuronowy_ngramowy_model/trigram1.drawio.png \"Diagram prostego bigramowego neuronowego modelu języka\")\n\n"]},{"cell_type":"markdown","metadata":{},"source":["##### Rozbicie macierzy $C$\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Zamiast mnożyć macierz $C$ przez konkatenację dwóch wektorów, można\nrozbić macierz $C$ na dwie, powiedzmy $C_{-2}$ i $C_{-1}$, przemnażać\nje osobno przez odpowiadające im wektory i następnie **dodać** macierze,\ntak aby:\n\n$$C[E(w_{i-2}),E(w_{i-1})] = C_{-2}E(w_{i-2}) + C_{-1}E(w_{i-1}).$$\n\nMacierze $C_{-2}$ i $C_{-1}$ będą miały rozmiar $|V| \\times m$.\n\nPrzy tym podejściu możemy powiedzieć, że ostatni i przedostatni wyraz\nmają swoje osobne macierze o potencjalnie różnych wagach — co ma sens,\njako że na inne aspekty zwracamy uwagę przewidując kolejne słowo na\npodstawie wyrazu bezpośrednio poprzedzającego, a na inne — na\npodstawie słowa występującego dwie pozycje wcześniej.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["### Uogólnienie na $n$-gramowy model języka dla dowolnego $n$\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Łatwo uogólnić opisany wyżej trigramowy model języka dla dowolnego $n$.\nUogólniony model można przedstawić za pomocą wzoru:\n\n$$\\vec{y} = \\operatorname{softmax}(C[E(w_{i-n+1}),\\dots,E(w_{i-1})]),$$\n\ngdzie macierz $C$ ma rozmiar $|V| \\times nm$ lub za pomocą wzoru:\n\n$$\\vec{y} = \\operatorname{softmax}(C_{-(n-1)}E(w_{i-n+1}) + \\dots + C_{-1}E(w_{i-1}),$$\n\ngdzie macierze $C_{-(n-1)}$, …, $C_{-1}$ mają rozmiary $|V| \\times m$.\n\nPor. diagram:\n\n![img](./08_Neuronowy_ngramowy_model/ngram.drawio.png \"Diagram prostego n-gramowego neuronowego modelu języka\")\n\n"]},{"cell_type":"markdown","metadata":{},"source":["### Dodanie kolejnej warstwy\n\n"]},{"cell_type":"markdown","metadata":{},"source":["W wypadku trigramowego czy — ogólniej — n-gramowego modelu języka dla\n$n \\geq 3$ warto dodać kolejną (**ukrytą**) warstwę, na którą będziemy rzutować\nskonkatenowane embeddingi, zanim zrzutujemy je do długiego wektora\nprawdopodobieństw.\n\nZakładamy, że warstwa ukryta zawiera $h$ neuronów. Wartość $h$ powinna być mniejsza\nniż $nm$ (a może nawet od $m$).\n\n**Pytanie**: Dlaczego wartość $h > nm$ nie jest racjonalnym wyborem?\n\n**Pytanie**: Dlaczego dodanie kolejnej warstwy nie ma sensu dla modelu bigramowego?\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Funkcja aktywacji\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Aby warstwa ukryta wnosiła coś nowego, na wyjściu z tej funkcji musimy (dlaczego?)\nzastosować nieliniową **funkcji aktywacji**. Zazwyczaj jako funkcji\naktywacji w sieciach neuronowych używa się funkcji ReLU albo funkcji\nsigmoidalnej. W prostych neuronowych modelach języka sprawdza się też\n**tangens hiperboliczny** (tgh, w literaturze anglojęzycznej tanh):\n\n$$\\operatorname{tgh}(x) = \\frac{e^x - e^{-x}}{e^x + e^{-x}}.$$\n\n"]},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[{"data":{"image/png":"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","text/plain":""},"metadata":{},"output_type":"display_data"}],"source":["import matplotlib.pyplot as plt\nimport torch\nimport torch.nn as nn\n\nx = torch.linspace(-5,5,100)\nplt.xlabel(\"x\")\nplt.ylabel(\"y\")\na = torch.Tensor(x.size()[0]).fill_(2.)\nm = torch.stack([x, a])\nplt.plot(x, nn.functional.tanh(m)[0])\nfname = '08_Neuronowy_ngramowy_model/tanh.png'\nplt.savefig(fname)\nfname"]},{"cell_type":"markdown","metadata":{},"source":["##### Tangens hiperboliczny zastosowany dla wektora\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Tangens hiperboliczny wektora będzie po prostu wektorem tangensów\nhiperbolicznych poszczególnych wartości.\n\n"]},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[],"source":["import torch\nimport torch.nn as nn\n\nv = torch.Tensor([-100, -2.0, 0.0, 0.5, 1000.0])\nnn.functional.tanh(v)"]},{"cell_type":"markdown","metadata":{},"source":["[[[tensor](tensor)([-1.0000, -0.9640, 0.0000, 0.4621, 1.0000])]]\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Wzór i schemat dwuwarstwowego n-gramowego neuronowego modelu języka\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Dwuwarstwowy model języka będzie określony następującym wzorem:\n\n$$\\vec{y} = \\operatorname{softmax}(C\\operatorname{tgh}(W[E(w_{i-n+1}),\\dots,E(w_{i-1})])),$$\n\ngdzie:\n\n- $W$ jest wyuczalną macierzą wag o rozmiarze $h \\times nm$,\n- $C$ będzie macierzą o rozmiarze $|V| \\times h$.\n\nZmodyfikowaną sieć można przedstawić za pomocą następującego schematu:\n\n![img](./08_Neuronowy_ngramowy_model/ngram-tgh.drawio.png \"Dwuwarstwowy n-gramowy neuronowy model języka\")\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Liczba wag w modelu dwuwarstwowym\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Na wagi w modelu dwuwarstwowym składają się:\n\n- zanurzenia: $m|V|$,\n- wagi warstwy ukrytej: $hnm$,\n- wagi warstwy wyjściowej: $|V|h$,\n\na zatem łącznie:\n\n$$m|V| + hnm + |V|h$$\n\nJeśli $h \\approx m$ (co jest realistyczną opcją), wówczas otrzymamy oszacowanie:\n\n$$O(m|V| + nm^2).$$\n\nZauważmy, że względem $n$ oznacza to bardzo korzystną złożoność\n$O(n)$! Oznacza to, że nasz model może działać dla dużo większych\nwartości $n$ niż tradycyjny, statystyczny n-gramowy model języka (dla którego\nwartości $n > 5$ zazwyczaj nie mają sensu).\n\n"]},{"cell_type":"markdown","metadata":{},"source":["### Model worka słów\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Jak stwierdziliśmy przed chwilą, dwuwarstwowy n-gramowy model języka\nmoże działać dla stosunkowo dużego $n$. Zauważmy jednak, że istnieje\npewna słabość tego modelu. Otóż o ile intuicyjnie ma sens odróżniać\nsłowo poprzedzające, słowo występujące dwie pozycje wstecz i zapewne\ntrzy pozycje wstecz, a zatem uczyć się osobnych macierzy $C_{-1}$,\n$C_{-2}$, $C_{-3}$ to różnica między wpływem słowa\nwystępującego cztery pozycje wstecz i pięć pozycji wstecz jest już\nraczej nieistotna; innymi słowy różnica między macierzami $C_{-4}$ i\n$C_{-5}$ będzie raczej niewielka i sieć niepotrzebnie będzie uczyła\nsię dwukrotnie podobnych wag. Im dalej wstecz, tym różnica wpływu\nbędzie jeszcze mniej istotna, można np. przypuszczać, że różnica\nmiędzy $C_{-10}$ i $C_{-13}$ nie powinna być duża.\n\nSpróbujmy najpierw zaproponować radykalne podejście, w którym nie\nbędziemy w ogóle uwzględniać pozycji słów (lub będziemy je uwzględniać\nw niewielkim stopniu), później połączymy to z omówionym wcześniej\nmodelem $n$-gramowym.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Agregacja wektorów\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Zamiast patrzeć na kilka poprzedzających słów, można przewidywać na\npodstawie **całego** ciągu słów poprzedzających odgadywane słowo. Zauważmy jednak, że\nsieć neuronowa musi mieć ustaloną strukturę, nie możemy zmieniać jej\nrozmiaru. Musimy zatem najpierw zagregować cały ciąg do wektora o\n**stałej** długości. Potrzebujemy zatem pewnej funkcji agregującej $A$, takiej by\n$A(w_1,\\dots,w_{i-1})$ było wektorem o stałej długości, niezależnie od $i$.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Worek słów\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Najprostszą funkcją agregującą jest po prostu… suma. Dodajemy po\nprostu zanurzenia słów:\n\n$$A(w_1,\\dots,w_{i-1}) = E(w_1) + \\dots + E(w_{i-1}) = \\sum_{j=1}^{i-1} E(w_j).$$\n\n**Uwaga**: zanurzenia słów nie zależą od pozycji słowa (podobnie było w wypadku n-gramowego modelu!).\n\nJeśli rozmiar zanurzenia (embeddingu) wynosi $m$, wówczas rozmiar\nwektora uzyskanego dla całego poprzedzającego tekstu wynosi również $m$.\n\nProste dodawanie wydaje się bardzo „prostacką” metodą, a jednak\nsuma wektorów słów jest **zaskakująco skuteczną metodą zanurzenia\n(embedowania) całych tekstów (doc2vec)**. Prostym wariantem dodawania jest obliczanie **średniej wektorów**:\n\n$$A(w_1,\\dots,w_{i-1}) = \\frac{E(w_1) + \\dots + E(w_{i-1})}{i-1} = \\frac{\\sum_{j=1}^{i-1} E(w_j)}{i-1}.$$\n\nTak czy siak uzyskany wektor **nie zależy od kolejności słów**\n(dodawanie jest przemienne i łączne!). Mówimy więc o **worku słów**\n(*bag of words*, *BoW*) — co ma symbolizować fakt, że słowa są\nprzemieszane, niczym produkty w torbie na zakupy.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["##### Schemat graficzny modelu typu worek słów\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Po zanurzeniu całego poprzedzającego tekstu postępujemy podobnie jak w\nmodelu bigramowym — rzutujemy embedding na długi wektor wartości, na\nktórym stosujemy funkcję softmax:\n\n![img](./08_Neuronowy_ngramowy_model/bow1.drawio.png \"Model typu worek słów\")\n\nOdpowiada to wzorowi:\n\n$$y = \\operatorname{softmax}(C\\sum_{j=1}^{i-1} E(w_j)).$$\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Jak traktować powtarzające się słowa?\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Według wzoru podanego wyżej, jeśli słowo w poprzedzającym tekście\npojawia się więcej niż raz, jego embedding zostanie zsumowany odpowiednią liczbę razy.\nNa przykład embedding tekstu *to be or not to be* będzie wynosił:\n\n$$E(\\mathrm{to}) + E(\\mathrm{be}) + E(\\mathrm{or}) + E(\\mathrm{not}) + E(\\mathrm{to}) + E(\\mathrm{be}) = 2E(\\mathrm{to}) + 2E(\\mathrm{be}) + E(\\mathrm{or}) + E(\\mathrm{not}).$$\n\nInnymi słowy, choć w worku słów nie uwzględniamy kolejności słów, to\n**liczba wystąpień** ma dla nas ciągle znaczenie. Można powiedzieć, że\ntraktujemy poprzedzający tekst jako **multizbiór** (struktura\nmatematyczna, w której nie uwzględnia się kolejności, choć zachowana\njest informacja o liczbie wystąpień).\n\n"]},{"cell_type":"markdown","metadata":{},"source":["##### Zbiór słów\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Oczywiście moglibyśmy przy agregowaniu zanurzeń pomijać powtarzające\nsię słowa, a zatem zamiast multizbioru słów rozpatrywać po prostu ich zbiór:\n\n$$A(w_1,\\dots,w_{i-1}) = \\sum_{w \\in \\{w_1,\\dots,w_{i-1}\\}} E(w).$$\n\nJest kwestią dyskusyjną, czy to lepsze czy gorsze podejście — w końcu\nliczba wystąpień np. słów *Ukraina* czy *Polska* może wpływać w jakimś\nstopniu na prawdopodobieństwo kolejnego słowa (*Kijów* czy\n*Warszawa*?).\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Worek słów a wektoryzacja tf\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Wzór na sumę zanurzeń słów można przekształcić w taki sposób, by\nsumować po wszystkich słowach ze słownika, zamiast po słowach rzeczywiście występujących w tekście:\n\n$$A(w_1,\\dots,w_{i-1}) = \\sum_{j=1}^{i-1} E(w_j) = \\sum_{w \\in V} \\#wE(w)$$\n\ngdzie $\\#w$ to liczba wystąpień słowa $w$ w ciagu $w_1,\\dots,w_{i-1}$ (w wielu przypadkach równa zero!).\n\nJeśli teraz zanurzenia będziemy reprezentować jako macierz $E$ (por. poprzedni wykład),\nwówczas sumę można przedstawić jako iloczyn macierzy $E$ i pewnego wektora:\n\n$$A(w_1,\\dots,w_{i-1}) = E(w) [\\#w^1,\\dots,\\#w^{|V|}]^T.$$\n\n(Odróżniamy $w^i$ jako $i$-ty wyraz w słowniku $V$ od $w_i$ jako $i$-tego wyraz w rozpatrywanym ciągu).\n\nZwróćmy uwagę, że wektor $[\\#w_1,\\dots,\\#w_{|V|}]$ to po prostu\nreprezentacja wektora poprzedzającego tekstu (tj. ciągu\n$(w_1,\\dots,w_{i-1})$) przy użyciu schematu wektoryzacji tf (*term\nfrequency*). Przypomnijmy, że tf to reprezentacja tekstu przy użyciu\nwektorów o rozmiarze $|V|$ — na każdej pozycji odnotowujemy liczbę wystąpień.\nWektory tf są **rzadkie**, tj. na wielu pozycjach zawierają zera.\n\nInnymi słowy, nasz model języka *bag of words* można przedstawić za pomocą wzoru:\n\n$$y = \\operatorname{softmax}(C\\operatorname{tf}(w_1,\\dots,w_{i-1})),$$\n\nco można zilustrować w następujący sposób:\n\n![img](./08_Neuronowy_ngramowy_model/bow2.drawio.png \"Model typu worek słów — alternatywna reprezentacja\")\n\nMożna stwierdzić, że zanurzenie tekstu przekształca rzadki, długi wektor\ntf w gęsty, krótki wektor.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["### Ważenie słów\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Czy wszystkie słowa są tak samo istotne? Rzecz jasna, nie:\n\n- jak już wiemy z naszych rozważań dotyczących n-gramowych modeli języka, słowa bezpośrednio\n poprzedzające odgadywany wyraz mają większy wpływ niż słowa wcześniejsze;\n intuicyjnie, wpływ słów stopniowo spada — tym bardziej, im bardziej słowo jest oddalone od słowa odgadywanego;\n- jak wiemy z wyszukiwania informacji, słowa, które występują w wielu tekstach czy dokumentach, powinny mieć\n mniejsze znaczenie, w skrajnym przypadku słowa występujące w prawie każdym tekście (*że*, *w*, *i* itd.) powinny\n być praktycznie pomijane jako *stop words* (jeśli rozpatrywać je w „masie” worka słów — oczywiście\n to, czy słowo poprzedzające odgadywane słowo to *że*, *w* czy *i* ma olbrzymie znaczenie!).\n\nZamiast po prostu dodawać zanurzenia, można operować na sumie (bądź średniej) ważonej:\n\n$$\\sum_{j=1}^{i-1} \\omega(j, w_j)E(w_j),$$\n\ngdzie $\\omega(j, w_j)$ jest pewną wagą, która może zależeć od pozycji $j$ lub samego słowa $w_j$.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Uwzględnienie pozycji\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Można w pewnym stopniu złamać „workowatość” naszej sieci przez proste\nuwzględnienie pozycji słowa, np. w taki sposób:\n\n$$\\omega(j, w_j) = \\beta^{i-j-1},$$\n\ndla pewnego hiperparametru $\\beta$. Na przykład jeśli $\\beta=0,9$,\nwówczas słowo bezpośrednio poprzedzające dane słowo ma $1 / 0,9^9 \\approx 2,58$\nwiększy wpływ niż słowo występujące 10 pozycji wstecz.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Odwrócona częstość dokumentowa\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Aby większą wagę przykładać do słów występujących w mniejszej liczbie\ndokumentów, możemy użyć, znanej z wyszukiwania informacji,\nodwrotnej częstości dokumentowej (*inverted document frequency*, *idf*):\n\n$$\\omega(j, w_j) = \\operatorname{idf}_S(w_j) = \\operatorname{log}\\frac{|S|}{\\operatorname{df}_S(w_j)},$$\n\ngdzie:\n\n- $S$ jest pewną kolekcją dokumentów czy tekstów, z którego pochodzi przedmiotowy ciąg słów,\n- $\\operatorname{df}_S(w)$ to częstość dokumentowa słowa $w$ w kolekcji $S$, tzn. odpowiedź na pytanie,\n w ilu dokumentach występuje $w$.\n\nRzecz jasna, ten sposób ważenia oznacza tak naprawdę zastosowanie wektoryzacji tf-idf zamiast tf,\nnasza sieć będzie dana zatem wzorem:\n\n$$y = \\operatorname{softmax}(C\\operatorname{tfidf}(w_1,\\dots,w_{i-1})).$$\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Bardziej skomplikowane sposoby ważenia słów\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Można oczywiście połączyć odwrotną częstość dokumentową z uwzględnieniem pozycji słowa:\n\n$$\\omega(j, w_j) = \\beta^{i-j-1}\\operatorname{idf}_S(w_j).$$\n\n**Uwaga**: „wagi” $\\omega(j, w_j)$ nie są tak naprawdę wyuczalnymi\nwagami (parametrami) naszej sieci neuronowej, terminologia może być\ntutaj myląca. Z punktu widzenia sieci neuronowej $\\omega(j, w_j)$ są\nstałe i **nie** są optymalizowane w procesie propagacji wstecznej. Innymi\nsłowy, tak zdefiniowane $\\omega(j, w_j)$ zależą tylko od:\n\n- hiperparametru $\\beta$, który może być optymalizowany już poza siecią (w procesie **hiperoptymalizacji**),\n- wartości $\\operatorname{idf}_S(w_j)$ wyliczanych wcześniej na podstawie kolekcji $S$.\n\n**Pytanie**: czy wagi $\\omega(j, w_j)$ mogłyby sensownie uwzględniać\njakieś parametry wyuczalne z całą siecią?\n\n"]},{"cell_type":"markdown","metadata":{},"source":["### Modelowanie języka przy użyciu bardziej złożonych neuronowych sieci *feed-forward*\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Można połączyć zalety obu ogólnych podejść (n-gramowego modelu i worka\nsłów) — można **równocześnie** traktować w specjalny sposób (na\nprzykład) dwa poprzedzające wyrazy, wszystkie zaś inne wyrazy\nreprezentować jako „tło” modelowane za pomocą worka słów lub podobnej\nreprezentacji. Osiągamy to poprzez konkatenację wektora\npoprzedzającego słowa, słowa występującego dwie pozycje wstecz oraz\nzagregowanego zanurzenia całego wcześniejszego tekstu:\n\n$$y = \\operatorname{softmax}(C[E(w_{i-1}),E(w_{i-2}),A(w_1,\\dots,w_{i-3})]),$$\n\nczy lepiej z dodatkową warstwą ukrytą:\n\n$$y = \\operatorname{softmax}(C\\operatorname{tgh}(W[E(w_{i-1}),E(w_{i-2}),A(w_1,\\dots,w_{i-3})])),$$\n\nW tak uzyskanym dwuwarstwowym neuronowym modelu języka, łączącym model\ntrigramowy z workiem słów, macierz $W$ ma rozmiar $h \\times 3m$.\n\n**Pytanie**: jakie mamy możliwości, jeśli zamiast przewidywać kolejne słowo, mamy za zadanie\nodgadywać słowo w luce (jak w wyzwaniach typu *word gap*)?\n\n"]},{"cell_type":"markdown","metadata":{},"source":["### Literatura\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Skuteczny n-gramowy neuronowy model języka opisano po raz pierwszy\nw pracy [A Neural Probabilistic Language Model](https://www.jmlr.org/papers/volume3/bengio03a/bengio03a.pdf) autorstwa Yoshua Bengio i in.\n\n"]}],"metadata":{"org":null,"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.5.2"}},"nbformat":4,"nbformat_minor":0} \ No newline at end of file + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Neuronowy n-gramowy model języka\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Omówiony w poprzedniej części neuronowy bigramowy model języka\n", + "warunkuje kolejny wyraz jedynie względem bezpośrednio poprzedzającego\n", + "— jak w każdym bigramowym modelu przyjmujemy założenie, że $w_i$\n", + "zależy tylko od $w_{i-1}$. Rzecz jasna jest to bardzo duże\n", + "ograniczenie, w rzeczywistości bardzo często prawdopodobieństwo\n", + "kolejnego wyrazu zależy od wyrazu dwie, trzy, cztery itd. pozycje\n", + "wstecz czy w ogólności od wszystkich wyrazów poprzedzających (bez\n", + "względu na ich pozycje).\n", + "\n", + "**Pytanie**: Wskaż zależności o zasięgu większym niż 1 wyraz w zdaniu\n", + "/Zatopieni w kłębach dymu cygar i pochyleni nad butelkami z ciemnego\n", + "szkła obywatele tej dzielnicy, jedni zakładali się o wygranę lub\n", + "przegranę Anglii, drudzy o bankructwo Wokulskiego; jedni nazywali\n", + "geniuszem Bismarcka, drudzy — awanturnikiem Wokulskiego; jedni\n", + "krytykowali postępowanie prezydenta MacMahona, inni twierdzili, że\n", + "Wokulski jest zdecydowanym wariatem, jeżeli nie czymś gorszym…/\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Trigramowy neuronowy model języka\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Spróbujmy najpierw rozszerzyć nasz model na trigramy, to znaczy\n", + "będziemy przewidywać słowo $w_i$ na podstawie słów $w_{i-2}$ i\n", + "$w_{i-1}$.\n", + "\n", + "Najprostsze rozwiązanie polegałoby na zanurzeniu pary $(w_{i-2},\n", + "w_{i-1})$ w całości i postępowaniu jak w przypadku modelu bigramowego.\n", + "Byłoby to jednak zupełnie niepraktyczne, jako że:\n", + "\n", + "- liczba zanurzeń do wyuczenia byłaby olbrzymia ($|V|^2$ — byłoby to\n", + " ewentualnie akceptowalne dla modeli operujących na krótszych\n", + " jednostkach niż słowa, np. na znakach),\n", + "- w szczególności zanurzenia dla par $(v, u)$, $(u, v)$, $(u, u)$ i\n", + " $(v, v)$ nie miałyby ze sobą nic wspólnego.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Konketanacja zanurzeń\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Właściwsze rozwiązanie polega na zanurzeniu dalej pojedynczych słów i\n", + "następnie ich **konkatenowaniu**.\n", + "\n", + "Przypomnijmy, że konkatenacja wektorów $\\vec{x_1}$ i $\\vec{x_2}$ to wektor o rozmiarze\n", + "$|\\vec{x_1}| + |\\vec{x_2}|$ powstały ze „sklejania” wektorów $\\vec{x_1}$ i $\\vec{x_2}$.\n", + "Konkatenację wektorów $\\vec{x_1}$ i $\\vec{x_2}$ będziemy oznaczać za pomocą $[\\vec{x_1}, \\vec{x_2}]$.\n", + "\n", + "Przykład: jeśli $\\vec{x_1} = [-1, 2, 0]$ i $\\vec{x_2} = [3, -3]$,\n", + "wówczas $[\\vec{x_1}, \\vec{x_2}] = [-1, 2, 0, 3, -3]$\n", + "\n", + "Oznacza to, że nasza macierz „kontekstowa” $C$ powinna mieć w modelu trigramowym rozmiar nie\n", + "$|V| \\times m$, lecz $|V| \\times (m+m)$ = $|V| \\times 2m$ i wyjście będzie zdefiniowane za pomocą wzoru:\n", + "\n", + "$$\\vec{y} = \\operatorname{softmax}(C[E(w_{i-2}),E(w_{i-1})]),$$\n", + "\n", + "co można przedstawić za pomocą następującego schematu:\n", + "\n", + "![img](./08_Neuronowy_ngramowy_model/trigram1.drawio.png \"Diagram prostego bigramowego neuronowego modelu języka\")\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Rozbicie macierzy $C$\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Zamiast mnożyć macierz $C$ przez konkatenację dwóch wektorów, można\n", + "rozbić macierz $C$ na dwie, powiedzmy $C_{-2}$ i $C_{-1}$, przemnażać\n", + "je osobno przez odpowiadające im wektory i następnie **dodać** macierze,\n", + "tak aby:\n", + "\n", + "$$C[E(w_{i-2}),E(w_{i-1})] = C_{-2}E(w_{i-2}) + C_{-1}E(w_{i-1}).$$\n", + "\n", + "Macierze $C_{-2}$ i $C_{-1}$ będą miały rozmiar $|V| \\times m$.\n", + "\n", + "Przy tym podejściu możemy powiedzieć, że ostatni i przedostatni wyraz\n", + "mają swoje osobne macierze o potencjalnie różnych wagach — co ma sens,\n", + "jako że na inne aspekty zwracamy uwagę przewidując kolejne słowo na\n", + "podstawie wyrazu bezpośrednio poprzedzającego, a na inne — na\n", + "podstawie słowa występującego dwie pozycje wcześniej.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Uogólnienie na $n$-gramowy model języka dla dowolnego $n$\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Łatwo uogólnić opisany wyżej trigramowy model języka dla dowolnego $n$.\n", + "Uogólniony model można przedstawić za pomocą wzoru:\n", + "\n", + "$$\\vec{y} = \\operatorname{softmax}(C[E(w_{i-n+1}),\\dots,E(w_{i-1})]),$$\n", + "\n", + "gdzie macierz $C$ ma rozmiar $|V| \\times nm$ lub za pomocą wzoru:\n", + "\n", + "$$\\vec{y} = \\operatorname{softmax}(C_{-(n-1)}E(w_{i-n+1}) + \\dots + C_{-1}E(w_{i-1}),$$\n", + "\n", + "gdzie macierze $C_{-(n-1)}$, …, $C_{-1}$ mają rozmiary $|V| \\times m$.\n", + "\n", + "Por. diagram:\n", + "\n", + "![img](./08_Neuronowy_ngramowy_model/ngram.drawio.png \"Diagram prostego n-gramowego neuronowego modelu języka\")\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Dodanie kolejnej warstwy\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "W wypadku trigramowego czy — ogólniej — n-gramowego modelu języka dla\n", + "$n \\geq 3$ warto dodać kolejną (**ukrytą**) warstwę, na którą będziemy rzutować\n", + "skonkatenowane embeddingi, zanim zrzutujemy je do długiego wektora\n", + "prawdopodobieństw.\n", + "\n", + "Zakładamy, że warstwa ukryta zawiera $h$ neuronów. Wartość $h$ powinna być mniejsza\n", + "niż $nm$ (a może nawet od $m$).\n", + "\n", + "**Pytanie**: Dlaczego wartość $h > nm$ nie jest racjonalnym wyborem?\n", + "\n", + "**Pytanie**: Dlaczego dodanie kolejnej warstwy nie ma sensu dla modelu bigramowego?\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Funkcja aktywacji\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Aby warstwa ukryta wnosiła coś nowego, na wyjściu z tej funkcji musimy (dlaczego?)\n", + "zastosować nieliniową **funkcji aktywacji**. Zazwyczaj jako funkcji\n", + "aktywacji w sieciach neuronowych używa się funkcji ReLU albo funkcji\n", + "sigmoidalnej. W prostych neuronowych modelach języka sprawdza się też\n", + "**tangens hiperboliczny** (tgh, w literaturze anglojęzycznej tanh):\n", + "\n", + "$$\\operatorname{tgh}(x) = \\frac{e^x - e^{-x}}{e^x + e^{-x}}.$$\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "import torch\n", + "import torch.nn as nn\n", + "\n", + "x = torch.linspace(-5,5,100)\n", + "plt.xlabel(\"x\")\n", + "plt.ylabel(\"y\")\n", + "a = torch.Tensor(x.size()[0]).fill_(2.)\n", + "m = torch.stack([x, a])\n", + "plt.plot(x, nn.functional.tanh(m)[0])\n", + "fname = '08_Neuronowy_ngramowy_model/tanh.png'\n", + "plt.savefig(fname)\n", + "fname" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Tangens hiperboliczny zastosowany dla wektora\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Tangens hiperboliczny wektora będzie po prostu wektorem tangensów\n", + "hiperbolicznych poszczególnych wartości.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import torch\n", + "import torch.nn as nn\n", + "\n", + "v = torch.Tensor([-100, -2.0, 0.0, 0.5, 1000.0])\n", + "nn.functional.tanh(v)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[[tensor](tensor)([-1.0000, -0.9640, 0.0000, 0.4621, 1.0000])]]\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Wzór i schemat dwuwarstwowego n-gramowego neuronowego modelu języka\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Dwuwarstwowy model języka będzie określony następującym wzorem:\n", + "\n", + "$$\\vec{y} = \\operatorname{softmax}(C\\operatorname{tgh}(W[E(w_{i-n+1}),\\dots,E(w_{i-1})])),$$\n", + "\n", + "gdzie:\n", + "\n", + "- $W$ jest wyuczalną macierzą wag o rozmiarze $h \\times nm$,\n", + "- $C$ będzie macierzą o rozmiarze $|V| \\times h$.\n", + "\n", + "Zmodyfikowaną sieć można przedstawić za pomocą następującego schematu:\n", + "\n", + "![img](./08_Neuronowy_ngramowy_model/ngram-tgh.drawio.png \"Dwuwarstwowy n-gramowy neuronowy model języka\")\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Liczba wag w modelu dwuwarstwowym\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Na wagi w modelu dwuwarstwowym składają się:\n", + "\n", + "- zanurzenia: $m|V|$,\n", + "- wagi warstwy ukrytej: $hnm$,\n", + "- wagi warstwy wyjściowej: $|V|h$,\n", + "\n", + "a zatem łącznie:\n", + "\n", + "$$m|V| + hnm + |V|h$$\n", + "\n", + "Jeśli $h \\approx m$ (co jest realistyczną opcją), wówczas otrzymamy oszacowanie:\n", + "\n", + "$$O(m|V| + nm^2).$$\n", + "\n", + "Zauważmy, że względem $n$ oznacza to bardzo korzystną złożoność\n", + "$O(n)$! Oznacza to, że nasz model może działać dla dużo większych\n", + "wartości $n$ niż tradycyjny, statystyczny n-gramowy model języka (dla którego\n", + "wartości $n > 5$ zazwyczaj nie mają sensu).\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Model worka słów\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Jak stwierdziliśmy przed chwilą, dwuwarstwowy n-gramowy model języka\n", + "może działać dla stosunkowo dużego $n$. Zauważmy jednak, że istnieje\n", + "pewna słabość tego modelu. Otóż o ile intuicyjnie ma sens odróżniać\n", + "słowo poprzedzające, słowo występujące dwie pozycje wstecz i zapewne\n", + "trzy pozycje wstecz, a zatem uczyć się osobnych macierzy $C_{-1}$,\n", + "$C_{-2}$, $C_{-3}$ to różnica między wpływem słowa\n", + "występującego cztery pozycje wstecz i pięć pozycji wstecz jest już\n", + "raczej nieistotna; innymi słowy różnica między macierzami $C_{-4}$ i\n", + "$C_{-5}$ będzie raczej niewielka i sieć niepotrzebnie będzie uczyła\n", + "się dwukrotnie podobnych wag. Im dalej wstecz, tym różnica wpływu\n", + "będzie jeszcze mniej istotna, można np. przypuszczać, że różnica\n", + "między $C_{-10}$ i $C_{-13}$ nie powinna być duża.\n", + "\n", + "Spróbujmy najpierw zaproponować radykalne podejście, w którym nie\n", + "będziemy w ogóle uwzględniać pozycji słów (lub będziemy je uwzględniać\n", + "w niewielkim stopniu), później połączymy to z omówionym wcześniej\n", + "modelem $n$-gramowym.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Agregacja wektorów\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Zamiast patrzeć na kilka poprzedzających słów, można przewidywać na\n", + "podstawie **całego** ciągu słów poprzedzających odgadywane słowo. Zauważmy jednak, że\n", + "sieć neuronowa musi mieć ustaloną strukturę, nie możemy zmieniać jej\n", + "rozmiaru. Musimy zatem najpierw zagregować cały ciąg do wektora o\n", + "**stałej** długości. Potrzebujemy zatem pewnej funkcji agregującej $A$, takiej by\n", + "$A(w_1,\\dots,w_{i-1})$ było wektorem o stałej długości, niezależnie od $i$.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Worek słów\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Najprostszą funkcją agregującą jest po prostu… suma. Dodajemy po\n", + "prostu zanurzenia słów:\n", + "\n", + "$$A(w_1,\\dots,w_{i-1}) = E(w_1) + \\dots + E(w_{i-1}) = \\sum_{j=1}^{i-1} E(w_j).$$\n", + "\n", + "**Uwaga**: zanurzenia słów nie zależą od pozycji słowa (podobnie było w wypadku n-gramowego modelu!).\n", + "\n", + "Jeśli rozmiar zanurzenia (embeddingu) wynosi $m$, wówczas rozmiar\n", + "wektora uzyskanego dla całego poprzedzającego tekstu wynosi również $m$.\n", + "\n", + "Proste dodawanie wydaje się bardzo „prostacką” metodą, a jednak\n", + "suma wektorów słów jest **zaskakująco skuteczną metodą zanurzenia\n", + "(embedowania) całych tekstów (doc2vec)**. Prostym wariantem dodawania jest obliczanie **średniej wektorów**:\n", + "\n", + "$$A(w_1,\\dots,w_{i-1}) = \\frac{E(w_1) + \\dots + E(w_{i-1})}{i-1} = \\frac{\\sum_{j=1}^{i-1} E(w_j)}{i-1}.$$\n", + "\n", + "Tak czy siak uzyskany wektor **nie zależy od kolejności słów**\n", + "(dodawanie jest przemienne i łączne!). Mówimy więc o **worku słów**\n", + "(*bag of words*, *BoW*) — co ma symbolizować fakt, że słowa są\n", + "przemieszane, niczym produkty w torbie na zakupy.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Schemat graficzny modelu typu worek słów\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Po zanurzeniu całego poprzedzającego tekstu postępujemy podobnie jak w\n", + "modelu bigramowym — rzutujemy embedding na długi wektor wartości, na\n", + "którym stosujemy funkcję softmax:\n", + "\n", + "![img](./08_Neuronowy_ngramowy_model/bow1.drawio.png \"Model typu worek słów\")\n", + "\n", + "Odpowiada to wzorowi:\n", + "\n", + "$$y = \\operatorname{softmax}(C\\sum_{j=1}^{i-1} E(w_j)).$$\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Jak traktować powtarzające się słowa?\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Według wzoru podanego wyżej, jeśli słowo w poprzedzającym tekście\n", + "pojawia się więcej niż raz, jego embedding zostanie zsumowany odpowiednią liczbę razy.\n", + "Na przykład embedding tekstu *to be or not to be* będzie wynosił:\n", + "\n", + "$$E(\\mathrm{to}) + E(\\mathrm{be}) + E(\\mathrm{or}) + E(\\mathrm{not}) + E(\\mathrm{to}) + E(\\mathrm{be}) = 2E(\\mathrm{to}) + 2E(\\mathrm{be}) + E(\\mathrm{or}) + E(\\mathrm{not}).$$\n", + "\n", + "Innymi słowy, choć w worku słów nie uwzględniamy kolejności słów, to\n", + "**liczba wystąpień** ma dla nas ciągle znaczenie. Można powiedzieć, że\n", + "traktujemy poprzedzający tekst jako **multizbiór** (struktura\n", + "matematyczna, w której nie uwzględnia się kolejności, choć zachowana\n", + "jest informacja o liczbie wystąpień).\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Zbiór słów\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Oczywiście moglibyśmy przy agregowaniu zanurzeń pomijać powtarzające\n", + "się słowa, a zatem zamiast multizbioru słów rozpatrywać po prostu ich zbiór:\n", + "\n", + "$$A(w_1,\\dots,w_{i-1}) = \\sum_{w \\in \\{w_1,\\dots,w_{i-1}\\}} E(w).$$\n", + "\n", + "Jest kwestią dyskusyjną, czy to lepsze czy gorsze podejście — w końcu\n", + "liczba wystąpień np. słów *Ukraina* czy *Polska* może wpływać w jakimś\n", + "stopniu na prawdopodobieństwo kolejnego słowa (*Kijów* czy\n", + "*Warszawa*?).\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Worek słów a wektoryzacja tf\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Wzór na sumę zanurzeń słów można przekształcić w taki sposób, by\n", + "sumować po wszystkich słowach ze słownika, zamiast po słowach rzeczywiście występujących w tekście:\n", + "\n", + "$$A(w_1,\\dots,w_{i-1}) = \\sum_{j=1}^{i-1} E(w_j) = \\sum_{w \\in V} \\#wE(w)$$\n", + "\n", + "gdzie $\\#w$ to liczba wystąpień słowa $w$ w ciagu $w_1,\\dots,w_{i-1}$ (w wielu przypadkach równa zero!).\n", + "\n", + "Jeśli teraz zanurzenia będziemy reprezentować jako macierz $E$ (por. poprzedni wykład),\n", + "wówczas sumę można przedstawić jako iloczyn macierzy $E$ i pewnego wektora:\n", + "\n", + "$$A(w_1,\\dots,w_{i-1}) = E(w) [\\#w^1,\\dots,\\#w^{|V|}]^T.$$\n", + "\n", + "(Odróżniamy $w^i$ jako $i$-ty wyraz w słowniku $V$ od $w_i$ jako $i$-tego wyraz w rozpatrywanym ciągu).\n", + "\n", + "Zwróćmy uwagę, że wektor $[\\#w_1,\\dots,\\#w_{|V|}]$ to po prostu\n", + "reprezentacja wektora poprzedzającego tekstu (tj. ciągu\n", + "$(w_1,\\dots,w_{i-1})$) przy użyciu schematu wektoryzacji tf (*term\n", + "frequency*). Przypomnijmy, że tf to reprezentacja tekstu przy użyciu\n", + "wektorów o rozmiarze $|V|$ — na każdej pozycji odnotowujemy liczbę wystąpień.\n", + "Wektory tf są **rzadkie**, tj. na wielu pozycjach zawierają zera.\n", + "\n", + "Innymi słowy, nasz model języka *bag of words* można przedstawić za pomocą wzoru:\n", + "\n", + "$$y = \\operatorname{softmax}(C\\operatorname{tf}(w_1,\\dots,w_{i-1})),$$\n", + "\n", + "co można zilustrować w następujący sposób:\n", + "\n", + "![img](./08_Neuronowy_ngramowy_model/bow2.drawio.png \"Model typu worek słów — alternatywna reprezentacja\")\n", + "\n", + "Można stwierdzić, że zanurzenie tekstu przekształca rzadki, długi wektor\n", + "tf w gęsty, krótki wektor.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Ważenie słów\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Czy wszystkie słowa są tak samo istotne? Rzecz jasna, nie:\n", + "\n", + "- jak już wiemy z naszych rozważań dotyczących n-gramowych modeli języka, słowa bezpośrednio\n", + " poprzedzające odgadywany wyraz mają większy wpływ niż słowa wcześniejsze;\n", + " intuicyjnie, wpływ słów stopniowo spada — tym bardziej, im bardziej słowo jest oddalone od słowa odgadywanego;\n", + "- jak wiemy z wyszukiwania informacji, słowa, które występują w wielu tekstach czy dokumentach, powinny mieć\n", + " mniejsze znaczenie, w skrajnym przypadku słowa występujące w prawie każdym tekście (*że*, *w*, *i* itd.) powinny\n", + " być praktycznie pomijane jako *stop words* (jeśli rozpatrywać je w „masie” worka słów — oczywiście\n", + " to, czy słowo poprzedzające odgadywane słowo to *że*, *w* czy *i* ma olbrzymie znaczenie!).\n", + "\n", + "Zamiast po prostu dodawać zanurzenia, można operować na sumie (bądź średniej) ważonej:\n", + "\n", + "$$\\sum_{j=1}^{i-1} \\omega(j, w_j)E(w_j),$$\n", + "\n", + "gdzie $\\omega(j, w_j)$ jest pewną wagą, która może zależeć od pozycji $j$ lub samego słowa $w_j$.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Uwzględnienie pozycji\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Można w pewnym stopniu złamać „workowatość” naszej sieci przez proste\n", + "uwzględnienie pozycji słowa, np. w taki sposób:\n", + "\n", + "$$\\omega(j, w_j) = \\beta^{i-j-1},$$\n", + "\n", + "dla pewnego hiperparametru $\\beta$. Na przykład jeśli $\\beta=0,9$,\n", + "wówczas słowo bezpośrednio poprzedzające dane słowo ma $1 / 0,9^9 \\approx 2,58$\n", + "większy wpływ niż słowo występujące 10 pozycji wstecz.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Odwrócona częstość dokumentowa\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Aby większą wagę przykładać do słów występujących w mniejszej liczbie\n", + "dokumentów, możemy użyć, znanej z wyszukiwania informacji,\n", + "odwrotnej częstości dokumentowej (*inverted document frequency*, *idf*):\n", + "\n", + "$$\\omega(j, w_j) = \\operatorname{idf}_S(w_j) = \\operatorname{log}\\frac{|S|}{\\operatorname{df}_S(w_j)},$$\n", + "\n", + "gdzie:\n", + "\n", + "- $S$ jest pewną kolekcją dokumentów czy tekstów, z którego pochodzi przedmiotowy ciąg słów,\n", + "- $\\operatorname{df}_S(w)$ to częstość dokumentowa słowa $w$ w kolekcji $S$, tzn. odpowiedź na pytanie,\n", + " w ilu dokumentach występuje $w$.\n", + "\n", + "Rzecz jasna, ten sposób ważenia oznacza tak naprawdę zastosowanie wektoryzacji tf-idf zamiast tf,\n", + "nasza sieć będzie dana zatem wzorem:\n", + "\n", + "$$y = \\operatorname{softmax}(C\\operatorname{tfidf}(w_1,\\dots,w_{i-1})).$$\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Bardziej skomplikowane sposoby ważenia słów\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Można oczywiście połączyć odwrotną częstość dokumentową z uwzględnieniem pozycji słowa:\n", + "\n", + "$$\\omega(j, w_j) = \\beta^{i-j-1}\\operatorname{idf}_S(w_j).$$\n", + "\n", + "**Uwaga**: „wagi” $\\omega(j, w_j)$ nie są tak naprawdę wyuczalnymi\n", + "wagami (parametrami) naszej sieci neuronowej, terminologia może być\n", + "tutaj myląca. Z punktu widzenia sieci neuronowej $\\omega(j, w_j)$ są\n", + "stałe i **nie** są optymalizowane w procesie propagacji wstecznej. Innymi\n", + "słowy, tak zdefiniowane $\\omega(j, w_j)$ zależą tylko od:\n", + "\n", + "- hiperparametru $\\beta$, który może być optymalizowany już poza siecią (w procesie **hiperoptymalizacji**),\n", + "- wartości $\\operatorname{idf}_S(w_j)$ wyliczanych wcześniej na podstawie kolekcji $S$.\n", + "\n", + "**Pytanie**: czy wagi $\\omega(j, w_j)$ mogłyby sensownie uwzględniać\n", + "jakieś parametry wyuczalne z całą siecią?\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Modelowanie języka przy użyciu bardziej złożonych neuronowych sieci *feed-forward*\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Można połączyć zalety obu ogólnych podejść (n-gramowego modelu i worka\n", + "słów) — można **równocześnie** traktować w specjalny sposób (na\n", + "przykład) dwa poprzedzające wyrazy, wszystkie zaś inne wyrazy\n", + "reprezentować jako „tło” modelowane za pomocą worka słów lub podobnej\n", + "reprezentacji. Osiągamy to poprzez konkatenację wektora\n", + "poprzedzającego słowa, słowa występującego dwie pozycje wstecz oraz\n", + "zagregowanego zanurzenia całego wcześniejszego tekstu:\n", + "\n", + "$$y = \\operatorname{softmax}(C[E(w_{i-1}),E(w_{i-2}),A(w_1,\\dots,w_{i-3})]),$$\n", + "\n", + "czy lepiej z dodatkową warstwą ukrytą:\n", + "\n", + "$$y = \\operatorname{softmax}(C\\operatorname{tgh}(W[E(w_{i-1}),E(w_{i-2}),A(w_1,\\dots,w_{i-3})])),$$\n", + "\n", + "W tak uzyskanym dwuwarstwowym neuronowym modelu języka, łączącym model\n", + "trigramowy z workiem słów, macierz $W$ ma rozmiar $h \\times 3m$.\n", + "\n", + "**Pytanie**: jakie mamy możliwości, jeśli zamiast przewidywać kolejne słowo, mamy za zadanie\n", + "odgadywać słowo w luce (jak w wyzwaniach typu *word gap*)?\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Literatura\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Skuteczny n-gramowy neuronowy model języka opisano po raz pierwszy\n", + "w pracy [A Neural Probabilistic Language Model](https://www.jmlr.org/papers/volume3/bengio03a/bengio03a.pdf) autorstwa Yoshua Bengio i in.\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.2" + }, + "org": null + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/wyk/08_Neuronowy_ngramowy_model.pdf b/wyk/08_Neuronowy_ngramowy_model.pdf new file mode 100644 index 0000000..c997ccd Binary files /dev/null and b/wyk/08_Neuronowy_ngramowy_model.pdf differ diff --git a/wyk/09_Rekurencyjny_model_jezyka.ipynb b/wyk/09_Rekurencyjny_model_jezyka.ipynb index 32b1366..2617202 100644 --- a/wyk/09_Rekurencyjny_model_jezyka.ipynb +++ b/wyk/09_Rekurencyjny_model_jezyka.ipynb @@ -1 +1,414 @@ -{"cells":[{"cell_type":"markdown","metadata":{},"source":["## Model języka oparty na rekurencyjnej sieci neuronowej\n\n"]},{"cell_type":"markdown","metadata":{},"source":["### Podejście rekurencyjne\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Na poprzednim wykładzie rozpatrywaliśmy różne funkcje\n$A(w_1,\\dots,w_{i-1})$, dzięki którym możliwe było „skompresowanie” ciągu słów\n(a właściwie ich zanurzeń) o dowolnej długości w wektor o stałej długości.\n\nFunkcję $A$ moglibyśmy zdefiniować w inny sposób, w sposób ****rekurencyjny****.\n\nOtóż moglibyśmy zdekomponować funkcję $A$ do\n\n- pewnego stanu początkowego $\\vec{s_0} \\in \\mathcal{R}^p$,\n- pewnej funkcji rekurencyjnej $R : \\mathcal{R}^p \\times \\mathcal{R}^m \\rightarrow \\mathcal{R}^p$.\n\nWówczas funkcję $A$ można będzie zdefiniować rekurencyjnie jako:\n\n$$A(w_1,\\dots,w_t) = R(A(w_1,\\dots,w_{t-1}), E(w_t)),$$\n\nprzy czym dla ciągu pustego:\n\n$$A(\\epsilon) = \\vec{s_0}$$\n\nPrzypomnijmy, że $m$ to rozmiar zanurzenia (embeddingu). Z kolei $p$ to rozmiar wektora stanu\n(często $p=m$, ale nie jest to konieczne).\n\nPrzy takim podejściu rekurencyjnym wprowadzamy niejako „strzałkę\nczasu”, możemy mówić o przetwarzaniu krok po kroku.\n\nW wypadku modelowania języka możemy końcowy wektor stanu zrzutować do wektora o rozmiarze słownika\ni zastosować softmax:\n\n$$\\vec{y} = \\operatorname{softmax}(CA(w_1,\\dots,w_{i-1})),$$\n\ngdzie $C$ jest wyuczalną macierzą o rozmiarze $|V| \\times p$.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["### Worek słów zdefiniowany rekurencyjnie\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Nietrudno zdefiniować model „worka słów” w taki rekurencyjny sposób:\n\n- $p=m$,\n- $\\vec{s_0} = [0,\\dots,0]$,\n- $R(\\vec{s}, \\vec{x}) = \\vec{s} + \\vec{x}.$\n\nDodawanie (również wektorowe) jest operacją przemienną i łączną, więc\nto rekurencyjne spojrzenie niewiele tu wnosi. Można jednak zastosować\ninną funkcję $R$, która nie jest przemienna — w ten sposób wyjdziemy poza\nnieuporządkowany worek słów.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["### Związek z programowaniem funkcyjnym\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Zauważmy, że stosowane tutaj podejście jest tożsame z zastosowaniem funkcji typu `fold`\nw językach funkcyjnych:\n\n![img](./09_Rekurencyjny_model_jezyka/fold.png \"Opis funkcji foldl w języku Haskell\")\n\nW Pythonie odpowiednik `fold` jest funkcja `reduce` z pakietu `functools`:\n\n"]},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":"18"}],"source":["from functools import reduce\n\ndef product(ns):\n return reduce(lambda a, b: a * b, ns, 1)\n\nproduct([2, 3, 1, 3])"]},{"cell_type":"markdown","metadata":{},"source":["### Sieci rekurencyjne\n\n"]},{"cell_type":"markdown","metadata":{},"source":["W jaki sposób „złamać” przemienność i wprowadzić porządek? Jedną z\nnajprostszych operacji nieprzemiennych jest konkatenacja — możemy\ndokonać konkatenacji wektora stanu i bieżącego stanu, a następnie\nzastosować jakąś prostą operację (na wyjściu musimy mieć wektor o\nrozmiarze $p$, nie $p + m$!), dobrze przy okazji „złamać” też\nliniowość operacji. Możemy po prostu zastosować rzutowanie (mnożenie\nprzez macierz) i jakąś prostą funkcję aktywacji (na przykład sigmoidę):\n\n$$R(\\vec{s}, \\vec{e}) = \\sigma(W[\\vec{s},\\vec{e}] + \\vec{b}).$$\n\nDodatkowo jeszcze wprowadziliśmy wektor obciążeń $\\vec{b}$, a zatem wyuczalne wagi obejmują:\n\n- macierz $W \\in \\mathcal{R}^p \\times \\mathcal{R}^{p+m}$,\n- wektor obciążeń $b \\in \\mathcal{R}^p$.\n\nOlbrzymią zaletą sieci rekurencyjnych jest fakt, że liczba wag nie zależy od rozmiaru wejścia!\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Zwykła sieć rekurencyjna\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Wyżej zdefiniową sieć nazywamy „zwykłą” siecią rekurencyjną (*Vanilla RNN*).\n\n**Uwaga**: przez RNN czasami rozumie się taką „zwykłą” sieć\nrekurencyjną, a czasami szerszą klasę sieci rekurencyjnych\nobejmujących również sieci GRU czy LSTM (zob. poniżej).\n\n![img](./09_Rekurencyjny_model_jezyka/rnn.drawio.png \"Schemat prostego modelu języka opartego na zwykłej sieci rekurencyjnych\")\n\n**Uwaga**: powyższy schemat nie obejmuje już „całego” działania sieci,\n tylko pojedynczy krok czasowy.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Praktyczna niestosowalność prostych sieci RNN\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Niestety w praktyce proste sieci RNN sprawiają duże trudności jeśli\nchodzi o propagację wsteczną — pojawia się zjawisko zanikającego\n(rzadziej: eksplodującego) gradientu. Dlatego zaproponowano różne\nmodyfikacje sieci RNN. Zacznijmy od omówienia stosunkowo prostej sieci GRU.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["### Sieć GRU\n\n"]},{"cell_type":"markdown","metadata":{},"source":["GRU (*Gated Recurrent Unit*) to sieć z dwiema ****bramkami**** (*gates*):\n\n- bramką resetu (*reset gate*) $\\Gamma_\\gamma \\in \\mathcal{R}^p$ — która określa, w jakim\n stopniu sieć ma pamiętać albo zapominać stan z poprzedniego kroku,\n- bramką aktualizacji (*update gate*) $\\Gamma_u \\in \\mathcal{R}^p$ — która określa wpływ\n bieżącego wyrazu na zmianę stanu.\n\nTak więc w skrajnym przypadku:\n\n- jeśli $\\Gamma_\\gamma = [0,\\dots,0]$, sieć całkowicie zapomina\n informację płynącą z poprzednich wyrazów,\n- jeśli $\\Gamma_u = [0,\\dots,0]$, sieć nie bierze pod uwagę\n bieżącego wyrazu.\n\nZauważmy, że bramki mogą selektywnie, na każdej pozycji wektora stanu,\nsterować przepływem informacji. Na przykład $\\Gamma_\\gamma =\n[0,1,\\dots,1]$ oznacza, że pierwsza pozycja wektora stanu jest\nzapominana, a pozostałe — wnoszą wkład w całości.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Wzory\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Najpierw zdefiniujmy pośredni stan $\\vec{\\xi} \\in \\mathcal{R}^p$:\n\n$$\\vec{\\xi_t} = \\operatorname{tanh}(W_{\\xi}[\\Gamma_\\gamma \\bullet c_{t-1}, E(w_t)] + b_{\\xi}),$$\n\ngdzie $\\bullet$ oznacza iloczyn Hadamarda (nie iloczyn skalarny!) dwóch wektorów:\n\n$$[x_1,\\dots,x_n] \\bullet [y_1,\\dots,y_n] = [x_1 y_1,\\dots,x_n y_n].$$\n\nObliczanie $\\vec{\\xi_t}$ bardzo przypomina zwykłą sieć rekurencyjną,\njedyna różnica polega na tym, że za pomocą bramki $\\Gamma_\\gamma$\nmodulujemy wpływ poprzedniego stanu.\n\nOstateczna wartość stanu jest średnią ważoną poprzedniego stanu i bieżącego stanu pośredniego:\n\n$$\\vec{c_t} = \\Gamma_u \\bullet \\vec{\\xi_t} + (1 - \\Gamma_u) \\bullet \\vec{c_{t-1}}.$$\n\nSkąd się biorą bramki $\\Gamma_\\gamma$ i $\\Gamma_u$? Również z poprzedniego stanu i z biężacego wyrazu.\n\n$$\\Gamma_\\gamma = \\sigma(W_\\gamma[\\vec{c_{t-1}},E(w_t)] + b_\\gamma),$$\n\n$$\\Gamma_u = \\sigma(W_u[\\vec{c_{t-1}},E(w_t)] + b_u),$$\n\n"]}],"metadata":{"org":null,"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.5.2"}},"nbformat":4,"nbformat_minor":0} \ No newline at end of file +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Model języka oparty na rekurencyjnej sieci neuronowej\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Podejście rekurencyjne\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Na poprzednim wykładzie rozpatrywaliśmy różne funkcje\n", + "$A(w_1,\\dots,w_{i-1})$, dzięki którym możliwe było „skompresowanie” ciągu słów\n", + "(a właściwie ich zanurzeń) o dowolnej długości w wektor o stałej długości.\n", + "\n", + "Funkcję $A$ moglibyśmy zdefiniować w inny sposób, w sposób ****rekurencyjny****.\n", + "\n", + "Otóż moglibyśmy zdekomponować funkcję $A$ do\n", + "\n", + "- pewnego stanu początkowego $\\vec{s_0} \\in \\mathcal{R}^p$,\n", + "- pewnej funkcji rekurencyjnej $R : \\mathcal{R}^p \\times \\mathcal{R}^m \\rightarrow \\mathcal{R}^p$.\n", + "\n", + "Wówczas funkcję $A$ można będzie zdefiniować rekurencyjnie jako:\n", + "\n", + "$$A(w_1,\\dots,w_t) = R(A(w_1,\\dots,w_{t-1}), E(w_t)),$$\n", + "\n", + "przy czym dla ciągu pustego:\n", + "\n", + "$$A(\\epsilon) = \\vec{s_0}$$\n", + "\n", + "Przypomnijmy, że $m$ to rozmiar zanurzenia (embeddingu). Z kolei $p$ to rozmiar wektora stanu\n", + "(często $p=m$, ale nie jest to konieczne).\n", + "\n", + "Przy takim podejściu rekurencyjnym wprowadzamy niejako „strzałkę\n", + "czasu”, możemy mówić o przetwarzaniu krok po kroku.\n", + "\n", + "W wypadku modelowania języka możemy końcowy wektor stanu zrzutować do wektora o rozmiarze słownika\n", + "i zastosować softmax:\n", + "\n", + "$$\\vec{y} = \\operatorname{softmax}(CA(w_1,\\dots,w_{i-1})),$$\n", + "\n", + "gdzie $C$ jest wyuczalną macierzą o rozmiarze $|V| \\times p$.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Worek słów zdefiniowany rekurencyjnie\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Nietrudno zdefiniować model „worka słów” w taki rekurencyjny sposób:\n", + "\n", + "- $p=m$,\n", + "- $\\vec{s_0} = [0,\\dots,0]$,\n", + "- $R(\\vec{s}, \\vec{x}) = \\vec{s} + \\vec{x}.$\n", + "\n", + "Dodawanie (również wektorowe) jest operacją przemienną i łączną, więc\n", + "to rekurencyjne spojrzenie niewiele tu wnosi. Można jednak zastosować\n", + "inną funkcję $R$, która nie jest przemienna — w ten sposób wyjdziemy poza\n", + "nieuporządkowany worek słów.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Związek z programowaniem funkcyjnym\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Zauważmy, że stosowane tutaj podejście jest tożsame z zastosowaniem funkcji typu `fold`\n", + "w językach funkcyjnych:\n", + "\n", + "![img](./09_Rekurencyjny_model_jezyka/fold.png \"Opis funkcji foldl w języku Haskell\")\n", + "\n", + "W Pythonie odpowiednik `fold` jest funkcja `reduce` z pakietu `functools`:\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "18" + ] + } + ], + "source": [ + "from functools import reduce\n", + "\n", + "def product(ns):\n", + " return reduce(lambda a, b: a * b, ns, 1)\n", + "\n", + "product([2, 3, 1, 3])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sieci rekurencyjne\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "W jaki sposób „złamać” przemienność i wprowadzić porządek? Jedną z\n", + "najprostszych operacji nieprzemiennych jest konkatenacja — możemy\n", + "dokonać konkatenacji wektora stanu i bieżącego stanu, a następnie\n", + "zastosować jakąś prostą operację (na wyjściu musimy mieć wektor o\n", + "rozmiarze $p$, nie $p + m$!), dobrze przy okazji „złamać” też\n", + "liniowość operacji. Możemy po prostu zastosować rzutowanie (mnożenie\n", + "przez macierz) i jakąś prostą funkcję aktywacji (na przykład sigmoidę):\n", + "\n", + "$$R(\\vec{s}, \\vec{e}) = \\sigma(W[\\vec{s},\\vec{e}] + \\vec{b}).$$\n", + "\n", + "Dodatkowo jeszcze wprowadziliśmy wektor obciążeń $\\vec{b}$, a zatem wyuczalne wagi obejmują:\n", + "\n", + "- macierz $W \\in \\mathcal{R}^p \\times \\mathcal{R}^{p+m}$,\n", + "- wektor obciążeń $b \\in \\mathcal{R}^p$.\n", + "\n", + "Olbrzymią zaletą sieci rekurencyjnych jest fakt, że liczba wag nie zależy od rozmiaru wejścia!\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Zwykła sieć rekurencyjna\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Wyżej zdefiniową sieć nazywamy „zwykłą” siecią rekurencyjną (*Vanilla RNN*).\n", + "\n", + "**Uwaga**: przez RNN czasami rozumie się taką „zwykłą” sieć\n", + "rekurencyjną, a czasami szerszą klasę sieci rekurencyjnych\n", + "obejmujących również sieci GRU czy LSTM (zob. poniżej).\n", + "\n", + "![img](./09_Rekurencyjny_model_jezyka/rnn.drawio.png \"Schemat prostego modelu języka opartego na zwykłej sieci rekurencyjnych\")\n", + "\n", + "**Uwaga**: powyższy schemat nie obejmuje już „całego” działania sieci,\n", + " tylko pojedynczy krok czasowy.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Praktyczna niestosowalność prostych sieci RNN\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Niestety w praktyce proste sieci RNN sprawiają duże trudności jeśli\n", + "chodzi o propagację wsteczną — pojawia się zjawisko zanikającego\n", + "(rzadziej: eksplodującego) gradientu. Dlatego zaproponowano różne\n", + "modyfikacje sieci RNN. Zacznijmy od omówienia stosunkowo prostej sieci GRU.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sieć GRU\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "GRU (*Gated Recurrent Unit*) to sieć z dwiema ****bramkami**** (*gates*):\n", + "\n", + "- bramką resetu (*reset gate*) $\\Gamma_\\gamma \\in \\mathcal{R}^p$ — która określa, w jakim\n", + " stopniu sieć ma pamiętać albo zapominać stan z poprzedniego kroku,\n", + "- bramką aktualizacji (*update gate*) $\\Gamma_u \\in \\mathcal{R}^p$ — która określa wpływ\n", + " bieżącego wyrazu na zmianę stanu.\n", + "\n", + "Tak więc w skrajnym przypadku:\n", + "\n", + "- jeśli $\\Gamma_\\gamma = [0,\\dots,0]$, sieć całkowicie zapomina\n", + " informację płynącą z poprzednich wyrazów,\n", + "- jeśli $\\Gamma_u = [0,\\dots,0]$, sieć nie bierze pod uwagę\n", + " bieżącego wyrazu.\n", + "\n", + "Zauważmy, że bramki mogą selektywnie, na każdej pozycji wektora stanu,\n", + "sterować przepływem informacji. Na przykład $\\Gamma_\\gamma =\n", + "[0,1,\\dots,1]$ oznacza, że pierwsza pozycja wektora stanu jest\n", + "zapominana, a pozostałe — wnoszą wkład w całości.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Wzory\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Najpierw zdefiniujmy pośredni stan $\\vec{\\xi} \\in \\mathcal{R}^p$:\n", + "\n", + "$$\\vec{\\xi_t} = \\operatorname{tanh}(W_{\\xi}[\\Gamma_\\gamma \\bullet \\vec{s_{t-1}}, E(w_t)] + b_{\\xi}),$$\n", + "\n", + "gdzie $\\bullet$ oznacza iloczyn Hadamarda (nie iloczyn skalarny!) dwóch wektorów:\n", + "\n", + "$$[x_1,\\dots,x_n] \\bullet [y_1,\\dots,y_n] = [x_1 y_1,\\dots,x_n y_n].$$\n", + "\n", + "Jak widać, obliczanie $\\vec{\\xi_t}$ bardzo przypomina zwykłą sieć rekurencyjną,\n", + "jedyna różnica polega na tym, że za pomocą bramki $\\Gamma_\\gamma$\n", + "modulujemy wpływ poprzedniego stanu.\n", + "\n", + "Ostateczna wartość stanu jest średnią ważoną poprzedniego stanu i bieżącego stanu pośredniego:\n", + "\n", + "$$\\vec{s_t} = \\Gamma_u \\bullet \\vec{\\xi_t} + (1 - \\Gamma_u) \\bullet \\vec{s_{t-1}}.$$\n", + "\n", + "Skąd się biorą bramki $\\Gamma_\\gamma$ i $\\Gamma_u$? Również z poprzedniego stanu i z biężacego wyrazu.\n", + "\n", + "$$\\Gamma_\\gamma = \\sigma(W_\\gamma[\\vec{s_{t-1}},E(w_t)] + \\vec{b_\\gamma}),$$\n", + "\n", + "$$\\Gamma_u = \\sigma(W_u[\\vec{s_{t-1}},E(w_t)] + \\vec{b_u}),$$\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sieć LSTM\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Architektura LSTM (*Long Short-Term Memory*), choć powstała wcześniej\n", + "niż GRU, jest od niej nieco bardziej skomplikowana.\n", + "\n", + "- zamiast dwóch bramek LSTM zawiera ****trzy bramki****: bramkę wejścia (*input gate*),\n", + " bramkę wyjścia (*output gate*) i bramkę zapominania (*forget gate*),\n", + "- oprócz ukrytego stanu $\\vec{s_t}$ sieć LSTM posiada również ****komórkę pamięci**** (*memory cell*),\n", + " $\\vec{c_t}$, komórka pamięci, w przeciwieństwie do stanu, zmienia się wolniej (intuicyjnie:\n", + " *jeśli nie zrobimy nic specjalnego, wartość komórki pamięci się nie zmieni*).\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Wzory\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Komórka pamięci modulowana jest za pomocą bramki zapominania ($\\Gamma_f$) i bramki\n", + "wejścia ($\\Gamma_i$), bramki te określają na ile uwzględniamy, odpowiednio,\n", + "poprzednią wartość komórki pamięci $\\vec{c_{t-1}}$ i wejście, a\n", + "właściwie wejście w połączeniu z poprzednim stanem:\n", + "\n", + "$$\\vec{c_t} = \\Gamma_f \\bullet \\vec{c_{t-1}} + \\Gamma_i \\bullet \\vec{\\xi_t},$$\n", + "\n", + "gdzie wektor pomocniczy $\\vec{\\xi_t}$ wyliczany jest w następujący sposób:\n", + "\n", + "$$\\vec{\\xi_t} = \\operatorname{tanh}(W_{\\xi}[\\vec{s_{t-1}}, E(w_t)] + \\vec{b_\\xi}.$$\n", + "\n", + "Nowa wartość stanu sieci nie zależy bezpośrednio od poprzedniej wartości stanu, lecz\n", + "jest równa komórce pamięci modulowanej bramką wyjścia:\n", + "\n", + "$$\\vec{h_t} = \\Gamma_o \\bullet \\operatorname{tanh}(\\vec{c_t}).$$\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Obliczanie bramek\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Wartości wszystkie trzech bramek są liczone w identyczny sposób (wzory\n", + "różnią się tylko macierzami wag i wektorem obciążeń):\n", + "\n", + "$$\\Gamma_f = \\sigma(W_f[\\vec{s_{t-1}}, E(w_t)] + \\vec{b_f}),$$\n", + "\n", + "$$\\Gamma_i = \\sigma(W_i[\\vec{s_{t-1}}, E(w_t)] + \\vec{b_i}),$$\n", + "\n", + "$$\\Gamma_o = \\sigma(W_o[\\vec{s_{t-1}}, E(w_t)] + \\vec{b_o}).$$\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Wartości początkowe\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Początkowe wartości stanu i komórki pamięci mogą być ustawione na zero:\n", + "\n", + "$$\\vec{s_0} = \\vec{0},$$\n", + "\n", + "$$\\vec{c_0} = \\vec{0}.$$\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Podsumowanie\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Sieci LSTM dominowały w zagadnieniach przetwarzania języka naturalnego\n", + "(ogólniej: przetwarzania sekwencji) do czasu pojawienia się\n", + "architektury Transformer w 2017 roku.\n", + "\n", + "Na sieci LSTM oparty był ELMo, jeden z pierwszych dużych\n", + "****pretrenowanych modeli języka****, dostrajanych później pod konkretne\n", + "zadania (na przykład klasyfikację tekstu), zob. artykuł [Deep\n", + "contextualized word\n", + "representations]([https://arxiv.org/pdf/1802.05365.pdf](https://arxiv.org/pdf/1802.05365.pdf)). Dokładniej\n", + "mówiąc, ELMo był siecią ****BiLSTM****, połączeniem dwóch sieci, jednej\n", + "działającej z lewej strony na prawą, drugiej — z prawej do lewej.\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.2" + }, + "org": null + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/wyk/09_Rekurencyjny_model_jezyka.org b/wyk/09_Rekurencyjny_model_jezyka.org index 03ad731..3ec2335 100644 --- a/wyk/09_Rekurencyjny_model_jezyka.org +++ b/wyk/09_Rekurencyjny_model_jezyka.org @@ -136,22 +136,84 @@ zapominana, a pozostałe — wnoszą wkład w całości. Najpierw zdefiniujmy pośredni stan $\vec{\xi} \in \mathcal{R}^p$: -$$\vec{\xi_t} = \operatorname{tanh}(W_{\xi}[\Gamma_\gamma \bullet c_{t-1}, E(w_t)] + b_{\xi}),$$ +$$\vec{\xi_t} = \operatorname{tanh}(W_{\xi}[\Gamma_\gamma \bullet \vec{s_{t-1}}, E(w_t)] + b_{\xi}),$$ gdzie $\bullet$ oznacza iloczyn Hadamarda (nie iloczyn skalarny!) dwóch wektorów: $$[x_1,\dots,x_n] \bullet [y_1,\dots,y_n] = [x_1 y_1,\dots,x_n y_n].$$ -Obliczanie $\vec{\xi_t}$ bardzo przypomina zwykłą sieć rekurencyjną, +Jak widać, obliczanie $\vec{\xi_t}$ bardzo przypomina zwykłą sieć rekurencyjną, jedyna różnica polega na tym, że za pomocą bramki $\Gamma_\gamma$ modulujemy wpływ poprzedniego stanu. Ostateczna wartość stanu jest średnią ważoną poprzedniego stanu i bieżącego stanu pośredniego: -$$\vec{c_t} = \Gamma_u \bullet \vec{\xi_t} + (1 - \Gamma_u) \bullet \vec{c_{t-1}}.$$ +$$\vec{s_t} = \Gamma_u \bullet \vec{\xi_t} + (1 - \Gamma_u) \bullet \vec{s_{t-1}}.$$ Skąd się biorą bramki $\Gamma_\gamma$ i $\Gamma_u$? Również z poprzedniego stanu i z biężacego wyrazu. -$$\Gamma_\gamma = \sigma(W_\gamma[\vec{c_{t-1}},E(w_t)] + b_\gamma),$$ +$$\Gamma_\gamma = \sigma(W_\gamma[\vec{s_{t-1}},E(w_t)] + \vec{b_\gamma}),$$ -$$\Gamma_u = \sigma(W_u[\vec{c_{t-1}},E(w_t)] + b_u),$$ +$$\Gamma_u = \sigma(W_u[\vec{s_{t-1}},E(w_t)] + \vec{b_u}),$$ + +** Sieć LSTM + +Architektura LSTM (/Long Short-Term Memory/), choć powstała wcześniej +niż GRU, jest od niej nieco bardziej skomplikowana. + +- zamiast dwóch bramek LSTM zawiera **trzy bramki**: bramkę wejścia (/input gate/), + bramkę wyjścia (/output gate/) i bramkę zapominania (/forget gate/), +- oprócz ukrytego stanu $\vec{s_t}$ sieć LSTM posiada również **komórkę pamięci** (/memory cell/), + $\vec{c_t}$, komórka pamięci, w przeciwieństwie do stanu, zmienia się wolniej (intuicyjnie: + /jeśli nie zrobimy nic specjalnego, wartość komórki pamięci się nie zmieni/). + +*** Wzory + +Komórka pamięci modulowana jest za pomocą bramki zapominania ($\Gamma_f$) i bramki +wejścia ($\Gamma_i$), bramki te określają na ile uwzględniamy, odpowiednio, +poprzednią wartość komórki pamięci $\vec{c_{t-1}}$ i wejście, a +właściwie wejście w połączeniu z poprzednim stanem: + +$$\vec{c_t} = \Gamma_f \bullet \vec{c_{t-1}} + \Gamma_i \bullet \vec{\xi_t},$$ + +gdzie wektor pomocniczy $\vec{\xi_t}$ wyliczany jest w następujący sposób: + +$$\vec{\xi_t} = \operatorname{tanh}(W_{\xi}[\vec{s_{t-1}}, E(w_t)] + \vec{b_\xi}.$$ + +Nowa wartość stanu sieci nie zależy bezpośrednio od poprzedniej wartości stanu, lecz +jest równa komórce pamięci modulowanej bramką wyjścia: + +$$\vec{h_t} = \Gamma_o \bullet \operatorname{tanh}(\vec{c_t}).$$ + +*** Obliczanie bramek + +Wartości wszystkie trzech bramek są liczone w identyczny sposób (wzory +różnią się tylko macierzami wag i wektorem obciążeń): + +$$\Gamma_f = \sigma(W_f[\vec{s_{t-1}}, E(w_t)] + \vec{b_f}),$$ + +$$\Gamma_i = \sigma(W_i[\vec{s_{t-1}}, E(w_t)] + \vec{b_i}),$$ + +$$\Gamma_o = \sigma(W_o[\vec{s_{t-1}}, E(w_t)] + \vec{b_o}).$$ + +*** Wartości początkowe + +Początkowe wartości stanu i komórki pamięci mogą być ustawione na zero: + +$$\vec{s_0} = \vec{0},$$ + +$$\vec{c_0} = \vec{0}.$$ + +*** Podsumowanie + +Sieci LSTM dominowały w zagadnieniach przetwarzania języka naturalnego +(ogólniej: przetwarzania sekwencji) do czasu pojawienia się +architektury Transformer w 2017 roku. + +Na sieci LSTM oparty był ELMo, jeden z pierwszych dużych +**pretrenowanych modeli języka**, dostrajanych później pod konkretne +zadania (na przykład klasyfikację tekstu), zob. artykuł [Deep +contextualized word +representations](https://arxiv.org/pdf/1802.05365.pdf). Dokładniej +mówiąc, ELMo był siecią **BiLSTM**, połączeniem dwóch sieci, jednej +działającej z lewej strony na prawą, drugiej — z prawej do lewej. diff --git a/wyk/10_Atencja.ipynb b/wyk/10_Atencja.ipynb new file mode 100644 index 0000000..0f8a603 --- /dev/null +++ b/wyk/10_Atencja.ipynb @@ -0,0 +1 @@ +{"cells":[{"cell_type":"markdown","metadata":{},"source":["## Atencja\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Sieci LSTM w roku 2017/2018 zostały wyparte przez nową, pod pewnymi\nwzględami prostszą, architekturę Transformer. Sieci Transformer oparte\nsą zasadniczo na prostej idei **atencji** (*attention*), pierwszy\nartykuł wprowadzający sieci Transformer nosił nawet tytuł\n[Attention Is All You Need]([https://arxiv.org/abs/1706.03762](https://arxiv.org/abs/1706.03762)).\n\nIntuicyjnie, atencja jest rodzajem uwagi, którą sieć może selektywnie\nkierować na wybrane miejsca (w modelowaniu języka: wybrane wyrazy).\n\nIdea atencji jest jednak wcześniejsza, powstała jako ulepszenie sieci\nrekurencyjnych. My omówimy ją jednak na jeszcze prostszym przykładzie\nużycia w modelowaniu języka za pomocą hybrydy modelu\nbigramowego i modelu worka słów.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["### Prosty przykład zastosowania atencji\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Wróćmy do naszego przykładu z Wykładu 8, w którym łączyliśmy $n$-gramowy\nmodel języka z workiem słów. Przyjmijmy bigramowy model języka ($n=2$), wówczas:\n\n$$y = \\operatorname{softmax}(C[E(w_{i-1}),A(w_1,\\dots,w_{i-2})]),$$\n\ngdzie $A$ była prostą agregacją (np. sumą albo średnią) embeddingów\n$E(w_1),\\dots,E(w_{i-2})$. Aby wyjść z prostego nieuporządkowanego\nmodelu worka słów, próbowaliśmy w prosty sposób uwzględnić pozycję\nwyrazów czy ich istotność (za pomocą odwrotnej częstości\ndokumentowej). Oba te sposoby niestety zupełnie nie uwzględniają kontekstu.\n\nInnymi słowy, chcielibyśmy mieć sumę ważoną zanurzeń:\n\n$$A(w_1,\\dots,j) = \\omega_1 E(w_1) + \\dots \\omega_j E(w_j) = \\sum_{k=1}^j \\omega_k E(w_k),$$\n\ntak by $\\omega_k$ w sposób bardziej zasadniczy zależały od lokalnego kontekstu, a\nnie tylko od pozycji $k$ czy słowa $w_k$. W naszym prostym przypadku\njako kontekst możemy rozpatrywać słowo bezpośrednio poprzedzające\nodgadywane słowa (kontekstem jest $w_{i-1}$).\n\nWygodnie również przyjąć, że $\\sum_{k=1}^j \\omega_k = 1$, wówczas mamy do czynienia ze średnią ważoną.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Nieznormalizowane wagi atencji\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Będziemy liczyć nieznormalizowane ****wagi atencji****\n$\\hat{\\alpha}_{k,j}$. Określają one, jak bardzo słowo $w_j$ „zwraca\nuwagę” na poszczególne, inne słowa. Innymi słowy, wagi je opisują, jak\nbardzo słowo $w_k$ pasuje do naszego kontekstu, czyli słowa $w_j$.\n\nNajprostszy sposób mierzenia dopasowania to po prostu iloczyn skalarn:\n\n$$\\hat{\\alpha}_{k,j} = E(w_k)E(w_j),$$\n\nmożna też alternatywnie złamać symetrię iloczynu skalarnego i\nwyliczać dopasowanie za pomocą prostej sieci feed-forward:\n\n$$\\hat{\\alpha}_{k,j} =\n\\vec{v}\\operatorname{tanh}(W_{\\alpha}[E(w_k),E(w_j)] +\n\\vec{b_{\\alpha}}).$$\n\nW drugim przypadku pojawiają się dodatkowe wyuczalne paramatery: macierz $W_{\\alpha}$, wektory\n$\\vec{b_{\\alpha}}$ i $\\vec{v}$.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Normalizacja wag atencji\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Jak już wspomniano, dobrze żeby wagi atencji sumowały się do 1. W tym celu możemy po prostu zastosować\nfunkcję softmax:\n\n$$\\alpha_{k,j} = \\operatorname{softmax}([\\hat{\\alpha}_{1,j},\\dots,\\hat{\\alpha}_{j-1,j}]).$$\n\nZauważmy jednak, że otrzymanego z funkcji softmax wektora\n$[\\alpha_{1,j},\\dots,\\alpha_{j-1,j}]$ tym razem nie interpretujemy jako rozkład prawdopodobieństwa.\nJest to raczej rozkład uwagi, atencji słowa $w_j$ względem innych słów.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Użycie wag atencji w prostym neuronowym modelu języka\n\n"]},{"cell_type":"markdown","metadata":{},"source":["Teraz jako wagi $\\omega$ w naszym modelu języka możemy przyjąć:\n\n$$\\omega_k = \\alpha_{k,i-1}.$$\n\nOznacza to, że z naszego worka będziemy „wyjmowali” słowa w sposób\nselektywny w zależności od wyrazu, który bezpośrednio poprzedza\nsłowo odgadywane.\n\n"]},{"cell_type":"markdown","metadata":{},"source":["#### Diagram\n\n"]},{"cell_type":"markdown","metadata":{},"source":["![img](./10_Atencja/simple-attention.drawio.png \"Atencja użyta w prostym neuronowym modelu języka\")\n\n"]}],"metadata":{"org":null,"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.5.2"}},"nbformat":4,"nbformat_minor":0} \ No newline at end of file diff --git a/wyk/10_Atencja.org b/wyk/10_Atencja.org new file mode 100644 index 0000000..e976b7c --- /dev/null +++ b/wyk/10_Atencja.org @@ -0,0 +1,287 @@ + +* Atencja + +Sieci LSTM w roku 2017/2018 zostały wyparte przez nową, pod pewnymi +względami prostszą, architekturę Transformer. Sieci Transformer oparte +są zasadniczo na prostej idei *atencji* (/attention/), pierwszy +artykuł wprowadzający sieci Transformer nosił nawet tytuł +[Attention Is All You Need](https://arxiv.org/abs/1706.03762). + +Intuicyjnie, atencja jest rodzajem uwagi, którą sieć może selektywnie +kierować na wybrane miejsca (w modelowaniu języka: wybrane wyrazy). + +Idea atencji jest jednak wcześniejsza, powstała jako ulepszenie sieci +rekurencyjnych. My omówimy ją jednak na jeszcze prostszym przykładzie +użycia w modelowaniu języka za pomocą hybrydy modelu +bigramowego i modelu worka słów. + +** Prosty przykład zastosowania atencji + +Wróćmy do naszego przykładu z Wykładu 8, w którym łączyliśmy $n$-gramowy +model języka z workiem słów. Przyjmijmy bigramowy model języka ($n=2$), wówczas: + +$$y = \operatorname{softmax}(C[E(w_{i-1}),A(w_1,\dots,w_{i-2})]),$$ + +gdzie $A$ była prostą agregacją (np. sumą albo średnią) embeddingów +$E(w_1),\dots,E(w_{i-2})$. Aby wyjść z nieuporządkowanego +modelu worka słów, próbowaliśmy w prosty sposób uwzględnić pozycję +wyrazów czy ich istotność (za pomocą odwrotnej częstości +dokumentowej). Oba te sposoby niestety zupełnie nie uwzględniają *kontekstu*. + +Innymi słowy, chcielibyśmy mieć sumę ważoną zanurzeń: + +$$A(w_1,\dots,j) = \omega_1 E(w_1) + \dots + \omega_j E(w_j) = \sum_{k=1}^j \omega_k E(w_k),$$ + +tak by $\omega_k$ w sposób bardziej zasadniczy zależały od lokalnego kontekstu, a +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ą. + +*** 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 +bardzo słowo $w_k$ pasuje do naszego kontekstu, czyli słowa $w_j$. + +Najprostszy sposób mierzenia dopasowania to po prostu iloczyn skalarny: + +$$\hat{\alpha}_{k,j} = E(w_k)E(w_j),$$ + +można też alternatywnie złamać symetrię iloczynu skalarnego i +wyliczać dopasowanie za pomocą prostej sieci feed-forward: + +$$\hat{\alpha}_{k,j} = +\vec{v}\operatorname{tanh}(W_{\alpha}[E(w_k),E(w_j)] + +\vec{b_{\alpha}}).$$ + +W drugim przypadku pojawiają się dodatkowe wyuczalne paramatery: macierz $W_{\alpha}$, wektory +$\vec{b_{\alpha}}$ i $\vec{v}$. + +*** Normalizacja wag atencji + +Jak już wspomniano, dobrze żeby wagi atencji sumowały się do 1. W tym celu możemy po prostu zastosować +funkcję softmax: + +$$\alpha_{k,j} = \operatorname{softmax}([\hat{\alpha}_{1,j},\dots,\hat{\alpha}_{j-1,j}]).$$ + +Zauważmy jednak, że otrzymanego z funkcji softmax wektora +$[\alpha_{1,j},\dots,\alpha_{j-1,j}]$ tym razem nie interpretujemy jako rozkład prawdopodobieństwa. +Jest to raczej rozkład uwagi, atencji słowa $w_j$ względem innych słów. + +*** Użycie wag atencji w prostym neuronowym modelu języka + +Teraz jako wagi $\omega$ w naszym modelu języka możemy przyjąć: + +$$\omega_k = \alpha_{k,i-1}.$$ + +Oznacza to, że z naszego worka będziemy „wyjmowali” słowa w sposób +selektywny, w zależności od wyrazu, który bezpośrednio poprzedza +słowo odgadywane. + +*** Diagram + +#+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 + +Atencję wprowadzono pierwotnie jako uzupełnienie sieci rekurencyjnej. +Potrzeba ta pojawiła się na początku rozwoju *neuronowego tłumaczenia +maszynowego* (/neural machine translation/, /NMT/), czyli tłumaczenia +maszynowego (automatycznego) realizowanego za pomocą sieci neuronowych. + +Neuronowe tłumaczenie maszynowe jest właściwie rozszerzeniem idei +modelowania języka na biteksty (teksty równoległe). Omówmy najpierw +podstawy generowania tekstu. + +*** Model języka jako generator + +Jak pamiętamy, model języka $M$ wylicza prawdopodobieństwo tekstu $w_1,\dots,w_N$: + +$$P_M(w_1,\dots,w_N) = ?.$$ + +Zazwyczaj jest to równoważne obliczaniu rozkładu prawdopodobieństwa kolejnego słowa: + +$$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 +długości tej kontynuacji). + +Najprostszy sposób wygenerowania pierwszego wyrazu dokończenia polega +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}? + +Słowo $w_{p+1}$ możemy dołączyć do prefiksu i powtórzyć procedurę: + +$$w_{p+2} = \operatorname{argmax}_w P_M(w|w_1,\dots,w_p,w_{p+1}),$$ + +i tak dalej. + +*Pytanie*: Kiedy zakończymy procedurę generowania? + +Omawiana procedura jest najprostszym sposobem, czasami nie daje +najlepszego wyniku, na przykład może pojawić się efekt „jąkania” +(model generuje w kółko ten sam wyraz), dlatego opracowano bardziej +wymyślne sposoby generowania w oparciu o modele języka. Omówimy je później. + +*** Zastosowania generatora opartego na modelu języka + +Mogłoby się wydawać, że generator tekstu ma raczej ograniczone +zastosowanie (generowanie fake newsów?). Okazuje się jednak, że +zaskakująco wiele zadań przetwarzania języka naturalnego można +przedstawić jako zadanie generowania tekstu. Przykładem jest tłumaczenie maszynowe. + +*** Tłumaczenie maszynowe jako zadanie generowania tekstu + +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 +jest uzyskanie tekstu docelowego (/target sentence/) +$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/). + +Współczesne tłumaczenie maszynowe jest oparte na metodach +statystycznych — system uczy się na podstawie obszernego zbioru +odpowiadających sobie zdań w obu językach. Taki zbiór nazywamy +korpusem równoległym (/parallel corpus/). Duży zbiór korpusów +równoległych dla wielu języków można znaleźć na stronie projektu [OPUS](https://opus.nlpl.eu/). +Zobaczmy na przykład fragment EUROPARL (protokoły Parlamentu Europejskiego): + +#+BEGIN_SRC +$ wget 'https://opus.nlpl.eu/download.php?f=Europarl/v8/moses/en-pl.txt.zip' -O en-pl.txt.zip +$ unzip en-pl.txt.zip +$ paste Europarl.en-pl.en Europarl.en-pl.pl | shuf -n 5 +The adoption of these amendments by the Committee on the Environment meant that we could place more emphasis on patients' rights to information, rather than make it an option for the pharmaceutical industries to provide that information. Przyjęcie tych poprawek przez Komisję Ochrony Środowiska Naturalnego oznaczało, że mogliśmy położyć większy nacisk na prawo pacjentów do informacji, zamiast uczynić zeń możliwość, z której branża farmaceutyczna może skorzystać w celu dostarczenia informacji. +I hope that the High Representative - who is not here today - will raise this episode with China and also with Nepal, whose own nascent democracy is kept afloat partly by EU taxpayers' money in the form of financial aid. Mam nadzieję, że nieobecna dzisiaj wysoka przedstawiciel poruszy tę kwestię w rozmowach z Chinami, ale również z Nepalem, którego młoda demokracja funkcjonuje częściowo dzięki finansowej pomocy pochodzącej z pieniędzy podatników w UE. +Immunity and privileges of Renato Brunetta (vote) Wniosek o obronę immunitetu parlamentarnego Renata Brunetty (głosowanie) +The 'new Member States' - actually, the name continues to be sort of conditional, making it easier to distinguish between the 'old' Member States and those that acceded to the EU after two enlargement rounds, owing to their particular historical background and perhaps the fact that they are poorer than the old ones."Nowe państwa członkowskie” - ta nazwa nadal ma w pewnym sensie charakter warunkowy i ułatwia rozróżnienie pomiędzy "starszymi” państwami członkowskimi oraz tymi, które przystąpiły do UE po dwóch rundach rozszerzenia, które wyróżnia ich szczególna historia, a zapewne także fakt, że są uboższe, niż starsze państwa członkowskie. +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, +tylko angielsko-polską mieszankę, to znaczy uczymy model, który najpierw modeluje prawdopodobieństwo +po stronie źródłowej (powiedzmy — angielskiej): + +#+BEGIN_SRC +The number of armed attacks also ? +#+END_SRC + +W momencie napotkania specjalnego tokenu końca zdania źródłowego (powiedzmy ~~) model +powinien nauczyć się, że musi przerzucić się na modelowanie tekstu w języku docelowym (powiedzmy — polskim): + +#+BEGIN_SRC +The number of armed attacks also rose by 200% overall.Także liczba ataków ? +#+END_SRC + +W czasie uczenia wykorzystujemy korpus równoległy traktując go po prostu jako zwykły ciągły tekst +(dodajemy tylko specjalne tokeny końca zdania źródłowego i końca zdania docelowego). + +W fazie inferencji (w tłumaczeniu maszynowym tradycyjnie nazywaną +*dekodowaniem*) zamieniamy nasz model języka w generator i podajemy +tłumaczone zdanie jako prefiks, doklejając tylko token ~~. + +**** Neuronowe modele języka jako translatory + +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. + +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 +zostać skompresowana do wektora o stałym rozmiarze. (Dlaczego? W +momencie osiągnięcia tokenu ~~ cały stan sieci to kombinacja +właściwego stanu $\vec{s_i}$ i komórki pamięci $\vec{c_i}$.) + +Neuronowe tłumaczenie oparte na sieciach LSTM działa względnie dobrze +dla krótkich zdań, dla dłuższych rezultaty są gorsze — po prostu sieć +nie jest w stanie skompresować w wektorze o stałej długości znaczenia +całego zdania. Na początku rozwoju neuronowego tłumaczenia maszynowego +opracowano kilka metod radzenia sobie z tym problemem (np. zaskakująco +dobrze działa odwrócenie zdania źródłowego — siec LSTM łatwiej zacząć +generować zdanie docelowe, jeśli niedawno „widziała” początek zdania +źródłowego, przynajmniej dla pary języków o podobnym szyku). + +Najlepsze efekty dodało dodanie atencji do modelu LSTM + +**** Atencja w sieciach rekurencyjnych + +Funkcję rekurencyjną można rozbudować o trzeci argument, w którym +podany będzie wynik działania atencji $A'$ względem ostatniego wyrazu, tj.: + +$$A(w_1,\dots,w_t) = R(A(w_1,\dots,w_{t-1}), A'(w_1,\dots,w_{t-1}), E(w_t)),$$ + +W czasie tłumaczenia model może kierować swoją uwagę na wyrazy +powiązane z aktualnie tłumaczonym fragmentem (zazwyczaj — po prostu odpowiedniki). diff --git a/wyk/10_Atencja/simple-attention.drawio b/wyk/10_Atencja/simple-attention.drawio new file mode 100644 index 0000000..76a6105 --- /dev/null +++ b/wyk/10_Atencja/simple-attention.drawio @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/wyk/10_Atencja/simple-attention.drawio.png b/wyk/10_Atencja/simple-attention.drawio.png new file mode 100644 index 0000000..3506327 Binary files /dev/null and b/wyk/10_Atencja/simple-attention.drawio.png differ