Catch up on everthing we announced at this year's Firebase Summit. Learn more

Utiliser un modèle TensorFlow Lite pour l'inférence avec ML Kit sur Android

Vous pouvez utiliser ML Kit pour effectuer l' inférence sur l'appareil avec un tensorflow Lite modèle.

Cette API nécessite Android SDK niveau 16 (Jelly Bean) ou plus récent.

Avant que tu commences

  1. Si vous avez pas déjà, ajoutez Firebase à votre projet Android .
  2. Ajoutez les dépendances pour les bibliothèques ML Kit Android à votre module (app-niveau) de fichier Gradle (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'
    }
    
  3. Convertissez le modèle TensorFlow que vous souhaitez utiliser au format TensorFlow Lite. Voir TOCO: tensorflow Lite Converter Optimizing .

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 une nouvelle version de l'application, et vous pouvez utiliser Remote Config et A/B Testing pour proposer dynamiquement différents modèles à différents groupes d'utilisateurs.

Si vous choisissez de ne fournir le modèle qu'en l'hébergeant avec Firebase et de ne pas l'associer à votre application, vous pouvez réduire la taille de téléchargement initiale de votre application. Gardez à l'esprit, cependant, que si le modèle n'est pas fourni avec votre application, aucune fonctionnalité liée au modèle ne sera disponible jusqu'à ce que votre application télécharge le modèle pour la première fois.

En regroupant votre modèle avec votre application, vous pouvez vous assurer que les fonctionnalités de 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 :

  1. Dans la section ML Kit de la console Firebase , cliquez sur l'onglet Personnaliser.
  2. Cliquez sur Ajouter modèle personnalisé (ou Ajouter un autre modèle).
  3. Indiquez un nom qui sera utilisé pour identifier votre modèle dans votre projet Firebase, puis télécharger le fichier modèle Lite tensorflow ( qui se terminent généralement dans .tflite ou .lite ).
  4. En manifeste, DÉCLARE de votre application que l' autorisation Internet est nécessaire:
    <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).

Regroupez des modèles avec une application

Pour regrouper votre modèle tensorflow Lite avec votre application, copiez le fichier modèle (se terminent généralement dans .tflite ou .lite ) à votre application assets/ dossier. (Vous devrez peut - être créer le dossier d' abord par un clic droit sur l' app/ dossier, puis en cliquant sur Nouveau> Dossier> Dossier actif.)

Ensuite, ajoutez ce qui suit à votre application build.gradle fichier pour vous assurer Gradle ne compriment pas les modèles lors de la construction de l'application:

android {

    // ...

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

Le fichier modèle sera inclus dans le package de l'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, commencez par configurer 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é par Firebase

Si vous avez organisé votre modèle avec Firebase, créez un FirebaseCustomRemoteModel objet, en spécifiant le nom que vous avez attribué le modèle lorsque vous l' avez téléchargé:

Java

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

Kotlin+KTX

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

Ensuite, lancez 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 ne se trouve pas sur l'appareil ou si une version plus récente du modèle est disponible, la tâche téléchargera de manière asynchrone le modèle 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 emmitouflé le modèle avec votre application, créez un FirebaseCustomLocalModel objet, en spécifiant le nom du modèle Lite tensorflow:

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ète à partir de votre modèle

Après avoir configuré vos sources de modèle, créez un FirebaseModelInterpreter objet d'un d'entre eux.

Si vous avez seulement un modèle fourni localement, il suffit de créer un interprète de votre FirebaseCustomLocalModel objet:

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 avez 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 du gestionnaire de modèle isModelDownloaded() méthode.

Bien que vous n'ayez qu'à le confirmer avant d'exécuter l'interpréteur, si vous disposez à la fois d'un modèle hébergé à distance et d'un modèle fourni 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 n'avez qu'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 au gestionnaire de modèle download() méthode:

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écifiez 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 soit des byte , int , long ou float valeurs. Vous devez configurer ML Kit avec le nombre et les dimensions ("forme") des baies utilisées 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 de l'entrée et la sortie de votre modèle, vous pouvez configurer l'interpréteur de modèle de votre application en créant un FirebaseModelInputOutputOptions objet.

Par exemple, un modèle de classification d' image en virgule flottante peut prendre comme entrée une N matrice de x224x224x3 de float valeurs, ce qui représente un lot de N 224x224 trois canaux images (RGB), et produire en sortie une liste de 1000 float valeurs, chacune représentant la probabilité que l'image soit membre de l'une des 1000 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é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, obtenez vos données d'entrée et effectuez toutes les transformations sur les données qui sont nécessaires pour obtenir un tableau d'entrée de la bonne forme pour votre modèle.

Par exemple, si vous avez un modèle de classification de l' image avec une forme d'entrée [1] 224 224 3 valeurs à virgule flottante, on peut générer un tableau d'entrée d'un Bitmap objet 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
    }
}

Ensuite, créez un FirebaseModelInputs objet avec vos données d'entrée, et le transmettre et l'entrée du modèle et la spécification de sortie à l' interpréteur de modèle est run méthode:

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 la sortie en appelant le getOutput() méthode de l'objet qui est passé à l'auditeur de succès. 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, à l'étape suivante, vous pouvez 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 : Modèle de sécurité

Quelle que soit la manière dont vous rendez vos modèles TensorFlow Lite disponibles pour 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 tellement spécifiques à l'application et obscurcis par les optimisations que le risque est similaire à celui des concurrents qui désassemblent et réutilisent 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 plus récent, le modèle est téléchargé dans un répertoire qui est exclu de la sauvegarde automatique .

Le niveau de l' API Android 20 ans et plus, le modèle est téléchargé dans un répertoire nommé com.google.firebase.ml.custom.models dans le stockage interne app-privé. Si vous avez activé la sauvegarde de fichiers en utilisant BackupAgent , vous pouvez choisir d'exclure ce répertoire.