教会计算机以人的方式看图
一张图片胜过千言万语,甚至N行代码。网友们经常使用的一句留言是,no picture, you say nothing。随着生活节奏的加快,人们越来越没有耐心和时间去看大段的文字,更喜欢具有视觉冲击性的内容,比如,图片,视频等,因为其所含的内容更加生动直观。 许多产品是在外观上吸引到我们的目光,比如在浏览购物网站上的商品、寻找民宿上的房间租赁等,看起来怎么样往往是我们决定购买的重要因素。感知事物的方式能强有力预测出我们想要的东西是什么,因此,这对于评测而言是一个有价值的因素。 然而,让计算机以人类的方式理解图像已经成为计算机科学的挑战,且已持续一段时间了。自2012年以来,深度学习在图像分类或物体检测等感知任务中的效果慢慢开始超越或碾压经典方法,如直方梯度图(HOG)。导致这种转变的主要原因之一是,深度学习在足够大的数据集上训练时,能够自动地提取有意义的特征表示。
这也是为什么许多团队,比如Pinterest、StitchFix和Flickr 都开始使用深度学习来学习图像特征,并基于用户认为视觉上令人愉悦的内容提供推荐。同样,Insight的研究员也使用深度学习为应用程序建立模型,例如推荐购买太阳镜以及寻找艺术风格等。 目前,许多的推荐系统都是基于协同过滤(collaboratiove filtering):利用用户关联来提出建议(即,假设喜欢你喜欢的东西的用户也会喜欢”)。但是,这些基于协同过滤的模型需要大量数据,其效果才能准确,并且难以处理尚未被任何人查看过的新内容。如果将内容表示应用于基于内容的推荐系统中,那么该系统不会遇到上述问题。 此外,这些表示还允许消费者高效地搜索照片库,以寻找到与他们刚刚拍摄的自拍(通过图像查询)相似的图像,或者用于特定物品(通过文本查询)的照片,这方面的常见示例包括关键字搜图以及以图搜图功能。 根据我们多年语义理解项目的技术经验,希望编写一个教程,介绍如何构建自己的特征表示,包括图像和文本数据,以及如何有效地进行相似性搜索。希望看完本文,读者能够对任意大小的数据无论数据集的大小如何,都能够从头开始构建出一个快速语义搜索模型。
在机器学习中,就像在软件工程中一样,有很多方法可以解决同一个问题,但每种方法都有不同的权衡及侧重点。如果是正在进行研究或本地原型设计,就可以摆脱效率非常低的解决方案。但是,如果是要构建一个需要可维护和可扩展的图像相似性搜索引擎,则必须考虑如何适应数据演变以及模型运行的速度。 下面让我们思考一些方法:
方法1的工作流程
在方法1中,我们构建了一个端到端模型,该模型在所有的图像上进行训练,将图像作为输入,并输出所有图像的相似度得分。预测过程耗时很短(一次前向传播过程即可),但是,当每次添加新图像时,我们都需要重新训练得到一个新模型。此外,当类别多的时候,也会很难正确地优化它。这种方法虽然看起来很简单且预测过程很快,但是不能够扩展到大型的数据集。此外,我们还必须手动标记数据集与图像的相似性,这个过程可能非常耗时。
方法2的工作流程
在方法2中,构建一个接收两个图像的模型,并输出0到1之间的成对相似得分(例如,孪生网络Siamese Networks)。这些模型对于大型数据集是准确的,但会另外导致一个可伸缩性问题。我们通常希望通过查看大量图像来查找相似的图像,因此我们必须为数据集中的每个图像对都运行一次相似度模型。如果模型采用的是CNN网络,并且有十几个图像时,那么这个过程就非常慢了。此外,这个方法仅适用于图像相似性搜索,而不适用于文本搜索。虽然此方法可扩展到大型数据集,但运行速度很慢。
方法3的工作流程
方法3是一种更简单的方法,类似于字嵌入。如果找到一个富有表现力的矢量表示或嵌入图像,就可以通过观察矢量彼此之间的距离来计算相似性。这种类型的搜索是深入研究的常见问题,许多库都实现了快速解决方案(本文使用Annoy)。此外,提前计算出数据库中所有图像的矢量,这种方法既快速(一次正向传递就是一种有效的相似性搜索),又可以进行扩展。最后,如果我们设法为图像和单词找到常见的嵌入,就可以使用它们来进行文本到图像的搜索!由于其简单性和高效性,第三种方法作为本文的实现方法。
那么,如何实际使用深度学习表示来创建搜索引擎呢?我们的最终目标是拥有一个搜索引擎,可以接收图像并输出相似的图像或标签,还能接收文本并输出类似的单词或图像。为了实现这一目标,将经历三个连续的步骤:
图像数据集由1000张图像组成,分为20个类别,每类图像包含50个图像。该数据集可在此处找到,此数据集包含每个图像的类别和一组说明。为了使这个问题的难度更高,并且为了表明本方法的概括性,本文只使用了类别,而忽略说明。总共有20个类别,如下所示:
aeroplane
bicycle
bird
boat
bottle
bus
car
cat
chair
cow
dining_table
dog
horse
motorbike
person
potted_plant
sheep
sofa
train
tv_monitor
图像示例
从上图中可以看到,标签非常嘈杂:许多图像都包含多个类别,图像的标签并不总是来自最突出的内容。例如,在右下方,图像被标记为chair(椅子)
而不是person(人)
,而该图的中心是有3个人,且几乎看不见椅子。
此外,加载已在Wikipedia上预训练的单词嵌入(本文使用GloVe模型中的单词嵌入),使用这些向量将文本合并到语义搜索中。
现在要加载一个在大型数据集(Imagenet)上预先训练过的模型,并且可以在线免费获取。本文使用VGG16网络为图像生成嵌入,注意,这里本文采用的方法适用于任何最新的CNN架构(不局限于VGG16)。
VGG网络
生成嵌入是什么意思?我们将使用预先训练模型倒数第二层前的网络结构,并存储对应的权重值。在下图中,用绿色突出显示表示嵌入层,该嵌入层位于最终分类层之前。
嵌入层只在最终的决策层前使用
一旦使用该模型生成图像特征,就可以将其存储到磁盘中,重新使用时无需再次进行推理!这也是嵌入在实际应用中如此受欢迎的原因之一,因为可以大幅提升效率。除了将它们存储到磁盘之外,还将使用Annoy构建嵌入的快速索引,这将允许我们非常快速地找到任何给定嵌入的最近嵌入。 以下是本文得到的嵌入。现在每个图像都由一个大小为4096的稀疏向量表示。注意:向量稀疏的原因是在激活函数之后将负数归零。
图像嵌入
现在可以简单地接收图像,获得其嵌入后,并查看快速索引以查找类似的嵌入,从而找到类似的图像。这是特别有用的,因为图像标签通常很嘈杂,且图像比标签更多。例如,在数据集中,有一个类别cat(猫)
和一个类别bottle(瓶子)
。您认为下面这张图片会被标记为哪个类别?
猫或瓶子?
正确的答案是瓶子,这是一个经常在真实数据集中出现的实际问题。将图像标记为唯一类别是非常受限的,这就是为什么希望使用更细粒的表示。幸运的是,这也正是深度学习所擅长的!下面看看使用嵌入的图像搜索是否比通过人为标记的更好。
搜索相似的图片todataset / bottle / 2008_000112.jpg
,可以看见该图像位于bottle(瓶子)类别。
结果出人意料的好,搜索得到很多猫的图像,而不是瓶子的图像,这看起来很合理!由于预训练网络的训练集中包含各类图像,这样包括猫,因此它能够准确地找到相似的图像,即使它之前从未接受过本文选定数据集的训练。 但是,最下面一行中间的一幅图像显示了一个瓶架。一般而言,这种方法执行后找到类似的图像,但有时我们只对图像的一部分感兴趣。
例如,给定一张包含猫和瓶子的图像,我们可能只对和猫类似的图像感兴趣,而不是瓶子。
解决上文问题的常用方法是,首先使用目标检测模型,检测猫图像,然后对原始图像裁剪之后再进行图像搜索,这样做会很大程度地增加计算开销,如果可能的话,希望能够避免这种开销。
有一种更简单的“hacky”方法,包括重新权衡激活值,这可以通过加载最初丢弃的最后一层权重来完成,并且仅使用与正在寻找的类索引相关联的权重来重新加权嵌入。例如,在下图中,使用Siamese cat
类的权重来重新权衡数据集上的激活(用绿色突出显示)。
加权嵌入
根据Imagenet,Siamese cat中的284
类权衡激活,来研究它是如何工作的。
正在搜索类似的图像todataset / bottle / 2008_000112.jpg
使用加权特征:
可以看到,搜索一直偏向于寻找Siamese cat的照片,而不再展示任何的瓶子图像,此外,可能会注意到最后一张照片是一只羊!这就非常有趣了,此时的模型又导致了另外一种不同类型的错误,但模型更适合目前的任务需求。 从上面的结果可以看出,通过宽泛方式搜索类似的图像,或者通过调整模型所训练的特定类别,使得模型向前迈出的了一大步,但由于使用的是在Imagenet上预训练的模型,因此仅限于1000个Imagenet类别。这些类别不能包罗万象,所以希望找到更加灵活的东西。另外,如果我们只是在不提供输入图像的情况下搜索猫呢? 为了做到这一点,使用的不仅仅是简单的技巧,还需利用一个能够理解单词语义能力的模型。
下面进入自然语言处理(NLP)世界,可以使用类似的方法来索引和搜索单词。
从GloVe加载了一组预先训练的矢量,这些矢量是通过从维基百科上爬虫并学习该数据集中单词之间的语义关系而获得的。
像之前一样创建一个索引,这次包含所有GloVe向量。之后就可以在嵌入中搜索类似的单词。
例如,搜索said
,返回[word,distance]列表:
tuesday
)也表明这个模型存在一些瑕疵,但它会让我们这种方法会让我们开始起步。现在,让我们尝试在模型中既包含单词,又包含图像。
使用嵌入之间的距离作为搜索方法似乎看起来非常合理,大多数单词在含义上与原始单词非常相似,但对单词和图像的表示似乎并不兼容。图像的嵌入大小为4096,而单词的嵌入大小为300,如何使用一个来搜索另一个?此外,即使两个嵌入大小都相同,它们也会以完全不同的方式进行训练,因此图像和相关单词很可能不会发生有随机相同的嵌入。因此,需要训练一个联合模型。
图像<-->文本
现在创建一个混合模型,可以从单词到图像,反之亦然。
在本教程中,将第一次实践自己的模型,模型是从一篇名为DeViSE的优秀论文中汲取灵感。我们的想法是通过重新训练图像模型,并改变其标签的类型来结合这两种表示。
通常,图像分类器被训练为从许多类中选择一个类别(Imagenet为1000类)。以Imagenet为例,转化最后一层为大小1000的一维向量来表示每个类的概率。这意味着模型没有语义理解哪些类与其他类相似,即将猫的图像分类为狗导致与将其分类为飞机的错误是一样的。
对于混合模型,用我们的类别单词向量替换模型的最后一层,这允许模型学习到将图像语义映射到单词语义,这也意味着类似的类将彼此更接近(因为cat
的单词向量比airplane
更靠近dog
)。我们将预测一个大小为300的语义丰富的单词向量,而不是大小为1000的单词向量,通过添加两个全连接层来实现此目的:
以下是在Imagenet上训练模型时的样子:
这是修改后模型的样子:
在数据集的训练集上重新训练我们的模型,以学习预测与图像标签相关联的单词向量。例如,对于具有类别cat的图像,尝试预测与cat相关联的300长度向量。训练需要一些时间,但这仍然要比Imagenet训练快得多。 与通常的数据集相比,本文使用的训练数据(数据集的80%作为训练集,即800个图像)是微不足道的(Imagenet有一百万张图像)。如果使用传统的类别训练技术,我们不会指望模型在测试集上表现得非常好,并且也不会期望它在全新的例子上有不错的效果。 一旦模型被训练好,就可以从上面获得GloVe单词索引,并通过运行数据集中的所有图像,将其保存到磁盘,构建图像特征的新快速索引。
现在可以轻松地从任何图像中提取标签,只需将我们的图像提供给训练有素的网络,保存出来的大小为300的矢量,并从GloVe中找到英语单词索引中最接近的单词。让我们试试下面这张图片——它的类别标签是瓶子,虽然它包含各种各样的物品。 图像16 以下是生成的标签:
最重要的是,可以使用联合嵌入,输入任何单词都可以搜索图像数据库。只需要从GloVe获取预先训练好的单词嵌入,并找到具有最相似嵌入的图像即可。
首先从搜索dog
这个词开始:
搜索dog
术语的结果
结果相当不错,但是我们可以从标签上训练的任何分类器中都得到这个!
搜索ocean
术语的结果。
模型了解ocean
与water
类似,并从boat
类中返回许多物品。
搜索街道又会发生什么呢?
搜索“street”的结果
从图中可以看到,返回的图像来自各种类别(car
,dog
,bicycle
,bus
,person
),但大多数图像都包含或靠近街道,尽管我们在训练模型时从未使用过这个概念。因为通过预先训练的单词向量,利用外部知识来学习比简单类别在语义上更丰富的图像向量映射,所以模型可以很好地概括为外部概念。
英语虽然已经发展了很久,但还不足以为一切都有对应的词。例如,没有英文单词表示“躺在沙发上的猫”,但这是一个对输入搜索引擎完全有效的查询。如果想要同时搜索多个单词,就可以使用一种非常简单的方法,即利用单词向量的算术属性。事实证明,总结两个单词向量通常是非常有效的。因此,如果只是通过使用猫和沙发的平均单词矢量来搜索我们的图像,就可以希望获得非常像猫、像沙发一样的图像、或者在沙发上有猫的图像。
多个单词的组合嵌入
下面使用混合嵌入搜索
搜索sofa
+cat
的结果
从图中可以看到,结果不错。因为大多数图像都包含一些毛茸茸的动物和一个沙发。我们的模型只训练单个单词,也可以处理两个单词的组合,但还没有构建Google Image Search,但对于相对简单的架构来说,本文绝对是有用的。 这种方法实际上可以很自然地扩展到各种域,感兴趣的读者可以应用于各自的领域之中。
希望读者能够发现这篇文章中丰富的内容,它揭开了一些基于内容的推荐和语义搜索世界的神秘面纱,感兴趣的读者快上手试试吧。