总览
了解图像字幕生成的注意力机制
实现注意力机制以在python中生成字幕
介绍
注意机制是人类所具有的复杂的认知能力。当人们收到信息时,他们可以有意识地选择一些主要信息,而忽略其他次要信息。
这种自我选择的能力称为注意力。注意机制使神经网络能够专注于其输入子集以选择特定特征。
近年来,神经网络推动了图像字幕的巨大发展。研究人员正在为计算机视觉和序列到序列建模系统寻找更具挑战性的应用程序。他们试图用人类的术语描述世界。之前我们看到了通过Merge架构进行图像标题处理的过程,今天,我们将探讨一种更为复杂而精致的设计来解决此问题。
注意机制已成为深度学习社区中从业者的首选方法。它最初是在使用Seq2Seq模型的神经机器翻译的背景下设计的,但今天我们将看看它在图像字幕中的实现。
注意机制不是将整个图像压缩为静态表示,而是使显着特征在需要时动态地走在最前列。当图像中有很多杂波时,这一点尤其重要。
让我们举个例子来更好地理解:
我们的目标是生成一个标题,例如“两只白狗在雪地上奔跑”。为此,我们将看到如何实现一种称为Bahdanau的注意力或本地注意力的特定类型的注意力机制。
通过这种方式,我们可以看到模型在生成标题时将焦点放在图像的哪些部分。此实现将需要深度学习的强大背景。
目录
1、问题陈述的处理
2、了解数据集
3、实现
3.1、导入所需的库
3.2、数据加载和预处理
3.3、模型定义
3.4、模型训练
3.5、贪婪搜索和BLEU评估
4、下一步是什么?
5、尾注
问题陈述的处理
编码器-解码器图像字幕系统将使用将产生隐藏状态的预训练卷积神经网络对图像进行编码。然后,它将使用LSTM解码此隐藏状态并生成标题。
对于每个序列元素,将先前元素的输出与新序列数据结合起来用作输入。这为RNN网络提供了一种记忆,可能使字幕更具信息性和上下文感知能力。
但是RNN的训练和评估在计算上往往很昂贵,因此在实践中,内存只限于少数几个元素。注意模型可以通过从输入图像中选择最相关的元素来帮助解决此问题。使用Attention机制,首先将图像分为n个部分,然后我们计算每个图像的图像表示形式。当RNN生成新单词时,注意机制将注意力集中在图像的相关部分上,因此解码器仅使用特定的图片的一部分。
在Bahdanau或本地关注中,关注仅放在少数几个来源位置。由于全球关注集中于所有目标词的所有来源方词,因此在计算上非常昂贵。为了克服这种缺陷,本地注意力选择只关注每个目标词的编码器隐藏状态的一小部分。
局部注意力首先找到对齐位置,然后在其位置所在的左右窗口中计算注意力权重,最后对上下文向量进行加权。局部注意的主要优点是减少了注意机制计算的成本。
在计算中,本地注意力不是考虑源语言端的所有单词,而是根据预测函数预测在当前解码时要对齐的源语言端的位置,然后在上下文窗口中导航, 仅考虑窗口中的单词。
Bahdanau注意的设计
编码器和解码器的所有隐藏状态用于生成上下文向量。注意机制将输入和输出序列与前馈网络参数化的比对得分进行比对。它有助于注意源序列中最相关的信息。该模型基于与源位置和先前生成的目标词关联的上下文向量来预测目标词。
为了参考原始字幕评估字幕,我们使用一种称为BLEU的评估方法。它是使用最广泛的评估指标。它用于分析要评估的翻译语句与参考翻译语句之间n-gram的相关性。
在本文中,多个图像等效于翻译中的多个源语言句子。BLEU的优点是考虑更长的匹配信息,它认为的粒度是n元语法字而不是单词。BLEU的缺点是无论匹配哪种n-gram,都将被视为相同。
我希望这使您对我们如何处理此问题陈述有所了解。让我们深入研究实施!
了解数据集
我使用了Flickr8k数据集,其中每个图像都与五个不同的标题相关联,这些标题描述了所收集的图像中描述的实体和事件。
Flickr8k体积小巧,可以使用CPU在低端笔记本电脑/台式机上轻松进行培训,因此是一个很好的入门数据集。
我们的数据集结构如下:
让我们实现字幕生成的注意力机制!
步骤1:导入所需的库
在这里,我们将利用Tensorflow创建模型并对其进行训练。大部分代码归功于TensorFlow教程。如果您想要GPU进行训练,则可以使用Google Colab或Kaggle笔记本。
import
import
as
import
as
from
import
from
import
from
import
import
from
import
import
as
import
"ignore"
import
import
import
as
from
import
from
import
from
import
from
import
from
import
from
import
from
import
from
import
from
import
from
import
from
import
from
import
from
import
from
import
from
import
from
import
from
import
from
import
from
import
步骤2:数据加载和预处理
定义图像和字幕路径,并检查数据集中总共有多少图像。
"/content/gdrive/My Drive/FLICKR8K/Flicker8k_Dataset"
"/content/gdrive/My Drive/FLICKR8K/Flickr8k_text/Flickr8k.token.txt"
"Total Images in Dataset = {}"
输出如下:
我们创建一个数据框来存储图像ID和标题,以便于使用。
'r'
for
in
'\n'
'\t'
if
1
continue
0
"#"
1
"filename"
"index"
"caption"
'index'
'filename'
'caption'
'2258277193_586949ec62.jpg.1'
输出如下:
接下来,让我们可视化一些图片及其5个标题:
5
224
3
1
10
20
for
in
10
14
'/'
"caption"
"filename"
2
1
2
'off'
0
1
0
for
in
0
20
1
输出如下:
接下来,让我们看看我们当前的词汇量是多少:
for
in
'Vocabulary Size: %d'
输出如下:
接下来执行一些文本清理,例如删除标点符号,单个字符和数字值:
def
remove_punctuation
(text_original)
return
def
remove_single_character
(text)
""
for
in
if
1
" "
return
def
remove_numeric
(text)
""
for
in
if
" "
return
def
text_clean
(text_original)
return
for
in
"caption"
现在让我们看一下清理后词汇量的大小
for
in
'Clean Vocabulary Size: %d'
输出如下:
接下来,我们将所有标题和图像路径保存在两个列表中,以便我们可以使用路径集立即加载图像。我们还向每个字幕添加了“ ”和“ ”标签,以便模型可以理解每个字幕的开始和结束。
"/content/gdrive/My Drive/FLICKR8K/Flicker8k_Dataset/"
for
in
"caption"
'
'
'
'
10
输出如下:
for
in
"filename"
10
输出如下:
现在您可以看到我们有40455个图像路径和标题。
{len(all_img_name_vector)}
{len(all_captions)}
输出如下:
我们将仅取每个批次的40000个,以便可以正确选择批次大小,即如果批次大小= 64,则可以选择625个批次。为此,我们定义了一个函数来将数据集限制为40000个图像和标题。
def
data_limiter
(num,total_captions,all_img_name_vector)
1
return
40000
步骤3:模型定义
让我们使用VGG16定义图像特征提取模型。我们必须记住,这里不需要分类图像,只需要为图像提取图像矢量即可。因此,我们从模型中删除了softmax层。我们必须先将所有图像预处理为相同大小,即224×224,然后再将其输入模型。
def
load_image
(image_path)
3
224
224
return
False
'imagenet'
-1
输出如下:
接下来,让我们将每个图片名称映射到要加载图片的函数:
64
我们提取特征并将其存储在各自的.npy文件中,然后将这些特征通过编码器传递.NPY文件存储在任何计算机上重建数组所需的所有信息,包括dtype和shape信息。
for
in
0
-1
3
for
in
"utf-8"
接下来,我们标记标题,并为数据中所有唯一的单词建立词汇表。我们还将词汇量限制在前5000个单词以节省内存。我们将更换的话不词汇与令牌
5000
"
"
'!"#$%&()*+.,-/:;=?@[\]^_`{|}~ '
'
'
0
0
'
'
'post'
让我们可视化填充的训练和标题以及标记化的向量:
3
输出如下:
3
输出如下:
接下来,我们可以计算所有字幕的最大和最小长度:
def
calc_max_length
(tensor)
return
for
in
def
calc_min_length
(tensor)
return
for
in
'Max Length of any caption : Min Length of any caption = '
" : "
输出如下:
接下来,使用80-20拆分创建训练和验证集:
0.2
0
定义训练参数:
64
1000
256
512
1
512
49
def
map_func
(img_name, cap)
'utf-8'
'.npy'
return
# Use map to load the numpy files in parallel
lambda
接下来,让我们重点定义编码器-解码器的体系结构。本文定义的架构类似于论文“ Show and Tell:一种神经图像字幕生成器”中描述的架构:-
VGG-16编码器定义如下:
class
VGG16_Encoder
(tf.keras.Model)
# This encoder passes the features through a Fully connected layer
def
__init__
(self, embedding_dim)
# shape after fc == (batch_size, 49, embedding_dim)
0.5
None
None
def
call
(self, x)
#x= self.dropout(x)
return
我们基于GPU / CPU功能定义RNN
def
rnn_type
(units)
if
return
True
True
'glorot_uniform'
else
return
True
True
'sigmoid'
'glorot_uniform'
接下来,使用Bahdanau注意定义RNN解码器:
class
Rnn_Local_Decoder
(tf.keras.Model)
def
__init__
(self, embedding_dim, units, vocab_size)
True
True
'glorot_uniform'
0.5
None
None
-1
0.99
0.001
True
True
'zeros'
'ones'
'zeros'
'ones'
None
None
None
None
# Implementing Attention Mechanism
1
def
call
(self, x, features, hidden)
# features shape ==> (64,49,256) ==> Output from ENCODER
# hidden shape == (batch_size, hidden_size) ==>(64,512)
# hidden_with_time_axis shape == (batch_size, 1, hidden_size) ==> (64,1,512)
1
# score shape == (64, 49, 1)
# Attention Function
'''e(ij) = f(s(t-1),h(j))'''
''' e(ij) = Vattn(T)*tanh(Uattn * h(j) + Wattn * s(t))'''
# self.Uattn(features) : (64,49,512)
# self.Wattn(hidden_with_time_axis) : (64,1,512)
# tf.nn.tanh(self.Uattn(features) + self.Wattn(hidden_with_time_axis)) : (64,49,512)
# self.Vattn(tf.nn.tanh(self.Uattn(features) + self.Wattn(hidden_with_time_axis))) : (64,49,1) ==> score
# you get 1 at the last axis because you are applying score to self.Vattn
# Then find Probability using Softmax
'''attention_weights(alpha(ij)) = softmax(e(ij))'''
1
# attention_weights shape == (64, 49, 1)
# Give weights to the different pixels in the image
''' C(t) = Summation(j=1 to T) (attention_weights * VGG-16 features) '''
1
# Context Vector(64,256) = AttentionWeights(64,49,1) * features(64,49,256)
# context_vector shape after sum == (64, 256)
# x shape after passing through embedding == (64, 1, 256)
# x shape after concatenation == (64, 1, 512)
1
-1
# passing the concatenated vector to the GRU
# shape == (batch_size, max_length, hidden_size)
# x shape == (batch_size * max_length, hidden_size)
-1
2
# Adding Dropout and BatchNorm Layers
# output shape == (64 * 512)
# shape : (64 * 8329(vocab))
return
def
reset_state
(self, batch_size)
return
接下来,我们定义损失函数和优化器:
True
'none'
def
loss_function
(real, pred)
0
return
步骤4:模型训练
接下来,让我们定义培训步骤。我们使用一种称为教师强制的技术,该技术将目标单词作为下一个输入传递给解码器。此技术有助于快速了解正确的序列或序列的正确统计属性。
@tf.function
def
train_step
(img_tensor, target)
0
# initializing the hidden state for each batch
# because the captions are not related from image to image
0
'
'
1
with
as
for
in
1
1
# passing the features through the decoder
# using teacher forcing
1
1
return
接下来,我们训练模型:
20
for
in
0
for
in
if
100
0
print
'Epoch {} Batch {} Loss {:.4f}'
1
1
# storing the epoch end loss value to plot later
print
'Epoch {} Loss {:.6f}'
1
print
'Time taken for 1 epoch {} sec\n'
让我们绘制误差图:
'Epochs'
'Loss'
'Loss Plot'
输出如下:
步骤5:贪婪搜寻和BLEU评估
让我们定义定义字幕的贪婪方法:
def
evaluate
(image)
1
0
0
0
-1
3
'
'
0
for
in
-1
0
if
'
'
return
0
return
另外,我们定义了一个函数来绘制生成的每个单词的注意力图,就像在简介中看到的那样
def
plot_attention
(image, result, attention_plot)
10
10
for
in
8
8
2
2
1
'gray'
0.6
最后,让我们在文章开头为图片生成标题,看看注意力机制关注什么并生成
# captions on the validation set
0
'/content/gdrive/My Drive/FLICKR8K/Flicker8k_Dataset/2319175397_3e586cfaf8.jpg'
# real_caption = ' '.join([tokenizer.index_word[i] for i in cap_val[rid] if i not in [0]])
# remove
and
from the real_caption
' '
1
1
'Two white dogs are playing in the snow'
#remove "
" in result
for
in
if
"
"
for
in
if
"
"
#remove
from result
' '
' '
1
0
100
print
'Real Caption:'
print
'Prediction Caption:'
输出如下:
您可以看到我们能够生成与真实字幕相同的字幕。让我们尝试一下测试集中的其他图像。
0
' '
for
in
if
not
in
0
' '
1
1
' '
1
0
#remove "
" in result
for
in
if
"
"
#remove
from result
' '
' '
1
0
print
'Real Caption:'
print
'Prediction Caption:'
{round(time.time()-start)}
输出如下:
您可以看到,即使我们的字幕与真实字幕有很大不同,它仍然非常准确。它能够识别出女人的黄色衬衫和她的手在口袋里。
让我们看看另一个:
0
' '
for
in
if
not
in
0
# remove
and
from the real_caption
' '
1
1
' '
1
0
#remove "
" in result
for
in
if
"
"
for
in
if
"
"
#remove
from result
' '
' '
1
0
100
print
'Real Caption:'
print
'Prediction Caption:'
在这里,我们可以看到我们的字幕比真实的字幕之一更好地定义了图像。
在那里!我们已经成功实现了用于生成图像标题的注意力机制。
下一步是什么?
近年来,注意力机制得到了高度利用,这仅仅是更多先进系统的开始。您可以实施以改善模型的事情:-利用较大的数据集,尤其是MS COCO数据集或比MS COCO大26倍的Stock3M数据集。实现不同的注意力机制,例如带有Visual Sentinel和的自适应注意力。语义注意实现基于Transformer的模型,该模型的性能应比LSTM好得多。为图像特征提取实现更好的体系结构,例如Inception,Xception和Efficient network。
尾注
这对注意力机制及其如何应用于深度学习应用程序非常有趣。在注意力机制和取得最新成果方面进行了大量研究。请务必尝试我的一些建议!您觉得这篇文章对您有帮助吗?请在下面的评论部分中分享您的宝贵反馈。
程序员摸鱼指南
摸鱼哲学深度践行者
6篇原创内容
Official Account
作者:沂水寒城,CSDN博客专家,个人研究方向:机器学习、深度学习、NLP、CV
Blog: http://yishuihancheng.blog.csdn.net
赞 赏 作 者
更多阅读
2020 年最佳流行 Python 库 Top 10
2020 Python中文社区热门文章 Top 10
5分钟快速掌握 Python 定时任务框架
点击下方阅读原文加入
社区会员
原文链接