如何在 TensorFlow 部署推薦系統:建立模型 (一)

本文是系列教程中的第一部分,將向您展示如何在 Google Cloud Platform (GCP) 中使用 TensorFlowCloud ML Engine 來實踐機器學習 (ML) 的推薦系統。而這個部分會介紹如何在開發系統上安裝 TensorFlow 模型程式碼並在 MovieLens 的資料集上執行模型。

本教程的推薦系統使用加權交替最小平方 (WALS) 演算法,而 WALS 包含於 TensorFlow 程式庫中的 contrib.factorization package,採用大型矩陣因式分解對用戶和項目做評分。更多 WALS 相關的資訊,請參閱概述

文章詳細描述了模型的程式碼,包括資料資料的前置處理和在 TensorFlow 裡面執行 WALS 演算法。

該系列包括下列部分:

目標

  • 理解 TensorFlow 程式碼的結構,如何將 WALS 方法運用於矩陣因式分解。
  • 在本地端執行TensorFlow 範例程式碼,對MovieLens 資料集執行推薦程序。

成本

本教程使用的 Cloud StorageCloud ML Engine,兩者皆是可計費的服務,您可以使用 pricing calculator 來評估專案使用的成本。本教程預計專案成本為 0.15 美元,若您是新的GCP用戶,則可能有獲得免費試用的資格。

在您開始之前

  1. 選擇或建立 GCP 專案。
    GO TO THE MANAGE RESOURCES PAGE
  2. 請確認您已為專案啟用計費功能。
    瞭解如何啟用計費功能
  3. 啟用 Compute Engine 和 Cloud ML Engine APIs。
    ENABLE THE APIS
  4. (optional) 如果要在自己的電腦上執行此教程 (而不使用 Cloud Shell),請確保您的電腦已經安裝 Python 2.7

運行 TensorFlow 模型

至少具備 7G 記憶體的 Compute Engine instance,您才能執行下面這段步驟,或是您可以在本地端的 macOS 或 linux 上運行本節中的步驟,而這種情況下,您則不需要建立 Compute Engine instance。

建立 Compute Engine instance

  1. 在 Google Cloud Platform Console 中,前往 VM Instances 頁面。
    GO TO THE VM INSTANCES PAGE
  2. 點擊 Create instance.
  3. 根據您的喜好命名並選擇一個區域, 假如您沒有偏好的區域,請選擇一個地理位置與您較為靠近的區域。
  4. Machine Type 下拉式選單中,選擇 2 vCPUs 及類型 n1-standard-2。
  5. Access Scopes 的部分,選擇 Allow Full Access To All APIs.
  6. 點擊 Create.

安裝程式碼

  1. 連接到新的 instance。更多詳細的訊息,請參閱 Connecting to Instances
  1. 在新的 instance 中, 將範例程式碼複製下來:
git clone https://github.com/GoogleCloudPlatform/tensorflow-recommendation-wals
  1. 安裝 miniconda。範例程式碼需要用到 Python 2.7.
wget https://repo.continuum.io/miniconda/Miniconda2-latest-Linux-x86_64.sh
bash Miniconda2-latest-Linux-x86_64.sh
  1. 安裝 Python 和 TensorFlow。本教程假設 TensorFlow 的版本為 1.4.1。
cd tensorflow-recommendation-wals
conda create -n tfrec
conda install -n tfrec --file conda.txt
source activate tfrec
pip install -r requirements.txt
pip install tensorflow==1.4.1
  1. 在 Cloud Shell 當中,下載 MovieLens 資料集,這裡提供幾個版本的資料集。

a. 若是以開發為目的,這邊建議使用 100k 的版本,其中包含了來自 943 個用戶對於 1682 個項目的 100,000 評分。使用下列命令來下載它:

curl -O 'http://files.grouplens.org/datasets/movielens/ml-100k.zip'
unzip ml-100k.zip
mkdir -p data
cp ml-100k/u.data data/

b. 若是以訓練為目的,我們推薦 1m 的資料集,其中包含一百萬個評分等級,1m 和 100k 的格式有些許的不同。舉例來說,評分文件用:: 符號來區隔,範例程式碼允許您使用 –delimiter 參數指定資料集搭配分隔符號。

curl -O 'http://files.grouplens.org/datasets/movielens/ml-1m.zip'
unzip ml-1m.zip
mkdir -p data
cp ml-1m/ratings.dat data/

c. 您還可以在這個專案使用 20m 的資料集,這個資料集使用 CSV 檔。如果使用此資料集,則必須傳遞 –headers 旗標,因為該文件包含標題行。

curl -O 'http://files.grouplens.org/datasets/movielens/ml-20m.zip'
unzip ml-20m.zip
mkdir -p data
cp ml-20m/ratings.csv data/

理解模型的程式碼

模型的程式碼包含在 wals_ml_engine 目錄中,其更高階的功能可經由以下列檔案來部署:

mltrain.sh

  • 啟動各種類型 Cloud ML Engine 的作業。此 shell script 接受資料資料集檔案中的地區參數,分隔符號用於區分檔案內的值,和判斷資料檔案是否具有標頭行。最好的做法是建立自動配置和執行的 Cloud ML Engine 的作業腳本。

task.py

  • 解析 Cloud ML Engine 作業的參數並做訓練。

model.py

  • 加載資料集。
  • 根據資料建立兩個稀疏矩陣,一個用於訓練,另一個則用於測試。在評分的訓練稀疏矩陣上執行 WALS。

wals.py

  • 建立 WALS 的模型。
  • 執行 WALS 演算法。
  • 為了一組列或欄因素及評分矩陣,計算均方根誤差 (RMSE)。

模型如何對資料做預處理

將模型程式碼進行預處理以建立稀疏的評分矩陣並為矩陣因子分解做好準備。這牽涉到以下的步驟:

1. 模型程式碼從具分隔符號的文字檔中載入資料,每一行包括一個評分。

ratings_df = pd.read_csv(input_file,
                        sep=args['delimiter'],
                        names=headers,
                        header=header_row,
                        dtype={
                          'user_id': np.int32,
                          'item_id': np.int32,
                          'rating': np.float32,
                          'timestamp': np.int32,
                        })

2. 該程式碼為用戶和項目建立了一組唯一 ID (從 0 開始索引 0-indexed),唯一 ID 可保證跟稀疏評分矩陣中特定列和欄的索引相對應。

  • MovieLens 100k 資料使用 1-based IDs 其中最低的 index 為 1。為了正規化,程式碼會將每個 index 都減一。來自於 model.py
ratings = ratings_df.as_matrix(['user_id', 'item_id', 'rating'])
# deal with 1-based user indices
ratings[:,0] -= 1
ratings[:,1] -= 1
  • 1m 和 20m MovieLens 的資料集會跳過一些用戶和項目的 ID,這會產生一個問題:您必須將唯一的用戶ID集合對應到索引集合如同 [0 … num_users-1],並對項目 ID 做相同的動作。對應項目是使用 [numpy](http://www.numpy.org/) 的程式碼來完成的,程式碼產生一個陣列的大小 [0..max_item_id] 來執行對應,如果項目 ID 的最大值變得非常大,則此方法可能會占用太多的記憶體。
np_items = ratings_df.item_id.as_matrix()
unique_items = np.unique(np_items)
n_items = unique_items.shape[0]
max_item = unique_items[-1]

# map unique items down to an array 0..n_items-1
z = np.zeros(max_item+1, dtype=int)
z[unique_items] = np.arange(n_items)
i_r = z[np_items]
  • 對應用戶和項目的程式碼基本上是相同的。

    3. 模型程式碼隨機選擇一組評分測試集,預設的情況下有 10% 的評分將被選為測試集。接著這些評分會從訓練集當中被移除,並將用於評估用戶和項目因子預測的準確率。
test_set_size = len(ratings) / TEST_SET_RATIO
test_set_idx = np.random.choice(xrange(len(ratings)),
                               size=test_set_size, replace=False)
test_set_idx = sorted(test_set_idx)
ts_ratings = ratings[test_set_idx]
tr_ratings = np.delete(ratings, test_set_idx, axis=0)

4. 最後,程式碼以座標的形式 (coo_matrix) 來建立一個 scipy 的稀疏矩陣,其中包括了用戶和項目的索引及評分。coo_matrix 物件當作稀疏矩陣的封裝器,它還可用來驗證用戶和評分索引值,並檢查預處理中的錯誤。

u_tr, i_tr, r_tr = zip(*tr_ratings)
tr_sparse = coo_matrix((r_tr, (u_tr, i_tr)), shape=(n_users, n_items))

WALS 演算法如何應用於 TensorFlow

將資料進行預處理後,程式碼將傳送稀疏訓練矩陣到 TensorFlow 的 WALS 模型中,該模型將分解為列因子 X 和欄因子 Y。

執行模型的 TensorFlow 程式碼其實相當簡單,因為它是依賴於 TensorFlow 的 contrib.factorization_ops 模組中包含 WALSModel 類別。

  1. SparseTensor 物件已經初始化,其中用戶 ID 和項目 ID 當成索引,評分當作值。來自 wals.py 的內容如下:
input_tensor = tf.SparseTensor(indices=zip(data.row, data.col),
                               values=(data.data).astype(np.float32),
                               dense_shape=data.shape)

資料變數就是 coo_matrix 物件內的訓練評分,由預處理步驟中建立。

  1. 該模型被實例化:
model = factorization_ops.WALSModel(num_rows, num_cols, dim,
                                   unobserved_weight=unobs,
                                   regularization=reg,
                                   row_weights=row_wts,
                                   col_weights=col_wts)
  1. 列因子和欄因子的張量,由 WALSModel 類別自動產生及檢索,以便在矩陣因式分解後對其求值:
# retrieve the row and column factors
row_factor = model.row_factors[0]
col_factor = model.col_factors[0]
  1. 訓練過程使用 wals.py 中的 simple_train 方法,在 TensorFlow 的期間執行以下的循環:
row_update_op = model.update_row_factors(sp_input=input_tensor)[1]
col_update_op = model.update_col_factors(sp_input=input_tensor)[1]
sess.run(model.initialize_op)
sess.run(model.worker_init)
for _ in xrange(num_iterations):
   sess.run(model.row_update_prep_gramian_op)
   sess.run(model.initialize_row_update_op)
   sess.run(row_update_op)
   sess.run(model.col_update_prep_gramian_op)
   sess.run(model.initialize_col_update_op)
   sess.run(col_update_op)
  1. 在 num_iterations 迭代執行完成後,期間對於列和欄因子的張量求值,從而為每個因子產生 numpy 陣列:
# evaluate output factor matrices
output_row = row_factor.eval(session=session)
output_col = col_factor.eval(session=session)

這些因子陣列用於計算評分測試集的 RMSE,這兩個陣列也以 numpy 的格式儲存在輸出目錄上。

訓練模型

在這個情況下,訓練模型涉及將稀疏評分矩陣拆解成用戶因子矩陣 X 和項目因子矩陣  Y。被保存的用戶和項目因子可以用作推薦系統的基本模型。

該系統將用戶作為輸入,從 X 檢索該用戶的用戶向量,該向量乘以所有項目的 Y 向量,並且根據預測的評分傳送前 N 個項目。

本教程的第四部份詳細提供了使用訓練模型以進行預測的資訊,並說明如何將相關系統部屬在 GCP 上。

在本地端訓練模型

以開發為導向對於在本地端訓練模型是很有用的,他能讓您快速測試、更改程式碼,包含設中斷點以便於容易除錯。要在 Cloud Shell 或本地系統上運行,請使用選項 local 並從程式碼目錄運行 mltrain.sh

  • 對於 MovieLens 100k 的資料集,請指定 100k 資料檔案的路徑:
./mltrain.sh local ./../data/u.data
  • 對於 MovieLens 1m 的資料集,請包含 –delimiter 的選項並指定 1m 資料檔案的路徑:
./mltrain.sh local data/ratings.dat --delimiter ::
  • 對於 MovieLens 20m 的資料集,請使用 –delimiter–header 的選項
./mltrain.sh local data/ratings.csv --header --delimiter ,
 

訓練作業的輸出顯示了測試集計算得到的 RMSE。對於 1m 的資料集,使用程式碼中預設的超參數,應該輸出如下的結果:

INFO:tensorflow:Train Start: 
...
INFO:tensorflow:Train Finish: 
INFO:tensorflow:train RMSE = 1.06
INFO:tensorflow:test RMSE = 1.11
 

有關更多詳細信息,請參閱第二部分

RMSE 對應於預測評分與測試集相比的平均誤差。平均而言,算法生成的每個評分均在 1m 資料集的測試集中的實際用戶評分的 ±1.11 範圍內。WALS 演算法使用調校過的超參數執行效能會更好,就如本系列第二部分所示。

清除

如果您建立了 Compute Engine instance 執行個體用於 TensorFlow,則必須停止該 instance,以避免對您的 GCP 帳號產生費用。此 instance 並不會在第二部分中使用,但第三部分第四部份會需要 Compute Engine instance。

停止 Compute Engine instance

  1. 在 GCP Console 中打開 Compute Engine VM Instance 列表頁面。
  2. 選擇 instance 名稱。
  3. 點擊停止並確認操作。

刪除專案

避免支付費用最簡單的的方法就是刪除為本教程所建立的專案。

警告:刪除專案會發生以下結果:
1. 如果您使用現有的專案,您還需要刪除其他在專案中完成的工作。
2. 您無法重新使用已刪除的專案 ID,如果您建立打算將來繼續使用的自定義專案 ID,請改為刪除項目內的資源。此步驟可以確保使用專案 ID 的 URL,如 appspot.com URL 仍然可以使用。

如果您正在探索多個教程和快速入門,重複使用而不刪除他們可以避免您超出項目配額的限制。

  1. 在 GCP Console 中,前往 Projects 頁面。

GO TO THE PROJECTS PAGE

  1. 在專案列裡選擇要刪除的項目,然後點擊刪除專案。
  2. 在對話框中輸入專案的 ID 然後點擊關閉以刪除專案。

下一步

(原文翻譯自 Google Cloud。)

 

回到頂端