这篇博客是在我上周NYC MachineLearning做得报告基础上改写而成的。内容中包括Annoy,我开源的一个高维空间求(近似)最近邻的工具库。在接下来的博客中,我将把这个内容分成几个部分。第一部分包括向量模型,怎样度量相似性和分析为什么最近邻queries是有用的。
最近邻概念非常的简单。在一个空间(可能是多维空间),有很多的点,我们想快速地找到距离一个点最近的k个邻近点。
这个对大量应用来说,都是非常有用的。在我们开始介绍最近邻方法之前,先介绍一些向量模型。
向量空间模型和为什么最近邻是有用的
向量模型在很多应用中都非常受欢迎。他们已经被应用到自然语言处理领域很久了,比如LDA和PLSA(甚至更早在TF-IDF的文本空间)。最近也涌现出了一批新一代向量模型:word2vec, RNN等。
回到几年前的Netflix Prize比赛,获胜团队使用了一个巨大的组合模型算法,其中绝大部分功劳得归功于协同过滤算法。这个算法是目前最受欢迎的算法之一,广泛被用在推荐系统领域。
向量模型的基本思想是:表示一个对象成空间中的一个点,越靠近的点,之间越相似。如果我们使用word2vec来演示,它看上去像这样:
在这个例子中,两个词之间的相似度由他们之间的夹角大小决定。苹果与香蕉之间的距离要比船更近一些。
(附注:很多关于word2vec的文章,大都是介绍其用向量模型找词语的近义词,上图也正好示意了向量空间的结构。但是使用向量空间概念并不新,向量相似度绝对也是非常有用的)。
以最基本的形式,即数据已经被表示成向量;我们用机器学习领域最规范的数据集MNIST手写数据为例讲解。
构建一个手写数字的图像搜索引擎
MNIST数据集包含60,000张大小为28×28的手写图片,其中每张图片都是灰度图。一个最简单入手的方式,就是把每张28*28的灰度图向量展开成一个784维的向量。到这我们都没有引入任何机器学习的东西,但是接下来我们将回到正题,引入一些比较Cool的东西,比如神经网络和word2vec。
首先定义距离函数,如下图所示,这个公式是基于欧几里得距离(即类似古老有效的勾股定理):
这样我们就能够计算这个数据集中任意的两个数字之间的距离:
现在我们就可以在这个784维的空间里面搜索最近邻。可以看一下如下的示例。最左边的数字是我们的种子数字图片,右边是使用像素距离搜索到的最相似的几个数字图片。
你能看出,这样还是很奏效的。这些数字看上去还是很相似的,尽管还是有些最近邻数字图片,人一眼就能看出来是错的。
这个方法简单有趣,但是它的可扩展性并不好。你想想,如果是更大的图片,怎么办?如果是彩色图片呢?并且当我们不仅仅是考虑视觉上的相似性,而是更多像人一样去考虑图片之间的相似性时,又该如何去决定他们之间的相似程度?这个简单的距离定义还不能满足这些需求,仍然有很大空间去提升。
降维
当处理高维数据的时候,降维是一个非常有用的方法,它已经被广泛应用在各个领域。降维的原理是通过投射的方式,将高维向量压缩成低维向量。
1. 从高维空间,做降维处理,把维度降到小的维度(10-1000维)
2. 在这个空间上使用相似性计算
降维是一个很强大的技术,因为它能够把几乎任意对象转换到一个更小的空间,方便向量表出。通常这个空间涉及一个概念“潜在因子(latent)”,它不需要具体表示这个坐标轴代表什么意思(一个抽象坐标)。我们关心的是对象之间,如果之前相似,变换过后是否还是相似的。那我们现在所指的相似性是什么呢?从实际的情况看,它跟我们的数据息息相关。
那好我们以图像的深度卷积神经网络为例聊聊降维。一年前我有一个副业,就是对食物图片进行分类。看上去这个应用很愚蠢,但是最后目标是通过图片来判断食物的卡路里含量。通过这个项目我顺便学了些怎样使用卷积神经网络。之前我没有了解过这个算法,为此我还还花了一大笔钱在亚马逊的AWS上租了个GPU云主机,虽然有点心疼,但是很有意思。
为了训练卷积神经网络模型,我从Yelp和Foursquare上下载了6M的图片,并按照这篇论文的描述的方法,训练了这个模型。
这个网络的最外层是一个使用softmax的1244类分类器,很显然这是一个有监督的训练方法。上图以“辛拉面”为例描述了我们的网络结构,并加上了一些网络结构注释。仔细看可以发现“bottleneck”层,就是倒数第二层,一个128维的向量,这个就是我们想要的降维效果。
我们使用这个训练好的神经网络,并使用余弦相似性方法求得最相近的食物图片:
结果看上去不错!第一个是和油炸食品相似的食物。第二行是各种有白色碗的亚洲食物图片——让我印象深刻的是,这些食物都是不同不小,不同角度,并且像素之间的相似度比较低。最后一行是一些甜点,并且都是有巧克力酱的。我们得出结论,这个128维度的空间能够很好地表示这些食物对象。
那我们怎样去找到相似的物品呢?我并不打算更详细地讲解降维——关于它,有很多资料,你可以仔细读读。我将花更多的时间介绍如何在向量空间快速找到最近邻。实际上,在上面的数据集上,用Annoy去找一张图片的最近邻,只需要几毫秒,Annoy非常快。这就是为什么降维非常有用的原因。同时我们发现,高维数据结构能够被压缩表示。这个表示也使计算相似性和最近邻搜索变得更加容易。
协同过滤算法中的向量方法
当然,降维并不仅仅用在计算机视觉,正如之前提到的,自然语言处理,在这个领域也非常有用。在Spotify,向量模型广泛地应用在协同过滤推荐算法中。其思想是将艺术家,用户,曲目和另外的数据对象投射到一个更低维的空间,在这个空间去计算推荐就很容易。这钟方法几乎用在Spotify的所有推荐场景中,尤其是刚刚上线的每周推荐这个功能。
如果你对推荐系统感兴趣,你可以参考我之前做的几个分享:
· MusicRecommendations @ MLConf (2014)
· ML+Hadoop@ NYC Predictive Analytics(2013)
穷尽搜索作为基线
那我们怎样去找相似呢?在我们深入介绍Annoy的工作原理之前,先看看暴力穷尽这个基准搜索方式。字面上可以理解对每一个可能的对象,逐一计算其与已知对象之间的距离。
word2vec自带一个穷尽搜索工具。我们来比较一下它的效果!使用 GoogleNews-vectors-negative300.bin这个数据集,并且查询“Chinese river”,大概花了2分34秒输出了如下结果:
· Qiantang_River
· Yangtse
· Yangtze_River
· lake
· rivers
· creek
· Mekong_river
· Xiangjiang_River
· Beas_river
· Minjiang_River
我写了一个类似的工具Annoy(代码在Github上)。第一次运行的时候,要先做一些预处理工作,比如建索引,这个过程需要花一些时间。然后从硬盘加载索引文件(mmap)到内存中,这样搜索速度将非常快。我们同样查询“Chinese river”,如下是搜索结果:
· Yangtse
· Yangtze_River
· rivers
· creek
· Mekong_river
· Huangpu_River
· Ganges
· Thu_Bon
· Yangtze
· Yangtze_river
很惊人,Annoy只花了470毫秒,其中可能还包括加载Python解释器等操作时间。这个比word2vec自带的穷尽搜索快差不多300倍。
你有可能会注意到这两个搜索结果有些许的不同。这个主要是因为Annoy中的“A”代表了近似(approximate)意思。为了提高搜索效果,我们对搜索结果做了一些取舍。这样你可以根据实际需要在效果和速度之前做到权衡。如果你想用Annoy对100k的节点逐一搜索(稍后介绍),结果大概要花2秒:
· Qiantang_River
· Yangtse
· Yangtze_River
· lake
· rivers
· creek
· Mekong_river
· Xiangjiang_River
· Beas_river
· Minjiang_River
这个结果和穷尽搜索的结果一样——但是速度还是要快50倍左右。
使用最近邻的其他场景
当你在处理一些物理问题时,最近邻算法也是同样有用的。最后我举一个比较有意思的例子。在我更早期的博客中,给大家展示了从我纽约的公寓里面ping世界其他地方的IP地址所需要时间的世界地图:
这是一个简单的k-NN(k-nearest neighbors)回归应用,它在我早期的博客中可以看到。这个应用并没有使用降维算法,我们仅仅是用了三维坐标(经纬度映射到单元格)。
在下一期,我将深入介绍Annoy是如何工作的。敬请关注!
(翻译自http://erikbern.com/2015/09/24/nearest-neighbor-methods-vector-models-part-1/)
廖博森 @DataSpark