Ajuda a proteger a Grande Barreira de Corais com TensorFlow em Kaggle Junte Desafio

Modelo de transformador para compreensão de linguagem

Ver no TensorFlow.org Executar no Google Colab Ver fonte no GitHubBaixar caderno

Isso treina tutorial um modelo Transformer para traduzir um Português para Inglês conjunto de dados . Este é um exemplo avançado que pressupõe o conhecimento de geração de texto e atenção .

A idéia central por trás do modelo Transformer é auto-atenção -a capacidade de atender às diferentes posições da sequência de entrada para calcular uma representação dessa sequência. Transformer cria pilhas de camadas auto-atenção e é explicado abaixo nas seções Scaled atenção produto escalar e atenção Multi-cabeça.

Uma variável de tamanho de entrada alças modelo transformador usando pilhas de camadas auto-atenção, em vez de RNNs ou CNNs . Esta arquitetura geral tem uma série de vantagens:

  • Não faz suposições sobre as relações temporais / espaciais entre os dados. Isto é ideal para o processamento de um conjunto de objectos (por exemplo, unidades de StarCraft ).
  • As saídas da camada podem ser calculadas em paralelo, em vez de uma série como um RNN.
  • Itens distantes podem afetar a saída do outro sem passar por muitos RNN-passos, ou camadas de convolução (ver Scene Memory Transformer por exemplo).
  • Ele pode aprender dependências de longo alcance. Este é um desafio em muitas tarefas de sequência.

As desvantagens dessa arquitetura são:

  • Para uma série temporal, a saída para um passo de tempo é calculado a partir de toda a história em vez de apenas as entradas e do estado escondido atual. Isso pode ser menos eficiente.
  • Se a entrada não tem uma relação temporal / espacial, como texto, deve ser adicionado alguns codificação posicional ou o modelo vai ver efetivamente um saco de palavras.

Após treinar a modelo neste notebook, você poderá inserir uma frase em português e devolver a tradução em inglês.

Mapa de calor de atenção

Configurar

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

Baixe o conjunto de dados

Use conjuntos de dados TensorFlow para carregar o Português-Inglês dataset tradução do Open Project Tradução TED Talks .

Este conjunto de dados contém aproximadamente 50.000 exemplos de treinamento, 1.100 exemplos de validação e 2.000 exemplos de teste.

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

O tf.data.Dataset objeto retornado por TensorFlow conjuntos de dados produz pares de exemplos de texto:

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 .

Tokenização e detocagem de texto

Você não pode treinar um modelo diretamente no texto. O texto precisa ser convertido para alguma representação numérica primeiro. Normalmente, você converte o texto em sequências de IDs de token, que são usados ​​como índices em uma incorporação.

Uma implementação popular é demonstrado no subword tokenizer tutorial constrói tokenizers subword ( text.BertTokenizer ) otimizados para este conjunto de dados e os exporta em um saved_model .

Baixe e descompacte e importar o 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)

O tf.saved_model contém dois tokenizers de texto, um para Inglês e outro para Português. Ambos têm os mesmos métodos:

[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']

O tokenize método converte um lote de cordas para um acolchoado-lote de IDs de token. Este método divide a pontuação, minúsculas e unicode-normaliza a entrada antes de tokenizar. Essa padronização não é visível aqui porque os dados de entrada já estão padronizados.

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]

Os detokenize tentativas método para converter esses IDs token de volta ao texto legível:

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 .

O nível mais baixo lookup convertidos método de token-IDs para texto token:

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]']]>

Aqui você pode ver o aspecto "subpalavra" dos tokenizadores. A palavra "pesquisabilidade" é decomposta em "capacidade de pesquisa ##" e a palavra "serendipidade" em "s ## ere ## nd ## ip ## ity"

Configurar pipeline de entrada

Para construir um pipeline de entrada adequado para treinamento, você aplicará algumas transformações ao conjunto de dados.

Esta função será usada para codificar os lotes de texto bruto:

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

Este é um pipeline de entrada simples que processa, embaralha e agrupa os dados:

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)

Codificação posicional

As camadas de atenção veem sua entrada como um conjunto de vetores, sem ordem sequencial. Este modelo também não contém camadas recorrentes ou convolucionais. Por causa disso, uma "codificação posicional" é adicionada para fornecer ao modelo algumas informações sobre a posição relativa dos tokens na frase.

O vetor de codificação posicional é adicionado ao vetor de incorporação. Os embeddings representam um token em um espaço d-dimensional onde os tokens com significados semelhantes estarão mais próximos uns dos outros. Mas os embeddings não codificam a posição relativa dos tokens em uma frase. Então, depois de se adicionar a codificação posicional, fichas será mais próximos uns dos outros com base na semelhança do seu significado e a sua posição no período, no espaço d-dimensional.

A fórmula para calcular a codificação posicional é a seguinte:

\[\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

Mascaramento

Mascare todos os tokens de pad no lote da sequência. Isso garante que o modelo não trate o preenchimento como entrada. A máscara indica onde o valor pad 0 está presente: ele produz um 1 nesses locais, e um 0 contrário.

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)>

A máscara de antecipação é usada para mascarar os tokens futuros em uma sequência. Em outras palavras, a máscara indica quais entradas não devem ser usadas.

Isso significa que para prever o terceiro token, apenas o primeiro e o segundo token serão usados. Da mesma forma, para prever o quarto token, apenas o primeiro, o segundo e o terceiro tokens serão usados ​​e assim por diante.

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)>

Atenção ao produto escalonado

scaled_dot_product_attention

A função de atenção usada pelo transformador tem três entradas: Q (consulta), K (chave), V (valor). A equação usada para calcular os pesos de atenção é:

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

A atenção do produto escalar é dimensionada por um fator de raiz quadrada da profundidade. Isso é feito porque, para grandes valores de profundidade, o produto escalar cresce em magnitude empurrando a função softmax onde tem pequenos gradientes, resultando em um softmax muito difícil.

Por exemplo, considere que Q e K têm uma média de 0 e variância 1. A multiplicação de matrizes terá uma média 0 e variância de dk . Então, a raiz quadrada de dk é usado para o dimensionamento, de modo a obter uma variação consistente, independentemente do valor da dk . Se a variação for muito baixa, a saída pode ser muito plana para otimizar de forma eficaz. Se a variação for muito alta, o softmax pode saturar na inicialização, dificultando o aprendizado.

A máscara é multiplicada por -1e9 (próximo ao infinito negativo). Isso é feito porque a máscara é somada à multiplicação da matriz em escala de Q e K e é aplicada imediatamente antes de um softmax. O objetivo é zerar essas células, e grandes entradas negativas para softmax estão perto de zero na saída.

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

Como a normalização softmax é feita em K, seus valores decidem a importância dada a Q.

A saída representa a multiplicação dos pesos de atenção e o vetor V (valor). Isso garante que os tokens nos quais você deseja se concentrar sejam mantidos como estão e os tokens irrelevantes sejam eliminados.

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)

Passe todas as consultas juntas.

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)

Atenção multi-cabeça

atenção multi-cabeça

A atenção de várias cabeças consiste em quatro partes:

  • Camadas lineares.
  • Atenção ao produto escalado em escala.
  • Camada linear final.

Cada bloco de atenção com várias cabeças recebe três entradas; Q (consulta), K (chave), V (valor). Eles são colocados em camadas lineares (densas) antes da função de atenção de várias cabeças.

No diagrama acima, (K,Q,V) são passados através de (lineares sepearte Dense ) camadas para cada cabeça de atenção. Para simplicidade / eficiência o código abaixo implementa este usando uma única camada densa com num_heads vezes o número de saídas. A saída é reorganizado a uma forma de (batch, num_heads, ...) antes de aplicar a função de atenção.

O scaled_dot_product_attention função definido acima é aplicado em uma única chamada, transmitido para a eficiência. Uma máscara apropriada deve ser usada na etapa de atenção. A saída atenção para cada cabeça é então concatenados (usando tf.transpose , e tf.reshape ) e colocado através de um último Dense camada.

Em vez de uma única cabeça de atenção, Q, K e V são divididos em várias cabeças porque permite que o modelo atenda conjuntamente às informações de diferentes subespaços de representação em diferentes posições. Após a divisão, cada cabeça tem uma dimensionalidade reduzida, então o custo total de computação é o mesmo que a atenção de uma única cabeça com dimensionalidade total.

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

Criar um MultiHeadAttention camada para experimentar. Em cada local na seqüência, y , o MultiHeadAttention corre todos os 8 cabeças de atenção em todos os outros locais na seqüência, retornando um novo vetor do mesmo comprimento em cada local.

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]))

Rede de feed forward inteligente

A rede de alimentação direta pontual consiste em duas camadas totalmente conectadas com uma ativação ReLU entre elas.

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])

Codificador e decodificador

transformador

O modelo de transformador segue o mesmo padrão geral, como um padrão de sequência com a sequência com o modelo de atenção .

  • A frase de entrada é passado através de N camadas de codificador, que gera uma saída para cada símbolo na sequência.
  • O decodificador atende à saída do codificador e sua própria entrada (autoatenção) para prever a próxima palavra.

Camada codificadora

Cada camada do codificador consiste em subcamadas:

  1. Atenção multi-cabeça (com máscara de preenchimento)
  2. Aponte redes de feed forward inteligentes.

Cada uma dessas subcamadas tem uma conexão residual em torno dela seguida por uma normalização de camada. As conexões residuais ajudam a evitar o problema do gradiente de desaparecimento em redes profundas.

A saída de cada sub-camada é LayerNorm(x + Sublayer(x)) . A normalização é feita no d_model eixo (última). Existem N camadas de codificador no transformador.

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])

Camada decodificadora

Cada camada do decodificador consiste em subcamadas:

  1. Atenção de várias cabeças mascarada (com máscara de observação e máscara de preenchimento)
  2. Atenção multi-cabeça (com máscara de enchimento). V (valor) e K (chave) recebe a saída do codificador como entradas. Q (query) recebe a saída do atenção multi-cabeça subcamada mascarado.
  3. Aponte redes de feed forward inteligente

Cada uma dessas subcamadas tem uma conexão residual em torno dela seguida por uma normalização de camada. A saída de cada sub-camada é LayerNorm(x + Sublayer(x)) . A normalização é feita no d_model eixo (última).

Existem N camadas de decodificador no transformador.

Como Q recebe a saída do primeiro bloco de atenção do decodificador e K recebe a saída do codificador, os pesos de atenção representam a importância dada à entrada do decodificador com base na saída do codificador. Em outras palavras, o decodificador prevê o próximo token olhando para a saída do codificador e atendendo automaticamente sua própria saída. Veja a demonstração acima na seção de atenção ao produto escalonado.

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])

Codificador

O Encoder consiste em:

  1. Incorporação de entrada
  2. Codificação Posicional
  3. Camadas do codificador N

A entrada é submetida a uma incorporação que é somada à codificação posicional. A saída desse somatório é a entrada para as camadas do codificador. A saída do codificador é a entrada para o decodificador.

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)

Decodificador

O Decoder consiste de:

  1. Incorporação de saída
  2. Codificação Posicional
  3. Camadas de decodificador N

O destino é submetido a uma incorporação que se soma à codificação posicional. A saída desse somatório é a entrada para as camadas do decodificador. A saída do decodificador é a entrada para a camada linear final.

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]))

Crie o Transformer

O transformador consiste no codificador, decodificador e uma camada linear final. A saída do decodificador é a entrada para a camada linear e sua saída é retornada.

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])

Definir hiperparâmetros

Para manter este exemplo pequeno e relativamente rápido, os valores para num_layers, d_model, dff foram reduzidos.

O modelo de base descrito no papel usado: num_layers=6, d_model=512, dff=2048 .

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

Otimizador

Use o otimizador de Adam com um programador taxa de aprendizagem personalizado de acordo com a fórmula no papel .

\[\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

Perda e métricas

Como as sequências de destino são preenchidas, é importante aplicar uma máscara de preenchimento ao calcular a perda.

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')

Treinamento e verificação

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)

Crie o caminho do ponto de verificação e o gerenciador de ponto de verificação. Isso será usado para salvar postos de controle a cada n épocas.

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!!')

O destino é dividido em tar_inp e tar_real. tar_inp é passado como uma entrada para o decodificador. tar_real é que mesmo entrada deslocado por 1: Em cada local em tar_input , tar_real contém a marca seguinte que deve ser prevista.

Por exemplo, sentence = "SOS Um leão na selva está dormindo EOS"

tar_inp = "SOS Um leão na selva está dormindo"

tar_real = "Um leão na selva está dormindo EOS"

O transformador é um modelo auto-regressivo: ele faz previsões uma parte de cada vez e usa sua saída até agora para decidir o que fazer a seguir.

Durante o treino este exemplo usa-professor forçando (como no tutorial geração de texto ). Forçar o professor é passar a saída verdadeira para a próxima etapa de tempo, independentemente do que o modelo prevê na etapa de tempo atual.

Como o transformador prevê cada token, auto-atenção lhe permite olhar para os símbolos anteriores na sequência de entrada para melhor prever o próximo token.

Para evitar que o modelo espie a saída esperada, o modelo usa uma máscara de antecipação.

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))

O português é usado como idioma de entrada e o inglês é o idioma de chegada.

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

Executar inferência

As etapas a seguir são usadas para inferência:

  • Codificar a sentença de entrada usando o tokenizer Português ( tokenizers.pt ). Esta é a entrada do codificador.
  • O descodificador de entrada é inicializado para o [START] token.
  • Calcule as máscaras de preenchimento e as máscaras de previsão.
  • O decoder , em seguida, envia as previsões, olhando para a encoder output e sua própria saída (auto-atenção).
  • Concatene o token previsto para a entrada do decodificador e passe-o para o decodificador.
  • Nesta abordagem, o decodificador prevê o próximo token com base nos tokens anteriores que ele previu.
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

Criar uma instância dessa Translator classe, e experimentá-lo algumas vezes:

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 .

Parcelas de atenção

Os Translator classe retorna um dicionário de atenção mapeia você pode usar para visualizar o funcionamento interno do modelo:

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

O modelo se sai bem com palavras desconhecidas. Nem "triceratops" ou "enciclopédia" estão no conjunto de dados de entrada e o modelo quase aprende a transliterá-los, mesmo sem um vocabulário compartilhado:

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

Exportar

Esse modelo de inferência está funcionando, então ao lado você vai exportá-lo como um tf.saved_model .

Para fazer isso, envolvê-la em outro tf.Module sub-classe, desta vez com um tf.function na __call__ método:

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

No exemplo acima tf.function somente a expressão de saída é retornado. Graças à execução non-estrita em tf.function quaisquer valores desnecessários não são computados.

translator = ExportTranslator(translator)

Desde que o modelo está a descodificar as previsões usando tf.argmax as previsões são deterministas. O modelo original e uma recarregado a partir de sua SavedModel deve dar previsões idênticos:

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 .'

Resumo

Neste tutorial, você aprendeu sobre codificação posicional, atenção de várias cabeças, a importância do mascaramento e como criar um transformador.

Tente usar um conjunto de dados diferente para treinar o transformador. Você também pode criar o transformador de base ou o transformador XL alterando os hiperparâmetros acima. Você também pode usar as camadas definidas aqui para criar BERT e trem estado dos modelos de arte. Além disso, você pode implementar a pesquisa de feixe para obter melhores previsões.