遷移到:http://www.bdata-cap.com/newsinfo/1741432.html
本文內容
- 用戶評分表
- 曼哈頓(Manhattan)距離
- 歐式(Euclidean)距離
- 余弦相似度(cos simliarity)
推薦算法以及數據挖掘算法,計算“距離”是必須的~最近想搭一個推薦系統,看了一些資料和書《寫給程序員的數據挖掘指南》,此書不錯,推薦大家看看,講解得很透徹,有理論有代碼,還有相關網站。看完后,你立刻就能把推薦算法應用在你的項目中~
本文先主要說明如何計算物品或用戶之間的“距離”,陸續會介紹推薦算法本身~
用戶評分表
大體上,推薦算法可以有兩種簡單的思路:一是相似的用戶,二是相似的物品。
前者,把與你相似的用戶喜歡(或購買或評價高)的商品推薦給你,也就是說,如果你跟某個用戶的喜好比較接近,那么就可以把這個用戶喜歡的,而你不知道(或沒瀏覽過,或沒購買過等等)的物品推薦給你。什么叫“喜好接近”,就是對某些物品的評價也好,購買也罷,都比較接近,就認為,你和他喜好相同~
前者的缺陷在於,用戶的評價畢竟是少數,想想,你評價過(顯式評價)的物品有多少!大多數還是隱式評價,所謂隱式評價,如果你購買一個物品,那顯然你會喜歡他,不然也不會買~因此,利用相似的用戶是有局限性的。不如利用相似的物品來推薦。
下面“距離”算法主要針對計算用戶之間的距離(相似性)。
假設,8個用戶對8個樂隊進行評分,如下表所示。橫向是用戶,縱向是樂隊。
表 1 用戶評分表
曼哈頓(Manhattan)距離
計算距離最簡單的方法是曼哈頓距離。假設,先考慮二維情況,只有兩個樂隊 x 和 y,用戶A的評價為(x1,y1),用戶B的評價為(x2,y2),那么,它們之間的曼哈頓距離為
因此,Angelica 與 Bill 之間的曼哈頓距離如下表所示。
表 2 Angelica 與 Bill 的曼哈頓距離
那么,Angelica 與 Bill 之間的曼哈頓距離為 9,即第二列減第三列的絕對值,最后累加。
注意,必須是這兩個用戶都評分的樂隊。
可以推廣到n個樂隊,即n維向量,用戶 A(x1,x2,…,xn),用戶B(y1,y2,…,yn) ,那么它們之間的曼哈頓距離為
則用戶之間的曼哈頓距離如下表所示。
表 3 用戶之間的曼哈頓距離
曼哈頓距離的最大好處就是簡單,只是加減法而已。如果有幾百萬個用戶,計算起來會很快。
不僅可以擴展到 n 個樂隊,當然也可以擴展到 m 個用戶,它們可以形成一個矩陣。下面的其他距離同理。
Netflix 當初出 100 萬美元獎勵給能提升推薦算法 10% 准確率的團隊或人,而贏得獎金的人就是使用了一種叫奇異矩陣分解的方法~
歐式(Euclidean)距離
除了曼哈頓距離外,還可以計算兩個用戶之間的歐式距離。
還是先考慮兩個樂隊 x 和 y 的情況,假設,用戶A=(x1,y1),用戶B=(x2,y2),那么它們之間的歐式距離:
Angelica 與 Bill 之間的曼哈頓距離如下表所示。
表 4 Angelica 與 Bill 的歐式距離
推廣到 n 個樂隊,用戶 A(x1,x2,…,xn),用戶B(y1,y2,…,yn)
表 5 用戶之間的歐式距離
但曼哈頓距離和歐式距離,有個缺點。對比一下 Hailey 與 Veronica 和 Jordyn,Hailey 與前者只有兩個樂隊評過分,而與后者是五個。換句話說,Hailey 與 Veronica 的距離是基於二維的,而 Hailey 與 Jordyn 是基於五維。想想都覺得有問題。
所以,曼哈頓距離和歐式距離適合數據比較稠密、缺失值比較少的情況。如果缺失值很多,余弦相似度就比較合適。
曼哈頓距離和歐式距離,有通用公式,稱為閔可夫斯基距離(Minkowski Distance)。
余弦相似度(cos simliarity)
假設,有兩個樂隊,用戶A=(x1,y1),用戶B=(x2,y2),那么他們之間的余弦相識度為:
表 6 Angelica 與 Bill 的余弦相似度
推廣到n維,用戶A和B,對n個樂隊的評分分別為(x1,x2,...,xn)和(y1,y2,...,yn),則他們之間的余弦相似度為
源代碼 dis.py
#
# dis.py
#
from math import *
teams = [
"Blues Traveler",
"Broken Bells",
"Deadmau5",
"Norah Jones",
"Phoenix",
"Slightly Stoopid",
"The Strokes",
"Vampire Weekend"
]
users = {
"Angelica": {
"Blues Traveler": 3.5,
"Broken Bells": 2,
"Norah Jones": 4.5,
"Phoenix": 5,
"Slightly Stoopid": 1.5,
"The Strokes": 2.5,
"Vampire Weekend": 2
},
"Bill": {
"Blues Traveler": 2,
"Broken Bells": 3.5,
"Deadmau5": 4,
"Phoenix": 2,
"Slightly Stoopid": 3.5,
"Vampire Weekend": 3
},
"Chan": {
"Blues Traveler": 5,
"Broken Bells": 1,
"Deadmau5": 1,
"Norah Jones": 3,
"Phoenix": 5,
"Slightly Stoopid": 1
},
"Dan": {
"Blues Traveler": 3,
"Broken Bells": 4,
"Deadmau5": 4.5,
"Phoenix": 3,
"Slightly Stoopid": 4.5,
"The Strokes": 4,
"Vampire Weekend": 2
},
"Hailey": {
"Broken Bells": 4,
"Deadmau5": 1,
"Norah Jones": 4,
"The Strokes": 4,
"Vampire Weekend": 1
},
"Jordyn": {
"Broken Bells": 4.5,
"Deadmau5": 4,
"Norah Jones": 5,
"Phoenix": 5,
"Slightly Stoopid": 4.5,
"The Strokes": 4,
"Vampire Weekend": 4
},
"Sam": {
"Blues Traveler": 5,
"Broken Bells": 2,
"Norah Jones": 3,
"Phoenix": 5,
"Slightly Stoopid": 4,
"The Strokes": 5
},
"Veronica": {
"Blues Traveler": 3,
"Norah Jones": 5,
"Phoenix": 4,
"Slightly Stoopid": 2.5,
"The Strokes": 3
}
}
def manhattan(rating1, rating2):
"""Computes the Manhattan distance. Both rating1 and rating2 are dictionaries
of the form {'The Strokes': 3.0, 'Slightly Stoopid': 2.5}"""
distance = 0
commonRatings = False
for key in rating1:
if key in rating2:
distance += abs(rating1[key] - rating2[key])
commonRatings = True
if commonRatings:
return distance
else:
return -1 #Indicates no ratings in common
def euclidean(rating1, rating2):
"""Computes the euclidean distance. Both rating1 and rating2 are dictionaries
of the form {'The Strokes': 3.0, 'Slightly Stoopid': 2.5}"""
distance = 0
commonRatings = False
for key in rating1:
if key in rating2:
distance += pow(rating1[key] - rating2[key],2)
commonRatings = True
if commonRatings:
return sqrt(distance)
else:
return -1 #Indicates no ratings in common
def minkowski(rating1, rating2, r):
"""Computes the minkowski distance. Both rating1 and rating2 are dictionaries
of the form {'The Strokes': 3.0, 'Slightly Stoopid': 2.5}"""
distance = 0
commonRatings = False
for key in rating1:
if key in rating2:
distance += pow(abs(rating1[key] - rating2[key]),r)
commonRatings = True
if commonRatings:
return pow(distance, 1.0/r)
else:
return -1 #Indicates no ratings in common
def cosineSimilarity (rating1, rating2):
"""Computes the Cosine Similarity distance. Both rating1 and rating2 are dictionaries
of the form {'The Strokes': 3.0, 'Slightly Stoopid': 2.5}"""
sum_xy = 0
sum_sqr_x = 0
sum_sqr_y = 0
for key in teams:
if key in rating1 and key in rating2:
sum_xy += rating1[key]* rating2[key]
sum_sqr_x += pow(rating1[key], 2)
sum_sqr_y += pow(rating2[key], 2)
elif key not in rating1 and key in rating2:
sum_xy += 0
sum_sqr_x += 0
sum_sqr_y += pow(rating2[key], 2)
elif key in rating1 and key not in rating2:
sum_xy += 0
sum_sqr_x += pow(rating1[key], 2)
sum_sqr_y += 0
else:
sum_xy += 0
sum_sqr_x += 0
sum_sqr_y += 0
if sum_sqr_x ==0 or sum_sqr_y==0:
return -1 #Indicates no ratings in common
else:
return sum_xy / (sqrt(sum_sqr_x) * sqrt(sum_sqr_y))
def pearson(rating1, rating2):
"""Computes the pearson distance. Both rating1 and rating2 are dictionaries
of the form {'The Strokes': 3.0, 'Slightly Stoopid': 2.5}"""
sum_xy = 0
sum_x = 0
sum_y = 0
sum_x2 = 0
sum_y2 = 0
n = 0
for key in rating1:
if key in rating2:
n += 1
x = rating1[key]
y = rating2[key]
sum_xy += x * y
sum_x += x
sum_y += y
sum_x2 += pow(x, 2)
sum_y2 += pow(y, 2)
# now compute denominator
denominator = sqrt(sum_x2 - pow(sum_x, 2) / n) * sqrt(sum_y2 - pow(sum_y, 2) / n)
if denominator == 0:
return 0
else:
return (sum_xy - (sum_x * sum_y) / n) / denominator











