Aide à protéger la Grande barrière de corail avec tensorflow sur Kaggle Rejoignez Défi

Modèle de transformateur pour la compréhension du langage

Voir sur TensorFlow.org Exécuter dans Google Colab Voir la source sur GitHubTélécharger le cahier

Ce train tutoriel un modèle Transformer pour traduire un ensemble de données portugais vers l' anglais . Ceci est un exemple avancé qui suppose une connaissance de génération de texte et d' attention .

L'idée de base derrière le modèle Transformer est auto-attention -la capacité à assister à différentes positions de la séquence d'entrée pour calculer une représentation de cette séquence. Transformer crée des piles de couches auto-attention et est expliquée ci - dessous dans les sections Scaled attention des produits de points et l' attention multi-tête.

Une poignée de modèle de transformateur d' entrée de taille variable en utilisant des piles de couches auto-attention au lieu de RNNs ou CNNs . Cette architecture générale présente de nombreux avantages :

  • Il ne fait aucune hypothèse sur les relations temporelles/spatiales entre les données. Ceci est idéal pour le traitement d' un ensemble d'objets (par exemple, des unités de StarCraft ).
  • Les sorties de couche peuvent être calculées en parallèle, au lieu d'une série comme un RNN.
  • Éléments éloignés peuvent influer sur la production de l'autre sans passer par de nombreuses étapes RNN, ou des couches de convolution (voir scène Transformer mémoire par exemple).
  • Il peut apprendre des dépendances à longue portée. C'est un défi dans de nombreuses tâches de séquence.

Les inconvénients de cette architecture sont :

  • Pour une série chronologique, la sortie pour un pas de temps est calculé à partir de toute l'histoire au lieu de seulement les entrées et l' état caché actuel. Cela peut être moins efficace.
  • Si l'entrée a une relation temporelle / spatiale, comme le texte, un codage de position doit être ajoutée ou le modèle verra effectivement un sac de mots.

Après avoir entraîné le modèle dans ce cahier, vous pourrez saisir une phrase en portugais et renvoyer la traduction en anglais.

Attention carte thermique

Installer

pip install tensorflow_datasets
pip install -U tensorflow-text
import collections
import logging
import os
import pathlib
import re
import string
import sys
import time

import numpy as np
import matplotlib.pyplot as plt

import tensorflow_datasets as tfds
import tensorflow_text as text
import tensorflow as tf
logging.getLogger('tensorflow').setLevel(logging.ERROR)  # suppress warnings

Télécharger le jeu de données

Utilisez des jeux de données tensorflow pour charger l' ensemble de données de traduction anglais- du TED Talks Ouvrir un projet de traduction .

Cet ensemble de données contient environ 50 000 exemples d'apprentissage, 1 100 exemples de validation et 2 000 exemples de test.

examples, metadata = tfds.load('ted_hrlr_translate/pt_to_en', with_info=True,
                               as_supervised=True)
train_examples, val_examples = examples['train'], examples['validation']

Le tf.data.Dataset objet retourné par les ensembles de données tensorflow donne des paires d'exemples de texte:

for pt_examples, en_examples in train_examples.batch(3).take(1):
  for pt in pt_examples.numpy():
    print(pt.decode('utf-8'))

  print()

  for en in en_examples.numpy():
    print(en.decode('utf-8'))
e quando melhoramos a procura , tiramos a única vantagem da impressão , que é a serendipidade .
mas e se estes fatores fossem ativos ?
mas eles não tinham a curiosidade de me testar .

and when you improve searchability , you actually take away the one advantage of print , which is serendipity .
but what if it were active ?
but they did n't test for curiosity .

Tokenisation et détokenisation de texte

Vous ne pouvez pas entraîner un modèle directement sur du texte. Le texte doit d'abord être converti en une représentation numérique. En règle générale, vous convertissez le texte en séquences d'ID de jeton, qui sont utilisées comme index dans une incorporation.

Une mise en œuvre populaire est démontrée dans le sous - text.BertTokenizer mot tokenizer tutoriel construit tokenizers (sous - mots text.BertTokenizer ) optimisés pour cet ensemble de données et les exportations les dans un saved_model .

Téléchargez et décompressez et importer le saved_model :

model_name = "ted_hrlr_translate_pt_en_converter"
tf.keras.utils.get_file(
    f"{model_name}.zip",
    f"https://storage.googleapis.com/download.tensorflow.org/models/{model_name}.zip",
    cache_dir='.', cache_subdir='', extract=True
)
Downloading data from https://storage.googleapis.com/download.tensorflow.org/models/ted_hrlr_translate_pt_en_converter.zip
188416/184801 [==============================] - 0s 0us/step
196608/184801 [===============================] - 0s 0us/step
'./ted_hrlr_translate_pt_en_converter.zip'
tokenizers = tf.saved_model.load(model_name)

Le tf.saved_model contient deux tokenizers texte, l' un pour l' anglais et un pour le portugais. Les deux ont les mêmes méthodes :

[item for item in dir(tokenizers.en) if not item.startswith('_')]
['detokenize',
 'get_reserved_tokens',
 'get_vocab_path',
 'get_vocab_size',
 'lookup',
 'tokenize',
 'tokenizer',
 'vocab']

La tokenize méthode convertit un lot de chaînes à un lot d'ID rembourré jeton. Cette méthode divise la ponctuation, les minuscules et normalise l'entrée avant la tokenisation. Cette standardisation n'est pas visible ici car les données d'entrée sont déjà standardisées.

for en in en_examples.numpy():
  print(en.decode('utf-8'))
and when you improve searchability , you actually take away the one advantage of print , which is serendipity .
but what if it were active ?
but they did n't test for curiosity .
encoded = tokenizers.en.tokenize(en_examples)

for row in encoded.to_list():
  print(row)
[2, 72, 117, 79, 1259, 1491, 2362, 13, 79, 150, 184, 311, 71, 103, 2308, 74, 2679, 13, 148, 80, 55, 4840, 1434, 2423, 540, 15, 3]
[2, 87, 90, 107, 76, 129, 1852, 30, 3]
[2, 87, 83, 149, 50, 9, 56, 664, 85, 2512, 15, 3]

Les detokenize tentatives de méthode pour convertir ces ID jeton retour au texte lisible par l' homme:

round_trip = tokenizers.en.detokenize(encoded)
for line in round_trip.numpy():
  print(line.decode('utf-8'))
and when you improve searchability , you actually take away the one advantage of print , which is serendipity .
but what if it were active ?
but they did n ' t test for curiosity .

Le niveau inférieur lookup la méthode des convertis de jeton-ID à jeton texte:

tokens = tokenizers.en.lookup(encoded)
tokens
<tf.RaggedTensor [[b'[START]', b'and', b'when', b'you', b'improve', b'search', b'##ability', b',', b'you', b'actually', b'take', b'away', b'the', b'one', b'advantage', b'of', b'print', b',', b'which', b'is', b's', b'##ere', b'##nd', b'##ip', b'##ity', b'.', b'[END]'], [b'[START]', b'but', b'what', b'if', b'it', b'were', b'active', b'?', b'[END]'], [b'[START]', b'but', b'they', b'did', b'n', b"'", b't', b'test', b'for', b'curiosity', b'.', b'[END]']]>

Ici vous pouvez voir l'aspect "sous-mot" des tokenizers. Le mot "searchability" est décomposé en "search ##ability" et le mot "serendipity" en "s ##ere ##nd ##ip ##ity"

Configurer le pipeline d'entrée

Pour créer un pipeline d'entrée adapté à l'entraînement, vous appliquerez quelques transformations à l'ensemble de données.

Cette fonction sera utilisée pour encoder les lots de texte brut :

def tokenize_pairs(pt, en):
    pt = tokenizers.pt.tokenize(pt)
    # Convert from ragged to dense, padding with zeros.
    pt = pt.to_tensor()

    en = tokenizers.en.tokenize(en)
    # Convert from ragged to dense, padding with zeros.
    en = en.to_tensor()
    return pt, en

Voici un pipeline d'entrée simple qui traite, mélange et regroupe les données :

BUFFER_SIZE = 20000
BATCH_SIZE = 64
def make_batches(ds):
  return (
      ds
      .cache()
      .shuffle(BUFFER_SIZE)
      .batch(BATCH_SIZE)
      .map(tokenize_pairs, num_parallel_calls=tf.data.AUTOTUNE)
      .prefetch(tf.data.AUTOTUNE))


train_batches = make_batches(train_examples)
val_batches = make_batches(val_examples)

Encodage positionnel

Les couches d'attention voient leur entrée comme un ensemble de vecteurs, sans ordre séquentiel. Ce modèle ne contient pas non plus de couches récurrentes ou convolutives. Pour cette raison, un "encodage positionnel" est ajouté pour donner au modèle des informations sur la position relative des jetons dans la phrase.

Le vecteur de codage positionnel est ajouté au vecteur d'inclusion. Les incorporations représentent un jeton dans un espace de dimension d où les jetons ayant une signification similaire seront plus proches les uns des autres. Mais les plongements n'encodent pas la position relative des jetons dans une phrase. Après avoir ajouté le codage de position, les jetons seront plus proches les uns des autres en fonction de la similitude de leur signification et leur position dans la phrase, dans l'espace d dimensions.

La formule de calcul de l'encodage positionnel est la suivante :

\[\Large{PE_{(pos, 2i)} = \sin(pos / 10000^{2i / d_{model} })} \]

\[\Large{PE_{(pos, 2i+1)} = \cos(pos / 10000^{2i / d_{model} })} \]

def get_angles(pos, i, d_model):
  angle_rates = 1 / np.power(10000, (2 * (i//2)) / np.float32(d_model))
  return pos * angle_rates
def positional_encoding(position, d_model):
  angle_rads = get_angles(np.arange(position)[:, np.newaxis],
                          np.arange(d_model)[np.newaxis, :],
                          d_model)

  # apply sin to even indices in the array; 2i
  angle_rads[:, 0::2] = np.sin(angle_rads[:, 0::2])

  # apply cos to odd indices in the array; 2i+1
  angle_rads[:, 1::2] = np.cos(angle_rads[:, 1::2])

  pos_encoding = angle_rads[np.newaxis, ...]

  return tf.cast(pos_encoding, dtype=tf.float32)
n, d = 2048, 512
pos_encoding = positional_encoding(n, d)
print(pos_encoding.shape)
pos_encoding = pos_encoding[0]

# Juggle the dimensions for the plot
pos_encoding = tf.reshape(pos_encoding, (n, d//2, 2))
pos_encoding = tf.transpose(pos_encoding, (2, 1, 0))
pos_encoding = tf.reshape(pos_encoding, (d, n))

plt.pcolormesh(pos_encoding, cmap='RdBu')
plt.ylabel('Depth')
plt.xlabel('Position')
plt.colorbar()
plt.show()
(1, 2048, 512)

png

Masquage

Masquez tous les jetons pad dans le lot de séquence. Cela garantit que le modèle ne traite pas le remplissage comme entrée. Le masque indique l' endroit où la valeur de tampon 0 est présent: il délivre en sortie un 1 à ces endroits, et un 0 autrement.

def create_padding_mask(seq):
  seq = tf.cast(tf.math.equal(seq, 0), tf.float32)

  # add extra dimensions to add the padding
  # to the attention logits.
  return seq[:, tf.newaxis, tf.newaxis, :]  # (batch_size, 1, 1, seq_len)
x = tf.constant([[7, 6, 0, 0, 1], [1, 2, 3, 0, 0], [0, 0, 0, 4, 5]])
create_padding_mask(x)
<tf.Tensor: shape=(3, 1, 1, 5), dtype=float32, numpy=
array([[[[0., 0., 1., 1., 0.]]],


       [[[0., 0., 0., 1., 1.]]],


       [[[1., 1., 1., 0., 0.]]]], dtype=float32)>

Le masque d'anticipation est utilisé pour masquer les futurs jetons dans une séquence. En d'autres termes, le masque indique quelles entrées ne doivent pas être utilisées.

Cela signifie que pour prédire le troisième jeton, seuls les premier et deuxième jetons seront utilisés. De même pour prédire le quatrième jeton, seuls les premier, deuxième et troisième jetons seront utilisés et ainsi de suite.

def create_look_ahead_mask(size):
  mask = 1 - tf.linalg.band_part(tf.ones((size, size)), -1, 0)
  return mask  # (seq_len, seq_len)
x = tf.random.uniform((1, 3))
temp = create_look_ahead_mask(x.shape[1])
temp
<tf.Tensor: shape=(3, 3), dtype=float32, numpy=
array([[0., 1., 1.],
       [0., 0., 1.],
       [0., 0., 0.]], dtype=float32)>

Attention produit scalaire à l'échelle

scaled_dot_product_attention

La fonction attention utilisée par le transformateur prend trois entrées : Q (requête), K (clé), V (valeur). L'équation utilisée pour calculer les poids d'attention est :

\[\Large{Attention(Q, K, V) = softmax_k\left(\frac{QK^T}{\sqrt{d_k} }\right) V} \]

L'attention du produit scalaire est mise à l'échelle par un facteur de racine carrée de la profondeur. Ceci est dû au fait que pour de grandes valeurs de profondeur, le produit scalaire augmente en amplitude, poussant la fonction softmax où il a de petits gradients résultant en un softmax très dur.

Par exemple, considérer que Q et K ont une moyenne de 0 et de variance 1. Leur multiplication matricielle aura une moyenne de 0 et la variance de dk . Ainsi , la racine carrée de dk est utilisé pour mise à l' échelle, de sorte que vous obtenez une variance constante quelle que soit la valeur de dk . Si la variance est trop faible, la sortie peut être trop plate pour être optimisée efficacement. Si la variance est trop élevée, le softmax peut saturer à l'initialisation, ce qui rend difficile l'apprentissage.

Le masque est multiplié par -1e9 (proche de l'infini négatif). Ceci est fait parce que le masque est additionné avec la multiplication matricielle mise à l'échelle de Q et K et est appliqué immédiatement avant un softmax. L'objectif est de mettre à zéro ces cellules, et les grandes entrées négatives de softmax sont proches de zéro dans la sortie.

def scaled_dot_product_attention(q, k, v, mask):
  """Calculate the attention weights.
  q, k, v must have matching leading dimensions.
  k, v must have matching penultimate dimension, i.e.: seq_len_k = seq_len_v.
  The mask has different shapes depending on its type(padding or look ahead)
  but it must be broadcastable for addition.

  Args:
    q: query shape == (..., seq_len_q, depth)
    k: key shape == (..., seq_len_k, depth)
    v: value shape == (..., seq_len_v, depth_v)
    mask: Float tensor with shape broadcastable
          to (..., seq_len_q, seq_len_k). Defaults to None.

  Returns:
    output, attention_weights
  """

  matmul_qk = tf.matmul(q, k, transpose_b=True)  # (..., seq_len_q, seq_len_k)

  # scale matmul_qk
  dk = tf.cast(tf.shape(k)[-1], tf.float32)
  scaled_attention_logits = matmul_qk / tf.math.sqrt(dk)

  # add the mask to the scaled tensor.
  if mask is not None:
    scaled_attention_logits += (mask * -1e9)

  # softmax is normalized on the last axis (seq_len_k) so that the scores
  # add up to 1.
  attention_weights = tf.nn.softmax(scaled_attention_logits, axis=-1)  # (..., seq_len_q, seq_len_k)

  output = tf.matmul(attention_weights, v)  # (..., seq_len_q, depth_v)

  return output, attention_weights

Comme la normalisation softmax est effectuée sur K, ses valeurs décident de l'importance accordée à Q.

La sortie représente la multiplication des poids d'attention et du vecteur V (valeur). Cela garantit que les jetons sur lesquels vous souhaitez vous concentrer sont conservés tels quels et que les jetons non pertinents sont vidés.

def print_out(q, k, v):
  temp_out, temp_attn = scaled_dot_product_attention(
      q, k, v, None)
  print('Attention weights are:')
  print(temp_attn)
  print('Output is:')
  print(temp_out)
np.set_printoptions(suppress=True)

temp_k = tf.constant([[10, 0, 0],
                      [0, 10, 0],
                      [0, 0, 10],
                      [0, 0, 10]], dtype=tf.float32)  # (4, 3)

temp_v = tf.constant([[1, 0],
                      [10, 0],
                      [100, 5],
                      [1000, 6]], dtype=tf.float32)  # (4, 2)

# This `query` aligns with the second `key`,
# so the second `value` is returned.
temp_q = tf.constant([[0, 10, 0]], dtype=tf.float32)  # (1, 3)
print_out(temp_q, temp_k, temp_v)
Attention weights are:
tf.Tensor([[0. 1. 0. 0.]], shape=(1, 4), dtype=float32)
Output is:
tf.Tensor([[10.  0.]], shape=(1, 2), dtype=float32)
# This query aligns with a repeated key (third and fourth),
# so all associated values get averaged.
temp_q = tf.constant([[0, 0, 10]], dtype=tf.float32)  # (1, 3)
print_out(temp_q, temp_k, temp_v)
Attention weights are:
tf.Tensor([[0.  0.  0.5 0.5]], shape=(1, 4), dtype=float32)
Output is:
tf.Tensor([[550.    5.5]], shape=(1, 2), dtype=float32)
# This query aligns equally with the first and second key,
# so their values get averaged.
temp_q = tf.constant([[10, 10, 0]], dtype=tf.float32)  # (1, 3)
print_out(temp_q, temp_k, temp_v)
Attention weights are:
tf.Tensor([[0.5 0.5 0.  0. ]], shape=(1, 4), dtype=float32)
Output is:
tf.Tensor([[5.5 0. ]], shape=(1, 2), dtype=float32)

Passez toutes les requêtes ensemble.

temp_q = tf.constant([[0, 0, 10],
                      [0, 10, 0],
                      [10, 10, 0]], dtype=tf.float32)  # (3, 3)
print_out(temp_q, temp_k, temp_v)
Attention weights are:
tf.Tensor(
[[0.  0.  0.5 0.5]
 [0.  1.  0.  0. ]
 [0.5 0.5 0.  0. ]], shape=(3, 4), dtype=float32)
Output is:
tf.Tensor(
[[550.    5.5]
 [ 10.    0. ]
 [  5.5   0. ]], shape=(3, 2), dtype=float32)

Attention multi-têtes

attention à plusieurs têtes

L'attention multi-têtes se compose de quatre parties :

  • Couches linéaires.
  • Attention au produit scalaire à l'échelle.
  • Couche linéaire finale.

Chaque bloc d'attention multi-têtes reçoit trois entrées ; Q (requête), K (clé), V (valeur). Ceux-ci sont passés à travers des couches linéaires (Dense) avant la fonction d'attention multi-têtes.

Dans le schéma ci - dessus (K,Q,V) sont passés à travers linéaires (sepearte Dense couches) pour chaque tête d'attention. Par souci de simplicité / efficacité du code ci - dessous met en œuvre cette utilisation d' une couche dense unique avec num_heads fois plus de sorties. La sortie est réarrangée en une forme de (batch, num_heads, ...) avant d' appliquer la fonction d'attention.

La scaled_dot_product_attention fonction définie ci - dessus est appliqué en un seul appel, diffusé pour l' efficacité. Un masque approprié doit être utilisé dans l'étape d'attention. La sortie de l' attention pour chaque tête est ensuite concaténée ( en utilisant tf.transpose et tf.reshape ) et soumis à une finale Dense couche.

Au lieu d'une seule tête d'attention, Q, K et V sont divisés en plusieurs têtes car cela permet au modèle de s'occuper conjointement des informations de différents sous-espaces de représentation à différentes positions. Après la division, chaque tête a une dimensionnalité réduite, de sorte que le coût de calcul total est le même qu'une attention à une seule tête avec une dimensionnalité complète.

class MultiHeadAttention(tf.keras.layers.Layer):
  def __init__(self, d_model, num_heads):
    super(MultiHeadAttention, self).__init__()
    self.num_heads = num_heads
    self.d_model = d_model

    assert d_model % self.num_heads == 0

    self.depth = d_model // self.num_heads

    self.wq = tf.keras.layers.Dense(d_model)
    self.wk = tf.keras.layers.Dense(d_model)
    self.wv = tf.keras.layers.Dense(d_model)

    self.dense = tf.keras.layers.Dense(d_model)

  def split_heads(self, x, batch_size):
    """Split the last dimension into (num_heads, depth).
    Transpose the result such that the shape is (batch_size, num_heads, seq_len, depth)
    """
    x = tf.reshape(x, (batch_size, -1, self.num_heads, self.depth))
    return tf.transpose(x, perm=[0, 2, 1, 3])

  def call(self, v, k, q, mask):
    batch_size = tf.shape(q)[0]

    q = self.wq(q)  # (batch_size, seq_len, d_model)
    k = self.wk(k)  # (batch_size, seq_len, d_model)
    v = self.wv(v)  # (batch_size, seq_len, d_model)

    q = self.split_heads(q, batch_size)  # (batch_size, num_heads, seq_len_q, depth)
    k = self.split_heads(k, batch_size)  # (batch_size, num_heads, seq_len_k, depth)
    v = self.split_heads(v, batch_size)  # (batch_size, num_heads, seq_len_v, depth)

    # scaled_attention.shape == (batch_size, num_heads, seq_len_q, depth)
    # attention_weights.shape == (batch_size, num_heads, seq_len_q, seq_len_k)
    scaled_attention, attention_weights = scaled_dot_product_attention(
        q, k, v, mask)

    scaled_attention = tf.transpose(scaled_attention, perm=[0, 2, 1, 3])  # (batch_size, seq_len_q, num_heads, depth)

    concat_attention = tf.reshape(scaled_attention,
                                  (batch_size, -1, self.d_model))  # (batch_size, seq_len_q, d_model)

    output = self.dense(concat_attention)  # (batch_size, seq_len_q, d_model)

    return output, attention_weights

Créer une MultiHeadAttention couche pour essayer. A chaque emplacement dans la séquence, y , le MultiHeadAttention court les 8 têtes d'attention dans tous les autres endroits dans la séquence, le retour d' un nouveau vecteur de la même longueur à chaque endroit.

temp_mha = MultiHeadAttention(d_model=512, num_heads=8)
y = tf.random.uniform((1, 60, 512))  # (batch_size, encoder_sequence, d_model)
out, attn = temp_mha(y, k=y, q=y, mask=None)
out.shape, attn.shape
(TensorShape([1, 60, 512]), TensorShape([1, 8, 60, 60]))

Réseau d'anticipation point par point

Le réseau d'anticipation point par point se compose de deux couches entièrement connectées avec une activation ReLU entre les deux.

def point_wise_feed_forward_network(d_model, dff):
  return tf.keras.Sequential([
      tf.keras.layers.Dense(dff, activation='relu'),  # (batch_size, seq_len, dff)
      tf.keras.layers.Dense(d_model)  # (batch_size, seq_len, d_model)
  ])
sample_ffn = point_wise_feed_forward_network(512, 2048)
sample_ffn(tf.random.uniform((64, 50, 512))).shape
TensorShape([64, 50, 512])

Encodeur et décodeur

transformateur

Le modèle de transformateur suit le même schéma général en tant que norme séquence à la séquence avec le modèle de l' attention .

  • La phrase d'entrée est passé à travers les N couches de codeur qui génère un signal de sortie pour chaque jeton dans la séquence.
  • Le décodeur s'occupe de la sortie du codeur et de sa propre entrée (auto-attention) pour prédire le mot suivant.

Couche d'encodeur

Chaque couche d'encodeur se compose de sous-couches :

  1. Attention multi-têtes (avec masque de rembourrage)
  2. Réseaux d'avance par point.

Chacune de ces sous-couches est entourée d'une connexion résiduelle suivie d'une normalisation de couche. Les connexions résiduelles aident à éviter le problème du gradient de disparition dans les réseaux profonds.

La sortie de chaque sous - couche est LayerNorm(x + Sublayer(x)) - LayerNorm(x + Sublayer(x)) . La normalisation se fait sur le d_model axe (dernier). Il y a N couches d'encodeur dans le transformateur.

class EncoderLayer(tf.keras.layers.Layer):
  def __init__(self, d_model, num_heads, dff, rate=0.1):
    super(EncoderLayer, self).__init__()

    self.mha = MultiHeadAttention(d_model, num_heads)
    self.ffn = point_wise_feed_forward_network(d_model, dff)

    self.layernorm1 = tf.keras.layers.LayerNormalization(epsilon=1e-6)
    self.layernorm2 = tf.keras.layers.LayerNormalization(epsilon=1e-6)

    self.dropout1 = tf.keras.layers.Dropout(rate)
    self.dropout2 = tf.keras.layers.Dropout(rate)

  def call(self, x, training, mask):

    attn_output, _ = self.mha(x, x, x, mask)  # (batch_size, input_seq_len, d_model)
    attn_output = self.dropout1(attn_output, training=training)
    out1 = self.layernorm1(x + attn_output)  # (batch_size, input_seq_len, d_model)

    ffn_output = self.ffn(out1)  # (batch_size, input_seq_len, d_model)
    ffn_output = self.dropout2(ffn_output, training=training)
    out2 = self.layernorm2(out1 + ffn_output)  # (batch_size, input_seq_len, d_model)

    return out2
sample_encoder_layer = EncoderLayer(512, 8, 2048)

sample_encoder_layer_output = sample_encoder_layer(
    tf.random.uniform((64, 43, 512)), False, None)

sample_encoder_layer_output.shape  # (batch_size, input_seq_len, d_model)
TensorShape([64, 43, 512])

Couche décodeur

Chaque couche de décodeur est constituée de sous-couches :

  1. Attention multi-têtes masquée (avec masque d'anticipation et masque de rembourrage)
  2. Attention multi-têtes (avec masque de rembourrage). V (valeur) et K ( n ) reçoivent la sortie du codeur en tant qu'entrées. Q (requête) reçoit la sortie de la sous - couche d'attention à têtes multiples masqué.
  3. Réseaux de feed-forward point par point

Chacune de ces sous-couches est entourée d'une connexion résiduelle suivie d'une normalisation de couche. La sortie de chaque sous - couche est LayerNorm(x + Sublayer(x)) - LayerNorm(x + Sublayer(x)) . La normalisation se fait sur le d_model axe (dernier).

Il y a N couches de décodeur dans le transformateur.

Lorsque Q reçoit la sortie du premier bloc d'attention du décodeur et que K reçoit la sortie du codeur, les poids d'attention représentent l'importance accordée à l'entrée du décodeur en fonction de la sortie du codeur. En d'autres termes, le décodeur prédit le prochain jeton en regardant la sortie du codeur et en s'occupant automatiquement de sa propre sortie. Voir la démonstration ci-dessus dans la section d'attention sur les produits à points mis à l'échelle.

class DecoderLayer(tf.keras.layers.Layer):
  def __init__(self, d_model, num_heads, dff, rate=0.1):
    super(DecoderLayer, self).__init__()

    self.mha1 = MultiHeadAttention(d_model, num_heads)
    self.mha2 = MultiHeadAttention(d_model, num_heads)

    self.ffn = point_wise_feed_forward_network(d_model, dff)

    self.layernorm1 = tf.keras.layers.LayerNormalization(epsilon=1e-6)
    self.layernorm2 = tf.keras.layers.LayerNormalization(epsilon=1e-6)
    self.layernorm3 = tf.keras.layers.LayerNormalization(epsilon=1e-6)

    self.dropout1 = tf.keras.layers.Dropout(rate)
    self.dropout2 = tf.keras.layers.Dropout(rate)
    self.dropout3 = tf.keras.layers.Dropout(rate)

  def call(self, x, enc_output, training,
           look_ahead_mask, padding_mask):
    # enc_output.shape == (batch_size, input_seq_len, d_model)

    attn1, attn_weights_block1 = self.mha1(x, x, x, look_ahead_mask)  # (batch_size, target_seq_len, d_model)
    attn1 = self.dropout1(attn1, training=training)
    out1 = self.layernorm1(attn1 + x)

    attn2, attn_weights_block2 = self.mha2(
        enc_output, enc_output, out1, padding_mask)  # (batch_size, target_seq_len, d_model)
    attn2 = self.dropout2(attn2, training=training)
    out2 = self.layernorm2(attn2 + out1)  # (batch_size, target_seq_len, d_model)

    ffn_output = self.ffn(out2)  # (batch_size, target_seq_len, d_model)
    ffn_output = self.dropout3(ffn_output, training=training)
    out3 = self.layernorm3(ffn_output + out2)  # (batch_size, target_seq_len, d_model)

    return out3, attn_weights_block1, attn_weights_block2
sample_decoder_layer = DecoderLayer(512, 8, 2048)

sample_decoder_layer_output, _, _ = sample_decoder_layer(
    tf.random.uniform((64, 50, 512)), sample_encoder_layer_output,
    False, None, None)

sample_decoder_layer_output.shape  # (batch_size, target_seq_len, d_model)
TensorShape([64, 50, 512])

Encodeur

Le Encoder se compose de:

  1. Intégration d'entrée
  2. Encodage positionnel
  3. N couches d'encodeur

L'entrée est soumise à une incorporation qui est additionnée à l'encodage positionnel. La sortie de cette sommation est l'entrée des couches d'encodeur. La sortie de l'encodeur est l'entrée du décodeur.

class Encoder(tf.keras.layers.Layer):
  def __init__(self, num_layers, d_model, num_heads, dff, input_vocab_size,
               maximum_position_encoding, rate=0.1):
    super(Encoder, self).__init__()

    self.d_model = d_model
    self.num_layers = num_layers

    self.embedding = tf.keras.layers.Embedding(input_vocab_size, d_model)
    self.pos_encoding = positional_encoding(maximum_position_encoding,
                                            self.d_model)

    self.enc_layers = [EncoderLayer(d_model, num_heads, dff, rate)
                       for _ in range(num_layers)]

    self.dropout = tf.keras.layers.Dropout(rate)

  def call(self, x, training, mask):

    seq_len = tf.shape(x)[1]

    # adding embedding and position encoding.
    x = self.embedding(x)  # (batch_size, input_seq_len, d_model)
    x *= tf.math.sqrt(tf.cast(self.d_model, tf.float32))
    x += self.pos_encoding[:, :seq_len, :]

    x = self.dropout(x, training=training)

    for i in range(self.num_layers):
      x = self.enc_layers[i](x, training, mask)

    return x  # (batch_size, input_seq_len, d_model)
sample_encoder = Encoder(num_layers=2, d_model=512, num_heads=8,
                         dff=2048, input_vocab_size=8500,
                         maximum_position_encoding=10000)
temp_input = tf.random.uniform((64, 62), dtype=tf.int64, minval=0, maxval=200)

sample_encoder_output = sample_encoder(temp_input, training=False, mask=None)

print(sample_encoder_output.shape)  # (batch_size, input_seq_len, d_model)
(64, 62, 512)

Décodeur

Le Decoder se compose de:

  1. Intégration de la sortie
  2. Encodage positionnel
  3. N couches de décodeur

La cible est soumise à un plongement qui est additionné à l'encodage positionnel. La sortie de cette sommation est l'entrée des couches décodeurs. La sortie du décodeur est l'entrée de la couche linéaire finale.

class Decoder(tf.keras.layers.Layer):
  def __init__(self, num_layers, d_model, num_heads, dff, target_vocab_size,
               maximum_position_encoding, rate=0.1):
    super(Decoder, self).__init__()

    self.d_model = d_model
    self.num_layers = num_layers

    self.embedding = tf.keras.layers.Embedding(target_vocab_size, d_model)
    self.pos_encoding = positional_encoding(maximum_position_encoding, d_model)

    self.dec_layers = [DecoderLayer(d_model, num_heads, dff, rate)
                       for _ in range(num_layers)]
    self.dropout = tf.keras.layers.Dropout(rate)

  def call(self, x, enc_output, training,
           look_ahead_mask, padding_mask):

    seq_len = tf.shape(x)[1]
    attention_weights = {}

    x = self.embedding(x)  # (batch_size, target_seq_len, d_model)
    x *= tf.math.sqrt(tf.cast(self.d_model, tf.float32))
    x += self.pos_encoding[:, :seq_len, :]

    x = self.dropout(x, training=training)

    for i in range(self.num_layers):
      x, block1, block2 = self.dec_layers[i](x, enc_output, training,
                                             look_ahead_mask, padding_mask)

      attention_weights[f'decoder_layer{i+1}_block1'] = block1
      attention_weights[f'decoder_layer{i+1}_block2'] = block2

    # x.shape == (batch_size, target_seq_len, d_model)
    return x, attention_weights
sample_decoder = Decoder(num_layers=2, d_model=512, num_heads=8,
                         dff=2048, target_vocab_size=8000,
                         maximum_position_encoding=5000)
temp_input = tf.random.uniform((64, 26), dtype=tf.int64, minval=0, maxval=200)

output, attn = sample_decoder(temp_input,
                              enc_output=sample_encoder_output,
                              training=False,
                              look_ahead_mask=None,
                              padding_mask=None)

output.shape, attn['decoder_layer2_block2'].shape
(TensorShape([64, 26, 512]), TensorShape([64, 8, 26, 62]))

Créer le transformateur

Le transformateur se compose de l'encodeur, du décodeur et d'une couche linéaire finale. La sortie du décodeur est l'entrée de la couche linéaire et sa sortie est renvoyée.

class Transformer(tf.keras.Model):
  def __init__(self, num_layers, d_model, num_heads, dff, input_vocab_size,
               target_vocab_size, pe_input, pe_target, rate=0.1):
    super().__init__()
    self.encoder = Encoder(num_layers, d_model, num_heads, dff,
                             input_vocab_size, pe_input, rate)

    self.decoder = Decoder(num_layers, d_model, num_heads, dff,
                           target_vocab_size, pe_target, rate)

    self.final_layer = tf.keras.layers.Dense(target_vocab_size)

  def call(self, inputs, training):
    # Keras models prefer if you pass all your inputs in the first argument
    inp, tar = inputs

    enc_padding_mask, look_ahead_mask, dec_padding_mask = self.create_masks(inp, tar)

    enc_output = self.encoder(inp, training, enc_padding_mask)  # (batch_size, inp_seq_len, d_model)

    # dec_output.shape == (batch_size, tar_seq_len, d_model)
    dec_output, attention_weights = self.decoder(
        tar, enc_output, training, look_ahead_mask, dec_padding_mask)

    final_output = self.final_layer(dec_output)  # (batch_size, tar_seq_len, target_vocab_size)

    return final_output, attention_weights

  def create_masks(self, inp, tar):
    # Encoder padding mask
    enc_padding_mask = create_padding_mask(inp)

    # Used in the 2nd attention block in the decoder.
    # This padding mask is used to mask the encoder outputs.
    dec_padding_mask = create_padding_mask(inp)

    # Used in the 1st attention block in the decoder.
    # It is used to pad and mask future tokens in the input received by
    # the decoder.
    look_ahead_mask = create_look_ahead_mask(tf.shape(tar)[1])
    dec_target_padding_mask = create_padding_mask(tar)
    look_ahead_mask = tf.maximum(dec_target_padding_mask, look_ahead_mask)

    return enc_padding_mask, look_ahead_mask, dec_padding_mask
sample_transformer = Transformer(
    num_layers=2, d_model=512, num_heads=8, dff=2048,
    input_vocab_size=8500, target_vocab_size=8000,
    pe_input=10000, pe_target=6000)

temp_input = tf.random.uniform((64, 38), dtype=tf.int64, minval=0, maxval=200)
temp_target = tf.random.uniform((64, 36), dtype=tf.int64, minval=0, maxval=200)

fn_out, _ = sample_transformer([temp_input, temp_target], training=False)

fn_out.shape  # (batch_size, tar_seq_len, target_vocab_size)
TensorShape([64, 36, 8000])

Définir des hyperparamètres

Pour garder cet exemple petite et relativement rapide, les valeurs de num_layers, d_model, dff ont été réduits.

Le modèle de base décrit dans le document utilisé: num_layers=6, d_model=512, dff=2048 .

num_layers = 4
d_model = 128
dff = 512
num_heads = 8
dropout_rate = 0.1

Optimiseur

Utiliser l'optimiseur Adam avec un programmateur de taux d'apprentissage personnalisé selon la formule dans le papier .

\[\Large{lrate = d_{model}^{-0.5} * \min(step{\_}num^{-0.5}, step{\_}num \cdot warmup{\_}steps^{-1.5})}\]

class CustomSchedule(tf.keras.optimizers.schedules.LearningRateSchedule):
  def __init__(self, d_model, warmup_steps=4000):
    super(CustomSchedule, self).__init__()

    self.d_model = d_model
    self.d_model = tf.cast(self.d_model, tf.float32)

    self.warmup_steps = warmup_steps

  def __call__(self, step):
    arg1 = tf.math.rsqrt(step)
    arg2 = step * (self.warmup_steps ** -1.5)

    return tf.math.rsqrt(self.d_model) * tf.math.minimum(arg1, arg2)
learning_rate = CustomSchedule(d_model)

optimizer = tf.keras.optimizers.Adam(learning_rate, beta_1=0.9, beta_2=0.98,
                                     epsilon=1e-9)
temp_learning_rate_schedule = CustomSchedule(d_model)

plt.plot(temp_learning_rate_schedule(tf.range(40000, dtype=tf.float32)))
plt.ylabel("Learning Rate")
plt.xlabel("Train Step")
Text(0.5, 0, 'Train Step')

png

Perte et métriques

Les séquences cibles étant rembourrées, il est important d'appliquer un masque de remplissage lors du calcul de la perte.

loss_object = tf.keras.losses.SparseCategoricalCrossentropy(
    from_logits=True, reduction='none')
def loss_function(real, pred):
  mask = tf.math.logical_not(tf.math.equal(real, 0))
  loss_ = loss_object(real, pred)

  mask = tf.cast(mask, dtype=loss_.dtype)
  loss_ *= mask

  return tf.reduce_sum(loss_)/tf.reduce_sum(mask)


def accuracy_function(real, pred):
  accuracies = tf.equal(real, tf.argmax(pred, axis=2))

  mask = tf.math.logical_not(tf.math.equal(real, 0))
  accuracies = tf.math.logical_and(mask, accuracies)

  accuracies = tf.cast(accuracies, dtype=tf.float32)
  mask = tf.cast(mask, dtype=tf.float32)
  return tf.reduce_sum(accuracies)/tf.reduce_sum(mask)
train_loss = tf.keras.metrics.Mean(name='train_loss')
train_accuracy = tf.keras.metrics.Mean(name='train_accuracy')

Formation et point de contrôle

transformer = Transformer(
    num_layers=num_layers,
    d_model=d_model,
    num_heads=num_heads,
    dff=dff,
    input_vocab_size=tokenizers.pt.get_vocab_size().numpy(),
    target_vocab_size=tokenizers.en.get_vocab_size().numpy(),
    pe_input=1000,
    pe_target=1000,
    rate=dropout_rate)

Créez le chemin du point de contrôle et le gestionnaire de points de contrôle. Il sera utilisé pour sauver tous les points de contrôle n époques.

checkpoint_path = "./checkpoints/train"

ckpt = tf.train.Checkpoint(transformer=transformer,
                           optimizer=optimizer)

ckpt_manager = tf.train.CheckpointManager(ckpt, checkpoint_path, max_to_keep=5)

# if a checkpoint exists, restore the latest checkpoint.
if ckpt_manager.latest_checkpoint:
  ckpt.restore(ckpt_manager.latest_checkpoint)
  print('Latest checkpoint restored!!')

La cible est divisée en tar_inp et tar_real. tar_inp est transmis comme entrée au décodeur. tar_real est que même entrée décalée de 1: À chaque emplacement dans tar_input , tar_real contient le jeton suivant qui doit être prédite.

Par exemple, sentence = « SOS Un lion dans la jungle dort EOS »

tar_inp = « SOS Un lion dans la jungle dort »

tar_real = "lion A dans la jungle dort EOS"

Le transformateur est un modèle auto-régressif : il fait des prédictions une partie à la fois et utilise sa sortie jusqu'à présent pour décider quoi faire ensuite.

Pendant la formation , cet exemple utilise-forçant l' enseignant (comme dans le tutoriel de génération de texte ). Le forçage de l'enseignant consiste à transmettre la sortie réelle au pas de temps suivant, indépendamment de ce que le modèle prédit au pas de temps actuel.

Comme le transformateur prédit chaque jeton, l' auto-attention lui permet de regarder les jetons précédents dans la séquence d'entrée pour mieux prédire le prochain jeton.

Pour empêcher le modèle de regarder la sortie attendue, le modèle utilise un masque d'anticipation.

EPOCHS = 20
# The @tf.function trace-compiles train_step into a TF graph for faster
# execution. The function specializes to the precise shape of the argument
# tensors. To avoid re-tracing due to the variable sequence lengths or variable
# batch sizes (the last batch is smaller), use input_signature to specify
# more generic shapes.

train_step_signature = [
    tf.TensorSpec(shape=(None, None), dtype=tf.int64),
    tf.TensorSpec(shape=(None, None), dtype=tf.int64),
]


@tf.function(input_signature=train_step_signature)
def train_step(inp, tar):
  tar_inp = tar[:, :-1]
  tar_real = tar[:, 1:]

  with tf.GradientTape() as tape:
    predictions, _ = transformer([inp, tar_inp],
                                 training = True)
    loss = loss_function(tar_real, predictions)

  gradients = tape.gradient(loss, transformer.trainable_variables)
  optimizer.apply_gradients(zip(gradients, transformer.trainable_variables))

  train_loss(loss)
  train_accuracy(accuracy_function(tar_real, predictions))

Le portugais est utilisé comme langue d'entrée et l'anglais est la langue cible.

for epoch in range(EPOCHS):
  start = time.time()

  train_loss.reset_states()
  train_accuracy.reset_states()

  # inp -> portuguese, tar -> english
  for (batch, (inp, tar)) in enumerate(train_batches):
    train_step(inp, tar)

    if batch % 50 == 0:
      print(f'Epoch {epoch + 1} Batch {batch} Loss {train_loss.result():.4f} Accuracy {train_accuracy.result():.4f}')

  if (epoch + 1) % 5 == 0:
    ckpt_save_path = ckpt_manager.save()
    print(f'Saving checkpoint for epoch {epoch+1} at {ckpt_save_path}')

  print(f'Epoch {epoch + 1} Loss {train_loss.result():.4f} Accuracy {train_accuracy.result():.4f}')

  print(f'Time taken for 1 epoch: {time.time() - start:.2f} secs\n')
Epoch 1 Batch 0 Loss 8.8389 Accuracy 0.0000
Epoch 1 Batch 50 Loss 8.7850 Accuracy 0.0015
Epoch 1 Batch 100 Loss 8.6954 Accuracy 0.0258
Epoch 1 Batch 150 Loss 8.5875 Accuracy 0.0361
Epoch 1 Batch 200 Loss 8.4497 Accuracy 0.0396
Epoch 1 Batch 250 Loss 8.2832 Accuracy 0.0412
Epoch 1 Batch 300 Loss 8.0957 Accuracy 0.0424
Epoch 1 Batch 350 Loss 7.9001 Accuracy 0.0462
Epoch 1 Batch 400 Loss 7.7106 Accuracy 0.0530
Epoch 1 Batch 450 Loss 7.5449 Accuracy 0.0608
Epoch 1 Batch 500 Loss 7.3965 Accuracy 0.0686
Epoch 1 Batch 550 Loss 7.2629 Accuracy 0.0764
Epoch 1 Batch 600 Loss 7.1374 Accuracy 0.0841
Epoch 1 Batch 650 Loss 7.0225 Accuracy 0.0915
Epoch 1 Batch 700 Loss 6.9148 Accuracy 0.0987
Epoch 1 Batch 750 Loss 6.8137 Accuracy 0.1055
Epoch 1 Batch 800 Loss 6.7228 Accuracy 0.1115
Epoch 1 Loss 6.7072 Accuracy 0.1126
Time taken for 1 epoch: 62.48 secs

Epoch 2 Batch 0 Loss 5.3320 Accuracy 0.2099
Epoch 2 Batch 50 Loss 5.2248 Accuracy 0.2119
Epoch 2 Batch 100 Loss 5.1947 Accuracy 0.2152
Epoch 2 Batch 150 Loss 5.1643 Accuracy 0.2191
Epoch 2 Batch 200 Loss 5.1383 Accuracy 0.2224
Epoch 2 Batch 250 Loss 5.1123 Accuracy 0.2254
Epoch 2 Batch 300 Loss 5.0864 Accuracy 0.2277
Epoch 2 Batch 350 Loss 5.0697 Accuracy 0.2291
Epoch 2 Batch 400 Loss 5.0487 Accuracy 0.2313
Epoch 2 Batch 450 Loss 5.0272 Accuracy 0.2331
Epoch 2 Batch 500 Loss 5.0060 Accuracy 0.2349
Epoch 2 Batch 550 Loss 4.9882 Accuracy 0.2365
Epoch 2 Batch 600 Loss 4.9677 Accuracy 0.2382
Epoch 2 Batch 650 Loss 4.9496 Accuracy 0.2400
Epoch 2 Batch 700 Loss 4.9314 Accuracy 0.2417
Epoch 2 Batch 750 Loss 4.9172 Accuracy 0.2429
Epoch 2 Batch 800 Loss 4.9005 Accuracy 0.2444
Epoch 2 Loss 4.8976 Accuracy 0.2447
Time taken for 1 epoch: 47.81 secs

Epoch 3 Batch 0 Loss 4.7853 Accuracy 0.2318
Epoch 3 Batch 50 Loss 4.6041 Accuracy 0.2673
Epoch 3 Batch 100 Loss 4.5869 Accuracy 0.2693
Epoch 3 Batch 150 Loss 4.5681 Accuracy 0.2723
Epoch 3 Batch 200 Loss 4.5502 Accuracy 0.2748
Epoch 3 Batch 250 Loss 4.5433 Accuracy 0.2755
Epoch 3 Batch 300 Loss 4.5279 Accuracy 0.2771
Epoch 3 Batch 350 Loss 4.5123 Accuracy 0.2792
Epoch 3 Batch 400 Loss 4.5001 Accuracy 0.2807
Epoch 3 Batch 450 Loss 4.4849 Accuracy 0.2825
Epoch 3 Batch 500 Loss 4.4699 Accuracy 0.2842
Epoch 3 Batch 550 Loss 4.4543 Accuracy 0.2861
Epoch 3 Batch 600 Loss 4.4397 Accuracy 0.2879
Epoch 3 Batch 650 Loss 4.4260 Accuracy 0.2896
Epoch 3 Batch 700 Loss 4.4098 Accuracy 0.2918
Epoch 3 Batch 750 Loss 4.3934 Accuracy 0.2938
Epoch 3 Batch 800 Loss 4.3774 Accuracy 0.2958
Epoch 3 Loss 4.3743 Accuracy 0.2961
Time taken for 1 epoch: 47.78 secs

Epoch 4 Batch 0 Loss 4.1607 Accuracy 0.3198
Epoch 4 Batch 50 Loss 4.0206 Accuracy 0.3366
Epoch 4 Batch 100 Loss 4.0181 Accuracy 0.3379
Epoch 4 Batch 150 Loss 4.0005 Accuracy 0.3390
Epoch 4 Batch 200 Loss 3.9809 Accuracy 0.3420
Epoch 4 Batch 250 Loss 3.9716 Accuracy 0.3436
Epoch 4 Batch 300 Loss 3.9561 Accuracy 0.3453
Epoch 4 Batch 350 Loss 3.9396 Accuracy 0.3476
Epoch 4 Batch 400 Loss 3.9287 Accuracy 0.3492
Epoch 4 Batch 450 Loss 3.9099 Accuracy 0.3516
Epoch 4 Batch 500 Loss 3.8948 Accuracy 0.3534
Epoch 4 Batch 550 Loss 3.8796 Accuracy 0.3554
Epoch 4 Batch 600 Loss 3.8647 Accuracy 0.3573
Epoch 4 Batch 650 Loss 3.8496 Accuracy 0.3593
Epoch 4 Batch 700 Loss 3.8345 Accuracy 0.3613
Epoch 4 Batch 750 Loss 3.8216 Accuracy 0.3631
Epoch 4 Batch 800 Loss 3.8113 Accuracy 0.3645
Epoch 4 Loss 3.8084 Accuracy 0.3649
Time taken for 1 epoch: 47.74 secs

Epoch 5 Batch 0 Loss 3.7892 Accuracy 0.3515
Epoch 5 Batch 50 Loss 3.5277 Accuracy 0.3971
Epoch 5 Batch 100 Loss 3.4909 Accuracy 0.4026
Epoch 5 Batch 150 Loss 3.4709 Accuracy 0.4059
Epoch 5 Batch 200 Loss 3.4637 Accuracy 0.4068
Epoch 5 Batch 250 Loss 3.4614 Accuracy 0.4071
Epoch 5 Batch 300 Loss 3.4471 Accuracy 0.4093
Epoch 5 Batch 350 Loss 3.4360 Accuracy 0.4102
Epoch 5 Batch 400 Loss 3.4277 Accuracy 0.4112
Epoch 5 Batch 450 Loss 3.4186 Accuracy 0.4124
Epoch 5 Batch 500 Loss 3.4127 Accuracy 0.4133
Epoch 5 Batch 550 Loss 3.4023 Accuracy 0.4145
Epoch 5 Batch 600 Loss 3.3931 Accuracy 0.4154
Epoch 5 Batch 650 Loss 3.3821 Accuracy 0.4169
Epoch 5 Batch 700 Loss 3.3719 Accuracy 0.4183
Epoch 5 Batch 750 Loss 3.3632 Accuracy 0.4195
Epoch 5 Batch 800 Loss 3.3550 Accuracy 0.4205
Saving checkpoint for epoch 5 at ./checkpoints/train/ckpt-1
Epoch 5 Loss 3.3540 Accuracy 0.4206
Time taken for 1 epoch: 47.47 secs

Epoch 6 Batch 0 Loss 3.5078 Accuracy 0.3912
Epoch 6 Batch 50 Loss 3.1028 Accuracy 0.4487
Epoch 6 Batch 100 Loss 3.0855 Accuracy 0.4503
Epoch 6 Batch 150 Loss 3.0784 Accuracy 0.4515
Epoch 6 Batch 200 Loss 3.0664 Accuracy 0.4534
Epoch 6 Batch 250 Loss 3.0621 Accuracy 0.4537
Epoch 6 Batch 300 Loss 3.0512 Accuracy 0.4556
Epoch 6 Batch 350 Loss 3.0368 Accuracy 0.4580
Epoch 6 Batch 400 Loss 3.0283 Accuracy 0.4592
Epoch 6 Batch 450 Loss 3.0176 Accuracy 0.4608
Epoch 6 Batch 500 Loss 3.0083 Accuracy 0.4623
Epoch 6 Batch 550 Loss 2.9966 Accuracy 0.4639
Epoch 6 Batch 600 Loss 2.9871 Accuracy 0.4652
Epoch 6 Batch 650 Loss 2.9777 Accuracy 0.4664
Epoch 6 Batch 700 Loss 2.9722 Accuracy 0.4673
Epoch 6 Batch 750 Loss 2.9650 Accuracy 0.4684
Epoch 6 Batch 800 Loss 2.9562 Accuracy 0.4697
Epoch 6 Loss 2.9548 Accuracy 0.4698
Time taken for 1 epoch: 47.10 secs

Epoch 7 Batch 0 Loss 2.7935 Accuracy 0.4985
Epoch 7 Batch 50 Loss 2.6880 Accuracy 0.5017
Epoch 7 Batch 100 Loss 2.6676 Accuracy 0.5053
Epoch 7 Batch 150 Loss 2.6658 Accuracy 0.5062
Epoch 7 Batch 200 Loss 2.6631 Accuracy 0.5068
Epoch 7 Batch 250 Loss 2.6634 Accuracy 0.5069
Epoch 7 Batch 300 Loss 2.6575 Accuracy 0.5078
Epoch 7 Batch 350 Loss 2.6485 Accuracy 0.5088
Epoch 7 Batch 400 Loss 2.6472 Accuracy 0.5092
Epoch 7 Batch 450 Loss 2.6432 Accuracy 0.5097
Epoch 7 Batch 500 Loss 2.6398 Accuracy 0.5103
Epoch 7 Batch 550 Loss 2.6372 Accuracy 0.5111
Epoch 7 Batch 600 Loss 2.6340 Accuracy 0.5115
Epoch 7 Batch 650 Loss 2.6292 Accuracy 0.5123
Epoch 7 Batch 700 Loss 2.6266 Accuracy 0.5127
Epoch 7 Batch 750 Loss 2.6253 Accuracy 0.5129
Epoch 7 Batch 800 Loss 2.6217 Accuracy 0.5134
Epoch 7 Loss 2.6215 Accuracy 0.5134
Time taken for 1 epoch: 47.31 secs

Epoch 8 Batch 0 Loss 2.5891 Accuracy 0.5090
Epoch 8 Batch 50 Loss 2.4158 Accuracy 0.5395
Epoch 8 Batch 100 Loss 2.4099 Accuracy 0.5399
Epoch 8 Batch 150 Loss 2.4026 Accuracy 0.5408
Epoch 8 Batch 200 Loss 2.4023 Accuracy 0.5409
Epoch 8 Batch 250 Loss 2.4053 Accuracy 0.5404
Epoch 8 Batch 300 Loss 2.4085 Accuracy 0.5395
Epoch 8 Batch 350 Loss 2.4022 Accuracy 0.5406
Epoch 8 Batch 400 Loss 2.4031 Accuracy 0.5409
Epoch 8 Batch 450 Loss 2.3993 Accuracy 0.5416
Epoch 8 Batch 500 Loss 2.3970 Accuracy 0.5421
Epoch 8 Batch 550 Loss 2.3945 Accuracy 0.5429
Epoch 8 Batch 600 Loss 2.3908 Accuracy 0.5433
Epoch 8 Batch 650 Loss 2.3884 Accuracy 0.5439
Epoch 8 Batch 700 Loss 2.3864 Accuracy 0.5443
Epoch 8 Batch 750 Loss 2.3860 Accuracy 0.5443
Epoch 8 Batch 800 Loss 2.3829 Accuracy 0.5448
Epoch 8 Loss 2.3815 Accuracy 0.5450
Time taken for 1 epoch: 47.15 secs

Epoch 9 Batch 0 Loss 2.4162 Accuracy 0.5382
Epoch 9 Batch 50 Loss 2.2300 Accuracy 0.5635
Epoch 9 Batch 100 Loss 2.2048 Accuracy 0.5665
Epoch 9 Batch 150 Loss 2.2009 Accuracy 0.5681
Epoch 9 Batch 200 Loss 2.2006 Accuracy 0.5687
Epoch 9 Batch 250 Loss 2.2034 Accuracy 0.5682
Epoch 9 Batch 300 Loss 2.1974 Accuracy 0.5690
Epoch 9 Batch 350 Loss 2.2068 Accuracy 0.5678
Epoch 9 Batch 400 Loss 2.2058 Accuracy 0.5682
Epoch 9 Batch 450 Loss 2.2109 Accuracy 0.5677
Epoch 9 Batch 500 Loss 2.2112 Accuracy 0.5676
Epoch 9 Batch 550 Loss 2.2080 Accuracy 0.5685
Epoch 9 Batch 600 Loss 2.2106 Accuracy 0.5680
Epoch 9 Batch 650 Loss 2.2084 Accuracy 0.5684
Epoch 9 Batch 700 Loss 2.2082 Accuracy 0.5686
Epoch 9 Batch 750 Loss 2.2089 Accuracy 0.5685
Epoch 9 Batch 800 Loss 2.2084 Accuracy 0.5687
Epoch 9 Loss 2.2083 Accuracy 0.5686
Time taken for 1 epoch: 46.95 secs

Epoch 10 Batch 0 Loss 2.2234 Accuracy 0.5604
Epoch 10 Batch 50 Loss 2.1030 Accuracy 0.5814
Epoch 10 Batch 100 Loss 2.0774 Accuracy 0.5866
Epoch 10 Batch 150 Loss 2.0655 Accuracy 0.5881
Epoch 10 Batch 200 Loss 2.0661 Accuracy 0.5880
Epoch 10 Batch 250 Loss 2.0635 Accuracy 0.5883
Epoch 10 Batch 300 Loss 2.0616 Accuracy 0.5888
Epoch 10 Batch 350 Loss 2.0636 Accuracy 0.5886
Epoch 10 Batch 400 Loss 2.0621 Accuracy 0.5888
Epoch 10 Batch 450 Loss 2.0670 Accuracy 0.5878
Epoch 10 Batch 500 Loss 2.0652 Accuracy 0.5880
Epoch 10 Batch 550 Loss 2.0686 Accuracy 0.5875
Epoch 10 Batch 600 Loss 2.0692 Accuracy 0.5875
Epoch 10 Batch 650 Loss 2.0691 Accuracy 0.5876
Epoch 10 Batch 700 Loss 2.0707 Accuracy 0.5876
Epoch 10 Batch 750 Loss 2.0704 Accuracy 0.5878
Epoch 10 Batch 800 Loss 2.0722 Accuracy 0.5876
Saving checkpoint for epoch 10 at ./checkpoints/train/ckpt-2
Epoch 10 Loss 2.0715 Accuracy 0.5879
Time taken for 1 epoch: 47.13 secs

Epoch 11 Batch 0 Loss 1.9391 Accuracy 0.6270
Epoch 11 Batch 50 Loss 1.9758 Accuracy 0.5977
Epoch 11 Batch 100 Loss 1.9564 Accuracy 0.6024
Epoch 11 Batch 150 Loss 1.9526 Accuracy 0.6037
Epoch 11 Batch 200 Loss 1.9573 Accuracy 0.6030
Epoch 11 Batch 250 Loss 1.9471 Accuracy 0.6048
Epoch 11 Batch 300 Loss 1.9459 Accuracy 0.6048
Epoch 11 Batch 350 Loss 1.9490 Accuracy 0.6042
Epoch 11 Batch 400 Loss 1.9518 Accuracy 0.6040
Epoch 11 Batch 450 Loss 1.9549 Accuracy 0.6038
Epoch 11 Batch 500 Loss 1.9543 Accuracy 0.6040
Epoch 11 Batch 550 Loss 1.9537 Accuracy 0.6043
Epoch 11 Batch 600 Loss 1.9546 Accuracy 0.6042
Epoch 11 Batch 650 Loss 1.9556 Accuracy 0.6040
Epoch 11 Batch 700 Loss 1.9582 Accuracy 0.6038
Epoch 11 Batch 750 Loss 1.9592 Accuracy 0.6037
Epoch 11 Batch 800 Loss 1.9606 Accuracy 0.6037
Epoch 11 Loss 1.9614 Accuracy 0.6036
Time taken for 1 epoch: 46.59 secs

Epoch 12 Batch 0 Loss 1.7234 Accuracy 0.6256
Epoch 12 Batch 50 Loss 1.8507 Accuracy 0.6169
Epoch 12 Batch 100 Loss 1.8460 Accuracy 0.6184
Epoch 12 Batch 150 Loss 1.8366 Accuracy 0.6207
Epoch 12 Batch 200 Loss 1.8454 Accuracy 0.6196
Epoch 12 Batch 250 Loss 1.8398 Accuracy 0.6205
Epoch 12 Batch 300 Loss 1.8441 Accuracy 0.6202
Epoch 12 Batch 350 Loss 1.8440 Accuracy 0.6199
Epoch 12 Batch 400 Loss 1.8443 Accuracy 0.6200
Epoch 12 Batch 450 Loss 1.8452 Accuracy 0.6198
Epoch 12 Batch 500 Loss 1.8499 Accuracy 0.6193
Epoch 12 Batch 550 Loss 1.8520 Accuracy 0.6190
Epoch 12 Batch 600 Loss 1.8554 Accuracy 0.6185
Epoch 12 Batch 650 Loss 1.8587 Accuracy 0.6179
Epoch 12 Batch 700 Loss 1.8610 Accuracy 0.6176
Epoch 12 Batch 750 Loss 1.8655 Accuracy 0.6170
Epoch 12 Batch 800 Loss 1.8684 Accuracy 0.6167
Epoch 12 Loss 1.8688 Accuracy 0.6167
Time taken for 1 epoch: 46.38 secs

Epoch 13 Batch 0 Loss 1.7697 Accuracy 0.6350
Epoch 13 Batch 50 Loss 1.7443 Accuracy 0.6337
Epoch 13 Batch 100 Loss 1.7422 Accuracy 0.6352
Epoch 13 Batch 150 Loss 1.7497 Accuracy 0.6331
Epoch 13 Batch 200 Loss 1.7603 Accuracy 0.6315
Epoch 13 Batch 250 Loss 1.7623 Accuracy 0.6316
Epoch 13 Batch 300 Loss 1.7656 Accuracy 0.6315
Epoch 13 Batch 350 Loss 1.7669 Accuracy 0.6312
Epoch 13 Batch 400 Loss 1.7683 Accuracy 0.6310
Epoch 13 Batch 450 Loss 1.7758 Accuracy 0.6298
Epoch 13 Batch 500 Loss 1.7758 Accuracy 0.6300
Epoch 13 Batch 550 Loss 1.7784 Accuracy 0.6296
Epoch 13 Batch 600 Loss 1.7804 Accuracy 0.6294
Epoch 13 Batch 650 Loss 1.7825 Accuracy 0.6291
Epoch 13 Batch 700 Loss 1.7867 Accuracy 0.6286
Epoch 13 Batch 750 Loss 1.7878 Accuracy 0.6284
Epoch 13 Batch 800 Loss 1.7913 Accuracy 0.6280
Epoch 13 Loss 1.7916 Accuracy 0.6280
Time taken for 1 epoch: 46.41 secs

Epoch 14 Batch 0 Loss 1.4785 Accuracy 0.6762
Epoch 14 Batch 50 Loss 1.7171 Accuracy 0.6378
Epoch 14 Batch 100 Loss 1.6949 Accuracy 0.6424
Epoch 14 Batch 150 Loss 1.6862 Accuracy 0.6440
Epoch 14 Batch 200 Loss 1.6893 Accuracy 0.6442
Epoch 14 Batch 250 Loss 1.6927 Accuracy 0.6432
Epoch 14 Batch 300 Loss 1.6943 Accuracy 0.6428
Epoch 14 Batch 350 Loss 1.6951 Accuracy 0.6427
Epoch 14 Batch 400 Loss 1.6961 Accuracy 0.6426
Epoch 14 Batch 450 Loss 1.7003 Accuracy 0.6420
Epoch 14 Batch 500 Loss 1.7028 Accuracy 0.6415
Epoch 14 Batch 550 Loss 1.7027 Accuracy 0.6413
Epoch 14 Batch 600 Loss 1.7066 Accuracy 0.6406
Epoch 14 Batch 650 Loss 1.7110 Accuracy 0.6399
Epoch 14 Batch 700 Loss 1.7140 Accuracy 0.6395
Epoch 14 Batch 750 Loss 1.7174 Accuracy 0.6390
Epoch 14 Batch 800 Loss 1.7198 Accuracy 0.6389
Epoch 14 Loss 1.7210 Accuracy 0.6387
Time taken for 1 epoch: 46.84 secs

Epoch 15 Batch 0 Loss 1.7209 Accuracy 0.6358
Epoch 15 Batch 50 Loss 1.6188 Accuracy 0.6545
Epoch 15 Batch 100 Loss 1.6084 Accuracy 0.6567
Epoch 15 Batch 150 Loss 1.6169 Accuracy 0.6557
Epoch 15 Batch 200 Loss 1.6215 Accuracy 0.6542
Epoch 15 Batch 250 Loss 1.6268 Accuracy 0.6534
Epoch 15 Batch 300 Loss 1.6257 Accuracy 0.6539
Epoch 15 Batch 350 Loss 1.6300 Accuracy 0.6531
Epoch 15 Batch 400 Loss 1.6318 Accuracy 0.6527
Epoch 15 Batch 450 Loss 1.6360 Accuracy 0.6521
Epoch 15 Batch 500 Loss 1.6403 Accuracy 0.6515
Epoch 15 Batch 550 Loss 1.6445 Accuracy 0.6509
Epoch 15 Batch 600 Loss 1.6467 Accuracy 0.6505
Epoch 15 Batch 650 Loss 1.6501 Accuracy 0.6499
Epoch 15 Batch 700 Loss 1.6537 Accuracy 0.6495
Epoch 15 Batch 750 Loss 1.6560 Accuracy 0.6492
Epoch 15 Batch 800 Loss 1.6602 Accuracy 0.6486
Saving checkpoint for epoch 15 at ./checkpoints/train/ckpt-3
Epoch 15 Loss 1.6598 Accuracy 0.6487
Time taken for 1 epoch: 46.54 secs

Epoch 16 Batch 0 Loss 1.5373 Accuracy 0.6739
Epoch 16 Batch 50 Loss 1.5678 Accuracy 0.6614
Epoch 16 Batch 100 Loss 1.5524 Accuracy 0.6646
Epoch 16 Batch 150 Loss 1.5550 Accuracy 0.6640
Epoch 16 Batch 200 Loss 1.5612 Accuracy 0.6627
Epoch 16 Batch 250 Loss 1.5703 Accuracy 0.6608
Epoch 16 Batch 300 Loss 1.5771 Accuracy 0.6597
Epoch 16 Batch 350 Loss 1.5812 Accuracy 0.6593
Epoch 16 Batch 400 Loss 1.5846 Accuracy 0.6588
Epoch 16 Batch 450 Loss 1.5864 Accuracy 0.6587
Epoch 16 Batch 500 Loss 1.5906 Accuracy 0.6581
Epoch 16 Batch 550 Loss 1.5940 Accuracy 0.6575
Epoch 16 Batch 600 Loss 1.5938 Accuracy 0.6576
Epoch 16 Batch 650 Loss 1.5959 Accuracy 0.6575
Epoch 16 Batch 700 Loss 1.5991 Accuracy 0.6571
Epoch 16 Batch 750 Loss 1.6017 Accuracy 0.6568
Epoch 16 Batch 800 Loss 1.6035 Accuracy 0.6566
Epoch 16 Loss 1.6043 Accuracy 0.6565
Time taken for 1 epoch: 46.25 secs

Epoch 17 Batch 0 Loss 1.6442 Accuracy 0.6463
Epoch 17 Batch 50 Loss 1.5289 Accuracy 0.6670
Epoch 17 Batch 100 Loss 1.5225 Accuracy 0.6684
Epoch 17 Batch 150 Loss 1.5237 Accuracy 0.6685
Epoch 17 Batch 200 Loss 1.5301 Accuracy 0.6674
Epoch 17 Batch 250 Loss 1.5323 Accuracy 0.6672
Epoch 17 Batch 300 Loss 1.5332 Accuracy 0.6670
Epoch 17 Batch 350 Loss 1.5349 Accuracy 0.6667
Epoch 17 Batch 400 Loss 1.5377 Accuracy 0.6662
Epoch 17 Batch 450 Loss 1.5399 Accuracy 0.6660
Epoch 17 Batch 500 Loss 1.5412 Accuracy 0.6660
Epoch 17 Batch 550 Loss 1.5468 Accuracy 0.6650
Epoch 17 Batch 600 Loss 1.5474 Accuracy 0.6651
Epoch 17 Batch 650 Loss 1.5504 Accuracy 0.6647
Epoch 17 Batch 700 Loss 1.5534 Accuracy 0.6643
Epoch 17 Batch 750 Loss 1.5562 Accuracy 0.6639
Epoch 17 Batch 800 Loss 1.5583 Accuracy 0.6635
Epoch 17 Loss 1.5593 Accuracy 0.6633
Time taken for 1 epoch: 46.66 secs

Epoch 18 Batch 0 Loss 1.5130 Accuracy 0.6849
Epoch 18 Batch 50 Loss 1.4575 Accuracy 0.6796
Epoch 18 Batch 100 Loss 1.4698 Accuracy 0.6770
Epoch 18 Batch 150 Loss 1.4760 Accuracy 0.6765
Epoch 18 Batch 200 Loss 1.4783 Accuracy 0.6762
Epoch 18 Batch 250 Loss 1.4811 Accuracy 0.6758
Epoch 18 Batch 300 Loss 1.4870 Accuracy 0.6749
Epoch 18 Batch 350 Loss 1.4929 Accuracy 0.6738
Epoch 18 Batch 400 Loss 1.4945 Accuracy 0.6736
Epoch 18 Batch 450 Loss 1.4979 Accuracy 0.6730
Epoch 18 Batch 500 Loss 1.4970 Accuracy 0.6731
Epoch 18 Batch 550 Loss 1.5004 Accuracy 0.6725
Epoch 18 Batch 600 Loss 1.5034 Accuracy 0.6721
Epoch 18 Batch 650 Loss 1.5073 Accuracy 0.6715
Epoch 18 Batch 700 Loss 1.5096 Accuracy 0.6712
Epoch 18 Batch 750 Loss 1.5129 Accuracy 0.6707
Epoch 18 Batch 800 Loss 1.5164 Accuracy 0.6701
Epoch 18 Loss 1.5160 Accuracy 0.6702
Time taken for 1 epoch: 46.40 secs

Epoch 19 Batch 0 Loss 1.5139 Accuracy 0.6813
Epoch 19 Batch 50 Loss 1.4341 Accuracy 0.6832
Epoch 19 Batch 100 Loss 1.4357 Accuracy 0.6828
Epoch 19 Batch 150 Loss 1.4364 Accuracy 0.6829
Epoch 19 Batch 200 Loss 1.4439 Accuracy 0.6816
Epoch 19 Batch 250 Loss 1.4448 Accuracy 0.6816
Epoch 19 Batch 300 Loss 1.4494 Accuracy 0.6812
Epoch 19 Batch 350 Loss 1.4543 Accuracy 0.6805
Epoch 19 Batch 400 Loss 1.4543 Accuracy 0.6804
Epoch 19 Batch 450 Loss 1.4583 Accuracy 0.6795
Epoch 19 Batch 500 Loss 1.4609 Accuracy 0.6789
Epoch 19 Batch 550 Loss 1.4637 Accuracy 0.6786
Epoch 19 Batch 600 Loss 1.4641 Accuracy 0.6787
Epoch 19 Batch 650 Loss 1.4674 Accuracy 0.6780
Epoch 19 Batch 700 Loss 1.4703 Accuracy 0.6776
Epoch 19 Batch 750 Loss 1.4722 Accuracy 0.6773
Epoch 19 Batch 800 Loss 1.4760 Accuracy 0.6768
Epoch 19 Loss 1.4767 Accuracy 0.6766
Time taken for 1 epoch: 46.45 secs

Epoch 20 Batch 0 Loss 1.3065 Accuracy 0.7057
Epoch 20 Batch 50 Loss 1.3843 Accuracy 0.6909
Epoch 20 Batch 100 Loss 1.3941 Accuracy 0.6891
Epoch 20 Batch 150 Loss 1.3995 Accuracy 0.6883
Epoch 20 Batch 200 Loss 1.3997 Accuracy 0.6888
Epoch 20 Batch 250 Loss 1.4033 Accuracy 0.6884
Epoch 20 Batch 300 Loss 1.4076 Accuracy 0.6873
Epoch 20 Batch 350 Loss 1.4130 Accuracy 0.6864
Epoch 20 Batch 400 Loss 1.4144 Accuracy 0.6862
Epoch 20 Batch 450 Loss 1.4186 Accuracy 0.6853
Epoch 20 Batch 500 Loss 1.4206 Accuracy 0.6851
Epoch 20 Batch 550 Loss 1.4225 Accuracy 0.6847
Epoch 20 Batch 600 Loss 1.4253 Accuracy 0.6843
Epoch 20 Batch 650 Loss 1.4299 Accuracy 0.6835
Epoch 20 Batch 700 Loss 1.4324 Accuracy 0.6831
Epoch 20 Batch 750 Loss 1.4376 Accuracy 0.6823
Epoch 20 Batch 800 Loss 1.4405 Accuracy 0.6817
Saving checkpoint for epoch 20 at ./checkpoints/train/ckpt-4
Epoch 20 Loss 1.4410 Accuracy 0.6816
Time taken for 1 epoch: 46.87 secs

Exécuter l'inférence

Les étapes suivantes sont utilisées pour l'inférence :

  • Encodez la phrase d'entrée en utilisant le tokenizer portugais ( tokenizers.pt ). C'est l'entrée du codeur.
  • L'entrée du décodeur est initialisé à la [START] jeton.
  • Calculez les masques de remplissage et les masques d'anticipation.
  • Le decoder délivre alors les prédictions en regardant la encoder output et sa propre sortie (auto-attention).
  • Concaténez le jeton prédit à l'entrée du décodeur et transmettez-le au décodeur.
  • Dans cette approche, le décodeur prédit le jeton suivant sur la base des jetons précédents qu'il a prédits.
class Translator(tf.Module):
  def __init__(self, tokenizers, transformer):
    self.tokenizers = tokenizers
    self.transformer = transformer

  def __call__(self, sentence, max_length=20):
    # input sentence is portuguese, hence adding the start and end token
    assert isinstance(sentence, tf.Tensor)
    if len(sentence.shape) == 0:
      sentence = sentence[tf.newaxis]

    sentence = self.tokenizers.pt.tokenize(sentence).to_tensor()

    encoder_input = sentence

    # as the target is english, the first token to the transformer should be the
    # english start token.
    start_end = self.tokenizers.en.tokenize([''])[0]
    start = start_end[0][tf.newaxis]
    end = start_end[1][tf.newaxis]

    # `tf.TensorArray` is required here (instead of a python list) so that the
    # dynamic-loop can be traced by `tf.function`.
    output_array = tf.TensorArray(dtype=tf.int64, size=0, dynamic_size=True)
    output_array = output_array.write(0, start)

    for i in tf.range(max_length):
      output = tf.transpose(output_array.stack())
      predictions, _ = self.transformer([encoder_input, output], training=False)

      # select the last token from the seq_len dimension
      predictions = predictions[:, -1:, :]  # (batch_size, 1, vocab_size)

      predicted_id = tf.argmax(predictions, axis=-1)

      # concatentate the predicted_id to the output which is given to the decoder
      # as its input.
      output_array = output_array.write(i+1, predicted_id[0])

      if predicted_id == end:
        break

    output = tf.transpose(output_array.stack())
    # output.shape (1, tokens)
    text = tokenizers.en.detokenize(output)[0]  # shape: ()

    tokens = tokenizers.en.lookup(output)[0]

    # `tf.function` prevents us from using the attention_weights that were
    # calculated on the last iteration of the loop. So recalculate them outside
    # the loop.
    _, attention_weights = self.transformer([encoder_input, output[:,:-1]], training=False)

    return text, tokens, attention_weights

Créer une instance de ce Translator classe, et l' essayer plusieurs fois:

translator = Translator(tokenizers, transformer)
def print_translation(sentence, tokens, ground_truth):
  print(f'{"Input:":15s}: {sentence}')
  print(f'{"Prediction":15s}: {tokens.numpy().decode("utf-8")}')
  print(f'{"Ground truth":15s}: {ground_truth}')
sentence = "este é um problema que temos que resolver."
ground_truth = "this is a problem we have to solve ."

translated_text, translated_tokens, attention_weights = translator(
    tf.constant(sentence))
print_translation(sentence, translated_text, ground_truth)
Input:         : este é um problema que temos que resolver.
Prediction     : this is a problem that we have to solve .
Ground truth   : this is a problem we have to solve .
sentence = "os meus vizinhos ouviram sobre esta ideia."
ground_truth = "and my neighboring homes heard about this idea ."

translated_text, translated_tokens, attention_weights = translator(
    tf.constant(sentence))
print_translation(sentence, translated_text, ground_truth)
Input:         : os meus vizinhos ouviram sobre esta ideia.
Prediction     : my neighbors heard about this idea .
Ground truth   : and my neighboring homes heard about this idea .
sentence = "vou então muito rapidamente partilhar convosco algumas histórias de algumas coisas mágicas que aconteceram."
ground_truth = "so i \'ll just share with you some stories very quickly of some magical things that have happened ."

translated_text, translated_tokens, attention_weights = translator(
    tf.constant(sentence))
print_translation(sentence, translated_text, ground_truth)
Input:         : vou então muito rapidamente partilhar convosco algumas histórias de algumas coisas mágicas que aconteceram.
Prediction     : so i ' m going to be very quickly share with you some of the magic stories that happened .
Ground truth   : so i 'll just share with you some stories very quickly of some magical things that have happened .

Points d'attention

Le Translator retourne classe un dictionnaire d'attention cartes que vous pouvez utiliser pour visualiser le fonctionnement interne du modèle:

sentence = "este é o primeiro livro que eu fiz."
ground_truth = "this is the first book i've ever done."

translated_text, translated_tokens, attention_weights = translator(
    tf.constant(sentence))
print_translation(sentence, translated_text, ground_truth)
Input:         : este é o primeiro livro que eu fiz.
Prediction     : this is the first book i did .
Ground truth   : this is the first book i've ever done.
def plot_attention_head(in_tokens, translated_tokens, attention):
  # The plot is of the attention when a token was generated.
  # The model didn't generate `<START>` in the output. Skip it.
  translated_tokens = translated_tokens[1:]

  ax = plt.gca()
  ax.matshow(attention)
  ax.set_xticks(range(len(in_tokens)))
  ax.set_yticks(range(len(translated_tokens)))

  labels = [label.decode('utf-8') for label in in_tokens.numpy()]
  ax.set_xticklabels(
      labels, rotation=90)

  labels = [label.decode('utf-8') for label in translated_tokens.numpy()]
  ax.set_yticklabels(labels)
head = 0
# shape: (batch=1, num_heads, seq_len_q, seq_len_k)
attention_heads = tf.squeeze(
  attention_weights['decoder_layer4_block2'], 0)
attention = attention_heads[head]
attention.shape
TensorShape([9, 11])
in_tokens = tf.convert_to_tensor([sentence])
in_tokens = tokenizers.pt.tokenize(in_tokens).to_tensor()
in_tokens = tokenizers.pt.lookup(in_tokens)[0]
in_tokens
<tf.Tensor: shape=(11,), dtype=string, numpy=
array([b'[START]', b'este', b'e', b'o', b'primeiro', b'livro', b'que',
       b'eu', b'fiz', b'.', b'[END]'], dtype=object)>
translated_tokens
<tf.Tensor: shape=(10,), dtype=string, numpy=
array([b'[START]', b'this', b'is', b'the', b'first', b'book', b'i',
       b'did', b'.', b'[END]'], dtype=object)>
plot_attention_head(in_tokens, translated_tokens, attention)

png

def plot_attention_weights(sentence, translated_tokens, attention_heads):
  in_tokens = tf.convert_to_tensor([sentence])
  in_tokens = tokenizers.pt.tokenize(in_tokens).to_tensor()
  in_tokens = tokenizers.pt.lookup(in_tokens)[0]
  in_tokens

  fig = plt.figure(figsize=(16, 8))

  for h, head in enumerate(attention_heads):
    ax = fig.add_subplot(2, 4, h+1)

    plot_attention_head(in_tokens, translated_tokens, head)

    ax.set_xlabel(f'Head {h+1}')

  plt.tight_layout()
  plt.show()
plot_attention_weights(sentence, translated_tokens,
                       attention_weights['decoder_layer4_block2'][0])

png

Le modèle s'en sort bien avec les mots inconnus. Ni "tricératops" ni "encyclopédie" ne figurent dans l'ensemble de données d'entrée et le modèle apprend presque à les translittérer, même sans vocabulaire partagé :

sentence = "Eu li sobre triceratops na enciclopédia."
ground_truth = "I read about triceratops in the encyclopedia."

translated_text, translated_tokens, attention_weights = translator(
    tf.constant(sentence))
print_translation(sentence, translated_text, ground_truth)

plot_attention_weights(sentence, translated_tokens,
                       attention_weights['decoder_layer4_block2'][0])
Input:         : Eu li sobre triceratops na enciclopédia.
Prediction     : i read about trivalopat nairconcissus in the encyclo
Ground truth   : I read about triceratops in the encyclopedia.

png

Exportation

Ce modèle d'inférence fonctionne, donc la prochaine vous exporter comme tf.saved_model .

Pour ce faire, l' envelopper dans une autre tf.Module sous-classe, cette fois avec une tf.function sur la __call__ méthode:

class ExportTranslator(tf.Module):
  def __init__(self, translator):
    self.translator = translator

  @tf.function(input_signature=[tf.TensorSpec(shape=[], dtype=tf.string)])
  def __call__(self, sentence):
    (result, 
     tokens,
     attention_weights) = self.translator(sentence, max_length=100)

    return result

Dans ce qui précède tf.function que la phrase de sortie est renvoyée. Merci à l' exécution non stricte dans tf.function toutes les valeurs inutiles ne sont jamais calculés.

translator = ExportTranslator(translator)

Étant donné que le modèle décode les prédictions en utilisant tf.argmax les prédictions sont déterministes. Le modèle original et une rechargées de son SavedModel devraient donner des prédictions identiques:

translator("este é o primeiro livro que eu fiz.").numpy()
b'this is the first book i did .'
tf.saved_model.save(translator, export_dir='translator')
2021-11-02 15:48:30.232789: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
WARNING:absl:Found untraced functions such as embedding_4_layer_call_and_return_conditional_losses, embedding_4_layer_call_fn, dropout_37_layer_call_and_return_conditional_losses, dropout_37_layer_call_fn, embedding_5_layer_call_and_return_conditional_losses while saving (showing 5 of 560). These functions will not be directly callable after loading.
reloaded = tf.saved_model.load('translator')
reloaded("este é o primeiro livro que eu fiz.").numpy()
b'this is the first book i did .'

Résumé

Dans ce didacticiel, vous avez appris l'encodage positionnel, l'attention multi-têtes, l'importance du masquage et comment créer un transformateur.

Essayez d'utiliser un jeu de données différent pour entraîner le transformateur. Vous pouvez également créer le transformateur de base ou le transformateur XL en modifiant les hyperparamètres ci-dessus. Vous pouvez également utiliser les couches définies ici pour créer BERT état et le train des modèles d'art. De plus, vous pouvez implémenter la recherche de faisceau pour obtenir de meilleures prédictions.