初学推荐系统-02-协同过滤 (UserCF & ItermCF) -附简单示例和优缺点分析

1. 协同过滤算法是什么?

协同过滤(Collaborative Filtering)推荐算法是最经典、最常用的推荐算法。
1.1 基本思想

  • 根据用户的之前的喜好以及其他兴趣相近的选择来给用户推荐物品(基于对用户历史数据的挖掘,发现用户的喜欢偏好,进而预测用户可能喜欢的产品进行推荐)。
  • 一般仅仅基于用户的历史行为数据,不依赖于其他任何附加项的信息。

1.2 目前比较广泛的CF算法是基于邻域的方法,如下:

  • 基于用户的协同过滤算法(UserCF):给用户推荐和他兴趣相似的其他用户喜欢的产品
  • 基于物品的协同过滤算法(ItermCF):给用户推荐和他之前喜欢的物品相似的物品

2. 相似性度量方法

2.1 杰卡德(Jaccard)相似系数

这是衡量两个集合的相似度的一种指标,两个用户u和V交互商品交集占并集的数量的比值;称为两个集合的杰卡德相似系数,用符号sim_{uv}表示,其中N(u),N(v)分别表示用户u和用户v交互商品的集合。

[sim_{uv}=frac{|N(u) cap N(v)|}{{|N(u)| cup|N(v)|}} ]

常用来评估某用户是否会对某商品进行打分.

2.2 余弦相似度

余弦值在[0, π]的区间范围单调递减,故余弦值越小,角度越大;反之,余弦值越大,角度越小.
两个向量的余弦值的计算公式

故在集合的角度来描述交互的商品数量的乘积, 公式如下:

[sim_{uv} = frac{|N(u) · N(v)|} {sqrt{{|N(u)| * |N(v)|}}} ]

这个在具体实现的时候, 可以使用sklearn库的cosine_similarity进行实现:

from sklearn.metrics.pairwise import cosine_similarity
i = [1, 0, 0, 0]
j = [1, 0.5, 0.5, 0]
consine_similarity([a, b])

2.3 皮尔逊相关系数
相比余弦相似度,皮尔逊相关系数通过使用用户的品均分对各个独立评分进行了修正,减少了用户评分偏置的影响(如用户A习惯打高分/用户B习惯只打低分).

调包实现皮尔逊评分:

from scipy.stats import pearsonr

i = [1, 0, 0, 0]
j = [1, 0.5, 0.5, 0]
pearsonr(i, j)

3. 基于用户的协同过滤(UserCF) : 猜测用户对某件物品的喜欢程度

图片
**UserCF包括两个步骤**: 1. 找到和目标用户兴趣相似的人群集合 2. 从中找到这个集合汇总的用户喜欢的,且目标用户没有听说过的物品推荐给目标用户. 3. 根据这n个用户对准备推荐的物品的评分情况和与目标用户的相似程度会猜测出目标用户对物品的评分, 如果评分比较高的话, 就把物品推荐给目标用户, 否则不推荐。

最终结果的预测 加权近似估算

利用用户相似度和相似用户的评价加权平均获得用户的评价预测, 用下面式子表示:
(u,p为用户, s为商品, 权重w_{u,s}是用户u和用户s的相似度, R_{s,p}是用户s对物品p的评分。)

[R_{mathrm{u}, mathrm{p}}=frac{sum_{mathrm{s} in S}left(w_{mathrm{u}, mathrm{s}} cdot R_{mathrm{s}, mathrm{p}} ight)}{sum_{mathrm{s} in S} w_{mathrm{u}, mathrm{s}}} ]

优化版本: 用户相似度作为权值, 但后面不单纯的是其他用户对物品的评分, 而是该物品的评分与此用户的所有评分的差值进行加权平均, 这时候考虑到了有的用户内心的评分标准不一的情况, 即有的用户喜欢打高分, 有的用户喜欢打低分的情况。

[P_{i, j}=ar{R}_{i}+frac{sum_{k=1}^{n}left(S_{i, k}left(R_{k, j}-ar{R}_{k} ight) ight)}{sum_{k=1}^{n} S_{j, k}} ]

动手计算

计算余弦相似性

from sklearn.metrics.pairwise import cosine)similarity

计算协相关性

import numpy as np
np.corrcoef([...])

4. UserCF编程实现

总结简单三步骤: 计算用户相似性矩阵、得到前N个用户相似用户、计算最终得分。

    # **4.1 首先, 准备物品-人员 历史评分表**
    items = loadData()
    item_df = pd.DataFrame(items).T
    print(item_df)
    """
         1    2    3    4    5
    A  5.0  3.0  4.0  3.0  1.0
    B  3.0  1.0  3.0  3.0  5.0
    C  4.0  2.0  4.0  1.0  5.0
    D  4.0  3.0  3.0  5.0  2.0
    E  NaN  3.0  5.0  4.0  1.0
    """
    # **4.2 计算用户相似性矩阵**
    user_similar_matrix = pd.DataFrame(np.zeros((5, 5)), index=['A', 'B', 'C', 'D', 'E'], columns=['A', 'B', 'C', 'D', 'E'])
    pos_dict = 'ABCDE'

    len = item_df.shape[0]
    for i in range(0, len):
        for j in range(0, len):
            if i==j: continue
            ith = np.array(item_df.iloc[i,:])
            jth = np.array(item_df.iloc[j,:])
            # 去除两者之一有None的商品
            for ienum in range(ith.size - 1, -1, -1):
                if np.isnan(ith[ienum]) or np.isnan(jth[ienum]):
                    ith = np.delete(ith, ienum)
                    jth = np.delete(jth, ienum)
            # print(ith, '-', jth)
            x = np.corrcoef(ith, jth, rowvar=False)
            # print(x)
            user_similar_matrix[pos_dict[i]][pos_dict[j]] = x[0][1]
    print(user_similar_matrix)

    """
              A         B         C         D         E
    A  0.000000 -0.476731 -0.123091  0.532181  0.969458
    B -0.476731  0.000000  0.645497 -0.310087 -0.478091
    C -0.123091  0.645497  0.000000 -0.720577 -0.427618
    D  0.532181 -0.310087 -0.720577  0.000000  0.581675
    E  0.969458 -0.478091 -0.427618  0.581675  0.000000
    """

    # **4.3 计算得出用户E的最相似的几个用户**
    n=3
    similarity_users = user_similar_matrix['E'].sort_values(ascending=False)[:n].index.tolist()[0:-1]
    print('similarity_items:
', similarity_users)
    #  ['A', 'D', 'E']


    # **4.4 近似加权求解** 只针对商品'1'
    """计算最终得分"""
    base_score = np.mean(item_df[1])
    weighted_scores = 0.
    corr_values_sum = 0.
    for user in similarity_users:  # [2, 3]
        corr_value = user_similar_matrix[user][1]            # 两个用户之间的相似性
        mean_user_score = np.mean(item_df.loc[user])    # 每个用户的打分平均值
        weighted_scores += corr_value * (item_df.loc[user][1]-mean_user_score)      # 加权分数
        corr_values_sum += corr_value
    final_scores = base_score + weighted_scores / corr_values_sum
    print('用户E对物品1的打分: ', final_scores)
    item_df.loc['E'][1] = final_scores
    print(item_df)
    """
    similarity_items:  ['A', 'D']
    
    用户E对物品1的打分:  5.327077237976979
              1    2    3    4    5
    A  5.000000  3.0  4.0  3.0  1.0
    B  3.000000  1.0  3.0  3.0  5.0
    C  4.000000  2.0  4.0  1.0  5.0
    D  4.000000  3.0  3.0  5.0  2.0
    E  5.327077  3.0  5.0  4.0  1.0
    """

5. UserCF优缺点

优点:

  • 适用于用户规模较少,不需要掌握专门的领域知识
  • 可以帮助用户发现潜在的兴趣点(基于相似人群的兴趣数据)

缺点:

  • 数据稀疏性: 稀疏;一个网站的商品数量远大于用户数量,不同用户之间购买的商品重叠性极低;导致算法无法找到合适的用户邻居,即根据共同的购买经历来确定的相似用户。(这导致UserCF不适用于那些正反馈获取较为困难的应用场景,如酒店预订,大件商品的购买等低频应用)
  • 算法扩展性:随着用户数量剧增后变差

    基于用户的协同过滤需要维护用户相似度矩阵,以便于快速地找出TopN相似的用户,随着用户数量——平方倍数的增加,也不适用于用户量特别大的情况下使用。
  • 无法冷启动,处理新的项目
    综上,用于UserCF上的几点缺陷,导致很多电商平台没有使用这种算法,而是采用ItermCF的算法来实现最初的推荐系统。

6. 基于物品的协同过滤

基本思想

ItemCF不计算物品的属性相似度,而是根据用户的历史偏好数据来计算物品之间的相似性,比如很多人买了A之后也买了B,那么推荐用户A在买了A之后,也推荐B。
该算法认为, 物品a和物品c具有很大的相似度是因为喜欢物品a的用户大都喜欢物品c。

图片
**概要计算步骤**
- 根据用户的历史行为数据, 计算物品之间的相似度 - 根据物品的相似度和用户的历史行为数据来给用户生成推荐列表 (购买了该商品的用户也经常购买了其他商品) 图片

ItemCF步骤

  • 将每个物品的评分集合,作为一个向量
  • 计算向量之间最近的几个物品
  • 找出需要评分的物品的最相近的几个物品去进行打分

ItermCF代码实践

总结简单三步骤: 计算物品相似性矩阵、得到前N个相似物品、计算最终得分。

    # **6.1 首先, 准备物品-人员 历史评分表**
    items = loadData()
    item_df = pd.DataFrame(items).T
    print(item_df)
    """
         1    2    3    4    5
    A  5.0  3.0  4.0  3.0  1.0
    B  3.0  1.0  3.0  3.0  5.0
    C  4.0  2.0  4.0  1.0  5.0
    D  4.0  3.0  3.0  5.0  2.0
    E  NaN  3.0  5.0  4.0  1.0
    """

    #  6.2 计算物品相似性矩阵
    iterm_similar_matrix = pd.DataFrame(np.zeros((5, 5)), index=['1', '2', '3', '4', '5'], columns=['1', '2', '3', '4', '5'])
    pos_dict = '12345'

    len = item_df.shape[0]
    for i in range(0, len):
        for j in range(0, len):
            if i==j: continue
            ith = np.array(item_df[i+1])
            jth = np.array(item_df[j+1])
            # 去除两者之一有None的商品
            for ienum in range(ith.size - 1, -1, -1):
                if np.isnan(ith[ienum]) or np.isnan(jth[ienum]):
                    ith = np.delete(ith, ienum)
                    jth = np.delete(jth, ienum)
            # print(ith, '-', jth)
            x = np.corrcoef(ith, jth, rowvar=False)
            # print(x)
            iterm_similar_matrix[pos_dict[i]][pos_dict[j]] = x[0][1]
    print(iterm_similar_matrix)
    """
              1         2         3         4         5
    1  0.000000  0.852803  0.707107  0.000000 -0.792118
    2  0.852803  0.000000  0.467707  0.489956 -0.900149
    3  0.707107  0.467707  0.000000 -0.161165 -0.466569
    4  0.000000  0.489956 -0.161165  0.000000 -0.641503
    5 -0.792118 -0.900149 -0.466569 -0.641503  0.000000
    """

    # **6.3 计算得出和物品1的最相似的几个物品**
    n=3
    similarity_items = iterm_similar_matrix['1'].sort_values(ascending=False)[:n].index.tolist()[0:-1]
    print('similarity_items:
', similarity_items)
    # ['2', '3']

    # 其他同UserCF
    # **4.4 近似加权求解** 只针对商品'1'
    """计算最终得分"""
    base_score = np.mean(item_df[1])
    weighted_scores = 0.
    corr_values_sum = 0.
    for item in similarity_items:  # [2, 3]
        item = int(item)
        corr_value = iterm_similar_matrix[str(item)][1]   # 两个物品之间的相似性
        mean_user_score = np.mean(item_df[item])    # 每个物品的打分平均值
        weighted_scores += corr_value * (item_df.loc['E'][item] - mean_user_score)      # 加权分数
        corr_values_sum += corr_value
    final_scores = base_score + weighted_scores / corr_values_sum
    print('物品2,3 对物品1的打分: ', final_scores)
    item_df.loc['E'][1] = final_scores
    print(item_df)
    """
    物品2,3 对物品1的打分:  5.2
         1    2    3    4    5
    A  5.0  3.0  4.0  3.0  1.0
    B  3.0  1.0  3.0  3.0  5.0
    C  4.0  2.0  4.0  1.0  5.0
    D  4.0  3.0  3.0  5.0  2.0
    E  5.2  3.0  5.0  4.0  1.0
    """

7. 算法评估&评测指标 (集合法)

基于用户或物品的协同过滤的评测指标基本一致,如下:

  • R表示投放的推荐商品
  • T表示实际喜欢的物品
  • R ∩ T = 推荐成功的
  1. 召回率 (较全面的体现)

[operatorname{Recall}=frac{sum_{u}|R(u) cap T(u)|}{sum_{u}|T(u)|} ]

  1. 精确率 (在推荐次数有限时使用,需要有把握地推荐)

[operatorname{Recall}=frac{sum_{u}|R(u) cap T(u)|}{sum_{u}|R(u)|} ]

  1. 覆盖率
    覆盖率反映了推荐算法发掘长尾的能力, 覆盖率越高, 说明推荐算法越能将长尾中的物品推荐给用户。

[ ext { Coverage }=frac{left|igcup_{u in U} R(u) ight|}{|I|} ]

  1. 新颖度
    用推荐列表中物品的平均流行度度量推荐结果的新颖度。 如果推荐出的物品都很热门, 说明推荐的新颖度较低. 由于物品的流行度分布呈长尾分布, 所以为了流行度的平均值更加稳定, 在计算平均流行度时对每个物品的流行度取对数。

8. 协同过滤算法的权重改进

image-20200923122142218

  • 基础算法
    图1为最简单的计算物品相关度的公式, 分子为同时喜好itemi和itemj的用户数.
  • 对热门物品的惩罚 (避免过热的商品带偏
    图1存在一个问题, 如果 item-j 是很热门的商品,导致很多喜欢 item-i 的用户都喜欢 item-j,这时 w_{ij} 就会非常大。同样,几乎所有的物品都和 item-j 的相关度非常高,这显然是不合理的。所以图2中分母通过引入 N(j) 来对 item-j 的热度进行惩罚。如果物品很热门, 那么N(j)就会越大, 对应的权重就会变小。
  • 对热门物品的进一步惩罚
    如果 item-j 极度热门,上面的算法还是不够的。举个例子,《Harry Potter》非常火,买任何一本书的人都会购买它,即使通过图2的方法对它进行了惩罚,但是《Harry Potter》仍然会获得很高的相似度。这就是推荐系统领域著名的 Harry Potter Problem。
    如果需要进一步对热门物品惩罚,可以继续修改公式为如图3所示,通过调节参数 α,α 越大,惩罚力度越大,热门物品的相似度越低,整体结果的平均热门程度越低。
  • 对活跃用户的惩罚
    同样的,Item-based CF 也需要考虑活跃用户(即一个活跃用户(专门做刷单)可能买了非常多的物品)的影响,活跃用户对物品相似度的贡献应该小于不活跃用户。图4为集合了该权重的算法。

9. 协同过滤算法的问题分析

协同过滤算法存在的问题之一就是泛化能力弱, 即协同过滤无法将两个物品相似的信息推广到其他物品的相似性上。 导致的问题是热门物品具有很强的头部效应, 容易跟大量物品产生相似, 而尾部物品由于特征向量稀疏, 导致很少被推荐。

协同过滤的天然缺陷:推荐系统头部效应明显, 处理稀疏向量的能力弱。

矩阵分解技术(Matrix Factorization,MF)被提出, 该方法在协同过滤共现矩阵的基础上, 使用更稠密的隐向量表示用户和物品, 挖掘用户和物品的隐含兴趣和隐含特征, 在一定程度上弥补协同过滤模型处理稀疏矩阵能力不足的问题。

10. 参考资料

你不逼自己一把,你永远都不知道自己有多优秀!只有经历了一些事,你才会懂得好好珍惜眼前的时光!
原文地址:https://www.cnblogs.com/zhazhaacmer/p/13861483.html