Vous pouvez utiliser ML Kit pour effectuer une inférence sur l'appareil avec un modèle TensorFlow Lite .
Cette API nécessite le SDK Android niveau 16 (Jelly Bean) ou plus récent.
Avant que tu commences
- Si vous ne l'avez pas déjà fait, ajoutez Firebase à votre projet Android .
- Ajoutez les dépendances des bibliothèques ML Kit Android au fichier Gradle de votre module (au niveau de l'application) (généralement
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' }
- Convertissez le modèle TensorFlow que vous souhaitez utiliser au format TensorFlow Lite. Voir TOCO : Convertisseur d'optimisation TensorFlow Lite .
Hébergez ou regroupez votre modèle
Avant de pouvoir utiliser un modèle TensorFlow Lite pour l'inférence dans votre application, vous devez rendre le modèle disponible pour ML Kit. ML Kit peut utiliser des modèles TensorFlow Lite hébergés à distance à l'aide de Firebase, fournis avec le binaire de l'application, ou les deux.
En hébergeant un modèle sur Firebase, vous pouvez mettre à jour le modèle sans publier de nouvelle version de l'application, et vous pouvez utiliser la configuration à distance et les tests A/B pour proposer dynamiquement différents modèles à différents ensembles d'utilisateurs.
Si vous choisissez de fournir le modèle uniquement en l'hébergeant avec Firebase, et de ne pas le regrouper avec votre application, vous pouvez réduire la taille de téléchargement initiale de votre application. Gardez toutefois à l’esprit que si le modèle n’est pas fourni avec votre application, aucune fonctionnalité liée au modèle ne sera disponible tant que votre application n’aura pas téléchargé le modèle pour la première fois.
En regroupant votre modèle avec votre application, vous pouvez vous assurer que les fonctionnalités ML de votre application fonctionnent toujours lorsque le modèle hébergé par Firebase n'est pas disponible.
Héberger des modèles sur Firebase
Pour héberger votre modèle TensorFlow Lite sur Firebase :
- Dans la section ML Kit de la console Firebase , cliquez sur l'onglet Personnalisé .
- Cliquez sur Ajouter un modèle personnalisé (ou Ajouter un autre modèle ).
- Spécifiez un nom qui sera utilisé pour identifier votre modèle dans votre projet Firebase, puis téléchargez le fichier de modèle TensorFlow Lite (se terminant généralement par
.tflite
ou.lite
). - Dans le manifeste de votre application, déclarez qu'une autorisation INTERNET est requise :
<uses-permission android:name="android.permission.INTERNET" />
Après avoir ajouté un modèle personnalisé à votre projet Firebase, vous pouvez référencer le modèle dans vos applications en utilisant le nom que vous avez spécifié. À tout moment, vous pouvez télécharger un nouveau modèle TensorFlow Lite, et votre application téléchargera le nouveau modèle et commencera à l'utiliser au prochain redémarrage de l'application. Vous pouvez définir les conditions d'appareil requises pour que votre application tente de mettre à jour le modèle (voir ci-dessous).
Regrouper des modèles avec une application
Pour regrouper votre modèle TensorFlow Lite avec votre application, copiez le fichier de modèle (se terminant généralement par .tflite
ou .lite
) dans le dossier assets/
de votre application. (Vous devrez peut-être d'abord créer le dossier en cliquant avec le bouton droit sur le dossier app/
, puis en cliquant sur Nouveau > Dossier > Dossier d'actifs .)
Ensuite, ajoutez ce qui suit au fichier build.gradle
de votre application pour vous assurer que Gradle ne compresse pas les modèles lors de la création de l'application :
android {
// ...
aaptOptions {
noCompress "tflite" // Your model's file extension: "tflite", "lite", etc.
}
}
Le fichier modèle sera inclus dans le package d'application et disponible pour ML Kit en tant qu'actif brut.
Charger le modèle
Pour utiliser votre modèle TensorFlow Lite dans votre application, configurez d'abord ML Kit avec les emplacements où votre modèle est disponible : à distance à l'aide de Firebase, dans le stockage local ou les deux. Si vous spécifiez à la fois un modèle local et un modèle distant, vous pouvez utiliser le modèle distant s'il est disponible et revenir au modèle stocké localement si le modèle distant n'est pas disponible.Configurer un modèle hébergé sur Firebase
Si vous avez hébergé votre modèle avec Firebase, créez un objet FirebaseCustomRemoteModel
, en spécifiant le nom que vous avez attribué au modèle lorsque vous l'avez importé :
Java
FirebaseCustomRemoteModel remoteModel =
new FirebaseCustomRemoteModel.Builder("your_model").build();
Kotlin+KTX
val remoteModel = FirebaseCustomRemoteModel.Builder("your_model").build()
Ensuite, démarrez la tâche de téléchargement du modèle, en spécifiant les conditions dans lesquelles vous souhaitez autoriser le téléchargement. Si le modèle n'est pas sur l'appareil ou si une version plus récente du modèle est disponible, la tâche téléchargera le modèle de manière asynchrone depuis Firebase :
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.
}
De nombreuses applications démarrent la tâche de téléchargement dans leur code d'initialisation, mais vous pouvez le faire à tout moment avant de devoir utiliser le modèle.
Configurer un modèle local
Si vous avez regroupé le modèle avec votre application, créez un objet FirebaseCustomLocalModel
en spécifiant le nom de fichier du modèle TensorFlow Lite :
Java
FirebaseCustomLocalModel localModel = new FirebaseCustomLocalModel.Builder()
.setAssetFilePath("your_model.tflite")
.build();
Kotlin+KTX
val localModel = FirebaseCustomLocalModel.Builder()
.setAssetFilePath("your_model.tflite")
.build()
Créez un interpréteur à partir de votre modèle
Après avoir configuré vos sources de modèle, créez un objet FirebaseModelInterpreter
à partir de l'une d'entre elles.
Si vous disposez uniquement d'un modèle regroupé localement, créez simplement un interpréteur à partir de votre objet FirebaseCustomLocalModel
:
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)
Si vous disposez d'un modèle hébergé à distance, vous devrez vérifier qu'il a été téléchargé avant de l'exécuter. Vous pouvez vérifier l'état de la tâche de téléchargement de modèle à l'aide de la méthode isModelDownloaded()
du gestionnaire de modèles.
Bien que vous n'ayez qu'à confirmer cela avant d'exécuter l'interpréteur, si vous disposez à la fois d'un modèle hébergé à distance et d'un modèle regroupé localement, il peut être judicieux d'effectuer cette vérification lors de l'instanciation de l'interpréteur de modèle : créez un interpréteur à partir du modèle distant si il a été téléchargé, et à partir du modèle local sinon.
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)
}
Si vous disposez uniquement d'un modèle hébergé à distance, vous devez désactiver les fonctionnalités liées au modèle (par exemple, griser ou masquer une partie de votre interface utilisateur) jusqu'à ce que vous confirmiez que le modèle a été téléchargé. Vous pouvez le faire en attachant un écouteur à la méthode download()
du gestionnaire de modèles :
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.
}
Spécifier l'entrée et la sortie du modèle
Ensuite, configurez les formats d’entrée et de sortie de l’interpréteur de modèle.
Un modèle TensorFlow Lite prend en entrée et produit en sortie un ou plusieurs tableaux multidimensionnels. Ces tableaux contiennent des valeurs byte
, int
, long
ou float
. Vous devez configurer ML Kit avec le nombre et les dimensions (« forme ») des tableaux utilisés par votre modèle.
Si vous ne connaissez pas la forme et le type de données des entrées et sorties de votre modèle, vous pouvez utiliser l'interpréteur Python TensorFlow Lite pour inspecter votre modèle. Par exemple:
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'>
Après avoir déterminé le format d'entrée et de sortie de votre modèle, vous pouvez configurer l'interpréteur de modèle de votre application en créant un objet FirebaseModelInputOutputOptions
.
Par exemple, un modèle de classification d'images à virgule flottante peut prendre en entrée un tableau N x224x224x3 de valeurs float
, représentant un lot de N images à trois canaux (RVB) de 224x224, et produire en sortie une liste de 1 000 valeurs float
, chacune représentant le probabilité que l'image soit membre de l'une des 1 000 catégories prédites par le modèle.
Pour un tel modèle, vous configureriez l'entrée et la sortie de l'interpréteur de modèle comme indiqué ci-dessous :
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()
Effectuer une inférence sur les données d'entrée
Enfin, pour effectuer une inférence à l'aide du modèle, récupérez vos données d'entrée et effectuez toutes les transformations sur les données nécessaires pour obtenir un tableau d'entrée de la forme adaptée à votre modèle. Par exemple, si vous disposez d'un modèle de classification d'images avec une forme d'entrée de valeurs à virgule flottante [1 224 224 3], vous pouvez générer un tableau d'entrée à partir d'un objet Bitmap
, comme indiqué dans l'exemple suivant :
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 } }
Créez ensuite un objet FirebaseModelInputs
avec vos données d'entrée, puis transmettez-le ainsi que les spécifications d'entrée et de sortie du modèle à la méthode run
de l'interpréteur de modèle :
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 // ... }
Si l'appel réussit, vous pouvez obtenir le résultat en appelant la méthode getOutput()
de l'objet transmis à l'écouteur de réussite. Par exemple:
Java
float[][] output = result.getOutput(0); float[] probabilities = output[0];
Kotlin+KTX
val output = result.getOutput<Array<FloatArray>>(0) val probabilities = output[0]
La façon dont vous utilisez la sortie dépend du modèle que vous utilisez.
Par exemple, si vous effectuez une classification, vous pouvez ensuite mapper les index du résultat aux étiquettes qu'ils représentent :
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])) }
Annexe : sécurité du modèle
Quelle que soit la façon dont vous mettez vos modèles TensorFlow Lite à la disposition de ML Kit, ML Kit les stocke au format protobuf sérialisé standard dans le stockage local.
En théorie, cela signifie que n’importe qui peut copier votre modèle. Cependant, dans la pratique, la plupart des modèles sont si spécifiques à l'application et obscurcis par les optimisations que le risque est similaire à celui de concurrents désassemblant et réutilisant votre code. Néanmoins, vous devez être conscient de ce risque avant d'utiliser un modèle personnalisé dans votre application.
Sur Android API niveau 21 (Lollipop) et versions ultérieures, le modèle est téléchargé dans un répertoire exclu de la sauvegarde automatique .
Sur l'API Android de niveau 20 et antérieur, le modèle est téléchargé dans un répertoire nommé com.google.firebase.ml.custom.models
dans le stockage interne privé de l'application. Si vous avez activé la sauvegarde de fichiers à l'aide de BackupAgent
, vous pouvez choisir d'exclure ce répertoire.