推薦算法——距離算法

遷移到:http://www.bdata-cap.com/newsinfo/1741432.html

本文內容

  • 用戶評分表
  • 曼哈頓(Manhattan)距離
  • 歐式(Euclidean)距離
  • 餘弦類似度(cos simliarity)

推薦算法以及數據挖掘算法,計算「距離」是必須的~最近想搭一個推薦系統,看了一些資料和書《寫給程序員的數據挖掘指南》,此書不錯,推薦你們看看,講解得很透徹,有理論有代碼,還有相關網站。看完後,你馬上就能把推薦算法應用在你的項目中~html

本文先主要說明如何計算物品或用戶之間的「距離」,陸續會介紹推薦算法自己~程序員

用戶評分表


大致上,推薦算法能夠有兩種簡單的思路:一是類似的用戶,二是類似的物品。算法

前者,把與你類似的用戶喜歡(或購買或評價高)的商品推薦給你,也就是說,若是你跟某個用戶的喜愛比較接近,那麼就能夠把這個用戶喜歡的,而你不知道(或沒瀏覽過,或沒購買過等等)的物品推薦給你。什麼叫「喜愛接近」,就是對某些物品的評價也好,購買也罷,都比較接近,就認爲,你和他喜愛相同~app

前者的缺陷在於,用戶的評價畢竟是少數,想一想,你評價過(顯式評價)的物品有多少!大多數仍是隱式評價,所謂隱式評價,若是你購買一個物品,那顯然你會喜歡他,否則也不會買~所以,利用類似的用戶是有侷限性的。不如利用類似的物品來推薦。ide

下面「距離」算法主要針對計算用戶之間的距離(類似性)。oop

假設,8個用戶對8個樂隊進行評分,以下表所示。橫向是用戶,縱向是樂隊。網站

表 1 用戶評分表idea

2016-04-21_172447

曼哈頓(Manhattan)距離


 

計算距離最簡單的方法是曼哈頓距離。假設,先考慮二維狀況,只有兩個樂隊 x 和 y,用戶A的評價爲(x1,y1),用戶B的評價爲(x2,y2),那麼,它們之間的曼哈頓距離爲spa

2016-04-22_161015

所以,Angelica 與 Bill  之間的曼哈頓距離以下表所示。3d

表 2 Angelica 與 Bill 的曼哈頓距離

2016-04-21_172921

那麼,Angelica 與 Bill 之間的曼哈頓距離爲 9,即第二列減第三列的絕對值,最後累加。

注意,必須是這兩個用戶都評分的樂隊。

能夠推廣到n個樂隊,即n維向量,用戶 A(x1,x2,…,xn),用戶B(y1,y2,…,yn) ,那麼它們之間的曼哈頓距離爲

2016-04-22_165849 

則用戶之間的曼哈頓距離以下表所示。

表 3 用戶之間的曼哈頓距離

2016-04-21_173036

曼哈頓距離的最大好處就是簡單,只是加減法而已。若是有幾百萬個用戶,計算起來會很快。

不只能夠擴展到 n 個樂隊,固然也能夠擴展到 m 個用戶,它們能夠造成一個矩陣。下面的其餘距離同理。

Netflix 當初出 100 萬美圓獎勵給能提高推薦算法 10% 準確率的團隊或人,而贏得獎金的人就是使用了一種叫奇異矩陣分解的方法~

歐式(Euclidean)距離


除了曼哈頓距離外,還能夠計算兩個用戶之間的歐式距離。

仍是先考慮兩個樂隊 x 和 y 的狀況,假設,用戶A=(x1,y1),用戶B=(x2,y2),那麼它們之間的歐式距離:

2016-04-22_162648 

Angelica 與 Bill 之間的曼哈頓距離以下表所示。

表 4 Angelica 與 Bill 的歐式距離

2016-04-21_173319

推廣到 n 個樂隊,用戶 A(x1,x2,…,xn),用戶B(y1,y2,…,yn)

2016-04-22_163329

表 5 用戶之間的歐式距離

2016-04-21_173447

但曼哈頓距離和歐式距離,有個缺點。對比一下 Hailey 與 Veronica 和 Jordyn,Hailey 與前者只有兩個樂隊評過度,而與後者是五個。換句話說,Hailey 與 Veronica 的距離是基於二維的,而 Hailey 與 Jordyn 是基於五維。想一想都以爲有問題。

因此,曼哈頓距離和歐式距離適合數據比較稠密、缺失值比較少的狀況。若是缺失值不少,餘弦類似度就比較合適。

曼哈頓距離和歐式距離,有通用公式,稱爲閔可夫斯基距離(Minkowski Distance)。

餘弦類似度(cos simliarity)

 


 

假設,有兩個樂隊,用戶A=(x1,y1),用戶B=(x2,y2),那麼他們之間的餘弦相識度爲:

2016-04-22_164052

表 6 Angelica 與 Bill 的餘弦類似度

2016-04-21_174001

 

推廣到n維,用戶A和B,對n個樂隊的評分分別爲(x1,x2,...,xn)和(y1,y2,...,yn),則他們之間的餘弦類似度爲

2016-04-22_164619

源代碼 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
相關文章
相關標籤/搜索