[go: up one dir, main page]

0% found this document useful (0 votes)
19 views55 pages

RNN LSTM From Scratch - Ipynb

This document provides a comprehensive guide on building Recurrent Neural Networks (RNNs) and Long Short-Term Memory networks (LSTMs) from scratch, primarily for educational purposes in a deep learning course. It covers the representation of sequential data, including token encoding methods like one-hot encoding, and outlines the process of generating a dataset for language modeling tasks. Additionally, it includes code examples for creating datasets, partitioning them, and implementing the necessary functions to facilitate training and evaluation of the models.

Uploaded by

ashketchum251276
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
19 views55 pages

RNN LSTM From Scratch - Ipynb

This document provides a comprehensive guide on building Recurrent Neural Networks (RNNs) and Long Short-Term Memory networks (LSTMs) from scratch, primarily for educational purposes in a deep learning course. It covers the representation of sequential data, including token encoding methods like one-hot encoding, and outlines the process of generating a dataset for language modeling tasks. Additionally, it includes code examples for creating datasets, partitioning them, and implementing the necessary functions to facilitate training and evaluation of the models.

Uploaded by

ashketchum251276
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 55

{

"cells": [
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "y-CptVs7iACc"
},
"source": [
"# How to build RNNs and LSTMs from scratch\n",
"\n",
"Originally developed by me (Nicklas Hansen), Peter Christensen and Alexander
Johansen as educational material for the graduate deep learning course at the
Technical University of Denmark (DTU). You can access the full course material
[here](https://github.com/DeepLearningDTU/02456-deep-learning-with-PyTorch).\n",
"____\n",
"\n",
"In this lab we will introduce different ways of learning from sequential
data.\n",
"As an example, we will train a neural network to do language modelling, i.e.
predict the next token in a sentence. In the context of natural language processing
a token could be a character or a word, but mind you that the concepts introduced
here apply to all kinds of sequential data, such as e.g. protein sequences, weather
measurements, audio signals or monetary transaction history, just to name a few.\
n",
"\n",
"To really get a grasp of what is going on inside the recurrent neural networks
that we are about to teach you, we will carry out a substantial part of this
exercise in numpy rather than PyTorch. Once you get a hold of it, we will proceed
to the PyTorch implementation.\n",
"\n",
"In this notebook we will show you:\n",
"* How to represent categorical variables in networks\n",
"* How to build a recurrent neural network (RNN) from scratch\n",
"* How to build a LSTM network from scratch\n",
"* How to build a LSTM network in PyTorch"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "XapO8SLwiACd"
},
"source": [
"## Representing tokens or text\n",
"\n",
"In previous labs we mainly considered data $x \\in \\mathrm{R}^d$, where $d$
is the feature space dimension.\n",
"With time sequences our data can be represented as $x \\in \\
mathrm{R}^{t \\, \\times \\, d}$, where $t$ is the sequence length. \n",
"This emphasises sequence dependence and that the samples along the sequence
are not independent and identically distributed (i.i.d.).\n",
"We will model functions as $\\mathrm{R}^{t \\, \\times \\, d} \\rightarrow \\
mathrm{R}^c$, where $c$ is the amount of classes in the output.\n",
"\n",
"There are several ways to represent sequences. With text, the challenge is how
to represent a word as a feature vector in $d$ dimensions, as we are required to
represent text with decimal numbers in order to apply neural networks to it.\n",
"\n",
"In this exercise we will use a simple one-hot encoding but for categorical
variables that can take on many values (e.g. words in the English language) this
may be infeasible. For such scenarios, you can project the encodings into a smaller
space by use of embeddings. If you want to learn more about tokens, encodings and
embeddings than what is covered in this exercise, we highly recommend [this
lecture](https://www.youtube.com/watch?
v=kEMJRjEdNzM&list=PLoROMvodv4rOhcuXMZkNm7j3fVwBBY42z)."
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "bdA4LPsFiACe"
},
"source": [
"### One-hot encoding over vocabulary\n",
"\n",
"One way to represent a fixed amount of words is by making a one-hot encoded
vector, which consists of 0s in all cells with the exception of a single 1 in a
cell used uniquely to identify each word.\n",
"\n",
"| vocabulary | one-hot encoded vector |\n",
"| ------------- |--------------------------|\n",
"| Paris | $= [1, 0, 0, \\ldots, 0]$ |\n",
"| Rome | $= [0, 1, 0, \\ldots, 0]$ |\n",
"| Copenhagen | $= [0, 0, 1, \\ldots, 0]$ |\n",
"\n",
"Representing a large vocabulary with one-hot encodings often becomes
inefficient because of the size of each sparse vector.\n",
"To overcome this challenge it is common practice to truncate the vocabulary to
contain the $k$ most used words and represent the rest with a special symbol, $\\
mathtt{UNK}$, to define unknown/unimportant words.\n",
"This often causes entities such as names to be represented with $\\mathtt{UNK}
$ because they are rare.\n",
"\n",
"Consider the following text\n",
"> I love the corny jokes in Spielberg's new movie.\n",
"\n",
"where an example result would be similar to\n",
"> I love the corny jokes in $\\mathtt{UNK}$'s new movie."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Generating a dataset"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For this exercise we will create a simple dataset that we can learn from. We
generate sequences of the form:\n",
"\n",
"`a a a a b b b b EOS`, `a a b b EOS`, `a a a a a b b b b b EOS`\n",
"\n",
"where `EOS` is a special character denoting the end of a sequence. The task is
to predict the next token $t_n$, i.e. `a`, `b`, `EOS` or the unknown token `UNK`
given the sequence of tokens $\\{ t_{1}, t_{2}, \\dots , t_{n-1}\\}$ and we are to
process sequences in a sequential manner. As such, the network will need to learn
that e.g. 5 `b`s and an `EOS` token will occur following 5 `a`s."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"A single sample from the generated dataset:\n",
"['a', 'a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b', 'b',
'EOS']\n"
]
}
],
"source": [
"import numpy as np\n",
"\n",
"# Set seed such that we always get the same dataset\n",
"np.random.seed(42)\n",
"\n",
"def generate_dataset(num_sequences=100):\n",
" \"\"\"\n",
" Generates a number of sequences as our dataset.\n",
" \n",
" Args:\n",
" `num_sequences`: the number of sequences to be generated.\n",
" \n",
" Returns a list of sequences.\n",
" \"\"\"\n",
" samples = []\n",
" \n",
" for _ in range(num_sequences): \n",
" num_tokens = np.random.randint(1, 10)\n",
" sample = ['a'] * num_tokens + ['b'] * num_tokens + ['EOS']\n",
" samples.append(sample)\n",
" \n",
" return samples\n",
"\n",
"\n",
"sequences = generate_dataset()\n",
"\n",
"print('A single sample from the generated dataset:')\n",
"print(sequences[0])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Representing tokens as indices"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To build a one-hot encoding, we need to assign each possible word in our
vocabulary an index. We do that by creating two dictionaries: one that allows us to
go from a given word to its corresponding index in our vocabulary, and one for the
reverse direction. Let's call them `word_to_idx` and `idx_to_word`. The keyword
`num_words` specifies the maximum size of our vocabulary. If we try to access a
word that does not exist in our vocabulary, it is automatically replaced by the
`UNK` token or its corresponding index."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"We have 100 sentences and 4 unique tokens in our dataset (including UNK).\
n",
"\n",
"The index of 'b' is 1\n",
"The word corresponding to index 1 is 'b'\n"
]
}
],
"source": [
"from collections import defaultdict\n",
"\n",
"def sequences_to_dicts(sequences):\n",
" \"\"\"\n",
" Creates word_to_idx and idx_to_word dictionaries for a list of sequences.\
n",
" \"\"\"\n",
" # A bit of Python-magic to flatten a nested list\n",
" flatten = lambda l: [item for sublist in l for item in sublist]\n",
" \n",
" # Flatten the dataset\n",
" all_words = flatten(sequences)\n",
" \n",
" # Count number of word occurences\n",
" word_count = defaultdict(int)\n",
" for word in flatten(sequences):\n",
" word_count[word] += 1\n",
"\n",
" # Sort by frequency\n",
" word_count = sorted(list(word_count.items()), key=lambda l: -l[1])\n",
"\n",
" # Create a list of all unique words\n",
" unique_words = [item[0] for item in word_count]\n",
" \n",
" # Add UNK token to list of words\n",
" unique_words.append('UNK')\n",
"\n",
" # Count number of sequences and number of unique words\n",
" num_sentences, vocab_size = len(sequences), len(unique_words)\n",
"\n",
" # Create dictionaries so that we can go from word to index and back\n",
" # If a word is not in our vocabulary, we assign it to token 'UNK'\n",
" word_to_idx = defaultdict(lambda: num_words)\n",
" idx_to_word = defaultdict(lambda: 'UNK')\n",
"\n",
" # Fill dictionaries\n",
" for idx, word in enumerate(unique_words):\n",
" # YOUR CODE HERE!\n",
" word_to_idx[word] = idx\n",
" idx_to_word[idx] = word\n",
"\n",
" return word_to_idx, idx_to_word, num_sentences, vocab_size\n",
"\n",
"\n",
"word_to_idx, idx_to_word, num_sequences, vocab_size =
sequences_to_dicts(sequences)\n",
"\n",
"print(f'We have {num_sequences} sentences and {len(word_to_idx)} unique tokens
in our dataset (including UNK).\\n')\n",
"print('The index of \\'b\\' is', word_to_idx['b'])\n",
"print(f'The word corresponding to index 1 is \\'{idx_to_word[1]}\\'')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercise:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Complete the `sequences_to_dicts` function above. You will need to fill the
`word_to_idx` and `idx_to_word` dictionaries so that we can go back and forth
between the two representations."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Partitioning the dataset"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To build our dataset, we need to create inputs and targets for each sequences
and partition sentences it into training, validation and test sets. 80%, 10% and
10% is a common distribution, but mind you that this largely depends on the size of
the dataset. Since we are doing next-word predictions, our target sequence is
simply the input sequence shifted by one word.\n",
"\n",
"We can use PyTorch's `Dataset` class to build a simple dataset where we can
easily retrieve (inputs, targets) pairs for each of our sequences."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"We have 80 samples in the training set.\n",
"We have 10 samples in the validation set.\n",
"We have 10 samples in the test set.\n"
]
}
],
"source": [
"from torch.utils import data\n",
"\n",
"class Dataset(data.Dataset):\n",
" def __init__(self, inputs, targets):\n",
" self.inputs = inputs\n",
" self.targets = targets\n",
"\n",
" def __len__(self):\n",
" # Return the size of the dataset\n",
" return len(self.targets)\n",
"\n",
" def __getitem__(self, index):\n",
" # Retrieve inputs and targets at the given index\n",
" X = self.inputs[index]\n",
" y = self.targets[index]\n",
"\n",
" return X, y\n",
"\n",
" \n",
"def create_datasets(sequences, dataset_class, p_train=0.8, p_val=0.1,
p_test=0.1):\n",
" # Define partition sizes\n",
" num_train = int(len(sequences)*p_train)\n",
" num_val = int(len(sequences)*p_val)\n",
" num_test = int(len(sequences)*p_test)\n",
"\n",
" # Split sequences into partitions\n",
" sequences_train = sequences[:num_train]\n",
" sequences_val = sequences[num_train:num_train+num_val]\n",
" sequences_test = sequences[-num_test:]\n",
"\n",
" def get_inputs_targets_from_sequences(sequences):\n",
" # Define empty lists\n",
" inputs, targets = [], []\n",
" \n",
" # Append inputs and targets s.t. both lists contain L-1 words of a
sentence of length L\n",
" # but targets are shifted right by one so that we can predict the next
word\n",
" for sequence in sequences:\n",
" inputs.append(sequence[:-1])\n",
" targets.append(sequence[1:])\n",
" \n",
" return inputs, targets\n",
"\n",
" # Get inputs and targets for each partition\n",
" inputs_train, targets_train =
get_inputs_targets_from_sequences(sequences_train)\n",
" inputs_val, targets_val =
get_inputs_targets_from_sequences(sequences_val)\n",
" inputs_test, targets_test =
get_inputs_targets_from_sequences(sequences_test)\n",
"\n",
" # Create datasets\n",
" training_set = dataset_class(inputs_train, targets_train)\n",
" validation_set = dataset_class(inputs_val, targets_val)\n",
" test_set = dataset_class(inputs_test, targets_test)\n",
"\n",
" return training_set, validation_set, test_set\n",
" \n",
"\n",
"training_set, validation_set, test_set = create_datasets(sequences, Dataset)\
n",
"\n",
"print(f'We have {len(training_set)} samples in the training set.')\n",
"print(f'We have {len(validation_set)} samples in the validation set.')\n",
"print(f'We have {len(test_set)} samples in the test set.')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When working with more complex data than what we use in this exercise,
creating a PyTorch `DataLoader` on top of the dataset can be beneficial. A data
loader is basically a fancy generator/iterator that we can use to abstract away all
of the data handling and pre-processing + it's super useful for processing batches
of data as well! Data loaders will come in handy later when you start to work on
your projects, so be sure to check them out!\n",
"\n",
"For more information on how to use datasets and data loaders in PyTorch,
[consult the official
guide](https://pytorch.org/tutorials/beginner/data_loading_tutorial.html)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## One-hot encodings"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We now create a simple function that returns the one-hot encoded
representation of a given index of a word in our vocabulary. Notice that the shape
of the one-hot encoding is equal to the vocabulary (which can be huge!).
Additionally, we define a function to automatically one-hot encode a sentence."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Our one-hot encoding of 'a' has shape (4,).\n",
"Our one-hot encoding of 'a b' has shape (2, 4, 1).\n"
]
}
],
"source": [
"def one_hot_encode(idx, vocab_size):\n",
" \"\"\"\n",
" One-hot encodes a single word given its index and the size of the
vocabulary.\n",
" \n",
" Args:\n",
" `idx`: the index of the given word\n",
" `vocab_size`: the size of the vocabulary\n",
" \n",
" Returns a 1-D numpy array of length `vocab_size`.\n",
" \"\"\"\n",
" # Initialize the encoded array\n",
" one_hot = np.zeros(vocab_size)\n",
" \n",
" # Set the appropriate element to one\n",
" one_hot[idx] = 1.0\n",
"\n",
" return one_hot\n",
"\n",
"\n",
"def one_hot_encode_sequence(sequence, vocab_size):\n",
" \"\"\"\n",
" One-hot encodes a sequence of words given a fixed vocabulary size.\n",
" \n",
" Args:\n",
" `sentence`: a list of words to encode\n",
" `vocab_size`: the size of the vocabulary\n",
" \n",
" Returns a 3-D numpy array of shape (num words, vocab size, 1).\n",
" \"\"\"\n",
" # Encode each word in the sentence\n",
" encoding = np.array([one_hot_encode(word_to_idx[word], vocab_size) for
word in sequence])\n",
"\n",
" # Reshape encoding s.t. it has shape (num words, vocab size, 1)\n",
" encoding = encoding.reshape(encoding.shape[0], encoding.shape[1], 1)\n",
" \n",
" return encoding\n",
"\n",
"\n",
"test_word = one_hot_encode(word_to_idx['a'], vocab_size)\n",
"print(f'Our one-hot encoding of \\'a\\' has shape {test_word.shape}.')\n",
"\n",
"test_sentence = one_hot_encode_sequence(['a', 'b'], vocab_size)\n",
"print(f'Our one-hot encoding of \\'a b\\' has shape {test_sentence.shape}.')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Great! Now that we have our one-hot encodings in place, we can move on to the
RNNs!"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "MA6bxjGWjeSB"
},
"source": [
"# Introduction to Recurrent Neural Networks (RNN)\n",
"\n",
"Reading material: [blog post](http://karpathy.github.io/2015/05/21/rnn-
effectiveness/) and (optionally) [this lecture](https://www.youtube.com/watch?
v=iWea12EAu6U&list=PLoROMvodv4rOhcuXMZkNm7j3fVwBBY42z).\n",
"\n",
"___\n",
"\n",
"A recurrent neural network (RNN) is a type of neural network that has been
succesful in modelling sequential data, e.g. language, speech, protein sequences,
etc.\n",
"\n",
"A RNN performs its computations in a cyclic manner, where the same computation
is applied to every sample of a given sequence.\n",
"The idea is that the network should be able to use the previous computations
as some form of memory and apply this to future computations.\n",
"An image may best explain how this is to be understood,\n",
"\n",
"![rnn-unroll image](https://github.com/DeepLearningDTU/02456-deep-learning-
with-PyTorch/blob/master/static_files/rnn-unfold.png?raw=1)\n",
"\n",
"\n",
"where it the network contains the following elements:\n",
"\n",
"- $x$ is the input sequence of samples, \n",
"- $U$ is a weight matrix applied to the given input sample,\n",
"- $V$ is a weight matrix used for the recurrent computation in order to pass
memory along the sequence,\n",
"- $W$ is a weight matrix used to compute the output of the every timestep
(given that every timestep requires an output),\n",
"- $h$ is the hidden state (the network's memory) for a given time step, and\
n",
"- $o$ is the resulting output.\n",
"\n",
"When the network is unrolled as shown, it is easier to refer to a timestep,
$t$.\n",
"We have the following computations through the network:\n",
"\n",
"- $h_t = f(U\\,{x_t} + V\\,{h_{t-1}})$, where $f$ usually is an activation
function, e.g. $\\mathrm{tanh}$.\n",
"- $o_t = \\mathrm{softmax}(W\\,{h_t})$"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "GuvwbvsGz9KE"
},
"source": [
"## Implementing a RNN"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We will implement the forward pass, backward pass, optimization and training
loop for a RNN in numpy so that you can get familiar with the recurrent nature of
RNNs. Later, we will go back to PyTorch and appreciate how convenient the
implementation becomes."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's define the necessary model parameters. Recall that an $n \\times m$
weight matrix maps $\\mathbb{R}^{m} \\rightarrow \\mathbb{R}^{n}$."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"hidden_size = 50 # Number of dimensions in the hidden state\n",
"vocab_size = len(word_to_idx) # Size of the vocabulary used\n",
"\n",
"def init_orthogonal(param):\n",
" \"\"\"\n",
" Initializes weight parameters orthogonally.\n",
" \n",
" Refer to this paper for an explanation of this initialization:\n",
" https://arxiv.org/abs/1312.6120\n",
" \"\"\"\n",
" if param.ndim < 2:\n",
" raise ValueError(\"Only parameters with 2 or more dimensions are
supported.\")\n",
"\n",
" rows, cols = param.shape\n",
" \n",
" new_param = np.random.randn(rows, cols)\n",
" \n",
" if rows < cols:\n",
" new_param = new_param.T\n",
" \n",
" # Compute QR factorization\n",
" q, r = np.linalg.qr(new_param)\n",
" \n",
" # Make Q uniform according to https://arxiv.org/pdf/math-ph/0609050.pdf\
n",
" d = np.diag(r, 0)\n",
" ph = np.sign(d)\n",
" q *= ph\n",
"\n",
" if rows < cols:\n",
" q = q.T\n",
" \n",
" new_param = q\n",
" \n",
" return new_param\n",
"\n",
"\n",
"def init_rnn(hidden_size, vocab_size):\n",
" \"\"\"\n",
" Initializes our recurrent neural network.\n",
" \n",
" Args:\n",
" `hidden_size`: the dimensions of the hidden state\n",
" `vocab_size`: the dimensions of our vocabulary\n",
" \"\"\"\n",
" # Weight matrix (input to hidden state)\n",
" # YOUR CODE HERE!\n",
" U = np.zeros((hidden_size, vocab_size))\n",
"\n",
" # Weight matrix (recurrent computation)\n",
" # YOUR CODE HERE!\n",
" V = np.zeros((hidden_size, hidden_size))\n",
"\n",
" # Weight matrix (hidden state to output)\n",
" # YOUR CODE HERE!\n",
" W = np.zeros((vocab_size, hidden_size))\n",
"\n",
" # Bias (hidden state)\n",
" # YOUR CODE HERE!\n",
" b_hidden = np.zeros((hidden_size, 1))\n",
"\n",
" # Bias (output)\n",
" # YOUR CODE HERE!\n",
" b_out = np.zeros((vocab_size, 1))\n",
" \n",
" # Initialize weights\n",
" U = init_orthogonal(U)\n",
" V = init_orthogonal(V)\n",
" W = init_orthogonal(W)\n",
" \n",
" # Return parameters as a tuple\n",
" return U, V, W, b_hidden, b_out\n",
"\n",
"\n",
"params = init_rnn(hidden_size=hidden_size, vocab_size=vocab_size)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercise:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Define the appropriate shape of the weights and biases in the `init_rnn`
function.\n",
"\n",
"You only have to worry about the weight and bias dimensions; you can leave the
scaling as is. Refer to the equations and figure above if you're in doubt here."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Function definitions"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Below we have defined the `sigmoid`, `tanh` and `softmax` functions. You might
need them in a bit!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Sigmoid activation"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"def sigmoid(x, derivative=False):\n",
" \"\"\"\n",
" Computes the element-wise sigmoid activation function for an array x.\n",
"\n",
" Args:\n",
" `x`: the array where the function is applied\n",
" `derivative`: if set to True will return the derivative instead of the
forward pass\n",
" \"\"\"\n",
" x_safe = x + 1e-12\n",
" f = 1 / (1 + np.exp(-x_safe))\n",
" \n",
" if derivative: # Return the derivative of the function evaluated at x\n",
" return f * (1 - f)\n",
" else: # Return the forward pass of the function at x\n",
" return f"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Hyperbolic Tangent activation"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"def tanh(x, derivative=False):\n",
" \"\"\"\n",
" Computes the element-wise tanh activation function for an array x.\n",
"\n",
" Args:\n",
" `x`: the array where the function is applied\n",
" `derivative`: if set to True will return the derivative instead of the
forward pass\n",
" \"\"\"\n",
" x_safe = x + 1e-12\n",
" f = (np.exp(x_safe)-np.exp(-x_safe))/(np.exp(x_safe)+np.exp(-x_safe))\n",
" \n",
" if derivative: # Return the derivative of the function evaluated at x\n",
" return 1-f**2\n",
" else: # Return the forward pass of the function at x\n",
" return f"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Softmax"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"def softmax(x, derivative=False):\n",
" \"\"\"\n",
" Computes the softmax for an array x.\n",
" \n",
" Args:\n",
" `x`: the array where the function is applied\n",
" `derivative`: if set to True will return the derivative instead of the
forward pass\n",
" \"\"\"\n",
" x_safe = x + 1e-12\n",
" f = np.exp(x_safe) / np.sum(np.exp(x_safe))\n",
" \n",
" if derivative: # Return the derivative of the function evaluated at x\n",
" pass # We will not need this one\n",
" else: # Return the forward pass of the function at x\n",
" return f"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "WFUtyOtesrfe"
},
"source": [
"### Implement the forward pass"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now that we have all the definitions in place, we can start to implement a
forward pass."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"colab": {},
"colab_type": "code",
"id": "RNZ3IVjs4jFB"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Input sequence:\n",
"['a', 'a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b', 'b']\n",
"\n",
"Target sequence:\n",
"['a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'EOS']\n",
"\n",
"Predicted sequence:\n",
"['UNK', 'UNK', 'UNK', 'b', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'EOS', 'EOS',
'b']\n"
]
}
],
"source": [
"def forward_pass(inputs, hidden_state, params):\n",
" \"\"\"\n",
" Computes the forward pass of a vanilla RNN.\n",
" \n",
" Args:\n",
" `inputs`: sequence of inputs to be processed\n",
" `hidden_state`: an already initialized hidden state\n",
" `params`: the parameters of the RNN\n",
" \"\"\"\n",
" # First we unpack our parameters\n",
" U, V, W, b_hidden, b_out = params\n",
" \n",
" # Create a list to store outputs and hidden states\n",
" outputs, hidden_states = [], []\n",
" \n",
" # For each element in input sequence\n",
" for t in range(len(inputs)):\n",
"\n",
" # Compute new hidden state\n",
" # YOUR CODE HERE!\n",
" hidden_state = tanh(np.dot(U, inputs[t]) + np.dot(V, hidden_state) +
b_hidden)\n",
"\n",
" # Compute output\n",
" # YOUR CODE HERE!\n",
" out = softmax(np.dot(W, hidden_state) + b_out)\n",
" \n",
" # Save results and continue\n",
" outputs.append(out)\n",
" hidden_states.append(hidden_state.copy())\n",
" \n",
" return outputs, hidden_states\n",
"\n",
"\n",
"# Get first sequence in training set\n",
"test_input_sequence, test_target_sequence = training_set[0]\n",
"\n",
"# One-hot encode input and target sequence\n",
"test_input = one_hot_encode_sequence(test_input_sequence, vocab_size)\n",
"test_target = one_hot_encode_sequence(test_target_sequence, vocab_size)\n",
"\n",
"# Initialize hidden state as zeros\n",
"hidden_state = np.zeros((hidden_size, 1))\n",
"\n",
"# Now let's try out our new function\n",
"outputs, hidden_states = forward_pass(test_input, hidden_state, params)\n",
"\n",
"print('Input sequence:')\n",
"print(test_input_sequence)\n",
"\n",
"print('\\nTarget sequence:')\n",
"print(test_target_sequence)\n",
"\n",
"print('\\nPredicted sequence:')\n",
"print([idx_to_word[np.argmax(output)] for output in outputs])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercise:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Implement the forward pass in the code above. Refer to the equations and the
figure if you're in doubt."
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "Vug6QCohsx_S"
},
"source": [
"### Implement the backward pass"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"colab": {},
"colab_type": "code",
"id": "roXK0HW6s2Z_"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"We get a loss of:\n",
"4.553408581307239\n"
]
}
],
"source": [
"def clip_gradient_norm(grads, max_norm=0.25):\n",
" \"\"\"\n",
" Clips gradients to have a maximum norm of `max_norm`.\n",
" This is to prevent the exploding gradients problem.\n",
" \"\"\" \n",
" # Set the maximum of the norm to be of type float\n",
" max_norm = float(max_norm)\n",
" total_norm = 0\n",
" \n",
" # Calculate the L2 norm squared for each gradient and add them to the
total norm\n",
" for grad in grads:\n",
" grad_norm = np.sum(np.power(grad, 2))\n",
" total_norm += grad_norm\n",
" \n",
" total_norm = np.sqrt(total_norm)\n",
" \n",
" # Calculate clipping coeficient\n",
" clip_coef = max_norm / (total_norm + 1e-6)\n",
" \n",
" # If the total norm is larger than the maximum allowable norm, then clip
the gradient\n",
" if clip_coef < 1:\n",
" for grad in grads:\n",
" grad *= clip_coef\n",
" \n",
" return grads\n",
"\n",
"\n",
"def backward_pass(inputs, outputs, hidden_states, targets, params):\n",
" \"\"\"\n",
" Computes the backward pass of a vanilla RNN.\n",
" \n",
" Args:\n",
" `inputs`: sequence of inputs to be processed\n",
" `outputs`: sequence of outputs from the forward pass\n",
" `hidden_states`: sequence of hidden_states from the forward pass\n",
" `targets`: sequence of targets\n",
" `params`: the parameters of the RNN\n",
" \"\"\"\n",
" # First we unpack our parameters\n",
" U, V, W, b_hidden, b_out = params\n",
" \n",
" # Initialize gradients as zero\n",
" d_U, d_V, d_W = np.zeros_like(U), np.zeros_like(V), np.zeros_like(W)\n",
" d_b_hidden, d_b_out = np.zeros_like(b_hidden), np.zeros_like(b_out)\n",
" \n",
" # Keep track of hidden state derivative and loss\n",
" d_h_next = np.zeros_like(hidden_states[0])\n",
" loss = 0\n",
" \n",
" # For each element in output sequence\n",
" # NB: We iterate backwards s.t. t = N, N-1, ... 1, 0\n",
" for t in reversed(range(len(outputs))):\n",
"\n",
" # Compute cross-entropy loss (as a scalar)\n",
" # YOUR CODE HERE!\n",
" loss += -np.mean(np.log(outputs[t]+1e-12) * targets[t])\n",
" \n",
" # Backpropagate into output (derivative of cross-entropy)\n",
" # if you're confused about this step, see this link for an
explanation:\n",
" # http://cs231n.github.io/neural-networks-case-study/#grad\n",
" # YOUR CODE HERE!\n",
" d_o = outputs[t].copy()\n",
" d_o[np.argmax(targets[t])] -= 1\n",
" \n",
" # Backpropagate into W\n",
" # YOUR CODE HERE!\n",
" d_W += np.dot(d_o, hidden_states[t].T)\n",
" d_b_out += d_o\n",
" \n",
" # Backpropagate into h\n",
" # YOUR CODE HERE!\n",
" d_h = np.dot(W.T, d_o) + d_h_next\n",
" \n",
" # Backpropagate through non-linearity\n",
" d_f = tanh(hidden_states[t], derivative=True) * d_h\n",
" d_b_hidden += d_f\n",
" \n",
" # Backpropagate into U\n",
" # YOUR CODE HERE!\n",
" d_U += np.dot(d_f, inputs[t].T)\n",
" \n",
" # Backpropagate into V\n",
" # YOUR CODE HERE!\n",
" d_V += np.dot(d_f, hidden_states[t-1].T)\n",
" d_h_next = np.dot(V.T, d_f)\n",
" \n",
" # Pack gradients\n",
" grads = d_U, d_V, d_W, d_b_hidden, d_b_out \n",
" \n",
" # Clip gradients\n",
" grads = clip_gradient_norm(grads)\n",
" \n",
" return loss, grads\n",
"\n",
"\n",
"loss, grads = backward_pass(test_input, outputs, hidden_states, test_target,
params)\n",
"\n",
"print('We get a loss of:')\n",
"print(loss)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercise:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Implement the missing code in the backward pass above. Refer to previous weeks
for the definition of cross-entropy or follow [this
link](http://cs231n.github.io/neural-networks-case-study/#grad) for a hint."
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "Pxi7sbLus82R"
},
"source": [
"### Optimization"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now that we can do forward passes and compute gradients with backpropagation,
we're ready to train our network. For that we will need an optimizer. A common and
easy to implement optimization method is gradient descent, which has the update
rule: $\\theta_{n+1} = \\theta_{n} - \\eta \\frac{\\partial E}{\\partial \\
theta_{n}}$, where $\\eta$ is the learning rate and $E$ is our cost function. This
is essentially what's going on behind the scenes when you run `optimizer.step()` in
PyTorch using the stochastic gradient descent optimizer."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"colab": {},
"colab_type": "code",
"id": "N4yTZYkBtAVr"
},
"outputs": [],
"source": [
"def update_parameters(params, grads, lr=1e-3):\n",
" # Take a step\n",
" for param, grad in zip(params, grads):\n",
" param -= lr * grad\n",
" \n",
" return params"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "K7ArhViPtEck"
},
"source": [
"### Training loop"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We need to define a full training loop with a forward pass, backward pass,
optimization step and validation. Training will take approximately 5 minutes, so
you might want to read on while the notebook is running."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"colab": {},
"colab_type": "code",
"id": "YMcg90qYs6kW",
"scrolled": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Epoch 0, training loss: 3.7030434040001134, validation loss:
3.7253638510366778\n",
"Epoch 100, training loss: 2.548263239305574, validation loss:
2.565572023003428\n",
"Epoch 200, training loss: 1.9880142333179633, validation loss:
2.0131189666321054\n",
"Epoch 300, training loss: 1.7188008978786442, validation loss:
1.7539022584112005\n",
"Epoch 400, training loss: 1.5879113487879617, validation loss:
1.6303629072567305\n",
"Epoch 500, training loss: 1.5075009671922137, validation loss:
1.55336926746893\n",
"Epoch 600, training loss: 1.4474347963188194, validation loss:
1.493726595811963\n",
"Epoch 700, training loss: 1.3952514236497056, validation loss:
1.4396767069478495\n",
"Epoch 800, training loss: 1.3423156600350297, validation loss:
1.383039185570785\n",
"Epoch 900, training loss: 1.2773975256533354, validation loss:
1.3135134700978988\n",
"Input sentence:\n",
"['a', 'a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b', 'b']\n",
"\n",
"Target sequence:\n",
"['a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'EOS']\n",
"\n",
"Predicted sequence:\n",
"['a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'EOS']\n"
]
},
{
"data": {
"image/png":
"iVBORw0KGgoAAAANSUhEUgAAAYIAAAEGCAYAAABo25JHAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEg
AACxIB0t1+/
AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9y
Zy8QZhcZAAAgAElEQVR4nO3deXxU1f3/8dcnIRBkC5uyiWFRwg4xIIjKKuAuiijuVqXytXWv8rO2Klar1gW
11rVSrCiiiAVUEJEKVAUB2UHZFUFZlLAKBM7vjzOTjSxkGW4m834+HveRyZ07dz6X0XnnnHPvueacQ0REYl
dc0AWIiEiwFAQiIjFOQSAiEuMUBCIiMU5BICIS4yoEXUBR1alTxyUnJwddhohIVJk3b95W51zdvJ6LuiBIT
k5m7ty5QZchIhJVzGx9fs+pa0hEJMYpCEREYpyCQEQkxkXdGIGIHF0HDhxgw4YN/
Prrr0GXIkcgMTGRRo0akZCQcMSvURCISIE2bNhAtWrVSE5OxsyCLkcK4Jxj27ZtbNiwgSZNmhzx69Q1JCIF
+vXXX6ldu7ZCIAqYGbVr1y5y601BICKFUghEj+J8VjETBEuWwB13wN69QVciIlK2xEwQrF8PTz8Nn38edCU
iUhTbtm2jQ4cOdOjQgXr16tGwYcPM3/fv339E+7juuuv45ptvCtzm+eefZ/
To0aVRMqeddhoLFiwolX0dDTEzWHxGxqdU4HQ+mXCA3r2PCbocETlCtWvXzvxSfeCBB6hatSp33XVXjm2cc
zjniIvL+2/
bkSNHFvo+N998c8mLjVIx0yKodmxluvIFn0zSKXAi5cGqVato1aoVV1xxBa1bt2bTpk0MGTKEtLQ0Wrduzf
DhwzO3Df+FnpGRQVJSEsOGDaN9+/
Z07dqVzZs3A3DfffcxYsSIzO2HDRtG586dadGiBZ+HuhJ2797NxRdfTKtWrRg4cCBpaWmF/uX/
xhtv0LZtW9q0acO9994LQEZGBldddVXm+meffRaAp59+mlatWtGuXTuuvPLKUv83y0/
MtAjo1Ik+lR7ngTWn8fPPUKtW0AWJRKHbboPS7vLo0AFCX8BFtWLFCl5//
XXS0tIAePTRR6lVqxYZGRn07NmTgQMH0qpVqxyvSU9Pp3v37jz66KPccccdvPbaawwbNuywfTvnmDNnDhMm
TGD48OFMnjyZ5557jnr16jFu3DgWLlxIampqgfVt2LCB+
+67j7lz51KjRg369OnDpEmTqFu3Llu3bmXx4sUAbN+
+HYDHH3+c9evXU7Fixcx1R0PMtAioUIE+nXfgiOPTabpPs0h50KxZs8wQAHjrrbdITU0lNTWV5cuXs2zZss
NeU7lyZc466ywATj75ZNatW5fnvi+66KLDtpk1axaXXXYZAO3bt6d169YF1jd79mx69epFnTp1SEhI4PLLL
2fGjBk0b96cb775hltuuYUpU6ZQo0YNAFq3bs2VV17J6NGji3RBWEnFTosA6HRJMtVm7uCT9xwDL6kRdDki
0aeYf7lHSpUqVTIfr1y5kmeeeYY5c+aQlJTElVdemef59BUrVsx8HB8fT0ZGRp77rlSpUqHbFFft2rVZtGg
RH330Ec8//
zzjxo3j5ZdfZsqUKXz22WdMmDCBRx55hEWLFhEfH1+q752X2GkRAAn9etGT6XwyLehKRKS07dixg2rVqlG9
enU2bdrElClTSv09unXrxtixYwFYvHhxni2O7E455RSmT5/
Otm3byMjIYMyYMXTv3p0tW7bgnOOSSy5h+PDhzJ8/n4MHD7JhwwZ69erF448/
ztatW9mzZ0+pH0NeYqpFwIkn0qfmaCZsuYC1a6EIV2CLSBmXmppKq1atSElJ4YQTTqBbt26l/
h6///3vufrqq2nVqlXmEu7WyUujRo146KGH6NGjB845zjvvPM455xzmz5/
P9ddfj3MOM+Oxxx4jIyODyy+/
nJ07d3Lo0CHuuusuqlWrVurHkBdzLrr6y9PS0lxJbkyzfOCfaDXuIV558SA3/
DbyTS6RaLd8+XJatmwZdBllQkZGBhkZGSQmJrJy5Ur69u3LypUrqVChbP1NnddnZmbznHNpeW1ftqo/
ClIGtqHBuB/45N1K3PDbOkGXIyJRZNeuXfTu3ZuMjAycc7z00ktlLgSKI/
qPoIisT2/68AEffj6QQ4cgn+tPREQOk5SUxLx584Iuo9TF3tdgnTqcecJKtu6pwsKFQRcjIhK82AsCoPe5l
QH45IN9AVciIhK8mAyC+heeQmuWMPW9HUGXIiISuJgMAk47jb7xnzJzUQ1NSy0iMS9iQWBmiWY2x8wWmtlS
M3swj22uNbMtZrYgtNwQqXpySEykf/tN/HqwIv/
971F5RxEppp49ex52cdiIESMYOnRoga+rWrUqABs3bmTgwIF5btOjRw8KOx19xIgROS7sOvvss0tlHqAHHn
iAJ554osT7KQ2RbBHsA3o559oDHYD+ZtYlj+3eds51CC2vRrCeHM4YVI/
K7GHyOzuP1luKSDEMHjyYMWPG5Fg3ZswYBg8efESvb9CgAe++
+26x3z93EHz44YckJSUVe39lUcSCwHm7Qr8mhJYyc/
Va4vl96cl0Jn94MOhSRKQAAwcO5IMPPsi8Cc26devYuHEjp59+euZ5/ampqbRt25b//Oc/
h71+3bp1tGnTBoC9e/
dy2WWX0bJlSwYMGMDebH3DQ4cOzZzC+v777wfg2WefZePGjfTs2ZOePXsCkJyczNatWwF46qmnaNOmDW3at
MmcwnrdunW0bNmSG2+8kdatW9O3b98c75OXBQsW0KVLF9q1a8eAAQP45ZdfMt8/
PC11eLK7zz77LPPGPB07dmTnzpL/
MRvR6wjMLB6YBzQHnnfOzc5js4vN7AzgW+B259z3eexnCDAEoHHjxqVTXEoK/
WuO4ZafzmHNGmjatHR2K1KeBTELda1atejcuTMfffQRF1xwAWPGjGHQoEGYGYmJiYwfP57q1auzdetWunTp
wvnnn5/
vfXtfeOEFjjnmGJYvX86iRYtyTCP98MMPU6tWLQ4ePEjv3r1ZtGgRt9xyC0899RTTp0+nTp2cF6DOmzePkS
NHMnv2bJxznHLKKXTv3p2aNWuycuVK3nrrLV555RUGDRrEuHHjCry/wNVXX81zzz1H9+7d+fOf/
8yDDz7IiBEjePTRR1m7di2VKlXK7I564okneP755+nWrRu7du0iMTGxCP/
aeYvoYLFz7qBzrgPQCOhsZm1ybTIRSHbOtQOmAqPy2c/Lzrk051xa3bp1S6c4M/
r38w2UyZNKd2ZBESld2buHsncLOee49957adeuHX369OGHH37gp59+ync/
M2bMyPxCbteuHe3atct8buzYsaSmptKxY0eWLl1a6IRys2bNYsCAAVSpUoWqVaty0UUXMXPmTACaNGlChw4
dgIKnugZ/
f4Tt27fTvXt3AK655hpmzJiRWeMVV1zBG2+8kXkFc7du3bjjjjt49tln2b59e6lc2XxUrix2zm03s+lAf2B
JtvXbsm32KvD40agn7MTBaTQbs4rJY2rwf7eUUsCIlGNBzUJ9wQUXcPvttzN//
nz27NnDySefDMDo0aPZsmUL8+bNIyEhgeTk5Dynni7M2rVreeKJJ/
jqq6+oWbMm1157bbH2Exaewhr8NNaFdQ3l54MPPmDGjBlMnDiRhx9+mMWLFzNs2DDOOeccPvzwQ7p168aUK
VNISUkpdq0Q2bOG6ppZUuhxZeBMYEWubepn+/V8YHmk6slTr170j5vKp3Ors0/
XlomUWVWrVqVnz5785je/yTFInJ6ezrHHHktCQgLTp09n/
fr1Be7njDPO4M033wRgyZIlLFq0CPBTWFepUoUaNWrw008/8dFHH2W+plq1ann2w59+
+um8//777Nmzh927dzN+/HhOP/30Ih9bjRo1qFmzZmZr4t///jfdu3fn0KFDfP/99/
Ts2ZPHHnuM9PR0du3axerVq2nbti333HMPnTp1YsWKFYW8Q+Ei2SKoD4wKjRPEAWOdc5PMbDgw1zk3AbjFz
M4HMoCfgWsjWM/hqlalf9sNPL+wErNmQe/eR/
XdRaQIBg8ezIABA3KcQXTFFVdw3nnn0bZtW9LS0gr9y3jo0KFcd911tGzZkpYtW2a2LNq3b0/
Hjh1JSUnh+OOPzzGF9ZAhQ+jfvz8NGjRg+vTpmetTU1O59tpr6dy5MwA33HADHTt2LLAbKD+jRo3ipptuYs
+ePTRt2pSRI0dy8OBBrrzyStLT03HOccstt5CUlMSf/
vQnpk+fTlxcHK1bt86821pJxNw01LntfuQZav3xJm757T7+9mL1UtuvSHmhaaijT1GnoY7NK4uzqXLhmZzB
DCZP1ICxiMSmmA8CWrakf9JslmysxfeHnbgqIlL+KQjM6H+mv6hsygdqFYjkJdq6kGNZcT4rBQHQ6vIOHM9
3fPTmz0GXIlLmJCYmsm3bNoVBFHDOsW3btiJfZBZzdyjLi/
XuxVlxY3hr9lXs3w8VKwZdkUjZ0ahRIzZs2MCWLVuCLkWOQGJiIo0aNSrSaxQEANWqcV7bdby8MJHPPoMzz
wy6IJGyIyEhgSZNmgRdhkSQuoZCel/
uZyOd+EZ60KWIiBxVCoKQyhefTW+mMekDUFeoiMQSBUFYs2acV38ea7fVoJC5pkREyhUFQTbnDPCjxBPfKf
5kUyIi0UZBkE3Dy7uTyjwmvrWr8I1FRMoJBUF2XbpwXuVpfPFtLUI3IBIRKfcUBNnFx3Ne79044vhwom5hK
SKxQUGQS8dr2lOfjUwcpauMRSQ2KAhyievfl3PjPmLKF9UI3StbRKRcUxDkVrUq57X/
jp37EwndNlREpFxTEOSh99UNSWQvE1//JehSREQiTkGQh2Mu6k8fPuE/k+J0lbGIlHsKgrw0bsxFx89l/
S81+PrroIsREYksBUE+zhtclXgyeO91XVwmIuWbgiAfda46i+58xntvHwi6FBGRiFIQ5Kd1ay6qO4vlP9Zk
+fKgixERiRwFQX7MuHCQn4Ru/
Og9ARcjIhI5CoICNLymD134gvcUBCJSjikICpKWxkU1PmXeujqsXx90MSIikaEgKIgZAy70FxKMf0v3KBCR
8klBUIjmvzmDdizkvVE7gy5FRCQiFASF6daNi6pMYdaK2vz0U9DFiIiUPgVBYeLjueisvTjiGD9W1xSISPm
jIDgCbW7oSgrLefvl9KBLEREpdRELAjNLNLM5ZrbQzJaa2YN5bFPJzN42s1VmNtvMkiNVT0lYr55cVvk/
fLakFhs3Bl2NiEjpimSLYB/
QyznXHugA9DezLrm2uR74xTnXHHgaeCyC9RRfQgKXnrsHRxzvjNbdakSkfIlYEDgvPGNbQmjJPanzBcCo0O
N3gd5mZpGqqSRShvakA18z5hWdPSQi5UtExwjMLN7MFgCbganOudm5NmkIfA/
gnMsA0oHaeexniJnNNbO5W7ZsiWTJ+TvjDC6r+gFfrqzN2rXBlCAiEgkRDQLn3EHnXAegEdDZzNoUcz8vO+
fSnHNpdevWLd0ij1R8PJcOPAjA2Nf3BlODiEgEHJWzhpxz24HpQP9cT/0AHA9gZhWAGsC2o1FTcST/
th9d+IIxrykIRKT8iORZQ3XNLCn0uDJwJrAi12YTgGtCjwcCnzpXhm8OecopXFbzYxZ8V4sVuY9ERCRKRbJ
FUB+YbmaLgK/
wYwSTzGy4mZ0f2uafQG0zWwXcAQyLYD0lZ8YlgytgHOLtkbuDrkZEpFRYWf4DPC9paWlu7ty5wRWwcCE9O/
zMD8d25Jsfkyib5ziJiORkZvOcc2l5Pacri4uqXTuurjeVlZuT+PLLoIsRESk5BUFRmTHwxpocw25GPbcj6
GpEREpMQVAM1W64lIt4j7fHJ/
CrblMgIlFOQVAcjRtzTYdFbP+1MhP+E11jLCIiuSkIiqnnbe1pxPeMGvFL0KWIiJSIgqCY4gcO4KqEt5k8O
4lNm4KuRkSk+BQExVWlCtecu41DLo7RIzUjqYhELwVBCbS4xU85MfIfe4iyyzFERDIpCErijDO4odZ4lv2Q
xOefB12MiEjxKAhKIi6Oy25KojrpvPSE7lMgItFJQVBCVYZezZWMZuzERH7+OehqRESKTkFQUo0a8dvuK9h
3MIHXRx4MuhoRkSJTEJSCdnf3pwtf8OJTuzVoLCJRR0FQGvr147e13uWbjdWZMSPoYkREikZBUBri4xl0c1
1qsF2DxiISdRQEpeSYm67mWhvFux9W1pXGIhJVFASlpUEDft9nBRmH4vjHMweCrkZE5IgpCEpRs3sv5Xwm8
OLzGezV/
e1FJEooCEpT9+7c1mwSW3dVZvQbOn1IRKKDgqA0mdH93m60ZwEjHtGppCISHRQEpcwuH8zt1f7J0nVVmTYt
6GpERAqnIChtiYlc9vu6HMePPP2X3UFXIyJSKAVBBFT6/
RBujnuRDz+rwqJFQVcjIlIwBUEk1KvH7y75iWrs4JH79wVdjYhIgRQEEVLzT7/j//
gHY9+vyLffBl2NiEj+FASR0ro1t/dfQSV+5dGHdIGZiJRdCoIIOu7B/
+NGXuHfb8axfn3Q1YiI5E1BEEmdO/OH077EDh3kb3/
NCLoaEZE8KQgi7PjhN3INo3j1n7BhQ9DViIgcrthBYGa3lWYh5VaPHtyX+hGHMg7x0AO6g5mIlD0laRHcUd
CTZna8mU03s2VmttTMbs1jmx5mlm5mC0LLn0tQT9lkxgkPD+EmXuSfI42VK4MuSEQkp5IEgRXyfAZwp3OuF
dAFuNnMWuWx3UznXIfQMrwE9ZRd/
frxx05TqeR+5f77NFYgImVLSYKgwCnVnHObnHPzQ493AsuBhiV4v+hlxnF/
u4vb3NO8NbYCCxcGXZCISJYCg8DMdprZjjyWnRThS93MkoGOwOw8nu5qZgvN7CMza53P64eY2Vwzm7tly5Y
jfduypXt37uoxjyTbzn3D1CoQkbKjwCBwzlVzzlXPY6nmnIs/kjcws6rAOOA259yOXE/
PB05wzrUHngPez6eOl51zac65tLp16x7J25ZJNR8bxj3uUSZNrsD06UFXIyLileSsoe+OYJsEfAiMds69l/
t559wO59yu0OMPgQQzq1Pcmsq8zp259by1nGDruf33GRzUSUQiUgZEbLDYzAz4J7DcOfdUPtvUC22HmXUO1
bOtBDWVeZUfe4DHGMbCpRUYOTLoakREIjhYDHQDrgJ6ZTs99Gwzu8nMbgptMxBYYmYLgWeBy5wr5/
f1atmSQUNrcyqf88dhGezI3VkmInKUWUHfu2aW37UCBvzROVcrIlUVIC0tzc2dO/
dov23p2rKFr5oMovPu6QwbBn/
9a9AFiUh5Z2bznHNpeT1XWIugWj5LVeCZ0iwyptStS6cHzuFqRvHUk4c0TbWIBKrAFkFZVC5aBAD79rHppO
6kbJhK555V+HhqHFbYJXoiIsVUUIugQiEvLGjKB+ece6hElcWySpWoP+IeHrloGL+b9jxjxsDgwUEXJSKxq
LCuod15LADXA/dEsK7YcOGF3NR/
PWlx87j91oNs3x50QSISiwq7oOzJ8AK8DFQGrgPGAE2PQn3lmxnxf3+GF+N/x5atxh//
GHRBIhKLCj191MxqmdlfgEX4rqRU59w9zrnNEa8uFjRrxsn3ncXv3HO88IJjdl6TcIiIRFBhcw39DfgK2Am
0dc494Jz75ahUFkvuvpuHmv6LhvE/ct21h/
j116ALEpFYUliL4E6gAXAfsDH7pHNmpkuhSktiItVfeIxXM65l+Yo4Hnww6IJEJJYUNkYQ55yrnMfkc9Wcc
9WPVpExoW9f+l11HNfbazz+uGPOnKALEpFYoXsWlyUjRvBk3UdpEP8T117j1EUkIkeFgqAsqVWLGi89zqsH
rmH5CuP++4MuSERigYKgrLnwQvpdVosb7VX+9jen+xaISMQpCMqi557j6dp/
4cSK33HVVY5t5XpibhEJmoKgLKpThyovPslb+wawedNBbrwRomxKKBGJIgqCsurii0m9rgN/
PTSM8ePhlVeCLkhEyisFQVn27LPc3mwCZ1aawW23OZYtC7ogESmPFARlWdWqxL35BqMyrqDqoR1cfLFj586
gixKR8kZBUNZ17kz94UMZs28A337juP56jReISOlSEESDe+6hVw/HI/F/
5p13YMSIoAsSkfJEQRAN4uPhzTe5u9arXFhlKn/
4g2PmzKCLEpHyQkEQLerXx94ew7/2DKJp5U0MGuTYsCHookSkPFAQRJMePajx12GM33Umu3/Zz/
nnw+7dhb9MRKQgCoJoc/fdtD6/OWMODGThQseVV8KhQ0EXJSLRTEEQbcxg1CjObvYNT1b+E++/
j25xKSIloiCIRklJMGECt8b/nSG13uHRR+Ff/
wq6KBGJVgqCaJWSgr0zlr9vv4redRZy442ODz8MuigRiUYKgmjWty8JI/
7Ge1tPp23tjQwcCF98EXRRIhJtFATR7ne/o/pNV/
DRT6k0rJrOOefA0qVBFyUi0URBEO3M4LnnOO7czny8pSOV3K/
06wfr1gVdmIhECwVBeVChArz9Nk261mPK7tPYnX6Anj1h/
fqgCxORaBCxIDCz481supktM7OlZnZrHtuYmT1rZqvMbJGZpUaqnnLvmGNg4kTaNd/
DVHcm27dl0KOHwkBEChfJFkEGcKdzrhXQBbjZzFrl2uYs4MTQMgR4IYL1lH+1a8PkyaQlrWJqhbPZ/
vNBhYGIFCpiQeCc2+Scmx96vBNYDjTMtdkFwOvO+xJIMrP6kaopJjRuDJ98QlrFRUxNOIftPx+ke3dYuTLo
wkSkrDoqYwRmlgx0BGbneqoh8H223zdweFhgZkPMbK6Zzd2yZUukyiw/
UlLg009Ji5vPJxXPYfeOg3TrBvPnB12YiJRFEQ8CM6sKjANuc87tKM4+nHMvO+fSnHNpdevWLd0Cy6tWreD
TTznZzWVWxV4cU/EAPXrAp58GXZiIlDURDQIzS8CHwGjn3Ht5bPIDcHy23xuF1klpaNMGpk2jRcZS/
revEycct5ezzoK33w66MBEpSyJ51pAB/
wSWO+eeymezCcDVobOHugDpzrlNkaopJrVvDzNn0jBxGzM2p9A5JZ3LLoP779espSLiRbJF0A24CuhlZgtC
y9lmdpOZ3RTa5kNgDbAKeAX4vwjWE7tatoT//
Y+a9RL55NsTuO7MDQwfDpdcovsZiAhUiNSOnXOzACtkGwfcHKkaJJvGjWHmTCr1788/
pyXT9pKZ3DWuK926wXvvQdOmQRcoIkHRlcWx5Nhj4b//xfr15fZ3TuWDC15m/
XpHx47w7rtBFyciQVEQxJrq1WHCBLj1VvqP/y1fd7yelBMPcsklcPPN8OuvQRcoIkebgiAWVagAI0bAP/
5B8ozXmbk7lTuv3cY//
gFdusCiRUEXKCJHk4Iglg0dClOnUvHnH3libGMm3vlfNm2CtDR4+GHIyAi6QBE5GhQEsa5nT/
j6azj5ZM59sidL+9/
JRRdkcN990LUrLFkSdIEiEmkKAoEGDfwlx3ffTZ3Xn2LMsvaMfXQN69ZBx45w112wc2fQRYpIpCgIxKtQAR
57DCZPhu3bueS+Fiy/
4UmuvfoQTz7ppy8aOxacC7pQESltCgLJqV8/3x90ySXUefQuXll6Kl+MXsNxx8Gll/
qepNm5pw4UkaimIJDD1awJb74JY8bAypV0uaYFX/X9I/8YsZ/ly/
2ZRQMHwjffBF2oiJQGBYHk79JLYcUKuOIK4h97hKHPpLD6hY954AGYMgVat4YhQ2DNmqALFZGSUBBIwerWh
X/
9C6ZPh0qVqHpxP+7/6lxWffANQ4fCqFFw0klw9dWwfHnQxYpIcSgI5Mj06AELFsDjj8OsWRzXqzXPZQxlze
wt3HILjBvnWwgDB2oMQSTaKAjkyFWqBH/4A6xa5S9Ge/VVGp7RjKeqP8C6hencey9MnerHEE45Bd54A/
btC7poESmMgkC
Krk4deO45f3bRmWfCgw9SN+0E/hJ/PxsW/8Lf/
w7p6XDVVXDCCf7eB999F3TRIpIfBYEUX4sWvk9owQLo3RuGD6da22Ru/
vFPLPvvZqZM8dNVPPQQJCdD377w1luwd2/QhYtIdgoCKbn27X0gLFzoWwh/+QtxyY3pO/
YGJj22lDVrfKvg22/h8suhfn3fs/S//+kuaSJlgYJASk+7dv7GBitWwG9+469FaNOG5Jv6c/
8pk1mz6hDTpsF55/mzjU47zXcd3XEHfPmlrloWCYq5KPu/Ly0tzc2dOzfoMuRIbNsGL73kxxN+/
NH3D91wA1x3HTurNWDiRHj7bT+rxf79/iZqgwbBBRf4Ce/
i44M+AJHyw8zmOefS8nxOQSARt28fvP8+vPyyn9wuPh7OPRduvBH69mX77gQmTPBzGX38MRw4ALVqwdln+9
ZDv35Qo0bQByES3RQEUnasWgWvvgojR8Lmzf6CtUGD/OBB166k7zA+/hgmToQPP/
SNigoV4PTTfTD07u2HJOLUqSlSJAoCKXv274ePPvLjCBMm+HtkJif7QBg8GFq35uAh48svYdIkHwxLl/
qX1qkDvXpBnz5+adIk0CMRiQoKAinbdu6E8eN9KEyd6k8lat4cLrwQBgzwV6jFxfHDD75n6ZNP/
LJxo39506b+wufTT/cD0M2agVmgRyRS5igIJHr89JMfT3j/fZg2zQ8YHHecH0E+/3w/D/
Yxx+CcPzkpHAozZ8Ivv/hd1KvnAyG8tG/vu5dEYpmCQKJTerrvPho/
3g8Y7Nrlp7k44wzo39+PIrdqBWYcOuQnvZs1yy8zZ8L69X43VapA585ZS6dO0KiRWg0SWxQEEv327YPPPvP
zX0+eDMuW+fWNGvlQ6NMHunf3zYGQ77/3F63NmuUnwlu40DcwwG+WPRg6dfK3YRAprxQEUv58950PhSlT/
LjCjh1+fUqKHzDo0eOwYPj1Vx8GX30Fc+b4JfvNdZo1gw4d/NK+vf+ploOUFwoCKd8yMmD+fPjvf/
0ya5YfgAY/H1KPHnDqqf4qtebNc3yzb98O8+b5UJg/30+btGpV1q5r1coKh/
CSkgIJCUfzAEVKTkEgsSUjA77+OmcwhFsMder4s5C6dvVLp05QtWqOl2IXsaYAAA4dSURBVO/
cCYsX+1AIL4sX+xYFQMWKPgxat865NG2qq6Gl7FIQSGw7eNCPKXz5JXzxhV9WrPDPxcVB27b+Bgonn+yXNm
38oHQ2GRl+0ryFC30wLFnir2sID0gDJCb6gGjTJmdAJCfrAjgJnoJAJLeff/
b9QeFg+Oor308Evt+nTRtITc0Kh7ZtoXLlw3azc6c/
W2npUr+EA2LDhqxtjjnGB0SLFn4JPz7pJP+cyNEQSBCY2WvAucBm51ybPJ7vAfwHWBta9Z5zbnhh+1UQSEQ
4B2vX+oGCefOylp9/9s/Hx/s/79u397Ostm3rl/r18xxNTk/3jZBwQCxf7gem16/
POcvq8ccfHhAtWvhBarUipDQFFQRnALuA1wsIgrucc+cWZb8KAjlqnPNnJ82blxUQixZlXdIMfjQ5ezC0a+
cDI9e4Q9jevbBypQ+F8LJihf8ZHt8G3/g46aSsYDjpJD/
O3bw51K6tM5mk6AoKgohdb+mcm2FmyZHav0jEmfkbJpxwAlx0Udb6bdv86PHixT4YFi+G116D3buztmna1H
cvtWzpl5QUaNmSytWr066dz4vsnPMzdecOiLlz/S0est/
AJykpKxTCy4kn+p916yokpOgiOkYQCoJJBbQIxgEbgI341sHSfPYzBBgC0Lhx45PXZx+hEykLDh2Cdeuygm
HxYt8ntHJl1lVsAA0a5AiGzKVevXy/
wfft871Wq1b53a1albWsW5czJKpVOzwcwksBbyExILDB4kKCoDpwyDm3y8zOBp5xzp1Y2D7VNSRR5cABWLP
G/3m/fLlfwo+z9wXVqOHDISXFf4OHl+bN/
bd7Pvbv92GQPRzCYbF2rT9hKqxKlcNbEuGlQQONSZR3ZTII8th2HZDmnNta0HYKAikXnPNjDdmDITyinH0M
Avyf8tnDIXtIFHDa0YEDfogjdyti5UofEtkbKomJ/
srqvLqcGjXS9RHlQSBjBIUxs3rAT845Z2ad8fdP3hZUPSJHlRk0bOiXPn1yPrd7d9Y3dvblgw/
87KzZNWx4eEA0bQpNm5JQtSrNmvkv+NwyMnxIrF59eEhMnuy7o8IqVvS7zKslccIJmtm1PIjkWUNvAT2AOs
BPwP1AAoBz7kUz+x0wFMgA9gJ3OOc+L2y/ahFITNuxI2f/T/ag2LIl57bHHpsZCjRrlvNx/
fr59gUdOgQ//JAzILIve/ZkbVuhgr9gLq+QSE4+7Lo8CZAuKBOJBdu3+2/qNWtyLqtX+z//
s48qV6rkb+2WV0g0aZJvl1P47Ka8AmLlypzDHnFx0Lhx3iHRtGme1+dJBCkIRGJdeMBg9erDQ2L16pzf4OD
HJbIHRPbAyOf0I+dg69b8WxLha/PCGjXKOySaNcv3MgwpAQWBiOTPOf8tnT0ksj/+/vucl0NXrpx/
l1Nysh95zkP4LfIKic2bc25br15WMITPtE1J8W+jMYniURCISPHt2+fnxli92p9uFA6J8M/
sF9KFB8FzB0X4Z506ebYmduzIOyS+/dZ3RYUlJPjx8PCZtuGAaNGiwLNsBQWBiESKc36QOnc4hB/
nPhW2WrW8u5uaNfMDChUrHvYW27dnXWkdPtt2xQofFNmvk2jUKGdAtGzpL+6uWzfC/
wZRQkEgIsHYu9df8ZY7KMKti/BNHsCPLh9//OGtiPDjXPcS3b/f7yZ7QIR/
7tqVtd1xx2VNBRVeWrWKvZlfFQQiUvYcOuT7ffIam1i9+vCBg6QkHwonnphzNr6TToLq1TM3C1+rt2xZ1mw
f4Rk/
wrlj5scfcgdE8+bl9wprBYGIRJ9duw4fk1i92p+nmnuSpXr1coZD+GeTJpn3FT140L88HAxLlvifq1Zl7ap
qVX870pNP9rejSE31XU3lYYBaQSAi5cu+ff5bPTxV67ffZv3cmm2WmgoVfLfSSSflvH1cy5aZfUN79/
rWw6JF/g6n8+b5u9CFL5xLTPS3oQgHQ2qq30W0XSynIBCR2PHzz1nBkDsk9u/
325j5bqbWrXPeWzQlBSpV4uBBv/
n8+Vm3ovj666xbXyck+JZDly7+LqdduvjdleXZXRUEIiIZGb4VEb6faPjnt9/658DPrte8uQ+Fdu2gY0e/
NGrEIWesWeODYe5cmD3b/wy3HOrU8aEQXjp39sMaZYWCQEQkP/v3+zDIHg5LlvjBg/
D3Y+3avgnQsWPWzxYtyHDxLF3qQ+HLL/
3P5cuzXpaS4kOha1fo1s2frRTUYLSCQESkqHbt8gMHCxb4fqGvv/ajy+HupcqVfauhQwdIS/
NNgFatSN9dgblzs4Jh9uysE6CSkrJC4bTToFOno3caq4JARKQ0HDjgL1QIB8PXX/ugSE/3zx9zjD/
lqHNnv5xyCu74xqxZa/zvf2QuS0P3YqxQwQ8+h4OhWzd/
3UMkKAhERCLFOd+NNGdO1vL111k3dTj22Kxg6NwZOnXiZ2rxxRdZwTBnTtY1Ds2a5QyGlJTS6U5SEIiIHE3
79/
tupOzhkH3woEULOPVU30906qnsb9aS+QviMoNh1qys20vUrOk37dYNzj7bn8paHAoCEZGg7djhz0P98kv44
gu/hK95qFHDn4MaCgbX+RRWba6eIxhWrIB774WHHy7e2ysIRETKmnCX0uef+1D4/HN/tpJz/
oKENm1ytBq21WzOwUPGsccW7+0UBCIi0SA93Xcjff65X778Musqtjp1YNgwuPPOYu26TN68XkREcqlRA848
0y/
gJ0FatiyrxdCwYUTeVkEgIlJWxcX5LqI2beDGGyP3NhHbs4iIRAUFgYhIjFMQiIjEOAWBiEiMUxCIiMQ4BY
GISIxTEIiIxDgFgYhIjIu6KSbMbAuwvpgvrwNsLXSr8kXHHBt0zLGhJMd8gnOubl5PRF0QlISZzc1vro3yS
sccG3TMsSFSx6yuIRGRGKcgEBGJcbEWBC8HXUAAdMyxQcccGyJyzDE1RiAiIoeLtRaBiIjkoiAQEYlxMRME
ZtbfzL4xs1VmNizoekqLmR1vZtPNbJmZLTWzW0Pra5nZVDNbGfpZM7TezOzZ0L/
DIjNLDfYIisfM4s3sazObFPq9iZnNDh3X22ZWMbS+Uuj3VaHnk4OsuyTMLMnM3jWzFWa23My6lufP2cxuD/
03vcTM3jKzxPL4OZvZa2a22cyWZFtX5M/VzK4Jbb/
SzK4pSg0xEQRmFg88D5wFtAIGm1mrYKsqNRnAnc65VkAX4ObQsQ0DpjnnTgSmhX4H/
29wYmgZArxw9EsuFbcCy7P9/hjwtHOuOfALcH1o/fXAL6H1T4e2i1bPAJOdcylAe/
zxl8vP2cwaArcAac65NkA8cBnl83P+F9A/17oifa5mVgu4HzgF6AzcHw6PI+KcK/cL0BWYku33/
wf8v6DritCx/gc4E/gGqB9aVx/
4JvT4JWBwtu0zt4uWBWgU+p+jFzAJMPzVlhVyf97AFKBr6HGF0HYW9DEU45hrAGtz115eP2egIfA9UCv0uU
0C+pXXzxlIBpYU93MFBgMvZVufY7vClphoEZD1H1XYhtC6ciXUHO4IzAaOc85tCj31I3Bc6HF5+LcYAdwNH
Ar9XhvY7pzLCP2e/Zgyjzf0fHpo+2jTBNgCjAx1ib1qZlUop5+zc+4H4AngO2AT/nObR/n/
nMOK+rmW6POOlSAo98ysKjAOuM05tyP7c87/
iVAuzhM2s3OBzc65eUHXcpRVAFKBF5xzHYHdZHUXAOXuc64JXIAPwAZAFQ7vPokJR+NzjZUg+AE4PtvvjUL
rygUzS8CHwGjn3Huh1T+ZWf3Q8/
WBzaH10f5v0Q0438zWAWPw3UPPAElmViG0TfZjyjze0PM1gG1Hs+BSsgHY4JybHfr9XXwwlNfPuQ+w1jm3x
Tl3AHgP/
9mX9885rKifa4k+71gJgq+AE0NnHFTEDzpNCLimUmFmBvwTWO6ceyrbUxOA8JkD1+DHDsLrrw6dfdAFSM/
WBC3znHP/zznXyDmXjP8cP3XOXQFMBwaGNst9vOF/h4Gh7aPur2bn3I/
A92bWIrSqN7CMcvo547uEupjZMaH/
xsPHW64/52yK+rlOAfqaWc1Qa6pvaN2RCXqQ5CgOxpwNfAusBv4YdD2leFyn4ZuNi4AFoeVsfP/
oNGAl8AlQK7S94c+gWg0sxp+VEfhxFPPYewCTQo+bAnOAVcA7QKXQ+sTQ76tCzzcNuu4SHG8HYG7os34fqF
meP2fgQWAFsAT4N1CpPH7OwFv4cZAD+Jbf9cX5XIHfhI5/
FXBdUWrQFBMiIjEuVrqGREQkHwoCEZEYpyAQEYlxCgIRkRinIBARiXEKApFczOygmS3ItpTabLVmlpx9lkm
RsqBC4ZuIxJy9zrkOQRchcrSoRSByhMxsnZk9bmaLzWyOmTUPrU82s09D88NPM7PGofXHmdl4M1sYWk4N7S
rezF4JzbX/sZlVDuygRFAQiOSlcq6uoUuzPZfunGsL/B0/
CyrAc8Ao51w7YDTwbGj9s8Bnzrn2+HmBlobWnwg875xrDWwHLo7w8YgUSFcWi+RiZrucc1XzWL8O6OWcWxO
a6O9H51xtM9uKnzv+QGj9JudcHTPbAjRyzu3Lto9kYKrzNxzBzO4BEpxzf4n8kYnkTS0CkaJx+Twuin3ZHh
9EY3USMAWBSNFcmu3nF6HHn+NnQgW4ApgZejwNGAqZ91iucbSKFCkK/SUicrjKZrYg2+
+TnXPhU0hrmtki/F/1g0Prfo+/c9gf8HcRuy60/lbgZTO7Hv+X/
1D8LJMiZYrGCESOUGiMIM05tzXoWkRKk7qGRERinFoEIiIxTi0CEZEYpyAQEYlxCgIRkRinIBARiXEKAhGR
GPf/AaF/H4NG5NobAAAAAElFTkSuQmCC\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"import matplotlib.pyplot as plt\n",
"%matplotlib inline\n",
"\n",
"# Hyper-parameters\n",
"num_epochs = 1000\n",
"\n",
"# Initialize a new network\n",
"params = init_rnn(hidden_size=hidden_size, vocab_size=vocab_size)\n",
"\n",
"# Initialize hidden state as zeros\n",
"hidden_state = np.zeros((hidden_size, 1))\n",
"\n",
"# Track loss\n",
"training_loss, validation_loss = [], []\n",
"\n",
"# For each epoch\n",
"for i in range(num_epochs):\n",
" \n",
" # Track loss\n",
" epoch_training_loss = 0\n",
" epoch_validation_loss = 0\n",
" \n",
" # For each sentence in validation set\n",
" for inputs, targets in validation_set:\n",
" \n",
" # One-hot encode input and target sequence\n",
" inputs_one_hot = one_hot_encode_sequence(inputs, vocab_size)\n",
" targets_one_hot = one_hot_encode_sequence(targets, vocab_size)\n",
" \n",
" # Re-initialize hidden state\n",
" hidden_state = np.zeros_like(hidden_state)\n",
"\n",
" # Forward pass\n",
" # YOUR CODE HERE!\n",
" outputs, hidden_states = forward_pass(inputs_one_hot, hidden_state,
params)\n",
"\n",
" # Backward pass\n",
" # YOUR CODE HERE!\n",
" loss, _ = backward_pass(inputs_one_hot, outputs, hidden_states,
targets_one_hot, params)\n",
" \n",
" # Update loss\n",
" epoch_validation_loss += loss\n",
" \n",
" # For each sentence in training set\n",
" for inputs, targets in training_set:\n",
" \n",
" # One-hot encode input and target sequence\n",
" inputs_one_hot = one_hot_encode_sequence(inputs, vocab_size)\n",
" targets_one_hot = one_hot_encode_sequence(targets, vocab_size)\n",
" \n",
" # Re-initialize hidden state\n",
" hidden_state = np.zeros_like(hidden_state)\n",
"\n",
" # Forward pass\n",
" # YOUR CODE HERE!\n",
" outputs, hidden_states = forward_pass(inputs_one_hot, hidden_state,
params)\n",
"\n",
" # Backward pass\n",
" # YOUR CODE HERE!\n",
" loss, grads = backward_pass(inputs_one_hot, outputs, hidden_states,
targets_one_hot, params)\n",
" \n",
" if np.isnan(loss):\n",
" raise ValueError('Gradients have vanished!')\n",
" \n",
" # Update parameters\n",
" params = update_parameters(params, grads, lr=3e-4)\n",
" \n",
" # Update loss\n",
" epoch_training_loss += loss\n",
" \n",
" # Save loss for plot\n",
" training_loss.append(epoch_training_loss/len(training_set))\n",
" validation_loss.append(epoch_validation_loss/len(validation_set))\n",
"\n",
" # Print loss every 100 epochs\n",
" if i % 100 == 0:\n",
" print(f'Epoch {i}, training loss: {training_loss[-1]}, validation
loss: {validation_loss[-1]}')\n",
"\n",
"\n",
"# Get first sentence in test set\n",
"inputs, targets = test_set[1]\n",
"\n",
"# One-hot encode input and target sequence\n",
"inputs_one_hot = one_hot_encode_sequence(inputs, vocab_size)\n",
"targets_one_hot = one_hot_encode_sequence(targets, vocab_size)\n",
"\n",
"# Initialize hidden state as zeros\n",
"hidden_state = np.zeros((hidden_size, 1))\n",
"\n",
"# Forward pass\n",
"outputs, hidden_states = forward_pass(inputs_one_hot, hidden_state, params)\
n",
"output_sentence = [idx_to_word[np.argmax(output)] for output in outputs]\n",
"print('Input sentence:')\n",
"print(inputs)\n",
"\n",
"print('\\nTarget sequence:')\n",
"print(targets)\n",
"\n",
"print('\\nPredicted sequence:')\n",
"print([idx_to_word[np.argmax(output)] for output in outputs])\n",
"\n",
"# Plot training and validation loss\n",
"epoch = np.arange(len(training_loss))\n",
"plt.figure()\n",
"plt.plot(epoch, training_loss, 'r', label='Training loss',)\n",
"plt.plot(epoch, validation_loss, 'b', label='Validation loss')\n",
"plt.legend()\n",
"plt.xlabel('Epoch'), plt.ylabel('NLL')\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercise:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Complete the training loop above and run the training. You can leave the
hyper-parameters and network size unchanged.\n",
"\n",
"A correct implementation should yield a loss **below 1.0** after 2000 epochs.
How well does it predict the sequence?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Extrapolation"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now that we have trained a RNN model, it's time to put it to test. We will
provide the network with a starting sentence and let it `freestyle` from there!"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Example:\n",
"['a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b']\n"
]
}
],
"source": [
"def freestyle(params, sentence='', num_generate=4):\n",
" \"\"\"\n",
" Takes in a sentence as a string and outputs a sequence\n",
" based on the predictions of the RNN.\n",
" \n",
" Args:\n",
" `params`: the parameters of the network\n",
" `sentence`: string with whitespace-separated tokens\n",
" `num_generate`: the number of tokens to generate\n",
" \"\"\"\n",
" sentence = sentence.split(' ')\n",
" \n",
" sentence_one_hot = one_hot_encode_sequence(sentence, vocab_size)\n",
" \n",
" # Initialize hidden state as zeros\n",
" hidden_state = np.zeros((hidden_size, 1))\n",
"\n",
" # Generate hidden state for sentence\n",
" outputs, hidden_states = forward_pass(sentence_one_hot, hidden_state,
params)\n",
" \n",
" # Output sentence\n",
" output_sentence = sentence\n",
" \n",
" # Append first prediction\n",
" word = idx_to_word[np.argmax(outputs[-1])] \n",
" output_sentence.append(word)\n",
" \n",
" # Forward pass\n",
" for i in range(num_generate):\n",
"\n",
" # Get the latest prediction and latest hidden state\n",
" output = outputs[-1]\n",
" hidden_state = hidden_states[-1]\n",
" \n",
" # Reshape our output to match the input shape of our forward pass\n",
" output = output.reshape(1, output.shape[0], output.shape[1])\n",
" \n",
" # Forward pass\n",
" outputs, hidden_states = forward_pass(output, hidden_state, params)\
n",
" \n",
" # Compute the index the most likely word and look up the corresponding
word\n",
" word = idx_to_word[np.argmax(outputs)]\n",
" \n",
" output_sentence.append(word)\n",
" \n",
" return output_sentence\n",
" \n",
"# Perform freestyle\n",
"print('Example:')\n",
"print(freestyle(params, sentence='a a a a a b'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercise:\n",
"\n",
"What did you observe while building and training the model? How well does it
freestyle? How many epochs are (roughly) needed for learning to make good
predictions?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercise (optional):"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Alter the forward pass, backward pass and training loop to handle batches of
inputs. You will see a great performance increase!"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "sGeKM44bvISA"
},
"source": [
"# Introduction to the Long Short-Term Memory (LSTM) Cell\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "X44hQ653vNCj"
},
"source": [
"Reading material: [Christopher Olah's
walk-through](http://colah.github.io/posts/2015-08-Understanding-LSTMs/).\n",
"\n",
"___\n",
"\n",
"\n",
"A vanilla RNN suffers from [the vanishing gradients
problem](http://neuralnetworksanddeeplearning.com/chap5.html#the_vanishing_gradient
_problem) which gives challenges in saving memory over longer sequences. To combat
these issues the gated hidden units were created. The two most prominent gated
hidden units are the Long Short-Term Memory (LSTM) cell and the Gated Recurrent
Unit (GRU), both of which have shown increased performance in saving and reusing
memory in later timesteps. In this exercise, we will focus on LSTM but you would
easily be able to go ahead and implement the GRU as well based on the principles
that you learn here.\n",
"\n",
"Below is a figure of the LSTM cell:"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "5Rgc-g3zwV9f"
},
"source": [
"![lstm](https://i.imgur.com/3VkmUCe.png)\n",
"Source: https://arxiv.org/abs/1412.7828"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "ytasZ5cqw4W1"
},
"source": [
"\n",
"The LSTM cell contains three gates, input, forget, output gates and a memory
cell.\n",
"The output of the LSTM unit is computed with the following functions, where
$\\sigma = \\mathrm{softmax}$.\n",
"We have input gate $i$, forget gate $f$, and output gate $o$ defines as\n",
"\n",
"- $i = \\sigma ( W^i [h_{t-1}, x_t])$\n",
"\n",
"- $f = \\sigma ( W^f [h_{t-1},x_t])$\n",
"\n",
"- $o = \\sigma ( W^o [h_{t-1},x_t])$\n",
"\n",
"where $W^i, W^f, W^o$ are weight matrices applied to a concatenated $h_{t-1}$
(hidden state vector) and $x_t$ (input vector) for each respective gate.\n",
"\n",
"$h_{t-1}$, from the previous time step along with the current input $x_t$ are
used to compute the a candidate $g$\n",
"\n",
"- $g = \\mathrm{tanh}( W^g [h_{t-1}, x_t])$\n",
"\n",
"The value of the cell's memory, $c_t$, is updated as\n",
"\n",
"- $c_t = c_{t-1} \\circ f + g \\circ i$\n",
"\n",
"where $c_{t-1}$ is the previous memory, and $\\circ$ refers to element-wise
multiplication.\n",
"\n",
"The output, $h_t$, is computed as\n",
"\n",
"- $h_t = \\mathrm{tanh}(c_t) \\circ o$\n",
"\n",
"and it is used for both the timestep's output and the next timestep, whereas
$c_t$ is exclusively sent to the next timestep.\n",
"This makes $c_t$ a memory feature, and is not used directly to compute the
output of the timestep."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Initialiation of a LSTM network"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Similarly to before we will implement the forward pass, backward pass,
optimization and training loop, now for an LSTM in numpy. Later, we will go back to
PyTorch and appreciate how convenient the implementation becomes."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [],
"source": [
"# Size of concatenated hidden + input vector\n",
"z_size = hidden_size + vocab_size \n",
"\n",
"def init_lstm(hidden_size, vocab_size, z_size):\n",
" \"\"\"\n",
" Initializes our LSTM network.\n",
" \n",
" Args:\n",
" `hidden_size`: the dimensions of the hidden state\n",
" `vocab_size`: the dimensions of our vocabulary\n",
" `z_size`: the dimensions of the concatenated input \n",
" \"\"\"\n",
" # Weight matrix (forget gate)\n",
" # YOUR CODE HERE!\n",
" W_f = np.random.randn(hidden_size, z_size)\n",
" \n",
" # Bias for forget gate\n",
" b_f = np.zeros((hidden_size, 1))\n",
"\n",
" # Weight matrix (input gate)\n",
" # YOUR CODE HERE!\n",
" W_i = np.random.randn(hidden_size, z_size)\n",
" \n",
" # Bias for input gate\n",
" b_i = np.zeros((hidden_size, 1))\n",
"\n",
" # Weight matrix (candidate)\n",
" # YOUR CODE HERE!\n",
" W_g = np.random.randn(hidden_size, z_size)\n",
" \n",
" # Bias for candidate\n",
" b_g = np.zeros((hidden_size, 1))\n",
"\n",
" # Weight matrix of the output gate\n",
" # YOUR CODE HERE!\n",
" W_o = np.random.randn(hidden_size, z_size)\n",
" b_o = np.zeros((hidden_size, 1))\n",
"\n",
" # Weight matrix relating the hidden-state to the output\n",
" # YOUR CODE HERE!\n",
" W_v = np.random.randn(vocab_size, hidden_size)\n",
" b_v = np.zeros((vocab_size, 1))\n",
" \n",
" # Initialize weights according to https://arxiv.org/abs/1312.6120\n",
" W_f = init_orthogonal(W_f)\n",
" W_i = init_orthogonal(W_i)\n",
" W_g = init_orthogonal(W_g)\n",
" W_o = init_orthogonal(W_o)\n",
" W_v = init_orthogonal(W_v)\n",
"\n",
" return W_f, W_i, W_g, W_o, W_v, b_f, b_i, b_g, b_o, b_v\n",
"\n",
"\n",
"params = init_lstm(hidden_size=hidden_size, vocab_size=vocab_size,
z_size=z_size)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercise:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Implement the initlizations in the code above. Refer to the equations and the
figure if you're in doubt."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Forward pass"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As before we need to bring the equations into pieces of code. This is done in
the following function:"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Input sentence:\n",
"['a', 'a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b', 'b']\n",
"\n",
"Target sequence:\n",
"['a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'EOS']\n",
"\n",
"Predicted sequence:\n",
"['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'EOS']\n"
]
}
],
"source": [
"def forward(inputs, h_prev, C_prev, p):\n",
" \"\"\"\n",
" Arguments:\n",
" x -- your input data at timestep \"t\", numpy array of shape (n_x, m).\n",
" h_prev -- Hidden state at timestep \"t-1\", numpy array of shape (n_a, m)\
n",
" C_prev -- Memory state at timestep \"t-1\", numpy array of shape (n_a, m)\
n",
" p -- python list containing:\n",
" W_f -- Weight matrix of the forget gate, numpy array
of shape (n_a, n_a + n_x)\n",
" b_f -- Bias of the forget gate, numpy array of shape
(n_a, 1)\n",
" W_i -- Weight matrix of the update gate, numpy array
of shape (n_a, n_a + n_x)\n",
" b_i -- Bias of the update gate, numpy array of shape
(n_a, 1)\n",
" W_g -- Weight matrix of the first \"tanh\", numpy
array of shape (n_a, n_a + n_x)\n",
" b_g -- Bias of the first \"tanh\", numpy array of
shape (n_a, 1)\n",
" W_o -- Weight matrix of the output gate, numpy array
of shape (n_a, n_a + n_x)\n",
" b_o -- Bias of the output gate, numpy array of shape
(n_a, 1)\n",
" W_v -- Weight matrix relating the hidden-state to the
output, numpy array of shape (n_v, n_a)\n",
" b_v -- Bias relating the hidden-state to the output,
numpy array of shape (n_v, 1)\n",
" Returns:\n",
" z_s, f_s, i_s, g_s, C_s, o_s, h_s, v_s -- lists of size m containing the
computations in each forward pass\n",
" outputs -- prediction at timestep \"t\", numpy array of shape (n_v, m)\n",
" \"\"\"\n",
" assert h_prev.shape == (hidden_size, 1)\n",
" assert C_prev.shape == (hidden_size, 1)\n",
"\n",
" # First we unpack our parameters\n",
" W_f, W_i, W_g, W_o, W_v, b_f, b_i, b_g, b_o, b_v = p\n",
" \n",
" # Save a list of computations for each of the components in the LSTM\n",
" x_s, z_s, f_s, i_s, = [], [] ,[], []\n",
" g_s, C_s, o_s, h_s = [], [] ,[], []\n",
" v_s, output_s = [], [] \n",
" \n",
" # Append the initial cell and hidden state to their respective lists\n",
" h_s.append(h_prev)\n",
" C_s.append(C_prev)\n",
" \n",
" for x in inputs:\n",
" \n",
" # YOUR CODE HERE!\n",
" # Concatenate input and hidden state\n",
" z = np.row_stack((h_prev, x))\n",
" z_s.append(z)\n",
" \n",
" # YOUR CODE HERE!\n",
" # Calculate forget gate\n",
" f = sigmoid(np.dot(W_f, z) + b_f)\n",
" f_s.append(f)\n",
" \n",
" # Calculate input gate\n",
" i = sigmoid(np.dot(W_i, z) + b_i)\n",
" i_s.append(i)\n",
" \n",
" # Calculate candidate\n",
" g = tanh(np.dot(W_g, z) + b_g)\n",
" g_s.append(g)\n",
" \n",
" # YOUR CODE HERE!\n",
" # Calculate memory state\n",
" C_prev = f * C_prev + i * g \n",
" C_s.append(C_prev)\n",
" \n",
" # Calculate output gate\n",
" o = sigmoid(np.dot(W_o, z) + b_o)\n",
" o_s.append(o)\n",
" \n",
" # Calculate hidden state\n",
" h_prev = o * tanh(C_prev)\n",
" h_s.append(h_prev)\n",
"\n",
" # Calculate logits\n",
" v = np.dot(W_v, h_prev) + b_v\n",
" v_s.append(v)\n",
" \n",
" # Calculate softmax\n",
" output = softmax(v)\n",
" output_s.append(output)\n",
"\n",
" return z_s, f_s, i_s, g_s, C_s, o_s, h_s, v_s, output_s\n",
"\n",
"\n",
"# Get first sentence in test set\n",
"inputs, targets = test_set[1]\n",
"\n",
"# One-hot encode input and target sequence\n",
"inputs_one_hot = one_hot_encode_sequence(inputs, vocab_size)\n",
"targets_one_hot = one_hot_encode_sequence(targets, vocab_size)\n",
"\n",
"# Initialize hidden state as zeros\n",
"h = np.zeros((hidden_size, 1))\n",
"c = np.zeros((hidden_size, 1))\n",
"\n",
"# Forward pass\n",
"z_s, f_s, i_s, g_s, C_s, o_s, h_s, v_s, outputs = forward(inputs_one_hot, h,
c, params)\n",
"\n",
"output_sentence = [idx_to_word[np.argmax(output)] for output in outputs]\n",
"print('Input sentence:')\n",
"print(inputs)\n",
"\n",
"print('\\nTarget sequence:')\n",
"print(targets)\n",
"\n",
"print('\\nPredicted sequence:')\n",
"print([idx_to_word[np.argmax(output)] for output in outputs])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercise:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Complete the implementation of the LSTM forward pass above. Refer to the
equations and figures further up if you're in doubt."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Backward pass"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Similar to the RNN in numpy we also need to specify a backward pass.
Fortunately some of the TA's have a lot of free time to do that for you :-) "
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"We get a loss of:\n",
"4.940597374930116\n"
]
}
],
"source": [
"def backward(z, f, i, g, C, o, h, v, outputs, targets, p = params):\n",
" \"\"\"\n",
" Arguments:\n",
" z -- your concatenated input data as a list of size m.\n",
" f -- your forget gate computations as a list of size m.\n",
" i -- your input gate computations as a list of size m.\n",
" g -- your candidate computations as a list of size m.\n",
" C -- your Cell states as a list of size m+1.\n",
" o -- your output gate computations as a list of size m.\n",
" h -- your Hidden state computations as a list of size m+1.\n",
" v -- your logit computations as a list of size m.\n",
" outputs -- your outputs as a list of size m.\n",
" targets -- your targets as a list of size m.\n",
" p -- python list containing:\n",
" W_f -- Weight matrix of the forget gate, numpy array
of shape (n_a, n_a + n_x)\n",
" b_f -- Bias of the forget gate, numpy array of shape
(n_a, 1)\n",
" W_i -- Weight matrix of the update gate, numpy array
of shape (n_a, n_a + n_x)\n",
" b_i -- Bias of the update gate, numpy array of shape
(n_a, 1)\n",
" W_g -- Weight matrix of the first \"tanh\", numpy
array of shape (n_a, n_a + n_x)\n",
" b_g -- Bias of the first \"tanh\", numpy array of
shape (n_a, 1)\n",
" W_o -- Weight matrix of the output gate, numpy array
of shape (n_a, n_a + n_x)\n",
" b_o -- Bias of the output gate, numpy array of shape
(n_a, 1)\n",
" W_v -- Weight matrix relating the hidden-state to the
output, numpy array of shape (n_v, n_a)\n",
" b_v -- Bias relating the hidden-state to the output,
numpy array of shape (n_v, 1)\n",
" Returns:\n",
" loss -- crossentropy loss for all elements in output\n",
" grads -- lists of gradients of every element in p\n",
" \"\"\"\n",
"\n",
" # Unpack parameters\n",
" W_f, W_i, W_g, W_o, W_v, b_f, b_i, b_g, b_o, b_v = p\n",
"\n",
" # Initialize gradients as zero\n",
" W_f_d = np.zeros_like(W_f)\n",
" b_f_d = np.zeros_like(b_f)\n",
"\n",
" W_i_d = np.zeros_like(W_i)\n",
" b_i_d = np.zeros_like(b_i)\n",
"\n",
" W_g_d = np.zeros_like(W_g)\n",
" b_g_d = np.zeros_like(b_g)\n",
"\n",
" W_o_d = np.zeros_like(W_o)\n",
" b_o_d = np.zeros_like(b_o)\n",
"\n",
" W_v_d = np.zeros_like(W_v)\n",
" b_v_d = np.zeros_like(b_v)\n",
" \n",
" # Set the next cell and hidden state equal to zero\n",
" dh_next = np.zeros_like(h[0])\n",
" dC_next = np.zeros_like(C[0])\n",
" \n",
" # Track loss\n",
" loss = 0\n",
" \n",
" for t in reversed(range(len(outputs))):\n",
" \n",
" # Compute the cross entropy\n",
" loss += -np.mean(np.log(outputs[t]) * targets[t])\n",
" # Get the previous hidden cell state\n",
" C_prev= C[t-1]\n",
" \n",
" # Compute the derivative of the relation of the hidden-state to the
output gate\n",
" dv = np.copy(outputs[t])\n",
" dv[np.argmax(targets[t])] -= 1\n",
"\n",
" # Update the gradient of the relation of the hidden-state to the
output gate\n",
" W_v_d += np.dot(dv, h[t].T)\n",
" b_v_d += dv\n",
"\n",
" # Compute the derivative of the hidden state and output gate\n",
" dh = np.dot(W_v.T, dv) \n",
" dh += dh_next\n",
" do = dh * tanh(C[t])\n",
" do = sigmoid(o[t], derivative=True)*do\n",
" \n",
" # Update the gradients with respect to the output gate\n",
" W_o_d += np.dot(do, z[t].T)\n",
" b_o_d += do\n",
"\n",
" # Compute the derivative of the cell state and candidate g\n",
" dC = np.copy(dC_next)\n",
" dC += dh * o[t] * tanh(tanh(C[t]), derivative=True)\n",
" dg = dC * i[t]\n",
" dg = tanh(g[t], derivative=True) * dg\n",
" \n",
" # Update the gradients with respect to the candidate\n",
" W_g_d += np.dot(dg, z[t].T)\n",
" b_g_d += dg\n",
"\n",
" # Compute the derivative of the input gate and update its gradients\
n",
" di = dC * g[t]\n",
" di = sigmoid(i[t], True) * di\n",
" W_i_d += np.dot(di, z[t].T)\n",
" b_i_d += di\n",
"\n",
" # Compute the derivative of the forget gate and update its gradients\
n",
" df = dC * C_prev\n",
" df = sigmoid(f[t]) * df\n",
" W_f_d += np.dot(df, z[t].T)\n",
" b_f_d += df\n",
"\n",
" # Compute the derivative of the input and update the gradients of the
previous hidden and cell state\n",
" dz = (np.dot(W_f.T, df)\n",
" + np.dot(W_i.T, di)\n",
" + np.dot(W_g.T, dg)\n",
" + np.dot(W_o.T, do))\n",
" dh_prev = dz[:hidden_size, :]\n",
" dC_prev = f[t] * dC\n",
" \n",
" grads= W_f_d, W_i_d, W_g_d, W_o_d, W_v_d, b_f_d, b_i_d, b_g_d, b_o_d,
b_v_d\n",
" \n",
" # Clip gradients\n",
" grads = clip_gradient_norm(grads)\n",
" \n",
" return loss, grads\n",
"\n",
"\n",
"# Perform a backward pass\n",
"loss, grads = backward(z_s, f_s, i_s, g_s, C_s, o_s, h_s, v_s, outputs,
targets_one_hot, params)\n",
"\n",
"print('We get a loss of:')\n",
"print(loss)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Training loop"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now let's see if the LSTM works after being trained for a few epochs."
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Epoch 0, training loss: 2.9126594208151486, validation loss:
3.7627056786333304\n",
"Epoch 5, training loss: 1.2746721453770287, validation loss:
1.3007947595266003\n",
"Epoch 10, training loss: 1.1492599735091498, validation loss:
1.139792652078412\n",
"Epoch 15, training loss: 0.9836092135358966, validation loss:
0.9939491353315226\n",
"Epoch 20, training loss: 0.8791899647621024, validation loss:
0.8975219678277728\n",
"Epoch 25, training loss: 0.8008628436073784, validation loss:
0.8188702401659995\n",
"Epoch 30, training loss: 0.7547721298664058, validation loss:
0.7598792343202023\n",
"Epoch 35, training loss: 0.7316903442197215, validation loss:
0.7280875207336994\n",
"Epoch 40, training loss: 0.7181688528186634, validation loss:
0.7100343175260162\n",
"Epoch 45, training loss: 0.7091859818959984, validation loss:
0.69897796740037\n",
"Input sentence:\n",
"['a', 'a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b', 'b']\n",
"\n",
"Target sequence:\n",
"['a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'EOS']\n",
"\n",
"Predicted sequence:\n",
"['a', 'a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b', 'EOS']\n"
]
},
{
"data": {
"image/png":
"iVBORw0KGgoAAAANSUhEUgAAAYIAAAEGCAYAAABo25JHAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEg
AACxIB0t1+/
AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9y
Zy8QZhcZAAAgAElEQVR4nO3deXxV1b3//9cnEwkkJBCiDEECipCE2YhotAwOValSbLQq1GptudL+alvrrdZ
ap9Z7tddai/
W2tdahaqX+pM5aHEBRtEDgQpgNKEIYQyAhgUwnWd8/9kkIIYQhOTlJ9vv5eOzH2WefnZPPxmPeZ+2119rmn
ENERPwrItwFiIhIeCkIRER8TkEgIuJzCgIREZ9TEIiI+FxUuAs4Xr169XJpaWnhLkNEpENZunTpbudcSlOv
dbggSEtLIzc3N9xliIh0KGb25ZFe06khERGfUxCIiPicgkBExOc6XB+BiLSt6upqCgoKqKioCHcpcgxiY2N
JTU0lOjr6mH9GQSAizSooKCAhIYG0tDTMLNzlSDOccxQVFVFQUMDAgQOP+ed0akhEmlVRUUFycrJCoAMwM5
KTk4+79aYgEJGjUgh0HCfy38o3QbBqFdx5JxQVhbsSEZH2xTdBkJ8P998PW7aEuxIROR5FRUWMGjWKUaNG0
bt3b/r161f/vKqq6pje44YbbmD9+vXN7vPYY4/x/
PPPt0bJnHvuuSxfvrxV3qst+KazuGdP73HPnvDWISLHJzk5uf6P6j333EN8fDy33nrrIfs453DOERHR9Hfb
p5566qi/5wc/
+EHLi+2gfNMiUBCIdC4bNmwgIyODadOmkZmZyfbt25kxYwZZWVlkZmZy33331e9b9w09EAiQlJTE7bffzsi
RIzn77LPZtWsXAHfeeSePPPJI/f633347Y8eOZciQIXzyyScA7N+/n2984xtkZGSQk5NDVlbWUb/5P/
fccwwfPpxhw4Zxxx13ABAIBPjWt75Vv33WrFkA/
O53vyMjI4MRI0Ywffr0Vv83OxK1CETk2P34x9DapzxGjYLgH+DjtW7dOv72t7+RlZUFwAMPPEDPnj0JBAJM
nDiRnJwcMjIyDvmZkpISxo8fzwMPPMAtt9zCk08+ye23337YezvnWLx4Ma+99hr33Xcf//
rXv3j00Ufp3bs3c+bMYcWKFYwZM6bZ+goKCrjzzjvJzc0lMTGRCy64gDfeeIOUlBR2797NypUrASguLgbgN
7/5DV9+
+SUxMTH129qC71oE6iwW6TxOPfXU+hAAeOGFFxgzZgxjxoxh7dq1rFmz5rCfiYuL45JLLgHgjDPOYNOmTU2
+9xVXXHHYPh9//DFXX301ACNHjiQzM7PZ+hYtWsSkSZPo1asX0dHRXHvttSxYsIDTTjuN9evXc/
PNNzN37lwSExMByMzMZPr06Tz//PPHNSCspXzTIoiL8xa1CERa4AS/uYdKt27d6tfz8/P5/e9/
z+LFi0lKSmL69OlNXk8fExNTvx4ZGUkgEGjyvbt06XLUfU5UcnIyeXl5vP322zz22GPMmTOHxx9/nLlz5/
Lhhx/y2muv8V//9V/k5eURGRnZqr+7Kb5pEYDXKlAQiHRO+/
btIyEhge7du7N9+3bmzp3b6r8jOzubF198EYCVK1c22eJo6KyzzmL+/
PkUFRURCASYPXs248ePp7CwEOccV155Jffddx/
Lli2jpqaGgoICJk2axG9+8xt2797NgQMHWv0YmuKbFgEoCEQ6szFjxpCRkcHQoUMZMGAA2dnZrf47fvjDH3
LdddeRkZFRv9Sd1mlKamoqv/
rVr5gwYQLOOS677DImT57MsmXLuPHGG3HOYWY8+OCDBAIBrr32WkpLS6mtreXWW28lISGh1Y+hKeaca5Nf1
FqysrLcid6YZsIEcA4+/
LB1axLpzNauXUt6enq4y2gXAoEAgUCA2NhY8vPzueiii8jPzycqqn19p27qv5mZLXXOZTW1f/
uqPsSSk+EoY0pERI6orKyM888/n0AggHOOP//
5z+0uBE5Exz+C46BTQyLSEklJSSxdujTcZbQ6X3YWd7CzYSIiIeW7IKishPLycFciItJ+
+C4IQKeHREQaClkQmFmsmS02sxVmttrM7m1in+vNrNDMlgeX74aqHvA6i0Gji0VEGgpli6ASmOScGwmMAi4
2s3FN7PcP59yo4PJECOtRi0CkA5o4ceJhg8MeeeQRZs6c2ezPxcfHA7Bt2zZycnKa3GfChAkc7XL0Rx555J
CBXZdeemmrzAN0zz338NBDD7X4fVpDyILAecqCT6ODS1i7aRUEIh3PNddcw+zZsw/
ZNnv2bK655ppj+vm+ffvy0ksvnfDvbxwEb731FklJSSf8fu1RSPsIzCzSzJYDu4B3nXOLmtjtG2aWZ2YvmV
n/I7zPDDPLNbPcwsLCE65HQSDS8eTk5PDmm2/
W34Rm06ZNbNu2jfPOO6/+uv4xY8YwfPhwXn311cN+ftOmTQwbNgyA8vJyrr76atLT05k6dSrlDa4cmTlzZv
0U1nfffTcAs2bNYtu2bUycOJGJEycCkJaWxu7duwF4+OGHGTZsGMOGDaufwnrTpk2kp6fzve99j8zMTC666
KJDfk9Tli9fzrhx4xgxYgRTp05l79699b+/blrqusnuPvzww/ob84wePZrS0tIT/
retE9JxBM65GmCUmSUBL5vZMOfcqga7vA684JyrNLP/
AJ4BJjXxPo8Dj4M3svhE61EQiLRMOGah7tmzJ2PHjuXtt99mypQpzJ49m6uuugozIzY2lpdffpnu3buze/
duxo0bx+WXX37E+/b+8Y9/pGvXrqxdu5a8vLxDppG+//776dmzJzU1NZx//vnk5eVx88038/DDDzN//
nx69ep1yHstXbqUp556ikWLFuGc46yzzmL8+PH06NGD/
Px8XnjhBf7yl79w1VVXMWfOnGbvL3Ddddfx6KOPMn78eO666y7uvfdeHnnkER544AG+
+OILunTpUn866qGHHuKxxx4jOzubsrIyYmNjj+Nfu2ltctWQc64YmA9c3Gh7kXOuMvj0CeCMUNbRtSvExqq
zWKSjaXh6qOFpIeccd9xxByNGjOCCCy5g69at7Ny584jvs2DBgvo/
yCNGjGDEiBH1r7344ouMGTOG0aNHs3r16qNOKPfxxx8zdepUunXrRnx8PFdccQUfffQRAAMHDmTUqFFA81N
dg3d/hOLiYsaPHw/
At7/9bRYsWFBf47Rp03juuefqRzBnZ2dzyy23MGvWLIqLi1tlZHPIWgRmlgJUO+eKzSwOuBB4sNE+fZxz24
NPLwfWhqqeOhpdLHLiwjUL9ZQpU/jJT37CsmXLOHDgAGec4X1nfP755yksLGTp0qVER0eTlpbW5NTTR/
PFF1/w0EMPsWTJEnr06MH1119/Qu9Tp24Ka/CmsT7aqaEjefPNN1mwYAGvv/46999/
PytXruT2229n8uTJvPXWW2RnZzN37lyGDh16wrVCaFsEfYD5ZpYHLMHrI3jDzO4zs8uD+9wcvLR0BXAzcH0
I6wEUBCIdUXx8PBMnTuQ73/nOIZ3EJSUlnHTSSURHRzN//ny+/PLLZt/nK1/5Cn//
+98BWLVqFXl5eYA3hXW3bt1ITExk586dvP322/U/k5CQ0OR5+PPOO49XXnmFAwcOsH//fl5++WXOO+
+84z62xMREevToUd+aePbZZxk/
fjy1tbVs2bKFiRMn8uCDD1JSUkJZWRkbN25k+PDh3HbbbZx55pmsW7fuuH9nYyFrETjn8oDRTWy/
q8H6z4Gfh6qGpigIRDqma665hqlTpx5yBdG0adO47LLLGD58OFlZWUf9Zjxz5kxuuOEG0tPTSU9Pr29ZjBw
5ktGjRzN06FD69+9/yBTWM2bM4OKLL6Zv377Mnz+/fvuYMWO4/vrrGTt2LADf/
e53GT16dLOngY7kmWee4aabbuLAgQMMGjSIp556ipqaGqZPn05JSQnOOW6++WaSkpL45S9/
yfz584mIiCAzM7P+bmst4atpqAGmToWNGyH4RUBEjkLTUHc8xzsNta+mmABvdLE6i0VEDvJdEOjUkIjIoXw
ZBBUVmoFU5Hh0tFPIfnYi/
618GQSgVoHIsYqNjaWoqEhh0AE45ygqKjruQWa+ukMZHDoDab9+4a1FpCNITU2loKCAlkzvIm0nNjaW1NTU
4/
oZ3wWBWgQixyc6OpqBAweGuwwJIZ0aEhHxOQWBiIjPKQhERHzOd0HQtSt06aJBZSIidXwXBGYaVCYi0pDvg
gAUBCIiDSkIRER8TkEgIuJzvgwCzUAqInKQL4NALQIRkYN8GwTl5ZqBVEQEfBwEAHv3hrcOEZH2wNdBoNND
IiI+DYKGU1GLiPidL4NALQIRkYMUBCIiPheyIDCzWDNbbGYrzGy1md3bxD5dzOwfZrbBzBaZWVqo6mlIQSA
iclAoWwSVwCTn3EhgFHCxmY1rtM+NwF7n3GnA74AHQ1hPvW7dIDpaQSAiAiEMAucpCz6NDi6N7349BXgmuP
4ScL6ZWahqqmOm0cUiInVC2kdgZpFmthzYBbzrnFvUaJd+wBYA51wAKAGSm3ifGWaWa2a5rXUDbY0uFhHxh
DQInHM1zrlRQCow1syGneD7PO6cy3LOZaWkpLRKbQoCERFPm1w15JwrBuYDFzd6aSvQH8DMooBEoE1O2CgI
REQ8obxqKMXMkoLrccCFwLpGu70GfDu4ngPMc8417kcICQWBiIgnKoTv3Qd4xswi8QLnRefcG2Z2H5DrnHs
N+CvwrJltAPYAV4ewnkOos1hExBOyIHDO5QGjm9h+V4P1CuDKUNVwiPfegzvugH/
+E1JT6dkTDhyAigqIjW2TCkRE2iX/jCyuqYElS+DLLwHNQCoiUsc/QdC/v/
e4eTOg0cUiInX8FwRbtgAKAhGROv4JgoQESEysDwJNRS0i4vFPEACccopaBCIijfgrCPr3Vx+BiEgj/
guCYIsgPh6iohQEIiL+C4Ldu6G8HDONLhYRAT8GAUBBAaDRxSIi4LcgOOUU77FBP4FaBCLid/
4KgibGEigIRMTv/BUEqaneo4JARKSev4KgSxc46aRDBpUpCETE7/wVBOD1EzToIygrg6qqMNckIhJG/
guCBmMJNKhMRERBACgIRMTf/BkEpaVQUqIgEBHBr0EAsHlz/QykCgIR8TP/
BUHdoLItW+pbBBpdLCJ+5r8gaDCoTKeGRET8GAR9+kBkJGzZQkKCt6ogEBE/
818QREZC376webNmIBURwY9BAIfcqUyji0XE70IWBGbW38zmm9kaM1ttZj9qYp8JZlZiZsuDy12hqucQjcY
SqLNYRPwsKoTvHQB+6pxbZmYJwFIze9c5t6bRfh85574WwjoO178/
vPwy1NbSs2cEW7e26W8XEWlXQtYicM5td84tC66XAmuBfqH6fcelf3+orITCQvURiIjvtUkfgZmlAaOBRU2
8fLaZrTCzt80ssy3qaXwJqYJARPws5EFgZvHAHODHzrl9jV5eBgxwzo0EHgVeOcJ7zDCzXDPLLSwsbHlRDQ
aVJSd7M05UV7f8bUVEOqKQBoGZReOFwPPOuX82ft05t885VxZcfwuINrNeTez3uHMuyzmXlZKS0vLCNKhMR
KReKK8aMuCvwFrn3MNH2Kd3cD/MbGywntBfw9OrF8TGwubNCgIR8b1QXjWUDXwLWGlmy4Pb7gBOAXDO/
QnIAWaaWQAoB652zrkQ1uQx825buWULPS/yNikIRMSvQhYEzrmPATvKPn8A/hCqGpoVHFSmFoGI+J0/
RxZD/
aCyXsEeiV27wluOiEi4+DsItm0jtXeAmBj47LNwFyQiEh7+DoLaWqJ2bWPIEFjTeLyziIhP+DsIALZsISND
QSAi/
uXfIGgwqCw9Hb74AsrLw1uSiEg4nHAQmNmPW7OQNteoReAcrF8f3pJERMKhJS2CW1qtinDo3t1bNm8mI8Pb
pNNDIuJHLQmCZscIdAjBS0gHD/ZuXKYgEBE/
akkQhH4EcKgFB5XFxMDgwbB2bbgLEhFpe82OLDazUpr+g29A15BU1Jb694fcXADS09UiEBF/
ajYInHMJbVVIWPTvD4WFUF5ORkYcr70GVVUQExPuwkRE2k5Lrhra3JqFhEXdlUMFBWRkQE0N5OeHtyQRkba
mzmKov4QUdHpIRPxHncUAW7YwZIg3O7U6jEXEb47WWXyksQIGxLd+OW0sNdV73LyZuDgYNEgtAhHxn6Pdj6
C5zuLft2YhYREbCykpsGULoCuHRMSfjnbV0L1tVUjYBAeVAWRkwDvvQCAAUaG8d5uISDtytFNDdzXzsnPO/
aqV62l7p5xSf6lQRoZ3+ejnn8Ppp4e5LhGRNnK0zuL9TSwANwK3hbCuttO/
P2z2roStu3JIHcYi4ifNBoFz7rd1C/A4EAfcAMwGBrVBfaHXvz+UlkJJCUOHepvUTyAifnLUy0fNrKeZ/
RrIwzuVNMY5d5tzrnPc5bfBWIKEBO+pgkBE/KTZIDCz/
wGWAKXAcOfcPc65vW1SWVtpEASA7lYmIr5ztBbBT4G+wJ3ANjPbF1xKzWxf6MtrA4OCZ7iCd6XJyPD6CGpr
w1iTiEgbOlofQYRzLs45l+Cc695gSXDOdW+rIkOqTx+vVfDpp4AXBOXl9f3HIiKdXsjuWWxm/
c1svpmtMbPVZvajJvYxM5tlZhvMLM/
MxoSqnmZlZ8PCheCc5hwSEd8J5c3rA8BPnXMZwDjgB2aW0WifS4DBwWUG8McQ1nNk2dmwdSts3kx6urdJQS
AifhGyIHDObXfOLQuulwJrgX6NdpsC/M15/
g0kmVmfUNV0RNnZ3uPChfToAb17KwhExD9C2SKoZ2ZpwGhgUaOX+gFbGjwv4PCwwMxmmFmumeUWFha2foHD
h0N8vHd6CF05JCL+EvIgMLN4YA7wY+fcCV1p5Jx73DmX5ZzLSklJad0CwZtY6Kyz4JNPgINXDrmOP9G2iMh
RhTQIzCwaLwSed879s4ldtgL9GzxPDW5re9nZkJcHpaVkZMC+fbBtW1gqERFpU6G8asiAvwJrnXMPH2G314
DrglcPjQNKnHPbQ1VTs7KzvcED//63OoxFxFdC2SLIBr4FTDKz5cHlUjO7ycxuCu7zFvA5sAH4C/
D9ENbTvHHjICICFi7UJaQi4ishm3XfOfcxR7mvsXPOAT8IVQ3HpXt3r9N44UJS7obkZAWBiPhDm1w11GGcc
w78+99YbU19h7GISGenIGgoOxvKymDlSjIyYPVqXTkkIp2fgqChBgPL0tNhzx4IxbAFEZH2REHQ0IAB0Lev
OoxFxFcUBA2Z1U9ApyAQEb9QEDR2zjmweTN9awtITITly8NdkIhIaCkIGgv2E9inn3DhhfD661BTE+aaRER
CSEHQ2KhR0LUrLFxITg7s2FE/
BZGISKekIGgsOhrGjoWFC7n0UoiNhTlzwl2UiEjoKAiakp0Ny5eTYGV89ateEOgexiLSWSkImpKd7XUMLFl
CTg4UFMDixeEuSkQkNBQETRk3zntcuJDLLvPOFr30UnhLEhEJFQVBU3r0gMxMWLiQxES48ELv9JCmmxCRzk
hBcCTZ2fDpp1BbS04ObNoEy5aFuygRkdanIDiS7GwoKYHVq5kyxbubpU4PiUhnpCA4kgYT0PXsCRMnekGg0
0Mi0tkoCI5k0CDo1w9eeQWAnBzYsAFWrgxzXSIirUxBcCRm8P3vw9y5sGIFX/
+6dydLnR4Skc5GQdCcmTMhPh7+53846ST4ylcUBCLS+SgImtOjB8yYAbNnw6ZN5OR4t6/
U1NQi0pkoCI7mJz/xThM9/
DBTp3qrmntIRDoTBcHRpKbCtGnwxBP0jdnNOefo9JCIdC4KgmPxs59BeTk89hg5OZCXB/
n54S5KRKR1hCwIzOxJM9tlZquO8PoEMysxs+XB5a5Q1dJiGRlw2WXw6KNccfEBQKeHRKTzCGWL4Gng4qPs8
5FzblRwuS+EtbTcz34GRUWc8t6TnHUW/PWvXiNBRKSjC1kQOOcWAHtC9f5t7txzvfsZ//
a33Hd3DRs2wC9/Ge6iRERaLtx9BGeb2Qoze9vMMo+0k5nNMLNcM8stLCxsy/oOddttsGkTF+39BzfdBA8/
DB99FL5yRERag7kQTp5jZmnAG865YU281h2odc6VmdmlwO+dc4OP9p5ZWVkuNze31Ws9JrW1MGwYxMRQ9tH
/MXKUAbBihTfuTESkvTKzpc65rKZeC1uLwDm3zzlXFlx/C4g2s17hqueYRETAf/4nrFhB/
Cfv8PTT8MUXXveBiEhHFbYgMLPeZmbB9bHBWorCVc8xmzbNm4zuF7/gvKxybrkF/
vhHeOedcBcmInJiQnn56AvAp8AQMyswsxvN7CYzuym4Sw6wysxWALOAq10oz1O1lpgYmDXLu0vNN7/
Jr+8JkJ4ON94IxcXhLk5E5PiFtI8gFMLaR9DQ//4v/OAH8J3vsOQ/
nuDsc4zp0+Hpp8NdmIjI4dplH0GH9/3vw113wZNPcuYrv+COO+CZZ+DVV8NdmIjI8VEQtMQ993izk/
73f3Nn4qOMGuWdIvrkk3AXJiJy7BQELWHmnSK64gpibr2ZF7/1OklJMGEC/
OUv4S5OROTYKAhaKjISnn8exo9n8O3fYMmD85g0yWsozJwJVVXhLlBEpHkKgtYQG+t1DmRk0GPapbw57lfc
dmuAP/0JJk2CHTvCXaCIyJEpCFpLYiK8+y58/etE3nsXD7yWyQu/
WMWyZZCVBUuWhLtAEZGmKQhaU0qKd1vLuXOhpoar7x/OJ+N/
TnREgHPP9W52ptaBiLQ3CoJQuOgiWLUK7r6bUfMeZknx6Uwbs4ZHH3UMGgQ//
Sns3BnuIkVEPAqCUImN9S4vXbmSXmedypP/zmRd1zO46pRPeeQRx6BBjp/9DMI5maqICCgIQu/
0072JiN5/n9Muz+DpLyextnYIV0S/wW8fqmVgWi3f+x4sWOBNbioi0tYUBG3BzLt86LnnYMcOTv/
TT3l26P2sdhlcWf4sLzxZzvjxMKhfJb/4eS3r1oW7YBHxE801FE5r1sCzz7L/
rQ95JW8gzzGdd7iIWiLJOmUXV+Y4Jl+fQsawCLx5WkVETkxzcw0pCNqLwkKYN48dry7ihbeTeK54Mss4A4C
02B1cmvklk79mTLxxEHH92/dtG0Sk/VEQdDTOwYYNbHl9OW+/WsWby/
vx3r4zOUA34jjAhK6LmXR6AedPdIyckkZE1hjo1i3cVYtIO6Yg6AQqivaz4OnPefOflbyzsjfrSlMB6EkRE
/mASX3WMunscoZcMgjLPgeGDPHuqCYigoKgU9q6Fea9XMK8V/bx/
pIEtuxLAqA325nAB0yIW8yErDJOv3CAFwxnnw1xcWGuWkTCRUHQyTkHGzfC+
+85PnyzlA8+jmJ7cVcA+rCNCXzAxKiPmTh2P6denolddCGMHKkWg4iPKAh8xjnIz4cPPoAP361k/
jzH9j2xAPRnMxOZz6SEXCZOcJxyTTZMngzdu4e3aBEJKQWBzzkHn30G8+bBvDfL+WCBsbvUC4bTWc9XI97j
q1lFjL/xNOKvvAR69AhzxSLS2hQEcojaWm8qpHnv1/
LOi8V8kBtPeSCGaKo41xby1cFfcOm3khn24wuweF2NJNIZKAikWRUV8PGCWub+bQdz5xord/
cBIM02MSU9n8v/ow/n3ZRJdIxGtYl0VAoCOS7btjre/
P0GXn3hAO8VDKGSWJIiSpg8YgtTburDpdOTNWxBpINREMgJ27+zjHd+vZjX/
v9K3tiZxW5S6BpZwdfGFXHlD3tz6WWRdO0a7ipF5GiaC4KQXT9oZk+a2S4zW3WE183MZpnZBjPLM7MxoapF
Tly3k+OZ+ugkntpxCTvWlTB/
2hN8u8tsPlgYxZVXR5KSVMXVXytlzhwoLw93tSJyIkJ5IfnTwMXNvH4JMDi4zAD+GMJapBVEDjmNCc99l/
8tmc62fy5i3rg7uK7
6Sea9WU5ODqT0qObanCpeecXrdxCRjiGkp4bMLA14wzk3rInX/
gx84Jx7Ifh8PTDBObe9uffUqaF2ZutWAk88zYd/
XseL289lDt+giF4kxFUzZYpx1bVRXHCBBjWLhFtYTg0dg37AlgbPC4LbDmNmM8ws18xyC3VLr/
alXz+i7v4F52/9G39eNJrtP/xv5va8hqvKn+HN2fu4/
HJITgpw+SVVPP64NzWGiLQv4WwRvAE84Jz7OPj8feA251yzX/fVIugAampgwQKqn/sH817czRtl43mdy/
iSNABGD9nP167sygUXGmeeqdaCSFsI21VDOjUkBAKweDHu7X+x5pXPeGNVGm8wmU84h1oiiY6s4YyMcs69s
CvnfiWC7GzopdstiLS69hoEk4H/
D7gUOAuY5Zwbe7T3VBB0cIWF8O677Hn1IxbOr+LjwtNZSDZLOJMqugBwap8DjBgTyfAzujBiBIwYAYMGQWR
kmGsX6cDCEgRm9gIwAegF7ATuBqIBnHN/MjMD/oB3ZdEB4IajnRYCBUGnU1AACxdS8cG/
yX2vmIUbTmYpY8hjBPkMphbvr3/
X2Boy0h2np0cxZAicfvrBJT4+zMcg0gFoQJl0HPv2QW4u5OZSvmgFaz7dR972XuQxgtVk8llUBpsDfXENrn
Po08cxeLBx2mkctiQkhPFYRNoRBYF0bLt3w9Kl3rJiBeXL1rJhA3zGYD7jdD6LGcaG2GFsCKSx40DiIT/
au/ehrYe65dRTISYmTMcjEgYKAul8yspg5UpYsQKWL/
fWV62ibF8NGzmVDZxGfuKZ5CeM8QJjX2927Tt4eVJkpBcG6emHLzrVJJ2RgkD8wTnYvNkLhbpl1SpYtw6qq
ymhO/
kRQ1nfezzrEs9iraWzZl8q+TsSCAQOzqyalgbDhkFmpvc4bBgMHQqxseE7NJGWUhCIv1VXe3fmWbXKW+oC4
vPPwTmqiWJjTAZr+l3Imu7jWE0Gq4pTWb8tgepqLyAiImDw4MMDYvBgiIoK8/
GJHAMFgUhT9u+HtWsPDYjVq+uHP1cTRX7sCFb3vYCV8WezuiadVXv7smFHPLW1XkDExMCQIZCRcehy2mnqg
5D2RUEgcjyKi2HNGi8UVq8+uL5tGwAVdGFd5DBWpfeBTn4AAAmySURBVExkZfzZrHVDWF2SyhdF3XHOC4io
KG/sw5Ah3jJ06MH1Xr3AdI8faWMKApHWsG8frF/
v9Tk0XDZuhMpKDhDHeoawOmoUaxLPZn10JuurBpJfchJVNQfPHyUmOk491Tj1VA5ZBg2Cfv10qklCQ0EgEk
q1td7AuA0bvCU/33vctAm++IKaklK+ZADrGcJ6hrAhcigbu2Sw0Q1kU2UfqmsP/
uWPjHT0O7mGAQONAQMjGTAABgyA1FQvJFJToUcPtSjk+CkIRMKpuNgLhWAwUFBQv9Rs2caWbZFsrBnA5wzi
SwYcXGwgW10faji0iRAbHaBfz3JST66mT2/
o3S+S3gNi6D0glt59jJNPhpNOgpQUiI4OyxFLO6QgEGnPamth1y6vk3rnTtixo34JbNvFts0Btu6KZuueOA
pKE9lKXwpIZSv92EFvdtCbUro3+dZJ0WWkxO3npIQDpHSvpFdSDck9aumZDMm9IkjuHU3P3jH07BtLj75x9
EjtRlxClFocnVBzQaCzkSLhFhHhDYHu3fuwl6KAU4IL4E3xvXevN9q6sBD2roM9n7B/
+z52FATYsa2W7bsiKdwbRWFpLIVlcRRWJFC4vTsbtyaziF4UkVw/
wV9ToqmihxXTI7KUpOgyEqPL6d6lgsTYShLjqujetYbE+AAJ3WrpnuBIiIeExAhvSYokoUcU3RKjiE3sgsX
FevOMxzZ4rFtiYnSOq51QEIh0JJGR3mVHvXp5lyIFdQNODS5HVFUFpaW4km3s31nGnm0VFG2rpGhHNXsKay
guhr3Fxt59kRSXRbF3fzTFB7pQUpnE5rI49hXHURLoxn7X7ZhKjaCGeMroxv7g4x66coBu7Kcb+731iAq6R
lXRNaqKuKhq4qIDdI2pJi6mhriYWuK61BIbU0tcrPPW44y4WEdsLHSJiyA2zoiNM6Jio7AuMV64REcffGy8
3tTz6Givh/5I63XPo6K8f/9OGF4KAhG/
iImB5GQsOZn4QRBPg5bGcQgEvAuoSkuhtKSW0qIqb9lTTemeaspKApTtq6Vsn2N/maOsDMr2d2H/
gVgOVKRQWhHBzspI9ldGcaAqmv3V0ZRXRB/SaX68IqihC5XNLjFUBR/
L6UIxMVQ1uURTfdjjIesRtURH1hIV6YiO9NajoxzRUc7bVvdYlymRtURFm7ce5YiKtvrHqCiw6GDARDV6bL
jUbTv/fJg8+YT/nY5EQSAixyUqCnr29BbvbrexwaVlAgEoL/
eWAwe8paLCW8rLDz6Wl0Nl5cHXKiuhojyC8v1dqKqIprK8K5UVtVSWOyorHJWVjsoKr0FUXAWVVVBVZVRWG
ZXVRnXAW6oCEcHlKDe+qA0u1S0+ZMALsSirIZIaoqghygJE4S3etuC6q2HGqs+5pfVzQEEgIu1DVJQ3bfiJ
TR1uQGRwaRnnvFCqrvaWqqqm1xvu0/B548e69bql4Ws1NRAIRAYX7/nB7dRvq1s/
+fLTW3x8TVEQiIg0YHawi8AvIo6+i4iIdGYKAhERn1MQiIj4nIJARMTnFAQiIj6nIBAR8TkFgYiIzykIRER
8rsNNQ21mhcCXJ/jjvYDdrVhOR+LXY9dx+4uO+8gGOOdSmnqhwwVBS5hZ7pHm4+7s/
HrsOm5/0XGfGJ0aEhHxOQWBiIjP+S0IHg93AWHk12PXcfuLjvsE+KqPQEREDue3FoGIiDSiIBAR8TnfBIGZ
XWxm681sg5ndHu56QsXMnjSzXWa2qsG2nmb2rpnlBx97hLPGUDCz/mY238zWmNlqM/
tRcHunPnYzizWzxWa2Injc9wa3DzSzRcHP+z/
MLCbctYaCmUWa2f+Z2RvB553+uM1sk5mtNLPlZpYb3Naiz7kvgsDMIoHHgEuADOAaM8sIb1Uh8zRwcaNttw
PvO+cGA+8Hn3c2AeCnzrkMYBzwg+B/485+7JXAJOfcSGAUcLGZjQMeBH7nnDsN2AvcGMYaQ+lHwNoGz/
1y3BOdc6MajB1o0efcF0EAjAU2OOc+d85VAbOBKWGuKSSccwuAPY02TwGeCa4/
A3y9TYtqA8657c65ZcH1Urw/
Dv3o5MfuPGXBp9HBxQGTgJeC2zvdcQOYWSowGXgi+NzwwXEfQYs+534Jgn7AlgbPC4Lb/
OJk59z24PoO4ORwFhNqZpYGjAYW4YNjD54eWQ7sAt4FNgLFzrlAcJfO+nl/BPgZUBt8now/
jtsB75jZUjObEdzWos+5bl7vM845Z2ad9pphM4sH5gA/
ds7t874kejrrsTvnaoBRZpYEvAwMDXNJIWdmXwN2OeeWmtmEcNfTxs51zm01s5OAd81sXcMXT+Rz7pcWwVa
gf4PnqcFtfrHTzPoABB93hbmekDCzaLwQeN4598/
gZl8cO4BzrhiYD5wNJJlZ3Re9zvh5zwYuN7NNeKd6JwG/p/
MfN865rcHHXXjBP5YWfs79EgRLgMHBKwpigKuB18JcU1t6Dfh2cP3bwKthrCUkgueH/
wqsdc493OClTn3sZpYSbAlgZnHAhXj9I/OBnOBune64nXM/d86lOufS8P5/
nuecm0YnP24z62ZmCXXrwEXAKlr4OffNyGIzuxTvnGIk8KRz7v4wlxQSZvYCMAFvWtqdwN3AK8CLwCl4U3h
f5Zxr3KHcoZnZucBHwEoOnjO+A6+foNMeu5mNwOscjMT7Yveic+4+MxuE9025J/B/
wHTnXGX4Kg2d4KmhW51zX+vsxx08vpeDT6OAvzvn7jezZFrwOfdNEIiISNP8cmpIRESOQEEgIuJzCgIREZ9
TEIiI+JyCQETE5xQEIo2YWU1wZse6pdUmqjOztIYzw4q0B5piQuRw5c65UeEuQqStqEUgcoyC88D/JjgX/
GIzOy24Pc3M5plZnpm9b2anBLefbGYvB+8VsMLMzgm+VaSZ/
SV4/4B3giOCRcJGQSByuLhGp4a+2eC1EufccOAPeCPVAR4FnnHOjQCeB2YFt88CPgzeK2AMsDq4fTDwmHMu
EygGvhHi4xFplkYWizRiZmXOufgmtm/
CuwnM58EJ7nY455LNbDfQxzlXHdy+3TnXy8wKgdSGUxwEp8h+N3gDEczsNiDaOffr0B+ZSNPUIhA5Pu4I68
ej4dw3NaivTsJMQSByfL7Z4PHT4PoneDNgAkzDm/
wOvFsGzoT6m8cktlWRIsdD30REDhcXvONXnX855+ouIe1hZnl43+qvCW77IfCUmf0nUAjcENz+I+BxM7sR7
5v/
TGA7Iu2M+ghEjlGwjyDLObc73LWItCadGhIR8Tm1CEREfE4tAhERn1MQiIj4nIJARMTnFAQiIj6nIBAR8bn
/B9TUCojQpB6uAAAAAElFTkSuQmCC\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"# Hyper-parameters\n",
"num_epochs = 50\n",
"\n",
"# Initialize a new network\n",
"z_size = hidden_size + vocab_size # Size of concatenated hidden + input
vector\n",
"params = init_lstm(hidden_size=hidden_size, vocab_size=vocab_size,
z_size=z_size)\n",
"\n",
"# Initialize hidden state as zeros\n",
"hidden_state = np.zeros((hidden_size, 1))\n",
"\n",
"# Track loss\n",
"training_loss, validation_loss = [], []\n",
"\n",
"# For each epoch\n",
"for i in range(num_epochs):\n",
" \n",
" # Track loss\n",
" epoch_training_loss = 0\n",
" epoch_validation_loss = 0\n",
" \n",
" # For each sentence in validation set\n",
" for inputs, targets in validation_set:\n",
" \n",
" # One-hot encode input and target sequence\n",
" inputs_one_hot = one_hot_encode_sequence(inputs, vocab_size)\n",
" targets_one_hot = one_hot_encode_sequence(targets, vocab_size)\n",
"\n",
" # Initialize hidden state and cell state as zeros\n",
" h = np.zeros((hidden_size, 1))\n",
" c = np.zeros((hidden_size, 1))\n",
"\n",
" # Forward pass\n",
" z_s, f_s, i_s, g_s, C_s, o_s, h_s, v_s, outputs =
forward(inputs_one_hot, h, c, params)\n",
" \n",
" # Backward pass\n",
" loss, _ = backward(z_s, f_s, i_s, g_s, C_s, o_s, h_s, v_s, outputs,
targets_one_hot, params)\n",
" \n",
" # Update loss\n",
" epoch_validation_loss += loss\n",
" \n",
" # For each sentence in training set\n",
" for inputs, targets in training_set:\n",
" \n",
" # One-hot encode input and target sequence\n",
" inputs_one_hot = one_hot_encode_sequence(inputs, vocab_size)\n",
" targets_one_hot = one_hot_encode_sequence(targets, vocab_size)\n",
"\n",
" # Initialize hidden state and cell state as zeros\n",
" h = np.zeros((hidden_size, 1))\n",
" c = np.zeros((hidden_size, 1))\n",
"\n",
" # Forward pass\n",
" z_s, f_s, i_s, g_s, C_s, o_s, h_s, v_s, outputs =
forward(inputs_one_hot, h, c, params)\n",
" \n",
" # Backward pass\n",
" loss, grads = backward(z_s, f_s, i_s, g_s, C_s, o_s, h_s, v_s,
outputs, targets_one_hot, params)\n",
" \n",
" # Update parameters\n",
" params = update_parameters(params, grads, lr=1e-1)\n",
" \n",
" # Update loss\n",
" epoch_training_loss += loss\n",
" \n",
" # Save loss for plot\n",
" training_loss.append(epoch_training_loss/len(training_set))\n",
" validation_loss.append(epoch_validation_loss/len(validation_set))\n",
"\n",
" # Print loss every 5 epochs\n",
" if i % 5 == 0:\n",
" print(f'Epoch {i}, training loss: {training_loss[-1]}, validation
loss: {validation_loss[-1]}')\n",
"\n",
"\n",
" \n",
"# Get first sentence in test set\n",
"inputs, targets = test_set[1]\n",
"\n",
"# One-hot encode input and target sequence\n",
"inputs_one_hot = one_hot_encode_sequence(inputs, vocab_size)\n",
"targets_one_hot = one_hot_encode_sequence(targets, vocab_size)\n",
"\n",
"# Initialize hidden state as zeros\n",
"h = np.zeros((hidden_size, 1))\n",
"c = np.zeros((hidden_size, 1))\n",
"\n",
"# Forward pass\n",
"z_s, f_s, i_s, g_s, C_s, o_s, h_s, v_s, outputs = forward(inputs_one_hot, h,
c, params)\n",
"\n",
"# Print example\n",
"print('Input sentence:')\n",
"print(inputs)\n",
"\n",
"print('\\nTarget sequence:')\n",
"print(targets)\n",
"\n",
"print('\\nPredicted sequence:')\n",
"print([idx_to_word[np.argmax(output)] for output in outputs])\n",
"\n",
"# Plot training and validation loss\n",
"epoch = np.arange(len(training_loss))\n",
"plt.figure()\n",
"plt.plot(epoch, training_loss, 'r', label='Training loss',)\n",
"plt.plot(epoch, validation_loss, 'b', label='Validation loss')\n",
"plt.legend()\n",
"plt.xlabel('Epoch'), plt.ylabel('NLL')\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercise:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Run the training loop above. What do you notice about the loss and number of
epochs compared to the vanilla RNN from earlier?"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "gi51eWgKxyOk"
},
"source": [
"## PyTorch implementation of the LSTM\n",
"\n",
"Now that we know how the LSTM cell works, let's see how easy it is to use in
PyTorch!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Definition of our LSTM network. We define a LSTM layer using the [nn.LSTM]
(https://pytorch.org/docs/stable/nn.html#lstm) class. The LSTM layer takes as
argument the size of the input and the size of the hidden state like in our numpy
implementation."
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Net(\n",
" (lstm): LSTM(4, 50)\n",
" (l_out): Linear(in_features=50, out_features=4, bias=False)\n",
")\n"
]
}
],
"source": [
"import torch\n",
"import torch.nn as nn\n",
"import torch.nn.functional as F\n",
"\n",
"class Net(nn.Module):\n",
" def __init__(self):\n",
" super(Net, self).__init__()\n",
" \n",
" # Recurrent layer\n",
" # YOUR CODE HERE!\n",
" self.lstm = nn.LSTM(input_size=vocab_size,\n",
" hidden_size=50,\n",
" num_layers=1,\n",
" bidirectional=False)\n",
" \n",
" # Output layer\n",
" self.l_out = nn.Linear(in_features=50,\n",
" out_features=vocab_size,\n",
" bias=False)\n",
" \n",
" def forward(self, x):\n",
" # RNN returns output and last hidden state\n",
" x, (h, c) = self.lstm(x)\n",
" \n",
" # Flatten output for feed-forward layer\n",
" x = x.view(-1, self.lstm.hidden_size)\n",
" \n",
" # Output layer\n",
" x = self.l_out(x)\n",
" \n",
" return x\n",
"\n",
"net = Net()\n",
"print(net)"
]
},
{
"cell_type": "markdown",
"metadata": {
"colab_type": "text",
"id": "4WpALf2-x7Ty"
},
"source": [
"### Training loop"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"It's time for us to train our network. In the section below, you will get to
put your deep learning skills to use and create your own training loop. You may
want to consult previous exercises if you cannot recall how to define the training
loop."
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {
"colab": {},
"colab_type": "code",
"id": "2URKsyFDx8xG"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Epoch 0, training loss: 1.300930306315422, validation loss:
1.3728503584861755\n",
"Epoch 5, training loss: 0.7285211995244026, validation loss:
0.7846524477005005\n",
"Epoch 10, training loss: 0.5349573764950037, validation loss:
0.5857458531856536\n",
"Epoch 15, training loss: 0.44862118251621724, validation loss:
0.48371400535106657\n",
"Epoch 20, training loss: 0.400629423931241, validation loss:
0.4290568709373474\n",
"Epoch 25, training loss: 0.3734992817044258, validation loss:
0.39898631721735\n",
"Epoch 30, training loss: 0.3537159925326705, validation loss:
0.375779590010643\n",
"Epoch 35, training loss: 0.33958207685500386, validation loss:
0.35797602534294126\n",
"Epoch 40, training loss: 0.3286479884758592, validation loss:
0.3436737462878227\n",
"Epoch 45, training loss: 0.32034364696592094, validation loss:
0.33199286460876465\n",
"\n",
"Input sequence:\n",
"['a', 'a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b', 'b']\n",
"\n",
"Target sequence:\n",
"['a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'EOS']\n",
"\n",
"Predicted sequence:\n",
"['a', 'a', 'a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'b', 'b', 'EOS']\n"
]
},
{
"data": {
"image/png":
"iVBORw0KGgoAAAANSUhEUgAAAYIAAAEGCAYAAABo25JHAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEg
AACxIB0t1+/
AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9y
Zy8QZhcZAAAgAElEQVR4nO3deXhU5dnH8e+dEAhCIGEXAwKCQNgxggoUERdcKRWpCLValUq1qNQWam2xWFt
rrSK8tEpdqyi1WtwteilKcQdkERGBABJkSVgTFiHJ8/7xzGQjC4RMJsn5fa7rXDNz5szMfTCe+zy7OecQEZ
Hgiol2ACIiEl1KBCIiAadEICIScEoEIiIBp0QgIhJwdaIdwLFq1qyZa9euXbTDEBGpURYvXpzpnGte0ns1L
hG0a9eORYsWRTsMEZEaxcw2lvZexKqGzOxxM9tuZl+Uc9zpZpZjZiMjFYuIiJQukm0ETwLDyjrAzGKBPwNv
RTAOEREpQ8QSgXNuAbCznMN+DrwIbI9UHCIiUraotRGY2UnACGAIcHo5x44DxgG0bds28sGJSL7Dhw+Tnp7
OwYMHox2KHIX4+HiSk5OJi4s76s9Es7F4GjDJOZdnZmUe6JybBcwCSE1N1eRIIlUoPT2dhIQE2rVrR3n/
r0p0OefYsWMH6enptG/f/qg/F81EkArMCf1hNQMuMrMc59xLUYxJRIo5ePCgkkANYWY0bdqUjIyMY/
pc1BKBcy4/XZnZk8BrSgIi1ZOSQM1Rkf9Wkew++hzwEdDZzNLN7Dozu9HMbozUb5bliy/gV7+CrKxo/
LqISPUVyV5Do51zJzrn4pxzyc65x5xzDzvnHi7h2Guccy9EKhaA9evhL3+BFSsi+SsiUtl27NhB79696d27
N61ateKkk07Kf33o0KGj+o5rr72W1atXl3nMzJkzmT17dmWEzMCBA1m6dGmlfFdVqHEjiyuqd2//
uHQpnHVWdGMRkaPXtGnT/
IvqXXfdRcOGDbn99tuLHOOcwzlHTEzJ97ZPPPFEub9z0003HX+wNVRgJp1LToYmTXwiEJGab+3ataSkpDBm
zBi6devGli1bGDduHKmpqXTr1o2pU6fmHxu+Q8/JySExMZHJkyfTq1cvzjzzTLZv98OY7rzzTqZNm5Z//
OTJk+nXrx+dO3fmww8/BGDfvn1cfvnlpKSkMHLkSFJTU8u983/mmWfo0aMH3bt354477gAgJyeHH/3oR/
n7p0+fDsCDDz5ISkoKPXv2ZOzYsZX+b1aawJQIzHypQIlA5Djcemvl/0/UuzeELsDH6quvvuKf//
wnqampANx77700adKEnJwchgwZwsiRI0lJSSnymT179jB48GDuvfdeJk6cyOOPP87kyZOP+G7nHJ9+
+imvvPIKU6dO5b///S8zZsygVatWvPjiiyxbtoy+ffuWGV96ejp33nknixYtonHjxpx77rm89tprNG/
enMzMTFaE6qp3794NwH333cfGjRupW7du/r6qEJgSAfi/
txUrICcn2pGISGU45ZRT8pMAwHPPPUffvn3p27cvq1at4ssvvzziM/
Xr1+fCCy8E4LTTTmPDhg0lfvcPfvCDI45ZuHAhV155JQC9evWiW7duZcb3ySefcM4559CsWTPi4uK46qqrW
LBgAR07dmT16tVMmDCBefPm0bhxYwC6devG2LFjmT179jENCDtegSkRgE8EBw/
CmjXQtWu0oxGpgSp45x4pDRo0yH++Zs0aHnroIT799FMSExMZO3ZsiaOh69atm/
88NjaWnFLuDOvVq1fuMRXVtGlTli9fzptvvsnMmTN58cUXmTVrFvPmzeP999/nlVde4Y9//
CPLly8nNja2Un+7JIErEYCqh0Rqo71795KQkECjRo3YsmUL8+bNq/TfGDBgAM8//zwAK1asKLHEUVj//
v2ZP38+O3bsICcnhzlz5jB48GAyMjJwznHFFVcwdepUlixZQm5uLunp6Zxzzjncd999ZGZmsn///ko/
h5IEqkTQpQvUresTwejR0Y5GRCpT3759SUlJoUuXLpx88skMGDCg0n/j5z//
OVdffTUpKSn5W7hapyTJycncfffdnH322TjnuPTSS7n44otZsmQJ1113Hc45zIw///
nP5OTkcNVVV5GVlUVeXh633347CQkJlX4OJTHnatbUPampqe54FqY57TRo1gwicLMgUiutWrWKrqpLBXxvn
5ycHOLj41mzZg3nn38+a9asoU6d6nVPXdJ/
MzNb7JxLLen46hV9FejdG159FZzzPYlERI5WdnY2Q4cOJScnB+ccjzzySLVLAhVR88/gGPXqBY8/
Dlu3woknRjsaEalJEhMTWbx4cbTDqHSBaiwGNRiLiBQXnETgHGzdSq/uuYASgYhIWHASwezZcOKJNM5cR/
v2SgQiImHBSQTh1XrS0jTVhIhIIcFJBB06+MdQIlizBrKzoxuSiJRvyJAhRwwOmzZtGuPHjy/
zcw0bNgTg22+/ZeTIkSUec/bZZ1Ned/Rp06YVGdh10UUXVco8QHfddRf333//
cX9PZQhOImjVCuLjYf16evf2TQZam0Ck+hs9ejRz5swpsm/
OnDmMPspRoa1bt+aFFyq+3EnxRPDGG2+QmJhY4e+rjoKTCMx89VCoRACqHhKpCUaOHMnrr7+evwjNhg0b+P
bbbxk0aFB+v/
6+ffvSo0cPXn755SM+v2HDBrp37w7AgQMHuPLKK+natSsjRozgwIED+ceNHz8+fwrrKVOmADB9+nS+/
fZbhgwZwpAhQwBo164dmZmZADzwwAN0796d7t27509hvWHDBrp27coNN9xAt27dOP/
884v8TkmWLl3KGWecQc+ePRkxYgS7du3K//3wtNThye7ef//9/
IV5+vTpQ1YlLLsYrHEEHTpAWhpt2kBSEixbFu2ARGqWaMxC3aRJE/
r168ebb77J8OHDmTNnDqNGjcLMiI+PZ+7cuTRq1IjMzEzOOOMMLrvsslLX7f373//
OCSecwKpVq1i+fHmRaaTvuecemjRpQm5uLkOHDmX58uVMmDCBBx54gPnz59OsWbMi37V48WKeeOIJPvnkE5
xz9O/fn8GDB5OUlMSaNWt47rnn+Mc//sGoUaN48cUXy1xf4Oqrr2bGjBkMHjyY3/3ud/
z+979n2rRp3Hvvvaxfv5569erlV0fdf//9zJw5kwEDBpCdnU18fPwx/
GuXLDglAshPBIZTg7FIDVK4eqhwtZBzjjvuuIOePXty7rnnsnnzZrZt21bq9yxYsCD/gtyzZ0969uyZ/
97zzz9P37596dOnDytXrix3QrmFCxcyYsQIGjRoQMOGDfnBD37A//
73PwDat29P71DVQ1lTXYNfH2H37t0MHjwYgB//
+McsWLAgP8YxY8bwzDPP5I9gHjBgABMnTmT69Ons3r27UkY2B69EsHcv7NpF795NePhhyM2FKpjlVaRWiNY
s1MOHD+e2225jyZIl7N+/n9NOOw2A2bNnk5GRweLFi4mLi6Ndu3YlTj1dnvXr13P//
ffz2WefkZSUxDXXXFOh7wkLT2ENfhrr8qqGSvP666+zYMECXn31Ve655x5WrFjB5MmTufjii3njjTcYMGAA
8+bNo0uXLhWOFYJWIijUhbRXLzhwwPceEpHqrWHDhgwZMoSf/OQnRRqJ9+zZQ4sWLYiLi2P+/
Pls3LixzO/53ve+x7PPPgvAF198wfLlywE/hXWDBg1o3Lgx27Zt480338z/TEJCQon18IMGDeKll15i//
797Nu3j7lz5zJo0KBjPrfGjRuTlJSUX5p4+umnGTx4MHl5eWzatIkhQ4bw5z//
mT179pCdnc26devo0aMHkyZN4vTTT+err7465t8sLnglAgg1GPtJ+JYu9dNTi0j1Nnr0aEaMGFGkB9GYMWO
49NJL6dGjB6mpqeXeGY8fP55rr72Wrl270rVr1/
ySRa9evejTpw9dunShTZs2RaawHjduHMOGDaN169bMnz8/f3/
fvn255ppr6NevHwDXX389ffr0KbMaqDRPPfUUN954I/
v376dDhw488cQT5ObmMnbsWPbs2YNzjgkTJpCYmMhvf/tb5s+fT0xMDN26dctfbe14BGsa6uxsSEiAP/
2JQxMn07AhTJwI995buTGK1CaahrrmOdZpqINVNdSwIbRoAWlp1K0L3bqpwVhEJFiJAHz10Pr1AOo5JCJCE
BNBaFAZ+ESwbZtfm0BESlfTqpCDrCL/
rYKXCDp0gI0bISdHI4xFjkJ8fDw7duxQMqgBnHPs2LHjmAeZBavXEPhEkJsLmzbRq5fvTrp0KQwbFuW4RKq
p5ORk0tPTycjIiHYochTi4+NJTk4+ps9ELBGY2ePAJcB251z3Et4fA0wCDMgCxjvnIj/
pQ6EupIlD29OunUoEImWJi4ujfXgMjtRKkawaehIo6z57PTDYOdcDuBuYFcFYCoT/
oNVgLCICRDAROOcWADvLeP9D59yu0MuPgWMry1RUcjLUqZPfYNyrF3z9NezbVyW/
LiJS7VSXxuLrgDfLPaoyxMZCu3ZFeg45B198USW/
LiJS7UQ9EZjZEHwimFTGMePMbJGZLaqUBqvQLKTgSwSgKalFJLiimgjMrCfwKDDcObejtOOcc7Occ6nOudT
mzZsf/w+3b5/fRnDyyX7hstWrj/9rRURqoqglAjNrC/wH+JFz7usq/fEOHSAzE/
buJSYGOnVSIhCR4Ipk99HngLOBZmaWDkwB4gCccw8DvwOaAn8LrSaUU9qESJUu3IV0/
Xro1YvOnVU1JCLBFbFE4Jwrc2Vp59z1wPWR+v0yFRpLEE4Ec+fCoUNQt25UIhIRiZqoNxZHReFEAHTu7Acb
h16KiARKMBNBYqLfQg3GnTv73WonEJEgCmYigCJdSE891e9SIhCRIFIiwBcOWrRQIhCRYAp2Ili/
HvLyAF899HXVdmIVEakWgpsI2rf33YS2bAF8IlCJQESCKLiJoISeQxkZsGtXGZ8REamFlAgKJQJQqUBEgie
4iaBtW4iJUSIQkcALbiKoWxfatMlPBO3b+2UK1GAsIkET3EQARWYhjYvztUUqEYhI0AQ7ERQaSwDqOSQiwa
REsGUL7N8P+ESwZo2fd0hEJCiUCAA2bAB8IvjuO/
jmm+iFJCJS1YKdCNq394+afE5EAizYiaDYWILw5HPqOSQiQRLsRNC8OTRokJ8IWrSAxo1VIhCRYAl2IjAr0
nPITD2HRCR4gp0IQF1IRSTwlAjCg8qcA3wiSE+HffuiHJeISBVRIujQwV/
1MzKAgp5Da9ZEMSYRkSqkRKBZSEUk4JQIwolg7VoAOnb0jcZKBCISFEoEnTr5LqSffgpA/
fp+hmolAhEJCiWCOnXgjDNg4cL8Xeo5JCJBokQAMGAALFsGe/
cCBYkg1JFIRKRWUyIAGDgQ8vLg448Bnwiys2Hr1ijHJSJSBZQIwFcNxcTABx8A6jkkIsGiRACQkAC9e+e3E
4Qnn1MiEJEgiFgiMLPHzWy7mX1RyvtmZtPNbK2ZLTezvpGK5agMGOCrhg4fJjnZ9x5SIhCRIIhkieBJYFgZ
718IdApt44C/RzCW8g0c6FcqW7aMmBhfKlAiEJEgiFgicM4tAHaWcchw4J/
O+xhINLMTIxVPuQYM8I+h6qHOnbUugYgEQzTbCE4CNhV6nR7adwQzG2dmi8xsUUZoTqDKj+YkaNeuSCJYvx
4OHYrMz4mIVBc1orHYOTfLOZfqnEtt3rx55H5o4ECfCJyjc2e/
iP26dZH7ORGR6iCaiWAz0KbQ6+TQvugZOBC2bYO0NHUhFZHAiGYieAW4OtR76Axgj3NuSxTj8YkAYOFCdSE
VkcCIZPfR54CPgM5mlm5m15nZjWZ2Y+iQN4A0YC3wD+BnkYrlqHXtComJsHAhjRpBmzb5g41FRGqtOpH6Yu
fc6HLed8BNkfr9ComJ8b2HQg3GI0fCzJmwaxckJUU5NhGRCKkRjcVVauBA+OoryMxkzBjfa+iFF6IdlIhI5
CgRFBduJ/jwQ/
r2hS5dYPbs6IYkIhJJSgTFpaZC3bqwcCFmMHYsvP8+fPNNtAMTEYkMJYLi4uN9MgjNRHrVVX73s89GMSYRk
QhSIijJwIHw2Wdw4ADt2/v242ee0UI1IlI7KRGUZMAAOHwYFi0CfPXQypWwfHmU4xIRiQAlgpKcdZZ/
DHUjveIKv7TxM89EMSYRkQhRIihJs2Z+cFmonaBpU7joInjuOT//
kIhIbaJEUJqBA30iyMsDfPXQ5s2+B5GISG2iRFCaAQNg92748ksALrnEr2ip6iERqW2UCEpTaAI68EtXjhz
pRxkfOBDFuEREKpkSQWk6dIDkZHj55fxdY8dCVha89loU4xIRqWRKBKUxgxtugP/+N3/NysGD/
UJmqh4SkdpEiaAsP/2pn25ixgwAYmNh9Gh44w3YsSPKsYmIVBIlgrK0bAlXXglPPgl79gC+eignB/
797+iGJiJSWZQIyjNhAmRnwxNPANCzJ3TvDo88ojEFIlI7VDgRmNmtlRlItXXaab4r6YwZkJuLGfz617B0K
cyaFe3gRESO3/GUCCZWWhTV3YQJkJbmGwfw7QRDh/
qEsHVrlGMTETlOx5MIrNKiqO5GjPBdSR96CPAdiv72Nz+eYGJw0qGI1FLHkwiCMylzXBz87Gfwzjt+GlLg1
FPhjjv8/ENvvx3l+EREjkOZicDMssxsbwlbFnBSFcVYPdxwg1+0Zvr0/
F2TJkGnTj5HHDwYxdhERI5DmYnAOZfgnGtUwpbgnIutqiCrhWbNYMwYePpp2LkT8Hnhb3+DtWvhT3+Kcnwi
IhV0PL2GgreK74QJvmHg0Ufzd517rs8P994Lq1dHMTYRkQpSY/Gx6NkTzj4bZs70o8pC/
vpXPyndz36m5SxFpOZRY/
GxuuUW+OabIpPRtWzpSwTvvguzZ0cxNhGRCjBXxi2smZXWOdKA3zjnmkQkqjKkpqa6RaG1hKMiNxc6doQWL
eCjjyDG59K8PL/CZVqaX9u4VavohSgiUpyZLXbOpZb0XnklgoRStobAQ5UZZI0RGwu//
z18+qmfZyIkJgYee8zPRjF2rKafEJGao8wSQXUU9RIB+IaACy6Ajz/2K5glJ+e/
9dhjcP31cPfdcOedUYxRRKSQskoE5VUN/
a6M73XOubvL+eFh+JJDLPCoc+7eYu+3BZ4CEkPHTHbOvVHWd1aLRAC+Dqh7dzjvPHjpJT/
cGJ8jxo6FOXN8m8HgwVGOU0SE46sa2lfCBnAdMKmcH40FZgIXAinAaDNLKXbYncDzzrk+wJXA38qJp/
ro0AGmToVXXoEXX8zfbQYPP+ybEa66CjIyohijiMhRKG9A2V/
DGzALqA9cC8wBOpTz3f2Atc65NOfcodBnhhf/CaBR6Hlj4NtjjD+6br0V+vaFm2+GXbvydyckwL/
+5Rev+dGPfEOyiEh1VW73UTNrYmZ/
AJYDdYC+zrlJzrnt5Xz0JGBTodfpHDktxV3AWDNLB94Afl5KDOPMbJGZLcqoTrfYder4wWWZmfCrXxV5q3d
vePBBmDcP/vKXKMUnInIUyptr6C/
AZ0AW0MM5d5dzbldZnzlGo4EnnXPJwEXA02Z2REzOuVnOuVTnXGrz5s0r8ecrQZ8+8Itf+IQwf36Rt268Ea
64An7zG/jggyjFJyJSjvIai/OA74Acig4gM3xjcaMSP+g/eyZwl3PugtDrX+M/
9KdCx6wEhjnnNoVepwFnlFXaqDaNxYXt3+9HHZv5QQT16+e/tWePrz06dMi/
lZQUxThFJLAq3FjsnItxztUvYfK5hLKSQMhnQCcza29mdfGNwa8UO+YbYGgoyK5APFCN6n6O0gkn+DEFa9f
6fqOFNG7sp6pOT4f7749SfCIiZYjYmsXOuRzgZmAesArfO2ilmU01s8tCh/
0CuMHMlgHPAde4mjawIWzoULj2WrjvPihWYunXD0aN8uvaVKcmDhER0ICyyrVrF/
ToAY0awZIlfp7qkFWr/
LCD225TyUBEqt7xjCOQY5GU5IcWr1p1xLDirl39dNUzZ8KWLVGKT0SkBEoEle2CC3x3oQcegP/
9r8hbU6bA4cNaxEZEqhclgkj4y1+gfXu45ho/C13IKaf4ZoRHHvEzWYuIVAdKBJHQsCE8+SSsXw+//
GWRt377W/94zz1VH5aISEmUCCJl0CCYONFPPDRvXv7utm3hhhvg8cf9vHUiItGmRBBJf/
iDbyX+yU+KzEV0xx1+doqpU6MYm4hIiBJBJMXHwz//Cdu2+YXvQ1q39usbP/
20FrwXkehTIoi01FTflfSZZ+D11/
N3T5rkZ6K4667ohSYiAkoEVeOOO6BLF18qOHgQ8EseT5jgp6tesSLK8YlIoCkRVIW6deH//s+3Dt93X/
7u22+HxESfEGrYAG8RqUWUCKrK0KF+Tuo//cl3KwWaNIF774X33vM1RyIi0aBEUJUeeABiY/
2EQyHXXw9nnOGXNNi5M4qxiUhgKRFUpeRkP6Ls5ZfhjTcAiInxI4137oRf/
zrK8YlIICkRVLXbboPOnYs0HPfs6XfPmgUffhjl+EQkcJQIqlrdujBjBqxbV2Q+6ilToE0bP1/
d4cNRjE9EAkeJIBrOOw9GjvQTDm3YAPjpiWbM8F1Jp0+PbngiEixKBNHywAO+gaBQw/
Hw4XDZZb50oNlJRaSqKBFES5s2vuH4pZfg1Vfzd0+f7scU3HJLFGMTkUBRIoimiRN9S/
E11+RXEZ18sp924qWX4NlnoxmciASFEkE01a0LL74Iubm+zSDUi+jWW+G00/
zSlsOHw9dfRzlOEanVlAiirWNHeOopWLw4vz4oLg4WLvSDkOfPh27dfHLQgDMRiQQlgupg+HCYPNkPJHjyS
cDPYD15MqxZ45czmDHD54yHHoJDh6IbrojULkoE1cXdd8OQITB+PCxblr+7ZUs/
8njpUl9ddOutfkoKlQ5EpLIoEVQXderAc8/5meguvxx27y7ydo8e8NZb8MILsHIlXHQRZGVFKVYRqVWUCKq
Tli3h3/+GjRvh6qshL6/I22Y+R8yZA4sWwfe/n9++LCJSYUoE1c1ZZ8Ff/
+rHFvzxjyUeMmKEb0p4910YNUpTUojI8VEiqI5+/nMYO9YPOHv66RIPGTsWZs70+eLHP/
Y9UEVEKqJOtAOQEpjBo4/C5s2+y9CJJ8K55x5x2M9+5tsJJk+GhAR4+GH/
URGRYxHREoGZDTOz1Wa21swml3LMKDP70sxWmpnG0obVqwdz50LXrvCDH/
huQyWYNMmvYzBrFvzyl1ryUkSOXcQSgZnFAjOBC4EUYLSZpRQ7phPwa2CAc64bcGuk4qmRGjeGN9/0Cxtfe
KFvRC7BPffAzTf7poUf/
hCys6s4ThGp0SJZIugHrHXOpTnnDgFzgOHFjrkBmOmc2wXgnNsewXhqppNO8sng4EEYNqzEAQRmfrK6+
+7zM1b0769pKUTk6EUyEZwEbCr0Oj20r7BTgVPN7AMz+9jMhkUwnpqrWzc/
C11amh+FXEKfUTNfNfTWW7BtG5x+OrzyShRiFZEaJ9q9huoAnYCzgdHAP8wssfhBZjbOzBaZ2aKMjIwqDrG
aGDzY9yBauBBGjy51nomhQ/
20RZ06+ZwxZcoRwxFERIqIZCLYDLQp9Do5tK+wdOAV59xh59x64Gt8YijCOTfLOZfqnEtt3rx5xAKu9kaN8
pMOvfSSv8rv31/iYSef7PPFtdfC1Klw6aWwa1cVxyoiNUYkE8FnQCcza29mdYErgeKVFS/
hSwOYWTN8VVFaBGOq+W6+Gf7xD5g3Dy64APbsKfGw+Hh47DH4+9/
h7bd9VdEXX1RxrCJSI0QsETjncoCbgXnAKuB559xKM5tqZpeFDpsH7DCzL4H5wC+dczsiFVOtcf31fp6JTz
7xE9WVUl1mBjfeCO+9B/v2+cnqXnihakMVkerPXA3reJ6amuoWLVoU7TCqhzff9JMPnXyyv+1PTi710G+
/9Yd+/LEfd3D33RAbW4WxikhUmdli51xqSe9Fu7FYjseFF/oqos2bYeBAWLu21ENbt/
Ylgxtu8AveXHKJ2g1ExFMiqOkGDfLLmO3b55PBBx+Uemi9en4E8iOPwDvv+HaDzz+vwlhFpFpSIqgNTjsN/
vc/aNjQtxn87W9lzjUxbpwvHezfD/36wV13adUzkSBTIqgtunSBzz6D886Dm26C664rc7GCs87yvYh+
+EP4/e99QihlOiMRqeWUCGqTpCQ/L/
VvfwtPPOGrjb75ptTDmzSBZ57xwxLCo5FVOhAJHiWC2iYmxo8imzsXVq/21Ubz55f5keHD/
fKXV15ZUDpQ24FIcCgR1Fbf/z58+ik0a+ari6ZMKfNWv0kTP4PFyy/
70kFqKtx6K+zdW4Uxi0hUKBHUZl26+EFno0f7UkK/frBsWZkfuewy+PJL+OlP/
YymXbvC889rnQOR2kyJoLZr1Mjf6r/0Emzd6m/
17767zIWOk5J8x6OPP4aWLX2D8oUXljlMQURqMCWCoAg3BFxxBfzud3DmmeVOPtSvn69deugh+PBD6N7d1z
BlZVVRzCJSJZQIgqRpU3j2WT/
h0Dff+IbkKVNKncUUoE4dmDDBtzuPGOFrmDp0gAcfLLN3qojUIEoEQXT55b50cPnl/
srepUu5DQEnngjPPedLCL17w8SJfs2DRx+FnJwqjF1EKp0SQVA1b+5LBwsW+JLCD38IZ59dbmPy6af7+e3e
ecevonnDDZCS4pNEbm7VhC4ilUuJIOgGDYJFi/wERCtXQt+
+MH48ZGaW+bFzzoGPPvLdTevVg6uu8gWLRx5RlZFITaNEIH4+6nHjYM2agoVv2reHO++EnTtL/
ZiZ7266dCn8+9+QmOjXPzj5ZPjjHzW7qUhNoUQgBZKSfBeh5ct9f9F77vEJYcoU2L271I/
FxsLIkb794N13oU8f+M1voG1b+MUvYMOGqjsFETl2SgRypJQU33i8bBmce65vUG7Xzs8/
UcrSmOBLCEOGwH//60sJl13m88opp/jeq2+/
DXl5VXcaInJ0lAikdD17wosv+omHhgzxM9K1bQu33w4bN5b50V69YPZsWL/er4j20Udw/
vk+x0yfXmY+EZEqpkQg5evd209it3gxXOuHneIAAA/cSURBVHQRTJvmb/
NHjfLDj8vQpg384Q+waZMf4JyUBLfc4nscjRvnq5M0fYVIdCkRyNHr29f3E01L8wMJ3nrLj1A+80xflVTGp
Hb16sHYsb5k8NlnPofMng39+/vSw/TpZbZLi0gEKRHIsWvbFu67D9LT/RU8I8OPQ2jTBn71K/j66zI/
npoKjz8O334LDz8M8fG+lNC6te+G+tZbGqQmUpXM1bByeWpqqlu0aFG0w5DCcnNh3jzf7fTVV/
3r733Pjza7/HKoX7/
cr1i2DB57zFcf7d7tx7tdfrnPL4MG+Z5JIlJxZrbYOZda4ntKBFKptm6FJ5/0c0+sW+cHF4waBWPGwMCBfu
GcMhw8CG++Cf/6l88p+/
f76S2uuMJ3UT3jDIiLq5pTEalNlAik6uXlwfvv+9v8uXP9Fb1tW782wtixfirTcuzbB6+/
DnPmwBtvwHffQUKC78B03nl+O/
VU321VRMqmRCDRlZ3t56KYPds3AOTm+q6pV17p639OPbXcr9i7149DCG9paX5/
mzZ+qMOAAb7NukuXcgsdIoGkRCDVx/
btvofR7NkFXU979PD1PiNH+oEGR2HduoKkMH9+wXQWiYm+J1K4M9Npp/k59USCTolAqqdNm+A///
HrI3zwgR9Q0LWrX/jgkkv8yjhH0Uqcl+c7Kn30kd8+/
tivuRP+027b1vd87dOn4LF1a1UpSbAoEUj1t2WLb0t44QU/NXZuLjRr5uc8uvhiuOACf7t/
lPbu9eMVlizx2+ef+2QR/nNv0cInhcJbu3ZKDlJ7KRFIzbJrl29LeO0130q8c6cvGZx1lp//esgQX/
8TH39MX5uV5efTW7zYJ4YlS+DLLwvGLCQm+tJCjx6+CaNnT+jWDU44IQLnKFLFopYIzGwY8BAQCzzqnLu3l
OMuB14ATnfOlXmVVyIImNxc+OQTnxTmzfNXcOd8EjjzTJ8Uzj7bNwZU4Ip98KCvRlqyxCeIZcv86337/
PtmfiW2Hj18R6fu3X1y6NTJL+MpUlNEJRGYWSzwNXAekA58Box2zn1Z7LgE4HWgLnCzEoGUafduX3X03nu+
lXjZMp8YYmP91bp/f9+20L+/70JUgZFoeXl+srzly4tu69YVVC3Vreu/vlu3gi0lxU/
BpMFvUh1FKxGcCdzlnLsg9PrXAM65PxU7bhrwNvBL4HYlAjkmO3f6huZPPvEz2H36acHUpg0b+gnz+vQp2F
JS/FW8Ag4cgK+
+8iWGwts33xQcU69eQYI49VTo2NEnh44dfe8ltUFItJSVCCJZuD0J2FTodTrQv1hgfYE2zrnXzeyXpX2RmY
0DxgG0bds2AqFKjdWkCVx6qd/
A386vWVOQFJYs8RMbhet64uL8VbpXL98IEG4QaNmy3J+qX78gnxSWlQWrVvn2hpUr/eMHH/
j5+QrfZzVu7BNC8a1TJ994rSQh0RLJEsFIYJhz7vrQ6x8B/
Z1zN4dexwDvAtc45zaY2XuoRCCRkJsLa9f61XI+/9xvy5f76TDCmjf3CaF7d9+FNSXFPzZrVuGf/
e47X8W0du2R24YNPqywhg19yeGUU6BDh6KPbdtqWg05ftWyasjMGgPrgOzQR1oBO4HLykoGSgRSaTIyYMWK
gm35cn87Hy49gE8QXbv6rXNnX99z6ql+Cc/
jaC0+fNiv7bNmjU8M4cf16/323XcFx8bG+mTQocORW6dOvqQhUp5oJYI6+MbiocBmfGPxVc65laUc/
x4qEUi05eX56bW//NJv4TqfVasKhi+DTwKnnOKTQvhWPry1a1fhdohwCN9+6xun09L84/r1/
nlamh+cXVjLlgX5qXCeatvWd4lVlZNAdLuPXgRMw3cffdw5d4+ZTQUWOedeKXbseygRSHWWmelHpYW31av9
Y1qan1QvLCbGT4LUvr3fOnQoeN6+PbRqdVxX5+xsnxjWrfMlicIhFa7tAl/
l1Lat304+2YfVpg0kJxc8HsUs4VILaECZSCQ5B9u2+Stz4dv4tDR/
xd6ypejx9eoVXJmLb23b+nU8K1ii2LvXJ4SNG31vpvC2caPfMjOP/EzTpj4hJCf7nw5v4detW/
slRlWyqNmUCESi6cAB3zocbgDYsKHgyrxxo08ihZn5q2/hW/nCiaNt2wo3DBw4AJs3+2mewlt6un/
cvNlvGRlHfq5ePR9S69Y+OZx4YslbkyZKGNWVEoFIdXbwoL8Sl3QbH35efD3oxo19Uih86154O/
FEf6tfgTm5v/vOF2LS031i2LLFt1mEt82b/WN29pGfrVvXt1m0auW34s/DW4sW/hSUNKpOtMYRiMjRiI/
33X86dSr5/bw830IcLkEUruvZvNmPldi+veigBfAN2oWvxOGtRYuiV+WWLYvU/
dSr59u727UrO+zsbJ8ktm71j+Ft2za/b9MmP/
Hf9u3+FIqrV68glBYtfAetFi2Kbs2bF2xqy4gclQhEaoPDh4vexoevzlu3FmxbtpR+VY6L82Mmil+Rw1fhZ
s2KPk9KOurSRm6ub5vYtu3Ibft2/5iRUfC8eOEnrGHDookhHEo4tPBj06b+eWKiFikqTCUCkdouLq6gTaEs
eXmwY0fRq3B4y8gouCKnpfnnWVklf09MjG8QaNas4Mpb+LFpU/
9+06bENm1KyyZNaNm5CfSsV2Z4zvmf3L7db4VDCj/PyPA5bfly//
zgwdJDLBxSkyalb0lJBc8bNQpeAlEiEAmSmJiC2+mjceCAv53PzCy4Coef79jhn+/
Y4RPHZ5/516Xd0oOfIbbwlTf8mJgISUlYUhKNkpJolJhIx6Qk6JwE/
RP9+6VMO75vX0Fo4ZDCYYWfZ2b6mrTPP/fTUxUeM1jSP1Fiom/DyN9sL403f0njjctp0jqeppcNoFn/U/
JzXrhk0qBBzWz3UNWQiFQe5/
xVdudOfyXesaPg+c6dflBe4cfw8127io7FKEm9ev4KfcRVunHRfY0aHfm8USNISPANDWZ8953/
yR07jgwl/HzPrjz2rN7CnrWZ7NkLe60xu+s0Y8/
hE3CUXGSoX78gz4Zr1po2Lfj5hITSnyck+FwXqUSiqiERqRpmvjI/
PJLtWBw65KcZDyeGXbv8TLK7dxdsu3b5xz17/
JaeXvC6vEQCfr6OhATqJSTQKiGBVo0a+atxeAsnjYYH4D//
9G0rHTrAb8fDtddC04bkZu1n14NPkvnQbHbshB29hpJ58dVkNu54RHXWypU+2ZRVAikeXqNG/
p8vIeHIx8sugyuuOLZ/1qOhEoGI1A6HD/sRdXv2FDyGt717feNDeAu/
LvwY3rKyfEK78EK46SYYNqzkRoN9++Dvf4f77vNX/
uTkoqWUcGJJSCDvhIZk10lkrzUmyxqxl0ZkuYZkuQbsPXwCWTn1yTocT9Z3ddl7MI7sg3FkH4ghO9vIyvI9
tLKzYdw4mDSpYv88KhGISO0XF1fQUH088vJ86aS8pVAbNIDbb4fx42HWLL9IUjiZZGT40eV79kBWFjH799M
IaHQscZj5uqYTTvCP9etD3E+BiRU/
t1IoEYiIFBYTc2zrYTdoALfdVvYxeXm+e9O+fb4Ka98+vx044F+HtwMHCvaHt/
D+AweOat2MilAiEBGJtJgYf2dfgXW1q0LAesuKiEhxSgQiIgGnRCAiEnBKBCIiAadEICIScEoEIiIBp0QgI
hJwSgQiIgFX4+YaMrMMYGMFP94MKGH57kAI6rnrvINF5126k51zJc4/
XuMSwfEws0WlTbpU2wX13HXewaLzrhhVDYmIBJwSgYhIwAUtEcyKdgBRFNRz13kHi867AgLVRiAiIkcKWol
ARESKUSIQEQm4wCQCMxtmZqvNbK2ZTY52PJFiZo+b2XYz+6LQviZm9raZrQk9JkUzxkgwszZmNt/
MvjSzlWZ2S2h/rT53M4s3s0/NbFnovH8f2t/ezD4J/b3/
y8zqRjvWSDCzWDP73MxeC72u9edtZhvMbIWZLTWzRaF9x/
V3HohEYGaxwEzgQiAFGG1mKdGNKmKeBIYV2zcZeMc51wl4J/S6tskBfuGcSwHOAG4K/
Teu7ef+HXCOc64X0BsYZmZnAH8GHnTOdQR2AddFMcZIugVYVeh1UM57iHOud6GxA8f1dx6IRAD0A9Y659Kc
c4eAOcDwKMcUEc65BcDOYruHA0+Fnj8FfL9Kg6oCzrktzrkloedZ+IvDSdTyc3deduhlXGhzwDnAC6H9te6
8AcwsGbgYeDT02gjAeZfiuP7Og5IITgI2FXqdHtoXFC2dc1tCz7cCkVkBu5ows3ZAH+ATAnDuoeqRpcB24G
1gHbDbOZcTOqS2/
r1PA34F5IVeNyUY5+2At8xssZmNC+07rr9zLV4fMM45Z2a1ts+wmTUEXgRudc7t9TeJXm09d+dcLtDbzBKB
uUCXKIcUcWZ2CbDdObfYzM6OdjxVbKBzbrOZtQDeNrOvCr9Zkb/
zoJQINgNtCr1ODu0Lim1mdiJA6HF7lOOJCDOLwyeB2c65/4R2B+LcAZxzu4H5wJlAopmFb/
Rq49/7AOAyM9uAr+o9B3iI2n/eOOc2hx634xN/
P47z7zwoieAzoFOoR0Fd4ErglSjHVJVeAX4cev5j4OUoxhIRofrhx4BVzrkHCr1Vq8/
dzJqHSgKYWX3gPHz7yHxgZOiwWnfezrlfO+eSnXPt8P8/
v+ucG0MtP28za2BmCeHnwPnAFxzn33lgRhab2UX4OsVY4HHn3D1RDikizOw54Gz8tLTbgCnAS8DzQFv8FN6
jnHPFG5RrNDMbCPwPWEFBnfEd+HaCWnvuZtYT3zgYi7+xe945N9XMOuDvlJsAnwNjnXPfRS/
SyAlVDd3unLuktp936Pzmhl7WAZ51zt1jZk05jr/
zwCQCEREpWVCqhkREpBRKBCIiAadEICIScEoEIiIBp0QgIhJwSgQixZhZbmhmx/
BWaRPVmVm7wjPDilQHmmJC5EgHnHO9ox2ESFVRiUDkKIXmgb8vNBf8p2bWMbS/nZm9a2bLzewdM2sb2t/
SzOaG1gpYZmZnhb4q1sz+EVo/4K3QiGCRqFEiEDlS/WJVQz8s9N4e51wP4P/
wI9UBZgBPOed6ArOB6aH904H3Q2sF9AVWhvZ3AmY657oBu4HLI3w+ImXSyGKRYsws2znXsIT9G/
CLwKSFJrjb6pxramaZwInOucOh/Vucc83MLANILjzFQWiK7LdDC4hgZpOAOOfcHyJ/
ZiIlU4lA5Ni4Up4fi8Jz3+SitjqJMiUCkWPzw0KPH4Wef4ifARNgDH7yO/BLBo6H/
MVjGldVkCLHQnciIkeqH1rxK+y/zrlwF9IkM1uOv6sfHdr3c+AJM/slkAFcG9p/CzDLzK7D3/
mPB7YgUs2ojUDkKIXaCFKdc5nRjkWkMqlqSEQk4FQiEBEJOJUIREQCTolARCTglAhERAJOiUBEJOCUCEREA
u7/AR1K4dIXw3/UAAAAAElFTkSuQmCC\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"# Hyper-parameters\n",
"num_epochs = 50\n",
"\n",
"# Initialize a new network\n",
"net = Net()\n",
"\n",
"# Define a loss function and optimizer for this problem\n",
"# YOUR CODE HERE!\n",
"criterion = nn.CrossEntropyLoss()\n",
"optimizer = torch.optim.Adam(net.parameters(), lr=3e-4)\n",
"\n",
"# Track loss\n",
"training_loss, validation_loss = [], []\n",
"\n",
"# For each epoch\n",
"for i in range(num_epochs):\n",
" \n",
" # Track loss\n",
" epoch_training_loss = 0\n",
" epoch_validation_loss = 0\n",
" \n",
" net.eval()\n",
" \n",
" # For each sentence in validation set\n",
" for inputs, targets in validation_set:\n",
" \n",
" # One-hot encode input and target sequence\n",
" inputs_one_hot = one_hot_encode_sequence(inputs, vocab_size)\n",
" targets_idx = [word_to_idx[word] for word in targets]\n",
" \n",
" # Convert input to tensor\n",
" inputs_one_hot = torch.Tensor(inputs_one_hot)\n",
" inputs_one_hot = inputs_one_hot.permute(0, 2, 1)\n",
" \n",
" # Convert target to tensor\n",
" targets_idx = torch.LongTensor(targets_idx)\n",
" \n",
" # Forward pass\n",
" # YOUR CODE HERE!\n",
" outputs = net.forward(inputs_one_hot)\n",
" \n",
" # Compute loss\n",
" # YOUR CODE HERE!\n",
" loss = criterion(outputs, targets_idx)\n",
" \n",
" # Update loss\n",
" epoch_validation_loss += loss.detach().numpy()\n",
" \n",
" net.train()\n",
" \n",
" # For each sentence in training set\n",
" for inputs, targets in training_set:\n",
" \n",
" # One-hot encode input and target sequence\n",
" inputs_one_hot = one_hot_encode_sequence(inputs, vocab_size)\n",
" targets_idx = [word_to_idx[word] for word in targets]\n",
" \n",
" # Convert input to tensor\n",
" inputs_one_hot = torch.Tensor(inputs_one_hot)\n",
" inputs_one_hot = inputs_one_hot.permute(0, 2, 1)\n",
" \n",
" # Convert target to tensor\n",
" targets_idx = torch.LongTensor(targets_idx)\n",
" \n",
" # Forward pass\n",
" # YOUR CODE HERE!\n",
" outputs = net.forward(inputs_one_hot)\n",
" \n",
" # Compute loss\n",
" # YOUR CODE HERE!\n",
" loss = criterion(outputs, targets_idx)\n",
" \n",
" # Backward pass\n",
" # YOUR CODE HERE!\n",
" optimizer.zero_grad()\n",
" loss.backward()\n",
" optimizer.step()\n",
" \n",
" # Update loss\n",
" epoch_training_loss += loss.detach().numpy()\n",
" \n",
" # Save loss for plot\n",
" training_loss.append(epoch_training_loss/len(training_set))\n",
" validation_loss.append(epoch_validation_loss/len(validation_set))\n",
"\n",
" # Print loss every 5 epochs\n",
" if i % 5 == 0:\n",
" print(f'Epoch {i}, training loss: {training_loss[-1]}, validation
loss: {validation_loss[-1]}')\n",
"\n",
" \n",
"# Get first sentence in test set\n",
"inputs, targets = test_set[1]\n",
"\n",
"# One-hot encode input and target sequence\n",
"inputs_one_hot = one_hot_encode_sequence(inputs, vocab_size)\n",
"targets_idx = [word_to_idx[word] for word in targets]\n",
"\n",
"# Convert input to tensor\n",
"inputs_one_hot = torch.Tensor(inputs_one_hot)\n",
"inputs_one_hot = inputs_one_hot.permute(0, 2, 1)\n",
"\n",
"# Convert target to tensor\n",
"targets_idx = torch.LongTensor(targets_idx)\n",
"\n",
"# Forward pass\n",
"# YOUR CODE HERE!\n",
"outputs = net.forward(inputs_one_hot).data.numpy()\n",
"\n",
"print('\\nInput sequence:')\n",
"print(inputs)\n",
"\n",
"print('\\nTarget sequence:')\n",
"print(targets)\n",
"\n",
"print('\\nPredicted sequence:')\n",
"print([idx_to_word[np.argmax(output)] for output in outputs])\n",
"\n",
"# Plot training and validation loss\n",
"epoch = np.arange(len(training_loss))\n",
"plt.figure()\n",
"plt.plot(epoch, training_loss, 'r', label='Training loss',)\n",
"plt.plot(epoch, validation_loss, 'b', label='Validation loss')\n",
"plt.legend()\n",
"plt.xlabel('Epoch'), plt.ylabel('NLL')\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercise:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Finish the training loop above and run the training. Compare your loss to the
numpy implementation. Are they similar?\n",
"\n",
"Try to play around with the hyper-parameters and hidden dimensions. How low of
a negative log-likelihood can you achieve?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercise (optional)\n",
"\n",
"Aside from the LSTM cell, various other RNN cells exist. The gated recurrent
unit (GRU) is a variation of the LSTM cell that uses less parameters. Try to look
it up in the [PyTorch documentation](https://pytorch.org/docs/stable/nn.html#gru)
and switch out the LSTM cell in the code above. What do you notice in terms of
performance and convergence speed?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Where to go from here?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In this notebook you have learned how to use embeddings, recurrent neural
networks and the LSTM cell in particular.\n",
"\n",
"As we have already seen, RNNs are excellent for sequential data such as
language. But what do we do if we're modelling data with strong dependency in both
directions? Like in many things deep learning, we can build powerful models by
stacking layers on top of each other: *bi-directional* RNNs consist of two LSTM
cells, one for each direction. A sequence is first fed into the forward LSTM cell
and the reversed sequence is then used as input to the backward LSTM cell together
with the last hidden state from the forward LSTM cell. Follow [this
link](https://pdfs.semanticscholar.org/4b80/89bc9b49f84de43acc2eb8900035f7d492b2.pd
f) for the original paper from 1997(!).\n",
"\n",
"For even deeper representations, multiple layers of both uni-directional and
bi-directional RNNs can be stacked ontop of each other, just like feed-forward and
convolutional layers! For more information on this, check out the [LSTM PyTorch
documentation](https://pytorch.org/docs/stable/nn.html#lstm)."
]
}
],
"metadata": {
"colab": {
"collapsed_sections": [],
"name": "5_Sequencial_Data (1).ipynb",
"provenance": [],
"version": "0.3.2"
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.4"
}
},
"nbformat": 4,
"nbformat_minor": 1
}

You might also like