TensorFlow Lite と Firebase を使用してアプリにおすすめを追加する - Android Codelab

1. 概要

TensorFlow Lite と Firebase によるおすすめ機能の Codelab へようこそ。この Codelab では、TensorFlow Lite と Firebase を使用してレコメンデーション モデルをアプリにデプロイする方法について説明します。この Codelab は、こちらの TensorFlow Lite のに基づいています。

レコメンデーションを使用すると、アプリは機械学習を使用して、各ユーザーに最も関連性の高いコンテンツをインテリジェントに配信できます。過去のユーザー行動を考慮し、他の多数のユーザーの集計行動に基づいてトレーニングされたモデルを使用して、ユーザーが今後関心を持つ可能性のあるアプリのコンテンツを提案します。

このチュートリアルでは、Firebase アナリティクスを使用してアプリのユーザーからデータを取得し、そのデータからレコメンデーション用の ML モデルを構築し、そのモデルを Android アプリで使用して推論を実行し、レコメンデーションを取得する方法について説明します。特に、ユーザーが以前に高く評価した映画のリストに基づいて、ユーザーが視聴する可能性が高い映画がおすすめとして表示されます。

学習内容

  • Firebase アナリティクスを Android アプリに統合してユーザー行動データを収集する
  • そのデータを Google BigQuery にエクスポートする
  • データを前処理して TF Lite レコメンデーション モデルをトレーニングする
  • TF Lite モデルを Firebase ML にデプロイし、アプリからアクセスする
  • モデルを使用してデバイス上の推論を実行し、ユーザーにおすすめを提案する

必要なもの

  • 最新の Android Studio バージョン。
  • サンプルコード。
  • Android 7 以降を搭載し、Google Play 開発者サービス 9.8 以降がインストールされているテストデバイス、または Google Play 開発者サービス 9.8 以降がインストールされているエミュレータ
  • デバイスを使用している場合は、接続ケーブル。

このチュートリアルをどのように使用されますか?

通読のみ 通読して演習を行う

Android アプリ作成のご経験についてお答えください。

初心者 中級者 上級者

2. サンプルコードを取得する

コマンドラインから GitHub リポジトリのクローンを作成します。

$ git clone https://github.com/FirebaseExtended/codelab-contentrecommendation-android.git

3. スターター アプリをインポートする

Android Studio で、サンプルコードのダウンロードから codelab-recommendations-android ディレクトリ(android_studio_folder.png)を選択します([File] > [Open] > .../codelab-recommendations-android/start)。

Android Studio で開始プロジェクトが開いているはずです。

4. Firebase コンソール プロジェクトを作成する

新しいプロジェクトを作成する

  1. Firebase コンソールに移動します。
  2. [プロジェクトを追加](初めての場合は [プロジェクトを作成])を選択します。
  3. プロジェクト名を選択または入力し、[続行] をクリックします。
  4. [このプロジェクトで Google アナリティクスを有効にする] が有効になっていることを確認します。
  5. Firebase コンソールで残りの設定手順を実施した後、[プロジェクトを作成](既存の Google プロジェクトを使用する場合は [Firebase を追加])をクリックします。

5. Firebase を追加する

  1. 新しいプロジェクトの概要画面で、Android アイコンをクリックして設定ワークフローを起動します。
  2. Codelab のパッケージ名(com.google.firebase.codelabs.recommendations)を入力します。
  3. [アプリの登録] を選択します。

アプリに google-services.json ファイルを追加する

パッケージ名を追加して [登録] を選択したら、[google-services.json をダウンロード] をクリックして Firebase Android 構成ファイルを取得し、google-services.json ファイルをプロジェクトの app ディレクトリにコピーします。ファイルがダウンロードされたら、コンソールに表示される次の手順をスキップできます(これらの手順は、build-android-start プロジェクトですでに完了しています)。

アプリに google-services プラグインを追加する

google-services プラグインは、google-services.json ファイルを使用して、Firebase を使用するようにアプリを構成します。プロジェクトの build.gradle.kts ファイルには、次の行がすでに追加されているはずです(確認してください)。

app/build.grade.kts

plugins {
    id("com.google.gms.google-services")
}

build.grade.kts

plugins {
    id("com.google.gms.google-services") version "4.3.15" apply false
}

プロジェクトを Gradle ファイルと同期する

すべての依存関係がアプリで使用可能であることを確認するには、この時点でプロジェクトを Gradle ファイルと同期する必要があります。Android Studio のツールバーから [File] > [Sync Project with Gradle Files] を選択します。

6. スターター アプリを実行する

プロジェクトを Android Studio にインポートし、JSON ファイルで google-services プラグインを構成したので、アプリを初めて実行する準備ができました。Android デバイスを接続し、Android Studio ツールバーの [Run](execute.png)をクリックします。

デバイスでアプリが起動するはずです。この時点で、映画のリストを含むタブ、[高評価の映画] タブ、[おすすめ] タブが表示される、機能するアプリが完成しています。映画のリストで映画をクリックすると、その映画がお気に入りリストに追加されます。Codelab の残りの手順を完了すると、[おすすめ] タブで映画のレコメンデーションを生成できるようになります。

7. アプリに Firebase アナリティクスを追加する

このステップでは、Firebase アナリティクスをアプリに追加して、ユーザー行動データ(この場合は、ユーザーが気に入っている映画)を記録します。このデータは、今後のステップで集計され、レコメンデーション モデルのトレーニングに使用されます。

Firebase BoM とアナリティクスの依存関係を追加

Firebase アナリティクスをアプリに追加するには、次の依存関係が必要です。これらの依存関係は、app/build.gradle.kts ファイルにすでに含まれている必要があります(確認してください)。

app/build.grade.kts

implementation(platform("com.google.firebase:firebase-bom:32.0.0"))
implementation("com.google.firebase:firebase-analytics-ktx")

アプリで Firebase アナリティクスを設定する

LikedMoviesViewModel には、ユーザーがお気に入りに登録した映画を保存する関数が含まれています。ユーザーが新しい映画に「高評価」を付けるたびに、アナリティクス ログイベントを送信して、その高評価を記録したいとします。

ユーザーが映画の「高評価」をクリックしたときにアナリティクス イベントを登録するには、次のコードで onMovieLiked 関数を追加します。

LikedMoviesViewModel.kt

import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.analytics.ktx.logEvent
import com.google.firebase.ktx.Firebase


class LikedMoviesViewModel internal constructor (application: Application) : AndroidViewModel(application) {

    ...

    fun onMovieLiked(movie: Movie) {
        movies.setLike(movie, true)
        logAnalyticsEvent(movie.id.toString())
    }
       
}

次のフィールドと関数を追加して、ユーザーの [高評価] リストに映画が追加されたときにアナリティクス イベントを記録します。

LikedMoviesViewModel.kt

import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.analytics.ktx.logEvent
import com.google.firebase.ktx.Firebase


class LikedMoviesViewModel internal constructor (application: Application) : AndroidViewModel(application) {
    ...
    private val firebaseAnalytics = Firebase.analytics

    ...

    /**
     * Logs an event in Firebase Analytics that is used in aggregate to train the recommendations
     * model.
     */
    private fun logAnalyticsEvent(id: String) {
        firebaseAnalytics.logEvent(FirebaseAnalytics.Event.SELECT_ITEM) {
            param(FirebaseAnalytics.Param.ITEM_ID, id)
        }
    }

8. アナリティクスとの統合をテストする

このステップでは、アプリでアナリティクス イベントを生成し、Firebase コンソールに送信されていることを確認します。

アナリティクスのデバッグ ロギングを有効にする

Firebase Analytics は、ユーザーのバッテリー駆動時間を最大限に活用するように設計されており、デバイス上でイベントを一括処理し、Firebase に送信するのはごくまれです。デバッグ目的で、この動作を無効にして、イベントがリアルタイムでロギングされるようにするには、シェルで次のコマンドを実行します。

ターミナル

adb shell setprop debug.firebase.analytics.app com.google.firebase.codelabs.recommendations

アナリティクス イベントが生成されていることを確認する

  1. Android Studio で Logcat ウィンドウを開き、アプリからのロギングを確認します。
  2. Logcat フィルタを文字列「Logging event」に設定します。
  3. アプリで映画を高評価するたびに、「select_item」アナリティクス イベントが送信されていることを確認します。

これで、Firebase アナリティクスがアプリに正常に統合されました。ユーザーがアプリを使用して映画に高評価を付けると、高評価が集計されて記録されます。この Codelab の残りの部分では、この集計データを使用したおすすめモデルのトレーニングを行います。次の手順は省略可能です。Logcat で確認したアナリティクス イベントが Firebase コンソールにもストリーミングされるようにします。次のページに進んでいただいても構いません。

省略可: Firebase コンソールでアナリティクス イベントを確認する

  1. Firebase コンソールに移動します。
  2. [アナリティクス] で [DebugView] を選択します。
  3. Android Studio で [実行] を選択してアプリを起動し、[高評価] リストに映画を追加します。
  4. Firebase コンソールの DebugView で、アプリで映画を追加するときにこれらのイベントが記録されていることを確認します。

9. アナリティクス データを BigQuery にエクスポートする

BigQuery は、大量のデータを検証して処理できる Google Cloud プロダクトです。このステップでは、Firebase コンソール プロジェクトを BigQuery に接続して、アプリで生成されたアナリティクス データが BigQuery に自動的にエクスポートされるようにします。

BigQuery エクスポートを有効にする

  1. Firebase コンソールに移動します。
  2. [プロジェクトの概要] の横にある設定の歯車アイコンを選択し、[プロジェクトの設定] を選択します。
  3. [Integrations] タブを選択します。
  4. [BigQuery] ブロック内の [リンク](または [管理])を選択します。
  5. [Firebase と BigQuery のリンクについて] の手順で [次へ] を選択します。
  6. [統合を設定] セクションで、スイッチをクリックして Google アナリティクス データの送信を有効にし、[BigQuery にリンク] を選択します。

これで、Firebase コンソール プロジェクトで Firebase アナリティクスのイベントデータを BigQuery に自動的に送信できるようになりました。この処理は、ユーザーが操作しなくても自動的に実行されます。ただし、BigQuery にアナリティクス データセットを作成する最初のエクスポートは、24 時間以内に行われない場合があります。データセットが作成されると、Firebase は新しいアナリティクス イベントを BigQuery の 1 日単位のテーブルに継続的にエクスポートし、過去の日のイベントをイベント テーブルにグループ化します。

レコメンデーション モデルをトレーニングするには、大量のデータが必要です。大量のデータを生成するアプリはまだないため、次のステップでは、サンプル データセットを BigQuery にインポートして、このチュートリアルの残りの部分で使用します。

10. BigQuery を使用してモデル トレーニング データを取得する

Firebase コンソールを BigQuery に接続してエクスポートを設定したので、しばらくするとアプリ アナリティクスのイベントデータが BigQuery コンソールに自動的に表示されます。このチュートリアルで使用する初期データを取得するため、このステップでは、既存のサンプル データセットを BigQuery コンソールにインポートして、おすすめモデルのトレーニングに使用します。

サンプル データセットを BigQuery にインポートする

  1. Google Cloud コンソールで BigQuery ダッシュボードに移動します。
  2. メニューでプロジェクト名を選択します。
  3. BigQuery の左側のナビゲーションの下部にあるプロジェクト名を選択して、詳細を表示します。
  4. [データセットを作成] を選択して、データセット作成パネルを開きます。
  5. [データセット ID] に「firebase_recommendations_dataset」と入力し、[データセットを作成] を選択します。
  6. 新しいデータセットが左側のメニューのプロジェクト名の下に表示されます。これをクリックします。
  7. [テーブルを作成] を選択して、テーブル作成パネルを開きます。
  8. [テーブルの作成元] で [Google Cloud Storage] を選択します。
  9. [GCS バケットからファイルを選択] フィールドに「gs://firebase-recommendations/recommendations-test/formatted_data_filtered.txt」と入力します。
  10. [ファイル形式] プルダウンで [JSONL] を選択します。
  11. [テーブル名] に「recommendations_table」と入力します。
  12. [スキーマ] > [自動検出] > [スキーマと入力パラメータ] のチェックボックスをオンにします。
  13. [テーブルを作成] を選択します。

サンプル データセットを探索する

この時点で、必要に応じてスキーマを調べて、このデータセットをプレビューできます。

  1. 左側のメニューで [firebase-recommendations-dataset] を選択して、含まれているテーブルを開きます。
  2. recommendations-table テーブルを選択して、テーブル スキーマを表示します。
  3. [プレビュー] を選択すると、このテーブルに含まれる実際のアナリティクス イベントデータが表示されます。

サービス アカウントの認証情報を作成する

次に、Google Cloud コンソール プロジェクトでサービス アカウントの認証情報を作成します。この認証情報は、次のステップで Colab 環境で BigQuery データにアクセスして読み込むために使用します。

  1. Google Cloud プロジェクトの課金が有効になっていることを確認します。
  2. BigQuery API と BigQuery Storage API を有効にします。<こちらをクリック>
  3. [サービス アカウント キーの作成] ページに移動します。
  4. [サービス アカウント] リストから [新しいサービス アカウント] を選択します。
  5. [サービス アカウント名] フィールドに名前を入力します。
  6. [役割] リストで、[プロジェクト] > [オーナー] を選択します。
  7. [作成] をクリックします。キーを含む JSON ファイルがパソコンにダウンロードされます。

次のステップでは、Google Colab を使用してこのデータを前処理し、おすすめモデルをトレーニングします。

11. データを前処理してレコメンデーション モデルをトレーニングします。

この手順では、Colab ノートブックを使用して次の操作を行います。

  1. BigQuery データを Colab ノートブックにインポートする
  2. データを前処理してモデルのトレーニング用に準備する
  3. アナリティクス データでレコメンデーション モデルをトレーニングする
  4. モデルを TF Lite モデルとしてエクスポートする
  5. モデルを Firebase コンソールにデプロイして、アプリで使用できるようにする

Colab トレーニング ノートブックを起動する前に、Firebase Model Management API を有効にして、Colab がトレーニング済みモデルを Firebase コンソールにデプロイできるようにします。

Firebase Model Management API を有効にする

ML モデルを保存するバケットを作成する

Firebase コンソールで [Storage] に移動し、[始める] をクリックします。fbbea78f0eb3dc9f.png

ダイアログに沿ってバケットを設定します。

19517c0d6d2aa14d.png

Firebase ML API を有効にする

Google Cloud コンソールの Firebase ML API ページに移動し、[有効にする] をクリックします。

Colab ノートブックを使用してモデルをトレーニングしてデプロイする

次のリンクを使用して Colab ノートブックを開き、手順を完了します。Colab ノートブックでの手順を完了すると、Firebase コンソールにデプロイされた TF Lite モデルファイルがアプリに同期されます。

Colab で開く

12. アプリにモデルをダウンロードする

このステップでは、先ほどトレーニングしたモデルを Firebase Machine Learning からダウンロードするようにアプリを変更します。

Firebase ML の依存関係を追加する

アプリで Firebase ML モデルを使用するには、次の依存関係が必要です。これはすでに追加されているはずです(確認してください)。

app/build.grade.kts

implementation("com.google.firebase:firebase-ml-modeldownloader:24.1.2")

Firebase Model Manager API を使用してモデルをダウンロードする

次のコードを RecommendationClient.kt にコピーして、モデルのダウンロードが行われる条件を設定し、リモートモデルをアプリと同期するダウンロード タスクを作成します。

RecommendationClient.kt

    private fun downloadModel(modelName: String) {
        val conditions = CustomModelDownloadConditions.Builder()
            .requireWifi()
            .build()
        FirebaseModelDownloader.getInstance()
            .getModel(modelName, DownloadType.LOCAL_MODEL, conditions)
            .addOnCompleteListener {
                if (!it.isSuccessful) {
                    showToast(context, "Failed to get model file.")
                } else {
                    showToast(context, "Downloaded remote model: $modelName")
                    GlobalScope.launch { initializeInterpreter(it.result) }
                }
            }
            .addOnFailureListener {
                showToast(context, "Model download failed for recommendations, please check your connection.")
            }
    }

13. Tensorflow Lite レコメンデーション モデルをアプリに統合する

Tensorflow Lite ランタイムを使用すると、アプリでモデルを使用してレコメンデーションを生成できます。前の手順では、ダウンロードしたモデルファイルを使用して TFlite インタープリタを初期化しました。このステップでは、まず推論ステップでモデルに付随する辞書とラベルを読み込みます。次に、モデルへの入力を生成するための前処理と、推論から結果を抽出する後処理を追加します。

辞書とラベルを読み込む

レコメンデーション モデルによるレコメンデーション候補の生成に使用されるラベルは、res/assets フォルダの sorted_movie_vocab.json ファイルに記載されています。次のコードをコピーして、これらの候補を読み込みます。

RecommendationClient.kt

    /** Load recommendation candidate list.  */
    private suspend fun loadCandidateList() {
        return withContext(Dispatchers.IO) {
            val collection = MovieRepository.getInstance(context).getContent()
            for (item in collection) {
                candidates[item.id] = item
            }
            Log.v(TAG, "Candidate list loaded.")
        }
    }

前処理を実装する

前処理のステップでは、モデルの想定と一致するように入力データの形式を変更します。ここでは、ユーザーの「高評価」がまだ多く生成されていない場合は、入力の長さをプレースホルダ値でパディングします。以下のコードをコピーします。

RecommendationClient.kt

    /** Given a list of selected items, preprocess to get tflite input.  */
    @Synchronized
    private suspend fun preprocess(selectedMovies: List<Movie>): IntArray {
        return withContext(Dispatchers.Default) {
            val inputContext = IntArray(config.inputLength)
            for (i in 0 until config.inputLength) {
                if (i < selectedMovies.size) {
                    val (id) = selectedMovies[i]
                    inputContext[i] = id
                } else {
                    // Padding input.
                    inputContext[i] = config.pad
                }
            }
            inputContext
        }
    }


インタープリタを実行して推奨事項を生成する

ここでは、前の手順でダウンロードしたモデルを使用して、前処理済みの入力に対して推論を実行します。モデルの入力と出力のタイプを設定し、推論を実行して映画のレコメンデーションを生成します。次のコードをアプリにコピーします。

RecommendationClient.kt

    /** Given a list of selected items, and returns the recommendation results.  */
    @Synchronized
    suspend fun recommend(selectedMovies: List<Movie>): List<Result> {
        return withContext(Dispatchers.Default) {
            val inputs = arrayOf<Any>(preprocess(selectedMovies))

            // Run inference.
            val outputIds = IntArray(config.outputLength)
            val confidences = FloatArray(config.outputLength)
            val outputs: MutableMap<Int, Any> = HashMap()
            outputs[config.outputIdsIndex] = outputIds
            outputs[config.outputScoresIndex] = confidences
            tflite?.let {
                it.runForMultipleInputsOutputs(inputs, outputs)
                postprocess(outputIds, confidences, selectedMovies)
            } ?: run {
                Log.e(TAG, "No tflite interpreter loaded")
                emptyList()
            }
        }
    }



ポスト処理を実装する

最後に、このステップでモデルの出力をポストプロセッシングし、信頼度が最も高い結果を選択し、含まれる値(ユーザーがすでに高評価を付けた映画)を削除します。次のコードをアプリにコピーします。

RecommendationClient.kt

    /** Postprocess to gets results from tflite inference.  */
    @Synchronized
    private suspend fun postprocess(
        outputIds: IntArray, confidences: FloatArray, selectedMovies: List<Movie>
    ): List<Result> {
        return withContext(Dispatchers.Default) {
            val results = ArrayList<Result>()

            // Add recommendation results. Filter null or contained items.
            for (i in outputIds.indices) {
                if (results.size >= config.topK) {
                    Log.v(TAG, String.format("Selected top K: %d. Ignore the rest.", config.topK))
                    break
                }
                val id = outputIds[i]
                val item = candidates[id]
                if (item == null) {
                    Log.v(TAG, String.format("Inference output[%d]. Id: %s is null", i, id))
                    continue
                }
                if (selectedMovies.contains(item)) {
                    Log.v(TAG, String.format("Inference output[%d]. Id: %s is contained", i, id))
                    continue
                }
                val result = Result(
                    id, item,
                    confidences[i]
                )
                results.add(result)
                Log.v(TAG, String.format("Inference output[%d]. Result: %s", i, result))
            }
            results
        }
    }


アプリをテストする

アプリを再実行します。映画をいくつか選択すると、新しいモデルが自動的にダウンロードされ、おすすめの生成が開始されます。

14. 完了

TensorFlow Lite と Firebase を使用して、アプリにおすすめ機能を構築しました。この Codelab で説明する手法とパイプラインは一般化でき、他のタイプのおすすめコンテンツの提供にも使用できます。

学習した内容

  • Firebase ML
  • Firebase アナリティクス
  • アナリティクス イベントを BigQuery にエクスポートする
  • アナリティクス イベントを事前処理する
  • レコメンデーションの TensorFlow モデルをトレーニングする
  • モデルをエクスポートして Firebase コンソールにデプロイする
  • アプリで映画のおすすめを提供する

次のステップ

  • アプリに Firebase ML のおすすめを実装する。

詳細

質問がある場合

問題を報告する