├── imgs ├── decoder.png ├── seq2seq.png ├── decoder@2x.png ├── seq2seq@2x.png ├── word-encoding.png ├── decoder-network.png ├── encoder-network.png ├── word-encoding@2x.png ├── attention-decoder-network.png ├── encoder-network.dot ├── decoder-network.dot └── attention-decoder-network.dot ├── README.md ├── seq2seq_translation_tutorial.ipynb └── seq2seq_translation_tutorial-usf.ipynb /imgs/decoder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ananthsankar/seq2seq_attention/HEAD/imgs/decoder.png -------------------------------------------------------------------------------- /imgs/seq2seq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ananthsankar/seq2seq_attention/HEAD/imgs/seq2seq.png -------------------------------------------------------------------------------- /imgs/decoder@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ananthsankar/seq2seq_attention/HEAD/imgs/decoder@2x.png -------------------------------------------------------------------------------- /imgs/seq2seq@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ananthsankar/seq2seq_attention/HEAD/imgs/seq2seq@2x.png -------------------------------------------------------------------------------- /imgs/word-encoding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ananthsankar/seq2seq_attention/HEAD/imgs/word-encoding.png -------------------------------------------------------------------------------- /imgs/decoder-network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ananthsankar/seq2seq_attention/HEAD/imgs/decoder-network.png -------------------------------------------------------------------------------- /imgs/encoder-network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ananthsankar/seq2seq_attention/HEAD/imgs/encoder-network.png -------------------------------------------------------------------------------- /imgs/word-encoding@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ananthsankar/seq2seq_attention/HEAD/imgs/word-encoding@2x.png -------------------------------------------------------------------------------- /imgs/attention-decoder-network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ananthsankar/seq2seq_attention/HEAD/imgs/attention-decoder-network.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # seq2seq_attention 2 | Sequence to sequence model with attention for translation 3 | This is a Jupyter notebook implementing sequence to sequence with attention model in Pytorch 4 | for machine translation. The original notebook was copied from 5 | https://pytorch.org/tutorials/intermediate/seq2seq_translation_tutorial.html#loading-data-files 6 | -------------------------------------------------------------------------------- /imgs/encoder-network.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | 3 | // Main styles 4 | nodesep=0.3; ranksep=0.15; 5 | 6 | node [shape=rect, fillcolor=darkorange, color=white, style=filled, fontsize=11, fontname="arial", height=0.2]; 7 | edge [color=gray, arrowsize=0.5]; 8 | 9 | // Layout 10 | {rank=same;input;prev_hidden} 11 | 12 | input -> embedding; 13 | embedding -> embedded; 14 | embedded -> gru; 15 | prev_hidden -> gru; 16 | gru -> output; 17 | gru -> hidden; 18 | 19 | embedding [fillcolor=dodgerblue, fontcolor=white]; 20 | gru [fillcolor=dodgerblue, fontcolor=white]; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /imgs/decoder-network.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | 3 | // Main styles 4 | nodesep=0.3; ranksep=0.15; 5 | 6 | node [shape=rect, fillcolor=darkorange, color=white, style=filled, fontsize=11, fontname="arial", height=0.2]; 7 | edge [color=gray, arrowsize=0.5]; 8 | 9 | // Layout 10 | {rank=same;input;prev_hidden} 11 | 12 | input -> embedding; 13 | embedding -> relu; 14 | relu -> gru; 15 | 16 | prev_hidden -> gru; 17 | gru -> out; 18 | gru -> hidden; 19 | 20 | out -> softmax; 21 | softmax -> output; 22 | 23 | {rank=same;output;hidden} 24 | 25 | // Layer nodes 26 | embedding [fillcolor=dodgerblue, fontcolor=white]; 27 | gru [fillcolor=dodgerblue, fontcolor=white]; 28 | out [fillcolor=dodgerblue, fontcolor=white]; 29 | 30 | // Function nodes 31 | relu [fillcolor=palegreen]; 32 | softmax [fillcolor=palegreen]; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /imgs/attention-decoder-network.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | 3 | // Main styles 4 | nodesep=0.3; ranksep=0.15; 5 | 6 | node [shape=rect, fillcolor=darkorange, color=white, style=filled, fontsize=11, fontname="arial", height=0.2]; 7 | edge [color=gray, arrowsize=0.5]; 8 | 9 | // Layout 10 | {rank=same;input;prev_hidden;encoder_outputs} 11 | 12 | 13 | input -> embedding; 14 | embedding -> dropout; 15 | dropout -> embedded; 16 | 17 | embedded -> attn; 18 | prev_hidden -> attn; 19 | attn -> attn_softmax; 20 | attn_softmax -> attn_weights; 21 | attn_weights -> bmm; 22 | encoder_outputs -> bmm; 23 | bmm -> attn_applied; 24 | attn_applied -> attn_combine; 25 | embedded -> attn_combine; 26 | 27 | attn_combine -> relu -> gru; 28 | prev_hidden -> gru; 29 | gru -> out; 30 | gru -> hidden; 31 | 32 | out -> softmax; 33 | softmax -> output; 34 | 35 | {rank=same;output;hidden} 36 | 37 | // Layer nodes 38 | embedding [fillcolor=dodgerblue, fontcolor=white]; 39 | attn [fillcolor=dodgerblue, fontcolor=white]; 40 | attn_combine [fillcolor=dodgerblue, fontcolor=white]; 41 | bmm [fillcolor=dodgerblue, fontcolor=white]; 42 | gru [fillcolor=dodgerblue, fontcolor=white]; 43 | out [fillcolor=dodgerblue, fontcolor=white]; 44 | 45 | // Function nodes 46 | dropout [fillcolor=palegreen]; 47 | relu [fillcolor=palegreen]; 48 | softmax [fillcolor=palegreen]; 49 | attn_softmax [fillcolor=palegreen]; 50 | 51 | } 52 | -------------------------------------------------------------------------------- /seq2seq_translation_tutorial.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "collapsed": false 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "%matplotlib inline" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "\nTranslation with a Sequence to Sequence Network and Attention\n*************************************************************\n**Author**: `Sean Robertson `_\n\nIn this project we will be teaching a neural network to translate from\nFrench to English.\n\n::\n\n [KEY: > input, = target, < output]\n\n > il est en train de peindre un tableau .\n = he is painting a picture .\n < he is painting a picture .\n\n > pourquoi ne pas essayer ce vin delicieux ?\n = why not try that delicious wine ?\n < why not try that delicious wine ?\n\n > elle n est pas poete mais romanciere .\n = she is not a poet but a novelist .\n < she not not a poet but a novelist .\n\n > vous etes trop maigre .\n = you re too skinny .\n < you re all alone .\n\n... to varying degrees of success.\n\nThis is made possible by the simple but powerful idea of the `sequence\nto sequence network `__, in which two\nrecurrent neural networks work together to transform one sequence to\nanother. An encoder network condenses an input sequence into a vector,\nand a decoder network unfolds that vector into a new sequence.\n\n.. figure:: /_static/img/seq-seq-images/seq2seq.png\n :alt:\n\nTo improve upon this model we'll use an `attention\nmechanism `__, which lets the decoder\nlearn to focus over a specific range of the input sequence.\n\n**Recommended Reading:**\n\nI assume you have at least installed PyTorch, know Python, and\nunderstand Tensors:\n\n- https://pytorch.org/ For installation instructions\n- :doc:`/beginner/deep_learning_60min_blitz` to get started with PyTorch in general\n- :doc:`/beginner/pytorch_with_examples` for a wide and deep overview\n- :doc:`/beginner/former_torchies_tutorial` if you are former Lua Torch user\n\n\nIt would also be useful to know about Sequence to Sequence networks and\nhow they work:\n\n- `Learning Phrase Representations using RNN Encoder-Decoder for\n Statistical Machine Translation `__\n- `Sequence to Sequence Learning with Neural\n Networks `__\n- `Neural Machine Translation by Jointly Learning to Align and\n Translate `__\n- `A Neural Conversational Model `__\n\nYou will also find the previous tutorials on\n:doc:`/intermediate/char_rnn_classification_tutorial`\nand :doc:`/intermediate/char_rnn_generation_tutorial`\nhelpful as those concepts are very similar to the Encoder and Decoder\nmodels, respectively.\n\nAnd for more, read the papers that introduced these topics:\n\n- `Learning Phrase Representations using RNN Encoder-Decoder for\n Statistical Machine Translation `__\n- `Sequence to Sequence Learning with Neural\n Networks `__\n- `Neural Machine Translation by Jointly Learning to Align and\n Translate `__\n- `A Neural Conversational Model `__\n\n\n**Requirements**\n\n" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": null, 24 | "metadata": { 25 | "collapsed": false 26 | }, 27 | "outputs": [], 28 | "source": [ 29 | "from __future__ import unicode_literals, print_function, division\nfrom io import open\nimport unicodedata\nimport string\nimport re\nimport random\n\nimport torch\nimport torch.nn as nn\nfrom torch import optim\nimport torch.nn.functional as F\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")" 30 | ] 31 | }, 32 | { 33 | "cell_type": "markdown", 34 | "metadata": {}, 35 | "source": [ 36 | "Loading data files\n==================\n\nThe data for this project is a set of many thousands of English to\nFrench translation pairs.\n\n`This question on Open Data Stack\nExchange `__\npointed me to the open translation site https://tatoeba.org/ which has\ndownloads available at https://tatoeba.org/eng/downloads - and better\nyet, someone did the extra work of splitting language pairs into\nindividual text files here: https://www.manythings.org/anki/\n\nThe English to French pairs are too big to include in the repo, so\ndownload to ``data/eng-fra.txt`` before continuing. The file is a tab\nseparated list of translation pairs:\n\n::\n\n I am cold. J'ai froid.\n\n.. Note::\n Download the data from\n `here `_\n and extract it to the current directory.\n\n" 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "metadata": {}, 42 | "source": [ 43 | "Similar to the character encoding used in the character-level RNN\ntutorials, we will be representing each word in a language as a one-hot\nvector, or giant vector of zeros except for a single one (at the index\nof the word). Compared to the dozens of characters that might exist in a\nlanguage, there are many many more words, so the encoding vector is much\nlarger. We will however cheat a bit and trim the data to only use a few\nthousand words per language.\n\n.. figure:: /_static/img/seq-seq-images/word-encoding.png\n :alt:\n\n\n\n" 44 | ] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "metadata": {}, 49 | "source": [ 50 | "We'll need a unique index per word to use as the inputs and targets of\nthe networks later. To keep track of all this we will use a helper class\ncalled ``Lang`` which has word \u2192 index (``word2index``) and index \u2192 word\n(``index2word``) dictionaries, as well as a count of each word\n``word2count`` to use to later replace rare words.\n\n\n" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": null, 56 | "metadata": { 57 | "collapsed": false 58 | }, 59 | "outputs": [], 60 | "source": [ 61 | "SOS_token = 0\nEOS_token = 1\n\n\nclass Lang:\n def __init__(self, name):\n self.name = name\n self.word2index = {}\n self.word2count = {}\n self.index2word = {0: \"SOS\", 1: \"EOS\"}\n self.n_words = 2 # Count SOS and EOS\n\n def addSentence(self, sentence):\n for word in sentence.split(' '):\n self.addWord(word)\n\n def addWord(self, word):\n if word not in self.word2index:\n self.word2index[word] = self.n_words\n self.word2count[word] = 1\n self.index2word[self.n_words] = word\n self.n_words += 1\n else:\n self.word2count[word] += 1" 62 | ] 63 | }, 64 | { 65 | "cell_type": "markdown", 66 | "metadata": {}, 67 | "source": [ 68 | "The files are all in Unicode, to simplify we will turn Unicode\ncharacters to ASCII, make everything lowercase, and trim most\npunctuation.\n\n\n" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": null, 74 | "metadata": { 75 | "collapsed": false 76 | }, 77 | "outputs": [], 78 | "source": [ 79 | "# Turn a Unicode string to plain ASCII, thanks to\n# https://stackoverflow.com/a/518232/2809427\ndef unicodeToAscii(s):\n return ''.join(\n c for c in unicodedata.normalize('NFD', s)\n if unicodedata.category(c) != 'Mn'\n )\n\n# Lowercase, trim, and remove non-letter characters\n\n\ndef normalizeString(s):\n s = unicodeToAscii(s.lower().strip())\n s = re.sub(r\"([.!?])\", r\" \\1\", s)\n s = re.sub(r\"[^a-zA-Z.!?]+\", r\" \", s)\n return s" 80 | ] 81 | }, 82 | { 83 | "cell_type": "markdown", 84 | "metadata": {}, 85 | "source": [ 86 | "To read the data file we will split the file into lines, and then split\nlines into pairs. The files are all English \u2192 Other Language, so if we\nwant to translate from Other Language \u2192 English I added the ``reverse``\nflag to reverse the pairs.\n\n\n" 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": null, 92 | "metadata": { 93 | "collapsed": false 94 | }, 95 | "outputs": [], 96 | "source": [ 97 | "def readLangs(lang1, lang2, reverse=False):\n print(\"Reading lines...\")\n\n # Read the file and split into lines\n lines = open('data/%s-%s.txt' % (lang1, lang2), encoding='utf-8').\\\n read().strip().split('\\n')\n\n # Split every line into pairs and normalize\n pairs = [[normalizeString(s) for s in l.split('\\t')] for l in lines]\n\n # Reverse pairs, make Lang instances\n if reverse:\n pairs = [list(reversed(p)) for p in pairs]\n input_lang = Lang(lang2)\n output_lang = Lang(lang1)\n else:\n input_lang = Lang(lang1)\n output_lang = Lang(lang2)\n\n return input_lang, output_lang, pairs" 98 | ] 99 | }, 100 | { 101 | "cell_type": "markdown", 102 | "metadata": {}, 103 | "source": [ 104 | "Since there are a *lot* of example sentences and we want to train\nsomething quickly, we'll trim the data set to only relatively short and\nsimple sentences. Here the maximum length is 10 words (that includes\nending punctuation) and we're filtering to sentences that translate to\nthe form \"I am\" or \"He is\" etc. (accounting for apostrophes replaced\nearlier).\n\n\n" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": null, 110 | "metadata": { 111 | "collapsed": false 112 | }, 113 | "outputs": [], 114 | "source": [ 115 | "MAX_LENGTH = 10\n\neng_prefixes = (\n \"i am \", \"i m \",\n \"he is\", \"he s \",\n \"she is\", \"she s \",\n \"you are\", \"you re \",\n \"we are\", \"we re \",\n \"they are\", \"they re \"\n)\n\n\ndef filterPair(p):\n return len(p[0].split(' ')) < MAX_LENGTH and \\\n len(p[1].split(' ')) < MAX_LENGTH and \\\n p[1].startswith(eng_prefixes)\n\n\ndef filterPairs(pairs):\n return [pair for pair in pairs if filterPair(pair)]" 116 | ] 117 | }, 118 | { 119 | "cell_type": "markdown", 120 | "metadata": {}, 121 | "source": [ 122 | "The full process for preparing the data is:\n\n- Read text file and split into lines, split lines into pairs\n- Normalize text, filter by length and content\n- Make word lists from sentences in pairs\n\n\n" 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": null, 128 | "metadata": { 129 | "collapsed": false 130 | }, 131 | "outputs": [], 132 | "source": [ 133 | "def prepareData(lang1, lang2, reverse=False):\n input_lang, output_lang, pairs = readLangs(lang1, lang2, reverse)\n print(\"Read %s sentence pairs\" % len(pairs))\n pairs = filterPairs(pairs)\n print(\"Trimmed to %s sentence pairs\" % len(pairs))\n print(\"Counting words...\")\n for pair in pairs:\n input_lang.addSentence(pair[0])\n output_lang.addSentence(pair[1])\n print(\"Counted words:\")\n print(input_lang.name, input_lang.n_words)\n print(output_lang.name, output_lang.n_words)\n return input_lang, output_lang, pairs\n\n\ninput_lang, output_lang, pairs = prepareData('eng', 'fra', True)\nprint(random.choice(pairs))" 134 | ] 135 | }, 136 | { 137 | "cell_type": "markdown", 138 | "metadata": {}, 139 | "source": [ 140 | "The Seq2Seq Model\n=================\n\nA Recurrent Neural Network, or RNN, is a network that operates on a\nsequence and uses its own output as input for subsequent steps.\n\nA `Sequence to Sequence network `__, or\nseq2seq network, or `Encoder Decoder\nnetwork `__, is a model\nconsisting of two RNNs called the encoder and decoder. The encoder reads\nan input sequence and outputs a single vector, and the decoder reads\nthat vector to produce an output sequence.\n\n.. figure:: /_static/img/seq-seq-images/seq2seq.png\n :alt:\n\nUnlike sequence prediction with a single RNN, where every input\ncorresponds to an output, the seq2seq model frees us from sequence\nlength and order, which makes it ideal for translation between two\nlanguages.\n\nConsider the sentence \"Je ne suis pas le chat noir\" \u2192 \"I am not the\nblack cat\". Most of the words in the input sentence have a direct\ntranslation in the output sentence, but are in slightly different\norders, e.g. \"chat noir\" and \"black cat\". Because of the \"ne/pas\"\nconstruction there is also one more word in the input sentence. It would\nbe difficult to produce a correct translation directly from the sequence\nof input words.\n\nWith a seq2seq model the encoder creates a single vector which, in the\nideal case, encodes the \"meaning\" of the input sequence into a single\nvector \u2014 a single point in some N dimensional space of sentences.\n\n\n" 141 | ] 142 | }, 143 | { 144 | "cell_type": "markdown", 145 | "metadata": {}, 146 | "source": [ 147 | "The Encoder\n-----------\n\nThe encoder of a seq2seq network is a RNN that outputs some value for\nevery word from the input sentence. For every input word the encoder\noutputs a vector and a hidden state, and uses the hidden state for the\nnext input word.\n\n.. figure:: /_static/img/seq-seq-images/encoder-network.png\n :alt:\n\n\n\n" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": null, 153 | "metadata": { 154 | "collapsed": false 155 | }, 156 | "outputs": [], 157 | "source": [ 158 | "class EncoderRNN(nn.Module):\n def __init__(self, input_size, hidden_size):\n super(EncoderRNN, self).__init__()\n self.hidden_size = hidden_size\n\n self.embedding = nn.Embedding(input_size, hidden_size)\n self.gru = nn.GRU(hidden_size, hidden_size)\n\n def forward(self, input, hidden):\n embedded = self.embedding(input).view(1, 1, -1)\n output = embedded\n output, hidden = self.gru(output, hidden)\n return output, hidden\n\n def initHidden(self):\n return torch.zeros(1, 1, self.hidden_size, device=device)" 159 | ] 160 | }, 161 | { 162 | "cell_type": "markdown", 163 | "metadata": {}, 164 | "source": [ 165 | "The Decoder\n-----------\n\nThe decoder is another RNN that takes the encoder output vector(s) and\noutputs a sequence of words to create the translation.\n\n\n" 166 | ] 167 | }, 168 | { 169 | "cell_type": "markdown", 170 | "metadata": {}, 171 | "source": [ 172 | "Simple Decoder\n^^^^^^^^^^^^^^\n\nIn the simplest seq2seq decoder we use only last output of the encoder.\nThis last output is sometimes called the *context vector* as it encodes\ncontext from the entire sequence. This context vector is used as the\ninitial hidden state of the decoder.\n\nAt every step of decoding, the decoder is given an input token and\nhidden state. The initial input token is the start-of-string ````\ntoken, and the first hidden state is the context vector (the encoder's\nlast hidden state).\n\n.. figure:: /_static/img/seq-seq-images/decoder-network.png\n :alt:\n\n\n\n" 173 | ] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "execution_count": null, 178 | "metadata": { 179 | "collapsed": false 180 | }, 181 | "outputs": [], 182 | "source": [ 183 | "class DecoderRNN(nn.Module):\n def __init__(self, hidden_size, output_size):\n super(DecoderRNN, self).__init__()\n self.hidden_size = hidden_size\n\n self.embedding = nn.Embedding(output_size, hidden_size)\n self.gru = nn.GRU(hidden_size, hidden_size)\n self.out = nn.Linear(hidden_size, output_size)\n self.softmax = nn.LogSoftmax(dim=1)\n\n def forward(self, input, hidden):\n output = self.embedding(input).view(1, 1, -1)\n output = F.relu(output)\n output, hidden = self.gru(output, hidden)\n output = self.softmax(self.out(output[0]))\n return output, hidden\n\n def initHidden(self):\n return torch.zeros(1, 1, self.hidden_size, device=device)" 184 | ] 185 | }, 186 | { 187 | "cell_type": "markdown", 188 | "metadata": {}, 189 | "source": [ 190 | "I encourage you to train and observe the results of this model, but to\nsave space we'll be going straight for the gold and introducing the\nAttention Mechanism.\n\n\n" 191 | ] 192 | }, 193 | { 194 | "cell_type": "markdown", 195 | "metadata": {}, 196 | "source": [ 197 | "Attention Decoder\n^^^^^^^^^^^^^^^^^\n\nIf only the context vector is passed betweeen the encoder and decoder,\nthat single vector carries the burden of encoding the entire sentence.\n\nAttention allows the decoder network to \"focus\" on a different part of\nthe encoder's outputs for every step of the decoder's own outputs. First\nwe calculate a set of *attention weights*. These will be multiplied by\nthe encoder output vectors to create a weighted combination. The result\n(called ``attn_applied`` in the code) should contain information about\nthat specific part of the input sequence, and thus help the decoder\nchoose the right output words.\n\n.. figure:: https://i.imgur.com/1152PYf.png\n :alt:\n\nCalculating the attention weights is done with another feed-forward\nlayer ``attn``, using the decoder's input and hidden state as inputs.\nBecause there are sentences of all sizes in the training data, to\nactually create and train this layer we have to choose a maximum\nsentence length (input length, for encoder outputs) that it can apply\nto. Sentences of the maximum length will use all the attention weights,\nwhile shorter sentences will only use the first few.\n\n.. figure:: /_static/img/seq-seq-images/attention-decoder-network.png\n :alt:\n\n\n\n" 198 | ] 199 | }, 200 | { 201 | "cell_type": "code", 202 | "execution_count": null, 203 | "metadata": { 204 | "collapsed": false 205 | }, 206 | "outputs": [], 207 | "source": [ 208 | "class AttnDecoderRNN(nn.Module):\n def __init__(self, hidden_size, output_size, dropout_p=0.1, max_length=MAX_LENGTH):\n super(AttnDecoderRNN, self).__init__()\n self.hidden_size = hidden_size\n self.output_size = output_size\n self.dropout_p = dropout_p\n self.max_length = max_length\n\n self.embedding = nn.Embedding(self.output_size, self.hidden_size)\n self.attn = nn.Linear(self.hidden_size * 2, self.max_length)\n self.attn_combine = nn.Linear(self.hidden_size * 2, self.hidden_size)\n self.dropout = nn.Dropout(self.dropout_p)\n self.gru = nn.GRU(self.hidden_size, self.hidden_size)\n self.out = nn.Linear(self.hidden_size, self.output_size)\n\n def forward(self, input, hidden, encoder_outputs):\n embedded = self.embedding(input).view(1, 1, -1)\n embedded = self.dropout(embedded)\n\n attn_weights = F.softmax(\n self.attn(torch.cat((embedded[0], hidden[0]), 1)), dim=1)\n attn_applied = torch.bmm(attn_weights.unsqueeze(0),\n encoder_outputs.unsqueeze(0))\n\n output = torch.cat((embedded[0], attn_applied[0]), 1)\n output = self.attn_combine(output).unsqueeze(0)\n\n output = F.relu(output)\n output, hidden = self.gru(output, hidden)\n\n output = F.log_softmax(self.out(output[0]), dim=1)\n return output, hidden, attn_weights\n\n def initHidden(self):\n return torch.zeros(1, 1, self.hidden_size, device=device)" 209 | ] 210 | }, 211 | { 212 | "cell_type": "markdown", 213 | "metadata": {}, 214 | "source": [ 215 | "

Note

There are other forms of attention that work around the length\n limitation by using a relative position approach. Read about \"local\n attention\" in `Effective Approaches to Attention-based Neural Machine\n Translation `__.

\n\nTraining\n========\n\nPreparing Training Data\n-----------------------\n\nTo train, for each pair we will need an input tensor (indexes of the\nwords in the input sentence) and target tensor (indexes of the words in\nthe target sentence). While creating these vectors we will append the\nEOS token to both sequences.\n\n\n" 216 | ] 217 | }, 218 | { 219 | "cell_type": "code", 220 | "execution_count": null, 221 | "metadata": { 222 | "collapsed": false 223 | }, 224 | "outputs": [], 225 | "source": [ 226 | "def indexesFromSentence(lang, sentence):\n return [lang.word2index[word] for word in sentence.split(' ')]\n\n\ndef tensorFromSentence(lang, sentence):\n indexes = indexesFromSentence(lang, sentence)\n indexes.append(EOS_token)\n return torch.tensor(indexes, dtype=torch.long, device=device).view(-1, 1)\n\n\ndef tensorsFromPair(pair):\n input_tensor = tensorFromSentence(input_lang, pair[0])\n target_tensor = tensorFromSentence(output_lang, pair[1])\n return (input_tensor, target_tensor)" 227 | ] 228 | }, 229 | { 230 | "cell_type": "markdown", 231 | "metadata": {}, 232 | "source": [ 233 | "Training the Model\n------------------\n\nTo train we run the input sentence through the encoder, and keep track\nof every output and the latest hidden state. Then the decoder is given\nthe ```` token as its first input, and the last hidden state of the\nencoder as its first hidden state.\n\n\"Teacher forcing\" is the concept of using the real target outputs as\neach next input, instead of using the decoder's guess as the next input.\nUsing teacher forcing causes it to converge faster but `when the trained\nnetwork is exploited, it may exhibit\ninstability `__.\n\nYou can observe outputs of teacher-forced networks that read with\ncoherent grammar but wander far from the correct translation -\nintuitively it has learned to represent the output grammar and can \"pick\nup\" the meaning once the teacher tells it the first few words, but it\nhas not properly learned how to create the sentence from the translation\nin the first place.\n\nBecause of the freedom PyTorch's autograd gives us, we can randomly\nchoose to use teacher forcing or not with a simple if statement. Turn\n``teacher_forcing_ratio`` up to use more of it.\n\n\n" 234 | ] 235 | }, 236 | { 237 | "cell_type": "code", 238 | "execution_count": null, 239 | "metadata": { 240 | "collapsed": false 241 | }, 242 | "outputs": [], 243 | "source": [ 244 | "teacher_forcing_ratio = 0.5\n\n\ndef train(input_tensor, target_tensor, encoder, decoder, encoder_optimizer, decoder_optimizer, criterion, max_length=MAX_LENGTH):\n encoder_hidden = encoder.initHidden()\n\n encoder_optimizer.zero_grad()\n decoder_optimizer.zero_grad()\n\n input_length = input_tensor.size(0)\n target_length = target_tensor.size(0)\n\n encoder_outputs = torch.zeros(max_length, encoder.hidden_size, device=device)\n\n loss = 0\n\n for ei in range(input_length):\n encoder_output, encoder_hidden = encoder(\n input_tensor[ei], encoder_hidden)\n encoder_outputs[ei] = encoder_output[0, 0]\n\n decoder_input = torch.tensor([[SOS_token]], device=device)\n\n decoder_hidden = encoder_hidden\n\n use_teacher_forcing = True if random.random() < teacher_forcing_ratio else False\n\n if use_teacher_forcing:\n # Teacher forcing: Feed the target as the next input\n for di in range(target_length):\n decoder_output, decoder_hidden, decoder_attention = decoder(\n decoder_input, decoder_hidden, encoder_outputs)\n loss += criterion(decoder_output, target_tensor[di])\n decoder_input = target_tensor[di] # Teacher forcing\n\n else:\n # Without teacher forcing: use its own predictions as the next input\n for di in range(target_length):\n decoder_output, decoder_hidden, decoder_attention = decoder(\n decoder_input, decoder_hidden, encoder_outputs)\n topv, topi = decoder_output.topk(1)\n decoder_input = topi.squeeze().detach() # detach from history as input\n\n loss += criterion(decoder_output, target_tensor[di])\n if decoder_input.item() == EOS_token:\n break\n\n loss.backward()\n\n encoder_optimizer.step()\n decoder_optimizer.step()\n\n return loss.item() / target_length" 245 | ] 246 | }, 247 | { 248 | "cell_type": "markdown", 249 | "metadata": {}, 250 | "source": [ 251 | "This is a helper function to print time elapsed and estimated time\nremaining given the current time and progress %.\n\n\n" 252 | ] 253 | }, 254 | { 255 | "cell_type": "code", 256 | "execution_count": null, 257 | "metadata": { 258 | "collapsed": false 259 | }, 260 | "outputs": [], 261 | "source": [ 262 | "import time\nimport math\n\n\ndef asMinutes(s):\n m = math.floor(s / 60)\n s -= m * 60\n return '%dm %ds' % (m, s)\n\n\ndef timeSince(since, percent):\n now = time.time()\n s = now - since\n es = s / (percent)\n rs = es - s\n return '%s (- %s)' % (asMinutes(s), asMinutes(rs))" 263 | ] 264 | }, 265 | { 266 | "cell_type": "markdown", 267 | "metadata": {}, 268 | "source": [ 269 | "The whole training process looks like this:\n\n- Start a timer\n- Initialize optimizers and criterion\n- Create set of training pairs\n- Start empty losses array for plotting\n\nThen we call ``train`` many times and occasionally print the progress (%\nof examples, time so far, estimated time) and average loss.\n\n\n" 270 | ] 271 | }, 272 | { 273 | "cell_type": "code", 274 | "execution_count": null, 275 | "metadata": { 276 | "collapsed": false 277 | }, 278 | "outputs": [], 279 | "source": [ 280 | "def trainIters(encoder, decoder, n_iters, print_every=1000, plot_every=100, learning_rate=0.01):\n start = time.time()\n plot_losses = []\n print_loss_total = 0 # Reset every print_every\n plot_loss_total = 0 # Reset every plot_every\n\n encoder_optimizer = optim.SGD(encoder.parameters(), lr=learning_rate)\n decoder_optimizer = optim.SGD(decoder.parameters(), lr=learning_rate)\n training_pairs = [tensorsFromPair(random.choice(pairs))\n for i in range(n_iters)]\n criterion = nn.NLLLoss()\n\n for iter in range(1, n_iters + 1):\n training_pair = training_pairs[iter - 1]\n input_tensor = training_pair[0]\n target_tensor = training_pair[1]\n\n loss = train(input_tensor, target_tensor, encoder,\n decoder, encoder_optimizer, decoder_optimizer, criterion)\n print_loss_total += loss\n plot_loss_total += loss\n\n if iter % print_every == 0:\n print_loss_avg = print_loss_total / print_every\n print_loss_total = 0\n print('%s (%d %d%%) %.4f' % (timeSince(start, iter / n_iters),\n iter, iter / n_iters * 100, print_loss_avg))\n\n if iter % plot_every == 0:\n plot_loss_avg = plot_loss_total / plot_every\n plot_losses.append(plot_loss_avg)\n plot_loss_total = 0\n\n showPlot(plot_losses)" 281 | ] 282 | }, 283 | { 284 | "cell_type": "markdown", 285 | "metadata": {}, 286 | "source": [ 287 | "Plotting results\n----------------\n\nPlotting is done with matplotlib, using the array of loss values\n``plot_losses`` saved while training.\n\n\n" 288 | ] 289 | }, 290 | { 291 | "cell_type": "code", 292 | "execution_count": null, 293 | "metadata": { 294 | "collapsed": false 295 | }, 296 | "outputs": [], 297 | "source": [ 298 | "import matplotlib.pyplot as plt\nplt.switch_backend('agg')\nimport matplotlib.ticker as ticker\nimport numpy as np\n\n\ndef showPlot(points):\n plt.figure()\n fig, ax = plt.subplots()\n # this locator puts ticks at regular intervals\n loc = ticker.MultipleLocator(base=0.2)\n ax.yaxis.set_major_locator(loc)\n plt.plot(points)" 299 | ] 300 | }, 301 | { 302 | "cell_type": "markdown", 303 | "metadata": {}, 304 | "source": [ 305 | "Evaluation\n==========\n\nEvaluation is mostly the same as training, but there are no targets so\nwe simply feed the decoder's predictions back to itself for each step.\nEvery time it predicts a word we add it to the output string, and if it\npredicts the EOS token we stop there. We also store the decoder's\nattention outputs for display later.\n\n\n" 306 | ] 307 | }, 308 | { 309 | "cell_type": "code", 310 | "execution_count": null, 311 | "metadata": { 312 | "collapsed": false 313 | }, 314 | "outputs": [], 315 | "source": [ 316 | "def evaluate(encoder, decoder, sentence, max_length=MAX_LENGTH):\n with torch.no_grad():\n input_tensor = tensorFromSentence(input_lang, sentence)\n input_length = input_tensor.size()[0]\n encoder_hidden = encoder.initHidden()\n\n encoder_outputs = torch.zeros(max_length, encoder.hidden_size, device=device)\n\n for ei in range(input_length):\n encoder_output, encoder_hidden = encoder(input_tensor[ei],\n encoder_hidden)\n encoder_outputs[ei] += encoder_output[0, 0]\n\n decoder_input = torch.tensor([[SOS_token]], device=device) # SOS\n\n decoder_hidden = encoder_hidden\n\n decoded_words = []\n decoder_attentions = torch.zeros(max_length, max_length)\n\n for di in range(max_length):\n decoder_output, decoder_hidden, decoder_attention = decoder(\n decoder_input, decoder_hidden, encoder_outputs)\n decoder_attentions[di] = decoder_attention.data\n topv, topi = decoder_output.data.topk(1)\n if topi.item() == EOS_token:\n decoded_words.append('')\n break\n else:\n decoded_words.append(output_lang.index2word[topi.item()])\n\n decoder_input = topi.squeeze().detach()\n\n return decoded_words, decoder_attentions[:di + 1]" 317 | ] 318 | }, 319 | { 320 | "cell_type": "markdown", 321 | "metadata": {}, 322 | "source": [ 323 | "We can evaluate random sentences from the training set and print out the\ninput, target, and output to make some subjective quality judgements:\n\n\n" 324 | ] 325 | }, 326 | { 327 | "cell_type": "code", 328 | "execution_count": null, 329 | "metadata": { 330 | "collapsed": false 331 | }, 332 | "outputs": [], 333 | "source": [ 334 | "def evaluateRandomly(encoder, decoder, n=10):\n for i in range(n):\n pair = random.choice(pairs)\n print('>', pair[0])\n print('=', pair[1])\n output_words, attentions = evaluate(encoder, decoder, pair[0])\n output_sentence = ' '.join(output_words)\n print('<', output_sentence)\n print('')" 335 | ] 336 | }, 337 | { 338 | "cell_type": "markdown", 339 | "metadata": {}, 340 | "source": [ 341 | "Training and Evaluating\n=======================\n\nWith all these helper functions in place (it looks like extra work, but\nit makes it easier to run multiple experiments) we can actually\ninitialize a network and start training.\n\nRemember that the input sentences were heavily filtered. For this small\ndataset we can use relatively small networks of 256 hidden nodes and a\nsingle GRU layer. After about 40 minutes on a MacBook CPU we'll get some\nreasonable results.\n\n.. Note::\n If you run this notebook you can train, interrupt the kernel,\n evaluate, and continue training later. Comment out the lines where the\n encoder and decoder are initialized and run ``trainIters`` again.\n\n\n" 342 | ] 343 | }, 344 | { 345 | "cell_type": "code", 346 | "execution_count": null, 347 | "metadata": { 348 | "collapsed": false 349 | }, 350 | "outputs": [], 351 | "source": [ 352 | "hidden_size = 256\nencoder1 = EncoderRNN(input_lang.n_words, hidden_size).to(device)\nattn_decoder1 = AttnDecoderRNN(hidden_size, output_lang.n_words, dropout_p=0.1).to(device)\n\ntrainIters(encoder1, attn_decoder1, 75000, print_every=5000)" 353 | ] 354 | }, 355 | { 356 | "cell_type": "code", 357 | "execution_count": null, 358 | "metadata": { 359 | "collapsed": false 360 | }, 361 | "outputs": [], 362 | "source": [ 363 | "evaluateRandomly(encoder1, attn_decoder1)" 364 | ] 365 | }, 366 | { 367 | "cell_type": "markdown", 368 | "metadata": {}, 369 | "source": [ 370 | "Visualizing Attention\n---------------------\n\nA useful property of the attention mechanism is its highly interpretable\noutputs. Because it is used to weight specific encoder outputs of the\ninput sequence, we can imagine looking where the network is focused most\nat each time step.\n\nYou could simply run ``plt.matshow(attentions)`` to see attention output\ndisplayed as a matrix, with the columns being input steps and rows being\noutput steps:\n\n\n" 371 | ] 372 | }, 373 | { 374 | "cell_type": "code", 375 | "execution_count": null, 376 | "metadata": { 377 | "collapsed": false 378 | }, 379 | "outputs": [], 380 | "source": [ 381 | "output_words, attentions = evaluate(\n encoder1, attn_decoder1, \"je suis trop froid .\")\nplt.matshow(attentions.numpy())" 382 | ] 383 | }, 384 | { 385 | "cell_type": "markdown", 386 | "metadata": {}, 387 | "source": [ 388 | "For a better viewing experience we will do the extra work of adding axes\nand labels:\n\n\n" 389 | ] 390 | }, 391 | { 392 | "cell_type": "code", 393 | "execution_count": null, 394 | "metadata": { 395 | "collapsed": false 396 | }, 397 | "outputs": [], 398 | "source": [ 399 | "def showAttention(input_sentence, output_words, attentions):\n # Set up figure with colorbar\n fig = plt.figure()\n ax = fig.add_subplot(111)\n cax = ax.matshow(attentions.numpy(), cmap='bone')\n fig.colorbar(cax)\n\n # Set up axes\n ax.set_xticklabels([''] + input_sentence.split(' ') +\n [''], rotation=90)\n ax.set_yticklabels([''] + output_words)\n\n # Show label at every tick\n ax.xaxis.set_major_locator(ticker.MultipleLocator(1))\n ax.yaxis.set_major_locator(ticker.MultipleLocator(1))\n\n plt.show()\n\n\ndef evaluateAndShowAttention(input_sentence):\n output_words, attentions = evaluate(\n encoder1, attn_decoder1, input_sentence)\n print('input =', input_sentence)\n print('output =', ' '.join(output_words))\n showAttention(input_sentence, output_words, attentions)\n\n\nevaluateAndShowAttention(\"elle a cinq ans de moins que moi .\")\n\nevaluateAndShowAttention(\"elle est trop petit .\")\n\nevaluateAndShowAttention(\"je ne crains pas de mourir .\")\n\nevaluateAndShowAttention(\"c est un jeune directeur plein de talent .\")" 400 | ] 401 | }, 402 | { 403 | "cell_type": "markdown", 404 | "metadata": {}, 405 | "source": [ 406 | "Exercises\n=========\n\n- Try with a different dataset\n\n - Another language pair\n - Human \u2192 Machine (e.g. IOT commands)\n - Chat \u2192 Response\n - Question \u2192 Answer\n\n- Replace the embeddings with pre-trained word embeddings such as word2vec or\n GloVe\n- Try with more layers, more hidden units, and more sentences. Compare\n the training time and results.\n- If you use a translation file where pairs have two of the same phrase\n (``I am test \\t I am test``), you can use this as an autoencoder. Try\n this:\n\n - Train as an autoencoder\n - Save only the Encoder network\n - Train a new Decoder for translation from there\n\n\n" 407 | ] 408 | } 409 | ], 410 | "metadata": { 411 | "kernelspec": { 412 | "display_name": "Python 3", 413 | "language": "python", 414 | "name": "python3" 415 | }, 416 | "language_info": { 417 | "codemirror_mode": { 418 | "name": "ipython", 419 | "version": 3 420 | }, 421 | "file_extension": ".py", 422 | "mimetype": "text/x-python", 423 | "name": "python", 424 | "nbconvert_exporter": "python", 425 | "pygments_lexer": "ipython3", 426 | "version": "3.6.8" 427 | } 428 | }, 429 | "nbformat": 4, 430 | "nbformat_minor": 0 431 | } 432 | -------------------------------------------------------------------------------- /seq2seq_translation_tutorial-usf.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Translation with a Sequence to Sequence Network and Attention" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "In this project we will be teaching a neural network to translate from\n", 15 | "French to English.\n", 16 | "\n", 17 | "::\n", 18 | "\n", 19 | " [KEY: > input, = target, < output]\n", 20 | "\n", 21 | " > il est en train de peindre un tableau .\n", 22 | " = he is painting a picture .\n", 23 | " < he is painting a picture .\n", 24 | "\n", 25 | " > pourquoi ne pas essayer ce vin delicieux ?\n", 26 | " = why not try that delicious wine ?\n", 27 | " < why not try that delicious wine ?\n", 28 | "\n", 29 | " > elle n est pas poete mais romanciere .\n", 30 | " = she is not a poet but a novelist .\n", 31 | " < she not not a poet but a novelist .\n", 32 | "\n", 33 | " > vous etes trop maigre .\n", 34 | " = you re too skinny .\n", 35 | " < you re all alone .\n", 36 | "\n", 37 | "... to varying degrees of success.\n", 38 | "\n", 39 | "This is made possible by the simple but powerful idea of the [sequence\n", 40 | "to sequence network](https://arxiv.org/abs/1409.3215>), in which two\n", 41 | "recurrent neural networks work together to transform one sequence to\n", 42 | "another. An encoder network condenses an input sequence into a vector,\n", 43 | "and a decoder network unfolds that vector into a new sequence.\n", 44 | "\n", 45 | "![](imgs/seq2seq.png)\n", 46 | "\n", 47 | "To improve upon this model we'll use an [attention\n", 48 | "mechanism](https://arxiv.org/abs/1409.0473), which lets the decoder\n", 49 | "learn to focus over a specific range of the input sequence." 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 1, 55 | "metadata": {}, 56 | "outputs": [], 57 | "source": [ 58 | "from __future__ import unicode_literals, print_function, division\n", 59 | "from io import open\n", 60 | "import unicodedata\n", 61 | "import string\n", 62 | "import re\n", 63 | "import random\n", 64 | "\n", 65 | "import torch\n", 66 | "import torch.nn as nn\n", 67 | "from torch import optim\n", 68 | "import torch.nn.functional as F\n", 69 | "%matplotlib inline\n", 70 | "\n", 71 | "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")" 72 | ] 73 | }, 74 | { 75 | "cell_type": "markdown", 76 | "metadata": {}, 77 | "source": [ 78 | "## Loading data files\n", 79 | "The data for this project is a set of many thousands of English to\n", 80 | "French translation pairs." 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": 2, 86 | "metadata": {}, 87 | "outputs": [], 88 | "source": [ 89 | "def download_dataset():\n", 90 | " ! wget https://download.pytorch.org/tutorial/data.zip\n", 91 | " ! unzip data.zip" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": 9, 97 | "metadata": {}, 98 | "outputs": [ 99 | { 100 | "name": "stdout", 101 | "output_type": "stream", 102 | "text": [ 103 | "/bin/sh: wget: command not found\n", 104 | "unzip: cannot find or open data.zip, data.zip.zip or data.zip.ZIP.\n" 105 | ] 106 | } 107 | ], 108 | "source": [ 109 | "# to download the dataset\n", 110 | "#download_dataset()" 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": {}, 116 | "source": [ 117 | "We will be representing each word in a language as a one-hot\n", 118 | "vector, or giant vector of zeros except for a single one (at the index\n", 119 | "of the word). \n", 120 | "![](imgs/word-encoding.png) " 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "metadata": {}, 126 | "source": [ 127 | "We'll need a unique index per word to use as the inputs and targets of\n", 128 | "the networks later. To keep track of all this we will use a helper class\n", 129 | "called ``Lang`` which has word → index (``word2index``) and index → word\n", 130 | "(``index2word``) dictionaries, as well as a count of each word\n", 131 | "``word2count`` to use to later replace rare words." 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": 10, 137 | "metadata": {}, 138 | "outputs": [], 139 | "source": [ 140 | "SOS_token = 0\n", 141 | "EOS_token = 1\n", 142 | "class Lang:\n", 143 | " def __init__(self, name):\n", 144 | " self.name = name\n", 145 | " self.word2index = {}\n", 146 | " self.word2count = {}\n", 147 | " self.index2word = {0: \"SOS\", 1: \"EOS\"}\n", 148 | " self.n_words = 2 # Count SOS and EOS\n", 149 | "\n", 150 | " def addSentence(self, sentence):\n", 151 | " for word in sentence.split(' '):\n", 152 | " self.addWord(word)\n", 153 | "\n", 154 | " def addWord(self, word):\n", 155 | " if word not in self.word2index:\n", 156 | " self.word2index[word] = self.n_words\n", 157 | " self.word2count[word] = 1\n", 158 | " self.index2word[self.n_words] = word\n", 159 | " self.n_words += 1\n", 160 | " else:\n", 161 | " self.word2count[word] += 1" 162 | ] 163 | }, 164 | { 165 | "cell_type": "markdown", 166 | "metadata": {}, 167 | "source": [ 168 | "The files are all in Unicode, to simplify we will turn Unicode\n", 169 | "characters to ASCII, make everything lowercase, and trim most\n", 170 | "punctuation.\n", 171 | "\n", 172 | "\n" 173 | ] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "execution_count": 11, 178 | "metadata": {}, 179 | "outputs": [], 180 | "source": [ 181 | "def unicodeToAscii(s):\n", 182 | " \"\"\"Turn a Unicode string to plain ASCII\n", 183 | " \n", 184 | " https://stackoverflow.com/a/518232/2809427\n", 185 | " \"\"\"\n", 186 | " return ''.join(c for c in unicodedata.normalize('NFD', s)\n", 187 | " if unicodedata.category(c) != 'Mn'\n", 188 | " )\n", 189 | "\n", 190 | "def normalizeString(s):\n", 191 | " \"\"\"Lowercase, trim, and remove non-letter characters\"\"\"\n", 192 | " s = unicodeToAscii(s.lower().strip())\n", 193 | " s = re.sub(r\"([.!?])\", r\" \\1\", s)\n", 194 | " s = re.sub(r\"[^a-zA-Z.!?]+\", r\" \", s)\n", 195 | " return s" 196 | ] 197 | }, 198 | { 199 | "cell_type": "markdown", 200 | "metadata": {}, 201 | "source": [ 202 | "To read the data file we will split the file into lines, and then split\n", 203 | "lines into pairs. The files are all English → Other Language, so if we\n", 204 | "want to translate from Other Language → English I added the ``reverse``\n", 205 | "flag to reverse the pairs.\n", 206 | "\n", 207 | "\n" 208 | ] 209 | }, 210 | { 211 | "cell_type": "code", 212 | "execution_count": 12, 213 | "metadata": {}, 214 | "outputs": [], 215 | "source": [ 216 | "def readLangs(lang1, lang2, reverse=False):\n", 217 | " print(\"Reading lines...\")\n", 218 | "\n", 219 | " # Read the file and split into lines\n", 220 | " lines = open('data/%s-%s.txt' % (lang1, lang2), encoding='utf-8').\\\n", 221 | " read().strip().split('\\n')\n", 222 | "\n", 223 | " # Split every line into pairs and normalize\n", 224 | " pairs = [[normalizeString(s) for s in l.split('\\t')] for l in lines]\n", 225 | "\n", 226 | " # Reverse pairs, make Lang instances\n", 227 | " if reverse:\n", 228 | " pairs = [list(reversed(p)) for p in pairs]\n", 229 | " input_lang = Lang(lang2)\n", 230 | " output_lang = Lang(lang1)\n", 231 | " else:\n", 232 | " input_lang = Lang(lang1)\n", 233 | " output_lang = Lang(lang2)\n", 234 | "\n", 235 | " return input_lang, output_lang, pairs" 236 | ] 237 | }, 238 | { 239 | "cell_type": "markdown", 240 | "metadata": {}, 241 | "source": [ 242 | "Since there are a *lot* of example sentences and we want to train\n", 243 | "something quickly, we'll trim the data set to only relatively short and\n", 244 | "simple sentences. Here the maximum length is 10 words (that includes\n", 245 | "ending punctuation) and we're filtering to sentences that translate to\n", 246 | "the form \"We are\" and \"They are\". (accounting for apostrophes replaced\n", 247 | "earlier).\n", 248 | "\n", 249 | "\n" 250 | ] 251 | }, 252 | { 253 | "cell_type": "code", 254 | "execution_count": 72, 255 | "metadata": {}, 256 | "outputs": [], 257 | "source": [ 258 | "MAX_LENGTH = 10\n", 259 | "\n", 260 | "eng_prefixes = (\n", 261 | " #\"i am \", \"i m \",\n", 262 | " #\"he is\", \"he s \",\n", 263 | " #\"she is\", \"she s \",\n", 264 | " #\"you are\", \"you re \",\n", 265 | " # I was experimenting using just \"we are sentences\"\n", 266 | " \"we are\", \"we re \",\n", 267 | " #\"they are\", \"they re \"\n", 268 | ")\n", 269 | "\n", 270 | "\n", 271 | "def filterPair(p):\n", 272 | " return len(p[0].split(' ')) < MAX_LENGTH and \\\n", 273 | " len(p[1].split(' ')) < MAX_LENGTH and \\\n", 274 | " p[1].startswith(eng_prefixes)\n", 275 | "\n", 276 | "\n", 277 | "def filterPairs(pairs):\n", 278 | " return [pair for pair in pairs if filterPair(pair)]" 279 | ] 280 | }, 281 | { 282 | "cell_type": "markdown", 283 | "metadata": {}, 284 | "source": [ 285 | "The full process for preparing the data is:\n", 286 | "\n", 287 | "- Read text file and split into lines, split lines into pairs\n", 288 | "- Normalize text, filter by length and content\n", 289 | "- Make word lists from sentences in pairs\n", 290 | "\n", 291 | "\n" 292 | ] 293 | }, 294 | { 295 | "cell_type": "code", 296 | "execution_count": 73, 297 | "metadata": {}, 298 | "outputs": [ 299 | { 300 | "name": "stdout", 301 | "output_type": "stream", 302 | "text": [ 303 | "Reading lines...\n", 304 | "Read 135842 sentence pairs\n", 305 | "Trimmed to 1113 sentence pairs\n", 306 | "number of test pairs: 10\n", 307 | "number of train pairs: 1103\n", 308 | "Counting words...\n", 309 | "Counted words:\n", 310 | "fra 906\n", 311 | "eng 695\n", 312 | "[u'nous sommes tous deux temoins .', u'we re both witnesses .']\n" 313 | ] 314 | } 315 | ], 316 | "source": [ 317 | "NUM_TRAIN = 2000\n", 318 | "def prepareData(lang1, lang2, reverse=False):\n", 319 | " input_lang, output_lang, pairs = readLangs(lang1, lang2, reverse)\n", 320 | " print(\"Read %s sentence pairs\" % len(pairs))\n", 321 | " pairs = filterPairs(pairs)\n", 322 | " print(\"Trimmed to %s sentence pairs\" % len(pairs))\n", 323 | " \n", 324 | " #randomize the data with a fixed seed for repeatability\n", 325 | " random.seed(4)\n", 326 | " random.shuffle(pairs)\n", 327 | " pairs = pairs[0:NUM_TRAIN+10]\n", 328 | " #choose the first 10 pairs for testing and the rest for training\n", 329 | " testPairs = pairs[0:10]\n", 330 | " trainPairs = pairs[10:len(pairs)]\n", 331 | "\n", 332 | " \n", 333 | " print(\"number of test pairs: %s\" % len(testPairs))\n", 334 | " print(\"number of train pairs: %s\" % len(trainPairs))\n", 335 | " \n", 336 | " print(\"Counting words...\")\n", 337 | " cnt = 0\n", 338 | " for pair in pairs:\n", 339 | " input_lang.addSentence(pair[0])\n", 340 | " output_lang.addSentence(pair[1])\n", 341 | " \n", 342 | " print(\"Counted words:\")\n", 343 | " print(input_lang.name, input_lang.n_words)\n", 344 | " print(output_lang.name, output_lang.n_words)\n", 345 | " return input_lang, output_lang, pairs, trainPairs, testPairs\n", 346 | "\n", 347 | "input_lang, output_lang, pairs, trainPairs, testPairs = prepareData('eng', 'fra', True)\n", 348 | "random.seed(4)\n", 349 | "print(random.choice(pairs))" 350 | ] 351 | }, 352 | { 353 | "cell_type": "markdown", 354 | "metadata": {}, 355 | "source": [ 356 | "The Seq2Seq Model\n", 357 | "=================\n", 358 | "\n", 359 | "A Recurrent Neural Network, or RNN, is a network that operates on a\n", 360 | "sequence and uses its own output as input for subsequent steps.\n", 361 | "\n", 362 | "A `Sequence to Sequence network `__, or\n", 363 | "seq2seq network, or `Encoder Decoder\n", 364 | "network `__, is a model\n", 365 | "consisting of two RNNs called the encoder and decoder. The encoder reads\n", 366 | "an input sequence and outputs a single vector, and the decoder reads\n", 367 | "that vector to produce an output sequence.\n", 368 | "\n", 369 | "![](imgs/seq2seq.png)\n", 370 | "\n", 371 | "Unlike sequence prediction with a single RNN, where every input\n", 372 | "corresponds to an output, the seq2seq model frees us from sequence\n", 373 | "length and order, which makes it ideal for translation between two\n", 374 | "languages.\n", 375 | "\n", 376 | "Consider the sentence \"Je ne suis pas le chat noir\" → \"I am not the\n", 377 | "black cat\". Most of the words in the input sentence have a direct\n", 378 | "translation in the output sentence, but are in slightly different\n", 379 | "orders, e.g. \"chat noir\" and \"black cat\". Because of the \"ne/pas\"\n", 380 | "construction there is also one more word in the input sentence. It would\n", 381 | "be difficult to produce a correct translation directly from the sequence\n", 382 | "of input words.\n", 383 | "\n", 384 | "With a seq2seq model the encoder creates a single vector which, in the\n", 385 | "ideal case, encodes the \"meaning\" of the input sequence into a single\n", 386 | "vector — a single point in some N dimensional space of sentences.\n", 387 | "\n", 388 | "\n" 389 | ] 390 | }, 391 | { 392 | "cell_type": "markdown", 393 | "metadata": {}, 394 | "source": [ 395 | "The Encoder\n", 396 | "-----------\n", 397 | "\n", 398 | "The encoder of a seq2seq network is a RNN that outputs some value for\n", 399 | "every word from the input sentence. For every input word the encoder\n", 400 | "outputs a vector and a hidden state, and uses the hidden state for the\n", 401 | "next input word.\n", 402 | "\n", 403 | "![](imgs/encoder-network.png)" 404 | ] 405 | }, 406 | { 407 | "cell_type": "code", 408 | "execution_count": 15, 409 | "metadata": {}, 410 | "outputs": [], 411 | "source": [ 412 | "class EncoderRNN(nn.Module):\n", 413 | " def __init__(self, input_size, hidden_size):\n", 414 | " super(EncoderRNN, self).__init__()\n", 415 | " self.hidden_size = hidden_size\n", 416 | "\n", 417 | " self.embedding = nn.Embedding(input_size, hidden_size)\n", 418 | " self.gru = nn.GRU(hidden_size, hidden_size)\n", 419 | "\n", 420 | " def forward(self, input, hidden):\n", 421 | " embedded = self.embedding(input).view(1, 1, -1)\n", 422 | " output = embedded\n", 423 | " output, hidden = self.gru(output, hidden)\n", 424 | " return output, hidden\n", 425 | "\n", 426 | " def initHidden(self):\n", 427 | " return torch.zeros(1, 1, self.hidden_size, device=device)" 428 | ] 429 | }, 430 | { 431 | "cell_type": "markdown", 432 | "metadata": {}, 433 | "source": [ 434 | "The Decoder\n", 435 | "-----------\n", 436 | "\n", 437 | "The decoder is another RNN that takes the encoder output vector(s) and\n", 438 | "outputs a sequence of words to create the translation.\n", 439 | "\n", 440 | "\n" 441 | ] 442 | }, 443 | { 444 | "cell_type": "markdown", 445 | "metadata": {}, 446 | "source": [ 447 | "Simple Decoder\n", 448 | "\n", 449 | "In the simplest seq2seq decoder we use only last output of the encoder.\n", 450 | "This last output is sometimes called the *context vector* as it encodes\n", 451 | "context from the entire sequence. This context vector is used as the\n", 452 | "initial hidden state of the decoder.\n", 453 | "\n", 454 | "At every step of decoding, the decoder is given an input token and\n", 455 | "hidden state. The initial input token is the start-of-string ````\n", 456 | "token, and the first hidden state is the context vector (the encoder's\n", 457 | "last hidden state).\n", 458 | "\n", 459 | "![](imgs/decoder-network.png)" 460 | ] 461 | }, 462 | { 463 | "cell_type": "code", 464 | "execution_count": 16, 465 | "metadata": {}, 466 | "outputs": [], 467 | "source": [ 468 | "class DecoderRNN(nn.Module):\n", 469 | " def __init__(self, hidden_size, output_size):\n", 470 | " super(DecoderRNN, self).__init__()\n", 471 | " self.hidden_size = hidden_size\n", 472 | "\n", 473 | " self.embedding = nn.Embedding(output_size, hidden_size)\n", 474 | " self.gru = nn.GRU(hidden_size, hidden_size)\n", 475 | " self.out = nn.Linear(hidden_size, output_size)\n", 476 | " self.softmax = nn.LogSoftmax(dim=1)\n", 477 | "\n", 478 | " def forward(self, input, hidden):\n", 479 | " output = self.embedding(input).view(1, 1, -1)\n", 480 | " output = F.relu(output)\n", 481 | " output, hidden = self.gru(output, hidden)\n", 482 | " output = self.softmax(self.out(output[0]))\n", 483 | " return output, hidden\n", 484 | "\n", 485 | " def initHidden(self):\n", 486 | " return torch.zeros(1, 1, self.hidden_size, device=device)" 487 | ] 488 | }, 489 | { 490 | "cell_type": "markdown", 491 | "metadata": {}, 492 | "source": [ 493 | "I encourage you to train and observe the results of this model, but to\n", 494 | "save space we'll be going straight for the gold and introducing the\n", 495 | "Attention Mechanism.\n", 496 | "\n", 497 | "\n" 498 | ] 499 | }, 500 | { 501 | "cell_type": "markdown", 502 | "metadata": {}, 503 | "source": [ 504 | "Attention Decoder\n", 505 | "\n", 506 | "\n", 507 | "If only the context vector is passed betweeen the encoder and decoder,\n", 508 | "that single vector carries the burden of encoding the entire sentence.\n", 509 | "\n", 510 | "Attention allows the decoder network to \"focus\" on a different part of\n", 511 | "the encoder's outputs for every step of the decoder's own outputs. First\n", 512 | "we calculate a set of *attention weights*. These will be multiplied by\n", 513 | "the encoder output vectors to create a weighted combination. The result\n", 514 | "(called ``attn_applied`` in the code) should contain information about\n", 515 | "that specific part of the input sequence, and thus help the decoder\n", 516 | "choose the right output words.\n", 517 | "\n", 518 | "![](https://i.imgur.com/1152PYf.png)\n", 519 | "\n", 520 | "Calculating the attention weights is done with another feed-forward\n", 521 | "layer ``attn``, using the decoder's input and hidden state as inputs.\n", 522 | "Because there are sentences of all sizes in the training data, to\n", 523 | "actually create and train this layer we have to choose a maximum\n", 524 | "sentence length (input length, for encoder outputs) that it can apply\n", 525 | "to. Sentences of the maximum length will use all the attention weights,\n", 526 | "while shorter sentences will only use the first few.\n", 527 | "\n", 528 | "![](imgs/attention-decoder-network.png)\n", 529 | "\n", 530 | "\n" 531 | ] 532 | }, 533 | { 534 | "cell_type": "code", 535 | "execution_count": 17, 536 | "metadata": {}, 537 | "outputs": [], 538 | "source": [ 539 | "class AttnDecoderRNN(nn.Module):\n", 540 | " def __init__(self, hidden_size, output_size, dropout_p=0.1, max_length=MAX_LENGTH):\n", 541 | " super(AttnDecoderRNN, self).__init__()\n", 542 | " self.hidden_size = hidden_size\n", 543 | " self.output_size = output_size\n", 544 | " self.dropout_p = dropout_p\n", 545 | " self.max_length = max_length\n", 546 | "\n", 547 | " self.embedding = nn.Embedding(self.output_size, self.hidden_size)\n", 548 | " self.attn = nn.Linear(self.hidden_size * 2, self.max_length)\n", 549 | " self.attn_combine = nn.Linear(self.hidden_size * 2, self.hidden_size)\n", 550 | " self.dropout = nn.Dropout(self.dropout_p)\n", 551 | " self.gru = nn.GRU(self.hidden_size, self.hidden_size)\n", 552 | " self.out = nn.Linear(self.hidden_size, self.output_size)\n", 553 | "\n", 554 | " def forward(self, input, hidden, encoder_outputs):\n", 555 | " #print('debug: in AttnDecoderRNN')\n", 556 | " embedded = self.embedding(input).view(1, 1, -1)\n", 557 | " embedded = self.dropout(embedded)\n", 558 | " \n", 559 | " #print('debug')\n", 560 | " #print(embedded[0].size(), hidden[0].size())\n", 561 | "\n", 562 | " attn_weights = F.softmax(\n", 563 | " self.attn(torch.cat((embedded[0], hidden[0]), 1)), dim=1)\n", 564 | "\n", 565 | " \n", 566 | " attn_applied = torch.bmm(attn_weights.unsqueeze(0),\n", 567 | " encoder_outputs.unsqueeze(0))\n", 568 | "\n", 569 | " output = torch.cat((embedded[0], attn_applied[0]), 1)\n", 570 | " output = self.attn_combine(output).unsqueeze(0)\n", 571 | "\n", 572 | " output = F.relu(output)\n", 573 | " output, hidden = self.gru(output, hidden)\n", 574 | "\n", 575 | " output = F.log_softmax(self.out(output[0]), dim=1)\n", 576 | " return output, hidden, attn_weights\n", 577 | "\n", 578 | " def initHidden(self):\n", 579 | " return torch.zeros(1, 1, self.hidden_size, device=device)" 580 | ] 581 | }, 582 | { 583 | "cell_type": "code", 584 | "execution_count": 18, 585 | "metadata": {}, 586 | "outputs": [], 587 | "source": [ 588 | "class AttnDotDecoderRNN(nn.Module):\n", 589 | " def __init__(self, hidden_size, output_size, dropout_p=0.1, max_length=MAX_LENGTH):\n", 590 | " super(AttnDotDecoderRNN, self).__init__()\n", 591 | " self.hidden_size = hidden_size\n", 592 | " self.output_size = output_size\n", 593 | " self.dropout_p = dropout_p\n", 594 | " self.max_length = max_length\n", 595 | "\n", 596 | " self.embedding = nn.Embedding(self.output_size, self.hidden_size)\n", 597 | " self.attn = nn.Linear(self.hidden_size * 2, 1)\n", 598 | " self.attn_combine = nn.Linear(self.hidden_size * 2, self.hidden_size)\n", 599 | " self.dropout = nn.Dropout(self.dropout_p)\n", 600 | " self.gru = nn.GRU(self.hidden_size, self.hidden_size)\n", 601 | " self.out = nn.Linear(self.hidden_size, self.output_size)\n", 602 | "\n", 603 | " def forward(self, input, hidden, encoder_outputs):\n", 604 | " embedded = self.embedding(input).view(1, 1, -1)\n", 605 | " embedded = self.dropout(embedded)\n", 606 | "\n", 607 | " scorefunc = torch.zeros(1, encoder_outputs.size(0), device=device)\n", 608 | " for ei in range(encoder_outputs.size(0)):\n", 609 | " scorefunc[0, ei] = torch.dot(encoder_outputs[ei], hidden[0,0])\n", 610 | " \n", 611 | " attn_weights = F.softmax(scorefunc, dim=1)\n", 612 | " \n", 613 | " attn_applied = torch.bmm(attn_weights.unsqueeze(0),\n", 614 | " encoder_outputs.unsqueeze(0))\n", 615 | "\n", 616 | " output = torch.cat((embedded[0], attn_applied[0]), 1)\n", 617 | " output = self.attn_combine(output).unsqueeze(0)\n", 618 | "\n", 619 | " output = F.relu(output)\n", 620 | " output, hidden = self.gru(output, hidden)\n", 621 | "\n", 622 | " output = F.log_softmax(self.out(output[0]), dim=1)\n", 623 | " return output, hidden, attn_weights\n", 624 | "\n", 625 | " def initHidden(self):\n", 626 | " return torch.zeros(1, 1, self.hidden_size, device=device)" 627 | ] 628 | }, 629 | { 630 | "cell_type": "code", 631 | "execution_count": 190, 632 | "metadata": {}, 633 | "outputs": [], 634 | "source": [ 635 | "class AttnMultiplicativeDecoderRNN(nn.Module):\n", 636 | " def __init__(self, hidden_size, output_size, dropout_p=0.1, max_length=MAX_LENGTH):\n", 637 | " super(AttnMultiplicativeDecoderRNN, self).__init__()\n", 638 | " self.hidden_size = hidden_size\n", 639 | " self.output_size = output_size\n", 640 | " self.dropout_p = dropout_p\n", 641 | " self.max_length = max_length\n", 642 | "\n", 643 | " self.embedding = nn.Embedding(self.output_size, self.hidden_size)\n", 644 | " self.attn = nn.Linear(self.hidden_size, self.hidden_size)\n", 645 | " self.attn_combine = nn.Linear(self.hidden_size * 2, self.hidden_size)\n", 646 | " self.dropout = nn.Dropout(self.dropout_p)\n", 647 | " self.gru = nn.GRU(self.hidden_size, self.hidden_size)\n", 648 | " self.out = nn.Linear(self.hidden_size, self.output_size)\n", 649 | "\n", 650 | " def forward(self, input, hidden, encoder_outputs):\n", 651 | " embedded = self.embedding(input).view(1, 1, -1)\n", 652 | " embedded = self.dropout(embedded)\n", 653 | "\n", 654 | " scorefunc = torch.zeros(1, encoder_outputs.size(0), device=device)\n", 655 | " for ei in range(encoder_outputs.size(0)):\n", 656 | " scorefunc[0, ei] = torch.dot(encoder_outputs[ei], self.attn(hidden[0,0]))\n", 657 | " \n", 658 | " attn_weights = F.softmax(scorefunc, dim=1)\n", 659 | " \n", 660 | " attn_applied = torch.bmm(attn_weights.unsqueeze(0),\n", 661 | " encoder_outputs.unsqueeze(0))\n", 662 | "\n", 663 | " output = torch.cat((embedded[0], attn_applied[0]), 1)\n", 664 | " output = self.attn_combine(output).unsqueeze(0)\n", 665 | "\n", 666 | " output = F.relu(output)\n", 667 | " output, hidden = self.gru(output, hidden)\n", 668 | "\n", 669 | " output = F.log_softmax(self.out(output[0]), dim=1)\n", 670 | " return output, hidden, attn_weights\n", 671 | "\n", 672 | " def initHidden(self):\n", 673 | " return torch.zeros(1, 1, self.hidden_size, device=device)" 674 | ] 675 | }, 676 | { 677 | "cell_type": "code", 678 | "execution_count": 20, 679 | "metadata": {}, 680 | "outputs": [], 681 | "source": [ 682 | "class AttnAdditiveDecoderRNN(nn.Module):\n", 683 | " def __init__(self, hidden_size, output_size, dropout_p=0.1, max_length=MAX_LENGTH):\n", 684 | " super(AttnAdditiveDecoderRNN, self).__init__()\n", 685 | " self.hidden_size = hidden_size\n", 686 | " self.output_size = output_size\n", 687 | " self.dropout_p = dropout_p\n", 688 | " self.max_length = max_length\n", 689 | "\n", 690 | " self.embedding = nn.Embedding(self.output_size, self.hidden_size)\n", 691 | " self.attn = nn.Linear(2*self.hidden_size, self.hidden_size)\n", 692 | " self.mydot = nn.Linear(self.hidden_size, 1)\n", 693 | " self.attn_combine = nn.Linear(self.hidden_size * 2, self.hidden_size)\n", 694 | " self.dropout = nn.Dropout(self.dropout_p)\n", 695 | " self.gru = nn.GRU(self.hidden_size, self.hidden_size)\n", 696 | " self.out = nn.Linear(self.hidden_size, self.output_size)\n", 697 | "\n", 698 | " def forward(self, input, hidden, encoder_outputs):\n", 699 | " embedded = self.embedding(input).view(1, 1, -1)\n", 700 | " embedded = self.dropout(embedded)\n", 701 | "\n", 702 | " scorefunc = torch.zeros(1, encoder_outputs.size(0), device=device)\n", 703 | " for ei in range(encoder_outputs.size(0)):\n", 704 | " scorefunc[0, ei] = self.mydot(torch.tanh(self.attn(torch.cat((encoder_outputs[ei].unsqueeze(0), hidden[0]),1))))\n", 705 | " \n", 706 | " attn_weights = F.softmax(scorefunc, dim=1)\n", 707 | " \n", 708 | " attn_applied = torch.bmm(attn_weights.unsqueeze(0),\n", 709 | " encoder_outputs.unsqueeze(0))\n", 710 | "\n", 711 | " output = torch.cat((embedded[0], attn_applied[0]), 1)\n", 712 | " output = self.attn_combine(output).unsqueeze(0)\n", 713 | "\n", 714 | " output = F.relu(output)\n", 715 | " output, hidden = self.gru(output, hidden)\n", 716 | "\n", 717 | " output = F.log_softmax(self.out(output[0]), dim=1)\n", 718 | " return output, hidden, attn_weights\n", 719 | "\n", 720 | " def initHidden(self):\n", 721 | " return torch.zeros(1, 1, self.hidden_size, device=device)" 722 | ] 723 | }, 724 | { 725 | "cell_type": "code", 726 | "execution_count": 133, 727 | "metadata": {}, 728 | "outputs": [], 729 | "source": [ 730 | "class AttnAdditive2DecoderRNN(nn.Module):\n", 731 | " def __init__(self, hidden_size, output_size, dropout_p=0.1, max_length=MAX_LENGTH):\n", 732 | " super(AttnAdditive2DecoderRNN, self).__init__()\n", 733 | " self.hidden_size = hidden_size\n", 734 | " self.output_size = output_size\n", 735 | " self.dropout_p = dropout_p\n", 736 | " self.max_length = max_length\n", 737 | "\n", 738 | " self.embedding = nn.Embedding(self.output_size, self.hidden_size)\n", 739 | " self.attn = nn.Linear(2*self.hidden_size, self.hidden_size)\n", 740 | " self.mydot = nn.Linear(self.hidden_size, 1)\n", 741 | " self.attn_combine = nn.Linear(self.hidden_size * 2, self.hidden_size)\n", 742 | " self.dropout = nn.Dropout(self.dropout_p)\n", 743 | " self.gru = nn.GRU(self.hidden_size, self.hidden_size)\n", 744 | " self.out1 = nn.Linear(self.hidden_size, self.output_size)\n", 745 | " self.out2 = nn.Linear(self.hidden_size * 2, self.output_size)\n", 746 | "\n", 747 | " def forward(self, input, hidden, encoder_outputs):\n", 748 | " embedded = self.embedding(input).view(1, 1, -1)\n", 749 | " embedded = self.dropout(embedded)\n", 750 | " embedded = F.relu(embedded)\n", 751 | " ###output, hidden = self.gru(output, hidden)\n", 752 | " output, hidden = self.gru(embedded, hidden)\n", 753 | "\n", 754 | " scorefunc = torch.zeros(1, encoder_outputs.size(0), device=device)\n", 755 | " for ei in range(encoder_outputs.size(0)):\n", 756 | " scorefunc[0, ei] = self.mydot(torch.tanh(self.attn(torch.cat((encoder_outputs[ei].unsqueeze(0), hidden[0]),1))))\n", 757 | " \n", 758 | " attn_weights = F.softmax(scorefunc, dim=1)\n", 759 | " \n", 760 | " attn_applied = torch.bmm(attn_weights.unsqueeze(0),\n", 761 | " encoder_outputs.unsqueeze(0))\n", 762 | "\n", 763 | " output = torch.cat((hidden[0], attn_applied[0]), 1)\n", 764 | " #output = self.attn_combine(output).unsqueeze(0)\n", 765 | " #output = F.relu(output)\n", 766 | " #output = F.log_softmax(self.out(output[0]), dim=1)\n", 767 | " ###output = F.log_softmax(self.out2(output), dim=1)\n", 768 | " output = self.attn_combine(output)\n", 769 | " output = F.log_softmax(self.out1(output), dim=1)\n", 770 | "\n", 771 | " return output, hidden, attn_weights\n", 772 | "\n", 773 | " def initHidden(self):\n", 774 | " return torch.zeros(1, 1, self.hidden_size, device=device)" 775 | ] 776 | }, 777 | { 778 | "cell_type": "markdown", 779 | "metadata": {}, 780 | "source": [ 781 | "

Note

There are other forms of attention that work around the length\n", 782 | " limitation by using a relative position approach. Read about \"local\n", 783 | " attention\" in `Effective Approaches to Attention-based Neural Machine\n", 784 | " Translation `__.

\n", 785 | "\n", 786 | "Training\n", 787 | "========\n", 788 | "\n", 789 | "Preparing Training Data\n", 790 | "-----------------------\n", 791 | "\n", 792 | "To train, for each pair we will need an input tensor (indexes of the\n", 793 | "words in the input sentence) and target tensor (indexes of the words in\n", 794 | "the target sentence). While creating these vectors we will append the\n", 795 | "EOS token to both sequences.\n", 796 | "\n", 797 | "\n" 798 | ] 799 | }, 800 | { 801 | "cell_type": "code", 802 | "execution_count": 21, 803 | "metadata": {}, 804 | "outputs": [], 805 | "source": [ 806 | "def indexesFromSentence(lang, sentence):\n", 807 | " return [lang.word2index[word] for word in sentence.split(' ')]\n", 808 | "\n", 809 | "\n", 810 | "def tensorFromSentence(lang, sentence):\n", 811 | " indexes = indexesFromSentence(lang, sentence)\n", 812 | " indexes.append(EOS_token)\n", 813 | " return torch.tensor(indexes, dtype=torch.long, device=device).view(-1, 1)\n", 814 | "\n", 815 | "\n", 816 | "def tensorsFromPair(pair):\n", 817 | " input_tensor = tensorFromSentence(input_lang, pair[0])\n", 818 | " target_tensor = tensorFromSentence(output_lang, pair[1])\n", 819 | " return (input_tensor, target_tensor)" 820 | ] 821 | }, 822 | { 823 | "cell_type": "markdown", 824 | "metadata": {}, 825 | "source": [ 826 | "Training the Model\n", 827 | "------------------\n", 828 | "\n", 829 | "To train we run the input sentence through the encoder, and keep track\n", 830 | "of every output and the latest hidden state. Then the decoder is given\n", 831 | "the ```` token as its first input, and the last hidden state of the\n", 832 | "encoder as its first hidden state.\n", 833 | "\n", 834 | "\"Teacher forcing\" is the concept of using the real target outputs as\n", 835 | "each next input, instead of using the decoder's guess as the next input.\n", 836 | "Using teacher forcing causes it to converge faster but `when the trained\n", 837 | "network is exploited, it may exhibit\n", 838 | "instability `__.\n", 839 | "\n", 840 | "You can observe outputs of teacher-forced networks that read with\n", 841 | "coherent grammar but wander far from the correct translation -\n", 842 | "intuitively it has learned to represent the output grammar and can \"pick\n", 843 | "up\" the meaning once the teacher tells it the first few words, but it\n", 844 | "has not properly learned how to create the sentence from the translation\n", 845 | "in the first place.\n", 846 | "\n", 847 | "Because of the freedom PyTorch's autograd gives us, we can randomly\n", 848 | "choose to use teacher forcing or not with a simple if statement. Turn\n", 849 | "``teacher_forcing_ratio`` up to use more of it.\n", 850 | "\n", 851 | "\n" 852 | ] 853 | }, 854 | { 855 | "cell_type": "code", 856 | "execution_count": 154, 857 | "metadata": {}, 858 | "outputs": [], 859 | "source": [ 860 | "teacher_forcing_ratio = 0.0\n", 861 | "\n", 862 | "def train(input_tensor, target_tensor, encoder, decoder, encoder_optimizer, decoder_optimizer, criterion, max_length=MAX_LENGTH, attention=True):\n", 863 | " encoder_hidden = encoder.initHidden()\n", 864 | "\n", 865 | " encoder_optimizer.zero_grad()\n", 866 | " decoder_optimizer.zero_grad()\n", 867 | "\n", 868 | " input_length = input_tensor.size(0)\n", 869 | " target_length = target_tensor.size(0)\n", 870 | "\n", 871 | " encoder_outputs = torch.zeros(max_length, encoder.hidden_size, device=device)\n", 872 | "\n", 873 | " loss = 0\n", 874 | "\n", 875 | " for ei in range(input_length):\n", 876 | " encoder_output, encoder_hidden = encoder(\n", 877 | " input_tensor[ei], encoder_hidden)\n", 878 | " encoder_outputs[ei] = encoder_output[0, 0]\n", 879 | "\n", 880 | " decoder_input = torch.tensor([[SOS_token]], device=device)\n", 881 | "\n", 882 | " #decoder_hidden = encoder_hidden\n", 883 | " decoder_hidden = decoder.initHidden()\n", 884 | " #decoder_hidden = encoder_outputs[0].unsqueeze(0).unsqueeze(0)\n", 885 | "\n", 886 | " use_teacher_forcing = True if random.random() < teacher_forcing_ratio else False\n", 887 | "\n", 888 | " if use_teacher_forcing:\n", 889 | " # Teacher forcing: Feed the target as the next input\n", 890 | " for di in range(target_length):\n", 891 | " if attention:\n", 892 | " decoder_output, decoder_hidden, decoder_attention = decoder(\n", 893 | " decoder_input, decoder_hidden, encoder_outputs)\n", 894 | " else:\n", 895 | " decoder_output, decoder_hidden = decoder(decoder_input, decoder_hidden)\n", 896 | " \n", 897 | " loss += criterion(decoder_output, target_tensor[di])\n", 898 | " decoder_input = target_tensor[di] # Teacher forcing\n", 899 | "\n", 900 | "\n", 901 | " else:\n", 902 | " # Without teacher forcing: use its own predictions as the next input\n", 903 | " for di in range(target_length):\n", 904 | " if attention:\n", 905 | " decoder_output, decoder_hidden, decoder_attention = decoder(\n", 906 | " decoder_input, decoder_hidden, encoder_outputs)\n", 907 | " else:\n", 908 | " decoder_output, decoder_hidden = decoder(decoder_input, decoder_hidden)\n", 909 | " \n", 910 | " topv, topi = decoder_output.topk(1)\n", 911 | " decoder_input = topi.squeeze().detach() # detach from history as input\n", 912 | "\n", 913 | " loss += criterion(decoder_output, target_tensor[di])\n", 914 | " if decoder_input.item() == EOS_token:\n", 915 | " break\n", 916 | "\n", 917 | " loss.backward()\n", 918 | "\n", 919 | " encoder_optimizer.step()\n", 920 | " decoder_optimizer.step()\n", 921 | "\n", 922 | " return loss.item() / target_length" 923 | ] 924 | }, 925 | { 926 | "cell_type": "markdown", 927 | "metadata": {}, 928 | "source": [ 929 | "This is a helper function to print time elapsed and estimated time\n", 930 | "remaining given the current time and progress %.\n", 931 | "\n", 932 | "\n" 933 | ] 934 | }, 935 | { 936 | "cell_type": "code", 937 | "execution_count": 23, 938 | "metadata": {}, 939 | "outputs": [], 940 | "source": [ 941 | "import time\n", 942 | "import math\n", 943 | "\n", 944 | "\n", 945 | "def asMinutes(s):\n", 946 | " m = math.floor(s / 60)\n", 947 | " s -= m * 60\n", 948 | " return '%dm %ds' % (m, s)\n", 949 | "\n", 950 | "\n", 951 | "def timeSince(since, percent):\n", 952 | " now = time.time()\n", 953 | " s = now - since\n", 954 | " es = s / (percent)\n", 955 | " rs = es - s\n", 956 | " return '%s (- %s)' % (asMinutes(s), asMinutes(rs))" 957 | ] 958 | }, 959 | { 960 | "cell_type": "markdown", 961 | "metadata": {}, 962 | "source": [ 963 | "The whole training process looks like this:\n", 964 | "\n", 965 | "- Start a timer\n", 966 | "- Initialize optimizers and criterion\n", 967 | "- Create set of training pairs\n", 968 | "- Start empty losses array for plotting\n", 969 | "\n", 970 | "Then we call ``train`` many times and occasionally print the progress (%\n", 971 | "of examples, time so far, estimated time) and average loss.\n", 972 | "\n", 973 | "\n" 974 | ] 975 | }, 976 | { 977 | "cell_type": "code", 978 | "execution_count": 159, 979 | "metadata": {}, 980 | "outputs": [], 981 | "source": [ 982 | "def trainIters(encoder, decoder, n_iters, print_every=1000, plot_every=100, learning_rate=0.01, attention=True):\n", 983 | " start = time.time()\n", 984 | " plot_losses = []\n", 985 | " print_loss_total = 0 # Reset every print_every\n", 986 | " plot_loss_total = 0 # Reset every plot_every\n", 987 | "\n", 988 | " encoder_optimizer = optim.SGD(encoder.parameters(), lr=learning_rate)\n", 989 | " decoder_optimizer = optim.SGD(decoder.parameters(), lr=learning_rate)\n", 990 | " \n", 991 | " #set the seed for repeatable expts\n", 992 | " random.seed(4)\n", 993 | " training_pairs = [tensorsFromPair(random.choice(trainPairs))\n", 994 | " for i in range(n_iters)]\n", 995 | "\n", 996 | " \n", 997 | " criterion = nn.NLLLoss()\n", 998 | "\n", 999 | " for iter in range(1, n_iters + 1):\n", 1000 | " training_pair = training_pairs[iter - 1]\n", 1001 | " input_tensor = training_pair[0]\n", 1002 | " target_tensor = training_pair[1]\n", 1003 | " \n", 1004 | " loss = train(input_tensor, target_tensor, encoder,\n", 1005 | " decoder, encoder_optimizer, decoder_optimizer, criterion, attention=attention)\n", 1006 | " print_loss_total += loss\n", 1007 | " plot_loss_total += loss\n", 1008 | " #print('debug: in trainIters')\n", 1009 | "\n", 1010 | " if iter % print_every == 0:\n", 1011 | " print_loss_avg = print_loss_total / print_every\n", 1012 | " print_loss_total = 0\n", 1013 | " print('%s (%d %d%%) %.4f' % (timeSince(start, iter / n_iters),\n", 1014 | " iter, iter / n_iters * 100, print_loss_avg))\n", 1015 | "\n", 1016 | " if iter % plot_every == 0:\n", 1017 | " plot_loss_avg = plot_loss_total / plot_every\n", 1018 | " plot_losses.append(plot_loss_avg)\n", 1019 | " plot_loss_total = 0\n", 1020 | "\n", 1021 | " showPlot(plot_losses)" 1022 | ] 1023 | }, 1024 | { 1025 | "cell_type": "markdown", 1026 | "metadata": {}, 1027 | "source": [ 1028 | "Plotting results\n", 1029 | "----------------\n", 1030 | "\n", 1031 | "Plotting is done with matplotlib, using the array of loss values\n", 1032 | "``plot_losses`` saved while training.\n", 1033 | "\n", 1034 | "\n" 1035 | ] 1036 | }, 1037 | { 1038 | "cell_type": "code", 1039 | "execution_count": 25, 1040 | "metadata": {}, 1041 | "outputs": [], 1042 | "source": [ 1043 | "import matplotlib.pyplot as plt\n", 1044 | "#plt.switch_backend('agg')\n", 1045 | "import matplotlib.ticker as ticker\n", 1046 | "import numpy as np\n", 1047 | "\n", 1048 | "\n", 1049 | "def showPlot(points):\n", 1050 | " plt.figure()\n", 1051 | " fig, ax = plt.subplots()\n", 1052 | " # this locator puts ticks at regular intervals\n", 1053 | " loc = ticker.MultipleLocator(base=0.2)\n", 1054 | " ax.yaxis.set_major_locator(loc)\n", 1055 | " plt.plot(points)" 1056 | ] 1057 | }, 1058 | { 1059 | "cell_type": "markdown", 1060 | "metadata": {}, 1061 | "source": [ 1062 | "Evaluation\n", 1063 | "==========\n", 1064 | "\n", 1065 | "Evaluation is mostly the same as training, but there are no targets so\n", 1066 | "we simply feed the decoder's predictions back to itself for each step.\n", 1067 | "Every time it predicts a word we add it to the output string, and if it\n", 1068 | "predicts the EOS token we stop there. We also store the decoder's\n", 1069 | "attention outputs for display later.\n", 1070 | "\n", 1071 | "\n" 1072 | ] 1073 | }, 1074 | { 1075 | "cell_type": "code", 1076 | "execution_count": 167, 1077 | "metadata": {}, 1078 | "outputs": [], 1079 | "source": [ 1080 | "def evaluate(encoder, decoder, sentence, max_length=MAX_LENGTH, attention=True):\n", 1081 | " print('debug: in evaluate')\n", 1082 | " # set in eval mode so that dropout does not cause random outputs in decoding\n", 1083 | " decoder = decoder.eval()\n", 1084 | " with torch.no_grad():\n", 1085 | " input_tensor = tensorFromSentence(input_lang, sentence)\n", 1086 | " input_length = input_tensor.size()[0]\n", 1087 | " encoder_hidden = encoder.initHidden()\n", 1088 | "\n", 1089 | " encoder_outputs = torch.zeros(max_length, encoder.hidden_size, device=device)\n", 1090 | "\n", 1091 | " for ei in range(input_length):\n", 1092 | " encoder_output, encoder_hidden = encoder(input_tensor[ei],\n", 1093 | " encoder_hidden)\n", 1094 | " encoder_outputs[ei] += encoder_output[0, 0]\n", 1095 | "\n", 1096 | " decoder_input = torch.tensor([[SOS_token]], device=device) # SOS\n", 1097 | "\n", 1098 | " #decoder_hidden = encoder_hidden\n", 1099 | " decoder_hidden = decoder.initHidden()\n", 1100 | " #decoder_hidden = encoder_outputs[0].unsqueeze(0).unsqueeze(0)\n", 1101 | "\n", 1102 | " decoded_words = []\n", 1103 | " decoder_attentions = torch.zeros(max_length, max_length)\n", 1104 | "\n", 1105 | " for di in range(max_length):\n", 1106 | " if attention:\n", 1107 | " decoder_output, decoder_hidden, decoder_attention = decoder(\n", 1108 | " decoder_input, decoder_hidden, encoder_outputs)\n", 1109 | " decoder_attentions[di] = decoder_attention.data\n", 1110 | " else:\n", 1111 | " decoder_output, decoder_hidden = decoder(decoder_input, decoder_hidden)\n", 1112 | "\n", 1113 | " topv, topi = decoder_output.data.topk(1)\n", 1114 | " if topi.item() == EOS_token:\n", 1115 | " decoded_words.append('')\n", 1116 | " break\n", 1117 | " else:\n", 1118 | " decoded_words.append(output_lang.index2word[topi.item()])\n", 1119 | "\n", 1120 | " decoder_input = topi.squeeze().detach()\n", 1121 | "\n", 1122 | " return decoded_words, decoder_attentions[:di + 1]" 1123 | ] 1124 | }, 1125 | { 1126 | "cell_type": "markdown", 1127 | "metadata": {}, 1128 | "source": [ 1129 | "We can evaluate random sentences from the training set and print out the\n", 1130 | "input, target, and output to make some subjective quality judgements:\n", 1131 | "\n", 1132 | "\n" 1133 | ] 1134 | }, 1135 | { 1136 | "cell_type": "code", 1137 | "execution_count": 168, 1138 | "metadata": {}, 1139 | "outputs": [], 1140 | "source": [ 1141 | "def evaluateRandomly(encoder, decoder, n=10, attention=True):\n", 1142 | " for i in range(n):\n", 1143 | " #pair = random.choice(trainPairs)\n", 1144 | " pair = trainPairs[i]\n", 1145 | " print('>', pair[0])\n", 1146 | " print('=', pair[1])\n", 1147 | " output_words, attentions = evaluate(encoder, decoder, pair[0], attention=attention)\n", 1148 | " output_sentence = ' '.join(output_words)\n", 1149 | " print('<', output_sentence)\n", 1150 | " print('')" 1151 | ] 1152 | }, 1153 | { 1154 | "cell_type": "code", 1155 | "execution_count": 169, 1156 | "metadata": {}, 1157 | "outputs": [], 1158 | "source": [ 1159 | "def evaluateTestSet(encoder, decoder, attention=True):\n", 1160 | " print('debug: in evaluateTestSet')\n", 1161 | " for pair in testPairs:\n", 1162 | " print('>', pair[0])\n", 1163 | " print('=', pair[1])\n", 1164 | " output_words, attentions = evaluate(encoder, decoder, pair[0], attention=attention)\n", 1165 | " output_sentence = ' '.join(output_words)\n", 1166 | " print('<', output_sentence)\n", 1167 | " print('')" 1168 | ] 1169 | }, 1170 | { 1171 | "cell_type": "markdown", 1172 | "metadata": {}, 1173 | "source": [ 1174 | "Training and Evaluating\n", 1175 | "=======================\n", 1176 | "\n", 1177 | "With all these helper functions in place (it looks like extra work, but\n", 1178 | "it makes it easier to run multiple experiments) we can actually\n", 1179 | "initialize a network and start training.\n", 1180 | "\n", 1181 | "Remember that the input sentences were heavily filtered. For this small\n", 1182 | "dataset we can use relatively small networks of 256 hidden nodes and a\n", 1183 | "single GRU layer. After about 40 minutes on a MacBook CPU we'll get some\n", 1184 | "reasonable results.\n", 1185 | "\n", 1186 | ".. Note::\n", 1187 | " If you run this notebook you can train, interrupt the kernel,\n", 1188 | " evaluate, and continue training later. Comment out the lines where the\n", 1189 | " encoder and decoder are initialized and run ``trainIters`` again.\n", 1190 | "\n", 1191 | "\n" 1192 | ] 1193 | }, 1194 | { 1195 | "cell_type": "code", 1196 | "execution_count": 191, 1197 | "metadata": { 1198 | "scrolled": true 1199 | }, 1200 | "outputs": [ 1201 | { 1202 | "name": "stdout", 1203 | "output_type": "stream", 1204 | "text": [ 1205 | "0m 27s (- 0m 27s) (1000 50%) 2.4112\n", 1206 | "0m 56s (- 0m 0s) (2000 100%) 2.1921\n" 1207 | ] 1208 | }, 1209 | { 1210 | "data": { 1211 | "text/plain": [ 1212 | "
" 1213 | ] 1214 | }, 1215 | "metadata": {}, 1216 | "output_type": "display_data" 1217 | }, 1218 | { 1219 | "data": { 1220 | "image/png": "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\n", 1221 | "text/plain": [ 1222 | "
" 1223 | ] 1224 | }, 1225 | "metadata": { 1226 | "needs_background": "light" 1227 | }, 1228 | "output_type": "display_data" 1229 | } 1230 | ], 1231 | "source": [ 1232 | "hidden_size = 256\n", 1233 | "encoder1 = EncoderRNN(input_lang.n_words, hidden_size).to(device)\n", 1234 | "decoder1 = AttnMultiplicativeDecoderRNN(hidden_size, output_lang.n_words, dropout_p=0.1).to(device)\n", 1235 | "#decoder1 = DecoderRNN(hidden_size, output_lang.n_words).to(device)\n", 1236 | "\n", 1237 | "#encoder1.load_state_dict(torch.load('encoder_params'))\n", 1238 | "#decoder1.load_state_dict(torch.load('decoder_params'))\n", 1239 | "trainIters(encoder1, decoder1, 25000, print_every=1000, learning_rate = 0.01, attention = True)\n", 1240 | "torch.save(encoder1.state_dict(), 'encoder_params')\n", 1241 | "torch.save(decoder1.state_dict(), 'decoder_params')" 1242 | ] 1243 | }, 1244 | { 1245 | "cell_type": "code", 1246 | "execution_count": 178, 1247 | "metadata": { 1248 | "scrolled": true 1249 | }, 1250 | "outputs": [ 1251 | { 1252 | "name": "stdout", 1253 | "output_type": "stream", 1254 | "text": [ 1255 | "Evaluating 10 random examples from training\n", 1256 | "> nous sommes tous etudiants .\n", 1257 | "= we re all students .\n", 1258 | "debug: in evaluate\n", 1259 | "< we re all students . \n", 1260 | "\n", 1261 | "> nous sommes perdus .\n", 1262 | "= we re lost .\n", 1263 | "debug: in evaluate\n", 1264 | "< we re lost . \n", 1265 | "\n", 1266 | "> nous sommes tous en danger .\n", 1267 | "= we re all at risk .\n", 1268 | "debug: in evaluate\n", 1269 | "< we re all at risk . \n", 1270 | "\n", 1271 | "> nous allons manger tout de suite .\n", 1272 | "= we re going to eat right now .\n", 1273 | "debug: in evaluate\n", 1274 | "< we re going to eat right now . \n", 1275 | "\n", 1276 | "> nous sommes toutes prisonnieres .\n", 1277 | "= we re all prisoners .\n", 1278 | "debug: in evaluate\n", 1279 | "< we re all prisoners . \n", 1280 | "\n", 1281 | "> nous sommes trop en retard .\n", 1282 | "= we re too late .\n", 1283 | "debug: in evaluate\n", 1284 | "< we re too late . \n", 1285 | "\n", 1286 | "> nous sommes en train de manger des pommes .\n", 1287 | "= we re eating apples .\n", 1288 | "debug: in evaluate\n", 1289 | "< we re eating apples . \n", 1290 | "\n", 1291 | "> nous y sommes bientot .\n", 1292 | "= we re almost there .\n", 1293 | "debug: in evaluate\n", 1294 | "< we re almost there . \n", 1295 | "\n", 1296 | "> nous sommes toutes en colere .\n", 1297 | "= we re all angry .\n", 1298 | "debug: in evaluate\n", 1299 | "< we re all angry . \n", 1300 | "\n", 1301 | "> nous sommes toutes occupees .\n", 1302 | "= we re all busy .\n", 1303 | "debug: in evaluate\n", 1304 | "< we re all busy . \n", 1305 | "\n" 1306 | ] 1307 | } 1308 | ], 1309 | "source": [ 1310 | "print(\"Evaluating 10 random examples from training\")\n", 1311 | "evaluateRandomly(encoder1, decoder1, attention=True)" 1312 | ] 1313 | }, 1314 | { 1315 | "cell_type": "code", 1316 | "execution_count": 179, 1317 | "metadata": {}, 1318 | "outputs": [ 1319 | { 1320 | "name": "stdout", 1321 | "output_type": "stream", 1322 | "text": [ 1323 | "Evaluating the 10 held out test examples\n", 1324 | "debug: in evaluateTestSet\n", 1325 | "> nous sommes riches .\n", 1326 | "= we re wealthy .\n", 1327 | "debug: in evaluate\n", 1328 | "< we re rich . \n", 1329 | "\n", 1330 | "> nous sommes de retour tot .\n", 1331 | "= we re back early .\n", 1332 | "debug: in evaluate\n", 1333 | "< we re back early . \n", 1334 | "\n", 1335 | "> nous sommes deprimes .\n", 1336 | "= we re depressed .\n", 1337 | "debug: in evaluate\n", 1338 | "< we re even . \n", 1339 | "\n", 1340 | "> nous contribuons .\n", 1341 | "= we re contributing .\n", 1342 | "debug: in evaluate\n", 1343 | "< we re watching . \n", 1344 | "\n", 1345 | "> nous sommes tres en retard .\n", 1346 | "= we re really late .\n", 1347 | "debug: in evaluate\n", 1348 | "< we re very late . \n", 1349 | "\n", 1350 | "> nous nous ennuyons tous .\n", 1351 | "= we re all bored .\n", 1352 | "debug: in evaluate\n", 1353 | "< we re all bored . \n", 1354 | "\n", 1355 | "> nous appartenons tous a la meme equipe .\n", 1356 | "= we re all on the same team .\n", 1357 | "debug: in evaluate\n", 1358 | "< we re all at the same team . \n", 1359 | "\n", 1360 | "> nous sommes plutot occupees ici .\n", 1361 | "= we re kind of busy here .\n", 1362 | "debug: in evaluate\n", 1363 | "< we re kind here . \n", 1364 | "\n", 1365 | "> nous ne sommes pas de la meme famille .\n", 1366 | "= we re not family .\n", 1367 | "debug: in evaluate\n", 1368 | "< we re not same same age . \n", 1369 | "\n", 1370 | "> nous ne sommes pas interesses .\n", 1371 | "= we re not interested .\n", 1372 | "debug: in evaluate\n", 1373 | "< we re not really . \n", 1374 | "\n" 1375 | ] 1376 | } 1377 | ], 1378 | "source": [ 1379 | "print(\"Evaluating the 10 held out test examples\")\n", 1380 | "evaluateTestSet(encoder1, decoder1, attention=True)" 1381 | ] 1382 | }, 1383 | { 1384 | "cell_type": "markdown", 1385 | "metadata": {}, 1386 | "source": [ 1387 | "Visualizing Attention\n", 1388 | "---------------------\n", 1389 | "\n", 1390 | "A useful property of the attention mechanism is its highly interpretable\n", 1391 | "outputs. Because it is used to weight specific encoder outputs of the\n", 1392 | "input sequence, we can imagine looking where the network is focused most\n", 1393 | "at each time step.\n", 1394 | "\n", 1395 | "You could simply run ``plt.matshow(attentions)`` to see attention output\n", 1396 | "displayed as a matrix, with the columns being input steps and rows being\n", 1397 | "output steps:\n", 1398 | "\n", 1399 | "\n" 1400 | ] 1401 | }, 1402 | { 1403 | "cell_type": "code", 1404 | "execution_count": 36, 1405 | "metadata": {}, 1406 | "outputs": [ 1407 | { 1408 | "name": "stdout", 1409 | "output_type": "stream", 1410 | "text": [ 1411 | "debug: in evaluate\n" 1412 | ] 1413 | }, 1414 | { 1415 | "ename": "KeyError", 1416 | "evalue": "u'venues'", 1417 | "output_type": "error", 1418 | "traceback": [ 1419 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 1420 | "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", 1421 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m output_words, attentions = evaluate(\n\u001b[0;32m----> 2\u001b[0;31m encoder1, attn_decoder1, \"nous sommes des nouvelles venues .\")\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0mplt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmatshow\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mattentions\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnumpy\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", 1422 | "\u001b[0;32m\u001b[0m in \u001b[0;36mevaluate\u001b[0;34m(encoder, decoder, sentence, max_length)\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mdecoder\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdecoder\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0meval\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mno_grad\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----> 6\u001b[0;31m \u001b[0minput_tensor\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtensorFromSentence\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minput_lang\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msentence\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 7\u001b[0m \u001b[0minput_length\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0minput_tensor\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[0mencoder_hidden\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mencoder\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minitHidden\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 1423 | "\u001b[0;32m\u001b[0m in \u001b[0;36mtensorFromSentence\u001b[0;34m(lang, sentence)\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mtensorFromSentence\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlang\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msentence\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 6\u001b[0;31m \u001b[0mindexes\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mindexesFromSentence\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlang\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msentence\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 7\u001b[0m \u001b[0mindexes\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mEOS_token\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtensor\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mindexes\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlong\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdevice\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdevice\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mview\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 1424 | "\u001b[0;32m\u001b[0m in \u001b[0;36mindexesFromSentence\u001b[0;34m(lang, sentence)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mindexesFromSentence\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlang\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msentence\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mlang\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mword2index\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mword\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mword\u001b[0m \u001b[0;32min\u001b[0m \u001b[0msentence\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msplit\u001b[0m\u001b[0;34m(\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[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mtensorFromSentence\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlang\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msentence\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 1425 | "\u001b[0;31mKeyError\u001b[0m: u'venues'" 1426 | ] 1427 | } 1428 | ], 1429 | "source": [ 1430 | "output_words, attentions = evaluate(\n", 1431 | " encoder1, decoder1, \"nous sommes des nouvelles venues .\")\n", 1432 | "plt.matshow(attentions.numpy())" 1433 | ] 1434 | }, 1435 | { 1436 | "cell_type": "markdown", 1437 | "metadata": {}, 1438 | "source": [ 1439 | "For a better viewing experience we will do the extra work of adding axes\n", 1440 | "and labels:\n", 1441 | "\n", 1442 | "\n" 1443 | ] 1444 | }, 1445 | { 1446 | "cell_type": "code", 1447 | "execution_count": 180, 1448 | "metadata": {}, 1449 | "outputs": [ 1450 | { 1451 | "name": "stdout", 1452 | "output_type": "stream", 1453 | "text": [ 1454 | "debug: in evaluate\n", 1455 | "input = nous sommes tous etudiants .\n", 1456 | "output = we re all students . \n" 1457 | ] 1458 | }, 1459 | { 1460 | "data": { 1461 | "image/png": "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\n", 1462 | "text/plain": [ 1463 | "
" 1464 | ] 1465 | }, 1466 | "metadata": { 1467 | "needs_background": "light" 1468 | }, 1469 | "output_type": "display_data" 1470 | }, 1471 | { 1472 | "name": "stdout", 1473 | "output_type": "stream", 1474 | "text": [ 1475 | "debug: in evaluate\n", 1476 | "input = nous sommes perdus .\n", 1477 | "output = we re lost . \n" 1478 | ] 1479 | }, 1480 | { 1481 | "data": { 1482 | "image/png": "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\n", 1483 | "text/plain": [ 1484 | "
" 1485 | ] 1486 | }, 1487 | "metadata": { 1488 | "needs_background": "light" 1489 | }, 1490 | "output_type": "display_data" 1491 | }, 1492 | { 1493 | "name": "stdout", 1494 | "output_type": "stream", 1495 | "text": [ 1496 | "debug: in evaluate\n", 1497 | "input = nous sommes tous en danger .\n", 1498 | "output = we re all at risk . \n" 1499 | ] 1500 | }, 1501 | { 1502 | "data": { 1503 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXgAAAENCAYAAAAFcn7UAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvIxREBQAAHFBJREFUeJzt3Xu4XVV97vHvm0AkQEA0eAUNWFCuQggoAhU12IAIh0pFxAsqYrVKleKtWvSAPT1i+1jrAUu8HVGPHvECUREsAoIIkhAgEC6VIpSoLQYQLCiQ7Ld/zLlhZbGz18q6zbnnfj8888mac809fmPvhN8ae8xxkW0iIqJ5ZlRdgYiIGI4k+IiIhkqCj4hoqCT4iIiGSoKPiGioJPiIiIZKgo+IaKgk+IiIhkqCHzJJm0maUb7eUdJhkjauul4R0XzKTNbhknQ1cACwFXAlsAx40PYxlVYsIhovLfjhk+0HgT8FPm37CGDniusUEdNAEvzwSdK+wDHA98trG1VYn4iYJpLgh+/dwAeB79heKWl74OKK6xQR00D64EdE0ma2H6i6HhExfaQFP2SS9pV0I3BTef58SWdUXK0pQdJMSe+puh7DosI5knaqui7RTEnww/ePwJ8AdwPYvg7440prNEXYXgscXnU9hujlwALguKorEs2UBD8Ctu9su7S2kopMTZdL+j+SDpA0f/youlID8haK5P5KSXnwHgOXf1TDd6ekFwGWNAs4gbK7JrryovLPU1quGXhpBXUZGElzgV1sny/pQuAI4OyKqxUNk4esQ1b+j/wpYCEg4IfAX9q+u9KKRaUknQhsavtjkvYGTrW9qOp6RbMkwUetSXoq8L+AZ9g+WNLOwL62P19x1foi6Xpgke1flufXAYdO0J0X0bMk+CGTtB3wLmAeLV1itg+rqk5TiaQfAF8EPmT7+WVf9TW2d6u4aj2T9ETgKNtntlw7CFht+5rqahZNkwQ/ZGXL7PPA9cDY+HXbP66sUlOIpKW295Z0je09y2vX2t6j6rpF1F0esg7fH2z/U9WVmMIekPRkigerSHohcF+1VeqdpLcCl9j+uSQBXwBeBdwOvDEt+BiktOCHTNJrgR0oHq4+NH7d9vLKKjWFlEMiPw3sCtwAbA0caXtFpRXrkaQbgD1tP1L+2/grivHwewIfsX1ApRWMRkkLfvh2A15PMaxvvItmyg/zGxXbyyW9GHguxSikW2w/UnG1+rGmpf6HAmeVI6oulHRahfWKBkqCH74jgO1tP1x1RaYiSX/admlHSfcB19u+q4o69WlM0tOBe4GXAX/b8t7saqoUTTUtE7yk5wCrbD8k6UBgd4qW1G+HEO464InAVExGdfAWYF8eW4HzQIqNU3aUdIrtL1dVsR6dTLHpy0xgie2VAOVvKbdVWbFonmnZBy/pWoo1QOYBFwBLgOfaPmQIsS6h+ABZyrp98Bkm2QVJ3wWOs/2f5flTgc9QTPG/1PauVdavF+VQzzm27225thnF/4//VV3NommmZQseGLO9RtIRwD/a/rSkYY1e+MiQyp0u5o0n99JdwI6275E0VfvinwT8haRdKJ7H3Aic0fZ9RvRtui429oiko4E3At8rrw1lI2zbPy7HvF9DMRZ+/BgoSadJ2kLSxpJ+JGm1pNcNOk4FLpP0PUlvlPRG4Fzg0rLFO4wutaGStB/Fb3MAZwFfKV//rHwvYmCmaxfNzsCfA1fY/lo52/Qo2/97CLGOB04Ffk8xikaAbW8/4DjX2t6j/K3kfwDvAS62/fxBxhm1cqz4q4D9KH52PwG+5Sn6D1fSlcDb28e7S9oDONP2C6qpWTTRtEzwoyTp5xRrp6wecpyVtneR9FmKBHi+pOumeoJvGkk32p5w0/XJ3ovoxbTsg5f0C8qZka0G3aou/Rvw4BDKbfddSTdT/KbwDklbA38YQdyhKodJfhx4CkULfvw3oC0qrVjvJGmr1ges5cUnMX27TGNIpmULvpz6Pm4T4M+AJ9k+eQix9qRYLOtnrDuK5oQhxNoKuN/2WkmbAlvY/o9BxxklSbcCr7TdiDX0yy67twInAeOzmfei+BD7QusCZBH9mpYJfiKSfmJ7/yGUexVFv3H7YmNfGnCcN0x03fZZg4wzapIut92oh4+SDgXeB7SOovmE7e9WWrFonOnaRdO65dsMijHxc4YUbo3tE4dUdqu9W15vQjFLcjnFSI2pbJmk/w+cw7q/AX27uir1x/b3eGz0VsTQTMsED/xDy+s1FCv5vXpIsS4ufy3/LusmqHsGGcT2u1rPJW0JTLVZnhPZguIZxstbrhmYkgle0jdsv7p8/XHb729574e2X77+r47YMOmiGbLygW67gQ+TnCDuxsAK2zsNM05smLZ17Zfbnj/RexGDMC1b8GXr9iPAH5eXfgycYnvg64zb3m7QZU6knNI//mk9E9gJ+MaQYm1N8aBwHuvuUvXmIcTahGI9ml0oup6GFmtEJmtRpbUVAzUtEzzFJgs38Fi3zOspRrq0r1zYt7Il/XYe+zC5hGJCy6Cn2f99y+s1wB22Vw04xrhzgcuAC4G1Q4ox7svAzcCfAKcAxwBTeUTNpuXIqhnA7PL1+PDPrCYZAzUtu2gm2vJtWNvASfocxTII46NmXg+stX3cEGI9lccetl41rOV0R7ll3ni3haQVtncvPzAvsD0l19OXdPFk79t+yajqEv1btGiRV6/uPIfx6quvvsD2ohFUaR3TtQX/e0n72/4JPLo+yO+HFGvvttmkF5X7tA6UpFcDn6D4DUHApyW91/Y3Bx0L+J6kQ2yfN4Sy243/pvNbSbsC/0HRNTQlJYE3y+rVq1m6dGnH+2bMmDF3BNV5nOma4N8OfKnsi4di84U3DinWWknPsf1vAJK2ZzjdGh+i+DC5q4yzNUUXyjAS/F8CH5T0MEUCHubs0sXlBK4PUyzrvDnwN0OIMzKSZlOsiHldy7VnUfxm98vqaha9GKtxL8h0TfA3AacBz6HYjOM+igW6hrHP50kUQyXHN3OYB7xpCHFmtHXJ3M3wpr5vSdEXvp3tU8rk9PRBBpDUOndg/Od1evnnZoOMVYE1wLcl7W77gfLa54C/BpLgpxADde7mnq4J/lyKpWaXM/z/oZ5MsWH0POBw4EUUHyiD9gNJFwBfK8+PAobVhXI6xazcl1I8+Pwd8C3WnWzVr/GJZ88ty11Snr8SuHSAcUau3HD7OxR/R18oPyC3tr2s4qrFBjOu8eCn6ZrgtxnhA4+/sX22pC2AgygmWX0GGPSysAbOBPan6DJZDLxwwDHGvcD2/PFNUmzfK2nWIAPY/p9QTP4B5tv+XXn+UeDsQcaqyOeAz1KM6HoDxSiumGoMa8fqm+Cn6+p1P5W024hijfe3vwL4Z9vnAgNNhqWDbH/b9om232P7O8DBQ4gDxYYpMynHbZf9/WOTf0nPngW0blj+MFP4Ies42zcDSNoROJpmzDqedkzRB9/pqMp0bcHvDxxbzjJ9iMceEu4+hFi/lHQmsBD4uKQnMMAPVklvB94BbC+p9RnCHODyQcVp80/Ad4CnSPpb4EiKh6DD8GXgqrJLw8ARPDbkdOgkPW2IK3J+nqIlv6J9+eCYOurcBz9dx8E/e6Lrtu8YQqxNgUXA9bZ/LunpwG62fzig8rcEtgL+DvhAy1u/G/R6N21xn0exoJmAHw1zOd9ycbgDytNL23dDGiZJ37f9iiGVvSnwa+BVti8cRowYrvl77eVLL+/cjpoze/bVtheMoErrmJYJPiJiEPacP98/7iLBb7npppUk+OnaRRMRMRB1biQnwUdE9MjA2hon+Ok6iuZR5VrtiTUFYjXxe0qsqRNnfWx3PKoy7RM8MMp/HIk1NeIk1tSKVWmCzzDJiIgmqriF3kmjEryknn7SvX5dYvUea9NNN3xdslmzNmGzzbbc4O/pac96xgbHmvu0p/GcnXba4Fi33XzzBseCev9dTYVYPcZZbXvrfuJmLZqICey0074ji/XBz5w6slhH7rPPyGLNmDFzZLGaaGxs7UDmvawdG9Yk7v4lwUdE9CyLjUVENJINNV5rLAk+IqIf6YOPiGioJPiIiAYaXy64rpLgIyJ6ZWcUTUREU6WLJiKigQwZJhkR0VR1HiZZ2WJjkt4n6YTy9SclXVS+fpmkr0h6uaQrJC2XdLakzauqa0TE+mQ1yYldymPbsC0ANpe0McV+qddT7PG50PZ8YBlwYiW1jIiYRJ0TfJVdNFcDe0maQ7Hx9XKKRH8AsATYGbhcEsAs4IqJCinXgq50udCImJ6cUTQTs/2IpNuBNwE/BVYALwGeA/wC+BfbR3dRzmJgMYx2lbyICKj3KJqqN/y4FDip/PMy4M+Ba4Ergf0k/REUu89L2rGyWkZETGB8olNdN/yoOsFfBjwduML2fwJ/AC6z/RvgWOBrklZQJPznVVbLiIj1cBf/VaXSYZK2fwRs3HK+Y8vri4C9q6hXRES3MkwyIqKBbDM2Ntbx6IakRZJukXSrpA9M8P6zJF0s6RpJKyQd0qnMJPiIiD4Mog9e0kzgdOBgihGER0vaue22DwPfsL0n8BrgjE7lJsFHRPRhQOPg9wFutX2b7YeBrwOHt4cCxjcz3hL4VadCs1RBREQfBjRM8pnAnS3nq4AXtN3zUeCHkt4FbAYs7FRoWvARET1yF90zZRfNXEnLWo72yZmaqPi286OB/2t7G+AQ4MuSJs3hacFHRPShy2GQq20vmOT9VcC2Lefb8PgumLcAiwBsXyFpE2AucNf6Ck0LPiKiRwbWjrnj0YWlwA6StpM0i+Ih6pK2e/4deBmApJ2ATYDfTFZoWvAREX0YRB+87TWS3glcAMwEvmB7paRTgGW2lwB/BXxW0nsoPluOdYfgSfDxqI03fsLIYs2aNXtksZZeePXIYk3clTocm2++1chiNdH9968eSDmDWorA9nnAeW3XTm55fSOw34aUmQQfEdGripcD7iQJPiKiR6beq0kmwUdE9KHK1SI7SYKPiOhDEnxERAONrwdfV0nwERG9ykPWiIjmSgs+IqKBMoomIqLB1na5oUcVkuAjInpW7Z6rndQ2wUsSINv1/XiMiGnNLo66qtVqkpLmSbpJ0hnAcuD1kq6QtFzS2ZI2r7qOERGtBrFl37DUKsGXngucBRxEsf7xQtvzgWXAiVVWLCKi3YC27BuKOnbR3GH7SkmHUmw+e3nRW8Ms4Ir2m8udUdp3R4mIGLpMdNpwD5R/CvgX20dPdrPtxcBiAEn1/UlHRPPYjNV4FE0du2jGXQnsJ+mPACRtKmnHiusUEbGu8Setkx0VqWMLHgDbv5F0LPA1SeM7UXwY+NfqahURsS53tyVfJWqV4G3fDuzacn4RsHdlFYqI6KDGXfD1SvAREVNJ0QNT3wyfBB8R0Yck+IiIRjJja+s7iiYJPiKiR+miiYhosCT4iIimSoKPiGimGuf3JPiIiJ45D1mjT/c+8EDnmwZg7hZbjiQOwMobLhtZrCuuOGdksUbp/vtXV12FaS9b9kVENFgSfEREQyXBR0Q0kQ1ZbCwiopnSgo+IaCADY2nBR0Q0UJYqiIhormz4ERHRSE4LPiKiqeqc4Ou86TaSbpc0t3z9X1XXJyKi1fhywZ2ObkhaJOkWSbdK+sB67nm1pBslrZT0/zqVmRZ8REQfvLb/FrykmcDpwEHAKmCppCW2b2y5Zwfgg8B+tu+V9JRO5damBS/pHElXl59Mx1ddn4iIbgyoBb8PcKvt22w/DHwdOLztnrcCp9u+t4x7V6dC69SCf7PteyTNpvj0+lbVFYqImNQGdMF08EzgzpbzVcAL2u7ZEUDS5cBM4KO2z5+s0Dol+BMkHVG+3hbYoZsvKlv7afFHRCW6TPBzJS1rOV9se3HLuSYquu18I4q8eCCwDXCZpF1t/3Z9QWuR4CUdCCwE9rX9oKRLgE26+dryh7S4LKe+j7MjonE2YLng1bYXTPL+KoqG7bhtgF9NcM+Vth8BfiHpFoqEv3R9hdalD35L4N4yuT8PeGHVFYqI6MjgtWMdjy4sBXaQtJ2kWcBrgCVt95wDvASgHF24I3DbZIXWJcGfD2wkaQVwKnBlxfWJiOhC5wes3bTwba8B3glcANwEfMP2SkmnSDqsvO0C4G5JNwIXA++1ffdk5daii8b2Q8DBE7w1r+WezUdWoYiILg1qnpPt84Dz2q6d3PLawInl0ZVaJPiIiKmqzjNZk+AjInpkZ7GxiIjGSgs+IqKRzNhYV6NkKpEEHxHRq2z4ERHRYOmDj4honmIma9W1WL8k+IiIPqSLJvqy1WabjSSONLqJzffdv3pksWbN6mpZo4F45JGHRhYrasBmrLulCCqRBB8R0Ye04CMiGmgDVpOsRBJ8RESvav6UNQk+IqJnA9vRaSiS4CMi+uD6PmNNgo+I6JnJUgUREU2Uh6wREQ2WBB8R0Uiu9XrwddmTdVKS/rrqOkREPE65mmS/e7IOy5RI8EASfETUk935qEjtumgknQNsC2wCfArYHpgt6Vpgpe1jqqxfRMQ4A2M17qKpXYIH3mz7HkmzgaXAi4F32t6j4npFRKwre7JusBMkHVG+3hbYYbKbJR0PHD/0WkVEPE5msnZN0oHAQmBf2w9KuoSiq2a9bC8GFpdfX9+fdEQ0UhJ897YE7i2T+/OAF5bXH5G0se1HKqxbRMTj1DnB120UzfnARpJWAKcCV5bXFwMrJH21sppFRLSxwWvHOh5VqVUL3vZDwMETvHUJ8P7R1iYiorMaN+DrleAjIqaWPGSNiGisJPiIiCZyEnxERCOZTHSKiGgo42z4ERHRQOmiiYhorhrn9yT4qUGjiaLRxAH44oUXjyzWAQf82chiXXTRV0YWK+ohffAREQ1U9z1Z67ZUQUTE1DHAHZ0kLZJ0i6RbJX1gkvuOlGRJCzqVmRZ8RETPzNgARtFImgmcDhwErAKWSlpi+8a2++YAJwA/66bctOAjIvrgMXc8urAPcKvt22w/DHwdOHyC+04FTgP+0E2hSfAREb0qOuEHsSfrM4E7W85XldceJWlPYFvb3+u2eumiiYjo0Xh+78JcSctazheXmxWNm2gI26MlS5oBfBI4dkPqlwQfEdGHLh+irrY92UPRVRRblI7bBvhVy/kcYFfgknI489OAJZIOs936wbGOJPiIiF7ZjA1mQ4+lwA6StgN+CbwGeO1jYXwfMHf8vNzO9KTJkjukDz4ioi+DGCZpew3wTuAC4CbgG7ZXSjpF0mG91i0t+IiIHg1yopPt84Dz2q6dvJ57D+ymzEpb8JLOk/TESd6/XdLc9b0fEVG1QU10GobKWvAqnhQcaru+a21GREyq62GQlRhpC17SPEk3SToDWA6slTRX0maSvi/pOkk3SDqq7etmSzpf0ltHWd+IiEkZPNb5qEoVLfjnAm+y/Q5Jt5fXFgG/sv0KAElbtty/OcWsrrNsn9VemKTjgeOHW+WIiIkNYqmCYamiD/4O21e2XbseWCjp45IOKIcEjTsX+OJEyR3A9mLbCzqMMY2IGLjxh6x17YOvIsE/0H7B9r8Ce1Ek+r+T1Prk+HLgYI1ysfKIiG4McDXJYajFOHhJzwAetP0V4O+B+S1vnwzcDZxRRd0iItav80JjVW4IUosED+wGXCXpWuBDwMfa3n83sImk00Zes4iIyQxmsbGhGOlDVtu3U6ynMH4+r3x5QXm03z+v5fRNQ6xaRERPTH2HSWYma0REj2wzNra26mqsVxJ8REQf6rwnaxJ8REQfkuAjIhoqCT4iooGKce71ncmaBB8R0Yck+IiIhkoXzQjNnDmab6nOf6m9GtXPDuD9rz12ZLFeeeRxI4u1xz0vHVmsX/zi+pHFaqL77vvNQMqpcy5oXIKPiBid9MFHRDSSnRZ8RERjJcFHRDSScY03/EiCj4jog0mCj4hopHTRREQ0UB6yRkQ0VrVb8nWSBB8R0YesBx8R0VBpwUdENFHFe652kgQfEdEjkz1ZIyIaK2vRDJGk44Hjq65HRExHGUUzVLYXA4sBJNX3Jx0RjTSWpQoiIpqneMZa3wQ/o+oKdEvSeZKeUXU9IiIe43Jf1smPqkyZFrztQ6quQ0TE46QPPiKimeo8THLKdNFERNTRoLpoJC2SdIukWyV9YIL3T5R0o6QVkn4k6dmdykyCj4jokW3GxtZ2PDqRNBM4HTgY2Bk4WtLObbddAyywvTvwTeC0TuUmwUdE9GFALfh9gFtt32b7YeDrwOFtcS62/WB5eiWwTadCk+AjIvowoAT/TODOlvNV5bX1eQvwg06F5iFrREQfukzgcyUtazlfXE7SHKeJip6oIEmvAxYAL+4UNAk+IqJnhu4mOq22vWCS91cB27acbwP8qv0mSQuBDwEvtv1Qp6BJ8BERPbJhbDAzWZcCO0jaDvgl8Brgta03SNoTOBNYZPuubgptXIKv87oQdbd27ZqRxVqz5uGRxVp2+UUji/W697xjZLE+/LY3jCxWrN8gZqraXiPpncAFwEzgC7ZXSjoFWGZ7CfAJYHPgbEkA/277sMnKbVyCj4gYHQ9sLRrb5wHntV07ueX1wg0tMwk+IqIPWS44IqKhkuAjIhqoWC44CT4iooGM3XkpgqokwUdE9CEt+IiIhkqCj4hopGy6HRHRSI3fk1XSJeUi9deWxzdb3jte0s3lcZWk/VveO1TSNZKuKxexf1u/dYmIGLXG7ckqaRawse0HykvH2F7Wds+hwNuA/W2vljQfOEfSPsDdwGJgH9urJD0BmFd+3Va27+3t24mIGCXjGi+PskEteEk7SfoH4BZgxw63vx94r+3VALaXA18C/gKYQ/Hhcnf53kO2bym/7ihJN0g6SdLWG1K/iIhRcxf/VaVjgpe0maQ3SfoJ8DngJmB329e03PbVli6aT5TXdgGubituGbCL7XuAJcAdkr4m6RhJMwBs/zPFtlWzgUslfbPcqzCbk0RE7dhjHY+qdNNF82tgBXCc7ZvXc8/jumjWQ5SL2Ns+TtJuwELgJOAg4NjyvTuBUyV9DFgEfJ7iw+JxK6dJOh44vovYEREDVfeZrN20io+kWJ/4O5JO7mYn79KNwF5t1+aX1wGwfb3tT1Ik91e13lj21Z8BfBo4G/jgREFsL7a9oMNi+hERQ9D5AWuVHwAdE7ztH9o+CtgfuA84V9KFkuZ1+NLTgI9LejKApD0oWuhnSNpc0oEt9+4B3FHe93JJK4CPAZcAO9t+t+2VG/B9RUSMxNjYWMejKl2PorF9N/Ap4FNl67p1AYavSvp9+Xq17YW2l0h6JvBTSQZ+B7zO9q8lzQHeJ+lM4PfAA5TdMxQPXl9p+46+vrOIiBGo8zj4noZJ2r6q5fWBk9z3GeAzE1z/HXDIer6m/cFsREQ9FZ3wVddivTKTNSKiR4ZKh0F2kgQfEdGHOo+iSYKPiOhD4/rgIyICwJWOkukkCT4iokd1n+iUBB8R0Yck+IiIRjKkDz4iopnqPExSdf71YkNJ+g3lkgcbYC6wegjVSaypGyexplasXuM823ZfS5JvtNHGnjPnSR3v++1v77q6ivWyGtWC7+UvS9KyUf3gE2tqxEmsqRVrlN9TO9uMja3tfGNFGpXgIyJGrc69IEnwERF9SIKvt8WJNWViNfF7SqypE2dCdU7wjXrIGhExSjNnbuTZm2ze8b4HHrwvD1kjIqYS24w5D1kjIhqpzr0gSfAREX1Igo+IaKRqN9XuJAk+IqIPWQ8+IqKBslxwRERjOS34iIimSoKPiGiodNFERDTTBbbndnHfqJZoXkeWKoiIaKgZVVcgIiKGIwk+IqKhkuAjIhoqCT4ioqGS4CMiGioJPiKioZLgIyIaKgk+IqKhkuAjIhrqvwGxRFu1kQRAcgAAAABJRU5ErkJggg==\n", 1504 | "text/plain": [ 1505 | "
" 1506 | ] 1507 | }, 1508 | "metadata": { 1509 | "needs_background": "light" 1510 | }, 1511 | "output_type": "display_data" 1512 | }, 1513 | { 1514 | "name": "stdout", 1515 | "output_type": "stream", 1516 | "text": [ 1517 | "debug: in evaluate\n", 1518 | "input = nous allons manger tout de suite .\n", 1519 | "output = we re going to eat right now . \n" 1520 | ] 1521 | }, 1522 | { 1523 | "data": { 1524 | "image/png": "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\n", 1525 | "text/plain": [ 1526 | "
" 1527 | ] 1528 | }, 1529 | "metadata": { 1530 | "needs_background": "light" 1531 | }, 1532 | "output_type": "display_data" 1533 | }, 1534 | { 1535 | "name": "stdout", 1536 | "output_type": "stream", 1537 | "text": [ 1538 | "debug: in evaluate\n", 1539 | "input = nous sommes toutes prisonnieres .\n", 1540 | "output = we re all prisoners . \n" 1541 | ] 1542 | }, 1543 | { 1544 | "data": { 1545 | "image/png": "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\n", 1546 | "text/plain": [ 1547 | "
" 1548 | ] 1549 | }, 1550 | "metadata": { 1551 | "needs_background": "light" 1552 | }, 1553 | "output_type": "display_data" 1554 | }, 1555 | { 1556 | "name": "stdout", 1557 | "output_type": "stream", 1558 | "text": [ 1559 | "debug: in evaluate\n", 1560 | "input = nous sommes trop en retard .\n", 1561 | "output = we re too late . \n" 1562 | ] 1563 | }, 1564 | { 1565 | "data": { 1566 | "image/png": "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\n", 1567 | "text/plain": [ 1568 | "
" 1569 | ] 1570 | }, 1571 | "metadata": { 1572 | "needs_background": "light" 1573 | }, 1574 | "output_type": "display_data" 1575 | }, 1576 | { 1577 | "name": "stdout", 1578 | "output_type": "stream", 1579 | "text": [ 1580 | "debug: in evaluate\n", 1581 | "input = nous sommes en train de manger des pommes .\n", 1582 | "output = we re eating apples . \n" 1583 | ] 1584 | }, 1585 | { 1586 | "data": { 1587 | "image/png": "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\n", 1588 | "text/plain": [ 1589 | "
" 1590 | ] 1591 | }, 1592 | "metadata": { 1593 | "needs_background": "light" 1594 | }, 1595 | "output_type": "display_data" 1596 | }, 1597 | { 1598 | "name": "stdout", 1599 | "output_type": "stream", 1600 | "text": [ 1601 | "debug: in evaluate\n", 1602 | "input = nous y sommes bientot .\n", 1603 | "output = we re almost there . \n" 1604 | ] 1605 | }, 1606 | { 1607 | "data": { 1608 | "image/png": "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\n", 1609 | "text/plain": [ 1610 | "
" 1611 | ] 1612 | }, 1613 | "metadata": { 1614 | "needs_background": "light" 1615 | }, 1616 | "output_type": "display_data" 1617 | }, 1618 | { 1619 | "name": "stdout", 1620 | "output_type": "stream", 1621 | "text": [ 1622 | "debug: in evaluate\n", 1623 | "input = nous sommes toutes en colere .\n", 1624 | "output = we re all angry . \n" 1625 | ] 1626 | }, 1627 | { 1628 | "data": { 1629 | "image/png": "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\n", 1630 | "text/plain": [ 1631 | "
" 1632 | ] 1633 | }, 1634 | "metadata": { 1635 | "needs_background": "light" 1636 | }, 1637 | "output_type": "display_data" 1638 | }, 1639 | { 1640 | "name": "stdout", 1641 | "output_type": "stream", 1642 | "text": [ 1643 | "debug: in evaluate\n", 1644 | "input = nous sommes toutes occupees .\n", 1645 | "output = we re all busy . \n" 1646 | ] 1647 | }, 1648 | { 1649 | "data": { 1650 | "image/png": "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\n", 1651 | "text/plain": [ 1652 | "
" 1653 | ] 1654 | }, 1655 | "metadata": { 1656 | "needs_background": "light" 1657 | }, 1658 | "output_type": "display_data" 1659 | } 1660 | ], 1661 | "source": [ 1662 | "def showAttention(input_sentence, output_words, attentions):\n", 1663 | " # Set up figure with colorbar\n", 1664 | " fig = plt.figure()\n", 1665 | " ax = fig.add_subplot(111)\n", 1666 | " cax = ax.matshow(attentions.numpy(), cmap='bone')\n", 1667 | " fig.colorbar(cax)\n", 1668 | "\n", 1669 | " # Set up axes\n", 1670 | " ax.set_xticklabels([''] + input_sentence.split(' ') +\n", 1671 | " [''], rotation=90)\n", 1672 | " ax.set_yticklabels([''] + output_words)\n", 1673 | "\n", 1674 | " # Show label at every tick\n", 1675 | " ax.xaxis.set_major_locator(ticker.MultipleLocator(1))\n", 1676 | " ax.yaxis.set_major_locator(ticker.MultipleLocator(1))\n", 1677 | "\n", 1678 | " plt.show()\n", 1679 | "\n", 1680 | "\n", 1681 | "def evaluateAndShowAttention(input_sentence):\n", 1682 | " output_words, attentions = evaluate(\n", 1683 | " encoder1, decoder1, input_sentence)\n", 1684 | " print('input =', input_sentence)\n", 1685 | " print('output =', ' '.join(output_words))\n", 1686 | " showAttention(input_sentence, output_words, attentions)\n", 1687 | "\n", 1688 | "#for pair in testPairs:\n", 1689 | "for i in range(10):\n", 1690 | " pair = trainPairs[i]\n", 1691 | " evaluateAndShowAttention(pair[0])\n", 1692 | "\n", 1693 | "#evaluateAndShowAttention(\"ils envoient de l aide .\")\n", 1694 | "#evaluateAndShowAttention(\"nous allons peindre le mur .\")\n", 1695 | "\n", 1696 | "#evaluateAndShowAttention(\"nous nous rejouissons de vous revoir .\")\n", 1697 | "\n", 1698 | "#evaluateAndShowAttention(\"ils sont sortis faire les magasins .\")\n", 1699 | "\n", 1700 | "#evaluateAndShowAttention(\"nous sommes sous son commandement .\")" 1701 | ] 1702 | }, 1703 | { 1704 | "cell_type": "markdown", 1705 | "metadata": {}, 1706 | "source": [ 1707 | "Exercises\n", 1708 | "=========\n", 1709 | "\n", 1710 | "- Try with a different dataset\n", 1711 | "\n", 1712 | " - Another language pair\n", 1713 | " - Human → Machine (e.g. IOT commands)\n", 1714 | " - Chat → Response\n", 1715 | " - Question → Answer\n", 1716 | "\n", 1717 | "- Replace the embeddings with pre-trained word embeddings such as word2vec or\n", 1718 | " GloVe\n", 1719 | "- Try with more layers, more hidden units, and more sentences. Compare\n", 1720 | " the training time and results.\n", 1721 | "- If you use a translation file where pairs have two of the same phrase\n", 1722 | " (``I am test \\t I am test``), you can use this as an autoencoder. Try\n", 1723 | " this:\n", 1724 | "\n", 1725 | " - Train as an autoencoder\n", 1726 | " - Save only the Encoder network\n", 1727 | " - Train a new Decoder for translation from there\n", 1728 | "\n", 1729 | "\n" 1730 | ] 1731 | }, 1732 | { 1733 | "cell_type": "markdown", 1734 | "metadata": {}, 1735 | "source": [ 1736 | "# Credits\n", 1737 | "The original notebook was written by Sean Robertson _" 1738 | ] 1739 | }, 1740 | { 1741 | "cell_type": "markdown", 1742 | "metadata": {}, 1743 | "source": [ 1744 | "# Recommended Reading:\n", 1745 | "\n", 1746 | "I assume you have at least installed PyTorch, know Python, and\n", 1747 | "understand Tensors:\n", 1748 | "\n", 1749 | "- https://pytorch.org/ For installation instructions\n", 1750 | "- :doc:`/beginner/deep_learning_60min_blitz` to get started with PyTorch in general\n", 1751 | "- :doc:`/beginner/pytorch_with_examples` for a wide and deep overview\n", 1752 | "- :doc:`/beginner/former_torchies_tutorial` if you are former Lua Torch user\n", 1753 | "\n", 1754 | "\n", 1755 | "It would also be useful to know about Sequence to Sequence networks and\n", 1756 | "how they work:\n", 1757 | "\n", 1758 | "- `Learning Phrase Representations using RNN Encoder-Decoder for\n", 1759 | " Statistical Machine Translation `__\n", 1760 | "- `Sequence to Sequence Learning with Neural\n", 1761 | " Networks `__\n", 1762 | "- `Neural Machine Translation by Jointly Learning to Align and\n", 1763 | " Translate `__\n", 1764 | "- `A Neural Conversational Model `__\n", 1765 | "\n", 1766 | "You will also find the previous tutorials on\n", 1767 | ":doc:`/intermediate/char_rnn_classification_tutorial`\n", 1768 | "and :doc:`/intermediate/char_rnn_generation_tutorial`\n", 1769 | "helpful as those concepts are very similar to the Encoder and Decoder\n", 1770 | "models, respectively.\n", 1771 | "\n", 1772 | "And for more, read the papers that introduced these topics:\n", 1773 | "\n", 1774 | "- `Learning Phrase Representations using RNN Encoder-Decoder for\n", 1775 | " Statistical Machine Translation `__\n", 1776 | "- `Sequence to Sequence Learning with Neural\n", 1777 | " Networks `__\n", 1778 | "- `Neural Machine Translation by Jointly Learning to Align and\n", 1779 | " Translate `__\n", 1780 | "- `A Neural Conversational Model `__" 1781 | ] 1782 | }, 1783 | { 1784 | "cell_type": "code", 1785 | "execution_count": null, 1786 | "metadata": {}, 1787 | "outputs": [], 1788 | "source": [] 1789 | } 1790 | ], 1791 | "metadata": { 1792 | "kernelspec": { 1793 | "display_name": "Python 2", 1794 | "language": "python", 1795 | "name": "python2" 1796 | }, 1797 | "language_info": { 1798 | "codemirror_mode": { 1799 | "name": "ipython", 1800 | "version": 2 1801 | }, 1802 | "file_extension": ".py", 1803 | "mimetype": "text/x-python", 1804 | "name": "python", 1805 | "nbconvert_exporter": "python", 1806 | "pygments_lexer": "ipython2", 1807 | "version": "2.7.15" 1808 | }, 1809 | "toc": { 1810 | "nav_menu": {}, 1811 | "number_sections": true, 1812 | "sideBar": true, 1813 | "skip_h1_title": false, 1814 | "toc_cell": false, 1815 | "toc_position": {}, 1816 | "toc_section_display": "block", 1817 | "toc_window_display": false 1818 | } 1819 | }, 1820 | "nbformat": 4, 1821 | "nbformat_minor": 1 1822 | } 1823 | --------------------------------------------------------------------------------