協同過濾演算法 預測和推薦

1 協同過濾演算法介紹

  1.什麼是協同過濾演算法

      1. 協同過濾推薦演算法是誕生最早,並且較為著名的推薦演算法,主要的功能是預測和推薦

      2. 演算法通過對用戶歷史行為數據的挖掘發現用戶的偏好,基於不同的偏好對用戶進行群組劃分並推薦品味相似的商品

      3. 協同過濾推薦演算法分為兩類,分別是基於用戶的協同過濾演算法(user-based collaboratIve filtering),和基於物品的協同過濾演算法(item-based collaborative filtering)。

      4. 簡單的說就是:人以類聚,物以群分。下面我們將分別說明這兩類推薦演算法的原理和實現方法。

  2.基於用戶的協同過濾演算法

      1. 協同過濾演算法是一種基於關聯規則的演算法,以購物行為為例。

      2. 假設有甲和乙兩名用戶,有a、b、c三款產品。

      3. 如果甲和乙都購買了a和b這兩種產品,我們可以假定甲和乙有近似的購物品味。

      4. 當甲購買了產品c而乙還沒有購買c的時候,我們就可以把c也推薦給乙。

      5. 這是一種典型的user-based情況,就是以user的特性做為一種關聯。

      舉例:    

        1)直覺分析:「用戶A/B」都喜歡物品A和物品B,從而「用戶A/B」的口味最為相近
        2)因此,為「用戶A」推薦物品時可參考「用戶B」的偏好,從而推薦D

        

   3.基於物品的協同過濾演算法

      舉例:

        1)物品組合(A,D)被同時偏好出現的次數最多,因而可以認為A/D兩件物品的相似度最高

        2)從而,可以為選擇了A物品的用戶推薦D物品

        

2 歐幾里德距離評價

  1.歐幾里得度量 是什麼?

      1. 歐幾里得度量(euclidean metric)(也稱歐氏距離)是一個通常採用的距離定義

      2. 指在m維空間中兩個點之間的真實距離,或者向量的自然長度(即該點到原點的距離)。

      3. 在二維和三維空間中的歐氏距離就是兩點之間的實際距離

        

      4. n維空間的公式

         

  2. 藉助「歐幾里得度量」 尋找偏好相似的用戶原理

      1)在示例中,5個用戶分別對兩件商品進行了評分。

      2)這裡的分值可能表示真實的購買,也可以是用戶對商品不同行為的量化指標。

      3)例如,瀏覽商品的次數,向朋友推薦商品,收藏,分享,或評論等等。

      4)這些行為都可以表示用戶對商品的態度和偏好程度。

      

      5)在散點圖中,Y軸是商品1的評分,X軸是商品2的評分,通過用戶的分布情況可以發現,A,C,D三個用戶距離較近。

      6)用戶A(3.3 6.5)和用戶C(3.6 6.3),用戶D(3.4 5.8)對兩件商品的評分較為接近。而用戶E和用戶B則形成了另一個群體。

      7)散點圖雖然直觀,但無法投入實際的應用,也不能準確的度量用戶間的關係。

      8)因此我們需要通過數字對用戶的關係進行準確的度量,並依據這些關係完成商品的推薦。

      

  3.歐幾里德係數 對上面用戶評價分析

      1. 通過公式我們獲得了5個用戶相互間的歐幾里德係數,也就是用戶間的距離。

      2. 係數越小表示兩個用戶間的距離越近,偏好也越是接近。

      3. 不過這裡有個問題,太小的數值可能無法準確的表現出不同用戶間距離的差異,因此我們對求得的係數取倒數,使用戶間的距離約接近,數值越大。

      4. 在下面的表格中,可以發現,用戶A&C用戶A&D和用戶C&D距離較近。同時用戶B&E的距離也較為接近。與我們前面在散點圖中看到的情況一致。

      

3 使用協同過濾演算法簡單測試

  1、測試數據

      第一步,將數據讀取並格式化為字典形式,便於解析

      第二步:藉助”歐幾里德”演算法計算用戶相似度

      第三步:計算某個用戶與其他用戶的相似度

      第四步:根據相似度最高的用戶喜好商品排序,把相似度最高用戶的喜好推薦給當前用戶

#photo.txt
1,華為p30,2.0
1,三星s10,5.0
1,小米9,2.6
2,華為p30,1.0
2,vivo,5.0
2,htc,4.6
3,魅族,2.0
3,iphone,5.0
3,pixel2,2.6
---------------------------------------------------------
with open('./phone.txt', 'r', encoding='utf-8') as fp:
    content = fp.readlines()

# 第一步,將數據讀取並格式化為字典形式,便於解析
def parse_data():
    with open('./phone.txt','r',encoding='utf-8') as fp:
        content = fp.readlines()
    # 將用戶、評分、和手機寫入字典data
    data = {}
    for line in content:
        line = line.strip().split(',')
        #如果字典中沒有某位用戶,則使用用戶ID來創建這位用戶
        if not line[0] in data.keys():
            data[line[0]] = {line[1]:line[2]}
        #否則直接添加以該用戶ID為key字典中
        else:
            data[line[0]][line[1]] = line[2]
    return data
data = parse_data()

'''
{
    "1":{
        "華為p30":"2.0",
        "三星s10":"5.0",
        "小米9":"2.6"
    },
    "2":{
        "華為p30":"1.0",
        "vivo":"5.0",
        "htc":"4.6"
    },
    "3":{
        "魅族":"2.0",
        "iphone":"5.0",
        "pixel2":"2.6"
    }
}
'''


# 第二步:藉助"歐幾里德"演算法計算用戶相似度
from math import *
def Euclid(user1, user2):
    # 取出兩位用戶購買過的手機和評分
    user1_data = data[user1]
    user2_data = data[user2]
    distance = 0
    # 找到兩位用戶都購買過的手機,並計算歐式距離
    for key in user1_data.keys():
        if key in user2_data.keys():
            # 注意,distance越大表示兩者越相似
            distance += pow(float(user1_data[key]) - float(user2_data[key]), 2)
    return 1 / (1 + sqrt(distance))  # 這裡返回值越小,相似度越大


# 第三步:計算某個用戶與其他用戶的相似度
def top_simliar(userID):
    res = []
    for userid in data.keys():
        #排除與自己計算相似度
        if not userid == userID:
            simliar = Euclid(userID,userid)
            res.append((userid,simliar))  # res = # [('2', 0.5), ('3', 1.0)]
    res.sort(key=lambda val:val[1])
    return res


# 第四步:根據相似度最高的用戶喜好商品排序,把相似度最高用戶的喜好推薦給當前用戶
def recommend(userid):
    #相似度最高的用戶
    top_sim_user = top_simliar(userid)[0][0]    # top_sim_user=2  找到相似度最高的用戶ID
    #相似度最高的用戶的購買記錄
    items = data[top_sim_user]    # items = {'華為p30': '1.0', 'vivo': '5.0', 'htc': '4.6'}
    recommendations = []
    #篩選出該用戶未購買的手機並添加到列表中
    for item in items.keys():
        if item not in data[userid].keys():
            recommendations.append((item,items[item]))
    recommendations.sort(key=lambda val:val[1],reverse=True)#按照評分排序
    return recommendations


if __name__ == '__main__':
    # 找到與用戶id為1的用戶相似度最高的用戶
    print(recommend('1'))  #  [('vivo', '5.0'), ('htc', '4.6')]