推荐系统——电影评分预测算法(基于用户的协同过滤、基于物品的协同过滤)
给定用户u和用户v,令N(u)表示用户u曾经有过正反馈的物品集合,令N(v)为用户v曾经有过正反馈的物品集合。给定物品i和物品j,令U(i)表示对物品i有过正反馈的用户集合,令U(j)为对物品j有过正反馈的用户集合。其中,r^ui表示预测评分,S(u,K)表示和用户u最相似的K个用户集合,N(i)表示对物品i有过评分的用户集合。其中,r^ui表示预测评分,S(i,K)表示和物品i最相似的K个物
一、协同过滤算法原理
协同过滤是一种常用的推荐系统方法,它根据用户或物品之间的相似度,为目标用户推荐他们可能感兴趣的物品。协同过滤有两种主要的类型:基于用户的协同过滤(UBCF)和基于物品的协同过滤(IBCF)
1、User-Based CF(基于用户的协同过滤)
UBCF是一种方法,它找到和目标用户兴趣相似的用户集合,并推荐这些相似用户喜欢的、目标用户没有接触过的物品。1例如,如果A和B都喜欢科幻电影和书籍,而A看过并喜欢一部新的科幻电影,那么UBCF就会把这部电影推荐给B
UBCF的核心是计算用户之间的相似度,通常使用皮尔逊相关系数或余弦相似度作为相似度度量。给定用户u和用户v,令N(u)表示用户u曾经有过正反馈的物品集合,令N(v)为用户v曾经有过正反馈的物品集合。2那么,皮尔逊相关系数可以定义为:
其中,rui表示用户u对物品i的评分,rˉu表示用户u对所有物品的平均评分。
根据用户之间的相似度,可以为目标用户预测他对某个物品的评分。预测公式如下:
其中,r^ui表示预测评分,S(u,K)表示和用户u最相似的K个用户集合,N(i)表示对物品i有过评分的用户集合。
2、Item-Based CF(基于物品的协同过滤)
IBCF是一种方法,它找到和目标用户喜欢的物品相似的物品,并推荐这些相似物品给目标用户。例如,如果A喜欢某系列的书籍和电影,而有一本新的奇幻小说和某系列书籍很相似,那么IBCF就会把这本小说推荐给A。
IBCF的核心是计算物品之间的相似度,通常也使用皮尔逊相关系数或余弦相似度作为相似度度量。给定物品i和物品j,令U(i)表示对物品i有过正反馈的用户集合,令U(j)为对物品j有过正反馈的用户集合。2那么,皮尔逊相关系数可以定义为:
其中,rui表示用户u对物品i的评分,rˉi表示物品i的平均评分。
根据物品之间的相似度,可以为目标用户预测他对某个物品的评分。预测公式如下:
其中,r^ui表示预测评分,S(i,K)表示和物品i最相似的K个物品集合,N(u)表示用户u有过评分的物品集合。
二、 数据集下载
MovieLens Latest Datasets Small
下载ml-latest-small.zip,数据量小,便于我们单机使用和运行
下载地址:https://files.grouplens.org/datasets/movielens/ml-latest-small.zip
目标:根据m1-latest-sma11/ratings.csv(用户-电影评分数据),分别实现User-Based CF和ltem-BasedCF,并进行电影评分的预测,然后为用户实现电影推荐
三、数据集加载
加载数据集中的rating.csv,并转化为用户-电影评分矩阵
import os
import pandas as pd
import numpy as np
DATA_PATH="./datasets/ml-latest-small/ratings.csv"
CACHE_DIR="./datasets/cache/"
def load_data(data_path):
'''
加载数据
:param data_path:数据集路径
:param cache_path:数据集缓存路径
:return 用户-物品评分矩阵
'''
#数据集缓存地址
cache_path=os.path.join(CACHE_DIR, "ratings_matrix_cache")
print("开始加载数据集...")
if os.path.exists(cache_path): #判断是否存在缓存文件
print("加载缓存中...")
ratings_matrix=pd.read_pickle(cache_path)
print("从缓存加载数据集完毕")
else:
print("加载新数据中...")
#设置要加载的数据字段的类型
dtype={"userId": np.int32, "movieId": np.int32, "rating": np.float32}
#加载数据,我们只用前三列数据,分别是用户ID、电影ID、以及用户对电影的对应评分
ratings=pd.read_csv(DATA_PATH, dtype=dtype, usecols=range(3))
#透视表,将电影ID转换为列名称,转换成为一个User-Movie的评分矩阵
ratings_matrix = pd.pivot_table(data=ratings, index=["userId"], columns=["movieId"], values="rating")
#存入缓存文件
ratings_matrix.to_pickle(cache_path)
print("数据集加载完毕")
return ratings_matrix
ratings_matrix=load_data(DATA_PATH)
四、相似度计算
计算用户或物品两两相似度
def compute_persion_similarity(ratings_matrix, based="user"):
'''
计算皮尔逊相关系数
:param ratings_matrix:用户物品评分矩阵
:param based: "user" or "item"
:return: 相似度矩阵
'''
user_similarity_cache_path=os.path.join(CACHE_DIR, "user_similarity_cache")
item_similarity_cache_path=os.path.join(CACHE_DIR, "item_similarity_cache")
#基于皮尔逊相关系数计算相似度
#用户相似度
if based=="user":
if os.path.exists(user_similarity_cache_path):
print("正从缓存加载用户相似度矩阵")
similarity=pd.read_pickle(user_similarity_cache_path)
else:
print("开始计算用户相似度矩阵")
similarity=ratings_matrix.T.corr()
similarity.to_pickle(user_similarity_cache_path)
elif based=="item":
if os.path.exists(item_similarity_cache_path):
print("正从缓存加载物品相似度矩阵")
similarity=pd.read_pickle(item_similarity_cache_path)
else:
print("开始计算物品相似度矩阵")
similarity=ratings_matrix.corr()
similarity.to_pickle(item_similarity_cache_path)
else:
raise Exception("Unhandled 'based' value: %s"%based)
print("相似度矩阵计算/加载完毕")
return similarity
user_similar=compute_persion_similarity(ratings_matrix, based="user")
item_similar=compute_persion_similarity(ratings_matrix, based="item")
五、User-Based CF 和Item-Based CF预测评分(基于用户的协同过滤和基于物品的协同过滤)
评分预测公式:
1、User-Based CF 算法
def predict_userBasedCF(uid, iid, ratings_matrix, user_similar):
'''
预测给定用户对给定物品的评分值
:param uid:用户id
:param iid:物品id
:param ratings_matrix:用户-物品评分矩阵
:param user_similar:用户两两相似度矩阵
:return:预测的评分值
'''
#1.找出uid用户的相似用户
similar_users=user_similar[uid].drop([uid]).dropna()
#相似用户筛选规则:正相关的用户
similar_users=similar_users.where(similar_users>0).dropna()
if similar_users.empty is True:
raise Exception("用户{}没有相似的用户".format(uid))
#2.从uid用户的相邻用户中筛选出对iid物品有评分记录的近邻用户
ids=set(ratings_matrix[iid].dropna().index)&set(similar_users.index)
finally_similar_users=similar_users.loc[list(ids)]
#3.结合uid用户与其近邻用户的相似度预测uid用户对iid用户的评分
sum_up=0 #评分预测部分的分子部分的值
sum_down=0 #评分预测部分的分母部分的值
for sim_uid, similarity in finally_similar_users.iteritems():
#近邻用户的评分数据
sim_user_rated_movies=ratings_matrix.loc[sim_uid].dropna()
#近邻用户对iid物品的评分
sim_user_rating_for_item=sim_user_rated_movies[iid]
#计算分子的值
sum_up+=similarity * sim_user_rating_for_item
#计算分母的值
sum_down+=similarity
#计算预测的评分并返回
predict_rating=sum_up/sum_down
print("预测出用户{0}对电影{1}的评分为:{2:.2f}".format(uid, iid, predict_rating))
return round(predict_rating, 2)
2、Item-Based CF 算法
def predict_itemBasedCF(uid, iid, ratings_matrix, item_similar):
'''
预测给定用户对给定物品的评分值
: param uid:用户ID
: par am iid:物品ID
: param ratings_matrix:用户-物品评分矩阵
: param item_similar:物品两两相似度矩阵
: return:预测的评分值
'''
print("开始预测用户{0}对电影{1}的评分...".format(uid, iid))
#1.找出iid物品的相似物品
similar_items = item_similar[iid]. drop([iid]).dropnh()#相似物品筛选规则:正相关的物品
similar_items = similar_items.where(simi1ar_items>0).dropna()
if similar_items.empty is True:
raise Exception("物品{}没有相似的物品".format(iid))
# 2.从iid物品的近邻相似物品中筛选出uid用户评分过的物品
ids = set(ratings_matrix.loc[uid].dropna().index)&set(similar_items.index)
finally_similar_items = similar_items.loc[list(ids)]
# 3.结合iid物品与其相似物品的相似度和uid用户对其相似物品的评分,预测uid对iid的评分sum_up = 0#评分预测公式的分子部分的值
sum_down = 0#评分预测公式的分母部分的值
for sim_iid, simi1arity in finally_similar_items.iteritems() :
#近邻物品的评分数据
sim_item_rated_movies = ratings_matrix[sim_iid].dropna()
# uid用户对相似物品物品的评分
sim_item_rating_from_user = sim_item_rated_movies[uid]
#计算分子的值
sum_up += sim1lar1ty * s1m_1tem_rating_from_user
#计算分母的值
sum_down += simi1arity
#计算预测的评分值并返回
predict_rating = sum_up/sum_down
print("预测出用户{0}对电影{1}的评分:{2:.2f}".format(uid, iid, predict_rating))
return round(predict_rating, 2)
3、预测全部评分
def _predict_a11(uid, item_ids, ratings_matrix, user_similar ):
'''
预测全部评分
: par am uid:用户id
: param item_ids:要预测的物品id列表
: par am ratings_matrix:用户-物品打分矩阵: par am user_similar :用户两两间的相似度: return:生成器,逐个返回预测评分
'''
#逐个预测
for iid in item_ids :
try:
rating = predict_userBasedCF(uid, iid,ratings_matrix, user_similar)
except Exception as e:
print(e)
else:
yield uid, iid, rating
def predict_all(uid, ratings_matrix, user_similar, filter_rule=None):
'''
预测全部评分,并可根据条件进行前置过滤
: par am uid:用户ID
: par am ratings_matrix:用户-物品打分矩阵: par am user_simi1ar :用户两两间的相似度
: param filter_rule:过滤规则,只能是四选一,否则将抛异常: "unhot" , "rated",[ "unhot" , "rated"], None
: return:生成器,逐个返回预测评分.
'''
if not filter_rule:
item_ids = ratings_matrix.columns
elif isinstance(filter_rule, str) and filter_rule == "unhot" :
'''过滤非热门电影'''
#统计每部电影的评分数
count = ratings_matrix.count()
#过滤出评分数高于10的电影,作为热门电影
item_ids = count.where(count>10).dropna().index
elif isinstance(filter_rule, str) and filter_rule == "rated":
'''过滤用户评分过的电影'''
#获取用户对所有电影的评分记录
user_ratings = ratings_matrix.loc[uid]
#评分范围是1-5,小于6的都是评分过的,除此以外的都是没有评分的_ = user_ratings<6
item_ids = _.where(_==False). dropna().index
elif isinstance(filter_rule, list) and set(filter_rule) == set(["unhot", "rated"]):
'''过滤非热门和用户已经评分过的电影'''
count = ratings_matrix.count()
ids1 = count. where(count >10).dropna().index
user_ratings = ratings_matrix.loc[uid]
_= user_ratings < 6
ids2 = _.where(_ == False).dropna() .index
#取二者交集
item_ids = set(ids1)&set(ids2)
else:
raise Exception("无效的过滤参数")
yield from _predict_a11(uid, item_ids, ratings_matrix, user_similar)
六、电影评分预测
1、基于用户的协同过滤的评分预测(以用户1对所有电影的评分预测为例)
#基于用户的协同过滤的评分预测——用户1对所有电影的评分预测
#根据预测评分为指定用户进行TOP-N推荐
def top_k_rs_result(k):
results = predict_all(1, ratings_matrix, user_similar, filter_rule=[ "unhot" ,"rated"])
return sorted(results, key=lambda x: x[2], reverse=True)[:k]
from pprint import pprint
result = top_k_rs_result(20)
pprint(result)
……
2、基于物品的协同过滤的评分预测(以用户1对所有电影的评分预测为例)
#基于物品的协同过滤的评分预测——用户1对所有电影的评分预测
#根据预测评分为指定用户进行TOP-N推荐
def top_k_rs_result(k):
results = predict_all(1, ratings_matrix, item_similar, filter_rule=[ "unhot" ,"rated"])
return sorted(results, key=lambda x: x[2], reverse=True)[:k]
from pprint import pprint
result = top_k_rs_result(20)
pprint(result)
……
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)