Python機器學習案例教程——推薦系統

主流的推薦系統算法大致分為兩類:

基於用戶行為數據的協同過濾算法

基於內容數據的過濾算法

大致而言,基於內容數據的算法適用於cold start,即用戶和項目都比較少的時候,而基於用戶行為數據的協同過濾算法在用戶和項目較多,數據比較豐富的情況下有較高的準確率。

除此之外,還包括基於社會網絡數據的推薦,基於語境(上下文)感知數據的推薦,基於心理學數據的推薦等等。

基於用戶行為數據的算法

1.1 基於用戶的協同過濾算法(user-based CF)

一個用戶喜歡和他具有相似喜好的用戶喜歡的項目, 兩個用戶喜歡的項目交集越大, 這兩個用戶越相似。

兩個用戶興趣相似度的計算可以有多種方法,常見的如 Pearson相關(皮爾遜相似性)和餘弦相似度計算。

適用場景和優缺點:這個是cf中的一種,它的主要特色是可以發現和用戶具有同樣taste的人,有句俗話叫做觀其友知其人,大概也是這個道理吧。找到用戶的相似用戶,通過相似用戶喜歡的item推薦給該用戶。因為用戶的相似用戶群還是比較敏感的,所以要頻繁地計算出用戶的相似用戶矩陣,這樣的話運算量會非常大。而且這個算法往往推薦出來的item很多都是大家都喜歡的比較hot的item,有的時候它提供的結果並不是個性化,反而成了大眾化的推薦了。用這種算法的web應用一般都是item更新頻繁,比如提供資訊類服務的應用(以“指閱”為代表的),或者笑話類推薦(以“冷笑話精選”為代表的)。當然這種算法的一箇中間產物—–用戶相似度矩陣是一個很有用的東西,社交類的網站可以利用這個中間產物來為用戶提供相同品位的好友推薦

1.2 基於商品的協同過濾

基於商品的協同過濾推薦(item-based CF)基於這樣的假設: 一個用戶會喜歡與他之前喜歡的商品相似的商品。因此,基於商品的協同過濾推薦關鍵在於計算物品之間的相似度。

適用場景和優缺點:這個算法是cf中的一種,也是當今很多大型網站都在採用的核心算法之一。對於商城網站(以Amazon為代表,當然也包括京東那種具有搞笑特色的推薦系統在內),影視類推薦,圖書類推薦,音樂類推薦系統來說,item的增長速度遠不如user的增長速度,而且item之間的相似性遠不如user之間的相似性那麼敏感,所以可以在離線系統中將item的相似度矩陣計算好,以供線上可以近乎即時地進行推薦。因為這種方法靠的是item之間的相關性進行推薦,所以推薦的item一般都和喜歡的item內容或者特性高度相似,很難推薦出用戶潛在喜歡的item,多樣性也比較差

基於用戶的協同過濾和基於商品的協同過濾統稱為基於鄰域的推薦 (nearest neighbor recommendation),也稱作基於記憶的推薦算法(memory-based recommendation)。

基於鄰域的推薦算法需要維護一個用戶相似度矩陣或商品相似度矩陣, 因此對於商品的數目更新速度遠遠小於用戶數目的增長速度的情況,宜採用基於商品的推薦算法, 如 Amazon 建立的推薦系統正是基於商品的協同過濾推薦算法, 還有移動應用產品的推薦。另外, 有研究表明, 基於商品的算法一般在性能上要優於基於用戶的算法。

注意:協同過濾不提取用戶特徵,也不提取商品特徵,只是計算向相似度,當有新商品出來時,沒有任何用戶購買過時,是不會推薦的。

1.3 基於模型的協同過濾

基於模型的協同過濾推薦 (model-based CF) 是採用機器學習或數據挖掘等算法, 用訓練數據來學習識別複雜模式, 從而得到學習模型, 然後基於學習模型在數據集上進行智能預測。後面我們瞭解的矩陣分解就是其中的一種。

隱語義模型 (latent semantic CF models)/矩陣分解模型(matrix factorization)

貝葉斯信念網協同過濾模型(Bayesian belief nets CF models

聚類協同過濾模型 (clustering CF models)

概率因素模型(probabilistic factor models)

基於內容數據的推薦

對一個給定的用戶, 推薦與他之前喜歡的商品在內容上有相似性的其他商品。這有點類似於廣告點擊率預測。需要用戶特徵和內容特徵。進行分類預測。

這種推薦僅需要得到兩類信息: 商品特徵的描述和用戶過去的喜好信息。

利用領域專家給項目打標籤的方法 , 也即傳統的分類系統(Taxonomy),

另一種是用戶給項目打標籤, 也即大眾分類系統 (Folksolomy)。

基於內容的推薦,很大程度上是在進行文本挖掘。web應用提供的內容或者爬取的內容在推給用戶之前可以做一些挖掘,比如資訊類的應用,將抓取到的資訊,通過文本分析那一套算法提取出每篇資訊的關鍵詞,以及統計頻次和逆向文檔頻率來聚類或者笨一點地話計算出資訊的相似度矩陣,即共同的key words越多,兩篇資訊的相似度越高。當你的用戶很少很少,你的顯式反饋數據非常非常少的時候,你可以根據用戶的瀏覽或者搜索等等各種行為,來給用戶進行推薦。再猥瑣一點的話,你可以在用戶剛剛註冊好你的應用的時候,給他一些提問,比如讓他輸入一些感興趣的話題啊,或者對以前看過的電影打分什麼的。(當然這些電影都是你從各個簇中隨機選取的,要足夠多樣性)這個算法它好就好在,不需要拿到用戶–項目的評分矩陣,只需要知道用戶喜歡什麼,就可以很快速地推薦給用戶十分相關的item。這個算法需要每天都要根據你抓取的資訊,不斷地計算item之間的相似性。這個算法有個好處在於可以從容應對上面的兩個算法其實都很難應對的問題,就是如果你想推出一個新的item,因為沒有一個人有對這個new item的評分,所以上述的兩個算法不可能推薦新的東西給你,但你可以用基於內容的算法將新的item計算出它屬於哪個類,然後時不時地推出你的新item,這點對於商城尤其重要。因為新商品也是有特徵的,而這個特徵跟其他商品的特徵是一樣的。

Knn(鄰近算法)

K最近鄰(k-Nearest Neighbor,KNN)分類算法,是一個理論上比較成熟的方法,也是最簡單的機器學習算法之一。該方法的思路是:如果一個樣本在特徵空間中的k個最相似(即特徵空間中最鄰近)的樣本中的大多數屬於某一個類別,則該樣本也屬於這個類別。KNN算法中,所選擇的鄰居都是已經正確分類的對象。該方法在定類決策上只依據最鄰近的一個或者幾個樣本的類別來決定待分樣本所屬的類別。 KNN方法雖然從原理上也依賴於極限定理,但在類別決策時,只與極少量的相鄰樣本有關。由於KNN方法主要靠周圍有限的鄰近的樣本,而不是靠判別類域的方法來確定所屬類別的,因此對於類域的交叉或重疊較多的待分樣本集來說,KNN方法較其他方法更為適合

基於矩陣分解的推薦算法

矩陣分解的一般形式

矩陣分解是指將一個矩陣分解成兩個或者多個矩陣的乘積。對於上述的用戶-商品矩陣(評分矩陣),記為Rm×n。可以將其分解成兩個或者多個矩陣的乘積,假設分解成兩個矩陣Pm×k和Qk×n,我們要使得矩陣Pm×k和Qk×n的乘積能夠還原原始的矩陣Rm×n


Python機器學習案例教程——推薦系統


其中,矩陣Pm×k表示的是m個用戶與k個主題之間的關係,而矩陣Qk×n表示的是k個主題與n個商品之間的關係。

利用矩陣分解進行預測

在上述的矩陣分解的過程中,將原始的評分矩陣Rm×nRm×n分解成兩個矩陣Pm×kPm×k和Qk×nQk×n的乘積:

損失函數

可以使用原始的評分矩陣Rm×n與重新構建的評分矩陣R^m×n之間的誤差的平方作為損失函數,即:


Python機器學習案例教程——推薦系統


最終,需要求解所有的非“-”項的損失之和的最小值:


Python機器學習案例教程——推薦系統


損失函數的求解

對於上述的平方損失函數,可以通過梯度下降法求解,梯度下降法的核心步驟是

求解損失函數的負梯度:


Python機器學習案例教程——推薦系統


根據負梯度的方向更新變量:


Python機器學習案例教程——推薦系統


通過迭代,直到算法最終收斂。

加入正則項的損失函數即求解方法

通常在求解的過程中,為了能夠有較好的泛化能力,會在損失函數中加入正則項,以對參數進行約束,加入L2正則的損失函數為:


Python機器學習案例教程——推薦系統


利用梯度下降法的求解過程為:

求解損失函數的負梯度:


Python機器學習案例教程——推薦系統


根據負梯度的方向更新變量:


Python機器學習案例教程——推薦系統


通過迭代,直到算法最終收斂。

預測

利用上述的過程,我們可以得到矩陣Pm×k和Qk×n,這樣便可以為用戶i對商品j進行打分:


Python機器學習案例教程——推薦系統


Svd(奇異值分解)

svd的全稱是:Singular Value Decomposition,翻譯過來是奇異值分解,是一種矩陣分解的方法。其實,這個方法是提取一般實矩陣“特徵值”的算法,(這裡特徵值加引號是因為,特徵值是針對方陣來定義的,而一般的m*n的實矩陣是沒有特徵值的。)其實,矩陣就是一個線性變換的表示方法,因為一個向量乘一個矩陣的結果是一個向量,第一個向量通過線性變換來變成第二個向量。線性變換有許多變換方向,比如你可以對一個圖像矩陣做伸縮同時也做平移。那麼特徵值和特徵向量又是什麼?一個特徵向量就是表示其中的一個變換方向,而對應的特徵值則表示這個變換方向對於整個線性變換有多麼重要。書歸正傳,那麼奇異值又是什麼?我覺得奇異值就是特徵值從方陣往一般實矩陣的一個推廣。你將一個m*n的實矩陣和它的轉置相乘,就會得到一個方陣,然後對這個方陣做特徵值分解,得到的特徵值就是所謂的奇異值的平方。我的意思是說,某種意義上,可以講奇異值和特徵值理解為一回事。那麼拿到奇異值又會有什麼用呢?拿到奇異值後,我們就可以抓到主要的成分,丟掉次要和非常次要的成分進行分析。也就是說,我們可以對原來的龐大的常常又非常稀疏的矩陣進行降維和分解,而分解後得到的矩陣都是稠密矩陣。最終我們會得到一個表示user特性的矩陣和一個表示item特性的矩陣。拿到這些數據之後,我們就可以進行推薦了,而且也可以很容易地進行聚類分析。這個算法的好處在於,可以解決rating矩陣的稀疏性問題,同時可以降低矩陣的維度,提高運算速度。但它的缺點是付出的空間代價太大。在做svd分解時,你需要先把一個大的rating矩陣分解成三個大的矩陣,這三個矩陣需要存在計算機內存中,然後才能進行降維。其實,svd這個方法的思路和PCA(主成分分析法)很像,抓住主要矛盾,忽略次要矛盾。分解降維後的矩陣非常約等於原來的矩陣。

聚類算法

這裡用到的聚類算法,是用來降低維度以及為並行計算作準備的。拿到rating矩陣之後,可以通過這些評分將用戶自然地聚成幾簇,然後用上述的算法對各個簇做推薦算法並行計算,充分地利用好所有計算資源。當然你也可以在svd分解之後,拿到user和item矩陣之後,對這兩個矩陣分別作聚類分析,你可以得到user的簇以及item的簇。這樣的結果會非常有意義,你可以作好友推薦,相似item推薦等等。在基於內容的算法中,因為很多資訊之間並不是那麼的相關,把他們都相互計算相似度,會得到很多的0,所以沒有必要。因此可以在計算之前,對整個item做個聚類,然後分別對各簇來做相似度計算。聚類算法中,我用過性能最好的也是最簡單的就是k-means。

基於社會網絡數據的推薦

在社會網絡中, 與其他用戶進行鏈接的用戶, 表示願意分享一定共同的興趣愛好, 或者他們之間有相似的品味(同質性原理),

這裡的相似性被期望來幫助改進推薦準確率和質量

基於社會網絡數據的推薦(social network-based recommendation)早先大部分是基於領域的方法。首先探索打分者的社會網絡, 聚集打分者的打分來計算預測打分; 然後找到打分者的鄰居。除了簡單的基於鄰域的方法, 還有許多是基於模型的推薦方法,如採用矩陣分解方法、 圖模型, 將用戶的社會網絡和用戶物品的喜好關係建模到一張圖中, 聯合基於項目的推薦和基於信任(trust-based)的推薦, 然後利用隨機遊走算法給用戶做推薦。採用異構數據源對用戶興趣進行建模, 從異構社會網絡中學習相關性。

這裡, 基於信任的推薦是將信任度引入推薦系統, 用信任度代替相似度。有許多方法可以計算用戶的 top-N信任鄰居。用一個信任矩陣 (如 Eigentrust)來計算 top-N 被信任的用戶, 或者激活擴散模型(spreading activation model)通過他們接收到的能量來給結點排序。

4. 基於語境(上下文)感知數據的推薦(context aware-based recommendation)

語境信息類型包括時間、 信息、 外界物理環境 (如天氣、溫度等)、 設備類型、 周圍人員、 活動狀態、 目的/意圖等。

還有些系統考慮了情緒、 計算平臺、 網絡條件、社會網絡等更為廣泛的語境。

(1)、通過語境驅動的查詢和搜索推薦

典型的是使用語境信息來查詢或搜索一個特 定的資源應答(如餐館), 並給出最好的匹配資源推薦給用戶(如離用戶最近的當前營業著的餐館)

(2)、通過語境喜好的啟發和評估推薦

使用多維方法來整合語境信息到推薦系統, 這裡傳統的二維用戶/項目範例被延伸來支持額外的語境維度, 如時間、 位置和公司。

5. 基於人口統計學數據的推薦

基於人口統計學數據的推薦 (demographic-based recommendation)是根據人口統計學數據對每個用戶建立一個用戶剖面(user profile), 系統根據用戶的剖面圖, 計算用戶間相似度, 得到當前用戶的最近鄰集, 最後系統會把基於 “鄰居” 用戶群喜好的項目推薦給當前用戶。

6. 基於心理學數據的推薦

人的心理特徵和情感因素在用戶做決策時非常重要。人類情感、 個性及其模型已經被廣泛地在計算機上實施。基於心理學數據的推薦(psychology-based recommendation)方法有情感智能 (emotional intelligence)、 滿意度 (satisfaction) 、 心理作用 (psychological effects)、 個性特質 (personalitybased)。

值得一提的是, Neflix競賽獲獎者之一就是學心理學的。

7. 基於大數據的推薦(big data-based recommendation)

目前, 大數據通常被認為是當前傳統技術難以處理的, 這裡的傳統技術也包括傳統推薦技術。

基於大數據的推薦(big data-based recommendation)有兩個內涵: 一個是大數據使得傳統推薦技術更加準確; 另一個是傳統的技術已經不能滿足需求, 大數據需要新的推薦技術。

大數據的來源多樣, 對推薦系統而言,“大數據”包含系統可觸及到的數據, 如用戶行為數據、 社會網絡數據、 人口統計學數據、 語境感知數據等。

大數據環境下的推薦是至少基於兩種類型的數據而進行的推薦。研究表明, 基於大數據的各種混合推薦算法的推薦效果要優於單純的基於一種數據的推薦, 如將基於社會網絡的推薦和協同過濾推薦結合, 將基於內容的推薦和協同過濾方法結合, 跨領域推薦。

案例

在樣本數據集中,數據存儲一般以矩陣的形式,每一行代表一個對象,每一列代表一個特徵屬性。而文本介紹一種稀疏高維的特徵數據集。例如在文本分類中,文章的特徵用文章包含的單詞表示,在客戶購物中,客戶的特徵用所購買的物品表示。這種對象的特徵維度往往都是很大的,並且是稀疏的。對於這種數據集進行計算,算法往往比較複雜。

一個普通的特徵數據集如下表:


Python機器學習案例教程——推薦系統


一個稀疏高維特徵數據集如下:0表示沒買,1表示買


Python機器學習案例教程——推薦系統


高維稀疏特徵數據集是經常存在的,例如:

影評人給電影打分。 向影評人推薦相似影評人,向影評人推薦電影。

買家給寶貝評分。向淘寶買家推薦寶貝等。

讀者停留在某類文章上的時間長度。向讀者推薦類似文件的強度。

上網用戶對某博客進行了點擊。向用戶推薦感性的博主。

有多的應用需要在工作中學會洞察。

對這種數據集進行聚類或者分類往往比較困難,而且尤其當我們關係的特徵出現的數量很少還要主要注意進行樣本均衡。這些讀者可以在以後的學習中注意。

本文僅以電影推薦這個例子來講述匹配與推薦的算法(協作性過濾算法)。

構造數據集

我們先使用如下簡單的數據集。

注意:實際中特徵數據集可能是稀疏的,因為本文重點是為了瞭解匹配和推薦機制。

# 偏好數據集(人-電影-評分)

<code>prefs={
'name1': {'movie1': 2.5, 'movie2': 3.5,'movie3': 3.0, 'movie4': 3.5, 'movie5': 2.5, 'movie6': 3.0},
'name2': {'movie1': 3.0, 'movie2': 3.5,'movie3': 1.5, 'movie4': 5.0, 'movie6': 3.0,'movie5': 3.5},
'name3': {'movie1': 2.5, 'movie2': 3.0,'movie4': 3.5, 'movie6': 4.0},

'name4': {'movie2': 3.5, 'movie3': 3.0, 'movie6': 4.5, 'movie4': 4.0, 'movie5': 2.5},
'name5': {'movie1': 3.0, 'movie2': 4.0, 'movie3': 2.0, 'movie4': 3.0, 'movie6': 3.0,'movie5': 2.0},
'name6': {'movie1': 3.0, 'movie2': 4.0, 'movie6': 3.0, 'movie4': 5.0, 'movie5': 3.5},
'name7': {'movie2':4.5,'movie5':1.0,'movie4':4.0}
}/<code>


相似度計算方法

我們可以對行或對列進行相似度計算。這裡先了解如何對計算兩個行的相似程度。我們採用歐幾里得距離和皮爾遜相似度。將計算的值映射的0-1範圍上來代表相似程度,1代表完全相同,0代表完全不同。

注意:讀者可以思考,如何計算高維稀疏特徵數據集中兩行之間的相似度

歐幾里得距離計算

公式:|x| = √( x[1]2 + x[2]2 + … + x[n]2 ) 歐式距離百科

獲取兩個行(對象)的相同列,生成一個局部特徵數據集。再根據這個局部特徵數據集計算歐式距離(這是因為屬性數據集太係數,直接計算相似度會計算出來的值特別小)。

<code>from math import sqrt
# 計算兩行之間的歐幾里得距離,以此來代表相似度。prefs表示偏好數據集
def sim_distance(prefs,row1_name,row2_name):
# 首先計算是否有共同列(都看過的電影)

si={}
for item in prefs[row1_name]:
if item in prefs[row2_name]: si[item]=1
# 如果沒有共同列,則兩行之間相似度為0
if len(si)==0: return 0
# 根據共同列計算兩行的歐幾里得距離,並將距離映射到0-1上。0表示完全不相似,1表示完全相似
sum_of_squares=sum([pow(prefs[row1_name][item]-prefs[row2_name][item],2) for item in prefs[row1_name] if item in prefs[row2_name]])
return 1/(1+sum_of_squares)/<code>


皮爾遜相似度計算

假設有兩個向量X、Y,那麼兩向量間的皮爾遜相關係數可通過以下公式計算:

公式一:


Python機器學習案例教程——推薦系統


可以將公式進行多種演變,其中一種轉換為下面的公式。

公式二:


Python機器學習案例教程——推薦系統


其中E是數學期望,cov表示協方差,N表示變量取值的個數。

我們就根據公式二進行計算。

獲取兩個行(對象)的相同列,生成一個局部特徵數據集。再根據這個局部特徵數據集計算皮爾遜相似度(這是因為屬性數據集太係數,直接計算相似度會計算出來的值特別小)。

雖然皮爾遜相似度的計算過程更復雜一些,但它在數據不是很規範的時候(例如:影評者對影片的評價總是相對於平均水平偏離很大時),會傾向於給出很更好的結果。

<code># 計算兩行的皮爾遜相似度,以此來代表相似度。prefs表示數據集
def sim_pearson(prefs,row1_name,row2_name):
# 首先計算是否有共同列(都看過的電影)
si={}
for item in prefs[row1_name]:
if item in prefs[row2_name]: si[item]=1
# 如果沒有共同列,兩行之間相似度為0
if len(si)==0: return 0
# 得到列表元素個數
n=len(si)
# 對兩行的共同列求和
sum1=sum([prefs[row1_name][it] for it in si])
sum2=sum([prefs[row2_name][it] for it in si])
# 對兩行的共同列求平方和
sum1Sq=sum([pow(prefs[row1_name][it],2) for it in si])
sum2Sq=sum([pow(prefs[row2_name][it],2) for it in si])
# 對兩行的共同列求乘積之和

pSum=sum([prefs[row1_name][it]*prefs[row2_name][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/<code>


匹配相似行

有了每兩行之間的相似度計算方法,就可以計算每行的相似行了。在計算相似行時,對於兩個特徵屬性的數量不同的行計算相似度時,只用計算公共特徵屬性即可。

# 匹配相似行

# 根據偏好數據集,返回與某個行最匹配的n行。person表示要匹配的行(人),similarity表示相似度計算函數

<code>def topMatches(prefs,row_name,n=5,similarity=sim_pearson):
scores=[(similarity(prefs,row_name,other),other) for other in prefs if other!=row_name]
scores.sort()
scores.reverse()
num = n
if n>len(scores):num= len(scores)
return scores[0:num]/<code>


利用相似行估計列的值,並排名

有了相似行,某一行就可以根據相似行對各列的評值,來估計當前行各列存在的空白值。為了避免算法的不精準,所以相似行不止一個,每個相似行根據相似度確定權重。最後估值採用加權平均的方式。

對當前行各列存在的空白值進行估計以後,將估計的值進行排名推薦。

# 利用相似行,估計某行所有列存在的空白值,並排名(估計影片評分,並排名推薦)

<code># 利用所有其他行的各列取值的加權平均(相似度為權值),為某行各列提供估值
def getRecommendations(prefs,row_name,similarity=sim_pearson):
totals={}
simSums={}
for other in prefs:
# 不和自己做比較
if other==row_name: continue
sim=similarity(prefs,row_name,other)
# 忽略評價值為0或為負的情況
if sim<=0: continue
for item in prefs[other]:
# 只對自己還未有的列進行臨時估值
if item not in prefs[row_name] or prefs[row_name][item]==0:
# 相似度*臨時估值
totals.setdefault(item,0)
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/<code>


匹配相似列

對於數據來說他無法識別每行的含義,只是在做行間運算。如果我們把數據矩陣進行轉置,再做行間運算。那就是進行的列匹配。

數據集轉置

<code>def transformPrefs(prefs):
result={}
for row_name in prefs:
for item in prefs[row_name]:
result.setdefault(item,{})
# 將行與列對調
result[item][row_name]=prefs[row_name][item]
return result/<code>


匹配相似列

<code># 匹配相似列,返回各列的匹配集合(因為各列的匹配可提前在用戶登陸前完成),
# 根據轉置後的偏好數據集,獲取每列相似的n個其他列
def calculateSimilarItems(prefs,n=10):
# 建立字典,以給出與這些列最為相近的所有其他列
itemMatch={}
# 以列為中心對偏好矩陣實施轉置處理
itemPrefs=transformPrefs(prefs)
c=0
for item in itemPrefs:
# 針對大數據集更新狀態變量
c+=1
if c%100==0: print("%d / %d" % (c,len(itemPrefs)))

# 尋找最為相近的列
scores=topMatches(itemPrefs,item,n=n,similarity=sim_distance)
itemMatch[item]=scores
return itemMatch #返回每列匹配的其他列/<code>


利用相似列,對某一行的各列空白處進行估值

與根據相似行對各列的空白值進行估計類似。

<code># 利用相似列,對某一行的各列進行估值,(估計影片評分,並排名推薦):根據偏好數據集和提前構造好的物品匹配庫,向用戶推薦物品
def getRecommendedItems(prefs,itemMatch,row_name):
onerow=prefs[row_name] #獲取當前行所擁有的列
scores={}
totalSim={}
# 循環遍歷由當前行所擁有的列
for (item,rating) in onerow.items( ):
# 循環遍歷與當前列相似的列
for (similarity,item2) in itemMatch[item]:
# 忽略行已經擁有的列
if item2 in onerow: 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/<code>


運行試驗

有了上面的算法,我們就可以來嘗試效果了。

<code>if __name__=="__main__":     #只有在執行當前模塊時才會運行此函數
#利用相似人推薦相似物品
rankings = getRecommendations(prefs,'name7')
print(rankings) #打印推薦排名
#利用相似物品推薦相似物品
itemMatch = calculateSimilarItems(prefs) # 提前計算所有物品的相似物品
rankings = getRecommendedItems(prefs,itemMatch,'name7')
print(rankings) #打印推薦排名/<code>


應該選用哪一種相似性度量方式

我們在此處已經介紹了兩種不同的度量方式,但實際上,還有許多其他的衡量兩個數組相似度的方式。例如jaccard係數和曼哈頓距離算法。使用哪種方式最優取決於具體的應用。如果你想看看哪種方式最優建議你都嘗試一下。

距離度量總結參考:https://blog.csdn.net/luanpeng825485697/article/details/80032013

矩陣分解的代碼

<code>from numpy import *
def load_data(path):
f = open(path)
data = []
for line in f.readlines():
arr = []
lines = line.strip().split("\\t")
for x in lines:
if x != "-":
arr.append(float(x))
else:
arr.append(float(0))
# print arr
data.append(arr)
# print data
return data
def gradAscent(data, K):
dataMat = mat(data)
print(dataMat)
m, n = shape(dataMat)
p = mat(random.random((m, K)))
q = mat(random.random((K, n)))
alpha = 0.0002
beta = 0.02
maxCycles = 10000
for step in range(maxCycles):
for i in range(m):
for j in range(n):
if dataMat[i, j] > 0:
# print dataMat[i,j]
error = dataMat[i, j]
for k in range(K):
error = error - p[i, k] * q[k, j]
for k in range(K):
p[i, k] = p[i, k] + alpha * (2 * error * q[k, j] - beta * p[i, k])
q[k, j] = q[k, j] + alpha * (2 * error * p[i, k] - beta * q[k, j])
loss = 0.0
for i in range(m):
for j in range(n):
if dataMat[i, j] > 0:
error = 0.0
for k in range(K):
error = error + p[i, k] * q[k, j]
loss = (dataMat[i, j] - error) * (dataMat[i, j] - error)
for k in range(K):
loss = loss + beta * (p[i, k] * p[i, k] + q[k, j] * q[k, j]) / 2
if loss < 0.001:

break
# print step
if step % 1000 == 0:
print(loss)
return p, q
if __name__ == "__main__":
dataMatrix = load_data("./data")
p, q = gradAscent(dataMatrix, 5)
'''
p = mat(ones((4,10)))
print p
q = mat(ones((10,5)))
'''
result = p * q
print(p)
print(q)
print(result)/<code>


分享到:


相關文章: