Google is committed to advancing racial equity for Black communities. See how.
Questa pagina è stata tradotta dall'API Cloud Translation.
Switch to English

Utilizza un modello TensorFlow Lite personalizzato su Android

Se la tua app utilizza modelli TensorFlow Lite personalizzati, puoi utilizzare Firebase ML per distribuire i tuoi modelli. Distribuendo modelli con Firebase, puoi ridurre la dimensione del download iniziale della tua app e aggiornare i modelli ML della tua app senza rilasciare una nuova versione della tua app. Inoltre, con Remote Config e A / B Testing, puoi offrire dinamicamente diversi modelli a diversi gruppi di utenti.

Modelli TensorFlow Lite

I modelli TensorFlow Lite sono modelli ML ottimizzati per l'esecuzione su dispositivi mobili. Per ottenere un modello TensorFlow Lite:

Prima di iniziare

  1. Se non l'hai già fatto, aggiungi Firebase al tuo progetto Android .
  2. Utilizzando Firebase Android BoM , dichiara la dipendenza per la libreria Firebase ML Custom Models Android nel file Gradle del tuo modulo (a livello di app) (solitamente app/build.gradle ).

    Inoltre, come parte della configurazione dei modelli personalizzati Firebase ML, è necessario aggiungere l'SDK TensorFlow Lite alla tua app.

    dependencies {
        // Import the BoM for the Firebase platform
        implementation platform('com.google.firebase:firebase-bom:26.1.0')
    
        // Declare the dependency for the Firebase ML Custom Models library
        // When using the BoM, you don't specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-ml-model-interpreter'
    // Also declare the dependency for the TensorFlow Lite library and specify its version implementation 'org.tensorflow:tensorflow-lite:2.3.0'
    }

    Utilizzando Firebase Android BoM , la tua app utilizzerà sempre versioni compatibili delle librerie Firebase Android.

    (Alternativa) Dichiara le dipendenze della libreria Firebase senza utilizzare BoM

    Se scegli di non utilizzare Firebase BoM, devi specificare ciascuna versione della libreria Firebase nella relativa riga di dipendenza.

    Tieni presente che se utilizzi più librerie Firebase nella tua app, ti consigliamo vivamente di utilizzare BoM per gestire le versioni delle librerie, il che garantisce che tutte le versioni siano compatibili.

    dependencies {
        // Declare the dependency for the Firebase ML Custom Models library
        // When NOT using the BoM, you must specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-ml-model-interpreter:22.0.4'
    // Also declare the dependency for the TensorFlow Lite library and specify its version implementation 'org.tensorflow:tensorflow-lite:2.3.0'
    }
  3. Nel manifest della tua app, dichiara che è richiesta l'autorizzazione INTERNET:
    <uses-permission android:name="android.permission.INTERNET" />

1. Distribuisci il tuo modello

Distribuisci i tuoi modelli TensorFlow personalizzati utilizzando la console Firebase o gli SDK Firebase Admin Python e Node.js. Vedi Distribuire e gestire modelli personalizzati .

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 necessarie affinché la tua app tenti di aggiornare il modello (vedi sotto).

2. Scarica il modello sul dispositivo

Per utilizzare il tuo modello TensorFlow Lite nella tua app, utilizza prima l'SDK Firebase ML per scaricare l'ultima versione del modello sul dispositivo.

Per avviare il download del modello, chiama il metodo download() del gestore modelli, specificando il nome che hai assegnato al modello quando lo hai caricato e le condizioni in cui desideri consentire il download. Se il modello non è sul dispositivo o se è disponibile una versione più recente del modello, l'attività scaricherà il modello in modo asincrono da Firebase.

È necessario disabilitare la funzionalità relativa al modello, ad esempio, disattivare o nascondere parte dell'interfaccia utente finché non si conferma che il modello è stato scaricato.

Giava

FirebaseCustomRemoteModel remoteModel =
      new FirebaseCustomRemoteModel.Builder("your_model").build();
FirebaseModelDownloadConditions conditions = new FirebaseModelDownloadConditions.Builder()
        .requireWifi()
        .build();
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

val remoteModel = FirebaseCustomRemoteModel.Builder("your_model").build()
val conditions = FirebaseModelDownloadConditions.Builder()
    .requireWifi()
    .build()
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.
    }

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

3. Inizializza un interprete TensorFlow Lite

Dopo aver scaricato il modello sul dispositivo, è possibile ottenere il percorso del file del modello chiamando il metodo getLatestModelFile() del gestore getLatestModelFile() . Utilizza questo valore per creare un'istanza di un interprete TensorFlow Lite:

Giava

FirebaseCustomRemoteModel remoteModel = new FirebaseCustomRemoteModel.Builder("your_model").build();
FirebaseModelManager.getInstance().getLatestModelFile(remoteModel)
        .addOnCompleteListener(new OnCompleteListener<File>() {
            @Override
            public void onComplete(@NonNull Task<File> task) {
                File modelFile = task.getResult();
                if (modelFile != null) {
                    interpreter = new Interpreter(modelFile);
                }
            }
        });

Kotlin + KTX

val remoteModel = FirebaseCustomRemoteModel.Builder("your_model").build()
FirebaseModelManager.getInstance().getLatestModelFile(remoteModel)
    .addOnCompleteListener { task ->
        val modelFile = task.result
        if (modelFile != null) {
            interpreter = Interpreter(modelFile)
        }
    }

4. Eseguire l'inferenza sui dati di input

Ottieni le forme di input e output del tuo modello

L'interprete del modello TensorFlow Lite prende come input e produce come output uno o più array multidimensionali. Questi array contengono valori byte , int , long o float . Prima di poter passare dati a un modello o utilizzarne il risultato, è necessario conoscere il numero e le dimensioni ("forma") degli array utilizzati dal modello.

Se hai creato il modello da solo o se il formato di input e output del modello è documentato, potresti già disporre di queste informazioni. Se non conosci la forma e il tipo di dati dell'input e dell'output del tuo modello, puoi utilizzare l'interprete TensorFlow Lite per ispezionare il tuo modello. Per esempio:

Pitone

import tensorflow as tf

interpreter = tf.lite.Interpreter(model_path="your_model.tflite")
interpreter.allocate_tensors()

# Print input shape and type
inputs = interpreter.get_input_details()
print('{} input(s):'.format(len(inputs)))
for i in range(0, len(inputs)):
    print('{} {}'.format(inputs[i]['shape'], inputs[i]['dtype']))

# Print output shape and type
outputs = interpreter.get_output_details()
print('\n{} output(s):'.format(len(outputs)))
for i in range(0, len(outputs)):
    print('{} {}'.format(outputs[i]['shape'], outputs[i]['dtype']))

Output di esempio:

1 input(s):
[  1 224 224   3] <class 'numpy.float32'>

1 output(s):
[1 1000] <class 'numpy.float32'>

Esegui l'interprete

Dopo aver determinato il formato dell'input e dell'output del modello, ottenere i dati di input ed eseguire le trasformazioni sui dati necessarie per ottenere un 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 ByteBuffer input da un oggetto Bitmap come mostrato nell'esempio seguente:

Giava

Bitmap bitmap = Bitmap.createScaledBitmap(yourInputImage, 224, 224, true);
ByteBuffer input = ByteBuffer.allocateDirect(224 * 224 * 3 * 4).order(ByteOrder.nativeOrder());
for (int y = 0; y < 224; y++) {
    for (int x = 0; x < 224; x++) {
        int px = bitmap.getPixel(x, y);

        // Get channel values from the pixel value.
        int r = Color.red(px);
        int g = Color.green(px);
        int b = Color.blue(px);

        // Normalize channel values to [-1.0, 1.0]. This requirement depends
        // on the model. For example, some models might require values to be
        // normalized to the range [0.0, 1.0] instead.
        float rf = (r - 127) / 255.0f;
        float gf = (g - 127) / 255.0f;
        float bf = (b - 127) / 255.0f;

        input.putFloat(rf);
        input.putFloat(gf);
        input.putFloat(bf);
    }
}

Kotlin + KTX

val bitmap = Bitmap.createScaledBitmap(yourInputImage, 224, 224, true)
val input = ByteBuffer.allocateDirect(224*224*3*4).order(ByteOrder.nativeOrder())
for (y in 0 until 224) {
    for (x in 0 until 224) {
        val px = bitmap.getPixel(x, y)

        // Get channel values from the pixel value.
        val r = Color.red(px)
        val g = Color.green(px)
        val b = Color.blue(px)

        // Normalize channel values to [-1.0, 1.0]. This requirement depends on the model.
        // For example, some models might require values to be normalized to the range
        // [0.0, 1.0] instead.
        val rf = (r - 127) / 255f
        val gf = (g - 127) / 255f
        val bf = (b - 127) / 255f

        input.putFloat(rf)
        input.putFloat(gf)
        input.putFloat(bf)
    }
}

Quindi, alloca un ByteBuffer abbastanza grande da contenere l'output del modello e passa il buffer di input e il buffer di output al metodo run() dell'interprete TensorFlow Lite. Ad esempio, per una forma di output di [1 1000] valori a virgola mobile:

Giava

int bufferSize = 1000 * java.lang.Float.SIZE / java.lang.Byte.SIZE;
ByteBuffer modelOutput = ByteBuffer.allocateDirect(bufferSize).order(ByteOrder.nativeOrder());
interpreter.run(input, modelOutput);

Kotlin + KTX

val bufferSize = 1000 * java.lang.Float.SIZE / java.lang.Byte.SIZE
val modelOutput = ByteBuffer.allocateDirect(bufferSize).order(ByteOrder.nativeOrder())
interpreter?.run(input, modelOutput)

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:

Giava

modelOutput.rewind();
FloatBuffer probabilities = modelOutput.asFloatBuffer();
try {
    BufferedReader reader = new BufferedReader(
            new InputStreamReader(getAssets().open("custom_labels.txt")));
    for (int i = 0; i < probabilities.capacity(); i++) {
        String label = reader.readLine();
        float probability = probabilities.get(i);
        Log.i(TAG, String.format("%s: %1.4f", label, probability));
    }
} catch (IOException e) {
    // File not found?
}

Kotlin + KTX

modelOutput.rewind()
val probabilities = modelOutput.asFloatBuffer()
try {
    val reader = BufferedReader(
            InputStreamReader(assets.open("custom_labels.txt")))
    for (i in probabilities.capacity()) {
        val label: String = reader.readLine()
        val probability = probabilities.get(i)
        println("$label: $probability")
    }
} catch (e: IOException) {
    // File not found?
}

Appendice: fall back to a local-bundled model

Quando ospiti il ​​tuo modello con Firebase, qualsiasi funzionalità relativa al modello non sarà disponibile fino a quando l'app non scarica il modello per la prima volta. Per alcune app, questo potrebbe andare bene, ma se il tuo modello abilita le funzionalità di base, potresti voler raggruppare una versione del tuo modello con la tua app e utilizzare la migliore versione disponibile. In questo modo, puoi assicurarti che le funzionalità ML della tua app funzionino quando il modello ospitato da Firebase non è disponibile.

Per raggruppare il tuo modello TensorFlow Lite con la tua app:

  1. Copia il file del modello (che di solito termina con .tflite o .lite ) nella cartella delle assets/ dell'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 .)

  2. 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", "lite"
        }
    }
    

Quindi, utilizza il modello in bundle localmente quando il modello ospitato non è disponibile:

Giava

FirebaseCustomRemoteModel remoteModel =
        new FirebaseCustomRemoteModel.Builder("your_model").build();
FirebaseModelManager.getInstance().getLatestModelFile(remoteModel)
        .addOnCompleteListener(new OnCompleteListener<File>() {
            @Override
            public void onComplete(@NonNull Task<File> task) {
                File modelFile = task.getResult();
                if (modelFile != null) {
                    interpreter = new Interpreter(modelFile);
                } else {
                    try {
                        InputStream inputStream = getAssets().open("your_fallback_model.tflite");
                        byte[] model = new byte[inputStream.available()];
                        inputStream.read(model);
                        ByteBuffer buffer = ByteBuffer.allocateDirect(model.length)
                                .order(ByteOrder.nativeOrder());
                        buffer.put(model);
                        interpreter = new Interpreter(buffer);
                    } catch (IOException e) {
                        // File not found?
                    }
                }
            }
        });

Kotlin + KTX

val remoteModel = FirebaseCustomRemoteModel.Builder("your_model").build()
FirebaseModelManager.getInstance().getLatestModelFile(remoteModel)
    .addOnCompleteListener { task ->
        val modelFile = task.result
        if (modelFile != null) {
            interpreter = Interpreter(modelFile)
        } else {
            val model = assets.open("your_fallback_model.tflite").readBytes()
            val buffer = ByteBuffer.allocateDirect(model.size).order(ByteOrder.nativeOrder())
            buffer.put(model)
            interpreter = Interpreter(buffer)
        }
    }

Appendice: sicurezza del modello

Indipendentemente da come rendi i tuoi modelli TensorFlow Lite disponibili per Firebase ML, Firebase ML li memorizza 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 smontano e riutilizzano il codice. Tuttavia, dovresti essere consapevole di questo rischio prima di utilizzare un modello personalizzato nella tua app.

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

A partire dal livello 20 dell'API Android, 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.