文章目录

  • 一、什么是推荐系统
  • 二、产生原因
    • 由上我们可以得知,推荐系统产生的两大原因
  • 三、应用场景
  • 四、推荐系统的评测方法
    • 1. 推荐系统的三大实验方法
      • a. 离线实验:
      • b. 用户调查
      • c. 在线实验
      • 《Summary》
    • 2. 推荐系统的常用评测指标
  • 五、目前常用的推荐算法
  • 六、十大开源推荐系统
  • 七、例子实践(网络电视节目的精准营销推荐)
    • 1. 目标
    • 2. 算法思路
    • 3. 输入数据
    • 4. 具体实现步骤:
        • 2. 刻画用户画像(User Profiles)& 得出推荐集
      • b. 基于用户的协同过滤算法UserCF的实现:
        • 1. 准备好输入数据
        • 2. 挖掘用户的邻居,得出推荐集
    • 5. 相关说明
      • a. 利用改进的皮尔逊相关系数计算用户之间的相似度
      • b. 基于用户之间的相似度得到用户对于节目的感兴趣程度
  • Reference Materials :

一、什么是推荐系统

  1. 维基定义:推荐系统是一种信息过滤系统,用于预测用户对物品的“评分”或“偏好”。(即实质上是一种预测系统)
  2. 具体定义:推荐系统是一个可以根据用户(users)的历史行为、社交关系、兴趣点、所处上下文环境等去判断用户的当前需求或感兴趣的物品(items)的系统。
  3. 别名:个性化推荐系统(见名知义,即“为你服务”)


二、产生原因

你可以假想一下,在一个悠闲的下午,你手中怀揣着一袋零食,眼前摆着一部电脑,突发奇想的你想要让一部喜剧电影来陪伴你度过这个美好的下午。这时,你轻点了几下鼠标,跳转到了一个视频门户网站。你又轻轻敲了键盘,在右上角的搜索框内键入"喜剧"关键词,结果你发现,整个页面是一列表的喜剧电影,琳琅满目,但也让你手足失措,不知道要看哪一部,或者其中列出的一些电影你之前已经看过了。你感慨道:哎,有没有一个专门为我服务的工具呢,让它帮我选一下电影,我可不想把我宝贵的时间花在找电影的功夫上。。。


此时,我们可以看出,我们在现实生活中会经常遇到信息过载的问题,特别是随着最近互联网的迅速发展,世界的数据量呈螺旋式增长,所以,我们需要一个工具,或者说,是一个系统,来帮我们做一些决策,给出一些建议供你选择。而且这个工具是自动化的,换句话说,它可以分析你的历史兴趣,从庞大的数据集当中寻找出符合你兴趣的物品让你去选择,这个工具就是个性化推荐系统

推荐算法其实在1992年就提出来了,但是实际上是最近这些年才火起来,因为互联网的爆发,导致了我们所谓的“大数据”,我们就可以很好地利用这些数据,来实行我们的推荐算法,进行各种精准营销业务和个性化服务。

由上我们可以得知,推荐系统产生的两大原因

  • 信息过载
  • 用户需求的多样性(也就是用户在大部分时候没有明确的需求)

所以,一般只有同时满足这两个原因时,我们才会使用推荐系统,不然可能适得其反。



三、应用场景

  1. 电子商务

    比如著名的电子商务网站亚马逊的个性化商品推荐列表和相关商品的推荐列表。

  2. 电影和视频网站

    比如著名的Netflix的在线视频推荐系统以及YouTube的基于Deep Learning的推荐系统。

  3. 个性化音乐网络平台

    比如博主使用的网易云音乐中的“每日推荐”。

  4. 社交网络

    比如我们熟悉的Facebook和Twitter的社交网络应用中信息流的会话推荐。

  5. 个性化阅读

    比如国外著名的Google Reader,它允许用户关注自己感兴趣的人,然后看到所关注用户分享的文章。

  6. 基于位置的服务

    比如我们经常使用的美团App,它会获取我们实时的地理位置信息,然后基于这些位置信息给我们推荐附近的且我们可能感兴趣的店铺。

  7. 个性化邮件

    上面已经说了,在定义里面提到了“过滤”,这里就是指个性化的邮件过滤推荐系统,它会帮我们过滤掉垃圾邮件,然后留下我们所感兴趣的邮件并依据重要性进行排序。最早的一个个性化邮件推荐系统是Tapestry。

  8. 个性化广告

    这个很容易理解,现在个性化广告投放甚至都成为了一门独立的学科----计算广告学。



四、推荐系统的评测方法

一个推荐系统产生之后,我们还不可以贸然实行,还需评测一下它的具体性能,才可以上线实行,要不然会出现意想不到的后果,甚至与我们想要的效果想违背。

  • 完整的推荐系统中的三个参与方

    • 用户
    • 物品提供者
    • 提供推荐系统的网站

在介绍推荐系统的指标之前,先说一下可以获得这些指标的实验方法~~

1. 推荐系统的三大实验方法

a. 离线实验:

  1. 通过日志系统获得用户行为数据,按照一定的格式生成一个标准的数据集
  2. 将数据集按照一定的规则分成训练集和测试集
  3. 在训练集上训练用户兴趣模型,在测试集上进行预测
  4. 通过事先定义的离线指标评测算法在测试集上的预测结果

优点:

  • 不需要真实用户的参与
  • 不需要有对实际系统的控制权
  • 速度快,可以测试大量算法

缺点:

  • 无法计算商业关心的指标,例如点击率、转化率等

b. 用户调查

由于离线试验的指标与商业指标存在差异,高准确率不等于高用户满意度。所以,想要准确的评测一个算法,需要相对比较真实的环境。最好的方法就是直接上线测试,而由于无法确定算法的影响,上线测试会有比较大的风险,这时,我们的用户调查就派上用场了。很多离线试验无法获得的主观感受指标都可以通过用户调查获得。

PS:过程大致和产品的用户调查差不多,但应尽量保证双盲实验,尽量使用多样用户群,并保证用户属性的平衡。

优点:

  • 可以获得很多体现用户主观感受的指标
  • 相对在线实验风险很低,出现错误后很容易弥补

缺点

  • 招募测试用户代价较大,很难组织大规模的测试用户
  • 设计双盲实验较困难,并且用户在测试环境下的行为和真实环境下的行为可能有所不同,导致测试结果在真实环境下无法重现。

c. 在线实验

在线实验就是大杀器了,它可以统计到最真实的用户反馈和商业指标。在完成必要的离线试验和用户调查后,可以采用AB测试的方式比较新旧算法

AB测试(一种在线评测算法的实验方法):

根据一定规则将用户随机分成几组,对不同组的用户采用不同的算法,然后统计不同组用户的各种不同的评测指标来比较不同算法。比如可以统计不同组用户的点击率,通过该指标来比较不同算法的性能。


《Summary》

一个新的推荐系统的最终上线,需要完成上面的三个实验:

  • Firstly:通过离线实验证明它在很多离线指标上优于现有的算法。
  • Secondly:通过用户调查确定它的用户满意度不低于现有的算法。
  • Finally: 最后通过在线的AB测试确定它在我们关心的指标上优于现有的算法。

2. 推荐系统的常用评测指标

由于网上的资料很多,加上这个不是本文所要阐述的重点,这里仅罗列一下,具体公式可以自行Google~~

  • 用户满意度

    用户满意度是推荐系统最重要的指标,但是,用户满意度无法离线计算,只能通过用户调查或在线实验得到。

  • 预测准确度

    是度量一个推荐系统预测用户行为的能力,是最重要的离线评测方法。

  • TopN推荐

    TopN 推荐是指,用户会不会对物品感兴趣。TopN 推荐有两个重要指标:准确率和召回率。

    • 准确率:为用户推荐且用户感兴趣的物品,在推荐结果列表中所占的比例。
    • 召回率:为用户推荐且用户感兴趣的物品,在用户感兴趣的所有物品列表中所占的比例。
  • 评分预测

    评分预测是指用户会对一个物品产生怎样的评分,由此可以习得用户的兴趣模型或用户画像。

  • 覆盖率

    覆盖率描述一个推荐系统对长尾的发掘能力。覆盖率没有唯一的定义方法,一个简单的定义是,推荐列表中的物品占总物品数的比例。

  • 实时性

  • 健壮性

  • 商业目标

    。。。

    。。。

    。。。

此外,还有一些偏向主观方面的指标, 比如:多样性、新颖性、惊喜度、信任度等。。。


五、目前常用的推荐算法

  • 基于物品的协同过滤算法(Item-based Collaborative Filtering),可简称为ItemCF。

  • 基于用户的协同过滤算法(User-based Collaborative Filtering),可简称为UserCF。

  • 隐语义与矩阵分解模型(Latent Factor Model)

  • 基于内容的推荐算法(Content-based Recommendation),可简称为CB。

  • 基于深度学习的推荐算法

    。。。

    。。。


六、十大开源推荐系统

有兴趣的可以了解一下~~

  • SVDFeature
  • LibMF
  • LibFM
  • Lenskit
  • GraphLab
  • Mahout
  • Myrrix(http://myrrix.com/)
  • EasyRec
  • Waffles
  • RapidMiner

七、例子实践(网络电视节目的精准营销推荐)

简化说明: 本例子来自今年(2018)的泰迪杯的B题,但这里已经过大量简化, 辛苦了我辛勤耕耘的队友。这里仅通过三个用户的收视数据来实现我们的混合推荐算法。

1. 目标

基于每位用户的观看记录以及节目信息,对每位用户实行节目的个性化推荐。


2. 算法思路

将UserCF 与 CB 进行“后融合”


3. 输入数据

  1. 用户的观看记录: “用户A对于其三个月来所看过节目的评分.xls”,“用户B对于其三个月来所看过节目的评分.xls”,“用户C对于其三个月来所看过节目的评分.xls”。具体格式如下:
    (对于其中的A、B列,是一些辅助数据,这里不会使用,可以忽略)

  2. 备选推荐节目集: “备选推荐节目集及所属类型.xlsx”。具体格式如下:


4. 具体实现步骤:

#### a. 基于内容的推荐算法CB的实现: ##### 1. 刻画节目画像(Item Profiles) > 将“备选推荐节目集及所属类型.xlsx” 转换为01矩阵表示的节目表“备选推荐节目集及所属类型01矩阵.xlsx”。

Source Code:
items_labels_to_01matrix.py

# 代码说明:
# 根据"D:\Recommender Systems\备选推荐节目集及所属类型.xlsx"生成"D:\Recommender Systems\备选推荐节目集及所属类型01矩阵表.xlsx"import pandas as pd
import numpy as npif __name__ == '__main__':df = pd.read_excel("D:\Recommender Systems\备选推荐节目集及所属类型.xlsx")(m, n) = df.shapedata_array = np.array(df.iloc[1:m+1,:])# 按指定顺序排列的所有标签all_labels = ['教育', '戏曲', '悬疑', '科幻', '惊悚', '动作', '资讯', '武侠', '剧情', '警匪', '生活', '军事', '言情', '体育', '冒险', '纪实', '少儿教育', '少儿', '综艺', '古装', '搞笑', '广告']labels_num = len(all_labels)# 按顺序提取所有节目的名称all_items_names = np.array(df.iloc[:m+1, 0])[1:]# 创建一个01矩阵,0表示该节目不属于该类型,1表示该节目属于该类型data_to_be_written = []for i in range(len(all_items_names)):# 每个节目的01行向量vector = [0] * labels_numlabels_names = str(data_array[i][1]).split(" ")for j in range(len(labels_names)):location = all_labels.index(labels_names[j])vector[location] = 1data_to_be_written.append(vector)# 将01矩阵写入“备选推荐节目集及所属类型01矩阵表”df = pd.DataFrame(data_to_be_written, index=all_items_names, columns=all_labels)df.to_excel("D:\Recommender Systems\备选推荐节目集及所属类型01矩阵表.xlsx")# PS: 记得在生成的“备选推荐节目集及所属类型01矩阵表”中节目名那一列的首个空白的单元格中打上“节目名”

Result:

2. 刻画用户画像(User Profiles)& 得出推荐集

根据“用户A\B\C对于其三个月来所看过节目的评分.xls”三个表,得出“所有用户对其看过的节目的评分矩阵.xlsx”。

这个要考虑到权重以及其他因素,具体方法取决于实际应用,我就直接把代码的输出结果的样式给出来。
Result:

根据“用户A\B\C对于其三个月来所看过节目的评分.xls”三个表,得出“所有用户看过的节目及所属类型的01矩阵.xlsx”。

Source Code:
items_saw_labels_to_01matrix.py

# 代码说明:
# 根据"D:\Recommender Systems\用户A/B/C对于其三个月来所看过节目的评分.xls"
# 生成"D:\Recommender Systems\所有用户看过的节目及所属类型的01矩阵.xlsx"
import pandas as pd
import numpy as npif __name__ == '__main__':all_users_names = ['A', 'B', 'C']# 所有用户看过的节目名 all_items_users_saw = [item2, item3, item4]# 所有用户看过的节目名对应的类型 all_items_users_saw_labels = ["label2 label3", "label3", ...]all_items_users_saw = []all_items_users_saw_labels = []for j in range(len(all_users_names)):fileToBeRead = "D:\Recommender Systems\用户" + all_users_names[j] + "对于其三个月来所看过节目的评分.xls"df = pd.read_excel(fileToBeRead)(m, n) = df.shapedata_array = np.array(df)for i in range(m):# 不重复记录相同的节目if data_array[i][2] not in all_items_users_saw:all_items_users_saw.append(data_array[i][2])all_items_users_saw_labels.append(data_array[i][3])# 生成"所有用户看过的节目及所属类型的01矩阵"all_labels = ['教育', '戏曲', '悬疑', '科幻', '惊悚', '动作', '资讯', '武侠', '剧情', '警匪', '生活', '军事', '言情', '体育', '冒险', '纪实', '少儿教育', '少儿', '综艺', '古装', '搞笑', '广告']labels_num = len(all_labels)all_items_labels_01_vectors = []for i in range(len(all_items_users_saw)):vector = [0] * labels_numlabels_names = all_items_users_saw_labels[i].split(" ")for j in range(len(labels_names)):location = all_labels.index(labels_names[j])vector[location] = 1all_items_labels_01_vectors.append(vector)df = pd.DataFrame(all_items_labels_01_vectors, index=all_items_users_saw, columns=all_labels)df.to_excel("D:\Recommender Systems\所有用户看过的节目及所属类型的01矩阵.xlsx")# PS: 记得在生成的“所有用户看过的节目及所属类型的01矩阵表”中节目名那一列的首个空白的单元格中打上“节目名”

Result:

> 根据“所有用户对其看过的节目的评分矩阵.xlsx”以及“所有用户看过的节目及所属类型的01矩阵.xlsx”得到用户画像(一个关于评分数据的行向量,表示某个用户对于各个类型的评分),然后将每个用户画像与所有**备选推荐节目**的画像(一个关于是否含有/具备该类型的01行向量,表示某个节目含有/具备哪些类型)进行相似度的计算,然后将推荐节目集按相似度进行降序排序,最后取出topN个节目。CB的具体实现如下:

Source Code:
CB.py

# 代码说明:
# 基于内容的推荐算法的具体实现import math
import numpy as np
import pandas as pd# 创建节目画像
# 参数说明:
# items_profiles = {item1:{'label1':1, 'label2': 0, 'label3': 0, ...}, item2:{...}...}
def createItemsProfiles(data_array, labels_names, items_names):items_profiles = {}for i in range(len(items_names)):items_profiles[items_names[i]] = {}for j in range(len(labels_names)):items_profiles[items_names[i]][labels_names[j]] = data_array[i][j]return items_profiles# 创建用户画像
# 参数说明:
# data_array: 所有用户对于其所看过的节目的评分矩阵 data_array = [[2, 0, 0, 1.1, ...], [0, 0, 1.1, ...], ...]
# users_profiles = {user1:{'label1':1.1, 'label2': 0.5, 'label3': 0.0, ...}, user2:{...}...}
def createUsersProfiles(data_array, users_names, items_names, labels_names, items_profiles):users_profiles = {}# 计算每个用户对所看过的所有节目的平均隐性评分# users_average_scores_list = [1.2, 2.2, 4.3,...]users_average_scores_list = []# 统计每个用户所看过的节目(不加入隐性评分信息)# items_users_saw = {user1:[item1, item3, item5], user2:[...],...}items_users_saw = {}# 统计每个用户所看过的节目及评分# items_users_saw_scores = {user1:[[item1, 1.1], [item2, 4.1]], user2:...}items_users_saw_scores = {}for i in range(len(users_names)):items_users_saw_scores[users_names[i]] = []items_users_saw[users_names[i]] = []count = 0sum = 0.0for j in range(len(items_names)):# 用户对该节目隐性评分为正,表示真正看过该节目if data_array[i][j] > 0:items_users_saw[users_names[i]].append(items_names[j])items_users_saw_scores[users_names[i]].append([items_names[j], data_array[i][j]])count += 1sum += data_array[i][j]if count == 0:users_average_scores_list.append(0)else:users_average_scores_list.append(sum / count)for i in range(len(users_names)):users_profiles[users_names[i]] = {}for j in range(len(labels_names)):count = 0score = 0.0for item in items_users_saw_scores[users_names[i]]:# 参数:# 用户user1对于类型label1的隐性评分: user1_score_to_label1# 用户user1对于其看过的含有类型label1的节目item i 的评分: score_to_item i# 用户user1对其所看过的所有节目的平均评分: user1_average_score# 用户user1看过的节目总数: items_count# 公式: user1_score_to_label1 = Sigma(score_to_item i - user1_average_score)/items_count# 该节目含有特定标签labels_names[j]if items_profiles[item[0]][labels_names[j]] > 0:score += (item[1] - users_average_scores_list[i])count += 1# 如果求出的值太小,直接置0if abs(score) < 1e-6:score = 0.0if count == 0:result = 0.0else:result = score / countusers_profiles[users_names[i]][labels_names[j]] = resultreturn (users_profiles, items_users_saw)# 计算用户画像向量与节目画像向量的距离(相似度)
# 向量相似度计算公式:
# cos(user, item) = sigma_ui/sqrt(sigma_u * sigma_i)# 参数说明:
# user_profile: 某一用户user的画像 user = {'label1':1.1, 'label2': 0.5, 'label3': 0.0, ...}
# item: 某一节目item的画像 item = {'label1':1, 'label2': 0, 'label3': 0, ...}
# labels_names: 所有类型名
def calCosDistance(user, item, labels_names):sigma_ui = 0.0sigma_u = 0.0sigma_i = 0.0for label in labels_names:sigma_ui += user[label] * item[label]sigma_u += (user[label] * user[label])sigma_i += (item[label] * item[label])if sigma_u == 0.0 or sigma_i == 0.0:  # 若分母为0,相似度为0return 0return sigma_ui/math.sqrt(sigma_u * sigma_i)# 基于内容的推荐算法:
# 借助特定某个用户user的画像user_profile和备选推荐节目集的画像items_profiles,通过计算向量之间的相似度得出推荐节目集# 参数说明:
# user_profile: 某一用户user的画像 user_profile = {'label1':1.1, 'label2': 0.5, 'label3': 0.0, ...}
# items_profiles: 备选推荐节目集的节目画像: items_profiles = {item1:{'label1':1, 'label2': 0, 'label3': 0}, item2:{...}...}
# items_names: 备选推荐节目集中的所有节目名
# labels_names: 所有类型名
# items_user_saw: 用户user看过的节目def contentBased(user_profile, items_profiles, items_names, labels_names, items_user_saw):# 对于用户user的推荐节目集为 recommend_items = [[节目名, 该节目画像与该用户画像的相似度], ...]recommend_items = []for i in range(len(items_names)):# 从备选推荐节目集中的选择用户user没有看过的节目if items_names[i] not in items_user_saw:recommend_items.append([items_names[i], calCosDistance(user_profile, items_profiles[items_names[i]], labels_names)])# 将推荐节目集按相似度降序排列recommend_items.sort(key=lambda item: item[1], reverse=True)return recommend_items# 输出推荐给该用户的节目列表
# max_num:最多输出的推荐节目数
def printRecommendedItems(recommend_items_sorted, max_num):count = 0for item, degree in recommend_items_sorted:print("节目名:%s, 推荐指数:%f" % (item, degree))count += 1if count == max_num:break# 主程序
if __name__ == '__main__':all_users_names = ['A', 'B', 'C']all_labels = ['教育', '戏曲', '悬疑', '科幻', '惊悚', '动作', '资讯', '武侠', '剧情', '警匪', '生活', '军事', '言情', '体育', '冒险', '纪实','少儿教育', '少儿', '综艺', '古装', '搞笑', '广告']labels_num = len(all_labels)df1 = pd.read_excel("D:\Recommender Systems\所有用户对其看过的节目的评分矩阵.xlsx")(m1, n1) = df1.shape# 所有用户对其看过的节目的评分矩阵# data_array1 = [[0.1804 0.042 0.11  0.07  0.19  0.56  0.14  0.3  0.32 0, ...], [...]]data_array1 = np.array(df1.iloc[:m1 + 1, 1:])# 按照"所有用户对其看过的节目的评分矩阵"的列序排列的所有用户观看过的节目名称items_users_saw_names1 = df1.columns[1:].tolist()df2 = pd.read_excel("D:\Recommender Systems\所有用户看过的节目及所属类型的01矩阵.xlsx")(m2, n2) = df2.shapedata_array2 = np.array(df2.iloc[:m2 + 1, 1:])# 按照"所有用户看过的节目及所属类型的01矩阵"的列序排列的所有用户观看过的节目名称items_users_saw_names2 = np.array(df2.iloc[:m2 + 1, 0]).tolist()# 为用户看过的节目建立节目画像items_users_saw_profiles = createItemsProfiles(data_array2, all_labels, items_users_saw_names2)# 建立用户画像users_profiles和用户看过的节目集items_users_saw(users_profiles, items_users_saw) = createUsersProfiles(data_array1, all_users_names, items_users_saw_names1, all_labels, items_users_saw_profiles)df3 = pd.read_excel("D:\Recommender Systems\备选推荐节目集及所属类型01矩阵.xlsx")(m3, n3) = df3.shapedata_array3 = np.array(df3.iloc[:m3 + 1, 1:])# 按照"备选推荐节目集及所属类型01矩阵"的列序排列的所有用户观看过的节目名称items_to_be_recommended_names = np.array(df3.iloc[:m3 + 1, 0]).tolist()# 为备选推荐节目集建立节目画像items_to_be_recommended_profiles = createItemsProfiles(data_array3, all_labels, items_to_be_recommended_names)for user in all_users_names:print("对于用户 %s 的推荐节目如下:" % user)recommend_items = contentBased(users_profiles[user], items_to_be_recommended_profiles, items_to_be_recommended_names, all_labels, items_users_saw[user])printRecommendedItems(recommend_items, 3)print()

Result:

b. 基于用户的协同过滤算法UserCF的实现:

1. 准备好输入数据

从上面的CB已经得到“备选推荐节目集及所属类型01矩阵.xlsx"和”所有用户对其看过的节目的评分矩阵.xlsx“

2. 挖掘用户的邻居,得出推荐集

找出每个用户的k个邻居,结合所有k个邻居看过的节目对该用户进行节目的推荐。注意被推荐的节目应该不包含该用户看过的节目,也不包含不在备选推荐节目集中的节目。(因为备选推荐节目集是商家盈利的节目集,也就是点播收费之类的。而用户观看的节目集包括免费的卫视直播节目以及付费点播的节目,也就是与备选推荐节目集有交集)

Source Code:
UserCF.py

# 代码说明:
# 基于用户的协同过滤算法的具体实现import math
import numpy as np
import pandas as pd# 借助pearson相关系数进行修正后的余弦相似度计算公式,计算两个用户之间的相似度
# 记  sim(user1, user2) = sigma_xy /sqrt(sigma_x * sigma_y)
# user1和user2都表示为[[节目名称,隐性评分], [节目名称,隐性评分]],如user1 = [['节目一', 3.2], ['节目四', 0.2], ['节目八', 6.5], ...]def calCosDistByPearson(user1, user2):x = 0.0y = 0.0sigma_xy = 0.0sigma_x = 0.0sigma_y = 0.0for item in user1:x += item[1]# user1对其看过的所有节目的平均评分average_x = x / len(user1)for item in user2:y += item[1]# user2对其看过的所有节目的平均评分average_y = y / len(user2)for item1 in user1:for item2 in user2:if item1[0] == item2[0]:  # 对user1和user2都共同看过的节目才考虑进去sigma_xy += (item1[1] - average_x) * (item2[1] - average_y)sigma_x += (item1[1] - average_x) * (item1[1] - average_x)sigma_y += (item2[1] - average_y) * (item2[1] - average_y)if sigma_x == 0.0 or sigma_y == 0.0:  # 若分母为0,相似度为0return 0return sigma_xy/math.sqrt(sigma_x * sigma_y)# 创建所有用户的观看信息(包含隐性评分信息),“从用户到节目”
# 格式例子:users_to_items = {用户一:[['节目一', 3.2], ['节目四', 0.2], ['节目八', 6.5]], 用户二: ... }
def createUsersDict(df):(m, n) = df.shapedata_array = np.array(df.iloc[:m + 1, 1:])users_names = np.array(df.iloc[:m + 1, 0]).tolist()items_names = np.array(df.columns)[1:]users_to_items = {}for i in range(len(users_names)):user_and_scores_list = []for j in range(len(items_names)):if data_array[i][j] > 0:user_and_scores_list.append([items_names[j], data_array[i][j]])users_to_items[users_names[i]] = user_and_scores_listreturn users_to_items# 创建所有节目被哪些用户观看的字典,也就是创建“从节目到用户”的倒排表items_and_users
# items_to_users = {节目一: [用户一, 用户三], 节目二: ... }
def createItemsDict(df):(m, n) = df.shapedata_array = np.array(df.iloc[:m + 1, 1:])users_names = np.array(df.iloc[:m + 1, 0]).tolist()items_names = np.array(df.columns)[1:]items_to_users = {}for i in range(len(items_names)):users_list = []for j in range(len(users_names)):if data_array[j][i] > 0:users_list.append(users_names[j])items_to_users[items_names[i]] = users_listreturn items_to_users# 找出与用户user_name相关的所有用户(即邻居)并依照相似度排序
# neighbors_distance = [[用户名, 相似度大小], [...], ...] = [['用户四', 0.78],[...], ...]
def findSimilarUsers(users_dict, items_dict, user_name):neighbors = []   # neighbors表示与该用户看过相同节目的所有用户for items in users_dict[user_name]:for neighbor in items_dict[items[0]]:if neighbor != user_name and neighbor not in neighbors:neighbors.append(neighbor)# 计算该用户与其所有邻居的相似度并降序排序neighbors_distance = []for neighbor in neighbors:distance = calCosDistByPearson(users_dict[user_name], users_dict[neighbor])neighbors_distance.append([neighbor, distance])neighbors_distance.sort(key=lambda item: item[1], reverse=True)return neighbors_distance# 基于用户的协同过滤算法
# K为邻居个数,是一个重要参数,参数调优时使用
def userCF(user_name, users_dict, items_dict, K, all_items_names_to_be_recommend):# recommend_items = {节目名:某个看过该节目的该用户user_name的邻居与该用户的相似度, ...}recommend_items = {}# 将上面的recommend_items转换成列表形式并排序为recommend_items_sorted = [[节目一, 该用户对节目一的感兴趣程度],[...], ...]recommend_items_sorted = []# 用户user_name看过的节目items_user_saw = []for item in users_dict[user_name]:items_user_saw.append(item[0])# 找出与该用户相似度最大的K个用户(邻居)similar_users = findSimilarUsers(users_dict, items_dict, user_name)if len(similar_users) < K:k_similar_user = similar_userselse:k_similar_user = similar_users[:K]# 得出对该用户的推荐节目集for user in k_similar_user:for item in users_dict[user[0]]:# 该用户user_name没有看过的节目才添加进来,才可以推荐给该用户if item[0] not in items_user_saw:# 而且该节目必须是在备选推荐节目集中if item[0] in all_items_names_to_be_recommend:if item[0] not in recommend_items:# recommend_items是一个字典。第一次迭代中,表示将第一个邻居用户与该用户的相似度加到节目名上,后续迭代如果有其他邻居用户也看过该节目,# 也将其与该用户的相似度加到节目名上,迭代的结果就是该用户对该节目的感兴趣程度recommend_items[item[0]] = user[1]else:# 如果某个节目有k个邻居用户看过,则将这k个邻居用户与该用户的相似度相加,得到该用户对某个节目的感兴趣程度recommend_items[item[0]] += user[1]for key in recommend_items:recommend_items_sorted.append([key, recommend_items[key]])# 对推荐节目集按用户感兴趣程度降序排序recommend_items_sorted.sort(key=lambda item: item[1], reverse=True)return recommend_items_sorted# 输出推荐给该用户的节目列表
# max_num:最多输出的推荐节目数
def printRecommendItems(recommend_items_sorted, max_num):count = 0for item, degree in recommend_items_sorted:print("节目名:%s, 推荐指数:%f" % (item, degree))count += 1if count == max_num:break# 主程序
if __name__ == '__main__':all_users_names = ['A', 'B', 'C']df1 = pd.read_excel("D:\Recommender Systems\备选推荐节目集及所属类型01矩阵.xlsx")(m1, n1) = df1.shape# 按照"备选推荐节目集及所属类型01矩阵"的列序排列的所有用户观看过的节目名称items_to_be_recommended_names = np.array(df1.iloc[:m1 + 1, 0]).tolist()df2 = pd.read_excel("D:\Recommender Systems\所有用户对其看过的节目的评分矩阵.xlsx")# users_dict = {用户一:[['节目一', 3.2], ['节目四', 0.2], ['节目八', 6.5]], 用户二: ... }users_dict = createUsersDict(df2)# items_dict = {节目一: [用户一, 用户三], 节目二: [...], ... }items_dict = createItemsDict(df2)for user in all_users_names:print("对于用户 %s 的推荐节目如下:" % user)recommend_items = userCF(user, users_dict, items_dict, 2, items_to_be_recommended_names)printRecommendItems(recommend_items, 3)print()

Result:

PS: 用户相似度计算产生负值为正常现象

####c. 实现混合推荐
将CB和userCF的两个推荐集按一定比例混合

Source Code:
CB_Mixture_userCF.py

from recommender_system.CB import *
from recommender_system.UserCF import *# 输出推荐给该用户的节目列表
# max_num:最多输出的推荐节目数
def printRecommendItems(recommend_items_sorted, max_num):count = 0for item, degree in recommend_items_sorted:print("节目名:%s, 推荐指数:%f" % (item, degree))count += 1if count == max_num:breakif __name__ == '__main__':all_users_names = ['A', 'B', 'C']all_labels = ['教育', '戏曲', '悬疑', '科幻', '惊悚', '动作', '资讯', '武侠', '剧情', '警匪', '生活', '军事', '言情', '体育', '冒险', '纪实','少儿教育', '少儿', '综艺', '古装', '搞笑', '广告']labels_num = len(all_labels)df1 = pd.read_excel("D:\Recommender Systems\所有用户对其看过的节目的评分矩阵.xlsx")(m1, n1) = df1.shape# 所有用户对其看过的节目的评分矩阵# data_array1 = [[0.1804 0.042 0.11  0.07  0.19  0.56  0.14  0.3  0.32 0, ...], [...]]data_array1 = np.array(df1.iloc[:m1 + 1, 1:])# 按照"所有用户对其看过的节目的评分矩阵"的列序排列的所有用户观看过的节目名称items_users_saw_names1 = df1.columns[1:].tolist()# users_dict = {用户一:[['节目一', 3.2], ['节目四', 0.2], ['节目八', 6.5]], 用户二: ... }users_dict = createUsersDict(df1)# items_dict = {节目一: [用户一, 用户三], 节目二: [...], ... }items_dict = createItemsDict(df1)df2 = pd.read_excel("D:\Recommender Systems\所有用户看过的节目及所属类型的01矩阵.xlsx")(m2, n2) = df2.shapedata_array2 = np.array(df2.iloc[:m2 + 1, 1:])# 按照"所有用户看过的节目及所属类型的01矩阵"的列序排列的所有用户观看过的节目名称items_users_saw_names2 = np.array(df2.iloc[:m2 + 1, 0]).tolist()# 为用户看过的节目建立节目画像items_users_saw_profiles = createItemsProfiles(data_array2, all_labels, items_users_saw_names2)# 建立用户画像users_profiles和用户看过的节目集items_users_saw(users_profiles, items_users_saw) = createUsersProfiles(data_array1, all_users_names, items_users_saw_names1,all_labels, items_users_saw_profiles)df3 = pd.read_excel("D:\Recommender Systems\备选推荐节目集及所属类型01矩阵.xlsx")(m3, n3) = df3.shapedata_array3 = np.array(df3.iloc[:m3 + 1, 1:])# 按照"备选推荐节目集及所属类型01矩阵"的列序排列的所有用户观看过的节目名称items_to_be_recommended_names = np.array(df3.iloc[:m3 + 1, 0]).tolist()# 为备选推荐节目集建立节目画像items_to_be_recommended_profiles = createItemsProfiles(data_array3, all_labels, items_to_be_recommended_names)# 两种推荐算法后融合,也就是将两种推荐算法对某个用户分别产生的两个推荐节目集按不同比例混合,得出最后的对该用户的推荐结果# 对于每个用户推荐topN个节目,在两种推荐算法产生的推荐集中分别选取比例为w1和w2的推荐结果,CB占w1, userCF占w2# w1 + w2 = 1 且 w1 * topN + w2 * topN = topNtopN = 5w1 = 0.7w2 = 0.3# 从CB的推荐集中选出前topW1项topW1 = int(w1 * topN)# 从userCF的推荐集中选出前topW2项topW2 = topN-topW1for user in all_users_names:# 对于用户user的最终混合推荐节目集recommend_items = []# CB# recommend_items1 =  [[节目名, 该节目与该用户user画像的相似度], ...]recommend_items1 = contentBased(users_profiles[user], items_to_be_recommended_profiles, items_to_be_recommended_names, all_labels, items_users_saw[user])len1 = len(recommend_items1)if len1 <= topW1:recommend_items = recommend_items + recommend_items1else:recommend_items = recommend_items + recommend_items1[:topW1]# userCF# recommend_item2 = [[节目名, 该用户user对该节目的感兴趣程度],...]recommend_items2 = userCF(user, users_dict, items_dict, 2, items_to_be_recommended_names)len2 = len(recommend_items2)if len2 <= topW2:recommend_items = recommend_items + recommend_items2else:recommend_items = recommend_items + recommend_items2[:topW2]# 将推荐结果按推荐指数降序排序recommend_items.sort(key=lambda item: item[1], reverse=True)print("对于用户 %s 的推荐节目如下" % user)printRecommendItems(recommend_items, 5)print()

Result:


5. 相关说明

a. 利用改进的皮尔逊相关系数计算用户之间的相似度

Pearson相关系数用来衡量两个数据集合之间的相似性。Pearson相关系数的一个优点是可以避免评分等级膨胀(grade inflation)的问题,也就是说有的用户可能倾向于对所有的电影都给比较高的分数,而有的用户则会比较苛刻,给分都比较低。对于这种情况,Pearson相关系数可以处理。计算用户x与用户y之间的相似度的公式如上面所示。

其中x,y表示具体的用户,Sxy表示用户x与用户y看过的节目的交集,rxs为用户x对于节目s的评分,-rx为用户x对于其所看过的所有节目的平均评分。

PS: 计算用户x和y之间的相似度时并不是去拿原始的评分向量去计算,而是只关注他们所看过的节目的交集Sxy。这是因为一个用户只对很少的物品有过评分,这样用户评分向量是个高度稀疏的向量,采用Pearson相关系数计算两个用户的相似度时很不准。

b. 基于用户之间的相似度得到用户对于节目的感兴趣程度

得到用户之间的兴趣相似度后,userCF算法会给用户推荐和他兴趣最相似的K个用户喜欢的物品。上面的公式度量了userCF算法中用户u对物品i的感兴趣程度。其中,S(u,K)包含和用户u兴趣最接近的K个用户,N(i)是对物品i有过行为的用户集合,w是用户u和用户v的兴趣相似度,r是用户v对物品i的兴趣,这里令所有的r=1.



Reference Materials :

1. [推荐算法]基于用户的协同过滤算法
2. 基于用户的协同过滤算法原理分析及代码实现
3. python 实现协同过滤推荐算法
4. 一个简单的基于内容的推荐算法]
5. 10 Minutes to pandas
6. 喻玲. 面向家庭用户的互联网电视资源推荐模型研究[D]
7. 付小飞. 基于用户画像的移动广告推荐技术的研究与应用[Z]
8. 孙亮. 一种基于云计算平台的网络电视混合推荐方法的研究[D]
9. 杨武、唐瑞、卢玲. 基于内容的推荐与协同过滤融合的新闻推荐方法[Z]
10. 项亮.推荐系统实践


相关数据&代码获取:

其中代码包名为”recommender_system“,表格数据包名为”Recommender Systems“,将表格数据包解压到电脑D盘即可运行上述代码.

该实例中所用到的表格
代码包

没有积分的小伙伴,我已将项目开源(代码和数据包),可以直接获取,欢迎star、fork:
项目地址

《网络电视节目推荐系统----基于用户协同过滤与基于内容的推荐算法的后融合》相关推荐

  1. ComeFuture英伽学院——2020年 全国大学生英语竞赛【C类初赛真题解析】(持续更新)

    视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...

  2. ComeFuture英伽学院——2019年 全国大学生英语竞赛【C类初赛真题解析】大小作文——详细解析

    视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...

  3. 信息学奥赛真题解析(玩具谜题)

    玩具谜题(2016年信息学奥赛提高组真题) 题目描述 小南有一套可爱的玩具小人, 它们各有不同的职业.有一天, 这些玩具小人把小南的眼镜藏了起来.小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的 ...

  4. 信息学奥赛之初赛 第1轮 讲解(01-08课)

    信息学奥赛之初赛讲解 01 计算机概述 系统基本结构 信息学奥赛之初赛讲解 01 计算机概述 系统基本结构_哔哩哔哩_bilibili 信息学奥赛之初赛讲解 02 软件系统 计算机语言 进制转换 信息 ...

  5. 信息学奥赛一本通习题答案(五)

    最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...

  6. 信息学奥赛一本通习题答案(三)

    最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...

  7. 信息学奥赛一本通 提高篇 第六部分 数学基础 相关的真题

    第1章   快速幂 1875:[13NOIP提高组]转圈游戏 信息学奥赛一本通(C++版)在线评测系统 第2 章  素数 第 3 章  约数 第 4 章  同余问题 第 5 章  矩阵乘法 第 6 章 ...

  8. 信息学奥赛一本通题目代码(非题库)

    为了完善自己学c++,很多人都去读相关文献,就比如<信息学奥赛一本通>,可又对题目无从下手,从今天开始,我将把书上的题目一 一的解析下来,可以做参考,如果有错,可以告诉我,将在下次解析里重 ...

  9. 信息学奥赛一本通(C++版) 刷题 记录

    总目录详见:https://blog.csdn.net/mrcrack/article/details/86501716 信息学奥赛一本通(C++版) 刷题 记录 http://ybt.ssoier. ...

  10. 最近公共祖先三种算法详解 + 模板题 建议新手收藏 例题: 信息学奥赛一本通 祖孙询问 距离

    首先什么是最近公共祖先?? 如图:红色节点的祖先为红色的1, 2, 3. 绿色节点的祖先为绿色的1, 2, 3, 4. 他们的最近公共祖先即他们最先相交的地方,如在上图中黄色的点就是他们的最近公共祖先 ...

最新文章

  1. Android反编工具的使用-Android Killer
  2. mysql cmake 参数详解
  3. Funambol DM 安装过程
  4. 用计算机解决问题 评课稿,总结反思:二年级数学lbrack;解决问题rsqb;评课稿
  5. EOS1.1版本新特性介绍
  6. 开课吧Java课堂:什么是线程优先级?
  7. 你还会不会选择我(刘墉 )
  8. SPSS说明附学习方法
  9. java 最新Xss攻击与防护(全方位360°详解)
  10. 分类模型中准确率、敏感度、特异度的理解
  11. 号称“不限速“的阿里网盘,官宣要停止了,寿命仅仅1年
  12. 使用U盘安装windows系统时提示找不到任何设备驱动程序
  13. C#之Base64编码解码
  14. coreldraw基本工具(2)
  15. ubuntu rsync 命令拷贝,显示进度
  16. XGBoost线性回归工控数据分析实践案例(原生篇)
  17. 本地mysql设置成DMZ主机远程访问的方法
  18. 互联网金融开发软件公司值得选择的有哪些?
  19. 单目运算符、双目运算符、三目运算符
  20. 使用静默方式安装Oracle数据库软件

热门文章

  1. 写作小技能:Media Platform 类型定位
  2. 【linux系统配置 3】ubuntu1604-搜狗拼音安装指南
  3. 为何电动车电池充不了电或充不满电?与充电桩有关系吗?
  4. apache poi word表格样式(表格固定间隔,表格字体颜色)、段落样式(段落字体颜色,格式)
  5. xp如何添加桌面计算机回收站,详解桌面回收站图标在XP电脑中操作删除的详细步骤...
  6. 科普:关于屏幕的各种性能参数
  7. Blender 新手入门练习1
  8. java读取properties文件,通过数据池(BasicDataSource)连接mysql数据库
  9. 网站测试基本方法-32. 接口测试
  10. ShaderWeaver使用教程-—第五节—水中倒影