1. 程式人生 > >協同過濾演算法概述與python 實現協同過濾演算法基於內容(usr-item,item-item)

協同過濾演算法概述與python 實現協同過濾演算法基於內容(usr-item,item-item)

協調過濾推薦概述

  協同過濾(Collaborative Filtering)作為推薦演算法中最經典的型別,包括線上的協同和離線的過濾兩部分。所謂線上協同,就是通過線上資料找到使用者可能喜歡的物品,而離線過濾,則是過濾掉一些不值得推薦的資料,比比如推薦值評分低的資料,或者雖然推薦值高但是使用者已經購買的資料。
  協同過濾的模型一般為m個物品,m個使用者的資料,只有部分使用者和部分資料之間是有評分資料的,其它部分評分是空白,此時我們要用已有的部分稀疏資料來預測那些空白的物品和資料之間的評分關係,找到最高評分的物品推薦給使用者。
 

三種協同過濾推薦

  一般來說,協同過濾推薦分為三種類型。第一種是基於使用者(user-based)的協同過濾,第二種是基於專案(item-based)的協同過濾,第三種是基於模型(model based)的協同過濾。
  協同過濾的優缺點:

直通車
  基於使用者(user-based)的協同過濾主要考慮的是使用者和使用者之間的相似度,只要找出相似使用者喜歡的物品,並預測目標使用者對對應物品的評分,就可以找到評分最高的若干個物品推薦給使用者。而基於專案(item-based)的協同過濾和基於使用者的協同過濾類似,只不過這時我們轉向找到物品和物品之間的相似度,只有找到了目標使用者對某些物品的評分,那麼我們就可以對相似度高的類似物品進行預測,將評分最高的若干個相似物品推薦給使用者。比如你在網上買了一本機器學習相關的書,網站馬上會推薦一堆機器學習,大資料相關的書給你,這裡就明顯用到了基於專案的協同過濾思想。
我們可以簡單比較下基於使用者的協同過濾和基於專案的協同過濾:基於使用者的協同過濾需要線上找使用者和使用者之間的相似度關係,計算複雜度肯定會比基於基於專案的協同過濾高。但是可以幫助使用者找到新類別的有驚喜的物品。而基於專案的協同過濾,由於考慮的物品的相似性一段時間不會改變,因此可以很容易的離線計算,準確度一般也可以接受,但是推薦的多樣性來說,就很難帶給使用者驚喜了。一般對於小型的推薦系統來說,基於專案的協同過濾肯定是主流。但是如果是大型的推薦系統來說,則可以考慮基於使用者的協同過濾,當然更加可以考慮我們的第三種類型,基於模型的協同過濾。
基於模型(model based)的協同過濾是目前最主流的協同過濾型別了,我們的一大堆機器學習演算法也可以在這裡找到用武之地。下面我們就重點介紹基於模型的協同過濾。
我們將使用MovieLens資料集,它是在實現和測試推薦引擎時所使用的最常見的資料集之一。它包含來自於943個使用者以及精選的1682部電影的100K個電影打分。資料下載
連線
,相關資料說明直通車

步驟

讀取原始資料構建矩陣

從原始資料中讀取,並建立dataframe:

#u.data檔案中包含了完整資料集。
u_data_path="C:\\Users\\lenovo\\Desktop\\ml-100k\\"
header = ['user_id', 'item_id', 'rating', 'timestamp']
df = pd.read_csv(u_data_path+'ml-100k/u.data', sep='\t', names=header)
print(df.head(5))
print(len(df))
#觀察資料前5行。接下來,讓我們統計其中的使用者和電影總數。
n_users = df.user_id.unique().shape[0] #unique()為去重.shape[0]行個數 n_items = df.item_id.unique().shape[0] print ('Number of users = ' + str(n_users) + ' | Number of movies = ' + str(n_items)) #切割訓練集與測試級 from sklearn import model_selection as cv train_data, test_data = cv.train_test_split(df, test_size=0.25)

這裡寫圖片描述
   基於內容協同過濾法可以被主要分為兩部分:使用者-專案過濾(user-item filtering)和專案-專案過濾( item-item filtering)。 user-item filtering選取一個特定使用者,基於評價相似性找到與該使用者相似的其他使用者,並推薦那些相似使用者所喜歡的專案。相比之下, item-item filtering 先選取一個專案,然後找出也喜歡這個專案的其他使用者,並找出這些使用者或相似使用者也喜歡的其他專案,推薦過程需要專案並輸出其他專案。
Item-Item Collaborative Filtering: “Users who liked this item also liked …”

User-Item Collaborative Filtering: “Users who are similar to you also liked …”
  在這兩種情況中,你根據整個資料集建立了一個使用者-專案的矩陣。因為已經把資料分成了測試和訓練兩部分所以你需要建立兩個[943 x 1682]矩陣。訓練矩陣包含75%的評價,測試矩陣包含25%的矩陣。
使用者-專案矩陣例子:
這裡寫圖片描述
  建立了使用者-專案矩陣之後,計算相似性並建立一個相似度矩陣。
  在產品-產品協同過濾中的產品之間的相似性值是通過觀察所有對兩個產品之間的打分的使用者來度量的。
  這裡寫圖片描述
  對於使用者-產品協同過濾,使用者之間的相似性值是通過觀察所有同時被兩個使用者打分的產品來度量的。
  這裡寫圖片描述
Item-Item Collaborative Filtering演算法中專案之間的相似度依靠觀測所有的已對相同專案評價的使用者來測算。

  對於User-Item Collaborative Filtering演算法,使用者之間的相似性依靠觀測相同使用者已評價的所有專案。
  推薦系統中通常使用餘弦相似性作為距離度量,在n維孔空間中評價被視為向量,基於這些向量之間的夾角來計算相似性。
  使用者a和m可以用下面的公式計算餘弦相似性,其中你可以使用使用者向量這裡寫圖片描述之間的點積然後除以這兩個向量歐式長度之乘。
這裡寫圖片描述
要計算產品m和b之間的相似性,使用公式:
這裡寫圖片描述

建立兩個矩陣為測試和訓練資料集。
#Create two user-item matrices, one for training and another for testing
#差別在於train_data與test_data
train_data_matrix = np.zeros((n_users, n_items))
print(train_data_matrix.shape)
for line in train_data.itertuples():
    # print(line)
    # print("line={}\nlen(line)={}\n,line[1]={}\n,line[1]-1={}\n,line[2]={}\n,line[2]-1={}\n".format(line,len(line),line[1],line[1]-1,line[2],line[2]-1))
    train_data_matrix[line[1]-1, line[2]-1] = line[3]
test_data_matrix = np.zeros((n_users, n_items))
for line in test_data.itertuples():
    test_data_matrix[line[1]-1, line[2]-1] = line[3]
計算相似度
 # 你可以使用 sklearn 的pairwise_distances函式來計算餘弦相似性。注意,因為評價都為正值輸出取值應為0到1.
from sklearn.metrics.pairwise import pairwise_distances
user_similarity = pairwise_distances(train_data_matrix, metric='cosine')
#矩陣的轉置實現主題的相似度
item_similarity = pairwise_distances(train_data_matrix.T, metric='cosine')
預測

  下一步是作出預測。既然構造了相似度矩陣user_similarity和item_similarity,因此你可以運用下面的公式為user-based CF做一個預測:
這裡寫圖片描述
  使用者k和使用者a之間的相似度根據一個相似使用者a的一系列評價的乘積(修正為該使用者的平均評價)的權重。你將需要標準化相似度這樣可以使評價維持在1到5之間,最後一步,統計你想預測使用者平均評價的總和。
  這裡考慮到的問題是一些使用者評價所有電影時可能要麼給最高分,要麼給最低分。這些使用者給出評價的相對不同比絕對值更重要。例如:設想,使用者k對他最喜歡的電影評價4顆星,其他的好電影則評價3顆星。假設現在另一個使用者t對他/她喜歡的一部電影評價為5顆星,看了想睡覺的一部電影評價為3顆星。這兩位使用者電影口味可能很相似但使用評價體系的方法不同。
  當為item-based CF做一個推薦時候,你不要糾正使用者的平均評價,因為使用者本身用查詢來做預測。
  這裡寫圖片描述

def predict(ratings, similarity, type='user'):
    if type == 'user':
        mean_user_rating = ratings.mean(axis=1)
        #You use np.newaxis so that mean_user_rating has same format as ratings
        ratings_diff = (ratings - mean_user_rating[:, np.newaxis])
        pred = mean_user_rating[:, np.newaxis] + similarity.dot(ratings_diff) / np.array([np.abs(similarity).sum(axis=1)]).T
    elif type == 'item':
        pred = ratings.dot(similarity) / np.array([np.abs(similarity).sum(axis=1)])    
    return pred

兩種方法預測

item_prediction = predict(train_data_matrix, item_similarity, type='item')
user_prediction = predict(train_data_matrix, user_similarity, type='user')
評估

  有許多的評價指標,但是用於評估預測精度最流行的指標之一是Root Mean Squared Error (RMSE)。
這裡寫圖片描述
  你可以用sklearn中的 mean_square_error (MSE)函式,RMSE只是MSE其中的一個平方根。想閱讀更多關於不同評估指標你可以 檢視這篇文章
  因為你僅僅想考慮在這個測試資料集中的預測評價,你可以用prediction[ground_truth.
nonzero()]過濾測試矩陣中所有其他的元素。

#有許多的評價指標,但是用於評估預測精度最流行的指標之一是Root Mean Squared Error (RMSE)。
from sklearn.metrics import mean_squared_error
from math import sqrt
def rmse(prediction, ground_truth):
    prediction = prediction[ground_truth.nonzero()].flatten()#nonzero(a)返回陣列a中值不為零的元素的下標,相當於對稀疏矩陣進行提取
    ground_truth = ground_truth[ground_truth.nonzero()].flatten()
    return sqrt(mean_squared_error(prediction, ground_truth))

print ('User-based CF RMSE: ' + str(rmse(user_prediction, test_data_matrix)))
print ('Item-based CF RMSE: ' + str(rmse(item_prediction, test_data_matrix)))   

這裡寫圖片描述
Memory-based演算法容易實施併產生合理的預測質量。memory-based CF的缺點是它不能擴充套件到現實世界的場景和沒有處理眾所周知的冷啟動問題(面對新使用者或者新專案進去系統時)。Model-based CF方法可伸縮並且能處理 比memory-based方法更高等級的稀疏度,面對新使用者或者沒有任何評價的新專案進入系統時也會變差。
參考文獻:Ethan Rosenthal關於Memory-Based Collaborative Filtering的部落格直通車

畫一個圖來詮釋以下,基於usr的,畫的不好多多見諒!!!
這裡寫圖片描述

完整程式碼:

#!usr/bin/env python
#_*_ coding:utf-8 _*_

"""
title:python 實現協同過濾演算法基於使用者與基於內容
"""
import numpy as np
import pandas as pd

#u.data檔案中包含了完整資料集。
u_data_path="C:\\Users\\lenovo\\Desktop\\ml-100k\\"
header = ['user_id', 'item_id', 'rating', 'timestamp']
df = pd.read_csv(u_data_path+'ml-100k/u.data', sep='\t', names=header)
print(df.head(5))
print(len(df))
#觀察資料前兩行。接下來,讓我們統計其中的使用者和電影總數。
n_users = df.user_id.unique().shape[0]  #unique()為去重.shape[0]行個數
n_items = df.item_id.unique().shape[0]
print ('Number of users = ' + str(n_users) + ' | Number of movies = ' + str(n_items))
from sklearn import model_selection as cv
train_data, test_data = cv.train_test_split(df, test_size=0.25)

#Create two user-item matrices, one for training and another for testing
#差別在於train_data與test_data
train_data_matrix = np.zeros((n_users, n_items))
print(train_data_matrix.shape)
for line in train_data.itertuples():
    train_data_matrix[line[1]-1, line[2]-1] = line[3
test_data_matrix = np.zeros((n_users, n_items))
for line in test_data.itertuples():
    test_data_matrix[line[1]-1, line[2]-1] = line[3]
# 你可以使用 sklearn 的pairwise_distances函式來計算餘弦相似性。注意,因為評價都為正值輸出取值應為0到1.
from sklearn.metrics.pairwise import pairwise_distances
user_similarity = pairwise_distances(train_data_matrix, metric='cosine')
#矩陣的轉置實現主題的相似度
item_similarity = pairwise_distances(train_data_matrix.T, metric='cosine')

def predict(ratings, similarity, type='user'):
    if type == 'user':
        mean_user_rating = ratings.mean(axis=1)
        #You use np.newaxis so that mean_user_rating has same format as ratings
        ratings_diff = (ratings - mean_user_rating[:, np.newaxis])
        pred = mean_user_rating[:, np.newaxis] + similarity.dot(ratings_diff) / np.array([np.abs(similarity).sum(axis=1)]).T
    elif type == 'item':
        pred = ratings.dot(similarity) / np.array([np.abs(similarity).sum(axis=1)])
    return pred
item_prediction = predict(train_data_matrix, item_similarity, type='item')
user_prediction = predict(train_data_matrix, user_similarity, type='user')

#有許多的評價指標,但是用於評估預測精度最流行的指標之一是Root Mean Squared Error (RMSE)。
from sklearn.metrics import mean_squared_error
from math import sqrt
def rmse(prediction, ground_truth):
    prediction = prediction[ground_truth.nonzero()].flatten()#nonzero(a)返回陣列a中值不為零的元素的下標,相當於對稀疏矩陣進行提取
    ground_truth = ground_truth[ground_truth.nonzero()].flatten()
    return sqrt(mean_squared_error(prediction, ground_truth))

print ('User-based CF RMSE: ' + str(rmse(user_prediction, test_data_matrix)))
print ('Item-based CF RMSE: ' + str(rmse(item_prediction, test_data_matrix)))

參考文獻:英文版:直通車
參考文獻2:直通車
中文版:直通車