神经网络,一网多用
用单个模型实现图像搜索,图像说明,相似单词以及相似图像。
众所周知,神经网络擅长解决一个特定的单独任务,但是它们却很难处理多种任务。这不像人类的大脑可以运用同一个概念完成许多不同的任务。
在看到一个分形的图像后,你能够处理多个与它相关的任务:
• 在一个图像的集合中,把猫与分形区别开来
• 在一张纸上,大致地划出一个分形的样子(它不一定完美,但也不是随手乱画的)
• 识别图像是否与分形图像相似(你能够自动将相似度由高到低排列)
• 闭上你的眼睛并想象一个分形的样子(甚至当视觉还未输入时,即使仅仅扫过一眼,你仍然可以将它想象出来。这是多么酷呀!)
你怎样能够去完成所有这些任务呢?在你的大脑中有一个神经网络专门来执行这些任务吗?
现代神经科学给出的答案是,在你的大脑内信息是通过不同部分共享及交流的。大脑的这种功能如何运作是一个需要深入研究的领域,但我们还是能找到一些线索。答案可能就存在于数据怎样在神经网络之中储存和处理的。
精彩的表示世界
表示,顾名思义,指的是信息是怎样编译在一个网络之中的。当一个单词,一句话或者一个图像(或者其他任何东西)输入到一个训练好的神经网络中,随着权重乘以输入以及激活函数的应用,它会在连续的层上转换。最终,在输出层,我们能得到类标签,股票价格或者其他网络训练的结果。
这个输入到输出的神奇转换源于输入的转换发生在连续的层之中。这些输入数据的转换被称为表现。一个关键的想法在于每一层会使下一层的工作变得简单。这个使连续层更简单的过程使得激活(在一个特定层转换输入数据)是有意义的。
我表达的“有意义的”是什么意思呢?让我们来看一个图像分类器在不同层上激活的例子。
一个图像分类网络所做的是从像素空间中转化一个图像到越来越高等级的概念空间。所以,一个表示成RGB 数值的汽车的图像将先表示在第一层的边缘空间,然后在第二层的外圈和形状空间中,然后在最后一层之前,它将开始被表示成高等级的物体比如轮胎,门,等等。
这个愈加丰富的表示(因为深度网络的层次性而自动发生)使图像识别的工作变得简易。最终层需要做的是权衡轮胎和门的概念来使对象更像汽车,或者权衡耳朵和眼睛来使对象更贴近人。
你能用表示来做什么?
因为这些中间的层存储着有意义的输入数据的编码,你可以在多个工作任务中使用同样的信息。举个例子,你可以用一个语言模型(一个训练好的循环神经网络用来预测下一个单词)将一个特定神经元的激活转化对一句话的情绪(观点)的预测。
令人惊奇的事实是情绪神经元在语言模型的无监督任务中自然生成。网络被训练用来预测下一个单词,从未被要求用来预测情绪。或许因为情绪是有用的概念,网络产生它以用来在语言模型上更好地工作。
一旦你有了表示的概念,你就可以从完全不同的角度看待深度神经网络。你将发觉表示作为一种可传递的语言使不同的网络(或者同一网络的不同部分)之间的交谈成为可能。
通过建立四合一网络探索表示
为了完整地了解表示是什么,让我们建立我们自己的深度神经网络实现四个任务:
图像说明生成器:提供一个图像,对它生成一个说明
相似单词生成器:提供一个单词,找到其他与它相似的单词
视觉相似图像搜索:提供一个图像,找到最与它相似的图像
通过描述内容搜索图像:通过一段文字描述来搜索符合相关内容的图像
这里三个任务中的每一个都是一个独立的项目,通常来说需要三个模型。但是我们将要运用一个模型来完成所有的任务。
代码将用Pytorch写在Jupyter Notebook里面。你能从这个 repository 下载。
第一部分:图像说明
网上有许多好的教程来实现图像说明,所以我不再深入说明。我的实现是与这个教程一样的:Building an Automated Image Captioning Application. 关键的不同在于我的实现是在Pytorch,而教程用的是Keras.
为了仿照,你需要下载 Flickr8K 数据集。填写this form,你将在你的邮箱中收到下载链接。在 “Flicker8k_Dataset” 文件夹中提取zip 文件,这在你笔记的同一个目录下。你需要从这里下载说明from here。提取说明在“caption_datasets”文件夹。
模型
关于图像说明,基本上有两个部分:a)一个图像编译器来提取输入的图像并将它表示成一种对说明有意义的格式。b) 一个说明解码器来提取图像表示和它输出的文字描述。
图像编译器是一个深度卷积网络,而说明破译器是是个传统的LSTM/GRU 循环神经网络。我们当然能在脚本上训练他们。但是这要求比我们拥有的(8000 图像)更多的数据,而且需要更长的训练时间。所以,我们使用一种已经存在的图像分类器,使用它最终层的前一层激活。
这就是接下来诸多神奇的表示例子中的第一个。我使用的是PyTorch modelzoo 上的初始网络。它在 ImageNet 训练,可以对100个类别的图像进行分类,并且可以给我们一个能够放入循环神经网络的表示。
注意这个起始网络以前从来没有被用来训练图像说明任务。但是,它是有效的!
像我在我的 generate machine learning ideas via machine learning 的中做的,我们可以使用提前训练好的语言模型来作为说明解码器。但是这一次,因为我在教程中重新实现一个模型并且效果很好,我就简单的依照它并且从脚本上训练一个解码器。
全部的模型结构如下:
你可以从脚本中训练它,但是它会在CPU上花上几天来运行(我没有在GPU上最优化它)。但是别担心!我的笔记本已经花了好几个不眠之夜,所以你可以直接享受已经训练好的模型的成果。
表现
我已经实现了集束搜索并且得到了很好的结果。下面是测试集的图像通过网络得到说明的例子:
让我们看看网络是怎样说明我的照片的:
还不错!令人印象深刻的是网络知道图像里包括一个男人穿着白色T恤。语法是有一点差(我确保更多的训练来修复它),但是基础的要素是良好的。
并且如果输入的图像中有一部分是网络从来没有在之前见过的,它会趋向于失败。举一个例子,我很好奇网络会给iPhone X 的图像怎样的标签。
它做的并不好。但是总体而言,我对它的表现比较满意。同时,在学习图像说明时,它也为我们利用网络生成的表示开发其他功能建立了良好的基础。
第二部分-相似单词
回想我们从一个图像表示中解码说明做了什么。我们反馈表示到LSTM/GRU 网络中,产生了一个结果,把它作为第一个单词处理,然后取第一个单词并且把它放回到网络中来产生第二个单词。它一直持续到网络产生一个特殊的令牌表明句子的结束。
为了反馈单词到网络中,我们需要转化单词成表示作为网络的输入。这就意味着如果输入层由300个神经元组成,对于所有说明中8000多个单词里的每一个单词,我们需要有300个数字来专门联系该单词。这个转化单词到数字化表示的过程被叫做单词嵌入(或者单词表示)。
有一些已经存在的单词嵌入比如 word2vec 或者 GLoVE,我们可以下载并使用。但是在这个项目里,我们从脚本中学习一个单词嵌入。我们开始随机的生成单词嵌入并探索在训练完成时我们的网络关于单词学到了什么。
因为我们无法视觉化一个100维度的数字空间,我们使用一个很棒的工具叫做t-SNE 来将学到的单词嵌入视觉化在二维中。t-SNE 是一种减少维度的技术,它试图使高维空间中的相邻空间与低维度空间的相邻空间保持相同。
嵌入的视觉化
谈的足够多了。让我们来看看一个单词嵌入的空间是怎么被我们的说明解码器学习的(不像其他有百万计的单词和句子的语言任务,我们的解码器在训练集中仅仅看到了大约30000语句)。
所以。我们的网络已经学习到了像“playing”,”plays”,”playing”这些词 是非常相似的(它们有相似的表示,红色箭头指出的簇可以表明)。让我们在这个二维空间中探索另一个区域:
这个区域看起来有成簇的数字——2,3,4,5 等等。
另一个:
它知道人与儿童是相似的。并且,它隐隐地推断出了物体的形状。
相似单词
我们能使用100维度的表示来建立提供与输入单词最相似单词的函数。它的工作原理很简单:选取100维度的表示并在数据库中找到它与其他所有单词的余弦相似度。
让我们看看与单词“男孩“最相似的单词:
还不错。“骑手”是一个例外,但是“孩子们”,“孩子”和“刚学步的儿童”是对的。网络认为与单词“追逐”相似的词语是:
“chases”是很好的,但是我不确定为什么它认为“警察”是相似的。
单词类比
一个令人吃惊的关于单词嵌入的事实是你可以在它们身上做微积分。你能取两个单词(比如“国王”和“皇后”),对它们的表示相减得到一个方向。当你应用这个方向到另一个单词表示(比如“男人”),你会得到一个接近于实际类比词(比如“女人”)的单词表示。这就是为什么word2vec在面世时如此出名的原因:
我很好奇通过说明解码器学习的表示是否有相似的属性。即使我很怀疑,因为训练的数据并不多(大约30000语句),但我决定试一试。
网络学习的类比并不完美(一些单词真正地出现少于10次,所以网络没有足够的信息来学习)。我不得不眯着眼找,但它确实也找到了点类比。
如果骑对应坐,那么走对应的是什么?我的网络给的是“躺”(这还不赖!)。
相似的,如果“man”的复数形式是“men”,那么“woman”的复数形式是:
第二个结果是“women”,它是非常精准的。
最后,如果草是绿色的,那么天空是什么颜色:
网络认为天空是银色或者灰色,它们没有一个是蓝色,但都是颜色。令人惊喜的是网络能够推断出颜色的方向。
第三部分-相似图像
如果单词表示将相似单词簇为一团,那么图像表示会怎么样?我在图像表示上应用了同样的t-SNE技术(将300维度的张量作为说明解码器的第一步的输入)。
视觉化
点代表不同图像(我没有取8000个图像的整个集合,但是是一个100个图像左右的样本)。红色箭头指向临近的一簇表示。
所以,跑车聚集在一起。
所以孩子们在类似森林/草地的区域玩耍。
并且篮球运动员聚集在一起。
寻找与输入的图像相似的图像
对于相似词任务,我们被局限于我们的测试集词汇来寻找相似词(如果一个单词在测试集不存在,我们的解码器不能学会它的嵌入)。然而,对于相似的图像任务,我们有图像表示生成器能提取任何输入图像和生成它的编码。
这意味着我们能使用余弦相识度的方式来建立一个通过图像搜索的方程,如下:
第一步:提取数据库或者目标文件夹里的所有的图像并且储存它们的表示(由图像编码器提供)
第二步:当一个使用者想去搜索与他已经有的图像最相似的图像时,提取新图像的表示并且在数据库中寻找与它最接近的一个(通过余弦相似度)
谷歌图像好像是使用这个(或者非常相似)方法来加强他们的反向图像搜索功能。
让我们看看我们的网络做的怎么样。我点击下面这张我在Goa度假时的照片。
注意这个图像是我自己的。我们正在使用的模型之前并未见过它。当我查找与它相似的图像时,网络从 Flickr8K 数据集中给我以下的结果:
是不是出人意料?我没有期望这么好的表现,但是我们确实得到了。深度神经网络超乎寻常地优秀。
第四部分-通过描述搜索图像
在这个最后的部分,我们反向运行一个图像说明生成器。不是取一个图像并且对它生成一个说明,而是我们输入一个说明(文字描述)并且寻找与它最接近匹配的图像。
听起来太棒了而感觉不像真的?好吧,它不是的。这是我们怎么做的:
第一步:用一个完全随机的300维度的输入张量作为开始,而不是用来自编码器的300维度的图像表示。
第二步:冻结整个网络的所有层(即指导Pytorch不要计算梯度)
第三步:假设随机生成的输入张量来自图像编码器并且把它放进说明解码器。
第四步:给定一个随机输入,获取从网络中生成的说明,与用户提供的说明相比较
第五步:比较生成的和用户提供的说明,计算损失
第六步:找到使损失最小化的张量梯度(即每一个300维数字变化的量和方向,使得当张量输入解码器时,输出的说明能够最接近用户提供的说明)
第七步:改变梯度方向上的向量(根据学习率前进一小步)
继续第四步到第七步直到收敛或者损失在特定的阈值以下。
最后一步:取最后输入的张量并且使用它的值,通过余弦相似度来寻找最接近的图像。
当我们做完这个,结果是神奇的;
我搜索“一只狗”,这是网络找到的图像:
我搜索“一个男孩的微笑”:
最后我搜索:
前两个结果是;
和
令人惊喜,是不是?
总结和一个挑战
作为提醒,你可以从我的github下载并且完成所有的这些项目。它包括数据预处理,模型描述,训练好的图像说明网络以及可视化的代码。它不包括Flickr8K 数据集或者说明,你需要另外下载。
我希望你们喜欢这篇文章。如果你们想更进一步,这里有一个挑战:从已有的描述中生成一个图像。就像从一个随机的起点生成一个与用户提供的说明匹配的300维度的图像表示,你能够更进一步并且为一个用户提供的说明从脚本中生成一个图像吗?
它比我们在这篇文章中处理的要难10倍,但我感觉它是可行的。如果有这么一项服务不仅仅能根据文字搜索图像,而且能够生成它,这是多么酷呀!
在未来,如果谷歌图像做到了这个并且开始给出了还新生成的图像(像“两只独角兽飞翔在由披萨做的地毯上”),我不会感到惊讶。
我希望你们能够安全又愉快的在表示的世界探索。
感谢 Ishan Goel 的审查和建议。
作者:Paras Chopra翻译: Chongli Wu、Yan Wang