8. 创建图像描述模型
- 学习地址:https://www.cloudskillsboost.google/course_templates/542
- 本课程教您如何使用深度学习创建图像字幕模型。您将了解图像字幕模型的不同组件,例如编码器和解码器,以及如何训练和评估您的模型。在本课程结束时,您将能够创建自己的图像字幕模型并使用它们为图像生成字幕
- 视频1:https://youtu.be/7a5SMSCzTg8
引言
大家好,我是谷歌高级解决方案实验室的机器学习工程师 Takumi。在这个系列中,我们将深入探讨生成式人工智能背后的核心技术。特别是,在本篇博客中,我们将讨论如何通过使用编码器-解码器、注意力机制和一些Transformer等技术,实际创建一个简单的图像字幕生成模型。
图像字幕任务和数据集
图像字幕任务是指基于输入的图像,生成描述该图像内容的一段文本。对于此任务,我们将使用一组成对的图像和文本数据。我们的目标是建立和训练一个可以根据图像生成这些类型的文本描述的模型。
编码器-解码器模型架构
我们将通过构建一个编码器-解码器模型来实现这一目标。在这种模型中,编码器和解码器处理不同形式的数据—图像和文本。
- 编码器: 我们将图像传递给编码器。编码器从图像中提取信息,并创建一些特征向量。
- 解码器: 然后,我们将这些特征向量传递给解码器。解码器实际上构建标题,通过生成一个接一个的单词。
实现细节
图像编码器
在代码方面,我们使用来自Keras应用程序的经典InceptionResNetV2
作为图像编码器。但同样,这可以是任何其他的图像主干网络。
文本解码器
解码器部分有点复杂,让我们仔细看一下它的组件和工作原理:
- 嵌入层: 创建词嵌入,将单词转换为向量表示。
- GRU层: GRU(门控循环单元)是RNN(递归神经网络)的一种变体。它接受输入,更新其内部状态,并生成输出。通过传递像文本这样的顺序数据,它可以保持与先前输入(例如先前的单词)的顺序依赖关系。
- 注意力层: GRU的输出进入注意力层。这一层混合了来自编码器(图像)和解码器(文本)的信息。
这个解码器本身是一个迭代操作。因此,通过自回归地一次又一次地调用它,我们最终可以生成完整的文本。
因此,通过传递像文本这样的顺序数据,它可以保持与先前输入的顺序依赖关系,例如先前的单词。GRU 输出进入注意力层,该层混合了文本和图像的信息。
注意力机制
在 TensorFlow Keras 中,我们可以像使用其他层一样,轻松地添加预定义的注意力层。例如,我们可以使用 tf.keras.layers.Attention
。如果您想使用更多类似 Transformer 的架构,可以选择 tf.keras.layers.MultiHeadAttention
,它使用多个注意力头。
在注意力层内部,该层从文本数据中关注图像特征,通过这样做,它可以通过混合两种信息来计算注意力分数。
在代码中,注意力层有两个输入:gru_output
和 encoder_output
。在内部,gru_output
用作注意力的查询和键,而 encoder_output
用作值。
Skip Connection(残差连接)
解码器架构还包含一个名为 skip-connection 或残差连接的设计模式。这种模式涉及将中间层的输出添加到之后的层。这种架构从 ResNet 开始就变得非常流行,并且在深度神经网络,特别是 Transformer 中被广泛使用。
使用 skip-connection,尤其是在设计非常深的神经网络时,可以帮助避免梯度消失和爆炸问题,从而使网络更容易训练。
推理阶段:为任意图像生成字幕
现在,我们转向如何在实际应用中使用训练过的模型。在推理阶段,我们的目标是为给定的图像生成一段描述性的文本。以下是实现这一目标的步骤:
- 生成 GRU 初始状态,并创建一个
<start>
令牌: 在训练阶段,TensorFlow Keras 可以自动处理每个序列的 GRU 状态。但在推理阶段,我们需要编写逻辑来明确地初始化 GRU 状态。同时,我们传递一个特殊的<start>
令牌,表示句子的开头。 - 将输入图像传递给编码器并提取特征向量: 与训练阶段相同,我们首先将输入图像传递给编码器,以获得与图像相关的特征向量。
- 自回归地调用解码器:
在这个步骤中,我们通过自回归地调用解码器,一次生成一个单词,直到生成一个特殊的
<end>
令牌,表示句子的结束,或者直到生成的字幕达到某个预定义的最大长度。
下面是一个推理阶段的示例代码:
pythonCopy code
def generate_caption(image):
initial_state = gru.get_initial_state(image)
input_token = '<start>'
caption = []
for _ in range(max_caption_length):
input_vector = token_to_vector(input_token)
features, state = gru(input_vector, initial_state)
attention = attention_layer([features, encoder_output])
output = output_layer(attention)
predicted_token = vector_to_token(output)
caption.append(predicted_token)
if predicted_token == '<end>':
break
input_token = predicted_token
initial_state = state
return ' '.join(caption)
初始化GRU状态和<start>令牌
推理阶段的第一步涉及到两个关键的初始化:
- GRU状态初始化: 在这个例子中,我们使用一个全零向量来初始化GRU的状态。
- <start>令牌初始化: 我们将
<start>
设置为解码器的第一个输入词。
这里使用的 word_to_index
函数将单词转换为整数索引,这是标准的文本处理技术。
图像预处理和编码
接下来,我们对输入图像进行一系列的预处理步骤,并将其传递给我们训练过的编码器:
- 读取和解码JPEG图像: 这将图像文件转换为可以处理的数值数据。
- 调整图像大小: 图像从任意尺寸调整到特定的分辨率。
- 缩放像素值: 将像素值从0到255的范围缩放到0到1的范围。
自回归字幕生成
最后一个阶段涉及到一个解码循环,该循环稍微复杂一些,因此需要仔细解释:
在这个循环中,解码器被反复调用,每次都接收三个输入:
dec_input
(解码器输入): 这应该是前一步预测出的单词令牌。如果这是第一次迭代,则这将是<start>
令牌。gru_state
(GRU状态): 这是当前的GRU状态。值得注意的是,解码器在每步都会输出一个更新的GRU状态。features
(图像特征): 这些是用编码器从图像中提取出的特征。
通过传递这些参数,我们可以获得对下一个单词的实际预测。
代码示例
以下是推理阶段的伪代码示例:
pythonCopy code
def generate_caption(image):
gru_state = initialize_with_zeros()
dec_input = word_to_index('<start>')
features = encode_image(image)
for _ in range(max_caption_length):
predictions, gru_state = decoder(dec_input, gru_state, features)
predicted_token = index_to_word(np.argmax(predictions))
if predicted_token == '<end>':
break
dec_input = word_to_index(predicted_token)
return ' '.join(caption)
总结
在这篇博客中,我们深入探讨了一个图像字幕生成模型的推理阶段,详细解释了如何为新的图像生成描述性字幕的步骤和对应的代码实现。
虽然这是一个简单的图像到文本的生成模型,但它的核心思想和结构与大型语言生成模型(例如 Google Bard)非常相似。它们都是根据某种形式的输入信息(在我们的例子中是图像),通过一个自回归的过程,一个单词接一个单词地生成输出,同时嵌入了大量参数的知识。
在下一篇博客中,我们将深入探讨整个实现的代码,并演示模型实际生成的字幕效果。
感谢您的阅读,敬请期待下一篇博客!
创建一个简单的图像字幕生成模型:代码笔记本解析
在这篇博客中,我们将一起浏览一个完整的代码笔记本,了解如何构建一个简单的图像字幕生成模型。这个笔记本包含了所有构建和使用图像字幕模型的过程和说明。
环境和依赖设置
在笔记本的第一个单元格中,我们首先安装所有需要的依赖项,包括 TensorFlow 和 Keras。这里列举了模型所需的各种层,包括 GRU、添加层、注意力层、密集层、嵌入层和层归一化层。
pythonCopy code
# 示例
!pip install tensorflow keras
定义超参数
在下一个单元格中,我们定义了一些模型的超参数。这些参数包括词汇量大小,指定用于特征提取的编码器模型(在这个例子中是 InceptionResNetV2),以及图像的维度和特征形状。特征形状(8, 8, 1536)是由 InceptionResNetV2 产生的输出形状。
pythonCopy code
# 示例
VOCAB_SIZE = 5000
FEATURE_EXTRACTOR = 'inception_resnet_v2'
IMG_HEIGHT = 299
IMG_WIDTH = 299
IMG_CHANNELS = 3
FEATURES_SHAPE = (8, 8, 1536)
加载和预处理数据
接下来,我们使用 TensorFlow 数据集(TFDS)来加载COCO字幕数据集。加载数据后,我们应用一系列预处理步骤,以便将图像和相应的文本描述进行配对。
pythonCopy code
# 示例import tensorflow_datasets as tfds
def preprocess_image(image, caption):
# 对图像进行预处理
...
return image, caption
# 加载数据集
data, info = tfds.load("coco_captions", with_info=True)
# 对数据集中的每一个样本应用预处理
data = data.map(preprocess_image)
查看样本数据
为了确保我们的数据加载和预处理步骤是正确的,笔记本中也包括了一个部分来可视化一些样本数据。这使我们能够直观地查看图像及其对应的文本描述。
pythonCopy code
# 示例import matplotlib.pyplot as plt
# 从数据集中获取一个样本for img, caption in data.take(1):
plt.imshow(img)
print("Caption:", caption.numpy())
文本数据预处理
由于我们的数据集包含文本(字幕)数据,这些数据需要以标准方式进行预处理。在这个步骤中,我们为每个字幕添加特殊的开始和结束标记。这些标记允许我们的模型识别字幕的开始和结束。
pythonCopy code
def preprocess_caption(caption):
return '<start> ' + caption + ' <end>'
创建分词器
接下来,我们创建一个分词器,它将文本转换为模型可以处理的数值数据。具体来说,分词器将每个单词映射到一个唯一的整数索引。TensorFlow 提供了一个非常方便的 TextVectorization
模块,我们可以用它来自动创建这样的分词器。
pythonCopy code
from tensorflow.keras.layers.experimental.preprocessing import TextVectorization
# 创建一个TextVectorization层
tokenizer = TextVectorization(max_tokens=VOCAB_SIZE)
# 在字幕数据上拟合分词器
tokenizer.adapt(data.map(lambda img, cap: cap))
数据集准备
现在,我们已经定义了如何预处理图像和文本数据,接下来我们需要组织这些数据,形成我们的训练、验证和测试数据集。对于每一对图像和字幕,我们还需要创建一个目标(或标签)序列,它是原始字幕序列向右移动一个位置的版本。这将用于训练我们的模型预测下一个单词。
pythonCopy code
def create_dataset(images, captions):
# 使用分词器将文本数据转换为整数序列
tokenized_captions = tokenizer(captions)
# 创建目标序列
target = tokenized_captions[:, 1:]
return (images, tokenized_captions), target
# 创建训练、验证和测试数据集
train_data = data['train'].map(create_dataset)
val_data = data['val'].map(create_dataset)
test_data = data['test'].map(create_dataset)
构建模型
在这一部分,我们将详细讨论如何实现我们的图像字幕生成模型。这个模型主要由两部分组成:编码器和解码器。
编码器
编码器的作用是将输入图像转换为一组特征向量,这些特征向量包含了图像的重要信息。在我们的模型中,编码器是一个预训练的卷积神经网络(CNN)。
pythonCopy code
from tensorflow.keras.applications import InceptionResNetV2
# 使用预训练的InceptionResNetV2作为编码器
image_model = InceptionResNetV2(weights='imagenet', include_top=False)
new_input = image_model.input
hidden_layer = Flatten()(image_model.layers[-1].output)
encoder = Model(inputs=new_input, outputs=hidden_layer)
# 冻结编码器的权重
encoder.trainable = False
解码器
解码器部分的任务是根据编码器提供的图像特征生成描述图像内容的文本。这部分模型的结构稍微复杂一些,因为它涉及到序列生成,并且在这个过程中使用了注意力机制。
pythonCopy code
from tensorflow.keras.layers import Embedding, GRU, Dense, Attention, Add, LayerNormalization
# 定义解码器的各个组件
embedding_layer = Embedding(input_dim=VOCAB_SIZE, output_dim=EMBEDDING_DIM)
gru_layer = GRU(RNN_UNITS, return_sequences=True, return_state=True)
attention_layer = Attention()
add_layer = Add()
layer_norm = LayerNormalization()
output_layer = Dense(VOCAB_SIZE)
# 构建解码器模型# ...
组合模型
现在我们有了单独的编码器和解码器,我们需要将它们组合在一起,形成一个完整的图像字幕生成模型。
pythonCopy code
from tensorflow.keras.models import Model
# 定义模型的输入和输出
inputs = (encoder.input, captions_input)
outputs = decoder_output
# 创建模型
model = Model(inputs, outputs)
自定义损失函数
由于我们的任务是生成文本序列,并且这些序列可能具有不同的长度,我们需要特殊处理损失函数。我们使用稀疏分类交叉熵作为损失函数,但是需要屏蔽填充的部分。
pythonCopy code
import tensorflow as tf
def loss_function(real, pred):
# 使用tf.keras.losses.sparse_categorical_crossentropy计算原始损失
loss = tf.keras.losses.sparse_categorical_crossentropy(real, pred, from_logits=True)
# 创建一个屏蔽,以便我们可以忽略填充位置的损失
mask = tf.math.logical_not(tf.math.equal(real, 0))
mask = tf.cast(mask, dtype=loss.dtype)
loss *= mask
# 返回平均损失return tf.reduce_mean(loss)
编译模型
最后,我们需要编译我们的模型,这样我们就可以开始训练它了。
pythonCopy code
# 编译模型
model.compile(optimizer='adam', loss=loss_function)
训练模型
一旦我们的模型被编译,我们就可以开始训练它了。在本教程中,我使用了一个单一的 GPU 来进行训练。每个 epoch 大约需要 15 至 20 分钟的时间。当然,你可以根据自己的需求进行更多的训练,这可能会得到更好的结果。
pythonCopy code
# 训练模型
history = model.fit(dataset, epochs=1)
推理与生成字幕
训练完成后,我们可以使用我们的模型为新的图像生成字幕。在这一步,我们需要稍微修改解码器的结构,以便我们可以手动控制 GRU 的状态。
重构解码器
在推理阶段,我们需要更细粒度地控制解码器的行为。因此,我们重构解码器模型,使其可以接收额外的 GRU 状态输入,并返回新的 GRU 状态。
pythonCopy code
# 重构解码器# ...
自定义推理循环
为了生成字幕,我们编写一个自定义推理循环,它会一次产生一个单词,直到生成一个完整的句子。
pythonCopy code
def generate_caption(image_path):
# 初始化 GRU 状态
initial_state = np.zeros((1, RNN_UNITS))
# 预处理图像并通过编码器
image = preprocess_image(image_path)
features = encoder.predict(image)
# 初始化解码器输入为 <start> 令牌
dec_input = np.array([tokenizer.word_index['<start>']])
# 存储生成的字幕
caption = []
for _ in range(MAX_CAPTION_LENGTH):
# 通过解码器
predictions, state = decoder.predict([dec_input, features, initial_state])
# 选择下一个单词的 ID
predicted_id = np.argmax(predictions[0])
# 将新单词添加到字幕
caption.append(tokenizer.index_word[predicted_id])
# 判断是否达到了 <end> 令牌或达到了最大长度if (predicted_id == tokenizer.word_index['<end>']) or len(caption) >= MAX_CAPTION_LENGTH:
break# 更新解码器的输入和状态
dec_input = np.array([predicted_id])
initial_state = state
return ' '.join(caption)
生成字幕示例
现在我们可以使用我们的模型为新的图像生成字幕了。
pythonCopy code
# 生成字幕
image_path = "path/to/your/image.jpg"
caption = generate_caption(image_path)
print("Generated Caption: ", caption)
小结
我们讨论了如何训练一个图像字幕生成模型,并如何使用它来为新的图像生成字幕。我们首先讨论了如何训练模型,然后深入了解了推理阶段的步骤,包括重构解码器和编写一个自定义的推理循环。
这个模型虽然相对简单,但它能够生成有意义的、与图像内容相关的文本。这一点表明,即使是一个简单的基于注意力机制的模型也能产生令人印象深刻的结果。
感谢您的观看!
课程1 字幕
00:01大家好,我是谷歌高级解决方案实验室的机器学习工程师 Takumi。目前,很多人都在谈论生成式人工智能及其新进展。正如你们中的一些人可能知道的那样,谷歌和谷歌云也发布了如此多的生成人工智能
00:22相关的新产品和功能。但在这个视频系列中,我们的目标不是创建最先进的生成式 AI,也不是介绍 Google Cloud 新产品。相反,我们将解释它们背后的技术类型。
00:43特别是在本视频中,我将讨论如何通过使用编码器-解码器、注意力机制和一些 Transformer 等技术实际创建一个非常简单的生成模型,即图像捕获模型。
01:01如果您不熟悉这些概念,我建议您在此之前查看其他讨论它们的视频。好吧,如果你准备好了,让我们首先谈谈图像捕捉任务和数据集。
01:19我们将使用这种数据集。如您所见,有很多成对的图像和文本数据。我们的目标是建立和训练一个可以生成这些类型的文本说明的模型
01:32基于图像。我们将通过构建这种模型来实现它。如您所见,它是一个编码器-解码器模型。但在这种情况下,编码器和解码器处理不同形式的数据、图像和文本。
01:51我们将图像传递给编码器,它从图像中提取信息,并创建一些特征向量。然后将向量传递给实际构建标题的解码器,通过生成
02:07一个接一个的话。我们将通过构建这种模型来实现它。如您所见,它是一个编码器-解码器模型。但在这种情况下,编码器和解码器处理不同形式的数据、图像和文本。
02:14我们将图像传递给编码器,它从图像中提取信息,并创建一些特征向量。然后将向量传递给实际构建标题的解码器,通过生成
02:22一个接一个的话。所以代码也很简单。在代码方面,我们将在下一个视频中看到整个笔记本,所以这里我们只关注一些重要的行。
02:39如您所见,在这种情况下,我们使用来自 keras 应用程序的经典 InceptionResNetV2。但同样,这可以是任何图像主干 下一部分,解码器有点复杂。
02:56因此,让我们非常仔细地看一下。这是解码器的架构。它一个一个地获取单词,混合来自编码器的单词和图像的信息
03:12输出,并尝试预测下一个单词。这个解码器本身就是一个迭代操作。因此,通过自回归地一次又一次地调用它,我们最终可以生成文本。这种解码器设计有很多变体,但在这里我们构建类似 Transformer 的
03:35体系结构,尽管我们仍然使用 RNN 或 GRU。让我们放大每个组件。第一个嵌入层创建词嵌入,这在其他视频中讨论过。我们将它传递给 GRU 层。
03:56如果你忘了GRU是什么,它是递归神经网络的变种,也可以叫RNN。RNN 接受输入,更新其内部状态,并生成输出。
04:09因此,通过传递像文本这样的顺序数据,它可以保持与先前输入的顺序依赖关系,例如先前的单词。GRU 输出进入注意力层,该层混合了文本和图像的信息。
04:26在 TensorFlow Keras 中,我们可以像使用其他层一样使用预定义层。有多种实现,但这里我们简单地使用 tf.keras.layers.Attention。但是如果你想使用更多类似 Transformer 的架构,你也可以选择 tf.keras.layers.MultiHeadAttention
04:48它使用多个注意力头。您可以简单地以几乎相同的方式切换和使用它。在注意力层内部,它看起来像你可能已经在另一个
05:01关于注意力机制的视频,但这里的独特之处在于,它从文本数据中关注图像特征。通过这样做,它可以通过混合两种信息来计算注意力分数。
05:18回到代码,你可以看到这个注意力层有两个输入,gru_output 和 encoder_output。在内部,gru_output 用作注意力查询和键,encoder_output 用作值。最后一个组件是添加层和层归一化层。
05:45添加层只是添加两个相同形状的向量。如您所见,GRU 输出被传递到我们讨论的注意力层,并直接传递到这个添加层。这两个流最终合并到这个添加层中。
06:05这种架构称为skip-connection,是自ResNet以来非常流行的深度神经网络设计模式。所以也叫残差连接。这种跳过连接很有用,尤其是当你想设计一个非常深的神经网络时
06:27网络。它也被用在 Transformer 中。有了这个,现在我们可以构建整个解码器。所以我们准备好使用字幕来训练编码器-解码器图像字幕模型
06:42数据集。我们将在下一个视频中看到它是如何工作的。但在继续下一个视频之前,我想更多地解释一下推理阶段,我们实际上可以为任意图像生成字幕,因为这个过程
06:59可能看起来有点复杂。在这里您可以看到三个步骤,我们将在自定义推理函数中实现这些步骤。数字 1”生成 GRU 初始状态,并创建一个 <start> 令牌。
07:16在训练阶段,TensorFlow Keras 可以自动处理每个序列的 GRU 状态,但在这个推理阶段,由于我们设计了自己的自定义循环,我们需要编写一个逻辑
07:29去处理它。所以在每个字幕的开始,我们用一些值明确地初始化 GRU 状态。同时,记住我们的解码器是一个自回归函数,但是因为我们
07:48在推理开始时没有得到任何单词预测,我们传递 <start> 令牌,这是一个特殊的令牌,表示句子的开头。第二,将输入图像传递给编码器并提取特征向量,正如我们所讨论的,
08:07和数字 3,...,在这个 for 循环中,我们通过自回归调用解码器来定义字幕生成的所有过程。<end> token也是一个特殊的token,表示句子的结束。
08:42所以当我们的解码器生成这个token的时候,我们就可以完成这个for循环了。或者,当字幕的长度达到某个数字时,您可以跳出循环。
08:56让我们一个一个地看代码。在第一步中,我们初始化两件事:GRU 状态和 <start> 令牌。在这种情况下,GRU 状态只是用 0 个向量初始化。
09:12我们将 <start> 设置为解码器的第一个输入词。关于此处使用的 word_to_index 函数,我将在下一个视频中进行解释,但基本上它只是将单词标记为单词标记,这是标准的文本处理
09:30技术。在下一步中,我们对输入图像进行一些预处理,并将其传递给我们训练的编码器。在图像预处理方面,它读取和解码 jpeg,从任意位置调整大小
09:49resolution 到特定的分辨率,并将比例从 0 更改为 255,更改为 0 到 1。最后一个阶段,decode for loop,它有点复杂,所以我将在下一个解释
10:07视频与实际代码更详细,但这里的主要内容是通过传递 3 个东西来调用解码器。dec_input 表示decoder input,应该有前面预测的word token
10:24迭代。正如我们所说,如果这是第一次迭代,这将是 <start> 标记。gru_state 是我们讨论的当前 gru 状态。请注意,解码器还会输出更新后的 gru_state。
10:45最后但同样重要的是特征,这是我们用编码器提取的图像特征。通过传递它们,我们可以获得实际的下一个单词预测。这是一个非常简单的图像文本生成模型,但是这种迭代是
11:05即使在非常大的语言生成模型中也非常相似,比如 Google Bard。他们基本上是根据一些信息,一个一个地自回归地预测下一个单词,学习嵌入大量参数的知识。
11:27在下一个视频中,我将带您浏览整个笔记本。然后,我们将检查该模型可以生成什么样的字幕。非常感谢您的收看,下个视频见。
课程2 字幕
视频2:https://youtu.be/c8VO_Lf1cjA
00:00大家好。我是谷歌高级解决方案实验室的机器学习工程师 Takumi 这是图像字幕会议的下半部分。如果你还没有看过前半部分,我建议先看看。
00:15在本视频中,我将带您浏览整个代码笔记本,帮助您了解如何创建一个非常简单的生成模型。所有设置信息
00:27写在 ASL GitHub 存储库中。您可以在幻灯片或此视频下方的说明中找到链接。设置好 Vertex AI 后,我在工作台环境中克隆了 repo。
00:42按照说明,您可以在 asl-ml-immersion notebooks and multimodal solutions 下找到图像字幕笔记本。干得好。您可以在 Python 笔记本中找到图像字幕点。所以请打开这个文件
01:06在这里,您可以看到构建和使用我们在上一个视频中讨论的图像字幕模型的所有过程和说明。让我们从第一个单元格来看。
01:19在第一个单元格中。当然,我们安装了所有依赖项,包括 tensorflow keras,在这里我们可以找到 TensorFlow keras 层并安装图像字幕模型所需的顺序层
01:34包括 GRU,添加层,注意力层或密集层嵌入层现在层归一化层。所以让我们一个一个地运行,在下一个单元格中我们定义一些超参数,包括词汇量,
01:58这意味着我们将使用多少词汇表来制作图片说明。或者你可以找到一个特征提取器,这意味着我们要在编码器模型中使用什么样的模型。
02:10所以在这种情况下,正如我们在之前的视频中讨论的那样,我们指定了 inception resnet v2,这是非常经典的基于 CNN 的模型,图像下方的所有定义、高度、宽度通道和特征形状都来自
02:27inception、resnet v2 的定义,尤其是这个特征形状。8, 8, 1536 是这个 inception resnet v2 产生的形状。所以让我们用这种方式来定义酷。
02:49所以在下一个单元格中,我们将从 tfds 加载数据,这意味着 TensorFlow 数据集。所以 TensorFlow 数据集以这个名称“coco captions”托管这个字幕数据集
03:03所以我们可以指定这个名称和加载数据。加载数据后,我们可以通过一些预处理功能,获取此处定义的图像级别,获取图像级别,
03:18在这里我们可以找到一些预处理,非常基本的预处理,包括改变图像的大小或者图像尺度的变化以及返回图像张量
03:32和标题同时。那么让我们以同样的方式运行,让我们来看看一些例子。在这里我们可以看到,例如,
03:54一个随机的例子,每对图像和文字对我来说都很有意义。如此宽的盘子,上面有烤三明治、薯条和薯条。和另一个图像的另一个标题。
04:11我们有很多形象。所以如果你想看另一个例子,你可以再次运行这个单元格,你会看到另一个例子。让我们继续吧。
04:24因此,由于我们有文本数据,我们需要以某种标准方式预处理该文本数据。因此,在此单元格中,我们添加开始和结束特殊标记,我们也在幻灯片中讨论过。
04:41所以通过添加这个,我们可以将这个标记作为一种特殊符号来处理,这个开始说话意味着特殊标记,这意味着句子的开头。
04:54同样,结束标记表示句子的结尾。所以我们可以像趋势图一样添加这些东西并传递这个函数。
05:07他们让我们继续前进。这是一个非常重要的预处理。所以现在我们有文本数据,标题数据。所以我们要创建分词器。所以通过创建分词器,我们可以分词
05:29像一些索引的开始标记或猫或狗。在 TensorFlow 中,这非常容易。您可以只使用此文本矢量化模块,您可以通过传递所有数据或标题数据来调用
05:48到此文本矢量化层,因此在我的环境中需要大约 5 分钟的时间。所以让我们等到它完成。现在它完成了。现在让我们试试这个分词器
06:07通过传递一些示例句子,开始标记这是一个句子结束标记。所以现在你可以看到它以这种方式被标记化了。所以在这里你可以找到很多填充物
06:25通过更改此最大字幕长度,您可以在此处控制此填充的长度。但在这种情况下,我们指定 64。因此字幕的顺序将以这种方式填充
06:41直到这个最大字幕长度。并且以同样的方式你可以看到这个分词器的行为 这非常有用。创建后,您可以在不同的字幕中应用此分词器
07:01并将文本数据转换为白色标记处的标记。在这一点上创建转换器很好。所以在这里我们可以找到字符串查找层,字符串查找层,
07:16和创建转换器从想要索引和索引到想要。所以我们稍后会用到这些模块。所以这非常有用,然后我们可以创建最终数据集。
07:32所以这是一个非常重要的部分。所以我们有火车。我们将添加额外的 create_ds 函数,这个函数如您所见,它返回图像张量标题,这是元组
07:50图像张量将进入编码器,标题将进入解码器。我们还创建了 Target,即标签。在这个函数中你可以发现这个目标是从标题创建的
08:06by the is shifting just caption in the in a word. 好的。通过这样做,我们正在创建我们将创建一个移位的标题,这意味着下一个单词 A
08:23我们将利用它作为目标。因此,让我们定义并应用此函数并以指定的批量大小创建一个批次,一切就绪。那么让我们来看看一些数据集。
08:49干得好。所以你可以在这个形状中找到图像,在形状中找到标题,在与标题相同的形状中找到标题,因为我们只是在移动。
09:00和不。所以我们用零值填充移位的部分看起来不错。所以下一部分是模型。大部分模型代码已经在之前的视频中解释过了,所以我将快速过一遍。
09:17但是,如果您对此不是很熟悉并且对此很有信心,那么您可以返回到上一张幻灯片并检查编码器和解码器内部发生了什么。
09:28所以在这段视频中。所以让我们快速运行这些东西。所以这是编码器,正如你所看到的,我们只是将 inception resnet V2 应用于图像数据。
09:43请注意,在这种情况下,我们冻结了这个 cnn 的大部分部分,因为我们不需要接受培训。这个模型,基本上这种骨干是预训练的
09:56通过在这种情况下使用庞大的数据集图像网络数据集。所以当然,如果你想训练,再次微调,这是可能的,但在这种情况下,我们希望你只保留权重
10:10预训练。那么接下来让我们继续解码器。正如我们讨论的那样它有点复杂,在这里你可以找到很多关于注意力层的说明
10:25以及解码器的步骤,我们在之前的视频中讨论过。在这里我们可以找到一个定义,这样你就可以找到嵌入层来创建嵌入和第一个 GRU 层
10:41注意力层添加层归一化层和最终的密集层。所以让我们这样定义。所以模型看起来像这个嵌入层 GRU attention add layer normalization,然后这个。
11:01在定义解码器和编码器之后它有这么多参数,我们可以创建最终模型 TF Keras 模型并定义输入和输出。正如你所看到的,它有两个输入,
11:23图像输入进入编码器,文字输入进入解码器,输出应该是解码器输出。现在模型已准备就绪,但在运行训练之前,我们需要像往常一样定义丢失的功能。
11:45因此,就损失而言,我们的模型基本上是一个分类模型,因为解码器为每个类、每个词类、每个词汇生成了很多概率。
11:58所以我们可以像往常一样使用稀疏分类过程熵来解决分类问题。但在这种情况下,我们的数据被填充了,所以它有很多零值和很多无意义的值。
12:15所以我们想删除那部分。因此,为了做到这一点,我们定义了这个自定义损失函数,然后一切就绪。所以让我们编译模型
12:31我们可以进行培训。在训练方面,用一个 GPU,一个 T4 GPU 训练一个 epoch 需要 15 分钟到 20 分钟。所以如果你想
12:46添加额外的时代,没关系。你可以这样做,我认为你可以获得稍微好一点的结果。但是 epoch 一个 epoch 足以检查它是如何工作的。
13:00因此,让我们将其保持为一个并运行训练,让我们等待 15 到 20 分钟,直到它完成训练。现在训练已经完成,让我们用它来制作字幕,
13:16但在此之前,我们需要重建用于推理的解码器,以便手动控制生长状态。正如我们在上一个视频中所说的那样。所以在这个单元格中,通过重新使用经过训练的层,
13:32我们正在创建一个推理模型。所以这里可以找到train decoder GRU train decoder attention等等。与train训练模型相比,我们增加了
13:51GRU 状态到它的 Io。对于输入,我们添加 GRU 状态输入,对于输出,我们添加 Jerry 的状态作为输出。所以通过这样做我们可以控制 GRU 状态
14:07在推理组。好的,让我们使用这个自定义推理循环函数生成文本。我们已经在之前的视频中讨论过它应该有什么样的组件,但让我们简单回顾一下。
14:27所以首先我们初始化 GRU 状态,在这种情况下只是简单地用零向量初始化。然后在这里我们获取图像,然后预处理图像并将其传递给编码器。
14:41当然,训练编码器和我们可以获得特征图像的特征,然后再将其传递给我们的解码器。所以我们也初始化这个,这个开始标记作为第一个单词
14:59然后我们将一次又一次地重复整个循环并一个一个地生成文本。所以一个看起来像这个编码解码器的步骤,当然,
15:13然后它根据概率返回很多预测。所以有很多方法可以找到最终的词,
15:25最终从名单中选出一大堆病房的概率。但在这种情况下,我们随机使用词 kind of stochasticly 来引入一些随机性。所以正是这些代码行正在这样做
15:42并最终拾取一些单词,然后将其带回边缘,通过使用分词器将其带回单词标记中的单词并附加到列表中。
15:58所以最终我们应该得到一些字幕。那么让我们来看看结果吧。所以定义了这个函数,让我们调用它,这样你就可以看到这个图像的标题示例。
16:17因此,此示例图像直接位于此中。只需将其传递给 JPEG,它就会返回五个字幕。看起来像站在球棒旁边的棒球运动员
16:33棒球场上的接球手或类似的东西。它在语法上并不完美,但您仍然可以看到它正在生成文本、生成多个文本并生成有意义的文本。
16:51而且我们还可以看到我们的模型正在捕获重要信息,例如棒球或接球手或一个人站在另一个人或棒球场旁边或类似的东西。
17:08所以它仍然不是很完美,但它正在生成非常有意义的文本。这很令人惊讶,不是吗?所以模型非常简单。我们只是堆叠编码器和解码器,然后传递图像
17:25将图像数据加盖到编码器,解码器以自回归方式逐一生成字幕。所以只要把它堆叠起来,我们就可以创建这种非常小的生成模型。
17:40好的。目前有很多生成式大型语言模型。当然,他们拥有更复杂、更大的网络,并训练了更大的数据集。但架构可能看起来类似于这个简单的模型。
17:56非常感谢您观看此视频。我希望你喜欢。如果你喜欢这个演示文稿,你会在我们的 ASL Github 存储库中找到更多关于笔记本的 90 plus immersion
18:10如果你觉得它有用。请不要忘记为存储库加注星标。
资源:
https://github.com/GoogleCloudPlatform/asl-ml-immersion/blob/master/notebooks/multi_modal/solutions/image_captioning.ipynb