获取我们在 Firebase 峰会上发布的所有信息,了解 Firebase 可如何帮助您加快应用开发速度并满怀信心地运行应用。了解详情

Utilizzare un modello TensorFlow Lite per l'inferenza con ML Kit su Android

Puoi utilizzare ML Kit per eseguire l'inferenza sul dispositivo con un modello TensorFlow Lite .

Questa API richiede Android SDK livello 16 (Jelly Bean) o successivo.

Prima di iniziare

  1. Se non l'hai già fatto, aggiungi Firebase al tuo progetto Android .
  2. Aggiungi le dipendenze per le librerie Android di ML Kit al file Gradle del tuo modulo (a livello di app) (in genere 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. Converti il ​​modello TensorFlow che desideri utilizzare nel formato TensorFlow Lite. Vedere TOCO: TensorFlow Lite Optimizing Converter .

Ospita o raggruppa il tuo modello

Prima di poter utilizzare un modello TensorFlow Lite per l'inferenza nella tua app, devi rendere il modello disponibile per ML Kit. ML Kit può utilizzare i modelli TensorFlow Lite ospitati in remoto utilizzando Firebase, in bundle con il file binario dell'app o entrambi.

Ospitando un modello su Firebase, puoi aggiornare il modello senza rilasciare una nuova versione dell'app e puoi utilizzare Remote Config e A/B Testing per servire dinamicamente diversi modelli a diversi gruppi di utenti.

Se scegli di fornire il modello solo ospitandolo con Firebase e non raggruppandolo con la tua app, puoi ridurre le dimensioni iniziali del download della tua app. Tieni presente, tuttavia, che se il modello non è in bundle con la tua app, qualsiasi funzionalità relativa al modello non sarà disponibile fino a quando la tua app non scaricherà il modello per la prima volta.

Raggruppando il tuo modello con la tua app, puoi assicurarti che le funzionalità ML della tua app continuino a funzionare quando il modello ospitato da Firebase non è disponibile.

Ospita modelli su Firebase

Per ospitare il tuo modello TensorFlow Lite su Firebase:

  1. Nella sezione Kit ML della console Firebase , fai clic sulla scheda Personalizzato .
  2. Fare clic su Aggiungi modello personalizzato (o Aggiungi un altro modello ).
  3. Specifica un nome che verrà utilizzato per identificare il tuo modello nel tuo progetto Firebase, quindi carica il file del modello TensorFlow Lite (di solito termina con .tflite o .lite ).
  4. Nel manifest della tua app, dichiara che è richiesta l'autorizzazione INTERNET:
    <uses-permission android:name="android.permission.INTERNET" />
    

Dopo aver aggiunto un modello personalizzato al tuo progetto Firebase, puoi fare riferimento al modello nelle tue app utilizzando il nome che hai specificato. In qualsiasi momento, puoi caricare un nuovo modello TensorFlow Lite e la tua app scaricherà il nuovo modello e inizierà a usarlo al successivo riavvio dell'app. Puoi definire le condizioni del dispositivo richieste affinché la tua app tenti di aggiornare il modello (vedi sotto).

Raggruppa i modelli con un'app

Per raggruppare il tuo modello TensorFlow Lite con la tua app, copia il file del modello (di solito termina con .tflite o .lite ) nella cartella assets/ della tua app. (Potrebbe essere necessario creare prima la cartella facendo clic con il pulsante destro del mouse app/ cartella, quindi facendo clic su Nuovo > Cartella > Cartella risorse .)

Quindi, aggiungi quanto segue al file build.gradle della tua app per assicurarti che Gradle non comprima i modelli durante la creazione dell'app:

android {

    // ...

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

Il file del modello verrà incluso nel pacchetto dell'app e sarà disponibile per ML Kit come risorsa non elaborata.

Carica il modello

Per utilizzare il tuo modello TensorFlow Lite nella tua app, configura innanzitutto ML Kit con le posizioni in cui il tuo modello è disponibile: in remoto utilizzando Firebase, nell'archivio locale o entrambi. Se specifichi sia un modello locale che uno remoto, puoi utilizzare il modello remoto se disponibile e ricorrere al modello archiviato localmente se il modello remoto non è disponibile.

Configura un modello ospitato da Firebase

Se hai ospitato il tuo modello con Firebase, crea un oggetto FirebaseCustomRemoteModel , specificando il nome che hai assegnato al modello quando lo hai caricato:

Java

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

Kotlin+KTX

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

Quindi, avviare l'attività di download del modello, specificando le condizioni in base alle quali si desidera consentire il download. Se il modello non è sul dispositivo o se è disponibile una versione più recente del modello, l'attività scaricherà in modo asincrono il modello da 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.
    }

Molte app avviano l'attività di download nel codice di inizializzazione, ma puoi farlo in qualsiasi momento prima di dover usare il modello.

Configura un modello locale

Se hai raggruppato il modello con la tua app, crea un oggetto FirebaseCustomLocalModel , specificando il nome file del modello TensorFlow Lite:

Java

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

Kotlin+KTX

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

Crea un interprete dal tuo modello

Dopo aver configurato le origini del modello, crea un oggetto FirebaseModelInterpreter da una di esse.

Se hai solo un modello in bundle localmente, crea semplicemente un interprete dal tuo oggetto 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)

Se disponi di un modello ospitato in remoto, dovrai verificare che sia stato scaricato prima di eseguirlo. È possibile controllare lo stato dell'attività di download del modello utilizzando il metodo isModelDownloaded() del gestore del modello.

Anche se devi solo confermare questo prima di eseguire l'interprete, se hai sia un modello ospitato in remoto che un modello in bundle localmente, potrebbe avere senso eseguire questo controllo quando crei un'istanza dell'interprete del modello: crea un interprete dal modello remoto se è stato scaricato, altrimenti dal modello locale.

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)
}

Se disponi solo di un modello ospitato in remoto, devi disabilitare la funzionalità relativa al modello, ad esempio oscurare o nascondere parte dell'interfaccia utente, finché non confermi che il modello è stato scaricato. Puoi farlo collegando un listener al metodo download() del gestore del modello:

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.
    }

Specifica l'input e l'output del modello

Successivamente, configurare i formati di input e output dell'interprete del modello.

Un modello TensorFlow Lite prende come input e produce come output uno o più array multidimensionali. Questi array contengono valori byte , int , long o float . È necessario configurare ML Kit con il numero e le dimensioni ("forma") degli array utilizzati dal modello.

Se non conosci la forma e il tipo di dati dell'input e dell'output del tuo modello, puoi utilizzare l'interprete Python di TensorFlow Lite per ispezionare il tuo modello. Per esempio:

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'>

Dopo aver determinato il formato dell'input e dell'output del modello, puoi configurare l'interprete del modello dell'app creando un oggetto FirebaseModelInputOutputOptions .

Ad esempio, un modello di classificazione di immagini a virgola mobile potrebbe prendere come input un array N x224x224x3 di valori float , che rappresentano un batch di N 224x224 immagini a tre canali (RGB) e produrre come output un elenco di 1000 valori float , ognuno dei quali rappresenta il probabilità che l'immagine sia un membro di una delle 1000 categorie previste dal modello.

Per un tale modello, dovresti configurare l'input e l'output dell'interprete del modello come mostrato di seguito:

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()

Eseguire l'inferenza sui dati di input

Infine, per eseguire l'inferenza utilizzando il modello, ottenere i dati di input ed eseguire tutte le trasformazioni sui dati necessarie per ottenere un array di input della forma corretta per il modello.

Ad esempio, se si dispone di un modello di classificazione delle immagini con una forma di input di [1 224 224 3] valori a virgola mobile, è possibile generare un array di input da un oggetto Bitmap come mostrato nell'esempio seguente:

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
    }
}

Quindi, crea un oggetto FirebaseModelInputs con i tuoi dati di input e passalo insieme alle specifiche di input e output del modello al metodo run dell'interprete del modello:

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
            // ...
        }

Se la chiamata riesce, puoi ottenere l'output chiamando il metodo getOutput() dell'oggetto passato al listener di successo. Per esempio:

Java

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

Kotlin+KTX

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

Il modo in cui utilizzi l'output dipende dal modello che stai utilizzando.

Ad esempio, se stai eseguendo la classificazione, come passaggio successivo, potresti mappare gli indici del risultato alle etichette che rappresentano:

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]))
}

Appendice: Sicurezza del modello

Indipendentemente da come rendi disponibili i tuoi modelli TensorFlow Lite a ML Kit, ML Kit li archivia nel formato protobuf serializzato standard nella memoria locale.

In teoria, questo significa che chiunque può copiare il tuo modello. Tuttavia, in pratica, la maggior parte dei modelli è così specifica per l'applicazione e offuscata dalle ottimizzazioni che il rischio è simile a quello dei concorrenti che disassemblano e riutilizzano il codice. Tuttavia, dovresti essere consapevole di questo rischio prima di usare un modello personalizzato nella tua app.

Sul livello API Android 21 (Lollipop) e versioni successive, il modello viene scaricato in una directory esclusa dal backup automatico .

Sul livello API Android 20 e versioni precedenti, il modello viene scaricato in una directory denominata com.google.firebase.ml.custom.models nella memoria interna privata dell'app. Se hai abilitato il backup dei file utilizzando BackupAgent , potresti scegliere di escludere questa directory.