BigQuery ML を使用して e コマースのレコメンデーション システムを構築する

BigQuery ML を使用して BigQuery の顧客データからプロダクトやサービスのレコメンデーションを生成することで、レコメンデーション システムを構築する方法を学習します。次に、そのデータを他の本番環境システムで使用できるようにする方法(Google アナリティクス 360 や Cloud Storage へのエクスポート、BigQuery テーブルからのプログラムによる読み取りなど)を学習します。

この方法は、すでに BigQuery にデータがある場合に推奨されます。機械学習モデルの作成、BigQuery の顧客データによるトレーニング、デプロイはすべて、標準 SQL クエリを使用して、BigQuery ML で行えます。データを別のプロダクトにエクスポートする必要も、モデルのトレーニングとデプロイのパイプラインを構築する必要もありません。また、必要なコンピューティング リソースを処理できるように、BigQuery は自動スケーリングされます。

このチュートリアルで作成する機械学習モデルは、行列分解を使用します。これは、ユーザーの好みのデータに基づいてレコメンデーション システムを作成する場合の、一般的で効果的な方法です。このアプローチの詳細については、行列分解をご覧ください。

このチュートリアルでは、BigQuery でホストされ、一般公開されている Google アナリティクスのサンプル データセットを使用します。このデータセットは、Google ブランドの商品を販売する実在の e コマースストア、Google Merchandise Store の 12 か月間(2016 年 8 月から 2017 年 8 月)の難読化されたアナリティクス 360 データを提供します。

目標

  • サンプルデータを、行列分解モデルのトレーニングに適した形式に変換する。
  • 行列分解モデルを作成、トレーニング、デプロイする。
  • デプロイされたモデルから、お客様の関心が最も高い商品に関する予測を取得する。
  • BigQuery から 1 つ以上の他のプロダクトに予測データをエクスポートし、これを使用してお客様にレコメンデーションする。

料金

このチュートリアルでは、課金対象である次の Google Cloud コンポーネントを使用します。

  • BigQuery
  • BigQuery ML

料金計算ツールを使用すると、予想使用量に基づいて費用の見積もりを算出できます。新しい Google Cloud ユーザーは無料トライアルをご利用いただけます。

始める前に

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

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

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

このチュートリアルを終了した後、作成したリソースを削除すると、それ以上の請求は発生しません。詳しくは、クリーンアップをご覧ください。

サンプルデータの処理

行列分解を使用する場合は、明示的または暗黙的なユーザー フィードバックを評価し、お客様の好みを判断します。明示的なフィードバックを使用するには、データセットに 1~5 の評価など、商品に対するユーザーの好みに関するデータが含まれている必要があります。明示的なフィードバックがない場合、他の行動指標を使用して、お客様の好みを推測します。たとえば、ユーザーが商品の詳細ページを閲覧した合計時間を調べます。このチュートリアルでは、このアプローチを使用します。

行列分解モデルをトレーニングするには、お客様、評価対象のアイテム、暗黙的な評価を識別する列を含むテーブルが必要です。このセクションでは、useriditemIdsession_duration 列を含むテーブルを作成します。ここで、session_duration 列には、該当アイテムの商品ページで費やされたユーザーのセッション継続時間が含まれます。

Google アナリティクスのサンプル データセットのデータを使用して、このようなテーブルを作成するには、次の手順を行います。

  1. Google Cloud Marketplace で Google アナリティクスのサンプル データセットを開き、[データセットを表示] をクリックします。BigQuery コンソールが開き、Google アナリティクスのサンプル データセットが選択された状態になります。
  2. [リソース] セクションで、このチュートリアルを実施するプロジェクトを選択します。
  3. [データセットを作成] をクリックします。

    [データセットを作成] ボタンの場所を表示。

  4. [データセットを作成] ページで次の操作を行います。

    • [データセット ID] に「bqml」と入力します。
    • [データのロケーション] で、最も近いロケーションを選択します。
    • [データセットを作成] をクリックします。
  5. この手順は、データをアナリティクス 360 にエクスポートする場合に行います。それ以外の場合は、スキップしてください。

    [クエリエディタ] ペインで、次の SQL ステートメントを実行して、Google アナリティクスのサンプルデータのサブセットを新しいテーブルにコピーし、clientId フィールドに入力します。これは、次のステップで暗黙的なユーザー フィードバックを集計するときのキーとして使用します。

    CREATE OR REPLACE TABLE bqml.ga_clientid_sample AS (
      SELECT *
      FROM `bigquery-public-data.google_analytics_sample.ga_sessions_2017*`
      LIMIT 100000);
    
     UPDATE bqml.ga_clientid_sample
       SET clientId = fullvisitorId
       WHERE true;
    

    アナリティクス 360 にオーディエンス データをインポートするときは、キーフィールドとして clientId を使用する必要があります。clientId は通常、ハッシュされた fullVisitorId ですが、Google アナリティクスのサンプル データセットには入力されていません。独自のアナリティクス 360 データで clientId を設定するには、カスタム ディメンションを作成して入力します。

  6. 次の SQL ステートメントを実行して、トレーニング データを含むテーブルを作成します。データをアナリティクス 360 にエクスポートする場合は、clientId フィールドをキーとして使用するバージョンを実行します。その他のマーケティング システムでデータを使用する場合は、fullVisitorId フィールドをキーとして使用するバージョンを実行します。

    clientId

    CREATE OR REPLACE TABLE bqml.aggregate_web_stats AS (
      WITH
        durations AS (
          --calculate pageview durations
          SELECT
            CONCAT(clientId,'-',
                 CAST(visitNumber AS STRING),'-',
                 CAST(hitNumber AS STRING) ) AS visitorId_session_hit,
            LEAD(time, 1) OVER (
              PARTITION BY CONCAT(clientId,'-',CAST(visitNumber AS STRING))
              ORDER BY
              time ASC ) - time AS pageview_duration
          FROM
            `bqml.ga_clientid_sample`,
            UNNEST(hits) AS hit
        ),
        prodview_durations AS (
          --filter for product detail pages only
         SELECT
            CONCAT(clientId,'-',CAST(visitNumber AS STRING)) AS userId,
            productSKU AS itemId,
            IFNULL(dur.pageview_duration,
             1) AS pageview_duration,
          FROM
            `bqml.ga_clientid_sample` t,
            UNNEST(hits) AS hits,
            UNNEST(hits.product) AS hits_product
          JOIN
            durations dur
          ON
            CONCAT(clientId,'-',
                   CAST(visitNumber AS STRING),'-',
                   CAST(hitNumber AS STRING)) = dur.visitorId_session_hit
          WHERE
          eCommerceAction.action_type = "2"
        ),
        aggregate_web_stats AS(
          --sum pageview durations by userId, itemId
          SELECT
            userId,
            itemId,
            SUM(pageview_duration) AS session_duration
          FROM
            prodview_durations
          GROUP BY
            userId,
            itemId )
        SELECT
         *
       FROM
          aggregate_web_stats
    );
    

    fullVisitorId

    CREATE OR REPLACE TABLE bqml.aggregate_web_stats AS (
      WITH
        durations AS (
          --calculate pageview durations
          SELECT
            CONCAT(fullVisitorId,'-',
                 CAST(visitNumber AS STRING),'-',
                 CAST(hitNumber AS STRING) ) AS visitorId_session_hit,
            LEAD(time, 1) OVER (
              PARTITION BY CONCAT(fullVisitorId,'-',CAST(visitNumber AS STRING))
              ORDER BY
              time ASC ) - time AS pageview_duration
          FROM
            `bigquery-public-data.google_analytics_sample.ga_sessions_2017*`,
            UNNEST(hits) AS hit
        ),
        prodview_durations AS (
          --filter for product detail pages only
         SELECT
            CONCAT(fullVisitorId,'-',CAST(visitNumber AS STRING)) AS userId,
            productSKU AS itemId,
            IFNULL(dur.pageview_duration,
             1) AS pageview_duration,
          FROM
            `bigquery-public-data.google_analytics_sample.ga_sessions_2017*` t,
            UNNEST(hits) AS hits,
            UNNEST(hits.product) AS hits_product
          JOIN
            durations dur
          ON
            CONCAT(fullVisitorId,'-',
                   CAST(visitNumber AS STRING),'-',
                   CAST(hitNumber AS STRING)) = dur.visitorId_session_hit
          WHERE
          eCommerceAction.action_type = "2"
        ),
        aggregate_web_stats AS(
          --sum pageview durations by userId, itemId
          SELECT
            userId,
            itemId,
            SUM(pageview_duration) AS session_duration
          FROM
            prodview_durations
          GROUP BY
            userId,
            itemId )
        SELECT
         *
       FROM
          aggregate_web_stats
    );
    
  7. 次の SQL ステートメントを実行し、得られた bqml.aggregate_web_stats テーブルのサンプルデータを表示します。

    SELECT
     *
    FROM
      bqml.aggregate_web_stats
    LIMIT
      10;
    

    次のような結果が表示されます。

    処理されたトレーニング データの最初の 10 行。

Flex Slots のスロットの購入

BigQuery のオンデマンド料金を利用している場合は、Flex Slots のスロットを購入してから、行列分解モデルのトレーニング用に予約と割り当てを作成する必要があります。BigQuery で定額料金を利用している場合は、このセクションをスキップできます。

Flex Slots のスロットを購入するには、bigquery.reservations.create 権限が必要です。この権限は、プロジェクトのオーナーのほか、Identity and Access Management の事前定義ロールの bigquery.adminbigquery.resourceAdmin にも付与されています。

  1. BigQuery コンソールで、[予約] をクリックします。
  2. API を有効にするために [BigQuery Reservation API] ページにリダイレクトされた場合は、[有効にする] をクリックします。それ以外の場合は、次のステップに進みます。
  3. [予約] ページで、[スロットの購入] をクリックします。
  4. [スロットの購入] ページで、次のようにオプションを設定します。

    1. [コミット期間] で [フレックス] を選択します。
    2. [ロケーション] で、サンプルデータの処理の手順でデータセットを作成したときに選択したロケーションを選択します。
    3. [スロット数] で [500] を選択します。
    4. [次へ] をクリックします。
    5. [注文確認] に「CONFIRM」と入力します。

  5. [購入] をクリックします。

  6. [スロット コミットメントを表示] をクリックします。

  7. 容量がプロビジョニングされるまでに、最大で 20 分間かかります。容量がプロビジョニングされると、スロット コミットメント ステータスが緑色に変わり、チェックマーク が表示されます。

  8. [予約を作成] をクリックします。

  9. [予約を作成] ページで、次のようにオプションを設定します。

    1. [予約名] に「model」と入力します。
    2. [ロケーション] で、Flex Slots のスロットを購入したロケーションを選択します。
    3. [スロット数] に「500」と入力します。
    4. [保存] をクリックします。[予約] ページに戻ります。
  10. [割り当て] タブを選択します。

  11. [組織、フォルダ、プロジェクトを選択] で、[参照] をクリックします。

  12. このチュートリアルを実施するプロジェクトの名前を入力します。

  13. [選択] をクリックします。

  14. [予約] で、作成したモデルの予約を選択します。

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

  16. [BigQuery] をクリックして、BigQuery コンソールに戻ります。

モデルの作成、トレーニング、デプロイ

行列分解モデルを作成、トレーニング、デプロイするには、CREATE MODEL SQL ステートメントを実行します。

      CREATE OR REPLACE MODEL bqml.retail_recommender
      OPTIONS(model_type='matrix_factorization',
            user_col='userId',
            item_col='itemId',
            rating_col='session_duration',
            feedback_type='implicit'
            )
      AS
      SELECT * FROM bqml.aggregate_web_stats;

トレーニングが完了すると、トレーニング済みのモデルが bqml.retail_recommender としてデプロイされます。

トレーニング済みモデルを使用して予測を行う

デプロイされた bqml.retail_recommender モデルから予測を取得するには、ML.RECOMMEND SQL 関数を使用します。

  1. レコメンデーション データの例を表示するために、次の SQL ステートメントを実行して、指定した userId の上位 5 件のレコメンデーションを表す予測を取得します。

    DECLARE MY_USERID STRING DEFAULT "0824461277962362623-1";
    
    SELECT
      *
    FROM
      ML.RECOMMEND(MODEL `bqml.retail_recommender`,
      (SELECT MY_USERID as userID)
                  )
    ORDER BY predicted_session_duration_confidence DESC
    LIMIT 5;
    

    次のような結果が表示されます。

    指定したユーザー ID の上位 5 件のレコメンデーション。

  2. 次の SQL ステートメントを実行して、すべてのユーザーの上位 5 件の予測を取得します。ここでは大量の行が生成されるため、出力はテーブルに書き込まれます。その後、最初の 10 個のレコードが取得され、データの例が表示されます。

    -- Create output table of top 5 predictions
    CREATE OR REPLACE TABLE bqml.prod_recommendations AS (
    WITH predictions AS (
        SELECT
          userId,
          ARRAY_AGG(STRUCT(itemId,
                           predicted_session_duration_confidence)
                    ORDER BY
                      predicted_session_duration_confidence DESC
                    LIMIT 5) as recommended
        FROM ML.RECOMMEND(MODEL bqml.retail_recommender)
        GROUP BY userId
    )
    
    SELECT
      userId,
      itemId,
      predicted_session_duration_confidence
    FROM
      predictions p,
      UNNEST(recommended)
    );
    
    -- Show table
    SELECT
     *
    FROM
      bqml.prod_recommendations
    ORDER BY
      userId
    LIMIT
      10;
    

    次のような結果が表示されます。

    すべてのユーザーの最初の 10 件のレコメンデーション。

本番環境での予測されたレコメンデーションの使用

レコメンデーションを作成した後、本番環境パイプラインで使用できるようにする方法は、ユースケースによって異なります。以降のセクションでは、予測データをアナリティクス 360 または Cloud Storage にエクスポートする方法と、BigQuery から Pandas データフレームにプログラムでデータを読み取る方法について説明します。

アナリティクス 360 にレコメンデーションをエクスポートする

アナリティクス 360 にデータをエクスポートする場合は、商品ごとに、お客様がその商品を購入する可能性を示す次のような列を用意することをおすすめします。

clientId likelihoodProductA likelihoodProductB
123 .6527238 .3464891
456 .8720673 .2750274
789 .5620734 .9127595

商品ごとに「購入する可能性」列を作成するには、BigQuery の簡単な 1 ステップの pivot() の説明に従って、pivot() プロシージャを作成します。

  1. 次の SQL ステートメントを実行して、pivot プロシージャを作成します。

    CREATE OR REPLACE FUNCTION
    `bqml.normalize_col_name`(col_name STRING) AS (
      REGEXP_REPLACE(col_name,r'[/+#|]', '_'
    ));
    
    CREATE OR REPLACE PROCEDURE `bqml.pivot`(
      table_name STRING
      , destination_table STRING
      , row_ids ARRAY<STRING>
      , pivot_col_name STRING
     , pivot_col_value STRING
      , max_columns INT64
      , aggregation STRING
      , optional_limit STRING
      )
    
    BEGIN
    
      DECLARE pivotter STRING;
    
      EXECUTE IMMEDIATE (
       "SELECT STRING_AGG(' "||aggregation
        ||"""(IF('||@pivot_col_name||'="'||x.value||'", '||@pivot_col_value||', null)) e_'||bqml.normalize_col_name(x.value))
       FROM UNNEST((
           SELECT APPROX_TOP_COUNT("""||pivot_col_name||", @max_columns) FROM `"||table_name||"`)) x"
      ) INTO pivotter
      USING pivot_col_name AS pivot_col_name, pivot_col_value AS pivot_col_value, max_columns AS max_columns;
    
      EXECUTE IMMEDIATE (
       'CREATE OR REPLACE TABLE `'||destination_table
       ||'` AS SELECT '
       ||(SELECT STRING_AGG(x) FROM UNNEST(row_ids) x)
       ||', '||pivotter
       ||' FROM `'||table_name||'` GROUP BY '
       || (SELECT STRING_AGG(''||(i+1)) FROM UNNEST(row_ids) WITH OFFSET i)||' ORDER BY '
       || (SELECT STRING_AGG(''||(i+1)) FROM UNNEST(row_ids) WITH OFFSET i)
       ||' '||optional_limit
      );
    
    END;
    
  2. 次の SQL ステートメントを実行して、clientId と各商品の「購入する可能性」列を含むテーブルを作成します。

    CALL bqml.pivot(
      'bqml.prod_recommendations' # source table
      , 'bqml.prod_recommendations_export' # destination table
      , ['userId'] # row IDs
      , 'itemId' # pivot column name
      , 'predicted_session_duration_confidence' # pivot column value
      , 30 # max number of columns
      , 'AVG' # aggregation
      , '' # optional_limit
    );
    
  3. 次の SQL ステートメントを実行し、得られた bqml.prod_recommendations_export テーブルのサンプルデータを表示します。

    SELECT
      *
    FROM
      bqml.prod_recommendations_export
    ORDER BY
      userId
    LIMIT
      10;
    

    次のような結果が表示されます。

    すべてのユーザーの最初の 10 件のレコメンデーション。

適切な形式のデータを作成したら、CSV ファイルとして保存し、データのインポートを使用してデータをアナリティクス 360 にインポートします。エクスポートされたレコメンデーション データの列名は、アナリティクス 360 のデータ インポート スキーマにマッピングされている必要があります。たとえば、データ インポート スキーマが ga:clientId, ga:dimension1, ga:dimension2 の場合、データの列名も ga:clientId, ga:dimension1, ga:dimension2 とする必要があります。BigQuery では列名にコロンを使用できないため、インポートする前に、エクスポートされた CSV ファイルの列名を更新する必要があります。

BigQuery ML モデルMoDeM(Model Deployment for Marketing)のリファレンス実装を使用して、データをアナリティクス 360 に簡単に読み込むこともできます。まずは、BQML デプロイ テンプレート ノートブックのインタラクティブな手順を使用してください。

Cloud Storage へのレコメンデーションのエクスポート

テーブルデータのエクスポートの手順で、BigQuery テーブルからレコメンデーション データを Cloud Storage にエクスポートします。

レコメンデーションをプログラムで読み取る

BigQuery クライアント ライブラリを使用してテーブルデータをダウンロードするの手順で、BigQuery Storage API を使用して、BigQuery テーブルから Pandas データフレームにレコメンデーション データを読み取ります。または、いずれかの BigQuery クライアント ライブラリを使用して、独自のソリューションをプログラミングすることもできます。

まとめ

チュートリアルを完了し、BigQuery ML を使用してレコメンデーション システムをトレーニングする方法、モデルをデプロイする方法、本番環境で結果を使用する方法を学びました。

クリーンアップ

このチュートリアルで使用したリソースについて、Google Cloud アカウントに課金されないようにするには、リソースを含むプロジェクトを削除するか、プロジェクトは削除せず、これらのリソースのみを削除します。

いずれにしても、今後これらのリソースについて料金が発生しないように、削除する必要があります。以降のセクションでは、このようなリソースを削除する方法を説明します。

プロジェクトの削除

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

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

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

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

コンポーネントの削除

プロジェクトを削除しない場合は、以下のセクションに従って、このチュートリアルの課金対象コンポーネントを削除してください。

BigQuery データセットの削除

  1. BigQuery コンソールを開きます
  2. [リソース] セクションで、このチュートリアルを実施しているプロジェクトを展開し、bqml データセットを選択します。
  3. データセット ペインのヘッダーにある [データセットの削除] をクリックします。
  4. 上に重なったウィンドウで、「bqml」と入力して [削除] をクリックします。

Flex Slots のスロットの削除

Flex Slots のスロットを作成した場合は、次の手順で削除します。

  1. BigQuery コンソールで、[予約] をクリックします。
  2. [割り当て] タブを選択します。
  3. モデルの予約に作成した割り当ての行で、[操作] 列にある [その他] をクリックし、[削除] をクリックします。
  4. [予約] タブを選択します。
  5. モデルの予約の行を探して、[操作] 列にある [その他] をクリックし、[削除] をクリックします。
  6. [スロット コミットメント] タブを選択します。
  7. 購入した 500 個の Flex Slots のスロットを含む行を見つけて、[操作] 列にある [その他] をクリックし、[削除] をクリックします。
  8. [スロット コミットメントの削除の確認] に「REMOVE」と入力します。
  9. [続行] をクリックします。

次のステップ