兩種最普遍的推薦系統(tǒng)的類型是基于內(nèi)容和協(xié)同過(guò)濾(CF)。協(xié)同過(guò)濾基于用戶對(duì)產(chǎn)品的態(tài)度產(chǎn)生推薦,基于內(nèi)容的推薦系統(tǒng)基于物品屬性的相似性進(jìn)行推薦。CF可以分為基于內(nèi)存的協(xié)同過(guò)濾和基于模型的協(xié)同過(guò)濾。
我們將使用MovieLens數(shù)據(jù)集,它是在實(shí)現(xiàn)和測(cè)試推薦引擎時(shí)所使用的最常見(jiàn)的數(shù)據(jù)集之一,包含來(lái)自943個(gè)用戶以及精選的1682部電影的評(píng)分。數(shù)據(jù)下載地址
導(dǎo)入numpy和pandas庫(kù)
import numpy as np
import pandas as pd
讀入u.data數(shù)據(jù)文件
header = ['user_id', 'item_id', 'rating', 'timestamp']
df = pd.read_csv('u.data', sep = '\t', names = header)
查看用戶和電影的數(shù)量
n_users = df.user_id.unique().shape[0]
n_items = df.item_id.unique().shape[0]
print 'Number of users = ' + str(n_users) + ' | Number of movies = ' + str(n_items)
Number of users = 943 | Number of movies = 1682
使用scikit-learn庫(kù)將數(shù)據(jù)集分割成測(cè)試集和訓(xùn)練集,調(diào)用Cross_validation.train_test_split根據(jù)測(cè)試樣本的比例(test_size)將數(shù)據(jù)混洗并分割成兩個(gè)數(shù)據(jù)集。
from sklearn import cross_validation as cv
train_data,test_data = cv.train_test_split(df, test_size = 0.25)
基于內(nèi)存的協(xié)同過(guò)濾
基于內(nèi)存的協(xié)同過(guò)濾方法可以分為兩個(gè)部分:用戶-產(chǎn)品協(xié)同過(guò)濾和產(chǎn)品-產(chǎn)品協(xié)同過(guò)濾。用戶-產(chǎn)品協(xié)同過(guò)濾將選取一個(gè)特定的用戶,基于打分的相似性發(fā)現(xiàn)類似于該用戶的用戶,并推薦那些相似用戶喜歡的產(chǎn)品。產(chǎn)品-產(chǎn)品協(xié)同過(guò)濾會(huì)選取一個(gè)產(chǎn)品,發(fā)現(xiàn)喜歡該產(chǎn)品的用戶,并找到這些相似用戶還喜歡的其它產(chǎn)品。
用戶-產(chǎn)品協(xié)同過(guò)濾:“喜歡這東西的人也喜歡……”
產(chǎn)品-產(chǎn)品協(xié)同過(guò)濾:“像你一樣的人也喜歡……”
在這兩種情況下,從整個(gè)數(shù)據(jù)集構(gòu)建一個(gè)用戶產(chǎn)品矩陣。
用戶產(chǎn)品矩陣的例子:
計(jì)算相似性,并創(chuàng)建一個(gè)相似性矩陣。
在產(chǎn)品-產(chǎn)品協(xié)同過(guò)濾中的產(chǎn)品之間的相似性是通過(guò)觀察所有對(duì)兩個(gè)產(chǎn)品打分的用戶來(lái)度量的。
在用戶-產(chǎn)品協(xié)同過(guò)濾中的用戶之間的相似性是通過(guò)觀察所有同時(shí)被兩個(gè)用戶打分的產(chǎn)品來(lái)度量的。
通常用于推薦系統(tǒng)中的距離矩陣是余弦相似性,其中,打分被看成n維空間中的向量,而相似性是基于這些向量之間的角度進(jìn)行計(jì)算的。用戶a和m的余弦相似性可以用下面的公式進(jìn)行計(jì)算:
要計(jì)算產(chǎn)品m和b之間的相似性,使用公式:
創(chuàng)建用戶產(chǎn)品矩陣,針對(duì)測(cè)試數(shù)據(jù)和訓(xùn)練數(shù)據(jù),創(chuàng)建兩個(gè)矩陣:
train_data_matrix = np.zeros((n_users,n_items))
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函數(shù)來(lái)計(jì)算余弦相似性。
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")
已經(jīng)創(chuàng)建了相似性矩陣:user_similarity和item_similarity,因此,可以通過(guò)基于用戶的CF應(yīng)用下面的公式做出預(yù)測(cè):
可以將用戶k和用戶a之間的相似性看成權(quán)重,乘以相似用戶a(校正的平均評(píng)分用戶)的評(píng)分,這里需要規(guī)范化該值,使得打分位于1到5之間,最后對(duì)嘗試預(yù)測(cè)的用戶的平均評(píng)分求和。
基于產(chǎn)品的CF應(yīng)用下面的公司進(jìn)行預(yù)測(cè),此時(shí)無(wú)需糾正用戶的平均打分
def predict(rating, similarity, type = 'user'):
if type == 'user':
mean_user_rating = rating.mean(axis = 1)
rating_diff = (rating - mean_user_rating[:,np.newaxis])
pred = mean_user_rating[:,np.newaxis] + similarity.dot(rating_diff) / np.array([np.abs(similarity).sum(axis=1)]).T
elif type == 'item':
pred = rating.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')
評(píng)估
這里采用均方根誤差(RMSE)來(lái)度量預(yù)測(cè)評(píng)分的準(zhǔn)確性
可以使用sklearn的mean_square_error(MSE)函數(shù),其中RMSE僅僅是MSE的平方根。
from sklearn.metrics import mean_squared_error
from math import sqrt
def rmse(prediction, ground_truth):
prediction = prediction[ground_truth.nonzero()].flatten()
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))
User based CF RMSE: 3.12466203536
Item based CF RMSe: 3.45056350625
可以看出,基于內(nèi)存的算法很容易實(shí)現(xiàn)并產(chǎn)生合理的預(yù)測(cè)質(zhì)量。
基于模型的協(xié)同過(guò)濾
基于模型的協(xié)同過(guò)濾是基于矩陣分解(MF)的,矩陣分解廣泛應(yīng)用于推薦系統(tǒng)中,它比基于內(nèi)存的CF有更好的擴(kuò)展性和稀疏性。MF的目標(biāo)是從已知的評(píng)分中學(xué)習(xí)用戶的潛在喜好和產(chǎn)品的潛在屬性,隨后通過(guò)用戶和產(chǎn)品的潛在特征的點(diǎn)積來(lái)預(yù)測(cè)未知的評(píng)分。
計(jì)算MovieLens數(shù)據(jù)集的稀疏度:
sparsity = round(1.0 - len(df) / float(n_users*n_items),3)
print 'The sparsity level of MovieLen100K is ' + str(sparsity * 100) + '%'
The sparsity level of MovieLen100K is 93.7%
SVD
一般的方程可以表示為:
給定m * n矩陣X:
U 是一個(gè)(m * r)正交矩陣
S 是一個(gè)對(duì)角線上為非負(fù)實(shí)數(shù)的(r * r)對(duì)角矩陣
V^T是一個(gè)(r * n)正交矩陣
S的對(duì)角線上的元素被稱為X的奇異值。
陣X可以被分解成U,S和V。U矩陣表示對(duì)應(yīng)于隱藏特性空間中的用戶的特性矩陣,而V矩陣表示對(duì)應(yīng)于隱藏特性空間中的產(chǎn)品的特性矩陣。
現(xiàn)在,可以通過(guò)U, S和V^T的點(diǎn)積進(jìn)行預(yù)測(cè)了:
import scipy.sparse as sp
from scipy.sparse.linalg import svds
u, s, vt = svds(train_data_matrix, k = 20)
s_diag_matrix = np.diag(s)
x_pred = np.dot(np.dot(u,s_diag_matrix),vt)
print 'User-based CF MSE: ' + str(rmse(x_pred, test_data_matrix))
User-based CF MSE: 2.72035726617
總結(jié):
實(shí)現(xiàn)了簡(jiǎn)單的協(xié)同過(guò)濾方法,包括基于內(nèi)存的CF和基于模型的CF
基于內(nèi)存的模型是基于產(chǎn)品或用戶之間的相似性,這里采用余弦相似性。
基于模型的CD是基于矩陣分解,采用SVD來(lái)分解矩陣
標(biāo)準(zhǔn)的協(xié)同過(guò)濾方法在面對(duì)冷啟動(dòng)的情況時(shí)表現(xiàn)不佳。