推荐系统组队学习(一)、协同过滤算法

协同过滤算法

协同过滤(Collaborative Filtering)推荐算法是最经典、最常用的推荐算法。其基本思想是根据用户之前的喜好以及其他兴趣相近的用户的选择来给用户推荐物品(基于对用户历史行为数据的挖掘发现用户的喜好偏向, 并预测用户可能喜好的产品进行推荐)

目前应用比较广泛的协同过滤算法是基于邻域的方法, 而这种方法主要有下面两种算法:

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

相似性度量方法

  1. 杰卡德(Jaccard)相似系数
    杰卡德(Jaccard)相似系数是衡量两个集合的相似度一种指标。 s i m u v = ∣ N ( u ) ∣ ∩ ∣ N ( v ) ∣ ∣ N ( u ) ∣ ∪ N ( v ) sim_{uv} = \frac{|N(u)| \cap |N(v)|}{\sqrt{|N(u)| \cup{N(v)}}} simuv=N(u)N(v) N(u)N(v)杰卡德相似系数一般无法反映具体用户的评分喜好信息, 所以常用来评估用户是否会对某商品进行打分, 而不是预估用户会对某商品打多少分。
  2. 余弦相似度
    余弦相似度衡量了两个向量的夹角,夹角越小越相似。
    相比于Jaccard公式来说就是分母有差异,不是两个用户交互商品的并集的数量,而是两个用户分别交互的商品数量的乘积,公式如下:
    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)
    从向量的角度进行描述,令矩阵为用户-商品交互矩阵
    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
from sklearn.metrics.pairwise import cosine_similarity
i = [1,0,0,0]
j = [1,0.5,0.5,0]
sim_ij = cosine_similarity([i,j])
print(sim_ij)
		>>>
			[[1.         0.81649658]
 			 [0.81649658 1.        ]]
  1. 皮尔逊相关系数
    相比余弦相似度,皮尔逊相关系数通过使用用户的平均分对各独立评分进行修正,减小了用户评分偏置的影响。
    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]
sim_ij = pearsonr(i,j)
print(sim_ij)
		>>>
			(0.816496580927726, 0.18350341907227397)

基于用户的协同过滤

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

  1. 找到和目标用户兴趣相似的集合
  2. 找到这个集合中的用户喜欢的, 且目标用户没有听说过的物品推荐给目标用户。
-物品1物品2物品3物品4物品5
User19658??
User215424
User355875
User498798
User553354
步骤一

计算User1与User2的余弦相似性
s i m ( U s e r 1 , U s e r 2 ) = c o s ( U s e r 1 , U s e r 2 ) = 9 + 30 + 20 + 16 ( 81 + 36 + 25 + 64 ) ⋅ ( 1 + 25 + 16 + 4 ) = 0.7705 sim_{(User1,User2)} = cos(User1,User2) = \frac{9+30+20+16}{\sqrt{(81+36+25+64)} \cdot \sqrt{(1+25+16+4)}} = 0.7705 sim(User1,User2)=cos(User1,User2)=(81+36+25+64) (1+25+16+4) 9+30+20+16=0.7705
计算User1与User2的皮尔逊相关系数
User1_ave = 7, User2_ave = 3
向量减去均值:
User1(2,-1,-2,1), User2(-2,2,1,-1)
再求这两个新向量的余弦相似度:
s i m ( U s e r 1 , U s e r 2 ) = c o s ( U s e r 1 , U s e r 2 ) = − 4 − 2 − 2 − 1 ( 4 + 1 + 4 + 1 ) ⋅ ( 4 + 4 + 1 + 1 ) = − 0.9 sim_{(User1,User2)} = cos(User1,User2) = \frac{-4-2-2-1}{\sqrt{(4+1+4+1)} \cdot \sqrt{(4+4+1+1)}} = -0.9 sim(User1,User2)=cos(User1,User2)=(4+1+4+1) (4+4+1+1) 4221=0.9

这里我们使用皮尔逊相关系数, 也就是User1与User2的相似度是-0.9。同样的方式, 我们就可以计算与其他用户的相似度:

from scipy.stats import pearsonr
User1 = [9,6,5,8]
User2 = [1,5,4,2]
User3 = [5,5,8,7]
User4 = [9,8,7,9]
User5 = [5,3,3,5]
pear_12 = pearsonr(User1,User2)
pear_13 = pearsonr(User1,User3)
pear_14 = pearsonr(User1,User4)
pear_15 = pearsonr(User1,User5)
print('User1 与 User2的相似度为:',pear_12[0])
print('User1 与 User3的相似度为:',pear_13[0])
print('User1 与 User4的相似度为:',pear_14[0])
print('User1 与 User5的相似度为:',pear_15[0])

		>>>
			User1 与 User2的相似度为: -0.9
			User1 与 User3的相似度为: -0.48686449556014766
			User1 与 User4的相似度为: 0.9534625892455924
			User1 与 User5的相似度为: 0.9486832980505138

由此可知 User4 与 User5 同 User1 最为相似,分别为 0.9535,0.9487

步骤二

根据相似度用户计算User1对物品5的最终得分, 可以计算出User1对物品5的最终得分是:
P U s e r 1 , 物 品 5 = R ˉ U s e r 1 + ∑ k = 1 2 ( S U s e r 1 , u s e r k ( R u s e r k , 物 品 5 − R ˉ u s e r k ) ) ∑ k = 1 2 S U s e r 1 , u s e r k = 7 + 0.9535 ∗ ( 8 − 8.25 ) + 0.9487 ∗ ( 4 − 4 ) 0.9535 + 0.9487 = 6.87 P_{User1, 物品5}=\bar{R}{User1}+\frac{\sum{k=1}^{2}\left(S_{User1,user k}\left(R_{userk, 物品5}-\bar{R}{userk}\right)\right)}{\sum{k=1}^{2} S_{User1, userk}}=7+\frac{0.9535*(8-8.25)+0.9487*(4-4)}{0.9535+0.9487}= 6.87 PUser1,5=RˉUser1+k=12SUser1,userkk=12(SUser1,userk(Ruserk,5Rˉuserk))=7+0.9535+0.94870.9535(88.25)+0.9487(44)=6.87

步骤三

根据用户评分对用户进行推荐
我们就得到了 User1 对物品5的得分是4.87, 根据 User1 的打分对物品排个序从大到小:

  • 物品1 > 物品4 > 物品5 > 物品2 > 物品3

这时候,如果要向 User1 推荐2款产品的话, 我们就可以推荐物品1和物品4给 User1

UserCF编程实现

# 协同过滤算法
# UserCF编程实现
# 上面的过程其实就是三步:
#       1. 计算用户相似性矩阵、
#       2. 得到前n个相似用户、
#       3. 计算最终得分。
import numpy as np
import pandas as pd
# 余弦相似度
from sklearn.metrics.pairwise import cosine_similarity
# 皮尔逊相关系数
from scipy.stats import pearsonr

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()
print("items:",items)
print("users:",users)
item_df = pd.DataFrame(items).T
user_df = pd.DataFrame(users).T
print("item_df\n:",item_df)
print("user_df:\n",user_df)

# 计算相似性矩阵
# ***************************************************************
#     因为要求用户和用户两两的相关性, 所以需要用双层循环遍历用户-物品
# 评分数据, 当不是同一个用户的时候, 我们要去遍历物品-用户评分数
# 据, 在里面去找这两个用户同时对该物品评过分的数据放入到这两个用
# 户向量中。
# ***************************************************************
print(len(users))
similarity_matrix = pd.DataFrame(np.zeros((len(users), len(users))), index=[1, 2, 3, 4, 5], columns=[1, 2, 3, 4, 5])
print('similarity_matrix\n',similarity_matrix)

# 遍历每条用户-物品评分数据
for UserID in users:
    for othersUsers in users:
        vec_user = []
        vec_otheruser = []
        print(UserID,othersUsers)
        if UserID != othersUsers:
            for itemsId in items: # 遍历物品-用户评分数据
                itemRatings = items[itemsId]
                print(itemRatings)
                if UserID in itemRatings and othersUsers in itemRatings:# 说明两个用户都对该物品评过分
                    vec_user.append(itemRatings[UserID])
                    vec_otheruser.append((itemRatings[othersUsers]))
                # 这里可以获得相似性矩阵(共现矩阵)
            similarity_matrix[UserID][othersUsers] = np.corrcoef(np.array(vec_user), np.array(vec_otheruser))[0][1]
            # similarity_matrix[userID][otheruserId] = cosine_similarity(np.array(vec_user), np.array(vec_otheruser))[0][1]
            print('similarity_matrix',similarity_matrix)

# 计算前n个相似的用户
n = 2
print(similarity_matrix[1].sort_values(ascending=False))
similarity_user = similarity_matrix[1].sort_values(ascending=False)[:n].index.tolist()
print(similarity_user)

# 计算最终得分
base_score = np.mean(np.array([value for value in users[1].values()]))
print(base_score)
weight_scores = 0.
corr_value_sum = 0.
for user in similarity_user:
    corr_value = similarity_matrix[1][user] # 两个用户之间的相似性
    mean_user_score = np.mean(np.array([value for value in users[user].values()]))
    weight_scores += corr_value * (users[user]['E'] - mean_user_score)
    corr_value_sum += corr_value
final_scores = base_score + weight_scores / corr_value_sum
print('用户Aliced对物品5的打分:',final_scores)
user_df[1].E = final_scores
print(user_df)

		>>>
			similarity_matrix           1         2         3         4         5
1  0.000000  0.852803  0.707107  0.000000 -0.792118
2  0.852803  0.000000  0.467707  0.489956 -0.900149
3  0.707107  0.467707  0.000000 -0.161165 -0.466569
4  0.000000  0.489956 -0.161165  0.000000 -0.641503
5 -0.792118 -0.900149 -0.466569 -0.641503  0.000000

用户Aliced对物品5的打分: 4.871979899370592

基于物品的协同过滤

代码实现

import numpy as np
import pandas as pd
# 余弦相似度
from sklearn.metrics.pairwise import cosine_similarity
# 皮尔逊相关系数
from scipy.stats import pearsonr

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

# 三步法:
#       1.计算Alice与其他用户的相似度
#       2.根据相似度用户计算Alice对物品5的最终得分
#       3.根据用户评分对用户进行推荐
#
items,user = loadData()
# 计算物品的相似矩阵
similarity_matrix = pd.DataFrame(np.ones((len(items),len(items))),index=['A','B','C','D','E'], columns=['A', 'B', 'C', 'D', 'E'])
print('物品的相似矩阵:',similarity_matrix)
user_df = pd.DataFrame(user).T

# 遍历每条物品-用户评分数据
for itemId in items:
    for otheritemId in items:
        vec_item = []# 定义列表, 保存当前两个物品的向量值
        vec_otheritem = []
        if itemId != otheritemId:
            print(itemId,otheritemId)# 物品不同
            for userId in user: # 遍历用户-物品评分数据
                userRatings = user[userId]# 每条数据为该用户对所有物品的评分, 这也是个字典
                if itemId in userRatings and otheritemId in userRatings:
                    vec_item.append(userRatings[itemId])
                    vec_otheritem.append(userRatings[otheritemId])

            print('vec_item',vec_item)
            print('vec_otheritem',vec_otheritem)
            # 获得相似性矩阵
            # print(cosine_similarity(np.array(vec_item),np.array(vec_otheritem)))
            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]
            print(similarity_matrix[itemId][otheritemId])
print('物品的相似度矩阵为:',similarity_matrix)

# 得到物品5相似的前n个物品
n = 2
similarity_items = similarity_matrix['E'].sort_values(ascending=False)[1:n+1].index.tolist()
print('物品5相似的前n个物品:',similarity_items)

# 计算最终得分
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*(user[1][item] - mean_item_score)
    corr_values_sum += corr_value

print(user[1])
final_score = base_score + weighted_scores / corr_values_sum
print('用户1对物品5的打分为:',final_score)
user_df.loc[1]['E'] = final_score
print(user_df)

	>>>
		物品的相似度矩阵为:           A         B         C         D         E
A  1.000000 -0.476731 -0.123091  0.532181  0.969458
B -0.476731  1.000000  0.645497 -0.310087 -0.478091
C -0.123091  0.645497  1.000000 -0.720577 -0.427618
D  0.532181 -0.310087 -0.720577  1.000000  0.581675
E  0.969458 -0.478091 -0.427618  0.581675  1.000000
物品5相似的前n个物品: ['A', 'D']
用户1对物品5的打分为: 4.6
     A    B    C    D    E
1  5.0  3.0  4.0  4.0  4.6
2  3.0  1.0  2.0  3.0  3.0
3  4.0  3.0  4.0  3.0  5.0
4  3.0  3.0  1.0  5.0  4.0
5  1.0  5.0  5.0  2.0  1.0

算法评估

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

协同过滤算法的问题分析
协同过滤算法存在的问题之一就是泛化能力弱,即协同过滤无法将两个物品相似的信息推广到其他物品的相似性上导致的问题是:

  热门物品具有很强的头部效应, 容易跟大量物品产生相似, 而尾部物品由于特征向量稀疏, 导致很少被推荐。以这就是协同过滤的天然缺陷:推荐系统头部效应明显, 处理稀疏向量的能力弱。

为了解决这个问题, 同时增加模型的泛化能力,2006年,矩阵分解技术(Matrix Factorization,MF)被提出

  该方法在协同过滤共现矩阵的基础上, 使用更稠密的隐向量表示用户和物品, 挖掘用户和物品的隐含兴趣和隐含特征, 在一定程度上弥补协同过滤模型处理稀疏矩阵能力不足的问题。

1.什么时候使用UserCF,什么时候使用ItemCF?为什么?

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

2.协同过滤在计算上有什么缺点?有什么比较好的思路可以解决(缓解)?

 较差的稀疏向量处理能力

3.上面介绍的相似度计算方法有什么优劣之处?
cosine相似度还是比较常用的, 一般效果也不会太差, 但是对于评分数据不规范的时候,也就是说, 存在有的用户喜欢打高分, 有的用户喜欢打低分情况的时候,有的用户喜欢乱打分的情况, 这时候consine相似度算出来的结果可能就不是那么准确了
对于这种用户评分偏置的情况, 余弦相似度就不是那么好了, 可以考虑使用下面的皮尔逊相关系数。
4.协同过滤还存在其他什么缺陷?有什么比较好的思路可以解决(缓解)?
协同过滤的特点就是完全没有利用到物品本身或者是用户自身的属性, 仅仅利用了用户与物品的交互信息就可以实现推荐,比较简单高效, 但这也是它的一个短板所在, 由于无法有效的引入用户年龄, 性别,商品描述,商品分类,当前时间,地点等一系列用户特征、物品特征和上下文特征, 这就造成了有效信息的遗漏,不能充分利用其它特征数据。
为了解决这个问题, 在推荐模型中引用更多的特征,推荐系统慢慢的从以协同过滤为核心到了以逻辑回归模型为核心, 提出了能够综合不同类型特征的机器学习模型。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值