Skip to content

基于协同过滤的电影推荐

介绍

前面我们已经基本掌握了协同过滤推荐算法,以及其中两种最基本的实现方案:User-Based CF 和 Item-Based CF,下面我们将利用真的数据来进行实战演练。

数据集下载

MovieLens Latest Datasets Small

建议下载 ml-latest-small.zip,数据量小,便于我们单机使用和运行

目标:根据 ml-latest-small/ratings.csv(用户-电影评分数据),分别实现 User-Based CF 和 Item-Based CF,并进行电影评分的预测,然后为用户实现电影推荐

数据集加载

  • 加载 ratings.csv,并转换为用户-电影评分矩阵

    Click me to view the code
    python
    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 = 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)
    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 = 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)

相似度计算

  • 计算用户或物品两两相似度:

    Click me to view the code
    python
    # ......
    
    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)
    # ......
    
    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)

TIP

以上实现,仅用于实验阶段,因为工业上、或生产环境中,数据量是远超过我们本例中使用的数据量的,而 pandas 是无法支撑起大批量数据的运算的,因此工业上通常会使用 spark、mapReduce 等分布式计算框架来实现。

User-Based CF 预测评分

评分预测公式:

pred(u,i)=r^ui=vUsim(u,v)rvivU|sim(u,v)|

算法实现

  • 实现评分预测方法:predict

    Click me to view the code
    python
    # ......
    
    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.items():
            # 近邻用户的评分数据
            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)
        # 预测用户1对物品2的评分
        predict(1, 2, ratings_matrix, user_similar)
    # ......
    
    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.items():
            # 近邻用户的评分数据
            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)
        # 预测用户1对物品2的评分
        predict(1, 2, ratings_matrix, user_similar)

    WARNING

    可能会出现 division by zero 错误,这是因为存在这种情况:计算用户 A 对电影 Item 的得分时,与用户 A 相似的其他用户群命名为 B,存在 B 中所有用户均没有对 Item 的评价记录,导致公式分母为零,从而出现错误。

  • 实现预测全部评分方法:predict_all

    Click me to view the code
    python
    # ......
    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, 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
  • 添加过滤规则

    Click me to view the code
    python
    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)
    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推荐:

    Click me to view the code
    python
    # ......
    
    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)
        pprint(result)
    # ......
    
    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)
        pprint(result)

Item-Based CF 预测评分

评分预测公式:

pred(u,i)=r^ui=jIratedsim(i,j)rujjIratedsim(i,j)

算法实现

  • 实现评分预测方法:predict

    • 方法说明:

      利用原始评分矩阵、以及物品间两两相似度,预测指定用户对指定物品的评分。

      如果无法预测,则抛出异常

    Click me to view the code
    python
    # ......
    
    def predict(uid, iid, ratings_matrix, item_similar):
        '''
        预测给定用户对给定物品的评分值
        :param uid: 用户ID
        :param iid: 物品ID
        :param ratings_matrix: 用户-物品评分矩阵
        :param item_similar: 物品两两相似度矩阵
        :return: 预测的评分值
        '''
        print("开始预测用户<%d>对电影<%d>的评分..."%(uid, iid))
        # 1. 找出iid物品的相似物品
        similar_items = item_similar[iid].drop([iid]).dropna()
        # 相似物品筛选规则:正相关的物品
        similar_items = similar_items.where(similar_items>0).dropna()
        if similar_items.empty is True:
            raise Exception("物品<%d>没有相似的物品" %id)
    
        # 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, similarity 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 += similarity * sim_item_rating_from_user
            # 计算分母的值
            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)
    
        item_similar = compute_pearson_similarity(ratings_matrix, based="item")
        # 预测用户1对物品1的评分
        predict(1, 1, ratings_matrix, item_similar)
        # 预测用户1对物品2的评分
        predict(1, 2, ratings_matrix, item_similar)
    # ......
    
    def predict(uid, iid, ratings_matrix, item_similar):
        '''
        预测给定用户对给定物品的评分值
        :param uid: 用户ID
        :param iid: 物品ID
        :param ratings_matrix: 用户-物品评分矩阵
        :param item_similar: 物品两两相似度矩阵
        :return: 预测的评分值
        '''
        print("开始预测用户<%d>对电影<%d>的评分..."%(uid, iid))
        # 1. 找出iid物品的相似物品
        similar_items = item_similar[iid].drop([iid]).dropna()
        # 相似物品筛选规则:正相关的物品
        similar_items = similar_items.where(similar_items>0).dropna()
        if similar_items.empty is True:
            raise Exception("物品<%d>没有相似的物品" %id)
    
        # 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, similarity 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 += similarity * sim_item_rating_from_user
            # 计算分母的值
            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)
    
        item_similar = compute_pearson_similarity(ratings_matrix, based="item")
        # 预测用户1对物品1的评分
        predict(1, 1, ratings_matrix, item_similar)
        # 预测用户1对物品2的评分
        predict(1, 2, ratings_matrix, item_similar)
  • 实现预测全部评分方法:predict_all

    Click me to view the code
    python
    # ......
    
    def predict_all(uid, ratings_matrix, item_similar):
        '''
        预测全部评分
        :param uid: 用户id
        :param ratings_matrix: 用户-物品打分矩阵
        :param item_similar: 物品两两间的相似度
        :return: 生成器,逐个返回预测评分
        '''
        # 准备要预测的物品的id列表
        item_ids = ratings_matrix.columns
        # 逐个预测
        for iid in item_ids:
            try:
                rating = predict(uid, iid, ratings_matrix, item_similar)
            except Exception as e:
                print(e)
            else:
                yield uid, iid, rating
    
    if __name__ == '__main__':
        ratings_matrix = load_data(DATA_PATH)
    
        item_similar = compute_pearson_similarity(ratings_matrix, based="item")
        for i in predict_all(1, ratings_matrix, item_similar):
            pass
    # ......
    
    def predict_all(uid, ratings_matrix, item_similar):
        '''
        预测全部评分
        :param uid: 用户id
        :param ratings_matrix: 用户-物品打分矩阵
        :param item_similar: 物品两两间的相似度
        :return: 生成器,逐个返回预测评分
        '''
        # 准备要预测的物品的id列表
        item_ids = ratings_matrix.columns
        # 逐个预测
        for iid in item_ids:
            try:
                rating = predict(uid, iid, ratings_matrix, item_similar)
            except Exception as e:
                print(e)
            else:
                yield uid, iid, rating
    
    if __name__ == '__main__':
        ratings_matrix = load_data(DATA_PATH)
    
        item_similar = compute_pearson_similarity(ratings_matrix, based="item")
        for i in predict_all(1, ratings_matrix, item_similar):
            pass
  • 添加过滤规则

    Click me to view the code
    python
    def _predict_all(uid, item_ids,ratings_matrix, item_similar):
        '''
        预测全部评分
        :param uid: 用户id
        :param item_ids: 要预测物品id列表
        :param ratings_matrix: 用户-物品打分矩阵
        :param item_similar: 物品两两间的相似度
        :return: 生成器,逐个返回预测评分
        '''
        # 逐个预测
        for iid in item_ids:
            try:
                rating = predict(uid, iid, ratings_matrix, item_similar)
            except Exception as e:
                print(e)
            else:
                yield uid, iid, rating
    
    def predict_all(uid, ratings_matrix, item_similar, filter_rule=None):
        '''
        预测全部评分,并可根据条件进行前置过滤
        :param uid: 用户ID
        :param ratings_matrix: 用户-物品打分矩阵
        :param item_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, item_similar)
    
    if __name__ == '__main__':
        ratings_matrix = load_data(DATA_PATH)
    
        item_similar = compute_pearson_similarity(ratings_matrix, based="item")
    
        for result in predict_all(1, ratings_matrix, item_similar, filter_rule=["unhot", "rated"]):
            print(result)
    def _predict_all(uid, item_ids,ratings_matrix, item_similar):
        '''
        预测全部评分
        :param uid: 用户id
        :param item_ids: 要预测物品id列表
        :param ratings_matrix: 用户-物品打分矩阵
        :param item_similar: 物品两两间的相似度
        :return: 生成器,逐个返回预测评分
        '''
        # 逐个预测
        for iid in item_ids:
            try:
                rating = predict(uid, iid, ratings_matrix, item_similar)
            except Exception as e:
                print(e)
            else:
                yield uid, iid, rating
    
    def predict_all(uid, ratings_matrix, item_similar, filter_rule=None):
        '''
        预测全部评分,并可根据条件进行前置过滤
        :param uid: 用户ID
        :param ratings_matrix: 用户-物品打分矩阵
        :param item_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, item_similar)
    
    if __name__ == '__main__':
        ratings_matrix = load_data(DATA_PATH)
    
        item_similar = compute_pearson_similarity(ratings_matrix, based="item")
    
        for result in predict_all(1, ratings_matrix, item_similar, filter_rule=["unhot", "rated"]):
            print(result)
  • 为指定用户推荐TOP-N结果

    Click me to view the code
    python
    # ......
    
    def top_k_rs_result(k):
        ratings_matrix = load_data(DATA_PATH)
    
        item_similar = compute_pearson_similarity(ratings_matrix, based="item")
        results = predict_all(1, ratings_matrix, item_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)
        pprint(result)
    # ......
    
    def top_k_rs_result(k):
        ratings_matrix = load_data(DATA_PATH)
    
        item_similar = compute_pearson_similarity(ratings_matrix, based="item")
        results = predict_all(1, ratings_matrix, item_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)
        pprint(result)