Um guia para usar transformadores com TensorFlow para geração de legenda

Conteúdo

Visão geral

  • Aprendendo sobre o modelo de ponta que são os Transformers.
  • Por favor, entenda como podemos implementar Transformers no problema de legenda de imagem já visto usando Tensorflow
  • Comparando os resultados de Transformers vs. Modelos de Atenção.

Introdução

Vimos que os mecanismos de atenção (no artigo anterior) tornaram-se parte integrante da modelagem de sequência convincente e modelos de transdução em várias tarefas (como a legenda das fotos), permitindo a modelagem de dependências independentemente de sua distância nas sequências de entrada ou saída.

image-caption-generator-3445730

O transformador, uma arquitetura modelo que evita a recorrência e, em vez de, depende inteiramente de um mecanismo de cuidado para estabelecer dependências globais entre a entrada e a saída. A arquitetura do Transformer permite uma paralelização significativamente maior e pode alcançar novos resultados de ponta em qualidade de tradução.

Neste artigo, vamos ver como pode Implementar o mecanismo de atenção para gerar legendas com Transformers usando TensorFlow.

Pré-requisitos antes de começar: –

Eu recomendo que você leia este artigo antes de começar:

Tabela de conteúdo

  1. Arquitetura de transformador
  2. Implementação do mecanismo de atenção para geração de legendas com Transformers usando Tensorflow
    1. Importar bibliotecas necessárias
    2. Carregamento e pré-processamento de dados
    3. Definição de modelo
    4. Codificação posicional
    5. Cuidado multi-cabeça
    6. Camada codificador-decodificador
    7. Transformador
    8. Hiperparâmetros do modelo
    9. Treinamento de modelo
    10. Avaliação BLEU
    11. Comparação
  3. Que segue?
  4. Notas finais

Arquitetura de transformador

screenshot-2021-01-04-at-10-59-06-am-235x300-8352109

A rede do transformador emprega uma arquitetura de codificador-decodificador semelhante à de um RNN. A principal diferença é que os transformadores podem receber a oração / sequência de entrada paralela, quer dizer, não há intervalo de tempo associado à entrada e todas as palavras da frase podem ser passadas simultaneamente.

Vamos começar entendendo a entrada para o transformador.

Considere uma tradução do inglês para o alemão. Alimentamos a frase inteira em inglês para a inserção de entrada. Uma camada de inserção de entrada pode ser considerada como um ponto no espaço onde palavras semelhantes em significado estão fisicamente mais próximas umas das outras., quer dizer, cada palavra é atribuída a um vetor com valores contínuos para representar aquela palavra.

Agora, um problema com isso é que a mesma palavra em frases diferentes pode ter significados diferentes, é aqui que entra a codificação de posição. Uma vez que os transformadores não contêm recorrência ou convolução, para o modelo usar a ordem da sequência, deve fazer uso de algumas informações sobre a posição relativa ou absoluta das palavras em uma sequência. A ideia é usar pesos fixos ou aprendidos que codificam informações relacionadas a uma posição específica de um token em uma frase.

de forma similar, a palavra alemã alvo é alimentada para a codificação de saída e seu vetor de codificação posicional é passado para o bloco decodificador.

O bloco codificador tem duas subcamadas. O primeiro é um mecanismo de autocuidado com várias cabeças, e a segunda é uma rede de alimentação direta simples, totalmente conectado dependendo da posição. Para cada palavra, podemos gerar um vetor de atenção que captura as relações contextuais entre as palavras em uma frase. A atenção com várias cabeças no codificador aplica um mecanismo de atenção específico chamado autoatenção. A autoatenção permite que os modelos associem cada palavra da entrada a outras palavras.

Além das duas subcamadas em cada camada do codificador, o decodificador insere uma terceira subcamada, que executa a atenção de várias cabeças na saída da pilha do codificador. Semelhante ao codificador, usamos conexões residuais em torno de cada uma das subcamadas, seguido pela normalização da camada. Os vetores de atenção das palavras alemãs e os vetores de atenção das frases em inglês do codificador são passados ​​para a segunda atenção com várias cabeças.

Este bloco de atenção determinará quão intimamente cada vetor de palavras está relacionado entre si.. É aqui que o mapeamento de palavras do inglês para o alemão é feito. O decodificador é coberto com uma camada linear que atua como um classificador e um softmax para obter as probabilidades da palavra.

Agora que você tem uma visão geral básica de como os transformadores funcionam, vamos ver como podemos implementá-lo para tarefa de legenda de imagem usando Tensorflow e comparar nossos resultados com outros métodos.

Implementação do mecanismo de atenção para a geração de legendas com Transformers usando TensorFlow

Você pode encontrar o código-fonte completo em meu Github perfil.

Paso 1: – Importe as bibliotecas necessárias

Aqui, usaremos o Tensorflow para criar nosso modelo e treiná-lo. A maior parte do crédito do código vai para o TensorFlow tutoriais. Você pode usar os notebooks Google Colab ou Kaggle se quiser que uma GPU o treine.

importar string
importar numpy como np
importar pandas como pd
from numpy import array
da imagem de importação PIL
importar picles

import matplotlib.pyplot as plt
import sys, Tempo, os, advertências
warnings.filterwarnings("ignorar")
importar re

importação difícil
importar tensorflow como tf
from tqdm import tqdm
de nltk.translate.bleu_score import frase_bleu

de keras.preprocessing.sequence import pad_sequences
from keras.utils import to_categorical
de keras.utils import plot_model
de keras.models import Model
de keras.layers import Input
de keras.layers import Dense, BatchNormalization
de keras.layers import LSTM
from keras.layers import Embedding
de keras.layers import Dropout
de keras.layers.merge importar adicionar
de keras.callbacks import ModelCheckpoint
de keras.preprocessing.image import load_img, img_to_array
from keras.preprocessing.text import Tokenizer

de sklearn.utils import shuffle
de sklearn.model_selection import train_test_split

Paso 2: – Carregamento e pré-processamento de dados

Defina nossa imagem e caminho de legenda e verifique quantas imagens no total estão presentes no conjunto de dados.

image_path = "/content / gdrive / My Drive / FLICKR8K / Flicker8k_Dataset"
dir_Flickr_text = "/content / gdrive / My Drive / FLICKR8K / Flickr8k_text / Flickr8k.token.txt"
jpgs = os.listdir(caminho_da_imagem)

imprimir("Total de imagens no conjunto de dados = {}".formato(len(jpgs)))

Produção:

screenshot-2020-11-10-at-4-12-12-pm-300x36-5478144

Criamos um quadro de dados para armazenar o id da imagem e as legendas para facilidade de uso.

arquivo = aberto(dir_Flickr_text,'r')
text = file.read()
file.close()

datatxt = []
para linha em text.split('n'):
   col = linha.split('t')
   se len(col) == 1:
       Prosseguir
   w = col[0].dividir("#")
   datatxt.append(C + [col[1].diminuir()])

data = pd.DataFrame(datatxt,colunas =["nome do arquivo","índice","rubrica"])
data = data.reindex(colunas =['índice','nome do arquivo','rubrica'])
dados = dados[data.filename != '2258277193_586949ec62.jpg.1']
uni_filenames = np.unique(data.filename.values)

data.head()

Produção:
screenshot-2020-11-10-at-12-51-08-pm-300x89-9251648

A seguir, Vamos visualizar algumas imagens e suas 5 legendas:

npic = 5
npix = 224
target_size = (npix,npix,3)
contagem = 1

fig = plt.figure(figsize =(10,20))
para jpgfnm em uni_filenames[10:14]:
   nome de arquivo = image_path + '/' + jpgfnm
   legendas = lista(dados["rubrica"].Lugar, colocar[dados["nome do arquivo"]== jpgfnm].valores)
   image_load = load_img(nome do arquivo, target_size = target_size)
   ax = fig.add_subplot(npic,2,contar,xticks =[],yticks =[])
   ax.imshow(carga_da_imagem)
   contagem += 1

   ax = fig.add_subplot(npic,2,contar)
   plt.axis('desligado')
   ax.plot()
   ax.set_xlim(0,1)
   ax.set_ylim(0,len(legendas))
   para eu, legenda em enumerar(legendas):
       ax.text(0,eu,rubrica,fontsize = 20)
   contagem += 1
plt.show()

Produção:

screenshot-2020-11-10-at-12-52-47-pm-300x206-6988469

A seguir, vamos ver qual é o tamanho do nosso vocabulário atual: –

vocabulário = []
para txt em data.caption.values:
   vocabulário.estender(txt.split())
imprimir('Tamanho do vocabulário: %d ' % len(definir(vocabulário)))

Produção:

screenshot-2020-11-10-at-12-54-39-pm-300x39-2020278A seguir, realizar uma limpeza de texto, como remover pontuação, caracteres individuais e valores numéricos:

def remove_punctuation(text_original):
   text_no_punctuation = text_original.translate(string.punctuation)
   Retorna(text_no_punctuation)

def remove_single_character(texto):
   text_len_more_than1 = ""
   para palavra em texto.split():
       se len(palavra) > 1:
           text_len_more_than1 += " " + palavra
   Retorna(text_len_more_than1)

def remove_numeric(texto):
   text_no_numeric = ""
   para palavra em texto.split():
       isalpha = palavra.isalpha()
       se isalpha:
           text_no_numeric += " " + palavra
   Retorna(text_no_numeric)

def text_clean(text_original):
   texto = remove_punctuation(text_original)
   texto = remove_single_character(texto)
   texto = remove_numeric(texto)
   Retorna(texto)

para eu, legenda em enumerar(data.caption.values):
   newcaption = text_clean(rubrica)
   dados["rubrica"].iloc[eu] = newcaption

Agora vamos olhar para o tamanho do nosso vocabulário após a limpeza.

clean_vocabulary = []
para txt em data.caption.values:
   clean_vocabulary.extend(txt.split())
imprimir('Limpar tamanho do vocabulário: %d ' % len(definir(limpar_vocabulário)))

Produção:

screenshot-2020-11-10-at-12-58-56-pm-300x33-8988181A seguir, salvamos todos os títulos e caminhos das imagens em duas listas para que possamos carregar as imagens ao mesmo tempo usando o caminho estabelecido. Nós também adicionamos tags ” e ” a cada título para que o modelo entenda o início e o fim de cada título.

PATH = "/content / gdrive / My Drive / FLICKR8K / Flicker8k_Dataset /"
all_captions = []
para legenda em dados["rubrica"].astype(str):
   legenda = '<começar> ' + legenda + ' <fim>'
   all_captions.append(rubrica)

all_captions[:10]

Produção:

screenshot-2020-11-10-at-1-01-47-pm-300x60-3232573

all_img_name_vector = []
para anotação nos dados["nome do arquivo"]:
   full_image_path = PATH + anotação
   all_img_name_vector.append(full_image_path)

all_img_name_vector[:10]

Produção:

screenshot-2020-11-10-at-1-02-21-pm-300x74-1466299
Agora você pode ver que temos 40455 caminhos e legendas de imagens.

imprimir(f"len(all_img_name_vector) : {len(all_img_name_vector)}")
imprimir(f"len(all_captions) : {len(all_captions)}")

Produção:

screenshot-2020-11-10-at-1-07-54-pm-300x41-7195579
Vamos levar sozinhos 40000 de cada um para que possamos selecionar o tamanho do lote corretamente, quer dizer, 625 lotes se tamanho do lote = 64. Para fazer isso, definimos uma função para limitar o conjunto de dados a 40000 imagens e legendas.

def data_limiter(num,total_captions,all_img_name_vector):
   train_captions, img_name_vector = shuffle(total_captions,all_img_name_vector,random_state = 1)
   train_captions = train_captions[:num]
   img_name_vector = img_name_vector[:num]
   train_captions de retorno,img_name_vector

train_captions,img_name_vector = data_limiter(40000,total_captions,all_img_name_vector)

Paso 3: – Definição do modelo

Vamos definir o modelo de extração de recursos de imagem usando InceptionV3. Devemos lembrar que não precisamos classificar as imagens aqui, só precisamos extrair um vetor de imagem para nossas imagens. Portanto, removemos a camada softmax do modelo. Devemos todos pré-processar todas as imagens com o mesmo tamanho, quer dizer, 299 × 299 antes de inseri-los no modelo, e a forma de saída desta camada é 8x8x2048.

def load_image(caminho_da_imagem):
   img = tf.io.read_file(caminho_da_imagem)
   img = tf.image.decode_jpeg(img, canais = 3)
   img = tf.image.resize(img, (299, 299))
   img = tf.keras.applications.inception_v3.preprocess_input(img)
   retorno img, image_path

image_model = tf.keras.applications.InceptionV3(include_top=Falso, pesos ="imagenet")
new_input = image_model.input
hidden_layer = image_model.layers[-1].output
image_features_extract_model = tf.keras.Model(new_input, hidden_layer)

A seguir, vamos atribuir o nome de cada imagem para a função de carregar a imagem. Iremos pré-processar cada imagem com InceptionV3 e armazenar em cache a saída para o disco e as características da imagem serão reformatadas para 64 × 2048.

encode_train = classificado(definir(img_name_vector))
image_dataset = tf.data.Dataset.from_tensor_slices(encode_train)
image_dataset = image_dataset.map(load_image, num_parallel_calls = tf.data.experimental.AUTOTUNE).lote(64)

Extraímos as características e as armazenamos nos respectivos .npy arquivos e, em seguida, passe essas características pelo codificador.Os arquivos NPY armazenam todas as informações necessárias para reconstruir uma matriz em qualquer computador, incluindo informações de tipo e forma.

para img, caminho em tqdm(image_dataset):
   batch_features = image_features_extract_model(img)
   batch_features = tf.reshape(batch_features,
                              (batch_features.shape[0], -1, batch_features.shape[3]))

 para bf, p em zip(batch_features, caminho):
   path_of_feature = p.numpy().decodificar("utf-8")
   np.save(path_of_feature, bf.numpy())

A seguir, convertemos as legendas em tokens e criamos um vocabulário de todas as palavras únicas nos dados. Também limitaremos o tamanho do vocabulário para 5000 palavras principais para economizar memória. Substituiremos palavras que não estão no vocabulário pelo token

top_k = 5000
tokenizer = tf.keras.preprocessing.text.Tokenizer(num_words = top_k,
                                                 oov_token="<desconhecido>",
                                                 filtros="!"#$%&()*+.,-/:;[e-mail protegido][]^ _`{|}~ ")

tokenizer.fit_on_texts(train_captions)
train_seqs = tokenizer.texts_to_sequences(train_captions)
tokenizer.word_index['<almofada>'] = 0
tokenizer.index_word[0] = '<almofada>'

train_seqs = tokenizer.texts_to_sequences(train_captions)
cap_vector = tf.keras.preprocessing.sequence.pad_sequences(train_seqs, padding = 'post')

A seguir, criar conjuntos de treinamento e validação usando uma divisão 80-20:

img_name_train, img_name_val, cap_train, cap_val = train_test_split(img_name_vector,cap_vector, test_size = 0.2, random_state = 0)

A seguir, vamos criar um conjunto de dados tf.data para usar no treinamento de nosso modelo.

BATCH_SIZE = 64
BUFFER_SIZE = 1000
num_steps = len(img_name_train) // TAMANHO DO BATCH

def map_func(img_name, boné):
   img_tensor = np.load(img_name.decode('utf-8')+'.npy')
   Retorna img_tensor, boné

dataset = tf.data.Dataset.from_tensor_slices((img_name_train, cap_train))
dataset = dataset.mapa(lambda item 1, item2: tf.numpy_function(map_func, [item 1, item2], [tf.float32, tf.int32]),num_parallel_calls = tf.data.experimental.AUTOTUNE)
dataset = dataset.shuffle(TAMANHO DO BUFFER).lote(TAMANHO DO BATCH)
dataset = dataset.prefetch(buffer_size = tf.data.experimental.AUTOTUNE)

Paso 4: – Codificação posicional

A codificação posicional usa funções seno e cosseno de diferentes frequências. Para cada índice ímpar no vetor de entrada, crie um vetor usando a função cos, para cada índice par, criar um vetor usando a função sin. Mais tarde, adicione esses vetores às suas incorporações de entrada correspondentes, que dá à rede informações sobre a posição de cada vetor.

def get_angles(pos, eu, d_model):
   angle_rates = 1 / np.power(10000, (2 * (eu//2)) / por exemplo, float32(d_model))
   Retorna pos * ângulos_rates

def positional_encoding_1d(posição, d_model):
   angle_rads = get_angles(np.arange(posição)[:, por exemplo, newaxis],
                           np.arange(d_model)[por exemplo, newaxis, :],
                           d_model)

   ângulo_rads[:, 0::2] = np.sin(ângulo_rads[:, 0::2])
   ângulo_rads[:, 1::2] = por exemplo cos(ângulo_rads[:, 1::2])
   pos_encoding = angle_rads[por exemplo, newaxis, ...]
   Retorna tf.cast(pos_encoding, dtype = tf.float32)

def positional_encoding_2d(fileira,col,d_model):
   afirmar d_model % 2 == 0
   row_pos = np.repeat(np.arange(fileira),col)[:,por exemplo, newaxis]
   col_pos = np.repeat(np.expand_dims(np.arange(col),0),fileira,eixo =0).remodelar(-1,1)

   angle_rads_row = get_angles(row_pos,np.arange(d_model //2)[por exemplo, newaxis,:],d_model //2)
   angle_rads_col = get_angles(col_pos,np.arange(d_model //2)[por exemplo, newaxis,:],d_model //2)

   angle_rads_row[:, 0::2] = np.sin(angle_rads_row[:, 0::2])
   angle_rads_row[:, 1::2] = por exemplo cos(angle_rads_row[:, 1::2])
   angle_rads_col[:, 0::2] = np.sin(angle_rads_col[:, 0::2])
   angle_rads_col[:, 1::2] = por exemplo cos(angle_rads_col[:, 1::2])
   pos_encoding = np.concatenato([angle_rads_row,angle_rads_col],eixo =1)[por exemplo, newaxis, ...]
   Retorna tf.cast(pos_encoding, dtype = tf.float32)

Paso 5: – Cuidado multi-cabeça

Calcule os pesos de atenção. q, k, v deve ter dimensões principais correspondentes. k, v deve ter a penúltima dimensão correspondente, quer dizer: seq_len_k = seq_len_v. A máscara tem diferentes formas dependendo do seu tipo (acolchoado ou olhar para frente) mas deve ser transmissível para a adição.

def create_padding_mask(seq):
   seq = tf.cast(tf.math.equal(seq, 0), tf.float32)
   Retorna seq[:, tf.newaxis, tf.newaxis, :]  # (tamanho do batch, 1, 1, seq_len)

def create_look_ahead_mask(Tamanho):
   máscara = 1 - tf.linalg.band_part(tf.ones((Tamanho, Tamanho)), -1, 0)
   Retorna mascarar  # (seq_len, seq_len)

def scaled_dot_product_attention(q, k, v, mascarar):
   matmul_qk = tf.matmul(q, k, transpose_b = True)  # (..., seq_len_q, seq_len_k)
   dk = tf.cast(tf.shape(k)[-1], tf.float32)
   scaled_attention_logits = matmul_qk / tf.math.sqrt(dk)
.
   E se máscara não é nenhum:
      scaled_attention_logits += (mascarar * -1e9) 

   attention_weights = tf.nn.softmax(scaled_attention_logits, eixo =-1) 
   saída = tf.matmul(atenção_pesos, v)  # (..., seq_len_q, profundidade_v)

   Retorna saída, atenção_pesos

classe MultiHeadAttention(tf.duro.camadas.Camada):
   def __iniciar__(auto, d_model, num_heads):
      super(MultiHeadAttention, auto).__iniciar__()
      auto.num_heads = num_heads
      auto.d_model = d_model
      afirmar d_model % auto.num_heads == 0
      auto.profundidade = d_model // auto.num_heads
      auto.wq = tf.keras.layers.Dense(d_model)
      auto.wk = tf.keras.layers.Dense(d_model)
      auto.wv = tf.keras.layers.Dense(d_model)
      auto.dense = tf.keras.layers.Dense(d_model)

   def split_heads(auto, x, tamanho do batch):
      x = tf.reshape(x, (tamanho do batch, -1, auto.num_heads, auto.profundidade))
      Retorna tf.transpose(x, perm =[0, 2, 1, 3])

   def ligar(auto, v, k, q, mascarar= Nenhum):
      batch_size = tf.shape(q)[0]
      q = auto.wq(q)  # (tamanho do batch, seq_len, d_model)
      k = auto.sem.(k)  # (tamanho do batch, seq_len, d_model)
      v = auto.wv(v)  # (tamanho do batch, seq_len, d_model)

      q = auto.split_heads(q, tamanho do batch)  # (tamanho do batch, num_heads, seq_len_q, profundidade)
      k = auto.split_heads(k, tamanho do batch)  # (tamanho do batch, num_heads, seq_len_k, profundidade)
      v = auto.split_heads(v, tamanho do batch)  # (tamanho do batch, num_heads, seq_len_v, profundidade)

      scaled_attention, ention_weights = scaled_dot_product_attention(q, k, v, mascarar)
      scaled_attention = tf.transpose(scaled_attention, perm =[0, 2, 1, 3])  # (tamanho do batch, seq_len_q,      num_heads, profundidade)

      concat_attention = tf.reshape(scaled_attention,
                                 (tamanho do batch, -1, auto.d_model))  # (tamanho do batch, seq_len_q, d_model)

      saída = auto.denso(concat_attention)  # (tamanho do batch, seq_len_q, d_model)
      Retorna saída, atenção_pesos

def point_wise_feed_forward_network(d_model, dff):
   Retorna tf.keras.Sequential([
                tf.hard.layers.Dense(dff, ativação ='relu'),  # (tamanho do batch, seq_len, dff)
                tf.hard.layers.Dense(d_model)  # (tamanho do batch, seq_len, d_model)])


Paso 6: – Camada codificador-decodificador

classe EncoderLayer(tf.duro.camadas.Camada):
   def __iniciar__(auto, d_model, num_heads, dff, avaliar=0.1):
      super(EncoderLayer, auto).__iniciar__()
      auto.mha = MultiHeadAttention(d_model, num_heads)
      auto.ffn = point_wise_feed_forward_network(d_model, dff)

      auto.layernorm1 = tf.hard.layers.LayerNormalization(épsilon =1e-6)
      auto.layernorm2 = tf.hard.layers.LayerNormalization(épsilon =1e-6)

      auto.dropout1 = tf.keras.layers.Dropout(avaliar)
      auto.dropout2 = tf.keras.layers.Dropout(avaliar)


   def ligar(auto, x, Treinamento, mascarar= Nenhum):
      attn_output, _ = auto.mha(x, x, x, mascarar)  # (tamanho do batch, input_seq_len, d_model)
      attn_output = auto.dropout1(attn_output, treinamento = treinamento)
      out1 = auto.layernorm1(x + attn_output)  # (tamanho do batch, input_seq_len, d_model)

      ffn_output = auto.ffn(out1)  # (tamanho do batch, input_seq_len, d_model)
      ffn_output = auto.dropout2(ffn_output, treinamento = treinamento)
      out2 = auto.layerernorm2(out1 + ffn_output)  # (tamanho do batch, input_seq_len, d_model)
      Retorna out2
classe DecoderLayer(tf.duro.camadas.Camada):
   def __iniciar__(auto, d_model, num_heads, dff, avaliar=0.1):
      super(DecoderLayer, auto).__iniciar__()
      auto.mha1 = MultiHeadAttention(d_model, num_heads)
      auto.mha2 = MultiHeadAttention(d_model, num_heads)

      auto.ffn = point_wise_feed_forward_network(d_model, dff)

      auto.layernorm1 = tf.hard.layers.LayerNormalization(épsilon =1e-6)
      auto.layernorm2 = tf.hard.layers.LayerNormalization(épsilon =1e-6)
      auto.layernorm3 = tf.hard.layers.LayerNormalization(épsilon =1e-6)

      auto.dropout1 = tf.keras.layers.Dropout(avaliar)
      auto.dropout2 = tf.keras.layers.Dropout(avaliar)
      auto.dropout3 = tf.keras.layers.Dropout(avaliar)

   def ligar(auto, x, enc_output, Treinamento,look_ahead_mask= Nenhum, padding_mask= Nenhum):
      attn1, attn_weights_block1 = auto.mha1(x, x, x, look_ahead_mask)  # (tamanho do batch, target_seq_len, d_model)
      attn1 = auto.dropout1(attn1, treinamento = treinamento)
      out1 = auto.layernorm1(attn1 + x)

      attn2, attn_weights_block2 = auto.mha2(enc_output, enc_output, out1, padding_mask) 
      attn2 = auto.dropout2(attn2, treinamento = treinamento)
      out2 = auto.layerernorm2(attn2 + out1)  # (tamanho do batch, target_seq_len, d_model)

      ffn_output = auto.ffn(out2)  # (tamanho do batch, target_seq_len, d_model)
      ffn_output = auto.dropout3(ffn_output, treinamento = treinamento)
      out3 = auto.layerernorm3(ffn_output + out2)  # (tamanho do batch, target_seq_len, d_model)

      Retorna out3, attn_weights_block1, attn_weights_block2
classe Codificador(tf.duro.camadas.Camada):
   def __iniciar__(auto, num_layers, d_model, num_heads, dff, tamanho_da_linha,tamanho_col,avaliar=0.1):
      super(Codificador, auto).__iniciar__()
      auto.d_model = d_model
      auto.num_camadas = num_camadas

      auto.embedding = tf.keras.layers.Dense(auto.d_model,ativação ='relu')
      auto.pos_encoding = posicional_encoding_2d(tamanho_da_linha,tamanho_col,auto.d_model)

      auto.enc_layers = [EncoderLayer(d_model, num_heads, dff, avaliar) para _ no faixa(num_layers)]
      auto.dropout = tf.keras.layers.Dropout(avaliar)

   def ligar(auto, x, Treinamento, mascarar= Nenhum):
      seq_len = tf.shape(x)[1]
      x = auto.incorporação(x)  # (tamanho do batch, input_seq_len(H * W), d_model)
      x += auto.pos_encoding[:, :seq_len, :]
      x = auto.cair fora(x, treinamento = treinamento)

      para eu em faixa(auto.num_layers):
         x = auto.enc_layers[eu](x, Treinamento, mascarar)

      Retorna x  # (tamanho do batch, input_seq_len, d_model)
classe Decodificador(tf.duro.camadas.Camada):
   def __iniciar__(auto, num_layers,d_model,num_heads,dff, target_vocab_size, maximum_position_encoding,   avaliar=0.1):
      super(Decodificador, auto).__iniciar__()
      auto.d_model = d_model
      auto.num_camadas = num_camadas

      auto.embedding = tf.keras.layers.Embedding(target_vocab_size, d_model)
      auto.pos_encoding = positional_encoding_1d(maximum_position_encoding, d_model)

      auto.dec_layers = [DecoderLayer(d_model, num_heads, dff, avaliar)
                         para _ no faixa(num_layers)]
      auto.dropout = tf.keras.layers.Dropout(avaliar)

   def ligar(auto, x, enc_output, Treinamento,look_ahead_mask=Nenhum, padding_mask=Nenhum):
      seq_len = tf.shape(x)[1]
      atenção_pesos = {}

      x = auto.incorporação(x)  # (tamanho do batch, target_seq_len, d_model)
      x *= tf.math.sqrt(tf.cast(auto.d_model, tf.float32))
      x += auto.pos_encoding[:, :seq_len, :]
      x = auto.cair fora(x, treinamento = treinamento)

      para eu no faixa(auto.num_layers):
         x, Bloco 1, bloco 2 = auto.dec_layers[eu](x, enc_output, Treinamento,
                                            look_ahead_mask, padding_mask)
         
         atenção_pesos['decoder_layer{}_Bloco 1'.formato(i +1)] = bloco1
         atenção_pesos['decoder_layer{}_block2 '.formato(i +1)] = bloco2

      Retorna x, atenção_pesos

Paso 7: – Transformador

classe Transformador(tf.duro.Modelo):
   def __iniciar__(auto, num_layers, d_model, num_heads, dff,tamanho_da_linha,tamanho_col,
              target_vocab_size,max_pos_encoding, avaliar=0.1):
      super(Transformador, auto).__iniciar__()
      auto.encoder = Encoder(num_layers, d_model, num_heads, dff,tamanho_da_linha,tamanho_col, avaliar)
      auto.decoder = decoder(num_layers, d_model, num_heads, dff,
                          target_vocab_size,max_pos_encoding, avaliar)
      auto.final_layer = tf.keras.layers.Dense(target_vocab_size)

   def ligar(auto, inp, alcatrão, Treinamento,look_ahead_mask=Nenhum,dec_padding_mask=Nenhum,enc_padding_mask=Nenhum   ):
      enc_output = auto.codificador(inp, Treinamento, enc_padding_mask)  # (tamanho do batch, inp_seq_len, d_model      )
      dec_output, atenção_pesos = auto.decodificador(
      alcatrão, enc_output, Treinamento, look_ahead_mask, dec_padding_mask)
      final_output = auto.final_layer(dec_output)  # (tamanho do batch, tar_seq_len, target_vocab_size)
      Retorna final_output, atenção_pesos

Paso 8: – Modelo de hiperparámetros

Definir parâmetros de treinamento:

num_layer = 4
d_model = 512
dff = 2048
num_heads = 8
row_size = 8
col_size = 8
target_vocab_size = top_k + 1
dropout_rate = 0.1 
classe CustomSchedule(tf.duro.otimizadores.horários.LearningRateSchedule):
   def __iniciar__(auto, d_model, warmup_steps= 4000):
      super(CustomSchedule, auto).__iniciar__()
      auto.d_model = d_model
      auto.d_model = tf.cast(auto.d_model, tf.float32)
      auto.warmup_steps = warmup_steps

   def __ligar__(auto, Passo):
      arg1 = tf.math.rsqrt(Passo)
      arg2 = passo * (auto.warmup_steps ** -1.5)
      Retorna tf.math.rsqrt(auto.d_model) * tf.math.minimum(arg1, arg2)
learning_rate = CustomSchedule(d_model)
optimizer = tf.hard.optimizers.Adam(taxa de Aprendizagem, beta_1 = 0,9, beta_2 = 0,98,
                                    epsilon=1e-9)
loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits =Verdade, redução ='Nenhum')

def loss_function(real, pred):
   mask = tf.math.logical_not(tf.math.equal(real, 0))
   loss_ = loss_object(real, pred)
   mask = tf.cast(mascarar, dtype = loss_.dtype)
   perda_ * = máscara
  Retorna tf.reduce_sum(perda_)/tf.reduce_sum(mascarar)
train_loss = tf.keras.metrics.Mean(nome ="perda de trem")
train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(nome ="train_accuracy")
transformador = transformador(num_layer,d_model,num_heads,dff,tamanho_da_linha,tamanho_col,target_vocab_size,                                 max_pos_encoding = target_vocab_size,taxa = dropout_rate)

Paso 9: – Treinamento de modelo

def create_masks_decoder(alcatrão):
   look_ahead_mask = create_look_ahead_mask(tf.shape(alcatrão)[1])
   dec_target_padding_mask = create_padding_mask(alcatrão)
   combinado_mask = tf.maximum(dec_target_padding_mask, look_ahead_mask)
   Retorna máscara_combinada
@ tf.function
def train_step(img_tensor, alcatrão):
   tar_inp = tar[:, :-1]
   tar_real = tar[:, 1:]
   dec_mask = create_masks_decoder(tar_inp)
   com tf.GradientTape() Como fita:
      previsões, _ = transformador(img_tensor, tar_inp,Verdade, dec_mask)
      loss = loss_function(tar_real, previsões)

   gradientes = tape.gradient(perda, transformer.trainable_variables)   
   optimizer.apply_gradients(fecho eclair(gradientes, transformer.trainable_variables))
   perda de trem(perda)
   train_accuracy(tar_real, previsões)
para época no faixa(30):
   start = time.time()
   train_loss.reset_states()
   train_accuracy.reset_states()
   para (lote, (img_tensor, alcatrão)) no enumerar(conjunto de dados):
      train_step(img_tensor, alcatrão)
      E se lote % 50 == 0:
         imprimir ('Época {} Lote {} Perda {:.4f} Precisão {:.4f}'.formato(
         época + 1, lote, train_loss.result(), train_accuracy.result()))

   imprimir ('Época {} Perda {:.4f} Precisão {:.4f}'.formato(época + 1,
                                               train_loss.result(),
                                               train_accuracy.result()))
   imprimir ('Tempo gasto para 1 época: {} secsn '.formato(time.time() - começar))

Paso 10: – Avaliação BLEU

def Avalie(imagem):
   temp_input = tf.expand_dims(load_image(imagem)[0], 0)
   img_tensor_val = image_features_extract_model(temp_input)
   img_tensor_val = tf.reshape(img_tensor_val, (img_tensor_val.shape[0], -1, img_tensor_val.shape[3]))
   start_token = tokenizer.word_index['<começar>']
   end_token = tokenizer.word_index['<fim>']
   decoder_input = [start_token]
   output = tf.expand_dims(decoder_input, 0) #tokens
   resultado = [] #lista de palavras

   fou eu no faixa(100):
      dec_mask = create_masks_decoder(saída)
      previsões, atenção_pesos = transformador(img_tensor_val,saída,Falso,dec_mask)
      previsões = previsões[: ,-1:, :]  # (tamanho do batch, 1, tamanho_do_ vocabulário)
      predicted_id = tf.cast(tf.argmax(previsões, eixo = -1), tf.int32)
      E se predicted_id == end_token:
         Retorna resultado,tf.squeeze(saída, eixo = 0), atenção_pesos
      result.append(tokenizer.index_word[int(predicted_id)])
      output = tf.concat([saída, predicted_id], eixo = -1)

   Retorna resultado,tf.squeeze(saída, eixo = 0), atenção_pesos
rid = np.random.randint(0, len(img_name_val))
image = img_name_val[livrar]
real_caption = ''.Junte([tokenizer.index_word[eu] para eu no cap_val[livrar] E se eu não no [0]])
rubrica,resultado,atenção_pesos = avaliar(imagem)

first = real_caption.split('', 1)[1]
real_caption = first.rsplit('', 1)[0]

para eu no rubrica:
   E se i =="<desconhecido>":
      caption.remove(eu)

para eu no real_caption:
   E se i =="<desconhecido>":
      real_caption.remove(eu)

result_join = ''.Junte(rubrica)
result_final = result_join.rsplit('', 1)[0]
real_appn = []
real_appn.append(real_caption.split())
referência = real_appn
candidato = legenda

pontuação = frase_bleu(referência, candidato, pesos =(1.0,0,0,0))
imprimir(f"Pontuação BLUE-1: {pontuação * 100}")
pontuação = frase_bleu(referência, candidato, pesos =(0.5,0.5,0,0))
imprimir(f"Pontuação BLUE-2: {pontuação * 100}")
pontuação = frase_bleu(referência, candidato, pesos =(0.3,0.3,0.3,0))
imprimir(f"Pontuação BLUE-3: {pontuação * 100}")
pontuação = frase_bleu(referência, candidato, pesos =(0.25,0.25,0.25,0.25))
imprimir(f"Pontuação BLUE-4: {pontuação * 100}")
imprimir ('Real Caption:', real_caption)
imprimir ('Predicted Caption:', ''.Junte(rubrica))
temp_image = np.array(Imagem.abrir(imagem))
plt.imshow(temp_image)

Produção:

screenshot-2021-01-04-at-5-42-52-pm-300x262-8569923

rid = np.random.randint(0, len(img_name_val))
image = img_name_val[livrar]
real_caption = ''.Junte([tokenizer.index_word[eu] para eu no cap_val[livrar] E se eu não no [0]])
rubrica,resultado,atenção_pesos = avaliar(imagem)

first = real_caption.split('', 1)[1]
real_caption = first.rsplit('', 1)[0]

para eu no rubrica:
   E se i =="<desconhecido>":
      caption.remove(eu)

para eu no real_caption:
   E se i =="<desconhecido>":
      real_caption.remove(eu)

result_join = ''.Junte(rubrica)
result_final = result_join.rsplit('', 1)[0]
real_appn = []
real_appn.append(real_caption.split())
referência = real_appn
candidato = legenda

pontuação = frase_bleu(referência, candidato, pesos =(1.0,0,0,0))
imprimir(f"Pontuação BLUE-1: {pontuação * 100}")
pontuação = frase_bleu(referência, candidato, pesos =(0.5,0.5,0,0))
imprimir(f"Pontuação BLUE-2: {pontuação * 100}")
pontuação = frase_bleu(referência, candidato, pesos =(0.3,0.3,0.3,0))
imprimir(f"Pontuação BLUE-3: {pontuação * 100}")
pontuação = frase_bleu(referência, candidato, pesos =(0.25,0.25,0.25,0.25))
imprimir(f"Pontuação BLUE-4: {pontuação * 100}")
imprimir ('Real Caption:', real_caption)
imprimir ('Predicted Caption:', ''.Junte(rubrica))
temp_image = np.array(Imagem.abrir(imagem))
plt.imshow(temp_image)

Produção:

screenshot-2021-01-04-at-5-43-18-pm-300x195-7367287

Paso 11: – Comparação

Vamos comparar as pontuações BLEU alcançadas no artigo anterior usando a atenção de Bahdanau versus nossos transformadores.

screenshot-2021-01-04-at-5-47-34-pm-300x229-2319025screenshot-2021-01-04-at-5-42-52-pm-300x262-8569923

As pontuações BLEU à esquerda usam a atenção de Bahdanau e as pontuações BLEU à direita usam Transformers. Como podemos ver, O transformador funciona muito melhor do que apenas um modelo de atendimento.

screenshot-2021-01-04-at-5-47-53-pm-300x204-4763985screenshot-2021-01-04-at-5-43-26-pm-300x179-8466381

E aí está! Implementamos Transformers com sucesso usando o Tensorflow e vimos como ele pode produzir resultados de ponta..

Notas finais

Em resumo, Os transformadores são melhores do que todas as outras arquiteturas que vimos antes porque evitam totalmente a recursão, processando frases como um todo e aprendendo as relações entre as palavras graças a mecanismos de atenção com várias cabeças e embeddings posicionais. Também deve ser observado que os transformadores que usam Tensorflow só podem capturar dependências dentro do tamanho de entrada fixo usado para treiná-los.

Existem muitos transformadores novos e poderosos como o Transformer-XL, Transformador emaranhado, Transformador de memória em malha que também pode ser implementado para aplicativos como Image Captions para obter resultados ainda melhores.

Você acha útil este artigo? Compartilhe seus valiosos comentários na seção de comentários abaixo.. Sinta-se à vontade para compartilhar seus livros de código completos também, que será útil para os membros da nossa comunidade.

Assine a nossa newsletter

Nós não enviaremos SPAM para você. Nós odiamos isso tanto quanto você.