案例--算法实现:Item-Based CF 预测评分

评分预测公式:

算法实现

  • 实现评分预测方法:predict

    • 方法说明:

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

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

    # ......
    
    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.ix[uid].dropna().index)&set(similar_items.index)
        finally_similar_items = similar_items.ix[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

    # ......
    
    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, 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.ix[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.ix[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结果

    # ......
    
    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)
    

二 推荐系统评估

  • 好的推荐系统可以实现用户, 服务提供方, 内容提供方的共赢

  • 显示反馈和隐式反馈

    显式反馈 隐式反馈
    例子 电影/书籍评分 是否喜欢这个推荐 播放/点击 评论 下载 购买
    准确性
    数量
    获取成本
  • 常用评估指标

    • 准确性 • 信任度 • 满意度 • 实时性 • 覆盖率 • 鲁棒性 • 多样性 • 可扩展性 • 新颖性 • 商业⽬标 • 惊喜度 • ⽤户留存

    • 准确性 (理论角度) Netflix 美国录像带租赁
      • 评分预测
        • RMSE MAE

      • topN推荐
        • 召回率 精准率

    • 准确性 (业务角度)

    • 覆盖度
      • 信息熵 对于推荐越大越好

      • 覆盖率

    • 多样性&新颖性&惊喜性
      • 多样性:推荐列表中两两物品的不相似性。(相似性如何度量?

      • 新颖性:未曾关注的类别、作者;推荐结果的平均流⾏度

      • 惊喜性:历史不相似(惊)但很满意(喜)

      • 往往需要牺牲准确性

      • 使⽤历史⾏为预测⽤户对某个物品的喜爱程度

      • 系统过度强调实时性

    • Exploitation & Exploration 探索与利用问题
      • Exploitation(开发 利用):选择现在可能最佳的⽅案

      • Exploration(探测 搜索):选择现在不确定的⼀些⽅案,但未来可能会有⾼收益的⽅案

      • 在做两类决策的过程中,不断更新对所有决策的不确定性的认知,优化 长期的⽬标

    • EE问题实践
      • 兴趣扩展: 相似话题, 搭配推荐

      • 人群算法: userCF 用户聚类

      • 平衡个性化推荐和热门推荐比例

      • 随机丢弃用户行为历史

      • 随机扰动模型参数

    • EE可能带来的问题
      • 探索伤害用户体验, 可能导致用户流失

      • 探索带来的长期收益(留存率)评估周期长, KPI压力大

      • 如何平衡实时兴趣和长期兴趣

      • 如何平衡短期产品体验和长期系统生态

      • 如何平衡大众口味和小众需求

    • 评估方法
      • 问卷调查: 成本高

      • 离线评估:
        • 只能在用户看到过的候选集上做评估, 且跟线上真实效果存在偏差

        • 只能评估少数指标

        • 速度快, 不损害用户体验

      • 在线评估: 灰度发布 & A/B测试 50% 全量上线

      • 实践: 离线评估和在线评估结合, 定期做问卷调查

 

你可能感兴趣的:(人工智能,算法,python,推荐算法)