Sehen Sie sich Keynotes, Produktsitzungen, Workshops und mehr in Google I / O an. Siehe Wiedergabeliste

Bildunterschrift mit visueller Aufmerksamkeit

Ansicht auf TensorFlow.org In Google Colab ausführen Quelle auf GitHub anzeigen Notizbuch herunterladen

Bei einem Bild wie dem folgenden Beispiel ist es unser Ziel, eine Beschriftung wie "Ein Surfer, der auf einer Welle reitet" zu generieren.

Mann surft

Bildquelle ; Lizenz: Public Domain

Um dies zu erreichen, verwenden Sie ein auf Aufmerksamkeit basierendes Modell, mit dem wir sehen können, auf welche Teile des Bildes sich das Modell konzentriert, wenn es eine Beschriftung generiert.

Prognose

Die Modellarchitektur ähnelt Show, Attend and Tell: Generierung neuronaler Bildunterschriften mit visueller Aufmerksamkeit .

Dieses Notizbuch ist ein End-to-End-Beispiel. Wenn Sie das Notizbuch ausführen, lädt es den MS-COCO- Datensatz herunter, verarbeitet eine Zwischenmenge von Bildern mit Inception V3 vor und verarbeitet sie zwischen, trainiert ein Encoder-Decoder-Modell und generiert mit dem trainierten Modell Untertitel für neue Bilder.

In diesem Beispiel trainieren Sie ein Modell mit einer relativ kleinen Datenmenge - den ersten 30.000 Untertiteln für etwa 20.000 Bilder (da das Dataset mehrere Bildunterschriften pro Bild enthält).

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

Laden Sie den MS-COCO-Datensatz herunter und bereiten Sie ihn vor

Sie verwenden den MS-COCO-Datensatz , um unser Modell zu trainieren. Der Datensatz enthält über 82.000 Bilder, von denen jedes mindestens 5 verschiedene Beschriftungsanmerkungen enthält. Der folgende Code lädt den Datensatz automatisch herunter und extrahiert ihn.

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

Optional: Begrenzen Sie die Größe des Trainingssatzes

Um das Training für dieses Tutorial zu beschleunigen, verwenden Sie eine Teilmenge von 30.000 Bildunterschriften und die entsprechenden Bilder, um unser Modell zu trainieren. Die Entscheidung, mehr Daten zu verwenden, würde zu einer verbesserten Untertitelqualität führen.

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> two brown bears lying together and relaxing on a rock <end>

png

Verarbeiten Sie die Bilder mit InceptionV3 vor

Als Nächstes verwenden Sie InceptionV3 (das in Imagenet vorab trainiert wurde), um jedes Bild zu klassifizieren. Sie extrahieren Features aus der letzten Faltungsebene.

Zunächst konvertieren Sie die Bilder in das erwartete Format von InceptionV3, indem Sie:

  • Ändern der Bildgröße auf 299 x 299 Pixel
  • Verarbeiten Sie die Bilder mit der Methode preprocess_input vor , um das Bild so zu normalisieren, dass es Pixel im Bereich von -1 bis 1 enthält, was dem Format der Bilder entspricht, die zum Trainieren von InceptionV3 verwendet werden.
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

Initialisieren Sie InceptionV3 und laden Sie die vorab trainierten Imagenet-Gewichte

Jetzt erstellen Sie ein tf.keras-Modell, bei dem die Ausgabeebene die letzte Faltungsschicht in der InceptionV3-Architektur ist. Die Form der Ausgabe dieser Ebene ist 8x8x2048 . Sie verwenden die letzte Faltungsschicht, da Sie in diesem Beispiel die Aufmerksamkeit verwenden. Sie führen diese Initialisierung nicht während des Trainings durch, da dies zu einem Engpass führen kann.

  • Sie leiten jedes Bild über das Netzwerk weiter und speichern den resultierenden Vektor in einem Wörterbuch (image_name -> feature_vector).
  • Nachdem alle Bilder durch das Netzwerk übertragen wurden, speichern Sie das Wörterbuch auf der Festplatte.
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

Zwischenspeichern der aus InceptionV3 extrahierten Funktionen

Sie werden jedes Image mit InceptionV3 vorverarbeiten und die Ausgabe auf der Festplatte zwischenspeichern. Das Zwischenspeichern der Ausgabe im RAM wäre schneller, aber auch speicherintensiv und erfordert 8 * 8 * 2048 Floats pro Bild. Zum Zeitpunkt des Schreibens überschreitet dies die Speicherbeschränkungen von Colab (derzeit 12 GB Speicher).

Die Leistung könnte durch eine ausgefeiltere Caching-Strategie verbessert werden (z. B. durch Sharding der Images, um die Festplatten-E / A mit wahlfreiem Zugriff zu reduzieren), dies würde jedoch mehr Code erfordern.

Das Caching dauert ungefähr 10 Minuten, um in Colab mit einer GPU ausgeführt zu werden. Wenn Sie einen Fortschrittsbalken sehen möchten, können Sie:

  1. installiere tqdm :

    !pip install -q tqdm

  2. Importiere tqdm:

    from tqdm import tqdm

  3. Ändern Sie die folgende Zeile:

    for img, path in image_dataset:

    zu:

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

Vorverarbeitung und Tokenisierung der Untertitel

  • Zuerst werden Sie die Beschriftungen tokenisieren (z. B. durch Aufteilen auf Leerzeichen). Dies gibt uns ein Vokabular aller eindeutigen Wörter in den Daten (zum Beispiel "Surfen", "Fußball" usw.).
  • Als nächstes beschränken Sie die Vokabulargröße auf die obersten 5.000 Wörter (um Speicherplatz zu sparen). Sie ersetzen alle anderen Wörter durch das Token "UNK" (unbekannt).
  • Anschließend erstellen Sie Zuordnungen von Wort zu Index und von Index zu Wort.
  • Schließlich füllen Sie alle Sequenzen so auf, dass sie dieselbe Länge wie die längste haben.
# 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)

Teilen Sie die Daten in Training und Test auf

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

Erstellen Sie ein tf.data-Dataset für das Training

Unsere Bilder und Bildunterschriften sind fertig! Als Nächstes erstellen wir ein tf.data-Dataset, das zum Trainieren unseres Modells verwendet werden soll.

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

Modell

Unterhaltsame Tatsache: Der unten stehende Decoder ist identisch mit dem im Beispiel für neuronale maschinelle Übersetzung mit Aufmerksamkeit .

Die Modellarchitektur ist vom Papier Show, Attend and Tell inspiriert.

  • In diesem Beispiel extrahieren Sie die Merkmale aus der unteren Faltungsschicht von InceptionV3 und geben uns einen Formvektor (8, 8, 2048).
  • Sie quetschen das auf eine Form von (64, 2048).
  • Dieser Vektor wird dann durch den CNN-Codierer geleitet (der aus einer einzelnen vollständig verbundenen Schicht besteht).
  • Die RNN (hier GRU) kümmert sich um das Bild, um das nächste Wort vorherzusagen.
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_)

Kontrollpunkt

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)

Ausbildung

  • Sie extrahieren die in den jeweiligen .npy Dateien gespeicherten Features und übergeben diese Features dann über den Encoder.
  • Der Encoderausgang, der versteckte Zustand (auf 0 initialisiert) und der Decodereingang (der das Starttoken ist) werden an den Decoder übergeben.
  • Der Decoder gibt die Vorhersagen und den verborgenen Zustand des Decoders zurück.
  • Der verborgene Zustand des Decoders wird dann an das Modell zurückgegeben und die Vorhersagen werden verwendet, um den Verlust zu berechnen.
  • Verwenden Sie das Erzwingen des Lehrers, um die nächste Eingabe in den Decoder zu bestimmen.
  • Das Erzwingen des Lehrers ist die Technik, bei der das Zielwort als nächste Eingabe an den Decoder übergeben wird.
  • Der letzte Schritt besteht darin, die Farbverläufe zu berechnen und auf den Optimierer und die Rückausbreitung anzuwenden.
# 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 1.9253
Epoch 1 Batch 100 Loss 1.0291
Epoch 1 Batch 200 Loss 0.9413
Epoch 1 Batch 300 Loss 0.8769
Epoch 1 Loss 0.995607
Time taken for 1 epoch 132.04 sec

Epoch 2 Batch 0 Loss 0.8563
Epoch 2 Batch 100 Loss 0.7218
Epoch 2 Batch 200 Loss 0.7245
Epoch 2 Batch 300 Loss 0.8244
Epoch 2 Loss 0.757344
Time taken for 1 epoch 47.62 sec

Epoch 3 Batch 0 Loss 0.7345
Epoch 3 Batch 100 Loss 0.7220
Epoch 3 Batch 200 Loss 0.6223
Epoch 3 Batch 300 Loss 0.6888
Epoch 3 Loss 0.682421
Time taken for 1 epoch 47.19 sec

Epoch 4 Batch 0 Loss 0.6640
Epoch 4 Batch 100 Loss 0.5588
Epoch 4 Batch 200 Loss 0.6241
Epoch 4 Batch 300 Loss 0.6511
Epoch 4 Loss 0.634715
Time taken for 1 epoch 47.03 sec

Epoch 5 Batch 0 Loss 0.6118
Epoch 5 Batch 100 Loss 0.6183
Epoch 5 Batch 200 Loss 0.6742
Epoch 5 Batch 300 Loss 0.5720
Epoch 5 Loss 0.595271
Time taken for 1 epoch 48.29 sec

Epoch 6 Batch 0 Loss 0.6411
Epoch 6 Batch 100 Loss 0.5683
Epoch 6 Batch 200 Loss 0.5578
Epoch 6 Batch 300 Loss 0.5706
Epoch 6 Loss 0.562115
Time taken for 1 epoch 48.05 sec

Epoch 7 Batch 0 Loss 0.5801
Epoch 7 Batch 100 Loss 0.5177
Epoch 7 Batch 200 Loss 0.4892
Epoch 7 Batch 300 Loss 0.5099
Epoch 7 Loss 0.532225
Time taken for 1 epoch 47.26 sec

Epoch 8 Batch 0 Loss 0.5718
Epoch 8 Batch 100 Loss 0.5307
Epoch 8 Batch 200 Loss 0.4917
Epoch 8 Batch 300 Loss 0.5391
Epoch 8 Loss 0.504676
Time taken for 1 epoch 47.27 sec

Epoch 9 Batch 0 Loss 0.5103
Epoch 9 Batch 100 Loss 0.5037
Epoch 9 Batch 200 Loss 0.5306
Epoch 9 Batch 300 Loss 0.4305
Epoch 9 Loss 0.478346
Time taken for 1 epoch 47.07 sec

Epoch 10 Batch 0 Loss 0.4872
Epoch 10 Batch 100 Loss 0.4551
Epoch 10 Batch 200 Loss 0.4485
Epoch 10 Batch 300 Loss 0.4768
Epoch 10 Loss 0.455038
Time taken for 1 epoch 47.06 sec

Epoch 11 Batch 0 Loss 0.4802
Epoch 11 Batch 100 Loss 0.4332
Epoch 11 Batch 200 Loss 0.4093
Epoch 11 Batch 300 Loss 0.3760
Epoch 11 Loss 0.430722
Time taken for 1 epoch 47.48 sec

Epoch 12 Batch 0 Loss 0.4423
Epoch 12 Batch 100 Loss 0.4336
Epoch 12 Batch 200 Loss 0.3845
Epoch 12 Batch 300 Loss 0.4071
Epoch 12 Loss 0.409110
Time taken for 1 epoch 46.93 sec

Epoch 13 Batch 0 Loss 0.4196
Epoch 13 Batch 100 Loss 0.3909
Epoch 13 Batch 200 Loss 0.4037
Epoch 13 Batch 300 Loss 0.3795
Epoch 13 Loss 0.389614
Time taken for 1 epoch 47.15 sec

Epoch 14 Batch 0 Loss 0.4171
Epoch 14 Batch 100 Loss 0.4034
Epoch 14 Batch 200 Loss 0.3479
Epoch 14 Batch 300 Loss 0.3864
Epoch 14 Loss 0.368141
Time taken for 1 epoch 46.99 sec

Epoch 15 Batch 0 Loss 0.3634
Epoch 15 Batch 100 Loss 0.3302
Epoch 15 Batch 200 Loss 0.3254
Epoch 15 Batch 300 Loss 0.3207
Epoch 15 Loss 0.349797
Time taken for 1 epoch 47.52 sec

Epoch 16 Batch 0 Loss 0.3496
Epoch 16 Batch 100 Loss 0.3430
Epoch 16 Batch 200 Loss 0.3176
Epoch 16 Batch 300 Loss 0.3312
Epoch 16 Loss 0.333141
Time taken for 1 epoch 47.37 sec

Epoch 17 Batch 0 Loss 0.3340
Epoch 17 Batch 100 Loss 0.3199
Epoch 17 Batch 200 Loss 0.3235
Epoch 17 Batch 300 Loss 0.3129
Epoch 17 Loss 0.316967
Time taken for 1 epoch 46.89 sec

Epoch 18 Batch 0 Loss 0.3187
Epoch 18 Batch 100 Loss 0.3215
Epoch 18 Batch 200 Loss 0.2849
Epoch 18 Batch 300 Loss 0.2791
Epoch 18 Loss 0.301642
Time taken for 1 epoch 46.76 sec

Epoch 19 Batch 0 Loss 0.2936
Epoch 19 Batch 100 Loss 0.2945
Epoch 19 Batch 200 Loss 0.2915
Epoch 19 Batch 300 Loss 0.2920
Epoch 19 Loss 0.287884
Time taken for 1 epoch 46.66 sec

Epoch 20 Batch 0 Loss 0.2980
Epoch 20 Batch 100 Loss 0.3259
Epoch 20 Batch 200 Loss 0.2756
Epoch 20 Batch 300 Loss 0.2547
Epoch 20 Loss 0.281900
Time taken for 1 epoch 46.99 sec
plt.plot(loss_plot)
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.title('Loss Plot')
plt.show()

png

Bildbeschriftung!

  • Die Auswertungsfunktion ähnelt der Trainingsschleife, außer dass Sie hier kein Lehrer-Forcen verwenden. Die Eingabe in den Decoder zu jedem Zeitschritt sind seine vorherigen Vorhersagen zusammen mit dem verborgenen Zustand und der Codiererausgabe.
  • Stoppen Sie die Vorhersage, wenn das Modell das End-Token vorhersagt.
  • Und speichern Sie die Aufmerksamkeitsgewichte für jeden Zeitschritt.
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> several <unk> of bananas that are leaning against a building <end>
Prediction Caption: a number of bananas and some bananas and bananas <end>
/home/kbuilder/.local/lib/python3.7/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

Probieren Sie es auf Ihren eigenen Bildern

Im Folgenden finden Sie zum Spaß eine Methode, mit der Sie Ihre eigenen Bilder mit dem gerade trainierten Modell beschriften können. Denken Sie daran, dass es mit einer relativ kleinen Datenmenge trainiert wurde und Ihre Bilder möglicherweise von den Trainingsdaten abweichen (seien Sie also auf seltsame Ergebnisse vorbereitet!).

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 on a large wave on a surfboard <end>
/home/kbuilder/.local/lib/python3.7/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

Nächste Schritte

Glückwunsch! Sie haben gerade ein Bildunterschriftenmodell mit Aufmerksamkeit trainiert. Schauen Sie sich als nächstes dieses Beispiel an: Neuronale maschinelle Übersetzung mit Aufmerksamkeit . Es verwendet eine ähnliche Architektur, um zwischen spanischen und englischen Sätzen zu übersetzen. Sie können auch experimentieren, indem Sie den Code in diesem Notizbuch an einem anderen Datensatz trainieren.