diff --git a/cw/08_Model_neuronowy_typu_word2vec.ipynb b/cw/08_Model_neuronowy_typu_word2vec.ipynb new file mode 100644 index 0000000..3c6a62b --- /dev/null +++ b/cw/08_Model_neuronowy_typu_word2vec.ipynb @@ -0,0 +1,159 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![Logo 1](https://git.wmi.amu.edu.pl/AITech/Szablon/raw/branch/master/Logotyp_AITech1.jpg)\n", + "
\n", + "

Modelowanie Języka

\n", + "

8. Model neuronowy typu word2vec [ćwiczenia]

\n", + "

Jakub Pokrywka (2022)

\n", + "
\n", + "\n", + "![Logo 2](https://git.wmi.amu.edu.pl/AITech/Szablon/raw/branch/master/Logotyp_AITech2.jpg)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Zadania" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Proszę wykonać zadanie 1 lub zadanie 2 (nie oba naraz). Zadanie 3 można zrobić niezależnie." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Zadanie 1\n", + "\n", + "Wzorując się na materiałach z wykładu stworzyć 5-gramowy model neuronowy oparty na jednym ze schematów z wykładu, np.\n", + "\n", + "\n", + "![img](bow1.drawio.png \"Model typu worek słów\")\n", + "\n", + "\n", + "Warunkiem koniecznym jest, żeby przewidywać słowo środkowe, np. Mając tekst ['Ala', 'ma', '[MASK]'\n", + " 'i', 'psa'] chcemy przewidzieć kontekst środkowego słowa (tutaj '[MASK]')\n", + "\n", + "\n", + "\n", + "\n", + "Warunki zaliczenia:\n", + "- wynik widoczny na platformie zarówno dla dev i dla test\n", + "- wynik dla dev i test lepszy (niższy) niż 6.50 (liczone przy pomocy geval)\n", + "- deadline do końca dnia 08.05\n", + "- commitując rozwiązanie proszę również umieścić rozwiązanie w pliku /run.py (czyli na szczycie katalogu). Można przekonwertować jupyter do pliku python przez File → Download as → Python. Rozwiązanie nie musi być w pythonie, może być w innym języku.\n", + "- zadania wykonujemy samodzielnie\n", + "- w nazwie commita podaj nr indeksu\n", + "- w tagach podaj **neural-network** oraz **5gram**!\n", + "- zadanie tym razem jest dla polskiego odpowiednika word-gap https://gonito.net/challenge-my-submissions/retro-gap\n", + "- metryka to LogLossHashed (praktycznie to samo, co PerlpexityHased). Przelicznik, to LogLossHased = log(PerplexityHashed). Podając równe prawd. dla każdego słowa dostaniemy 6.93, bo log(1024) = 6.93\n", + "\n", + "Punktacja:\n", + "- podstawa: 60 punktów\n", + "- 40 punktów z najlepszy wynik z 2 grup\n", + "- 20 punktów z 3 kolejno najlepszych wyników z 2 grup\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Jak stworzyć model?\n", + "- warto bazować na kodzie ze wykładu 7 Zanurzenia słów\n", + "- elementy, które na pewno trzeba będzie wykorzystać to: nn.Embedding, nn.Linear, nn.Softmax\n", + "- w odróżnieniu do materiałów z wykładu lepiej nie korzystać z nn.Sequential, tylko wszystki operacje zapisywać w model.forward. Przy użyciu sequential może być problem np. z dodawaniem lub konkatenacją tensorów" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### W jaki sposób uzyskać lepszy wynik?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Po pierwsze proszę stosować sie do rad z poprzednich cwiczeń (trenowanie przez kilka epok i monitorowanie wyniku na zbiorze deweloperskim)\n", + "- dobry start to zawsze zaczęcie od jak najprostszego modelu (czyli 1 warstwa liniowa, zwykłe dodawanie embeddingów słów) i dopiero później go rozbudowywać monitorując wynik. Jest to rada uniwersalna w uczeniu maszynowym.\n", + "- Poza tym warto wypróbować przynajmniej kilka modeli z wykładu. Mając zaimplementowany cały kod dla jednego modelu, wystarczy jedynie delikatnie zmienić architekturę modelu i wytrenować go od nowa. Cała reszta kodu zostaje bez zmian.\n", + "- warto spróbować dodanie np 2 warstw liniowych (lub nawet 3) zamiast jednej warstwy (koniecznie trzeba dodać między nimi funkcję aktywacji, np RELU).\n", + "- poza tym można zmieniać różne parametry (np. wielkość słownika, wielkość warstwy ukrytej, różne funkcje aktywacji)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Zadanie 2\n", + "\n", + "Proszę zrobić parameter Hyperparameter Tuning dla zadania 1 i zaprezentować na forum grupy razem z wnioskami\n", + "\n", + "- wymóg wyniku najlepszego modelu, conajwyżej 6.10\n", + "- wnioski nie muszą być specjalnie rozbudowane, prezentacja może trwać 3-5minut lub dłużej\n", + "- należy wybrać dla siebie metodę hypermarameter tuningu\n", + "- należy stworzyć conajmniej 10 modeli, należy pokazać wyniku dla conajmniej paru\n", + "- oczywiście kod musi być automatyczny (a nie ręcznie zmieniamy paratery), natomiast nie ma wymogu korzystania ze specjalnych bibliotek\n", + "- podstawa punktów 100\n", + "- za wynik lepszy (niższy) niż 5.50 +20 punktów\n", + "- użycie GPU na dowolnym cloud lub od WMI + 30 punktów\n", + "- termin 16.05 na zajęciach\n", + "- punkty przyznam na MS TEAMS, ale proszę zgłosić te rozwiązanie na gonito (nie trzeba w żadnym challenge ani z konkretymi tagami)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Zadanie 3\n", + "\n", + "Zaangażowanie się w pygonito.\n", + "\n", + "- https://github.com/filipggg/pygonito\n", + "- dobra okazja, żeby nauczyć się tworzyć paczki pythonowe\n", + "- wsparcie ode mnie lub prof. Gralińskiego przy tworzeniu\n", + "- może się przydać przy pracy magisterskiej (jeżeli wyzwanie będzie na gonito)\n", + "- ilość punktów zależy od zakresu pracy. Sczególnie warto samemu zaproponować co zrobić) Zakres prac i punktów do dogdania\n", + "- w celu ustalenia szczegółów proszę sie zgłosić do mnie\n", + "- termin dowolny" + ] + } + ], + "metadata": { + "author": "Jakub Pokrywka", + "email": "kubapok@wmi.amu.edu.pl", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "lang": "pl", + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + }, + "subtitle": "0.Informacje na temat przedmiotu[ćwiczenia]", + "title": "Ekstrakcja informacji", + "year": "2021" + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/wyk/07_Zanurzenia_slow.ipynb b/wyk/07_Zanurzenia_slow.ipynb new file mode 100644 index 0000000..122c577 --- /dev/null +++ b/wyk/07_Zanurzenia_slow.ipynb @@ -0,0 +1,992 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![Logo 1](https://git.wmi.amu.edu.pl/AITech/Szablon/raw/branch/master/Logotyp_AITech1.jpg)\n", + "
\n", + "

Modelowanie języka

\n", + "

7. Zanurzenia słów [wykład]

\n", + "

Filip Graliński (2022)

\n", + "
\n", + "\n", + "![Logo 2](https://git.wmi.amu.edu.pl/AITech/Szablon/raw/branch/master/Logotyp_AITech2.jpg)\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 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": 2, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/media/kuba/ssdsam/anaconda3/envs/lmzajecia/lib/python3.10/site-packages/torch/_masked/__init__.py:223: UserWarning: Failed to initialize NumPy: No module named 'numpy' (Triggered internally at /opt/conda/conda-bld/pytorch_1646755897462/work/torch/csrc/utils/tensor_numpy.cpp:68.)\n", + " example_input = torch.tensor([[-3, -2, -1], [0, 1, 2]])\n" + ] + }, + { + "data": { + "text/plain": [ + "16" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "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": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['', '', '', 'w', 'policyjny']" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "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": 4, + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'out' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "Input \u001b[0;32mIn [4]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 20\u001b[0m vocab\u001b[38;5;241m.\u001b[39mset_default_index(vocab[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m\u001b[39m\u001b[38;5;124m'\u001b[39m])\n\u001b[1;32m 21\u001b[0m ixs \u001b[38;5;241m=\u001b[39m torch\u001b[38;5;241m.\u001b[39mtensor(vocab\u001b[38;5;241m.\u001b[39mforward([\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mpies\u001b[39m\u001b[38;5;124m'\u001b[39m]))\n\u001b[0;32m---> 22\u001b[0m \u001b[43mout\u001b[49m[\u001b[38;5;241m0\u001b[39m][vocab[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mjest\u001b[39m\u001b[38;5;124m'\u001b[39m]]\n", + "\u001b[0;31mNameError\u001b[0m: name 'out' is not defined" + ] + } + ], + "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": 6, + "metadata": {}, + "outputs": [], + "source": [ + "!shuf < opensubtitlesA.pl.txt > opensubtitlesA.pl.shuf.txt" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "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": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(2, 72)" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from torch.utils.data import DataLoader\n", + "\n", + "next(iter(train_dataset))" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[tensor([ 2, 72, 615, 11, 92]), tensor([ 72, 615, 11, 92, 4])]" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from torch.utils.data import DataLoader\n", + "\n", + "next(iter(DataLoader(train_dataset, batch_size=5)))" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/media/kuba/ssdsam/anaconda3/envs/lmzajecia/lib/python3.10/site-packages/torch/nn/modules/container.py:141: UserWarning: Implicit dimension choice for softmax has been deprecated. Change the call to include dim=X as an argument.\n", + " input = module(input)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 tensor(10.2158, device='cuda:0', grad_fn=)\n", + "100 tensor(6.9743, device='cuda:0', grad_fn=)\n", + "200 tensor(6.2186, device='cuda:0', grad_fn=)\n", + "300 tensor(5.6430, device='cuda:0', grad_fn=)\n", + "400 tensor(5.3539, device='cuda:0', grad_fn=)\n", + "500 tensor(5.0689, device='cuda:0', grad_fn=)\n", + "600 tensor(4.9418, device='cuda:0', grad_fn=)\n", + "700 tensor(4.8142, device='cuda:0', grad_fn=)\n", + "800 tensor(4.6436, device='cuda:0', grad_fn=)\n", + "900 tensor(4.6770, device='cuda:0', grad_fn=)\n", + "1000 tensor(4.6069, device='cuda:0', grad_fn=)\n", + "1100 tensor(4.5514, device='cuda:0', grad_fn=)\n", + "1200 tensor(4.5288, device='cuda:0', grad_fn=)\n", + "1300 tensor(4.4578, device='cuda:0', grad_fn=)\n", + "1400 tensor(4.5290, device='cuda:0', grad_fn=)\n", + "1500 tensor(4.5229, device='cuda:0', grad_fn=)\n", + "1600 tensor(4.4973, device='cuda:0', grad_fn=)\n", + "1700 tensor(4.3793, device='cuda:0', grad_fn=)\n", + "1800 tensor(4.5056, device='cuda:0', grad_fn=)\n", + "1900 tensor(4.3709, device='cuda:0', grad_fn=)\n", + "2000 tensor(4.3841, device='cuda:0', grad_fn=)\n", + "2100 tensor(4.4515, device='cuda:0', grad_fn=)\n", + "2200 tensor(4.3367, device='cuda:0', grad_fn=)\n", + "2300 tensor(4.4187, device='cuda:0', grad_fn=)\n", + "2400 tensor(4.3672, device='cuda:0', grad_fn=)\n", + "2500 tensor(4.3117, device='cuda:0', grad_fn=)\n", + "2600 tensor(4.2908, device='cuda:0', grad_fn=)\n", + "2700 tensor(4.3188, device='cuda:0', grad_fn=)\n", + "2800 tensor(4.2870, device='cuda:0', grad_fn=)\n", + "2900 tensor(4.2855, device='cuda:0', grad_fn=)\n", + "3000 tensor(4.2927, device='cuda:0', grad_fn=)\n", + "3100 tensor(4.3358, device='cuda:0', grad_fn=)\n", + "3200 tensor(4.2719, device='cuda:0', grad_fn=)\n", + "3300 tensor(4.2606, device='cuda:0', grad_fn=)\n", + "3400 tensor(4.2953, device='cuda:0', grad_fn=)\n", + "3500 tensor(4.3175, device='cuda:0', grad_fn=)\n", + "3600 tensor(4.2448, device='cuda:0', grad_fn=)\n", + "3700 tensor(4.2430, device='cuda:0', grad_fn=)\n", + "3800 tensor(4.2586, device='cuda:0', grad_fn=)\n", + "3900 tensor(4.2905, device='cuda:0', grad_fn=)\n", + "4000 tensor(4.2455, device='cuda:0', grad_fn=)\n", + "4100 tensor(4.2214, device='cuda:0', grad_fn=)\n", + "4200 tensor(4.2325, device='cuda:0', grad_fn=)\n", + "4300 tensor(4.3036, device='cuda:0', grad_fn=)\n", + "4400 tensor(4.2335, device='cuda:0', grad_fn=)\n", + "4500 tensor(4.2377, device='cuda:0', grad_fn=)\n", + "4600 tensor(4.2109, device='cuda:0', grad_fn=)\n", + "4700 tensor(4.2942, device='cuda:0', grad_fn=)\n", + "4800 tensor(4.2234, device='cuda:0', grad_fn=)\n", + "4900 tensor(4.1918, device='cuda:0', grad_fn=)\n", + "5000 tensor(4.3084, device='cuda:0', grad_fn=)\n", + "5100 tensor(4.1666, device='cuda:0', grad_fn=)\n", + "5200 tensor(4.2307, device='cuda:0', grad_fn=)\n", + "5300 tensor(4.2050, device='cuda:0', grad_fn=)\n", + "5400 tensor(4.1853, device='cuda:0', grad_fn=)\n", + "5500 tensor(4.1917, device='cuda:0', grad_fn=)\n", + "5600 tensor(4.1453, device='cuda:0', grad_fn=)\n", + "5700 tensor(4.2423, device='cuda:0', grad_fn=)\n", + "5800 tensor(4.1972, device='cuda:0', grad_fn=)\n", + "5900 tensor(4.2143, device='cuda:0', grad_fn=)\n", + "6000 tensor(4.2172, device='cuda:0', grad_fn=)\n", + "6100 tensor(4.2463, device='cuda:0', grad_fn=)\n", + "6200 tensor(4.1756, device='cuda:0', grad_fn=)\n", + "6300 tensor(4.1223, device='cuda:0', grad_fn=)\n", + "6400 tensor(4.1852, device='cuda:0', grad_fn=)\n", + "6500 tensor(4.1559, device='cuda:0', grad_fn=)\n", + "6600 tensor(4.1833, device='cuda:0', grad_fn=)\n", + "6700 tensor(4.2090, device='cuda:0', grad_fn=)\n", + "6800 tensor(4.1896, device='cuda:0', grad_fn=)\n", + "6900 tensor(4.2057, device='cuda:0', grad_fn=)\n", + "7000 tensor(4.1523, device='cuda:0', grad_fn=)\n", + "7100 tensor(4.2645, device='cuda:0', grad_fn=)\n", + "7200 tensor(4.1974, device='cuda:0', grad_fn=)\n", + "7300 tensor(4.2031, device='cuda:0', grad_fn=)\n", + "7400 tensor(4.1613, device='cuda:0', grad_fn=)\n", + "7500 tensor(4.2018, device='cuda:0', grad_fn=)\n", + "7600 tensor(4.2197, device='cuda:0', grad_fn=)\n", + "7700 tensor(4.1976, device='cuda:0', grad_fn=)\n", + "7800 tensor(4.1650, device='cuda:0', grad_fn=)\n", + "7900 tensor(4.1380, device='cuda:0', grad_fn=)\n", + "8000 tensor(4.1014, device='cuda:0', grad_fn=)\n", + "8100 tensor(4.2058, device='cuda:0', grad_fn=)\n", + "8200 tensor(4.1514, device='cuda:0', grad_fn=)\n", + "8300 tensor(4.1187, device='cuda:0', grad_fn=)\n", + "8400 tensor(4.2438, device='cuda:0', grad_fn=)\n", + "8500 tensor(4.2094, device='cuda:0', grad_fn=)\n", + "8600 tensor(4.2077, device='cuda:0', grad_fn=)\n", + "8700 tensor(4.0819, device='cuda:0', grad_fn=)\n", + "8800 tensor(4.1766, device='cuda:0', grad_fn=)\n", + "8900 tensor(4.1805, device='cuda:0', grad_fn=)\n", + "9000 tensor(4.1847, device='cuda:0', grad_fn=)\n", + "9100 tensor(4.1929, device='cuda:0', grad_fn=)\n", + "9200 tensor(4.1434, device='cuda:0', grad_fn=)\n", + "9300 tensor(4.1678, device='cuda:0', grad_fn=)\n", + "9400 tensor(4.1699, device='cuda:0', grad_fn=)\n", + "9500 tensor(4.0885, device='cuda:0', grad_fn=)\n", + "9600 tensor(4.1544, device='cuda:0', grad_fn=)\n", + "9700 tensor(4.1828, device='cuda:0', grad_fn=)\n", + "9800 tensor(4.1314, device='cuda:0', grad_fn=)\n", + "9900 tensor(4.1473, device='cuda:0', grad_fn=)\n", + "10000 tensor(4.0948, device='cuda:0', grad_fn=)\n", + "10100 tensor(4.1396, device='cuda:0', grad_fn=)\n", + "10200 tensor(4.1999, device='cuda:0', grad_fn=)\n", + "10300 tensor(4.1027, device='cuda:0', grad_fn=)\n", + "10400 tensor(4.2049, device='cuda:0', grad_fn=)\n", + "10500 tensor(4.1470, device='cuda:0', grad_fn=)\n", + "10600 tensor(4.0974, device='cuda:0', grad_fn=)\n", + "10700 tensor(4.1239, device='cuda:0', grad_fn=)\n", + "10800 tensor(4.1381, device='cuda:0', grad_fn=)\n", + "10900 tensor(4.0569, device='cuda:0', grad_fn=)\n", + "11000 tensor(4.1138, device='cuda:0', grad_fn=)\n", + "11100 tensor(4.2053, device='cuda:0', grad_fn=)\n", + "11200 tensor(4.1404, device='cuda:0', grad_fn=)\n", + "11300 tensor(4.0741, device='cuda:0', grad_fn=)\n", + "11400 tensor(4.0090, device='cuda:0', grad_fn=)\n", + "11500 tensor(4.1568, device='cuda:0', grad_fn=)\n", + "11600 tensor(4.1498, device='cuda:0', grad_fn=)\n", + "11700 tensor(4.1052, device='cuda:0', grad_fn=)\n", + "11800 tensor(4.0600, device='cuda:0', grad_fn=)\n", + "11900 tensor(4.1274, device='cuda:0', grad_fn=)\n", + "12000 tensor(4.1346, device='cuda:0', grad_fn=)\n", + "12100 tensor(4.1024, device='cuda:0', grad_fn=)\n", + "12200 tensor(4.0966, device='cuda:0', grad_fn=)\n", + "12300 tensor(4.1036, device='cuda:0', grad_fn=)\n", + "12400 tensor(4.0127, device='cuda:0', grad_fn=)\n", + "12500 tensor(4.0575, device='cuda:0', grad_fn=)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "12600 tensor(4.1542, device='cuda:0', grad_fn=)\n", + "12700 tensor(4.1810, device='cuda:0', grad_fn=)\n", + "12800 tensor(4.1948, device='cuda:0', grad_fn=)\n", + "12900 tensor(4.1085, device='cuda:0', grad_fn=)\n", + "13000 tensor(4.1283, device='cuda:0', grad_fn=)\n", + "13100 tensor(4.1548, device='cuda:0', grad_fn=)\n", + "13200 tensor(4.1015, device='cuda:0', grad_fn=)\n", + "13300 tensor(4.1342, device='cuda:0', grad_fn=)\n", + "13400 tensor(4.0724, device='cuda:0', grad_fn=)\n", + "13500 tensor(4.1006, device='cuda:0', grad_fn=)\n", + "13600 tensor(4.0998, device='cuda:0', grad_fn=)\n", + "13700 tensor(4.1021, device='cuda:0', grad_fn=)\n", + "13800 tensor(4.1175, device='cuda:0', grad_fn=)\n", + "13900 tensor(4.1017, device='cuda:0', grad_fn=)\n", + "14000 tensor(4.1877, device='cuda:0', grad_fn=)\n", + "14100 tensor(4.1664, device='cuda:0', grad_fn=)\n", + "14200 tensor(4.1582, device='cuda:0', grad_fn=)\n", + "14300 tensor(4.1526, device='cuda:0', grad_fn=)\n", + "14400 tensor(4.1208, device='cuda:0', grad_fn=)\n", + "14500 tensor(4.0752, device='cuda:0', grad_fn=)\n", + "14600 tensor(4.1907, device='cuda:0', grad_fn=)\n", + "14700 tensor(4.0496, device='cuda:0', grad_fn=)\n", + "14800 tensor(4.1371, device='cuda:0', grad_fn=)\n", + "14900 tensor(4.1215, device='cuda:0', grad_fn=)\n", + "15000 tensor(4.1059, device='cuda:0', grad_fn=)\n", + "15100 tensor(4.0888, device='cuda:0', grad_fn=)\n", + "15200 tensor(4.1359, device='cuda:0', grad_fn=)\n", + "15300 tensor(4.1328, device='cuda:0', grad_fn=)\n", + "15400 tensor(4.1044, device='cuda:0', grad_fn=)\n", + "15500 tensor(4.1167, device='cuda:0', grad_fn=)\n", + "15600 tensor(4.0449, device='cuda:0', grad_fn=)\n", + "15700 tensor(4.1159, device='cuda:0', grad_fn=)\n", + "15800 tensor(4.1082, device='cuda:0', grad_fn=)\n", + "15900 tensor(4.1653, device='cuda:0', grad_fn=)\n", + "16000 tensor(4.1111, device='cuda:0', grad_fn=)\n", + "16100 tensor(4.0870, device='cuda:0', grad_fn=)\n", + "16200 tensor(4.1085, device='cuda:0', grad_fn=)\n", + "16300 tensor(4.1216, device='cuda:0', grad_fn=)\n", + "16400 tensor(4.1307, device='cuda:0', grad_fn=)\n", + "16500 tensor(4.0872, device='cuda:0', grad_fn=)\n", + "16600 tensor(4.0754, device='cuda:0', grad_fn=)\n", + "16700 tensor(4.0067, device='cuda:0', grad_fn=)\n", + "16800 tensor(4.0413, device='cuda:0', grad_fn=)\n", + "16900 tensor(4.1242, device='cuda:0', grad_fn=)\n", + "17000 tensor(4.1169, device='cuda:0', grad_fn=)\n", + "17100 tensor(4.0942, device='cuda:0', grad_fn=)\n", + "17200 tensor(4.1518, device='cuda:0', grad_fn=)\n", + "17300 tensor(4.0968, device='cuda:0', grad_fn=)\n", + "17400 tensor(4.0476, device='cuda:0', grad_fn=)\n", + "17500 tensor(4.0230, device='cuda:0', grad_fn=)\n", + "17600 tensor(4.1268, device='cuda:0', grad_fn=)\n", + "17700 tensor(4.0388, device='cuda:0', grad_fn=)\n", + "17800 tensor(4.1741, device='cuda:0', grad_fn=)\n", + "17900 tensor(4.1147, device='cuda:0', grad_fn=)\n", + "18000 tensor(4.2020, device='cuda:0', grad_fn=)\n", + "18100 tensor(4.0304, device='cuda:0', grad_fn=)\n", + "18200 tensor(4.1171, device='cuda:0', grad_fn=)\n", + "18300 tensor(4.0945, device='cuda:0', grad_fn=)\n", + "18400 tensor(4.1019, device='cuda:0', grad_fn=)\n", + "18500 tensor(4.1301, device='cuda:0', grad_fn=)\n", + "18600 tensor(4.0979, device='cuda:0', grad_fn=)\n", + "18700 tensor(4.0755, device='cuda:0', grad_fn=)\n", + "18800 tensor(4.0760, device='cuda:0', grad_fn=)\n", + "18900 tensor(4.0553, device='cuda:0', grad_fn=)\n", + "19000 tensor(4.1530, device='cuda:0', grad_fn=)\n", + "19100 tensor(4.1403, device='cuda:0', grad_fn=)\n", + "19200 tensor(4.1449, device='cuda:0', grad_fn=)\n", + "19300 tensor(4.0105, device='cuda:0', grad_fn=)\n", + "19400 tensor(4.0742, device='cuda:0', grad_fn=)\n", + "19500 tensor(4.0666, device='cuda:0', grad_fn=)\n", + "19600 tensor(4.1549, device='cuda:0', grad_fn=)\n", + "19700 tensor(4.0930, device='cuda:0', grad_fn=)\n", + "19800 tensor(4.1271, device='cuda:0', grad_fn=)\n", + "19900 tensor(4.1169, device='cuda:0', grad_fn=)\n", + "20000 tensor(4.1053, device='cuda:0', grad_fn=)\n", + "20100 tensor(4.1070, device='cuda:0', grad_fn=)\n", + "20200 tensor(4.0848, device='cuda:0', grad_fn=)\n", + "20300 tensor(4.1330, device='cuda:0', grad_fn=)\n", + "20400 tensor(3.9828, device='cuda:0', grad_fn=)\n", + "20500 tensor(4.1411, device='cuda:0', grad_fn=)\n", + "20600 tensor(4.0537, device='cuda:0', grad_fn=)\n", + "20700 tensor(4.1171, device='cuda:0', grad_fn=)\n", + "20800 tensor(4.0510, device='cuda:0', grad_fn=)\n", + "20900 tensor(4.1230, device='cuda:0', grad_fn=)\n", + "21000 tensor(4.1241, device='cuda:0', grad_fn=)\n", + "21100 tensor(4.1600, device='cuda:0', grad_fn=)\n", + "21200 tensor(4.0699, device='cuda:0', grad_fn=)\n", + "21300 tensor(4.0870, device='cuda:0', grad_fn=)\n", + "21400 tensor(4.0774, device='cuda:0', grad_fn=)\n", + "21500 tensor(4.1492, device='cuda:0', grad_fn=)\n", + "21600 tensor(4.0883, device='cuda:0', grad_fn=)\n", + "21700 tensor(4.0358, device='cuda:0', grad_fn=)\n", + "21800 tensor(4.0569, device='cuda:0', grad_fn=)\n", + "21900 tensor(4.0832, device='cuda:0', grad_fn=)\n", + "22000 tensor(4.0827, device='cuda:0', grad_fn=)\n", + "22100 tensor(4.0534, device='cuda:0', grad_fn=)\n", + "22200 tensor(4.0173, device='cuda:0', grad_fn=)\n", + "22300 tensor(4.0549, device='cuda:0', grad_fn=)\n", + "22400 tensor(4.0613, device='cuda:0', grad_fn=)\n", + "22500 tensor(4.1058, device='cuda:0', grad_fn=)\n", + "22600 tensor(4.1230, device='cuda:0', grad_fn=)\n", + "22700 tensor(4.1114, device='cuda:0', grad_fn=)\n", + "22800 tensor(4.0541, device='cuda:0', grad_fn=)\n", + "22900 tensor(4.0732, device='cuda:0', grad_fn=)\n", + "23000 tensor(4.0983, device='cuda:0', grad_fn=)\n", + "23100 tensor(4.0547, device='cuda:0', grad_fn=)\n", + "23200 tensor(4.1198, device='cuda:0', grad_fn=)\n", + "23300 tensor(4.0687, device='cuda:0', grad_fn=)\n", + "23400 tensor(4.0676, device='cuda:0', grad_fn=)\n", + "23500 tensor(4.0834, device='cuda:0', grad_fn=)\n", + "23600 tensor(4.0996, device='cuda:0', grad_fn=)\n", + "23700 tensor(4.0791, device='cuda:0', grad_fn=)\n", + "23800 tensor(4.0700, device='cuda:0', grad_fn=)\n", + "23900 tensor(4.0388, device='cuda:0', grad_fn=)\n", + "24000 tensor(4.0625, device='cuda:0', grad_fn=)\n", + "24100 tensor(4.1095, device='cuda:0', grad_fn=)\n", + "24200 tensor(4.1589, device='cuda:0', grad_fn=)\n", + "24300 tensor(4.1565, device='cuda:0', grad_fn=)\n", + "24400 tensor(4.1396, device='cuda:0', grad_fn=)\n", + "24500 tensor(4.1642, device='cuda:0', grad_fn=)\n", + "24600 tensor(4.0868, device='cuda:0', grad_fn=)\n", + "24700 tensor(4.0770, device='cuda:0', grad_fn=)\n", + "24800 tensor(4.0577, device='cuda:0', grad_fn=)\n", + "24900 tensor(4.0662, device='cuda:0', grad_fn=)\n", + "25000 tensor(4.0877, device='cuda:0', grad_fn=)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "25100 tensor(4.0505, device='cuda:0', grad_fn=)\n", + "25200 tensor(4.1542, device='cuda:0', grad_fn=)\n", + "25300 tensor(4.0740, device='cuda:0', grad_fn=)\n", + "25400 tensor(4.0893, device='cuda:0', grad_fn=)\n", + "25500 tensor(4.0370, device='cuda:0', grad_fn=)\n", + "25600 tensor(4.1480, device='cuda:0', grad_fn=)\n", + "25700 tensor(4.1070, device='cuda:0', grad_fn=)\n", + "25800 tensor(4.0381, device='cuda:0', grad_fn=)\n", + "25900 tensor(4.0800, device='cuda:0', grad_fn=)\n", + "26000 tensor(4.0842, device='cuda:0', grad_fn=)\n", + "26100 tensor(4.1127, device='cuda:0', grad_fn=)\n", + "26200 tensor(4.1184, device='cuda:0', grad_fn=)\n", + "26300 tensor(4.0885, device='cuda:0', grad_fn=)\n", + "26400 tensor(4.1423, device='cuda:0', grad_fn=)\n", + "26500 tensor(4.1359, device='cuda:0', grad_fn=)\n", + "26600 tensor(4.0986, device='cuda:0', grad_fn=)\n", + "26700 tensor(4.0580, device='cuda:0', grad_fn=)\n", + "26800 tensor(4.0806, device='cuda:0', grad_fn=)\n", + "26900 tensor(4.0169, device='cuda:0', grad_fn=)\n", + "27000 tensor(4.1111, device='cuda:0', grad_fn=)\n", + "27100 tensor(4.1417, device='cuda:0', grad_fn=)\n", + "27200 tensor(4.1497, device='cuda:0', grad_fn=)\n", + "27300 tensor(4.1093, device='cuda:0', grad_fn=)\n", + "27400 tensor(4.0306, device='cuda:0', grad_fn=)\n", + "27500 tensor(4.1214, device='cuda:0', grad_fn=)\n", + "27600 tensor(4.0745, device='cuda:0', grad_fn=)\n", + "27700 tensor(4.0559, device='cuda:0', grad_fn=)\n", + "27800 tensor(4.0286, device='cuda:0', grad_fn=)\n", + "27900 tensor(4.1266, device='cuda:0', grad_fn=)\n", + "28000 tensor(3.9690, device='cuda:0', grad_fn=)\n", + "28100 tensor(4.1141, device='cuda:0', grad_fn=)\n", + "28200 tensor(4.0565, device='cuda:0', grad_fn=)\n", + "28300 tensor(4.0682, device='cuda:0', grad_fn=)\n", + "28400 tensor(4.0646, device='cuda:0', grad_fn=)\n", + "28500 tensor(4.0386, device='cuda:0', grad_fn=)\n", + "28600 tensor(4.0903, device='cuda:0', grad_fn=)\n", + "28700 tensor(4.1060, device='cuda:0', grad_fn=)\n" + ] + } + ], + "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": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "SimpleBigramNeuralLanguageModel(\n", + " (model): Sequential(\n", + " (0): Embedding(20000, 100)\n", + " (1): Linear(in_features=100, out_features=20000, bias=True)\n", + " (2): Softmax(dim=None)\n", + " )\n", + ")" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('mnie', 26, 0.16004179418087006),\n", + " ('ciebie', 73, 0.13592898845672607),\n", + " ('', 0, 0.12769868969917297),\n", + " ('nas', 83, 0.04033529385924339),\n", + " ('niego', 172, 0.033195145428180695),\n", + " ('niej', 247, 0.021507620811462402),\n", + " ('was', 162, 0.017743170261383057),\n", + " ('siebie', 181, 0.01618184894323349),\n", + " ('nich', 222, 0.01589815877377987),\n", + " ('pana', 156, 0.014923062175512314)]" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "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": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('.', 3, 0.3327740728855133),\n", + " ('z', 14, 0.191472589969635),\n", + " (',', 4, 0.18250100314617157),\n", + " ('w', 10, 0.06395534425973892),\n", + " ('?', 6, 0.059775471687316895),\n", + " ('i', 11, 0.019332991912961006),\n", + " ('ze', 60, 0.016418060287833214),\n", + " ('', 0, 0.014098692685365677),\n", + " ('na', 12, 0.01183203887194395),\n", + " ('...', 15, 0.010537521913647652)]" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "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": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('poszedł', 1088, 1.0),\n", + " ('wsiąść', 9766, 0.46510031819343567),\n", + " ('pojedzie', 6485, 0.4598822593688965),\n", + " ('wyjeżdża', 6459, 0.4378735423088074),\n", + " ('szedłem', 8969, 0.4232063889503479),\n", + " ('zadzwoniłem', 4889, 0.41752171516418457),\n", + " ('dotrzemy', 6098, 0.40929487347602844),\n", + " ('spóźnić', 9923, 0.4015277922153473),\n", + " ('pójdę', 635, 0.3992091119289398),\n", + " ('wrócimy', 2070, 0.39785560965538025)]" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "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" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.4" + }, + "org": null + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/wyk/07_Zanurzenia_slow.org b/wyk/07_Zanurzenia_slow.org new file mode 100644 index 0000000..f4c6a42 --- /dev/null +++ b/wyk/07_Zanurzenia_slow.org @@ -0,0 +1,338 @@ +* Zanurzenia słów + +W praktyce stosowalność słowosieci okazała się zaskakująco +ograniczona. Większy przełom w przetwarzaniu języka naturalnego przyniosły +wielowymiarowe reprezentacje słów, inaczej: zanurzenia słów. + +** „Wymiary” słów + +Moglibyśmy zanurzyć (ang. /embed/) w wielowymiarowej przestrzeni, tzn. zdefiniować odwzorowanie +$E \colon V \rightarrow \mathcal{R}^m$ dla pewnego $m$ i określić taki sposób estymowania +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 +(według jakiejś metryki odległości, na przykład zwykłej odległości euklidesowej): + +$$P(u|v) \approx P(u'|v').$$ + +$E(u)$ nazywamy zanurzeniem (embeddingiem) słowa. + +*** Wymiary określone z góry? + +Można by sobie wyobrazić, że $m$ wymiarów mogłoby być z góry +określonych przez lingwistę. Wymiary te byłyby związane z typowymi +„osiami” rozpatrywanymi w językoznawstwie, na przykład: + +- czy słowo jest wulgarne, pospolite, potoczne, neutralne czy książkowe? +- czy słowo jest archaiczne, wychodzące z użycia czy jest neologizmem? +- czy słowo dotyczy kobiet, czy mężczyzn (w sensie rodzaju gramatycznego i/lub + socjolingwistycznym)? +- czy słowo jest w liczbie pojedynczej czy mnogiej? +- czy słowo jest rzeczownikiem czy czasownikiem? +- czy słowo jest rdzennym słowem czy zapożyczeniem? +- czy słowo jest nazwą czy słowem pospolitym? +- czy słowo opisuje konkretną rzecz czy pojęcie abstrakcyjne? +- … + +W praktyce okazało się jednak, że lepiej, żeby komputer uczył się sam +możliwych wymiarów — z góry określamy tylko $m$ (liczbę wymiarów). + +** Bigramowy model języka oparty na zanurzeniach + +Zbudujemy teraz najprostszy model język oparty na zanurzeniach. Będzie to właściwie najprostszy +*neuronowy model języka*, jako że zbudowany model można traktować jako prostą sieć neuronową. + +*** Słownik + +W typowym neuronowym modelu języka rozmiar słownika musi być z góry +ograniczony. Zazwyczaj jest to liczba rzędu kilkudziesięciu wyrazów — +po prostu będziemy rozpatrywać $|V|$ najczęstszych wyrazów, pozostałe zamienimy +na specjalny token ~~ reprezentujący nieznany (/unknown/) wyraz. + +Aby utworzyć taki słownik użyjemy gotowej klasy ~Vocab~ z pakietu torchtext: + +#+BEGIN_SRC python :session mysession :exports both :results raw drawer + from itertools import islice + import regex as re + import sys + from torchtext.vocab import build_vocab_from_iterator + + + def get_words_from_line(line): + line = line.rstrip() + yield '' + for m in re.finditer(r'[\p{L}0-9\*]+|\p{P}+', line): + yield m.group(0).lower() + yield '' + + + def get_word_lines_from_file(file_name): + with open(file_name, 'r') as fh: + for line in fh: + yield get_words_from_line(line) + + vocab_size = 20000 + + vocab = build_vocab_from_iterator( + get_word_lines_from_file('opensubtitlesA.pl.txt'), + max_tokens = vocab_size, + specials = ['']) + + vocab['jest'] +#+END_SRC + +#+RESULTS: +:results: +16 +:end: + +#+BEGIN_SRC python :session mysession :exports both :results raw drawer +vocab.lookup_tokens([0, 1, 2, 10, 12345]) +#+END_SRC + +#+RESULTS: +:results: +['', '', '', 'w', 'wierzyli'] +:end: + +*** Definicja sieci + +Naszą prostą sieć neuronową zaimplementujemy używając frameworku PyTorch. + +#+BEGIN_SRC python :session mysession :exports both :results raw drawer + from torch import nn + import torch + + embed_size = 100 + + class SimpleBigramNeuralLanguageModel(nn.Module): + def __init__(self, vocabulary_size, embedding_size): + super(SimpleBigramNeuralLanguageModel, self).__init__() + self.model = nn.Sequential( + nn.Embedding(vocabulary_size, embedding_size), + nn.Linear(embedding_size, vocabulary_size), + nn.Softmax() + ) + + def forward(self, x): + return self.model(x) + + model = SimpleBigramNeuralLanguageModel(vocab_size, embed_size) + + vocab.set_default_index(vocab['']) + ixs = torch.tensor(vocab.forward(['pies'])) + out[0][vocab['jest']] +#+END_SRC + +#+RESULTS: +:results: +:end: + +Teraz wyuczmy model. Wpierw tylko potasujmy nasz plik: + +#+BEGIN_SRC +shuf < opensubtitlesA.pl.txt > opensubtitlesA.pl.shuf.txt +#+END_SRC + +#+BEGIN_SRC python :session mysession :exports both :results raw drawer + from torch.utils.data import IterableDataset + import itertools + + def look_ahead_iterator(gen): + prev = None + for item in gen: + if prev is not None: + yield (prev, item) + prev = item + + class Bigrams(IterableDataset): + def __init__(self, text_file, vocabulary_size): + self.vocab = build_vocab_from_iterator( + get_word_lines_from_file(text_file), + max_tokens = vocabulary_size, + specials = ['']) + self.vocab.set_default_index(self.vocab['']) + self.vocabulary_size = vocabulary_size + self.text_file = text_file + + def __iter__(self): + return look_ahead_iterator( + (self.vocab[t] for t in itertools.chain.from_iterable(get_word_lines_from_file(self.text_file)))) + + train_dataset = Bigrams('opensubtitlesA.pl.shuf.txt', vocab_size) +#+END_SRC + +#+RESULTS: +:results: +:end: + +#+BEGIN_SRC python :session mysession :exports both :results raw drawer + from torch.utils.data import DataLoader + + next(iter(train_dataset)) +#+END_SRC + +#+RESULTS: +:results: +(2, 5) +:end: + +#+BEGIN_SRC python :session mysession :exports both :results raw drawer + from torch.utils.data import DataLoader + + next(iter(DataLoader(train_dataset, batch_size=5))) +#+END_SRC + +#+RESULTS: +:results: +[tensor([ 2, 5, 51, 3481, 231]), tensor([ 5, 51, 3481, 231, 4])] +:end: + +#+BEGIN_SRC python :session mysession :exports both :results raw drawer + device = 'cuda' + model = SimpleBigramNeuralLanguageModel(vocab_size, embed_size).to(device) + data = DataLoader(train_dataset, batch_size=5000) + optimizer = torch.optim.Adam(model.parameters()) + criterion = torch.nn.NLLLoss() + + model.train() + step = 0 + for x, y in data: + x = x.to(device) + y = y.to(device) + optimizer.zero_grad() + ypredicted = model(x) + loss = criterion(torch.log(ypredicted), y) + if step % 100 == 0: + print(step, loss) + step += 1 + loss.backward() + optimizer.step() + + torch.save(model.state_dict(), 'model1.bin') +#+END_SRC + +#+RESULTS: +:results: +None +:end: + +Policzmy najbardziej prawdopodobne kontynuację dla zadanego słowa: + +#+BEGIN_SRC python :session mysession :exports both :results raw drawer + device = 'cuda' + model = SimpleBigramNeuralLanguageModel(vocab_size, embed_size).to(device) + model.load_state_dict(torch.load('model1.bin')) + model.eval() + + ixs = torch.tensor(vocab.forward(['dla'])).to(device) + + out = model(ixs) + top = torch.topk(out[0], 10) + top_indices = top.indices.tolist() + top_probs = top.values.tolist() + top_words = vocab.lookup_tokens(top_indices) + list(zip(top_words, top_indices, top_probs)) +#+END_SRC + +#+RESULTS: +:results: +[('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)] +:end: + +Teraz zbadajmy najbardziej podobne zanurzenia dla zadanego słowa: + +#+BEGIN_SRC python :session mysession :exports both :results raw drawer + vocab = train_dataset.vocab + ixs = torch.tensor(vocab.forward(['kłopot'])).to(device) + + out = model(ixs) + top = torch.topk(out[0], 10) + top_indices = top.indices.tolist() + top_probs = top.values.tolist() + top_words = vocab.lookup_tokens(top_indices) + list(zip(top_words, top_indices, top_probs)) +#+END_SRC + +#+RESULTS: +:results: +[('.', 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)] +:end: + +#+BEGIN_SRC python :session mysession :exports both :results raw drawer + cos = nn.CosineSimilarity(dim=1, eps=1e-6) + + embeddings = model.model[0].weight + + vec = embeddings[vocab['poszedł']] + + similarities = cos(vec, embeddings) + + top = torch.topk(similarities, 10) + + top_indices = top.indices.tolist() + top_probs = top.values.tolist() + top_words = vocab.lookup_tokens(top_indices) + list(zip(top_words, top_indices, top_probs)) +#+END_SRC + +#+RESULTS: +:results: +[('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)] +:end: + +*** Zapis przy użyciu wzoru matematycznego + +Powyżej zaprogramowaną sieć neuronową można opisać następującym wzorem: + +$$\vec{y} = \operatorname{softmax}(CE(w_{i-1}),$$ + +gdzie: + +- $w_{i-1}$ to pierwszy wyraz w bigramie (poprzedzający wyraz), +- $E(w)$ to zanurzenie (embedding) wyrazy $w$ — wektor o rozmiarze $m$, +- $C$ to macierz o rozmiarze $|V| \times m$, która rzutuje wektor zanurzenia w wektor o rozmiarze słownika, +- $\vec{y}$ to wyjściowy wektor prawdopodobieństw o rozmiarze $|V|$. + +**** Hiperparametry + +Zauważmy, że nasz model ma dwa hiperparametry: + +- $m$ — rozmiar zanurzenia, +- $|V|$ — rozmiar słownika, jeśli zakładamy, że możemy sterować + rozmiarem słownika (np. przez obcinanie słownika do zadanej liczby + najczęstszych wyrazów i zamiany pozostałych na specjalny token, powiedzmy, ~~. + +Oczywiście możemy próbować manipulować wartościami $m$ i $|V|$ w celu +polepszenia wyników naszego modelu. + +*Pytanie*: dlaczego nie ma sensu wartość $m \approx |V|$ ? dlaczego nie ma sensu wartość $m = 1$? + +*** Diagram sieci + +Jako że mnożenie przez macierz ($C$) oznacza po prostu zastosowanie +warstwy liniowej, naszą sieć możemy interpretować jako jednowarstwową +sieć neuronową, co można zilustrować za pomocą następującego diagramu: + +#+CAPTION: Diagram prostego bigramowego neuronowego modelu języka +[[./07_Zanurzenia_slow/bigram1.drawio.png]] + +*** Zanurzenie jako mnożenie przez macierz + +Uzyskanie zanurzenia ($E(w)$) zazwyczaj realizowane jest na zasadzie +odpytania (_look-up_). Co ciekawe, zanurzenie można intepretować jako +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 +wektora z gorącą jedynką (_one-hot encoding_), tzn. słowo $w$ zostanie +podane na wejściu jako wektor $\vec{1_V}(w) = [0,\ldots,0,1,0\ldots,0]$ o rozmiarze $|V|$ +złożony z samych zer z wyjątkiem jedynki na pozycji odpowiadającej indeksowi wyrazu $w$ w słowniku $V$. + +Wówczas wzór przyjmie postać: + +$$\vec{y} = \operatorname{softmax}(CE\vec{1_V}(w_{i-1})),$$ + +gdzie $E$ będzie tym razem macierzą $m \times |V|$. + +*Pytanie*: czy $\vec{1_V}(w)$ intepretujemy jako wektor wierszowy czy kolumnowy? + +W postaci diagramu można tę interpretację zilustrować w następujący sposób: + +#+CAPTION: Diagram prostego bigramowego neuronowego modelu języka z wejściem w postaci one-hot +[[./07_Zanurzenia_slow/bigram2.drawio.png]] diff --git a/wyk/08_Neuronowy_ngramowy_model.ipynb b/wyk/08_Neuronowy_ngramowy_model.ipynb new file mode 100644 index 0000000..c80c80e --- /dev/null +++ b/wyk/08_Neuronowy_ngramowy_model.ipynb @@ -0,0 +1,781 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![Logo 1](https://git.wmi.amu.edu.pl/AITech/Szablon/raw/branch/master/Logotyp_AITech1.jpg)\n", + "
\n", + "

Modelowanie języka

\n", + "

8. Neuronowy model języka [wykład]

\n", + "

Filip Graliński (2022)

\n", + "
\n", + "\n", + "![Logo 2](https://git.wmi.amu.edu.pl/AITech/Szablon/raw/branch/master/Logotyp_AITech2.jpg)\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 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": 2, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/media/kuba/ssdsam/anaconda3/envs/lmzajecia/lib/python3.10/site-packages/torch/nn/functional.py:1933: UserWarning: nn.functional.tanh is deprecated. Use torch.tanh instead.\n", + " warnings.warn(\"nn.functional.tanh is deprecated. Use torch.tanh instead.\")\n" + ] + }, + { + "data": { + "text/plain": [ + "'08_Neuronowy_ngramowy_model/tanh.png'" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "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": 3, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/media/kuba/ssdsam/anaconda3/envs/lmzajecia/lib/python3.10/site-packages/torch/nn/functional.py:1933: UserWarning: nn.functional.tanh is deprecated. Use torch.tanh instead.\n", + " warnings.warn(\"nn.functional.tanh is deprecated. Use torch.tanh instead.\")\n" + ] + }, + { + "data": { + "text/plain": [ + "tensor([-1.0000, -0.9640, 0.0000, 0.4621, 1.0000])" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "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.4" + }, + "org": null + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/wyk/08_Neuronowy_ngramowy_model.org b/wyk/08_Neuronowy_ngramowy_model.org new file mode 100644 index 0000000..ec88517 --- /dev/null +++ b/wyk/08_Neuronowy_ngramowy_model.org @@ -0,0 +1,406 @@ +* Neuronowy n-gramowy model języka + +Omówiony w poprzedniej części neuronowy bigramowy model języka +warunkuje kolejny wyraz jedynie względem bezpośrednio poprzedzającego +— jak w każdym bigramowym modelu przyjmujemy założenie, że $w_i$ +zależy tylko od $w_{i-1}$. Rzecz jasna jest to bardzo duże +ograniczenie, w rzeczywistości bardzo często prawdopodobieństwo +kolejnego wyrazu zależy od wyrazu dwie, trzy, cztery itd. pozycje +wstecz czy w ogólności od wszystkich wyrazów poprzedzających (bez +względu na ich pozycje). + +*Pytanie*: Wskaż zależności o zasięgu większym niż 1 wyraz w zdaniu +/Zatopieni w kłębach dymu cygar i pochyleni nad butelkami z ciemnego +szkła obywatele tej dzielnicy, jedni zakładali się o wygranę lub +przegranę Anglii, drudzy o bankructwo Wokulskiego; jedni nazywali +geniuszem Bismarcka, drudzy — awanturnikiem Wokulskiego; jedni +krytykowali postępowanie prezydenta MacMahona, inni twierdzili, że +Wokulski jest zdecydowanym wariatem, jeżeli nie czymś gorszym…/ + +** Trigramowy neuronowy model języka + +Spróbujmy najpierw rozszerzyć nasz model na trigramy, to znaczy +będziemy przewidywać słowo $w_i$ na podstawie słów $w_{i-2}$ i +$w_{i-1}$. + +Najprostsze rozwiązanie polegałoby na zanurzeniu pary $(w_{i-2}, +w_{i-1})$ w całości i postępowaniu jak w przypadku modelu bigramowego. +Byłoby to jednak zupełnie niepraktyczne, jako że: + +- liczba zanurzeń do wyuczenia byłaby olbrzymia ($|V|^2$ — byłoby to + ewentualnie akceptowalne dla modeli operujących na krótszych + jednostkach niż słowa, np. na znakach), +- w szczególności zanurzenia dla par $(v, u)$, $(u, v)$, $(u, u)$ i + $(v, v)$ nie miałyby ze sobą nic wspólnego. + +*** Konketanacja zanurzeń + +Właściwsze rozwiązanie polega na zanurzeniu dalej pojedynczych słów i +następnie ich *konkatenowaniu*. + +Przypomnijmy, że konkatenacja wektorów $\vec{x_1}$ i $\vec{x_2}$ to wektor o rozmiarze +$|\vec{x_1}| + |\vec{x_2}|$ powstały ze „sklejania” wektorów $\vec{x_1}$ i $\vec{x_2}$. +Konkatenację wektorów $\vec{x_1}$ i $\vec{x_2}$ będziemy oznaczać za pomocą $[\vec{x_1}, \vec{x_2}]$. + +Przykład: jeśli $\vec{x_1} = [-1, 2, 0]$ i $\vec{x_2} = [3, -3]$, +wówczas $[\vec{x_1}, \vec{x_2}] = [-1, 2, 0, 3, -3]$ + +Oznacza to, że nasza macierz „kontekstowa” $C$ powinna mieć w modelu trigramowym rozmiar nie +$|V| \times m$, lecz $|V| \times (m+m)$ = $|V| \times 2m$ i wyjście będzie zdefiniowane za pomocą wzoru: + +$$\vec{y} = \operatorname{softmax}(C[E(w_{i-2}),E(w_{i-1})]),$$ + +co można przedstawić za pomocą następującego schematu: + +#+CAPTION: Diagram prostego bigramowego neuronowego modelu języka +[[./08_Neuronowy_ngramowy_model/trigram1.drawio.png]] + +**** Rozbicie macierzy $C$ + +Zamiast mnożyć macierz $C$ przez konkatenację dwóch wektorów, można +rozbić macierz $C$ na dwie, powiedzmy $C_{-2}$ i $C_{-1}$, przemnażać +je osobno przez odpowiadające im wektory i następnie *dodać* macierze, +tak aby: + +$$C[E(w_{i-2}),E(w_{i-1})] = C_{-2}E(w_{i-2}) + C_{-1}E(w_{i-1}).$$ + +Macierze $C_{-2}$ i $C_{-1}$ będą miały rozmiar $|V| \times m$. + +Przy tym podejściu możemy powiedzieć, że ostatni i przedostatni wyraz +mają swoje osobne macierze o potencjalnie różnych wagach — co ma sens, +jako że na inne aspekty zwracamy uwagę przewidując kolejne słowo na +podstawie wyrazu bezpośrednio poprzedzającego, a na inne — na +podstawie słowa występującego dwie pozycje wcześniej. + +** Uogólnienie na $n$-gramowy model języka dla dowolnego $n$ + +Łatwo uogólnić opisany wyżej trigramowy model języka dla dowolnego $n$. +Uogólniony model można przedstawić za pomocą wzoru: + +$$\vec{y} = \operatorname{softmax}(C[E(w_{i-n+1}),\dots,E(w_{i-1})]),$$ + +gdzie macierz $C$ ma rozmiar $|V| \times nm$ lub za pomocą wzoru: + +$$\vec{y} = \operatorname{softmax}(C_{-(n-1)}E(w_{i-n+1}) + \dots + C_{-1}E(w_{i-1}),$$ + +gdzie macierze $C_{-(n-1)}$, \dots, $C_{-1}$ mają rozmiary $|V| \times m$. + +Por. diagram: + +#+CAPTION: Diagram prostego n-gramowego neuronowego modelu języka +[[./08_Neuronowy_ngramowy_model/ngram.drawio.png]] + +** Dodanie kolejnej warstwy + +W wypadku trigramowego czy — ogólniej — n-gramowego modelu języka dla +$n \geq 3$ warto dodać kolejną (*ukrytą*) warstwę, na którą będziemy rzutować +skonkatenowane embeddingi, zanim zrzutujemy je do długiego wektora +prawdopodobieństw. + +Zakładamy, że warstwa ukryta zawiera $h$ neuronów. Wartość $h$ powinna być mniejsza +niż $nm$ (a może nawet od $m$). + +*Pytanie*: Dlaczego wartość $h > nm$ nie jest racjonalnym wyborem? + +*Pytanie*: Dlaczego dodanie kolejnej warstwy nie ma sensu dla modelu bigramowego? + +*** Funkcja aktywacji + +Aby warstwa ukryta wnosiła coś nowego, na wyjściu z tej funkcji musimy (dlaczego?) +zastosować nieliniową *funkcji aktywacji*. Zazwyczaj jako funkcji +aktywacji w sieciach neuronowych używa się funkcji ReLU albo funkcji +sigmoidalnej. W prostych neuronowych modelach języka sprawdza się też +*tangens hiperboliczny* (tgh, w literaturze anglojęzycznej tanh): + +$$\operatorname{tgh}(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}}.$$ + +#+BEGIN_SRC ipython :session mysession :results file +import matplotlib.pyplot as plt +import torch +import torch.nn as nn + +x = torch.linspace(-5,5,100) +plt.xlabel("x") +plt.ylabel("y") +a = torch.Tensor(x.size()[0]).fill_(2.) +m = torch.stack([x, a]) +plt.plot(x, nn.functional.tanh(m)[0]) +fname = '08_Neuronowy_ngramowy_model/tanh.png' +plt.savefig(fname) +fname +#+END_SRC + +#+RESULTS: +[[file:08_Neuronowy_ngramowy_model/tanh.png]] + +**** Tangens hiperboliczny zastosowany dla wektora + +Tangens hiperboliczny wektora będzie po prostu wektorem tangensów +hiperbolicznych poszczególnych wartości. + +#+BEGIN_SRC ipython :session mysession :results file + import torch + import torch.nn as nn + + v = torch.Tensor([-100, -2.0, 0.0, 0.5, 1000.0]) + nn.functional.tanh(v) +#+END_SRC + +#+RESULTS: +[[file:tensor([-1.0000, -0.9640, 0.0000, 0.4621, 1.0000])]] + +*** Wzór i schemat dwuwarstwowego n-gramowego neuronowego modelu języka + +Dwuwarstwowy model języka będzie określony następującym wzorem: + +$$\vec{y} = \operatorname{softmax}(C\operatorname{tgh}(W[E(w_{i-n+1}),\dots,E(w_{i-1})])),$$ + +gdzie: + +- $W$ jest wyuczalną macierzą wag o rozmiarze $h \times nm$, +- $C$ będzie macierzą o rozmiarze $|V| \times h$. + +Zmodyfikowaną sieć można przedstawić za pomocą następującego schematu: + +#+CAPTION: Dwuwarstwowy n-gramowy neuronowy model języka +[[./08_Neuronowy_ngramowy_model/ngram-tgh.drawio.png]] + +*** Liczba wag w modelu dwuwarstwowym + +Na wagi w modelu dwuwarstwowym składają się: + +- zanurzenia: $m|V|$, +- wagi warstwy ukrytej: $hnm$, +- wagi warstwy wyjściowej: $|V|h$, + +a zatem łącznie: + +$$m|V| + hnm + |V|h$$ + +Jeśli $h \approx m$ (co jest realistyczną opcją), wówczas otrzymamy oszacowanie: + +$$O(m|V| + nm^2).$$ + +Zauważmy, że względem $n$ oznacza to bardzo korzystną złożoność +$O(n)$! Oznacza to, że nasz model może działać dla dużo większych +wartości $n$ niż tradycyjny, statystyczny n-gramowy model języka (dla którego +wartości $n > 5$ zazwyczaj nie mają sensu). + +** Model worka słów + +Jak stwierdziliśmy przed chwilą, dwuwarstwowy n-gramowy model języka +może działać dla stosunkowo dużego $n$. Zauważmy jednak, że istnieje +pewna słabość tego modelu. Otóż o ile intuicyjnie ma sens odróżniać +słowo poprzedzające, słowo występujące dwie pozycje wstecz i zapewne +trzy pozycje wstecz, a zatem uczyć się osobnych macierzy $C_{-1}$, +$C_{-2}$, $C_{-3}$ to różnica między wpływem słowa +występującego cztery pozycje wstecz i pięć pozycji wstecz jest już +raczej nieistotna; innymi słowy różnica między macierzami $C_{-4}$ i +$C_{-5}$ będzie raczej niewielka i sieć niepotrzebnie będzie uczyła +się dwukrotnie podobnych wag. Im dalej wstecz, tym różnica wpływu +będzie jeszcze mniej istotna, można np. przypuszczać, że różnica +między $C_{-10}$ i $C_{-13}$ nie powinna być duża. + +Spróbujmy najpierw zaproponować radykalne podejście, w którym nie +będziemy w ogóle uwzględniać pozycji słów (lub będziemy je uwzględniać +w niewielkim stopniu), później połączymy to z omówionym wcześniej +modelem $n$-gramowym. + +*** Agregacja wektorów + +Zamiast patrzeć na kilka poprzedzających słów, można przewidywać na +podstawie *całego* ciągu słów poprzedzających odgadywane słowo. Zauważmy jednak, że +sieć neuronowa musi mieć ustaloną strukturę, nie możemy zmieniać jej +rozmiaru. Musimy zatem najpierw zagregować cały ciąg do wektora o +*stałej* długości. Potrzebujemy zatem pewnej funkcji agregującej $A$, takiej by +$A(w_1,\dots,w_{i-1})$ było wektorem o stałej długości, niezależnie od $i$. + +*** Worek słów + +Najprostszą funkcją agregującą jest po prostu… suma. Dodajemy po +prostu zanurzenia słów: + +$$A(w_1,\dots,w_{i-1}) = E(w_1) + \dots + E(w_{i-1}) = \sum_{j=1}^{i-1} E(w_j).$$ + +*Uwaga*: zanurzenia słów nie zależą od pozycji słowa (podobnie było w wypadku n-gramowego modelu!). + +Jeśli rozmiar zanurzenia (embeddingu) wynosi $m$, wówczas rozmiar +wektora uzyskanego dla całego poprzedzającego tekstu wynosi również $m$. + +Proste dodawanie wydaje się bardzo „prostacką” metodą, a jednak +suma wektorów słów jest *zaskakująco skuteczną metodą zanurzenia +(embedowania) całych tekstów (doc2vec)*. Prostym wariantem dodawania jest obliczanie *średniej wektorów*: + +$$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}.$$ + +Tak czy siak uzyskany wektor *nie zależy od kolejności słów* +(dodawanie jest przemienne i łączne!). Mówimy więc o *worku słów* +(/bag of words/, /BoW/) — co ma symbolizować fakt, że słowa są +przemieszane, niczym produkty w torbie na zakupy. + +**** Schemat graficzny modelu typu worek słów + +Po zanurzeniu całego poprzedzającego tekstu postępujemy podobnie jak w +modelu bigramowym — rzutujemy embedding na długi wektor wartości, na +którym stosujemy funkcję softmax: + +#+CAPTION: Model typu worek słów +[[./08_Neuronowy_ngramowy_model/bow1.drawio.png]] + +Odpowiada to wzorowi: + +$$y = \operatorname{softmax}(C\sum_{j=1}^{i-1} E(w_j)).$$ + +*** Jak traktować powtarzające się słowa? + +Według wzoru podanego wyżej, jeśli słowo w poprzedzającym tekście +pojawia się więcej niż raz, jego embedding zostanie zsumowany odpowiednią liczbę razy. +Na przykład embedding tekstu /to be or not to be/ będzie wynosił: + +$$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}).$$ + +Innymi słowy, choć w worku słów nie uwzględniamy kolejności słów, to +*liczba wystąpień* ma dla nas ciągle znaczenie. Można powiedzieć, że +traktujemy poprzedzający tekst jako *multizbiór* (struktura +matematyczna, w której nie uwzględnia się kolejności, choć zachowana +jest informacja o liczbie wystąpień). + +**** Zbiór słów + +Oczywiście moglibyśmy przy agregowaniu zanurzeń pomijać powtarzające +się słowa, a zatem zamiast multizbioru słów rozpatrywać po prostu ich zbiór: + +$$A(w_1,\dots,w_{i-1}) = \sum_{w \in \{w_1,\dots,w_{i-1}\}} E(w).$$ + +Jest kwestią dyskusyjną, czy to lepsze czy gorsze podejście — w końcu +liczba wystąpień np. słów /Ukraina/ czy /Polska/ może wpływać w jakimś +stopniu na prawdopodobieństwo kolejnego słowa (/Kijów/ czy +/Warszawa/?). + +*** Worek słów a wektoryzacja tf + +Wzór na sumę zanurzeń słów można przekształcić w taki sposób, by +sumować po wszystkich słowach ze słownika, zamiast po słowach rzeczywiście występujących w tekście: + +$$A(w_1,\dots,w_{i-1}) = \sum_{j=1}^{i-1} E(w_j) = \sum_{w \in V} \#wE(w)$$ + +gdzie $\#w$ to liczba wystąpień słowa $w$ w ciagu $w_1,\dots,w_{i-1}$ (w wielu przypadkach równa zero!). + +Jeśli teraz zanurzenia będziemy reprezentować jako macierz $E$ (por. poprzedni wykład), +wówczas sumę można przedstawić jako iloczyn macierzy $E$ i pewnego wektora: + +$$A(w_1,\dots,w_{i-1}) = E(w) [\#w^1,\dots,\#w^{|V|}]^T.$$ + +(Odróżniamy $w^i$ jako $i$-ty wyraz w słowniku $V$ od $w_i$ jako $i$-tego wyraz w rozpatrywanym ciągu). + +Zwróćmy uwagę, że wektor $[\#w_1,\dots,\#w_{|V|}]$ to po prostu +reprezentacja wektora poprzedzającego tekstu (tj. ciągu +$(w_1,\dots,w_{i-1})$) przy użyciu schematu wektoryzacji tf (/term +frequency/). Przypomnijmy, że tf to reprezentacja tekstu przy użyciu +wektorów o rozmiarze $|V|$ — na każdej pozycji odnotowujemy liczbę wystąpień. +Wektory tf są *rzadkie*, tj. na wielu pozycjach zawierają zera. + +Innymi słowy, nasz model języka /bag of words/ można przedstawić za pomocą wzoru: + +$$y = \operatorname{softmax}(C\operatorname{tf}(w_1,\dots,w_{i-1})),$$ + +co można zilustrować w następujący sposób: + +#+CAPTION: Model typu worek słów — alternatywna reprezentacja +[[./08_Neuronowy_ngramowy_model/bow2.drawio.png]] + +Można stwierdzić, że zanurzenie tekstu przekształca rzadki, długi wektor +tf w gęsty, krótki wektor. + +** Ważenie słów + +Czy wszystkie słowa są tak samo istotne? Rzecz jasna, nie: + +- jak już wiemy z naszych rozważań dotyczących n-gramowych modeli języka, słowa bezpośrednio + poprzedzające odgadywany wyraz mają większy wpływ niż słowa wcześniejsze; + intuicyjnie, wpływ słów stopniowo spada — tym bardziej, im bardziej słowo jest oddalone od słowa odgadywanego; +- jak wiemy z wyszukiwania informacji, słowa, które występują w wielu tekstach czy dokumentach, powinny mieć + mniejsze znaczenie, w skrajnym przypadku słowa występujące w prawie każdym tekście (/że/, /w/, /i/ itd.) powinny + być praktycznie pomijane jako /stop words/ (jeśli rozpatrywać je w „masie” worka słów — oczywiście + to, czy słowo poprzedzające odgadywane słowo to /że/, /w/ czy /i/ ma olbrzymie znaczenie!). + +Zamiast po prostu dodawać zanurzenia, można operować na sumie (bądź średniej) ważonej: + +$$\sum_{j=1}^{i-1} \omega(j, w_j)E(w_j),$$ + +gdzie $\omega(j, w_j)$ jest pewną wagą, która może zależeć od pozycji $j$ lub samego słowa $w_j$. + +*** Uwzględnienie pozycji + +Można w pewnym stopniu złamać „workowatość” naszej sieci przez proste +uwzględnienie pozycji słowa, np. w taki sposób: + +$$\omega(j, w_j) = \beta^{i-j-1},$$ + +dla pewnego hiperparametru $\beta$. Na przykład jeśli $\beta=0,9$, +wówczas słowo bezpośrednio poprzedzające dane słowo ma $1 / 0,9^9 \approx 2,58$ +większy wpływ niż słowo występujące 10 pozycji wstecz. + +*** Odwrócona częstość dokumentowa + +Aby większą wagę przykładać do słów występujących w mniejszej liczbie +dokumentów, możemy użyć, znanej z wyszukiwania informacji, +odwrotnej częstości dokumentowej (/inverted document frequency/, /idf/): + +$$\omega(j, w_j) = \operatorname{idf}_S(w_j) = \operatorname{log}\frac{|S|}{\operatorname{df}_S(w_j)},$$ + +gdzie: + +- $S$ jest pewną kolekcją dokumentów czy tekstów, z którego pochodzi przedmiotowy ciąg słów, +- $\operatorname{df}_S(w)$ to częstość dokumentowa słowa $w$ w kolekcji $S$, tzn. odpowiedź na pytanie, + w ilu dokumentach występuje $w$. + +Rzecz jasna, ten sposób ważenia oznacza tak naprawdę zastosowanie wektoryzacji tf-idf zamiast tf, +nasza sieć będzie dana zatem wzorem: + +$$y = \operatorname{softmax}(C\operatorname{tfidf}(w_1,\dots,w_{i-1})).$$ + +*** Bardziej skomplikowane sposoby ważenia słów + +Można oczywiście połączyć odwrotną częstość dokumentową z uwzględnieniem pozycji słowa: + +$$\omega(j, w_j) = \beta^{i-j-1}\operatorname{idf}_S(w_j).$$ + +*Uwaga*: „wagi” $\omega(j, w_j)$ nie są tak naprawdę wyuczalnymi +wagami (parametrami) naszej sieci neuronowej, terminologia może być +tutaj myląca. Z punktu widzenia sieci neuronowej $\omega(j, w_j)$ są +stałe i *nie* są optymalizowane w procesie propagacji wstecznej. Innymi +słowy, tak zdefiniowane $\omega(j, w_j)$ zależą tylko od: + +- hiperparametru $\beta$, który może być optymalizowany już poza siecią (w procesie *hiperoptymalizacji*), +- wartości $\operatorname{idf}_S(w_j)$ wyliczanych wcześniej na podstawie kolekcji $S$. + +*Pytanie*: czy wagi $\omega(j, w_j)$ mogłyby sensownie uwzględniać +jakieś parametry wyuczalne z całą siecią? + +** Modelowanie języka przy użyciu bardziej złożonych neuronowych sieci /feed-forward/ + +Można połączyć zalety obu ogólnych podejść (n-gramowego modelu i worka +słów) — można *równocześnie* traktować w specjalny sposób (na +przykład) dwa poprzedzające wyrazy, wszystkie zaś inne wyrazy +reprezentować jako „tło” modelowane za pomocą worka słów lub podobnej +reprezentacji. Osiągamy to poprzez konkatenację wektora +poprzedzającego słowa, słowa występującego dwie pozycje wstecz oraz +zagregowanego zanurzenia całego wcześniejszego tekstu: + +$$y = \operatorname{softmax}(C[E(w_{i-1}),E(w_{i-2}),A(w_1,\dots,w_{i-3})]),$$ + +czy lepiej z dodatkową warstwą ukrytą: + +$$y = \operatorname{softmax}(C\operatorname{tgh}(W[E(w_{i-1}),E(w_{i-2}),A(w_1,\dots,w_{i-3})])),$$ + +W tak uzyskanym dwuwarstwowym neuronowym modelu języka, łączącym model +trigramowy z workiem słów, macierz $W$ ma rozmiar $h \times 3m$. + +*Pytanie*: jakie mamy możliwości, jeśli zamiast przewidywać kolejne słowo, mamy za zadanie +odgadywać słowo w luce (jak w wyzwaniach typu /word gap/)? + +** Literatura + +Skuteczny n-gramowy neuronowy model języka opisano po raz pierwszy +w pracy [[https://www.jmlr.org/papers/volume3/bengio03a/bengio03a.pdf][A Neural Probabilistic Language Model]] autorstwa Yoshua Bengio i in. diff --git a/wyk/08_Neuronowy_ngramowy_model/bow1.drawio b/wyk/08_Neuronowy_ngramowy_model/bow1.drawio new file mode 100644 index 0000000..0d30d50 --- /dev/null +++ b/wyk/08_Neuronowy_ngramowy_model/bow1.drawio @@ -0,0 +1 @@ +7VpZd9o4GP01PJLjDWM/BgKZmaaTtjmnU+alR8XCVjCWI4sY8utHQjLexNKwk3kB6Wr/tvvJdsPsTmb3BMTBZ+zBsGFo3qxh3jUMw7V09suBuQB027UE4hPkSSwHntAblKAm0SnyYFLqSDEOKYrL4BBHERzSEgYIwWm52wiH5VVj4MMa8DQEYR39B3k0EKhjtHP8D4j8IFuZHVC0TEDWWU6RBMDDqYAWhzN7DbNLMKaiNJl1YciFl8lFSKC/onW5MQIjutWAbu/xCwqcTz//fvZerAc4TN6aUhkJnWcHhh47v6xiQgPs4wiEvRztEDyNPMhn1Vgt7/OAccxAnYHPkNK5VCaYUsyggE5C2QpniP7gw29asjaQk/Hy3axYmWeViJL5j2JlkM/Aq/mwRS0bN8IRlRvhIu+I8/JDrhRjpi88JUPZ6+vj5/Txfjy+f/gz+vbS96nuPDQNaY6A+JCukbG+VDbzEognkO2PjSMwBBS9lvcBpLn6y365RllBKlWt4HWbfAXhVK5U03iuT66cNEAUPsVgcfSUOXVZdyvF9QoJhbMCVD+xbDU06dsyJpiurKe5g+mmxIKCc2XY3oVkKoRkh2zVjodeWdGni5MzZ7HSn412BzGxtu8EkPVkC5c6V4TMZEPLkkwowWPYxSEmDIlwxH1rhMKwAoEQ+RGrDpnEIcM7XNKIRahb2TBBnrdwTJXqys66B+1ZFe1ZTl179jGV515KCHt/pLHrkUbZz1FGt1OFHvv8Qo/ulo1Xb9WNd2nQR7Fe/QIZOCfdQTbfGgZ+v9k7W5q9bp6V3Tvnb/enN3ttJeWijENDjMfNaVwgWHT99GrrFXq1Tkyvej2DTPCITsDsepVgVZSga4oMVTuqFrZKUQl+Gze6rYZjAI/LgIDUwzH+heACtRKaAn4ZD/nvGHCw0/Wgjxv87ijSW8TLrS6K2P/39SmuIkUW0C9SRT5Kdmw4BmPHou0sQ13Bdgy3dRjbUd4/zYvLMJYpxTHv+EPsPPe/uoM++itwv7eM3sTrZLI7mzv+uk2eU8LR0k94x1cK6ePm2ftLi+XQLxixJXJdt91yytKu6FB4mRxVfEK5aSKjMpFww9pEt4SAeaFbzDskv7FhS/u9fZX7s4LYQW6cS+G+315Vt+f1z6T0D/tMqmW1K6x7zKdSSkKoh+ArCzf7Id30SXvRSWwPevff/gWfsNW3ps3sPc+5kO66TZ4T6Zp22QmOSrpKIV1e6nkppGsZbjXkvZd2FVMdiHgVK22g3o0jdiZfpd3Wyffm5uZ6+dPcIls/FHsq5d9WBFeR6ehZhnOtujDM02UySl04/8fwA8XwpR53jeC1iQ4Uv2vrbIjeG/rvHLuVWWHrUux1Pxn0QTLjFWRsVmhC287M9qVZVcotLrpclCWd2y9TnDU0k4WQb1kHZuKzvDG/GBefU4u5VlyZ2S5RnKwig4JBgCQWn6ON0IwbVmcFDyj0u/VrCdOsU4OloAbrUJfc9qW42mkvuUrZbftNh72jK++k4MN8wrGTE1QvuUb7iJdcpZDq7/tTQOSrthBFCPPSGaeru8UkqxyTDMXrrn29KmXV/HNZQSr5R8dm7z8= \ No newline at end of file diff --git a/wyk/08_Neuronowy_ngramowy_model/bow1.drawio.png b/wyk/08_Neuronowy_ngramowy_model/bow1.drawio.png new file mode 100644 index 0000000..c500af5 Binary files /dev/null and b/wyk/08_Neuronowy_ngramowy_model/bow1.drawio.png differ diff --git a/wyk/08_Neuronowy_ngramowy_model/bow2.drawio b/wyk/08_Neuronowy_ngramowy_model/bow2.drawio new file mode 100644 index 0000000..3d99bdb --- /dev/null +++ b/wyk/08_Neuronowy_ngramowy_model/bow2.drawio @@ -0,0 +1 @@ +5Vpdc+I2FP01nmkfkkGyAftxcYC2m22ym5lt05cdrS1sgbGIEDHw6ythGX/IEDYECOQBkI6vJOueo6srDYbpjud9hibhF+rjyIANf26YNwaEjgXEtwQWKQBajpUiASO+wnLggSyxAhsKnREfT0uGnNKIk0kZ9GgcY4+XMMQYTcpmAxqVR52gAGvAg4ciHf2H+DxMURu2c/wPTIIwG1lMMH0yRpmx6mIaIp8mKbSanNk1TJdRytPSeO7iSDov80vqgd6Gp+sXYzjmuzT4evclueuPRv3bP+NvT72AA/v2ykx7eUbRTE3YgK1I9NfxybMoBrJoQMGZlfww2h1yBY32TQpklmLIkvFqsnyReZDjucRDPo4EAERxyhkdYZdGlAkkprGw7AxIFFUgFJEgFlVPzBALvPOMGSeCm0/qwZj4vhymk4SE44cJ8uSYiVCiwBidxT6Wk2+sX6voLeVA2SeeFyDlvT6mY8zZQpiop1ZLaVJp2bJVPcmFkZmEBU2YCkNKisG655wtUVCE/QJ5juZr7AvxqiplPKQBjVHUzdGKV3KbW0onip4h5nyhViKacVomD88J/1c2v26q2qPqTJZv5sXK4iXnT+mMeXjLFFtqySMWYL7Fzk7t5Py3UslwhDh5Li/uNyemVbOqKkzlPIAN+i34/A3EC5yyeEFTF+9a0EdRL7DORb7C62xRaCSrj1l/spI3W9X2l729o+yB+a50b79/3Z9e9lBz0pQO+BjNL3jnBJXg09BZWGPHYWGnxIfR5chwm4YNkS99wFDi0wn9SfAKtaY8QTK5jeT3CEmw4/o4oIbMxdKkichy0yWx+P2+PXGqSbxS6CerIh8l5xKZtoi5Re1cAV070GkeRjsetYe9r85jj/wVOt+bsDv2O7W7+/acGXzYnLlptSv8HTNrTh4aT4BNWo/d/rf/0Gdq9axZduQ5q7TDcpxfSTxWtXvMiPCYFMF2NivJQzntAG737p6E9ucffw/9J+sWe9NlevDYI8lQTe8pEa+S7xLQqWqlXRFBmjipdsWz7stdwUpX6SS1rj4xhhYFs4k0mGqiW8/69TrUw8j19fXlRgKzkgOYzonjQLsmjKcxG2Sx+lK5gObpYnJtQGmeS0we0JirDqWDOxsi6AtnvmMGVbOy7Bq7xcGXO6qKYUNsfkW4rHVP+1wk8trbggNKa9dbtNaeEtwrBhzm0kwLijUe3LxntcpxErZrzq3mMQOlfsOSIKaOoRGJCV2VfnN/f9eb116kWFY5EMGaAyGAxyTF0UgRI7nFD6jU5QdusKmzPdQnG2uVebqNky+46m2dWZOYHOy6rpbbTFxnlKbvF/Iq2/zJU0MANAISPOKULZbIG8poxweXS4el35+YOiEHuzutZ0S/wV6ieMaWOCZY4EM0khegtRtT9wNtTGYNU/C4TOm33GyJ/JG8k04X0eWyUT2u1MSxIy8bSyMjMFzLcBzh/o9HSG0u/UaEiGr+v5r0AJr/O8ns/g8= \ No newline at end of file diff --git a/wyk/08_Neuronowy_ngramowy_model/bow2.drawio.png b/wyk/08_Neuronowy_ngramowy_model/bow2.drawio.png new file mode 100644 index 0000000..4debc2e Binary files /dev/null and b/wyk/08_Neuronowy_ngramowy_model/bow2.drawio.png differ diff --git a/wyk/08_Neuronowy_ngramowy_model/ngram-tgh.drawio b/wyk/08_Neuronowy_ngramowy_model/ngram-tgh.drawio new file mode 100644 index 0000000..099793b --- /dev/null +++ b/wyk/08_Neuronowy_ngramowy_model/ngram-tgh.drawio @@ -0,0 +1 @@ +7Vpbc5s4FP41PCZjxMXwWBzb6W4y3TQz3c2+dFSjgGoZUVkOdn99pSDMTb6kviVxZzKxdDi68J2PcxEYVm8yHzKYxrc0RMQAnXBuWFcGAKYNOuJHShZK4ttWLokYDpWsFNzjn0gJ1cBohkM0rSlySgnHaV04okmCRrwmg4zRrK72SEl91RRGqCW4H0HSlv6LQx7nUg90S/k1wlFcrGy6fn5lAgtlNcU0hiHNctHzzVl9w+oxSnnemsx7iEj0ClxyBAYrri43xlDCtxlw9+k2+zQcj4c3H5PPPwYRN72bC5DP8gTJTN2w2ixfFAgwOktCJCcxDSvIYszRfQpH8momjC5kMZ8Qdbm9KbXPJ8Q4mldEapNDRCeIs4VQUVeBpUyvKANs1c9K/M1CJ65gX8igMnm0nLpERTQUMC8AydKA5BKxahDiJ9GM+POd2+Iv+2p0Ayxg7V7lgkJTLFxTboAssOF1JKec0THqUUKZkCQ0EZrBIyakIYIER4nojgTiSMgDiTQWBP6gLkxwGMpltKYrjdvZj/WWT7yynmW1rece03h+C2sUiidcdSnjMY1oAkm/lDZQKXVuKE2Veb4jzhfKXcEZp3XjoTnm/8nhl47qPajJZPtqXu0sNoE/pTM2Qmtu0VV+EbII8TV6Xq4n73+tKRkikOOnugfcu2HcV+h6OnXyXjht8treMdlr2m+FvgJ1tqgMkt2HYj7ZKYc993anvbcl7U3rVfHeewO8N09P/M7KoIuLKEooHV/M0kqIxe8/wLp2Iz3yTxxgTbNlqQyyKc+gEBKcYCpb79UcdjNb7Wiy1c5R7dHO6af0kU/g/B1bATQd2OnNsFXVwOjPsdFzDA/AUILAYBbSlH7D6Flqq8coJPL/GEph0AtRRA15x3nFgWXb6eFE/H5ZX3VoqpZc9I01JedSsAAfiISlRp7l4UOFPMB3DkOeEfW+D+78hwH+K/a/OKA/CQNtxXniHMEx6ygdtSzXgnS+qfH+Mlk19B+KxRKlrb1GEQ8aNsxzczWqeua0YSLQbUyUJ++tiT4wBhcVtVQqTLffcJEUbb2vur5o5DsoybkE9/f5qit41x8jmWd7jOQ4DQYe8RiJumlggX7wlc5ur3vh3dXH61FxUHoqZwOO5W0armVjZZ7dd36YLHUf+sPP/8O/qT2wZxfFQX2lMteCeqw6fN0mX1OMtbwTxlgtSNafGHugGGvbfiPv/O0o257qUHFWs9KGSLtxxM6xVsvbdqy9vLx8v+HSah4KaRzHocKlFv/ui3MdUb0G55vvALfhC46Y72gN6P1x/Ady/MsD213dfnOiQzn91jobXP4G/Z0dvjaV7J4HX1+anq9Ou7dIz50d+b9TwXWY9LzlkTUIro6yTt1Jm5pzQrN7zKrUfSus3yN7nTZ7V39fcCr2Oi328ihuG4sQnE5XJRkVyOE0zT9ue8Rzabp9sLn5zkT3ylfDZftQXG6/F5+N2YLLFx9v633iblaxukW9VPUqzn68iuiWHxzmobf8btPq/wI= \ No newline at end of file diff --git a/wyk/08_Neuronowy_ngramowy_model/ngram-tgh.drawio.png b/wyk/08_Neuronowy_ngramowy_model/ngram-tgh.drawio.png new file mode 100644 index 0000000..c5407b2 Binary files /dev/null and b/wyk/08_Neuronowy_ngramowy_model/ngram-tgh.drawio.png differ diff --git a/wyk/08_Neuronowy_ngramowy_model/ngram.drawio b/wyk/08_Neuronowy_ngramowy_model/ngram.drawio new file mode 100644 index 0000000..0c0042b --- /dev/null +++ b/wyk/08_Neuronowy_ngramowy_model/ngram.drawio @@ -0,0 +1 @@ +7VpZc9owEP41fkwGn9iPhQA90mmTzKRNXzoqVmwV4XWFiKG/vhKW4zMYypUmfQHp0+r6dtlDg2b2p4sRQ3H4EXxMNaPjLzTzQjMMz9LFpwSWKaA7npUiASO+wnLghvzGCuwodE58PCsJcgDKSVwGxxBFeMxLGGIMkrLYPdDyrjEKcA24GSNaR78Qn4cp6hrdHH+LSRBmO4sLpiNTlAmrJWYh8iFJodXlzIFm9hkAT1vTRR9TSV7GS8rA8InRx4MxHPFNJlx9+ph8Gk0mo8t30fWvYcB19/LMSFd5QHSuLqwOy5cZAwzmkY/lIrpm9pKQcHwTo7EcTYTOBRbyKVXD9UOpcz5gxvGiAKlDjjBMMWdLIaJGDVOpXpmMYal+kvOvZzJhgfsMQ0rlwePSOSuioYjZgiSzgSSHil17PnkQzYCvbi4M20q+a90eEbR2L1IgkxQbl4QrJAtueJnJGWcwwX2gwAQSQSQke/eE0gqEKAki0R0LxrHAe5JpIgz4jRqYEt+X2zSqLlduZz/as4yy9kyzrj3nmMrzalxjX/zCVRcYDyGACNFBjlZYyWUuAWKlnp+Y86VyV2jOoaw8vCD8q5x+bqvenVpMti8Wxc6yjfwZzNkYr7mio/wiYgHma+TcVE7ef60qGaaIk4eyB9y7Ypzn53p0r2y8ul03Xss9pvXq1r9ivoJ1tixMkt27bD3Zyaeterubvbuh2evms7J79/nb/enNvvNkyCVZDKUAk7N5XAiw5OWHV8eqJEfeicOrrtc0lSA24wkSICURAdl6qeqwqrlqpyFX7RxVH/WMfgb3fIoWL1gLeiVsn14LG5UMDH5PtL6tuQbyJQcMJT7E8IPgFWqpX5FP5ecESbDX93EAmkyy03KDyLbdJ5H4vl1fcjSULCn0g1WR11KtiEJeZCtF23kMPQXbMTz7MLYzBvfn8Mq7G5L3oXdrG4Op32usNk+cH9h6maSjluSNJL3etHh/Waya+hmI2CLXtVsp4I2KDtO8XM0qvje1LGR0KwuliXttoTeMoWVBLJYCs80PnKVEG5+rLC8a6Qly43wk9+/ttanYXf+EpL/aJyTbrljgEZ+QkpvOL53Fzt1gdP0NfQBraM3P7NM6G2PbN6SKi2itrhsvnT22t1XXzo5eaCfN1IuPfcTKmqk2MPik9ZruCWNlI0nm64iV7Va/91hpWV4lffzraFlf6lDxsmGnlojZOmPnmNlot/WYeX5+/qzD3m6Oo/q00+A4jhr2ulvnLKII7f3TectOCjScii84dd7i/nf8B3L8hrenIqm60KGcfm2fFpffIr+twxfd/I8OqXj+dxFz8Ac= \ No newline at end of file diff --git a/wyk/08_Neuronowy_ngramowy_model/ngram.drawio.png b/wyk/08_Neuronowy_ngramowy_model/ngram.drawio.png new file mode 100644 index 0000000..3686ee8 Binary files /dev/null and b/wyk/08_Neuronowy_ngramowy_model/ngram.drawio.png differ diff --git a/wyk/08_Neuronowy_ngramowy_model/tanh.png b/wyk/08_Neuronowy_ngramowy_model/tanh.png new file mode 100644 index 0000000..3de0e45 Binary files /dev/null and b/wyk/08_Neuronowy_ngramowy_model/tanh.png differ diff --git a/wyk/08_Neuronowy_ngramowy_model/trigram1.drawio b/wyk/08_Neuronowy_ngramowy_model/trigram1.drawio new file mode 100644 index 0000000..7b43c21 --- /dev/null +++ b/wyk/08_Neuronowy_ngramowy_model/trigram1.drawio @@ -0,0 +1 @@ +5VldU+IwFP01fdTpJ5RHQXB3R8d1ndldn3YiiW0k7WVDsOCv34SmtKWBRVERfYHk5Puec3OT1PJ6yeyMo3F8AZgwy7XxzPJOLdft+I78VcA8B5xWx8+RiFOssRK4po9Eg7ZGpxSTSa2iAGCCjuvgENKUDEUNQ5xDVq92B6w+6hhFpAFcDxFror8oFnGOhm67xL8QGsXFyHKBeUmCisq6i0mMMGQ5tFic17e8HgcQeSqZ9QhTxivskltgsKZ0OTFOUrFNgyGE94Orzs2Afos7PwO3n+DukV7GRMyLBRMs16+zwEUMEaSI9Uu0y2GaYqJ6tWWurHMOMJagI8F7IsRck4mmAiQUi4TpUjKj4nclfaO7UunTWTUzLzKp4HPVxD4OiuxN0YPKlM0WuaJdvjq1pLVGK9iBKR/qWleXF9nl2Wh0dv41/fF3EAknPD9ytfgQj4jYUK+1pFb6BIGEyPnIdpwwJOhDfR5IizNa1iv5kwlNoZnOTZN8QGyqR2rwW7KnjJfFVJDrMVosPZMuXGdqrbkeCBdkVoGaK9alnq09We8Arq/zWelOjqexuOJKBbaLkYyab9rkDTWvBXwoqve2VL27o+oXTU84R/NKhTHQVEwqPX9XQEVaraAmLa++A8pE3mMpleXUnu9insHFWkzapovpg0xGYsGAjHJ+9sdqd6m0Tfs0B4qacuBa5RU5Ss8SdfVMBIcR6QEDLpEUUqXJO8rYCoQYjVKZHUqiicS7yk+pjGYnuiChGC8EbXL8ushfwvd920hQ1fdbr+T6RvI6h+L6z/fY1pYeGxp3iX0Frtb7C1zFeaoQrxM0xeu7b6lexz8U+e4WuZ4j+3BL2Tveu9J9+P51v3/Z22tDLi1iKAMYHU3HlQBLP354DZyVo3W45/DqOA2mMsQnIkMSZDSloFIflQ7fW6HDNtx07Dflo3kfnMCdSNDsA7Ow4hTO/lnY6srA4XFk9QIrdBFWNuAowzCGW0oWqK+9CDP1O0IK7PYwicBSm3F+3aAqHfRoKv9/br5yGK4sOXTLV5HPcltxQ/e4fqFchp6KdtxO8DraMb5VmKSz+/mgYROD5bbf5vb+oPNJjsX/f9DZ9QGm8cLih/6x+Y1l2Ul+MtftSiK36Mptr3SVH94bXT31WcgwUsd+6tzqLXZ+SjLq1nTp3fyU5BzyU9Juu06wRocvf9aV2fKrTM51+W3L6/8D \ No newline at end of file diff --git a/wyk/08_Neuronowy_ngramowy_model/trigram1.drawio.png b/wyk/08_Neuronowy_ngramowy_model/trigram1.drawio.png new file mode 100644 index 0000000..13d6e9c Binary files /dev/null and b/wyk/08_Neuronowy_ngramowy_model/trigram1.drawio.png differ