基于关联规则算法+协同过滤算法的电影推荐系统

  • 一、前言
    • 1.数据集介绍
    • 2.方法概述
    • 3.运行环境
  • 二、数据准备与预处理
    • 1.数据熟悉
    • 2.数据读取
    • 3.数据预处理
      • 3.1 无用属性删除
      • 3.2 缺失属性处理
      • 3.3 数据去重
      • 3.4 类型转换
      • 3.5 数据合并
  • 三、特征工程
  • 四、算法建模
    • 1.关联规则算法
      • 1.1 算法简介
      • 1.2 Apriori算法建模
    • 2.协同过滤算法
      • 2.1算法简介
      • 2.2 Collaborative filtering算法建模
  • 五、参考致谢

一、前言

1.数据集介绍

使用电影推荐系统常用的英文数据集The Movies Dataset, 该数据集包含了2017年7月之前公映的45,000条电影记录,每条记录包含了该影片的电影名、语种、演员、导演组、剧情关键词、预算、票房、平均评分、评分人数等重要信息。其中评分记录为270,000名用户对上述所有电影给出的2600万个评分,评分区间为1-5。
需要数据集的留下邮箱,看到后会第一时间发送的

2.方法概述

在介绍如何利用数据挖掘算法中的Apriori(关联规则)/collaborative filtering(协同过滤)算法来实现一个电影推荐系统。我们将通过加载数据、数据预处理、生成频繁项集和关联规则这几个步骤,最终通过关联规则生成电影推荐的列表。
任务目标:根据用户的观影记录或者喜好的电影列表,推荐相应的电影列表或者单部电影。例如,用户喜欢《Batman Returns》这部电影,要求推荐相应的电影列表

3.运行环境

python=3.6.5
numpy=1.18.1
matplotlib=3.3.
mlxtend=0.17.2

二、数据准备与预处理

1.数据熟悉

movies_metadata.csv: 电影元数据文件,包含了45,000部电影的每部电影的各个属性信息,一共包含24个字段,特征字段说明如下:

1)文件movies_metadata.csv包含24个字段,具体信息如下:
序号 字段名 数据类型 字段描述
1 adult String 成人
2 belongs_to_collection String 归属
3 budget Integer 预算
4 genres String 流派
5 homepage String 主页
6 id Integer 编码
7 imdb_id String imdb编码
8 original_language String 初始语言
9 original_title String 初始标题
10 overview String 概述
11 popularity String 声望
12 poster_path String 宣传路径
13 production_companies String 发行公司
14 production_countries String 发行国家
15 release_date String 发布时间
16 revenue Integer 收入
17 runtime Integer 运行
18 spoken_languages String 语言
19 status String 状态
20 tagline String 标题
21 title String 题目
22 video String 视频
23 vote_average Integer 平均投票
24 vote_count Integer 投票数

ratings_small.csv: 评分数据文件,包含700多个用户对9000多部电影的评分数据,一共包含以下4个字段:

2)文件ratings_small.csv包含4个字段,具体信息如下:
序号 字段名 数据类型 字段描述
1 userId Integer 用户编码
2 movieId Integer imdb编码
3 rating Integer 评级
4 timestamp Integer 时间戳

2.数据读取

导入需要的包

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

读取电影及评分数据

movies_df = pd.read_csv('movies_metadata.csv')
ratings_df = pd.read_csv('ratings_small.csv')

获得movies_df的尺寸

movies_df.shape

(45466, 24)

获取movies_df的前五行数据

movies_df.head()


获得ratings_df的尺寸

ratings_df.shape

(100004, 4)

获取ratings_df的前五行数据

ratings_df.head(5)

3.数据预处理

清洗movies_metadata.csv和ratings_small.csv的数据:缺失值处理,数据去重。movies_metadata.csv缺少评分数据,ratings_small.csv缺少电影名数据,因此我们需要合并两张表的数据生成所需的DataFrame

movies_df = movies_df[['title', 'id']] #截取title和id这两列的数据
movies_df.dtypes #查看每列的数据类型

3.1 无用属性删除

删掉timestamp列的数据

ratings_df.drop(['timestamp'], axis=1, inplace=True)

3.2 缺失属性处理


np.where(pd.to_numeric(movies_df['id'], errors='coerce').isna())

结果:(array([19730, 29503, 35587], dtype=int64),)

pd.to_numeric将id列的数据由字符串转为数值类型,不能转换的数据设置为NaN。
np.where返回满足()内条件的数据所在的位置。返回缺失值的位置,其中isna() 对于NaN返回True,否则返回False

movies_df.iloc[[19730, 29503, 35587]]


结果赋值给id列数据,删除id非法的行

movies_df['id'] = pd.to_numeric(movies_df['id'], errors='coerce')
movies_df.drop(np.where(movies_df['id'].isna())[0], inplace=True)
movies_df.shape

(45463, 2)

3.3 数据去重

movies_df.duplicated(['id', 'title']).sum() #返回重复项总数

30

movies_df.drop_duplicates(['id'], inplace=True) #数据去重
ratings_df.duplicated(['userId', 'movieId']).sum()

0

3.4 类型转换

对于movies_df的id列进行类型转换

movies_df['id'] = movies_df['id'].astype(np.int64)
movies_df.dtypes

3.5 数据合并

将左边的dataframe的movieId和右边的Dataframe的id进行对齐合并成新的Dataframe

ratings_df = pd.merge(ratings_df, movies_df, left_on='movieId', right_on='id')

去掉多余的id列

ratings_df.drop(['id'], axis=1, inplace=True)
ratings_df.head(10)

三、特征工程

有评分记录的电影的个数

len(ratings_df['title'].unique())

2794

统计每部电影的评分记录的总个数

ratings_count = ratings_df.groupby(['title'])['rating'].count().reset_index()

列的字段重命名

ratings_count = ratings_count.rename(columns={'rating':'totalRatings'})
ratings_count.head(5)


添加totalRatings字段

ratings_total = pd.merge(ratings_df,ratings_count, on='title', how='left')
ratings_total.head(5)


数据分析以截取合适的数据,获得关于totalRatings字段的统计信息

ratings_count['totalRatings'].describe()

ratings_count.hist()


查看分位点

ratings_count['totalRatings'].quantile(np.arange(.6,1,0.01))

由上述数据分析可知,21%的电影的评分记录个数超过20个,选取总评个数超过阈值的电影评分数据

votes_count_threshold = 20
ratings_top = ratings_total.query('totalRatings > @votes_count_threshold')
ratings_top.head(10)


检查有无缺失值

ratings_top.isna().sum()


检查是否有重复数据

ratings_top.duplicated(['userId','title']).sum()

140

只保留每个用户对每个电影的一条评分记录

ratings_top = ratings_top.drop_duplicates(['userId','title'])
ratings_top.duplicated(['userId','title']).sum()

调整表样式

df_for_apriori = ratings_top.pivot(index='userId',columns='title',values='rating')
df_for_apriori.head(5)

缺失值填充0,有效评分规则,1表示有效,0表示无效,并且对每个数据应用上述规则

df_for_apriori = df_for_apriori.fillna(0)
def encode_units(x):  if x <= 0:return 0if x > 0:return 1
df_for_apriori = df_for_apriori.applymap(encode_units)
df_for_apriori.head(5)

四、算法建模

1.关联规则算法

1.1 算法简介

例子:啤酒与尿布:
沃尔玛超市在分析销售记录时,发现了啤酒与尿布经常一起被购买,于是他们调整了货架将两者放在了一起,结果真的提升了啤酒的销量。 原因解释: 爸爸在给宝宝买尿布的时候,会顺便给自己买点啤酒?

概述:
Apriori算法是一种最有影响力的挖掘布尔关联规则的频繁项集的算法,其命名Apriori源于算法使用了频繁项集性质的先验(Prior)知识。接下来我们将以超市订单的例子理解关联分析相关的重要概念: Support(支持度)、Confidence(置信度)、Lift(提升度)。

Support(支持度):指某事件出现的概率,在本例中即指某个商品组合出现的次数占总次数的比例。例:Support(‘Bread’) = 4/5 = 0.8 Support(‘Milk’) = 4/5 = 0.8
Support(‘Bread+Milk’) = 3/5 = 0.6
Confidence(置信度):本质上是个条件概率,即当购买了商品A的前提下,购买商品B的概率。例:Confidence(‘Bread’—> ‘Milk’) = Support(‘Bread+Milk’)/ Support(‘Bread’) = 0.6/0.8 = 0.75
Lift(提升度): 指商品A的出现,对商品B的出现的概率的提升程度。
Lift(A->B) = Confidence(A, B) / Support(B)例:Lift(‘Bread’—> ‘Milk’) = 0.75/0.8 = 0.9375

对于Lift(提升度)有三种情况:
Lift(A->B)>1: 代表A对B的出现概率有提升。
Lift(A->B)=1: 代表A对B的出现概率没有提升,也没有下降。
Lift(A->B)<1: 代表A对B的出现概率有下降效果。

原理:
该算法挖掘关联规则的过程,即是**查找频繁项集(frequent itemset)**的过程:
频繁项集:支持度大于等于最小支持度(Min Support)阈值的项集。
非频繁集:支持度小于最小支持度的项集。

流程:
K = 1, 计算K项集的支持度;
筛选掉小于最小支持度的项集;
如果项集为空,则对应K-1项集的结果为最终结果。否则K = K+1重复2-3步

1.2 Apriori算法建模

导入关联规则算法相关包

from mlxtend.frequent_patterns import apriori
from mlxtend.frequent_patterns import association_rules

生成符合条件的频繁项集,并降序排列的频繁项集

frequent_itemsets = apriori(df_for_apriori, min_support=0.10, use_colnames=True)
frequent_itemsets.sort_values('support', ascending=False)

生成关联规则,只保留lift>1的部分

rules = association_rules(frequent_itemsets, metric="lift", min_threshold=1)
rules.sort_values('lift', ascending=False)


输出结果进行观察

all_antecedents = [list(x) for x in rules['antecedents'].values]
desired_indices = [i for i in range(len(all_antecedents)) if len(all_antecedents[i])==1 and all_antecedents[i][0]=='Batman Returns']
apriori_recommendations=rules.iloc[desired_indices,].sort_values(by=['lift'],ascending=False)
apriori_recommendations.head()

apriori_recommendations_list = [list(x) for x in apriori_recommendations['consequents'].values]
print("Apriori Recommendations for movie: Batman Returns\n")
for i in range(5):print("{0}: {1} with lift of {2}".format(i+1,apriori_recommendations_list[i],apriori_recommendations.iloc[i,6]))

结果说明:给出用户观看过的电影记录:《Batman Returns》,我们选出了lift(支持度)降序排列的前五条关联规则,给出了对应的推荐列表。

apriori_single_recommendations = apriori_recommendations.iloc[[x for x in range(len(apriori_recommendations_list)) if len(apriori_recommendations_list[x])==1],]
apriori_single_recommendations_list = [list(x) for x in apriori_single_recommendations['consequents'].values]
print("Apriori single-movie Recommendations for movie: Batman Returns\n")
for i in range(5):print("{0}: {1}, with lift of {2}".format(i+1,apriori_single_recommendations_list[i][0],apriori_single_recommendations.iloc[i,6]))

结果说明:我们约束consequents(后件)的长度为1,选出lift降序排列的前五个关联规则(关联规则格式为前件——>后件)。对于用户观看的电影记录《Batman Returns》,即antecedents(前件),我们根据规则按照推荐程度降序给出了单部电影推荐结果。

2.协同过滤算法

2.1算法简介

协同过滤一般是在海量的用户中发现一小部分和你品味比较相近的,在协同过滤中,这些用户称为邻居,然后根据他们喜欢的东西组织成一个排序的目录来推荐给你。问题的重点就是怎样去寻找和你比较相似的用户,怎么将那些邻居的喜好组织成一个排序的目录给用户。
文章主要介绍的是基于user的协同过滤,理论上是要找到找到和需要被推荐的用户最相似的用户,这里我们用邻居表示与你最为相似的用户。相似用户的计算可能有很多,在实际中我们会给出一个数字K表示和你最为相似的用户。在计算相似度的时候,理论上要计算被推荐的用户与所有用户的相似度,但是当数据量比较大的时候,这样做是很费时间的 ,数据集中可能有很多用户和需要被推荐的用户是没有关系的,在计算是完全是没有必要的,所以需要物品到用户的反查表,也就是没一件物品对应的用户信息,有了这个表,就可以过滤掉很多和你没有关系的用户,减少计算量。

2.2 Collaborative filtering算法建模

读取读取ratings_small.csv数据用于建模

ratings_path = "ratings_small.csv"
ratings_df = pd.read_csv(ratings_path)
ratings_df.head(5)


始的movieId并非是从0或1开始的连续值,为了便于构建其user-item矩阵,我们将重新排列movie-id

movie_id = ratings_df['movieId'].drop_duplicates()
movie_id.head()
movie_id = pd.DataFrame(movie_id)
movie_id['movieid'] = range(len(movie_id))
print(len(movie_id))
movie_id.head()

更新movieId–>movieid

ratings_df = pd.merge(ratings_df,movie_id,on=['movieId'],how='left')
ratings_df = ratings_df[['userId','movieid','rating','timestamp']] #更新movieId-->movieid

用户物品统计

n_users = ratings_df.userId.nunique()
n_items = ratings_df.movieid.nunique()
print(n_users)
print(n_items)

671
9066

# 拆分数据集
from sklearn.model_selection import train_test_split
# 按照训练集70%,测试集30%的比例对数据进行拆分
train_data,test_data =train_test_split(ratings_df,test_size=0.3)
# 训练集 用户-物品 矩阵
user_item_matrix = np.zeros((n_users,n_items))
for line in train_data.itertuples():#print(line)user_item_matrix[line[1]-1,line[2]] = line[3]

构建用户相似矩阵 - 采用余弦距离

from sklearn.metrics.pairwise import pairwise_distances
# 相似度计算定义为余弦距离
user_similarity_m = pairwise_distances(user_item_matrix,metric='cosine')# 每个用户数据为一行,此处不需要再进行转置

相似矩阵

user_similarity_m[0:5,0:5].round(2)


现在我们只分析上三角,得到等分位数

user_similarity_m_triu = np.triu(user_similarity_m,k=1) # 取得上三角数据
user_sim_nonzero = np.round(user_similarity_m_triu[user_similarity_m_triu.nonzero()],3)
np.percentile(user_sim_nonzero,np.arange(0,101,10))
mean_user_rating = user_item_matrix.mean(axis=1)
rating_diff = (user_item_matrix - mean_user_rating[:,np.newaxis])

np.newaxis作用:为mean_user_rating增加一个维度,实现加减操作

以np.array([np.abs(item_similarity_m).sum(axis=1)]是为了可以使评分在1~ 5 之间,使1~5的标准化

user_precdiction = mean_user_rating[:,np.newaxis] + user_similarity_m.dot(rating_diff) / np.array([np.abs(user_similarity_m).sum(axis=1)]).T

只取数据集中有评分的数据集进行评估

from sklearn.metrics import mean_squared_error
from math import sqrt
prediction_flatten = user_precdiction[user_item_matrix.nonzero()]
user_item_matrix_flatten = user_item_matrix[user_item_matrix.nonzero()]
error_train = sqrt(mean_squared_error(prediction_flatten,user_item_matrix_flatten)) # 均方根误差计算
print('训练集预测均方根误差:',error_train)

test_data_matrix = np.zeros((n_users,n_items))
for line in test_data.itertuples():test_data_matrix[line[1]-1,line[2]-1]=line[3]

预测矩阵


rating_diff = (test_data_matrix - mean_user_rating[:,np.newaxis]) # np.newaxis作用:为mean_user_rating增加一个维度,实现加减操作
user_precdiction = mean_user_rating[:,np.newaxis] + user_similarity_m.dot(rating_diff) / np.array([np.abs(user_similarity_m).sum(axis=1)]).T

只取数据集中有评分的数据集进行评估

prediction_flatten = user_precdiction[user_item_matrix.nonzero()]
user_item_matrix_flatten = user_item_matrix[user_item_matrix.nonzero()]
error_test = sqrt(mean_squared_error(prediction_flatten,user_item_matrix_flatten)) # 均方根误差计算
print('测试集预测均方根误差:',error_test)

五、参考致谢

协同过滤算法: collaborative filtering.
关联规则算法: Apriori.

基于关联规则(Apriori)+协同过滤(collaborative filtering)实现电影推荐系统相关推荐

  1. 基于Mahout实现协同过滤推荐算法的电影推荐系统

    1 Mahout介绍 Apache Mahout 是 Apache Software Foundation(ASF) 旗下的一个开源项目,提供一些可扩展的机器学习领域经典算法的实现,旨在帮助开发人员更 ...

  2. Python实现基于用户的协同过滤推荐算法构建电影推荐系统

    说明:这是一个机器学习实战项目(附带数据+代码+文档+视频讲解),如需数据+代码+文档+视频讲解可以直接到文章最后获取. 1.项目背景 基于用户的协同过滤推荐(User-based CF)的原理假设: ...

  3. Python实现基于物品的协同过滤推荐算法构建电影推荐系统

    说明:这是一个机器学习实战项目(附带数据+代码+文档+视频讲解),如需数据+代码+文档+视频讲解可以直接到文章最后获取. 1.项目背景 基于物品的协同过滤推荐(Item-based CF)的假设原理为 ...

  4. 基于机器学习实现协同过滤推荐算法的电影推荐系统

    推荐算法在互联网行业的应用非常广泛,今日头条.美团点评等都有个性化推荐,推荐算法抽象来讲,是一种对于内容满意度的拟合函数,涉及到用户特征和内容特征,作为模型训练所需维度的两大来源,而点击率,页面停留时 ...

  5. 推荐系统:协同过滤collaborative filtering

    http://blog.csdn.net/pipisorry/article/details/51788955 (个性化)推荐系统构建三大方法:基于内容的推荐content-based,协同过滤col ...

  6. 协同过滤(collaborative filtering)

    Author: Summer; Email: huangmeihong11@sina.com Datawhale 协同过滤简介 协同过滤是推荐算法中最常用的算法之一,它根据user与item的交互,发 ...

  7. 机器学习实战(十三)推荐系统(协同过滤 Collaborative Filtering)

    目录 0. 前言 1. 相似度 1.1. 欧式距离(Euclidean metric) 1.2. 皮尔逊相关系数(Pearson correlation coefficient) 1.3. 余弦相似度 ...

  8. 基于物品的协同过滤推荐算法_《推荐系统实践》3.基于物品的协同过滤算法

    基于物品的协同过滤算法(item-based collaborative filtering,以下简称ItemCF)算法思想:给用户推荐那些和他们之前喜欢的物品相似的物品. 不过,ItemCF算法并不 ...

  9. python协同过滤电影推荐_python实现基于用户的协同过滤算法(CF)——以电影评价数据(ml-100k)为例...

    程序简介 项目以ml-100k电影评分数据集为输入,实现了基于用户的协同过滤算法,最后预测的MAE为0.84,因为经过优化,10万条评分数据运行时间不超过2分钟 协同过滤算法(CF)基于对用户历史行为 ...

最新文章

  1. Django+xadmin打造在线教育平台(十)
  2. python dict sorted 排序
  3. 牛人,多看看他们写的东西
  4. 09产品经理要明白的人性思维-营销推广篇
  5. Java-泛型-ArrayList
  6. 2017.3.31 棋盘制作 失败总结
  7. 回归本源:JavaScript 之中的值和引用
  8. electron 自定义标题栏_electron+vue制作桌面应用--自定义标题栏
  9. linear-gradient常用实现效果
  10. Javascript模块化编程 (附WebTrends的dcsMultiTrack方法浅述)
  11. 医疗系统流程软件测试用例,医疗管理系统案例测试用例.doc
  12. STM32F207时钟系统解析
  13. vi设计手册的编辑形式
  14. 计算机组装日记,求微机组装与维护实习日记?
  15. 基于SuperMap iDesktop制作天地图1--10级详细说明
  16. 2563: 阿狸和桃子的游戏 贪心
  17. Vuepress-theme-reco 构建静态网页错误:在格式错误时超出了最大调用堆栈大小
  18. 【T+】畅捷通T+选项设置界面没有“数据精度”调整项
  19. 正则系列2: re.search用法
  20. 数加加众包深耕AI第8年,苹果加码人工智能和机器学习

热门文章

  1. ultrascale和arm区别_ZYNQ UltraScale+ MPSoc FPGA初学笔记
  2. echarts南丁格尔玫瑰图每块单独设置颜色
  3. 微信小程序用户隐私保护指引设置怎么填?
  4. 【干货】数据集网站汇总!
  5. 数字图像处理:图像几何变换(Matlab实现几何变换+原理解析
  6. 微信小程序毕业设计 基于微信小程序在线电子书阅读系统开题报告
  7. 文件压缩算法详细分析(ZIP)及解压实例解释
  8. js中isNaN、Number.isNaN,isFinite、Number.isFinite的区别
  9. [SCOI2009]windy数
  10. grafana监控oceanbase-obagent部署