1. 始める前に
映画やレストランのおすすめ、おもしろい動画のハイライトなど、レコメンデーション エンジン(レコメンダー)は、ML の非常に重要な応用となっています。Recommender を使用すると、多数の候補の中から魅力的なコンテンツをユーザーに提供できます。たとえば、Google Play ストアではインストールできるアプリが数多く用意されており、YouTube では数十億本もの動画を視聴できます。さらに多くのアプリや動画が日々追加されています。
この Codelab では、以下を使用してフルスタック Recommender を構築する方法を学びます。
- 映画のレコメンデーション用の取得モデルとランキング モデルをトレーニングするための TensorFlow Recommender
- モデルを提供する TensorFlow Serving
- Flutter でおすすめの映画を表示するクロスプラットフォーム アプリを作成
前提条件
- Dart を使った Flutter の開発に関する基本的な知識
- TensorFlow を使用した機械学習に関する基本的な知識(トレーニングとデプロイなど)
- レコメンデーション システムに関する基本的な知識
- Python、ターミナル、Docker に関する基本的な知識
学習内容
- TensorFlow Recommender を使用して取得モデルとランキング モデルをトレーニングする方法
- TensorFlow Serving を使用してトレーニング済みレコメンデーション モデルを提供する方法
- おすすめアイテムを表示するクロス プラットフォームの Flutter アプリを作成する方法
必要なもの
- Flutter SDK
- Flutter を使用するための Android と iOS の設定
- Flutter を使用するためのパソコンの設定
- Flutter を使用するためのウェブの設定
- Flutter と Dart を使用するための Visual Studio Code(VS Code)の設定
- Docker
- Bash
- Python 3.7+
- Colab へのアクセス権
2. Flutter の開発環境をセットアップする
Flutter 開発の場合、この Codelab を完了するには、Flutter SDK とエディタの 2 つのソフトウェアが必要です。
Codelab のフロントエンドは、次のいずれかのデバイスを使用して実行できます。
- iOS シミュレータ(Xcode ツールのインストールが必要)
- Android Emulator(Android Studio でセットアップが必要)
- ブラウザ(デバッグには Chrome が必要)
- Windows、Linux、macOS のデスクトップ アプリケーション。開発はデプロイする予定のプラットフォームで行う必要があります。たとえば、Windows のデスクトップ アプリを開発する場合は、適切なビルドチェーンにアクセスできるように Windows で開発する必要があります。オペレーティング システム固有の要件については、docs.flutter.dev/desktop に詳しい説明があります。
バックエンドについては、次のものが必要です。
- Linux マシンまたは Intel ベースの Mac
3. セットアップする
この Codelab のコードをダウンロードするには:
- この Codelab の GitHub リポジトリに移動します。
- [Code] > [Download zip] をクリックして、この Codelab のすべてのコードをダウンロードします。
- ダウンロードした zip ファイルを解凍して、
codelabs-main
ルートフォルダを展開します。これには必要なリソースがすべて含まれています。
この Codelab に必要なのは、複数のフォルダを含むリポジトリの tfrs-flutter/
サブディレクトリにあるファイルのみです。
step0
フォルダからstep5
フォルダには、この Codelab の各ステップで構築するスターター コードが含まれています。finished
フォルダには、完成したサンプルアプリの完全なコードが含まれています。- 各フォルダには、レコメンデーション エンジンのバックエンド コードを含む
backend
サブフォルダと、Flutter フロントエンド コードを含むfrontend
サブフォルダが含まれます。
4. プロジェクトの依存関係をダウンロードする
バックエンド
ここでは、Flask を使用してバックエンドを作成します。ターミナルを開いて次のコマンドを実行します。
pip install Flask flask-cors requests numpy
フロントエンド
- VS Code で、[File] > [Open folder] をクリックし、先ほどダウンロードしたソースコードの
step0
フォルダを選択します。 step0/frontend/lib/main.dart
ファイルを開きます。スターター アプリに必要なパッケージのダウンロードを求める VS Code ダイアログが表示されたら、[Get packages] をクリックします。- このダイアログが表示されない場合は、ターミナルを開いて
step0/frontend
フォルダでflutter pub get
コマンドを実行します。
5. ステップ 0: スターター アプリを実行する
- VS Code で
step0/frontend/lib/main.dart
ファイルを開き、Android Emulator または iOS Simulator が正しく設定され、ステータスバーに表示されていることを確認します。
たとえば、Android Emulator で Google Pixel 5 を使用する場合は次のようになります。
iOS シミュレータで iPhone 13 を使用する場合は次のようになります。
- [ Start debugging] をクリックします。
アプリを実行して操作する
Android Emulator または iOS シミュレータでアプリを起動します。UI は非常にシンプルです。ユーザーがユーザー ID としてテキストを入力できるテキスト フィールドがあります。Flutter アプリは、バックエンドにクエリ リクエストを送信します。バックエンドは 2 つのレコメンデーション モデルを実行し、映画のレコメンデーションのランキング リストを返します。フロントエンドは、レスポンスの受信後に UI に結果を表示します。
ここで [Recommend] をクリックしても、アプリはまだバックエンドと通信できないため、何も起こりません。
6. ステップ 1: レコメンデーション エンジンの検索モデルとランキング モデルを作成する
多くの場合、実際のレコメンデーション エンジンは複数のステージで構成されます。
- 検索ステージでは、可能性のあるすべての候補から数百の候補の初期セットを選択します。このモデルの主な目的は、ユーザーが興味のないすべての候補を効率的に除外することです。検索モデルは数百万の候補を処理する可能性があるため、計算効率が必要です。
- ランキング ステージでは、リトリーブ モデルの出力を受け取り、それらをファインチューニングして、可能な限り最適なレコメンデーションを選択します。タスクは、ユーザーが興味を持ちそうなアイテムの集合を、数百程度の見込み客の候補リストに絞り込むことです。
- ポスト ランキング ステージでは、多様性、鮮度、公平性を確保し、候補項目を数十程度の有用な推奨事項に再編成します。
この Codelab では、人気の MovieLens データセットを使用して、リトリーブ モデルとランキング モデルをトレーニングします。Colab から以下のトレーニング コードを開き、手順に沿って操作します。
7. ステップ 2: レコメンデーション エンジンのバックエンドを作成する
取得モデルとランキング モデルをトレーニングしたので、次はこれらのモデルをデプロイしてバックエンドを作成します。
TensorFlow Serving を開始する
おすすめの映画リストを生成するには取得モデルとランキング モデルの両方を使用する必要があるため、TensorFlow Serving を使用して両方を同時にデプロイします。
- ターミナルで、パソコンの
step2/backend
フォルダに移動して、Docker による TensorFlow Serving を起動します。
docker run -t --rm -p 8501:8501 -p 8500:8500 -v "$(pwd)/:/models/" tensorflow/serving --model_config_file=/models/models.config
Docker は、まず TensorFlow Serving のイメージを自動的にダウンロードします。これには 1 分ほどかかります。その後、TensorFlow Serving が起動します。次のようなログが出力されます。
2022-04-24 09:32:06.461702: I tensorflow_serving/model_servers/server_core.cc:465] Adding/updating models. 2022-04-24 09:32:06.461843: I tensorflow_serving/model_servers/server_core.cc:591] (Re-)adding model: retrieval 2022-04-24 09:32:06.461907: I tensorflow_serving/model_servers/server_core.cc:591] (Re-)adding model: ranking 2022-04-24 09:32:06.576920: I tensorflow_serving/core/basic_manager.cc:740] Successfully reserved resources to load servable {name: retrieval version: 123} 2022-04-24 09:32:06.576993: I tensorflow_serving/core/loader_harness.cc:66] Approving load for servable version {name: retrieval version: 123} 2022-04-24 09:32:06.577011: I tensorflow_serving/core/loader_harness.cc:74] Loading servable version {name: retrieval version: 123} 2022-04-24 09:32:06.577848: I external/org_tensorflow/tensorflow/cc/saved_model/reader.cc:38] Reading SavedModel from: /models/retrieval/exported-retrieval/123 2022-04-24 09:32:06.583809: I external/org_tensorflow/tensorflow/cc/saved_model/reader.cc:90] Reading meta graph with tags { serve } 2022-04-24 09:32:06.583879: I external/org_tensorflow/tensorflow/cc/saved_model/reader.cc:132] Reading SavedModel debug info (if present) from: /models/retrieval/exported-retrieval/123 2022-04-24 09:32:06.584970: I external/org_tensorflow/tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations: AVX2 FMA To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags. 2022-04-24 09:32:06.629900: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:206] Restoring SavedModel bundle. 2022-04-24 09:32:06.634662: I external/org_tensorflow/tensorflow/core/platform/profile_utils/cpu_utils.cc:114] CPU Frequency: 2800000000 Hz 2022-04-24 09:32:06.672534: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:190] Running initialization op on SavedModel bundle at path: /models/retrieval/exported-retrieval/123 2022-04-24 09:32:06.673629: I tensorflow_serving/core/basic_manager.cc:740] Successfully reserved resources to load servable {name: ranking version: 123} 2022-04-24 09:32:06.673765: I tensorflow_serving/core/loader_harness.cc:66] Approving load for servable version {name: ranking version: 123} 2022-04-24 09:32:06.673786: I tensorflow_serving/core/loader_harness.cc:74] Loading servable version {name: ranking version: 123} 2022-04-24 09:32:06.674731: I external/org_tensorflow/tensorflow/cc/saved_model/reader.cc:38] Reading SavedModel from: /models/ranking/exported-ranking/123 2022-04-24 09:32:06.683557: I external/org_tensorflow/tensorflow/cc/saved_model/reader.cc:90] Reading meta graph with tags { serve } 2022-04-24 09:32:06.683601: I external/org_tensorflow/tensorflow/cc/saved_model/reader.cc:132] Reading SavedModel debug info (if present) from: /models/ranking/exported-ranking/123 2022-04-24 09:32:06.688665: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:277] SavedModel load for tags { serve }; Status: success: OK. Took 110815 microseconds. 2022-04-24 09:32:06.690019: I tensorflow_serving/servables/tensorflow/saved_model_warmup_util.cc:59] No warmup data file found at /models/retrieval/exported-retrieval/123/assets.extra/tf_serving_warmup_requests 2022-04-24 09:32:06.693025: I tensorflow_serving/core/loader_harness.cc:87] Successfully loaded servable version {name: retrieval version: 123} 2022-04-24 09:32:06.702594: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:206] Restoring SavedModel bundle. 2022-04-24 09:32:06.745361: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:190] Running initialization op on SavedModel bundle at path: /models/ranking/exported-ranking/123 2022-04-24 09:32:06.772363: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:277] SavedModel load for tags { serve }; Status: success: OK. Took 97633 microseconds. 2022-04-24 09:32:06.774853: I tensorflow_serving/servables/tensorflow/saved_model_warmup_util.cc:59] No warmup data file found at /models/ranking/exported-ranking/123/assets.extra/tf_serving_warmup_requests 2022-04-24 09:32:06.777706: I tensorflow_serving/core/loader_harness.cc:87] Successfully loaded servable version {name: ranking version: 123} 2022-04-24 09:32:06.778969: I tensorflow_serving/model_servers/server_core.cc:486] Finished adding/updating models 2022-04-24 09:32:06.779030: I tensorflow_serving/model_servers/server.cc:367] Profiler service is enabled 2022-04-24 09:32:06.784217: I tensorflow_serving/model_servers/server.cc:393] Running gRPC ModelServer at 0.0.0.0:8500 ... [warn] getaddrinfo: address family for nodename not supported 2022-04-24 09:32:06.785748: I tensorflow_serving/model_servers/server.cc:414] Exporting HTTP/REST API at:localhost:8501 ... [evhttp_server.cc : 245] NET_LOG: Entering the event loop ...
新しいエンドポイントを作成する
TensorFlow Serving はチェーン接続をサポートしていないため複数のシーケンシャル モデルがある場合は、取得モデルとランキング モデルを接続する新しいサービスを作成する必要があります。
step2/backend/recommendations.py
ファイルのget_recommendations()
関数に、次のコードを追加します。
user_id = request.get_json()["user_id"] retrieval_request = json.dumps({"instances": [user_id]}) retrieval_response = requests.post(RETRIEVAL_URL, data=retrieval_request) movie_candidates = retrieval_response.json()["predictions"][0]["output_2"] ranking_queries = [ {"user_id": u, "movie_title": m} for (u, m) in zip([user_id] * NUM_OF_CANDIDATES, movie_candidates) ] ranking_request = json.dumps({"instances": ranking_queries}) ranking_response = requests.post(RANKING_URL, data=ranking_request) movies_scores = list(np.squeeze(ranking_response.json()["predictions"])) ranked_movies = [ m[1] for m in sorted(list(zip(movies_scores, movie_candidates)), reverse=True) ] return make_response(jsonify({"movies": ranked_movies}), 200)
Flask サービスを起動する
これで、Flask サービスを起動できるようになりました。
- ターミナルで
step2/backend/
フォルダに移動し、次のコマンドを実行します。
FLASK_APP=recommender.py FLASK_ENV=development flask run
Flask は http://localhost:5000/recommend
に新しいエンドポイントを起動します。次のようなログが表示されます。
* Serving Flask app 'recommender.py' (lazy loading) * Environment: development * Debug mode: on * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 705-382-264 127.0.0.1 - - [25/Apr/2022 19:44:47] "POST /recommend HTTP/1.1" 200 -
エンドポイントにサンプル リクエストを送信して、期待どおりに動作していることを確認できます。
curl -X POST -H "Content-Type: application/json" -d '{"user_id":"42"}' http://localhost:5000/recommend
エンドポイントは、ユーザー 42
におすすめの映画のリストを返します。
{ "movies": [ "While You Were Sleeping (1995)", "Preacher's Wife, The (1996)", "Michael (1996)", "Lion King, The (1994)", "Father of the Bride Part II (1995)", "Sleepless in Seattle (1993)", "101 Dalmatians (1996)", "Bridges of Madison County, The (1995)", "Rudy (1993)", "Jack (1996)" ] }
これで、ユーザー ID に基づいて映画をおすすめするバックエンドが正常に構築されました。
8. ステップ 3: Android および iOS 用の Flutter アプリを作成する
バックエンドの準備が整いました。これで、Flutter アプリから映画のレコメンデーションを照会するためのリクエストを、このコンテナに送信できます。
フロントエンド アプリは非常にシンプルです。ユーザー ID を受け取り、先ほど構築したバックエンドにリクエストを(recommend()
関数で)送信する TextField のみが含まれています。レスポンスを受信すると、アプリの UI はおすすめの映画を ListView に表示します。
step3/frontend/lib/main.dart
ファイルのrecommend()
関数に、次のコードを追加します。
final response = await http.post( Uri.parse('http://' + _server + ':5000/recommend'), headers: <String, String>{ 'Content-Type': 'application/json', }, body: jsonEncode(<String, String>{ 'user_id': _userIDController.text, }), );
アプリがバックエンドからレスポンスを受信したら、UI を更新して、指定したユーザーにおすすめの映画のリストを表示します。
- 上記のコードのすぐ下に次のコードを追加します。
if (response.statusCode == 200) { return List<String>.from(jsonDecode(response.body)['movies']); } else { throw Exception('Error response'); }
実行
- [ Start debugging] をクリックして、アプリが読み込まれるまで待ちます。
- ユーザー ID(42)を選択して [Recommend] を選択します。
9. ステップ 4: デスクトップ プラットフォームで Flutter アプリを実行する
Flutter は、Android と iOS だけでなく、Linux、Mac、Windows などのデスクトップ プラットフォームもサポートしています。
Linux
- VSCode のステータスバーで、対象デバイスが に設定されていることを確認します。
- [ Start debugging] をクリックして、アプリが読み込まれるまで待ちます。
- ユーザー ID(42)を選択して [Recommend] を選択します。
Mac
- Mac の場合、アプリがバックエンドに HTTP リクエストを送信するため、適切な利用資格を設定する必要があります。詳しくは、利用資格とアプリ サンドボックスをご覧ください。
このコードをそれぞれ step4/frontend/macOS/Runner/DebugProfile.entitlements
と step4/frontend/macOS/Runner/Release.entitlements
に追加します。
<key>com.apple.security.network.client</key>
<true/>
- VSCode のステータスバーで、対象デバイスが に設定されていることを確認します。
- [ Start debugging] をクリックして、アプリが読み込まれるまで待ちます。
- ユーザー ID(42)を選択して [Recommend] を選択します。
Windows
- VSCode のステータスバーで、対象デバイスが に設定されていることを確認します。
- [ Start debugging] をクリックして、アプリが読み込まれるまで待ちます。
- ユーザー ID(42)を選択して [Recommend] を選択します。
10. ステップ 5: ウェブ プラットフォームで Flutter アプリを実行する
もう 1 つできることは、Flutter アプリにウェブサポートを追加することです。ウェブ プラットフォームは、デフォルトで Flutter アプリに対して自動的に有効になるため、ユーザーが行う作業は起動することだけです。
- VSCode のステータスバーで、対象デバイスが に設定されていることを確認します。
- [ Start debug] をクリックし、アプリが Chrome ブラウザに読み込まれるまで待ちます。
- ユーザー ID(42)を選択して [Recommend] を選択します。
11. 完了
ユーザーに映画をおすすめするフルスタック アプリを作成しました。
このアプリでは映画をおすすめするだけですが、強力なレコメンデーション エンジンを構築するための全体的なワークフローについて学び、Flutter アプリでレコメンデーションを利用するスキルを習得しました。学習した内容は、他のシナリオ(e コマース、食品、ショート動画など)に簡単に応用できます。