ML Community Day คือวันที่ 9 พฤศจิกายน! ร่วมกับเราสำหรับการปรับปรุงจาก TensorFlow, JAX และอื่น ๆ เรียนรู้เพิ่มเติม

คำบรรยายภาพด้วยความใส่ใจ

ดูบน TensorFlow.org ทำงานใน Google Colab ดูแหล่งที่มาบน GitHub ดาวน์โหลดโน๊ตบุ๊ค

จากภาพดังตัวอย่างด้านล่าง เป้าหมายของคุณคือการสร้างคำบรรยาย เช่น "นักเล่นเซิร์ฟโต้คลื่น"

ผู้ชายท่อง

ที่มาของภาพ ; ใบอนุญาต: โดเมนสาธารณะ

เพื่อให้บรรลุสิ่งนี้ คุณจะต้องใช้โมเดลที่อิงตามความสนใจ ซึ่งช่วยให้เราเห็นว่าส่วนใดของรูปภาพที่โมเดลนั้นเน้นขณะที่สร้างคำบรรยาย

การทำนาย

สถาปัตยกรรมรูปแบบจะคล้ายกับการ แสดงเข้าร่วมประชุมและบอก: ประสาทคำบรรยายภาพ Generation กับ Visual เรียน

สมุดบันทึกนี้เป็นตัวอย่างตั้งแต่ต้นจนจบ เมื่อคุณเรียกใช้โน้ตบุ๊คก็ดาวน์โหลด MS-COCO ชุด, preprocesses และเก็บส่วนหนึ่งของภาพโดยใช้จัดตั้งกองทุน V3, การฝึกรูปแบบการเข้ารหัสถอดรหัสและสร้างคำบรรยายภาพใหม่โดยใช้รูปแบบการฝึกอบรม

ในตัวอย่างนี้ คุณจะฝึกแบบจำลองกับข้อมูลจำนวนเล็กน้อย—คำบรรยายภาพ 30,000 รายการแรกสำหรับรูปภาพประมาณ 20,000 ภาพ (เนื่องจากชุดข้อมูลมีคำอธิบายภาพหลายรายการต่อภาพ)

import tensorflow as tf

# You'll generate plots of attention in order to see which parts of an image
# your 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
2021-07-28 01:23:48.839502: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0

ดาวน์โหลดและเตรียมชุดข้อมูล 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 [==============================] - 17s 0us/step
Downloading data from http://images.cocodataset.org/zips/train2014.zip
13510574080/13510573713 [==============================] - 816s 0us/step

ทางเลือก: จำกัดขนาดของชุดการฝึก

เพื่อเพิ่มความเร็วในการฝึกอบรมสำหรับบทช่วยสอนนี้ คุณจะต้องใช้คำบรรยายใต้ภาพจำนวน 30,000 รายการและรูปภาพที่เกี่ยวข้องเพื่อฝึกโมเดลของคุณ การเลือกใช้ข้อมูลเพิ่มเติมจะส่งผลให้คุณภาพคำบรรยายดีขึ้น

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 close up of a person wearing a bow tie  <end>

png

ประมวลผลภาพล่วงหน้าโดยใช้ InceptionV3

ถัดไป คุณจะใช้ InceptionV3 (ซึ่งได้รับการฝึกอบรมล่วงหน้าบน Imagenet) เพื่อจัดประเภทแต่ละภาพ คุณจะแยกคุณสมบัติออกจากเลเยอร์ที่โค้งสุดท้าย

ขั้นแรก คุณจะต้องแปลงรูปภาพเป็นรูปแบบที่คาดไว้ของ InceptionV3 โดย:

  • การปรับขนาดรูปภาพเป็น 299px x 299px
  • preprocess ภาพ โดยใช้ preprocess_input วิธีการที่จะปรับภาพเพื่อให้มีพิกเซลในช่วง -1 ถึง 1 ซึ่งตรงกับรูปแบบของภาพที่ใช้ในการฝึกอบรม 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

เริ่มต้น InceptionV3 และโหลดน้ำหนัก Imagenet ที่ฝึกไว้ล่วงหน้า

ตอนนี้ คุณจะสร้างโมเดล tf.keras โดยที่เลเยอร์เอาต์พุตเป็นเลเยอร์แบบ Convolutional สุดท้ายในสถาปัตยกรรม InceptionV3 รูปร่างของการส่งออกของชั้นนี้คือ 8x8x2048 คุณใช้เลเยอร์ convolutional สุดท้ายเนื่องจากคุณกำลังใช้ความสนใจในตัวอย่างนี้ คุณไม่ได้ดำเนินการเริ่มต้นนี้ระหว่างการฝึกเพราะอาจกลายเป็นคอขวดได้

  • คุณส่งต่อภาพแต่ละภาพผ่านเครือข่ายและเก็บเวกเตอร์ที่ได้ไว้ในพจนานุกรม (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)
2021-07-28 01:38:51.724662: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcuda.so.1
2021-07-28 01:38:52.396689: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-07-28 01:38:52.397733: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1733] Found device 0 with properties: 
pciBusID: 0000:00:05.0 name: Tesla V100-SXM2-16GB computeCapability: 7.0
coreClock: 1.53GHz coreCount: 80 deviceMemorySize: 15.78GiB deviceMemoryBandwidth: 836.37GiB/s
2021-07-28 01:38:52.397773: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
2021-07-28 01:38:52.410682: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublas.so.11
2021-07-28 01:38:52.410775: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublasLt.so.11
2021-07-28 01:38:52.412998: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcufft.so.10
2021-07-28 01:38:52.416058: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcurand.so.10
2021-07-28 01:38:52.418038: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcusolver.so.11
2021-07-28 01:38:52.420886: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcusparse.so.11
2021-07-28 01:38:52.421795: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudnn.so.8
2021-07-28 01:38:52.421939: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-07-28 01:38:52.422953: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-07-28 01:38:52.423807: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1871] Adding visible gpu devices: 0
2021-07-28 01:38:52.424745: I tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 AVX512F FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
2021-07-28 01:38:52.425332: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-07-28 01:38:52.426304: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1733] Found device 0 with properties: 
pciBusID: 0000:00:05.0 name: Tesla V100-SXM2-16GB computeCapability: 7.0
coreClock: 1.53GHz coreCount: 80 deviceMemorySize: 15.78GiB deviceMemoryBandwidth: 836.37GiB/s
2021-07-28 01:38:52.426381: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-07-28 01:38:52.427321: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-07-28 01:38:52.428215: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1871] Adding visible gpu devices: 0
2021-07-28 01:38:52.429333: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
2021-07-28 01:38:53.947925: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1258] Device interconnect StreamExecutor with strength 1 edge matrix:
2021-07-28 01:38:53.947964: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1264]      0 
2021-07-28 01:38:53.947973: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1277] 0:   N 
2021-07-28 01:38:53.949235: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-07-28 01:38:53.950377: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-07-28 01:38:53.951333: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-07-28 01:38:53.952280: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1418] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 14646 MB memory) -> physical GPU (device: 0, name: Tesla V100-SXM2-16GB, pci bus id: 0000:00:05.0, compute capability: 7.0)
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/inception_v3/inception_v3_weights_tf_dim_ordering_tf_kernels_notop.h5
87916544/87910968 [==============================] - 4s 0us/step

การแคชคุณสมบัติที่ดึงมาจาก InceptionV3

คุณจะประมวลผลแต่ละภาพล่วงหน้าด้วย InceptionV3 และแคชเอาต์พุตไปยังดิสก์ การแคชเอาต์พุตใน RAM จะเร็วขึ้น แต่ยังต้องใช้หน่วยความจำมาก โดยต้องใช้ 8 * 8 * 2048 ลอยต่อภาพ ในขณะที่เขียน สิ่งนี้เกินขีดจำกัดหน่วยความจำของ Colab (ปัจจุบันมีหน่วยความจำ 12GB)

สามารถปรับปรุงประสิทธิภาพได้ด้วยกลยุทธ์การแคชที่ซับซ้อนยิ่งขึ้น (เช่น โดยการแบ่งกลุ่มอิมเมจเพื่อลด I/O ของดิสก์เข้าถึงโดยสุ่ม) แต่นั่นก็ต้องใช้โค้ดมากขึ้น

การแคชจะใช้เวลาประมาณ 10 นาทีในการรันใน Colab ด้วย GPU หากคุณต้องการดูแถบความคืบหน้า คุณสามารถ:

  1. ติดตั้ง tqdm :

    !pip install 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.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())
2021-07-28 01:39:00.906130: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:176] None of the MLIR Optimization Passes are enabled (registered 2)
2021-07-28 01:39:00.907617: I tensorflow/core/platform/profile_utils/cpu_utils.cc:114] CPU Frequency: 2000179999 Hz
2021-07-28 01:39:01.056105: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudnn.so.8
2021-07-28 01:39:03.207340: I tensorflow/stream_executor/cuda/cuda_dnn.cc:359] Loaded cuDNN version 8100
2021-07-28 01:39:08.876833: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublas.so.11
2021-07-28 01:39:10.057729: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublasLt.so.11

ประมวลผลล่วงหน้าและแปลงคำบรรยาย

  • ขั้นแรก คุณจะต้องสร้างโทเค็นของคำอธิบายภาพ (เช่น โดยแบ่งช่องว่าง) ข้อมูลนี้จะช่วยให้เราทราบคำศัพท์เฉพาะทั้งหมดในข้อมูล (เช่น "ท่อง" "ฟุตบอล" เป็นต้น)
  • ถัดไป คุณจะจำกัดขนาดคำศัพท์ไว้ที่ 5,000 คำบนสุด (เพื่อบันทึกหน่วยความจำ) คุณจะแทนที่คำอื่นๆ ทั้งหมดด้วยโทเค็น "UNK" (ไม่ทราบ)
  • จากนั้นคุณสร้างการแมปคำต่อดัชนีและดัชนีต่อคำ
  • สุดท้าย คุณวางลำดับทั้งหมดให้มีความยาวเท่ากันกับลำดับที่ยาวที่สุด
# Find the maximum length of any caption in the 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)

แบ่งข้อมูลออกเป็นการฝึกอบรมและการทดสอบ

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)
(24012, 24012, 6007, 6007)

สร้างชุดข้อมูล 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.AUTOTUNE)

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

แบบอย่าง

สนุกจริง: ถอดรหัสดังต่อไปนี้เป็นเหมือนหนึ่งในตัวอย่างสำหรับ ประสาทเครื่องแปลที่มีความสนใจ

สถาปัตยกรรมรูปแบบเป็นแรงบันดาลใจ แสดงเข้าร่วมและบอก กระดาษ

  • ในตัวอย่างนี้ คุณแยกคุณลักษณะออกจากเลเยอร์ Convolutional ล่างของ InceptionV3 ซึ่งจะทำให้เรามีเวกเตอร์ของรูปร่าง (8, 8, 2048)
  • คุณสควอชให้เป็นรูปร่างของ (64, 2048)
  • เวกเตอร์นี้จะถูกส่งผ่าน CNN Encoder (ซึ่งประกอบด้วยเลเยอร์ที่เชื่อมต่ออย่างสมบูรณ์)
  • 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)

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

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

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

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

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

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

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

    self.attention = BahdanauAttention(self.units)

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

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

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

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

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

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

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

    return x, state, attention_weights

  def reset_state(self, batch_size):
    return tf.zeros((batch_size, self.units))
encoder = CNN_Encoder(embedding_dim)
decoder = RNN_Decoder(embedding_dim, units, vocab_size)
optimizer = tf.keras.optimizers.Adam()
loss_object = tf.keras.losses.SparseCategoricalCrossentropy(
    from_logits=True, reduction='none')


def loss_function(real, pred):
  mask = tf.math.logical_not(tf.math.equal(real, 0))
  loss_ = loss_object(real, pred)

  mask = tf.cast(mask, dtype=loss_.dtype)
  loss_ *= mask

  return tf.reduce_mean(loss_)

ด่าน

checkpoint_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:
            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.9004
Epoch 1 Batch 100 Loss 1.0669
Epoch 1 Batch 200 Loss 0.8644
Epoch 1 Batch 300 Loss 0.7575
Epoch 1 Loss 0.971214
Time taken for 1 epoch 150.00 sec

Epoch 2 Batch 0 Loss 0.8469
Epoch 2 Batch 100 Loss 0.7256
Epoch 2 Batch 200 Loss 0.7352
Epoch 2 Batch 300 Loss 0.6788
Epoch 2 Loss 0.740390
Time taken for 1 epoch 52.59 sec

Epoch 3 Batch 0 Loss 0.7644
Epoch 3 Batch 100 Loss 0.6992
Epoch 3 Batch 200 Loss 0.6509
Epoch 3 Batch 300 Loss 0.5881
Epoch 3 Loss 0.665821
Time taken for 1 epoch 52.17 sec

Epoch 4 Batch 0 Loss 0.6438
Epoch 4 Batch 100 Loss 0.5957
Epoch 4 Batch 200 Loss 0.6577
Epoch 4 Batch 300 Loss 0.6111
Epoch 4 Loss 0.617179
Time taken for 1 epoch 51.69 sec

Epoch 5 Batch 0 Loss 0.5838
Epoch 5 Batch 100 Loss 0.6093
Epoch 5 Batch 200 Loss 0.6297
Epoch 5 Batch 300 Loss 0.5459
Epoch 5 Loss 0.579694
Time taken for 1 epoch 51.83 sec

Epoch 6 Batch 0 Loss 0.5737
Epoch 6 Batch 100 Loss 0.5443
Epoch 6 Batch 200 Loss 0.5537
Epoch 6 Batch 300 Loss 0.5825
Epoch 6 Loss 0.546888
Time taken for 1 epoch 51.11 sec

Epoch 7 Batch 0 Loss 0.5380
Epoch 7 Batch 100 Loss 0.5383
Epoch 7 Batch 200 Loss 0.4824
Epoch 7 Batch 300 Loss 0.4913
Epoch 7 Loss 0.517661
Time taken for 1 epoch 50.39 sec

Epoch 8 Batch 0 Loss 0.4940
Epoch 8 Batch 100 Loss 0.5097
Epoch 8 Batch 200 Loss 0.5023
Epoch 8 Batch 300 Loss 0.4532
Epoch 8 Loss 0.490831
Time taken for 1 epoch 50.93 sec

Epoch 9 Batch 0 Loss 0.4901
Epoch 9 Batch 100 Loss 0.4050
Epoch 9 Batch 200 Loss 0.4870
Epoch 9 Batch 300 Loss 0.4596
Epoch 9 Loss 0.465417
Time taken for 1 epoch 51.03 sec

Epoch 10 Batch 0 Loss 0.4536
Epoch 10 Batch 100 Loss 0.4588
Epoch 10 Batch 200 Loss 0.4160
Epoch 10 Batch 300 Loss 0.4242
Epoch 10 Loss 0.440437
Time taken for 1 epoch 51.10 sec

Epoch 11 Batch 0 Loss 0.4445
Epoch 11 Batch 100 Loss 0.4225
Epoch 11 Batch 200 Loss 0.4142
Epoch 11 Batch 300 Loss 0.4043
Epoch 11 Loss 0.418332
Time taken for 1 epoch 51.65 sec

Epoch 12 Batch 0 Loss 0.4569
Epoch 12 Batch 100 Loss 0.3960
Epoch 12 Batch 200 Loss 0.3994
Epoch 12 Batch 300 Loss 0.3606
Epoch 12 Loss 0.396864
Time taken for 1 epoch 50.60 sec

Epoch 13 Batch 0 Loss 0.3756
Epoch 13 Batch 100 Loss 0.3739
Epoch 13 Batch 200 Loss 0.3485
Epoch 13 Batch 300 Loss 0.3188
Epoch 13 Loss 0.377340
Time taken for 1 epoch 50.36 sec

Epoch 14 Batch 0 Loss 0.3615
Epoch 14 Batch 100 Loss 0.3441
Epoch 14 Batch 200 Loss 0.3526
Epoch 14 Batch 300 Loss 0.3481
Epoch 14 Loss 0.357066
Time taken for 1 epoch 50.84 sec

Epoch 15 Batch 0 Loss 0.3696
Epoch 15 Batch 100 Loss 0.3506
Epoch 15 Batch 200 Loss 0.3470
Epoch 15 Batch 300 Loss 0.3242
Epoch 15 Loss 0.339348
Time taken for 1 epoch 50.10 sec

Epoch 16 Batch 0 Loss 0.3250
Epoch 16 Batch 100 Loss 0.3281
Epoch 16 Batch 200 Loss 0.3296
Epoch 16 Batch 300 Loss 0.3140
Epoch 16 Loss 0.321988
Time taken for 1 epoch 50.75 sec

Epoch 17 Batch 0 Loss 0.2916
Epoch 17 Batch 100 Loss 0.2957
Epoch 17 Batch 200 Loss 0.3014
Epoch 17 Batch 300 Loss 0.2942
Epoch 17 Loss 0.306097
Time taken for 1 epoch 51.17 sec

Epoch 18 Batch 0 Loss 0.2839
Epoch 18 Batch 100 Loss 0.2937
Epoch 18 Batch 200 Loss 0.2837
Epoch 18 Batch 300 Loss 0.2717
Epoch 18 Loss 0.291026
Time taken for 1 epoch 49.86 sec

Epoch 19 Batch 0 Loss 0.3187
Epoch 19 Batch 100 Loss 0.3106
Epoch 19 Batch 200 Loss 0.2696
Epoch 19 Batch 300 Loss 0.2765
Epoch 19 Loss 0.278087
Time taken for 1 epoch 51.39 sec

Epoch 20 Batch 0 Loss 0.2823
Epoch 20 Batch 100 Loss 0.2791
Epoch 20 Batch 200 Loss 0.2556
Epoch 20 Batch 300 Loss 0.2598
Epoch 20 Loss 0.264479
Time taken for 1 epoch 51.29 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 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> some people are playing a game in a field <end>
Prediction Caption: a man and little boy that is playing frisbee in a <unk> <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

ลองใช้ภาพของคุณเอง

เพื่อความสนุก ด้านล่างนี้คือวิธีการที่คุณสามารถใช้เพื่อบรรยายภาพของคุณเองด้วยแบบจำลองที่คุณเพิ่งฝึก โปรดทราบว่ามีการฝึกอบรมเกี่ยวกับข้อมูลจำนวนเล็กน้อย และรูปภาพของคุณอาจแตกต่างจากข้อมูลการฝึก

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 5us/step
Prediction Caption: a man in <unk> as he rides a surf board <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

ขั้นตอนถัดไป

ยินดีด้วย! คุณเพิ่งฝึกโมเดลคำบรรยายภาพด้วยความเอาใจใส่ ถัดไปให้ดูที่ตัวอย่างนี้ ประสาทเครื่องแปลภาษาที่มีความสนใจ ใช้สถาปัตยกรรมที่คล้ายกันในการแปลระหว่างประโยคภาษาสเปนและภาษาอังกฤษ คุณยังสามารถทดลองฝึกโค้ดในสมุดบันทึกนี้กับชุดข้อมูลอื่นได้อีกด้วย