此次介绍Item(User)类似度的计算方法,其普遍运用于基于邻域的协同过滤算法的推荐系统。简而言之,基于邻域,就是基于相邻的元素进行推荐,而相邻元素的获得过程就是类似度的计算过程。html
对于空间上的点来讲:传统机器学习模型中KNN的距离度量方法(如欧式距离等),距离越近的点咱们把他们归为一类,也能够说他们更类似。python
对于空间上的向量来讲:方向更相同,向量越类似,这就是cosine度量方法的原理。git
问题来了,咱们获得不一样物品/用户的类似度有什么用呢❓github
🙋回答:从ItemCF的角度来讲,在获得物品之间的类似度\(w_{ij}\)(物品 i 和 j )以后,经过以下公式能够计算用户u对一个物品 j 的兴趣:算法
\(p_{uj}=\sum\limits_{i \in N(u)\cap S(j,k)}w_{ji}r_{ui}\tag{0}\)api
这里N(u)是用户喜欢的物品的集合,S(j, k)是物品j最类似的K个物品的集合,\(r_{ui}\)是用户u对物品i的兴趣程度。机器学习
这是一个在《推荐系统实践》中看到的公式,这里咱们研究两个用户users的兴趣类似度:给定用户u和用户v,令N(u),N(v)分别表示用户u,v曾经有过正反馈的物品集合。那么用户u和v的类似度为:函数
\(\omega_{uv}=\frac{|N(u)\cap N(v)|}{|N(u)\cup N(v)|} \tag{1}\)性能
上述公式简单表述就是:\(\frac{两个用户都感兴趣物品数目}{两个用户中只要有一个用户感兴趣的物品数目}\)学习
it’s the ratio of the size of the intersection to the size of the union of their preferred items
这是一个忽略了Preference value的类似度计算方式。
使用这个度量方法一般有两种状况:
Data中只有boolean值,并无rating值;
你认为数据的噪声不是很大。
与上述公式相同,只是在分母中加了个根号,这里咱们研究物品items的类似度:
\(\omega_{ij}=\frac{|N(i)\cap N(j)|}{\sqrt{|N(i)\cup N(j)|}}\tag{2}\)
这里N(i)和N(j)分别表示喜欢物品i 和物品j 的人数。
到这里为止,咱们研究的对象忽略的rating的具体分数,若是对象换作是评分,如电影评分(分数ratings有:1,2,3,4,5🌟),那么相应的cosine公式变换为:
\(\text{cosine_sim}(i, j) = \frac{\sum\limits_{u \in U_{ij}} r_{ui} \cdot r_{uj}}{\sqrt{\sum\limits_{u \in U_{ij}} r_{ui}^2} \cdot\sqrt{\sum\limits_{u \in U_{ij}} r_{uj}^2}}\tag{3}\)
其中\(r_{ui}\)和\(r_{uj}\)分别表示用户 u 对物品 i 和 j 的评分,\(U_{ij}\)表明同时喜欢物品 i 和 j 的用户集合。
如下为surprise库的cosine函数源码和分析:
def cosine(n_x, yr, min_support): ### 此处省略了一些东西 for y, y_ratings in iteritems(yr): ### xi和xj分别表示物品i和j ### 如下为生成(3)式中的分母和分子 for xi, ri in y_ratings: for xj, rj in y_ratings: freq[xi, xj] += 1 prods[xi, xj] += ri * rj sqi[xi, xj] += ri**2 sqj[xi, xj] += rj**2 ### 如下为使用(3)式进行计算 for xi in range(n_x): sim[xi, xi] = 1 for xj in range(xi + 1, n_x): if freq[xi, xj] < min_sprt: sim[xi, xj] = 0 else: denum = np.sqrt(sqi[xi, xj] * sqj[xi, xj]) sim[xi, xj] = prods[xi, xj] / denum sim[xj, xi] = sim[xi, xj] return sim ### 返回的结果sim是一个对称矩阵,行列的index表示对应每一个物品item,矩阵元素表示行列对应物品的类似度
若是在(3)式的基础上进行去均值的话,那么就获得了(4)式:
\(\text{pearson_sim}(i, j) = \frac{ \sum\limits_{u \in U_{ij}}(r_{ui} - \mu_i) \dot (r_{uj} - \mu_{j})} {\sqrt{\sum\limits_{u\in U_{ij}} (r_{ui} - \mu_i)^2} \cdot \sqrt{\sum\limits_{u \in U_{ij}} (r_{uj} - \mu_{j})^2} }\tag{4}\)
注意一点,这里的均值计算只考虑到同时喜欢物品i和j的用户集合\(U_{ij}\),对于其余不涉及物品i和j的用户,不要加到均值计算的过程当中。
一般来讲,不一样用户👨评分标准的差异要比不一样物品评分标准差异要高不少(The differences in the rating scales of individual users are often more pronounced than the differences in ratings given to individual items),由于不一样人的评分标准不同,对于某人来讲,他评分的全部物品分数都偏低。可是对于一个物品来讲,不一样物品之间所依据的评分标准都是大众评价的结果,这是一个被不一样标准泛化了的标准。
因此,当咱们计算物品类似度\(\text{pearson_sim}(i, j)\)时,减去的均值应该针对于用户,而不是物品。因此,PC能够优化为AC(Adjusted):
\(\text{ adjusted_sim}(i, j) = \frac{ \sum\limits_{u \in U_{ij}}(r_{ui} - \mu_u) \dot (r_{uj} - \mu_{u})} {\sqrt{\sum\limits_{u\in U_{ij}} (r_{ui} - \mu_u)^2} \cdot \sqrt{\sum\limits_{u \in U_{ij}} (r_{uj} - \mu_{u})^2} }\tag{5}\)
仍然考虑物品i和j的类似度,MSD考虑的角度为同时喜欢物品i和j的用户对于这两个物品的评分差距程度:
\(\text{msd}(i, j) = \frac{1}{|U_{ij}|} \cdot \sum\limits_{u \in U_{ij}} (r_{ui} - r_{uj})^2\tag{6}\)
(6)式表示均方差,值越小,物品i和j类似度越大。为了与以前的类似度表示一致(值越大,物品类似度越大),定义类似度为:
$ \text{msd_sim}(i,j) = \frac{1}{\text{msd}(i,j) + 1}\tag{7}$
对于推荐系统来讲,考虑到用户的数量,评分数据是至关稀疏的。上述方法获得的全部类似度权重一般只使用了很小一部分的评分。举个例子,假设两部很小众的电影正好同时只被两我的喜欢,运用上面的方法,咱们获得这两部影片类似度很高。然而实际状况可能并非这样,这可能咱们取的样本太少的缘故。因此,有这样一个思想很重要,即:当计算只用到很小范围的评分时,减少这个计算的类似度的权重
Reduce the magnitude of a similarity weight when this weight is computed using only a few ratings
咱们能够给计算出来的类似度一个惩罚(penalized),所用的评分集合\(U_{ij}\)越小,惩罚越大:
\(w_{ij}=\frac{min\{|U_{ij}|, \gamma\}}{\gamma} \times w_{ij}\tag{8}\)
当评分的用户集合大到必定程度时,惩罚消失。
活跃度跟高的用户一般会评分不少物品,覆盖范围也更广,也就是方差(var)越大,他们的评分多,可是贡献度却要少。
为何呢?假如一我的很是爱购物,在淘宝上疯狂买各类各样的东西,那么他的一个购买跟物品种类的相关性就很低。一样的,对于物品来讲,如电影《教父》,被不少人喜欢,那么根据它也很难找到与他类似的电影。简单来讲:活跃用户对物品类似度的贡献应该小于不活跃用户。
那么,咱们引入一个参数:
\(\lambda_{u} = log\frac{|I|}{|I_{u}|}\tag{9}\)
这个参数\(\lambda_{u}\)定义为用户u的活跃程度的倒数,\(I\)为全部物品,\(I_{u}\)为用户u有操做的物品,二者之商越大,表明活跃程度越低,即权重越高。
将该参数运用到Pearson中,即:
\(\text{pearson_sim}(i, j) = \frac{ \sum\limits_{u \in U_{ij}}\lambda_{u} (r_{ui} - \mu_i) \dot (r_{uj} - \mu_{j})} {\sqrt{\sum\limits_{u\in U_{ij}} \lambda_{u} (r_{ui} - \mu_i)^2} \cdot \sqrt{\sum\limits_{u \in U_{ij}} \lambda_{u} (r_{uj} - \mu_{j})^2} }\tag{10}\)
通常化,咱们能够把Pearson-baseline correlation定义以下:
\(\begin{align}\begin{aligned}\text{pearson_baseline_shrunk_sim}(u, v) = \frac{|I_{uv}| - 1} {|I_{uv}| - 1 + \text{shrinkage}} \cdot \omega_{uv}\\\text{pearson_baseline_shrunk_sim}(i, j)= \frac{|U_{ij}| - 1} {|U_{ij}| - 1 + \text{shrinkage}} \cdot \omega_{ij}\end{aligned}\end{align}\)
这也是surprise中pearson_baseline()
的计算方法。👌
下面使用surprise库对上面介绍的几种类似度度量进行比较:
import pandas as pd import numpy as np from surprise.prediction_algorithms.knns import KNNBasic from surprise import Dataset, Reader from surprise.model_selection import train_test_split
一、读取数据,预处理
为了方便,这里只使用ml-latest_small的movielens数据集进行操做
reader = Reader(rating_scale=(1, 5), line_format='user item rating timestamp') df_data = pd.read_csv('./data/ml-latest-small/ratings.csv', usecols=['userId','movieId','rating']) data = Dataset.load_from_df(df_data, reader) trainset, testset = train_test_split(data, test_size=0.2)
二、创建模型
创建KNN基于邻域的模型,其预测函数为(0)式的一个优化,即:
\(\hat{r}_{ui} = \frac{\sum\limits_{j \in N^k_u(i)} \text{sim}(i, j) \cdot r_{uj}}{\sum\limits_{j \in N^k_u(j)} \text{sim}(i, j)}\tag{11}\)
咱们分别使用cosine, msd, pearson以及pearson-baseline做为类似度度量进行比较,分别获得其precision和recall(这里使用Top5做为metric)
PS:precision_recall_at_k()函数见这里
sim = ['cosine', 'msd', 'pearson','pearson_baseline'] for s in sim: params = {'name': s, 'user_based': False} knn = KNNBasic(k=40, min_k=1, sim_options=params) knn.fit(trainset) predictions = knn.test(testset) precisions, recalls = precision_recall_at_k(predictions, k=5, threshold=3.5) print('Precision:', sum(prec for prec in precisions.values()) / len(precisions)) print('Recall:', sum(rec for rec in recalls.values()) / len(recalls)) print('')
Precision | Recall | |
---|---|---|
cosine | 0.765 | 0.343 |
msd | 0.807 | 0.367 |
pearson | 0.729 | 0.346 |
pearson-base | 0.776 | 0.391 |
因为数据量很小,上述的评测指数仅做参考
最近天气有点热,三伏天得了空调病,最后发现是颈椎引发的问题,期间还拔了颗顽固的智齿,也算是一波三折了。
Reference: