AI-For-Beginners/translations/pl/lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb

494 lines
28 KiB
Plaintext

{
"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 `<eos>`. 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>'] = 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 `<eos>`.\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 `<eos>`. 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 `<eos>`. 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 `<eos>`, 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": [
"<tensorflow.python.keras.callbacks.History at 0x7fa40c1245e0>"
]
},
"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": [
"<tensorflow.python.keras.callbacks.History at 0x7fa40c74e3d0>"
]
},
"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 &lt;b&gt;...&lt;/b&gt;\n",
"Little red riding hood ficed to the spam countered in European &lt;b&gt;...&lt;/b&gt;\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 &lt;b&gt;...&lt;/b&gt;\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<ipython-input-33-db32367a0feb>\u001b[0m in \u001b[0;36m<module>\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<ipython-input-33-db32367a0feb>\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<ipython-input-10-3f5fa6130b1d>\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<ipython-input-10-3f5fa6130b1d>\u001b[0m in \u001b[0;36m<listcomp>\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
}