1. 前言: 为什么会有该系列?

最近,打算写《零基础入门推荐系统》系列,为了系统地介绍推荐系统知识,以及加强基础的实践能力。

该系列将结合一些书籍,比如项亮的《推荐系统实践》,由于项亮的推荐系统实践更偏项目以及工程设计,对排序模型介绍比较少,为了弥补这一不足,《零基础入门推荐系统》会更多地介绍一些基础排序模型,比如FM,DeepFM,DIN等模型。当然,每个模型会结合数学原理和python代码进行介绍,强化理论知识和实践能力。

推荐系统简介

推荐系统的目标是根据用户的历史,社交,上下文环境等信息去判断用户当前感兴趣的内容。

推荐系统一般包含四个环节:召回,粗排序,精排序,重排。

召回的算法有:ItemCF,UserCF,关联规则,Embedding,序列匹配,同类型收集等。

排序算法有:策略规则,线性模型,线性模型结合树模型,因子分解机模型,深度学习模型等。

以下将介绍两种召回模型:基于用户的协同过滤模型,基于物品的过滤模型。

2. 推荐系统的几种评测指标

对用户

推荐
个物品(记为
), 记用户
在测试集上喜欢的物品集合为
, 然后可以通过准确率,召回率,覆盖率,流行度来评测推荐算法的精度。

2.1 召回率Recall

召回率描述在真实用户-物品评分记录中推荐物品的比例。

具体的代码:

def recall(true, pred):"""recall value = |true == pred|/len(true):param true: dict, {user:[item1, item2]:param pred: dict, recommend list for each user. e.g.{user:[(user2, similarity)]}:return: >>> true = {"u1":["item1", "item2"]}>>> pred = {"u1":[("u2", 0.6), ("u3", 0.1)]}"""pred_true = 0all_true = 0print("pred",pred)for user, items in pred.items():for item in items:v, _ = item[0], item[1]if v in true[user]:pred_true += 1all_true += len(true[user])if all_true == 0:return 0return pred_true*1.0 / all_true

2.2 精度Precision

精度描述在推荐列表中用户实际喜欢物品的比例。

def precision(true, pred):"""precision value = |true == pred|/len(pred):param true: dict, {user:[item1, item2]:param pred: dict, recommend list for each user. e.g.{user:[(user2, similarity)]}>>> true = {"u1":["item1", "item2"]}>>> pred = {"u1":[("u2", 0.6), ("u3", 0.1)]}:return:"""pred_true = 0all_pred = 0for user,items in pred.items():for item in items:v, _ = item[0], item[1]if v in true[user]:pred_true += 1all_pred += 1if all_pred == 0:return 0return pred_true*1.0 / all_pred

2.3 覆盖率 Coverage

描述最终推荐列表中的商品占总共商品的比例。如果所有的物品都被推荐给至少一个用户,那么覆盖率就是100%。

def coverage(actual_items, recommend_items):"""coverage = len(set(pred))/len(set(actual_items)):param actual_items: set(), all items.:param recommend_items: set(), all recommend items:return:>>> actual_items = set("item1", "item2")>>> recommend_items = set("item1")"""if len(set(actual_items)) == 0:return 1return (len(set(recommend_items))*1.0)/len(set(actual_items))

2.4 流行度 Popularity

描述推荐的物品是否很热门,这侧面反映推荐的新颖度。 定义,用户看过这个物品,该物品的流行度+1。

def popularity(user_cf, train, test, N, K):"""popularity means how many people have watched it. Log transformation is applied for stability.:param user_cf: recommend system.:param train: dict, the train set.:param test: dict, the test set.:param N: select top N items to recommend.:param K: select the moset K similar users.:return:>>> train = {"user":["item1", "item2"]}>>> test = {"user2":["item2"]}"""item_popularity = dict()for user, items in train.items():for item in items:item_popularity[item] = item_popularity.get(item,0)+1ret = 0n = 0for user in test.keys():recommend_list = user_cf.recommend(user, N, K)for item,sim in recommend_list:ret += math.log(1 + item_popularity[item])n += 1if n == 0:return 0ret = ret*1.0/nreturn ret

物品的流行度分布满足长尾分布,为了让流行度的平均值更加稳定,增加了log变换。

3. 基于领域的算法

3. 1 基于用户的协同过滤算法

步骤: (1)找到和目标用户兴趣相似的用户集合;

(2)根据该集合中用户喜欢的,且目标用户之前没见过的物品推荐给目标用户;

步骤(1)中衡量两个用户的相似度,可以通过以下公式计算:

其中,

表示用户
曾经有过正反馈的物品集合,令
为用户
曾经有过正反馈的物品集合。

或者通过余弦相似度:

在实际的计算中,由于很多用户之间并没有对同样的物品产生过行为,即

, 也就是说数据是稀疏的。

因此,我们首先经历物品到用户的倒排表,也就是每个物品保存对该物品产生行为的用户列表, 再进行计算用户间的相似度。

步骤(2)对目标用户进行推荐物品:

其中,

包含了和用户
兴趣最接近的
个用户,
是对物品
有过行为的用户集合,
是用户
和用户
的兴趣相似度,
代表用户
对物品
的兴趣。 如果使用单一行为的隐反馈数据,即无法获取用户对物品的评价,
.

用户相似度计算的改进 UserCF-IIF

之前用户相似度对不同的物品的权重都一样,但是两个用户对冷门物品采取过同样的行为更能说明他们兴趣的相似度。

其中,

惩罚了用户
和用户
共同兴趣列表中热门商品对他们相似度的影响。

缺点: 由于用户数目越来越大,计算用户兴趣相似度矩阵越来越困难,运算时间复杂度和空间复杂度的增长和用户数的增长近似于平方关系。 其次,基于用户的协同概率很难对推荐结果作为解释。

3. 2 基于物品的协同过滤算法

基于物品的协同过滤算法应用广泛。 基于物品的协同过滤算法分成两步: (1)计算物品之间的相似度; (2)根据物品的相似度和用户的历史行为给用户生成推荐列表;

步骤(1)中,

其中,

表示喜欢物品
的用户集合,令
为同时喜欢物品
和物品
的用户集合。上述公式统计喜欢物品
中有多少比例的用户也喜欢物品
.

上述公式存在一个问题,如果物品

很热门,那么
会接近于1,造成任何物品都会和热门的物品有很大的相似度。

为了避免推荐热门的物品,挖掘长尾信息,定义:

上述公式惩罚了物品

的权重,减轻了热门物品和很多物品相似的可能性。

步骤(2)中,ItemCF 通过以下公式计算用户

对一个物品
的兴趣:

其中,

是用户
喜欢的物品集合,
是和物品
最相似的
个物品的集合,
是物品
的相似度,
是用户
对物品
的兴趣。(对于隐反馈数据集,如果用户u对物品 i 有过行为,即可令
.)

物品相似度计算的改进 ItemCF-IUF

之前物品相似度对不同的用户的权重都一样,但是存在比较活跃的用户,买书并非出于自身的兴趣,这些用户对于他购买的书两两相似度的贡献应该远远小于不是很活跃但出于个人兴趣购买的用户。

IUF(Inverse User Frequence),即用户活跃度对数的倒数的参数,认为活跃用户对物品相似度的贡献应该小于不活跃的用户:

其中,

惩罚了用户
和用户
共同兴趣列表中热门商品对他们相似度的影响。

物品相似度的归一化

考虑到不同类别的相似度的单位应该一致,应该对物品相似度进行归一化。

比如,有两件物品A和B,如果A类物品之间的相似度是0.5, B类物品之间的相似度是0.6, A和B类间的相似度是0.2。 在这种情况下, 一个用户喜欢了10个A类和10个B类商品,ItemCF会推荐更多的B类商品。但归一化后,A类物品之间的相似度变成1,B类物品相似度也变成1,系统推荐A类物品和B类物品的数目会大致相等。因此,相似度的归一化可以提高推荐的多样性。

哈利波特问题 一开始,亚马逊网使用ItemCF会发现推荐的物品大多数和《哈利波特》有关,这是因为这本书太热门了,购买任何一本书的人几乎都会购买它。这导致了热门物品会获得比较大的相似度。

改进的方案是,增加对热门物品的惩罚:

其中,

, 通过提高
,就可以惩罚热门的物品
.

如果

, 这就是标准的ItemCF算法。

一般,

越大, 覆盖率会提高和流行度会减轻,但是,精确率和召回率会变差。

4. 实践

4.1 MovieLens数据集介绍

MovieLens是电影评分的集合,有各种大小。 数据集命名为1M,10M和20M,是因为它们包含1,10和20百万个评分。

为了方便实验集测试,本次实验使用了1 million的数据集。 下载链接:https://grouplens.org/datasets/movielens/

数据集一共包含三个文件:

users.dat 用户属性文件

UserID::Gender::Age::Occupation::Zip-code

movies.dat 电影本身属性文件

MovieID::Title::Genres

ratings.dat 用户给电影文件

UserID::Gender::Age::Occupation::Zip-code

4.2 基于用户的协同过滤实验

把选择top相似用户个数

设定80, 选择推荐给目标用户的商品数量
选择30。

训练集和测试集按0.8:0.2比例进行划分。

class UserCF(object):"""用户协同过滤,根据相似用户推荐内容"""def train(self, user_items):"""训练模型:return:"""self.user_items = user_items# 计算用户的协同矩阵self.user_sim_matrix = self.user_similarity(user_items)#self.user_sim_matrix = self.improved_user_similarity(user_items)return self.user_sim_matrixdef improved_user_similarity(self, user_items):"""improved method of calculating the similarity. UserCF-IIF:param user_items: {user1:[movie1,movie2], user2:[movie1]}:return:"""# build inverse table for item_usersitem_users = dict()for u, items in user_items.items():for i in items:if i not in item_users:item_users[i] = set()item_users[i].add(u)# calculate co-rated items between users.C = dict()N = dict()for item, users in item_users.items():# each user u and user v both like the same item, similarity add 1/log(1+U(item))for u in users:N[u] = N.get(u,0) + 1if u not in C:C[u] = dict()for v in users:if v == u:continueC[u][v] = C[u].get(v,0) + 1/math.log(1+len(users))# calculate final similarity matrix WW = dict()for u, related_users in C.items():if u not in W:W[u] = dict()for v, cuv in related_users.items():W[u][v] = cuv / math.sqrt(N[u] * N[v])return Wdef user_similarity(self, user_items):"""calculate the similarity between users.:param user_items: {user1:[movie1,movie2], user2:[movie1]}:return:"""# build inverse table for item_usersitem_users = dict()for u, items in user_items.items():for i in items:if i not in item_users:item_users[i] = set()item_users[i].add(u)# calculate co-rated items between users.C = dict()N = dict()for item, users in item_users.items():for u in users:N[u] = N.get(u,0) + 1if u not in C:C[u] = dict()for v in users:if v == u:continueC[u][v] = C[u].get(v,0) + 1# calculate final similarity matrix WW = dict()for u, related_users in C.items():if u not in W:W[u] = dict()for v, cuv in related_users.items():W[u][v] = cuv / math.sqrt(N[u] * N[v])return Wdef recommend(self, user, N, K):"""recommend item according to user.:param user::param N: the number of recommend items:param K: the number of most similar users:return:  recommend items dict, {item: similarity}"""already_items = set(self.user_items.get(user, set()))recommend_items = dict()for v, sim in sorted(self.user_sim_matrix.get(user,dict()).items(), key=lambda x:-x[1])[:K]:for item in self.user_items[v]:if item in already_items:continuerecommend_items[item] = recommend_items.get(item,0) + simrecommend_item_list = sorted(recommend_items.items(), key=lambda x:-x[1])[:N]return recommend_item_listdef recommend_users(self, users, N, K):""":param users: test set of users.:param N: the number of recommend items:param K: the number of most similar users:return: dict, {user:[movie1, movie2]}"""recommend_result = dict()for user in users:recommend_item_list = self.recommend(user, N, K)recommend_result[user] = recommend_item_listreturn recommend_result

实验结果:

结论:UserCF-IIF模型在各项性能优于 UserCF, 偏向考虑冷门物品来衡量两个用户的相似度更加合理。

测试不同的K值对UserCF模型的影响

结论:随着K值(目标用户相似的其他用户个数)的增大,模型的准确率和召回率会先增大,后减少;覆盖率和流行度在K值较小的时候,效果更好。

4.3 基于物品的协同过滤实验

class ItemCF(object):"""物品协同过滤,根据用户浏览过的物品推荐相似物品"""def train(self, user_items, alpha=0.5, normalization=False):"""训练模型:return:"""self.user_items = user_items# 计算物品的协同矩阵#self.item_sim_matrix = self.item_similarity(user_items, normalization=True)#self.item_sim_matrix = self.improved_item_similarity(user_items)self.item_sim_matrix = self.improved_item_similarity2(user_items, alpha=alpha, normalization=normalization)#print(self.item_sim_matrix)return self.item_sim_matrixdef improved_item_similarity(self, user_items, normalization=False):""":param user_items: {user1:[movie1,movie2], user2:[movie1]}:return: W: {items1: {item2: sim12, item3:sim13}}"""# calculate co-rated users between items.C = dict()N = dict()for user, items in user_items.items():for i in items:N[i] = N.get(i,0) + 1if i not in C:C[i] = dict()for j in items:if i == j:continueC[i][j] = C[i].get(j,0) + 1/math.log(1+len(items))# calculate final similarity matrix WW = dict()for i, related_items in C.items():if i not in W:W[i] = dict()for j, cij in related_items.items():W[i][j] = cij / math.sqrt(N[i] * N[j])if normalization:for i, item_list in W.items():item_list = [item/max(item_list) for item in item_list]W[i] = item_listreturn Wdef improved_item_similarity2(self, user_items, alpha=0.5, normalization=False):"""Solution for Harry Potter problem.:param user_items: {user1:[movie1,movie2], user2:[movie1]}:return: W: {items1: {item2: sim12, item3:sim13}}"""# calculate co-rated users between items.C = dict()N = dict()for user, items in user_items.items():for i in items:N[i] = N.get(i,0) + 1if i not in C:C[i] = dict()for j in items:if i == j:continueC[i][j] = C[i].get(j,0) + 1/math.log(1+len(items))# calculate final similarity matrix WW = dict()for i, related_items in C.items():if i not in W:W[i] = dict()for j, cij in related_items.items():# if N[i] < N[j]:W[i][j] = cij / (N[i]**(1-alpha) * N[j]**alpha)# else:#     W[i][j] = cij / (N[j] ** (1 - alpha) * N[i] ** alpha)if normalization:for i, item_list in W.items():item_list = [item/max(item_list) for item in item_list]W[i] = item_listreturn Wdef item_similarity(self, user_items, normalization=False):""":param user_items: {user1:[movie1,movie2], user2:[movie1]}:return: W: {items1: {item2: sim12, item3:sim13}}"""# calculate co-rated users between items.C = dict()N = dict()for user, items in user_items.items():for i in items:N[i] = N.get(i,0) + 1if i not in C:C[i] = dict()for j in items:if i == j:continueC[i][j] = C[i].get(j,0) + 1# calculate final similarity matrix WW = dict()for i, related_items in C.items():if i not in W:W[i] = dict()for j, cij in related_items.items():W[i][j] = cij / math.sqrt(N[i] * N[j])if normalization:for i, item_sim_dict in W.items():max_val = max(item_sim_dict.values())#print(max_val)for j,sim in item_sim_dict.items():item_sim_dict[j] = sim/max_valreturn Wdef recommend(self, user, N, K):"""recommend item according to the history items of users.:param user::param N: the number of recommend items:param K: the number of most similar users:return:  recommend items dict, {item: similarity}"""already_items = set(self.user_items.get(user, set()))recommend_items = dict()for i in already_items:for j, sim in sorted(self.item_sim_matrix.get(i,dict()).items(), key=lambda x:-x[1])[:K]:if j in already_items:continuerecommend_items[j] = recommend_items.get(j,0) + simrecommend_item_list = sorted(recommend_items.items(), key=lambda x:-x[1])[:N]return recommend_item_listdef recommend_users(self, users, N, K):""":param users::param N::param K::return: dict, {user:[movie1, movie2]}"""recommend_result = dict()for user in users:recommend_item_list = self.recommend(user, N, K)recommend_result[user] = recommend_item_listreturn recommend_result

实验结果:

结论:和ItemCF相比, ItemCF-IUF稍微提高了准确率和召回率,提高了覆盖率,流行度提高了(一些实验中,流行度也会减少,具体看实验数据集);说明,不活跃用户对物品相似度的贡献比活跃用户的贡献大的假设能提升模型性能。

测试不同的K对模型ItemCF的影响

结论:同样和UserCF模型一样,合理选择K值,才会让模型在准确率和召回率更佳,在本实验中ItemCF最佳的K是20左右,UserCF最佳的K是60左右。

测试ItemCF归一化的影响

结论:对不同类别的物品相似度进行归一化能提升模型的性能。

测试不同惩罚流行度系数

对ItemCF的影响

结论:普通的ItemCF算法的准确率和召回率最高,但是覆盖率和新颖度都不高,经过给热门物品更大惩罚后,模型的覆盖率和流行度得到改善。

5. 结论

(1)推荐系统评测指标除了介绍的准确率,召回率,覆盖率,流行度,还可以是用户满意度,多样性,新颖性,惊喜度,信任度,实时性,健壮性,商业目标等。 (2)UserCF的推荐更社会化,反映用户所在兴趣群体中物品的热门程度; ItemCF的推荐更加个性化,反映了用户自己的兴趣传承,但在技术角度上,如果物品更新的速度很快(比如新闻推荐中),ItemCF维护的物品相关度的表也需要更新很快,实践难度会很大。

这两种算法经过优化后,最终得到的离线性能其实是近似的。

完整的代码见GitHub 零基础入门推荐系统系列


参考:

  1. Github RecSys/Recommend System.ipynb;
  2. 推荐系统实践

基于hadoop的商品推荐系统_[零基础入门推荐系统(1)]基于用户和基于物品的协同过滤方法(python代码实现)...相关推荐

  1. 零基础入门推荐系统(新闻推荐)

    零基础入门推荐系统(新闻推荐) 比赛介绍 本次新人赛是Datawhale与天池联合发起的零基础入门系列赛事第五场 -- 零基础入门推荐系统之新闻推荐场景下的用户行为预测挑战赛. 赛题简介 此次比赛是新 ...

  2. 零基础入门推荐系统 - 新闻推荐-Task2 (DataWhale学习小组)

    零基础入门推荐系统 - 新闻推荐-Task2 (DataWhale学习小组) 数据探索性分析 加载需要的module %matplotlib inline import pandas as pd im ...

  3. 零基础入门推荐系统 - 新闻推荐实战-笔记四

    零基础入门推荐系统 - 新闻推荐实战-笔记四-特征工程 什么是特征工程 本次特征工程内容 已有特征 特征构造 负采样 什么是特征工程 工业界名言:数据和特征决定了机器学习的上限,而模型和算法只是逼近这 ...

  4. 数据结构和算法_零基础入门01

    数据结构和算法_零基础入门01 一.数据结构是什么? 逻辑结构.物理结构 二.算法 算法的五个基本特征 算法设计的要求 b站学习小甲鱼的数据结构与算法,自留笔记. 程序设计=数据结构+算法 一.数据结 ...

  5. 零基础入门推荐系统 - 新闻推荐(一)

    赛题地址 背景: 随着信息技术和互联网的发展,人们逐渐从信息匮乏的时代走入了信息过载(information overload)的时代.在这个时代,无论是信息消费者还是信息生产者都遇到了很大的挑战:作 ...

  6. python问题分享_零基础入门Python常见问题分享

    零基础入门Python常见问题有哪些?作为经常混迹在各大Python技术论坛的小编而言,见到最多的话题就是:学习Python难不难?零基础可以学习Python吗?如何学习Python?等等.今天小编就 ...

  7. 零基础学python 视频_零基础入门学习PYTHON(第2版)(微课视频版)

    小甲鱼畅销图书重磅升级,针对Python 3.7,通过生动的实例,让读者在实践中理解概念,在轻松.愉快中学会Python! 本书提倡理解为主,应用为王.因此,只要有可能,小甲鱼(注:作者)都会通过生动 ...

  8. 量化投资python教程_零基础入门Python量化投资全套教程,30+经典教材打包送!暑期get新技能就靠它了!...

    量化投资是指使用数理分析.编程.建模等方式,通过对样本数据进行集中比对处理,找到数据之间的关系,制定量化策略,并使用编写的软件程序来执行交易,从而获得投资回报的方式. 在如今的量化投资领域,已经有了无 ...

  9. 什么是python中子类父类_零基础入门:python中子类继承父类的__init__方法实例

    前言: 今天为大家带来的内容是零基础入门:python中子类继承父类的__init__方法实例!具有不错的参考意义,希望在此能够帮助到各位!(喜欢的话记得点赞转发关注不迷路哦) 使用Python写过面 ...

最新文章

  1. centos6.5-vsftp搭建
  2. Axis2 客户端调用 设置超时时间
  3. ftp 工具_ftp工具,ftp工具有哪些
  4. android终端系统时间,安卓应用修改系统时间
  5. idea 14.1.4 激活方式
  6. python getopt_python 5种 statsPython中的getopt函数使用详解
  7. Spring boot MultipartResolver
  8. elementUI给树控件中的节点添加图标
  9. opencv马赛克python实现
  10. Windows系统设置局域网共享 解决:登录失败,用户账号限制。可能的原因包括不允许空密码,登录时间限制,或强制的策略限制
  11. 1、Swing简介:Swing是什么?
  12. 制作u盘winpe启动盘_u盘启动盘制作教程
  13. SpringBoot+Shiro实现登陆拦截功能
  14. 图片情感分析(1):图像数据预处理
  15. Jetpack之LiveData
  16. 七彩虹将星x15xs 2022款 怎么样
  17. 微信小程序中页面跳转时要始终返回首页的问题
  18. USACO-Healthy Holsteins
  19. 2d激光重定位系列(一)AMCL:算法源码参数 相关资源整合
  20. 使用word2vec分析红楼梦中的人物

热门文章

  1. 2020最新苹果CMSV10 JAVA原生APP影视源码 有安装教程
  2. 华为nova8和华为nova8Plus哪个好-华为nova8和华为nova8Plus区别-哪个更值得入手-参数对比
  3. 7-2 查询水果价格 (15 分)
  4. 工业机器人九龙坡区职教中心_重庆市九龙坡职业教育中心(职教中心)简介简介...
  5. 华为stk_Aloo计算机在哪里,华为STK-AL00是什么型号
  6. jQuery的排他思想
  7. 微信小程序开发记录2——获取openid失败
  8. 解决Microsoft OneNote不能登录https://onedrive.live.com的问题
  9. 听见丨哈罗单车完成10亿元人民币D2轮融资,复星领投 场景化AI应用服务公司百可录完成A轮融资
  10. netlink使用方法