推荐系统组队学习——协同过滤

一、协同过滤介绍

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

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

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

协同过滤的核心是构建用户物品的评分矩阵,这个矩阵是稀疏的,因为用户不可能对所有的物品进行评价。向量维度上的取值可以是简单的 0 或者 1,也可以是1,2,3,4,5这种评分数据。

以下是两种典型的稀疏矩阵存储格式:

  1. CSR:这个存储稍微复杂点,是一个整体编码方式。它有三个组成:数值、列号和行偏移共同编码。
  2. COO:这个存储方式很简单,每个元素用一个三元组表示(行号,列号, 数值),只存储有值的元素,缺失值不存储。

这些存储格式,在常见的计算框架里面都是标准的,如 Spark 中,Python 的 NumPy 包中。一些著名的算法比赛也通常都是以这种格式提供数据。把原始行为日志转换成上面的格式,就可以使用常用计算框架的标准输入了。

二、相似度度量方法

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

两个用户 u 和 v 交互商品交集的数量占这两个用户交互商品并集的数量的比例,称为两个集合的杰卡德相似系数,用符号 sim 表示,其中 N(u),N(v)分别表示用户 u 和用户 v 交互商品的集合。
在这里插入图片描述
由于杰卡德相似系数一般无法反映具体用户的评分喜好信息, 所以常用来评估用户是否会对某商品进行打分, 而不是预估用户会对某商品打多少分。

2. 余弦相似度

余弦相似度衡量了两个向量的夹角,夹角越小越相似。首先从集合的角度描述余弦相似度,相比于Jaccard公式来说就是分母有差异,不是两个用户交互商品的并集的数量,而是两个用户分别交互的商品数量的乘积,公式如下:

从向量的角度进行描述,令矩阵 A 为用户-商品交互矩阵(因为是TopN推荐并不需要用户对物品的评分,只需要知道用户对商品是否有交互就行),即矩阵的每一行表示一个用户对所有商品的交互情况,有交互的商品值为1没有交互的商品值为0,矩阵的列表示所有商品。若用户和商品数量分别为 m,n的话,交互矩阵 就是一个 m 行 n 列的矩阵。此时用户的相似度可以表示为(其中 u*v 指的是向量点积):
在这里插入图片描述
为了避免存储这么大的稀疏矩阵,在计算用户相似度的时候一般会采用集合的方式进行计算。理论上向量之间的相似度计算公式都可以用来计算用户之间的相似度,但是会根据实际的情况选择不同的用户相似度度量方法。

from sklearn.metrics.pairwise import cosine_similarity
i = [1, 0, 0, 0]
j = [1, 0.5, 0.5, 0]
consine_similarity([a, b])

3. 皮尔逊相关系数

皮尔逊相关系数的公式与余弦相似度的计算公式非常的类似,首先对于上述的余弦相似度的计算公式写成求和的形式,其中rui ,rvi分别表示用户 u 和用户 v 对商品 i 是否有交互(或者具体的评分值):
在这里插入图片描述
如下是皮尔逊相关系数计算公式,减去了用户 u 和用户 v 交互的所有商品交互数量或者具体评分的平均值。
在这里插入图片描述
相比余弦相似度,皮尔逊相关系数通过使用用户的平均分对各独立评分进行修正,减小了用户评分偏置的影响。

from scipy.stats import pearsonr
i = [1, 0, 0, 0]
j = [1, 0.5, 0.5, 0]
pearsonr(i, j)

三、基于用户的协同过滤

原理

基于用户的协同过滤,简称User CF,原理是当一个用户A需要个性化推荐的时候, 我们可以先找到和他有相似兴趣的其他用户, 然后把那些用户喜欢的, 而用户A没有听说过的物品推荐给A。

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

  1. 基于相似性度量的方法找出与目标用户兴趣相似的用户
  2. 找到这个集合中的用户喜欢的, 且目标用户没有听说过的物品推荐给目标用户。

用户相似度计算存在两个问题,一是向量很长,二是用户量很大。

针对向量很长的问题,有两个解决办法:

  • 对向量采样计算,即从高维向量中随机取几维进行计算相似度,这个算法由 Twitter 提出,叫做 DIMSUM 算法,已经在 Spark 中实现了。
  • 向量化计算,想办法把循环转换成向量来直接计算,比如 Python 的 NumPy 。

针对用户量很大的问题,解决办法如下:

  • 将相似度计算拆成 Map Reduce 任务,将原始矩阵 Map 成键为用户对,值为两个用户对同一个物品的评分之积,Reduce 阶段对这些乘积再求和,Map Reduce 任务结束后再对这些值归一化
  • 不用基于用户的协同过滤

关于结果的预测有如下几种办法,一个技巧是利用MapReduce的思路来解决向量过长的问题:

  • 利用用户相似度和相似用户的评价加权平均获得用户的评价预测
    在这里插入图片描述
  • 利用该物品的评分与此用户的所有评分的差值进行加权平均,这时候考虑到了有的用户内心的评分标准不一的情况
    在这里插入图片描述

编程实现

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.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]
#similarity_matrix[userID][otheruserId] = cosine_similarity(np.array(vec_user), np.array(vec_otheruser))[0][1]

计算用户相似性矩阵

"""计算前n个相似的用户"""
n = 2
similarity_users = similarity_matrix[1].sort_values(ascending=False)[:n].index.tolist()    # [2, 3]   也就是用户1和用户2

计算前n个相似的用户

"""计算最终得分"""
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优缺点

User-based算法存在两个重大问题:

  1. 数据稀疏性:大型的电子商务推荐系统一般有非常多的物品,用户可能买的其中不到1%的物品,不同用户之间买的物品重叠性较低,导致算法无法找到偏好相似的用户。
  2. 算法扩展性:基于用户的协同过滤需要维护用户相似度矩阵以便快速的找出Topn相似用户, 该矩阵的存储开销非常大,存储空间随着用户数量的增加而增加,不适合用户数据量大的情况使用。

四、基于物品的协同过滤

原理

基于物品的协同过滤(ItemCF)的基本思想是预先根据所有用户的历史偏好数据计算物品之间的相似性,然后把与用户喜欢的物品相类似的物品推荐给用户。比如物品a和c非常相似,因为喜欢a的用户同时也喜欢c,而用户A喜欢a,所以把c推荐给用户A。

基于物品的协同过滤算法主要分为两步:

  1. 计算物品之间的相似度
  2. 根据物品的相似度和用户的历史行为给用户生成推荐列表(购买了该商品的用户也经常购买的其他商品)

有两种推荐场景

  1. Top-k推荐:要预测一个用户 u 对一个物品 i 的分数,遍历用户 u 评分过的所有物品,假如一共有 m 个,每一个物品和待计算物品 i 的相似度乘以用户的评分,这样加权求和后,除以所有这些相似度总和,就得到了一个加权平均评分,作为用户 u 对物品 i 的分数预测。

    这个过程都是离线完成后,去掉那些用户已经消费过的,保留分数最高的 k 个结果存储。当用户访问首页时,直接查询出来即可。

  2. 相关推荐:这类推荐不需要提前合并计算,当用户访问一个物品的详情页面时,或者完成 一个物品消费的结果面,直接获取这个物品的相似物品推荐。

    SlopeOne 算法 :经典的基于物品推荐,相似度矩阵计算无法实时更新。因此我们计算物品之间的距离,最后按照加权即可得到结果。

编程实现

"""计算物品的相似矩阵"""
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]

计算物品的相似矩阵

"""得到与物品5相似的前n个物品"""
n = 2
similarity_items = similarity_matrix['E'].sort_values(ascending=False)[:n].index.tolist()       # ['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

得到与物品5相似的前n个物品, 计算出最终得分来

五、算法评估

1. 召回率

对用户u推荐N个物品记为 R(u), 令用户u在测试集上喜欢的物品集合为 T(u), 那么召回率定义为:
在这里插入图片描述
在用户真实购买的影片里面, 模型真正预测出了多少, 考察的是模型推荐的全面性。

2. 准确率

在这里插入图片描述
在推荐的所有物品中, 用户真正看的有多少, 这个考察的是模型推荐的准确性。为了提高准确率, 模型需要把非常有把握的才对用户进行推荐, 所以这时候就减少了推荐数量, 而这往往损失了全面性, 真正预测出来的会非常少,所以实际应用中应该综合考虑两者的平衡

3. 覆盖率

覆盖率反映了推荐算法发掘长尾的能力, 覆盖率越高, 说明推荐算法越能将长尾中的物品推荐给用户。
在这里插入图片描述
该覆盖率表示最终的推荐列表中包含多大比例的物品。如果所有物品都被给推荐给至少一个用户, 那么覆盖率是100%。

4.新颖度

用推荐列表中物品的平均流行度度量推荐结果的新颖度。 如果推荐出的物品都很热门, 说明推荐的新颖度较低。 由于物品的流行度分布呈长尾分布, 所以为了流行度的平均值更加稳定, 在计算平均流行度时对每个物品的流行度取对数

六、协同过滤算法的权重改进

在这里插入图片描述

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

七、协同过滤算法的问题分析

协同过滤推荐算法给我们带来便利的同时,也存在一些问题,在实际应用过程中我们经常会遇到,以下就存在的一些典型的问题进行了分析。

1 数据稀疏性的问题

推荐系统面临的问题中最大的问题就是数据稀疏性问题,它会影响推荐系统推荐的准确度,在个性化推荐系统中,协同过滤是最常用的技术,数据稀疏性影响了协同过滤的结果,因为在很多推荐系统中只有很小一部分评分,当在评分很小的情况下,推荐的准确度也会下降。

目前来说解决数据稀疏问题最好的算法是降维技术,比如奇异值SVD分解。降维技术的关键是通过原始矩阵通过计算来求其低维度的相似来降低矩阵的维度,有一点弊端就是数据量大的时候运算成本很高,好在最近几年云计算发展迅猛,这些计算量都可以放在运动,来弥补计算量的问题,除了降维技术之外人口统计过滤也是解决数据稀疏性问题的很好的方法,人口统计过滤是通过用户资料的对比找到用户相似,然后通过相似的用户来进行推荐,这种方式可以解决数据量很稀疏的情况下使用。

2 冷启动问题

冷启动问题也被认作为推荐系统面临的问题中关键之一。冷启动产生原因是新用户进入系统没有用户行为数据,所以就不存在收集用户偏好的过程,进而也就很难进行有效的推荐。解决用户冷启动的问题可以采用上一小节中的人口统计过滤,其针对于冷启动也会有很好的效果,除此之外只能通过业务上调整去改变并获得用户的一些资料,从而进行有效推荐,具体的业务有利用用户注册信息、社交网络挖掘、启用用户信息。

具体做法是通过用户信息进行用户分类,利用社交网络导入用户的好友关系,然后通过好友关系来进行相关推荐,还可以通过用户的首次访问不立即给推荐反馈结果,而是通过引导页用户填写用户感兴趣的内容,然后通过这些收集到的准确信息来进行准确的推荐,一般我们会通过引导用户填写比较有代表性、区分性以及多样性的内容去进行选择,比如在豆瓣FM中,一般会采用热门精选的音乐以及分组好的音乐来给用户进行选择。解决内容冷启动问题可以通过物品的信息来解决新物品的推荐问题,一般会采用物品标签进行推荐,通过抽象出新物品的标签以及利用老物品的标签进行匹配,匹配成功就进行相关度的推荐。而系统的冷启动可以通过专家发挥专家的作用,在系统冷启动环节中,可以引入专家的知识,利用专家的知识构建起物品的相关度表,然后通过表格进行相关度的分析进行推荐。

3 扩展性问题

推荐系统中通过推荐算法获得之后,因为用户之前没有足够多的对物品进行评分,有时候很难掌握用户的各方面兴趣和需求,这就会导致出现过拟合现象,这一类现象就是推荐系统中的扩展性问题,这类问题对于推荐系统的体验上是一个很大的问题。

扩展性问题在实际应用中是无可避免的,其中受数据集大小的扩展,数据完整性等问题的限制,对于数据完整性的问题,通常是引入随机性,使算法收敛到全局最优或者逼近全局最优,但是推荐系统运行过程是动态变化的,同时也会受冷启动影响,用户/对象/喜好数据达到一定规模时,性能这个时候也会存在一些瓶颈,这个时候的大数据处理和算法的可扩展性成为推荐算法技术需要实施并迫切需要解决的问题。

4 泛化能力弱的问题

协同过滤算法存在的问题之一就是泛化能力弱, 即协同过滤无法将两个物品相似的信息推广到其他物品的相似性上。 导致的问题是热门物品具有很强的头部效应, 容易跟大量物品产生相似, 而尾部物品由于特征向量稀疏, 导致很少被推荐。 比如下面这个例子:
  在这里插入图片描述
A, B, C, D是物品, 看右边的物品共现矩阵, 可以发现物品D与A、B、C的相似度比较大, 所以很有可能将D推荐给用过A、B、C的用户。 但是物品D与其他物品相似的原因是因为D是一件热门商品, 系统无法找出A、B、C之间相似性的原因是其特征太稀疏, 缺乏相似性计算的直接数据。 所以这就是协同过滤的天然缺陷:推荐系统头部效应明显, 处理稀疏向量的能力弱。

为了解决这个问题,同时增加模型的泛化能力,2006年,矩阵分解技术(Matrix Factorization,MF) 被提出,该方法在协同过滤共现矩阵的基础上,使用更稠密的隐向量表示用户和物品,挖掘用户和物品的隐含兴趣和隐含特征,在一定程度上弥补协同过滤模型处理稀疏矩阵能力不足的问题。

5 其它一些问题和挑战

推荐系统在推荐的过程中还会存在隐私问题、噪声问题、解释性问题、合理性、新颖性等等,这些问题在推荐系统中很有挑战性,但是解决这些问题出现的同时也会改变推荐准确度,所以我们需要权衡这些问题和推荐之间的联系,目前很多算法都有在改进相关的问题,改进的措施也是多维度的。

八、其他问题

UserCF的适用情况:用户少, 物品多, 时效性较强的场合
ItemCF的适用情况:物品少,用户多,用户兴趣固定持久, 物品更新速度不是太快的场合

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值