View file src/colab/nlp_from_scratch_translation_with_a_sequence_to_sequence_network_with_new_dataloader_without_attention.py - Download

# -*- coding: utf-8 -*-
"""NLP From Scratch: Translation with a Sequence to Sequence Network with new dataloader without attention.ipynb

Automatically generated by Colab.

Original file is located at
    https://colab.research.google.com/drive/13fwa4tua5orLhLP7spa5yEFetIkNNOuv

Translation

https://pytorch.org/tutorials/intermediate/seq2seq_translation_tutorial.html

Author: Sean Robertson

Modified by Jacques Bailhache

This is the third and final tutorial on doing “NLP From Scratch”, where we write our own classes and functions to preprocess the data to do our NLP modeling tasks. We hope after you complete this tutorial that you’ll proceed to learn how torchtext can handle much of this preprocessing for you in the three tutorials immediately following this one.

In this project we will be teaching a neural network to translate from French to English.

[KEY: > input, = target, < output]

> il est en train de peindre un tableau .
= he is painting a picture .
< he is painting a picture .

> pourquoi ne pas essayer ce vin delicieux ?
= why not try that delicious wine ?
< why not try that delicious wine ?

> elle n est pas poete mais romanciere .
= she is not a poet but a novelist .
< she not not a poet but a novelist .

> vous etes trop maigre .
= you re too skinny .
< you re all alone .

… to varying degrees of success.

This is made possible by the simple but powerful idea of the sequence to sequence network, in which two recurrent neural networks work together to transform one sequence to another. An encoder network condenses an input sequence into a vector, and a decoder network unfolds that vector into a new sequence.

![image.png](data:image/png;base64,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)

To improve upon this model we’ll use an attention mechanism, which lets the decoder learn to focus over a specific range of the input sequence.

Recommended Reading:

I assume you have at least installed PyTorch, know Python, and understand Tensors:

    https://pytorch.org/ For installation instructions

    Deep Learning with PyTorch: A 60 Minute Blitz to get started with PyTorch in general

    Learning PyTorch with Examples for a wide and deep overview

    PyTorch for Former Torch Users if you are former Lua Torch user

It would also be useful to know about Sequence to Sequence networks and how they work:

    Learning Phrase Representations using RNN Encoder-Decoder for Statistical Machine Translation

    Sequence to Sequence Learning with Neural Networks

    Neural Machine Translation by Jointly Learning to Align and Translate

    A Neural Conversational Model

You will also find the previous tutorials on NLP From Scratch: Classifying Names with a Character-Level RNN and NLP From Scratch: Generating Names with a Character-Level RNN helpful as those concepts are very similar to the Encoder and Decoder models, respectively.

Requirements
"""

from __future__ import unicode_literals, print_function, division
from io import open
import unicodedata
import re
import random

import torch
import torch.nn as nn
from torch import optim
import torch.nn.functional as F

import numpy as np
from torch.utils.data import TensorDataset, DataLoader, RandomSampler

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

"""Loading data files

The data for this project is a set of many thousands of English to French translation pairs.

This question on Open Data Stack Exchange pointed me to the open translation site https://tatoeba.org/ which has downloads available at https://tatoeba.org/eng/downloads - and better yet, someone did the extra work of splitting language pairs into individual text files here: https://www.manythings.org/anki/

The English to French pairs are too big to include in the repository, so download to data/eng-fra.txt before continuing. The file is a tab separated list of translation pairs:

I am cold.    J'ai froid.

Note

Download the data from here and extract it to the current directory.

Similar to the character encoding used in the character-level RNN tutorials, we will be representing each word in a language as a one-hot vector, or giant vector of zeros except for a single one (at the index of the word). Compared to the dozens of characters that might exist in a language, there are many many more words, so the encoding vector is much larger. We will however cheat a bit and trim the data to only use a few thousand words per language.

![image.png](data:image/png;base64,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)

We’ll need a unique index per word to use as the inputs and targets of the networks later. To keep track of all this we will use a helper class called Lang which has word → index (word2index) and index → word (index2word) dictionaries, as well as a count of each word word2count which will be used to replace rare words later.
"""

!rm data.zip
!rm -r data
!wget https://download.pytorch.org/tutorial/data.zip
!unzip data.zip
!head -n 20 data/eng-fra.txt

SOS_token = 0
EOS_token = 1

class Lang:
    def __init__(self, name):
        self.name = name
        self.word2index = {}
        self.word2count = {}
        self.index2word = {0: "SOS", 1: "EOS"}
        self.n_words = 2  # Count SOS and EOS

    def addSentence(self, sentence):
        indexes = []
        for word in sentence.split(' '):
            indexes.append(self.addWord(word))
        return indexes

    def addWord(self, word):
        if word not in self.word2index:
            self.word2index[word] = self.n_words
            self.word2count[word] = 1
            self.index2word[self.n_words] = word
            self.n_words += 1
            return self.n_words - 1
        else:
            self.word2count[word] += 1
            return self.word2index[word]

testlang = Lang("test")
print(testlang.addWord("hello"))
print(testlang.addWord("bye"))
print(testlang.addWord("hello"))
print(testlang.addSentence("hello man bye"))

"""The files are all in Unicode, to simplify we will turn Unicode characters to ASCII, make everything lowercase, and trim most punctuation."""

# Turn a Unicode string to plain ASCII, thanks to
# https://stackoverflow.com/a/518232/2809427
def unicodeToAscii(s):
    return ''.join(
        c for c in unicodedata.normalize('NFD', s)
        if unicodedata.category(c) != 'Mn'
    )

# Lowercase, trim, and remove non-letter characters
def normalizeString(s):
    s = unicodeToAscii(s.lower().strip())
    s = re.sub(r"([.!?])", r" \1", s)
    s = re.sub(r"[^a-zA-Z!?]+", r" ", s)
    return s.strip()

"""Since there are a lot of example sentences and we want to train something quickly, we’ll trim the data set to only relatively short and simple sentences. Here the maximum length is MAX_LENGTH words (that includes ending punctuation)."""

MAX_LENGTH = 8

eng_prefixes = (
    "i am ", "i m ",
    "he is", "he s ",
    "she is", "she s ",
    "you are", "you re ",
    "we are", "we re ",
    "they are", "they re "
)

def filterPair(p):
    # return len(p[0].split(' ')) < MAX_LENGTH and len(p[1].split(' ')) < MAX_LENGTH and p[1].startswith(eng_prefixes)
    c1 = len(p[0].split(' ')) < MAX_LENGTH
    c2 = len(p[1].split(' ')) < MAX_LENGTH
    c3 = p[1].startswith(eng_prefixes)
    # print(f"c1={c1} c2={c2} c3={c3}")
    return c1 and c2

def filterPairs(pairs):
    return [pair for pair in pairs if filterPair(pair)]

"""To read the data file we will split the file into lines, and then split lines into pairs. The files are all English → Other Language, so if we want to translate from Other Language → English I added the reverse flag to reverse the pairs."""

def readLangs(langname1, langname2, reverse=False):

    lang1 = Lang(langname1)
    lang2 = Lang(langname2)

    print("Reading lines...")
    # Read the file and split into lines
    lines = open('data/%s-%s.txt' % (langname1, langname2), encoding='utf-8').\
        read().strip().split('\n')

    pairs = []

    for l in lines:
        s1, s2 = [normalizeString(s) for s in l.split('\t')]
        if filterPair([s1, s2]):
            t1 = lang1.addSentence(s1)
            t2 = lang2.addSentence(s2)
            if reverse:
                pairs.append([t2, t1])
            else:
                pairs.append([t1, t2])

    if reverse:
        return lang2, lang1, pairs
    else:
        return lang1, lang2, pairs

def indexesFromSentence(lang, sentence):
    return [lang.word2index[word] for word in sentence.split(' ')]

def tensorFromSentence(lang, sentence):
    indexes = indexesFromSentence(lang, sentence)
    indexes.append(EOS_token)
    return torch.tensor(indexes, dtype=torch.long, device=device).view(1, -1)

def tensorFromIndexes(indexes):
    return torch.tensor(indexes, dtype=torch.long, device=device).view(1, -1)

def sentenceFromIndexes(lang, indexes):
    return [lang.index2word[index] for index in indexes]

"""The full process for preparing the data is:

    Read text file and split into lines, split lines into pairs

    Normalize text, filter by length and content

    Make word lists from sentences in pairs
"""

def prepareData(lang1, lang2, reverse=False):
    input_lang, output_lang, pairs = readLangs(lang1, lang2, reverse)
    print("Read %s sentence pairs" % len(pairs))
    print("Counted words:")
    print(input_lang.name, input_lang.n_words)
    print(output_lang.name, output_lang.n_words)
    return input_lang, output_lang, pairs

input_lang, output_lang, pairs = prepareData('eng', 'fra', True)
print(random.choice(pairs))
print(pairs[15])
print(sentenceFromIndexes(input_lang, pairs[15][0]))
print(sentenceFromIndexes(output_lang, pairs[15][1]))

def get_dataloader(batch_size):
    input_lang, output_lang, pairs = prepareData('eng', 'fra', True)

    n = len(pairs)
    input_ids = np.zeros((n, MAX_LENGTH), dtype=np.int32)
    target_ids = np.zeros((n, MAX_LENGTH), dtype=np.int32)

    for idx, (inp_ids, tgt_ids) in enumerate(pairs):
        inp_ids.append(EOS_token)
        tgt_ids.append(EOS_token)
        input_ids[idx, :len(inp_ids)] = inp_ids
        target_ids[idx, :len(tgt_ids)] = tgt_ids

    train_data = TensorDataset(torch.LongTensor(input_ids).to(device),
                               torch.LongTensor(target_ids).to(device))

    train_sampler = RandomSampler(train_data)
    train_dataloader = DataLoader(train_data, sampler=train_sampler, batch_size=batch_size)
    return input_lang, output_lang, train_dataloader

batch_size = 5
input_lang, output_lang, train_dataloader = get_dataloader(batch_size)
for data in train_dataloader:
    print(data)
    inp, out = data
    print(f"inp = {inp}")
    print(f"out = {out}")
    break

"""The Seq2Seq Model

A Recurrent Neural Network, or RNN, is a network that operates on a sequence and uses its own output as input for subsequent steps.

A Sequence to Sequence network, or seq2seq network, or Encoder Decoder network, is a model consisting of two RNNs called the encoder and decoder. The encoder reads an input sequence and outputs a single vector, and the decoder reads that vector to produce an output sequence.

![image.png](data:image/png;base64,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)

Unlike sequence prediction with a single RNN, where every input corresponds to an output, the seq2seq model frees us from sequence length and order, which makes it ideal for translation between two languages.

Consider the sentence Je ne suis pas le chat noir → I am not the black cat. Most of the words in the input sentence have a direct translation in the output sentence, but are in slightly different orders, e.g. chat noir and black cat. Because of the ne/pas construction there is also one more word in the input sentence. It would be difficult to produce a correct translation directly from the sequence of input words.

With a seq2seq model the encoder creates a single vector which, in the ideal case, encodes the “meaning” of the input sequence into a single vector — a single point in some N dimensional space of sentences.
The Encoder

The encoder of a seq2seq network is a RNN that outputs some value for every word from the input sentence. For every input word the encoder outputs a vector and a hidden state, and uses the hidden state for the next input word.

![image.png](data:image/png;base64,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)
"""

class EncoderRNN(nn.Module):
    def __init__(self, input_size, hidden_size, dropout_p=0.1):
        super(EncoderRNN, self).__init__()
        self.hidden_size = hidden_size

        self.embedding = nn.Embedding(input_size, hidden_size)
        self.gru = nn.GRU(hidden_size, hidden_size, batch_first=True)
        self.dropout = nn.Dropout(dropout_p)

    def forward(self, input):
        # print(f"Encoder input: {input[0]}")
        embedded = self.embedding(input)
        dropped = self.dropout(embedded)
        # embedded = self.embedding(input)
        output, hidden = self.gru(dropped)
        # print(f"EncoderRNN input:{input.shape} embedded:{embedded.shape} dropped:{dropped.shape} output:{output.shape} hidden:{hidden.shape}")
        return output, hidden

"""The Decoder

The decoder is another RNN that takes the encoder output vector(s) and outputs a sequence of words to create the translation.
Simple Decoder

In the simplest seq2seq decoder we use only last output of the encoder. This last output is sometimes called the context vector as it encodes context from the entire sequence. This context vector is used as the initial hidden state of the decoder.

At every step of decoding, the decoder is given an input token and hidden state. The initial input token is the start-of-string  token, and the first hidden state is the context vector (the encoder’s last hidden state).

![image.png](data:image/png;base64,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)
"""

class DecoderRNN(nn.Module):
    def __init__(self, hidden_size, output_size):
        super(DecoderRNN, self).__init__()
        self.embedding = nn.Embedding(output_size, hidden_size)
        self.gru = nn.GRU(hidden_size, hidden_size, batch_first=True)
        self.out = nn.Linear(hidden_size, output_size)

    def forward(self, encoder_outputs, encoder_hidden, target_tensor=None):
        batch_size = encoder_outputs.size(0)
        decoder_input = torch.empty(batch_size, 1, dtype=torch.long, device=device).fill_(SOS_token)
        decoder_hidden = encoder_hidden
        decoder_outputs = []

        for i in range(MAX_LENGTH):
            decoder_output, decoder_hidden  = self.forward_step(decoder_input, decoder_hidden)
            decoder_outputs.append(decoder_output)

            if target_tensor is not None:
                # Teacher forcing: Feed the target as the next input
                decoder_input = target_tensor[:, i].unsqueeze(1) # Teacher forcing
            else:
                # Without teacher forcing: use its own predictions as the next input
                _, topi = decoder_output.topk(1)
                decoder_input = topi.squeeze(-1).detach()  # detach from history as input

        decoder_outputs = torch.cat(decoder_outputs, dim=1)
        decoder_outputs = F.log_softmax(decoder_outputs, dim=-1)
        # print(f"DecoderRNN encoder_outputs:{encoder_outputs.shape} encoder_hidden:{encoder_hidden.shape} target_tensor:{target_tensor.shape} decoder_outputs:{decoder_outputs.shape} decoder_hidden:{decoder_hidden.shape}")
        return decoder_outputs, decoder_hidden, None # We return `None` for consistency in the training loop

    def forward_step(self, input, hidden1):
        output1 = self.embedding(input)
        output2 = F.relu(output1)
        output3, hidden = self.gru(output2, hidden1)
        output = self.out(output3)
        # print(f"DecoderRNN step input:{input.shape} hidden1:{hidden1.shape} output1:{output1.shape} output2:{output2.shape} output3:{output3.shape} hidden:{hidden.shape} output:{output.shape}")
        return output, hidden

"""Training the Model

To train we run the input sentence through the encoder, and keep track of every output and the latest hidden state. Then the decoder is given the  token as its first input, and the last hidden state of the encoder as its first hidden state.

“Teacher forcing” is the concept of using the real target outputs as each next input, instead of using the decoder’s guess as the next input. Using teacher forcing causes it to converge faster but when the trained network is exploited, it may exhibit instability.

You can observe outputs of teacher-forced networks that read with coherent grammar but wander far from the correct translation - intuitively it has learned to represent the output grammar and can “pick up” the meaning once the teacher tells it the first few words, but it has not properly learned how to create the sentence from the translation in the first place.

Because of the freedom PyTorch’s autograd gives us, we can randomly choose to use teacher forcing or not with a simple if statement. Turn teacher_forcing_ratio up to use more of it.
"""

def train_epoch(dataloader, encoder, decoder, encoder_optimizer,
          decoder_optimizer, criterion):

    total_loss = 0
    for data in dataloader:
        input_tensor, target_tensor = data

        encoder_optimizer.zero_grad()
        decoder_optimizer.zero_grad()

        encoder_outputs, encoder_hidden = encoder(input_tensor)
        decoder_outputs, _, _ = decoder(encoder_outputs, encoder_hidden, target_tensor)

        loss = criterion(
            decoder_outputs.view(-1, decoder_outputs.size(-1)),
            target_tensor.view(-1)
        )
        loss.backward()

        encoder_optimizer.step()
        decoder_optimizer.step()

        total_loss += loss.item()

    return total_loss / len(dataloader)

"""This is a helper function to print time elapsed and estimated time remaining given the current time and progress %."""

import time
import math

def asMinutes(s):
    m = math.floor(s / 60)
    s -= m * 60
    return '%dm %ds' % (m, s)

def timeSince(since, percent):
    now = time.time()
    s = now - since
    es = s / (percent)
    rs = es - s
    return '%s (- %s)' % (asMinutes(s), asMinutes(rs))

"""Plotting results

Plotting is done with matplotlib, using the array of loss values plot_losses saved while training.
"""

import matplotlib.pyplot as plt
plt.switch_backend('agg')
import matplotlib.ticker as ticker
import numpy as np

def showPlot(points):
    plt.figure()
    fig, ax = plt.subplots()
    # this locator puts ticks at regular intervals
    loc = ticker.MultipleLocator(base=0.2)
    ax.yaxis.set_major_locator(loc)
    plt.plot(points)

"""The whole training process looks like this:

    Start a timer

    Initialize optimizers and criterion

    Create set of training pairs

    Start empty losses array for plotting

Then we call train many times and occasionally print the progress (% of examples, time so far, estimated time) and average loss.
"""

def train(train_dataloader, encoder, decoder, n_epochs, learning_rate=0.001,
               print_every=100, plot_every=100):
    start = time.time()
    plot_losses = []
    print_loss_total = 0  # Reset every print_every
    plot_loss_total = 0  # Reset every plot_every

    encoder_optimizer = optim.Adam(encoder.parameters(), lr=learning_rate)
    decoder_optimizer = optim.Adam(decoder.parameters(), lr=learning_rate)
    criterion = nn.NLLLoss()

    for epoch in range(1, n_epochs + 1):
        loss = train_epoch(train_dataloader, encoder, decoder, encoder_optimizer, decoder_optimizer, criterion)
        print_loss_total += loss
        plot_loss_total += loss

        if epoch % print_every == 0:
            print_loss_avg = print_loss_total / print_every
            print_loss_total = 0
            print('%s (%d %d%%) %.4f' % (timeSince(start, epoch / n_epochs),
                                        epoch, epoch / n_epochs * 100, print_loss_avg))

        if epoch % plot_every == 0:
            plot_loss_avg = plot_loss_total / plot_every
            plot_losses.append(plot_loss_avg)
            plot_loss_total = 0

    showPlot(plot_losses)

"""Evaluation

Evaluation is mostly the same as training, but there are no targets so we simply feed the decoder’s predictions back to itself for each step. Every time it predicts a word we add it to the output string, and if it predicts the EOS token we stop there. We also store the decoder’s attention outputs for display later.


"""

def evaluate(encoder, decoder, indexes, input_lang, output_lang):
    with torch.no_grad():
        input_tensor = tensorFromIndexes(indexes)

        encoder_outputs, encoder_hidden = encoder(input_tensor)
        decoder_outputs, decoder_hidden, decoder_attn = decoder(encoder_outputs, encoder_hidden)

        _, topi = decoder_outputs.topk(1)
        decoded_ids = topi.squeeze()

        decoded_words = []
        for idx in decoded_ids:
            if idx.item() == EOS_token:
                decoded_words.append('')
                break
            decoded_words.append(output_lang.index2word[idx.item()])
    return decoded_words, decoder_attn

"""We can evaluate random sentences from the training set and print out the input, target, and output to make some subjective quality judgements:"""

def evaluateRandomly(encoder, decoder, n=10):
    for i in range(n):
        pair = random.choice(pairs)
        print('input :', sentenceFromIndexes(input_lang, pair[0]))
        print('target:', sentenceFromIndexes(output_lang, pair[1]))
        output_words, _ = evaluate(encoder, decoder, pair[0], input_lang, output_lang)
        output_sentence = ' '.join(output_words)
        print('output:', output_sentence)
        print('')

"""Training and Evaluating

With all these helper functions in place (it looks like extra work, but it makes it easier to run multiple experiments) we can actually initialize a network and start training.

Remember that the input sentences were heavily filtered. For this small dataset we can use relatively small networks of 256 hidden nodes and a single GRU layer. After about 40 minutes on a MacBook CPU we’ll get some reasonable results.

Note

If you run this notebook you can train, interrupt the kernel, evaluate, and continue training later. Comment out the lines where the encoder and decoder are initialized and run trainIters again.

"""

hidden_size = 128
batch_size = 32

input_lang, output_lang, train_dataloader = get_dataloader(batch_size)

encoder = EncoderRNN(input_lang.n_words, hidden_size).to(device)
# decoder = AttnDecoderRNN(hidden_size, output_lang.n_words).to(device)
decoder = DecoderRNN(hidden_size, output_lang.n_words).to(device)

# train(train_dataloader, encoder, decoder, 80, print_every=5, plot_every=5)
train(train_dataloader, encoder, decoder, 10, print_every=1, plot_every=5)

"""Set dropout layers to eval mode"""

encoder.eval()
decoder.eval()
evaluateRandomly(encoder, decoder)

input_sentence = "vous devriez plutot boire un verre d eau"
output_words, _ = evaluate(encoder, decoder, indexesFromSentence(input_lang, input_sentence), input_lang, output_lang)
output_sentence = ' '.join(output_words)
print(output_sentence)

input_sentence = "je suis votre ami"
output_words, _ = evaluate(encoder, decoder, indexesFromSentence(input_lang, input_sentence), input_lang, output_lang)
output_sentence = ' '.join(output_words)
print(output_sentence)

input_sentence = "vous avez un beau chat"
output_words, _ = evaluate(encoder, decoder, indexesFromSentence(input_lang, input_sentence), input_lang, output_lang)
output_sentence = ' '.join(output_words)
print(output_sentence)

input_sentence = "vous avez bien fait"
output_words, _ = evaluate(encoder, decoder, indexesFromSentence(input_lang, input_sentence), input_lang, output_lang)
output_sentence = ' '.join(output_words)
print(output_sentence)

input_sentence = "comment savez vous ?"
output_words, _ = evaluate(encoder, decoder, indexesFromSentence(input_lang, input_sentence), input_lang, output_lang)
output_sentence = ' '.join(output_words)
print(output_sentence)

input_sentence = "il est alle a l ecole"
output_words, _ = evaluate(encoder, decoder, indexesFromSentence(input_lang, input_sentence), input_lang, output_lang)
output_sentence = ' '.join(output_words)
print(output_sentence)

input_sentence = "je ne le crois pas"
output_words, _ = evaluate(encoder, decoder, indexesFromSentence(input_lang, input_sentence), input_lang, output_lang)
output_sentence = ' '.join(output_words)
print(output_sentence)