自定義代碼實現UserCF——基於用戶的協同過濾算法

一、什麼是基於用戶協同過濾算法

  • 我覺得概念講高大上了,並沒有什麼用,反而還難以理解。我就用通俗的語言來描述一下這個算法的邏輯吧。這個算法的核心就是這樣的:當前如果有一個用戶 A 他正在等待被推薦,這時我們就會找出平時的行爲和 A 相似的用戶 B ,然後我們再講用戶 B 在意或者感興趣的且 A 還沒瀏覽過的商品推薦給 A ;這就是基於用戶的協同過濾算法。

二、如何計算相似度

  • 在前面我們瞭解到了UserCF的算法邏輯了之後,可能你會發現概念中有一個很重要的點就是,計算與A相似的用戶,我們用什麼一個標準來衡量兩個用戶的相似程度了;其實已經引出概念了,我們就是需要用相似度來表示兩個用戶之間的相似程度,但是相似度該如何計算呢?
  • 先別急,我們先來想想如果要描述一個人該怎麼描述,當然是用一些這個人的特徵來描述,對吧。我們先來看個用戶的特徵表
用戶 遊戲 學習
A 7 7
B 6 8
C 1 10
D 9 1
  • 上面這個中的數字代表各個用戶對學習和遊戲的看法(也可以看做是評分)滿分是10分,我們可以很容易的就把上面的數據加載到圖中來:
    在這裏插入圖片描述
    把圖畫出來了和你可以很容易的看出 AB 兩點捱得最近,也就是 B 與 A 相較於其他的點與 A 的相似度最高,那麼如何計算相似度呢?上面都已經說了是捱得比較近,捱得比較近指得是是很麼,肯定就是距離了唄,那距離公式就多了,我們平時最常用的就是歐式距離(x1x2)2+(y1y2)2\displaystyle \sqrt{(x_1-x_2)^2+(y_1-y_2)^2},這裏我們評判用戶的特徵就只用兩個方面,所以就是一個二維,但在實際場景當中,往往用於衡量一個用戶有很多的特徵倆描述,這時我們如果還想使用歐氏距離,就要把它推導至N維,也就是這樣:
    Distance(X,Y)=i=0n(x1y2)2\displaystyle Distance(X,Y) = \sqrt{\sum_{i=0}^{n}{(x_1-y2)}^2}
    至於如何公式的推導,想進一步瞭解的小夥伴可以看博主往期專門講距離公式的博文,上面有具體的推導過程, 以及其他的一些常用的距離公式算法推導,跳轉地址
  • 有了上面的概念,我們就知道,我們可以使用兩個用戶之間的 ‘特徵距離‘ 來衡量用戶的的相似度了,不過你先要明確一個概念就是,用戶的特徵距離值越大(也可以說兩個用戶離得越遠)那麼兩個用戶的相似度也就越小,意思就是距離是和相似度呈反比的。

三、算法思路

  • 1、首先我們就需要將用戶的一些特徵數據轉換成數值來衡量,因爲畢竟計算機還是比較擅長於數值的處理。
    例如現在有一份觀影數據表,表的含義就是各個用戶的觀影情況,以及對此電影的打分。

    用戶名 觀影名稱 評分
    A 老炮兒 3.5
    A 唐人街探案 1.0
    B 老炮兒 2.5
    B 唐人街探案 3.5
    B 星球大戰 3.0
    B 尋龍訣 3.5
    B 神探夏洛克 2.5
    B 小門神 3.0
    C 老炮兒 3.0
    C 唐人街探案 3.5
    C 星球大戰 1.5
    C 尋龍訣 5.0
    C 神探夏洛克 3.0
    C 小門神 3.5
    D 老炮兒 2.5
    D 唐人街探案 3.5
    D 尋龍訣 3.5
    D 神探夏洛克 4.0
    E 老炮兒 3.5
    E 唐人街探案 2.0
    E 星球大戰 4.5
    E 神探夏洛克 3.5
    E 小門神 2.0
    F 老炮兒 3.0
    F 唐人街探案 4.0
    F 星球大戰 2.0
    F 尋龍訣 3.0
    F 神探夏洛克 3.0
    F 小門神 2.0
    G 老炮兒 4.5
    G 唐人街探案 1.5
    G 星球大戰 3.0
    G 尋龍訣 5.0
    G 神探夏洛克 3.5

    上面的表有一個很關鍵的指標就是評分,可以通過這個值的大小來看出用戶對該電影的喜愛度(也可以說是感興趣程度),我們都知道描述一個用戶的特徵是多維度的,而且作爲分析需要,我們描述各個用戶都應該從相同的維度來描述。根據上面的思路,再根據表的結構,我們可以將上面的錶轉換成下面的格式:

    * 神探夏洛克 小門神 唐人街探案 尋龍訣 老炮兒 星球大戰
    A 0 0 1.0 0 3.5 0
    B 2.5 3.0 3.5 3.5 2.5 3.0
    C 3.0 3.5 3.5 5.0 3.0 1.5
    D 4.0 0 3.5 3.5 2.5 0
    E 3.5 2.0 2.0 0 3.5 4.5
    F 3.0 2.0 4.0 3.0 3.0 2.0
    G 3.5 0 1.5 5.0 4.5 3.0

    轉換成上面這個表的思路其實就是現將所有(不重複的電影名列出來)然後如果該用戶沒看過該電影,我們就將該用戶對該電影的評分置爲 0,通過這樣的轉換我們就將用戶的特徵由不規則的文本類型,轉換成了標準的數值特徵,例如:用戶 A 的特徵向量就爲VA=<0,0,1.0,0,3.5,0>V_A=<0,0,1.0,0,3.5,0>

  • 2、現在有了特徵矩陣,我們就可以來計算用戶之間的距離了,爲了後續計算的方便,我們不妨將所有用戶之間的距離都計算一遍,也就是計算一個距離矩陣:

    * A B C D E F G
    A \infty 6.61 7.42 5.96 6.12 5.94 6.89
    B 6.61 \infty 2.29 4.5 4.44 1.73 4.5
    C 7.42 2.29 \infty 4.24 6.24 2.6 4.58
    D 5.96 4.5 4.24 \infty 6.32 3.12 4.42
    E 6.12 4.44 6.24 6.32 \infty 4.44 5.7
    F 5.94 1.73 2.6 3.12 4.44 \infty 4.21
    G 6.89 4.5 4.58 4.42 5.7 4.21 \infty

    我們隨便找兩個用戶來作爲示例,例如要計算AB兩個用戶之間的距離,那麼我們讀前一個表可以知道VA=<0,0,1.0,0,3.5,0>,VB=<2.5,3.0,3.5,3.5,2.5,3.0>V_A=<0,0,1.0,0,3.5,0>,V_B=<2.5,3.0,3.5,3.5,2.5,3.0>,那麼AB之間的距離可以表示爲
    DAB=(02.5)2+(03.0)2+(13.5)2+(03.5)2+(3.52.5)2+(03.0)2=6.61\displaystyle D_{AB}=\sqrt{(0-2.5)^2+(0-3.0)^2+(1-3.5)^2+(0-3.5)^2+(3.5-2.5)^2+(0-3.0)^2}=6.61
    因爲用戶和自己求距離必然會是0,但是爲了後面我們要選取相似的用戶時不選到自己,那麼我們把用戶和自己求距離這裏置爲無限大即可。

  • 下只能在我們引入UserCF算法公式:
    p(u,i)=vS(u,k)N(i)wuvrvi\displaystyle p(u,i)=\sum_{v\in S(u,k)\cap N(i)}{w_{uv}r_{vi}}
    先來解釋一下公式中的參數含義:
    (1) S(u,k)S(u,k) 就是計算和用戶 u 最相似的 k 個用戶
    (2) N(i)N(i)表示對電影 i 有過評分記錄的用戶集合
    (3) wuvuvw_{uv}表示用戶u和用戶v之間的相似度
    (4) rvir_{vi}表示用戶 v 對電影 i 喜好度,也就是用戶 v 對 電影i的評分
    然後通過這個公式計算出來值就是推薦指數(爲了方便,我們這裏就用距離代表用戶之間的相似度,所以最後求出來的推薦度應該是越小,該物品就越值得推薦)

四、代碼實現

1、準備數據

import numpy as np

movies_data_dict = {
    'A': {'老炮兒':3.5,'唐人街探案': 1.0},
    'B': {'老炮兒':2.5,'唐人街探案': 3.5,'星球大戰': 3.0, '尋龍訣': 3.5, '神探夏洛克': 2.5, '小門神': 3.0},
    'C': {'老炮兒':3.0,'唐人街探案': 3.5,'星球大戰': 1.5, '尋龍訣': 5.0, '神探夏洛克': 3.0, '小門神': 3.5},
    'D': {'老炮兒':2.5,'唐人街探案': 3.5,'尋龍訣': 3.5, '神探夏洛克': 4.0},
    'E': {'老炮兒':3.5,'唐人街探案': 2.0,'星球大戰': 4.5, '神探夏洛克': 3.5,'小門神': 2.0},
    'F': {'老炮兒':3.0,'唐人街探案': 4.0,'星球大戰': 2.0, '尋龍訣': 3.0,'神探夏洛克': 3.0, '小門神': 2.0},
    'G': {'老炮兒':4.5,'唐人街探案': 1.5,'星球大戰': 3.0, '尋龍訣': 5.0,'神探夏洛克': 3.5}
    }

2、編寫將數據標準化的函數

# 將整個數據集轉換成矩陣形式,如果用戶沒有對該電影進行評分則該位置置爲 0
def MoveiesScaler(data):
    movie_index = {}
    i = 0
    for _,ratings in data.items():
        for movie,score in ratings.items():
            if movie_index.get(movie) is None:
                movie_index[movie] = i
                i += 1
    result_data = {}
    for user,ratings in data.items():
        user_ratings = np.zeros(len(movie_index))
        for movie,score in ratings.items():
            user_ratings[movie_index[movie]] = score
        result_data[user] = user_ratings
    return movie_index,result_data

3、編寫特徵距離計算函數

# 計算兩個用戶的歐式距離
def EuclideanDistance(feature1,feature2):
    return np.sqrt(np.sum(np.power(feature1-feature2,2)))

# 計算所有用戶倆倆之間的距離,也就是距離矩陣,用戶對自己求距離時將距離置爲無窮大方便後續處理
def DistanceMatrix(user_dict):
    result_matrix = np.zeros((len(user_dict),len(user_dict)))
    user_index = dict(zip(user_dict.keys(),range(len(user_dict))))
    features = list(user_dict.values())
    for r in range(len(features)):
        for c in range(r,len(features)):
            if c == r:
                result_matrix[r][c] = np.Inf
            else:
                ed = EuclideanDistance(features[r],features[c])
                result_matrix[r][c] = ed
                result_matrix[c][r] = ed
    return user_index,result_matrix

4、S(u,k)S(u,k) 和用戶u最相似的k個用戶

def similarity_max_k(similarity_matrix,user_index,u,k):
    similarity_list = similarity_matrix[user_index[u]]
    user_sim_map = dict(zip(similarity_list.tolist(),user_index.keys()))
    least_k_keys = sorted(user_sim_map)[:k]
    least_k = set({user_sim_map[k] for k in least_k_keys})
    return least_k

5、N(i)N(i)對電影 i 有過評分的用戶

def user_with_item(data_sets,i):
    user_set = set()
    for user,ratings in data_sets.items():
        if(ratings[i] != 0):
            user_set.add(user)
    return user_set

6、rvir_{vi} 用戶 v 對 電影i的評分

def interest_degree(data_sets,v,i):
    return data_sets[v][i]

7、將前面的函數組裝起來就是UserCF的公式實現

def recommended_score(datasets,u,k,i):
    user_index,similarity_matrix = DistanceMatrix(data_sets)
    similarity_k_set = similarity_max_k(similarity_matrix,user_index,u,k)
    user_item_set = user_with_item(data_sets,i)
    iter_set = similarity_k_set & user_item_set
    
    result = 0
    
    for v in iter_set:
        v_i = user_index[v]
        u_i = user_index[u]
        w = similarity_matrix[v_i][u_i]
        result += (w*interest_degree(data_sets,v,i))
    return result

8、查看各個商品對用戶的推薦度

for i in range(6):
    print(recommended_score(movies_data_dict,"A",2,i))

out:

32.70698224032311
44.60234092774856
11.874342087037917
38.6651698842296
41.64426370618284
11.874342087037917

我們可以尋找出推薦度最小的(因爲推薦度公式那兒我們已經換成了距離來計算,所以要越小越好)那幾個商品,且用戶A還沒有看過的,那麼我們就可以把這些商品推薦給他。
補充:我們可以使用不同的距離公式來計算兩個用戶之間的特徵距離,比較好的就是 Jaccard(傑卡德距離),餘弦距離您可以嘗試使用不同的距離計算公式來嘗試,然後調整您推薦算法的準確率。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章