【翻譯】微軟SAR——推薦系統實踐筆記

        【翻譯搬磚】本文爲微軟推薦系統實踐項目的SAR樣例的翻譯文檔(譯者: Funco),原文地址: SAR Single Node on MovieLens

        github: microsoft/recommenders 是微軟開源的推薦系統最佳實踐案例,提供了大量的推薦系統算法講解以及具體案例,並封裝了一些在推薦系統中通用的方法以供我們在以後的實踐中借鑑。這些特點,在當前文檔中都用體現,歡迎交流,共同學習。

        文中括號"()"包含一個數字,表示批註,每一段文本中的批註都會在該段文本後面補充具體內容,可能是名詞解釋,也可能是筆者關於不確定的翻譯結果給出原文參照。此外,原文中有大量出現 Item 這個單詞,我翻譯爲了 物品,但實際上,某些時候,翻譯爲 數據項 會使語句更通順,不這麼做的原因是,我希望規範名詞——參照基於物品的協同過濾 (ItemCF,Item-based Collaborative Filtering)

正文

SAR Single Node on MovieLens (Python, CPU)

        SAR(Simple Algorithm for Recommendation) 一種快速、可擴展的基於用戶操作記錄的個性化推薦算法,快速且可擴展。它能給出易於理解且可解釋的推薦結果,同時還處理了『冷啓動』問題**(1)。SAR是一種近鄰算法(在阿加沃爾的《推薦系統》中有提及(2)),用於爲每一位用戶找到最需要的物品。關於SAR的更多細節,可以參閱 deep dive notebook(3)**。

  1. It produces easily explainable and interpretable recommendations and handles “cold item” and “semi-cold user” scenarios.
  2. 阿加沃爾(Aggarwal)所著的**《推薦系統原理與實踐》(《Recommender Systems: The Textbook》)是推薦系統領域被廣泛推崇的書籍之一。其書關於近鄰算法**,尤其是近鄰協同過濾算法有大量描述。

SAR的優點

  1. 易於訓練和部署且高精度
  2. 快速訓練——只需要簡單的計算即可獲得預測矩陣
  3. 快速預測評分——只需要將相似度矩陣與評分向量作乘法運算

可以簡單的理解,SAR的優點就是從各方面來講都簡單快速,但又具備相當精度

如何正確使用SAR

  1. 不關注用戶特徵或物品特徵

    它在需要用戶特徵或物品特徵的場景下表現較差**(1)**

  2. 佔用內存高

    它需要創建多個稀疏矩陣**(2)。大多數基於矩陣分解的算法也存在該問題(3)**

  3. 它傾向於一個隱含的評分方案,但不預測評分**(4)**

  1. SAR本質是近鄰協同過濾
  2. 原文說的是會創建MxM的稀疏矩陣(M是物品數量),這只是相似度矩陣,其實我們還需要用戶-物品評分矩陣,經過一系列運算,還會產生作爲中間產物的矩陣
  3. 矩陣分解,常見如奇異值分解(SVD)特徵分解
  4. 這句話我也不曉得怎麼理解,原文是:it favors an implicit rating scenario and it does not predict ratings.

0 設置與依賴

# set the environment path to find Recommenders
import sys
sys.path.append("../../")

import logging
import numpy as np
import pandas as pd
import papermill as pm

from reco_utils.common.timer import Timer
from reco_utils.dataset import movielens
from reco_utils.dataset.python_splitters import python_stratified_split
from reco_utils.evaluation.python_evaluation import map_at_k, ndcg_at_k, precision_at_k, recall_at_k
from reco_utils.recommender.sar import SAR

print("System version: {}".format(sys.version))
print("Pandas version: {}".format(pd.__version__))

注意

  1. reco_utils 是原文所在項目的包,無法通過 pip install 獲取。

1 數據準備

        在這裏,SAR處理符合下述數據結構的數據:<User ID>, <Item ID>,<Time>,[<Event Type>], [<Event Weight>] (1)

        每行記錄表示一個用戶關於一件物品的一次操作記錄。該記錄可能是電商網站中不同類型的事件,比如:點擊瀏覽物品;添加至購物車;分享鏈接;諸如此類。每個事件我們可以指定不同的權值,比如:我們可以將『購買』定義爲10,而『查看詳情』可以定義爲1 (2)

        數據集 MovieLens 是 用戶對電影評分 的格式化數據,後續的示例代碼中都會使用該數據集展示算法過程。

  1. SAR is intended to be used on interactions with the following schema: , ,
  2. 這是 隱式評分 的案例,我只是想在這裏強調及時收集並記錄隱式評分對系統未來可能需要的分析很重要。

1.1 加載電影數據

# MOVIELENS_DATA_SIZE = '100k'
data = movielens.load_pandas_df(
    size=MOVIELENS_DATA_SIZE,
    local_cache_path=sys.path[1] + '/downloads/'
)

# Convert the float precision to 32-bit in order to reduce memory consumption 
data['rating'] = data['rating'].astype(np.float32)

data.head()

print(data)

輸出結果

       userID  itemID  rating  timestamp
0         196     242     3.0  881250949
1         186     302     3.0  891717742
2          22     377     1.0  878887116
3         244      51     2.0  880606923
4         166     346     1.0  886397596
...       ...     ...     ...        ...
99995     880     476     3.0  880175444
99996     716     204     5.0  879795543
99997     276    1090     1.0  874795795
99998      13     225     2.0  882399156
99999      12     203     3.0  879959583

[100000 rows x 4 columns]

注意

movielensreco_utils 的一個子包,**movielens.load_pandas_df() **會在每次被調用時都下載一部分資源到臨時文件夾中。建議是

  1. 在項目 recommenders 目錄下創建 downloads 文件夾

  2. 從 http://files.grouplens.org/datasets/movielens/ 下載對應zip

    如果movielens.load_pandas_df() 的參數 size‘100k’,則下載 ml-100k.zip 至剛剛創建的 downloads 文件夾中

  3. 指明參數 local_cache_path 爲剛剛創建的 downloads 文件夾

1.2 拆分數據

基於公共庫的python隨機拆分器拆分數據

我們將所有數據拆分爲測試集 (test set) 和訓練集 (train set),且在訓練過程中保留集**(1)**不可見,從而衡量算法性能。SAR基於用戶喜好生成推薦結果,因此,所有測試數據集中的用戶必須存在於訓練數據集中。爲了解決這一問題,我們可以使用函數 python_stratified_split (2) 來決定每個用戶保留數據在原數據中的佔比(比如,取25%作爲保留集),以此確保用戶同時存在於訓練集與測試集中。此外,dataset.python_splitters 中還提供了更多的數據切分方式。

  1. 保留集**(hold-out set):也稱爲保留數據(hold-out data)**,這裏描述的其實是一種模型評估方法——留出法(hold-out),在這裏,保留集即爲測試集
  2. python_stratified_splitdataset.python_splitters 只是微軟推薦系統項目中,reco_utils中實現的數據切分工具,提供了多種數據切分方式,無法通過 pip install 獲取。此外,python_stratified_split 主要通過 pandas.DataFrame.sample 實現隨機取樣。
train, test = python_stratified_split(data, ratio=0.75, col_user='userID', col_item='itemID', seed=42)

print("""
Train:
Total Ratings: {train_total}
Unique Users: {train_users}
Unique Items: {train_items}

Test:
Total Ratings: {test_total}
Unique Users: {test_users}
Unique Items: {test_items}
""".format(
    train_total=len(train),
    train_users=len(train['userID'].unique()),
    train_items=len(train['itemID'].unique()),
    test_total=len(test),
    test_users=len(test['userID'].unique()),
    test_items=len(test['itemID'].unique()),
))

輸出結果

Train:
Total Ratings: 74992
Unique Users: 943
Unique Items: 1649

Test:
Total Ratings: 25008
Unique Users: 943
Unique Items: 1444

2 訓練SAR模型

2.1 SAR算法實例與索引配置

        我們將單節點執行SAR,並配置每個索引列在數據集中對應的列名(實際使用中,其中的 timestamp 字段可以視具體情況忽略或保留)。

        更多參數詳情請參閱:deep dive notebook

logging.basicConfig(level=logging.DEBUG, 
                    format='%(asctime)s %(levelname)-8s %(message)s')

model = SAR(
    col_user="userID",
    col_item="itemID",
    col_rating="rating",
    col_timestamp="timestamp",
    similarity_type="jaccard", 
    time_decay_coefficient=30, 
    timedecay_formula=True
)
  1. 雅卡爾係數(jaccard):在上述代碼中,函數 SAR 的參數 similarity_type 被設置爲 jaccard,這是雅卡爾係數,可用於比較兩個集合的相似性。基本方法是兩集合交集大小與並集大小之比。具體參閱:百度百科 - Jaccard係數

2.2 通過訓練集訓練SAR模型並從我們的測試集獲取top-k個測試結果

        SAR首先計算物品-物品共現矩陣 (Co-occurence matrix),其中的元素表示兩個物品在任意用戶的評分記錄中共同出現的記錄。之後,我們就可以通過某種度量重新調整共現矩陣來計算物品相似度矩陣(在下面的代碼中,使用 Jaccard係數作爲相似度度量)。

        我們還可以通過一個喜好矩陣來獲取用戶-物品關聯性**(1)**。喜好程度由不同類型和發生時間的事件決定(比如用戶對電影的評分或是否瀏覽該電影)。

        我們通過喜好矩陣 A 與相似矩陣 S 相乘,可以得到一個預測分矩陣(推薦值矩陣) R。之後,我們再通過下述案例代碼中的函數 recommend_k_items,可以獲取每位用戶的top-k個最值得推薦的結果。

        從這裏我們可以找到對SAR算法的完整介紹:https://github.com/microsoft/recommenders/blob/de3782cce370a446e14e6b47e87686867fb7e069/notebooks/02_model/sar_deep_dive.ipynb

  1. 按我理解,這個關聯矩陣,即爲評價矩陣,不同於數據集,關聯矩陣用行-列表示用戶-物品。
with Timer() as train_time:
    model.fit(train)

print("Took {} seconds for training.".format(train_time.interval))

執行結果

2020-02-18 20:55:32,377 INFO     Collecting user affinity matrix
2020-02-18 20:55:32,392 INFO     Calculating time-decayed affinities
2020-02-18 20:55:32,463 INFO     Creating index columns
2020-02-18 20:55:32,587 INFO     Building user affinity sparse matrix
2020-02-18 20:55:32,595 INFO     Calculating item co-occurrence
2020-02-18 20:55:32,788 INFO     Calculating item similarity
2020-02-18 20:55:32,789 INFO     Using jaccard based similarity
2020-02-18 20:55:32,891 INFO     Done training

Took 0.5264033849998668 seconds for training.
with Timer() as test_time:
    top_k = model.recommend_k_items(test, remove_seen=True)

print("Took {} seconds for prediction.".format(test_time.interval))

執行結果

2020-02-18 20:55:35,758 INFO     Calculating recommendation scores
2020-02-18 20:55:35,851 INFO     Removing seen items

Took 0.14565897600004973 seconds for prediction.
top_k.head()

執行結果

userID itemID predictio
0 1 204 3.313306
1 1 89 3.280465
2 1 11 3.233867
3 1 367 3.192575
4 1 423 3.131517

2.3 評價SAR性能

        我們通過模塊 reco_utils.python_evaluation ,從一些常見的指標來評價SAR的性能,包括:平均精度 (MAP)、歸一化貼現累積增益 (NDCG)、精度和我們用SAR計算的每個用戶的top-k項的召回率 (1)。每個評分方法都需要指明數據集中具體的用戶id、物品id和評分的列名。

  1. 需要注意,精度 (Precision) 與 準確率 (Accuracy) 是有區別的。
eval_map = map_at_k(test, top_k, col_user='userID', col_item='itemID', col_rating='rating', k=TOP_K)
eval_ndcg = ndcg_at_k(test, top_k, col_user='userID', col_item='itemID', col_rating='rating', k=TOP_K)
eval_precision = precision_at_k(test, top_k, col_user='userID', col_item='itemID', col_rating='rating', k=TOP_K)
eval_recall = recall_at_k(test, top_k, col_user='userID', col_item='itemID', col_rating='rating', k=TOP_K)

print("Model:\t",
      "Top K:\t%d" % TOP_K,
      "MAP:\t%f" % eval_map,
      "NDCG:\t%f" % eval_ndcg,
      "Precision@K:\t%f" % eval_precision,
      "Recall@K:\t%f" % eval_recall, sep='\n')

執行結果

Model:	
Top K:	10
MAP:	0.110591
NDCG:	0.382461
Precision@K:	0.330753
Recall@K:	0.176385
# Now let's look at the results for a specific user
user_id = 876

ground_truth = test[test['userID']==user_id].sort_values(by='rating', ascending=False)[:TOP_K]
prediction = model.recommend_k_items(pd.DataFrame(dict(userID=[user_id])), remove_seen=True) 
pd.merge(ground_truth, prediction, on=['userID', 'itemID'], how='left')

執行結果

2020-02-18 20:55:40,935 INFO     Calculating recommendation scores
2020-02-18 20:55:40,951 INFO     Removing seen items
userId itemId rating timestamp predition
0 876 523 5.0 879428378 NaN
1 876 529 4.0 879428451 NaN
2 876 174 4.0 879428378 0.353567
3 876 276 4.0 879428354 NaN
4 876 288 3.0 879428101 NaN

        以上,我們看到,前面通過模型top-k推薦得到的測試集的最高評分物品被調整,而其他物品未變。離線評估算法很困難,因爲此時測試集僅包含歷史記錄,也不能代表用戶在所有物品範圍內的實際偏好。對於這一問題,我們也可以通過其他算法 (1) 或使用 hyper-parameters (2) 進行優化。

  1. 實際應用中,多爲混合集成的推薦系統,比如 UserCF + ItemCF、ItemCF + Content-based 或其他一些獨特的優化方式。關於這一部分,在 Aggarwal 的《推薦系統》中有詳細描述。
  2. hyper-parameters:是機器學習 (ML, Machine Learning) 的概念,在 ML 中,已經設置的參數稱爲 hyper-parameters。按照我個人理解,這裏原文想說的是通過 對增量數據進行機器學習 來持續優化模型。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章