Sie können das ML Kit verwenden, um Inferenzen auf dem Gerät mit einem TensorFlow Lite- Modell durchzuführen.
Diese API erfordert Android SDK Level 16 (Jelly Bean) oder höher.
Bevor Sie beginnen
- Falls noch nicht geschehen, fügen Sie Firebase zu Ihrem Android-Projekt hinzu .
- Fügen Sie die Abhängigkeiten für die ML Kit-Android-Bibliotheken zu Ihrer Modul-Gradle-Datei (auf App-Ebene) hinzu (normalerweise
app/build.gradle
):apply plugin: 'com.android.application' apply plugin: 'com.google.gms.google-services' dependencies { // ... implementation 'com.google.firebase:firebase-ml-model-interpreter:22.0.3' }
- Konvertieren Sie das TensorFlow-Modell, das Sie verwenden möchten, in das TensorFlow Lite-Format. Siehe TOCO: TensorFlow Lite Optimizing Converter .
Hosten oder bündeln Sie Ihr Modell
Bevor Sie ein TensorFlow Lite-Modell zur Inferenz in Ihrer App verwenden können, müssen Sie das Modell für ML Kit verfügbar machen. ML Kit kann TensorFlow Lite-Modelle verwenden, die remote über Firebase gehostet werden, gebündelt mit der App-Binärdatei oder beides.
Durch das Hosten eines Modells auf Firebase können Sie das Modell aktualisieren, ohne eine neue App-Version zu veröffentlichen, und Sie können Remote Config und A/B-Tests verwenden, um verschiedene Modelle dynamisch für verschiedene Benutzergruppen bereitzustellen.
Wenn Sie sich dafür entscheiden, das Modell nur durch Hosten mit Firebase bereitzustellen und es nicht mit Ihrer App zu bündeln, können Sie die anfängliche Downloadgröße Ihrer App reduzieren. Beachten Sie jedoch, dass, wenn das Modell nicht mit Ihrer App gebündelt ist, alle modellbezogenen Funktionen erst verfügbar sind, wenn Ihre App das Modell zum ersten Mal herunterlädt.
Durch die Bündelung Ihres Modells mit Ihrer App können Sie sicherstellen, dass die ML-Funktionen Ihrer App auch dann funktionieren, wenn das von Firebase gehostete Modell nicht verfügbar ist.
Hosten Sie Modelle auf Firebase
So hosten Sie Ihr TensorFlow Lite-Modell auf Firebase:
- Klicken Sie im Abschnitt „ML Kit“ der Firebase-Konsole auf die Registerkarte „Benutzerdefiniert“ .
- Klicken Sie auf „Benutzerdefiniertes Modell hinzufügen“ (oder „Anderes Modell hinzufügen “).
- 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 (normalerweise mit der Endung
.tflite
oder.lite
). - Erklären Sie im Manifest Ihrer App, dass eine 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 unter dem von Ihnen angegebenen Namen auf das Modell verweisen. Sie können jederzeit ein neues TensorFlow Lite-Modell hochladen, und Ihre App lädt das neue Modell herunter und beginnt mit der Verwendung, wenn die App das nächste Mal neu gestartet wird. Sie können die Gerätebedingungen definieren, die erforderlich sind, damit Ihre App versucht, das Modell zu aktualisieren (siehe unten).
Bündeln Sie Modelle mit einer App
Um Ihr TensorFlow Lite-Modell mit Ihrer App zu bündeln, kopieren Sie die Modelldatei (normalerweise mit der Endung .tflite
oder .lite
) in den assets/
-Ordner Ihrer App. (Möglicherweise müssen Sie den Ordner zuerst erstellen, indem Sie mit der rechten Maustaste auf den Ordner app/
klicken und dann auf Neu > Ordner > Assets-Ordner klicken.)
Fügen Sie dann Folgendes zur build.gradle
Datei Ihrer App hinzu, um sicherzustellen, dass Gradle die Modelle beim Erstellen der App nicht komprimiert:
android {
// ...
aaptOptions {
noCompress "tflite" // Your model's file extension: "tflite", "lite", etc.
}
}
Die Modelldatei wird im App-Paket enthalten und steht ML Kit als Roh-Asset zur Verfügung.
Laden Sie das Modell
Um Ihr TensorFlow Lite-Modell in Ihrer App zu verwenden, konfigurieren Sie zunächst ML Kit mit den Standorten, 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.Konfigurieren Sie ein von Firebase gehostetes Modell
Wenn Sie Ihr Modell mit Firebase gehostet haben, erstellen Sie ein FirebaseCustomRemoteModel
Objekt und geben Sie dabei den Namen an, den Sie dem Modell beim Hochladen zugewiesen haben:
Java
FirebaseCustomRemoteModel remoteModel =
new FirebaseCustomRemoteModel.Builder("your_model").build();
Kotlin+KTX
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 den Download zulassen möchten. Wenn sich das Modell nicht auf dem Gerät befindet oder eine neuere Version des Modells verfügbar ist, lädt die Aufgabe das Modell asynchron von Firebase herunter:
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+KTX
val conditions = FirebaseModelDownloadConditions.Builder()
.requireWifi()
.build()
FirebaseModelManager.getInstance().download(remoteModel, conditions)
.addOnCompleteListener {
// Success.
}
Viele Apps starten die Download-Aufgabe in ihrem Initialisierungscode, Sie können dies jedoch jederzeit tun, bevor Sie das Modell verwenden müssen.
Konfigurieren Sie ein lokales Modell
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+KTX
val localModel = FirebaseCustomLocalModel.Builder()
.setAssetFilePath("your_model.tflite")
.build()
Erstellen Sie einen Interpreter aus Ihrem Modell
Nachdem Sie Ihre Modellquellen konfiguriert haben, erstellen Sie aus einer davon ein FirebaseModelInterpreter
Objekt.
Wenn Sie nur über ein lokal gebündeltes Modell verfügen, 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+KTX
val options = FirebaseModelInterpreterOptions.Builder(localModel).build()
val interpreter = FirebaseModelInterpreter.getInstance(options)
Wenn Sie über ein remote gehostetes Modell verfügen, müssen Sie überprüfen, ob es heruntergeladen wurde, bevor Sie es ausführen. Sie können den Status der Modell-Download-Aufgabe mit der Methode isModelDownloaded()
des Modellmanagers überprüfen.
Obwohl Sie dies nur bestätigen müssen, bevor Sie den Interpreter ausführen, kann es sinnvoll sein, diese Prüfung bei der Instanziierung des Modellinterpreters durchzuführen, wenn Sie sowohl ein remote gehostetes Modell als auch ein lokal gebündeltes Modell haben: Erstellen Sie einen Interpreter aus dem Remote-Modell, wenn Es wurde heruntergeladen, andernfalls vom 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+KTX
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 über ein remote gehostetes Modell verfügen, sollten Sie modellbezogene Funktionen deaktivieren (z. B. einen Teil Ihrer Benutzeroberfläche ausgrauen oder ausblenden), bis Sie bestätigen, dass das Modell heruntergeladen wurde. Sie können dies tun, indem Sie einen Listener an 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+KTX
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.
}
Geben Sie die Eingabe und Ausgabe des Modells an
Als nächstes konfigurieren Sie die Eingabe- und Ausgabeformate des Modellinterpreters.
Ein TensorFlow Lite-Modell verwendet als Eingabe ein oder mehrere mehrdimensionale Arrays und erzeugt als Ausgabe. Diese Arrays enthalten entweder byte
, int
, long
oder float
-Werte. Sie müssen ML Kit mit der Anzahl und den Abmessungen („Form“) der Arrays konfigurieren, die Ihr Modell verwendet.
Wenn Sie die Form und den Datentyp der Eingabe und Ausgabe Ihres Modells nicht kennen, können Sie Ihr Modell mit dem Python-Interpreter TensorFlow Lite überprüfen. Zum 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 Eingabe und Ausgabe Ihres Modells festgelegt haben, können Sie den Modellinterpreter Ihrer App konfigurieren, indem Sie ein FirebaseModelInputOutputOptions
-Objekt erstellen.
Beispielsweise könnte ein Gleitkomma-Bildklassifizierungsmodell als Eingabe ein N x224 x 224 x 3-Array von float
verwenden, das einen Stapel von N 224 x 224 Dreikanalbildern (RGB) darstellt, und als Ausgabe eine Liste mit 1000 float
erzeugen, die jeweils die darstellen Wahrscheinlichkeit, dass das Bild zu einer der 1000 Kategorien gehört, die das Modell vorhersagt.
Für ein solches Modell würden Sie die Eingabe und Ausgabe des Modellinterpreters wie unten gezeigt 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+KTX
val inputOutputOptions = FirebaseModelInputOutputOptions.Builder() .setInputFormat(0, FirebaseModelDataType.FLOAT32, intArrayOf(1, 224, 224, 3)) .setOutputFormat(0, FirebaseModelDataType.FLOAT32, intArrayOf(1, 5)) .build()
Führen Sie Rückschlüsse auf Eingabedaten durch
Um schließlich eine Inferenz mithilfe des Modells durchzuführen, rufen Sie Ihre Eingabedaten ab und führen Sie alle erforderlichen Transformationen an den Daten durch, um ein Eingabearray mit der richtigen Form für Ihr Modell zu erhalten. Wenn Sie beispielsweise über ein Bildklassifizierungsmodell mit einer Eingabeform von [1 224 224 3] Gleitkommawerten verfügen, könnten 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+KTX
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 und die Eingabe- und Ausgabespezifikation des Modells an die run
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+KTX
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 Methode getOutput()
des Objekts aufrufen, das an den Erfolgslistener übergeben wird. Zum Beispiel:
Java
float[][] output = result.getOutput(0); float[] probabilities = output[0];
Kotlin+KTX
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 im nächsten Schritt die Indizes des Ergebnisses den Beschriftungen zuordnen, die sie darstellen:
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+KTX
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 mit dem der Konkurrenz vergleichbar ist, die Ihren Code zerlegt und wiederverwendet. Dennoch sollten Sie sich dieses Risikos bewusst sein, bevor Sie ein benutzerdefiniertes Modell in Ihrer App verwenden.
Auf Android API Level 21 (Lollipop) und höher wird das Modell in ein Verzeichnis heruntergeladen, das von der automatischen Sicherung ausgeschlossen ist.
Auf der Android-API-Ebene 20 und älter wird das Modell in ein Verzeichnis namens com.google.firebase.ml.custom.models
im privaten App-internen Speicher heruntergeladen. Wenn Sie die Dateisicherung mit BackupAgent
aktiviert haben, können Sie dieses Verzeichnis ausschließen.