推荐系统(2)-协同过滤1-UserCF、ItemCF

协同过滤

  • 1.CF概述
  • 2.数据表示
  • 3.衡量相似度
  • 4.共现矩阵
  • 5.UserCF
  • 6.ItemCF
  • 7.UserCF 与ItemCF 应用场景、主要缺陷
  • 8.基于UserCF 电影推荐demo

《深度学习/推荐系统》读书笔记

推荐系统的发展一日千里
传统的推荐模型(2010年前后):协同过滤、罗辑回归、因子分解、梯度提升树
深度学习推荐模型(2015年后):…

在深度学习推荐模型成为推荐、广告、搜索领域的主流,但是传统推荐模型学习十分必要。因其为深度学习推荐模型的基础,并且具备可解释性强、硬件环境要求低,易于快速训练和部署等不可替代的优势。

1.CF概述

协同过滤–Collaborative Filtering(CF)

  1. 1992年–Xerox研发基于协同过滤的邮件筛选系统
  2. 2003年–Amazon发表论文《Amazon.com Recommenders Item-to-Item Collaborative Filtering》,促使CF成为使用热点

基本思想:相似的人有相似的行为(购买),相似的物品有相似的属性(被喜欢)。
基本流程:数据表示、定义相似、找相似TopN、按照相似候选集进行推荐决策。

三类协同过滤算法:

  1. 用户协同过滤(UserCF):目标用户与相似用户的喜好相似
  2. 物品协同过滤(ItemCF):给用户推荐有正反馈物品的相似物品
  3. 矩阵分解协同过滤(Matrix Factorization,MF):解决共现矩阵稀疏的问题。

2.数据表示

特征向量表示用户/物品。
常用可解释特征:

  1. 用户向量–购买行为,评价行为,性别,年龄
  2. 物品向量–商品的类别,商品描述,用户反馈信息

数据的高级语义特征,拓扑结构特征需要专门建模分析。
挖掘数据特征表示本就是一个重要的研究方向吧。

3.衡量相似度

向量本身具有长度和方向两个属性,当存在两个向量相互作用时,还存在向量间夹角的问题。因此可以长度、夹角等不同的侧重点,定义不同的相似性度量。

常用的相似性度量指标

  1. 距离度量–考察差异向量的长度 ∣ ∣ x − y ∣ ∣ p ||x-y||_p xyp–1范数、2范数、无穷范数
  2. 余弦相似度:考察特征向量之间的夹角(x,y)– c o s ( x , y ) = < x , y > ∣ ∣ x ∣ ∣ ∗ ∣ ∣ y ∣ ∣ cos(x,y) = \frac{}{||x||*||y||} cos(x,y)=xy<x,y>
  3. 皮尔逊相关系数:与推荐内容十分相关的定义式

4.共现矩阵

是用户和商品对某一属性行为的矩阵表示,例如购买,点击,好评等行为。(缺陷就是只能表示一种属性。)

简单举例:以用户作为矩阵行坐标,商品作为矩阵的列坐标。矩阵中的元素就是某个用户是否购买了某件商品。

5.UserCF

问题:是否推荐物品A给用户X

  1. 收集各用户对商品库物品购买行为的共现矩阵(矩阵中缺失值的处理:取平均,直接去除)
  2. 计算TopN 相似用户
  3. 依据TopN 相似用户对物品A的购买行为,确定是否推荐物品A给用户X(意见不统一的采用投票法,评分加权平均法)

主要缺陷:

  1. 用户数远大于物品数,维护用户相似度矩阵的存储开销大。(两两相似N^2的空间)
  2. 用户历史数据非常稀疏,只有几次购买或者点击行为的用户很难准确找到相似用户。

6.ItemCF

实际在应用过程中,Amazon 和 Netflix 采用的是ItemCF 构建推荐系统。
通过计算物品列向量的相似度–》物品间相似度矩阵

  1. 构建共现矩阵[m*n]–m个用户, n件商品;
  2. 计算共现矩阵列向量之间的两两相似度,构建[n*n]物品相似度矩阵;
  3. 获取目标用户历史行为中有正反馈的物品列表;
  4. 利用物品相似度矩阵找出相似的TopK物品,构成相似物品集合;
  5. 依据相似度得分,生成最终推荐列表

强调:相似物品集合是 目标用户有正反馈的物品的相似物品
注意:多个正反馈物品相似于同一个物品,这个物品的相似度得分取多个相似度的加权平均值。

7.UserCF 与ItemCF 应用场景、主要缺陷

  1. UserCF:用户相似度,使其具备更强的社交特性,得知自己的同类最近喜欢什么,适用于新闻推荐场景,发现热点跟踪人点趋势。
  2. ItemCF:适用于兴趣变化较为稳定的应用,用户在一个时间段倾向于寻找类似上商品。电影,书籍,电视剧这种喜好风格,一段时间内变化比较小。

主要缺陷:推荐结果的头部效应比较明显,处理稀疏向量的能力比较弱

  1. 泛化能力弱,无法将两个物品相似推广到其他物品相似的计算上
  2. 热门商品具有很强的头部效应,更大家都相似。
  3. 而尾部商品由于特征向量稀疏,跟大家都不相似,导致很少被推荐。

为了解决上述问题,同时增加模型的泛化能力,矩阵分解技术被提出来(下一篇讲)
用更稠密的隐向量表示用户和物品,挖掘用户的隐藏兴趣和隐藏特征。

8.基于UserCF 电影推荐demo

(想找一个有关于推荐系统所有算法的repository,没有找到合适的,尤其是关于经典推荐算法。动手敲一敲呗。)
数据:用户对电影的评分
数据格式:

# [用户ID,电影ID,电影评分,时间标签] 8W条数据
1  1  2  3  876893171
2  1  3  4  878542960
3  1  4  3  876893119
4  1  5  3  889751712

基本思路:预测用户未评分的电影评分,给该每个用户推荐预测评分最高的TopN 个电影。预测的依据–相似用户对该电影评分的加权平均效果。
代码参考《python与数据挖掘》第10章
demo代码与数据

# 20210424 UserCF demo
# movie recommend
import pandas as pd


def prediction(df, userdf, Nn=15):
    # 预测用户未评分电影的评分
    corr = df.T.corr()     # 计算用户的相关person相关系数矩阵
    rats = userdf.copy()
    for usrid in userdf.index:
        print(usrid)
        # step1:获取用户未评分电影
        dfnull = df.loc[usrid][df.loc[usrid].isnull()]    # 用户user1没有评分的电影:('mov6',nan)
        usrv = df.loc[usrid].mean()                       # 用户user1电影评分的均值
        # step2: 预测未评分电影的分值
        for i in range(len(dfnull)):
            nft = (df[dfnull.index[i]]).notnull()         # 用户user1没有评分的电影,其他人评分与否
            if(Nn <= len(nft)):
                nlist = df[dfnull.index[i]][nft][:Nn]     # 用户user1没有评分的电影,前Nn有评分的人
            else:
                nlist = df[dfnull.index[i]][nft][:len(nft)]   # len(df[dfnull.index[i]][nft]) < len(nft), 有啥用呢
            # 1)获取非null相关系数,有评分人列表
            nlist = nlist[corr.loc[usrid, nlist.index].notnull()]  # 用户user1 和 有评分人的非null 相关系数的评分人列表
            nratsum, corsum = 0, 0
            if(0!=nlist.size):
                nv = df.loc[nlist.index,:].T.mean()         # 相关有评分人对所有电影的评分的平均值
                for index in nlist.index:                   # 相关评论人userx
                    ncor = corr.loc[usrid, index]           # 用户user1 和 userx相关系数
                    nratsum += ncor*(df[dfnull.index[i]][index]-nv[index])   # ncor*(df['mov6'][userx]-nv[userx])
                    corsum += abs(ncor)
                if(corsum != 0):
                    rats.at[usrid, dfnull.index[i]] = usrv + nratsum/corsum   # 预测用户user1对没有评分电影的评分
                else:
                    rats.at[usrid, dfnull.index[i]] = usrv                    # 无其他用户评分修正的情况下,用自己的评分均值填补
            else:
                rats.at[usrid,dfnull.index[i]] = None
    return rats
def recomm(df, userdf, Nn=15, TopN=3):
    # 依据为未评分电影预测评分,给出每个用户的推荐列表。
    ratings = prediction(df, userdf, Nn)
    recomm = []
    for usrid in userdf.index:
        # 按Nan值获取未评分项
        ratft = userdf.loc[usrid].isnull()
        ratnull = ratings.loc[usrid][ratft]
        # 对预测评分项进行排序
        if(len(ratnull) >= TopN):
            sortlist = (ratnull.sort_values(ascending=False)).index[:TopN]
        else:
            sortlist = ratnull.sort_values(ascending=False).index[:len(ratnull)]
        recomm.append(sortlist)
    return ratings,recomm



if __name__ == "__main__":
    print("------使用基于UserCF算法对电影进行推荐中...-----")
    traindata = pd.read_csv("./data/Chapter10/u1.base", sep='\t', index_col=None, header=None)  # [用户ID,电影ID,电影评分,时间标签] 8W条数据
    print(traindata.head())
    testdata = pd.read_csv("./data/Chapter10/u1.test", sep='\t', index_col=None, header=None)
    traindata = traindata[:1000]
    testdata = testdata[:1000]
    # 删除时间列--本例中没有用
    traindata.drop(3, axis=1, inplace=True)
    testdata.drop(3, axis=1, inplace=True)
    # 行与列重新命名
    traindata.rename(columns={0: 'userid', 1: 'movid', 2: 'rat'}, inplace=True)
    testdata.rename(columns={0: 'userid', 1: 'movid', 2: 'rat'}, inplace=True)
    # 整理成共现矩阵
    traindf = traindata.pivot(index='userid', columns='movid', values='rat')
    testdf = testdata.pivot(index='userid', columns='movid', values='rat')
    # 重命名表格的行和列
    traindf.rename(index={i: 'user%d'%i for i in traindf.index}, inplace=True)
    testdf.rename(index={i: 'user%d'%i for i in testdf.index}, inplace=True)
    traindf.rename(columns={i: 'mov%d'%i for i in traindf.columns}, inplace=True)
    testdf.rename(columns={i: 'mov%d'%i for i in testdf.columns}, inplace=True)
    print('d', traindata.head())
    userdf = traindf.loc[testdf.index]
    trainrats, trainrecom = recomm(traindf, userdf)
    print(trainrecom[:1])
    print(len(trainrecom))
    print('end')

输出:

# 每个用户的推荐电影列表,每人推荐3部
[Index(['mov189', 'mov396', 'mov390'], dtype='object', name='movid')]

你可能感兴趣的:(推荐系统)