# 实现并测试协同滤波算法
## 一、问题简述
### 1.1 推荐系统问题
推荐系统问题旨在用户推荐相关项,项可以是用户未观看过的电影、书籍,未访问过的网站,可以是任何可以购买的产品,实现一种个性化的推荐。
推荐系统可以总结为以下模型:
$$
\text{Utility Function: } u: X \times S \to R
$$
其中,$X$ 是用户的集合,$S$ 是项的集合,$R$ 是用户对项评分的集合,并且是关于项的有序集。
推荐系统问题主要的问题为:如何为矩阵收集已知的评级,如何从已知的评级中推断未知的评级,如何评估推断的好坏。收集评分可以通过显式收集用户的评分,也可以通过学习用户的行为预测评分;推断未知评分可以使用基于内容、协同相关、基于隐因子(矩阵分解)、基于深度模型的模型甚至混合模型等;评估推断的好坏时可以选择在评分表中划分一块区域用于测试,计算平方根误差(RMSE),Top K 的精确度等。
### 1.2 协同滤波算法
* **基于用户的协同滤波算法**
* 第一步:读取用户-项的评分矩阵 $R$。
* 第二步:跟据评分矩阵计算用户相似度矩阵 $S_U$,在计算相似度时我们选择皮尔森相关系数。我们可以将计算出的评分矩阵保存在文件中,以免下次重复计算。
* 第三步:假定我们要预测用户 $u$ 给项 $i$ 的评分。首先找到于目标用户最相似的 K 个用户 $U_{sim}$,并且这些用户对项 $i$ 有评分记录,根据以下公式计算预测评分:
$$
r_{u,i} = \frac{\sum_{v \in U_{sim}} s_{u,v}r_{v,i}}{\sum_{v \in U_{sim}} s_{u,v}}
$$
其中,$r_{u,i}$ 指用户 $u$ 对项 $i$ 的预测评分,$s_{u,v}$ 指用户 $u$ 和用户 $v$ 的相似度。
* **基于项的协同滤波算法**
* 第一步:读取用户-项的评分矩阵 $R$。
* 第二步:跟据评分矩阵计算用户相似度矩阵 $S_I$,在计算相似度时我们选择皮尔森相关系数。我们可以将计算出的评分矩阵保存在文件中,以免下次重复计算。
* 第三步:假定我们要预测用户 $u$ 给项 $i$ 的评分。首先找到于目标项最相似的 K 个项 $I_{sim}$,并且用户 $u$ 对这些项有评分记录,根据以下公式计算预测评分:
$$
r_{u,i} = \frac{\sum_{j \in I_{sim}} s_{i,j}r_{v,i}}{\sum_{j \in I_{sim}} s_{i,j}}
$$
其中,$r_{u,i}$ 指用户 $u$ 对项 $i$ 的预测评分,$s_{i,j}$ 指项 $i$ 和项 $j$ 的相似度。
* **协同滤波算法的评价**
* 适用场景:
* 基于用户的协同滤波算法:具备更强的社交特性,适用于用户少物品多,时效性较强的场景。比如新闻、博客、微内容推荐场景。此外基于用户的协同滤波算法能够为用户发现新的兴趣爱好。
* 基于项的协同滤波算法:更适用于兴趣变化较为稳定的应用,更接近于个性化的推荐,适合物品少用户多,用户兴趣固定持久,物品更新速度不是太快的场合,比如电影推荐。
* 协同滤波算法的优点:适用于任何类型的项,不需要特征选择
* 协同滤波算法的缺点:
* 冷启动问题:对于基于用户的协同滤波算法,需要积累足够多的用户,并且用户有一定评分时才能找到一个用户的相似用户,而基于项的协同滤波算法没有此问题。
* 稀疏性问题:项的数目一般很多,一个用户对项的评分往往不会很多,评分矩阵是稀疏的,难以找到对相同的项评分过的用户。
* 新的项、评分较少的项因为评分较少,难以被推荐。
## 二、协同滤波实现
我们基于 Pandas 实现协同滤波算法,使用 DataFrame 记录中间结果(评分矩阵和相似度矩阵),并保存到文件中以便于下一次快速读取,工具函数`save_matrix_to_pickle(matrix, dir_path, file_name)`和`load_matrix_from_pickle(file_path)`方法分别用于保存和读取 DataFrame。
* **载入评分表**:因为评分表文件以邻接表的形式存储评分信息,为了方便后续的相似度计算,我们在读入文件后将其转化为矩阵形式(行为用户,列为项)。实现于`load_data_to_matrix(file_path, step=",")`函数:
```python
data = pd.read_csv(file_path, dtype={"userId": np.int32, "movieId": np.int32, "rating": np.float32}, usecols=range(3), sep=step)
rating_matrix = data.pivot_table(index=["userId"], columns="movieId", values="rating")
```
* **计算相似度**:直接使用 Pandas 提供的 corr 函数计算矩阵皮尔森相关系数,计算返回$N \times N$的方阵,$N$是矩阵的列数。因为我们评分矩阵的行是用户,列是项,故计算项相似度是直接对评分矩阵调用`.corr`,计算用户的相似度时先对评分矩阵进行转置再调用`.corr`。实现于函数`compute_similarity(rating_matrix, based_type="user")`
```python
if based_type == "user":
similarity_matrix = rating_matrix.T.corr(method="pearson")
elif based_type == "item":
similarity_matrix = rating_matrix.corr(method="pearson")
```
* **预测用户 i 对项 j 的评分**:实现于`predict_item_score_for_user(user_id, item_id, rating_matrix, similarity_matrix, based_type="user", k=-1)`。
```python
"""
* PREDICT_ITEM_SCORE_FOR_USER 预测用户 i 对电影 j 的评分
* input:
* user_id: Integer 用户 ID
* item_id: Integer 电影 ID
* rating_movie: DataFrame 评分矩阵
* similarity_matrix: DataFrame 相似性矩阵
* based_type: "user" or "item" 相似性矩阵的类型(用户/项)
* k: Integer 计算预测分数的近邻个数,默认-1,表示计算所有相似度大于0的相似项
* return:
* r: Integer 用户对该电影评分的预测值
"""
```
* 对于User-based CF,我们首先挑选出和用户 i 相似度大于 0 的用户,再挑选出对项 j 评分过的用户,二者取交集,再跟据相似度进行排序,选择前 K 个计算预测评分。
```python
# 挑选出和用户 i 相似度大于 0 的用户
similar_users = similarity_matrix[user_id].drop(user_id).dropna()
similar_users = similar_users.where(similar_users > 0).dropna()
# 挑选出对项 j 评分过的用户
have_item_users = rating_matrix[item_id].dropna()
# 二者取交集,再跟据相似度进行排序
have_item_similar_users = similar_users.loc[list(set(similar_users.index) & set(have_item_users.index))]
have_item_similar_users.sort_values(ascending=False, inplace=True)
# 计算预测分数
a = 0
b = 0
for similar_user, similarity in have_item_similar_users.iteritems()[:k]:
a += similarity * rating_matrix.loc[similar_user, item_id]
b += similarity
r = a / b
```
* 对于Item-based CF,我们首先挑选出和项 j 相似度大于 0 的项,再挑选出用户 i 评分过的项,二者取交集,再跟据相似度进行排序,选择前 K 个计算预测评分。
``` python
# 选出和项 j 相似度大于 0 的项
similar_items = similarity_matrix[item_id].drop(item_id).dropna()
similar_items = similar_items.where(similar_items > 0).dropna()
# 挑选出用户 i 评分过的项
user_rated_items = rating_matrix.loc[user_id].dropna()
# 二者取交集,再跟据相似度进行排序
user_rated_similar_items = similar_items.loc[list(set(similar_items.index) & set(user_rated_items.index))]
user_rated_similar_items.sort_values(ascending=False, inplace=True)
# 计算预测分数
a = 0
b = 0
for similar_item, similarity in user_rated_similar_items.iteritems()[:k]:
a += similarity * rating_matrix.lo
神仙别闹
- 粉丝: 3783
- 资源: 7469
最新资源
- (源码)基于C++的Local Generals游戏系统.zip
- (源码)基于MQTT协议的智能插座系统.zip
- Insurence_20180221.sav
- 一个简单的 JavaScript 俄罗斯方块游戏.zip
- Python课程设计:基于OpenCV的人脸识别与检测源码
- 一个 JavaScript 有限状态机库.zip
- 一个 Java 序列化,反序列化库,用于将 Java 对象转换为 JSON 并转回.zip
- Современный учебник JavaScript.zip
- Udemy 课程 - 面向软件开发人员的 Java 编程大师班 讲师 - Tim Buchalka.zip
- Udemy 上的现代 JavaScript(从新手到忍者)课程的所有讲座文件 .zip
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈