基於鄰域的算法是推薦系統中最基本的算法,該算法不只在學術界獲得了深刻研究,並且在業界獲得了普遍應用。基於鄰域的算法分爲兩大類,一類是基於用戶的協同過濾算法,另外一類是基於物品的協同過濾算法。算法
咱們先來看看基於用戶的協同過濾算法,基於物品的協同過濾算法大致思路和基於用戶的差很少,能夠本身參考對比學習。學習
每一年新學期開始,剛進實驗室的師弟總會問師兄類似的問題,好比「我應該買什麼專業書啊」、「我應該看什麼論文啊」等。這個時候,師兄通常會給他們作出一些推薦。這就是現實中個性化推薦的一種例子。在這個例子中,師弟可能會請教不少師兄,而後作出最終的判斷。師弟之因此請教師兄,一方面是由於他們有社會關係,互相認識且信任對方,但更主要的緣由是師兄和師弟有共同的研究領域和興趣。那麼,在一個在線個性化推薦系統中,當一個用戶A須要個性化推薦時,能夠先找到和他有類似興趣的其餘用戶,而後把那些用戶喜歡的、而用戶A沒有據說過的物品推薦給A。這種方法稱爲基於用戶的協同過濾算法。ui
基於用戶的協同過濾算法主要包括兩個步驟。spa
(1) 找到和目標用戶興趣類似的用戶集合。code
(2) 找到這個集合中的用戶喜歡的,且目標用戶沒有據說過的物品推薦給目標用戶。blog
步驟(1)的關鍵就是計算兩個用戶的興趣類似度。這裏,協同過濾算法主要利用行爲的類似度計算興趣的類似度。給定用戶u和用戶v,令N(u)表示用戶u曾經有過正反饋的物品集合,令N(v)爲用戶v曾經有過正反饋的物品集合。那麼,咱們能夠經過以下的Jaccard公式簡單地計算u和v的興趣類似度或者經過餘弦公式:get
jaccard 餘項公式:it
: 數據挖掘
這個一個行爲記錄 咱們能夠根據餘弦公式計算以下table
以餘弦類似度爲例,實現該類似度能夠利用下面的僞代碼:
def UserSimilarity(train): W = dict() for u in train.keys(): for v in train.keys(): if u == v: continue W[u][v] = len(train[u] & train[v]) W[u][v] = /= math.sqrt(len(train[u]) * len(train[v]) * 1.0) return W
這種方法的時間複雜度是O(|U|*|U|),這在用戶數很大時很是耗時。事實上,不少用戶相互之間並無對一樣的物品產生過行爲,即不少時候N(u)^ N(v) = 0。上面的算法將不少時間浪費在了計算這種用戶之間的類似度上。若是換一個思路,咱們能夠首先計算出N(u)^ N(v) != 0 的用戶對(u,v),而後再對這種狀況除以分母sqrt(N(u)*N(v)) 。
爲此,能夠首先創建物品到用戶的倒排表,對於每一個物品都保存對該物品產生過行爲的用戶列表。令稀疏矩陣C[u][v]= N(u)^ N(v) 。那麼,假設用戶u和用戶v同時屬於倒排表中K個物品對應的用戶列表,就有C[u][v]=K。從而,能夠掃描倒排表中每一個物品對應的用戶列表,將用戶列表中的兩兩用戶對應的C[u][v]加1,最終就能夠獲得全部用戶之間不爲0的C[u][v]
def UserSimilarity(train): # build inverse table for item_users item_users = dict() for u, items in train.items(): for i in items.keys(): if i not in item_users: item_users[i] = set() item_users[i].add(u) #calculate co-rated items between users C = dict() N = dict() for i, users in item_users.items(): for u in users: N[u] += 1 for v in users: if u == v: continue C[u][v] += 1 #calculate finial similarity matrix W W = dict() for u, related_users in C.items(): for v, cuv in related_users.items(): W[u][v] = cuv / math.sqrt(N[u] * N[v]) return W
下面是按照想法創建的稀疏矩陣,對於物品a,將W[A][B]和W[B][A]加1,對於物品b,將W[A][C]和W[C][A]加1,以此類推,掃描完全部物品後,咱們能夠獲得最終的W矩陣,這裏的W是餘弦類似度中的分子部分,而後將W除以分母能夠獲得最終的用戶興趣類似度
獲得用戶之間的興趣類似度後,UserCF算法會給用戶推薦和他興趣最類似的K個用戶喜歡的物品。上面右邊公式度量了UserCF算法中用戶u對物品i的感興趣程度:其中,S(u, K)包含和用戶u興趣最接近的K個用戶,N(i)是對物品i有過行爲的用戶集合,Wuv是用戶u和用戶v的興趣類似度,Rvi表明用戶v對物品i的興趣,由於使用的是單一行爲的隱反饋數據,因此全部的Rvi=1。
以下代碼實現了上面的UserCF推薦算法:
def Recommend(user, train, W): rank = dict() interacted_items = train[user] for v, wuv in sorted(W[u].items, key=itemgetter(1), reverse=True)[0:K]: for i, rvi in train[v].items: if i in interacted_items: #we should filter items user interacted before continue rank[i] += wuv * rvi return rank
選取K=3,用戶A對物品c、e沒有過行爲,所以能夠把這兩個物品推薦給用戶A。根據UserCF算法,用戶A對物品c、e的興趣是:
若是兩個用戶都曾經買過《新華字典》,這絲絕不能說明他們興趣類似,由於絕大多數中國人小時候都買過《新華字典》。但若是兩個用戶都買過《數據挖掘導論》,那能夠認爲他們的興趣比較類似,由於只有研究數據挖掘的人才會買這本書。換句話說,兩個用戶對冷門物品採起過一樣的行爲更能說明他們興趣的類似度。所以,John S. Breese在論文①中提出了以下公式,根據用戶行爲計算用戶的興趣類似度:
分子中的倒數懲罰了用戶u和用戶v共同興趣列表中熱門物品對他們類似度的影響。N(i)是對物品i有過行爲的用戶集合,越熱門,N(i)越大
def UserSimilarity(train): # build inverse table for item_users item_users = dict() for u, items in train.items(): for i in items.keys(): if i not in item_users: item_users[i] = set() item_users[i].add(u) #calculate co-rated items between users C = dict() N = dict() for i, users in item_users.items(): for u in users: N[u] += 1 for v in users: if u == v: continue C[u][v] += 1 / math.log(1 + len(users)) #calculate finial similarity matrix W W = dict() for u, related_users in C.items(): for v, cuv in related_users.items(): W[u][v] = cuv / math.sqrt(N[u] * N[v]) return W