图像标题生成器模型结合了计算机视觉和机器翻译的最新进展,利用神经网络生成现实的 图像标题。神经图像标题模型被训练,以最大限度地产生给定输入图像的字幕的可能性。并且可以用来生成新的图像描述。例如,下面是使用
MS COCO数据集.训练的神经图像标题生成器可能生成的标题。
在这篇文章中,我们将通过一个中级水平的教程,介绍如何使用
谷歌的 Show和Tell 模型在Flickr30k数据集上训练图像标题生成器。我们使用
TensorFlow框架来构建、培训和测试我们的模型,因为它相对容易使用,并且拥有一个不断增长的在线社区。
为什么生成标题?
最近在计算机视觉和自然语言处理任务中应用深度神经网络的成功,启发了人工智能研究人员在这些以前分离领域的交集中探索新的研究机会。标题生成模型必须平衡对视觉线索和自然语言的理解。
这两个传统上不相关领域的交叉点有可能在很大程度上实现变革。虽然这项技术有一些简单的应用,比如为YouTube视频生成摘要,或为未标注的图像加标题,但更有创意的应用程序可以极大地提高大部分人的生活质量。与传统的计算机视觉试图使世界更容易访问和理解计算机一样,这项技术有潜力使我们的世界更容易理解。它可以作为一名导游,甚至可以作为日常生活的视觉辅助工具,比如来自意大利人工智能公司
Eyra.的Horus 可穿戴设备
一些装配要求
在我们开始之前,我们需要做一些准备。
首先,您需要安装Tensorflow。如果这是你第一次使用Tensorflow,我们建议你先回顾一下下面的文章:
Hello, TensorFlow! Building and training your first TensorFlow model.。
您将需要pandas、opencv2和Jupyter libraries来运行相关的代码。但是,为了简化安装过程,我们强烈建议您遵循
GitHub repo上的Docker安装指令。
您还需要下载图像嵌入和图片说明,用于Flickr30k数据集。下载链接也提供在
GitHub repo上。
现在,让我们开始吧!
图像标题生成器模型
我们将要培训的模型,每个图像将由一个深度卷积神经网络编码成一个4,096维向量表示。随后用一种生成RNN或递归神经网络的语言对其进行解码,以自然语言描述。
标题生成作为图像分类的扩展
图像分类是具有很多历史的计算机视觉任务,背后有许多强大的模型。要求模型可以将图像中呈现的形状和对象的相关视觉信息拼凑起来,将图像放入对象类别中。其他计算机视觉任务的机器学习模型,例如对象检测和图像分割等,不仅识别信息何时存在,而且还通过学习如何解释2D空间调和两种理解,确定物体的信息在图像中的位置。
利用转移学习
我们可以利用预先存在的模型来帮助标题图像。转移学习允许我们接受神经网络中不同任务的数据转换,并将其应用到我们的数据中。在我们的案例中,
VGG-16图像分类模型采用224x224像素图像,生成一个4096维特征向量,用于对图像进行分类。
我们可以从VGG- 16模型中获取表示(称为图像嵌入),并使用它来训练我们的模型的其余部分。对于本文的范围,我们已经抽象了VGG- 16的架构,并预先计算了4,096维度的特性,以加速训练。
加载VGG图像特征和image 标题相对简单:
def get_data(annotation_path, feature_path):
annotations = pd.read_table(annotation_path, sep='\t', header=None, names=['image', 'caption'])
return np.load(feature_path,'r'), annotations['caption'].values
了解标题
现在我们有了一个图像表示,我们需要我们的模型来学习将其解码成一个可以理解的标题。由于文本的串行特性,我们在RNN / LSTM网络中利用递归式(学习更多,阅读“
理解 LSTM 网络”)。这些网络被训练用来预测在给定的单词和图像表示序列中的下一个单词。
长短期记忆(LSTM)细胞允许模型更好地选择在标题词序列中使用什么信息,记住什么,以及忘记哪些信息。TensorFlow提供了一个包装器函数,为给定的输入和输出维度生成一个LSTM层。
为了将单词转换为适合于LSTM输入的固定长度表示,我们使用嵌入层来学习将单词映射到256维特性(或单词嵌入)。单词嵌入帮助我们把单词表示为向量,类似的单词向量在语义上是相似的。要了解更多的词汇嵌入是如何捕捉不同单词之间的关系的,请参阅“利用深度学习获取语义含义”。链接地址为https://www.oreilly.com/learning/capturing-semantic-meanings-using-deep-learning
在VGG- 16图像分类器中,卷积层提取一个4,096维的表示,通过一个最终的softmax层进行分类。由于LSTM单元期望将256维文本特性作为输入,我们需要将图像表示转换为用于目标标题的表示。为此,我们利用另一个嵌入层,学习将4,096维图像特征映射到256维文本特征的空间中。
建立和训练模型
这就是 Show和Tell Model 的样子:
在这个图中,{ s0,s1,…,sN }代表我们试图预测的标题的单词和{ wes1,wes1,…,wesn - 1 }是每个单词的嵌入向量。LSTM的输出{ p1,p2,…pN }是由模型为句子中的下一个字生成的概率分布。通过训练将每个单词的对数概率的负和最小化。
def build_model(self):
# declaring the placeholders for our extracted image feature vectors, our caption, and our mask
# (describes how long our caption is with an array of 0/1 values of length `maxlen`
img = tf.placeholder(tf.float32, [self.batch_size, self.dim_in])
caption_placeholder = tf.placeholder(tf.int32, [self.batch_size, self.n_lstm_steps])
mask = tf.placeholder(tf.float32, [self.batch_size, self.n_lstm_steps])
# getting an initial LSTM embedding from our image_imbedding
image_embedding = tf.matmul(img, self.img_embedding) + self.img_embedding_bias
# setting initial state of our LSTM
state = self.lstm.zero_state(self.batch_size, dtype=tf.float32)
total_ loss = 0.0
with tf.variable_scope("RNN"):
for i in range(self.n_lstm_steps):
if i > 0:
#if this isn't the first iteration of our LSTM we need to get the word_embedding corresponding
# to the (i-1)th word in our caption
with tf.device("/cpu:0"):
current_embedding = tf.nn.embedding_lookup(self.word_embedding, caption_placeholder[:,i-1]) + self.embedding_bias
else:
#if this is the first iteration of our LSTM we utilize the embedded image as our input
current_embedding = image_embedding
if i > 0:
# allows us to reuse the LSTM tensor variable on each iteration
tf.get_variable_scope().reuse_variables()
out, state = self.lstm(current_embedding, state)
print (out,self.word_encoding,self.word_encoding_bias)
if i > 0:
#get the one-hot representation of the next word in our caption
labels = tf.expand_dims(caption_placeholder[:, i], 1)
ix_range=tf.range(0, self.batch_size, 1)
ixs = tf.expand_dims(ix_range, 1)
concat = tf.concat([ixs, labels],1)
onehot = tf.sparse_to_dense(
concat, tf.stack([self.batch_size, self.n_words]), 1.0, 0.0)
#perform a softmax classification to generate the next word in the caption
logit = tf.matmul(out, self.word_encoding) + self.word_encoding_bias
xentropy = tf.nn.softmax_cross_entropy_with_logits(logits=logit, labels=onehot)
xentropy = xentropy * mask[:,i]
loss = tf.reduce_sum(xentropy)
total_loss += loss
total_loss = total_loss / tf.reduce_sum(mask[:,1:])
return total_loss, img, caption_placeholder, mask
使用推理生成标题
在训练之后,我们有一个模型,给出了一个单词出现在下一个标题的概率,给出了图像和之前的所有单词。我们如何使用这个来产生新的标题?
最简单的方法是输入一个输入图像,然后迭代输出下一个最可能的单词,建立一个标题。
def build_generator(self, maxlen, batchsize=1):
#same setup as `build_model` function
img = tf.placeholder(tf.float32, [self.batch_size, self.dim_in])
image_embedding = tf.matmul(img, self.img_embedding) + self.img_embedding_bias
state = self.lstm.zero_state(batchsize,dtype=tf.float32)
#declare list to hold the words of our generated captions
all_words = []
print (state,image_embedding,img)
with tf.variable_scope("RNN"):
# in the first iteration we have no previous word, so we directly pass in the image embedding
# and set the `previous_word` to the embedding of the start token ([0]) for the future iterations
output, state = self.lstm(image_embedding, state)
previous_word = tf.nn.embedding_lookup(self.word_embedding, [0]) + self.embedding_bias
for i in range(maxlen):
tf.get_variable_scope().reuse_variables()
out, state = self.lstm(previous_word, state)
# get a one-hot word encoding from the output of the LSTM
logit = tf.matmul(out, self.word_encoding) + self.word_encoding_bias
best_word = tf.argmax(logit, 1)
with tf.device("/cpu:0"):
# get the embedding of the best_word to use as input to the next iteration of our LSTM
previous_word = tf.nn.embedding_lookup(self.word_embedding, best_word)
previous_word += self.embedding_bias
all_words.append(best_word)
return img, all_words
在许多情况下,这是可行的,但通过“greedily”接受最可能的词语,我们最终可能不会得到最可能的标题。
一种可能的避免方法是使用一种叫做“
Beam Search”的方法。这使得一个人可以在保持推理计算的同时探索更大的空间。在下面的例子中,该算法维护了一个k = 2候选句子的列表,其中每个垂直时间步由每个粗体字的路径显示。
限制和讨论
神经图像标题生成器为学习从图像映射到human-level图像标题提供了一个有用的框架。通过对大量图像描述对的训练,模型学习从视觉特征获取相关的语义信息。
然而,在静态图像中,嵌入我们的标题生成器将侧重于我们的图像的特征,这对图像分类很有用,不一定对标题生成有用。为了提高每个功能中包含的任务相关信息的数量,我们可以训练图像嵌入模型(VGG16网络用于编码特征)作为标题生成模型的一部分,允许我们对图像编码器进行微调,以更好地适应生成标题的作用。
另外,如果我们仔细观察所生成的标题,我们会注意到它们很平凡,很普通。拿这个可能的图片标题举例:
这绝对是“giraffe standing next to a tree.”。然而,如果我们看看其他的图片,我们可能会注意到,它会生成一张“a giraffe next to a tree”的标题,因为长颈鹿在训练集中经常出现在树旁。
本文为编译文章,作者Raul Puri和Daniel Ricciardelli,原网址为
https://www.oreilly.com/learning/caption-this-with-tensorflow