TensorFlow.js による予測プリフェッチ

このチュートリアルでは、TensorFlow.js を使用してリソースの予測プリフェッチを行うサンプル Web アプリケーションを実行します。この例はAngularで構築されており、Google Merchandise Store からインスピレーションを得ていますが、データや実装の詳細は Google Merchandise Store と共有しません。

この例では、事前トレーニングされたモデルを使用して予測を行います。実際のシナリオでは、Web サイトの分析を使用してモデルをトレーニングする必要があります。 TFX を使用すると、このようなトレーニングを行うことができます。予測プリフェッチ用のカスタム モデルのトレーニングの詳細については、このブログ投稿を参照してください。

サンプルコードはGitHubで入手できます。

前提条件

このチュートリアルを完了するには、開発環境に以下がインストールされている必要があります。

サンプルをインストールする

ソース コードを取得し、依存関係をインストールします。

  1. tfjs-examplesリポジトリのクローンを作成するか、ダウンロードします。
  2. angular-predictive-prefetching/clientディレクトリに移動し、依存関係をインストールします。

    cd tfjs-examples/angular-predictive-prefetching/client && yarn
    
  3. angular-predictive-prefetching/serverディレクトリに移動し、依存関係をインストールします。

    cd ../server && yarn
    

サンプルを実行する

サーバーとクライアントの両方を起動します。

  1. サーバーを起動します。 serverディレクトリで、 yarn startを実行します。

  2. クライアントを起動します。

    1. 別のターミナル ウィンドウを開きます。
    2. tfjs-examples/angular-predictive-prefetching/clientに変更します。
    3. 次のコマンドを実行します。

      yarn build
      cd dist/merch-store
      npx serve -s .
      

      サーブパッケージをインストールするように求められる場合があります。その場合は、 「y」と入力してパッケージをインストールします。

  3. ブラウザでhttp://localhost:3000に移動します。模擬の Google 商品ストアが表示されるはずです。

DevTools を使って探索する

Chrome DevTools を使用して、プリフェッチの動作を確認します。

  1. DevTools を開いて[コンソール]を選択します。
  2. アプリケーション内のいくつかの異なるページに移動して、アプリケーションを準備します。次に、左側のナビゲーションで[セール]を選択します。次のようなログ出力が表示されるはずです。

    Navigating from: 'sale'
    'quickview' -> 0.381757915019989
    'apparel-unisex' -> 0.3150934875011444
    'store.html' -> 0.1957530975341797
    '' -> 0.052346792072057724
    'signin.html' -> 0.0007763378671370447
    

    この出力には、あなた (ユーザー) が次にアクセスするページの予測が表示されます。アプリケーションは、これらの予測に基づいてリソースを取得します。

  3. フェッチリクエストを表示するには、 「ネットワーク」を選択します。出力には少しノイズが含まれますが、予測されたページのリソースに対するリクエストを見つけることができるはずです。たとえば、 quickviewを予測した後、アプリケーションはhttp://localhost:8000/api/merch/quickviewにリクエストを送信します。

予測プリフェッチの仕組み

サンプルアプリでは、事前トレーニングされたモデルを使用して、ユーザーが次にアクセスするページを予測します。ユーザーが新しいページに移動すると、アプリはモデルをクエリし、予測されたページに関連付けられた画像をプリフェッチします。

アプリはService Workerで予測プリフェッチを実行するため、メインスレッドをブロックせずにモデルをクエリできます。ユーザーのナビゲーション履歴に基づいて、サービス担当者は将来のナビゲーションを予測し、関連する製品画像をプリフェッチします。

Service Worker は、Angular アプリのメイン ファイルmain.tsにロードされます。

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/prefetch.service-worker.js', { scope: '/' });
}

上記のスニペットは、 prefetch.service-worker.jsスクリプトをダウンロードし、バックグラウンドで実行します。

merch-display.component.tsでは、アプリはナビゲーション イベントを Service Worker に転送します。

this.route.params.subscribe((routeParams) => {
  this.getMerch(routeParams.category);
  if (this._serviceWorker) {
    this._serviceWorker.postMessage({ page: routeParams.category });
  }
});

上のスニペットでは、アプリは URL のパラメーターの変更を監視します。変更時に、スクリプトはページのカテゴリを Service Worker に転送します。

Service Worker スクリプトprefetch.service-worker.js は、メインスレッドからのメッセージを処理し、それらに基づいて予測を行い、関連するリソースをプリフェッチします。

Service Worker は、 loadGraphModelを使用して事前トレーニングされたモデルをロードします

const MODEL_URL = "/assets/model.json";

let model = null;
tf.loadGraphModel(MODEL_URL).then((m) => (model = m));

予測は次の関数式で行われます。

const predict = async (path, userId) => {
  if (!model) {
    return;
  }
  const page = pages.indexOf(path);
  const pageId = tf.tensor1d([parseInt(page)], "int32");

  const sessionIndex = tf.tensor1d([parseInt(userId)], "int32");

  const result = model.predict({
    cur_page: pageId,
    session_index: sessionIndex,
  });
  const values = result.dataSync();
  const orders = sortWithIndices(values).slice(0, 5);
  return orders;
};

次に、 predict関数がプリフェッチ関数によって呼び出されます。

const prefetch = async (path, sessionId) => {
  const predictions = await predict(path, sessionId);
  const formattedPredictions = predictions
    .map(([a, b]) => `'${b}' -> ${a}`)
    .join("\n");
  console.log(`Navigating from: '${path}'`);
  console.log(formattedPredictions);
  const connectionSpeed = navigator.connection.effectiveType;
  const threshold = connectionSpeeds[connectionSpeed];
  const cache = await caches.open(ImageCache);
  predictions.forEach(async ([probability, category]) => {
    if (probability >= threshold) {
      const merchs = (await getMerchList(category)).map(getUrl);
      [...new Set(merchs)].forEach((url) => {
        const request = new Request(url, {
          mode: "no-cors",
        });
        fetch(request).then((response) => cache.put(request, response));
      });
    }
  });
};

まず、 prefetch 、ユーザーが次にアクセスする可能性のあるページを予測します。次に、予測を繰り返します。各予測について、確率が接続速度に基づく特定のしきい値を超える場合、関数は予測されたページのリソースをフェッチします。次のページ要求の前にこれらのリソースを取得することで、アプリはコンテンツをより速く提供し、より良いユーザー エクスペリエンスを提供できる可能性があります。

次は何ですか

このチュートリアルでは、サンプル アプリは事前トレーニングされたモデルを使用して予測を行います。 TFXを使用して、予測プリフェッチ用のモデルをトレーニングできます。詳細については、 「機械学習を使用した Web ページのプリフェッチによるサイトの高速化」を参照してください。