ML Kit vous permet d'effectuer une inférence sur l'appareil TensorFlow Lite.
Cette API nécessite le SDK Android 16 (Jelly Bean) ou une version ultérieure.
Avant de commencer
- Si ce n'est pas déjà fait, Ajoutez Firebase à votre projet Android.
- Ajouter les dépendances des bibliothèques Android ML Kit à votre module
Fichier Gradle (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 doit mettre le modèle à la disposition de ML Kit. ML Kit peut utiliser TensorFlow Lite des modèles hébergés à distance à l'aide de Firebase, groupés avec le binaire de l'application, ou les deux.
En hébergeant un modèle sur Firebase, vous pouvez le mettre à jour sans publier de nouvelle version de l'application. Vous pouvez également utiliser Remote Config et A/B Testing pour diffuser dynamiquement différents modèles auprès de différents ensembles d'utilisateurs.
Si vous choisissez de fournir le modèle uniquement en l'hébergeant avec Firebase, et non de l'intégrer à 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, liées au modèle ne seront pas disponibles tant que votre application n'aura pas téléchargé pour la première fois.
En regroupant votre modèle avec votre application, vous pouvez vous assurer que ses fonctionnalités de ML continuent de fonctionner lorsque le modèle hébergé sur 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 servira à identifier votre modèle dans votre projet Firebase, puis importez le fichier de modèle TensorFlow Lite (qui se termine généralement par
.tflite
ou.lite
). - Dans le fichier manifeste de votre application, déclarez que l'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 dans vos applications en utilisant le nom spécifié. Vous pouvez importer un nouveau modèle TensorFlow Lite à tout moment. Votre application le téléchargera et commencera à l'utiliser lors du prochain redémarrage. Vous pouvez définir les conditions requises pour que votre application tente de mettre à jour le modèle (voir ci-dessous).
Regrouper les modèles avec une application
Pour grouper le modèle TensorFlow Lite avec votre application, copiez le fichier de modèle (généralement
se terminant par .tflite
ou .lite
) dans le dossier assets/
de votre application. (Vous devrez peut-être d'abord créer le dossier en effectuant un clic droit sur le dossier app/
, puis en cliquant sur New > Folder > Assets Folder (Nouveau > Dossier > Dossier d'éléments).)
Ajoutez ensuite le code suivant 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 de modèle sera inclus dans le package de l'application et disponible pour ML Kit en tant qu'élément 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, 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 stocké en local 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 FirebaseCustomRemoteModel
, en spécifiant le nom que vous avez attribué au modèle lors de son importation:
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 si vous souhaitez autoriser le téléchargement. Si le modèle ne figure pas sur l'appareil, ou si un modèle plus récent du modèle est disponible, la tâche téléchargera 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 d'avoir besoin d'utiliser le modèle.
Configurer un modèle local
Si vous avez fourni le modèle avec votre application, créez un 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éer un interpréteur à partir de votre modèle
Après avoir configuré les sources de votre modèle, créez un FirebaseModelInterpreter
de l'un d'eux.
Si vous ne disposez que d'un modèle groupé 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 devez vérifier qu'il a été
téléchargée avant
de l’exécuter. Vous pouvez vérifier l'état du téléchargement du modèle
à l'aide de la méthode isModelDownloaded()
du gestionnaire de modèles.
Bien que vous ne deviez le confirmer qu'avant d'exécuter l'interprète, si vous disposez à la fois d'un modèle hébergé à distance et d'un modèle groupé localement, il peut être judicieux d'effectuer cette vérification lors de l'instanciation de l'interprète du modèle : créez un interprète à partir du modèle distant s'il a été téléchargé, et à partir du modèle local dans le cas contraire.
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 ne disposez que 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 UI) jusqu'à ce que vous confirmiez que le modèle a été téléchargé. Pour ce faire, rattachez 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
Configurez ensuite les formats d'entrée et de sortie de l'interpréteur de modèle.
Un modèle TensorFlow Lite utilise un ou plusieurs tableaux multidimensionnels en entrée et produit un ou plusieurs tableaux multidimensionnels en sortie. Ces tableaux contiennent byte
,
Valeurs int
, long
ou float
. Vous devez
configurer ML Kit avec le nombre et les dimensions ("forme") des tableaux
du modèle de ML.
Si vous ne connaissez pas la forme ni 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. 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 des entrées et sorties de votre modèle, vous pouvez
configurer l'interpréteur de modèle de votre application en créant
objet FirebaseModelInputOutputOptions
.
Par exemple, un modèle de classification d'images à virgule flottante peut prendre comme entrée
NTableau x224 x 224x3 de valeurs float
, représentant un lot de
N des images à trois canaux (RVB) 224 x 224 et produisent en sortie une liste de
1 000 valeurs float
, chacune représentant la probabilité à laquelle l'image fait partie
l'une des 1 000 catégories prédites par le modèle.
Pour un tel modèle, vous devez configurer l'entrée et la sortie de l'interprète 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 des données d'entrée
Enfin, pour effectuer des inférences à l'aide du modèle, vous devez récupérer vos données d'entrée les transformations nécessaires aux données pour obtenir un tableau d'entrée des 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 illustré 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 } }
Ensuite, créez un objet FirebaseModelInputs
avec votre
les données d'entrée et les transmettre, ainsi que les spécifications d'entrée et de sortie du modèle, au
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 aboutit, vous pouvez obtenir le résultat en appelant la méthode getOutput()
.
de l'objet transmis à l'écouteur de réussite. 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 manière dont vous utilisez la sortie dépend du modèle que vous utilisez.
Par exemple, si vous effectuez une classification, vous pourriez mappez 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é des modèles
Quelle que soit la façon dont vous rendez vos modèles TensorFlow Lite disponibles ML Kit et ML Kit les stocke au format protobuf sérialisé standard dans stockage local.
En théorie, cela signifie que n'importe qui peut copier votre modèle. Toutefois, Dans la pratique, la plupart des modèles sont tellement spécifiques à une application et obscurcis par des optimisations dont le risque est similaire à celui des concurrents en démontrant et en réutilisant votre code. Toutefois, vous devez être conscient de ce risque avant d'utiliser un modèle personnalisé dans votre application.
Sur le niveau d'API Android 21 (Lollipop) ou version ultérieure, le modèle est téléchargé répertoire contenant exclus de la sauvegarde automatique.
Sous Android 20 ou version antérieure, le modèle est téléchargé dans un répertoire nommé com.google.firebase.ml.custom.models
dans l'espace de stockage interne privé de l'application. Si vous avez activé la sauvegarde de fichiers avec BackupAgent
,
vous pouvez choisir
d'exclure ce répertoire.