diff --git a/03_zadania.ipynb b/03_zadania.ipynb new file mode 100644 index 0000000..623da09 --- /dev/null +++ b/03_zadania.ipynb @@ -0,0 +1,68 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Rozwiązania do zadań proszę umieszczać w nowych komórkach pomiędzy zadaniami\n", + "Zadania (jeżeli wymagają napisania programu) piszemy w języku Python\n", + "\n", + "\n", + "Funkcje wspomagające wykonanie zadań znajdują się w materiałach wykładowych:\n", + "\n", + "https://git.wmi.amu.edu.pl/pms/moj-2024/src/branch/main/wyk\n", + "\n", + "oraz dla zajęć 03 bardzo pomocny będzie notebook:\n", + "\n", + "https://git.wmi.amu.edu.pl/filipg/aitech-moj-2023/src/branch/master/wyk/09_Zanurzenia_slow.ipynb" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Zadanie 1 (110 punktów)\n", + "\n", + "Na podstawie zbioru danych z poprzednich zajęć (https://git.wmi.amu.edu.pl/ryssta/moj-2024-ns-cw/src/branch/main/challenging_america_50k_texts.zip) stwórz tetragramowy (czyli wykorzystujący 3 słowa do predykcji czwartego słowa) neuronowy model języka bazujący na zanurzeniach słów. Zadanie proszę wykonać przy użyciu biblioteki torch (warto wykorzystać również bibliotekę torchtext) w języku Python. Kroki wymagane do wykonania zadania:\n", + "\n", + "1. Zamiana wielkich liter na małe oraz usunięcie wszystkich znaków niebędących literami od \"a\" do \"z\" lub spacją - 5 punktów\n", + "2. Stworzenie słownika składającego się z 20000 najczęściej występujących słów + token <unk> dla pozostałych słów (uwaga - token <unk> też posiada swoje zanurzenie) - 5 punktów\n", + "3. Poprawne zaimplementowanie architektury tetragramowego modelu bazującego na zanurzeniach o rozmiarze 200 przyjmującego na wejściu 3 tokeny - 40 punktów.\n", + "4. Wytrenowanie modelu na 3 epokach na całym korpusie z malejącą wartością funkcji straty w czasie trenowania (jeżeli będzie widać, że wartość funkcji straty nie maleje na przestrzeni treningu, to znaczy że coś jest nie tak - wtedy punkty nie zostaną przyznane) - 60 punktów" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Zadanie 2 (40 punktów)\n", + "\n", + "Za pomocą modelu z zadania 1, wygeneruj zdania (bazując na dodawaniu do sekwencji najbardziej prawdopodobnego tokenu) o długości 15 tokenów (słów) bez uwzględniania tokenu <unk> jako potencjalnie kolejny nowy token, zakładając następujące wejście do modelu:\n", + "1. it will be\n", + "2. they went for\n", + "3. (tutaj proszę wybrać samemu 3 słowa ze słownika i przeprowadzić proces generowania sekwencji)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "python39", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/03_zadania_helpful_codeblocks.ipynb b/03_zadania_helpful_codeblocks.ipynb new file mode 100644 index 0000000..1f16667 --- /dev/null +++ b/03_zadania_helpful_codeblocks.ipynb @@ -0,0 +1,341 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Funkcja build_vocab_from_iterator automatycznie tworzy słownik na podstawie najczęściej występujących słów, jednocześnie traktując pozostałe słowa jako <unk>" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "c:\\Users\\ryssta\\AppData\\Local\\anaconda3\\envs\\python39\\lib\\site-packages\\torchtext\\vocab\\__init__.py:4: UserWarning: \n", + "/!\\ IMPORTANT WARNING ABOUT TORCHTEXT STATUS /!\\ \n", + "Torchtext is deprecated and the last released version will be 0.18 (this one). You can silence this warning by calling the following at the beginnign of your scripts: `import torchtext; torchtext.disable_torchtext_deprecation_warning()`\n", + " warnings.warn(torchtext._TORCHTEXT_DEPRECATION_MSG)\n", + "c:\\Users\\ryssta\\AppData\\Local\\anaconda3\\envs\\python39\\lib\\site-packages\\torchtext\\utils.py:4: UserWarning: \n", + "/!\\ IMPORTANT WARNING ABOUT TORCHTEXT STATUS /!\\ \n", + "Torchtext is deprecated and the last released version will be 0.18 (this one). You can silence this warning by calling the following at the beginnign of your scripts: `import torchtext; torchtext.disable_torchtext_deprecation_warning()`\n", + " warnings.warn(torchtext._TORCHTEXT_DEPRECATION_MSG)\n" + ] + } + ], + "source": [ + "from torchtext.vocab import build_vocab_from_iterator\n", + "import io\n", + "import zipfile\n", + "import torch\n", + "\n", + "\n", + "with zipfile.ZipFile(\"challenging_america_50k_texts.zip\") as zf:\n", + " with io.TextIOWrapper(zf.open(\"challenging_america_50k_texts.txt\"), encoding=\"utf-8\") as f:\n", + " data = f.readlines()\n", + "\n", + "def get_words_from_line(line):\n", + " line = line.rstrip()\n", + " for t in line.split():\n", + " yield t\n", + "\n", + "\n", + "def get_word_lines_from_list(data):\n", + " for line in data:\n", + " yield get_words_from_line(line)\n", + "\n", + "vocab_size = 3000\n", + "vocab = build_vocab_from_iterator(\n", + " get_word_lines_from_list(data),\n", + " max_tokens = vocab_size,\n", + " specials = [''])\n", + "vocab.set_default_index(vocab[''])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Dostęp do par słowo, indeks ze słownika" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 0\n", + "witness 1755\n", + "Supreme 2520\n", + "seems 577\n", + "her 51\n", + "! 503\n", + "were 40\n", + "Messrs. 1911\n", + "small 282\n", + "council 2064\n", + "but 35\n" + ] + } + ], + "source": [ + "vocab_dict = vocab.get_stoi()\n", + "for x, key in enumerate(vocab_dict):\n", + " print(key, vocab_dict[key])\n", + " if x == 10:\n", + " break" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Słownik dla podanego słowa w postaci stringa zwraca indeks w słowniku, który będzie wykorzystywany przez sieć neuronową (słowa nie znajdujące się w słowniku mapowane są na token unk, który posiada indeks 0 - zostało to zdefiniowane poprzez metodę vocab.set_default_index(vocab['<unk>']))" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5\n", + "1\n", + "208\n", + "0\n", + "0\n", + "Dla listy słów:\n", + "[5, 1]\n" + ] + } + ], + "source": [ + "print(vocab[\"a\"])\n", + "print(vocab[\"the\"])\n", + "print(vocab[\"John\"])\n", + "print(vocab[\"awnifnawonf\"])\n", + "print(vocab[\"Poznań\"])\n", + "\n", + "print(\"Dla listy słów:\")\n", + "print(vocab.lookup_indices([\"a\", \"the\"]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Operacja odwrota (czyli odczytanie słowa na podstawie indeksu)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "the\n", + "attempt\n", + "Dla listy indeksów:\n", + "['', 'the', 'attempt', 'drew']\n" + ] + } + ], + "source": [ + "print(vocab.lookup_token(1))\n", + "print(vocab.lookup_token(1000))\n", + "\n", + "print(\"Dla listy indeksów:\")\n", + "print(vocab.lookup_tokens([0, 1, 1000, 2000]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Aby przekazać dane słowo (lub słowa) do sieci neuronowej, należy utworzyć na podstawie indeksu danego słowa Tensor" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "tensor(1)\n", + "tensor([ 1, 2020])\n" + ] + } + ], + "source": [ + "input_to_neural_network = torch.tensor(vocab[\"the\"])\n", + "print(input_to_neural_network)\n", + "\n", + "input_to_neural_network = torch.tensor(vocab.lookup_indices([\"the\", \"current\"]))\n", + "print(input_to_neural_network)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Zanurzenia słów uzyskujemy poprzez wykorzystanie warstwy torch.nn.Embedding" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Uzyskanie wartości embeddingów dla tokenu o indeksie 1 oraz tokenu o indeksie 5\n", + "tensor([[-1.0945, -0.1461, 1.2927],\n", + " [-0.0303, 0.5213, 1.1486]], grad_fn=)\n", + "##########################################################\n", + "Uzyskanie wartości embeddingów dla batcha z tokenami\n", + "tensor([[[-1.0945, -0.1461, 1.2927],\n", + " [ 0.2963, 0.1083, 0.0797]],\n", + "\n", + " [[-0.9783, 1.1639, 0.3828],\n", + " [ 1.1856, 1.1943, -0.5562]],\n", + "\n", + " [[-0.3472, 0.5670, -1.2830],\n", + " [-1.0945, -0.1461, 1.2927]]], grad_fn=)\n" + ] + } + ], + "source": [ + "vocab_size = 10\n", + "embedding_size = 3\n", + "embedding_layer = torch.nn.Embedding(vocab_size, embedding_size)\n", + "input_to_embedding_layer = torch.IntTensor([1, 5])\n", + "print(\"Uzyskanie wartości embeddingów dla tokenu o indeksie 1 oraz tokenu o indeksie 5\")\n", + "print(embedding_layer((input_to_embedding_layer)))\n", + "\n", + "batched_input_to_embedding_layer = torch.IntTensor([[1, 4],\n", + " [2, 9],\n", + " [3, 1]])\n", + "print(\"##########################################################\")\n", + "print(\"Uzyskanie wartości embeddingów dla batcha z tokenami\")\n", + "print(embedding_layer((batched_input_to_embedding_layer)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Aby połączyć ze sobą zanurzenia kilku słów (podczas trenowania modelu ngramowego innego niż unigramowy/bigramowy), należy użyć konkatenacji. W torchu do tego wykorzystujemy funkcję torch.cat" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "tensor([0.5000, 0.2000, 0.2000, 0.9100, 0.9200, 0.9300])\n" + ] + } + ], + "source": [ + "first_embedding = torch.Tensor([0.5, 0.2, 0.2])\n", + "second_embedding = torch.Tensor([0.91, 0.92, 0.93])\n", + "\n", + "concatenated_embeddings = torch.cat([first_embedding, second_embedding])\n", + "print(concatenated_embeddings)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### W przypadku korzystania z batchy musimy zwrócić uwagę na właściwą oś (axis) po której będziemy dokonywać konkatenacji" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Nieprawidłowa konkatenacja embeddingów (stworzenie 4 tensorów o wymiarze 3, zamiast 2 tensorów o wymiarze 6)\n", + "tensor([[0.5000, 0.2000, 0.2000],\n", + " [0.5100, 0.2100, 0.2100],\n", + " [0.9100, 0.9200, 0.9300],\n", + " [0.9110, 0.9220, 0.9330]])\n", + "#########################################################################\n", + "Prawidłowa konkatenacja embeddingów dzięki właściwej wartości parametru axis\n", + "Wtedy pierwsze 3 wartości (czyli dla pierwszego Tensora są to wartości 0.5000, 0.2000, 0.2000) reprezentują embedding pierwszego słowa\n", + "a kolejne 3 wartości reprezentują embedding drugiego słowa (czyli 0.9100, 0.9200, 0.9300)\n", + "tensor([[0.5000, 0.2000, 0.2000, 0.9100, 0.9200, 0.9300],\n", + " [0.5100, 0.2100, 0.2100, 0.9110, 0.9220, 0.9330]])\n" + ] + } + ], + "source": [ + "first_batched_embedding = torch.Tensor([[0.5, 0.2, 0.2], \n", + " [0.51, 0.21, 0.21]])\n", + "second_batched_embedding = torch.Tensor([[0.91, 0.92, 0.93],\n", + " [0.911, 0.922, 0.933]])\n", + "\n", + "concatenated_embeddings = torch.cat([first_batched_embedding, second_batched_embedding])\n", + "print(\"Nieprawidłowa konkatenacja embeddingów (stworzenie 4 tensorów o wymiarze 3, zamiast 2 tensorów o wymiarze 6)\")\n", + "print(concatenated_embeddings)\n", + "\n", + "properly_concatenated_embeddings = torch.cat([first_batched_embedding, second_batched_embedding], axis=1)\n", + "\n", + "print(\"#########################################################################\")\n", + "print(\"Prawidłowa konkatenacja embeddingów dzięki właściwej wartości parametru axis\")\n", + "print(\"Wtedy pierwsze 3 wartości (czyli dla pierwszego Tensora są to wartości 0.5000, 0.2000, 0.2000) reprezentują embedding pierwszego słowa\")\n", + "print(\"a kolejne 3 wartości reprezentują embedding drugiego słowa (czyli 0.9100, 0.9200, 0.9300)\")\n", + "print(properly_concatenated_embeddings)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "python39", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}