协同过滤(Collaborative Filtering)

  1. 协同过滤推荐(Collaborative Filtering Recommendation):该算法的核心是分析用户的兴趣和行为,利用共同行为习惯的群体有相似喜好的原则,推荐用户感兴趣的信息。
    1. 基于用户的协同过滤:找到和用户购买喜好相同的一批人,把相同爱好人群喜好的商品推荐给小王。
    2. 基于物品的协同过滤:找到物品类似的几个物品,如果用户喜欢其中某个物品,则将和该物品类似的物品推荐给用户。

下面通过一个例子,来理解使用协同过滤来实现物品推荐的简单思路。

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 个用户,结合这两个用户对该物品评分来估计出缺失分数,计算公式如下:

  1. U 与当前 user 最相似的多个用户,u 表示其中某一个用户;
  2. sim(u, user) 表示 u 与 user 的相似程度;
  3. \(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 个物品,结合这结合用户对这两个相似物品的评分来估计出未知物品的分数,计算公式如下:

  1. I 与当前 item 最相似的多个物品,i 表示其中某一个物品;
  2. sim(i, item) 表示 i 与 item 的相关程度;
  3. \(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. 协同过滤推荐

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

我们这里使用皮尔逊相关系数。

未经允许不得转载:一亩三分地 » 协同过滤(Collaborative Filtering)
评论 (0)

3 + 6 =