Bootstrap

推荐系统基本算法——协同过滤

传统推荐模型——协同过滤

一、概述

基本思想:

物以类聚,人以群分

目标场景:

用户对物品打分的情况,其中Alice未打分
通过其他的信息,来预测打分情况
在这里插入图片描述

方法:

1、基于User的协同过滤(UserCF)
找到跟Alice相似的用户A,根据用户A对物品5的打分,Alice与之相同
2、基于Item的协同过滤(ItemCF)
找到跟物品5相似的其他的物品B,根据Alice对物品B的打分,对物品5的打分与之相同


二、相似度计算公式

1、杰卡德相似度

衡量两个集合之间的相似度一种指标,A和B集合的相似度
在这里插入图片描述

2、余弦相似度

衡量用户向量 i , j i,j i,j之间的向量夹角大小,夹角越小,余弦值越大,相似度越大
在这里插入图片描述
推广到多个样本的相似度度量公式:
在这里插入图片描述

局限性:针对评分数据不规范时,例如有的用户习惯打高分或者低分,结果不准确
API

import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
vec1 = np.array([1, 2, 3, 4])
vec2 = np.array([5, 6, 7, 8])
cos_sim = cosine_similarity(vec1.reshape(1, -1), vec2.reshape(1, -1))
print(cos_sim[0][0])

3、皮尔逊相关系数

皮尔逊相关系数是一种度量两个变量间相关程度的方法。它是一个介于 1 和 -1 之间的值,其中,1 表示变量完全正相关, 0 表示无关,-1 表示完全负相关
在这里插入图片描述
0.8-1.0 极强相关
0.6-0.8 强相关
0.4-0.6 中等程度相关
0.2-0.4 弱相关
0.0-0.2 极弱相关或无相关
API:`

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

4、欧式距离

是一个通常采用的距离定义,指在m维空间中两个点之间的真实距离,或者向量的自然长度(即该点到原点的距离)。在二维和三维空间中的欧氏距离就是两点之间的实际距离。
那距离是如何反映出相似度的呢,很简单,我们总是希望相似度越大返回的值越大。所以我们取距离的倒数即可(因为距离越大相似度越小),又因为分母不能为0,所以我们往往取函数值加1的倒数。


三、基于User的协同过滤

一、
每一行的值都是用户各自的向量表示
在这里插入图片描述
在这里插入图片描述
二、
计算Alice与其他用户的相似度
在这里插入图片描述

三、
通过预测函数公式来计算最终评分
在这里插入图片描述


四、
代码

import numpy as np
from math import sqrt
'''
用户/物品 | 物品a | 物品b | 物品c | 物品d
用户A     |   √  |       |   √   | 
用户B     |   √  |   √   |       |   √
用户C     |   √  |   √   |   √   |   √ 
'''
def consine(ls_1,ls_2,m):
    fenzi = 0
    fenmu1 = 0
    fenmu2 = 0
    for i in range(m):
        fenzi += ls_1[i] * ls_2[i]
        fenmu1 += ls_1[i] * ls_1[i]
        fenmu2 += ls_2[i] * ls_2[i]

        # print("\n")
    fenmu = sqrt(fenmu1) * sqrt(fenmu2)
    return fenzi/fenmu
#预测函数
def predict(w,r=1):
    p=w*r
    return p

if __name__ == "__main__":
    user_item=np.array([[1,0,1,0],
                        [1,1,0,1],
                        [1,1,1,1]])
    print("用户矩阵")
    print(user_item)
    user = ['用户A', '用户B', '用户C']
    item = ['物品a', '物品b', '物品c', '物品d']
    n = len(user)  # n 个用户
    m = len(item)  # m 个物品
#利用余弦相似度构建用户相似度矩阵
    sim=np.zeros((n,n))#初始化n行n列,默认为0
    for i in range(n):
        for j in range(n):
            if i<j:
                sim[i][j]=consine(user_item[i],user_item[j],m)
                sim[j][i] = sim[i][j]    #这里顺序不能改变 赋值的关系
                print(sim[i][j])
    print("用户相似度矩阵")
    print(sim)
#预测用户评分
    max_sim = [0, 0, 0]  # 存放每个用户的相似用户
    r_list = [[], [], []]  # 存放推荐给每个用户的物品
    p = [[], [], []]  # 每个用户被推荐物品的预测值列表
    for i in range(n):  # n 个用户循环 n 次
        # 找到与用户 i 最相似的用户
        for j in range(len(sim[i])):  # range () 里面写 n 也可以,二者等同
            if max(sim[i]) != 0 and sim[i][j] == max(sim[i]):#max代表最大值
                max_sim[i] = user[j]  # 此时的 j 就是相似用户的编号
                break  # break 目的:一是结束当前循环,二是当前的 j 后面有用
        if max_sim[i] == 0:
            continue  # 等于 0,表明当前用户无相似用户,无需推荐,继续下个用户

        # 找出应该推荐的物品,并计算预测值
        K=1
        for k in range(K):  # 为了更契合预测值计算公式,因为这里 K=1,所以也可以省去这个 for
            for x in range(m):  # m 个物品循环 m 次
                if user_item[i][x] == 0 and user_item[j][x] == 1:  # 目标用户不知道,而相似用户知道
                    r_list[i].append(item[x])
                    p[i].append(predict(sim[i][j]))

                    # 打印结果
    for i in range(n):  # n 个用户循环 n 次
        if len(r_list[i]) > 0:  # 当前用户有被推荐的物品
            print("向{:}推荐的物品有:".format(user[i]), end='')#end的作用是防止光标移动到下一行
            print(r_list[i])
            print("该用户对以上物品该兴趣的预测值为:", end='')
            print(p[i])
        print()#输出一行,即换行
        向用户A推荐的物品有:['物品b', '物品d']
##结果:
用户矩阵
[[1 0 1 0]
 [1 1 0 1]
 [1 1 1 1]]
用户相似度矩阵
[[0.         0.40824829 0.70710678]
 [0.40824829 0.         0.8660254 ]
 [0.70710678 0.8660254  0.        ]]
向用户A推荐的物品有:['物品b', '物品d']
该用户对以上物品该兴趣的预测值为:[0.7071067811865475, 0.7071067811865475]

向用户B推荐的物品有:['物品c']
该用户对以上物品该兴趣的预测值为:[0.8660254037844387]



五、
适用场景:用户少,物品多,时效性强


四、基于Item的协同过滤

图解
在这里插入图片描述

每一列的值都是物品各自的向量表示

在这里插入图片描述
算法过程实现
一、
依旧建立用户—物品矩阵:
用户A:物品A、物品C(1,0,1)
用户B:物品A、物品C(1,0,1)
用户C:物品A、物品B(1,1,0)
二、
建立物品—用户倒排表
物品 a:用户 A、用户 B、用户 C(1,1,1)
物品 b:用户 C (0,0,1)
物品 c:用户 A、用户 B(1,1,0)
三、
计算物品之间相似度(利用余弦相似定理):

在这里插入图片描述
简而言之:预测值=最相似的物品的相似度*用户对其最相似物品的打分
三、
代码

import numpy as np
from math import sqrt
'''
用户/物品 | 物品a | 物品b | 物品c 
用户A    |   √    |          |   √
用户B    |   √    |          |   √
用户C    |   √    |   √     |
'''
#余弦相似度求相似
def consine(ls_1,ls_2,m):
    fenzi = 0
    fenmu1 = 0
    fenmu2 = 0
    for i in range(m):
        fenzi += ls_1[i] * ls_2[i]
        fenmu1 += ls_1[i] * ls_1[i]
        fenmu2 += ls_2[i] * ls_2[i]
    fenmu = sqrt(fenmu1) * sqrt(fenmu2)
    return fenzi/fenmu
#预测函数
def predict(w,r=1):
    p=w*r
    return p
#主函数
if __name__ == "__main__":
    user_item = np.array([[1, 0, 1],
                          [1, 0, 1],
                          [1,1, 0]])
    print("用户-物品矩阵:")
    print(user_item)  # 打印用户-物品矩阵
    user = ['用户A', '用户B', '用户C']
    item = ['物品a', '物品b', '物品c']
    n = len(user)  # n 个用户
    m = len(item)  # m 个物品

    #建立物品 - 用户倒排表
    item_user = user_item.T  #求转置即可
    print("物品-用户矩阵:")
    print(item_user)

    #构建物品 - 物品相似度矩阵
    sim = np.zeros((n, n))  # 相似度矩阵,默认全为 0
    for i in range(n):
        for j in range(n):
            if i < j:
                sim[i][j] = consine(item_user[i], item_user[j], m)  # 用到了前面定义的函数
                sim[j][i] = sim[i][j]  # 代表用户顺序不同但相似度一样
    print("得到的物品-物品相似度矩阵:")
    print(sim)  # 打印物品 - 物品相似度矩阵
    # 推荐物品
    max_sim = [0, 0, 0]  # 存放每个物品的相似物品
    r_list = [[], [], []]  # 存放推荐给每个用户的物品
    p = [[], [], []]  # 每个用户被推荐物品的预测值列表
    for i in range(m):  # m 个物品循环 m 次
        # 找到与物品 i 最相似的物品
        for j in range(len(sim[i])):  # range () 里面写 m 也可以,二者等同
            if max(sim[i]) != 0 and sim[i][j] == max(sim[i]):  # max代表最大值
                max_sim[i] = item[j]  # 此时的 j 就是相似物品的编号
                break  # break 目的:一是结束当前循环,二是当前的 j 后面有用
        if max_sim[i] == 0:
            continue  # 等于 0,表明当前物品无相似物品,继续下个物品


        # 找出应该推荐的物品,并计算预测值
        K=1
        for k in range(K):  # 为了更契合预测值计算公式,因为这里 K=1,所以也可以省去这个 for
            for x in range(n):  # n 个用户循环 n 次
                if item_user[i][x] == 1 and item_user[j][x] == 0:  # 当前物品用户知道,而相似物品该用户不知道
                    r_list[x].append(max_sim[i])
                    p[x].append(predict(sim[i][j]))

                    # 打印结果
    for i in range(n):  # n 个用户循环 n 次
        if len(r_list[i]) > 0:  # 当前用户有被推荐的物品
            print("向{:}推荐的物品有:".format(user[i]), end='')  # end的作用是防止光标移动到下一行
            print(r_list[i])
            print("该用户对以上物品该兴趣的预测值为:", end='')
            print(p[i])
        print()  # 输出一行空白,即换行
        用户-物品矩阵:
#结果:
[[1 0 1]
 [1 0 1]
 [1 1 0]]
物品-用户矩阵:
[[1 1 1]
 [0 0 1]
 [1 1 0]]
得到的物品-物品相似度矩阵:
[[0.         0.57735027 0.81649658]
 [0.57735027 0.         0.        ]
 [0.81649658 0.         0.        ]]


向用户C推荐的物品有:['物品c']
该用户对以上物品该兴趣的预测值为:[0.8164965809277259]



三、
在这里插入图片描述
所以Item相似矩阵维度小
使用场景:适用于电商平台User数量远远大于Item数量的应用场景,


五、UserCF vs ItemCF

区别:
在这里插入图片描述
缺点:
在这里插入图片描述

;