{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Generatywne sieci\n", "\n", "Rekurencyjne Sieci Neuronowe (RNN) oraz ich warianty z komórkami bramkowymi, takie jak Komórki Długiej Krótkoterminowej Pamięci (LSTM) i Bramkowe Jednostki Rekurencyjne (GRU), dostarczają mechanizmu do modelowania języka, czyli potrafią uczyć się kolejności słów i przewidywać następne słowo w sekwencji. Dzięki temu możemy używać RNN do **zadań generatywnych**, takich jak zwykłe generowanie tekstu, tłumaczenie maszynowe, a nawet opisywanie obrazów.\n", "\n", "W architekturze RNN, którą omawialiśmy w poprzedniej jednostce, każda jednostka RNN generowała kolejny ukryty stan jako wynik. Jednak możemy również dodać kolejny wynik do każdej rekurencyjnej jednostki, co pozwoli nam generować **sekwencję** (równą długości oryginalnej sekwencji). Co więcej, możemy używać jednostek RNN, które nie przyjmują wejścia na każdym kroku, a jedynie korzystają z początkowego wektora stanu, aby następnie generować sekwencję wyników.\n", "\n", "W tym notatniku skupimy się na prostych modelach generatywnych, które pomagają nam generować tekst. Dla uproszczenia zbudujmy **sieć na poziomie znaków**, która generuje tekst litera po literze. Podczas treningu musimy wziąć jakiś korpus tekstu i podzielić go na sekwencje liter.\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import tensorflow as tf\n", "from tensorflow import keras\n", "import tensorflow_datasets as tfds\n", "import numpy as np\n", "\n", "ds_train, ds_test = tfds.load('ag_news_subset').values()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Tworzenie słownika znaków\n", "\n", "Aby zbudować sieć generatywną na poziomie znaków, musimy podzielić tekst na pojedyncze znaki zamiast słów. Warstwa `TextVectorization`, której używaliśmy wcześniej, nie pozwala na to, więc mamy dwie opcje:\n", "\n", "* Ręczne wczytanie tekstu i tokenizacja \"na piechotę\", jak w [tym oficjalnym przykładzie Keras](https://keras.io/examples/generative/lstm_character_level_text_generation/)\n", "* Użycie klasy `Tokenizer` do tokenizacji na poziomie znaków.\n", "\n", "Wybierzemy drugą opcję. `Tokenizer` może być również używany do tokenizacji na poziomie słów, więc przejście z tokenizacji znakowej na słowną powinno być dość proste.\n", "\n", "Aby przeprowadzić tokenizację na poziomie znaków, musimy przekazać parametr `char_level=True`:\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "def extract_text(x):\n", " return x['title']+' '+x['description']\n", "\n", "def tupelize(x):\n", " return (extract_text(x),x['label'])\n", "\n", "tokenizer = keras.preprocessing.text.Tokenizer(char_level=True,lower=False)\n", "tokenizer.fit_on_texts([x['title'].numpy().decode('utf-8') for x in ds_train])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Chcemy również użyć jednego specjalnego tokenu, aby oznaczyć **koniec sekwencji**, który nazwiemy ``. Dodajmy go ręcznie do słownika:\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "eos_token = len(tokenizer.word_index)+1\n", "tokenizer.word_index[''] = eos_token\n", "\n", "vocab_size = eos_token + 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Teraz, aby zakodować tekst w sekwencje liczb, możemy użyć:\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[[48, 2, 10, 10, 5, 44, 1, 25, 5, 8, 10, 13, 78]]" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tokenizer.texts_to_sequences(['Hello, world!'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Trenowanie generatywnej RNN do tworzenia tytułów\n", "\n", "Sposób, w jaki będziemy trenować RNN do generowania tytułów wiadomości, wygląda następująco. Na każdym kroku weźmiemy jeden tytuł, który zostanie wprowadzony do RNN, a dla każdego znaku wejściowego poprosimy sieć o wygenerowanie kolejnego znaku wyjściowego:\n", "\n", "![Obraz przedstawiający przykład generowania słowa 'HELLO' przez RNN.](../../../../../lessons/5-NLP/17-GenerativeNetworks/images/rnn-generate.png)\n", "\n", "Dla ostatniego znaku naszej sekwencji poprosimy sieć o wygenerowanie tokenu ``.\n", "\n", "Główna różnica w generatywnej RNN, którą tutaj wykorzystujemy, polega na tym, że będziemy pobierać wyjście z każdego kroku RNN, a nie tylko z ostatniej komórki. Można to osiągnąć, ustawiając parametr `return_sequences` dla komórki RNN.\n", "\n", "Tak więc, podczas treningu, wejściem do sieci będzie sekwencja zakodowanych znaków o określonej długości, a wyjściem będzie sekwencja o tej samej długości, ale przesunięta o jeden element i zakończona tokenem ``. Minibatch będzie składać się z kilku takich sekwencji, a do wyrównania wszystkich sekwencji będziemy musieli użyć **paddingu**.\n", "\n", "Stwórzmy funkcje, które przekształcą dla nas zbiór danych. Ponieważ chcemy uzupełniać sekwencje na poziomie minibatch, najpierw podzielimy zbiór danych, wywołując `.batch()`, a następnie użyjemy `map`, aby dokonać transformacji. Funkcja transformacji będzie więc przyjmować cały minibatch jako parametr:\n" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "def title_batch(x):\n", " x = [t.numpy().decode('utf-8') for t in x]\n", " z = tokenizer.texts_to_sequences(x)\n", " z = tf.keras.preprocessing.sequence.pad_sequences(z)\n", " return tf.one_hot(z,vocab_size), tf.one_hot(tf.concat([z[:,1:],tf.constant(eos_token,shape=(len(z),1))],axis=1),vocab_size)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Kilka ważnych rzeczy, które robimy tutaj:\n", "* Najpierw wyciągamy rzeczywisty tekst z tensora tekstowego\n", "* `text_to_sequences` konwertuje listę ciągów znaków na listę tensora liczb całkowitych\n", "* `pad_sequences` uzupełnia te tensory do ich maksymalnej długości\n", "* Na końcu kodujemy wszystkie znaki w formacie one-hot, a także wykonujemy przesunięcie i dodajemy ``. Wkrótce zobaczymy, dlaczego potrzebujemy znaków zakodowanych w formacie one-hot.\n", "\n", "Jednak ta funkcja jest **Pythonowa**, czyli nie może być automatycznie przetłumaczona na graf obliczeniowy Tensorflow. Otrzymamy błędy, jeśli spróbujemy użyć tej funkcji bezpośrednio w funkcji `Dataset.map`. Musimy otoczyć to Pythonowe wywołanie, używając wrappera `py_function`:\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "def title_batch_fn(x):\n", " x = x['title']\n", " a,b = tf.py_function(title_batch,inp=[x],Tout=(tf.float32,tf.float32))\n", " return a,b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> **Uwaga**: Rozróżnienie między funkcjami transformacji w stylu Pythonowym a funkcjami Tensorflow może wydawać się zbyt skomplikowane i możesz się zastanawiać, dlaczego nie przekształcamy zbioru danych za pomocą standardowych funkcji Pythona przed przekazaniem go do `fit`. Chociaż jest to jak najbardziej możliwe, użycie `Dataset.map` ma ogromną zaletę, ponieważ pipeline przekształcania danych jest wykonywany za pomocą grafu obliczeniowego Tensorflow, co pozwala wykorzystać obliczenia na GPU i minimalizuje konieczność przesyłania danych między CPU a GPU.\n", "\n", "Teraz możemy zbudować naszą sieć generatora i rozpocząć trening. Może być oparta na dowolnej komórce rekurencyjnej, którą omówiliśmy w poprzedniej jednostce (prosta, LSTM lub GRU). W naszym przykładzie użyjemy LSTM.\n", "\n", "Ponieważ sieć przyjmuje znaki jako dane wejściowe, a rozmiar słownika jest dość mały, nie potrzebujemy warstwy osadzania (embedding). Dane wejściowe zakodowane w formacie one-hot mogą być bezpośrednio przekazane do komórki LSTM. Warstwa wyjściowa będzie klasyfikatorem `Dense`, który przekształci wynik LSTM w liczby tokenów zakodowanych w formacie one-hot.\n", "\n", "Dodatkowo, ponieważ mamy do czynienia z sekwencjami o zmiennej długości, możemy użyć warstwy `Masking`, aby utworzyć maskę ignorującą wypełnione (padded) części ciągu. Nie jest to ściśle konieczne, ponieważ nie interesuje nas szczególnie wszystko, co znajduje się poza tokenem ``, ale użyjemy jej, aby zdobyć doświadczenie z tego typu warstwą. `input_shape` będzie miało wartość `(None, vocab_size)`, gdzie `None` wskazuje na sekwencję o zmiennej długości, a kształt wyjściowy to również `(None, vocab_size)`, jak można zobaczyć w `summary`:\n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Model: \"sequential\"\n", "_________________________________________________________________\n", "Layer (type) Output Shape Param # \n", "=================================================================\n", "masking (Masking) (None, None, 84) 0 \n", "_________________________________________________________________\n", "lstm (LSTM) (None, None, 128) 109056 \n", "_________________________________________________________________\n", "dense (Dense) (None, None, 84) 10836 \n", "=================================================================\n", "Total params: 119,892\n", "Trainable params: 119,892\n", "Non-trainable params: 0\n", "_________________________________________________________________\n", "15000/15000 [==============================] - 229s 15ms/step - loss: 1.5385\n" ] }, { "data": { "text/plain": [ "" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "model = keras.models.Sequential([\n", " keras.layers.Masking(input_shape=(None,vocab_size)),\n", " keras.layers.LSTM(128,return_sequences=True),\n", " keras.layers.Dense(vocab_size,activation='softmax')\n", "])\n", "\n", "model.summary()\n", "model.compile(loss='categorical_crossentropy')\n", "\n", "model.fit(ds_train.batch(8).map(title_batch_fn))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Generowanie wyników\n", "\n", "Teraz, gdy model został wytrenowany, chcemy go użyć do generowania wyników. Przede wszystkim potrzebujemy sposobu na dekodowanie tekstu reprezentowanego przez sekwencję numerów tokenów. W tym celu moglibyśmy użyć funkcji `tokenizer.sequences_to_texts`; jednak nie działa ona dobrze z tokenizacją na poziomie znaków. Dlatego weźmiemy słownik tokenów z tokenizera (nazywany `word_index`), zbudujemy odwrotną mapę i napiszemy własną funkcję dekodującą:\n" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "reverse_map = {val:key for key, val in tokenizer.word_index.items()}\n", "\n", "def decode(x):\n", " return ''.join([reverse_map[t] for t in x])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Teraz rozpoczniemy generowanie. Zaczniemy od ciągu znaków `start`, zakodujemy go w sekwencję `inp`, a następnie na każdym kroku wywołamy naszą sieć, aby przewidzieć kolejny znak.\n", "\n", "Wyjście sieci `out` to wektor z `vocab_size` elementami, reprezentujący prawdopodobieństwa każdego tokenu. Możemy znaleźć najbardziej prawdopodobny numer tokenu, używając `argmax`. Następnie dodajemy ten znak do wygenerowanej listy tokenów i kontynuujemy generowanie. Ten proces generowania jednego znaku powtarza się `size` razy, aby wygenerować wymaganą liczbę znaków, a proces kończymy wcześniej, gdy napotkamy `eos_token`.\n" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Today #39;s lead to strike for the strike for the strike for the strike (AFP)'" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def generate(model,size=100,start='Today '):\n", " inp = tokenizer.texts_to_sequences([start])[0]\n", " chars = inp\n", " for i in range(size):\n", " out = model(tf.expand_dims(tf.one_hot(inp,vocab_size),0))[0][-1]\n", " nc = tf.argmax(out)\n", " if nc==eos_token:\n", " break\n", " chars.append(nc.numpy())\n", " inp = inp+[nc]\n", " return decode(chars)\n", " \n", "generate(model)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Próbkowanie wyników podczas treningu\n", "\n", "Ponieważ nie mamy żadnych użytecznych metryk, takich jak *dokładność*, jedynym sposobem, aby zobaczyć, że nasz model się poprawia, jest **próbkowanie** generowanego ciągu podczas treningu. Aby to zrobić, użyjemy **callbacków**, czyli funkcji, które możemy przekazać do funkcji `fit`, a które będą wywoływane okresowo podczas treningu.\n" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Epoch 1/3\n", "15000/15000 [==============================] - 226s 15ms/step - loss: 1.2703\n", "Today #39;s a lead in the company for the strike\n", "Epoch 2/3\n", "15000/15000 [==============================] - 227s 15ms/step - loss: 1.2057\n", "Today #39;s the Market Service on Security Start (AP)\n", "Epoch 3/3\n", "15000/15000 [==============================] - 226s 15ms/step - loss: 1.1752\n", "Today #39;s a line on the strike to start for the start\n" ] }, { "data": { "text/plain": [ "" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sampling_callback = keras.callbacks.LambdaCallback(\n", " on_epoch_end = lambda batch, logs: print(generate(model))\n", ")\n", "\n", "model.fit(ds_train.batch(8).map(title_batch_fn),callbacks=[sampling_callback],epochs=3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ten przykład generuje już całkiem dobry tekst, ale można go jeszcze ulepszyć na kilka sposobów:\n", "* **Więcej tekstu**. Użyliśmy tylko tytułów dla naszego zadania, ale warto poeksperymentować z pełnym tekstem. Pamiętaj, że RNN-y nie radzą sobie najlepiej z obsługą długich sekwencji, więc sensowne jest albo podzielenie ich na krótsze zdania, albo zawsze trenowanie na stałej długości sekwencji o jakiejś z góry określonej wartości `num_chars` (np. 256). Możesz spróbować zmienić powyższy przykład na taką architekturę, korzystając z [oficjalnego samouczka Keras](https://keras.io/examples/generative/lstm_character_level_text_generation/) jako inspiracji.\n", "* **Wielowarstwowy LSTM**. Warto spróbować użyć 2 lub 3 warstw komórek LSTM. Jak wspomnieliśmy w poprzedniej jednostce, każda warstwa LSTM wyodrębnia określone wzorce z tekstu, a w przypadku generatora na poziomie znaków możemy oczekiwać, że niższy poziom LSTM będzie odpowiedzialny za wyodrębnianie sylab, a wyższe poziomy - za słowa i kombinacje słów. Można to łatwo zaimplementować, przekazując parametr liczby warstw do konstruktora LSTM.\n", "* Możesz również poeksperymentować z **jednostkami GRU** i sprawdzić, które działają lepiej, oraz z **różnymi rozmiarami warstw ukrytych**. Zbyt duża warstwa ukryta może prowadzić do nadmiernego dopasowania (np. sieć nauczy się dokładnego tekstu), a mniejszy rozmiar może nie dawać dobrych wyników.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Generowanie miękkiego tekstu i temperatura\n", "\n", "W poprzedniej definicji `generate` zawsze wybieraliśmy znak o najwyższym prawdopodobieństwie jako kolejny znak w generowanym tekście. Skutkowało to tym, że tekst często \"cyklicznie\" powtarzał te same sekwencje znaków, jak w tym przykładzie:\n", "```\n", "today of the second the company and a second the company ...\n", "```\n", "\n", "Jednakże, jeśli spojrzymy na rozkład prawdopodobieństwa dla kolejnego znaku, może się okazać, że różnica między kilkoma najwyższymi prawdopodobieństwami nie jest duża, np. jeden znak może mieć prawdopodobieństwo 0.2, a inny 0.19, itd. Na przykład, gdy szukamy kolejnego znaku w sekwencji '*play*', kolejnym znakiem może być równie dobrze spacja, jak i **e** (jak w słowie *player*).\n", "\n", "Prowadzi nas to do wniosku, że nie zawsze jest \"sprawiedliwe\" wybieranie znaku o najwyższym prawdopodobieństwie, ponieważ wybór drugiego najwyższego może również prowadzić do sensownego tekstu. Bardziej rozsądne jest **losowanie** znaków z rozkładu prawdopodobieństwa podanego przez wynik sieci.\n", "\n", "Takie losowanie można przeprowadzić za pomocą funkcji `np.multinomial`, która implementuje tzw. **rozkład wielomianowy**. Funkcja, która implementuje to **miękkie** generowanie tekstu, jest zdefiniowana poniżej:\n" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "--- Temperature = 0.3\n", "Today #39;s strike #39; to start at the store return\n", "On Sunday PO to Be Data Profit Up (Reuters)\n", "Moscow, SP wins straight to the Microsoft #39;s control of the space start\n", "President olding of the blast start for the strike to pay <b>...</b>\n", "Little red riding hood ficed to the spam countered in European <b>...</b>\n", "\n", "--- Temperature = 0.8\n", "Today countie strikes ryder missile faces food market blut\n", "On Sunday collores lose-toppy of sale of Bullment in <b>...</b>\n", "Moscow, IBM Diffeiting in Afghan Software Hotels (Reuters)\n", "President Ol Luster for Profit Peaced Raised (AP)\n", "Little red riding hood dace on depart talks #39; bank up\n", "\n", "--- Temperature = 1.0\n", "Today wits House buiting debate fixes #39; supervice stake again\n", "On Sunday arling digital poaching In for level\n", "Moscow, DS Up 7, Top Proble Protest Caprey Mamarian Strike\n", "President teps help of roubler stepted lessabul-Dhalitics (AFP)\n", "Little red riding hood signs on cash in Carter-youb\n", "\n", "--- Temperature = 1.3\n", "Today wits flawer ro, pSIA figat's co DroftwavesIs Talo up\n", "On Sunday hround elitwing wint EU Powerburlinetien\n", "Moscow, Bazz #39;s sentries olymen winnelds' next for Olympite Huc?\n", "President lost securitys from power Elections in Smiltrials\n", "Little red riding hood vides profit, exponituity, profitmainalist-at said listers\n", "\n", "--- Temperature = 1.8\n", "Today #39;It: He deat: N.KA Asside\n", "On Sunday i arry Par aldeup patient Wo stele1\n" ] }, { "ename": "KeyError", "evalue": "0", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"\\n--- Temperature = {i}\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mj\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 20\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgenerate_soft\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0msize\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m300\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mstart\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mwords\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mj\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mtemperature\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m\u001b[0m in \u001b[0;36mgenerate_soft\u001b[0;34m(model, size, start, temperature)\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0mchars\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnc\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0minp\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0minp\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mnc\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 13\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchars\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 14\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0mwords\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m'Today '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'On Sunday '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'Moscow, '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'President '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'Little red riding hood '\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m\u001b[0m in \u001b[0;36mdecode\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;34m''\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mreverse_map\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mt\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;34m''\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mreverse_map\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mt\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mKeyError\u001b[0m: 0" ] } ], "source": [ "def generate_soft(model,size=100,start='Today ',temperature=1.0):\n", " inp = tokenizer.texts_to_sequences([start])[0]\n", " chars = inp\n", " for i in range(size):\n", " out = model(tf.expand_dims(tf.one_hot(inp,vocab_size),0))[0][-1]\n", " probs = tf.exp(tf.math.log(out)/temperature).numpy().astype(np.float64)\n", " probs = probs/np.sum(probs)\n", " nc = np.argmax(np.random.multinomial(1,probs,1))\n", " if nc==eos_token:\n", " break\n", " chars.append(nc)\n", " inp = inp+[nc]\n", " return decode(chars)\n", "\n", "words = ['Today ','On Sunday ','Moscow, ','President ','Little red riding hood ']\n", " \n", "for i in [0.3,0.8,1.0,1.3,1.8]:\n", " print(f\"\\n--- Temperature = {i}\")\n", " for j in range(5):\n", " print(generate_soft(model,size=300,start=words[j],temperature=i))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wprowadziliśmy jeszcze jeden parametr o nazwie **temperatura**, który służy do wskazania, jak bardzo powinniśmy trzymać się najwyższego prawdopodobieństwa. Jeśli temperatura wynosi 1.0, przeprowadzamy uczciwe próbkowanie multinomialne, a gdy temperatura dąży do nieskończoności - wszystkie prawdopodobieństwa stają się równe i losowo wybieramy następny znak. Na poniższym przykładzie możemy zaobserwować, że tekst staje się bezsensowny, gdy zbytnio zwiększymy temperaturę, a przypomina \"cyklicznie\" generowany tekst, gdy zbliża się do 0.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n---\n\n**Zastrzeżenie**: \nTen dokument został przetłumaczony za pomocą usługi tłumaczenia AI [Co-op Translator](https://github.com/Azure/co-op-translator). Chociaż dokładamy wszelkich starań, aby zapewnić poprawność tłumaczenia, prosimy pamiętać, że automatyczne tłumaczenia mogą zawierać błędy lub nieścisłości. Oryginalny dokument w jego rodzimym języku powinien być uznawany za źródło autorytatywne. W przypadku informacji o kluczowym znaczeniu zaleca się skorzystanie z profesjonalnego tłumaczenia przez człowieka. Nie ponosimy odpowiedzialności za jakiekolwiek nieporozumienia lub błędne interpretacje wynikające z użycia tego tłumaczenia.\n" ] } ], "metadata": { "interpreter": { "hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f" }, "kernelspec": { "display_name": "Python 3.8.12 ('py38')", "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.8.12" }, "coopTranslator": { "original_hash": "9fbb7d5fda708537649f71f5f646fcde", "translation_date": "2025-08-31T13:43:09+00:00", "source_file": "lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb", "language_code": "pl" } }, "nbformat": 4, "nbformat_minor": 4 }