Firebase Summit で発表されたすべての情報をご覧ください。Firebase を使用してアプリ開発を加速し、自信を持ってアプリを実行する方法を紹介しています。詳細

AndroidでカスタムTensorFlowLiteモデルを使用する

アプリでカスタムTensorFlow Liteモデルを使用している場合は、Firebase ML を使用してモデルをデプロイできます。モデルを Firebase でデプロイすることにより、アプリの初期ダウンロード サイズを削減し、アプリの新しいバージョンをリリースすることなくアプリの ML モデルを更新できます。また、Remote Config と A/B Testing を使用すると、さまざまなモデルをさまざまなユーザー セットに動的に提供できます。

TensorFlow Lite モデル

TensorFlow Lite モデルは、モバイル デバイスでの実行に最適化された ML モデルです。 TensorFlow Lite モデルを取得するには:

あなたが始める前に

  1. まだ行っていない場合は、 Firebase を Android プロジェクトに追加します
  2. モジュール (アプリ レベル) の Gradle ファイル(通常は<project>/<app-module>/build.gradle ) で、Firebase ML モデル ダウンローダー Android ライブラリの依存関係を追加します。ライブラリのバージョン管理には、 Firebase Android BoMを使用することをお勧めします。

    また、Firebase ML モデル ダウンローダのセットアップの一環として、TensorFlow Lite SDK をアプリに追加する必要があります。

    Java

    dependencies {
        // Import the BoM for the Firebase platform
        implementation platform('com.google.firebase:firebase-bom:31.1.0')
    
        // Add the dependency for the Firebase ML model downloader library
        // When using the BoM, you don't specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-ml-modeldownloader'
    // Also add the dependency for the TensorFlow Lite library and specify its version implementation 'org.tensorflow:tensorflow-lite:2.3.0'
    }

    Firebase Android BoMを使用すると、アプリは常に互換性のあるバージョンの Firebase Android ライブラリを使用します。

    (代替) BoM を使用せずに Firebase ライブラリの依存関係を追加する

    Firebase BoM を使用しないことを選択した場合は、依存関係の行で各 Firebase ライブラリ バージョンを指定する必要があります。

    アプリで複数のFirebase ライブラリを使用する場合は、BoM を使用してライブラリ バージョンを管理することを強くお勧めします。これにより、すべてのバージョンに互換性が確保されます。

    dependencies {
        // Add the dependency for the Firebase ML model downloader library
        // When NOT using the BoM, you must specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-ml-modeldownloader:24.1.1'
    // Also add the dependency for the TensorFlow Lite library and specify its version implementation 'org.tensorflow:tensorflow-lite:2.3.0'
    }

    Kotlin+KTX

    dependencies {
        // Import the BoM for the Firebase platform
        implementation platform('com.google.firebase:firebase-bom:31.1.0')
    
        // Add the dependency for the Firebase ML model downloader library
        // When using the BoM, you don't specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-ml-modeldownloader-ktx'
    // Also add the dependency for the TensorFlow Lite library and specify its version implementation 'org.tensorflow:tensorflow-lite:2.3.0'
    }

    Firebase Android BoMを使用すると、アプリは常に互換性のあるバージョンの Firebase Android ライブラリを使用します。

    (代替) BoM を使用せずに Firebase ライブラリの依存関係を追加する

    Firebase BoM を使用しないことを選択した場合は、依存関係の行で各 Firebase ライブラリ バージョンを指定する必要があります。

    アプリで複数のFirebase ライブラリを使用する場合は、BoM を使用してライブラリ バージョンを管理することを強くお勧めします。これにより、すべてのバージョンに互換性が確保されます。

    dependencies {
        // Add the dependency for the Firebase ML model downloader library
        // When NOT using the BoM, you must specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-ml-modeldownloader-ktx:24.1.1'
    // Also add the dependency for the TensorFlow Lite library and specify its version implementation 'org.tensorflow:tensorflow-lite:2.3.0'
    }
  3. アプリのマニフェストで、INTERNET アクセス許可が必要であることを宣言します:
    <uses-permission android:name="android.permission.INTERNET" />

1. モデルをデプロイする

Firebase コンソールまたは Firebase Admin Python および Node.js SDK のいずれかを使用して、カスタム TensorFlow モデルをデプロイします。カスタム モデルの展開と管理を参照してください。

カスタム モデルを Firebase プロジェクトに追加したら、指定した名前を使用してアプリでモデルを参照できます。 getModel()を呼び出すことで、いつでも新しい TensorFlow Lite モデルをデプロイし、新しいモデルをユーザーのデバイスにダウンロードできます (以下を参照)。

2. モデルをデバイスにダウンロードし、TensorFlow Lite インタープリターを初期化します

アプリで TensorFlow Lite モデルを使用するには、まず Firebase ML SDK を使用してモデルの最新バージョンをデバイスにダウンロードします。次に、モデルを使用して TensorFlow Lite インタープリターをインスタンス化します。

モデルのダウンロードを開始するには、モデル ダウンローダーのgetModel()メソッドを呼び出し、アップロード時にモデルに割り当てた名前、常に最新のモデルをダウンロードするかどうか、およびダウンロードを許可する条件を指定します。

次の 3 つのダウンロード動作から選択できます。

ダウンロードタイプ説明
ローカルモデルデバイスからローカル モデルを取得します。利用可能なローカル モデルがない場合、これはLATEST_MODELのように動作します。モデルの更新を確認することに関心がない場合は、このダウンロード タイプを使用してください。たとえば、Remote Config を使用してモデル名を取得し、常に新しい名前でモデルをアップロードする (推奨)。
LOCAL_MODEL_UPDATE_IN_BACKGROUNDデバイスからローカル モデルを取得し、バックグラウンドでモデルの更新を開始します。利用可能なローカル モデルがない場合、これはLATEST_MODELのように動作します。
最新モデル最新のモデルを入手してください。ローカル モデルが最新バージョンの場合は、ローカル モデルを返します。それ以外の場合は、最新のモデルをダウンロードしてください。この動作は、最新バージョンがダウンロードされるまでブロックされます (推奨されません)。この動作は、明示的に最新バージョンが必要な場合にのみ使用してください。

モデルがダウンロードされたことを確認するまで、モデル関連の機能 (UI の一部をグレー表示または非表示にするなど) を無効にする必要があります。

Java

CustomModelDownloadConditions conditions = new CustomModelDownloadConditions.Builder()
    .requireWifi()  // Also possible: .requireCharging() and .requireDeviceIdle()
    .build();
FirebaseModelDownloader.getInstance()
    .getModel("your_model", DownloadType.LOCAL_MODEL_UPDATE_IN_BACKGROUND, conditions)
    .addOnSuccessListener(new OnSuccessListener<CustomModel>() {
      @Override
      public void onSuccess(CustomModel model) {
        // Download complete. Depending on your app, you could enable the ML
        // feature, or switch from the local model to the remote model, etc.

        // The CustomModel object contains the local path of the model file,
        // which you can use to instantiate a TensorFlow Lite interpreter.
        File modelFile = model.getFile();
        if (modelFile != null) {
            interpreter = new Interpreter(modelFile);
        }
      }
    });

Kotlin+KTX

val conditions = CustomModelDownloadConditions.Builder()
        .requireWifi()  // Also possible: .requireCharging() and .requireDeviceIdle()
        .build()
FirebaseModelDownloader.getInstance()
        .getModel("your_model", DownloadType.LOCAL_MODEL_UPDATE_IN_BACKGROUND,
            conditions)
        .addOnSuccessListener { model: CustomModel? ->
            // Download complete. Depending on your app, you could enable the ML
            // feature, or switch from the local model to the remote model, etc.

            // The CustomModel object contains the local path of the model file,
            // which you can use to instantiate a TensorFlow Lite interpreter.
            val modelFile = model?.file
            if (modelFile != null) {
                interpreter = Interpreter(modelFile)
            }
        }

多くのアプリは初期化コードでダウンロード タスクを開始しますが、モデルの使用が必要になる前であればいつでも開始できます。

3. 入力データの推論を実行する

モデルの入力形状と出力形状を取得する

TensorFlow Lite モデル インタープリターは入力として受け取り、出力として 1 つ以上の多次元配列を生成します。これらの配列には、 byteintlong 、またはfloat値が含まれます。モデルにデータを渡したり、その結果を使用したりする前に、モデルが使用する配列の数と次元 (「形状」) を知っておく必要があります。

モデルを自分で作成した場合、またはモデルの入力と出力の形式が文書化されている場合は、この情報を既に持っている可能性があります。モデルの入力と出力の形状とデータ型がわからない場合は、TensorFlow Lite インタープリターを使用してモデルを調べることができます。例えば:

パイソン

import tensorflow as tf

interpreter = tf.lite.Interpreter(model_path="your_model.tflite")
interpreter.allocate_tensors()

# Print input shape and type
inputs = interpreter.get_input_details()
print('{} input(s):'.format(len(inputs)))
for i in range(0, len(inputs)):
    print('{} {}'.format(inputs[i]['shape'], inputs[i]['dtype']))

# Print output shape and type
outputs = interpreter.get_output_details()
print('\n{} output(s):'.format(len(outputs)))
for i in range(0, len(outputs)):
    print('{} {}'.format(outputs[i]['shape'], outputs[i]['dtype']))

出力例:

1 input(s):
[  1 224 224   3] <class 'numpy.float32'>

1 output(s):
[1 1000] <class 'numpy.float32'>

インタープリターを実行する

モデルの入力と出力の形式を決定したら、入力データを取得し、モデルに適した形状の入力を取得するために必要なデータの変換を実行します。

たとえば、 [1 224 224 3]浮動小数点値の入力形状を持つ画像分類モデルがある場合、次の例に示すように、 Bitmapオブジェクトから入力ByteBufferを生成できます。

Java

Bitmap bitmap = Bitmap.createScaledBitmap(yourInputImage, 224, 224, true);
ByteBuffer input = ByteBuffer.allocateDirect(224 * 224 * 3 * 4).order(ByteOrder.nativeOrder());
for (int y = 0; y < 224; y++) {
    for (int x = 0; x < 224; x++) {
        int px = bitmap.getPixel(x, y);

        // Get channel values from the pixel value.
        int r = Color.red(px);
        int g = Color.green(px);
        int b = Color.blue(px);

        // Normalize channel values to [-1.0, 1.0]. This requirement depends
        // on the model. For example, some models might require values to be
        // normalized to the range [0.0, 1.0] instead.
        float rf = (r - 127) / 255.0f;
        float gf = (g - 127) / 255.0f;
        float bf = (b - 127) / 255.0f;

        input.putFloat(rf);
        input.putFloat(gf);
        input.putFloat(bf);
    }
}

Kotlin+KTX

val bitmap = Bitmap.createScaledBitmap(yourInputImage, 224, 224, true)
val input = ByteBuffer.allocateDirect(224*224*3*4).order(ByteOrder.nativeOrder())
for (y in 0 until 224) {
    for (x in 0 until 224) {
        val px = bitmap.getPixel(x, y)

        // Get channel values from the pixel value.
        val r = Color.red(px)
        val g = Color.green(px)
        val b = Color.blue(px)

        // Normalize channel values to [-1.0, 1.0]. This requirement depends on the model.
        // For example, some models might require values to be normalized to the range
        // [0.0, 1.0] instead.
        val rf = (r - 127) / 255f
        val gf = (g - 127) / 255f
        val bf = (b - 127) / 255f

        input.putFloat(rf)
        input.putFloat(gf)
        input.putFloat(bf)
    }
}

次に、モデルの出力を格納するのに十分な大きさのByteBufferを割り当て、入力バッファーと出力バッファーを TensorFlow Lite インタープリターのrun()メソッドに渡します。たとえば、 [1 1000]浮動小数点値の出力形状の場合:

Java

int bufferSize = 1000 * java.lang.Float.SIZE / java.lang.Byte.SIZE;
ByteBuffer modelOutput = ByteBuffer.allocateDirect(bufferSize).order(ByteOrder.nativeOrder());
interpreter.run(input, modelOutput);

Kotlin+KTX

val bufferSize = 1000 * java.lang.Float.SIZE / java.lang.Byte.SIZE
val modelOutput = ByteBuffer.allocateDirect(bufferSize).order(ByteOrder.nativeOrder())
interpreter?.run(input, modelOutput)

出力の使用方法は、使用しているモデルによって異なります。

たとえば、分類を実行している場合、次のステップとして、結果のインデックスをそれらが表すラベルにマップすることがあります。

Java

modelOutput.rewind();
FloatBuffer probabilities = modelOutput.asFloatBuffer();
try {
    BufferedReader reader = new BufferedReader(
            new InputStreamReader(getAssets().open("custom_labels.txt")));
    for (int i = 0; i < probabilities.capacity(); i++) {
        String label = reader.readLine();
        float probability = probabilities.get(i);
        Log.i(TAG, String.format("%s: %1.4f", label, probability));
    }
} catch (IOException e) {
    // File not found?
}

Kotlin+KTX

modelOutput.rewind()
val probabilities = modelOutput.asFloatBuffer()
try {
    val reader = BufferedReader(
            InputStreamReader(assets.open("custom_labels.txt")))
    for (i in probabilities.capacity()) {
        val label: String = reader.readLine()
        val probability = probabilities.get(i)
        println("$label: $probability")
    }
} catch (e: IOException) {
    // File not found?
}

付録: モデルのセキュリティ

TensorFlow Lite モデルを Firebase ML で使用できるようにする方法に関係なく、Firebase ML はそれらを標準のシリアル化された protobuf 形式でローカル ストレージに保存します。

理論的には、これは誰でもあなたのモデルをコピーできることを意味します。ただし、実際には、ほとんどのモデルはアプリケーション固有であり、最適化によって難読化されているため、競合他社がコードを逆アセンブルして再利用する場合と同様のリスクがあります。ただし、アプリでカスタム モデルを使用する前に、このリスクを認識しておく必要があります。

Android API レベル 21 (Lollipop) 以降では、モデルは自動バックアップから除外されたディレクトリにダウンロードされます。

Android API レベル 20 以前では、モデルは app-private 内部ストレージのcom.google.firebase.ml.custom.modelsという名前のディレクトリにダウンロードされます。 BackupAgentを使用してファイルのバックアップを有効にした場合、このディレクトリを除外することを選択できます。