This commit is contained in:
louzefeng
2024-07-11 05:50:32 +00:00
parent bf99793fd0
commit d3828a7aee
6071 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,155 @@
<audio id="audio" title="07 | 人以群分,你是什么人就看到什么世界" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/0e/fa/0e5de777fe110fce2e40763f6a8a56fa.mp3"></audio>
要说提到推荐系统中,什么算法最名满天下,我想一定是协同过滤。在很多场合,甚至有人把协同过滤和推荐系统划等号,可见二者的关系多么紧密。
协同过滤的重点在于“协同”,所谓协同,也就是群体互帮互助,互相支持是集体智慧的体现,协同过滤也是这般简单直接,历久弥新。
# 协同过滤
当你的推荐系统度过了只能使用基于内容的推荐阶段后,就有了可观的用户行为了。这时候的用户行为通常是正向的,也就是用户或明或暗地表达着喜欢的行为。这些行为可以表达成一个用户和物品的关系矩阵,或者说网络、或者说是图,都是一个东西。
这个用户物品的关系矩阵中填充的就是用户对物品的态度,但并不是每个位置都有,需要的就是把那些还没有的地方填起来。这个关系矩阵是协同过滤的命根子,一切都围绕它来进行。
协同过滤是一个比较大的算法范畴。通常划分为两类:
1. 基于记忆的协同过滤Memory-Based
1. 基于模型的协同过滤Model-Based
基于记忆的协同过滤,现在看上去极其简单,就是记住每个人消费过什么东西,然后给他推荐相似的东西,或者推荐相似的人消费的东西。基于模型的协同过滤则是从用户物品关系矩阵中去学习一个模型,从而把那些矩阵空白处填满。
接下来一段时间我们就围绕这两个类别的协同过滤与你好好聊聊。今天我先来说的是基于记忆的协同过滤的一种——基于用户或者叫做User-Based User to User。
# 基于用户的协同过滤
## 背后的思想
你有没有过这种感觉,你遇到一个人,你发现他喜欢的书、喜欢的电影也基本上都是你喜欢的,从此以后,你就想老是想问他:还有什么好推荐的,最近又看了什么书,最近又看了什么电影?甚至不惜和他撞衫,和他穿一个风格的衣服。
对喽,这个感觉非常地自然直接,它就是基于用户的协同过滤背后思想。详细来说就是:先根据历史消费行为帮你找到一群和你口味很相似的用户;然后根据这些和你很相似的用户再消费了什么新的、你没有见过的物品,都可以推荐给你。
这就是我们常说的人以群分,你是什么人,你就会遇到什么人,所以说,要谨慎交友啊。
这其实也是一个给用户聚类的过程,把用户按照兴趣口味聚类成不同的群体,给用户产生的推荐就来自这个群体的平均值;所以要做好这个推荐,关键是如何量化“口味相似”这个看起来很直接简单的事情。这关系到一个用户会跟哪些人在同一个房间内,万一进错了房间,影响就会不好。
## 原理
书归正传,我们来说一说基于用户的协同过滤具体是怎么做的。前面说过,核心是那个用户物品的关系矩阵,这个矩阵是最原始的材料。
**第一步,准备用户向量,从这个矩阵中,理论上可以给每一个用户得到一个向量。**
为什么要说是“理论上”呢?因为得到向量的前提是:用户爸爸需要在我们的产品里有行为数据啊,否则就得不到这个向量。
这个向量有这么三个特点:
1. 向量的维度就是物品的个数;
1. 向量是稀疏的,也就是说并不是每个维度上都有数值,原因当然很简单,这个用户并不是消费过所有物品,废话嘛,连我们压箱底的都给用户推荐了,那当然不用再推荐什么了;
1. 向量维度上的取值可以是简单的0或者1也就是布尔值1表示喜欢过0表示没有当然因为是稀疏向量所以取值为0的就忽略了。
**第二步,用每一个用户的向量,两两计算用户之间的相似度,设定一个相似度阈值或者设定一个最大数量,为每个用户保留与其最相似的用户。**
这里两两计算相似度如何计算,市面上有很多相似度计算方法,你也可以自己设计,我们在后面的文章里会逐一介绍,这里先略过不提。
**第三步,为每一个用户产生推荐结果。**
把和他“臭味相投”的用户们喜欢过的物品汇总起来,去掉用户自己已经消费过的物品,剩下的排序输出就是推荐结果,是不是很简单。具体的汇总方式我们用一个公式来表示。
<img src="https://static001.geekbang.org/resource/image/be/fe/be24f9084f0fb0290de8d10c4761c3fe.png" alt="" />
这个公式也是很简单的。等号左边就是计算一个物品i和一个用户u的匹配分数等号右边是这个分数的计算过程分母是把和用户u相似的n个用户的相似度加起来分子是把这n个用户各自对物品i的态度按照相似度加权求和。
这里的态度最简单就是0或者11表示喜欢过0表示没有如果是评分则可以是0到5的取值。整个公式就是相似用户们的态度加权平均值。
## 实践
看上去简单得不值一提,但是在实现上却有一些坑,需要小心小心再小心。你想过以下这几个问题吗?
1. 只有原始用户行为日志,需要从中构造出矩阵,怎么做?
1. 如果用户的向量很长,计算一个相似度则耗时很久,怎么办?
1. 如果用户量很大,而且通常如此,两两计算用户相似度也是一个大坑,怎么办?
1. 在计算推荐时,看上去要为每一个用户计算他和每一个物品的分数,又是一个大坑,怎么办?
嗯……不要气馁,下面我会逐一说下如何化解这些问题。
**1 构造矩阵**
我们在做协同过滤计算时所用的矩阵是稀疏的说人话就是很多矩阵元素不用存因为是0。这里介绍典型的稀疏矩阵存储格式。
1. CSR这个存储稍微复杂点是一个整体编码方式。它有三个组成数值、列号和行偏移共同编码。
1. COO这个存储方式很简单每个元素用一个三元组表示行号列号数值只存储有值的元素缺失值不存储。
这些存储格式,在常见的计算框架里面都是标准的,如 Spark 中Python 的 NumPy 包中。一些著名的算法比赛也通常都是以这种格式提供数据。这里不再赘述了。
把你的原始行为日志转换成上面的格式,就可以使用常用计算框架的标准输入了。
**2 相似度计算**
相似度计算是个问题。
首先是单个相似度计算问题,如果碰上向量很长,无论什么相似度计算方法,都要遍历向量,如果用循环实现就更可观了,所以通常降低相似度计算复杂度的办法有两种。
<li>
对向量采样计算。道理很简单两个一百维的向量计算出的相似度是0.7我现在忍受一些精度的损失不用100维计算随机从中取出10维计算得到相似度是0.72显然用100维计算出的0.7更可信一些但是在计算复杂度降低十倍的情形下0.72和它误差也不大后者更经济。这个算法由Twitter提出叫做 DIMSUM 算法已经在Spark中实现了。
</li>
<li>
向量化计算。与其说这是一个小技巧,不如说这是一种思维方式。在机器学习领域,向量之间的计算是家常便饭,难道向量计算都要用循环实现吗?并不是,现代的线性代数库都支持直接的向量运算,比循环快很多。也就是我们在任何地方,都要想办法把循环转换成向量来直接计算,一般像常用的向量库都天然支持的,比如 Python 的 NumPy 。
</li>
其次的问题就是,如果用户量很大,两两之间计算代价就很大。
有两个办法来缓解这个问题:
第一个办法是:将相似度计算拆成 Map Reduce 任务将原始矩阵Map成键为用户对值为两个用户对同一个物品的评分之积Reduce 阶段对这些乘积再求和Map Reduce任务结束后再对这些值归一化
第二个办法是:不用基于用户的协同过滤。
另外,这种计算对象两两之间的相似度的任务,如果数据量不大,一般来说不超过百万个,然后矩阵又是稀疏的,那么有很多单机版本的工具其实更快,比如 KGraph、 GraphCHI 等。
**3 推荐计算**
得到了用户之间的相似度之后。接下来还有一个硬骨头,计算推荐分数。显然,为每一个用户计算每一个物品的推荐分数,计算次数是矩阵的所有元素个数,这个代价,你当然不能接受啊。这时候,你注意回想一下前面那个汇总公式,有这么几个特点我们可以来利用一下:
1. 只有相似用户喜欢过的物品需要计算,这个大大的赞,这个数量相比全部物品少了很多;
1. 把计算过程拆成Map Reduce任务。
拆Map Reduce任务的做法是
1. 遍历每个用户喜欢的物品列表;
1. 获取该用户的相似用户列表;
1. 把每一个喜欢的物品Map成两个记录发射出去一个是键为&lt;相似用户ID物品ID1&gt;三元组,可以拼成一个字符串,值为&lt;相似度&gt;,另一个是键为&lt;相似用户ID物品ID0&gt;三元组,值为&lt;喜欢程度*相似度&gt;其中的1和0为了区分两者在最后一步中会用到
1. Reduce阶段求和后输出
1. &lt;相似用户ID物品ID, 0&gt;的值除以&lt;相似用户ID物品ID, 1&gt;的值
一般来说,中小型公司如果没有特别必要的话,不要用分布式计算,看上去高大上、和大数据沾上边了,实际上得不偿失。
拆分Map Reduce任务也不一定非要用 Hadoop 或者Spark实现。也可以用单机实现这个过程。
因为一个Map过程其实就是将原来耦合的计算过程解耦合了、拍扁了这样的话我们可以利用多线程技术实现Map效果。例如C++里面 OpenMP 库可以让我们无痛使用多线程,充分剥削计算机所有的核。
**4 一些改进**
对于基于用户的协同过滤有一些常见的改进办法,改进主要集中在用户对物品的喜欢程度上:
1. 惩罚对热门物品的喜欢程度,这是因为,热门的东西很难反应出用户的真实兴趣,更可能是被煽动,或者无聊随便点击的情形,这是群体行为常见特点;
1. 增加喜欢程度的时间衰减,一般使用一个指数函数,指数就是一个负数,值和喜欢行为发生时间间隔正相关即可,这很好理解,小时候喜欢的东西不代表我现在的口味,人都是会变的,这是人性。
## 应用场景
最后,说一说基于用户的协同过滤有哪些应用场景。基于用户的协同过滤有两个产出:
1. 相似用户列表;
1. 基于用户的推荐结果。
所以我们不但可以推荐物品,还可以推荐用户!比如我们在一些社交平台上看到:“相似粉丝”“和你口味类似的人”等等都可以这样计算。
对于这个方法计算出来的推荐结果本身由于是基于口味计算得出所以在更强调个人隐私场景中应用更佳在这样的场景下不受大V影响更能反应真实的兴趣群体而非被煽动的乌合之众。
# 总结
今天,我与你聊了基于用户的协同过滤方法,也顺带普及了一下协同过滤这个大框架的思想。基于用户的协同过滤算法简单直接,但是非常有效。只是,在实现这个方法时,有很多需要注意的地方,比如:
1. 相似度计算本身如果遇到超大维度向量怎么办;
1. 两两计算用户相似度遇到用户量很大怎么办?
同时,我也聊到了如何改进这个推荐算法,希望能够帮到你,针对你自己的产品,你可以再多想几种改进办法吗?欢迎留言一起讨论。感谢你的收听,我们下次再见。
<img src="https://static001.geekbang.org/resource/image/87/b0/873b086966136189db14874181823fb0.jpg" alt="" />

View File

@@ -0,0 +1,130 @@
<audio id="audio" title="08 | 解密“看了又看”和“买了又买”" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/26/d5/2682f066b397e3fb425ae70e0f6d9ed5.mp3"></audio>
不管你有没有剁过手,你对“看了这个商品的还看了”这样的推荐形式一定不陌生。无论是猫还是狗,或者是其他电商网站,这样的推荐产品可以说是推荐系统的标配了。
类似的还有,如点评标记类网站的“喜欢了这部电影的还喜欢了”,社交媒体网站的“关注了这个人还关注了”,这些都只是文案类似,动词不同而已。
这样的推荐形式背后都是来自一个古老的推荐算法叫做基于物品的协同过滤通常也被叫作Item-Based因为后者更容易搜索到相关的文章所以被更多地提及。
如果做推荐系统不知道“基于物品的协同过滤”,那等同于做程序员不懂得冒泡排序。这个朴素的算法,就像是乔峰大战聚贤庄所用的“太祖长拳”一样,简单直接有效,读过高中就懂,用得好也能够战倒绝大多数的武林豪杰。今天,我就来和你聊聊这个朴素的算法。
## 基于物品Item-Based的八卦
基于物品的协同过滤算法诞生于1998年是由亚马逊首先提出的并在2001年由其发明者发表了相应的论文 Item-Based Collaborative Filtering Recommendation Algorithms )。
这篇论文在Google学术上引用数已近7000并且在WWW2016大会上被授予了“时间检验奖”颁奖词是“这篇杰出的论文深深地影响了实际应用”。历经了15年后仍然在发光发热这个奖它显然受之无愧。
虽然今天各家公司都在使用这个算法好像它是一个公共资源一样然而并不是这样亚马逊早在1998年也就是论文发表的三年前就申请了专利。
讲完了算法的八卦,开始说正事了。
## 基于物品Item-Based原理
在基于物品的协同过滤出现之前,信息过滤系统最常使用的是基于用户的协同过滤。基于用户的协同过滤首先计算相似用户,然后再根据相似用户的喜好推荐物品,这个算法有这么几个问题:
1. 用户数量往往比较大,计算起来非常吃力,成为瓶颈;
1. 用户的口味其实变化还是很快的,不是静态的,所以兴趣迁移问题很难反应出来;
1. 数据稀疏,用户和用户之间有共同的消费行为实际上是比较少的,而且一般都是一些热门物品,对发现用户兴趣帮助也不大。
和基于用户的不同,基于物品的协同过滤首先计算相似物品,然后再根据用户消费过、或者正在消费的物品为其推荐相似的,基于物品的算法怎么就解决了上面这些问题呢?
首先,物品的数量,或者严格的说,可以推荐的物品数量往往少于用户数量;所以一般计算物品之间的相似度就不会成为瓶颈。
其次,物品之间的相似度比较静态,它们变化的速度没有用户的口味变化快;所以完全解耦了用户兴趣迁移这个问题。
最后,物品对应的消费者数量较大,对于计算物品之间的相似度稀疏度是好过计算用户之间相似度的。
根据我在上一篇文章中所说,协同过滤最最依赖的是用户物品的关系矩阵,基于物品的协同过滤算法也不能例外,它的基本步骤是这样的:
1. 构建用户物品的关系矩阵,矩阵元素可以是用户的消费行为,也可以是消费后的评价,还可以是对消费行为的某种量化如时间、次数、费用等;
1. 假如矩阵的行表示物品,列表示用户的话,那么就两两计算行向量之间的相似度,得到物品相似度矩阵,行和列都是物品;
1. 产生推荐结果,根据推荐场景不同,有两种产生结果的形式。一种是为某一个物品推荐相关物品,另一种是在个人首页产生类似“猜你喜欢”的推荐结果。不要急,稍后我会分别说。
### 计算物品相似度
前面较为笼统地说要计算物品之间的相似度,现在详细说说这块。从用户物品关系矩阵中得到的物品向量长什么样子呢?我来给你描述一下:
1. 它是一个稀疏向量;
1. 向量的维度是用户,一个用户代表向量的一维,这个向量的总共维度是总用户数量;
1. 向量各个维度的取值是用户对这个物品的消费结果,可以是行为本身的布尔值,也可以是消费行为量化如时间长短、次数多少、费用大小等,还可以是消费的评价分数;
1. 没有消费过的就不再表示出来,所以说是一个稀疏向量。
接下来就是如何两两计算物品的相似度了,一般选择余弦相似度,当然还有其他的相似度计算法方法也可以。计算公式如下:
$$sim(i,j) = \frac{\sum_{k=1}^{n}{R_{ik} * R_{jk}}} { \sqrt{\sum_{k=1}^{n}{R_{ik}^{2}}} \sqrt{\sum_{k=1}^{n}{R_{jk}^{2}}}}$$
用文字解释一下这个公式:
>
分母是计算两个物品向量的长度,求元素值的平方和再开方。分子是两个向量的点积,相同位置的元素值相乘再求和。
很简单,因为这个公式出自中学数学课本,所以我刚才说读过高中就懂。
这个公式的物理意义就是计算两个向量的夹角余弦值相似度为1时对应角度是0好比时如胶似漆相似度为0时对应角度为90度毫不相干互为路人甲。
看上去计算量很大貌似每一个求和的复杂度都是和向量维度、也就是用户数量一样的。但是别忘了前面我说过他们都是稀疏向量也就是向量中绝大多数值都是0求和时不用算点积时更不用算甚至求点积时只用管两个物品的公共用户只是少许几个乘积而已。
物品之间的相似度计算是这个算法最可以改进的地方。通常的改进方向有下面两种。
**1. 物品中心化。**把矩阵中的分数,减去的是物品分数的均值;先计算每一个物品收到评分的均值,然后再把物品向量中的分数减去对应物品的均值。这样做的目的是什么呢?去掉物品中铁杆粉丝群体的非理性因素,例如一个流量明星的电影,其脑残粉可能会集体去打高分,那么用物品的均值来中心化就有一定的抑制作用。
**2. 用户中心化。**把矩阵中的分数,减去对应用户分数的均值;先计算每一个用户的评分均值,然后把他打过的所有分数都减去这个均值。
这样做的目的又是什么呢?每个人标准不一样,有的标准严苛,有的宽松,所以减去用户的均值可以在一定程度上仅仅保留了偏好,去掉了主观成分。
上面提到的相似度计算方法不只是适用于评分类矩阵也适用于行为矩阵。所谓行为矩阵即矩阵元素为0或者1的布尔值也就是在前面的专栏中讲过的隐式反馈。隐式反馈取值特殊有一些基于物品的改进推荐算法无法应用比如著名的Slope One算法。
### 计算推荐结果
在得到物品相似度之后,接下来就是为用户推荐他可能会感兴趣的物品了,基于物品的协同过滤,有两种应用场景。
**第一种属于TopK推荐形式上也常常属于类似“猜你喜欢”这样的。**
出发方式是当用户访问首页时,汇总和“用户已经消费过的物品相似”的物品,按照汇总后分数从高到低推出。汇总的公式是这样的:
$$\hat{R_{ui}} = \frac{\sum_{j=1}^{m}{sim(i,j)* R_{uj}}}{\sum_{j=1}^{m}{sim(i,j)}}$$
这个公式描述一下,核心思想就和基于用户的推荐算法一样,用相似度加权汇总。
要预测一个用户u对一个物品i的分数遍历用户u评分过的所有物品假如一共有m个每一个物品和待计算物品i的相似度乘以用户的评分这样加权求和后除以所有这些相似度总和就得到了一个加权平均评分作为用户u对物品i的分数预测。
和基于物品的推荐一样,我们在计算时不必对所有物品都计算一边,只需要按照用户评分过的物品,逐一取出和它们相似的物品出来就可以了。
这个过程都是离线完成后去掉那些用户已经消费过的保留分数最高的k个结果存储。当用户访问首页时直接查询出来即可。
**第二种属于相关推荐,也就是我们今天专栏题目所指的场景。**
这类推荐不需要提前合并计算,当用户访问一个物品的详情页面时,或者完成一个物品消费的结果面,直接获取这个物品的相似物品推荐,就是“看了又看”或者“买了又买”的推荐结果了。
### Slope One算法
经典的基于物品推荐相似度矩阵计算无法实时更新整个过程都是离线计算的而且还有另一个问题相似度计算时没有考虑相似度的置信问题。例如两个物品他们都被同一个用户喜欢了且只被这一个用户喜欢了那么余弦相似度计算的结果是1这个1在最后汇总计算推荐分数时对结果的影响却最大。
Slope One算法针对这些问题有很好的改进。在2005年首次问世Slope One算法专门针对评分矩阵不适用于行为矩阵。Slope One算法计算的不是物品之间的相似度而是计算的物品之间的距离相似度的反面。举个例子就一目了然下面是一个简单的评分矩阵
<img src="https://static001.geekbang.org/resource/image/3b/cb/3b7da53ac1304a3a5d8211648ec19dcb.png" alt="" />
这个矩阵反应了这些事实用户1给物品A、B、C都评分了分别是532用户2给物品A、B评分了分别是3、4用户3给物品B、C评分了分别是2、5。现在首先来两两计算物品之间的差距
<img src="https://static001.geekbang.org/resource/image/7d/8f/7d9e2189fccd9d186232c7f3ba7f858f.png" alt="" />
括号里表示两个物品的共同用户数量代表两个物品差距的置信程度。比如物品A和物品B之间的差距是0.5共同用户数是2反之物品B和物品A的差距是-0.5共同用户数还是2。知道这个差距后就可以用一个物品去预测另一个物品的评分。
如果只知道用户3给物品B的评分是2那么预测用户3给物品A的评分呢就是2.5因为从物品B到物品A的差距是0.5。
在此基础上继续推进,如果知道用户给多个物品评分了,怎么汇总这些分数呢?
方法是把单个预测的分数按照共同用户数加权求平均。比如现在知道用户3不但给物品B评分为2还给物品C评分为5物品B对物品A的预测是2.5分刚才计算过了物品C给物品A的预测是8分再加权平均。
$$ \frac{8*1 + 2.5 * 2 }{(1+2)} = 4.33 $$
就得到了推荐分数为4.33分。是不是很简单?
## 总结
今天我们在基于用户的协同过滤基础上介绍了比较常见的一个算法:基于物品的协同过滤。这个方法常常在电商网站上见到,“买了又买”“看了又看”这样的相关推荐,都是由这个推荐算法产生。
最后我们介绍了一个改良版的基于物品推荐算法Slope One。这里也留下了一个问题给你为什么说Slope One可以做到在线更新呢欢迎留言讨论。
<img src="https://static001.geekbang.org/resource/image/87/b0/873b086966136189db14874181823fb0.jpg" alt="" />

View File

@@ -0,0 +1,113 @@
<audio id="audio" title="09 | 协同过滤中的相似度计算方法有哪些" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/72/37/72741b6f13a9026eab2011666e622737.mp3"></audio>
今天,我们来聊聊协同过滤中的相似度计算方法有哪些。
## 相似度的本质
推荐系统中,推荐算法分为两个门派,一个是机器学习派,另一个就是相似度门派。机器学习派是后起之秀,而相似度派则是泰山北斗,以致撑起来推荐系统的半壁江山。
近邻推荐顾名思义就是在地理位置上住得近。如果用户有个邻居,那么社交软件上把邻居推荐给他在直观上就很合理,当然,如果邻居姓王的话,就不要推荐了。
这里说的近邻,并不一定只是在三维空间下的地理位置的近邻,在任意高维空间都可以找到近邻,尤其是当用户和物品的特征维度都很高时,要找到用户隔壁的邻居,就不是那么直观,需要选择好用适合的相似度度量办法。
近邻推荐的核心就是相似度计算方法的选择,由于近邻推荐并没有采用最优化思路,所以效果通常取决于矩阵的量化方式和相似度的选择。
相似度,与之配套的还有另一个概念就是距离,两者都是用来量化两个物体在高维空间中的亲疏程度的,它们是硬币的两面。
**推荐算法中的相似度门派,实际上有这么一个潜在假设:如果两个物体很相似,也就是距离很近,那么这两个物体就很容易产生一样的动作。**
如果两篇新闻很相似,那么他们很容易被同一个人先后点击阅读,如果两个用户很相似,那么他们就很容易点击同一个新闻。这种符合直觉的假设,大部分时候很奏效。
其实属于另一门派的推荐算法——机器学习中,也有很多算法在某种角度看做是相似度度量。
例如,逻辑回归或者线性回归中,一边是特征向量,另一边是模型参数向量,两者的点积运算,就可以看做是相似度计算,只不过其中的模型参数向量值并不是人肉指定的,而是从数据中由优化算法自动总结出来的。
在近邻推荐中,最常用的相似度是余弦相似度。然而可以选用的相似度并不只是余弦相似度,还有欧氏距离、皮尔逊相关度、自适应的余弦相似度、局部敏感哈希等。使用场景各不相同,今天,我会分别一一介绍如下。
## 相似度的计算方法
### 数据分类
在真正开始巡视相似度计算方法前,我先给你把度量对象做个简单分类。相似度计算对象是向量,或者叫做高维空间下的坐标,一个意思。那表示这个向量的数值就有两种:
1. 实数值;
1. 布尔值也就是0或者1。
下面介绍的不同计算方法适用于不同的数据种类。
### 1欧氏距离
欧氏距离如名字所料是一个欧式空间下度量距离的方法。两个物体都在同一个空间下表示为两个点假如叫做p和q分别都是n个坐标。那么欧式距离就是衡量这两个点之间的距离从p到q移动要经过的距离。欧式距离不适合布尔向量之间。
计算方式可以表示如下,我在文稿中放了一个公式,你可以点击查看。
$$E(p,q) = \sqrt{\sum_{i=1}^{n}{(p_{i} - q_{i})^{2}}}$$
这个公式就是,每一个坐标上的取值相减,求平方和,最后输出方根。
显然,欧式距离得到的值是一个非负数,最大值是正无穷。通常相似度计算度量结果希望是[-11]或者[01]之间,所以欧式距离要么无法直接使用到这个场景中,要么需要经过二次转化得到,我在文稿中放了一个最常用的转化公式,你可以点击查看。
$$ \frac{1}{1+E(p,q)} $$
距离加一后取倒数。这个公式能够把范围为0到正无穷的欧式距离转换为0到1的相似度。
欧式距离度量的是空间中两个点的绝对差异,适用于分析用户能力模型之间的差异,比如消费能力、贡献内容的能力等。
当然,虽然欧式距离计算两个点的距离,实际上,点的坐标表示和我们常说的向量表示是同一回事,希望这句话是废话,你早已懂得。
### 2余弦相似度
大名鼎鼎的余弦相似度度量的是两个向量之间的夹角其实就是用夹角的余弦值来度量所以名字叫余弦相似度。当两个向量的夹角为0度时余弦值为1当夹角为90度时余弦值为0为180度时余弦值则为-1。
余弦相似度在度量文本相似度、用户相似度、物品相似度的时候都较为常用;但是在这里需要提醒你一点,余弦相似度的特点:它与向量的长度无关。因为余弦相似度计算需要对向量长度做归一化:
$$cos(p,q) = \frac{\sum_{i}{p_{i}q_{i}}}{\sqrt{\sum_{i}{q_{i}^{2}}}\sqrt{\sum_{i}{p_{i}^{2}}}} $$
经过向量长度归一化后的相似度量方式,背后潜藏着这样一种思想:两个向量,只要方向一致,无论程度强弱,都可以视为“相似”。
这简直就是:招聘人才时只看价值观,不考核代码能力,只要肯干,搬砖嘛,谁搬不是搬。这样做错不错呢?很显然,有非常大的合理性。
比如我用140字的微博摘要了一篇5000字的博客内容两者得到的文本向量可以认为方向一致词频等程度不同但是余弦相似度仍然认为他们是相似的。
在协同过滤中,如果选择余弦相似度,某种程度上更加依赖两个物品的共同评价用户数,而不是用户给予的评分多少。这就是由于余弦相似度被向量长度归一化后的结果。
余弦相似度对绝对值大小不敏感这件事,在某些应用上仍然有些问题。
举个小例子用户A对两部电影评分分别是1分和2分用户B对同样这两部电影评分是4分和5分。用余弦相似度计算出来两个用户的相似度达到0.98。这和实际直觉不符用户A明显不喜欢这两部电影。
针对这个问题对余弦相似度有个改进改进的算法叫做调整的余弦相似度Adjusted Cosine Similarity。调整的方法很简单就是先计算向量每个维度上的均值然后每个向量在各个维度上都减去均值后再计算余弦相似度。
前面这个小例子,用调整的余弦相似度计算得到的相似度是-0.1,呈现出两个用户口味相反,和直觉相符。
### 3皮尔逊相关度
皮尔逊相关度实际上也是一种余弦相似度不过先对向量做了中心化向量p和q各自减去向量的均值后再计算余弦相似度。
$$R(p,q) = \frac{\sum_{i=1}^{n}{(p_{i} - \bar{p})(q_{i} - \bar{q})}}{\sqrt{\sum_{i=1}^{n}{(p_{i} - \bar{p})^{2}}}\sqrt{\sum_{i=1}^{n}{(q_{i} - \bar{q})^{2}}}}$$
皮尔逊相关度计算结果范围在-1到1。-1表示负相关1比表示正相关。皮尔逊相关度其实度量的是两个随机变量是不是在同增同减。
如果同时对两个随机变量采样当其中一个得到较大的值另一也较大其中一个较小时另一个也较小时这就是正相关计算出来的相关度就接近1这种情况属于沆瀣一气反之就接近-1。
由于皮尔逊相关度度量的时两个变量的变化趋势是否一致所以不适合用作计算布尔值向量之间相关度因为两个布尔向量也就是对应两个0-1分布的随机变量这样的随机变量变化只有有限的两个取值根本没有“变化趋势高低起伏”这一说。
### 4 杰卡德Jaccard相似度
杰卡德相似度,是两个集合的交集元素个数在并集中所占的比例。由于集合非常适用于布尔向量表示,所以杰卡德相似度简直就是为布尔值向量私人定做的。对应的计算方式是:
1. 分子是两个布尔向量做点积计算,得到的就是交集元素个数;
1. 分母是两个布尔向量做或运算,再求元素和。
余弦相似度适用于评分数据,杰卡德相似度适合用于隐式反馈数据。例如,使用用户的收藏行为,计算用户之间的相似度,杰卡德相似度就适合来承担这个任务。
## 总结
今天,我介绍了常用的几种相似度计算方法,以及其各自的使用场景。
这里的场景是按照数据形式划分的,按照向量维度取值是否是布尔值来看,杰卡德相似度就只适合布尔值向量,余弦相似度弹性略大,适合两种向量。欧式距离度量的是绝对差异,余弦相似度度量的是方向差异,但是调整的余弦相似度则可以避免这个弱点。
现在留给你一个问题:如果在一个社交网络中,要计算好友的相似度,你会选择哪种相似度来做?欢迎留言讨论。
感谢收听,我们下期再见。
<img src="https://static001.geekbang.org/resource/image/dc/2a/dc6f7ddc7a766cf76f1f21aedd16b42a.jpg" alt="" />