本文引自http://blog.csdn.net/database_zbye/article/details/8664516
本文介紹推薦系統(tǒng)、協(xié)同過濾思想,兩種基本的相似度衡量,并用python實現(xiàn)。最后就MovieLens數(shù)據(jù)集上作出簡單的推薦。
一、相關(guān)知識
(1)推薦系統(tǒng)
如今,推薦系統(tǒng)已經(jīng)在多方面得到應(yīng)用,例如淘寶、當當、亞馬遜等網(wǎng)站的商品推薦。而個性化推薦系統(tǒng)則是通過發(fā)掘用戶的興趣愛好,作出針對性的推薦。個性化推薦的方法較多,最常用的是協(xié)同過濾方法,而本文主要講的也是基于協(xié)同過濾的個性化推薦。
(2)協(xié)同過濾
協(xié)同過濾技術(shù)出現(xiàn)于20世紀70年代,到90年代形成較為成熟的理論框架。協(xié)同過濾的基本假設(shè)是如果用戶x和y對n個項目的評價或行為是相似的,那么他們對其他項目所持有的觀點也是相似的,即行為相似的用戶興趣也可能相似。
一個協(xié)同過濾算法的基本方法是對大批用戶進行搜索,從中找出興趣相似的用戶群。算法會對這些人的所偏愛的內(nèi)容進行考察,然后構(gòu)造出推薦列表,推薦給該群體的用戶。
(3)Python語言
二、相似度測量方法
主要有兩種:歐幾里得距離和皮爾遜相關(guān)度(pearson)
(1)數(shù)據(jù)集
Prefer以字典形式存放用戶看過的電影和評分。
Prefer = {"tommy":{'War':2.3,'The lord of wings':3.0,'Kongfu':5.0},
"lily":{'War':2.0,'The lord of wings':3.6,'Kongfu':4.1},
"jim":{'War':1.9,'The lord of wings':4.0,'Beautiful America':4.7,'the big bang':1.0},
'jack':{'War':2.8,'The lord of wings':3.5,'Kongfu':5.5}
}
(2)、尋找有相似興趣的用戶,方式是比較各用戶的評價數(shù)據(jù),計算用戶間的相似度。主要方法有2種即歐幾里得距離和皮爾遜相關(guān)度。
2-1歐幾里得距離
使用歐幾里得距離計算用戶的相似度時,將用戶評價的物品作為坐標軸,用戶填充到坐標體系中。以二維為例,

上圖中,Snakes和Dupree為電影,Toby,Lasclle等人對應(yīng)的點根據(jù)其評分情況被畫在相應(yīng)的位置。這樣兩者在偏好空間中的距離越近,他們的興趣就越相似。而此模型可以推廣至多維情況。
2-2 Pearson相關(guān)度評價
歐幾里得距離評價法是一種比較簡單的方法。但是由于存在一些用戶總是傾向于評分過高或過低(相對平均值),這是興趣相似的用戶并不能通過此方法計算出來。Pearson相關(guān)系數(shù)是根據(jù)兩組數(shù)據(jù)與某一直線的擬合程度來衡量的。

此坐標系以用戶為坐標軸,用戶所評分的電影顯示在對應(yīng)位置。本方法可修正結(jié)果,增強準確性。例如TOM, Lily對電影A,B,C的評分為(2, 4.1, 4), (3, 5, 5 ),則用pearson方法得到兩者相似度仍然較高,而歐幾里得距離法得到的相似度則偏低。實際上是用戶Lily傾向于評分更高。
(3)、編程并測試
#---------------歐幾里得空間距離方法---------------
設(shè)原先距離L,L越小,越相似。本處采用改進的測量方法:sim=1/L+1,sim越大,越相似
def sim_distance(prefer, person1, person2):
sim = {}
for item in prefer[person1]:
if item in prefer[person2]:
sim[item] = 1 #添加共同項到字典中
#無共同項,返回0
if len(sim)==0:
return 0
#計算所有共有項目的差值的平方和
sum_all = sum([pow(prefer[person1][item]-prefer[person2][item], 2)
for item in sim])
#返回改進的相似度函數(shù)
return 1/(1+sqrt(sum_all))
#測試
print("\n測試計算歐幾里得距離的方法sim_distance()....")
print("sim_distance(dic,'lily','jim') = ",sim_distance(dic, 'lily', 'jim'))
print("sim_distance(dic,'tommy','jim') = ",sim_distance(dic, 'tommy', 'jim'))
print("sim_distance(dic,'tommy','lily') = ",sim_distance(dic, 'tommy', 'lily'))
print("sim_distance(dic,'tommy','jack') = ",sim_distance(dic, 'tommy', 'jack'))
"""
結(jié)果:
0.7080595631785951
0.4814560089181300
0.471143138585317
0.5358983848622454
"""
#-----------------------pearson相關(guān)度系數(shù)------------------------
def sim_pearson(prefer, person1, person2):
sim = {}
#查找雙方都評價過的項
for item in prefer[person1]:
if item in prefer[person2]:
sim[item] = 1 #將相同項添加到字典sim中
#元素個數(shù)
n = len(sim)
if len(sim)==0:
return -1
# 所有偏好之和
sum1 = sum([prefer[person1][item] for item in sim]) #1.sum([1,4,5,,,]) 2.list的靈活生成方式!(Python靈活)
sum2 = sum([prefer[person2][item] for item in sim]) #!!!!寫成person1,導(dǎo)致程序一直runtime error!!!!
#求平方和
sum1Sq = sum( [pow(prefer[person1][item] ,2) for item in sim] )
sum2Sq = sum( [pow(prefer[person2][item] ,2) for item in sim] )
#求乘積之和 ∑XiYi
sumMulti = sum([prefer[person1][item]*prefer[person2][item] for item in sim])
#Pearson系數(shù)計算 http://baike.baidu.com/view/3891263.htm,計算錯誤
#num1 = n*sumMulti - sum1*sum2 #分子
#num2 = sqrt(n*sum1Sq-pow(sum1,2))*sqrt(n*sum2Sq-pow(sum2,2))
num1 = sumMulti - (sum1*sum2/n)
num2 = sqrt( (sum1Sq-pow(sum1,2)/n)*(sum2Sq-pow(sum2,2)/n))
if num2==0:
return 0
return num1/num2
#測試
print("\n測試計算Pearson系數(shù)的方法sim_pearson()....")
print("sim_pearson(dic,'lily','jim') = ",sim_pearson(dic, 'lily', 'jim'))
print("sim_pearson(dic,'tommy','jim') = ",sim_pearson(dic, 'tommy', 'jim'))
print("sim_pearson(dic,'tommy','lily') = ",sim_pearson(dic, 'tommy', 'lily'))
print("sim_pearson(dic,'tommy','jack') = ",sim_pearson(dic, 'tommy', 'jack'))
"""
結(jié)果:
0.9999999999999991
0.9999999999999988
0.8446877845160871
0.9999999999999973 **[比較tommy和jack,皮爾遜系數(shù)和歐幾里得距離差別很大,原因是jack一直評分偏高]**
"""
三、獲取推薦列表
依然以上面的prefer數(shù)據(jù)集為基礎(chǔ),根據(jù)計算結(jié)果將興趣相似的用戶評分較高的電影推薦給用戶。
(1)獲取相似用戶
#從用戶評價字典中返回Top-K匹配者
#K,相似度函數(shù) 為可選參數(shù)
def topMatches(prefer, person, n=2, similarity=sim_pearson):
scores=[ (similarity(prefer,person,other),other) for other in prefer if other!=person ]
#對scores列表排序,從高到底
scores.sort()
scores.reverse()
return scores[0:n] #返回排序列表, 注意[0:n],僅返回前n項;
#測試
print("\n測試topMatches()方法......")
print(topMatches(dic, 'tommy'))
"""
結(jié)果:[(0.9999999999999988, 'jim'), (0.9999999999999973, 'jack')] (相似度,姓名)
"""
(2)、獲取推薦
# 提供推薦,利用所有人評價的加權(quán)均值。 相似度高,影響因子越大。
def getRecommendations(prefer, person, similarity=sim_pearson):
totals = {}
simSums = {}
for other in prefer:
if other == person:
continue
else:
sim = similarity(prefer, person, other) #計算比較其他用戶的相似度
#相似度>0
if sim<=0: continue
for item in prefer[other]:
if item not in prefer[person]:
#加權(quán)評價值:相似度*評價值
totals.setdefault(item,0) #每輪循環(huán)開始時初始化為0
totals[item] += prefer[other][item]*sim
#相似度之和
simSums.setdefault(item,0)
simSums[item] += sim
#建立歸一化列表
ranks = [ (total/simSums[item],item) for item,total in totals.items() ]
#返回經(jīng)排序后的列表
ranks.sort()
ranks.reverse()
return ranks
#測試
print("\n測試推薦方法getRecommendations(prefer, person, similarity=sim_pearson)......")
print(getRecommendations(dic, 'tommy'))
"""
結(jié)果: [(4.7, 'Beautiful America')]
"""
四、利用MovieLens公開數(shù)據(jù)集做推薦
(1)、文件一loadMovieLens.py,加載測試集數(shù)據(jù)
def loadMovieLens():
str1 = '/data/movielens/u.item'
#獲取影片的id和標題(其他項類似)
movies = {}
for line in open(str1,'r'):
(id,title) = line.split('|')[0:2] #將返回列表的前2元素賦給元組
movies[id] = title
#print(movies[id]) [測試輸出正常,但是遇到UnicodeEncodeError?]
# 加載數(shù)據(jù)
prefer = {}
for line in open('/data/movielens/u.data'):
(user, movieid, rating,ts) = line.split('\t') #數(shù)據(jù)集中每行有4項
prefer.setdefault(user, {}) #設(shè)置字典的默認格式,元素是user:{}字典
prefer[user][movies[movieid]] = float(rating)
# 返回字典:user以及評價過的電影
return prefer
(2)、文件二:recommendation.py 做推薦
和三中介紹的方法放置于同一文件recommendation.py下。
from loadMovieLens import loadMovieLens
print("\n-------------------MovieLens 測試數(shù)據(jù)集的推薦系統(tǒng)--------------------")
prefers = loadMovieLens()
print("\n1.*********基于用戶的推薦**********\n")
print("用戶87的評價列表為:",prefers['87'])
print("\n**********推薦影片*********\n")
tuijian = getRecommendations(prefers, '87')[0:20]
print(tuijian)
(3)利用MovieLens做測試是出現(xiàn)編碼錯誤提示,后來發(fā)現(xiàn)時數(shù)據(jù)集中某行格式不規(guī)范,用軟件打開u.item,修改下即可。
參考書籍:programing collective intelligence.
浙公網(wǎng)安備 33010602011771號