Commit 3d6d8743 authored by Mirko Birbaumer's avatar Mirko Birbaumer
Browse files

Cells executed

parent a1c48493
%% Cell type:markdown id: tags:
 
# Part I : A Machine Translation Example with RNN Sequence-to-Sequence Models
 
We’ll demonstrate sequence-to-sequence modeling on a machine translation task.
We’ll start with a recurrent sequence model, and we’ll follow up with the full Transformer architecture in Part III.
 
We’ll be working with an English-to-Spanish translation dataset available at
www.manythings.org/anki/. Let’s download it:
 
%% Cell type:code id: tags:
 
``` python
!wget http://storage.googleapis.com/download.tensorflow.org/data/spa-eng.zip
!unzip -q spa-eng.zip
```
 
%%%% Output: stream
 
--2022-10-25 12:27:39-- http://storage.googleapis.com/download.tensorflow.org/data/spa-eng.zip
Resolving storage.googleapis.com (storage.googleapis.com)... 172.217.168.80, 216.58.215.240, 142.250.203.112, ...
Connecting to storage.googleapis.com (storage.googleapis.com)|172.217.168.80|:80... connected.
--2022-10-26 13:50:24-- http://storage.googleapis.com/download.tensorflow.org/data/spa-eng.zip
Resolving storage.googleapis.com (storage.googleapis.com)... 142.250.203.112, 172.217.168.16, 172.217.168.80, ...
Connecting to storage.googleapis.com (storage.googleapis.com)|142.250.203.112|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2638744 (2,5M) [application/zip]
Length: 2638744 (2.5M) [application/zip]
Saving to: ‘spa-eng.zip’
spa-eng.zip 100%[===================>] 2,52M 12,0MB/s in 0,2s
spa-eng.zip 100%[===================>] 2.52M --.-KB/s in 0.1s
2022-10-25 12:27:39 (12,0 MB/s) - ‘spa-eng.zip’ saved [2638744/2638744]
2022-10-26 13:50:24 (25.4 MB/s) - ‘spa-eng.zip’ saved [2638744/2638744]
 
%% Cell type:markdown id: tags:
 
The text file contains one example per line: an English sentence, followed by a tab
character, followed by the corresponding Spanish sentence. Let’s parse this file.
 
%% Cell type:code id: tags:
 
``` python
text_file = "spa-eng/spa.txt"
with open(text_file) as f:
# Iterate over the lines in the file.
lines = f.read().split("\n")[:-1]
text_pairs = []
for line in lines:
english, spanish = line.split("\t")
# Each line contains an English phrase and its
# Spanish translation, tab-separated.
spanish = "[start] " + spanish + " [end]"
# We prepend "[start]" and append "[end]" to the Spanish
# sentence, to match the template
text_pairs.append((english, spanish))
```
 
%% Cell type:markdown id: tags:
 
Our text_pairs look like this:
Our `text_pairs` look like this:
 
%% Cell type:code id: tags:
 
``` python
import random
print(random.choice(text_pairs))
```
 
%%%% Output: stream
("The time is always right to do what's right.", '[start] Siempre es el momento adecuado para hacer lo que es adecuado. [end]')
%% Cell type:markdown id: tags:
 
Let’s shuffle them and split them into the usual training, validation, and test sets:
 
%% Cell type:code id: tags:
 
``` python
import random
random.shuffle(text_pairs)
num_val_samples = int(0.15 * len(text_pairs))
num_train_samples = len(text_pairs) - 2 * num_val_samples
train_pairs = text_pairs[:num_train_samples]
val_pairs = text_pairs[num_train_samples:num_train_samples + num_val_samples]
test_pairs = text_pairs[num_train_samples + num_val_samples:]
```
 
%% Cell type:markdown id: tags:
 
Next, let’s prepare two separate `TextVectorization` layers: one for English and one
for Spanish. We’re going to need to customize the way strings are preprocessed:
 
- We need to preserve the "[start]" and "[end]" tokens that we’ve inserted. By default, the characters [ and ] would be stripped, but we want to keep them around so we can tell apart the word “start” and the start token "[start]".
- Punctuation is different from language to language! In the Spanish `TextVectorization` layer, if we’re going to strip punctuation characters, we need to
also strip the character ¿.
 
%% Cell type:markdown id: tags:
 
Note that for a non-toy translation model, we would treat punctuation characters as separate
tokens rather than stripping them, since we would want to be able to generate correctly
punctuated sentences. In our case, for simplicity, we’ll get rid of all punctuation.
 
%% Cell type:code id: tags:
 
``` python
import tensorflow as tf
import string
import re
strip_chars = string.punctuation + "¿"
strip_chars = strip_chars.replace("[", "")
strip_chars = strip_chars.replace("]", "")
def custom_standardization(input_string):
lowercase = tf.strings.lower(input_string)
return tf.strings.regex_replace(
# Prepare a custom string standardization function for the
# Spanish TextVectorization layer: it preserves [ and ] but strips ¿
# (as well as all other characters
# from strings.punctuation).
lowercase, f"[{re.escape(strip_chars)}]", "")
```
 
%% Cell type:code id: tags:
 
``` python
import tensorflow as tf
# To keep things simple, we’ll only look at
# the top 15,000 words in each language,
# and we’ll restrict sentences to 20 words.
vocab_size = 15000
sequence_length = 20
 
# The English layer
source_vectorization = tf.keras.layers.TextVectorization(
max_tokens=vocab_size,
output_mode="int",
output_sequence_length=sequence_length,
)
# The Spanish layer
target_vectorization = tf.keras.layers.TextVectorization(
max_tokens=vocab_size,
output_mode="int",
# Generate Spanish sentences that have one extra token,
# since we’ll need to offset the sentence by one step
# during training.
output_sequence_length=sequence_length + 1,
standardize=custom_standardization,
)
train_english_texts = [pair[0] for pair in train_pairs]
train_spanish_texts = [pair[1] for pair in train_pairs]
# Learn the vocabulary of each language.
source_vectorization.adapt(train_english_texts)
target_vectorization.adapt(train_spanish_texts)
 
train_english_texts = [pair[0] for pair in train_pairs]
train_spanish_texts = [pair[1] for pair in train_pairs]
source_vectorization.adapt(train_english_texts)
target_vectorization.adapt(train_spanish_texts)
```
 
%% Cell type:markdown id: tags:
 
Finally, we can turn our data into a `tf.data` pipeline. We want it to return a tuple
`(inputs, target)` where `inputs` is a dict with two keys, “encoder_inputs” (the English
sentence) and “decoder_inputs” (the Spanish sentence), and `target` is the Spanish
sentence offset by one step ahead.
 
%% Cell type:code id: tags:
 
``` python
batch_size = 64
def format_dataset(eng, spa):
eng = source_vectorization(eng)
spa = target_vectorization(spa)
return ({
"english": eng,
# The input Spanish sentence doesn’t include the last token
# to keep inputs and targets at the same length.
"spanish": spa[:, :-1],
# The target Spanish sentence is one step ahead. Both are still
# the same length (20 words).
}, spa[:, 1:])
 
def make_dataset(pairs):
eng_texts, spa_texts = zip(*pairs)
eng_texts = list(eng_texts)
spa_texts = list(spa_texts)
dataset = tf.data.Dataset.from_tensor_slices((eng_texts, spa_texts))
dataset = dataset.batch(batch_size)
dataset = dataset.map(format_dataset, num_parallel_calls=4)
# Use in-memory caching to speed up preprocessing.
return dataset.shuffle(2048).prefetch(16).cache()
 
train_ds = make_dataset(train_pairs)
val_ds = make_dataset(val_pairs)
```
 
%% Cell type:code id: tags:
 
``` python
for inputs, targets in train_ds.take(1):
print(f"inputs['english'].shape: {inputs['english'].shape}")
print(f"inputs['spanish'].shape: {inputs['spanish'].shape}")
print(f"targets.shape: {targets.shape}")
```
 
%% Cell type:markdown id: tags:
 
## Sequence-to-sequence learning with RNNs
## Sequence-to-Sequence Learning with RNNs
 
Recurrent neural networks dominated sequence-to-sequence learning from 2015–2017
before being overtaken by Transformer. They were the basis for many realworld
machine-translation systems. Google Translate
circa 2017 was powered by a stack of seven large LSTM layers. It’s still worth learning
about this approach today, as it provides an easy entry point to understanding
sequence-to-sequence models.
 
The simplest, naive way to use RNNs to turn a sequence into another sequence is
to keep the output of the RNN at each time step. In Keras, it would look like this:
 
%% Cell type:code id: tags:
 
``` python
inputs = tf.keras.Input(shape=(sequence_length,), dtype="int64")
x = tf.keras.layers.Embedding(input_dim=vocab_size, output_dim=128)(inputs)
x = tf.keras.layers.LSTM(32, return_sequences=True)(x)
outputs = tf.keras.layers.Dense(vocab_size, activation="softmax")(x)
model = tf.keras.Model(inputs, outputs)
```
 
%% Cell type:markdown id: tags:
 
However, there are two major issues with this approach:
 
- The target sequence must always be the same length as the source sequence. In practice, this is rarely the case. Technically, this isn’t critical, as you could always pad either the source sequence or the target sequence to make their lengths match.
 
- Due to the step-by-step nature of RNNs, the model will only be looking at tokens $0\ldots N$ in the source sequence in order to predict token $N$ in the target sequence. This constraint makes this setup unsuitable for most tasks, and particularly translation. Consider translating “The weather is nice today” to French — that would be “Il fait beau aujourd’hui.” You’d need to be able to predict “Il” from just “The,” “Il fait” from just “The weather,” etc., which is simply impossible.
 
%% Cell type:markdown id: tags:
 
If you’re a human translator, you’d start by reading the entire source sentence before
starting to translate it. This is especially important if you’re dealing with languages
that have wildly different word ordering, like English and Japanese. And that’s exactly
what standard sequence-to-sequence models do.
 
In a proper sequence-to-sequence setup, you would first use an
RNN (the encoder) to turn the entire source sequence into a single vector (or set of
vectors). This could be the last output of the RNN, or alternatively, its final internal
state vectors. Then you would use this vector (or vectors) as the initial state of another
 
%% Cell type:markdown id: tags:
 
RNN (the decoder), which would look at elements $0\ldots N$ in the target sequence, and
try to predict step $N+1$ in the target sequence.
 
Let’s implement this in Keras with GRU-based encoders and decoders. The choice
of GRU rather than LSTM makes things a bit simpler, since GRU only has a single
state vector, whereas LSTM has multiple. Let’s start with the encoder.
 
%% Cell type:markdown id: tags:
 
### GRU-based encoder
 
%% Cell type:code id: tags:
 
``` python
from tensorflow import keras
from tensorflow.keras import layers
embed_dim = 256
latent_dim = 1024
 
# The English source sentence goes here. Specifying the name of the input enables
# us to fit() the model with a dict of inputs.
source = keras.Input(shape=(None,), dtype="int64", name="english")
# Don’t forget masking: it’s critical in this setup.
x = layers.Embedding(vocab_size, embed_dim, mask_zero=True)(source)
encoded_source = layers.Bidirectional(
# Our encoded source sentence is the last output of a bidirectional GRU.
layers.GRU(latent_dim), merge_mode="sum")(x)
```
 
%% Cell type:markdown id: tags:
 
Next, let’s add the decoder — a simple GRU layer that takes as its initial state the
encoded source sentence. On top of it, we add a Dense layer that produces for each
output step a probability distribution over the Spanish vocabulary.
 
%% Cell type:markdown id: tags:
 
#### GRU-based decoder and the end-to-end model
 
%% Cell type:code id: tags:
 
``` python
# The Spanish target sentence goes here.
past_target = keras.Input(shape=(None,), dtype="int64", name="spanish")
# Don’t forget masking.
x = layers.Embedding(vocab_size, embed_dim, mask_zero=True)(past_target)
decoder_gru = layers.GRU(latent_dim, return_sequences=True)
# The encoded source sentence serves as the initial state of
# the decoder GRU.
x = decoder_gru(x, initial_state=encoded_source)
x = layers.Dropout(0.5)(x)
# Predicts the next token
target_next_step = layers.Dense(vocab_size, activation="softmax")(x)
# End-to-end model: maps the source sentence and the target sentence to the
# target sentence one step in the future
seq2seq_rnn = keras.Model([source, past_target], target_next_step)
```
 
%% Cell type:markdown id: tags:
 
During training, the decoder takes as input the entire target sequence, but thanks to
the step-by-step nature of RNNs, it only looks at tokens $0\ldots N$ in the input to predict
token $N$ in the output (which corresponds to the next token in the sequence, since
the output is intended to be offset by one step). This means we only use information
from the past to predict the future, as we should; otherwise we’d be cheating, and our
model would not work at inference time.
Let’s start training.
 
%% Cell type:code id: tags:
 
``` python
seq2seq_rnn.compile(
optimizer="rmsprop",
loss="sparse_categorical_crossentropy",
metrics=["accuracy"])
seq2seq_rnn.fit(train_ds, epochs=15, validation_data=val_ds)
```
 
%% Cell type:markdown id: tags:
 
We picked accuracy as a crude way to monitor validation-set performance during
training. We get to 64% accuracy: on average, the model predicts the next word in the
Spanish sentence correctly 64% of the time. However, in practice, next-token accuracy
isn’t a great metric for machine translation models, in particular because it makes the
assumption that the correct target tokens from $0$ to $N$ are already known when predicting
token N+1. In reality, during inference, you’re generating the target sentence
from scratch, and you can’t rely on previously generated tokens being 100% correct.
If you work on a real-world machine translation system, you will likely use “BLEU
scores” to evaluate your models — a metric that looks at entire generated sequences
and that seems to correlate well with human perception of translation quality.
 
 
At last, let’s use our model for inference. We’ll pick a few sentences in the test set
and check how our model translates them. We’ll start from the seed token, "[start]",
and feed it into the decoder model, together with the encoded English source sentence.
We’ll retrieve a next-token prediction, and we’ll re-inject it into the decoder
repeatedly, sampling one new target token at each iteration, until we get to "[end]"
or reach the maximum sentence length.
 
%% Cell type:markdown id: tags:
 
#### Translating new sentences with our RNN encoder and decoder
 
%% Cell type:code id: tags:
 
``` python
import numpy as np
spa_vocab = target_vectorization.get_vocabulary()
# Prepare a dict to convert token index predictions to string tokens.
spa_index_lookup = dict(zip(range(len(spa_vocab)), spa_vocab))
max_decoded_sentence_length = 20
def decode_sequence(input_sentence):
tokenized_input_sentence = source_vectorization([input_sentence])
# Seed token
decoded_sentence = "[start]"
for i in range(max_decoded_sentence_length):
tokenized_target_sentence = target_vectorization([decoded_sentence])
# Sample the next token.
next_token_predictions = seq2seq_rnn.predict(
[tokenized_input_sentence, tokenized_target_sentence])
# Convert the next token prediction to
# a string and append it to the generated sentence.
sampled_token_index = np.argmax(next_token_predictions[0, i, :])
sampled_token = spa_index_lookup[sampled_token_index]
decoded_sentence += " " + sampled_token
# Exit condition: either hit max
# length or sample a stop character
if sampled_token == "[end]":
break
return decoded_sentence
```
 
%% Cell type:code id: tags:
 
``` python
test_eng_texts = [pair[0] for pair in test_pairs]
for _ in range(20):
input_sentence = random.choice(test_eng_texts)
print("-")
print(input_sentence)
print(decode_sequence(input_sentence))
```
 
%% Cell type:markdown id: tags:
 
Note that this inference setup, while very simple, is rather inefficient, since we reprocess
the entire source sentence and the entire generated target sentence every time
we sample a new word. In a practical application, you’d factor the encoder and the
decoder as two separate models, and your decoder would only run a single step at
each token-sampling iteration, reusing its previous internal state.
 
Here are our translation results. Our model works decently well for a toy model,
though it still makes many basic mistakes.
 
%% Cell type:markdown id: tags:
 
There are many ways this toy model could be improved: We could use a deep stack of
recurrent layers for both the encoder and the decoder (note that for the decoder, this
makes state management a bit more involved). We could use an LSTM instead of a GRU.
And so on. Beyond such tweaks, however, the RNN approach to sequence-to-sequence
learning has a few fundamental limitations:
 
- The source sequence representation has to be held entirely in the encoder state vector(s), which puts significant limitations on the size and complexity of the sentences you can translate. It’s a bit as if a human were translating a sentence entirely from memory, without looking twice at the source sentence while producing the translation.
 
- RNNs have trouble dealing with very long sequences, since they tend to progressively forget about the past—by the time you’ve reached the 100th token in either sequence, little information remains about the start of the sequence. That means RNN-based models can’t hold onto long-term context, which can be essential for translating long documents.
 
%% Cell type:markdown id: tags:
 
These limitations are what has led the machine learning community to embrace the
Transformer architecture for sequence-to-sequence problems. Let’s take a look.
 
%% Cell type:markdown id: tags:
 
# Part II : Attention Basics
In this notebook, we look at how attention is implemented. We will focus on implementing attention in isolation from a larger model. That's because when implementing attention in a real-world model, a lot of the focus goes into piping the data and juggling the various vectors rather than the concepts of attention themselves.
 
We will implement attention scoring as well as calculating an attention context vector.
 
## Attention Scoring
### Inputs to the scoring function
Let's start by looking at the inputs we'll give to the scoring function. We will assume we're in the first step in the decoding phase. The first input to the scoring function is the hidden state of decoder (assuming a toy RNN with three hidden nodes -- not usable in real life, but easier to illustrate):
 
%% Cell type:code id: tags:
 
``` python
dec_hidden_state = [5,1,20]
```
 
%% Cell type:markdown id: tags:
 
Let's visualize this vector:
 
%% Cell type:code id: tags:
 
``` python
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
 
# Let's visualize our decoder hidden state
plt.figure(figsize=(1.5, 4.5))
sns.heatmap(np.transpose(np.matrix(dec_hidden_state)), annot=True, cmap=sns.light_palette("purple", as_cmap=True), linewidths=1)
```
 
%%%% Output: error
 
---------------------------------------------------------------------------
ModuleNotFoundError Traceback (most recent call last)
Input In [2], in <cell line: 4>()
2 import numpy as np
3 import matplotlib.pyplot as plt
----> 4 import seaborn as sns
6 # Let's visualize our decoder hidden state
7 plt.figure(figsize=(1.5, 4.5))
ModuleNotFoundError: No module named 'seaborn'
 
%% Cell type:markdown id: tags:
 
Our first scoring function will score a single annotation (encoder hidden state), which looks like this:
 
%% Cell type:code id: tags:
 
``` python
annotation = [3,12,45] #e.g. Encoder hidden state
```
 
%% Cell type:code id: tags:
 
``` python
# Let's visualize the single annotation
plt.figure(figsize=(1.5, 4.5))
sns.heatmap(np.transpose(np.matrix(annotation)), annot=True, cmap=sns.light_palette("orange", as_cmap=True), linewidths=1)
```
 
%%%% Output: execute_result
 
<AxesSubplot:>
 
%%%% Output: display_data
 
![](data:image/png;base64,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)
 
%% Cell type:markdown id: tags:
 
### IMPLEMENT: Scoring a Single Annotation
Let's calculate the dot product of a single annotation. Numpy's [dot()](https://docs.scipy.org/doc/numpy/reference/generated/numpy.dot.html) is a good candidate for this operation
 
%% Cell type:code id: tags:
 
``` python
def single_dot_attention_score(dec_hidden_state, enc_hidden_state):
# TODO: return the dot product of the two vectors
return np.dot(dec_hidden_state, enc_hidden_state)
 
single_dot_attention_score(dec_hidden_state, annotation)
```
 
%%%% Output: execute_result
 
927
 
%% Cell type:markdown id: tags:
 
### Annotations Matrix
Let's now look at scoring all the annotations at once. To do that, here's our annotation matrix:
 
%% Cell type:code id: tags:
 
``` python
annotations = np.transpose([[3,12,45], [59,2,5], [1,43,5], [4,3,45.3]])
```
 
%% Cell type:markdown id: tags:
 
And it can be visualized like this (each column is a hidden state of an encoder time step):
 
%% Cell type:code id: tags:
 
``` python
# Let's visualize our annotation (each column is an annotation)
ax = sns.heatmap(annotations, annot=True, cmap=sns.light_palette("orange", as_cmap=True), linewidths=1)
```
 
%%%% Output: display_data
 
![](data:image/png;base64,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)
 
%% Cell type:markdown id: tags:
 
### Scoring All Annotations at Once
Let's calculate the scores of all the annotations in one step using matrix multiplication. Let's continue to us the dot scoring method
 
 
$$\text{score}\left(h_t, \overline{h}_s\right) =
\begin{cases}
h^T_t\overline{h}_s & \quad \text{dot}\\
h^T_t W_a\overline{h}_s & \quad \text{general } \\
v_a\tanh\left(W_a [h^T_t, \overline{h}_s] \right) & \quad \text{concat }
\end{cases}$$
 
 
 
To do that, we'll have to transpose `dec_hidden_state` and [matrix multiply](https://docs.scipy.org/doc/numpy/reference/generated/numpy.matmul.html) it with `annotations`.
 
%% Cell type:code id: tags:
 
``` python
def dot_attention_score(dec_hidden_state, annotations):
# TODO: return the product of dec_hidden_state transpose and enc_hidden_states
return np.matmul(np.transpose(dec_hidden_state), annotations)
 
attention_weights_raw = dot_attention_score(dec_hidden_state, annotations)
attention_weights_raw
```
 
%%%% Output: execute_result
 
array([927., 397., 148., 929.])
 
%% Cell type:markdown id: tags:
 
Looking at these scores, can you guess which of the four vectors will get the most attention from the decoder at this time step?
 
## Softmax
Now that we have our scores, let's apply softmax:
 
 
$$ \begin{align} \alpha_t(s) & = \text{align}\left(h_t, \overline{h}_s\right) \\ & = \frac{\text{score}(h_t, \overline{h}_s)}{\sum_{s'} \text{score}(h_t, \overline{h}_{s'})} \end{align} $$
 
%% Cell type:code id: tags:
 
``` python
def softmax(x):
x = np.array(x, dtype=np.float128)
e_x = np.exp(x)
return e_x / e_x.sum(axis=0)
 
attention_weights = softmax(attention_weights_raw)
attention_weights
```
 
%%%% Output: execute_result
 
array([1.19202922e-001, 7.94715151e-232, 5.76614420e-340, 8.80797078e-001],
dtype=float128)
 
%% Cell type:markdown id: tags:
 
Even when knowing which annotation will get the most focus, it's interesting to see how drastic softmax makes the end score become. The first and last annotation had the respective scores of 927 and 929. But after softmax, the attention they'll get is 0.119 and 0.880 respectively.
 
# Applying the scores back on the annotations
Now that we have our scores, let's multiply each annotation by its score to proceed closer to the attention context vector. This is the multiplication part of this formula (we'll tackle the summation part in the latter cells)
 
$$ c_i = \sum_{j=1}^T \alpha_{ij} h_j $$
 
%% Cell type:code id: tags:
 
``` python
def apply_attention_scores(attention_weights, annotations):
# TODO: Multiple the annotations by their weights
return attention_weights * annotations