超級詳細的協同過濾推薦系統+完整Python實現及結果
協同過濾推薦系統在我們的日常生活之中無處不在,例如,在電子商城購物,系統會根據使用者的記錄或者其他的
資訊來推薦相應的產品給客戶,是一種智慧的生活方式。之所以交協同過濾,是因為在實現過濾推薦的時候是根據
其他人的行為來做預測的,基於相似使用者的喜好來實現使用者的喜好預測。
簡要介紹:
通過找到興趣相投,或者有共同經驗的群體,來向用戶推薦感興趣的資訊。
舉例,如何協同過濾,來對使用者A進行電影推薦?
答:簡要步驟如下
找到使用者A(user_id_1)的興趣愛好
找到與使用者A(user_id_1)具有相同電影興趣愛好的使用者群體集合Set<user_id>
找到該群體喜歡的電影集合Set<movie_id>
將這些電影Set<Movie_id>推薦給使用者A(user_id_1)
具體實施步驟如何?
答:簡要步驟如下
(1)畫一個大表格,橫座標是所有的movie_id,縱座標所有的user_id,交叉處代表這個使用者喜愛這部電影
Move_id_1 |
Move_id_2 |
Move_id_3 |
Move_id_4 |
Move_id_5 |
…… |
Move_id_110w |
User_id_1 |
1 |
1 |
1 |
|||
User_id_2 |
1 |
1 |
1 |
1 |
||
User_id_3 |
1 |
1 |
1 |
1 |
||
…………. |
||||||
…………. | ||||||
User_id_10w |
1 |
1 |
1 |
如上表:
橫座標,假設有10w部電影,所以橫座標有10w個movie_id,資料來源自資料庫
縱座標,假設有100w個使用者,所以縱座標有100w個user_id,資料也來自資料庫
交叉處,“1”代表使用者喜愛這部電影,資料來自日誌
畫外音:什麼是“喜歡”,需要人為定義,例如瀏覽過,查詢過,點贊過,反正日誌裡有這些資料
(2)找到使用者A(user_id_1)的興趣愛好
如上表,可以看到,使用者A喜歡電影{m1, m2, m3}
(3)找到與使用者A(user_id_1)具有相同電影興趣愛好的使用者群體集合Set<user_id>
如上表,可以看到,喜歡{m1, m2, m3}的使用者,除了u1,還有{u2, u3}
(4)找到該群體喜歡的電影集合Set<movie_id>
如上表,具備相同喜好的使用者群裡{u2, u3},還喜好的電影集合是{m4, m5}
畫外音:“協同”就體現在這裡。
(5)未來使用者A(use_id_1)來訪問網站時,要推薦電影{m4, m5}給ta。
具體實現步驟:
第一步:計算兩者之間的相似度
通常會先把二維表格繪製在一個圖中總,每個使用者資料表示一個點。
度量相似度計算的方法:a.曼哈頓距離計算(計算迅速,節省時間)
b.歐氏距離計算(計算兩個點之間的直線距離)
資料預處理:
或者
解壓讀取movies.csv和ratings.csv檔案
兩個檔案的資料格式如下:
通過如下程式提取資料:
#!/usr/bin/env python
# encoding: utf-8
"""
@Company:華中科技大學電氣學院聚變與等離子研究所
@version: V1.0
@author: YEXIN
@contact: [email protected] or [email protected] 2018--2020
@software: PyCharm
@file: recommend.py
@time: 2018/8/19 17:32
@Desc:讀取使用者的電影資料和評分資料
"""
import pandas as pd
movies = pd.read_csv("E:\PycharmWorks\ML\CollaborativeFiltering\ml-latest\movies.csv")
ratings = pd.read_csv("E:\PycharmWorks\ML\CollaborativeFiltering\ml-latest\\ratings.csv")##這裡注意如果路徑的中檔名開頭是r,要轉義。
data = pd.merge(movies,ratings,on = 'movieId')#通過兩資料框之間的movieId連線
data[['userId','rating','movieId','title']].sort_values('userId').to_csv('E:\PycharmWorks\ML\CollaborativeFiltering\ml-latest\data.csv',index=False)
結果:
採用python字典來表示每位使用者評論的電影和評分
#!/usr/bin/env python
# encoding: utf-8
"""
@Company:華中科技大學電氣學院聚變與等離子研究所
@version: V1.0
@author: YEXIN
@contact: [email protected] or [email protected] 2018--2020
@software: PyCharm
@file: movie_rating_user.py
@time: 2018/8/19 17:50
@Desc:採用python字典來表示每位使用者評論的電影和評分
"""
file = open("E:\PycharmWorks\ML\CollaborativeFiltering\ml-latest\data.csv",'r', encoding='UTF-8')#記得讀取檔案時加‘r’, encoding='UTF-8'
##讀取data.csv中每行中除了名字的資料
data = {}##存放每位使用者評論的電影和評分
for line in file.readlines()[1:100]:
#注意這裡不是readline()
line = line.strip().split(',')
#如果字典中沒有某位使用者,則使用使用者ID來建立這位使用者
if not line[0] in data.keys():
data[line[0]] = {line[3]:line[1]}
#否則直接新增以該使用者ID為key字典中
else:
data[line[0]][line[3]] = line[1]
print(data)
結果(部分):
計算任何兩位使用者之間的相似度,由於每位使用者評論的電影不完全一樣,所以獸先要找到兩位使用者共同評論過的電影
然後計算兩者之間的歐式距離,最後算出兩者之間的相似度。
#!/usr/bin/env python
# encoding: utf-8
"""
@Company:華中科技大學電氣學院聚變與等離子研究所
@version: V1.0
@author: YEXIN
@contact: [email protected] or [email protected] 2018--2020
@software: PyCharm
@file: movie_rating_user.py
@time: 2018/8/19 17:50
@Desc:採用python字典來表示每位使用者評論的電影和評分
"""
file = open("E:\PycharmWorks\ML\CollaborativeFiltering\ml-latest\data.csv",'r', encoding='UTF-8')#記得讀取檔案時加‘r’, encoding='UTF-8'
##讀取data.csv中每行中除了名字的資料
data = {}##存放每位使用者評論的電影和評分
for line in file.readlines()[1:100]:
#注意這裡不是readline()
line = line.strip().split(',')
#如果字典中沒有某位使用者,則使用使用者ID來建立這位使用者
if not line[0] in data.keys():
data[line[0]] = {line[3]:line[1]}
#否則直接新增以該使用者ID為key字典中
else:
data[line[0]][line[3]] = line[1]
#print(data)
"""計算任何兩位使用者之間的相似度,由於每位使用者評論的電影不完全一樣,所以獸先要找到兩位使用者共同評論過的電影
然後計算兩者之間的歐式距離,最後算出兩者之間的相似度
"""
from math import *
def Euclidean(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 top10_simliar(userID):
res = []
for userid in data.keys():
#排除與自己計算相似度
if not userid == userID:
simliar = Euclidean(userID,userid)
res.append((userid,simliar))
res.sort(key=lambda val:val[1])
return res[:4]
RES = top10_simliar('1')
print(RES)
使用者之間相似度結果:0表示兩位的影評幾乎一樣,1表示沒有共同的影評
根據相似度來推薦使用者:
########################################################################
#根據使用者推薦電影給其他人
def recommend(user):
#相似度最高的使用者
top_sim_user = top10_simliar(user)[0][0]
#相似度最高的使用者的觀影記錄
items = data[top_sim_user]
recommendations = []
#篩選出該使用者未觀看的電影並新增到列表中
for item in items.keys():
if item not in data[user].keys():
recommendations.append((item,items[item]))
recommendations.sort(key=lambda val:val[1],reverse=True)#按照評分排序
#返回評分最高的10部電影
return recommendations[:10]
Recommendations = recommend('1')
print(Recommendations)
推薦結果:
==================================================================================
但有時我們會碰到因為兩個使用者之間資料由於資料膨脹,一方資料大,一方資料小,但是兩者稱明顯的線性關係
我們引入Pearson相關係數來衡量兩個變數之間的線性相關性。
Pearson:-1~1 -1:完全負相關 1:完全正相關 0:不相關
相關係數 0.8-1.0 極強相關
0.6-0.8 強相關
0.4-0.6 中等程度相關
0.2-0.4 弱相關
0.0-0.2 極弱相關或無相關
公式:
python程式碼:
#########################################################################
##計算兩使用者之間的Pearson相關係數
def pearson_sim(user1,user2):
# 取出兩位使用者評論過的電影和評分
user1_data = data[user1]
user2_data = data[user2]
distance = 0
common = {}
# 找到兩位使用者都評論過的電影
for key in user1_data.keys():
if key in user2_data.keys():
common[key] = 1
if len(common) == 0:
return 0#如果沒有共同評論過的電影,則返回0
n = len(common)#共同電影數目
print(n,common)
##計算評分和
sum1 = sum([float(user1_data[movie]) for movie in common])
sum2 = sum([float(user2_data[movie]) for movie in common])
##計算評分平方和
sum1Sq = sum([pow(float(user1_data[movie]),2) for movie in common])
sum2Sq = sum([pow(float(user2_data[movie]),2) for movie in common])
##計算乘積和
PSum = sum([float(user1_data[it])*float(user2_data[it]) for it in common])
##計算相關係數
num = PSum - (sum1*sum2/n)
den = sqrt((sum1Sq-pow(sum1,2)/n)*(sum2Sq-pow(sum2,2)/n))
if den == 0:
return 0
r = num/den
return r
R = pearson_sim('1','3')
print(R)
pearson係數結果: