Reserve a data! O Google I / O retorna de 18 a 20 de maio Registre-se agora
Esta página foi traduzida pela API Cloud Translation.
Switch to English

Legendagem de imagens com atenção visual

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

Dada uma imagem como a do exemplo abaixo, nosso objetivo é gerar uma legenda como "um surfista surfando em uma onda".

Homem surfando

Fonte da imagem ; Licença: Domínio Público

Para fazer isso, você usará um modelo baseado em atenção, que nos permite ver em quais partes da imagem o modelo se concentra ao gerar uma legenda.

Predição

A arquitetura do modelo é semelhante a Show, Attend and Tell: Neural Image Caption Generation with Visual Attention .

Este bloco de notas é um exemplo de ponta a ponta. Ao executar o notebook, ele baixa o conjunto de dados MS-COCO , pré-processa e armazena em cache um subconjunto de imagens usando o Inception V3, treina um modelo codificador-decodificador e gera legendas em novas imagens usando o modelo treinado.

Neste exemplo, você treinará um modelo em uma quantidade relativamente pequena de dados - as primeiras 30.000 legendas para cerca de 20.000 imagens (porque há várias legendas por imagem no conjunto de dados).

import tensorflow as tf

# You'll generate plots of attention in order to see which parts of an image
# our model focuses on during captioning
import matplotlib.pyplot as plt

import collections
import random
import numpy as np
import os
import time
import json
from PIL import Image

Baixe e prepare o conjunto de dados MS-COCO

Você usará o conjunto de dados MS-COCO para treinar nosso modelo. O conjunto de dados contém mais de 82.000 imagens, cada uma com pelo menos 5 anotações de legenda diferentes. O código abaixo baixa e extrai o conjunto de dados automaticamente.

# Download caption annotation files
annotation_folder = '/annotations/'
if not os.path.exists(os.path.abspath('.') + annotation_folder):
  annotation_zip = tf.keras.utils.get_file('captions.zip',
                                           cache_subdir=os.path.abspath('.'),
                                           origin='http://images.cocodataset.org/annotations/annotations_trainval2014.zip',
                                           extract=True)
  annotation_file = os.path.dirname(annotation_zip)+'/annotations/captions_train2014.json'
  os.remove(annotation_zip)

# Download image files
image_folder = '/train2014/'
if not os.path.exists(os.path.abspath('.') + image_folder):
  image_zip = tf.keras.utils.get_file('train2014.zip',
                                      cache_subdir=os.path.abspath('.'),
                                      origin='http://images.cocodataset.org/zips/train2014.zip',
                                      extract=True)
  PATH = os.path.dirname(image_zip) + image_folder
  os.remove(image_zip)
else:
  PATH = os.path.abspath('.') + image_folder
Downloading data from http://images.cocodataset.org/annotations/annotations_trainval2014.zip
252878848/252872794 [==============================] - 16s 0us/step
Downloading data from http://images.cocodataset.org/zips/train2014.zip
13510574080/13510573713 [==============================] - 774s 0us/step

Opcional: limite o tamanho do conjunto de treinamento

Para acelerar o treinamento para este tutorial, você usará um subconjunto de 30.000 legendas e suas imagens correspondentes para treinar nosso modelo. A escolha de usar mais dados resultaria em melhor qualidade de legenda.

with open(annotation_file, 'r') as f:
    annotations = json.load(f)
# Group all captions together having the same image ID.
image_path_to_caption = collections.defaultdict(list)
for val in annotations['annotations']:
  caption = f"<start> {val['caption']} <end>"
  image_path = PATH + 'COCO_train2014_' + '%012d.jpg' % (val['image_id'])
  image_path_to_caption[image_path].append(caption)
image_paths = list(image_path_to_caption.keys())
random.shuffle(image_paths)

# Select the first 6000 image_paths from the shuffled set.
# Approximately each image id has 5 captions associated with it, so that will
# lead to 30,000 examples.
train_image_paths = image_paths[:6000]
print(len(train_image_paths))
6000
train_captions = []
img_name_vector = []

for image_path in train_image_paths:
  caption_list = image_path_to_caption[image_path]
  train_captions.extend(caption_list)
  img_name_vector.extend([image_path] * len(caption_list))
print(train_captions[0])
Image.open(img_name_vector[0])
<start> A laptop sitting on a desk open to a webpage. <end>

png

Pré-processe as imagens usando InceptionV3

A seguir, você usará o InceptionV3 (que é pré-treinado no Imagenet) para classificar cada imagem. Você extrairá recursos da última camada convolucional.

Primeiro, você converterá as imagens no formato esperado do InceptionV3:

  • Redimensionar a imagem para 299 por 299 pixels
  • Pré-processe as imagens usando o método preprocess_input para normalizar a imagem de forma que ela contenha pixels no intervalo de -1 a 1, que corresponde ao formato das imagens usadas para treinar o InceptionV3.
def load_image(image_path):
    img = tf.io.read_file(image_path)
    img = tf.image.decode_jpeg(img, channels=3)
    img = tf.image.resize(img, (299, 299))
    img = tf.keras.applications.inception_v3.preprocess_input(img)
    return img, image_path

Inicialize o InceptionV3 e carregue os pesos Imagenet pré-treinados

Agora você criará um modelo tf.keras em que a camada de saída é a última camada convolucional na arquitetura InceptionV3. O formato da saída dessa camada é 8x8x2048 . Você usa a última camada convolucional porque está usando a atenção neste exemplo. Você não executa essa inicialização durante o treinamento porque pode se tornar um gargalo.

  • Você encaminha cada imagem pela rede e armazena o vetor resultante em um dicionário (image_name -> feature_vector).
  • Depois que todas as imagens são transmitidas pela rede, você salva o dicionário no disco.
image_model = tf.keras.applications.InceptionV3(include_top=False,
                                                weights='imagenet')
new_input = image_model.input
hidden_layer = image_model.layers[-1].output

image_features_extract_model = tf.keras.Model(new_input, hidden_layer)
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/inception_v3/inception_v3_weights_tf_dim_ordering_tf_kernels_notop.h5
87916544/87910968 [==============================] - 3s 0us/step

Armazenando em cache os recursos extraídos do InceptionV3

Você irá pré-processar cada imagem com InceptionV3 e armazenar em cache a saída para o disco. Armazenar a saída em cache na RAM seria mais rápido, mas também consumiria muita memória, exigindo 8 * 8 * 2048 flutuações por imagem. No momento da escrita, isso excede as limitações de memória do Colab (atualmente 12 GB de memória).

O desempenho poderia ser melhorado com uma estratégia de cache mais sofisticada (por exemplo, fragmentando as imagens para reduzir a E / S de disco de acesso aleatório), mas isso exigiria mais código.

O armazenamento em cache levará cerca de 10 minutos para ser executado no Colab com uma GPU. Se quiser ver uma barra de progresso, você pode:

  1. instalar tqdm :

    !pip install -q tqdm

  2. Importar tqdm:

    from tqdm import tqdm

  3. Altere a seguinte linha:

    for img, path in image_dataset:

    para:

    for img, path in tqdm(image_dataset):

# Get unique images
encode_train = sorted(set(img_name_vector))

# Feel free to change batch_size according to your system configuration
image_dataset = tf.data.Dataset.from_tensor_slices(encode_train)
image_dataset = image_dataset.map(
  load_image, num_parallel_calls=tf.data.AUTOTUNE).batch(16)

for img, path in image_dataset:
  batch_features = image_features_extract_model(img)
  batch_features = tf.reshape(batch_features,
                              (batch_features.shape[0], -1, batch_features.shape[3]))

  for bf, p in zip(batch_features, path):
    path_of_feature = p.numpy().decode("utf-8")
    np.save(path_of_feature, bf.numpy())

Pré-processar e tokenizar as legendas

  • Primeiro, você tokenizará as legendas (por exemplo, dividindo em espaços). Isso nos dá um vocabulário de todas as palavras exclusivas nos dados (por exemplo, "surf", "futebol" e assim por diante).
  • A seguir, você limitará o tamanho do vocabulário às 5.000 palavras principais (para economizar memória). Você substituirá todas as outras palavras pelo token "UNK" (desconhecido).
  • Em seguida, você cria mapeamentos de palavra para índice e de índice para palavra.
  • Por fim, você preenche todas as sequências para que tenham o mesmo comprimento da mais longa.
# Find the maximum length of any caption in our dataset
def calc_max_length(tensor):
    return max(len(t) for t in tensor)
# Choose the top 5000 words from the vocabulary
top_k = 5000
tokenizer = tf.keras.preprocessing.text.Tokenizer(num_words=top_k,
                                                  oov_token="<unk>",
                                                  filters='!"#$%&()*+.,-/:;=?@[\]^_`{|}~ ')
tokenizer.fit_on_texts(train_captions)
tokenizer.word_index['<pad>'] = 0
tokenizer.index_word[0] = '<pad>'
# Create the tokenized vectors
train_seqs = tokenizer.texts_to_sequences(train_captions)
# Pad each vector to the max_length of the captions
# If you do not provide a max_length value, pad_sequences calculates it automatically
cap_vector = tf.keras.preprocessing.sequence.pad_sequences(train_seqs, padding='post')
# Calculates the max_length, which is used to store the attention weights
max_length = calc_max_length(train_seqs)

Divida os dados em treinamento e teste

img_to_cap_vector = collections.defaultdict(list)
for img, cap in zip(img_name_vector, cap_vector):
  img_to_cap_vector[img].append(cap)

# Create training and validation sets using an 80-20 split randomly.
img_keys = list(img_to_cap_vector.keys())
random.shuffle(img_keys)

slice_index = int(len(img_keys)*0.8)
img_name_train_keys, img_name_val_keys = img_keys[:slice_index], img_keys[slice_index:]

img_name_train = []
cap_train = []
for imgt in img_name_train_keys:
  capt_len = len(img_to_cap_vector[imgt])
  img_name_train.extend([imgt] * capt_len)
  cap_train.extend(img_to_cap_vector[imgt])

img_name_val = []
cap_val = []
for imgv in img_name_val_keys:
  capv_len = len(img_to_cap_vector[imgv])
  img_name_val.extend([imgv] * capv_len)
  cap_val.extend(img_to_cap_vector[imgv])
len(img_name_train), len(cap_train), len(img_name_val), len(cap_val)
(24011, 24011, 6003, 6003)

Crie um conjunto de dados tf.data para treinamento

Nossas imagens e legendas estão prontas! A seguir, vamos criar um conjunto de dados tf.data para usar no treinamento de nosso modelo.

# Feel free to change these parameters according to your system's configuration

BATCH_SIZE = 64
BUFFER_SIZE = 1000
embedding_dim = 256
units = 512
vocab_size = top_k + 1
num_steps = len(img_name_train) // BATCH_SIZE
# Shape of the vector extracted from InceptionV3 is (64, 2048)
# These two variables represent that vector shape
features_shape = 2048
attention_features_shape = 64
# Load the numpy files
def map_func(img_name, cap):
  img_tensor = np.load(img_name.decode('utf-8')+'.npy')
  return img_tensor, cap
dataset = tf.data.Dataset.from_tensor_slices((img_name_train, cap_train))

# Use map to load the numpy files in parallel
dataset = dataset.map(lambda item1, item2: tf.numpy_function(
          map_func, [item1, item2], [tf.float32, tf.int32]),
          num_parallel_calls=tf.data.AUTOTUNE)

# Shuffle and batch
dataset = dataset.shuffle(BUFFER_SIZE).batch(BATCH_SIZE)
dataset = dataset.prefetch(buffer_size=tf.data.AUTOTUNE)

Modelo

Curiosidade: o decodificador abaixo é idêntico ao do exemplo para Tradução Automática Neural com Atenção .

A arquitetura do modelo é inspirada no papel Show, Attend and Tell .

  • Neste exemplo, você extrai os recursos da camada convolucional inferior do InceptionV3, fornecendo um vetor de forma (8, 8, 2048).
  • Você esmaga isso para uma forma de (64, 2048).
  • Esse vetor é então passado pelo codificador CNN (que consiste em uma única camada totalmente conectada).
  • O RNN (aqui GRU) acompanha a imagem para prever a próxima palavra.
class BahdanauAttention(tf.keras.Model):
  def __init__(self, units):
    super(BahdanauAttention, self).__init__()
    self.W1 = tf.keras.layers.Dense(units)
    self.W2 = tf.keras.layers.Dense(units)
    self.V = tf.keras.layers.Dense(1)

  def call(self, features, hidden):
    # features(CNN_encoder output) shape == (batch_size, 64, embedding_dim)

    # hidden shape == (batch_size, hidden_size)
    # hidden_with_time_axis shape == (batch_size, 1, hidden_size)
    hidden_with_time_axis = tf.expand_dims(hidden, 1)

    # attention_hidden_layer shape == (batch_size, 64, units)
    attention_hidden_layer = (tf.nn.tanh(self.W1(features) +
                                         self.W2(hidden_with_time_axis)))

    # score shape == (batch_size, 64, 1)
    # This gives you an unnormalized score for each image feature.
    score = self.V(attention_hidden_layer)

    # attention_weights shape == (batch_size, 64, 1)
    attention_weights = tf.nn.softmax(score, axis=1)

    # context_vector shape after sum == (batch_size, hidden_size)
    context_vector = attention_weights * features
    context_vector = tf.reduce_sum(context_vector, axis=1)

    return context_vector, attention_weights
class CNN_Encoder(tf.keras.Model):
    # Since you have already extracted the features and dumped it
    # This encoder passes those features through a Fully connected layer
    def __init__(self, embedding_dim):
        super(CNN_Encoder, self).__init__()
        # shape after fc == (batch_size, 64, embedding_dim)
        self.fc = tf.keras.layers.Dense(embedding_dim)

    def call(self, x):
        x = self.fc(x)
        x = tf.nn.relu(x)
        return x
class RNN_Decoder(tf.keras.Model):
  def __init__(self, embedding_dim, units, vocab_size):
    super(RNN_Decoder, self).__init__()
    self.units = units

    self.embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)
    self.gru = tf.keras.layers.GRU(self.units,
                                   return_sequences=True,
                                   return_state=True,
                                   recurrent_initializer='glorot_uniform')
    self.fc1 = tf.keras.layers.Dense(self.units)
    self.fc2 = tf.keras.layers.Dense(vocab_size)

    self.attention = BahdanauAttention(self.units)

  def call(self, x, features, hidden):
    # defining attention as a separate model
    context_vector, attention_weights = self.attention(features, hidden)

    # x shape after passing through embedding == (batch_size, 1, embedding_dim)
    x = self.embedding(x)

    # x shape after concatenation == (batch_size, 1, embedding_dim + hidden_size)
    x = tf.concat([tf.expand_dims(context_vector, 1), x], axis=-1)

    # passing the concatenated vector to the GRU
    output, state = self.gru(x)

    # shape == (batch_size, max_length, hidden_size)
    x = self.fc1(output)

    # x shape == (batch_size * max_length, hidden_size)
    x = tf.reshape(x, (-1, x.shape[2]))

    # output shape == (batch_size * max_length, vocab)
    x = self.fc2(x)

    return x, state, attention_weights

  def reset_state(self, batch_size):
    return tf.zeros((batch_size, self.units))
encoder = CNN_Encoder(embedding_dim)
decoder = RNN_Decoder(embedding_dim, units, vocab_size)
optimizer = tf.keras.optimizers.Adam()
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_mean(loss_)

Checkpoint

checkpoint_path = "./checkpoints/train"
ckpt = tf.train.Checkpoint(encoder=encoder,
                           decoder=decoder,
                           optimizer=optimizer)
ckpt_manager = tf.train.CheckpointManager(ckpt, checkpoint_path, max_to_keep=5)
start_epoch = 0
if ckpt_manager.latest_checkpoint:
  start_epoch = int(ckpt_manager.latest_checkpoint.split('-')[-1])
  # restoring the latest checkpoint in checkpoint_path
  ckpt.restore(ckpt_manager.latest_checkpoint)

Treinamento

  • Você extrai os recursos armazenados nos respectivos arquivos .npy e, em seguida, passa esses recursos pelo codificador.
  • A saída do codificador, o estado oculto (inicializado em 0) e a entrada do decodificador (que é o token de início) são passados ​​para o decodificador.
  • O decodificador retorna as previsões e o estado oculto do decodificador.
  • O estado oculto do decodificador é então passado de volta para o modelo e as previsões são usadas para calcular a perda.
  • Use a força do professor para decidir a próxima entrada para o decodificador.
  • Forçar professor é a técnica em que a palavra alvo é passada como a próxima entrada para o decodificador.
  • A etapa final é calcular os gradientes e aplicá-los ao otimizador e retropropagar.
# adding this in a separate cell because if you run the training cell
# many times, the loss_plot array will be reset
loss_plot = []
@tf.function
def train_step(img_tensor, target):
  loss = 0

  # initializing the hidden state for each batch
  # because the captions are not related from image to image
  hidden = decoder.reset_state(batch_size=target.shape[0])

  dec_input = tf.expand_dims([tokenizer.word_index['<start>']] * target.shape[0], 1)

  with tf.GradientTape() as tape:
      features = encoder(img_tensor)

      for i in range(1, target.shape[1]):
          # passing the features through the decoder
          predictions, hidden, _ = decoder(dec_input, features, hidden)

          loss += loss_function(target[:, i], predictions)

          # using teacher forcing
          dec_input = tf.expand_dims(target[:, i], 1)

  total_loss = (loss / int(target.shape[1]))

  trainable_variables = encoder.trainable_variables + decoder.trainable_variables

  gradients = tape.gradient(loss, trainable_variables)

  optimizer.apply_gradients(zip(gradients, trainable_variables))

  return loss, total_loss
EPOCHS = 20

for epoch in range(start_epoch, EPOCHS):
    start = time.time()
    total_loss = 0

    for (batch, (img_tensor, target)) in enumerate(dataset):
        batch_loss, t_loss = train_step(img_tensor, target)
        total_loss += t_loss

        if batch % 100 == 0:
            average_batch_loss = batch_loss.numpy()/int(target.shape[1])
            print(f'Epoch {epoch+1} Batch {batch} Loss {average_batch_loss:.4f}')
    # storing the epoch end loss value to plot later
    loss_plot.append(total_loss / num_steps)

    if epoch % 5 == 0:
      ckpt_manager.save()

    print(f'Epoch {epoch+1} Loss {total_loss/num_steps:.6f}')
    print(f'Time taken for 1 epoch {time.time()-start:.2f} sec\n')
Epoch 1 Batch 0 Loss 2.0436
Epoch 1 Batch 100 Loss 1.2139
Epoch 1 Batch 200 Loss 1.0171
Epoch 1 Batch 300 Loss 0.9492
Epoch 1 Loss 1.094750
Time taken for 1 epoch 115.99 sec

Epoch 2 Batch 0 Loss 0.9204
Epoch 2 Batch 100 Loss 0.7476
Epoch 2 Batch 200 Loss 0.8300
Epoch 2 Batch 300 Loss 0.8252
Epoch 2 Loss 0.834296
Time taken for 1 epoch 45.89 sec

Epoch 3 Batch 0 Loss 0.7721
Epoch 3 Batch 100 Loss 0.7509
Epoch 3 Batch 200 Loss 0.8069
Epoch 3 Batch 300 Loss 0.7527
Epoch 3 Loss 0.752264
Time taken for 1 epoch 45.67 sec

Epoch 4 Batch 0 Loss 0.7364
Epoch 4 Batch 100 Loss 0.6961
Epoch 4 Batch 200 Loss 0.7111
Epoch 4 Batch 300 Loss 0.7302
Epoch 4 Loss 0.697394
Time taken for 1 epoch 47.18 sec

Epoch 5 Batch 0 Loss 0.6719
Epoch 5 Batch 100 Loss 0.6485
Epoch 5 Batch 200 Loss 0.6410
Epoch 5 Batch 300 Loss 0.5863
Epoch 5 Loss 0.656725
Time taken for 1 epoch 46.51 sec

Epoch 6 Batch 0 Loss 0.6303
Epoch 6 Batch 100 Loss 0.6145
Epoch 6 Batch 200 Loss 0.6173
Epoch 6 Batch 300 Loss 0.5613
Epoch 6 Loss 0.617368
Time taken for 1 epoch 45.54 sec

Epoch 7 Batch 0 Loss 0.5689
Epoch 7 Batch 100 Loss 0.5396
Epoch 7 Batch 200 Loss 0.6018
Epoch 7 Batch 300 Loss 0.6358
Epoch 7 Loss 0.584526
Time taken for 1 epoch 45.98 sec

Epoch 8 Batch 0 Loss 0.5883
Epoch 8 Batch 100 Loss 0.5473
Epoch 8 Batch 200 Loss 0.5676
Epoch 8 Batch 300 Loss 0.5417
Epoch 8 Loss 0.554602
Time taken for 1 epoch 45.03 sec

Epoch 9 Batch 0 Loss 0.5423
Epoch 9 Batch 100 Loss 0.5331
Epoch 9 Batch 200 Loss 0.5054
Epoch 9 Batch 300 Loss 0.4515
Epoch 9 Loss 0.525291
Time taken for 1 epoch 44.87 sec

Epoch 10 Batch 0 Loss 0.5095
Epoch 10 Batch 100 Loss 0.5017
Epoch 10 Batch 200 Loss 0.4781
Epoch 10 Batch 300 Loss 0.4704
Epoch 10 Loss 0.497998
Time taken for 1 epoch 45.22 sec

Epoch 11 Batch 0 Loss 0.4385
Epoch 11 Batch 100 Loss 0.4621
Epoch 11 Batch 200 Loss 0.5013
Epoch 11 Batch 300 Loss 0.5085
Epoch 11 Loss 0.473056
Time taken for 1 epoch 47.45 sec

Epoch 12 Batch 0 Loss 0.4842
Epoch 12 Batch 100 Loss 0.4583
Epoch 12 Batch 200 Loss 0.4253
Epoch 12 Batch 300 Loss 0.4387
Epoch 12 Loss 0.448735
Time taken for 1 epoch 47.58 sec

Epoch 13 Batch 0 Loss 0.4350
Epoch 13 Batch 100 Loss 0.4013
Epoch 13 Batch 200 Loss 0.4413
Epoch 13 Batch 300 Loss 0.4178
Epoch 13 Loss 0.425068
Time taken for 1 epoch 46.99 sec

Epoch 14 Batch 0 Loss 0.4234
Epoch 14 Batch 100 Loss 0.4112
Epoch 14 Batch 200 Loss 0.4164
Epoch 14 Batch 300 Loss 0.3829
Epoch 14 Loss 0.404426
Time taken for 1 epoch 46.81 sec

Epoch 15 Batch 0 Loss 0.3797
Epoch 15 Batch 100 Loss 0.3471
Epoch 15 Batch 200 Loss 0.3635
Epoch 15 Batch 300 Loss 0.3502
Epoch 15 Loss 0.384106
Time taken for 1 epoch 46.46 sec

Epoch 16 Batch 0 Loss 0.3790
Epoch 16 Batch 100 Loss 0.3795
Epoch 16 Batch 200 Loss 0.3778
Epoch 16 Batch 300 Loss 0.3371
Epoch 16 Loss 0.365713
Time taken for 1 epoch 44.86 sec

Epoch 17 Batch 0 Loss 0.3997
Epoch 17 Batch 100 Loss 0.3389
Epoch 17 Batch 200 Loss 0.3373
Epoch 17 Batch 300 Loss 0.3326
Epoch 17 Loss 0.349113
Time taken for 1 epoch 43.83 sec

Epoch 18 Batch 0 Loss 0.3439
Epoch 18 Batch 100 Loss 0.3746
Epoch 18 Batch 200 Loss 0.3828
Epoch 18 Batch 300 Loss 0.3210
Epoch 18 Loss 0.331469
Time taken for 1 epoch 45.52 sec

Epoch 19 Batch 0 Loss 0.3199
Epoch 19 Batch 100 Loss 0.3233
Epoch 19 Batch 200 Loss 0.2991
Epoch 19 Batch 300 Loss 0.3366
Epoch 19 Loss 0.315025
Time taken for 1 epoch 45.93 sec

Epoch 20 Batch 0 Loss 0.3088
Epoch 20 Batch 100 Loss 0.2845
Epoch 20 Batch 200 Loss 0.3127
Epoch 20 Batch 300 Loss 0.3254
Epoch 20 Loss 0.301194
Time taken for 1 epoch 45.48 sec
plt.plot(loss_plot)
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.title('Loss Plot')
plt.show()

png

Rubrica!

  • A função de avaliação é semelhante ao loop de treinamento, exceto que você não usa a força do professor aqui. A entrada para o decodificador em cada etapa de tempo são suas previsões anteriores junto com o estado oculto e a saída do codificador.
  • Pare de prever quando o modelo prevê o token final.
  • E armazene os pesos de atenção para cada passo de tempo.
def evaluate(image):
    attention_plot = np.zeros((max_length, attention_features_shape))

    hidden = decoder.reset_state(batch_size=1)

    temp_input = tf.expand_dims(load_image(image)[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]))

    features = encoder(img_tensor_val)

    dec_input = tf.expand_dims([tokenizer.word_index['<start>']], 0)
    result = []

    for i in range(max_length):
        predictions, hidden, attention_weights = decoder(dec_input,
                                                         features,
                                                         hidden)

        attention_plot[i] = tf.reshape(attention_weights, (-1, )).numpy()

        predicted_id = tf.random.categorical(predictions, 1)[0][0].numpy()
        result.append(tokenizer.index_word[predicted_id])

        if tokenizer.index_word[predicted_id] == '<end>':
            return result, attention_plot

        dec_input = tf.expand_dims([predicted_id], 0)

    attention_plot = attention_plot[:len(result), :]
    return result, attention_plot
def plot_attention(image, result, attention_plot):
    temp_image = np.array(Image.open(image))

    fig = plt.figure(figsize=(10, 10))

    len_result = len(result)
    for i in range(len_result):
        temp_att = np.resize(attention_plot[i], (8, 8))
        grid_size = max(np.ceil(len_result/2), 2)
        ax = fig.add_subplot(grid_size, grid_size, i+1)
        ax.set_title(result[i])
        img = ax.imshow(temp_image)
        ax.imshow(temp_att, cmap='gray', alpha=0.6, extent=img.get_extent())

    plt.tight_layout()
    plt.show()
# captions on the validation set
rid = np.random.randint(0, len(img_name_val))
image = img_name_val[rid]
real_caption = ' '.join([tokenizer.index_word[i]
                        for i in cap_val[rid] if i not in [0]])
result, attention_plot = evaluate(image)

print('Real Caption:', real_caption)
print('Prediction Caption:', ' '.join(result))
plot_attention(image, result, attention_plot)
Real Caption: <start> an image of a man with other men on skiis <end>
Prediction Caption: a small skiers standing on a snow around a woman in the snow <end>
/home/kbuilder/.local/lib/python3.6/site-packages/ipykernel_launcher.py:10: MatplotlibDeprecationWarning: Passing non-integers as three-element position specification is deprecated since 3.3 and will be removed two minor releases later.
  # Remove the CWD from sys.path while we load stuff.

png

Experimente em suas próprias imagens

Para se divertir, fornecemos a seguir um método que você pode usar para legendar suas próprias imagens com o modelo que acabamos de treinar. Lembre-se de que ele foi treinado com uma quantidade relativamente pequena de dados e suas imagens podem ser diferentes dos dados de treinamento (portanto, esteja preparado para resultados estranhos!)

image_url = 'https://tensorflow.org/images/surf.jpg'
image_extension = image_url[-4:]
image_path = tf.keras.utils.get_file('image'+image_extension, origin=image_url)

result, attention_plot = evaluate(image_path)
print('Prediction Caption:', ' '.join(result))
plot_attention(image_path, result, attention_plot)
# opening the image
Image.open(image_path)
Downloading data from https://tensorflow.org/images/surf.jpg
65536/64400 [==============================] - 0s 4us/step
Prediction Caption: a man rides a wave on a sunny day <end>
/home/kbuilder/.local/lib/python3.6/site-packages/ipykernel_launcher.py:10: MatplotlibDeprecationWarning: Passing non-integers as three-element position specification is deprecated since 3.3 and will be removed two minor releases later.
  # Remove the CWD from sys.path while we load stuff.

png

png

Próximos passos

Parabéns! Você acabou de treinar um modelo de legendagem de imagens com atenção. A seguir, dê uma olhada neste exemplo de tradução automática neural com atenção . Ele usa uma arquitetura semelhante para traduzir entre frases em espanhol e inglês. Você também pode experimentar treinar o código neste notebook em um conjunto de dados diferente.