读书笔记《推荐系统实战》| 利用用户行为数据

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_40006058/article/details/82958550

2 利用用户行为数据

为了给用户进行物品推荐,首先必须得了解用户的兴趣爱好,一般用户会在注册时给自己打标签。但是利用用户注册信息为用户进行推荐是不合理的:现有的自然语言处理技术不能很好的对用户的自我描述进行准确的理解;用户的兴趣会发生变化,但是却不会动态的更新自我描述;用户自己也知道怎么用语言去表达自己的兴趣爱好,或者不知道自己的爱好;所以我们要根据用户的历史行为对用户进行建模。

2.1 用户行为数据简介

这里的用户行为是指:某个用户对某个商品进行了某种操作,不关注用户或者商品的内容信息(用户性别,商品类别等)。
用户行为一般分为两种:显性反馈行为和隐性反馈行为。显性反馈行为是指可以反映用户对商品是否喜爱的行为,比如用户对电影的评分;隐性反馈行为就是不能反映用户对该商品是否喜好的行为,比如用户在某个时间观看了某个视频。

在这里插入图片描述
行为数据的具体内容。由于用户行为有很多中,无法用一个具体的方式完全的表示,但是大多数用户行为可以从下列几种角度去考虑:

User id 产生行为的用户的唯一标识
Item id 产生行为的对象的唯一标识
Behavior type 行为的种类(购买,浏览等)
Behavior weight 行为的权重。(如果是观看视频,可以用观看时长表示,如果是打分行为,可以用具体的分数表示)
Context 产生行为的上下文,主要是指时间和地点
Behavior content 行为的内容。(如果是评论行为,可以用评论的文本;如果是打标签行为,就是标签本身)

 无 上 下 文 信 息 的 隐 性 反 馈 数 据 集 每 一 条 行 为 记 录 仅 仅 包 含 用 户 ID 和 物 品 ID 。Book-Crossing就是这种类型的数据集。
 无上下文信息的显性反馈数据集 每一条记录包含用户ID、物品ID和用户对物品的评分。
 有上下文信息的隐性反馈数据集 每一条记录包含用户ID、物品ID和用户对物品产生行为的时间戳。 Lastfm数据集①就是这种类型的数据集。
 有上下文信息的显性反馈数据集 每一条记录包含用户ID、物品ID、用户对物品的评分和评分行为发生的时间戳。 Netflix Prize②提供的就是这种类型的数据集。
本章使用的数据集基本都是第一种数据集,即无上下文信息的隐性反馈数据集。

2.2 用户行为分析

在利用用户行为数据前,需要先对用户行为数据做些统计分析,了解数据的一般规律,这样可以对整个推荐系统的算法设计提供先验知识。这里主要考虑的是用户活跃度和商品流行度。用户的获取度是指:历史数据中,用户产生过行为的商品的数量。商品的流行度是指:历史数据中,商品被用户产生的动作的数量。
互联网中的大部分数据都服从长尾分布:F(x)=a*x^k
通过对多个数据集的实验表明:用户活跃度和商品流行度都服从长尾分布。横坐标x表示次数,纵坐标f(x)表示用户数量或者商品数量。
在这里插入图片描述
用户活跃度与商品流行度之间的关系。基于MovieLens数据设计实验:横坐标X表示用户活跃度,纵坐标f(x)表示具有活跃度x的所有用户动作过的商品的平均流行度。发现曲线呈明显下降的趋势,这表示用户越活跃,越倾向于浏览冷门的物品。这符合我们的一般观点:新用户倾向于浏览热门的物品,因为他们对网站还不熟悉,只能点击首页的热门物品,而老用户会逐渐开始浏览冷门的物品。
仅仅基于用户行为数据设计的推荐算法一般称为协同过滤算法。学术界对协同过滤算法进行了深入研究,提出了很多方法,比如基于邻域的方法(neighborhood-based)、 隐语义模型(latent factor model)、 基于图的随机游走算法(random walk on graph)等。在这些方法中,最著名的、在业界得到最广泛应用的算法是基于邻域的方法,而基于邻域的方法主要包含下面两种算法。
 基于用户的协同过滤算法 这种算法给用户推荐和他兴趣相似的其他用户喜欢的物品。
 基于物品的协同过滤算法 这种算法给用户推荐和他之前喜欢的物品相似的物品。

2.3 常用算法,实验设计以及算法评测

实验方法:主要是离线实验。
实验数据:GroupLens提供的MovieLens数据集。
实验设计:将数据按照均匀分布随机分成M份,M-1份用来作为训练集训练模型,最后一份作为测试集测试模型。为了防止模型的过拟合,需要进行M次实验。将M次在测试集上的结果的平均值作为最后的实验结果。

def SplitData(data, M, k, seed):
        test = []
        train = []
        random.seed(seed)
        for user, item in data:
             if random.randint(0,M) == k:
                 test.append([user,item])
             else:
                 train.append([user,item])
        return train, test
 """
每次实验选取不同的k(0≤k≤M-1)和相同的随机数种子seed(保证每次生成的随机数不同),进行M次实验就可以得到M个不同的训练集和测试集,然后分别进行实验,用M次实验的平均值作为最后的评测指标。
这样做主要是防止某次实验的结果是过拟合的结果(over fitting),但如果数据集够大,模型够简单,为了快速通过离线实验初步地选择算法,也可以只进行一次实验。
 """

评价指标:准确率,召回率,覆盖率,新颖度。这里用推荐列表中所以物品的平均流行度来表示新颖度。
在这里插入图片描述
在这里插入图片描述

def Precision(train, test, N):
    hit = 0
    all = 0
    for user in train.keys():
        tu = test[user]
        rank = GetRecommendation(user, N)
        for item, pui in rank: 
            if item in tu:
                 hit += 1
        all += N
    return hit / (all * 1.0)
def Recall(train, test, N):
    hit = 0
    all = 0
    for user in train.keys():
        tu = test[user]
        rank = GetRecommendation(user, N)
        for item, pui in rank:
            if item in tu:
                hit += 1
        all += len(tu)
    return hit / (all * 1.0)

覆盖率反映了推荐算法发掘长尾的 能力,覆盖率越高,说明推荐算法越能够将长尾中的物品推荐给用户。
在这里插入图片描述

def Coverage(train, test, N):
    recommend_items = set()
    all_items = set()
    for user in train.keys():
        for item in train[user].keys():
            all_items.add(item)
        rank = GetRecommendation(user, N)
        for item, pui in rank:
            recommend_items.add(item)
    return len(recommend_items) / (len(all_items) * 1.0)

新颖度这里用推荐列表中物品的平均流行度度量推荐结果的新颖度。如果推荐出的物品都很热门,说明推荐的新颖度较低,否则说明推荐结果比较新颖。

def Popularity(train, test, N):
         item_popularity = dict()
         for user, items in train.items():
              for item in items.keys()
                  if item not in item_popularity:
                      item_popularity[item] = 0
                  item_popularity[item] += 1
         ret = 0
         n=0
         for user in train.keys():
              rank = GetRecommendation(user, N)
              for item, pui in rank:
                  ret += math.log(1 + item_popularity[item])
                  n += 1
         ret /= n * 1.0
         return ret

2.4 基于领域的算法

2.4.1 基于用户的协同过滤算法

基于邻域的算法分为两大类,一类是基于用户的协同过滤算法,另一类是基于物品的协同过滤算法。
基础算法: 推荐算法的目的是为用户u推荐N个商品。为达到这么目的,需要分为3步走,第一步是找到与目标用户u兴趣相似的用户集合V,第二步是找到V中用户喜欢的,但是u没听过的商品集合J,第三步是计算目标用户u对J中商品j的分数Pu(u,j),按照分数排序为用户推荐前N个商品。
用户之间的相似度W(u,v): 主要通过两个用户动作过的物品的重合度来衡量。如果两个用户购买的商品基本都一样,说明两个用户很相似。用户u动作过的商品集合N(u)与用户v动作过的商品集合N(v)的相似度可以用杰卡德相似系数(Jaccard)或者余弦相似度(cos)。分子都一样,是集合的交集。前者的分母是集合的并集,后者的分母是长度的乘积开方。
在这里插入图片描述
相似度的改进:最原始的计算方法认为每个商品都是平等的,所以分子就是商品的数量。其实每个商品的流行度不一样,如果两个用户经常同时购买流行度较低的商品,更加能说明两个人比较相似。这里分子改为: 1/( log( 1+N(j) )).N(j)表示两人同时购买的商品j的流行度。
时间效率的改进:在计算所有用户间的相似度时,每计算两个用户间的距离,都需要遍历整个数据集。假设数据集的长度为L,用户数量为M,那么时间复杂度是O(MML)。将统计结果放入字典C[u][v]中。改进思想:建立物品到用户的倒排表,对于每个物品,保存对该物品产生动作的所有用户。这样只需要对数据进行一次遍历,就可以得到词典dict_item_user[u]。然后对该词典进行M*M次遍历就可以得到所有用户的动作过的商品的交集C[u][v]。
在这里插入图片描述

def UserSimilarity(train):
        # build inverse table for item_users
        item_users = dict()
        for u, items in train.items():
             for i in items.keys():
                 if i not in item_users:
                     item_users[i] = set()
                 item_users[i].add(u)
        #calculate co-rated items between users
        C = dict()
        N = dict()
        for i, users in item_users.items():
             for u in users:
                 N[u] += 1
                 for v in users:
                     if u == v:
                         continue
                     C[u][v] += 1
        #calculate finial similarity matrix W
        W = dict()
        for u, related_users in C.items():
             for v, cuv in related_users.items():
                 W[u][v] = cuv / math.sqrt(N[u] * N[v])
        return W

在这里插入图片描述
目标用户u对商品j的分数:Wuv*Rvj累加。V表示与用户u兴趣最接近的前K个用户,Wuv表示用户间的相似度,Rvj表示用户V对商品j的打分。如果没有打分,按照1计算。

2.4.2 基于物品的协同过滤算法

上面介绍的基于用户的CF算法有个2个很重要的缺点:随着网站的用户数目越来越大,计算用户间的相似度矩阵也越来越困难;推荐结果很难对用户进行解释。于是亚马逊提出了基于物品的CF算法。
基于物品的协同过滤是目前业界应用最多的算法。先介绍最基本的算法,再逐步介绍算法的改进。
基础算法:为目标用户u推荐N个商品。第一步计算物品间的相似度。第二步找出用户u历史喜欢的商品集合I,根据前一步得到的相似度矩阵,获得与每个商品i最相似的商品K个商品集合J(如果不重合,有IJ个商品)。第三步通过计算u对集合J中商品j的分数,根据分数推荐前N个商品给用户u。
商品间的相似度W(i,j):  如果两个商品经常被同一个用户购买,说明两个商品很相似。分子为同时购买商品i和商品j的用户的数量,分母为购买i的用户数量*购买j的用户数量 开方
在这里插入图片描述
在这里插入图片描述
相似度的改进:如果两个商品同时被一个活跃度低的用户购买,能够更加说明两个商品相似,所以需要对活跃用户进行惩罚。分母不变,分子改为 1 / ( log( 1+N(u)) ). N(u)表示同时购买i和j的用户的活跃度。
在这里插入图片描述
相似度的归一化:有学者发表了一篇论文,提到如果将ItemCF中的相似度矩阵按照最大值归一化,可以提高推荐的准确率。Wij=Wij /max(Wij) 分母表示商品i与其他所有商品的相似度的最大值。不是所有商品与其他商品的最大值。不知道在UserCF中,将相似度矩阵归一化效果会不会有提升?
时间效率的改进:建立从用户到商品的倒排表dict_user_item[user],存储每个用户喜欢的商品集合。
用户u对商品j的分数: Wij
Rui的累加。Wij表示用户喜欢的商品i与待推荐商品j的相似度,Rui表示用户u对自己喜欢的商品i的打分。如果商品j与用户喜欢的m个商品都很相似,那么就是累加m项。

2.4.3 UserCF和ItemCF的比较

UserCF的推荐结果着重于反映和用户兴趣相似的小群体的热点,而ItemCF的推荐结果着重于维系用户的历史兴趣。换句话说, UserCF的推荐更社会化,反映了用户所在的小型兴趣群体中物品的热门程度,而ItemCF的推荐更加个性化,反映了用户自己的兴趣传承。
那么我们到底应该选用UserCF还是ItemCF呢?最直接的想法是根据离线实验,看哪个的效果好就用哪个。但是离线实验的结果在选取推荐算法时不能起到决定作用。首先应该满足产品的需求,比如如果需要提供可解释性,就得选择ItemCF;其次要看时间代价。如果用户数量太多,就不能用UserCF;最后,离线的指标与在线的指标(如点击率)不一定成正比。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_40006058/article/details/82958550