Oglądaj prezentacje, sesje produktowe, warsztaty i nie tylko z playlisty Google I / O See

Podpisy graficzne z uwagą wizualną

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

Biorąc pod uwagę obraz podobny do poniższego przykładu, 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 to kompleksowy przykład. 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

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

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

Opcjonalnie: ogranicz rozmiar zestawu uczącego

Aby przyspieszyć szkolenie w tym samouczku, 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> two brown bears lying together and relaxing on a rock <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. Wyodrębnisz 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ć, zapisujesz słownik 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ć dzięki bardziej wyrafinowanej strategii buforowania (na przykład dzielenie obrazów na fragmenty 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 podpisy (na przykład dzieląc na spacje). To daje nam słownictwo zawierające wszystkie unikalne 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 wypeł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, 6001, 6001)

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, aby przewidzieć 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
    # 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 je 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 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:
            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

Podpis!

  • Funkcja oceny jest podobna do pętli szkoleniowej, z tym wyjątkiem, że nie używasz tu wymuszania nauczyciela. Dane wejściowe do 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 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

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

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.