开发 | 在玩图像分类和图像分割?来挑战基于 TensorFlow 的图像注解生成!

AI科技评论按:本文刊载于 Oreilly,AI科技评论编译。

玩过图像分类的开发者不少,许多人或许对图像分割(image segmentation)也不陌生,但图像注解(image caption)的难度,无疑比前两者更进一步。

原因无他:利用神经网络来生成贴合实际的图像注释,需要结合最新的计算机视觉和机器翻译技术,缺一不可。对于为输入图像生成文字注解,训练神经图像注解模型能使其成功几率最大化,并能生成新奇的图像描述。举个例子,下图便是在 MS COCO 数据集上训练的神经图像注解生成器,所输出的潜在注解。

左图注解:一个灰衣男子挥舞棒子,黑衣男子旁观;右图注解:一辆大巴车“坐”在一个人旁边

本文是一篇中级教程,旨在教给大家如何在 Flickr30k 数据集上训练图像注解生成模型,使用的是谷歌 Show and Tell 模型的变体。我们使用 TensorFlow 框架来创建、训练、测试模型,因为 TensorFlow 相对容易使用,并且有不断增长的庞大用户社群。

图像注解技术的价值

近来深度学习在 CV(计算机视觉)和 NLP(自然语言处理)领域的成功,激发了 AI 研究人员在这两者的交叉领域探索新的应用。而作为结果的注解生成模型,需要平衡对视觉线索和自然语言的理解。

这两门传统上泾渭分明、并不相关的领域之间所产生的交集,有潜力在业内产生广泛的影响。该技术有一些直接应用场景,比如为 YouTube 视频生成简介,又比如为无标签图像做注解,但其价值远不止于此。类似于传统的 CV 技术试图让现实世界变得让计算机更容易接触更容易理解,该技术有潜力让现实世界变得对于我们来说更容易理解。它可以作为我们的导游、日常生活的视觉辅助,比如意大利 AI 公司 Eyra 的为视觉障碍患者开发的可穿戴设备 Horus (古埃及神话中的荷鲁斯之眼)。

准备工作

首先,你需要安装 TensorFlow。

其次,你需要 pandas, opencv2 以及 Jupyter 来跑相关代码。但是,为了简化安装过程,我们强烈推荐你在我们的 GitHub 资源库里跟随 Docker 的安装指南。

你还需要下载 Flickr30k 数据集的图像注解和 image embeddings。下载链接也在 GitHub 资源库里。

现在教程开始。

图像注解生成模型

在高层级,这就是我们将要训练的模型。每一幅图像将会用深度 CNN 编码成 4,096 维的矢量表示。一个语言生成 RNN 会随后对其按次序解码,成为自然语言描述。

注解生成——作为图像分类的延伸

作为一个历史悠久的 CV 任务,图像分类背后有许多强大模型。图像分类能把图像中相关联的形状、物体的视觉信息拼凑到一起,把图像放入物体类别中。针对其他 CV 任务的机器学习模型,建立在图像分类的基础之上,比如物体识别和图像分割。它们不仅能对提供的信息进行识别,还能学习如何解读 2D 空间,调和两种理解,并决定图像中物体信息的位置分布。

对于注释生成,这引发了两个问题:

  1. 我们如何充分利用图像分类模型的成功,从图像提取重要信息?
  2. 我们的模型,该如何调和对语言和图像的理解?

利用迁移学习

我们可以利用已有的模型,推动图像注解。迁移学习使得——在不同任务上训练神经网络而学习到的数据变形,能用于我们的数据。在我们的例子中,VGG-16 图像分类模型导入 224x224 分辨率的图像,生成对分类图像非常有用的 4,096 维特征矢量。

我们可以把 VGG-16 模型的表示(即 image embedding)用于训练其他模型上面。受篇幅限制,本文对 VGG-16 的架构不欲详述,并预先计算好了 4,096 维特征,来加速训练。

载入 VGG 图像特征和图像注解比较直接:

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 单元允许模型在注解词语序列中,更好地选择使用哪条信息、记忆什么、又要忘记什么。TensorFlow 提供了一个 wrapper 函数,来对给定输入、输出维度生成 LSTM 层。

为了把词语转换成适合 LSTM 输入的固定长度表示,我们使用了一个 embedding 层,它能学习把词语映射到 256 维的特征(或 word-embedding)。Word-embedding 帮助我们把词语表示为矢量,相近的词语矢量在语义上也是近似的。

在 VGG-16 图像分类器里,卷积层提取了 4,096 维表示,传入最终的 softmax 层进行分类。由于 LSTM 单元需要 256 维文本特征作为输入,我们需要把图像表示转译为针对目标注解的表示。为实现这一点,我们使用了另一个 embedding 层,学习把 4,096 维图像特征映射到 256 维文本特征的空间。

创建和训练模型

整体上,这就是谷歌 Show and Tell 模型看起来的样子:

上图中, {s0, s1, ..., sN}代表了注解中我们想要预测的词语, {wes0, wes1, ..., wesN-1} 是每个词的 word embedding 矢量。LSTM 的输出 {p1, p2, ..., pN} 是模型生成的概率分布,针对句子中的下一个词。该模型被训练来最小化每个词语对数概率(log probabilities )的负数总和。

使用推理生成注解

训练之后,我们有了一个模型。给定图像和所有此前的词语,它能给出下一步某个词出现在注解中的概率。如何用它来生成新注解呢?

最简单的办法,是拿来一个输入图像,输出下一个可能性最高的词语,创建一个简单的图像注解。

这在许多情况下会奏,但“贪婪”地使用每一个概率最大的词语,未必能获得整体上概率最大的注解。

绕过这个难题的一个潜在方案,是采用一个名为"Beam Search"的方法。该算法会对长度 t 以内的 k 个最佳语句集反复考量,作为候选来生成 t + 1 大小的句子,只保留结果中的 k 个最佳选择。这允许开发者探索一个较大的优质注解空间,同时让推理在计算上可追踪。在下面的例子里,算法保持了一个 k = 2 的候选句子列表,即每个垂直时间步到每个加粗词语的路线。

局限性

对于学习把图像映射到人类级别的文字注解,该神经图像注解生成器提供了一个十分有用的框架。铜鼓偶在大量图像—注解成对数据上训练,该模型学会了通过视觉特征抓取相关语义信息。

但对于静态图片而言,嵌入我们的注解生成器,将会聚焦于图像中对分类有用的特征,而不是对注解生成有用的特征。为提升每个特征里涵盖的与任务相关的信息,我们可以训练图像嵌入模型(用来对特征进行编码的 VGG-16 网络)作为注解生成模型的一部分。这使得我们能为图像编码器调参,以更符合注解生成器的角色。

另外,如果我们仔细观察生成的注解,会发现它们基本是日常、常见的情形。下图便是例子:

几乎可以肯定,这图片是“长颈鹿站在一棵树旁边”。但是,如果我们看看其他图片,会立刻发现无论对于哪一张生成的注解都是“长颈鹿站在一棵树旁边”。这是因为训练集中的长颈鹿都出现在树旁边。

下一步

首先,如果你想要提升该模型,你需要看看谷歌的开源 Show and Tell 神经网络。它用 MS COCO 数据集和 Inception-v3 图像嵌入训练。

现有的前沿图像注解模型会包含一个视觉注意力机制(visual attention mechanism),使得模型能发现图像中感兴趣的部分,因而能在生成注解时选择性地聚焦。

另外,如果你对注解生成的前沿执行非常好奇,不妨看看这一篇论文:Show, Attend, and Tell: Neural Image Caption Generation with Visual Attention。

提醒:作者已将本文所有 Python 代码上传于 GitHub,地址:https://github.com/mlberkeley/oreilly-captions?utm_source=newsite&utm_medium=content&utm_campaign=lgen&utm_content=caption-this-with-tensorflow-top-cta。

原文发布于微信公众号 - AI科技评论(aitechtalk)

原文发表时间:2017-05-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏聊聊技术

原 初学图论-Kahn拓扑排序算法(Kah

2988
来自专栏xingoo, 一个梦想做发明家的程序员

20120918-向量实现《数据结构与算法分析》

#include <iostream> #include <list> #include <string> #include <vector> #include...

1846
来自专栏alexqdjay

HashMap 多线程下死循环分析及JDK8修复

1.1K4
来自专栏Phoenix的Android之旅

Java 集合 Vector

List有三种实现,ArrayList, LinkedList, Vector, 它们的区别在于, ArrayList是非线程安全的, Vector则是线程安全...

692
来自专栏开发与安全

算法:AOV网(Activity on Vextex Network)与拓扑排序

在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系,这样的有向图为顶点表示活动的网,我们称之为AOV网(Activity on Vextex ...

3977
来自专栏Hongten

ArrayList VS Vector(ArrayList和Vector的区别)_面试的时候经常出现

2202
来自专栏desperate633

LeetCode Invert Binary Tree题目分析

Invert a binary tree. 4 / \ 2 7 / \ / \1 3 6 9 to4 / \ 7 2 / \ / \9 6 3 1 Tri...

981
来自专栏计算机视觉与深度学习基础

Leetcode 114 Flatten Binary Tree to Linked List

Given a binary tree, flatten it to a linked list in-place. For example, Given...

2098
来自专栏java闲聊

JDK1.8 ArrayList 源码解析

当运行 ArrayList<Integer> list = new ArrayList<>() ; ,因为它没有指定初始容量,所以它调用的是它的无参构造

1232
来自专栏xingoo, 一个梦想做发明家的程序员

Spark踩坑——java.lang.AbstractMethodError

百度了一下说是版本不一致导致的。于是重新检查各个jar包,发现spark-sql-kafka的版本是2.2,而spark的版本是2.3,修改spark-sql-...

1260

扫码关注云+社区