机器学习入门笔记11

智能推荐系统

系列文章

智能推荐系统

智能推荐系统根据分析用户的行为,挖掘用户感兴趣的内容,进行个性化推荐。

基本概念

搭建智能推荐系统的算法有很多,常用的是协同过滤算法(collaborative filtering)。该算法基于用户群体的产品偏好数据,发现用户或物品之间的相关性,基于此完成推荐。

根据原理的不同,协同过滤算法分为两类——基于用户或物品的协同过滤算法。

该算法的本质是寻找相似的用户,进而推荐相似用户关注的内容。

该算法的本质是寻找相似的内容,进而为关注该内容的用户推荐它。

商业中,大多数应用场景基于使用物品的协同过滤算法,原因是相比物品,用户的数据一般多得多;且用户之间差距较大,但物品的相似度相对固定,方便计算与保存。

计算相似度的常见方法

欧氏距离

欧式距离即两点间的直线距离:

\\[ d(A,B)=\sqrt{(x_1-y_1)^2+(x_2-y_2)^2+\dots+(x_n-y_n)^2} \\]

距离越小,说明两者越接近(相似)。

除了直接比较欧氏距离,还可以利用衍生的公式比较两者相似度。基于欧氏距离的相似度 \\( sim(A,B) \\) 定义为:

\\[ sim(A,B)=\frac 1 {1+d(A,B)} \\]

两个数组之间的欧氏距离可以使用 np.linalg.norm(x - y) 来计算。

余项相似度

见前一章

皮尔逊相关系数

皮尔逊相关系数是用于描述两个变量间线性相关强弱程度的统计量,取值范围为 \\( [-1,1] \\) ,正值代表二者存在正相关,负值代表存在负相关。绝对值越大,相关性越强。

皮尔逊相关系数 \\( r \\) 的计算式如下:

\\[ r=\frac{\text{COV}(X,Y)}{\sqrt{D(X)} \sqrt{D(Y)}} \\]

其中,\\( \text{COV}(X,Y) \\) 为变量 \\( X \\)\\( Y \\) 的协方差,\\( D(X) \\)\\( D(Y) \\) 分别为变量 \\( X \\)\\( Y \\) 的方差。

Scipy 提供了一种计算皮尔逊相关系数的方式:

from scipy.stats import pearsonr
X = [1, 2, 4, 6, 9]
Y = [3, 5, 7, 8, 10]
corr = pearsonr(X, Y)
print(corr[0], corr[1], sep='\n')
0.9744883837453007 0.0048727247263547985

得到的结果是一个元组,包含两个浮点数:皮尔逊相关系数 \\( r \\) 和显著水平 \\( P \\) 值。\\( P \\) 值与显著检验性有关,小于 0.05 表示显著相关,即不是由偶然因素引起,此时 \\( r \\) 值才有意义。

除此之外,Pandas 的 DataFrame 对象提供了 .corrwith(other).corr() 方法,计算得到的结果就是皮尔逊相关系数。

实例:电影推荐系统

本节基于皮尔逊相关系数搭建电影推荐系统。其中数据来自 MovieLens,可以在 http://files.grouplens.org/datasets/movielens/ 下载。

这里下载了较小的 ml-latest-small.zip 。解压后主要用到以下两个文件:

使用以下代码将两者读取并拼接起来:

data = pd.merge(pd.read_csv('movies.csv'), pd.read_csv('ratings.csv'), on='movieId')
data.head(4)
movieIdtitlegenresuserIdratingtimestamp
01Toy Story (1995)Adventure|Animation|Children|Comedy|Fantasy14.0964982703
11Toy Story (1995)Adventure|Animation|Children|Comedy|Fantasy54.0847434962
21Toy Story (1995)Adventure|Animation|Children|Comedy|Fantasy74.51106635946
31Toy Story (1995)Adventure|Animation|Children|Comedy|Fantasy152.51510577970

接下来将电影评分表转换为数据透视表,并以电影评分作为数据透视表中显示的数据:

movies = data.pivot_table(index='userId', columns='title', values='rating')

可以检查该透视表的内容,不过大部分数据都为 NaN ,这是因为相对于电影数量,每个用户评分数量十分有限。

接下来以玩具总动员为例,分析应该向观看了该电影的用户推荐哪些电影。

首先从数据透视表中提取各用户对该电影的评分:

toy_story = movies['Toy Story (1995)']

然后就可以通过数据透视表的 .corrwith() 方法,计算每一部电影与该电影的相关系数:

similarity = pd.DataFrame(movies.corrwith(toy_story),
    columns=['relative coefficient']).dropna()
similarity.head(4)
relative coefficient
title
'burbs, The (1989)0.240563
(500) Days of Summer (2009)0.353833
*batteries not included (1987)-0.427425
10 Cent Pistol (2015)1.000000

最后,就可以通过相关系数得到相应的电影了。这里进一步筛去评价人数过少的电影,以免偶然因素对结果造成影响:

ratings = pd.DataFrame({'rating': data.groupby('title')['rating'].mean(),
                        'count': data.groupby('title')['rating'].count()})
similarity[similarity.join(ratings['count'])['count'] > 20] \
    .sort_values(by='relative coefficient', ascending=False).head()
relative coefficient
title
Toy Story (1995)1.000000
Darjeeling Limited, The (2007)0.868810
Paper, The (1994)0.805735
21 Grams (2003)0.793611
Amores Perros (Love's a Bitch) (2000)0.783188

至此,采用协同过滤算法搭建的智能推荐系统就完成了。除了自身外,它还推荐了 4 部电影。

(注释:结果仅供参考,因为其余 4 部电影都是限制级)