协同过滤算法-简单的python实现demo

概述

协同过滤(collaborative filtering)是推荐算法里面最经典也是最常用的。
该算法通过分析用户的兴趣,在用户群中找到指定用户的相似用户,综合这些相似用户对某一信息的评价,形成系统对该指定用户的喜好程度预测。
比如,你现在想看一部电影,但是不知道具体看哪一部,你会怎么做?大部分人会问问周围的朋友,看看他们最近有什么好看的电影可以推荐给自己,而我们一般会倾向于从口味比较类似的朋友那里得到推荐信息。这就是协同过滤的核心思想。
要实现协同过滤,需要一下几个步骤:

  1. 收集用户便好
  2. 找到相似的用户或物品
  3. 计算并推荐

评分

从用户的行为和便好中发现规律,并基于此进行推荐。所以,收集用户便好信息成为系统推荐效果最基础的决定因素。用户有很多种方式向系统提供自己的信息便好,比如评分、投票、转发、保存书签、购买、点击、页面停留时间等
以上行为是通用的,在实际推荐引擎设计中自己可以多添加一些特定的用户行为,并用他们表示对物品的喜好程度。通常情况下,在一个推荐系统中,用户行为都会多于一个。那么。如何组合这些不同的行为呢?
基本上有两种方式,如下:

  • 1.将不同的行为分组
    一般可以分为查看和购买,然后基于不同的用户行为,计算不同用户或物品的相似度。类似当当网或者亚马逊给出的“购买了该书的人还购买了”“查看了该书的人还看了”等
  • 2.对不同行为进行加权
    对不同行为产生的用户喜好程度进行加权,然后求出用户对物品的总体喜好程度。在我们收集好用户的行为数据后,还要对数据进行预处理,最核心的就是减噪和归一化。
    • 减噪:因为用户数据在使用过程中可能出现大量的噪声和误操作,所以需要过滤掉这些噪声
    • 归一化:不同的行为数据差别较大,比如,用户的查看数据肯定比购买数据大的多,所以需要通过归一化,数据才能更加准确

相似度

在对用户的行为进程分析得到用户的便好后,可以根据用户的便好计算相似用户或相似物品,然后可以根据相似用户和相似物品进行推荐。这就是协同过滤的两个分支,即基于用户的协同过滤和基于物品的协同过滤。
关于相似度的计算,现在有的几种算法都是基于向量的,其实就是计算向量的距离,距离越近,说明相似度值越大,在推荐系统中,用户-物品便好的二维矩阵中,我们可以将一个用户对所有物品的便好作为一个向量来计算用户之间的相似度,或者将所有用户对某个物品的便好作为一个向量来计算物品之间的相似度
几种常见的相似度计算方法:

  1. 同现相似度
    物品A和物品B的同现相似度定义如下:
    W A , B = ∣ N ( A ) ⋂ N ( B ) ∣ ∣ N ( A ) ∣ W_{A,B}=\frac{|N(A)\bigcap N(B)|}{|N(A)|} WA,B=N(A)N(A)N(B)
    N(A)是喜欢A的用户数, ∣ N ( A ) ⋂ N ( B ) ∣ |N(A)\bigcap N(B)| N(A)N(B)是同时喜欢A和B的用户数,即上式表示喜欢A的用户中有多少比例也喜欢物品B。
    上式存在一个问题,如果B是热门物品,那么 W A , B W_{A,B} WA,B就会很大,接近1.因此该公式造成任何物品与热门物品的相似度都很大。为了避免避免推荐热门物品,可以采取下面的公式。
    W A , B = ∣ N ( A ) ⋂ N ( B ) ∣ ∣ N ( A ) ∣ ∣ N ( B ) ∣ W_{A,B}=\frac{|N(A)\bigcap N(B)|}{|N(A)||N(B)|} WA,B=N(A)N(B)N(A)N(B)
    上式对物品B的权重进行了惩罚,减小了热门物品与很多物品相似的可能性.

  2. 欧几里得距离
    该距离最初用于计算两点的距离,假设 x , y x,y x,y是二维空间的两个点,他们之间的欧几里得距离如下:
    d ( x , y ) = ∑ ( x i − y i ) n d(x,y)=\sqrt{\sum(x_i-y_i)^n} d(x,y)=(xiyi)n
    当n等于2时,欧几里得距离就是平面上两个点的距离
    当用欧几里得表示相似度时,一般采用下面公式进行转换
    s i m ( x , y ) = 1 1 + d ( x , y ) sim(x,y)=\frac{1}{1+d(x,y)} sim(x,y)=1+d(x,y)1
    距离越小,相似度越大

  3. 余弦相似度
    余弦相似度是文本相似度度量中使用比较多的一种方法,对于两个向量X和Y,其对应的形式如下
    X = ( x 1 , x 2 , … … , x n ) ∈ R n X=(x_1,x_2,……,x_n)\in R^n X=(x1,x2,,xn)Rn
    Y = ( y 1 , y 2 , … … , y n ) ∈ R n Y=(y_1,y_2,……,y_n)\in R^n Y=(y1,y2,,yn)Rn
    对应的余弦相似度为
    C o s S i m ( X , Y ) = ∑ i x i y i ( ∑ i x i 2 ) ( ∑ i y i 2 ) CosSim(X,Y)=\frac{\sum_ix_iy_i}{\sqrt{(\sum_ix_i^2)}\sqrt{(\sum_iy_i^2)}} CosSim(X,Y)=(ixi2) (iyi2) ixiyi

  4. 皮尔逊相关系数
    皮尔逊相关系数一般用于计算两个定居变量间联系的紧密程度,取值范围为 [ − 1 , 1 ] [-1,1] [1,1]
    p ( x , y ) = ∑ x i y i − n x y ‾ ( n − 1 ) S x S y = n ∑ x i y i − ∑ x i ∑ y i n ∑ x i 2 − ( ∑ x i ) 2 n ∑ y i 2 − ( ∑ y i ) 2 p(x,y)=\frac{\sum x_iy_i-n\overline{xy}}{(n-1)S_xS_y}=\frac{n\sum x_iy_i-\sum x_i\sum y_i}{\sqrt{n\sum x_i^2-{(\sum x_i)^2}}\sqrt{n\sum y_i^2-{(\sum y_i)^2}}} p(x,y)=(n1)SxSyxiyinxy=nxi2(xi)2 nyi2(yi)2 nxiyixiyi
    S x , S y S_x,S_y Sx,Sy是x和y的样本标准差

  5. Cosine相似度
    Cosine相似度一般用于计算文档数据的相似度
    C ( x , y ) = x , y ∣ ∣ x ∣ ∣ 2 ∣ ∣ y ∣ ∣ 2 = ∑ x i y i ∑ x i 2 ∑ y i 2 C(x,y)=\frac{x,y}{||x||^2||y||^2}=\frac{\sum x_iy_i}{\sqrt{\sum x_i^2}\sqrt{\sum y_i^2}} C(x,y)=x2y2x,y=xi2 yi2 xiyi

  6. Tanimoto系数
    Tanimoto系数也被称为Jaccard系数,Cosine相似度的扩展,一般用于计算文档数据的相似度
    T ( x , y ) = x , y ∣ ∣ x ∣ ∣ 2 + ∣ ∣ y ∣ ∣ 2 − x ⋅ y = ∑ x i y i ∑ x i 2 + ∑ y i 2 − ∑ x i y i T(x,y)=\frac{x,y}{||x||^2+||y||^2-x·y}=\frac{\sum x_iy_i}{\sqrt{\sum x_i^2}+\sqrt{\sum y_i^2}-\sum x_iy_i} T(x,y)=x2+y2xyx,y=xi2 +yi2 xiyixiyi
    在计算用户相似度时,是将一个用户对所有物品的便好作为一个向量,而在计算物品相似度时,是将所有用户对某个物品的便好作为一个向量。求出相似度后,接下来可以计算相邻用户和相邻物品。

推荐计算

1、 基于用户的协同过滤(UserCF)

基于用户协同过滤的思想很简单,基于用户对物品的便好,找到相邻的用户,然后将相邻用户喜欢的物品推荐给当前用户。在计算时,就是将一个用户对所有物品的便好作为一个向量来计算用户之间的相似度,找到K个相邻用户后,根据相邻用户的相似度权重以及他们对物品的偏好,预测当前用户喜欢的物品,计算得到一个排序的物品列表进行推荐。

如下表给出的例子

物品/用户物品A物品B物品C物品D
用户A推荐
用户B
用户C

对于用户A,根据历史偏好,这里只计算一个邻居得到–用户C,然后可以将C喜欢的物品D推荐给A

2、基于物品的协同过滤(ItemCF)

基于物品的协同过滤原理与基于用户的协同过滤原理类似,只是在计算邻居时采用物品本身(而不是从用户角度),基于用户对物品的偏好找到相似的物品,然后根据用户的历史偏好推荐相似的物品从计算的角度看,就是将所有用户对某个物品的偏好作为一个相量,来计算物品之间的相似度,得到某物品的相似度后,如根据用户的历史偏好预测当前用户还没有表示偏好的物品,则计算得到一个排序的物品列表推荐。如下表

物品/用户物品A物品B物品C
用户A
用户B
用户C推荐

对于物品A,根据用户偏好–喜欢物品A的用户都喜欢C,得出物品A和物品C比较相似,而用户C喜欢物品A,纳闷可以推断用户C可能也喜欢物品C

协同过滤算法实现

简单演示
  • 相似度计算
    先定义一个余弦相似度计算的函数
def cos_sim(x,y):
    '''
    余弦相似性
    input:  x(mat):以行向量的形式存储,可以是用户或者商品
            y(mat):以行向量的形式存储,可以是用户或者商品
    output: x和y之间的余弦相似度
    '''
    numerator=np.dot(x,y.T)#x和y之间的内机
    denominator = np.sqrt(np.dot(x,x.T))*np.sqrt(np.dot(y,y.T))
    return (numerator/denominator)

接下来再定义一个计算相似度矩阵的函数

def similarity(data):
    '''
    计算矩阵中任意两行之间的相似度
    input:  data(mat):任意矩阵
    output: w(mat):任意两行之间的相似度
    '''
    m = np.shape(data)[0]#用户的数量
    #初始化相似度矩阵
    w = np.mat(np.zeros((m,m)))
    for i in range(m):
        for j in range(i,m):
            if i!=j:
                #计算任意两行之间的相似度
                w[i,j] = cos_sim(data[i,],data[j,])
                w[j,i] = w[i,j]
            else:
                w[i,j]=0#约定自身的相似度为0
    return w

简单定义一个数据

User\ItemI_1I_2I_3I_4I_5
U_143-5-
U_25-44-
U_34-5-3
U_423-1-
U_5-42-5

计算用户相似度

similarity(A)
matrix([[0.        , 0.74926865, 0.32      , 0.83152184, 0.25298221],
        [0.74926865, 0.        , 0.74926865, 0.49559463, 0.1579597 ],
        [0.32      , 0.74926865, 0.        , 0.30237158, 0.52704628],
        [0.83152184, 0.49559463, 0.30237158, 0.        , 0.47809144],
        [0.25298221, 0.1579597 , 0.52704628, 0.47809144, 0.        ]])
  • 基于用户的协同过滤

计算完用户之间的相似度后,利用用户之间的相似度为用户中没有打分的商品打分,具体计算方法为:
p ( u , i ) = ∑ v ∈ N ( i ) w u , v r v , i p(u,i)=\sum_{v\in N(i)}w_{u,v}r_{v,i} p(u,i)=vN(i)wu,vrv,i
其中,N(i)表示对商品i打过分的用户集合,比如上表中,对与User1,商品D_3没有打分,要想计算商品D_3的分数。对商品D_3打过分的用户有User2、User3、User5。 w u , v w_{u,v} wu,v表示用户u和用户v 之间的相似度, r v , i r_{v,i} rv,i表示用户v对商品i的打分。
具体计算的代码如下:

def user_base_recommend(data,w,user):
    '''
     基于用户相似性为用户user推荐商品
     input:  data(mat):用户商品矩阵
            w(mat):用户之间的相似度
            user(int):用户的编号
     output: predict(list):推荐列表
    '''
    m,n=np.shape(data)
    interaction = data[user,]#用户user与商品信息
    #先找到用户user没有打过分的商品
    not_inter = []
    for i in range(n):#n商品数量
        if interaction[i]==0:#没有打分的商品
            not_inter.append(i)
    #print(not_inter)
    #对没有打分的商品进行预测
    predict={}
    for x in not_inter:
        item = np.copy(data[:,x])#找到所有用户对商品x的打分信息
        for i in range(m):#对所有的用户
            if item[i] != 0:#如果用户对商品x打过分
                if x not in predict:
                    predict[x]=w[user,i]*item[i]
                    
                else:
                    predict[x] = predict[x]+w[user,i]*item[i]
                    
        #按照预测的大小排序
    return sorted(predict.items(),key=lambda d:d[1],reverse=True)
            

实际带入计算

user_base_recommend(A,w,0)
[(2, 5.10303902268836), (4, 2.2249110640673515)]

推荐TOP-N

def top_k(predict,k):
    '''为用户推荐前k个商品
    input:  predict(list):排好序的商品列表
            k(int):推荐的商品个数
    output: top_recom(list):top_k个商品
    '''
    top_recom=[]
    len_r = len(predict)#计算有多少待推荐商品
    if k>=len_r:#如果需要推荐的个数大于待推荐的数量,直接输出
        top_recom=predict
    else:
        for i in range(k):
            top_recom.append(predict[i])
            
    return top_recom

打印下结果

k=2
result=top_k(user_base_recommend(A,w,0),k)
for i in range(k):
    print("给User_0推荐的商品有:商品%d,打分为%.3f"%(result[i][0],result[i][1]))
    
给User_0推荐的商品有:商品2,打分为5.103
给User_0推荐的商品有:商品4,打分为2.225    
  • 基于商品的协同过滤

首先要计算Item之间的相似度,还是套用上面的similarity函数,前提是先把矩阵进行转置。
之后利用商品之间的相似度为用户没有打分的商品计算得分,计算方法:
p ( u , i ) = ∑ j ∈ I ( u ) w i , j r j , u p(u,i)=\sum_{j\in I(u)}w_{i,j}r_{j,u} p(u,i)=jI(u)wi,jrj,u
I ( u ) I(u) I(u)表示用户u打过分的商品集合,比如在表中,用户User1打过分的商品有商品D_1,商品D_2,商品D_4, w i , j w_{i,j} wi,j表示商品i和商品j之间的相似度, r j , u r_{j,u} rj,u表示用户u对商品j的打分。
原理与基于用户是相同的

Item_base_recommend(B,similarity(B),0)
[(2, 5.507604598998138), (4, 2.8186967825714824)]

完整代码:https://github.com/Andyszl/Recommendation_algorithm/blob/master/collaborative-filtering_demo.ipynb

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Andy_shenzl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值