实战智能推荐系统(7)-- 基于用户的协同过滤算法

时间:2022-12-07 15:09:30

什么是基于用户的协同过滤?

要我说,基于用户的协同过滤就是,当你在买东西的时候,提出购买过A物品的用户也购买过B。购买过A物品的用户就是跟你相似的用户,用他感兴趣的东西来预测你是否也感兴趣。这就是基于用户的协同过滤。

主要包含两个步骤:
(1)找到和目标用户相似的用户集合。

(2)找到这个集合中用户喜欢的,且目标用户没有听说过的物品推荐给目标用户。

现在我们来讨论一下第一步,找到用户相似的用户集合。就得计算用户之间的相似度。

计算用户的相似度

用户的相似度怎么计算,这里有一个余弦公式。令N(u) 表示用户 u 曾经有过正反馈的物品集合,令 N(v) 表示用户 v 曾经有过正反馈的物品集合。那么,我们可以通过下面的余弦公式进行用户 u 和 v 之间的相似度计算:

实战智能推荐系统(7)-- 基于用户的协同过滤算法

假设用户A 对物品{a,b,d} 有过行为,用户 B 对物品{a,c} 有过行为,利用余弦公式计算用户 A和 B 的相似度为

实战智能推荐系统(7)-- 基于用户的协同过滤算法

实现该相似度可以利用如下的代码:

def UserSimilarity(train):
    W = dict()
    for u in train.keys():
        for v in train.keys():
            if u == v:continue
            W[u][v] = len(train[u] & train[v])
            W[u][v] /= math.sqrt(len(train[u]) * len(train[v]) * 1.0)
    return W

计算用户相似度-- 代码加速

上述对用户计算相似度的方法,就是分别将用户两两之间计算。这种方法的时间复杂度是 O(|U|*|U|),这在用户数很大是非常的耗时。事实上,很多用户相互之间没有对同样的物品产生过行为,即很多时候|N(u)nN(v)| = 0 。

为此,可以首先建立物品到用户的倒排表,对于每个物品保存对该物品产生过行为的用户列表。假设用户 u 和 v 同时属于倒排表中k 个物品对应的用户列表,那么

实战智能推荐系统(7)-- 基于用户的协同过滤算法

倒排表示例图 2- 7 所示:

实战智能推荐系统(7)-- 基于用户的协同过滤算法

代码实现如下所示:

def UserSimilarity(train):
    #创建倒排表
    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)

    #建立 C[u][v] 
    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

    #关系表 W[u][v]
    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

计算用户的相似度 -- 算法改进

前面讲述了计算用户相似度的最简单的公式--余弦公式。但这个公式过于粗糙。以图书为例,如果两个用户都买过《新华字典》,这丝毫不能说明他们兴趣相似,因为几乎每个人都买过《新华字典》。但是如果两个用户都买过《数据挖掘》,那可以认为他们兴趣比较相似,因为只有研究数据挖掘的人才会买这本书。换句话说,两个用户对冷门物品采取过同样的行为更能说明他们兴趣的相似度。因此对前面余弦公式加入惩罚项的公式和代码分别如下:

实战智能推荐系统(7)-- 基于用户的协同过滤算法

def UserSimilarity(train):
    #建立倒排表
    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)
    
    #建立 C[u][v]
    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
                # 这里加入热门物品的惩罚项 1/math.log(1 + len(users))
                C[u][v] += 1 / math.log(1 + len(users)) * 1
    
    #关系表 W[u][v]
    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

用户相似度问题解决后,进入算法第二步,找到 K 个最相似的用户推荐物品给目标用户

下列公式度量了 UserCF 算法中用户 u 对物品 i 的感兴趣程度:

实战智能推荐系统(7)-- 基于用户的协同过滤算法

其中 S(u,K) 包含和用户 u 兴趣最接近的 K 个用户,N(i) 是对物品 i 有过行为的用户集合, wuv 是用户u 和用户v 的相似度,rvi 代表用户 v 对物品 i 的兴趣,因为使用的是单一行为的隐反馈,所以所有的 rvi = 1 。代码实现 UserCF 推荐算法如下:

def Recommend(user,train,W):
    rank = dict()
    interacted_items = train[user]
    for v,wuv in sorted(W[user].items(),key = itemgetter(1),reverse = True)[0:K]:
        for i,rvi in train[v].items():
            if i in interacted_items:continue
            rank[i] += wuv * rvi
    return rank

不同的 K 值对 UserCF 算法的影响和分析

实战智能推荐系统(7)-- 基于用户的协同过滤算法

准确率和召回率:可以看到推荐系统的精度指标并不和参数 K 成线性关系。因此选择合适的 K 值对获得高的推荐系统精度比较重要,推荐结果的精度对 K 也不是特别的敏感。
流行度:可以看到,K 越大则 UserCF算法的推荐结果就越热门。这是因为 K 决定了 UserCF 在给你做推荐时参考多少和你兴趣相似的其他用户的兴趣,那么如果 K 越大,参考的人越多,结果就越趋近于热门。
覆盖率: K 越大,流行度越高,自然覆盖率就可额下降。