基于协同过滤的电影评分推荐案例及相关代码

案例介绍

  • 我们要通过两种最基本的实现方案:User-Based CF和Item-Based CF,利用真实的数据来进行案例的分析演练。数据这边我们采用了MovieLens中的数据集,因为实验环境,对于数据量的要求不能特别高,所以我们可以下载ml-latest-small.zip 较小的数据集。进行电影评分的预测,然后为用户实现电影推荐。传送门链接请点击

数据集加载

  • ratings文件的展示:在做此实验时需要使用使用ratings.csv文件,我们需要保留userid、movieid及ratings列,去掉timestamp列。效果如图所示:
    在这里插入图片描述
#部分代码,仅供参考
dtype = {"userId": np.int32, "movieId": np.int32, "rating": np.float32}
ratings = pd.read_csv('./ml-latest-small/ratings.csv',dtype=dtype,usecols = range(3))
ratings.head()
  • 构建透视表(user-item的评分矩阵)
#部分代码,仅供参考
ratings_matrix = ratings.pivot_table(index = ['userId'],columns=['movieId'],values='rating')
ratings_matrix

在这里插入图片描述

  • 加载ratings.csv,并转换为用户-电影评分矩阵详细代码
import os

import pandas as pd
import numpy as np

DATA_PATH = "./ml-latest-small/ratings.csv"
CACHE_DIR = "./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) #python数据序列化文件pickle(数据量较小的情况下可以不使用)
        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 = ratings.pivot_table(index=["userId"], columns=["movieId"], values="rating")
        # 存入缓存文件
        ratings_matrix.to_pickle(cache_path)
        print("数据集加载完毕")
    return  ratings_matrix

if __name__ == '__main__':
    ratings_matrix = load_data(DATA_PATH)
    print(ratings_matrix)

相似度计算

  • 我们采用皮尔逊相关系数来计算user-movie之间两两相似度。
#部分代码,仅供参考:
user_similar = ratings_matrix.T.corr()
user_similar
item_similar = ratings_matrix.corr()
item_similar

在这里插入图片描述
在这里插入图片描述

  • 计算user-item两两相似度详细代码实现
def compute_pearson_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

if __name__ == '__main__':

    ratings_matrix = load_data(DATA_PATH)

    user_similar = compute_pearson_similarity(ratings_matrix, based="user")
    print(user_similar)
    item_similar = compute_pearson_similarity(ratings_matrix, based="item")
    print(item_similar)

备注

  • 以上的实现,仅适用于实验环境。工业、生产环境中,数据量比我们这个demo要大太多很多数据量都是几百GB甚至是TB级别的,而pandas框架是无法支撑起大批量数据的运算的,因此工业生产环境通常会使用Spark、MR等分布式计算框架来实现的。正如前面所说,推荐算法的思想和理念都是统一的,只是业务场景,数据量和环境不同。所以无论使用什么样的平台工具、数据量的大小,其实现原理都是相同的。

User-Based CF 预测评分

评分预测公式

在这里插入图片描述

实现评分预测predict

def predict(uid, iid, ratings_matrix, user_similar):
    '''
    预测给定用户对给定物品的评分值
    :param uid: 用户ID
    :param iid: 物品ID
    :param ratings_matrix: 用户-物品评分矩阵
    :param user_similar: 用户两两相似度矩阵
    :return: 预测的评分值
    '''
    print("开始预测用户<%d>对电影<%d>的评分..."%(uid, iid))
    # 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("用户<%d>没有相似的用户" % 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("预测出用户<%d>对电影<%d>的评分:%0.2f" % (uid, iid, predict_rating))
    return round(predict_rating, 2)

if __name__ == '__main__':
    ratings_matrix = load_data(DATA_PATH)

    user_similar = compute_pearson_similarity(ratings_matrix, based="user")
    # 预测用户1对物品1的评分
    predict(1, 1, ratings_matrix, user_similar)
    # 预测用户2对物品28的评分
    predict(2, 28, ratings_matrix, user_similar)

实现预测全部评分predict_all

def predict_all(uid, ratings_matrix, user_similar):
    '''
    预测全部评分
    :param uid: 用户id
    :param ratings_matrix: 用户-物品打分矩阵
    :param user_similar: 用户两两间的相似度
    :return: 生成器,逐个返回预测评分
    '''
    # 准备要预测的物品的id列表
    item_ids = ratings_matrix.columns
    # 逐个预测
    for iid in item_ids:
        try:
            rating = predict(uid, iid, ratings_matrix, user_similar)
        except Exception as e:
            print(e)
        else:
            yield uid, iid, rating

if __name__ == '__main__':
    ratings_matrix = load_data(DATA_PATH)

    user_similar = compute_pearson_similarity(ratings_matrix, based="user")

    for i in predict_all(1, ratings_matrix, user_similar):
        pass

添加过滤规则预测

  • 例如过滤冷门电影和已经有评分的电影
def _predict_all(uid, item_ids, ratings_matrix, user_similar):
    '''
    预测全部评分
    :param uid: 用户id
    :param item_ids: 要预测的物品id列表
    :param ratings_matrix: 用户-物品打分矩阵
    :param user_similar: 用户两两间的相似度
    :return: 生成器,逐个返回预测评分
    '''
    # 逐个预测
    for iid in item_ids:
        try:
            rating = predict(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):
    '''
    预测全部评分,并可根据条件进行前置过滤
    :param uid: 用户ID
    :param ratings_matrix: 用户-物品打分矩阵
    :param user_similar: 用户两两间的相似度
    :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_all(uid, item_ids, ratings_matrix, user_similar)

if __name__ == '__main__':
    ratings_matrix = load_data(DATA_PATH)

    user_similar = compute_pearson_similarity(ratings_matrix, based="user")

    for result in predict_all(1, ratings_matrix, user_similar, filter_rule=["unhot", "rated"]):
        print(result)

根据预测评分为指定用户进行TOP-N推荐

def top_k_rs_result(k):
    ratings_matrix = load_data(DATA_PATH)
    user_similar = compute_pearson_similarity(ratings_matrix, based="user")
    results = predict_all(1, ratings_matrix, user_similar, filter_rule=["unhot", "rated"])
    return sorted(results, key=lambda x: x[2], reverse=True)[:k]

if __name__ == '__main__':
    from pprint import pprint
    result = top_k_rs_result(20)
    print(result)

Item-Based CF 预测评分

评分预测公式

在这里插入图片描述

  • 这里代码部分就省略了,因为UBCF与IBCF实现代码基本相同。
  • 0
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值