推薦算法——距離算法


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

本文內容

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

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

本文先主要說明如何計算物品或用戶之間的“距離”,陸續會介紹推薦算法本身~

用戶評分表


大體上,推薦算法可以有兩種簡單的思路:一是相似的用戶,二是相似的物品。

前者,把與你相似的用戶喜歡(或購買或評價高)的商品推薦給你,也就是說,如果你跟某個用戶的喜好比較接近,那么就可以把這個用戶喜歡的,而你不知道(或沒瀏覽過,或沒購買過等等)的物品推薦給你。什么叫“喜好接近”,就是對某些物品的評價也好,購買也罷,都比較接近,就認為,你和他喜好相同~

前者的缺陷在於,用戶的評價畢竟是少數,想想,你評價過(顯式評價)的物品有多少!大多數還是隱式評價,所謂隱式評價,如果你購買一個物品,那顯然你會喜歡他,不然也不會買~因此,利用相似的用戶是有局限性的。不如利用相似的物品來推薦。

下面“距離”算法主要針對計算用戶之間的距離(相似性)。

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

表 1 用戶評分表

2016-04-21_172447

曼哈頓(Manhattan)距離


 

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

2016-04-22_161015

因此,Angelica 與 Bill  之間的曼哈頓距離如下表所示。

表 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM