推薦系統


本文結構:

  1. 推薦系統
  2. 常用方法
    1. 簡介
    2. 模型 cost, gradient 表達式
    3. 代碼實現
  3. 應用實例

參考:
Coursera-Andrew Ng 的 Machine Learning
Sirajology 的 Recommendation Systems - Learn Python for Data Science


1. 推薦系統

根據用戶的興趣特點和購買行為,向用戶推薦用戶感興趣的信息和商品。
為用戶節省時間,還能挖掘可能用戶自己都不知道的潛在興趣點。

生活中的例子:喜馬拉雅上根據我聽過的書推薦相關的內容,效果不錯,推薦的很多我都會訂閱。Youtube上根據我看過的視頻推薦內容,如果我在追劇,它會把最新的劇集放在我首頁,還有我可能感興趣的電影。

還有很多例子和方法,以及冷啟動等關鍵問題,推薦大家看《推薦系統實戰》這本書,之前去聽新浪微博的分享,這本書是他們推薦系統部門的必備材料。


2. 常用模型

方法有基于內容,基于物品,基于用戶和協同過濾等。

基于內容,物品,用戶的推薦就是把相關的特征表達為向量形式后,計算它們之間的距離,根據相似度高的來為你推薦。

基于內容,例如,要判斷文章間的相似性,就可以把每篇文章,按照它里面用到的單詞,把一篇文章表達為向量的形式,然后計算文章之間的距離。

如果我們有一個用戶對物品的打分矩陣,那么通過計算行向量間的距離,可以計算出物品之間的相似性,計算列向量的距離,可以得到用戶間的相似性。

根據距離的定義公式,計算出向量間的距離,找到最相近的幾個對象,再取平均值就可以作為預測值。

協同過濾

我們要解決一個推薦問題時,很自然的可以想到,用戶為什么喜歡這些物品,應該是因為這些物品具有某些特點,而用戶剛好對這些特點感興趣。

但具體是哪些特點呢,每個特點又該用什么值來量化呢,這一步可能并不好做。協同過濾就可以提供一個解決方案,即使你并不知道這些特點都有什么,即使你并不知道各個特點都是多少值,你仍然可以得到預測結果。

它的模型表示為,x\_i 為電影 i 具有的特點向量,y\_j 為用戶 j 對所有特點的偏好值向量。二者相乘就得到用戶 j 對電影 i 的打分,然后可以用來和實際分數進行比較:


  1. 寫出所有用戶對所有電影的預測分數與實際分數的平方差公式,再加上 theta 和 x 的正則項,就得到了目標函數,我們要使這個函數達到最小值。


  2. 然后計算 cost function 對 theta 和 x 的偏導:


  3. 接著可以用梯度下降法迭代求 cost function 的最小值。

最后可以用計算得到的 theta 和 x 相乘,得到打分矩陣中未知的部分。


3. 代碼實現

下面是用 matlab 實現的協同過濾。
其中的計算大多用矩陣表達,這樣比寫循環要快而且簡潔,代碼很簡單,也可以很容易地用python寫出來。

完整代碼鏈接

1. 引入數據

圖中可見用戶對電影的評分熱點圖。

%  Load data
load ('ex8_movies.mat');

imagesc(Y);
ylabel('Movies');
xlabel('Users');

2. 寫出 cost function 和 gradient 的表達式

其中 J 為 cost function,X\_grad,Theta\_grad 為相應的梯度。
先寫了沒有正則項的形式,在這基礎上又加上了正則項,當然可以合二為一直接寫帶有正則項的。

需要注意的是,在數據矩陣中,我們只將有打分的地方拿來計算,
所以在代碼中我們用 R 點乘誤差矩陣,這個 R 的意思,R(i, j) = 1 時,說明用戶 j 對電影 i 有評分,為 0 時就是沒有打分。

function [J, grad] = cofiCostFunc(params, Y, R, num_users, num_movies, ...
                                  num_features, lambda)

% Unfold the U and W matrices from params
X = reshape(params(1:num_movies*num_features), num_movies, num_features);
Theta = reshape(params(num_movies*num_features+1:end), ...
                num_users, num_features);

% ========= without Regularization
M = X*Theta'-Y;
Z = R.*M;   
J = 0.5 * sum( sum(Z.*Z) );

M_grad = X*Theta'-Y;
Z_grad = R.* M_grad;
X_grad = ( Z_grad )*Theta;   
Theta_grad = ( Z_grad )'*X; 

% ========= Regularization

Theta_regu = sum( sum(Theta.*Theta) )*lambda/2;
X_regu = sum( sum(X.*X) )*lambda/2;
J = J + Theta_regu + X_regu;

X_grad = X_grad + lambda*X;
Theta_grad = Theta_grad + lambda*Theta;


% Fold the U and W matrices to params
grad = [X_grad(:); Theta_grad(:)];

end

3.用隨機梯度下降來訓練模型

先隨機初始化 X 和 Theta。
定義 'MaxIter' 迭代次數和 'lambda' 正則項之后,
調用 'fmincg' 訓練模型,得到 cost function 達到最小時對應的 X 和 Theta。

% Set Initial Parameters (Theta, X)
X = randn(num_movies, num_features);
Theta = randn(num_users, num_features);

initial_parameters = [X(:); Theta(:)];

% Set options for fmincg
options = optimset('GradObj', 'on', 'MaxIter', 100);

% Set Regularization
lambda = 10;
theta = fmincg (@(t)(cofiCostFunc(t, Y, R, num_users, num_movies, ...
                                num_features, lambda)), ...
                initial_parameters, options);

% Unfold the returned theta back into U and W
X = reshape(theta(1:num_movies*num_features), num_movies, num_features);
Theta = reshape(theta(num_movies*num_features+1:end), ...
                num_users, num_features);

4.得到預測值

用 X * Theta' 得到預測值,其中第一列是目標用戶的向量。
再根據打分進行排序,輸出前10個推薦影片。

p = X * Theta';
my_predictions = p(:,1) + Ymean;

movieList = loadMovieList();

[r, ix] = sort(my_predictions, 'descend');
fprintf('\\nTop recommendations for you:\\n');
for i=1:10
    j = ix(i);
    fprintf('Predicting rating %.1f for movie %s\\n', my_predictions(j), ...
            movieList{j});
end

4. 用 python 的 lightFM 庫實現

完整代碼見 Sirajology 的 Github

今天會用到三個庫, Numpy, Scipy, LightFM。
先從 lightfm 這個庫里引入需要的數據,還有 LightFM 用來構建模型。

import numpy as np
from lightfm.datasets import fetch_movielens
from lightfm import LightFM

接下來導入數據。
它有1k的用戶對1700部電影的100k的評分數據。每個用戶對二十個左右的電影進行評分,分數由1到5,以字典的形式存儲。

#fetch data and format it
data = fetch_movielens(min_rating=4.0)

接下來建立模型
定義它的損失函數為 wrap,即給定一個用戶來找到與他相似的用戶,并且得到他們喜歡的電影的排序,用這個排序作為對新用戶的預測。用 fit 函數來訓練模型。

#create model
model = LightFM(loss='warp')
#train model
model.fit(data['train'], epochs=30, num_threads=2)

接下來就是建立推薦的函數 sample_recommendation

先從訓練數據中得到我們有多少個用戶和電影。
對每個用戶,取到他們已經喜歡的電影的前三個,
再取 model.predict 得到的預測值中,排名前三的 top_items。
打印出來可以觀察一下。

def sample_recommendation(model, data, user_ids):

    #number of users and movies in training data
    n_users, n_items = data['train'].shape

    #generate recommendations for each user we input
    for user_id in user_ids:

        #movies they already like
        known_positives = data['item_labels'][data['train'].tocsr()[user_id].indices]

        #movies our model predicts they will like
        scores = model.predict(user_id, np.arange(n_items))
        
        #rank them in order of most liked to least
        top_items = data['item_labels'][np.argsort(-scores)]

        #print out the results
        print("User %s" % user_id)
        print("     Known positives:")

        for x in known_positives[:3]:
            print("        %s" % x)

        print("     Recommended:")

        for x in top_items[:3]:
            print("        %s" % x)

在調用函數時只需要傳入 model,data 和 user_ids 即可。

sample_recommendation(model, data, [3, 25, 450])

歷史技術博文鏈接匯總

我是 不會停的蝸牛 Alice
85后全職主婦
喜歡人工智能,行動派
創造力,思考力,學習力提升修煉進行中
歡迎您的喜歡,關注和評論!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容