이 페이지는 Cloud Translation API를 통해 번역되었습니다.
Switch to English

시각적주의가있는 이미지 캡션

TensorFlow.org에서보기 Google Colab에서 실행 GitHub에서 소스보기 노트북 다운로드

아래 예와 같은 이미지가 주어지면 "파도를 타는 서퍼"와 같은 캡션을 생성하는 것이 목표입니다.

남자 서핑

이미지 소스 ; 라이센스 : Public Domain

이를 수행하기 위해 사용자는주의 기반 모델을 사용하여 모델이 캡션을 생성 할 때 이미지의 어떤 부분에 초점을 맞추는 지 볼 수 있습니다.

예측

모델 아키텍처는 Show, Attend and Tell : Neural Image Caption Generation with Visual Attention과 유사합니다.

이 노트북은 종단 간 예입니다. 노트북을 실행하면 MS-COCO 데이터 세트를 다운로드하고 Inception V3를 사용하여 이미지 하위 집합을 사전 처리 및 캐시하고 인코더-디코더 모델을 학습시키고 학습 된 모델을 사용하여 새 이미지에 대한 캡션을 생성합니다.

이 예에서는 비교적 적은 양의 데이터 (데이터 세트에 이미지 당 여러 캡션이 있기 때문에 약 20,000 개의 이미지에 대한 처음 30,000 개의 캡션)에 대해 모델을 학습합니다.

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 re
import numpy as np
import os
import time
import json
from glob import glob
from PIL import Image
import pickle

MS-COCO 데이터 세트 다운로드 및 준비

MS-COCO 데이터 세트 를 사용하여 모델을 학습합니다. 데이터 세트에는 82,000 개 이상의 이미지가 포함되어 있으며 각 이미지에는 5 개 이상의 서로 다른 캡션 주석이 있습니다. 아래 코드는 데이터 세트를 자동으로 다운로드하고 추출합니다.

# 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 [==============================] - 104s 0us/step
Downloading data from http://images.cocodataset.org/zips/train2014.zip
13510574080/13510573713 [==============================] - 800s 0us/step

선택 사항 : 훈련 세트의 크기 제한

이 자습서의 교육 속도를 높이기 위해 30,000 개의 캡션과 해당 이미지의 하위 집합을 사용하여 모델을 교육합니다. 더 많은 데이터를 사용하도록 선택하면 자막 품질이 향상됩니다.

# Read the json file
with open(annotation_file, 'r') as f:
    annotations = json.load(f)

# Store captions and image names in vectors
all_captions = []
all_img_name_vector = []

for annot in annotations['annotations']:
    caption = '<start> ' + annot['caption'] + ' <end>'
    image_id = annot['image_id']
    full_coco_image_path = PATH + 'COCO_train2014_' + '%012d.jpg' % (image_id)

    all_img_name_vector.append(full_coco_image_path)
    all_captions.append(caption)

# Shuffle captions and image_names together
# Set a random state
train_captions, img_name_vector = shuffle(all_captions,
                                          all_img_name_vector,
                                          random_state=1)

# Select the first 30000 captions from the shuffled set
num_examples = 30000
train_captions = train_captions[:num_examples]
img_name_vector = img_name_vector[:num_examples]
len(train_captions), len(all_captions)
(30000, 414113)

InceptionV3를 사용하여 이미지 전처리

다음으로 InceptionV3 (Imagenet에서 사전 학습 됨)을 사용하여 각 이미지를 분류합니다. 마지막 컨벌루션 레이어에서 특징을 추출합니다.

먼저 다음과 같이 이미지를 InceptionV3의 예상 형식으로 변환합니다.

  • 이미지 크기를 299x299 픽셀로 조정
  • preprocess_input 메서드를 사용 하여 이미지전처리 하여 InceptionV3을 훈련하는 데 사용되는 이미지 형식과 일치하는 -1에서 1 범위의 픽셀을 포함하도록 이미지를 정규화합니다.
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

InceptionV3를 초기화하고 사전 훈련 된 Imagenet 가중치로드

이제 출력 레이어가 InceptionV3 아키텍처의 마지막 컨볼 루션 레이어 인 tf.keras 모델을 생성합니다. 이 레이어의 출력 모양은 8x8x2048 입니다. 이 예제에서주의를 사용하고 있기 때문에 마지막 컨벌루션 레이어를 사용합니다. 병목 현상이 발생할 수 있으므로 훈련 중에이 초기화를 수행하지 않습니다.

  • 네트워크를 통해 각 이미지를 전달하고 결과 벡터를 사전 (image_name-> feature_vector)에 저장합니다.
  • 모든 이미지가 네트워크를 통해 전달 된 후 사전을 피클하고 디스크에 저장합니다.
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)

InceptionV3에서 추출한 기능 캐싱

InceptionV3로 각 이미지를 사전 처리하고 출력을 디스크에 캐시합니다. RAM에 출력을 캐싱하는 것이 더 빠르지 만 메모리를 많이 사용하므로 이미지 당 8 * 8 * 2048 부동 소수점이 필요합니다. 글을 쓰는 시점에서 이것은 Colab의 메모리 제한 (현재 12GB 메모리)을 초과합니다.

보다 정교한 캐싱 전략 (예 : 임의 액세스 디스크 I / O를 줄이기 위해 이미지 분할)을 사용하면 성능이 향상 될 수 있지만 더 많은 코드가 필요합니다.

캐싱은 GPU가있는 Colab에서 실행하는 데 약 10 분이 걸립니다. 진행률 표시 줄을 보려면 다음을 수행하십시오.

  1. tqdm 설치 :

    !pip install -q tqdm

  2. tqdm 가져 오기 :

    from tqdm import tqdm

  3. 다음 행을 변경하십시오.

    for img, path in image_dataset:

    에:

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

캡션 전처리 및 토큰 화

  • 먼저 캡션을 토큰 화합니다 (예 : 공백 분할). 이를 통해 데이터의 모든 고유 단어 (예 : "서핑", "축구"등)에 대한 어휘를 얻을 수 있습니다.
  • 다음으로 어휘 크기를 상위 5,000 단어로 제한합니다 (메모리 절약을 위해). 다른 모든 단어를 "UNK"(알 수 없음) 토큰으로 바꿉니다.
  • 그런 다음 단어-색인 및 색인-단어 매핑을 만듭니다.
  • 마지막으로 모든 시퀀스를 가장 긴 시퀀스와 동일한 길이로 채 웁니다.
# 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)
train_seqs = tokenizer.texts_to_sequences(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)

데이터를 훈련 및 테스트로 분할

# Create training and validation sets using an 80-20 split
img_name_train, img_name_val, cap_train, cap_val = train_test_split(img_name_vector,
                                                                    cap_vector,
                                                                    test_size=0.2,
                                                                    random_state=0)
len(img_name_train), len(cap_train), len(img_name_val), len(cap_val)
(24000, 24000, 6000, 6000)

학습용 tf.data 데이터 세트 만들기

이미지와 캡션이 준비되었습니다! 다음으로 모델 학습에 사용할 tf.data 데이터 세트를 만들어 보겠습니다.

# 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.experimental.AUTOTUNE)

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

모델

재미있는 사실 : 아래 디코더는 신경망 기계 번역 에 대한 예제의 디코더와 동일합니다.

모델 아키텍처는 Show, Attend and Tell 논문 에서 영감을 얻었습니다.

  • 이 예에서는 InceptionV3의 하위 컨벌루션 계층에서 특성을 추출하여 모양 벡터 (8, 8, 2048)를 제공합니다.
  • 당신은 그것을 (64, 2048)의 모양으로 스쿼시합니다.
  • 이 벡터는 CNN 인코더 (단일 완전 연결 계층으로 구성됨)를 통해 전달됩니다.
  • RNN (여기서 GRU)은 다음 단어를 예측하기 위해 이미지를 처리합니다.
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)

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

    # attention_weights shape == (batch_size, 64, 1)
    # you get 1 at the last axis because you are applying score to self.V
    attention_weights = tf.nn.softmax(self.V(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_)

검문소

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)

훈련

  • .npy 파일에 저장된 기능을 추출한 다음 해당 기능을 인코더를 통해 전달합니다.
  • 인코더 출력, 숨김 상태 (0으로 초기화 됨) 및 디코더 입력 (시작 토큰)이 디코더로 전달됩니다.
  • 디코더는 예측과 디코더 숨김 상태를 반환합니다.
  • 그런 다음 디코더 숨김 상태가 모델로 다시 전달되고 예측이 손실을 계산하는 데 사용됩니다.
  • 디코더에 대한 다음 입력을 결정하기 위해 교사 강제를 사용합니다.
  • 교사 강제는 대상 단어가 디코더의 다음 입력으로 전달되는 기술입니다.
  • 마지막 단계는 그래디언트를 계산하고이를 옵티 마이저에 적용하고 역 전파하는 것입니다.
# 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.0024
Epoch 1 Batch 100 Loss 1.1099
Epoch 1 Batch 200 Loss 0.9210
Epoch 1 Batch 300 Loss 0.9704
Epoch 1 Loss 1.032838
Time taken for 1 epoch 78.63309955596924 sec

Epoch 2 Batch 0 Loss 0.8746
Epoch 2 Batch 100 Loss 0.8212
Epoch 2 Batch 200 Loss 0.7379
Epoch 2 Batch 300 Loss 0.8287
Epoch 2 Loss 0.793472
Time taken for 1 epoch 37.38605332374573 sec

Epoch 3 Batch 0 Loss 0.6944
Epoch 3 Batch 100 Loss 0.6196
Epoch 3 Batch 200 Loss 0.7060
Epoch 3 Batch 300 Loss 0.6688
Epoch 3 Loss 0.724284
Time taken for 1 epoch 37.36788463592529 sec

Epoch 4 Batch 0 Loss 0.7018
Epoch 4 Batch 100 Loss 0.7010
Epoch 4 Batch 200 Loss 0.6184
Epoch 4 Batch 300 Loss 0.6427
Epoch 4 Loss 0.680071
Time taken for 1 epoch 37.096845865249634 sec

Epoch 5 Batch 0 Loss 0.6541
Epoch 5 Batch 100 Loss 0.6477
Epoch 5 Batch 200 Loss 0.7273
Epoch 5 Batch 300 Loss 0.6632
Epoch 5 Loss 0.643497
Time taken for 1 epoch 38.30310583114624 sec

Epoch 6 Batch 0 Loss 0.6118
Epoch 6 Batch 100 Loss 0.6131
Epoch 6 Batch 200 Loss 0.5591
Epoch 6 Batch 300 Loss 0.5657
Epoch 6 Loss 0.612107
Time taken for 1 epoch 38.99693965911865 sec

Epoch 7 Batch 0 Loss 0.5890
Epoch 7 Batch 100 Loss 0.6080
Epoch 7 Batch 200 Loss 0.5746
Epoch 7 Batch 300 Loss 0.5707
Epoch 7 Loss 0.580912
Time taken for 1 epoch 38.77566838264465 sec

Epoch 8 Batch 0 Loss 0.5400
Epoch 8 Batch 100 Loss 0.5292
Epoch 8 Batch 200 Loss 0.6036
Epoch 8 Batch 300 Loss 0.4905
Epoch 8 Loss 0.550826
Time taken for 1 epoch 38.637632608413696 sec

Epoch 9 Batch 0 Loss 0.5294
Epoch 9 Batch 100 Loss 0.5334
Epoch 9 Batch 200 Loss 0.5032
Epoch 9 Batch 300 Loss 0.5519
Epoch 9 Loss 0.520766
Time taken for 1 epoch 38.54794478416443 sec

Epoch 10 Batch 0 Loss 0.4945
Epoch 10 Batch 100 Loss 0.5077
Epoch 10 Batch 200 Loss 0.4902
Epoch 10 Batch 300 Loss 0.4618
Epoch 10 Loss 0.493960
Time taken for 1 epoch 38.564149141311646 sec

Epoch 11 Batch 0 Loss 0.5011
Epoch 11 Batch 100 Loss 0.4379
Epoch 11 Batch 200 Loss 0.4245
Epoch 11 Batch 300 Loss 0.4398
Epoch 11 Loss 0.465660
Time taken for 1 epoch 38.4509859085083 sec

Epoch 12 Batch 0 Loss 0.4839
Epoch 12 Batch 100 Loss 0.4430
Epoch 12 Batch 200 Loss 0.4449
Epoch 12 Batch 300 Loss 0.4329
Epoch 12 Loss 0.434701
Time taken for 1 epoch 37.219146728515625 sec

Epoch 13 Batch 0 Loss 0.4523
Epoch 13 Batch 100 Loss 0.4536
Epoch 13 Batch 200 Loss 0.4012
Epoch 13 Batch 300 Loss 0.3818
Epoch 13 Loss 0.409857
Time taken for 1 epoch 37.572779417037964 sec

Epoch 14 Batch 0 Loss 0.3965
Epoch 14 Batch 100 Loss 0.4271
Epoch 14 Batch 200 Loss 0.3263
Epoch 14 Batch 300 Loss 0.3605
Epoch 14 Loss 0.383877
Time taken for 1 epoch 37.23471689224243 sec

Epoch 15 Batch 0 Loss 0.4026
Epoch 15 Batch 100 Loss 0.3835
Epoch 15 Batch 200 Loss 0.3515
Epoch 15 Batch 300 Loss 0.3453
Epoch 15 Loss 0.356534
Time taken for 1 epoch 37.1954619884491 sec

Epoch 16 Batch 0 Loss 0.3576
Epoch 16 Batch 100 Loss 0.4040
Epoch 16 Batch 200 Loss 0.3457
Epoch 16 Batch 300 Loss 0.2976
Epoch 16 Loss 0.343869
Time taken for 1 epoch 37.72642493247986 sec

Epoch 17 Batch 0 Loss 0.3553
Epoch 17 Batch 100 Loss 0.3038
Epoch 17 Batch 200 Loss 0.3193
Epoch 17 Batch 300 Loss 0.2877
Epoch 17 Loss 0.313893
Time taken for 1 epoch 37.10719585418701 sec

Epoch 18 Batch 0 Loss 0.3163
Epoch 18 Batch 100 Loss 0.2660
Epoch 18 Batch 200 Loss 0.2767
Epoch 18 Batch 300 Loss 0.2749
Epoch 18 Loss 0.291296
Time taken for 1 epoch 37.06640863418579 sec

Epoch 19 Batch 0 Loss 0.3423
Epoch 19 Batch 100 Loss 0.2830
Epoch 19 Batch 200 Loss 0.2490
Epoch 19 Batch 300 Loss 0.2922
Epoch 19 Loss 0.275518
Time taken for 1 epoch 37.14825773239136 sec

Epoch 20 Batch 0 Loss 0.3110
Epoch 20 Batch 100 Loss 0.2570
Epoch 20 Batch 200 Loss 0.2369
Epoch 20 Batch 300 Loss 0.2567
Epoch 20 Loss 0.257957
Time taken for 1 epoch 37.29774260520935 sec


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

png

표제!

  • 평가 기능은 여기에서 교사 강제를 사용하지 않는다는 점을 제외하면 훈련 루프와 유사합니다. 각 시간 단계에서 디코더에 대한 입력은 숨겨진 상태 및 인코더 출력과 함께 이전 예측입니다.
  • 모델이 종료 토큰을 예측할 때 예측을 중지하십시오.
  • 그리고 모든 시간 단계에 대한주의 가중치를 저장합니다.
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> people putting candles on a cake that is sitting on a plate <end>
Prediction Caption: a birthday cake cutting the cake <end>

png

자신의 이미지에 사용해보십시오

재미를 위해 아래에서 방금 훈련 한 모델로 자신의 이미지에 캡션을 사용할 수있는 방법을 제공했습니다. 상대적으로 적은 양의 데이터로 훈련되었으며 이미지가 훈련 데이터와 다를 수 있습니다 (이상한 결과에 대비하십시오!).

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)
Prediction Caption: a man wearing glasses wearing a tie wearing a tie <end>

png

png

다음 단계

축하합니다! 주의를 기울여 이미지 캡션 모델을 학습했습니다. 다음으로, 주의기울인 신경 기계 번역 예제를 살펴보십시오. 유사한 아키텍처를 사용하여 스페인어와 영어 문장을 번역합니다. 다른 데이터 세트에서이 노트북의 코드 학습을 실험 할 수도 있습니다.