基於內容的推薦引擎是怎么工作的
基於內容的推薦系統,正如你的朋友和同事預期的那樣,會考慮商品的實際屬性,比如商品描述,商品名,價格等等。如果你以前從沒接觸過推薦系統,然后現在有人拿槍指着你的頭,強迫你在三十秒之內描述出來,你可能會描述這樣一個基於內容的系統:呃,呃,我可能會給你看一大堆來自同一個廠家,並且擁有類似的說明的產品。
你正在利用商品本身的屬性來推薦類似的商品。這樣做非常合理,因為這就是我們在真實世界中買東西的方式。我們去賣烤箱的那一排貨架,然后看這些烤箱,它們可能根據不同的品牌,價格,或着能在30分鍾之內烤熟一只完整的火雞,等等特點在貨架上擺放。
現今,推薦系統被用來個性化你在網上的體驗,告訴你買什么,去哪里吃,甚至是你應該和誰做朋友。人們口味各異,但通常有跡可循。人們傾向於喜歡那些與他們所喜歡的東西類似的東西,並且他們傾向於與那些親近的人有相似的口味。推薦系統試圖捕捉這些模式,以助於預測你還會喜歡什么東西。電子商務、社交媒體、視頻和在線新聞平台已經積極的部署了它們自己的推薦系統,以幫助它們的客戶更有效的選擇產品,從而實現雙贏。
兩種最普遍的推薦系統的類型是 基於內容和 協同過濾(CF)。協同過濾基於用戶對產品的態度產生推薦,也就是說,它使用“人群的智慧”來推薦產品。與此相反,基於內容的推薦系統集中於物品的屬性,並基於它們之間的相似性為你推薦。一般情況下,協作過濾(CF)是推薦引擎的主力。該算法具有能夠自身進行特征學習的一個非常有趣的特性,這意味着它可以開始學習使用哪些特性。CF可以分為 基於內存的協同過濾和 基於模型的協同過濾。在本教程中,你將使用奇異值分解(SVD)實現基於模型的CF和通過計算余弦相似實現基於內存的CF。 Python實現的具體實例文章內容:基於物品過濾與基於用戶過濾。
數據稀疏時候,用物品過濾最優;數據密集,兩者效果一樣。
下面以電影推薦為例:
一、原始數據處理:
原始數據為二維矩陣:行是用戶,列是電影:
| Lady in the Water | Snakes on a Plane | Just My Luck | Superman Returns | You, Me and Dupree | The Night Listener | |
| Lisa Rose | 2.5 | 3.5 | 3.5 | 3.5 | 2.5 | 3 |
| Gene Seymour | 3 | 3.5 | 1.5 | 5 | 3.5 | 3 |
| Michael Phillips | 2.5 | 3 | 3.5 | 4 | ||
| Claudia Puig | 3.5 | 3 | 4 | 2.5 | 4.5 | |
| Mick LaSalle | 3 | 4 | 2 | 3 | 2 | 3 |
| Jack Matthews | 3 | 4 | 5 | 3.5 | 3 | |
| Toby | 4.5 | 4 | 1 |
采用數據結構:字典套字典:如此即可表現出數據二維矩陣:結構即data[user][movie]=rating
此數據整體是一個字典,key=用戶,value=該用戶看過的電影信息 ,其中value又是一個字典(key=電影,value=評分)
data ={'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},
</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Gene Seymour</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: {<span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Lady in the Water</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 3.0, <span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Snakes on a Plane</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 3.5<span style="line-height:1.5 !important;">,
</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Just My Luck</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 1.5, <span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Superman Returns</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 5.0, <span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">The Night Listener</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 3.0<span style="line-height:1.5 !important;">,
</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">You, Me and Dupree</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 3.5<span style="line-height:1.5 !important;">},
</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Michael Phillips</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: {<span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Lady in the Water</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 2.5, <span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Snakes on a Plane</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 3.0<span style="line-height:1.5 !important;">,
</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Superman Returns</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 3.5, <span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">The Night Listener</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 4.0<span style="line-height:1.5 !important;">},
</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Claudia Puig</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: {<span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Snakes on a Plane</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 3.5, <span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Just My Luck</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 3.0<span style="line-height:1.5 !important;">,
</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">The Night Listener</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 4.5, <span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Superman Returns</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 4.0<span style="line-height:1.5 !important;">,
</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">You, Me and Dupree</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 2.5<span style="line-height:1.5 !important;">},
</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Mick LaSalle</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: {<span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Lady in the Water</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 3.0, <span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Snakes on a Plane</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 4.0<span style="line-height:1.5 !important;">,
</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Just My Luck</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 2.0, <span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Superman Returns</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 3.0, <span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">The Night Listener</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 3.0<span style="line-height:1.5 !important;">,
</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">You, Me and Dupree</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 2.0<span style="line-height:1.5 !important;">},
</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Jack Matthews</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: {<span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Lady in the Water</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 3.0, <span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Snakes on a Plane</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 4.0<span style="line-height:1.5 !important;">,
</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">The Night Listener</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 3.0, <span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Superman Returns</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 5.0, <span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">You, Me and Dupree</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: 3.5<span style="line-height:1.5 !important;">},
</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Toby</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>: {<span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Snakes on a Plane</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>:4.5,<span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">You, Me and Dupree</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>:1.0,<span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">Superman Returns</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span>:4.0<span style="line-height:1.5 !important;">}
}
二、物品過濾
1.先計算所有電影(物品)之間的相似度,構造一個包含相近電影(物品)的完整數據集。
1.1 匹配電影物品,找出那些電影物品是彼此相近的
因為計算以電影物品為主,所以先將上面data[user][movie]數據轉換成newdata[movie][user]格式,也即二維矩陣進行行列對換
def transformdata(data): ''' 物品之間的相似度 與 用戶之間的相似度 求解 一樣。故只需要將用戶換成物品即可 ''' newdata = {} users ={} for person in data: for movie in data[person]: #初始化 newdata.setdefault(movie,{}) #物品與用戶對調 newdata[movie][person] = data [person][movie] #字典可以直接寫[key],就表示插入key值了。非常簡便 return newdata
''' 調用此方法例子: print transformdata(data) 結果是: {'Lady in the Water': {'Lisa Rose': 2.5, 'Jack Matthews': 3.0, 'Michael Phillips': 2.5, 'Gene Seymour': 3.0, 'Mick LaSalle': 3.0}, 'Snakes on a Plane': {'Jack Matthews': 4.0, 'Mick LaSalle': 4.0, 'Claudia Puig': 3.5, 'Lisa Rose': 3.5, 'Toby': 4.5, 'Gene Seymour': 3.5, 'Michael Phillips': 3.0}, 'Just My Luck': {'Claudia Puig': 3.0, 'Lisa Rose': 3.0, 'Gene Seymour': 1.5, 'Mick LaSalle': 2.0}, 'Superman Returns': {'Jack Matthews': 5.0, 'Mick LaSalle': 3.0, 'Claudia Puig': 4.0, 'Lisa Rose': 3.5, 'Toby': 4.0, 'Gene Seymour': 5.0, 'Michael Phillips': 3.5}, 'The Night Listener': {'Jack Matthews': 3.0, 'Mick LaSalle': 3.0, 'Claudia Puig': 4.5, 'Lisa Rose': 3.0, 'Gene Seymour': 3.0, 'Michael Phillips': 4.0}, 'You, Me and Dupree': {'Jack Matthews': 3.5, 'Mick LaSalle': 2.0, 'Claudia Puig': 2.5, 'Lisa Rose': 2.5, 'Toby': 1.0, 'Gene Seymour': 3.5}} '''
1.2 用calSimilarItems()獲得所有電影之間的相似度。
1.2.1 計算相似度,就要涉及相似距離度量,這里列舉兩種:歐氏距離sim_distance ,皮爾遜sim_pearson 。兩種都已設法表示距離越大,越相似
from math import sqrt def sim_distance(data,person1,person2): '''歐氏距離求相似度,距離越大,越相似''' commonmovies = [ movie for movie in data[person1] if movie in data[person2]] if len(commonmovies)== 0: return 0 #平方和 sumSq =sum([pow(data[person1][movie] -data[person2][movie],2) for movie in commonmovies ] ) #使最終結果是,越相似,距離越大。所以將上面距離取倒數即可 sim = 1/(1+ sqrt(sumSq)) return simdef sim_pearson(data,person1,person2):
'''
計算上面格式的數據 里的 兩個用戶 相似度.
基於用戶過濾思路:找出兩個用戶看過的相同電影的評分,從而進行按pearson公式求值。那些非公共電影不列入求相似度值范圍。
基於物品過濾思路:找過兩部電影相同的觀影人給出的評分,從而按pearson公式求值
返回:評分的相似度,[-1,1]范圍,0最不相關,1,-1為正負相關,等於1時,表示兩個用戶完全一致評分
這里的data格式很重要,這里計算相似度是嚴格按照上面data格式所算。
此字典套字典格式,跟博客計算單詞個數 存儲格式一樣
'''
#計算pearson系數,先要收集兩個用戶公共電影名單
#commonmovies = [ movie for movie in data[person1] if movie in data[person2]] 分解步驟為如下:
commonmovies = [] #改成列表呢
for movie in data[person1]: #data[person1]是字典,默認第一個元素 in (字典)是指 key.所以這句話是指 對data[person1]字典里遍歷每一個key=movie
if movie in data[person2]: #data[person2]也是字典,表示該字典有key是movie.
commonmovies.append(movie) # commonmovie是 兩個用戶的公共電影名的列表<span style="color:rgb(0,128,0);line-height:1.5 !important;">#</span><span style="color:rgb(0,128,0);line-height:1.5 !important;">看過的公共電影個數</span> n =<span style="line-height:1.5 !important;"> float(len(commonmovies)) </span><span style="color:rgb(0,0,255);line-height:1.5 !important;">if</span> n==<span style="line-height:1.5 !important;">0: </span><span style="color:rgb(0,0,255);line-height:1.5 !important;">return</span><span style="line-height:1.5 !important;"> 0 </span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'''</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">下面正是計算pearson系數公式 </span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'''</span> <span style="color:rgb(0,128,0);line-height:1.5 !important;">#</span><span style="color:rgb(0,128,0);line-height:1.5 !important;">分布對兩個用戶的公共電影movie分數總和</span> sum1 = sum([data[person1][movie]<span style="color:rgb(0,0,255);line-height:1.5 !important;">for</span> movie <span style="color:rgb(0,0,255);line-height:1.5 !important;">in</span><span style="line-height:1.5 !important;"> commonmovies ]) sum2 </span>= sum([data[person2][movie]<span style="color:rgb(0,0,255);line-height:1.5 !important;">for</span> movie <span style="color:rgb(0,0,255);line-height:1.5 !important;">in</span><span style="line-height:1.5 !important;"> commonmovies]) </span><span style="color:rgb(0,128,0);line-height:1.5 !important;">#</span><span style="color:rgb(0,128,0);line-height:1.5 !important;">計算乘積之和</span> sum12 = sum([data[person1][movie]*data[person2][movie] <span style="color:rgb(0,0,255);line-height:1.5 !important;">for</span> movie <span style="color:rgb(0,0,255);line-height:1.5 !important;">in</span><span style="line-height:1.5 !important;"> commonmovies]) </span><span style="color:rgb(0,128,0);line-height:1.5 !important;">#</span><span style="color:rgb(0,128,0);line-height:1.5 !important;">計算平方和</span> sum1Sq = sum([ pow(data[person1][movie],2 ) <span style="color:rgb(0,0,255);line-height:1.5 !important;">for</span> movie <span style="color:rgb(0,0,255);line-height:1.5 !important;">in</span><span style="line-height:1.5 !important;"> commonmovies ]) sum2Sq </span>= sum([ pow(data[person2][movie],2 ) <span style="color:rgb(0,0,255);line-height:1.5 !important;">for</span> movie <span style="color:rgb(0,0,255);line-height:1.5 !important;">in</span><span style="line-height:1.5 !important;"> commonmovies ]) </span><span style="color:rgb(0,128,0);line-height:1.5 !important;">#</span><span style="color:rgb(0,128,0);line-height:1.5 !important;">計算分子 </span> num = sum12 - sum1*sum2/<span style="line-height:1.5 !important;">n </span><span style="color:rgb(0,128,0);line-height:1.5 !important;">#</span><span style="color:rgb(0,128,0);line-height:1.5 !important;">分母</span> den = sqrt((sum1Sq - pow(sum1,2)/n)*(sum2Sq - pow(sum2,2)/<span style="line-height:1.5 !important;">n)) </span><span style="color:rgb(0,0,255);line-height:1.5 !important;">if</span> den==0: <span style="color:rgb(0,0,255);line-height:1.5 !important;">return</span><span style="line-height:1.5 !important;"> 0 </span><span style="color:rgb(0,0,255);line-height:1.5 !important;">return</span> num/den</pre>
1.2.2 為單個電影物品返回最匹配結果
def topmatches(data,givenperson ,returnernum = 5,simscore = sim_pearson): ''' 用戶匹配推薦:給定一個用戶,返回對他口味最匹配的其他用戶 物品匹配: 給定一個物品,返回相近物品 輸入參數:對person進行默認推薦num=5個用戶(基於用戶過濾),或是返回5部電影物品(基於物品過濾),相似度計算用pearson計算 ''' #建立最終結果列表 usersscores =[(simscore(data,givenperson,other),other) for other in data if other != givenperson ] #對列表排序 usersscores.sort(cmp=None, key=None, reverse=True)</span><span style="color:rgb(0,0,255);line-height:1.5 !important;">return</span><span style="line-height:1.5 !important;"> usersscores[0:returnernum]
''' 調用以前方法:找物品相關匹配: moviedata = transformdata(data) #找出跟“超人回歸”這電影相關的電影 print topmatches(moviedata, 'Superman Returns')結果是:
[(0.6579516949597695, 'You, Me and Dupree'), (0.4879500364742689, 'Lady in the Water'),
(0.11180339887498941, 'Snakes on a Plane'), (-0.1798471947990544, 'The Night Listener'), (-0.42289003161103106, 'Just My Luck')]
其中負數表示,討厭此電影
'''
1.2.3 基於上面1.2.2,從為單一物品返回匹配結果 擴展到 為所有物品返回匹配結果
def calSimilarItems(data,num=10):
#以物品為中心,對偏好矩陣轉置
moviedata = transformdata(data) ItemAllMatches = {} for movie in moviedata: ItemAllMatches.setdefault(movie,[]) #對每個電影 都求它的匹配電影集,求電影之間的距離用歐式距離,用pearson距離測出的結果是不一樣的 ItemAllMatches[movie] = topmatches(moviedata, movie, num,simscore = sim_distance) return ItemAllMatches
''' 構造一個物品匹配完整集:包含所有物品的對應的匹配物品 即是電影相似度的字典: key = movie ,value = [(othermovie,simscore)]。其中value是一個元組對的列表 調用例子; print calSimilarItems(data) 結果為: {'Lady in the Water': [(0.7637626158259785, 'Snakes on a Plane'), (0.4879500364742689, 'Superman Returns'), (0.3333333333333333, 'You, Me and Dupree'), (-0.6123724356957927, 'The Night Listener'), (-0.9449111825230676, 'Just My Luck')], 'Snakes on a Plane': [(0.7637626158259785, 'Lady in the Water'), (0.11180339887498941, 'Superman Returns'), (-0.3333333333333333, 'Just My Luck'), (-0.5663521139548527, 'The Night Listener'), (-0.6454972243679047, 'You, Me and Dupree')], 'Just My Luck': [(0.5555555555555556, 'The Night Listener'), (-0.3333333333333333, 'Snakes on a Plane'), (-0.42289003161103106, 'Superman Returns'), (-0.4856618642571827, 'You, Me and Dupree'), (-0.9449111825230676, 'Lady in the Water')], ......... '''
2.推薦用戶沒看過的電影
某一部未看過電影分數= sum(該部未看過的電影與每一部已看電影之間相似度*已看電影的評分) /sum(未看電影與每一部已看電影之間相似度)
例如:未看電影A,已看電影B,C:
則,電影A分數 = [sim(A,B)*rating(B) +sim(A,C)*rating(C)] / [ sim(A,B) + sim(A,C)]
def getrecommendations(data,targetperson,moviesAllsimilarity): ''' 輸入movieAllSimilarity就是上面calsimilarItems已經計算好的所有物品之間的相似度數據集: ''' #獲得所有物品之間的相似數據集 scoresum = {} simsum = {} #遍歷所有看過的電影 for watchedmovie in data[targetperson]: rating = data[targetperson][watchedmovie] #遍歷與當前電影相近的電影 for(similarity,newmovie) in moviesAllsimilarity[watchedmovie]: #取一對元組 #已經對當前物品評價過,則忽略 if newmovie in data[targetperson] :continue
scoresum.setdefault(newmovie,0) simsum.setdefault(newmovie,0) #全部相似度求和 simsum[newmovie] += similarity #評價值與相似度加權之和 scoresum[newmovie] += rating * similarityrankings </span>= [(score/simsum[newmovie] , newmovie) <span style="color:rgb(0,0,255);line-height:1.5 !important;">for</span> newmovie,score <span style="color:rgb(0,0,255);line-height:1.5 !important;">in</span><span style="line-height:1.5 !important;"> scoresum.items() ] rankings.sort(cmp</span>=None, key=None, reverse=<span style="line-height:1.5 !important;">True) </span><span style="color:rgb(0,0,255);line-height:1.5 !important;">return</span><span style="line-height:1.5 !important;"> rankings</span></pre>'''調用此方法如下:
itemsAllsim = calSimilarItems(data) #這個值會事先計算好 print '基於物品過濾,為用戶Toby推薦的電影是:' print getrecommendations(data, 'Toby',itemsAllsim) 返回結果是: [(3.1667425234070894, 'The Night Listener'), (2.936629402844435, 'Just My Luck'), (2.868767392626467, 'Lady in the Water')]
這樣基於物品過濾推薦,為Toby用戶推薦的電影結果 就完成了。
'''
三、基於用戶過濾
3.1 推薦品味相近的用戶
#假設為用戶Toby進行推薦品味相當的用戶,則調用userstopmatcher方法,會返回一個影評人及其相似度 的列表 print topmatches(data, 'Toby', 3) #返回結果為: [(0.9912407071619299, 'Lisa Rose'), (0.9244734516419049, 'Mick LaSalle'), (0.8934051474415647, 'Claudia Puig')] #所以lisa分數最高,應該推薦lisa Rose品味跟Toby最近
3.2 推薦未看過的電影:
未看過電影分數=sum(被推薦用戶與其他用戶之間相似度*用戶對該電影評分)/sum(被推薦用戶與其他用戶之間相似度)
def recommendItems(data,givenperson,num =5 ,simscore = sim_pearson): ''' 物品推薦:給定一個用戶person,默認返回num=5物品 要兩個for,對用戶,物品 都進行 遍歷 ''' #所有變量盡量用字典,凡是列表能表示的字典都能表示,那何不用字典 itemsimsum={} #存給定用戶沒看過的電影的其他用戶評分加權 itemsum={}
#遍歷每個用戶,然后遍歷該用戶每個電影 for otheruser in data : #不要和自己比較 if otheruser == givenperson: continue #忽略相似度=0或小於0情況 sim = simscore(data,givenperson,otheruser) if sim <=0: continue<span style="color:rgb(0,0,255);line-height:1.5 !important;">for</span> itemmovie <span style="color:rgb(0,0,255);line-height:1.5 !important;">in</span><span style="line-height:1.5 !important;"> data[otheruser]: </span><span style="color:rgb(0,128,0);line-height:1.5 !important;">#</span><span style="color:rgb(0,128,0);line-height:1.5 !important;">只對用戶沒看過的電影進行推薦,參考了其他用戶的評價值(協同物品過濾是參考了歷史物品相似度值)</span> <span style="color:rgb(0,0,255);line-height:1.5 !important;">if</span> itemmovie <span style="color:rgb(0,0,255);line-height:1.5 !important;">not</span> <span style="color:rgb(0,0,255);line-height:1.5 !important;">in</span><span style="line-height:1.5 !important;"> data[givenperson]: </span><span style="color:rgb(0,128,0);line-height:1.5 !important;">#</span><span style="color:rgb(0,128,0);line-height:1.5 !important;">一定要初始化字典:初始化itemsum與itemsimsum</span>itemsum.setdefault(itemmovie,0)
itemsimsum.setdefault(itemmovie,0)
#用戶相似度*評價值
itemsum[itemmovie] += sim * data[otheruser][itemmovie]
itemsimsum[itemmovie] += sim</span><span style="color:rgb(0,128,0);line-height:1.5 !important;">#</span><span style="color:rgb(0,128,0);line-height:1.5 !important;">最終結果列表,列表包含一元組(item,分數)</span> rankings = [(itemsum[itemmovie] / itemsimsum[itemmovie],itemmovie) <span style="color:rgb(0,0,255);line-height:1.5 !important;">for</span> itemmovie <span style="color:rgb(0,0,255);line-height:1.5 !important;">in</span><span style="line-height:1.5 !important;"> itemsum] </span><span style="color:rgb(0,128,0);line-height:1.5 !important;">#</span><span style="color:rgb(0,128,0);line-height:1.5 !important;">結果排序</span> rankings.sort(cmp=None, key=None, reverse=<span style="line-height:1.5 !important;">True); </span><span style="color:rgb(0,0,255);line-height:1.5 !important;">return</span><span style="line-height:1.5 !important;"> rankings#調用此方法如下:
# print recommendItems(data, 'Toby', 3)返回結果:
#[(3.3477895267131013, 'The Night Listener'), (2.8325499182641614, 'Lady in the Water'), (2.5309807037655645, 'Just My Luck')]
四、用真實數據:Movielens數據集
4.1數據原型:
u.item 文件: 記錄了 電影id與電影名的映射關系 其內容節選: 1|Toy Story (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Toy%20Story%20(1995)|0|0|0|1|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0 2|GoldenEye (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?GoldenEye%20(1995)|0|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0u.data 文件:
每行信息是:用戶id,電影id,評分,評分時間(這里我們只用到前三個屬性)
其內容節選:
196 242 3 881250949
186 302 3 891717742
22 377 1 878887116
244 51 2 880606923
4.2 將movielens數據轉換成上面data數據同樣格式:
def formatMovieLens(filepath='D:/eclipse 3.62/workspace/Commendations/data/ml-100k'): ''' 這里用的是ml100k數據,只需要里面u.item和u.data兩個文件 把movielens數據轉換成此代碼要求的數據格式,即轉換成如同上面data字典套字典結構:dic[user] = {movie:score} ''' movie = {} #獲取影片標題。u.item記錄着影片id與影片名映射信息 for line in file(filepath+'/u.item'): (id,title) = line.split("|")[0:2] movie[id] = titleresultdata </span>=<span style="line-height:1.5 !important;"> {} </span><span style="color:rgb(0,0,255);line-height:1.5 !important;">for</span> line <span style="color:rgb(0,0,255);line-height:1.5 !important;">in</span> file(filepath +<span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">/u.data</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">'</span><span style="line-height:1.5 !important;">): (user,movieid,rating) </span>= line.split(<span style="color:rgb(128,0,0);line-height:1.5 !important;">"</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">\t</span><span style="color:rgb(128,0,0);line-height:1.5 !important;">"</span>)[0:3<span style="line-height:1.5 !important;">] resultdata.setdefault(user,{}) movietitle </span>=<span style="line-height:1.5 !important;"> movie[movieid] resultdata[user][movietitle] </span>=<span style="line-height:1.5 !important;"> float(rating) </span><span style="color:rgb(0,0,255);line-height:1.5 !important;">return</span> resultdata <br><br></pre>
4.3 測試結果,為用戶進行物品推薦。分別嘗試物品過濾,用戶過濾
mldata= formatMovieLens() # print "格式化movielens數據集后,取用戶id=87看過的電影:" print mldata['87'] #結果是:{'Birdcage, The (1996)': 4.0, 'E.T. the Extra-Terrestrial (1982)': 3.0, 'Bananas (1971)': 5.0, 'Sting, The (1973)': 5.0, 'Bad Boys (1995)': 4.0, 'In the Line of Fire (1993)': 5.0, 'Star Trek: The Wrath of Khan (1982)': 5.0, 'Speechless (1994)': 4.0, .....}#基於用戶過濾推薦
print "基於用戶過濾推薦:為用戶87推薦的電影排名前4部是:"
print getrecommendation(mldata, '87' )[0:30]
#結果是: [(5.0, 'They Made Me a Criminal (1939)'), (5.0, 'Star Kid (1997)'), (5.0, 'Santa with Muscles (1996)'), (5.0, 'Saint of Fort Washington, The (1993)'), (5.0, 'Marlene Dietrich: Shadow and Light (1996) '), (5.0, 'Great Day in Harlem, A (1994)'), (5.0, 'Entertaining Angels: The Dorothy Day Story (1996)'), (5.0, 'Boys, Les (1997)'), (4.89884443128923, 'Legal Deceit (1997)'), ...]#基於物品過濾推薦
計算所有物品之間的相似度
itemsim = calSimilarItems(mldata, 50)
print "基於物品過濾推薦:為用戶87推薦的電影排名前4部是:"
print getrecommendedItems(mldata, '87', itemsim)[0:10]
# 結果是:[(5.0, "What's Eating Gilbert Grape (1993)"), (5.0, 'Vertigo (1958)'), (5.0, 'Usual Suspects, The (1995)'), (5.0, 'Toy Story (1995)'), (5.0, 'Titanic (1997)'), (5.0, 'Sword in the Stone, The (1963)'), (5.0, 'Stand by Me (1986)'), (5.0, 'Sling Blade (1996)'), (5.0, 'Silence of the Lambs, The (1991)'), (5.0, 'Shining, The (1980)')]
<script>
(function(){
function setArticleH(btnReadmore,posi){
var winH = $(window).height();
var articleBox = $("div.article_content");
var artH = articleBox.height();
if(artH > winH*posi){
articleBox.css({
'height':winH*posi+'px',
'overflow':'hidden'
})
btnReadmore.click(function(){
articleBox.removeAttr("style");
$(this).parent().remove();
})
}else{
btnReadmore.parent().remove();
}
}
var btnReadmore = $("#btn-readmore");
if(btnReadmore.length>0){
if(currentUserName){
setArticleH(btnReadmore,3);
}else{
setArticleH(btnReadmore,1.2);
}
}
})()
</script>
</article>

