From 85740057975523b4521260f4eac1de935c6ffe81 Mon Sep 17 00:00:00 2001 From: Dmitri Soshnikov Date: Tue, 18 Jan 2022 12:34:32 +0300 Subject: [PATCH] Add Generative Networks --- 5-NLP/16-RNN/README.md | 2 +- .../GenerativePyTorch.ipynb | 395 +++++++++++++++ .../17-GenerativeNetworks/GenerativeTF.ipynb | 478 ++++++++++++++++++ 5-NLP/17-GenerativeNetworks/README.md | 44 ++ .../images/rnn-generate-inf.png | Bin 0 -> 6775 bytes .../images/rnn-generate.png | Bin 0 -> 18708 bytes .../unreasonable-effectiveness-of-rnn.jpg | Bin 0 -> 68679 bytes 7 files changed, 918 insertions(+), 1 deletion(-) create mode 100644 5-NLP/17-GenerativeNetworks/GenerativePyTorch.ipynb create mode 100644 5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb create mode 100644 5-NLP/17-GenerativeNetworks/README.md create mode 100644 5-NLP/17-GenerativeNetworks/images/rnn-generate-inf.png create mode 100644 5-NLP/17-GenerativeNetworks/images/rnn-generate.png create mode 100644 5-NLP/17-GenerativeNetworks/images/unreasonable-effectiveness-of-rnn.jpg diff --git a/5-NLP/16-RNN/README.md b/5-NLP/16-RNN/README.md index 0f403d5f..df1db0ed 100644 --- a/5-NLP/16-RNN/README.md +++ b/5-NLP/16-RNN/README.md @@ -18,7 +18,7 @@ Let's see how simple RNN cell is organized. It accepts previous state Si-1< Simple RNN cell has two weight matrices inside: one transforms input symbol (let call it W), and another one transforms input state (H). In this case the output of the network is calculated as σ(W×Xi+H×Si-1+b), where σ is the activation function, b is additional bias. -![RNN Cell Anatomy](images/rnn-anatomy.png) +RNN Cell Anatomy In many cases, input tokens are passed through the embedding layer before entering the RNN to lower the dimensionality. In this case, if the dimension of the input vectors is *emb_size*, and state vector is *hid_size* - the size of W is *emb_size*×*hid_size*, and the size of H is *hid_size*×*hid_size*. diff --git a/5-NLP/17-GenerativeNetworks/GenerativePyTorch.ipynb b/5-NLP/17-GenerativeNetworks/GenerativePyTorch.ipynb new file mode 100644 index 00000000..d75f0266 --- /dev/null +++ b/5-NLP/17-GenerativeNetworks/GenerativePyTorch.ipynb @@ -0,0 +1,395 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Generative networks\n", + "\n", + "Recurrent Neural Networks (RNNs) and their gated cell variants such as Long Short Term Memory Cells (LSTMs) and Gated Recurrent Units (GRUs) provided a mechanism for language modeling, i.e. they can learn word ordering and provide predictions for next word in a sequence. This allows us to use RNNs for **generative tasks**, such as ordinary text generation, machine translation, and even image captioning.\n", + "\n", + "In RNN architecture we discussed in the previous unit, each RNN unit produced next next hidden state as an output. However, we can also add another output to each recurrent unit, which would allow us to output a **sequence** (which is equal in length to the original sequence). Moreover, we can use RNN units that do not accept an input at each step, and just take some initial state vector, and then produce a sequence of outputs.\n", + "\n", + "In this notebook, we will focus on simple generative models that help us generate text. For simplicity, let's build **character-level network**, which generates text letter by letter. During training, we need to take some text corpus, and split it into letter sequences. " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loading dataset...\n", + "Building vocab...\n" + ] + } + ], + "source": [ + "import torch\n", + "import torchtext\n", + "import numpy as np\n", + "from torchnlp import *\n", + "train_dataset,test_dataset,classes,vocab = load_dataset()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Building character vocabulary\n", + "\n", + "To build character-level generative network, we need to split text into individual characters instead of words. This can be done by defining a different tokenizer:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Vocabulary size = 84\n", + "Encoding of 'a' is 4\n", + "Character with code 13 is h\n" + ] + } + ], + "source": [ + "def char_tokenizer(words):\n", + " return list(words) #[word for word in words]\n", + "\n", + "counter = collections.Counter()\n", + "for (label, line) in train_dataset:\n", + " counter.update(char_tokenizer(line))\n", + "vocab = torchtext.vocab.Vocab(counter)\n", + "\n", + "vocab_size = len(vocab)\n", + "print(f\"Vocabulary size = {vocab_size}\")\n", + "print(f\"Encoding of 'a' is {vocab.stoi['a']}\")\n", + "print(f\"Character with code 13 is {vocab.itos[13]}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's see the example of how we can encode the text from our dataset:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([43, 4, 11, 11, 2, 26, 5, 23, 2, 38, 3, 4, 10, 9, 2, 31, 11, 4,\n", + " 21, 2, 38, 4, 14, 25, 2, 34, 8, 5, 6, 2, 5, 13, 3, 2, 38, 11,\n", + " 4, 14, 25, 2, 55, 37, 3, 15, 5, 3, 10, 9, 56, 2, 37, 3, 15, 5,\n", + " 3, 10, 9, 2, 29, 2, 26, 13, 6, 10, 5, 29, 9, 3, 11, 11, 3, 10,\n", + " 9, 27, 2, 43, 4, 11, 11, 2, 26, 5, 10, 3, 3, 5, 58, 9, 2, 12,\n", + " 21, 7, 8, 12, 11, 7, 8, 18, 61, 22, 4, 8, 12, 2, 6, 19, 2, 15,\n", + " 11, 5, 10, 4, 29, 14, 20, 8, 7, 14, 9, 27, 2, 4, 10, 3, 2, 9,\n", + " 3, 3, 7, 8, 18, 2, 18, 10, 3, 3, 8, 2, 4, 18, 4, 7, 8, 23])" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def enc(x):\n", + " return torch.LongTensor(encode(x,voc=vocab,tokenizer=char_tokenizer))\n", + "\n", + "enc(train_dataset[0][1])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Training a generative RNN\n", + "\n", + "The way we will train RNN to generate text is the following. On each step, we will take a sequence of characters of length `nchars`, and ask the network to generate next output character for each input character:\n", + "\n", + "![Image showing an example RNN generation of the word 'HELLO'.](images/rnn-generate.png)\n", + "\n", + "Depending on the actual scenario, we may also want to include some special characters, such as *end-of-sequence* ``. In our case, we just want to train the network for endless text generation, thus we will fix the size of each sequence to be equal to `nchars` tokens. Consequently, each training example will consist of `nchars` inputs and `nchars` outputs (which are input sequence shifted one symbol to the left). Minibatch will consist of several such sequences.\n", + "\n", + "The way we will generate minibatches is to take each news text of length `l`, and generate all possible input-output combinations from it (there will be `l-nchars` such combinations). They will form one minibatch, and size of minibatches would be different at each training step. " + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(tensor([[43, 4, 11, ..., 18, 61, 22],\n", + " [ 4, 11, 11, ..., 61, 22, 4],\n", + " [11, 11, 2, ..., 22, 4, 8],\n", + " ...,\n", + " [37, 3, 15, ..., 4, 18, 4],\n", + " [ 3, 15, 5, ..., 18, 4, 7],\n", + " [15, 5, 3, ..., 4, 7, 8]], device='cuda:0'),\n", + " tensor([[ 4, 11, 11, ..., 61, 22, 4],\n", + " [11, 11, 2, ..., 22, 4, 8],\n", + " [11, 2, 26, ..., 4, 8, 12],\n", + " ...,\n", + " [ 3, 15, 5, ..., 18, 4, 7],\n", + " [15, 5, 3, ..., 4, 7, 8],\n", + " [ 5, 3, 10, ..., 7, 8, 23]], device='cuda:0'))" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nchars = 100\n", + "\n", + "def get_batch(s,nchars=nchars):\n", + " ins = torch.zeros(len(s)-nchars,nchars,dtype=torch.long,device=device)\n", + " outs = torch.zeros(len(s)-nchars,nchars,dtype=torch.long,device=device)\n", + " for i in range(len(s)-nchars):\n", + " ins[i] = enc(s[i:i+nchars])\n", + " outs[i] = enc(s[i+1:i+nchars+1])\n", + " return ins,outs\n", + "\n", + "get_batch(train_dataset[0][1])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's define generator network. It can be based on any recurrent cell which we discussed in the previous unit (simple, LSTM or GRU). In our example we will use LSTM.\n", + "\n", + "Because the network takes characters as input, and vocabulary size is pretty small, we do not need embedding layer, one-hot-encoded input can directly go to LSTM cell. However, because we pass character numbers as input, we need to one-hot-encode them before passing to LSTM. This is done by calling `one_hot` function during `forward` pass. Output encoder would be a linear layer that will convert hidden state into one-hot-encoded output." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "class LSTMGenerator(torch.nn.Module):\n", + " def __init__(self, vocab_size, hidden_dim):\n", + " super().__init__()\n", + " self.rnn = torch.nn.LSTM(vocab_size,hidden_dim,batch_first=True)\n", + " self.fc = torch.nn.Linear(hidden_dim, vocab_size)\n", + "\n", + " def forward(self, x, s=None):\n", + " x = torch.nn.functional.one_hot(x,vocab_size).to(torch.float32)\n", + " x,s = self.rnn(x,s)\n", + " return self.fc(x),s" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "During training, we want to be able to sample generated text. To do that, we will define `generate` function that will produce output string of length `size`, starting from the initial string `start`.\n", + "\n", + "The way it works is the following. First, we will pass the whole start string through the network, and take output state `s` and next predicted character `out`. Since `out` is one-hot encoded, we take `argmax` to get the index of the character `nc` in the vocabulary, and use `itos` to figure out the actual character and append it to the resulting list of characters `chars`. This process of generating one character is repeated `size` times to generate required number of characters. " + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "def generate(net,size=100,start='today '):\n", + " chars = list(start)\n", + " out, s = net(enc(chars).view(1,-1).to(device))\n", + " for i in range(size):\n", + " nc = torch.argmax(out[0][-1])\n", + " chars.append(vocab.itos[nc])\n", + " out, s = net(nc.view(1,-1),s)\n", + " return ''.join(chars)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's do the training! Training loop is almost the same as in all our previous examples, but instead of accuracy we print sampled generated text every 1000 epochs.\n", + "\n", + "Special attention needs to be paid to the way we compute loss. We need to compute loss given one-hot-encoded output `out`, and expected text `text_out`, which is the list of character indices. Luckily, the `cross_entropy` function expects unnormalized network output as first argument, and class number as the second, which is exactly what we have. It also performs automatic averaging over minibatch size.\n", + "\n", + "We also limit the training by `samples_to_train` samples, in order not to wait for too long. We encourage you to experiment and try longer training, possibly for several epochs (in which case you would need to create another loop around this code)." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Current loss = 4.442246913909912\n", + "today ggrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrgrg\n", + "Current loss = 2.1178359985351562\n", + "today and a could a the to the to the to the to the to the to the to the to the to the to the to the to th\n", + "Current loss = 1.6465336084365845\n", + "today on Tuesday the company to the United States and a policing to the United States and a policing to th\n", + "Current loss = 2.3716814517974854\n", + "today to the United States and a new men to the United States and a new men to the United States and a new\n", + "Current loss = 1.6844098567962646\n", + "today of the first the first the first the first the first the first the first the first the first the fir\n", + "Current loss = 1.702707052230835\n", + "today of the United States a said the United States a said the United States a said the United States a sa\n", + "Current loss = 1.9633255004882812\n", + "today of the first the first the first the first the first the first the first the first the first the fir\n", + "Current loss = 1.8642014265060425\n", + "today of the second a second a second a second a second a second a second a second a second a second a sec\n", + "Current loss = 1.7720613479614258\n", + "today and and and the company of the company of the company of the company of the company of the company o\n", + "Current loss = 1.52818763256073\n", + "today and the company of the company of the company of the company of the company of the company of the co\n", + "Current loss = 1.5444810390472412\n", + "today and the counters to the first the counters to the first the counters to the first the counters to th\n" + ] + } + ], + "source": [ + "net = LSTMGenerator(vocab_size,64).to(device)\n", + "\n", + "samples_to_train = 10000\n", + "optimizer = torch.optim.Adam(net.parameters(),0.01)\n", + "loss_fn = torch.nn.CrossEntropyLoss()\n", + "net.train()\n", + "for i,x in enumerate(train_dataset):\n", + " # x[0] is class label, x[1] is text\n", + " if len(x[1])-nchars<10:\n", + " continue\n", + " samples_to_train-=1\n", + " if not samples_to_train: break\n", + " text_in, text_out = get_batch(x[1])\n", + " optimizer.zero_grad()\n", + " out,s = net(text_in)\n", + " loss = torch.nn.functional.cross_entropy(out.view(-1,vocab_size),text_out.flatten()) #cross_entropy(out,labels)\n", + " loss.backward()\n", + " optimizer.step()\n", + " if i%1000==0:\n", + " print(f\"Current loss = {loss.item()}\")\n", + " print(generate(net))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This example already generates some pretty good text, but it can be further improved in several ways:\n", + "* **Better minibatch generation**. The way we prepared data for training was to generate one minibatch from one sample. This is not ideal, because minibatches are all of different sizes, and some of them even cannot be generated, because the text is smaller than `nchars`. Also, small minibatches do not load GPU sufficiently enough. It would be wiser to get one large chunk of text from all samples, then generate all input-output pairs, shuffle them, and generate minibatches of equal size.\n", + "* **Multilayer LSTM**. It makes sense to try 2 or 3 layers of LSTM cells. As we mentioned in the previous unit, each layer of LSTM extracts certain patterns from text, and in case of character-level generator we can expect lower LSTM level to be responsible for extracting syllables, and higher levels - for words and word combinations. This can be simply implemented by passing number-of-layers parameter to LSTM constructor.\n", + "* You may also want to experiment with **GRU units** and see which ones perform better, and with **different hidden layer sizes**. Too large hidden layer may result in overfitting (e.g. network will learn exact text), and smaller size might not produce good result." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Soft text generation and temperature\n", + "\n", + "In the previous definition of `generate`, we were always taking the character with highest probability as the next character in generated text. This resulted in the fact that the text often \"cycled\" between the same character sequences again and again, like in this example:\n", + "```\n", + "today of the second the company and a second the company ...\n", + "```\n", + "\n", + "However, if we look at the probability distribution for the next character, it could be that the difference between a few highest probabilities is not huge, e.g. one character can have probability 0.2, another - 0.19, etc. For example, when looking for the next character in the sequence '*play*', next character can equally well be either space, or **e** (as in the word *player*).\n", + "\n", + "This leads us to the conclusion that it is not always \"fair\" to select the character with higher probability, because choosing the second highest might still lead us to meaningful text. It is more wise to **sample** characters from the probability distribution given by the network output.\n", + "\n", + "This sampling can be done using `multinomial` function that implements so-called **multinomial distribution**. A function that implements this **soft** text generation is defined below:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Temperature = 0.3\n", + "Today and to has a software to in the first the power the gold medal was of the first and succer to the company will a report the first the and the gain the company in the and a new a report a pack of the four the first the company of the such with the half to a security to the and a success the first she\n", + "\n", + "--- Temperature = 0.8\n", + "Today drud out of the three-rent possiem that sales purssion has finminiaty women's from NAC Inc. (AP) -- Shimbon has weel with a may stelight first three flaw gold from their a scent, big study with a nighting sovicturner has slarh football at a hour of Angelage discression, into cubs, US year player sor\n", + "\n", + "--- Temperature = 1.0\n", + "Today by compoy, said to hup the couns ay rrope iist\\fill sinie-5-1- than he of a fightier Corp. the Vew, Mkli Unite Hold Austria on Tuesday resfare rextarted in the new has buy thisnillials thrust first capuration of the it larget expected the ir edulagy Airin Penny after Emonet Cuc Washieve an are Gurry\n", + "\n", + "--- Temperature = 1.3\n", + "Today cluscy,, wangled and-ox they, stee of as;\\seculity dillancrile inmution svanse gall ATHEYS today a first oresift 6-Jalf mangback explymate that wrook\" haffic illowbre overwage in Tecrian Hunrieleers to attowny service Adching, blanks governine? Aug. : : NE: Sir NFP (P2AAU) Bow SWDE: The ex2\"cut Pmoc\n", + "\n", + "--- Temperature = 1.8\n", + "Today sas gom, twing hWe a Dajfcou hamb--5 to bemolecresem ig irkembets plentll repws, scatchey: Actuss.io Theffouge, cirw biggemed Goiga propperinut you racive #5-Aeia:riato..Lf. N7TNap:,ser,wploy a Fraull tbashonerdlantuanseve /bBT -$06 Wemob-e.P EvVlaicy), ZOf0 cUSeballd sturk out houselty, TARZM) AbAe\n", + "\n" + ] + } + ], + "source": [ + "def generate_soft(net,size=100,start='today ',temperature=1.0):\n", + " chars = list(start)\n", + " out, s = net(enc(chars).view(1,-1).to(device))\n", + " for i in range(size):\n", + " #nc = torch.argmax(out[0][-1])\n", + " out_dist = out[0][-1].div(temperature).exp()\n", + " nc = torch.multinomial(out_dist,1)[0]\n", + " chars.append(vocab.itos[nc])\n", + " out, s = net(nc.view(1,-1),s)\n", + " return ''.join(chars)\n", + " \n", + "for i in [0.3,0.8,1.0,1.3,1.8]:\n", + " print(f\"--- Temperature = {i}\\n{generate_soft(net,size=300,start='Today ',temperature=i)}\\n\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We have introduced one more parameter called **temperature**, which is used to indicate how hard we should stick to the highest probability. If temperature is 1.0, we do fair multinomial sampling, and when temperature goes to infinity - all probabilities become equal, and we randomly select next character. In the example below we can observe that the text becomes meaningless when we increase the temperature too much, and it resembles \"cycled\" hard-generated text when it becomes closer to 0. " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "py37_pytorch", + "language": "python", + "name": "conda-env-py37_pytorch-py" + }, + "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.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb b/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb new file mode 100644 index 00000000..be81f195 --- /dev/null +++ b/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb @@ -0,0 +1,478 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Generative networks\n", + "\n", + "Recurrent Neural Networks (RNNs) and their gated cell variants such as Long Short Term Memory Cells (LSTMs) and Gated Recurrent Units (GRUs) provided a mechanism for language modeling, i.e. they can learn word ordering and provide predictions for next word in a sequence. This allows us to use RNNs for **generative tasks**, such as ordinary text generation, machine translation, and even image captioning.\n", + "\n", + "In RNN architecture we discussed in the previous unit, each RNN unit produced next next hidden state as an output. However, we can also add another output to each recurrent unit, which would allow us to output a **sequence** (which is equal in length to the original sequence). Moreover, we can use RNN units that do not accept an input at each step, and just take some initial state vector, and then produce a sequence of outputs.\n", + "\n", + "In this notebook, we will focus on simple generative models that help us generate text. For simplicity, let's build **character-level network**, which generates text letter by letter. During training, we need to take some text corpus, and split it into letter sequences. " + ] + }, + { + "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": [ + "## Building character vocabulary\n", + "\n", + "To build character-level generative network, we need to split text into individual characters instead of words. `TextVectorization` layer that we have been using before cannot do that, so we have to options:\n", + "\n", + "* Manually load text and do tokenization 'by hand', as in [this official Keras example](https://keras.io/examples/generative/lstm_character_level_text_generation/)\n", + "* Use `Tokenizer` class for character-level tokenization.\n", + "\n", + "We will go with the second option. `Tokenizer` can also be used to tokenize into words, so one should be able to switch from char-level to word-level tokenization quite easily.\n", + "\n", + "To do character-level tokenization, we need to pass `char_level=True` parameter:" + ] + }, + { + "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": [ + "We also want to use one special token to denote **end of sequence**, which we will call ``. Let's add it manually to the vocabulary:" + ] + }, + { + "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": [ + "Now, to encode text into sequences of numbers, we can use:" + ] + }, + { + "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": [ + "## Training a generative RNN to generate titles\n", + "\n", + "The way we will train RNN to generate news titles is the following. On each step, we will take one title, which will be fed into an RNN, and for each input character we will ask the network to generate next output character:\n", + "\n", + "![Image showing an example RNN generation of the word 'HELLO'.](./images/rnn-generate.png)\n", + "\n", + "For the last character of our sequence, we will ask the network to generate `` token.\n", + "\n", + "The main difference between generative RNN that we are using here is that we will take an output from each step of the RNN, and not just from the final cell. This can be achieved by specifying `return_sequences` parameter to the RNN cell.\n", + "\n", + "Thus, during the training, an input to the network would be a sequence of encoded characters of some length, and an output would be a sequence of the same length, but shifted by one element and terminated by ``. Minibatch will consist of several such sequences, and we would need to use **padding** to align all sequences.\n", + "\n", + "Let's create functions that will transform the dataset for us. Because we want to pad sequences on minibatch level, we will first batch the dataset by calling `.batch()`, and then `map` it in order to do transformation. So, the transformation function will take a whole minibatch as a parameter:" + ] + }, + { + "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": [ + "A few important things that we do here:\n", + "* We first extract the actual text from the string tensor\n", + "* `text_to_sequences` converts the list of strings into a list of integer tensors\n", + "* `pad_sequences` pads those tensors to their maximum length\n", + "* We finally one-hot encode all the characters, and also do the shifting and `` appending. We will soon see why we need one-hot-encoded characters\n", + "\n", + "However, this function is **Pythonic**, i.e. it cannot be automatically translated into Tensorflow computational graph. We will get errors if we try to use this function directly in the `Dataset.map` function. We need to enclose this Pythonic call by using `py_function` wrapper: " + ] + }, + { + "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": [ + "> **Note**: Differentiating between Pythonic and Tensorflow transformation functions may seem a little too complex, and you may be questioning why we do not transform the dataset using standard Python functions before passing it to `fit`. While this definitely can be done, using `Dataset.map` has a huge advantage, because data transformation pipeline is executed using Tensorflow computational graph, which takes advantage of GPU computations, and minimized the need to pass data between CPU/GPU.\n", + "\n", + "Now we can build our generator network and start training. It can be based on any recurrent cell which we discussed in the previous unit (simple, LSTM or GRU). In our example we will use LSTM.\n", + "\n", + "Because the network takes characters as input, and vocabulary size is pretty small, we do not need embedding layer, one-hot-encoded input can directly go into LSTM cell. Output layer would be a `Dense` classifier that will convert LSTM output into one-hot-encoded token numbers.\n", + "\n", + "In addition, since we are dealing with variable-length sequences, we can use `Masking` layer to create a mask that will ignore padded part of the string. This is not strictly needed, because we are not very much interested in everything that goes beyond `` token, but we will use it for the sake of getting some experience with this layer type. `input_shape` would be `(None, vocab_size)`, where `None` indicates the sequence of variable length, and output shape is `(None,vocab_size)` as well, as you can see from the `summary`:" + ] + }, + { + "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": [ + "## Generating output\n", + "\n", + "Now that we have trained the model, we want to use it to generate some output. First of all, we need a way to decode text represented by a sequence of token numbers. To do this, we could use `tokenizer.sequences_to_texts` function; however, it does not work well with character-level tokenization. Therefore we will take a dictionary of tokens from the tokenizer (called `word_index`), build a reverse map, and write our own decoding function:" + ] + }, + { + "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": [ + "Now, let's do generation. We will start with some string `start`, encode it into a sequence `inp`, and then on each step we will call our network to infer the next character. \n", + "\n", + "Output of the network `out` is a vector of `vocab_size` elements representing probablities of each token, and we can find the most probably token number by using `argmax`. We then append this character to the generated list of tokens, and proceed with generation. This process of generating one character is repeated `size` times to generate required number of characters, and we terminate early when `eos_token` is encountered." + ] + }, + { + "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": [ + "## Sampling output during training \n", + "\n", + "Because we do not have any useful metrics such as *accuracy*, the only way we can see that our model is getting better is by **sampling** generated string during training. To do it, we will use **callbacks**, i.e. functions that we can pass to the `fit` function, and that will be called periodically during training. " + ] + }, + { + "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": [ + "This example already generates some pretty good text, but it can be further improved in several ways:\n", + "* **More text**. We have only used titles for our task, but you may want to experiment with full text. Remember that RNNs are not too great with handling long sequences, so it makes sense either to split them into shorted sentences, or to always train on a fixed sequence length of some predefined value `num_chars` (say, 256). You may try to change the example above into such architecture, using [official Keras tutorial](https://keras.io/examples/generative/lstm_character_level_text_generation/) as an inspiration.\n", + "* **Multilayer LSTM**. It makes sense to try 2 or 3 layers of LSTM cells. As we mentioned in the previous unit, each layer of LSTM extracts certain patterns from text, and in case of character-level generator we can expect lower LSTM level to be responsible for extracting syllables, and higher levels - for words and word combinations. This can be simply implemented by passing number-of-layers parameter to LSTM constructor.\n", + "* You may also want to experiment with **GRU units** and see which ones perform better, and with **different hidden layer sizes**. Too large hidden layer may result in overfitting (e.g. network will learn exact text), and smaller size might not produce good result." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Soft text generation and temperature\n", + "\n", + "In the previous definition of `generate`, we were always taking the character with highest probability as the next character in generated text. This resulted in the fact that the text often \"cycled\" between the same character sequences again and again, like in this example:\n", + "```\n", + "today of the second the company and a second the company ...\n", + "```\n", + "\n", + "However, if we look at the probability distribution for the next character, it could be that the difference between a few highest probabilities is not huge, e.g. one character can have probability 0.2, another - 0.19, etc. For example, when looking for the next character in the sequence '*play*', next character can equally well be either space, or **e** (as in the word *player*).\n", + "\n", + "This leads us to the conclusion that it is not always \"fair\" to select the character with higher probability, because choosing the second highest might still lead us to meaningful text. It is more wise to **sample** characters from the probability distribution given by the network output.\n", + "\n", + "This sampling can be done using `np.multinomial` function that implements so-called **multinomial distribution**. A function that implements this **soft** text generation is defined below:" + ] + }, + { + "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": [ + "We have introduced one more parameter called **temperature**, which is used to indicate how hard we should stick to the highest probability. If temperature is 1.0, we do fair multinomial sampling, and when temperature goes to infinity - all probabilities become equal, and we randomly select next character. In the example below we can observe that the text becomes meaningless when we increase the temperature too much, and it resembles \"cycled\" hard-generated text when it becomes closer to 0. " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "py38_tensorflow", + "language": "python", + "name": "conda-env-py38_tensorflow-py" + }, + "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.10" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/5-NLP/17-GenerativeNetworks/README.md b/5-NLP/17-GenerativeNetworks/README.md new file mode 100644 index 00000000..5011a485 --- /dev/null +++ b/5-NLP/17-GenerativeNetworks/README.md @@ -0,0 +1,44 @@ +# Generative networks + +Recurrent Neural Networks (RNNs) and their gated cell variants such as Long Short Term Memory Cells (LSTMs) and Gated Recurrent Units (GRUs) provided a mechanism for language modeling, i.e. they can learn word ordering and provide predictions for next word in a sequence. This allows us to use RNNs for **generative tasks**, such as ordinary text generation, machine translation, and even image captioning. + +In RNN architecture we discussed in the previous unit, each RNN unit produced next next hidden state as an output. However, we can also add another output to each recurrent unit, which would allow us to output a **sequence** (which is equal in length to the original sequence). Moreover, we can use RNN units that do not accept an input at each step, and just take some initial state vector, and then produce a sequence of outputs. + +This allows for different neural architectures that are shown in the picture below: + +![Image showing common recurrent neural network patterns.](images/unreasonable-effectiveness-of-rnn.jpg) +*Image from blog post [Unreasonable Effectiveness of Recurrent Neural Networks](http://karpathy.github.io/2015/05/21/rnn-effectiveness/) by [Andrej Karpaty](http://karpathy.github.io/)* + +* **One-to-one** is a traditional neural network with one input and one output +* **One-to-many** is a generative architecture that accepts one input value, and generates a sequence of output values. For example, if we want to train **image captioning** network that would produce a textual description of a picture, we can a picture as input, pass it through CNN to obtain hidden state, and then have recurrent chain generate caption word-by-word +* **Many-to-one** corresponds to RNN architectures we described in the previous unit, such as text classification +* **Many-to-many**, or **sequence-to-sequence** corresponds to tasks such as **machine translation**, where we have first RNN collect all information from the input sequence into the hidden state, and another RNN chain unrolls this state into the output sequence. + +In this unit, we will focus on simple generative models that help us generate text. For simplicity, we will use character-level tokenization. + +The way we will train RNN to generate text is the following. On each step, we will take a sequence of characters of length `nchars`, and ask the network to generate next output character for each input character: + +![Image showing an example RNN generation of the word 'HELLO'.](images/rnn-generate.png) + +When generating text (during inference), we start with some **prompt**, which is passed through RNN cells to generate intermediate state, and then from this state the generation starts. We generate one character at a time, and pass the state and the generated character to another RNN cell to generate the next one, until we generate enough characters. + + + +## Continue to Notebooks + +* [Generative Networks with PyTorch](GenerativePyTorch.ipynb) +* [Generative Networks with Tensorflow](GenerativeTF.ipynb) + +## Soft text generation and temperature + +Output of each RNN cell is a probability distribution of characters. If we always take the character with highest probability as the next character in generated text, the text often can become "cycled" between the same character sequences again and again, like in this example: + +``` +today of the second the company and a second the company ... +``` + +However, if we look at the probability distribution for the next character, it could be that the difference between a few highest probabilities is not huge, e.g. one character can have probability 0.2, another - 0.19, etc. For example, when looking for the next character in the sequence '*play*', next character can equally well be either space, or **e** (as in the word *player*). + +This leads us to the conclusion that it is not always "fair" to select the character with higher probability, because choosing the second highest might still lead us to meaningful text. It is more wise to **sample** characters from the probability distribution given by the network output. We can also use a parameter, **temperature**, that will flatten out the probability distribution, in case we want to add more randomness, or make it more steep, if we want to stick more to the highest-probability characters. + +Have a look at how this soft text generation is implemented in the notebooks. diff --git a/5-NLP/17-GenerativeNetworks/images/rnn-generate-inf.png b/5-NLP/17-GenerativeNetworks/images/rnn-generate-inf.png new file mode 100644 index 0000000000000000000000000000000000000000..b491add6205b9ca214fc4e82357bb291519165fa GIT binary patch literal 6775 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 z%0Dp=OdW@^o?LyVrXRT>6SlfiCbZ+}S#ik=DTh!79CU77YO##sSw5{1B2&Lr)zLbplEw^UQ{rgDm7J)$zvD78!{qKk_yisdhDZW~w}=LW zC+R;grNva#J$%2Q1IRZ~AHd(dbp-nNgJ>?zSNyNC2A0zM0+|oxyTOd75!5`%601XM z#I8aOWdkY-Rq;4G?^CKMczYGyuL?%Z5{~q9Q^;YS0=QsbY443Ku3Bmx&#|_PqfVSu zy?z(3QGSQBYbu{5t{Dw~Ot`!pB6_2)qpfWwv?@N*LhX=mgE4333<;e(V;R_@qP^#q zac<1)B$62VPXF>K0@`iDh}rPleY`yVsgq%x_xgJzEK;Q^E9D;aIWSWU*KTsRBD1{;FBwMU?t~T0n?SWY&dnn<%_Y`E$X^jx76~!y9Qs&}) zAGmk5Cub?+kUO^?-x*!x`fE#>$s70zOrebKn+s85PNd*D^4}q=tXLQ8j*#JCNa7iP z{}D$0a3u;v8+~|s=hH8y?v!CNrg$c=#mkxz#%`qkJtBnQIi%L*bAczsTJ~tK)U@_0bPUocDs_c)t9$xx6 z^Sg_x|P0c&ojyKu(AtSVK zXno#v#}{Ny6Ej|D5�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ã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 literal 0 HcmV?d00001