基于用户行为分析的推荐算法

基于用户行为分析的推荐算法

这种算法称为协同过滤算法,协同过滤是指用户可以齐心协力,通过不断的和网站互动,使自己的推荐列表能够过滤掉自己不感兴趣的物品,从而越来越满足自己的需求。

用户行为数据简介

用户行为数据最简单的存在形式是日志。很多互联网业务会把原始日志按照用户行为汇总成为会话日志(session log),其中每个会话代表一次用户行为和对应的服务,如展示日志,点击日志。

用户行为在个性化推荐系统中一般分为显性反馈行为(explicit feedback)和隐性反馈行为(implicit feedback)。explicit feedback包括用户明确表示对物品的喜好的行为。implicit feedback 是指那些不能明确反应用户喜好的行为。与explicit feedback相比,隐性反馈虽然不明确,但是数量庞大。

显性反馈数据隐性反馈数据
用户兴趣明确不明确
数量很少庞大
存储数据库分布式文件系统
实时读取实时有延迟
正负反馈都有只有正反馈

用户行为分析

在利用用户行为数据设计推荐算法之前,首先需要对用户行为数据进行分析,了解数据中蕴含的一般规律,这样才能对算法的设计起到指导作用。以下用户行为数据的普遍规律。

用户活跃度和物品流行度的分布

互联网上很多数据分布都满足power Law也称长尾分布。

f ( x ) = α x k f(x)=\alpha x^k f(x)=αxk
研究发现,用户行为数据也满足power law分布, f i ( k ) f_i (k) fi(k)为被k个用户产生行为的物品数, f u ( k ) f_u(k) fu(k)为对k个物品产生行为的用户数。即
f i ( k ) = α i k i β f_i(k)=\alpha_i k^\beta_i fi(k)=αikiβ f u ( k ) = α u k u β f_u(k)=\alpha_u k^\beta_u fu(k)=αukuβ

用户活跃度和物品流行度的关系

根据这个规律可以发现,用户越活跃,越倾向于冷门物品。

仅仅基于用户行为数据设计的推荐算法一般称为协同过滤算法。学术界对协同过滤算法提出了很多方法,如:基于领域的方法,隐语义模型、基于图的随机游走算法等。应用最广泛的是:

  • 基于用户的协同过滤算法 这种算法给用户推荐和他兴趣相似的其他用户喜欢的物品
  • 基于物品的协同过滤算法 这种算法给用户推荐和他之前喜欢的物品相似的物品

实验设计和算法测评

数据集

采用GroupLens提供的MovieLens数据集,该数据集包含3个不同版本,选用中等大小的数据集,包含6000多用户对4000多部电影的100万条评分。该数据集是一个评分数据集。着重研究隐反馈数据集中的TopN推荐问题,因此忽略数据集中的评分纪录。TopN的推荐任务是预测用户会不会给某部电影评分,而不是预测用户在准备对电影评分的前提下给电影评多少分。

实验设计

首先,将用户行为数据集均匀随机分成M份(M=8),挑选一份作为测试集,其余的作为训练集。在训练集上建立用户兴趣模型,并在测试集上对用户行为进行预测,统计相应的评测指标。为了保证评测指标并不是过拟合的结果,需要进行M次试验,并且每次都使用不同的测试集。然后取M次试验的评测指标的平均值作为最终的评测指标。

下面代码为将用户评分文件中的数据分成训练集合测试集的过程。

def get_datas(input_file,M,k,seed) : #将用户评分文件中的数据分成训练集合测试集
    """
    get rating information
    Args: 
        input_file:user rating file
        M: 数据分成的份数
        k: 实验选取的测试集
        seed: 随机种子
    Return: two lists
       one:train datas
       another:test datas
    """
    if not os.path.exists(input_file) :
        return [],[]
    linenum = 0
    train_data = []
    test_data = []
    fp = open(input_file)
    random.seed(seed)
    for line in fp :
        if linenum == 0 :
            linenum += 1
            continue 
        item = line.strip().split(',')
        if len(item) < 4:
            continue
        if random.randint(0,M) == k :
            test_data.append([item[0],item[1],item[2]])
        else :
            train_data.append([item[0],item[1],item[2]])
    fp.close()
    return train_data,test_data

这里,每次选取不同的k和相同的随机种子seed,进行M次实验就可以得到M个不同的训练集和测试集,然后分别进行实验,用M次实验的平均值作为最后的评测指标。这样做主要就是为了防止某次实验结果是过拟合的结果(overf itting)。

评测指标

  1. 准确率/召回率
    对用户u推荐N个物品,记为( R ( u ) R(u) R(u)),令用户在测试集上喜欢的物品集合为( T ( u ) T(u) T(u)),然后通过准确率/召回率评测推荐算法的精度:
    r e c a l l = ∑ u ∣ R ( u ) a n d T ( u ) ∑ u ∣ T ( u ) ∣ recall=\frac{\sum_u |R(u) and T(u)}{\sum_u |T(u)|} recall=uT(u)uR(u)andT(u)
    p r e c i s i o n = ∑ u ∣ R ( u ) a n d T ( u ) ∑ u ∣ R ( u ) ∣ precision=\frac{\sum_u |R(u) and T(u)}{\sum_u |R(u)|} precision=uR(u)uR(u)andT(u)
    召回率描述有多少比例的用户-物品评分纪录包含在最终的推荐列表中,而准确率描述最终的推荐列表中有多少比例是发生过的用户-评分纪录。
  2. 覆盖率
    覆盖率反映推荐算法发掘长尾的能力,覆盖率越高,说明推荐算法越能够将长尾中的物品推荐给用户。
    C o v e r a g e = ⋃ u ∈ U R ( u ) I Coverage=\frac{\bigcup_{u\in U}R(u) }{I} Coverage=IuUR(u)
  3. 新颖度
    用推荐列表中的物品的平均流行度度量推荐结果的新颖度,如果推荐出的物品都很热门,说明推荐的新颖度较低,否则说明推荐结果比较新颖。

基于领域的算法

基于用户的协同过滤算法

基于用户的协同过滤算法就是将兴趣相同的人喜欢的物品推荐给你。
包括两个步骤:

  1. 找到和目标用户兴趣相同的用户集合
  2. 找到这个集合中的用户喜欢的,且目标用户没有听过的物品推荐给目标用户

步骤1的关键就是计算两个用户的兴趣相似度。这里,主要利用行为的相似度计算兴趣的相似度。给定用户 u u u v v v,令 N ( u ) N(u) N(u)为用户 u u u曾经有过正反馈的物品集合,令 N ( v ) N(v) N(v)为用户 v v v曾经有过正反馈的物品集合。计算方法有两种:

  • jaccard公式 : W u v = ∣ N ( u ) ⋂ N ( v ) ∣ ∣ N ( u ) ⋃ N ( v ) ∣ W_{uv}=\frac{|N(u)\bigcap N(v)|}{|N(u)\bigcup N(v)|} Wuv=N(u)N(v)N(u)N(v)
  • 通过余弦相似度计算 : W u v = ∣ N ( u ) ⋂ N ( v ) ∣ ∣ N ( u ) ∣ ∣ N ( v ) ∣ W_{uv}=\frac{|N(u)\bigcap N(v)|}{\sqrt{|N(u)||N(v)|}} Wuv=N(u)N(v) N(u)N(v)

源代码实现

import os
import math
import  numpy as np
import random
NumOfMovies = 9000
NumOfUsers = 700

def get_data(file):
    """
    读取数据
    """
    if not os.path.exists(file):
        return {}
    fp = open(file)
    data = {}
    linenum = 0
    for line in fp:
        if linenum == 0:
            linenum += 1
            continue
        line = line.split(',')
        userid,itemid = int(line[0]),int(line[1])
        if userid not in data:
            data[userid] = []
        data[userid].append(itemid)
    fp.close()
    return data

def split_data(data,M,k,seed):
    #将数据划分成为测试集和训练集
    test = {}
    train = {}
    random.seed(seed)
    for user,items in data.items():
        for i in items:
            if random.randint(0,M) == k:
                if user not in test:
                    test[user] =[]
                test[user].append(i)
            else:
                if user not in train:
                    train[user] = []
                train[user].append(i)
    return train,test

def UserSimilarity(train):
    #得到用户相似集合W
    #建立电影用户倒排表
    item_user = {}
    for u,items in train.items():
        for i in items:
            if i not in item_user:
                item_user[i] = []
            item_user[i].append(u)
    #计算C[u][v]即u和v共同观看的电影数
    C = {}
    N = np.zeros([NumOfUsers],dtype = np.int32)
    user_related = {}
    for i,users in item_user.items():
        for u in users:
            N[u] += 1
            if u not in C:
                C[u] = {}
            for v in users:
                if u == v:
                    continue
                if v not in C[u]:
                    C[u][v] = 0
                C[u][v] += (1/math.log(1+len(users)))
                if u not in user_related:
                    user_related[u] = []
                user_related[u].append(v)
    #求用户相似矩阵W
    W = np.zeros([NumOfUsers,NumOfUsers],dtype = np.float)
    for u,users in C.items():
        for v in users:
            W[u][v] += C[u][v] / math.sqrt(N[u] * N[v])
    return W ,user_related

def recommend(User,train,K,N,W,user_related):
    #通过相似矩阵W给用户产生推荐record
    k_user = {}
    rank ={}
    
    for v in user_related[User]:
        k_user[v] = W[User][v]
    k_user = sorted(k_user.items(),key = lambda x:x[1],reverse = True)[:K]
    for v,w in k_user:
        for item in train[v] :
            if item in train[User]:
                continue
            if item not in rank:
                rank[item] = 0
            rank[item] += w
    rank = sorted(rank.items(),key = lambda x:x[1],reverse = True)[:N]
    return rank

def Recall(train,test,N,k,W,user_related):
    #计算召回率
    hit = 0
    totla = 0
    for user in train:
        tu =test[user]
        rank = recommend(user,train,k,N,W,user_related)
        for item in rank :
            if item[0] in tu :
                hit += 1
        totla += len(tu)
    return hit/(totla*1.0)
    
def Precision(train,test,N,k,W,user_related):
    #计算召回率
    hit = 0
    totla = 0
    for user in train:
        tu =test[user]
        rank = recommend(user,train,k,N,W,user_related)
        for item in rank :
            if item[0] in tu :
                hit += 1
        totla += len(rank)
    return hit/(totla*1.0)


if __name__ == "__main__":
    data = get_data(r"F:\个性化推荐算法\UserCF\data\ratings.csv")
    train,test = split_data(data,2,1,1)
    del data
    W,user_relatde = UserSimilarity(train)
    recall = Recall(train,test,10,10,W,user_relatde)
    precision = Precision(train,test,10,10,W,user_relatde)
    print(recall,precision) 

注:以上参考《推荐系统实践》一书

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值