K近邻
- 基本思想
- 欧氏距离
- 算法流程
- 代码
- 基于近邻用户的协同过滤
- 基于近邻物品的协同过滤
- 杰卡德相似度
基本思想
我们根据涂色样本点和未涂色样本点 X 的距离给涂色样本点编号1-6,即:1号样本点距离X最近,其余次之。
那么问题来了:样本点 X 应该属于哪种颜色呢?是蓝色还是绿色?
其实,我们可以根据 X 的相邻样本点来判定。例如,和 X 距离最近的三个样本点中绿色占多数,那么 X 就属于为绿色;和 X 距离最近的 5 个样本点中蓝色占多数,那么 X 就属于蓝色。
这种解决问题的思路正是 K 近邻算法的基本思想:根据 K 个近邻样本的 y 值来预测自身的 y 值。具体到上面例子中的 y 值就是样本点的颜色。
K 近邻是监督学习中比较简单的一种算法,它既可以解决分类问题,也可以解决回归问题。
上面的涂色问题本质上就是利用 K 近邻算法给颜色未知的样本进行分类。颜色已知的样本属于训练样本,颜色未知的样本属于测试样本。
我们已经知道 K 近邻算法的基本思想是根据 K 个近邻样本的 y 值来预测自身的 y 值。具体到分类问题中,y 值就是样本类别的取值,一般采用多数表决的原则来对测试样本的类别进行预测。
那么如何使用 K 近邻来解决回归问题呢?请看下图:
回归问题中预测的 y 值是一个连续值,上图中每个样本点周围的数字代表其 y 值,K近邻是将离 X 最近的 K个样本的 y 值的平均值作为 X 的预测 y 值。例如:K=3 时,X 的预测输出 y = (1.2+3.4+8.3)/3 = 4.3;K=5 时,X 的预测输出 y = (1.2+3.4+8.3+5.5+4.5)/5 = 4.58 。
欧氏距离
图中的 a 和 b 代表两个样本,更具体一点说是样本的特征向量,每个样本的特征向量维度是2,即:每个样本都有两个特征。
a 和 b 其实是二维特征空间中的两个点,拓展到更高维的空间的距离公式是类似的:
发现规律了吗?其实就是先计算向量 a 和向量 b 对应维度数值差的平方和,然后再开方。由此我们可以写出任意维特征向量 a 和 b 的欧式距离公式:
算法流程
本门课中主要探讨 K 近邻在分类问题上的应用,下面我们来看一下 K 近邻分类算法的流程:
第一步:准备训练样本集:data = {[x1,x2,…,xn,y],…},其中 x1,x2,…,xn 是样本特征,y 是样本类别取值。
例如我们有一个特征维数为 2、样本数量为 3 的训练集:data = {[1,2,0],[3,1,0],[2,3,1]},则其中的样本分别为 [1,2,0]、[3,1,0]、[2,3,1],样本特征分别为 [1,2]、[3,1]、[2,3],样本类别分别为 0、0、1。
第二步:输入测试样本 A:[x1,x2,…,xn],例如测试样本的特征是二维的 [1,4]
第三步:计算测试样本 A 和所有训练样本的距离。
具体来讲,可以是第二步中的测试样本 [1,4] 和第一步中的训练样本 [1,2]、[3,1]、[2,3] 分别计算欧式距离。
第四步:按照距离递增排序。
第五步:选取与 A 距离最小的 K 个样本。
第六步:计算这 K 个样本所在类别的出现频率。
第七步:返回出现频率最高的类别作为 A 的预测分类。
K 近邻中的 K 值是人为设定的参数, K 值的选取会对预测结果产生影响。除了 K 值外,K 近邻的预测结果还受距离度量和决策规则的影响。
距离度量实际上是衡量两个样本的相似程度:距离越小,相似程度越高。常见的相似性度量函数有:欧氏距离、余弦相似性、皮尔逊相关系数
代码
# 1. 导入工具包
import numpy as np
import pandas as pd
from collections import Counter# 2. 欧式距离定义
def euclidean_distance(vec1,vec2):return np.sqrt(np.sum(np.square(vec1 - vec2)))# 3. 构造数据集
train_data = {'宝贝当家':[45,2,9,'喜剧片'],'美人鱼':[21,17,5,'喜剧片'],'澳门风云3':[54,9,11,'喜剧片'],'功夫熊猫3':[39,0,31,'喜剧片'],'谍影重重':[5,2,57,'动作片'],'叶问3':[3,2,65,'动作片'],'我的特工爷爷':[6,4,21,'动作片'],'奔爱':[7,46,4,'爱情片'],'夜孔雀':[9,39,8,'爱情片'],'代理情人':[9,38,2,'爱情片'],'新步步惊心':[8,34,17,'爱情片'],'伦敦陷落':[2,3,55,'动作片']}train_df = pd.DataFrame(train_data).T
train_df.columns = ['搞笑镜头','拥抱镜头','打斗镜头','电影类型']test_data = {'唐人街探案':[23,3,17]} # 4. 确定K值和分类的电影
K = 5
movie = '唐人街探案'# 5. 计算欧式距离
distance_list = []
for train_X in train_df.values[:,:-1]:test_X = np.array(test_data[movie])distance_list.append(euclidean_distance(train_X,test_X))# 6. 按照距离递增排序
distance_df = pd.DataFrame({"欧式距离":distance_list},index=train_df.index)
result = pd.concat([train_df,distance_df],axis=1).sort_values(by="欧式距离")# 7. ToDo:输出分类结果
d = Counter(result.head(K)['电影类型'])
print(movie,max(d,key=d.get))
基于近邻用户的协同过滤
假定有一个场景:某个周日的下午,你感觉很无聊,然后从电脑上打开了一个视频网站,想看下最近有什么好看的电影。然而你发现网站上的热门电影基本都看过,其他的电影又太多,不知道该看什么。想使用搜索框去查一下,但是又不知道该搜什么关键词,这个时候你的内心很焦灼,总不能挨个去尝试吧,那时间成本也太大了…
仔细想想还是有办法的,那就是问一下你的好朋友,他最近喜欢看什么电影,让他给你推荐几部好看的电影,这样就省去了自己去挑选和尝试的时间了。
这种思想其实就是基于近邻用户的协同过滤算法(简称UserCF):给用户 A 推荐和他有着相似观影兴趣的用户 B 喜欢观看的电影。如图所示:
从图中可以看出,用户 A 的好友用户 B 喜欢看电影 2、3、4,恰好电影 3 和电影 4 用户 A 没有看过,所以就可以把电影 3 和电影 4 推荐给用户 A 。
基于近邻用户的协同过滤算法很容易给出的推荐理由是:和你兴趣相似的人还喜欢下面的电影。
那为什么说用户 A 和用户 B 的观影兴趣相似呢?生活中的经验告诉我们:物以类聚人以群分。既然 A 和 B 能够成为好朋友,那么他们必然就有着某些共同的价值观和兴趣爱好。
体现在上面的这幅图中为:用户 A 和用户 B 都观看过电影 2 。
基于近邻用户的协同过滤算法,第一个要理解的点是近邻用户,也就是兴趣相似的用户;第二个要理解的点是协同过滤算法到底指的是什么?
所谓的协同过滤,其实指的是一类算法的称呼:根据用户的行为数据给用户产生推荐结果的一类算法。
上面我们是根据用户的观影记录,即每个用户看过哪些电影,来进行电影推荐,所以它属于协同过滤的一种。
既然可以将用户 B 喜欢的电影推荐给用户 A,那么用户 A 喜欢的电影也可以推荐给用户 B。所以,基于近邻用户的协同过滤算法是在观影兴趣相似的用户间互相推荐电影。如图所示:
细心观察可以发现,电影 2 并没有在用户 A 和用户 B 中进行推荐。原因是:推荐系统的本质是建立用户和物品的连接,电影 2 在用户 A 和用户 B 上已经存在连接了,所以也就不需要再进行推荐了。
另一方面,看过的电影一般就不再推荐了,这个也符合生活常识。
假如用户 A 前两天刚看过电影 2,然后你又给他推荐电影 2,这显然不是用户 A 想要的结果。
这里有一个发散性的想法:如果电影 2 是用户 A 和用户 B 十多年前看过的电影,是不是可以再推荐给他们怀旧一下呢,哈哈~
通过这个发散性的想法,我想表达的一个观点是:推荐系统要做的事情其实是分析用户的心理,让用户满意,给到他们想要的东西,也给到使他们惊喜和意外的东西。
下面我们来梳理一下基于近邻用户的协同过滤算法实现的流程:
比如我们需要给用户 A 推荐电影,那么首先要找到和用户 A 观影兴趣最相似的 K 个用户,然后再从这 K 个用户喜欢的电影中,找到用户 A 没有看过的电影,推荐给 A。
如图所示,先不考虑相似度的计算方法,K=3 的情况下,和用户 A 最相似的 3 个用户依次是用户 B、C、D,从这 3 个用户喜欢的电影集合中过滤掉用户 A 看过的电影,然后计算 A 对剩下的电影感兴趣的程度,从中选取最感兴趣的的 3 个电影推荐给用户 A 。这里的推荐数量根据产品需求来设定,不一定是 3。
这里的感兴趣程度计算方法是将每个电影上有观看行为的用户相似度求和得到的,例如 A 对电影 2 的感兴趣程度为:用户 B 的 0.8 + 用户 C 的 0.6 = 1.4
基于近邻物品的协同过滤
推荐系统会根据你过往看过的电影,从电影库中查找相似的电影推荐给你,这种方法叫做基于近邻物品的协同过滤算法(简称ItemCF)。如图所示:
用户 A 看过电影 1,那么就给他推荐相似的电影 2;用户 D 看过电影 2,那么就给他推荐相似的电影 1。电影 1 和电影 2 相似是因为他们有着共同的观影群体(B和C)。基于近邻物品的协同过滤算法常见的推荐理由是:看了该电影的用户还看了如下电影
基于近邻物品的协同过滤算法,第一个要理解的点是近邻物品,也就是用户群体相似的物品;第二个要理解的点是协同过滤,这个前面已经讲过,核心是利用了用户行为数据,此处不再赘述。
从上面的图中可以看出,推荐系统只在电影 2 和用户 A 之间、电影 1 和用户 D 之间建立了推荐路线,原因是用户 B 和用户 C 之前都和这两个电影有过观看连接。
换个说法,因为用户 B 看过电影 1 和电影 2,所以只能给用户 B 推荐和电影 1 或电影 2 相似的电影,而不是推荐电影 1 和电影 2 本身。图示仅作示意,实际上还有很多电影可以和用户 B 建立连接,前提是要满足用户 B 的兴趣。
总结一下:基于近邻物品的协同过滤算法是给用户推荐和他过去喜欢的电影相似的电影。具体的算法流程是:比如我们要给用户 A 推荐电影,首先要在用户 A 喜欢的电影中分别找到 K 个最相似的电影,然后再从这些电影中找到用户 A 没看过的电影推荐给 A 。
这里的感兴趣程度计算方法是将每个候选电影上的电影相似度求和得到的,例如 A 对电影 4 的感兴趣程度为:电影 4 和电影 1 的相似度 0.5 + 电影 4 和电影 2 的相似度 0.4 = 0.9
杰卡德相似度
前面我们讲的两种基于近邻的协同过滤算法有一个共同点,那就是计算相似度。其实在本章第 1节课中我们已经提到了几种相似度的计算方法,比如欧氏距离、余弦相似性、皮尔逊相关系数。
下面我们再介绍一种相似度的计算方法:杰卡德(Jaccard)相似度,杰卡德相似度是指两个集合的交集元素个数在并集中所占的比例。先来看一幅图片:
图中展现的是代表用户观影记录的行为矩阵,矩阵中的 1 表示用户看过对应的电影,0 表示没看过。据此,我们可以根据杰卡德相似度定义分别计算出用户的相似度矩阵和电影的相似度矩阵。先来看用户相似度矩阵:
图中用户相似度矩阵的取值是关于对角线对称的,实际上我们需要的只有右上角的 6个值。那么这些值是怎么算出来的呢?
就拿用户 B 和用户 D 的相似度 1/3 举例吧,杰卡德相似度其实就是交集和并集的占比,如图所示: