使用基于用户的协同过滤算法构建电影推荐系统(MovieLens数据集)

本文完整项目代码已托管至github,下载地址:https://github.com/gitren111/Movie-Recommender-By-user-based_cf

User_based_CF算法简介

User-based Collaborative Filtering(基于用户的协同过滤)算法通过计算用户之间的相似度来进行推荐。首先,构建用户-电影评分矩阵,然后利用皮尔逊相关系数衡量用户之间的相似度。预测某用户对电影的评分时,基于其与其他用户的相似度以及近邻用户的评分进行加权计算。该算法通过基准评分和相似度加权评分的组合,预测目标用户可能的兴趣,广泛应用于个性化推荐系统中。

概念与公式

  • 皮尔逊相关系数:协方差/各自标准差,通过归一化解决量纲差异大问题,取值在-1到1之间,越接近1说明越相似
    在这里插入图片描述

  • 用户相似度计算:构建用户的评分矩阵,然后用户两两之间用皮尔逊相关系数公式计算用户的相似度
    在这里插入图片描述

  • 预测用户u对电影的评分:

    • 基准评分:ru是用户u对所有电影的平均评分
    • 分子项:筛选近邻用户(与u用户正相关且对i电影有过评分的客户),求近邻客户相似度*(近邻客户对i电影评分-近邻客户平均评分)
    • 分母项:近邻客户的相似度数据绝对值和进行相似度归一化
      在这里插入图片描述

基于User_based_CF算法实现电影推荐功能

数据集加载,转换为用户-电影评分矩阵

ra_data_path = 'ml-latest-small/ml-latest-small/ratings.csv'
ra_cache_dir = 'cache'
cache_path = os.path.join(ra_cache_dir,'rating_matrix_cache')

def load_data(ra_data_path,cache_path):
    print('开始分批加载数据集...')
    if not os.path.exists(ra_cache_dir):
        os.makedirs(ra_cache_dir)

    if os.path.exists(cache_path):
        print('加载缓冲中...')
        ratings_matrix = pd.read_pickle(cache_path)
        print('从缓存加载数据集完毕')
    else:
        dtype = {'userId': np.int32, 'movieId': np.int32, 'rating': np.float32}
        print('加载新数据中')
        #加载前三列数据:用户ID、电影ID、评分
        ratings = pd.read_csv(ra_data_path,dtype=dtype,usecols=range(3))
        #转换为用户-电影评分矩阵
        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(ra_data_path,cache_path)
print(ratings_matrix.shape)
print(ratings_matrix.head())

用户相似度计算

def compute_persion_similarity(ratings_matrix,based='user'):
    user_similarity_cache_path = os.path.join(ra_cache_dir,'user_similarity_cache')

    #基于皮尔森相关系数计算相似度
    if not os.path.exists(ra_cache_dir):
        os.makedirs(ra_cache_dir)
    if os.path.exists(user_similarity_cache_path):
        print('正从缓存加载用户相似度矩阵')
        similarity = pd.read_pickle(user_similarity_cache_path)
    else:
        print('开始计算用户相似度矩阵')
        similarity = ratings_matrix.T.corr()
        #转置为行标签是电影ID,列标签是用户ID,计算用户间相似度矩阵(默认皮尔森)
        similarity.to_pickle(user_similarity_cache_path)
    print('相似度矩阵加载完毕')
    return similarity

user_similar = compute_persion_similarity(ratings_matrix,based='user')
print('用户相似度矩阵预览和大小')
print(user_similar.shape)
print(user_similar.head())

预测评分函数(基于用户协同过滤):user-based CF

公式:目标用户u的平均评分+k个近邻客户加总(近邻客户相似度*(近邻客户对i电影评分-近邻客户平均评分))/相似度

def predict_userBasedCF(uid,itemid,ratings_matrix,user_similar):
    #找到与用户u相关性的所有其他用户
    similar_user = user_similar[uid].drop(labels=[uid]).dropna()

    #(2)相似用户筛选
    similar_user = similar_user.where(similar_user>0).dropna()
    if similar_user.empty is True:
        raise Exception('客户{}没有相似客户'.format(uid))

    #(3)从客户u的相似客户中找出对物品i有评分的近邻客户
    id_user = set(ratings_matrix[itemid].dropna().index)&set(similar_user.index)
    finally_similar_users = similar_user.loc[list(id_user)]

    #(4)计算用户u的平均评分
    user_ratings = ratings_matrix.loc[uid].dropna()
    user_mean_rat = user_ratings.mean()

    #(5)公式计算
    sum_up = 0
    sum_down = 0
    for sim_uid,similarity in finally_similar_users.items():
        #近邻用户v所有电影的平均评分
        sim_user_rated_movies = ratings_matrix.loc[sim_uid].dropna()
        sim_user_rat = sim_user_rated_movies.mean()

        #近邻用户对物品i的评分
        sim_user_rating_for_item = sim_user_rated_movies[itemid]

        #分子计算:近邻客户与目标客户u相似度*(近邻客户对i评分-近邻客户平均评分)
        sum_up += similarity*(sim_user_rating_for_item - sim_user_rat)
        #分母计算
        sum_down += abs(similarity)
    #最后将所有相似客户的累加再除分母累加
    if sum_down == 0:
        print('分母为0不可计算')
        raise None
    predict_rating = user_mean_rat + sum_up/sum_down
    predict_rating = min(predict_rating,5)
    return round(predict_rating,2)

根据筛选条件来预测评分

def userBase_predict_all(uid,item_ids,ratings_matrix,user_similar):
    for iid in item_ids:
        try:
            rating = predict_userBasedCF(uid,iid,ratings_matrix,user_similar)
        except Exception as e:
            print('基于用户的协同过滤算法无法预测结果')
        else:
            yield uid,iid,rating

def predict_all(uid,ratings_matrix,similar_matrix,filter_rule=None):
    if not filter_rule:
        item_ids = ratings_matrix.columns

    #筛选掉非热门电影
    elif filter_rule == 'unhot':
        count = ratings_matrix.count()
        item_ids = count.where(count>10).dropna().index
    # 筛选没有评分的电影
    elif filter_rule == 'rated':
        user_ratings = ratings_matrix.loc[uid]
        item_ids = user_ratings[user_ratings.between(0,5)].index
    #筛选掉非热门且无评分电影
    elif set(filter_rule) == set(['unhot','rated']):
        count = ratings_matrix.count()
        ids1 = count.where(count>10).dropna().index
        user_ratings = ratings_matrix.loc[uid]
        ids2 = user_ratings[user_ratings.between(0,5)].index
        item_ids = set(ids1)&set(ids2)
    else:
        raise Exception('无效的筛选参数')
    yield from userBase_predict_all(uid, item_ids, ratings_matrix, similar_matrix)

预测电影评分

def topk_userBase_predict(k,filter_rule=None):
    results = predict_all(1,ratings_matrix,user_similar,filter_rule)
    results = sorted(results, key=lambda x: x[2], reverse=True)[:k]  # 截取前面k个
    print('基于用户的协同过滤评分预测,用户最喜欢的十部电影:')
    for uid, itemid, predict_rating in results:
        print('预测出用户{}对电影{}的评分为:{:.2f}'.format(uid, itemid, predict_rating))
    return results

topk_userBase_predict(10,filter_rule=['unhot','rated'])

输出结果

基于用户的协同过滤评分预测,用户最喜欢的十部电影:
预测出用户1对电影2571的评分为:5.00
预测出用户1对电影527的评分为:5.00
预测出用户1对电影50的评分为:5.00
预测出用户1对电影1089的评分为:5.00
预测出用户1对电影1617的评分为:5.00
预测出用户1对电影101的评分为:5.00
预测出用户1对电影1136的评分为:5.00
预测出用户1对电影2700的评分为:5.00
预测出用户1对电影3740的评分为:5.00
预测出用户1对电影1196的评分为:5.00

总结:基于用户的协同过滤算法优缺点

  • 优点:适合用户评分数据密集场景
  • 缺点:如果用户数量多或者评分矩阵稀疏(比如电商就是用户数量远大于商品),计算复杂度高,最终效果下降
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值