入门推荐系统——协同过滤

原始发表时间:2020-10-22
转载请注明出处。https://blog.csdn.net/qq_45048482/article/details/109186391
如有侵权,请联系 xiaomin186@foxmail.com 删除。
欢迎各位指正、交流!

1.协同过滤算法

基本思想:根据用户之前的喜好以及其他兴趣相近的用户的选择来给用户推荐物品(基于对用户历史行为数据的挖掘发现用户的喜好偏向, 并预测用户可能喜好的产品进行推荐),一般是仅仅基于用户的行为数据(评价、购买、下载等), 而不依赖于项的任何附加信息(物品自身特征)或者用户的任何附加信息(年龄, 性别等)。
应用比较广泛的协同过滤算法是基于邻域的方法, 而这种方法主要有下面两种算法:

  • 基于用户的协同过滤算法(UserCF): 给用户推荐和他兴趣相似的其他用户喜欢的产品
  • 基于物品的协同过滤算法(ItemCF): 给用户推荐和他之前喜欢的物品相似的物品

不管是UserCF还是ItemCF算法, 非常重要的步骤之一就是计算用户和用户或者物品和物品之间的相似度

2. 相似性度量方法

1. 杰卡德(Jaccard)相似系数

这个是衡量两个集合的相似度一种指标。
两个用户 u u u v v v交互商品交集的数量占这两个用户交互商品并集的数量的比例,称为两个集合的杰卡德相似系数,用符号 s i m u v sim_{uv} simuv表示,其中 N ( u ) , N ( v ) N(u),N(v) N(u),N(v)分别表示用户 u u u和用户 v v v交互商品的集合。 sim ⁡ u v = ∣ N ( u ) ∩ N ( v ) ∣ ∣ N ( u ) ∪ N ( v ) ∣ \operatorname{sim}_{u v}=\frac{|N(u) \cap N(v)|}{|N(u) \cup N(v)|} simuv=N(u)N(v)N(u)N(v)由于杰卡德相似系数一般无法反映具体用户的评分喜好信息, 所以常用来评估用户是否会对某商品进行打分, 而不是预估用户会对某商品打多少分。

2. 余弦相似度

衡量了两个向量的夹角,夹角越小越相似。
分母有差异是两个用户分别交互的商品数量的乘积,公式如下: s i m u v = ∣ N ( u ) ∣ ∩ ∣ N ( v ) ∣ ∣ N ( u ) ∣ ⋅ ∣ N ( v ) ∣ sim_{uv}=\frac{|N(u)| \cap |N(v)|}{\sqrt{|N(u)|\cdot|N(v)|}} simuv=N(u)N(v) N(u)N(v) 从向量的角度进行描述,令矩阵 A A A为用户-商品交互矩阵(因为是TopN推荐并不需要用户对物品的评分,只需要知道用户对商品是否有交互就行),即矩阵的每一行表示一个用户对所有商品的交互情况,有交互的商品值为1没有交互的商品值为0,矩阵的列表示所有商品。若用户和商品数量分别为 m , n m,n m,n的话,交互矩阵 A A A就是一个 m m m n n n列的矩阵。此时用户的相似度可以表示为(其中 u ⋅ v u\cdot v uv指的是向量点积): s i m u v = c o s ( u , v ) = u ⋅ v ∣ u ∣ ⋅ ∣ v ∣ sim_{uv} = cos(u,v) =\frac{u\cdot v}{|u|\cdot |v|} simuv=cos(u,v)=uvuv 上述用户-商品交互矩阵在现实情况下是非常的稀疏了,为了避免存储这么大的稀疏矩阵,在计算用户相似度的时候一般会采用集合的方式进行计算。理论上向量之间的相似度计算公式都可以用来计算用户之间的相似度,但是会根据实际的情况选择不同的用户相似度度量方法。

	这个在具体实现的时候, 可以使用cosine_similarity进行实现:
from sklearn.metrics.pairwise import cosine_similarity
i = [1, 0, 0, 0]
j = [1, 0.5, 0.5, 0]
consine_similarity([i, j])

3. 皮尔逊相关系数

它的公式与余弦相似度的计算公式非常的类似,首先对于上述的余弦相似度的计算公式写成求和的形式,其中 r u i , r v i r_{ui},r_{vi} rui,rvi分别表示用户 u u u和用户 v v v对商品 i i i是否有交互(或者具体的评分值): s i m u v = ∑ i r u i ∗ r v i ∑ i r u i 2 ∑ i r v i 2 sim_{uv} = \frac{\sum_i r_{ui}*r_{vi}}{\sqrt{\sum_i r_{ui}^2}\sqrt{\sum_i r_{vi}^2}} simuv=irui2 irvi2 iruirvi 如下是皮尔逊相关系数计算公式,其中 r u i , r v i r_{ui},r_{vi} rui,rvi分别表示用户 u u u和用户 v v v对商品 i i i是否有交互(或者具体的评分值), r ˉ u , r ˉ v \bar r_u, \bar r_v rˉu,rˉv分别表示用户 u u u和用户 v v v交互的所有商品交互数量或者具体评分的平均值。 s i m ( u , v ) = ∑ i ∈ I ( r u i − r ˉ u ) ( r v i − r ˉ v ) ∑ i ∈ I ( r u i − r ˉ u ) 2 ∑ i ∈ I ( r v i − r ˉ v ) 2 sim(u,v)=\frac{\sum_{i\in I}(r_{ui}-\bar r_u)(r_{vi}-\bar r_v)}{\sqrt{\sum_{i\in I }(r_{ui}-\bar r_u)^2}\sqrt{\sum_{i\in I }(r_{vi}-\bar r_v)^2}} sim(u,v)=iI(ruirˉu)2 iI(rvirˉv)2 iI(ruirˉu)(rvirˉv) 所以相比余弦相似度,皮尔逊相关系数通过使用用户的平均分对各独立评分进行修正,减小了用户评分偏置的影响。具体实现, 我们也是可以调包, 这个计算方式很多, 下面是其中的一种:

from scipy.stats import pearsonr

i = [1, 0, 0, 0]
j = [1, 0.5, 0.5, 0]
pearsonr(i, j)

3.基于用户的协同过滤

思想:当一个用户A需要个性化推荐的时候, 我们可以先找到和他有相似兴趣的其他用户, 然后把那些用户喜欢的, 而用户A没有听说过的物品推荐给A。

UserCFO

UserCF算法主要包括两个步骤:

  1. 找到和目标用户兴趣相似的集合
  2. 找到这个集合中的用户喜欢的, 且目标用户没有听说过的物品推荐给目标用户。

第一个步骤里面,基于前面给出的相似性度量的方法找出与目标用户兴趣相似的用户;而第二个步骤里面, 如何基于相似用户喜欢的物品来对目标用户进行推荐呢? 这个要依赖于目标用户对相似用户喜欢的物品的一个喜好程度, 那么如何衡量这个程度大小呢? 为了更好理解上面的两个步骤, 下面拿一个具体的例子把两个步骤具体化。

示例1

给用户推荐物品的过程可以形象化为一个猜测用户对商品进行打分的任务,上面表格里面是5个用户对于5件物品的一个打分情况,就可以理解为用户对物品的喜欢程度。

应用步骤:

  1. 根据前面的这些打分情况(或者说已有的用户向量)计算一下Alice和用户1, 2, 3, 4的相似程度, 找出与Alice最相似的n个用户
  2. 根据这n个用户对物品5的评分情况和与Alice的相似程度会猜测出Alice对物品5的评分, 如果评分比较高的话, 就把物品5推荐给用户Alice, 否则不推荐。

关于第一个步骤, 可使用上面已经的计算两个用户相似性的方法。 这里主要解决第二个问题, 如何产生最终结果的预测

最终结果预测
计算出向量之间的相似程度, 即可以计算出Alice和其他用户的相近程度。 这时候我们就可以选出与Alice最相近的前n个用户, 基于他们对物品5的评价猜测出Alice的打分值, 那么是怎么计算的呢?

常用的方式之一是利用用户相似度和相似用户的评价加权平均获得用户的评价预测, 用下面式子表示:
R u , p = ∑ s ∈ S ( w u , s ⋅ R s , p ) ∑ s ∈ S w u , s R_{\mathrm{u}, \mathrm{p}}=\frac{\sum_{\mathrm{s} \in S}\left(w_{\mathrm{u}, \mathrm{s}} \cdot R_{\mathrm{s}, \mathrm{p}}\right)}{\sum_{\mathrm{s} \in S} w_{\mathrm{u}, \mathrm{s}}} Ru,p=sSwu,ssS(wu,sRs,p) 这个式子里面, 权重 w u , s w_{u,s} wu,s是用户 u u u和用户 s s s的相似度, R s , p R_{s,p} Rs,p是用户 s s s对物品 p p p的评分。

还有一种方式如下, 这种方式考虑的更加全面, 依然是用户相似度作为权值,是相似用户对该物品的评分与此用户的所有评分的差值进行加权平均, 这时候考虑到了有的用户内心的评分标准不一的情况, 即有的用户喜欢打高分, 有的用户喜欢打低分的情况。公式如下:
P i , j = R ˉ i + ∑ k = 1 n ( S i , k ( R k , j − R ˉ k ) ) ∑ k = 1 n S j , k P_{i, j}=\bar{R}{i}+\frac{\sum_{k=1}^{n}\left(S_{i, k}\left(R_{k, j}-\bar{R}_{k}\right)\right)}{\sum_{k=1}^{n} S_{j, k}} Pi,j=Rˉi+k=1nSj,kk=1n(Si,k(Rk,jRˉk))

P i , j P_{i, j} Pi,j表示的是用户 i i i对物品 j j j 的评分, R i ˉ \bar{R_{i}} Riˉ表示的是用户 i i i的所有评分的平均值, n n n表示的是与用户 i i i相似的 n n n 个用户, S i , k S_{i,k} Si,k表示的是用户 i i i和用户 k k k的相似度, R k , j R_{k,j} Rk,j表示的是用户 k k k对物品 j j j的评分, R k ˉ \bar{R_{k}} Rkˉ表示的是用户 k k k的所有评分的平均值。
这一种计算方式更为推荐。下面的计算将使用这个方式。
在获得用户 u u u对不同物品的评价预测后, 最终的推荐列表根据预测评分进行排序得到。 至此,基于用户的协同过滤算法的推荐过程完成。

动手计算

结合前面的表格

  1. 计算Alice与其他用户的相似度(这里使用皮尔逊相关系数)
    计算1
    用皮尔逊相关系数, 也就是Alice与用户1的相似度是0.85。同样的方式, 我们就可以计算与其他用户的相似度, 这里可以使用numpy的相似度函数得到用户的相似性矩阵:
from sklearn.metrics.pairwise import cosine_similarity
users=np.array([[5,3,4,4], [3,1,2,3], [4,3,4,3], [3,3,1,5], [1,5,5,2]])
cosine_similarity(users)  # 余弦相似度

array([[1. , 0.9753213 , 0.99224264, 0.89072354, 0.79668736],
[0.9753213 , 1. , 0.94362852, 0.91160719, 0.67478587],
[0.99224264, 0.94362852, 1. , 0.85280287, 0.85811633],
[0.89072354, 0.91160719, 0.85280287, 1. , 0.67082039],
[0.79668736, 0.67478587, 0.85811633, 0.67082039, 1. ]])

np.corrcoef(users)  # 皮尔逊相关系数

array([[ 1. , 0.85280287, 0.70710678, 0. , -0.79211803],
[ 0.85280287, 1. , 0.30151134, 0.42640143, -0.88662069],
[ 0.70710678, 0.30151134, 1. , -0.70710678, -0.14002801],
[ 0. , 0.42640143, -0.70710678, 1. , -0.59408853],
[-0.79211803, -0.88662069, -0.14002801, -0.59408853, 1. ]])

从皮尔逊相关系数的计算结果可以看出,Alice和用户1-4的相似度分别为:0.85,0.7, 0, -0.79。所以和Alice最接近的两个用户是1和2。

  1. 根据相似度用户计算Alice对物品5的最终得分
    用户1对物品5的评分是3, 用户2对物品5的打分是5, 那么根据上面的计算公式, 可以计算出Alice对物品5的最终得分是 P A l i c e , 物 品 5 = R ˉ A l i c e + ∑ k = 1 2 ( S A l i c e , u s e r k ( R u s e r k , 物 品 5 − R ˉ u s e r k ) ) ∑ k = 1 2 S A l i c e , u s e r k = 4 + 0.85 ∗ ( 3 − 2.4 ) + 0.7 ∗ ( 5 − 3.8 ) 0.85 + 0.7 = 4.87 P_{Alice, 物品5}=\bar{R}_{Alice}+\frac{\sum_{k=1}^{2}\left(S_{Alice,user k}\left(R_{userk, 物品5}-\bar{R}_{userk}\right)\right)}{\sum_{k=1}^{2} S_{Alice, userk}}=4+\frac{0.85*(3-2.4)+0.7*(5-3.8)}{0.85+0.7}=4.87 PAlice,5=RˉAlice+k=12SAlice,userkk=12(SAlice,userk(Ruserk,5Rˉuserk))=4+0.85+0.70.85(32.4)+0.7(53.8)=4.87

  2. 根据用户评分对用户进行推荐 这时候, 我们就得到了Alice对物品5的得分是4.87, 根据Alice的打分对物品排个序从大到小: 物 品 1 > 物 品 5 > 物 品 3 = 物 品 4 > 物 品 2 物品1>物品5>物品3=物品4>物品2 1>5>3=4>2 这时候,如果要向Alice推荐2款产品的话, 我们就可以推荐物品1和物品5给Alice

4.UserCF编程实现

基本步骤:计算用户相似性矩阵、得到前n个相似用户、计算最终得分。
程序:

  1. 先把数据表给建立起来
    用两个字典, 第一个字典是物品-用户的评分映射, 键是物品1-5, 用A-E来表示, 每一个值又是一个字典, 表示的是每个用户对该物品的打分。 第二个字典是用户-物品的评分映射, 键是上面的五个用户, 用1-5表示, 值是该用户对每个物品的打分。
    用字典 而没有用pandas, 是因为上面举得这个例子其实是个个例, 在真实情况中, 我们知道, 用户对物品的打分情况并不会这么完整, 会存在大量的空值, 所以矩阵会很稀疏, 这时候用DataFrame, 会有大量的NaN。故这里用字典的形式存储。
# 定义数据集, 也就是那个表格, 注意这里我们采用字典存放数据, 因为实际情况中数据是非常稀疏的, 很少有情况是现在这样
def loadData():
    items={'A': {1: 5, 2: 3, 3: 4, 4: 3, 5: 1},
           'B': {1: 3, 2: 1, 3: 3, 4: 3, 5: 5},
           'C': {1: 4, 2: 2, 3: 4, 4: 1, 5: 5},
           'D': {1: 4, 2: 3, 3: 3, 4: 5, 5: 2},
           'E': {2: 3, 3: 5, 4: 4, 5: 1}
          }
    users={1: {'A': 5, 'B': 3, 'C': 4, 'D': 4},
           2: {'A': 3, 'B': 1, 'C': 2, 'D': 3, 'E': 3},
           3: {'A': 4, 'B': 3, 'C': 4, 'D': 3, 'E': 5},
           4: {'A': 3, 'B': 3, 'C': 1, 'D': 5, 'E': 4},
           5: {'A': 1, 'B': 5, 'C': 5, 'D': 2, 'E': 1}
          }
    return items,users

items, users = loadData()
item_df = pd.DataFrame(items).T
user_df = pd.DataFrame(users).T
  1. 计算用户相似性矩阵
    这个是一个共现矩阵, 5*5,行代表每个用户, 列代表每个用户, 值代表用户和用户的相关性,这里的思路是这样, 因为要求用户和用户两两的相关性, 所以需要用双层循环遍历用户-物品评分数据, 当不是同一个用户的时候, 我们要去遍历物品-用户评分数据, 在里面去找这两个用户同时对该物品评过分的数据放入到这两个用户向量中。 因为正常情况下会存在很多的NAN, 即可能用户并没有对某个物品进行评分过, 这样的不能当做用户向量的一部分, 没法计算相似性。 还是看代码吧, 感觉不太好描述:
"""计算用户相似性矩阵"""
"""计算用户相似性矩阵"""
similarity_matrix = pd.DataFrame(np.zeros((len(users), len(users))), index=[1, 2, 3, 4, 5], columns=[1, 2, 3, 4, 5])

# 遍历每条用户-物品评分数据
for userID in users:
    for otheruserId in users:
        vec_user = []
        vec_otheruser = []
        if userID != otheruserId:
            for itemId in items:   # 遍历物品-用户评分数据,找出两个用户都评分了的物品
                itemRatings = items[itemId]        # 这也是个字典  每条数据为所有用户对当前物品的评分
                if userID in itemRatings and otheruserId in itemRatings:  # 说明两个用户都对该物品评过分
                    vec_user.append(itemRatings[userID])
                    vec_otheruser.append(itemRatings[otheruserId])
            # 这里可以获得相似性矩阵(共现矩阵)
            # 对两个用户都评过分的物品,计算协方差矩阵
            similarity_matrix[userID][otheruserId] = np.corrcoef(np.array(vec_user), np.array(vec_otheruser))[0][1]  # 返回Pearson乘积矩相关系数
            # corrcoef()[0][1]返回covariance matrix协方差矩阵的Cov(X,Y)
            
            #similarity_matrix[userID][otheruserId] = cosine_similarity(np.array(vec_user), np.array(vec_otheruser))[0][1]

这里的similarity_matrix就是我们的用户相似性矩阵, 长下面这样:

similarity_matrix就是我们的用户相似性矩阵:
用户相似矩阵
有了相似性矩阵, 我们就可以得到与Alice最相关的前n个用户。

  1. 计算前n个相似的用户
"""计算前n个相似的用户""" # 矩阵下标从1开始
n = 2 # 只要相似的前2个用户
similarity_users = similarity_matrix[1].sort_values(ascending=False)[:n].index.tolist()      
#print(similarity_users)  # [2, 3],也就是用户1和用户2 
  1. 计算最终得分
"""计算最终得分"""
base_score = np.mean(np.array([value for value in users[1].values()]))
weighted_scores = 0.
corr_values_sum = 0.
for user in similarity_users:  # [2, 3]
    corr_value = similarity_matrix[1][user]            # 两个用户之间的相似性
    mean_user_score = np.mean(np.array([value for value in users[user].values()]))    # 每个用户的打分平均值
    weighted_scores += corr_value * (users[user]['E']-mean_user_score)      # 加权分数
    corr_values_sum += corr_value
final_scores = base_score + weighted_scores / corr_values_sum
print('用户Alice对物品5的打分: ', final_scores)
user_df.loc[1]['E'] = final_scores
user_df

结果:
UserCF的计算结果

这其实就是微型版的UserCF的工作过程了。

5.UserCF优缺点

两个重大问题:

  1. 数据稀疏性。
    一个大型的电子商务推荐系统一般有非常多的物品,用户可能买的其中不到1%的物品,不同用户之间买的物品重叠性较低,导致算法无法找到一个用户的邻居,即偏好相似的用户。这导致UserCF不适用于那些正反馈获取较困难的应用场景(如酒店预订, 大件商品购买等低频应用)
  2. 算法扩展性。
  3. 基于用户的协同过滤需要维护用户相似度矩阵以便快速的找出TopN相似用户, 该矩阵的存储开销非常大,存储空间随着用户数量的增加而增加,不适合用户数据量大的情况使用。

所以电商平台多采用了ItemCF算法实现最初的推荐系统。

6.基于物品的协同过滤

基本思想:预先根据所有用户的历史偏好数据计算物品之间的相似性,然后把与用户喜欢的物品相类似的物品推荐给用户。
比如物品a和c非常相似,因为喜欢a的用户同时也喜欢c,而用户A喜欢a,所以把c推荐给用户A。
ItemCF算法 主要通过分析用户的行为记录计算物品之间的相似度, 该算法认为, 物品a和物品c具有很大的相似度是因为喜欢物品a的用户大都喜欢物品c。
ItemCFO
ItemCF步骤:

  1. 计算物品之间的相似度
  2. 根据物品的相似度和用户的历史行为给用户生成推荐列表(购买了该商品的用户也经常购买的其他商品)
    示例1
    知道Alice对物品5打多少分, 基于物品的协同过滤算法会这么做:
  3. 首先计算一下物品5和物品1, 2, 3, 4之间的相似性(它们也是向量的形式, 每一列的值就是它们的向量表示, 因为ItemCF认为物品a和物品c具有很大的相似度是因为喜欢物品a的用户大都喜欢物品c, 所以就可以基于每个用户对该物品的打分或者说喜欢程度来向量化物品)
  4. 找出与物品5最相近的n个物品
  5. 根据Alice对最相近的n个物品的打分去计算对物品5的打分情况

动手计算
计算2

import pandas as pd
import numpy as np
import warnings
import random, math, os
from tqdm import tqdm
from sklearn.model_selection import train_test_split
warnings.filterwarnings('ignore')

from sklearn.metrics.pairwise import cosine_similarity
items = np.array([[3,4,3,1], [1, 3, 3, 5], [2, 4, 1, 5], [3,3,5,2],[3,5,4,1]])
cols=['item'+str(i) for i in range(1,6)]
pd.DataFrame(np.corrcoef(items), columns=cols, index=cols) # 皮尔逊相关系数

ItemCF的皮尔逊相关系数

pd.DataFrame(cosine_similarity(items), columns=cols, index=cols)  # 余弦相似性

ItemCF的余弦相似度
从计算结果可以看到,除了自己以外,和item5最相似的物品是1和4。
下面基于上面的公式计算最终得分:
P A l i c e , 物 品 5 = R ˉ 物 品 5 + ∑ k = 1 2 ( S 物 品 5 , 物 品 k ( R A l i c e , 物 品 k − R ˉ 物 品 k ) ) ∑ k = 1 2 S 物 品 k , 物 品 5 = 13 4 + 0.97 ∗ ( 5 − 3.2 ) + 0.58 ∗ ( 4 − 3.4 ) 0.97 + 0.58 = 4.6 P_{Alice, 物品5}=\bar{R}{物品5}+\frac{\sum{k=1}^{2}\left(S_{物品5,物品 k}\left(R_{Alice, 物品k}-\bar{R}{物品k}\right)\right)}{\sum{k=1}^{2} S_{物品k, 物品5}}=\frac{13}{4}+\frac{0.97*(5-3.2)+0.58*(4-3.4)}{0.97+0.58}=4.6 PAlice,5=Rˉ5+k=12Sk,5k=12(S5,k(RAlice,kRˉk))=413+0.97+0.580.97(53.2)+0.58(43.4)=4.6
这时候依然可以向Alice推荐物品5。

编程实现ItemCF

和UserCF差不多:

import pandas as pd
import numpy as np
import warnings
import random, math, os
from tqdm import tqdm
from sklearn.model_selection import train_test_split
warnings.filterwarnings('ignore')
# 定义数据集, 也就是那个表格, 注意这里我们采用字典存放数据, 因为实际情况中数据是非常稀疏的, 很少有情况是现在这样
def loadData():
    items={'A': {1: 5, 2: 3, 3: 4, 4: 3, 5: 1},
           'B': {1: 3, 2: 1, 3: 3, 4: 3, 5: 5},
           'C': {1: 4, 2: 2, 3: 4, 4: 1, 5: 5},
           'D': {1: 4, 2: 3, 3: 3, 4: 5, 5: 2},
           'E': {2: 3, 3: 5, 4: 4, 5: 1}
          }
    users={1: {'A': 5, 'B': 3, 'C': 4, 'D': 4},
           2: {'A': 3, 'B': 1, 'C': 2, 'D': 3, 'E': 3},
           3: {'A': 4, 'B': 3, 'C': 4, 'D': 3, 'E': 5},
           4: {'A': 3, 'B': 3, 'C': 1, 'D': 5, 'E': 4},
           5: {'A': 1, 'B': 5, 'C': 5, 'D': 2, 'E': 1}
          }
    return items,users

items, users = loadData()
item_df = pd.DataFrame(items).T
user_df = pd.DataFrame(users).T
"""计算物品的相似矩阵"""
similarity_matrix = pd.DataFrame(np.ones((len(items), len(items))), index=['A', 'B', 'C', 'D', 'E'], columns=['A', 'B', 'C', 'D', 'E'])

# 遍历每条物品-用户评分数据
for itemId in items:
    for otheritemId in items:
        vec_item = []         # 定义列表, 保存当前两个物品的向量值
        vec_otheritem = []
        #userRagingPairCount = 0     # 两件物品均评过分的用户数
        if itemId != otheritemId:    # 物品不同
            for userId in users:    # 遍历用户-物品评分数据
                userRatings = users[userId]    # 每条数据为该用户对所有物品的评分, 这也是个字典
                
                if itemId in userRatings and otheritemId in userRatings:   # 用户对这两个物品都评过分
                    #userRagingPairCount += 1
                    vec_item.append(userRatings[itemId])
                    vec_otheritem.append(userRatings[otheritemId])
            
            # 这里可以获得相似性矩阵(共现矩阵)
            similarity_matrix[itemId][otheritemId] = np.corrcoef(np.array(vec_item), np.array(vec_otheritem))[0][1]
            #similarity_matrix[itemId][otheritemId] = cosine_similarity(np.array(vec_item), np.array(vec_otheritem))[0][1]

物品相似度矩阵:
ItemCF相似矩阵
得到与物品5相似的前n个物品, 计算出最终得分来。

""""将自己和自己的相关性系数设置成0"""
r=len(similarity_matrix)
for i in range(0,r):
    similarity_matrix.iloc[i][i]=0
    
"""得到与物品5相似的前n个物品"""
n = 2
similarity_items = similarity_matrix['E'].sort_values(ascending=False)[:n].index.tolist()       
similarity_items  # ['A', 'D']

"""计算最终得分"""
base_score = np.mean(np.array([value for value in items['E'].values()]))
weighted_scores = 0.
corr_values_sum = 0.
for item in similarity_items:  # ['A', 'D']
    corr_value = similarity_matrix['E'][item]            # 两个物品之间的相似性
    mean_item_score = np.mean(np.array([value for value in items[item].values()]))    # 每个物品的打分平均值
    weighted_scores += corr_value * (users[1][item]-mean_item_score)      # 加权分数
    corr_values_sum += corr_value
final_scores = base_score + weighted_scores / corr_values_sum
print('用户Alice对物品5的打分: ', final_scores)
user_df.loc[1]['E'] = final_scores
user_df

结果:

ItemCF计算结果

7.算法评估

评测指标:

  1. 召回率
    对用户u推荐N个物品记为 R ( u ) R(u) R(u), 令用户u在测试集上喜欢的物品集合为 T ( u ) T(u) T(u), 那么召回率定义为: Recall ⁡ = ∑ u ∣ R ( u ) ∩ T ( u ) ∣ ∑ u ∣ T ( u ) ∣ \operatorname{Recall}=\frac{\sum_{u}|R(u) \cap T(u)|}{\sum_{u}|T(u)|} Recall=uT(u)uR(u)T(u) 这个意思就是说, 在用户真实购买或者看过的影片里面, 我模型真正预测出了多少, 这个考察的是模型推荐的一个全面性。
  2. 准确率
    定义为: Precision ⁡ = ∑ u ∣ R ( u ) ∩ T ( u ) ∣ ∑ u ∣ R ( u ) ∣ \operatorname{Precision}=\frac{\sum_{u} \mid R(u) \cap T(u)|}{\sum_{u}|R(u)|} Precision=uR(u)uR(u)T(u) 这个意思再说, 在我推荐的所有物品中, 用户真正看的有多少, 这个考察的是我模型推荐的一个准确性。 为了提高准确率, 模型需要把非常有把握的才对用户进行推荐, 所以这时候就减少了推荐的数量, 而这往往就损失了全面性, 真正预测出来的会非常少,所以实际应用中应该综合考虑两者的平衡。
  3. 覆盖率
    反映了推荐算法发掘长尾的能力, 覆盖率越高, 说明推荐算法越能将长尾中的物品推荐给用户。  Coverage  = ∣ ⋃ u ∈ U R ( u ) ∣ ∣ I ∣ \text { Coverage }=\frac{\left|\bigcup_{u \in U} R(u)\right|}{|I|}  Coverage =IuUR(u)
    该覆盖率表示最终的推荐列表中包含多大比例的物品。如果所有物品都被给推荐给至少一个用户, 那么覆盖率是100%。
  4. 新颖度
    用推荐列表中物品的平均流行度度量推荐结果的新颖度。 如果推荐出的物品都很热门, 说明推荐的新颖度较低。 由于物品的流行度分布呈长尾分布, 所以为了流行度的平均值更加稳定, 在计算平均流行度时对每个物品的流行度取对数。

8.协同过滤算法权重改进

协同过滤算法的权值改进

  • 基础算法
    图1为最简单的计算物品相关度的公式, 分子为同时喜好item-i和item-j的用户数
  • 对热门物品的惩罚
    图1存在一个问题, 如果 item-j 是很热门的商品,导致很多喜欢 item-i 的用户都喜欢 item-j,这时 w i j w_{ij} wij 就会非常大。同样,几乎所有的物品都和 item-j 的相关度非常高,这显然是不合理的。所以图2中分母通过引入 N ( j ) N(j) N(j) 来对 item-j 的热度进行惩罚。如果物品很热门, 那么 N ( j ) N(j) N(j)就会越大, 对应的权重就会变小。
  • 对热门物品的进一步惩罚
    如果 item-j 极度热门,上面的算法还是不够的。举个例子,《Harry Potter》非常火,买任何一本书的人都会购买它,即使通过图2的方法对它进行了惩罚,但是《Harry Potter》仍然会获得很高的相似度。这就是推荐系统领域著名的 Harry Potter Problem。
    如果需要进一步对热门物品惩罚,可以继续修改公式为如图3所示,通过调节参数 α α α α α α越大,惩罚力度越大,热门物品的相似度越低,整体结果的平均热门程度越低。
  • 对活跃用户的惩罚
    Item-based CF 也需要考虑活跃用户(即一个活跃用户(专门做刷单)可能买了非常多的物品)的影响,活跃用户对物品相似度的贡献应该小于不活跃用户。图4为集合了该权重的算法。

最后一个改进没懂

9.协同过滤算法的问题分析

泛化能力弱, 即协同过滤无法将两个物品相似的信息推广到其他物品的相似性上。 导致的问题是热门物品具有很强的头部效应, 容易跟大量物品产生相似, 而尾部物品由于特征向量稀疏, 导致很少被推荐。
为了解决这个问题, 同时增加模型的泛化能力,2006年,矩阵分解技术(Matrix Factorization,MF)被提出, 该方法在协同过滤共现矩阵的基础上, 使用更稠密的隐向量表示用户和物品, 挖掘用户和物品的隐含兴趣和隐含特征, 在一定程度上弥补协同过滤模型处理稀疏矩阵能力不足的问题。

10.思考

  1. 什么时候使用UserCF,什么时候使用ItemCF?为什么?
    答:UserCF 由于是基于用户相似度进行推荐, 所以具备更强的社交特性,适于用户少, 物品多, 时效性较强,看人与人的相似性的场合, 比如新闻推荐场景。
    ItemCF这个更适用于兴趣变化较为稳定的应用, 更接近于个性化的推荐, 适合物品少,用户多,用户兴趣固定持久, 物品更新速度不是太快的场合, 比如推荐艺术品, 音乐, 电影。

  2. 协同过滤在计算上有什么缺点?有什么比较好的思路可以解决或缓解?
    答:较差的稀疏向量处理能力,即泛化能力弱。热门物品具有很强的头部效应, 容易跟大量物品产生相似, 而尾部物品由于特征向量稀疏, 导致很少被推荐。
    改进:矩阵分解技术

  3. 上面介绍的相似度计算方法有什么优劣之处?
    答:余弦相似度不能避免个人打分习惯,存在用户评分偏置,考虑用皮尔逊相关系数。

  4. 协同过滤还存在其他什么缺陷?有什么比较好的思路可以解决(缓解)?
    答:它完全没有利用到物品本身或者是用户自身的属性, 仅仅利用了用户与物品的交互信息就可以实现推荐。造成了有效信息的遗漏,不能充分利用其它特征数据。
    推荐系统慢慢的从以协同过滤为核心到了以逻辑回归模型为核心, 提出了能够综合不同类型特征的机器学习模型。

11.参考

欢迎各位指正、交流!
原始发表时间:2020-10-22
转载请注明出处。https://blog.csdn.net/qq_45048482/article/details/109186391
如有侵权,请联系 xiaomin186@foxmail.com 删除。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值