Ir para o console

Usar um modelo do TensorFlow Lite para inferência com o Kit de ML no Android

Você pode usar o Kit de ML para realizar inferências no dispositivo com um modelo do TensorFlow Lite.

Esta API requer o SDK para Android nível 16 (Jelly Bean) ou versões mais recentes.

Consulte a Amostra do guia de início rápido do Kit de ML (em inglês) no GitHub para ver um exemplo da API em uso ou tente o codelab.

Antes de começar

  1. Adicione o Firebase ao seu projeto para Android, caso ainda não tenha feito isso.
  2. No arquivo build.gradle no nível do projeto, inclua o repositório Maven do Google nas seções buildscript e allprojects.
  3. Adicione as dependências das bibliotecas Android do Kit de ML ao seu arquivo Gradle do módulo (nível do aplicativo), que geralmente é app/build.gradle:
    dependencies {
      // ...
    
      implementation 'com.google.firebase:firebase-ml-model-interpreter:22.0.0'
    }
    apply plugin: 'com.google.gms.google-services'
    
  4. Converta o modelo do TensorFlow que você quer usar para o formato do TensorFlow Lite. Consulte TOCO: conversor de otimização do TensorFlow Lite.

Hospedar ou agrupar seu modelo

Antes de poder usar um modelo do TensorFlow Lite para inferência no seu app, é preciso disponibilizar o modelo para o Kit de ML. O kit pode usar modelos do TensorFlow Lite hospedados remotamente com o Firebase, armazenados em pacote com o binário do aplicativo, ou ambos.

Ao hospedar um modelo no Firebase, você pode atualizar o modelo sem liberar uma nova versão do aplicativo e pode usar a Configuração remota e o Teste A/B para disponibilizar dinamicamente diferentes modelos para diferentes conjuntos de usuários.

Se você optar por fornecer apenas o modelo hospedando-o com o Firebase em vez de incluí-lo no seu aplicativo, poderá reduzir o tamanho inicial do download do seu aplicativo. No entanto, lembre-se de que, se o modelo não estiver incluído no seu aplicativo, qualquer funcionalidade relacionada ao modelo não estará disponível até que seu aplicativo faça o download do modelo pela primeira vez.

Ao fornecer o modelo junto com o aplicativo, é possível garantir que os recursos de ML do aplicativo ainda funcionem quando o modelo hospedado pelo Firebase não estiver disponível.

Hospedar modelos no Firebase

Para hospedar seu modelo do TensorFlow Lite no Firebase:

  1. Na seção Kit de ML do Console do Firebase, clique na guia Personalizar.
  2. Clique em Adicionar modelo personalizado ou Adicionar outro modelo.
  3. Especifique um nome que será usado para identificar seu modelo no seu projeto do Firebase e, em seguida, faça o upload do arquivo do modelo do TensorFlow Lite (normalmente terminado em .tflite ou .lite).
  4. No manifesto do app, informe que a permissão INTERNET é necessária:
    <uses-permission android:name="android.permission.INTERNET" />
    

Depois de adicionar um modelo personalizado ao seu projeto do Firebase, você pode referenciá-lo nos seus apps usando o nome especificado. A qualquer momento, você pode fazer o upload de um novo modelo do TensorFlow Lite, e seu aplicativo fará o download do novo modelo e começará a usá-lo quando o aplicativo for reiniciado. Você pode definir as condições do dispositivo necessárias para que seu aplicativo tente atualizar o modelo. Veja como fazer isso abaixo.

Incluir modelos em um aplicativo

Para incluir seu modelo do TensorFlow Lite com o aplicativo, copie o arquivo do modelo (normalmente terminado em .tflite ou .lite ) para a pasta assets/ do aplicativo. Pode ser necessário criar a pasta primeiro clicando com o botão direito do mouse na pasta app/ e, em seguida, clicando em Novo > Pasta > Pasta de recursos.)

Em seguida, adicione o seguinte ao arquivo build.gradle do seu aplicativo para garantir que o Gradle não compacte os modelos ao criar o aplicativo:

android {

    // ...

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

O arquivo do modelo será incluído no pacote do app e estará disponível para o Kit de ML como um recurso bruto.

Carregar o modelo

Para usar seu modelo do TensorFlow Lite no app, primeiro configure o Kit de ML com os locais em que seu modelo está disponível: remotamente usando o Firebase, o armazenamento local ou ambos. Se você especificar um modelo local e remoto, poderá usar o modelo remoto se ele estiver disponível. Caso não esteja, o modelo usado será o local.

Configurar um modelo hospedado pelo Firebase

Se você hospedou seu modelo com o Firebase, crie um objeto FirebaseCustomRemoteModel especificando o nome atribuído ao modelo ao fazer o upload:

Java

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

Kotlin

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

Em seguida, inicie a tarefa de download do modelo, especificando as condições de acordo com as quais você quer permitir o download. Se o modelo não estiver no dispositivo ou se uma versão mais recente do modelo estiver disponível, a tarefa fará o download do modelo de forma assíncrona a partir do 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

val conditions = FirebaseModelDownloadConditions.Builder()
    .requireWifi()
    .build()
FirebaseModelManager.getInstance().download(remoteModel, conditions)
    .addOnCompleteListener {
        // Success.
    }

Muitos apps iniciam a tarefa de download no código de inicialização, mas você pode fazer isso a qualquer momento antes de precisar usar o modelo.

Configurar um modelo local

Se você agrupou o modelo com seu app, crie um objeto FirebaseCustomLocalModel, especificando o nome do arquivo do modelo do TensorFlow Lite:

Java

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

Kotlin

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

Criar um interpretador a partir do seu modelo

Depois de configurar as fontes de modelo, crie um objeto FirebaseModelInterpreter a partir de um deles.

Se você tiver apenas um modelo com pacote local, basta criar um interpretador a partir do seu objeto FirebaseCustomLocalModel:

Java

FirebaseModelInterpreter interpreter;
try {
    FirebaseModelInterpreterOptions options =
            new FirebaseModelInterpreterOptions.Builder(localModel).build();
    interpreter = FirebaseModelInterpreter.getInstance(options);
} catch (FirebaseMLException e) {
    // ...
}

Kotlin

val options = FirebaseModelInterpreterOptions.Builder(localModel).build()
val interpreter = FirebaseModelInterpreter.getInstance(options)

Se você tiver um modelo hospedado remotamente, será necessário verificar se foi feito o download dele antes de executá-lo. O status da tarefa de download do modelo pode ser verificado usando o método isModelDownloaded() do gerenciador de modelos.

Embora isso só precise ser confirmado antes da execução do interpretador, se você tiver um modelo hospedado remotamente e um modelo agrupado localmente, pode fazer sentido realizar essa verificação ao instanciar o interpretador de modelos: crie um interpretador a partir do modelo remoto se ele tiver sido transferido por download e a partir do modelo local, caso contrário.

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

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 você tiver apenas um modelo hospedado remotamente, desative o recurso relacionado ao modelo, por exemplo, usar o recurso esmaecido ou ocultar parte da IU, até confirmar que o download do modelo foi concluído. Para fazer isso, anexe um listener ao método download() do gerenciador de modelos:

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

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

Especificar a entrada e a saída do modelo

O próximo passo é configurar os formatos de entrada e saída do interpretador do modelo.

Um modelo do TensorFlow Lite utiliza como entrada e produz como saída uma ou mais matrizes multidimensionais. Essas matrizes contêm valores byte, int, long ou float. É preciso configurar o Kit de ML com o número e as dimensões ("forma") das matrizes usadas pelo modelo.

Se você não sabe qual é a forma e o tipo de dados da entrada e da saída do seu modelo, pode usar o interpretador do TensorFlow Lite em Python para inspecionar seu modelo. Por exemplo:

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

Depois de determinar o formato da entrada e da saída do modelo, configure o interpretador de modelos do app criando um objeto FirebaseModelInputOutputOptions.

Por exemplo, um modelo de classificação de imagens de ponto flutuante pode usar como entrada uma matriz Nx224x224x3 de valores float, que representa um lote de imagens N 224x224 de três canais (RGB). Além disso, o modelo produz como saída uma lista de 1.000 valores float, cada um representando a probabilidade de a imagem ser parte de uma das 1.000 categorias previstas pelo modelo.

Para um modelo assim, você configuraria a entrada e a saída do interpretador do modelo conforme abaixo:

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

val inputOutputOptions = FirebaseModelInputOutputOptions.Builder()
        .setInputFormat(0, FirebaseModelDataType.FLOAT32, intArrayOf(1, 224, 224, 3))
        .setOutputFormat(0, FirebaseModelDataType.FLOAT32, intArrayOf(1, 5))
        .build()

Realizar inferência em dados de entrada

Por último, para realizar a inferência usando o modelo, colete seus dados de entrada, execute quaisquer transformações nos dados que possam ser necessárias para obter uma matriz de entrada que tenha a forma certa para seu modelo.

Por exemplo, se você tiver um modelo de classificação de imagens com uma forma de entrada de [1 224 224 3] valores de ponto flutuante, poderá gerar uma matriz de entrada usando um objeto Bitmap conforme mostrado no exemplo a seguir:

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

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

Em seguida, crie um objeto FirebaseModelInputs com os dados de entrada e transmita-o com a especificação de entrada e saída do modelo ao método run do interpretador de modelos:

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

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

Você pode receber a saída chamando o método getOutput() do objeto passado ao listener de êxito. Exemplo:

Java

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

Kotlin

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

Como você usa a saída depende do modelo que está usando.

Por exemplo, se você estiver realizando uma classificação, o próximo passo poderá ser atribuir os índices do resultado aos rótulos representados:

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

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

Apêndice: segurança do modelo

Independentemente de como você disponibiliza seus modelos do TensorFlow Lite para o Kit de ML, o kit os armazena localmente no formato padrão protobuf serializado.

Teoricamente, isso significa que qualquer pessoa pode copiar seu modelo. No entanto, na prática, a maioria dos modelos é tão específica de cada aplicativo e ofuscada por otimizações que o risco é comparável ao de concorrentes desmontando e reutilizando seu código. Apesar disso, você deve estar ciente desse risco antes de usar um modelo personalizado no seu app.

Na API Android nível 21 (Lollipop) e versões mais recentes, o download do modelo é feito em um diretório que não é incluído no backup automático.

Na API Android nível 20 ou versões mais antigas, o download do modelo é feito em um diretório chamado com.google.firebase.ml.custom.models em um armazenamento interno privado do app. Se você ativou o backup de arquivos usando BackupAgent, poderá optar por excluir esse diretório.