Odpowiedz już dziś na lokalne wydarzenie TensorFlow Everywhere!
Ta strona została przetłumaczona przez Cloud Translation API.
Switch to English

Podpisy graficzne z uwagą wizualną

Zobacz na TensorFlow.org Uruchom w Google Colab Wyświetl źródło na GitHub Pobierz notatnik

Biorąc pod uwagę obraz taki jak na poniższym przykładzie, naszym celem jest wygenerowanie podpisu, takiego jak „surfer na fali”.

Mężczyzna surfuje

Źródło obrazu ; Licencja: domena publiczna

Aby to osiągnąć, użyjesz modelu opartego na uwadze, który pozwoli nam zobaczyć, na jakich częściach obrazu skupia się model podczas generowania podpisu.

Prognoza

Architektura modelu jest podobna do funkcji Show, Attend and Tell: Neural Image Caption Generation with Visual Attention .

Ten notatnik jest kompleksowym przykładem. Po uruchomieniu notebook pobiera zestaw danych MS-COCO , przetwarza wstępnie i buforuje podzbiór obrazów przy użyciu programu Inception V3, trenuje model kodera-dekodera i generuje podpisy do nowych obrazów przy użyciu wytrenowanego modelu.

W tym przykładzie wytrenujesz model na stosunkowo niewielkiej ilości danych - pierwszych 30 000 podpisów dla około 20 000 obrazów (ponieważ zbiór danych zawiera wiele podpisów na obraz).

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

# Scikit-learn includes many helpful utilities
from sklearn.model_selection import train_test_split
from sklearn.utils import shuffle

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

Pobierz i przygotuj zbiór danych MS-COCO

Będziesz używać zbioru danych MS-COCO do trenowania naszego modelu. Zbiór danych zawiera ponad 82 000 obrazów, z których każdy ma co najmniej 5 różnych adnotacji w podpisach. Poniższy kod automatycznie pobiera i wyodrębnia zestaw danych.

# 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 [==============================] - 775s 0us/step

Opcjonalnie: ogranicz rozmiar zestawu uczącego

Aby przyspieszyć szkolenie z tego samouczka, do trenowania naszego modelu użyjesz podzbioru 30 000 podpisów i odpowiadających im obrazów. Decydując się na użycie większej ilości danych, poprawiłaby się jakość napisów.

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 group of bikers driving down a curvy road. <end>

png

Przetwórz wstępnie obrazy za pomocą InceptionV3

Następnie użyjesz InceptionV3 (który jest wstępnie wytrenowany w Imagenet) do sklasyfikowania każdego obrazu. Wydobywasz cechy z ostatniej warstwy konwolucyjnej.

Najpierw przekonwertujesz obrazy do oczekiwanego formatu InceptionV3 przez:

  • Zmiana rozmiaru obrazu na 299 na 299 pikseli
  • Przetwórz wstępnie obrazy przy użyciu metody preprocess_input , aby znormalizować obraz tak, aby zawierał piksele w zakresie od -1 do 1, co odpowiada formatowi obrazów używanym do uczenia 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

Zainicjuj InceptionV3 i załaduj wstępnie wytrenowane wagi Imagenet

Teraz utworzysz model tf.keras, w którym warstwa wyjściowa jest ostatnią warstwą konwolucyjną w architekturze InceptionV3. Kształt wyniku tej warstwy to 8x8x2048 . Używasz ostatniej warstwy konwolucyjnej, ponieważ w tym przykładzie używasz uwagi. Nie wykonujesz tej inicjalizacji podczas treningu, ponieważ może to stać się wąskim gardłem.

  • Przekazujesz każdy obraz przez sieć i przechowujesz wynikowy wektor w słowniku (nazwa_obrazu -> wektor_funkcji).
  • Po przepuszczeniu wszystkich obrazów przez sieć, wytrawiasz słownik i zapisujesz go na dysku.
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 [==============================] - 1s 0us/step

Buforowanie funkcji wyodrębnionych z InceptionV3

Każdy obraz zostanie wstępnie przetworzony za pomocą InceptionV3 i buforowany wynik na dysku. Buforowanie danych wyjściowych w pamięci RAM byłoby szybsze, ale wymagałoby również 8 * 8 * 2048 wartości zmiennoprzecinkowych na obraz. W chwili pisania tego tekstu przekracza to ograniczenia pamięci Colab (obecnie 12 GB pamięci).

Wydajność można poprawić, stosując bardziej wyrafinowaną strategię buforowania (na przykład fragmentując obrazy w celu zmniejszenia operacji wejścia / wyjścia dysku o dostępie swobodnym), ale wymagałoby to więcej kodu.

Buforowanie zajmie około 10 minut w Colab z GPU. Jeśli chcesz zobaczyć pasek postępu, możesz:

  1. zainstaluj tqdm :

    !pip install -q tqdm

  2. Importuj tqdm:

    from tqdm import tqdm

  3. Zmień następujący wiersz:

    for img, path in image_dataset:

    do:

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

Przetwarzaj wstępnie i tokenizuj podpisy

  • Najpierw tokenizujesz napisy (na przykład dzieląc spacje). To daje nam słownictwo zawierające wszystkie unikatowe słowa w danych (na przykład „surfing”, „piłka nożna” itd.).
  • Następnie ograniczysz rozmiar słownictwa do górnych 5000 słów (aby zaoszczędzić pamięć). Zastąpisz wszystkie inne słowa tokenem „UNK” (nieznany).
  • Następnie utworzysz odwzorowania typu słowo na indeks i indeks na słowo.
  • Na koniec dopełniasz wszystkie sekwencje tak, aby miały taką samą długość jak najdłuższa.
# 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)

Podziel dane na trening i testy

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

Utwórz zestaw danych tf.data do szkolenia

Nasze obrazy i podpisy są gotowe! Następnie utwórzmy zestaw danych tf.data, który będzie używany do uczenia naszego modelu.

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

Model

Ciekawostka: poniższy dekoder jest identyczny z tym w przykładzie dla neuronowego tłumaczenia maszynowego z uwagą .

Architektura modelu jest inspirowana papierem Show, Attend and Tell .

  • W tym przykładzie wyodrębniasz cechy z dolnej warstwy konwolucyjnej InceptionV3, dając nam wektor kształtu (8, 8, 2048).
  • Zgniatasz to do kształtu (64, 2048).
  • Ten wektor jest następnie przepuszczany przez koder CNN (który składa się z pojedynczej w pełni połączonej warstwy).
  • RNN (tutaj GRU) nadzoruje obraz, przewidując następne słowo.
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 using pickle
    # 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_)

Punkt kontrolny

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)

Trening

  • .npy funkcje zapisane w odpowiednich plikach .npy , a następnie przekazujesz te funkcje przez koder.
  • Wyjście kodera, stan ukryty (zainicjowany na 0) i wejście dekodera (które jest tokenem startowym) są przekazywane do dekodera.
  • Dekoder zwraca przewidywania i stan ukryty dekodera.
  • Ukryty stan dekodera jest następnie przekazywany z powrotem do modelu, a prognozy są wykorzystywane do obliczenia strat.
  • Użyj wymuszania nauczyciela, aby zdecydować o kolejnym wejściu do dekodera.
  • Wymuszanie przez nauczyciela to technika, w której słowo docelowe jest przekazywane jako następne wejście do dekodera.
  • Ostatnim krokiem jest obliczenie gradientów i zastosowanie go do optymalizatora i wstecznej propagacji.
# 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:
            print ('Epoch {} Batch {} Loss {:.4f}'.format(
              epoch + 1, batch, batch_loss.numpy() / int(target.shape[1])))
    # storing the epoch end loss value to plot later
    loss_plot.append(total_loss / num_steps)

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

    print ('Epoch {} Loss {:.6f}'.format(epoch + 1,
                                         total_loss/num_steps))
    print ('Time taken for 1 epoch {} sec\n'.format(time.time() - start))
Epoch 1 Batch 0 Loss 2.0855
Epoch 1 Batch 100 Loss 1.1326
Epoch 1 Batch 200 Loss 1.0184
Epoch 1 Batch 300 Loss 0.8906
Epoch 1 Loss 1.034848
Time taken for 1 epoch 121.38977003097534 sec

Epoch 2 Batch 0 Loss 0.8610
Epoch 2 Batch 100 Loss 0.7849
Epoch 2 Batch 200 Loss 0.6981
Epoch 2 Batch 300 Loss 0.7580
Epoch 2 Loss 0.777551
Time taken for 1 epoch 48.040128231048584 sec

Epoch 3 Batch 0 Loss 0.7430
Epoch 3 Batch 100 Loss 0.7583
Epoch 3 Batch 200 Loss 0.6776
Epoch 3 Batch 300 Loss 0.6853
Epoch 3 Loss 0.699766
Time taken for 1 epoch 48.34805083274841 sec

Epoch 4 Batch 0 Loss 0.6904
Epoch 4 Batch 100 Loss 0.6152
Epoch 4 Batch 200 Loss 0.6951
Epoch 4 Batch 300 Loss 0.5573
Epoch 4 Loss 0.649461
Time taken for 1 epoch 48.36990928649902 sec

Epoch 5 Batch 0 Loss 0.6015
Epoch 5 Batch 100 Loss 0.6234
Epoch 5 Batch 200 Loss 0.5821
Epoch 5 Batch 300 Loss 0.5604
Epoch 5 Loss 0.608492
Time taken for 1 epoch 48.66079306602478 sec

Epoch 6 Batch 0 Loss 0.6084
Epoch 6 Batch 100 Loss 0.5867
Epoch 6 Batch 200 Loss 0.5379
Epoch 6 Batch 300 Loss 0.5830
Epoch 6 Loss 0.573350
Time taken for 1 epoch 48.2317054271698 sec

Epoch 7 Batch 0 Loss 0.5373
Epoch 7 Batch 100 Loss 0.5730
Epoch 7 Batch 200 Loss 0.5768
Epoch 7 Batch 300 Loss 0.5278
Epoch 7 Loss 0.542395
Time taken for 1 epoch 46.71699810028076 sec

Epoch 8 Batch 0 Loss 0.5626
Epoch 8 Batch 100 Loss 0.5408
Epoch 8 Batch 200 Loss 0.5306
Epoch 8 Batch 300 Loss 0.4971
Epoch 8 Loss 0.513767
Time taken for 1 epoch 46.61273503303528 sec

Epoch 9 Batch 0 Loss 0.5340
Epoch 9 Batch 100 Loss 0.4759
Epoch 9 Batch 200 Loss 0.4874
Epoch 9 Batch 300 Loss 0.4618
Epoch 9 Loss 0.486720
Time taken for 1 epoch 46.16280508041382 sec

Epoch 10 Batch 0 Loss 0.5077
Epoch 10 Batch 100 Loss 0.4576
Epoch 10 Batch 200 Loss 0.4943
Epoch 10 Batch 300 Loss 0.4825
Epoch 10 Loss 0.462035
Time taken for 1 epoch 45.447362422943115 sec

Epoch 11 Batch 0 Loss 0.4712
Epoch 11 Batch 100 Loss 0.4562
Epoch 11 Batch 200 Loss 0.4111
Epoch 11 Batch 300 Loss 0.4287
Epoch 11 Loss 0.437673
Time taken for 1 epoch 46.236756324768066 sec

Epoch 12 Batch 0 Loss 0.4276
Epoch 12 Batch 100 Loss 0.4670
Epoch 12 Batch 200 Loss 0.3784
Epoch 12 Batch 300 Loss 0.3727
Epoch 12 Loss 0.415731
Time taken for 1 epoch 45.804314613342285 sec

Epoch 13 Batch 0 Loss 0.3978
Epoch 13 Batch 100 Loss 0.4445
Epoch 13 Batch 200 Loss 0.3886
Epoch 13 Batch 300 Loss 0.3905
Epoch 13 Loss 0.395104
Time taken for 1 epoch 46.59288954734802 sec

Epoch 14 Batch 0 Loss 0.3933
Epoch 14 Batch 100 Loss 0.3902
Epoch 14 Batch 200 Loss 0.3981
Epoch 14 Batch 300 Loss 0.3542
Epoch 14 Loss 0.374481
Time taken for 1 epoch 47.27163743972778 sec

Epoch 15 Batch 0 Loss 0.3870
Epoch 15 Batch 100 Loss 0.3795
Epoch 15 Batch 200 Loss 0.3413
Epoch 15 Batch 300 Loss 0.3652
Epoch 15 Loss 0.355828
Time taken for 1 epoch 47.83658313751221 sec

Epoch 16 Batch 0 Loss 0.3481
Epoch 16 Batch 100 Loss 0.3419
Epoch 16 Batch 200 Loss 0.3293
Epoch 16 Batch 300 Loss 0.3081
Epoch 16 Loss 0.339233
Time taken for 1 epoch 47.61441230773926 sec

Epoch 17 Batch 0 Loss 0.3473
Epoch 17 Batch 100 Loss 0.3858
Epoch 17 Batch 200 Loss 0.3316
Epoch 17 Batch 300 Loss 0.3405
Epoch 17 Loss 0.327854
Time taken for 1 epoch 48.05988645553589 sec

Epoch 18 Batch 0 Loss 0.3211
Epoch 18 Batch 100 Loss 0.2949
Epoch 18 Batch 200 Loss 0.3149
Epoch 18 Batch 300 Loss 0.2979
Epoch 18 Loss 0.306653
Time taken for 1 epoch 49.181567907333374 sec

Epoch 19 Batch 0 Loss 0.3237
Epoch 19 Batch 100 Loss 0.3084
Epoch 19 Batch 200 Loss 0.2751
Epoch 19 Batch 300 Loss 0.2800
Epoch 19 Loss 0.294841
Time taken for 1 epoch 48.989463329315186 sec

Epoch 20 Batch 0 Loss 0.2953
Epoch 20 Batch 100 Loss 0.2998
Epoch 20 Batch 200 Loss 0.2749
Epoch 20 Batch 300 Loss 0.2625
Epoch 20 Loss 0.282680
Time taken for 1 epoch 48.99758243560791 sec


plt.plot(loss_plot)
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.title('Loss Plot')
plt.show()

png

Podpis!

  • Funkcja oceny jest podobna do pętli treningowej, z tym wyjątkiem, że nie używasz tu wymuszania nauczyciela. Dane wejściowe dla dekodera w każdym kroku czasowym to jego poprzednie przewidywania wraz ze stanem ukrytym i wyjściem kodera.
  • Przestań przewidywać, kiedy model przewiduje token końcowy.
  • I przechowuj wagi uwagi dla każdego kroku czasowego.
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 l in range(len_result):
        temp_att = np.resize(attention_plot[l], (8, 8))
        ax = fig.add_subplot(len_result//2, len_result//2, l+1)
        ax.set_title(result[l])
        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> elephant standing in large pool of water in <unk> area <end>
Prediction Caption: a large elephant standing on dirt road <end>

png

Wypróbuj na własnych obrazach

Dla zabawy poniżej udostępniliśmy metodę, której możesz użyć do podpisania własnych zdjęć modelem, który właśnie wytrenowaliśmy. Pamiętaj, że został on wyszkolony na stosunkowo niewielkiej ilości danych, a Twoje obrazy mogą różnić się od danych treningowych (więc przygotuj się na dziwne wyniki!)

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 person on a surfboard on top of a wave in <end>

png

png

Następne kroki

Gratulacje! Właśnie wytrenowałeś z uwagą model podpisów graficznych. Następnie spójrz na ten przykład neuronowe tłumaczenie maszynowe z uwagą . Wykorzystuje podobną architekturę do tłumaczenia zdań hiszpańskich i angielskich. Możesz również poeksperymentować z uczeniem kodu w tym notesie w innym zestawie danych.