集體智慧編程 第二章 提供推薦

我們要知道在購物網站中,如何構建一個系統,用以尋找具有相同品味的人,並根據他人的喜好自動給出推薦。

一個協作型過濾算法通常的做法是對一大羣人進行搜索,並從中找到與我們品味相近的一小羣人。算法會對這些人所偏愛的其他內容進行考察,並將它們組合起來構造出一個經過排名的推薦列表。


蒐集偏好

第一件事是尋找一種表達不同人和偏好的方法,這裏用Python的嵌套字典來達到目的。

新建一個名爲 recommendations.py 的文件,加入如下代碼構造一個數據集:

# -*- coding:utf-8 -*-
# 集體智慧編程 第二章
# recommendations.py
#下列字典使用 1 到 5 的評分,體現包括本人在內的每位影評者對某一給定影片的喜愛程度

critics = {'Lisa Rose':{'Lady in the Water':2.5, 'Snake 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, 'Snake 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, 'Snake on a Plane':3.0,
            'Superman Returns':3.5, 'The Night Listener':4.0},
            
            'Claudia Puig':{'Snake 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, 'Snake 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, 'Snake on a Plane':4.0,
            'The Night Listener':3.0, 'Superman Returns':5.0, 'You, Me and Dupree':3.5},
            
            'Toby':{'Snake on a Plane':4.5, 'You, Me and Dupree':1.0, 'Superman Returns':4.0}}


同樣的,如果我們正在架設一個購物網站,不妨用數字 1 來代表有人過去曾購買過某件商品,用數字 0 來代表未曾購買過任何商品。而對於一個新聞網站可以用 -1 、 0 、1 來代表不喜歡、沒有投票、喜歡。

寫一個 run1.py 來運行程序:

# run recommendations.py

from recommendations import critics
print critics['Lisa Rose']['Lady in the Water']
critics['Toby']['Snakes on a Plane'] = 4.5
print critics['Toby']
結果:



另外:

critics['Toby']['Snakes on a Plane'] = 4.5

這句話似乎沒什麼意義,因爲本來字典裏面也是 4.5 的評分。

====================================================================================

尋找相近的用戶

蒐集完偏好數據以後,我們需要有一種方法確定人們在品味方面的相似度。爲此,我們可以將每個人與所有其他人進行對比,並計算他們的相似度評價值。這裏有兩種體系:歐幾里得距離皮爾遜相關度。


歐幾里得距離評價:它以經過人們一致評價的物品爲座標軸,然後將參與評價的人繪製到地圖上,並考查他們彼此間的距離遠近。


就這樣簡單,兩個人的“距離”越近,偏好就越相似。

比如 Toby 和 LaSalle

from math import sqrt
1/(1+sqrt(pow(4.5-4,2) + pow(1-2,2)))

這個新函數總是返回介於 0 到 1 之間的數值,返回 1 表示兩人有一樣的偏好。

在 recommendations.py 裏面加入代碼:

from math import sqrt

# 返回一個有關 person1 和 person2 的基於距離的相似度評價
def sim_distance(prefs, person1, person2):
    #得到 shared_items 的列表
    si={}
    for item in prefs[person1]:
        if item in prefs[person2]:    
            si[item] = 1

    # 如果沒有共同之處,返回 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))


改變 run1 文件:

# run recommendations.py
import recommendations
from recommendations import critics
print critics['Lisa Rose']['Lady in the Water']
critics['Toby']['Snakes on a Plane'] = 4.5
print critics['Toby']

print '=========================================='

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

結果:


這個 0.294298055086 就是兩者的相似度評價。


皮爾遜相關度評價:該相關係數是判斷兩組數據與某一直線擬合程度的一種度量。

Mick給《Superman》打了3分,Gene打了5分,所以我們把這個影片定位在(3,5)處。這樣定位完畢以後,作一條線性迴歸線就行了。這條線稱爲“最佳擬合線”。

皮爾遜相關度評價算法首先會找出兩位評論者都曾評價過的物品,然後計算兩者評分總和與平方和,並求得評分的乘積之和。

在之前的代碼裏面添加:

# 返回 p1 和 p2 的皮爾遜相關係數
def sim_pearson(prefs, p1, p2):
    # 得到雙方都曾評價過的物品列表
    si = {}
    for item in prefs[p1]:
        if item in prefs[p2]:si[item] = 1

    # 得到 si 列表元素的個數
    n = len(si)

    # 如果兩者沒有共同之處,返回 1
    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


運行函數裏面寫上:

print '=========================================='
print 'sim_pearson'

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

結果:



==========================================================================

現在的問題是:應該用哪一種相似性度量方法?

其實這是個統計學問題,以後還會遇到Jaccard係數或者曼哈頓距離算法。只能說具體情況具體分析


=========================================================================================

爲評論者打分

我們有了兩個人進行比較的函數,下面就可以根據指定人員對每個人打分,找出最接近的匹配結果。

添加代碼:

# 從反映偏好的字典中返回最爲匹配者
# 返回結果的個數和相似度函數均爲可選參數
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]

給出全部運行程序:

# run recommendations.py
import recommendations
from recommendations import critics
print critics['Lisa Rose']['Lady in the Water']
critics['Toby']['Snakes on a Plane'] = 4.5
print critics['Toby']

print '\n=========================================='
print 'sim_distance'
print recommendations.sim_distance(recommendations.critics,'Lisa Rose','Gene Seymour')

print '\n=========================================='
print 'sim_pearson'
print recommendations.sim_pearson(recommendations.critics,'Lisa Rose','Gene Seymour')

print '\n=========================================='
print 'topMatches'
print recommendations.topMatches(recommendations.critics,'Toby', n = 3)

結果:



===========================================================

推薦物品

我們想要的其實是影片的推薦。

中列出了每位評論者的相關度評價值,以及他們對三部影片的評分情況。以S.x開頭的列是乘以評價值之後的相似度。如此一來,與我們相近的人對整體評價值有更多的貢獻。

同時還要考慮到,一部受更多人評論的影片會對結果有更大影響,也要修正這個問題。

在代碼裏面加入:

# 利用所有他人評價值的加權平均,爲某人提供建議
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) 
                # setdefault() 函數: 如果鍵不已經存在於字典中,將會添加鍵並將值設爲默認值。
                # dict.setdefault(key, default=None)
                # key -- 查找的鍵值。
                # default -- 鍵不存在時,設置的默認鍵值。
                totals[item] += prefs[other][item] * sim
                # 相似度之和
                simSums.setdefault(item, 0)
                simSums[item] += sim

    # 建立一個歸一化的列表
    rankings = [(total / simSums[item], item) for item, total in totals.items()] # 列表推導式
    # 返回經過排序的列表
    rankings.sort()
    rankings.reverse()
    return rankings


需要說明的是,這裏要注意列表推導式這個東西,百度一下格式和用法。


然後運行裏面添加:

print '\n=========================================='
print 'getRecommendations.sim_pearson'
print recommendations.getRecommendations(recommendations.critics, 'Toby')

print '\n=========================================='
print 'getRecommendations.sim_distance'
print recommendations.getRecommendations(recommendations.critics, 'Toby',\
                                         similarity = recommendations.sim_pearson)

結果:



這裏不僅得到了一個經過排名的影片列表,還推測除了自己對每部影片的評價情況。

電影推薦到此爲止,下一章是商品推薦。

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