2018-04-26
1.協同過濾
協同過濾(Collaborative Filtering)字面上的解釋就是在別人的幫助下來過濾篩選,協同過濾一般是在海量的用戶中發現一小部分和你品味比較相近的,在協同過濾中,這些用戶稱為鄰居,然后根據他們喜歡的東西組織成一個排序的目錄來推薦給你。問題的重點就是怎樣去尋找和你比較相似的用戶,怎么將那些鄰居的喜好組織成一個排序的目錄給你,要實現一個協同過濾的系統,需要以下幾個步驟:
1.收集用戶的愛好
2.找到相似的用戶或者物品
3.計算推薦
在收集用戶的喜歡方面,一般的方式有評分,投票,轉發,保存書簽,點擊連接等等 ,有了用戶的喜好之后,就可以通過這些共同的喜好來計算用戶之間的相似度了。
2.相似度計算
相似度的計算一般是基於向量的,可以將一個用戶對所有的物品的偏好作為一個向量來計算用戶之間的相似度,或者將所有用戶對於某一個物品的偏好作為一個向量計算物品之間的相似度,相似度的計算有下列幾種方式:
計算歐幾里得距離:
利用歐幾里得距離計算相似度時,將相似度定義如下:
皮爾遜相關系數:
其中sx,sy表示x和y的標准差。
Cosine相似度:
Tanimoto系數,也稱作Jaccard系數:
基於用戶的協同過濾:不考慮用戶、物品的屬性(特征)信息,它主要是根據用戶對物品的偏好(Preference)信息,發掘不同用戶之間口味(Taste)的相似性,使用這種用戶相似性來進行個性化推薦。基於用戶的協同過濾推薦,它是以用戶為中心,觀察與該用戶興趣相似的一個用戶的群體,將這個興趣相似的用戶群體所感興趣的其他物品,推薦給該用戶。
基於物品的協同過濾:也不考慮用戶、物品的屬性(特征)信息,它也是根據用戶對物品的偏好(Preference)信息,發掘不同物品之間的相似性。基於物品的協同過濾推薦,是以物品為中心,通過觀察用戶對物品的偏好行為,將相似的物品計算出來,可以認為這些相似的物品屬於特定的一組類別,然后根據某個用戶的歷史興趣計算其所屬的類別,然后看該類別是否屬於這些成組類別中的一個,最后將屬於成組類別所對應的物品推薦給該用戶。
基於模型的協同過濾:基於模型的協同過濾推薦,是采用機器學習的方法,通過離線計算實現推薦的,通常它會首先根據歷史數據,將數據集分成訓練集和測試集兩個數據集,使用訓練集進行訓練生成推薦模型,然后將推薦模型應用到測試集上,評估模型的優劣,如果模型到達實際所需要的精度,最后可以使用訓練得到的推薦模型進行推薦(預測)。可見,這種方法使用離線的歷史數據,進行模型訓練和評估,需要耗費較長的時間,依賴於實際的數據集規模、機器學習算法計算復雜度。
對於我們的用戶歷史行為數據,我們關注的是用戶與物品之間的關系,可以生成一個用戶-物品矩陣,矩陣元素表示用戶對物品的偏好行為(或者是評分),如果用戶和物品的數量都很大,那么可見這是一個超大稀疏矩陣,因為並不是每用戶都對所有的物品感興趣,只有每個用戶感興趣的物品才會有存在對應的偏好值,沒有感興趣的都為空缺值,最終我們是要從這些空缺值對應的物品中選擇出一些用戶可能會感興趣的,推薦給用戶,這樣才能實現個性化推薦。
下面,我們假設使用用戶-物品的評分矩陣來實現推薦,定義評分矩陣為R,那么通過將R分解為另外兩個低秩矩陣U和M,由於R是稀疏矩陣,很多位置沒有值,只能通過計算使用U與M的乘積近似矩陣R,那么只要最終得到的誤差盡量小,能滿足實際需要即可,一般使用均方根誤差(Root mean square error,RMSE)來衡量,那么就要計算一個目標函數的最小值,如下函數公式所示:
√[∑di^2/n]=Re,式中:n為測量次數;di為一組測量值與真值的偏差。
具體代碼如下:

# -*- coding: utf-8 -*- """ Spyder Editor This is a temporary script file. """ import os import codecs import numpy as np import pandas as pd from sklearn import cross_validation as cv from sklearn.metrics.pairwise import pairwise_distances #計算余弦相似性 os.getcwd() os.chdir("C:/Users/admin/Desktop/ml-100k/ml-100k") f=codecs.open('u.data', 'r') print (f.read()) header = ['user_id', 'item_id', 'rating', 'timestamp'] df = pd.read_csv('u.data', sep='\t', names=header) #計算唯一用戶和電影的數量 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) ) #使用scikit-learn庫將數據集分割成測試和訓練,根據測試樣本的比例(test_size),取0.25 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_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] #計算余弦相似性,輸出從0到1,因為打分都是正的 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') #評估 使用均方根誤差(RMSE) 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))) #基於模型的協同過濾 #計算稀疏度 sparsity=round(1.0-len(df)/float(n_users*n_items),3) print ('The sparsity level of MovieLens100K is ' + str(sparsity*100) + '%') import scipy.sparse as sp from scipy.sparse.linalg import svds #get SVD components from train matrix. Choose k. 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)))
參考地址:http://python.jobbole.com/85516/