Esta página foi traduzida pela API Cloud Translation.
Switch to English

Modelo de transformador para compreensão da linguagem

Ver no TensorFlow.org Executar no Google Colab Ver fonte no GitHub Baixar caderno

Este tutorial treina um modelo de Transformer para traduzir de português para inglês. Este é um exemplo avançado que pressupõe conhecimento de geração de texto e atenção .

A ideia central por trás do modelo do Transformer é a auto-atenção - a capacidade de atender a diferentes posições da sequência de entrada para calcular uma representação dessa sequência. O Transformer cria pilhas de camadas de autoatenção e é explicado abaixo nas seções Atenção ao produto escalonado 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 os relacionamentos temporais / espaciais entre os dados. Isso é ideal para processar um conjunto de objetos (por exemplo, unidades StarCraft ).
  • As saídas da camada podem ser calculadas em paralelo, em vez de uma série como um RNN.
  • Os itens distantes podem afetar a saída uns dos outros sem passar por muitas etapas RNN ou camadas de convolução (consulte 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 de um intervalo de tempo é calculada a partir de todo o histórico, em vez de apenas as entradas e o estado oculto 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

pip install -q tfds-nightly
WARNING: You are using pip version 20.2.2; however, version 20.2.3 is available.
You should consider upgrading via the '/tmpfs/src/tf_docs_env/bin/python -m pip install --upgrade pip' command.

import tensorflow_datasets as tfds
import tensorflow as tf

import time
import numpy as np
import matplotlib.pyplot as plt

Configurar pipeline de entrada

Use TFDS para carregar o conjunto de dados de tradução português-inglês do projeto de tradução aberta do TED Talks .

Este conjunto de dados contém aproximadamente 50000 exemplos de treinamento, 1100 exemplos de validação e 2000 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']
Downloading and preparing dataset ted_hrlr_translate/pt_to_en/1.0.0 (download: 124.94 MiB, generated: Unknown size, total: 124.94 MiB) to /home/kbuilder/tensorflow_datasets/ted_hrlr_translate/pt_to_en/1.0.0...
Shuffling and writing examples to /home/kbuilder/tensorflow_datasets/ted_hrlr_translate/pt_to_en/1.0.0.incompleteDDYXSP/ted_hrlr_translate-train.tfrecord
Shuffling and writing examples to /home/kbuilder/tensorflow_datasets/ted_hrlr_translate/pt_to_en/1.0.0.incompleteDDYXSP/ted_hrlr_translate-validation.tfrecord
Shuffling and writing examples to /home/kbuilder/tensorflow_datasets/ted_hrlr_translate/pt_to_en/1.0.0.incompleteDDYXSP/ted_hrlr_translate-test.tfrecord
Dataset ted_hrlr_translate downloaded and prepared to /home/kbuilder/tensorflow_datasets/ted_hrlr_translate/pt_to_en/1.0.0. Subsequent calls will reuse this data.

Crie um tokenizer de subwords personalizado a partir do conjunto de dados de treinamento.

tokenizer_en = tfds.deprecated.text.SubwordTextEncoder.build_from_corpus(
    (en.numpy() for pt, en in train_examples), target_vocab_size=2**13)

tokenizer_pt = tfds.deprecated.text.SubwordTextEncoder.build_from_corpus(
    (pt.numpy() for pt, en in train_examples), target_vocab_size=2**13)
sample_string = 'Transformer is awesome.'

tokenized_string = tokenizer_en.encode(sample_string)
print ('Tokenized string is {}'.format(tokenized_string))

original_string = tokenizer_en.decode(tokenized_string)
print ('The original string: {}'.format(original_string))

assert original_string == sample_string
Tokenized string is [7915, 1248, 7946, 7194, 13, 2799, 7877]
The original string: Transformer is awesome.

O tokenizer codifica a string dividindo-a em subpalavras se a palavra não estiver em seu dicionário.

for ts in tokenized_string:
  print ('{} ----> {}'.format(ts, tokenizer_en.decode([ts])))
7915 ----> T
1248 ----> ran
7946 ----> s
7194 ----> former 
13 ----> is 
2799 ----> awesome
7877 ----> .

BUFFER_SIZE = 20000
BATCH_SIZE = 64

Adicione um token de início e fim à entrada e ao destino.

def encode(lang1, lang2):
  lang1 = [tokenizer_pt.vocab_size] + tokenizer_pt.encode(
      lang1.numpy()) + [tokenizer_pt.vocab_size+1]

  lang2 = [tokenizer_en.vocab_size] + tokenizer_en.encode(
      lang2.numpy()) + [tokenizer_en.vocab_size+1]
  
  return lang1, lang2

Você deseja usar Dataset.map para aplicar esta função a cada elemento do conjunto de dados. Dataset.map é executado em modo gráfico.

  • Os tensores do gráfico não têm valor.
  • No modo gráfico, você só pode usar as operações e funções do TensorFlow.

Portanto, você não pode .map esta função diretamente: você precisa envolvê-la em uma função tf.py_function . A função tf.py_function irá passar tensores regulares (com um valor e um método .numpy() para acessá-lo), para a função python empacotada.

def tf_encode(pt, en):
  result_pt, result_en = tf.py_function(encode, [pt, en], [tf.int64, tf.int64])
  result_pt.set_shape([None])
  result_en.set_shape([None])

  return result_pt, result_en
MAX_LENGTH = 40
def filter_max_length(x, y, max_length=MAX_LENGTH):
  return tf.logical_and(tf.size(x) <= max_length,
                        tf.size(y) <= max_length)
train_dataset = train_examples.map(tf_encode)
train_dataset = train_dataset.filter(filter_max_length)
# cache the dataset to memory to get a speedup while reading from it.
train_dataset = train_dataset.cache()
train_dataset = train_dataset.shuffle(BUFFER_SIZE).padded_batch(BATCH_SIZE)
train_dataset = train_dataset.prefetch(tf.data.experimental.AUTOTUNE)


val_dataset = val_examples.map(tf_encode)
val_dataset = val_dataset.filter(filter_max_length).padded_batch(BATCH_SIZE)
pt_batch, en_batch = next(iter(val_dataset))
pt_batch, en_batch
(<tf.Tensor: shape=(64, 38), dtype=int64, numpy=
 array([[8214,  342, 3032, ...,    0,    0,    0],
        [8214,   95,  198, ...,    0,    0,    0],
        [8214, 4479, 7990, ...,    0,    0,    0],
        ...,
        [8214,  584,   12, ...,    0,    0,    0],
        [8214,   59, 1548, ...,    0,    0,    0],
        [8214,  118,   34, ...,    0,    0,    0]])>,
 <tf.Tensor: shape=(64, 40), dtype=int64, numpy=
 array([[8087,   98,   25, ...,    0,    0,    0],
        [8087,   12,   20, ...,    0,    0,    0],
        [8087,   12, 5453, ...,    0,    0,    0],
        ...,
        [8087,   18, 2059, ...,    0,    0,    0],
        [8087,   16, 1436, ...,    0,    0,    0],
        [8087,   15,   57, ...,    0,    0,    0]])>)

Codificação posicional

Como este modelo não contém nenhuma recorrência ou convolução, a codificação posicional é adicionada para fornecer ao modelo algumas informações sobre a posição relativa das palavras 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 das palavras em uma frase. Assim, após adicionar a codificação posicional, as palavras ficarão mais próximas umas das outras com base na similaridade de seu significado e sua posição na frase , no espaço d-dimensional.

Veja o bloco de notas sobre codificação posicional para aprender mais sobre isso. 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)
pos_encoding = positional_encoding(50, 512)
print (pos_encoding.shape)

plt.pcolormesh(pos_encoding[0], cmap='RdBu')
plt.xlabel('Depth')
plt.xlim((0, 512))
plt.ylabel('Position')
plt.colorbar()
plt.show()
(1, 50, 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 de pad 0 está presente: ela produz 1 nesses locais e 0 caso 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 a terceira palavra, apenas a primeira e a segunda palavras serão usadas. Da mesma forma, para prever a quarta palavra, apenas a primeira, a segunda e a terceira palavras serão usadas 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(\frac{QK^T}{\sqrt{d_k} }) 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 duro.

Por exemplo, considere que Q e K têm média 0 e variância 1. Sua multiplicação de matrizes terá média 0 e variância dk . Portanto, a raiz quadrada de dk é usada para dimensionar (e não qualquer outro número) porque o matmul de Q e K deve ter uma média de 0 e variância de 1, e você obtém um softmax mais suave.

A máscara é multiplicada por -1e9 (próximo ao infinito negativo). Isso é feito porque a máscara é somada com a 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 as palavras nas quais você deseja se concentrar sejam mantidas como estão e que as palavras irrelevantes sejam eliminadas.

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 e divididas em cabeças.
  • Atenção ao produto escalar em escala.
  • Concatenação de cabeças.
  • 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) e divididos em várias cabeças.

O scaled_dot_product_attention definido acima é aplicado a cada cabeçote (transmitido para eficiência). Uma máscara apropriada deve ser usada na etapa de atenção. A saída de atenção para cada cabeça é então concatenada (usando tf.transpose e tf.reshape ) e tf.reshape por uma camada Dense final.

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 a informações em diferentes posições de diferentes espaços representacionais. 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

Crie uma camada MultiHeadAttention para experimentar. Em cada local na sequência, y , o MultiHeadAttention executa todas as 8 cabeças de atenção em todos os outros locais na sequê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 ponto a ponto de alimentação direta 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 do transformador segue o mesmo padrão geral que o modelo de sequência padrão para sequência com atenção .

  • A frase de entrada é passada através de N camadas de codificador que geram uma saída para cada palavra / token 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 subcamada é LayerNorm(x + Sublayer(x)) . A normalização é feita no eixo d_model (último). 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 visualização e máscara de preenchimento)
  2. Atenção multi-cabeça (com máscara de enchimento). V (valor) e K (chave) recebem a saída do encoder como entradas. Q (consulta) recebe a saída da subcamada de atenção com várias cabeças mascaradas.
  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 subcamada é LayerNorm(x + Sublayer(x)) . A normalização é feita no eixo d_model (último).

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ê a próxima palavra olhando para a saída do codificador e autoatendendo 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. N camadas de codificador

A entrada é submetida a uma incorporação que é somada à codificação posicional. A saída deste 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 em:

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

O destino é submetido a uma incorporação que se soma à codificação posicional. A saída desta soma é 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['decoder_layer{}_block1'.format(i+1)] = block1
      attention_weights['decoder_layer{}_block2'.format(i+1)] = 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 transformador

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(Transformer, self).__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, inp, tar, training, enc_padding_mask, 
           look_ahead_mask, dec_padding_mask):

    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
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, 
                               enc_padding_mask=None, 
                               look_ahead_mask=None,
                               dec_padding_mask=None)

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 de num_layers, d_model e dff foram reduzidos.

Os valores usados ​​no modelo básico do transformador foram; num_camadas = 6 , d_modelo = 512 , dff = 2048 . Veja o artigo para todas as outras versões do transformador.

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

input_vocab_size = tokenizer_pt.vocab_size + 2
target_vocab_size = tokenizer_en.vocab_size + 2
dropout_rate = 0.1

Otimizador

Use o otimizador Adam com um programador de taxa de aprendizado personalizado de acordo com a fórmula do artigo .

$$\Large{lrate = d_{model}^{-0.5} * min(step{\_}num^{-0.5}, step{\_}num * 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)
train_loss = tf.keras.metrics.Mean(name='train_loss')
train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(
    name='train_accuracy')

Treinamento e verificação

transformer = Transformer(num_layers, d_model, num_heads, dff,
                          input_vocab_size, target_vocab_size, 
                          pe_input=input_vocab_size, 
                          pe_target=target_vocab_size,
                          rate=dropout_rate)
def create_masks(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)
  combined_mask = tf.maximum(dec_target_padding_mask, look_ahead_mask)
  
  return enc_padding_mask, combined_mask, dec_padding_mask

Crie o caminho do ponto de verificação e o gerenciador de ponto de verificação. Isso será usado para salvar pontos de verificação 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 é a mesma entrada deslocada por 1: Em cada local em tar_input , tar_real contém o próximo token que deve ser previsto.

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 treinamento, este exemplo usa a força do professor (como no tutorial de geração de texto ). Forçar o professor é passar a saída verdadeira para a próxima etapa, independentemente do que o modelo prevê na etapa atual.

À medida que o transformador prevê cada palavra, a autoatenção permite que ele examine as palavras anteriores na sequência de entrada para prever melhor a próxima palavra.

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:]
  
  enc_padding_mask, combined_mask, dec_padding_mask = create_masks(inp, tar_inp)
  
  with tf.GradientTape() as tape:
    predictions, _ = transformer(inp, tar_inp, 
                                 True, 
                                 enc_padding_mask, 
                                 combined_mask, 
                                 dec_padding_mask)
    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(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_dataset):
    train_step(inp, tar)
    
    if batch % 50 == 0:
      print ('Epoch {} Batch {} Loss {:.4f} Accuracy {:.4f}'.format(
          epoch + 1, batch, train_loss.result(), train_accuracy.result()))
      
  if (epoch + 1) % 5 == 0:
    ckpt_save_path = ckpt_manager.save()
    print ('Saving checkpoint for epoch {} at {}'.format(epoch+1,
                                                         ckpt_save_path))
    
  print ('Epoch {} Loss {:.4f} Accuracy {:.4f}'.format(epoch + 1, 
                                                train_loss.result(), 
                                                train_accuracy.result()))

  print ('Time taken for 1 epoch: {} secs\n'.format(time.time() - start))
Epoch 1 Batch 0 Loss 9.0005 Accuracy 0.0000
Epoch 1 Batch 50 Loss 8.9530 Accuracy 0.0007
Epoch 1 Batch 100 Loss 8.8622 Accuracy 0.0115
Epoch 1 Batch 150 Loss 8.7592 Accuracy 0.0167
Epoch 1 Batch 200 Loss 8.6354 Accuracy 0.0193
Epoch 1 Batch 250 Loss 8.4868 Accuracy 0.0216
Epoch 1 Batch 300 Loss 8.3156 Accuracy 0.0240
Epoch 1 Batch 350 Loss 8.1325 Accuracy 0.0283
Epoch 1 Batch 400 Loss 7.9531 Accuracy 0.0318
Epoch 1 Batch 450 Loss 7.7899 Accuracy 0.0348
Epoch 1 Batch 500 Loss 7.6434 Accuracy 0.0380
Epoch 1 Batch 550 Loss 7.5091 Accuracy 0.0415
Epoch 1 Batch 600 Loss 7.3815 Accuracy 0.0452
Epoch 1 Batch 650 Loss 7.2589 Accuracy 0.0487
Epoch 1 Batch 700 Loss 7.1422 Accuracy 0.0522
Epoch 1 Loss 7.1379 Accuracy 0.0523
Time taken for 1 epoch: 55.227755069732666 secs

Epoch 2 Batch 0 Loss 5.5778 Accuracy 0.0921
Epoch 2 Batch 50 Loss 5.4727 Accuracy 0.1035
Epoch 2 Batch 100 Loss 5.4241 Accuracy 0.1061
Epoch 2 Batch 150 Loss 5.3629 Accuracy 0.1087
Epoch 2 Batch 200 Loss 5.3115 Accuracy 0.1104
Epoch 2 Batch 250 Loss 5.2689 Accuracy 0.1126
Epoch 2 Batch 300 Loss 5.2314 Accuracy 0.1142
Epoch 2 Batch 350 Loss 5.1934 Accuracy 0.1159
Epoch 2 Batch 400 Loss 5.1581 Accuracy 0.1177
Epoch 2 Batch 450 Loss 5.1253 Accuracy 0.1192
Epoch 2 Batch 500 Loss 5.0975 Accuracy 0.1204
Epoch 2 Batch 550 Loss 5.0690 Accuracy 0.1218
Epoch 2 Batch 600 Loss 5.0440 Accuracy 0.1229
Epoch 2 Batch 650 Loss 5.0206 Accuracy 0.1243
Epoch 2 Batch 700 Loss 4.9982 Accuracy 0.1253
Epoch 2 Loss 4.9975 Accuracy 0.1254
Time taken for 1 epoch: 29.222673654556274 secs

Epoch 3 Batch 0 Loss 4.7296 Accuracy 0.1233
Epoch 3 Batch 50 Loss 4.5995 Accuracy 0.1418
Epoch 3 Batch 100 Loss 4.5976 Accuracy 0.1424
Epoch 3 Batch 150 Loss 4.5859 Accuracy 0.1431
Epoch 3 Batch 200 Loss 4.5728 Accuracy 0.1437
Epoch 3 Batch 250 Loss 4.5634 Accuracy 0.1444
Epoch 3 Batch 300 Loss 4.5507 Accuracy 0.1450
Epoch 3 Batch 350 Loss 4.5348 Accuracy 0.1464
Epoch 3 Batch 400 Loss 4.5219 Accuracy 0.1471
Epoch 3 Batch 450 Loss 4.5066 Accuracy 0.1480
Epoch 3 Batch 500 Loss 4.4918 Accuracy 0.1488
Epoch 3 Batch 550 Loss 4.4763 Accuracy 0.1497
Epoch 3 Batch 600 Loss 4.4627 Accuracy 0.1507
Epoch 3 Batch 650 Loss 4.4462 Accuracy 0.1517
Epoch 3 Batch 700 Loss 4.4327 Accuracy 0.1525
Epoch 3 Loss 4.4321 Accuracy 0.1525
Time taken for 1 epoch: 29.389445304870605 secs

Epoch 4 Batch 0 Loss 4.3032 Accuracy 0.1653
Epoch 4 Batch 50 Loss 4.0700 Accuracy 0.1695
Epoch 4 Batch 100 Loss 4.0660 Accuracy 0.1691
Epoch 4 Batch 150 Loss 4.0641 Accuracy 0.1704
Epoch 4 Batch 200 Loss 4.0517 Accuracy 0.1713
Epoch 4 Batch 250 Loss 4.0392 Accuracy 0.1725
Epoch 4 Batch 300 Loss 4.0218 Accuracy 0.1738
Epoch 4 Batch 350 Loss 4.0098 Accuracy 0.1747
Epoch 4 Batch 400 Loss 3.9930 Accuracy 0.1758
Epoch 4 Batch 450 Loss 3.9805 Accuracy 0.1766
Epoch 4 Batch 500 Loss 3.9684 Accuracy 0.1779
Epoch 4 Batch 550 Loss 3.9550 Accuracy 0.1788
Epoch 4 Batch 600 Loss 3.9410 Accuracy 0.1796
Epoch 4 Batch 650 Loss 3.9266 Accuracy 0.1806
Epoch 4 Batch 700 Loss 3.9129 Accuracy 0.1815
Epoch 4 Loss 3.9129 Accuracy 0.1815
Time taken for 1 epoch: 29.373191595077515 secs

Epoch 5 Batch 0 Loss 3.6592 Accuracy 0.2146
Epoch 5 Batch 50 Loss 3.5730 Accuracy 0.1983
Epoch 5 Batch 100 Loss 3.5666 Accuracy 0.2006
Epoch 5 Batch 150 Loss 3.5539 Accuracy 0.2003
Epoch 5 Batch 200 Loss 3.5464 Accuracy 0.2005
Epoch 5 Batch 250 Loss 3.5414 Accuracy 0.2011
Epoch 5 Batch 300 Loss 3.5311 Accuracy 0.2019
Epoch 5 Batch 350 Loss 3.5189 Accuracy 0.2023
Epoch 5 Batch 400 Loss 3.5088 Accuracy 0.2033
Epoch 5 Batch 450 Loss 3.4989 Accuracy 0.2044
Epoch 5 Batch 500 Loss 3.4944 Accuracy 0.2050
Epoch 5 Batch 550 Loss 3.4835 Accuracy 0.2058
Epoch 5 Batch 600 Loss 3.4762 Accuracy 0.2063
Epoch 5 Batch 650 Loss 3.4692 Accuracy 0.2069
Epoch 5 Batch 700 Loss 3.4570 Accuracy 0.2076
Saving checkpoint for epoch 5 at ./checkpoints/train/ckpt-1
Epoch 5 Loss 3.4570 Accuracy 0.2076
Time taken for 1 epoch: 29.62894082069397 secs

Epoch 6 Batch 0 Loss 3.0676 Accuracy 0.2079
Epoch 6 Batch 50 Loss 3.1464 Accuracy 0.2222
Epoch 6 Batch 100 Loss 3.1415 Accuracy 0.2224
Epoch 6 Batch 150 Loss 3.1251 Accuracy 0.2233
Epoch 6 Batch 200 Loss 3.1303 Accuracy 0.2233
Epoch 6 Batch 250 Loss 3.1271 Accuracy 0.2240
Epoch 6 Batch 300 Loss 3.1222 Accuracy 0.2243
Epoch 6 Batch 350 Loss 3.1155 Accuracy 0.2245
Epoch 6 Batch 400 Loss 3.1114 Accuracy 0.2252
Epoch 6 Batch 450 Loss 3.1075 Accuracy 0.2251
Epoch 6 Batch 500 Loss 3.1011 Accuracy 0.2256
Epoch 6 Batch 550 Loss 3.0944 Accuracy 0.2256
Epoch 6 Batch 600 Loss 3.0865 Accuracy 0.2259
Epoch 6 Batch 650 Loss 3.0789 Accuracy 0.2264
Epoch 6 Batch 700 Loss 3.0734 Accuracy 0.2272
Epoch 6 Loss 3.0726 Accuracy 0.2272
Time taken for 1 epoch: 29.619795560836792 secs

Epoch 7 Batch 0 Loss 2.6502 Accuracy 0.2699
Epoch 7 Batch 50 Loss 2.7479 Accuracy 0.2450
Epoch 7 Batch 100 Loss 2.7388 Accuracy 0.2455
Epoch 7 Batch 150 Loss 2.7329 Accuracy 0.2451
Epoch 7 Batch 200 Loss 2.7345 Accuracy 0.2455
Epoch 7 Batch 250 Loss 2.7379 Accuracy 0.2450
Epoch 7 Batch 300 Loss 2.7314 Accuracy 0.2457
Epoch 7 Batch 350 Loss 2.7256 Accuracy 0.2456
Epoch 7 Batch 400 Loss 2.7182 Accuracy 0.2467
Epoch 7 Batch 450 Loss 2.7106 Accuracy 0.2470
Epoch 7 Batch 500 Loss 2.7048 Accuracy 0.2477
Epoch 7 Batch 550 Loss 2.7012 Accuracy 0.2481
Epoch 7 Batch 600 Loss 2.6969 Accuracy 0.2483
Epoch 7 Batch 650 Loss 2.6922 Accuracy 0.2488
Epoch 7 Batch 700 Loss 2.6878 Accuracy 0.2494
Epoch 7 Loss 2.6879 Accuracy 0.2494
Time taken for 1 epoch: 29.344128608703613 secs

Epoch 8 Batch 0 Loss 2.0878 Accuracy 0.2922
Epoch 8 Batch 50 Loss 2.3954 Accuracy 0.2664
Epoch 8 Batch 100 Loss 2.3886 Accuracy 0.2660
Epoch 8 Batch 150 Loss 2.3882 Accuracy 0.2647
Epoch 8 Batch 200 Loss 2.3828 Accuracy 0.2647
Epoch 8 Batch 250 Loss 2.3849 Accuracy 0.2643
Epoch 8 Batch 300 Loss 2.3867 Accuracy 0.2639
Epoch 8 Batch 350 Loss 2.3880 Accuracy 0.2644
Epoch 8 Batch 400 Loss 2.3824 Accuracy 0.2650
Epoch 8 Batch 450 Loss 2.3813 Accuracy 0.2644
Epoch 8 Batch 500 Loss 2.3770 Accuracy 0.2649
Epoch 8 Batch 550 Loss 2.3748 Accuracy 0.2654
Epoch 8 Batch 600 Loss 2.3735 Accuracy 0.2656
Epoch 8 Batch 650 Loss 2.3718 Accuracy 0.2657
Epoch 8 Batch 700 Loss 2.3706 Accuracy 0.2661
Epoch 8 Loss 2.3704 Accuracy 0.2661
Time taken for 1 epoch: 29.539512157440186 secs

Epoch 9 Batch 0 Loss 2.3050 Accuracy 0.2683
Epoch 9 Batch 50 Loss 2.1005 Accuracy 0.2827
Epoch 9 Batch 100 Loss 2.1171 Accuracy 0.2826
Epoch 9 Batch 150 Loss 2.1191 Accuracy 0.2819
Epoch 9 Batch 200 Loss 2.1282 Accuracy 0.2807
Epoch 9 Batch 250 Loss 2.1299 Accuracy 0.2799
Epoch 9 Batch 300 Loss 2.1313 Accuracy 0.2804
Epoch 9 Batch 350 Loss 2.1334 Accuracy 0.2800
Epoch 9 Batch 400 Loss 2.1306 Accuracy 0.2803
Epoch 9 Batch 450 Loss 2.1340 Accuracy 0.2806
Epoch 9 Batch 500 Loss 2.1366 Accuracy 0.2805
Epoch 9 Batch 550 Loss 2.1365 Accuracy 0.2802
Epoch 9 Batch 600 Loss 2.1366 Accuracy 0.2802
Epoch 9 Batch 650 Loss 2.1381 Accuracy 0.2800
Epoch 9 Batch 700 Loss 2.1414 Accuracy 0.2801
Epoch 9 Loss 2.1417 Accuracy 0.2801
Time taken for 1 epoch: 29.506006717681885 secs

Epoch 10 Batch 0 Loss 1.7658 Accuracy 0.3218
Epoch 10 Batch 50 Loss 1.9077 Accuracy 0.2935
Epoch 10 Batch 100 Loss 1.9111 Accuracy 0.2916
Epoch 10 Batch 150 Loss 1.9228 Accuracy 0.2912
Epoch 10 Batch 200 Loss 1.9319 Accuracy 0.2899
Epoch 10 Batch 250 Loss 1.9373 Accuracy 0.2894
Epoch 10 Batch 300 Loss 1.9431 Accuracy 0.2894
Epoch 10 Batch 350 Loss 1.9470 Accuracy 0.2897
Epoch 10 Batch 400 Loss 1.9526 Accuracy 0.2898
Epoch 10 Batch 450 Loss 1.9540 Accuracy 0.2904
Epoch 10 Batch 500 Loss 1.9562 Accuracy 0.2904
Epoch 10 Batch 550 Loss 1.9577 Accuracy 0.2905
Epoch 10 Batch 600 Loss 1.9618 Accuracy 0.2907
Epoch 10 Batch 650 Loss 1.9647 Accuracy 0.2907
Epoch 10 Batch 700 Loss 1.9671 Accuracy 0.2905
Saving checkpoint for epoch 10 at ./checkpoints/train/ckpt-2
Epoch 10 Loss 1.9671 Accuracy 0.2906
Time taken for 1 epoch: 30.555190324783325 secs

Epoch 11 Batch 0 Loss 1.8220 Accuracy 0.2686
Epoch 11 Batch 50 Loss 1.7587 Accuracy 0.3028
Epoch 11 Batch 100 Loss 1.7730 Accuracy 0.3025
Epoch 11 Batch 150 Loss 1.7838 Accuracy 0.3021
Epoch 11 Batch 200 Loss 1.7953 Accuracy 0.3013
Epoch 11 Batch 250 Loss 1.7928 Accuracy 0.3009
Epoch 11 Batch 300 Loss 1.7985 Accuracy 0.2998
Epoch 11 Batch 350 Loss 1.8009 Accuracy 0.2997
Epoch 11 Batch 400 Loss 1.8098 Accuracy 0.2989
Epoch 11 Batch 450 Loss 1.8142 Accuracy 0.2989
Epoch 11 Batch 500 Loss 1.8169 Accuracy 0.2988
Epoch 11 Batch 550 Loss 1.8204 Accuracy 0.2987
Epoch 11 Batch 600 Loss 1.8239 Accuracy 0.2988
Epoch 11 Batch 650 Loss 1.8265 Accuracy 0.2989
Epoch 11 Batch 700 Loss 1.8288 Accuracy 0.2986
Epoch 11 Loss 1.8295 Accuracy 0.2986
Time taken for 1 epoch: 29.374881982803345 secs

Epoch 12 Batch 0 Loss 1.5971 Accuracy 0.3350
Epoch 12 Batch 50 Loss 1.6420 Accuracy 0.3110
Epoch 12 Batch 100 Loss 1.6574 Accuracy 0.3081
Epoch 12 Batch 150 Loss 1.6558 Accuracy 0.3081
Epoch 12 Batch 200 Loss 1.6598 Accuracy 0.3072
Epoch 12 Batch 250 Loss 1.6686 Accuracy 0.3080
Epoch 12 Batch 300 Loss 1.6677 Accuracy 0.3088
Epoch 12 Batch 350 Loss 1.6746 Accuracy 0.3083
Epoch 12 Batch 400 Loss 1.6796 Accuracy 0.3079
Epoch 12 Batch 450 Loss 1.6871 Accuracy 0.3072
Epoch 12 Batch 500 Loss 1.6933 Accuracy 0.3067
Epoch 12 Batch 550 Loss 1.6976 Accuracy 0.3065
Epoch 12 Batch 600 Loss 1.7026 Accuracy 0.3063
Epoch 12 Batch 650 Loss 1.7077 Accuracy 0.3066
Epoch 12 Batch 700 Loss 1.7117 Accuracy 0.3065
Epoch 12 Loss 1.7118 Accuracy 0.3065
Time taken for 1 epoch: 29.305912971496582 secs

Epoch 13 Batch 0 Loss 1.6015 Accuracy 0.3080
Epoch 13 Batch 50 Loss 1.5318 Accuracy 0.3176
Epoch 13 Batch 100 Loss 1.5372 Accuracy 0.3193
Epoch 13 Batch 150 Loss 1.5449 Accuracy 0.3186
Epoch 13 Batch 200 Loss 1.5585 Accuracy 0.3175
Epoch 13 Batch 250 Loss 1.5678 Accuracy 0.3173
Epoch 13 Batch 300 Loss 1.5719 Accuracy 0.3165
Epoch 13 Batch 350 Loss 1.5787 Accuracy 0.3162
Epoch 13 Batch 400 Loss 1.5831 Accuracy 0.3153
Epoch 13 Batch 450 Loss 1.5898 Accuracy 0.3153
Epoch 13 Batch 500 Loss 1.5935 Accuracy 0.3144
Epoch 13 Batch 550 Loss 1.5990 Accuracy 0.3145
Epoch 13 Batch 600 Loss 1.6024 Accuracy 0.3138
Epoch 13 Batch 650 Loss 1.6086 Accuracy 0.3134
Epoch 13 Batch 700 Loss 1.6109 Accuracy 0.3133
Epoch 13 Loss 1.6110 Accuracy 0.3133
Time taken for 1 epoch: 29.24092197418213 secs

Epoch 14 Batch 0 Loss 1.3890 Accuracy 0.2952
Epoch 14 Batch 50 Loss 1.4290 Accuracy 0.3250
Epoch 14 Batch 100 Loss 1.4379 Accuracy 0.3220
Epoch 14 Batch 150 Loss 1.4559 Accuracy 0.3212
Epoch 14 Batch 200 Loss 1.4676 Accuracy 0.3202
Epoch 14 Batch 250 Loss 1.4760 Accuracy 0.3207
Epoch 14 Batch 300 Loss 1.4885 Accuracy 0.3205
Epoch 14 Batch 350 Loss 1.4909 Accuracy 0.3211
Epoch 14 Batch 400 Loss 1.4946 Accuracy 0.3208
Epoch 14 Batch 450 Loss 1.5006 Accuracy 0.3207
Epoch 14 Batch 500 Loss 1.5077 Accuracy 0.3201
Epoch 14 Batch 550 Loss 1.5108 Accuracy 0.3199
Epoch 14 Batch 600 Loss 1.5153 Accuracy 0.3195
Epoch 14 Batch 650 Loss 1.5197 Accuracy 0.3195
Epoch 14 Batch 700 Loss 1.5246 Accuracy 0.3190
Epoch 14 Loss 1.5245 Accuracy 0.3190
Time taken for 1 epoch: 29.347501516342163 secs

Epoch 15 Batch 0 Loss 1.4946 Accuracy 0.3085
Epoch 15 Batch 50 Loss 1.3737 Accuracy 0.3301
Epoch 15 Batch 100 Loss 1.3672 Accuracy 0.3284
Epoch 15 Batch 150 Loss 1.3800 Accuracy 0.3283
Epoch 15 Batch 200 Loss 1.3942 Accuracy 0.3271
Epoch 15 Batch 250 Loss 1.3988 Accuracy 0.3272
Epoch 15 Batch 300 Loss 1.4014 Accuracy 0.3269
Epoch 15 Batch 350 Loss 1.4094 Accuracy 0.3262
Epoch 15 Batch 400 Loss 1.4166 Accuracy 0.3257
Epoch 15 Batch 450 Loss 1.4220 Accuracy 0.3255
Epoch 15 Batch 500 Loss 1.4285 Accuracy 0.3256
Epoch 15 Batch 550 Loss 1.4322 Accuracy 0.3253
Epoch 15 Batch 600 Loss 1.4377 Accuracy 0.3249
Epoch 15 Batch 650 Loss 1.4446 Accuracy 0.3244
Epoch 15 Batch 700 Loss 1.4488 Accuracy 0.3238
Saving checkpoint for epoch 15 at ./checkpoints/train/ckpt-3
Epoch 15 Loss 1.4488 Accuracy 0.3239
Time taken for 1 epoch: 29.638850927352905 secs

Epoch 16 Batch 0 Loss 1.1991 Accuracy 0.3530
Epoch 16 Batch 50 Loss 1.2971 Accuracy 0.3314
Epoch 16 Batch 100 Loss 1.3096 Accuracy 0.3355
Epoch 16 Batch 150 Loss 1.3160 Accuracy 0.3330
Epoch 16 Batch 200 Loss 1.3200 Accuracy 0.3330
Epoch 16 Batch 250 Loss 1.3260 Accuracy 0.3330
Epoch 16 Batch 300 Loss 1.3348 Accuracy 0.3321
Epoch 16 Batch 350 Loss 1.3402 Accuracy 0.3320
Epoch 16 Batch 400 Loss 1.3455 Accuracy 0.3312
Epoch 16 Batch 450 Loss 1.3533 Accuracy 0.3306
Epoch 16 Batch 500 Loss 1.3593 Accuracy 0.3302
Epoch 16 Batch 550 Loss 1.3660 Accuracy 0.3297
Epoch 16 Batch 600 Loss 1.3730 Accuracy 0.3296
Epoch 16 Batch 650 Loss 1.3783 Accuracy 0.3289
Epoch 16 Batch 700 Loss 1.3819 Accuracy 0.3285
Epoch 16 Loss 1.3820 Accuracy 0.3285
Time taken for 1 epoch: 29.525911331176758 secs

Epoch 17 Batch 0 Loss 1.3080 Accuracy 0.3113
Epoch 17 Batch 50 Loss 1.2531 Accuracy 0.3417
Epoch 17 Batch 100 Loss 1.2428 Accuracy 0.3396
Epoch 17 Batch 150 Loss 1.2567 Accuracy 0.3377
Epoch 17 Batch 200 Loss 1.2654 Accuracy 0.3366
Epoch 17 Batch 250 Loss 1.2703 Accuracy 0.3362
Epoch 17 Batch 300 Loss 1.2797 Accuracy 0.3349
Epoch 17 Batch 350 Loss 1.2865 Accuracy 0.3339
Epoch 17 Batch 400 Loss 1.2911 Accuracy 0.3340
Epoch 17 Batch 450 Loss 1.2950 Accuracy 0.3343
Epoch 17 Batch 500 Loss 1.3002 Accuracy 0.3339
Epoch 17 Batch 550 Loss 1.3051 Accuracy 0.3337
Epoch 17 Batch 600 Loss 1.3113 Accuracy 0.3334
Epoch 17 Batch 650 Loss 1.3173 Accuracy 0.3331
Epoch 17 Batch 700 Loss 1.3208 Accuracy 0.3332
Epoch 17 Loss 1.3207 Accuracy 0.3332
Time taken for 1 epoch: 29.23569130897522 secs

Epoch 18 Batch 0 Loss 1.0522 Accuracy 0.3385
Epoch 18 Batch 50 Loss 1.1916 Accuracy 0.3437
Epoch 18 Batch 100 Loss 1.1853 Accuracy 0.3432
Epoch 18 Batch 150 Loss 1.1994 Accuracy 0.3435
Epoch 18 Batch 200 Loss 1.2120 Accuracy 0.3424
Epoch 18 Batch 250 Loss 1.2183 Accuracy 0.3406
Epoch 18 Batch 300 Loss 1.2244 Accuracy 0.3403
Epoch 18 Batch 350 Loss 1.2316 Accuracy 0.3405
Epoch 18 Batch 400 Loss 1.2376 Accuracy 0.3396
Epoch 18 Batch 450 Loss 1.2428 Accuracy 0.3395
Epoch 18 Batch 500 Loss 1.2479 Accuracy 0.3394
Epoch 18 Batch 550 Loss 1.2537 Accuracy 0.3390
Epoch 18 Batch 600 Loss 1.2605 Accuracy 0.3380
Epoch 18 Batch 650 Loss 1.2669 Accuracy 0.3376
Epoch 18 Batch 700 Loss 1.2721 Accuracy 0.3369
Epoch 18 Loss 1.2723 Accuracy 0.3368
Time taken for 1 epoch: 29.23801350593567 secs

Epoch 19 Batch 0 Loss 1.0786 Accuracy 0.3205
Epoch 19 Batch 50 Loss 1.1217 Accuracy 0.3470
Epoch 19 Batch 100 Loss 1.1432 Accuracy 0.3462
Epoch 19 Batch 150 Loss 1.1478 Accuracy 0.3463
Epoch 19 Batch 200 Loss 1.1571 Accuracy 0.3463
Epoch 19 Batch 250 Loss 1.1640 Accuracy 0.3453
Epoch 19 Batch 300 Loss 1.1716 Accuracy 0.3451
Epoch 19 Batch 350 Loss 1.1778 Accuracy 0.3445
Epoch 19 Batch 400 Loss 1.1814 Accuracy 0.3428
Epoch 19 Batch 450 Loss 1.1885 Accuracy 0.3431
Epoch 19 Batch 500 Loss 1.1956 Accuracy 0.3424
Epoch 19 Batch 550 Loss 1.2013 Accuracy 0.3424
Epoch 19 Batch 600 Loss 1.2084 Accuracy 0.3419
Epoch 19 Batch 650 Loss 1.2147 Accuracy 0.3413
Epoch 19 Batch 700 Loss 1.2212 Accuracy 0.3407
Epoch 19 Loss 1.2213 Accuracy 0.3407
Time taken for 1 epoch: 29.36856746673584 secs

Epoch 20 Batch 0 Loss 1.1117 Accuracy 0.3153
Epoch 20 Batch 50 Loss 1.0757 Accuracy 0.3482
Epoch 20 Batch 100 Loss 1.0988 Accuracy 0.3480
Epoch 20 Batch 150 Loss 1.1089 Accuracy 0.3480
Epoch 20 Batch 200 Loss 1.1157 Accuracy 0.3472
Epoch 20 Batch 250 Loss 1.1256 Accuracy 0.3463
Epoch 20 Batch 300 Loss 1.1314 Accuracy 0.3468
Epoch 20 Batch 350 Loss 1.1376 Accuracy 0.3456
Epoch 20 Batch 400 Loss 1.1419 Accuracy 0.3456
Epoch 20 Batch 450 Loss 1.1479 Accuracy 0.3451
Epoch 20 Batch 500 Loss 1.1528 Accuracy 0.3453
Epoch 20 Batch 550 Loss 1.1580 Accuracy 0.3447
Epoch 20 Batch 600 Loss 1.1657 Accuracy 0.3447
Epoch 20 Batch 650 Loss 1.1710 Accuracy 0.3442
Epoch 20 Batch 700 Loss 1.1765 Accuracy 0.3440
Saving checkpoint for epoch 20 at ./checkpoints/train/ckpt-4
Epoch 20 Loss 1.1769 Accuracy 0.3440
Time taken for 1 epoch: 29.240405321121216 secs


Avalie

As seguintes etapas são usadas para avaliação:

  • Codifique a frase de entrada usando o tokenizer português ( tokenizer_pt ). Além disso, adicione o token inicial e final para que a entrada seja equivalente àquela com a qual o modelo é treinado. Esta é a entrada do codificador.
  • A entrada do decodificador é o start token == tokenizer_en.vocab_size .
  • Calcule as máscaras de preenchimento e as máscaras de previsão.
  • O decoder então produz as previsões olhando para a encoder output do encoder output e sua própria saída (autoatenção).
  • Selecione a última palavra e calcule o argmax dela.
  • Concatente a palavra prevista para a entrada do decodificador e passe-a para o decodificador.
  • Nesta abordagem, o decodificador prevê a próxima palavra com base nas palavras anteriores que ele previu.
def evaluate(inp_sentence):
  start_token = [tokenizer_pt.vocab_size]
  end_token = [tokenizer_pt.vocab_size + 1]
  
  # inp sentence is portuguese, hence adding the start and end token
  inp_sentence = start_token + tokenizer_pt.encode(inp_sentence) + end_token
  encoder_input = tf.expand_dims(inp_sentence, 0)
  
  # as the target is english, the first word to the transformer should be the
  # english start token.
  decoder_input = [tokenizer_en.vocab_size]
  output = tf.expand_dims(decoder_input, 0)
    
  for i in range(MAX_LENGTH):
    enc_padding_mask, combined_mask, dec_padding_mask = create_masks(
        encoder_input, output)
  
    # predictions.shape == (batch_size, seq_len, vocab_size)
    predictions, attention_weights = transformer(encoder_input, 
                                                 output,
                                                 False,
                                                 enc_padding_mask,
                                                 combined_mask,
                                                 dec_padding_mask)
    
    # select the last word from the seq_len dimension
    predictions = predictions[: ,-1:, :]  # (batch_size, 1, vocab_size)

    predicted_id = tf.cast(tf.argmax(predictions, axis=-1), tf.int32)
    
    # return the result if the predicted_id is equal to the end token
    if predicted_id == tokenizer_en.vocab_size+1:
      return tf.squeeze(output, axis=0), attention_weights
    
    # concatentate the predicted_id to the output which is given to the decoder
    # as its input.
    output = tf.concat([output, predicted_id], axis=-1)

  return tf.squeeze(output, axis=0), attention_weights
def plot_attention_weights(attention, sentence, result, layer):
  fig = plt.figure(figsize=(16, 8))
  
  sentence = tokenizer_pt.encode(sentence)
  
  attention = tf.squeeze(attention[layer], axis=0)
  
  for head in range(attention.shape[0]):
    ax = fig.add_subplot(2, 4, head+1)
    
    # plot the attention weights
    ax.matshow(attention[head][:-1, :], cmap='viridis')

    fontdict = {'fontsize': 10}
    
    ax.set_xticks(range(len(sentence)+2))
    ax.set_yticks(range(len(result)))
    
    ax.set_ylim(len(result)-1.5, -0.5)
        
    ax.set_xticklabels(
        ['<start>']+[tokenizer_pt.decode([i]) for i in sentence]+['<end>'], 
        fontdict=fontdict, rotation=90)
    
    ax.set_yticklabels([tokenizer_en.decode([i]) for i in result 
                        if i < tokenizer_en.vocab_size], 
                       fontdict=fontdict)
    
    ax.set_xlabel('Head {}'.format(head+1))
  
  plt.tight_layout()
  plt.show()
def translate(sentence, plot=''):
  result, attention_weights = evaluate(sentence)
  
  predicted_sentence = tokenizer_en.decode([i for i in result 
                                            if i < tokenizer_en.vocab_size])  

  print('Input: {}'.format(sentence))
  print('Predicted translation: {}'.format(predicted_sentence))
  
  if plot:
    plot_attention_weights(attention_weights, sentence, result, plot)
translate("este é um problema que temos que resolver.")
print ("Real translation: this is a problem we have to solve .")
Input: este é um problema que temos que resolver.
Predicted translation: this is a problem we have to contain ....
Real translation: this is a problem we have to solve .

translate("os meus vizinhos ouviram sobre esta ideia.")
print ("Real translation: and my neighboring homes heard about this idea .")
Input: os meus vizinhos ouviram sobre esta ideia.
Predicted translation: my neighbors heard about this idea .
Real translation: and my neighboring homes heard about this idea .

translate("vou então muito rapidamente partilhar convosco algumas histórias de algumas coisas mágicas que aconteceram.")
print ("Real translation: so i 'll just share with you some stories very quickly of some magical things that have happened .")
Input: vou então muito rapidamente partilhar convosco algumas histórias de algumas coisas mágicas que aconteceram.
Predicted translation: so i 'm very quickly to share with you some magic stories that had happened .
Real translation: so i 'll just share with you some stories very quickly of some magical things that have happened .

Você pode passar diferentes camadas e blocos de atenção do decodificador para o parâmetro de plot .

translate("este é o primeiro livro que eu fiz.", plot='decoder_layer4_block2')
print ("Real translation: this is the first book i've ever done.")
Input: este é o primeiro livro que eu fiz.
Predicted translation: this is the first book i did have .

png

Real translation: this is the first book i've ever done.

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 treinar modelos de última geração. Além disso, você pode implementar a pesquisa de feixe para obter melhores previsões.