# 协同过滤的原理及Python实现

Github: https://github.com/tushushu

```https://github.com/tushushu/imylu/blob/master/imylu/recommend/als.py
https://github.com/tushushu/imylu/blob/master/examples/als_example.py```

1. 原理篇

1.1 你听说过推荐算法么

1.2 什么是协同过滤

1.3 协同过滤的分类

1. 基于用户(user-based)的协同过滤，通过计算用户和用户的相似度找到跟用户A相似的用户B, C, D…再把这些用户喜欢的内容推荐给A；

2.基于物品(item-based)的协同过滤，通过计算物品和物品的相似度找到跟物品1相似的物品2, 3, 4…再把这些物品推荐给看过物品1的用户们；

3. 基于模型(model based)的协同过滤。主流的方法可以分为：矩阵分解，关联算法，聚类算法，分类算法，回归算法，神经网络。

1.4 矩阵分解

1.5 ALS

1.6 求解用户矩阵U和物品矩阵I

2. 实现篇

2.1 创建ALS类

```class ALS(object):
def __init__(self):
self.user_ids = None
self.item_ids = None
self.user_ids_dict = None
self.item_ids_dict = None
self.user_matrix = None
self.item_matrix = None
self.user_items = None
self.shape = None
self.rmse = None```

2.2 数据预处理

```def _process_data(self, X):
self.user_ids = tuple((set(map(lambda x: x[0], X))))
self.user_ids_dict = dict(map(lambda x: x[::-1],
enumerate(self.user_ids)))

self.item_ids = tuple((set(map(lambda x: x[1], X))))
self.item_ids_dict = dict(map(lambda x: x[::-1],
enumerate(self.item_ids)))

self.shape = (len(self.user_ids), len(self.item_ids))

ratings = defaultdict(lambda: defaultdict(int))
ratings_T = defaultdict(lambda: defaultdict(int))
for row in X:
user_id, item_id, rating = row
ratings[user_id][item_id] = rating
ratings_T[item_id][user_id] = rating

err_msg = "Length of user_ids %d and ratings %d not match!" % (
len(self.user_ids), len(ratings))
assert len(self.user_ids) == len(ratings), err_msg

err_msg = "Length of item_ids %d and ratings_T %d not match!" % (
len(self.item_ids), len(ratings_T))
assert len(self.item_ids) == len(ratings_T), err_msg
return ratings, ratings_T```

2.3 用户矩阵乘以评分矩阵

```def _users_mul_ratings(self, users, ratings_T):

def f(users_row, item_id):
user_ids = iter(ratings_T[item_id].keys())
scores = iter(ratings_T[item_id].values())
col_nos = map(lambda x: self.user_ids_dict[x], user_ids)
_users_row = map(lambda x: users_row[x], col_nos)
return sum(a * b for a, b in zip(_users_row, scores))

ret = [[f(users_row, item_id) for item_id in self.item_ids]
for users_row in users.data]
return Matrix(ret)```

2.4 物品矩阵乘以评分矩阵

```def _items_mul_ratings(self, items, ratings):

def f(items_row, user_id):
item_ids = iter(ratings[user_id].keys())
scores = iter(ratings[user_id].values())
col_nos = map(lambda x: self.item_ids_dict[x], item_ids)
_items_row = map(lambda x: items_row[x], col_nos)
return sum(a * b for a, b in zip(_items_row, scores))

ret = [[f(items_row, user_id) for user_id in self.user_ids]
for items_row in items.data]
return Matrix(ret)```

2.5 生成随机矩阵

```def _gen_random_matrix(self, n_rows, n_colums):
data = [[random() for _ in range(n_colums)] for _ in range(n_rows)]
return Matrix(data)```

2.6 计算RMSE

```def _get_rmse(self, ratings):
m, n = self.shape
mse = 0.0
n_elements = sum(map(len, ratings.values()))
for i in range(m):
for j in range(n):
user_id = self.user_ids[i]
item_id = self.item_ids[j]
rating = ratings[user_id][item_id]
if rating > 0:
user_row = self.user_matrix.col(i).transpose
item_col = self.item_matrix.col(j)
rating_hat = user_row.mat_mul(item_col).data[0][0]
square_error = (rating - rating_hat) ** 2
mse += square_error / n_elements
return mse ** 0.5```

2.7 训练模型

1.数据预处理 2.变量k合法性检查 3.生成随机矩阵U 4.交替计算矩阵U和矩阵I，并打印RMSE信息，直到迭代次数达到max_iter 5.保存最终的RMSE

```def fit(self, X, k, max_iter=10):
ratings, ratings_T = self._process_data(X)
self.user_items = {k: set(v.keys()) for k, v in ratings.items()}
m, n = self.shape

error_msg = "Parameter k must be less than the rank of original matrix"
assert k < min(m, n), error_msg

self.user_matrix = self._gen_random_matrix(k, m)

for i in range(max_iter):
if i % 2:
items = self.item_matrix
self.user_matrix = self._items_mul_ratings(
items.mat_mul(items.transpose).inverse.mat_mul(items),
ratings
)
else:
users = self.user_matrix
self.item_matrix = self._users_mul_ratings(
users.mat_mul(users.transpose).inverse.mat_mul(users),
ratings_T
)
rmse = self._get_rmse(ratings)
print("Iterations: %d, RMSE: %.6f" % (i + 1, rmse))

self.rmse = rmse```

2.8 预测一个用户

```def _predict(self, user_id, n_items):
users_col = self.user_matrix.col(self.user_ids_dict[user_id])
users_col = users_col.transpose

items_col = enumerate(users_col.mat_mul(self.item_matrix).data[0])
items_scores = map(lambda x: (self.item_ids[x[0]], x[1]), items_col)
viewed_items = self.user_items[user_id]
items_scores = filter(lambda x: x[0] not in viewed_items, items_scores)

return sorted(items_scores, key=lambda x: x[1], reverse=True)[:n_items]```

2.9 预测多个用户

```def predict(self, user_ids, n_items=10):
return [self._predict(user_id, n_items) for user_id in user_ids]```

3 效果评估

3.1 main函数

```@run_time
def main():
print("Tesing the accuracy of ALS...")

model = ALS()
model.fit(X, k=3, max_iter=5)
print()

print("Showing the predictions of users...")

user_ids = range(1, 5)
predictions = model.predict(user_ids, n_items=2)
for user_id, prediction in zip(user_ids, predictions):
_prediction = [format_prediction(item_id, score)
for item_id, score in prediction]
print("User id:%d recommedation: %s" % (user_id, _prediction))```

3.2 效果展示

3.3 工具函数

1.run_time - 测试函数运行时间

ALS的原理：鸡生蛋、蛋生鸡

ALS的实现：基本上就是矩阵乘法

0 条评论

• ### 实现属于自己的TensorFlow(一) - 计算图与前向传播

前言 前段时间因为课题需要使用了一段时间TensorFlow，感觉这种框架很有意思，除了可以搭建复杂的神经网络，也可以优化其他自己需要的计算模型，所以一直想自...

• ### GBDT回归的原理及Python实现

提到GBDT回归相信大家应该都不会觉得陌生，本文就GBDT回归的基本原理进行讲解，并手把手、肩并肩地带您实现这一算法。完整实现代码请参考本人的github。

• ### 用Python模拟登录学校教务系统抢课

-- Illustrations by Vladislav Solovjov --

• ### 协同过滤的原理及Python实现

作者：李小文，先后从事过数据分析、数据挖掘工作，主要开发语言是Python，现任一家小型互联网公司的算法工程师。

• ### 一个例子带你入门Python装饰器

在还未正式发布的python3.9中，有一个新功能值得关注，那就是任意表达式可以作为装饰器，如果你还不知道装饰器是什么，没关系，跟着本文一个例子搞明白，不过需要...

• ### 利用Python自制扫雷游戏

这次，我们来模仿做一个 XP 上的扫雷，感觉 XP 上的样式比 win7 上的好看多了。

• ### python 匿名代理访问浏览器

import mechanize import cookielib import random

• ### 基于Django的电子商务网站开发（连载25）

购物车模块包括“购物车中所有商品的显示”“添加商品进入购物车”“删除购物车中某种商品”“删除购物车中所有的商品”和“修改购物车中某种商品的数量”。