TensorFlow でのレコメンデーション: モデルの作成

この記事は、Google Cloud Platform(GCP)で TensorFlow 1.xAI Platform を使用して機械学習(ML)レコメンデーション システムを実装する方法を説明する、マルチパート チュートリアル シリーズのパート 1 です。このパートでは、TensorFlow モデルコードを開発システムにインストールし、MovieLens データセットでモデルを実行する方法について説明します。

このチュートリアルで説明するレコメンデーション システムは、加重交互最小二乗(WALS)アルゴリズムを使用します。WALS は、TensorFlow 1.x コードベースの contrib.factorization パッケージに含まれており、ユーザーとアイテムの評価に関する大規模な行列を因数分解するために使用されます。WALS の詳細については、概要をご覧ください。

この記事では、データの前処理や TensorFlow での WALS アルゴリズムの実行など、モデルのコードについて詳しく説明します。

シリーズは次のパートで構成されています。

目標

  • 行列因子分解に WALS を適用するために使用される TensorFlow コードの構造を理解する。
  • サンプルの TensorFlow コードをローカルで実行して、MovieLens データセットに対してレコメンデーションを実行する。

費用

このチュートリアルで使用する Cloud StorageAI Platform は、どちらも有料サービスです。料金計算ツールを使うと、予想使用量に基づいて料金を見積もることができます。このチュートリアルの予想料金は $0.15 です。GCP を初めて使用する方は、無料トライアルをご利用いただけます。

始める前に

  1. Cloud Console のプロジェクト セレクタページで、Cloud プロジェクトを選択または作成します。

    プロジェクト セレクタのページに移動

  2. Google Cloud プロジェクトに対して課金が有効になっていることを確認します。プロジェクトに対して課金が有効になっていることを確認する方法を学習する

  3. Compute Engine and AI Platform API を有効にします。

    API を有効にする

TensorFlow モデルの実行

このセクションのステップは、次の手順で説明するように、少なくとも 7G のメモリを搭載した Compute Engine インスタンスで実施できます。また、このセクションのステップは、ローカルの macOS または Linux システムでも実施できます。この場合、Compute Engine インスタンスを作成する必要はありません。

Compute Engine インスタンスの作成

  1. Google Cloud Console で、[VM インスタンス] ページに移動します。

    [VM インスタンス] ページに移動

  2. [インスタンスを作成] をクリックします。

  3. インスタンスに任意の名前を付け、ゾーンを選択します。優先するゾーンがまだない場合は、地理的に近いゾーンを選択します。

  4. [マシンタイプ] プルダウン リストで、[n1-standard-2] の [2 vCPUs] を選択します。

  5. [アクセス スコープ] セクションで、[すべての API に完全アクセス権を許可] を選択します。

  6. [作成] をクリックします。

コードのインストール

  1. [VM インスタンス] ページに移動します。

    [VM インスタンス] ページに移動

  2. インスタンスの行で、[SSH] をクリックしてブラウザベースのターミナルを開きます。このターミナルはインスタンスに安全に接続されます。

  3. 新しいターミナル ウィンドウで、インスタンスのソフトウェア リポジトリを更新します。

    sudo apt-get update
    
  4. gitbzip2unzip をインストールします。

    sudo apt-get install -y git bzip2 unzip
    
  5. サンプルコード リポジトリのクローンを作成します。

    git clone https://github.com/GoogleCloudPlatform/tensorflow-recommendation-wals
  6. miniconda をインストールします。サンプルコードには Python 2.7 が必要です。

    wget https://repo.continuum.io/miniconda/Miniconda2-latest-Linux-x86_64.sh
    bash Miniconda2-latest-Linux-x86_64.sh
    export PATH="/home/$USER/miniconda2/bin:$PATH"
  7. Python パッケージと TensorFlow をインストールします。このチュートリアルは TensorFlow の 1.x バージョンで実施します。

    cd tensorflow-recommendation-wals
    conda create -y -n tfrec
    conda install -y -n tfrec --file conda.txt
    source activate tfrec
    pip install -r requirements.txt
    pip install tensorflow==1.15
  8. MovieLens データセットをダウンロードします。データセットにはいくつかのバージョンがあります。

    • 目的が開発の場合、100k バージョンをおすすめします。このバージョンには、943 名のユーザーから得た 1,682 のアイテムに関する 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/
    • 目的がトレーニングの場合、1m データセットをおすすめします。これには、100 万件の評価が含まれます。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/
    • このプロジェクトでは 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
+ AI Platform のさまざまなタイプのジョブを起動します。このシェル スクリプトは、データセット ファイルの場所の引数、ファイル内の値を区切るための区切り文字の引数、データファイルにヘッダー行があるかどうかを示す引数を受け取ります。ベスト プラクティスとして、AI Platform のジョブを自動的に構成して実行するスクリプトを作成することをおすすめします。
task.py
+ AI Platform のジョブの引数を解析し、トレーニングを実行します。
model.py
+ データセットを読み込みます。
  • データから、トレーニング用とテスト用の 2 つの疎行列を作成します。評価のトレーニング疎行列に対して WALS を実行します。
wals.py
+ WALS モデルを作成します。
  • WALS アルゴリズムを実行します。
  • 一連の行因子 / 列因子と評価の行列に対して二乗平均平方根誤差(RMSE)を計算します。

モデルによるデータの前処理の方法

モデルコードは、データの前処理によって評価の疎行列を作成し、それを行列因子分解のために準備します。これには、次の手順が含まれます。

  1. モデルコードにより、区切り文字形式のテキスト ファイルからデータが読み込まれます。各行に 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 でインデックス付けされます。これにより、評価の疎行列の特定の行および列のインデックスに一意の ID が確実に対応します。

    • MovieLens 100k データでは、1 をベースとした ID が使用されます。これらの ID では、固有セットの最も小さいインデックスが 1 となります。正規化するために、コードにより各インデックスから 1 が減算されます。以下は 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 の一部がスキップされます。これにより問題が発生するため、一連の一意のユーザー 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))

TensorFlow での WALS アルゴリズムの実装方法

データが前処理された後、コードにより、トレーニングの疎行列が 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 オブジェクトです。

  2. モデルがインスタンス化されます。

    model = factorization_ops.WALSModel(num_rows, num_cols, dim,
                                        unobserved_weight=unobs,
                                        regularization=reg,
                                        row_weights=row_wts,
                                        col_weights=col_wts)
  3. 行因子テンソルと列因子テンソルが WALSModel クラスによって自動的に作成され、取得されます。このため、これらは行列を因子分解した後に評価できます。

    # retrieve the row and column factors
    row_factor = model.row_factors[0]
    col_factor = model.col_factors[0]
  4. トレーニング プロセスにより、wals.pysimple_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)
  5. num_iterations が反復実行された後、行因子テンソルと列因子テンソルがセッション内で評価され、各因子に対して numpy 配列が生成されます。

    # evaluate output factor matrices
    output_row = row_factor.eval(session=session)
    output_col = col_factor.eval(session=session)

これらの因子配列は、評価のテストセットに対して RMSE を計算するために使用されます。また、これらの 2 つの配列は numpy 形式で出力ディレクトリに保存されます。

モデルのトレーニング

ここでのモデルのトレーニングには、評価の疎行列をユーザー因子行列 X とアイテム因子行列 Y に因子分解する処理が伴います。保存されたユーザー因子とアイテム因子は、レコメンデーション システムのベースモデルとして使用できます。

このシステムは、ユーザーを入力として受け取り、そのユーザーのユーザー因子のベクトルを X から取得します。そのベクトルにすべてのアイテム因子 Y を乗算してから、予測された評価に従って上位 N 個のアイテムを返します。

このチュートリアルのパート 4 では、トレーニング済みモデルを使用して予測を行うシステムについて詳しく説明し、さらにそのようなシステムを GCP にデプロイする方法についても説明します。

ローカルでのモデルのトレーニング

目的が開発の場合、モデルをローカルでトレーニングすると効率的です。これにより、コードの変更を迅速にテストし、デバッグを容易にするブレークポイントを追加できます。Cloud Shell で、またはローカル システムからモデルを実行するには、wals_ml_engine ディレクトリから、local オプションを使用して mltrain.sh スクリプトを実行します。

cd wals_ml_engine
  • MovieLens 100k データセットの場合、100k データファイルのパスを指定します。

    ./mltrain.sh local ../data u.data
  • MovieLens 1m データセットの場合、--delimiter オプションを使用し、1m データファイルのパスを指定します。

    ./mltrain.sh local ../data ratings.dat --delimiter ::
  • MovieLens 20m データセットの場合、--delimiter オプションと --headers オプションを使用します。

    ./mltrain.sh local ../data ratings.csv --headers --delimiter ,

トレーニング ジョブの出力には、テストセットで計算された RMSE が表示されます。1m データセットの場合、ソースコードで指定されたデフォルトのハイパーパラメータを使用すると、出力は次のようになります。

INFO:tensorflow:Train Start: <timestamp>
...
INFO:tensorflow:Train Finish: <timestamp>
INFO:tensorflow:train RMSE = 1.29
INFO:tensorflow:test RMSE = 1.34

詳細については、パート 2 をご覧ください。

RMSE は、テストセットと比較した場合の予測評価の平均誤差に対応します。平均して、アルゴリズムによって生成された各評価は、1m データセットのテストセットにおける実際のユーザー評価の ± 1.29 以内です。このシリーズのパート 2 で説明するように、これよりもパフォーマンスに優れている WALS アルゴリズムでは、調整されたハイパーパラメータを使用します。

クリーンアップ

TensorFlow を実行するための Compute Engine インスタンスを作成した場合は、GCP アカウントへの課金が発生しないようにするために、そのインスタンスを停止する必要があります。このインスタンスは、このシリーズのパート 2 で使用しません。ただし、パート 3パート 4 では、Compute Engine インスタンスが必要になります。

Compute Engine インスタンスの停止

  1. Cloud Console で、[Compute Engine VM インスタンス] リストページを開きます。
  2. インスタンス名を選択します。
  3. [停止] をクリックし、このオペレーションを確認します。

プロジェクトの削除

課金をなくす最も簡単な方法は、チュートリアル用に作成したプロジェクトを削除することです。

プロジェクトを削除するには:

  1. Cloud Console で [リソースの管理] ページに移動します。

    [リソースの管理] ページに移動

  2. プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
  3. ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。

次のステップ