- 协同过滤推荐(Collaborative Filtering Recommendation):该算法的核心是分析用户的兴趣和行为,利用共同行为习惯的群体有相似喜好的原则,推荐用户感兴趣的信息。
- 基于用户的协同过滤:找到和用户购买喜好相同的一批人,把相同爱好人群喜好的商品推荐给小王。
- 基于物品的协同过滤:找到物品类似的几个物品,如果用户喜欢其中某个物品,则将和该物品类似的物品推荐给用户。
下面通过一个例子,来理解使用协同过滤来实现物品推荐的简单思路。
1. 数据构建
首先构建一个用户对物品的评分数据,数据中会包含一些 np.nan 表示的缺失值。这些缺失分数,我们可以使用 CF 来进行估计。
import pandas as pd
import numpy as np
def test():
# 数据表示 5 个用户,对 8 个物品的评分
np.random.seed(6)
hisrory = np.random.randint(0, 8, size=[5, 8]).astype(object)
# 随机在分数中构造缺失值
size = 4
i = np.random.randint(0, hisrory.shape[0], size=size)
j = np.random.randint(0, hisrory.shape[1], size=size)
hisrory[i, j] = np.nan
# 转换类型
index = ['user' + str(i + 1) for i in range(hisrory.shape[0])]
colum = ['item' + str(i + 1) for i in range(hisrory.shape[1])]
hisrory = pd.DataFrame(hisrory, index=index, columns=colum)
# 数据存储
hisrory.to_csv('data.csv')
if __name__ == '__main__':
test()
程序生成的数据内容如下:

2. User Based CF 评分估计
我们这里根据用户之间,或者商品之间的相关程度来估计出缺失值。首先计算出用户之间的相关程度。从相关程度最高的所有用户中选择前 2 个用户,结合这两个用户对该物品评分来估计出缺失分数,计算公式如下:

- U 与当前 user 最相似的多个用户,u 表示其中某一个用户;
- sim(u, user) 表示 u 与 user 的相似程度;
- \(r_{u, item}\) 表示 u 用户对 item 的评分
基本的思想就是,我虽然不知道 user1 的 item4 的评分多少,但是与我相似的用户对 item4 有评分,我就结合他们的分数,再使用上面的公式估计出未知的评分。
注意:这里的相关性用的是皮尔逊相关系数。相关系数的实现代码:
import pandas as pd
import numpy as np
# 物品相似度: 皮尔逊相关系数
def test01():
history = pd.read_csv('data.csv', index_col=0)
correlation = history.corr(method="pearson")
print(correlation)
correlation.to_csv('item.csv')
# 用户相似度: 皮尔逊相关系数
def test02():
history = pd.read_csv('data.csv', index_col=0)
correlation = history.transpose().corr(method="pearson")
print(correlation)
correlation.to_csv('user.csv')
if __name__ == '__main__':
test01()
print('-' * 80)
test02()
接下来,根据相关系数基于用户 CF 来估计缺失值:
def test01():
data = pd.read_csv('data.csv', index_col=0)
print(data)
user = pd.read_csv('user.csv', index_col=0)
print(user)
for i in range(data.shape[0]):
user_item = data.iloc[i]
user_mask = pd.isna(user_item)
# 判断是否存在缺失值
if np.any(user_mask):
# 将所有用户根据相关性进行降序排列
sorted_users = user.iloc[i].drop(user_item.name).sort_values(ascending=False)[:2]
# 获得相关性最强的用户
close_users = sorted_users.index
# 获得对应的相关性值
close_score = sorted_users.values
# 从 data 中获得相关性最强用户的对应缺失值列的分数
close_item = data.loc[close_users, user_mask].values
# 根据公式计算分子
numerator = np.sum(close_item * close_score[None].transpose(), axis=0)
# 计算根据公式分母
denominator = np.sum(close_score)
# 计算得到估计的缺失分数
estimate_score = np.round(numerator / denominator, decimals=2)
# 将估计分数填充到 data 数据中
data.loc[user_item.name, user_mask] = estimate_score
print(data)
data.to_csv('user_data.csv')
得到的结果是:

3. Item Based CF 评分估计
我们这里根据物品之间的相关程度来估计出缺失值。首先计算出物品之间的相关程度。从相关程度最高的所有物品中选择前 2 个物品,结合这结合用户对这两个相似物品的评分来估计出未知物品的分数,计算公式如下:

- I 与当前 item 最相似的多个物品,i 表示其中某一个物品;
- sim(i, item) 表示 i 与 item 的相关程度;
- \(r_{user, i}\) 表示用户 user 对物品 i 的评分。
注意:具体在实现时,由于原数据中某一列值全部相同的话,会导致皮尔逊相关系数为 NaN。对于这种情况,我们就使用相同的值来填充缺失值。例如:user5 的 item8 由于该列值都是 2,这里也就用 2 来填充缺失值。
def test02():
data = pd.read_csv('data.csv', index_col=0)
print(data)
print('-' * 80)
item = pd.read_csv('item.csv', index_col=0)
print(item)
print('-' * 80)
for i in range(data.shape[0]):
user_item = data.iloc[i]
user_mask = pd.isna(user_item)
# 判断是否存在缺失值
if np.any(user_mask):
# 获得包含缺失值的物品
item_nans = user_item[user_item.isna()].index
for item_nan in item_nans:
# 获得该物品的所有相似度并排序
similarity = item.loc[item_nan].drop(item_nan).sort_values(ascending=False)
# 获得相似的前两个物品
similarity = similarity[:2]
# 判断相似度是否全为 NaN
if np.all(pd.isna(similarity)):
data.loc[user_item.name, item_nan] = data[item_nan].mean()
continue
# 获得最相似两个物品名称
simi_names = similarity.index
# 获得最相似两个物品相似度
simi_value = similarity.values
# 获得当前用户对相似产品的评分
simi_score = data.loc[user_item.name, simi_names]
# 根据公式计算分子
numerator = np.sum(simi_score * simi_value)
# 计算根据公式分母
denominator = np.sum(simi_value)
# 估计分数
estimate_score = np.round(numerator / denominator, decimals=2)
data.loc[user_item.name, item_nan] = estimate_score
print(data)
data.to_csv('item_data.csv')
得到的结果是:

4. 协同过滤推荐
协同过滤推荐时,无论是基于用户的协同过滤,还是基于商品的协同过滤都需要计算用户之间,或者商品之间的相似程度,计算方法则有很多,比如:欧式距离、余弦相似度、皮尔逊相关系数、杰卡德距离等等。
我们这里使用皮尔逊相关系数。

冀公网安备13050302001966号