協同過濾算法-簡單的python實現demo

概述

協同過濾(collaborative filtering)是推薦算法裏面最經典也是最常用的。
該算法通過分析用戶的興趣,在用戶羣中找到指定用戶的相似用戶,綜合這些相似用戶對某一信息的評價,形成系統對該指定用戶的喜好程度預測。
比如,你現在想看一部電影,但是不知道具體看哪一部,你會怎麼做?大部分人會問問周圍的朋友,看看他們最近有什麼好看的電影可以推薦給自己,而我們一般會傾向於從口味比較類似的朋友那裏得到推薦信息。這就是協同過濾的核心思想。
要實現協同過濾,需要一下幾個步驟:

  1. 收集用戶便好
  2. 找到相似的用戶或物品
  3. 計算並推薦

評分

從用戶的行爲和便好中發現規律,並基於此進行推薦。所以,收集用戶便好信息成爲系統推薦效果最基礎的決定因素。用戶有很多種方式向系統提供自己的信息便好,比如評分、投票、轉發、保存書籤、購買、點擊、頁面停留時間等
以上行爲是通用的,在實際推薦引擎設計中自己可以多添加一些特定的用戶行爲,並用他們表示對物品的喜好程度。通常情況下,在一個推薦系統中,用戶行爲都會多於一個。那麼。如何組合這些不同的行爲呢?
基本上有兩種方式,如下:

  • 1.將不同的行爲分組
    一般可以分爲查看和購買,然後基於不同的用戶行爲,計算不同用戶或物品的相似度。類似噹噹網或者亞馬遜給出的“購買了該書的人還購買了”“查看了該書的人還看了”等
  • 2.對不同行爲進行加權
    對不同行爲產生的用戶喜好程度進行加權,然後求出用戶對物品的總體喜好程度。在我們收集好用戶的行爲數據後,還要對數據進行預處理,最核心的就是減噪和歸一化。
    • 減噪:因爲用戶數據在使用過程中可能出現大量的噪聲和誤操作,所以需要過濾掉這些噪聲
    • 歸一化:不同的行爲數據差別較大,比如,用戶的查看數據肯定比購買數據大的多,所以需要通過歸一化,數據才能更加準確

相似度

在對用戶的行爲進程分析得到用戶的便好後,可以根據用戶的便好計算相似用戶或相似物品,然後可以根據相似用戶和相似物品進行推薦。這就是協同過濾的兩個分支,即基於用戶的協同過濾和基於物品的協同過濾。
關於相似度的計算,現在有的幾種算法都是基於向量的,其實就是計算向量的距離,距離越近,說明相似度值越大,在推薦系統中,用戶-物品便好的二維矩陣中,我們可以將一個用戶對所有物品的便好作爲一個向量來計算用戶之間的相似度,或者將所有用戶對某個物品的便好作爲一個向量來計算物品之間的相似度
幾種常見的相似度計算方法:

  1. 同現相似度
    物品A和物品B的同現相似度定義如下:
    WA,B=N(A)N(B)N(A)W_{A,B}=\frac{|N(A)\bigcap N(B)|}{|N(A)|}
    N(A)是喜歡A的用戶數,N(A)N(B)|N(A)\bigcap N(B)|是同時喜歡A和B的用戶數,即上式表示喜歡A的用戶中有多少比例也喜歡物品B。
    上式存在一個問題,如果B是熱門物品,那麼WA,BW_{A,B}就會很大,接近1.因此該公式造成任何物品與熱門物品的相似度都很大。爲了避免避免推薦熱門物品,可以採取下面的公式。
    WA,B=N(A)N(B)N(A)N(B)W_{A,B}=\frac{|N(A)\bigcap N(B)|}{|N(A)||N(B)|}
    上式對物品B的權重進行了懲罰,減小了熱門物品與很多物品相似的可能性.

  2. 歐幾里得距離
    該距離最初用於計算兩點的距離,假設x,yx,y是二維空間的兩個點,他們之間的歐幾里得距離如下:
    d(x,y)=(xiyi)nd(x,y)=\sqrt{\sum(x_i-y_i)^n}
    當n等於2時,歐幾里得距離就是平面上兩個點的距離
    當用歐幾里得表示相似度時,一般採用下面公式進行轉換
    sim(x,y)=11+d(x,y)sim(x,y)=\frac{1}{1+d(x,y)}
    距離越小,相似度越大

  3. 餘弦相似度
    餘弦相似度是文本相似度度量中使用比較多的一種方法,對於兩個向量X和Y,其對應的形式如下
    X=(x1,x2,,xn)RnX=(x_1,x_2,……,x_n)\in R^n
    Y=(y1,y2,,yn)RnY=(y_1,y_2,……,y_n)\in R^n
    對應的餘弦相似度爲
    CosSim(X,Y)=ixiyi(ixi2)(iyi2)CosSim(X,Y)=\frac{\sum_ix_iy_i}{\sqrt{(\sum_ix_i^2)}\sqrt{(\sum_iy_i^2)}}

  4. 皮爾遜相關係數
    皮爾遜相關係數一般用於計算兩個定居變量間聯繫的緊密程度,取值範圍爲[1,1][-1,1]
    p(x,y)=xiyinxy(n1)SxSy=nxiyixiyinxi2(xi)2nyi2(yi)2p(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}}}
    Sx,SyS_x,S_y是x和y的樣本標準差

  5. Cosine相似度
    Cosine相似度一般用於計算文檔數據的相似度
    C(x,y)=x,yx2y2=xiyixi2yi2C(x,y)=\frac{x,y}{||x||^2||y||^2}=\frac{\sum x_iy_i}{\sqrt{\sum x_i^2}\sqrt{\sum y_i^2}}

  6. Tanimoto係數
    Tanimoto係數也被稱爲Jaccard係數,Cosine相似度的擴展,一般用於計算文檔數據的相似度
    T(x,y)=x,yx2+y2xy=xiyixi2+yi2xiyiT(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}
    在計算用戶相似度時,是將一個用戶對所有物品的便好作爲一個向量,而在計算物品相似度時,是將所有用戶對某個物品的便好作爲一個向量。求出相似度後,接下來可以計算相鄰用戶和相鄰物品。

推薦計算

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\Item I_1 I_2 I_3 I_4 I_5
U_1 4 3 - 5 -
U_2 5 - 4 4 -
U_3 4 - 5 - 3
U_4 2 3 - 1 -
U_5 - 4 2 - 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)=vN(i)wu,vrv,ip(u,i)=\sum_{v\in N(i)}w_{u,v}r_{v,i}
其中,N(i)表示對商品i打過分的用戶集合,比如上表中,對與User1,商品D_3沒有打分,要想計算商品D_3的分數。對商品D_3打過分的用戶有User2、User3、User5。wu,vw_{u,v}表示用戶u和用戶v 之間的相似度,rv,ir_{v,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)=jI(u)wi,jrj,up(u,i)=\sum_{j\in I(u)}w_{i,j}r_{j,u}
I(u)I(u)表示用戶u打過分的商品集合,比如在表中,用戶User1打過分的商品有商品D_1,商品D_2,商品D_4,wi,jw_{i,j}表示商品i和商品j之間的相似度,rj,ur_{j,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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章