1. 程式人生 > >推薦之協同過濾(思路簡單梳理)

推薦之協同過濾(思路簡單梳理)

用途

是什麼

  找一群跟我品味相近的人,去看看他們經常瀏覽的商品,並且我沒有瀏覽的推薦給我。

基於使用者的協同過濾怎麼做

第一步就是要收集每個人的偏好

  以電影為例,如果我有一份資料清單,有每個人對一些電影的評分,通過這些評分,我們就能知道某兩個人對電影品味相近程度。
critics={‘Lisa Rose’: {‘Lady in the Water’: 2.5, ‘Snakes on a Plane’: 3.5,
‘Just My Luck’: 3.0, ‘Superman Returns’: 3.5, ‘You, Me and Dupree’: 2.5,
‘The Night Listener’: 3.0},
‘Gene Seymour’: {‘Lady in the Water’: 3.0, ‘Snakes on a Plane’: 3.5,
‘Just My Luck’: 1.5, ‘Superman Returns’: 5.0, ‘The Night Listener’: 3.0,
‘You, Me and Dupree’: 3.5},
‘Michael Phillips’: {‘Lady in the Water’: 2.5, ‘Snakes on a Plane’: 3.0,
‘Superman Returns’: 3.5, ‘The Night Listener’: 4.0},
‘Claudia Puig’: {‘Snakes on a Plane’: 3.5, ‘Just My Luck’: 3.0,
‘The Night Listener’: 4.5, ‘Superman Returns’: 4.0,
‘You, Me and Dupree’: 2.5},
‘Mick LaSalle’: {‘Lady in the Water’: 3.0, ‘Snakes on a Plane’: 4.0,
‘Just My Luck’: 2.0, ‘Superman Returns’: 3.0, ‘The Night Listener’: 3.0,
‘You, Me and Dupree’: 2.0},
‘Jack Matthews’: {‘Lady in the Water’: 3.0, ‘Snakes on a Plane’: 4.0,
‘The Night Listener’: 3.0, ‘Superman Returns’: 5.0, ‘You, Me and Dupree’: 3.5},
‘Toby’: {‘Snakes on a Plane’:4.5,’You, Me and Dupree’:1.0,’Superman Returns’:4.0}}
  上面是一個清單的例子,8個人對不同電影的評分。

第二步:計算使用者之間相似

  三種計算兩個使用者之間相似程度的方法:分別是歐幾里得距離、皮爾遜相關度和Tanimoto相似度。
  1.歐幾里得距離
  簡單說就是計算同一個使用者對兩部電影的評價在二維座標系上的距離。這樣的話,距離越近,兩個使用者就越相似,距離越遠,兩個使用者的相似程度就越低,不過為了便於比較,我們需要把這個距離處理一下,使用這個距離的倒數,那麼倒數越大,相似性就越大,倒數越小,相似性就越低,不過如果完全一樣的時候,就會出現距離為0,就無法得到倒數,那麼我們給除數加1,就解決了這個問題。

from imp import reload
from math import
sqrt def sim_distance(prefs,person1,person2): si={} for item in prefs[person1]: if item in prefs[person2]: si[item]=1 #if they have no rating in common, return 0 if len(si)==0: return 0 #計算所有距離的和(差值的平方和) sum_of_squares=sum([pow(prefs[person1][item]-prefs[person2][item],2
) for item in prefs[person1] if item in prefs[person2]]) return 1/(1+sqrt(sum_of_squares))

  時間複雜度: O(n)
  關鍵公式:d=(p1p2)2
  在python下輸入:

>>> import recommendations
>>> critics = recommendations.critics
>>> recommendations.sim_distance(critics,'Lisa Rose','Gene Seymour')

就可以得到Lisa Rose和Gene Seymour的相似程度為0.14814814814814814
  2.皮爾遜相關係數
  如果兩個人對電影的評分,一個人總是比另一個人高,那是不是說明這兩個人之間的品味相差很大呢?不一定,也許是評判標準不一樣,但是之間一定是有相關性的。通過二維座標系,觀察兩個人對電影的評分,是可以找出一個線性關係。而這個直線的斜率,就是相關係數。
  求皮爾遜相關係數的程式碼:

def sim_pearson(prefs,p1,p2):
    si={}
    for item in prefs[p1]:
        if item in prefs[p2]:
        si[item]=1

    n=len(si) 
    if n==0:
        return 1

    #所有偏好求和
    sum1=sum([prefs[p1][it] for it in si])
    sum2=sum([prefs[p2][it] for it in si])

    #所有偏好求平方和
    sum1Sq=sum([pow(prefs[p1][it],2) for it in si])
    sum2Sq=sum([pow(prefs[p2][it],2) for it in si])

    #乘積之和
    pSum=sum([prefs[p1][it]*prefs[p2][it] for it in si])

    #計算皮爾遜相關係數
    num=pSum-(sum1*sum2/n)
    den=sqrt((sum1Sq-pow(sum1,2)/n)*(sum2Sq-pow(sum2,2)/n))

    if den==0:
        return 0
    r=num/den
    return r

  時間複雜度: O(n)
  關鍵公式:d=E(XY)E(X)E(Y)(E(X2)(E(X))2)(E(Y2)(E(Y))2)
  在python下輸入

>>> recommendations.sim_pearson(critics, 'Lisa Rose','Gene Seymour')

後可以得到Lisa Rose和Gene Seymour的相關係數為0.39605901719066977
  0就是不相關,越靠近1就是正相關,反之靠近-1就是負相關。
  3.Tanimoto相似度
  Tanimoto係數也稱為Jaccard係數,是Cosine相似度的擴充套件,也多用於計算文件資料的相似度
什麼情況下使用:處理無打分的偏好資料。

def sim_tanimoto(prefs, p1, p2):
    si = {}
    for item in prefs[p1]:
        if item in prefs[p2]:
            si[item] = 1
    length = len(si)
    if length == 0: return 0
    t = length/(len(prefs[p1]) + len(prefs[p2]) - length)
    return t

  關鍵公式:T(x,y)=xiyixi2+yi2xiyi
  什麼時候用歐幾里德,什麼時候用皮爾遜相關係數,取決於具體的應用和資料。

第三步:求與某一使用者品味相近的其他使用者的前幾名

#從字典中,選出品位最近的使用者
def topMatches(prefs,person,n=5,similarity=sim_pearson):
    #求引數使用者和其他所有使用者的相似係數
    scores=[(similarity(prefs,person,other),other) for other in prefs if other!=person]

    #排序
    scores.sort()
    scores.reverse()
    return scores[0:n]

第四步:根據品味相近的使用者推薦沒有看過的電影

  簡單的辦法就是去看那些品位相近的使用者,然後找一些我們沒看的電影看,不過這才草率了,如果這個使用者恰好喜歡什麼變態電影,我們豈不是要中標。所以最終還是需要對電影評分,這個分數需要和使用者的相似程度相關。

#利用所有他人評價值的加權平均,為某人提供建議
def getRecommendations(prefs,person,similarity=sim_pearson):
    totals={}
    simSums={}
    for other in prefs:
        if other==person:continue
        sim=similarity(prefs,person,other)

        #忽略評價值為零或者小於零的情況
        if sim<=0:continue

        for item in prefs[other]:
            #只對自己沒有看過的電影評價
            if item not in prefs[person] or prefs[person][item]==0:
                #相似度*評價值
                totals.setdefault(item,0)
                totals[item]+=prefs[other][item]*sim

                #相似度之和
                simSums.setdefault(item,0)
                simSums[item]+=sim

        #建立一個列表,列表裡面是person沒有看過的電影和評分
        rankings=[(total/simSums[item],item) for item,total in totals.items()]

        #排序
        rankings.sort()
        rankings.reverse()
        return rankings

  在python下輸入:

>>> recommendations.getRecommendations(critics, 'Michael Phillips')

就能得到為Michael Phillips推薦的電影和評分
[(2.8092760065251268, ‘Just My Luck’),
(2.694636703980363, ‘You, Me and Dupree’)]

  如果想知道哪些電影比較類似呢?怎麼做?
  我們可以把使用者和電影反過來,和尋找品位相近的使用者一樣,尋找類似的電影。

def transformPrefs(prefs):
    result={}
    for person in prefs:
        for item in prefs[person]:
            result.setdefault(item,{})

            result[item][person]=prefs[person][item]

    return result

基於物品的協同過濾怎麼做

  如果使用者成千上萬,這個時候去根據使用者計算就比較麻煩,因為使用者會不斷的增長,但是使用者所評價的物品的增長幅度不會有這麼快,那麼我們可以先把物品的相關程度計算出來,再推薦給使用者。
  為了比較物品,首先構造一個每個物品的相關物品的集合。

def calculateSimilarItems(prefs,n=10):
    #與這些電影最為相近的其他所有電影
    result={}

    #以物品為中心對偏好矩陣實施倒置處理
    itemPrefs=transformPrefs(prefs)
    c=0
    for item in itemPrefs:
    scores=topMatches(itemPrefs,item,n=n,similarity=sim_distance)
        result[item]=scores
    return result

  在python下輸入

>>> itemsim = recommendations.calculateSimilarItems(critics)

  這樣,給使用者推薦物品的時候,通過物品的相似度,還有相似物品的評分,通過公式計算,就能得出推薦使用者未購買或者評分的物品評分。

def getRecommendedItems(prefs,itemMatch,user):
    userRatings=prefs[user]
    scores={}
    totalSim={}
    #迴圈遍歷與當前物品相近的物品
    for (item,rating) in userRatings.items():

        for (similarity,item2) in itemMatch[item]:
            #如果該使用者已經對當前物品做出過評價,就忽略
            if item2 in userRatings:continue

            #評價值與相似度的加權之和
            scores.setdefault(item2,0)
            scores[item2]+=similarity*rating

            #全部相似度之和
            totalSim.setdefault(item2,0)
            totalSim[item2]+=similarity

    #將每個和值除以加權和,求平均值
    rankings=[(score/totalSim[item],item) for item,score in scores.items()]

    #排序
    rankings.sort()
    rankings.reverse()
    return rankings

  在python下輸入:

>>> recommendations.getRecommendedItems(critics, itemsim, 'Toby')

  可以得到:
[(3.182634730538922, ‘The Night Listener’),
(2.5983318700614575, ‘Just My Luck’),
(2.4730878186968837, ‘Lady in the Water’)]

另附

騰訊推薦系統架構圖
騰訊推薦系統架構圖


參考書籍:

  集體智慧程式設計