TensorFlow Lite-Modell für Inferenz mit ML Kit unter Android verwenden

Mit ML Kit können Sie die Inferenz auf dem Gerät mit einem TensorFlow Lite-Modell ausführen.

Für diese API ist Android SDK-Level 16 (Jelly Bean) oder höher erforderlich.

Hinweis

  1. Falls noch nicht geschehen, fügen Sie Ihrem Android-Projekt Firebase hinzu.
  2. Fügen Sie die Abhängigkeiten für die ML Kit Android-Bibliotheken in die Gradle-Datei Ihres Moduls (auf App-Ebene, in der Regel app/build.gradle) ein:
    apply plugin: 'com.android.application'
    apply plugin: 'com.google.gms.google-services'
    
    dependencies {
      // ...
    
      implementation 'com.google.firebase:firebase-ml-model-interpreter:22.0.3'
    }
  3. Konvertieren Sie das TensorFlow-Modell, das Sie verwenden möchten, in das TensorFlow Lite-Format. Weitere Informationen finden Sie unter TOCO: TensorFlow Lite Optimizing Converter.

Modell hosten oder bündeln

Bevor Sie ein TensorFlow Lite-Modell für die Inferenz in Ihrer App verwenden können, müssen Sie es für ML Kit verfügbar machen. Für ML Kit können TensorFlow Lite-Modelle verwendet werden, die remote mit Firebase gehostet werden, im App-Binärpaket enthalten sind oder beides.

Wenn Sie ein Modell auf Firebase hosten, können Sie es aktualisieren, ohne eine neue App-Version zu veröffentlichen. Außerdem können Sie mit Remote Config und A/B Testing verschiedenen Nutzergruppen dynamisch unterschiedliche Modelle bereitstellen.

Wenn Sie das Modell nur über Firebase hosten und nicht in Ihre App einbinden, können Sie die ursprüngliche Downloadgröße Ihrer App verringern. Wenn das Modell jedoch nicht in Ihre App eingebunden ist, sind alle modellbezogenen Funktionen erst verfügbar, wenn Ihre App das Modell zum ersten Mal herunterlädt.

Wenn Sie Ihr Modell mit Ihrer App bündeln, können Sie dafür sorgen, dass die ML-Funktionen Ihrer App auch dann funktionieren, wenn das in Firebase gehostete Modell nicht verfügbar ist.

Modelle in Firebase hosten

So hosten Sie Ihr TensorFlow Lite-Modell auf Firebase:

  1. Klicken Sie im Bereich ML Kit der Firebase-Konsole auf den Tab Benutzerdefiniert.
  2. Klicken Sie auf Benutzerdefiniertes Modell hinzufügen (oder Weiteres Modell hinzufügen).
  3. Geben Sie einen Namen an, der zur Identifizierung Ihres Modells in Ihrem Firebase-Projekt verwendet wird, und laden Sie dann die TensorFlow Lite-Modelldatei hoch (die normalerweise auf .tflite oder .lite endet).
  4. Deklarieren Sie im Manifest Ihrer App, dass die INTERNET-Berechtigung erforderlich ist:
    <uses-permission android:name="android.permission.INTERNET" />

Nachdem Sie Ihrem Firebase-Projekt ein benutzerdefiniertes Modell hinzugefügt haben, können Sie in Ihren Apps anhand des von Ihnen angegebenen Namens auf das Modell verweisen. Sie können jederzeit ein neues TensorFlow Lite-Modell hochladen. Ihre App lädt das neue Modell herunter und verwendet es beim nächsten Neustart. Sie können die Gerätebedingungen festlegen, die erfüllt sein müssen, damit Ihre App versucht, das Modell zu aktualisieren (siehe unten).

Modelle mit einer App bündeln

Wenn Sie Ihr TensorFlow Lite-Modell mit Ihrer App bündeln möchten, kopieren Sie die Modelldatei (die normalerweise auf .tflite oder .lite endet) in den Ordner assets/ Ihrer App. Möglicherweise müssen Sie den Ordner zuerst erstellen. Klicken Sie dazu mit der rechten Maustaste auf den Ordner app/ und dann auf Neu > Ordner > Assets-Ordner.

Fügen Sie dann der Datei build.gradle Ihrer App Folgendes hinzu, damit Gradle die Modelle beim Erstellen der App nicht komprimiert:

android {

    // ...

    aaptOptions {
        noCompress "tflite"  // Your model's file extension: "tflite", "lite", etc.
    }
}

Die Modelldatei wird in das App-Paket aufgenommen und ist für ML Kit als Roh-Asset verfügbar.

Modell laden

Wenn Sie Ihr TensorFlow Lite-Modell in Ihrer App verwenden möchten, müssen Sie ML Kit zuerst mit den Speicherorten konfigurieren, an denen Ihr Modell verfügbar ist: remote über Firebase, im lokalen Speicher oder beides. Wenn Sie sowohl ein lokales als auch ein Remote-Modell angeben, können Sie das Remote-Modell verwenden, sofern es verfügbar ist, und auf das lokal gespeicherte Modell zurückgreifen, wenn das Remote-Modell nicht verfügbar ist.

In Firebase gehostetes Modell konfigurieren

Wenn Sie Ihr Modell mit Firebase gehostet haben, erstellen Sie ein FirebaseCustomRemoteModel-Objekt und geben Sie den Namen an, den Sie dem Modell beim Hochladen zugewiesen haben:

Java

FirebaseCustomRemoteModel remoteModel =
        new FirebaseCustomRemoteModel.Builder("your_model").build();

Kotlin

val remoteModel = FirebaseCustomRemoteModel.Builder("your_model").build()

Starten Sie dann die Aufgabe zum Herunterladen des Modells und geben Sie die Bedingungen an, unter denen Sie das Herunterladen zulassen möchten. Wenn das Modell nicht auf dem Gerät vorhanden ist oder eine neuere Version des Modells verfügbar ist, wird das Modell asynchron von Firebase heruntergeladen:

Java

FirebaseModelDownloadConditions conditions = new FirebaseModelDownloadConditions.Builder()
        .requireWifi()
        .build();
FirebaseModelManager.getInstance().download(remoteModel, conditions)
        .addOnCompleteListener(new OnCompleteListener<Void>() {
            @Override
            public void onComplete(@NonNull Task<Void> task) {
                // Success.
            }
        });

Kotlin

val conditions = FirebaseModelDownloadConditions.Builder()
    .requireWifi()
    .build()
FirebaseModelManager.getInstance().download(remoteModel, conditions)
    .addOnCompleteListener {
        // Success.
    }

Viele Apps starten den Downloadvorgang in ihrem Initialisierungscode, aber Sie können dies jederzeit tun, bevor Sie das Modell verwenden müssen.

Lokales Modell konfigurieren

Wenn Sie das Modell mit Ihrer App gebündelt haben, erstellen Sie ein FirebaseCustomLocalModel-Objekt und geben Sie den Dateinamen des TensorFlow Lite-Modells an:

Java

FirebaseCustomLocalModel localModel = new FirebaseCustomLocalModel.Builder()
        .setAssetFilePath("your_model.tflite")
        .build();

Kotlin

val localModel = FirebaseCustomLocalModel.Builder()
    .setAssetFilePath("your_model.tflite")
    .build()

Interpreter aus Ihrem Modell erstellen

Nachdem Sie die Modellquellen konfiguriert haben, erstellen Sie ein FirebaseModelInterpreter-Objekt aus einer der Quellen.

Wenn Sie nur ein lokal gebündeltes Modell haben, erstellen Sie einfach einen Interpreter aus Ihrem FirebaseCustomLocalModel-Objekt:

Java

FirebaseModelInterpreter interpreter;
try {
    FirebaseModelInterpreterOptions options =
            new FirebaseModelInterpreterOptions.Builder(localModel).build();
    interpreter = FirebaseModelInterpreter.getInstance(options);
} catch (FirebaseMLException e) {
    // ...
}

Kotlin

val options = FirebaseModelInterpreterOptions.Builder(localModel).build()
val interpreter = FirebaseModelInterpreter.getInstance(options)

Wenn Sie ein Modell haben, das auf einem Remote-Server gehostet wird, müssen Sie prüfen, ob es heruntergeladen wurde, bevor Sie es ausführen. Sie können den Status des Modelldownloads mit der Methode isModelDownloaded() des Modellmanagers prüfen.

Sie müssen dies zwar nur vor dem Ausführen des Interpreters bestätigen, aber wenn Sie sowohl ein remote gehostetes als auch ein lokal gebündeltes Modell haben, kann es sinnvoll sein, diese Prüfung beim Instanziieren des Modellinterpreters durchzuführen: Erstellen Sie einen Interpreter aus dem Remote-Modell, wenn es heruntergeladen wurde, und andernfalls aus dem lokalen Modell.

Java

FirebaseModelManager.getInstance().isModelDownloaded(remoteModel)
        .addOnSuccessListener(new OnSuccessListener<Boolean>() {
            @Override
            public void onSuccess(Boolean isDownloaded) {
                FirebaseModelInterpreterOptions options;
                if (isDownloaded) {
                    options = new FirebaseModelInterpreterOptions.Builder(remoteModel).build();
                } else {
                    options = new FirebaseModelInterpreterOptions.Builder(localModel).build();
                }
                FirebaseModelInterpreter interpreter = FirebaseModelInterpreter.getInstance(options);
                // ...
            }
        });

Kotlin

FirebaseModelManager.getInstance().isModelDownloaded(remoteModel)
    .addOnSuccessListener { isDownloaded -> 
    val options =
        if (isDownloaded) {
            FirebaseModelInterpreterOptions.Builder(remoteModel).build()
        } else {
            FirebaseModelInterpreterOptions.Builder(localModel).build()
        }
    val interpreter = FirebaseModelInterpreter.getInstance(options)
}

Wenn Sie nur ein Remote-Modell haben, sollten Sie modellbezogene Funktionen deaktivieren, z. B. einen Teil der Benutzeroberfläche ausblenden oder ausgrauen, bis Sie bestätigen, dass das Modell heruntergeladen wurde. Dazu können Sie einen Listener an die download()-Methode des Modellmanagers anhängen:

Java

FirebaseModelManager.getInstance().download(remoteModel, conditions)
        .addOnSuccessListener(new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void v) {
              // Download complete. Depending on your app, you could enable
              // the ML feature, or switch from the local model to the remote
              // model, etc.
            }
        });

Kotlin

FirebaseModelManager.getInstance().download(remoteModel, conditions)
    .addOnCompleteListener {
        // Download complete. Depending on your app, you could enable the ML
        // feature, or switch from the local model to the remote model, etc.
    }

Eingabe und Ausgabe des Modells angeben

Konfigurieren Sie als Nächstes die Ein- und Ausgabeformate des Modellinterpreters.

Ein TensorFlow Lite-Modell verwendet als Eingabe und erzeugt als Ausgabe ein oder mehrere mehrdimensionale Arrays. Diese Arrays enthalten entweder byte-, int-, long- oder float-Werte. Sie müssen ML Kit mit der Anzahl und den Dimensionen („Form“) der Arrays konfigurieren, die von Ihrem Modell verwendet werden.

Wenn Sie die Form und den Datentyp der Ein- und Ausgabe Ihres Modells nicht kennen, können Sie den TensorFlow Lite Python-Interpreter verwenden, um Ihr Modell zu untersuchen. Beispiel:

import tensorflow as tf

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

# Print input shape and type
print(interpreter.get_input_details()[0]['shape'])  # Example: [1 224 224 3]
print(interpreter.get_input_details()[0]['dtype'])  # Example: <class 'numpy.float32'>

# Print output shape and type
print(interpreter.get_output_details()[0]['shape'])  # Example: [1 1000]
print(interpreter.get_output_details()[0]['dtype'])  # Example: <class 'numpy.float32'>

Nachdem Sie das Format der Ein- und Ausgabe Ihres Modells festgelegt haben, können Sie den Modellinterpreter Ihrer App konfigurieren, indem Sie ein FirebaseModelInputOutputOptions-Objekt erstellen.

Ein Gleitkomma-Modell zur Bildklassifizierung kann beispielsweise ein Nx224x224x3-Array mit float-Werten als Eingabe verwenden, das einen Batch von N 224x224-Bildern mit drei Kanälen (RGB) darstellt, und als Ausgabe eine Liste mit 1.000 float-Werten erzeugen, die jeweils die Wahrscheinlichkeit darstellen, dass das Bild zu einer der 1.000 Kategorien gehört, die das Modell vorhersagt.

Für ein solches Modell würden Sie die Ein- und Ausgabe des Modellinterpreters so konfigurieren:

Java

FirebaseModelInputOutputOptions inputOutputOptions =
        new FirebaseModelInputOutputOptions.Builder()
                .setInputFormat(0, FirebaseModelDataType.FLOAT32, new int[]{1, 224, 224, 3})
                .setOutputFormat(0, FirebaseModelDataType.FLOAT32, new int[]{1, 5})
                .build();

Kotlin

val inputOutputOptions = FirebaseModelInputOutputOptions.Builder()
        .setInputFormat(0, FirebaseModelDataType.FLOAT32, intArrayOf(1, 224, 224, 3))
        .setOutputFormat(0, FirebaseModelDataType.FLOAT32, intArrayOf(1, 5))
        .build()

Inferenz für Eingabedaten durchführen

Um schließlich die Inferenz mit dem Modell durchzuführen, rufen Sie Ihre Eingabedaten ab und führen Sie alle Transformationen an den Daten durch, die erforderlich sind, um ein Eingabearray mit der richtigen Form für Ihr Modell zu erhalten.

Wenn Sie beispielsweise ein Bildklassifizierungsmodell mit einer Eingabeform von [1 224 224 3] Gleitkommawerten haben, können Sie ein Eingabearray aus einem Bitmap-Objekt generieren, wie im folgenden Beispiel gezeigt:

Java

Bitmap bitmap = getYourInputImage();
bitmap = Bitmap.createScaledBitmap(bitmap, 224, 224, true);

int batchNum = 0;
float[][][][] input = new float[1][224][224][3];
for (int x = 0; x < 224; x++) {
    for (int y = 0; y < 224; y++) {
        int pixel = bitmap.getPixel(x, y);
        // Normalize channel values to [-1.0, 1.0]. This requirement varies by
        // model. For example, some models might require values to be normalized
        // to the range [0.0, 1.0] instead.
        input[batchNum][x][y][0] = (Color.red(pixel) - 127) / 128.0f;
        input[batchNum][x][y][1] = (Color.green(pixel) - 127) / 128.0f;
        input[batchNum][x][y][2] = (Color.blue(pixel) - 127) / 128.0f;
    }
}

Kotlin

val bitmap = Bitmap.createScaledBitmap(yourInputImage, 224, 224, true)

val batchNum = 0
val input = Array(1) { Array(224) { Array(224) { FloatArray(3) } } }
for (x in 0..223) {
    for (y in 0..223) {
        val pixel = bitmap.getPixel(x, y)
        // Normalize channel values to [-1.0, 1.0]. This requirement varies by
        // model. For example, some models might require values to be normalized
        // to the range [0.0, 1.0] instead.
        input[batchNum][x][y][0] = (Color.red(pixel) - 127) / 255.0f
        input[batchNum][x][y][1] = (Color.green(pixel) - 127) / 255.0f
        input[batchNum][x][y][2] = (Color.blue(pixel) - 127) / 255.0f
    }
}

Erstellen Sie dann ein FirebaseModelInputs-Objekt mit Ihren Eingabedaten und übergeben Sie es zusammen mit der Ein- und Ausgabespezifikation des Modells an die run-Methode des Modellinterpreters:

Java

FirebaseModelInputs inputs = new FirebaseModelInputs.Builder()
        .add(input)  // add() as many input arrays as your model requires
        .build();
firebaseInterpreter.run(inputs, inputOutputOptions)
        .addOnSuccessListener(
                new OnSuccessListener<FirebaseModelOutputs>() {
                    @Override
                    public void onSuccess(FirebaseModelOutputs result) {
                        // ...
                    }
                })
        .addOnFailureListener(
                new OnFailureListener() {
                    @Override
                    public void onFailure(@NonNull Exception e) {
                        // Task failed with an exception
                        // ...
                    }
                });

Kotlin

val inputs = FirebaseModelInputs.Builder()
        .add(input) // add() as many input arrays as your model requires
        .build()
firebaseInterpreter.run(inputs, inputOutputOptions)
        .addOnSuccessListener { result ->
            // ...
        }
        .addOnFailureListener { e ->
            // Task failed with an exception
            // ...
        }

Wenn der Aufruf erfolgreich ist, können Sie die Ausgabe abrufen, indem Sie die getOutput()-Methode des Objekts aufrufen, das an den Erfolgs-Listener übergeben wird. Beispiel:

Java

float[][] output = result.getOutput(0);
float[] probabilities = output[0];

Kotlin

val output = result.getOutput<Array<FloatArray>>(0)
val probabilities = output[0]

Wie Sie die Ausgabe verwenden, hängt vom verwendeten Modell ab.

Wenn Sie beispielsweise eine Klassifizierung durchführen, können Sie als Nächstes die Indexe des Ergebnisses den Labels zuordnen, die sie repräsentieren:

Java

BufferedReader reader = new BufferedReader(
        new InputStreamReader(getAssets().open("retrained_labels.txt")));
for (int i = 0; i < probabilities.length; i++) {
    String label = reader.readLine();
    Log.i("MLKit", String.format("%s: %1.4f", label, probabilities[i]));
}

Kotlin

val reader = BufferedReader(
        InputStreamReader(assets.open("retrained_labels.txt")))
for (i in probabilities.indices) {
    val label = reader.readLine()
    Log.i("MLKit", String.format("%s: %1.4f", label, probabilities[i]))
}

Anhang: Modellsicherheit

Unabhängig davon, wie Sie Ihre TensorFlow Lite-Modelle für ML Kit verfügbar machen, speichert ML Kit sie im standardmäßigen serialisierten Protobuf-Format im lokalen Speicher.

Theoretisch bedeutet das, dass jeder Ihr Modell kopieren kann. In der Praxis sind die meisten Modelle jedoch so anwendungsspezifisch und durch Optimierungen verschleiert, dass das Risiko ähnlich hoch ist wie das Risiko, dass Wettbewerber Ihren Code disassemblieren und wiederverwenden. Sie sollten sich dieses Risikos jedoch bewusst sein, bevor Sie ein benutzerdefiniertes Modell in Ihrer App verwenden.

Unter Android API‑Level 21 (Lollipop) und höher wird das Modell in ein Verzeichnis heruntergeladen, das vom automatischen Backup ausgeschlossen ist.

Unter Android-API-Level 20 und älter wird das Modell in ein Verzeichnis mit dem Namen com.google.firebase.ml.custom.models im app-privaten internen Speicher heruntergeladen. Wenn Sie die Dateisicherung mit BackupAgent aktiviert haben, können Sie dieses Verzeichnis ausschließen.