为什么不能通过优化RMSE得到Top-K Recommendations?

前几天写《RMSE for Top-K Recommendations:高手的盲点?》查文献时,注意到Google Recsys兴趣小组在去年就有这方面的讨论。因为已经快写完了,所以没有对Recsys兴趣小组提到的一些点深入讨论。今天针对Recsys兴趣小组的讨论,谈一些我的看法。

1)为什么不能通过优化RMSE得到Top-K Recommendations?

优化RMSE,实际上就是要预测用户对每个商品的评分(大多数电子商务网站可选的分值是1-5分)。理想情况下,如果算法能够准确地预
测用户的评分值,那么根据预测结果将评分最高的K个商品推荐给用户,就完成了Top-K推荐。问题在于,算法往往并不能够准确地预测用户的评分值,数据有噪声,或者太稀疏,都有可能造成这样的结果。

那么,不能准确预测用户评分值时,算法会如何处理呢?不妨假定训练集中某用户有10个评分为5的商品,1000个评分为1的商品。在以RSME为优化目标时,这1010个商品是同等重要的,因此,算法会花费更大的精力去保证评分为1的商品的预测值尽量准确,相比之下,评分为5的商品被忽视了。而对于Top-K推荐来说,算法实际上应该尽量保证评分为5的商品的预测的准确性,评分为1的商品的预测结果是无关紧要的。所以问题的关键不在于使用的是矩阵分解方法还是其他的比如knn方法,而在于你优化的是什么目标,优化的目标和评价指标是不是一致。

相似的问题其实有很多研究结果。比如对于分类问题,Jesse Davis讨论过AUC和Precision-Recall曲线的关系,其实这和RMSE和Top-K推荐的关系非常类似(文中Figure 7很好地展示了这一点),相应地,优化AUC和优化F-Score得到的结果也会完全不同。在Ranking方面,Pair-wise和List-wise其实也是类似的问题:首先是评价标准的不同,从而导致了算法的不同。

2)为什么在0-1矩阵上knn方法往往优于SVD方法?

用户评分并不总是那么容易获取,所以很多情况下,我们需要利用其他数据来实现推荐。最常见的是用户购买信息或者点击信息,也就是所谓的Implicit Feedback:用户购买或点击过的商品地评分设为1,其他设为0。

有很多试验结果显示,在Netflix Prize上表现优异的SVD算法以及它的各种变体,在0-1矩阵上表现非常差(比如KDD-Cup 2007的一些实验报告,以Recsys的讨论中,都提到了这个问题)。对于这个问题,首先还是要考虑优化的目标和评价指标是不是一致。如果评价指标是F-Score,优化目标却是AUC,那显然会南辕北辙。除此之外,还要注意到一个问题:SVD类似于一个回归(Regression)算法,而0-1矩阵更像一个分类问题的数据集,用回归方法去解决分类问题,性能差是可以预见的。对0-1矩阵的分解,需要采用不同于SVD的方法,这方面的研究,较早的有Schein在2003年的讨论,最近的工作是Rong Pan的One Class Collaborative Filtering

实际上,广为使用的pLSI也是在类似的背景下提出的。LSI(也就是SVD)假定数据是正态分布的,而词频是波淞分布的,用LSI处理(归一化之后的)词频矩阵并不合适,所以会有pLSI出现。当然,这也部分解释了为什么pLSI在0-1矩阵上效果差。

3)knn或Item-Based方法为什么能取得好的效果?

knn方法变体太多,它的效果很大程度上取决于所采用的距离函数,以及学习参数的选择。比如说,有可能以Pearson卡方作为距离函数能取得最好的Top-K推荐的效果,而以Cosine作为距离函数取得最好的RMSE效果,也有可能在k为30时能取得最好的Top-K推荐的效果,而k为100时取得最好的RMSE效果。总体来说,应用于推荐系统的knn方法大多属于ad hoc方法。相比较而言,Item-Based方法就更加ad hoc了。它们的成功,取决于在Validation集合上有效地进行交叉验证试验,选取合适的距离度量方法和学习参数。交叉验证的过程,就是这些方法拟合评价指标的过程。


  (欢迎转载 谢绝篡改 作者:chen_1st 微博:http://t.sina.com.cn/chen1st

import pandas as pd # 示例数据集结构 data = { 'user_id': ['U1', 'U1', 'U2', 'U3', 'U3'], 'item_id': ['S1', 'S3', 'S2', 'S1', 'S4'], 'rating': [5, 3, 4, 2, 5] } df = pd.DataFrame(data) from surprise import Dataset, Reader from surprise import SVD, KNNBaseline from surprise.model_selection import cross_validate # 1. 数据加载 reader = Reader(rating_scale=(1, 5)) data = Dataset.load_from_df(df[['user_id', 'item_id', 'rating']], reader) # ========== ID与名称映射 ========== SONG_MAPPING = { 'S4': 'Yesterday Once More', 'S7': 'My Heart Will Go On', 'S12': 'Unchained Melody', # 可扩展更多映射关系 } # ============================= # 2. 算法选择(矩阵分解示例) algo = SVD(n_factors=100, n_epochs=20, lr_all=0.005, reg_all=0.02) # 3. 交叉验证评估 cross_validate(algo, data, measures=['RMSE', 'MAE'], cv=3, verbose=True) # 4. 训练完整模型 trainset = data.build_full_trainset() algo.fit(trainset) # 5. 预测示例 uid = 'U2' # 用户ID iid = 'S3' # 歌曲ID pred = algo.predict(uid, iid) print(f'预测评分:{pred.est:.2f}') def get_top_recommendations(user_id, n=5): # 获取所有未评分歌曲 all_songs = df['item_id'].unique() rated_songs = df[df['user_id'] == user_id]['item_id'] unseen = [song for song in all_songs if song not in rated_songs.values] # 预测评分并排序 predictions = [algo.predict(user_id, song) for song in unseen] top = sorted(predictions, key=lambda x: x.est, reverse=True)[:n] return [(pred.iid, pred.est) for pred in top] def get_recommendations(user_id): # 原有预测逻辑(假设返回ID列表) recommended_ids = ['S4', 'S7', 'S12'] # 示例预测结果 # 转换ID为歌曲名称 return [SONG_MAPPING.get(sid, f'Unknown Song ({sid})') for sid in recommended_ids] 将这段代码转换成协同过滤算法,将优化好的代码输出给我
最新发布
03-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值