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

Você pode usar o kit de aprendizado de máquina 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 no GitHub para ver um exemplo de uso dessa API ou faça o codelab.

Antes de começar

  1. Se você ainda não adicionou o Firebase ao seu app, siga as etapas no guia de primeiros passos.
  2. Inclua as dependências do kit de ML no seu arquivo build.gradle no nível do app:
    dependencies {
      // ...
    
      implementation 'com.google.firebase:firebase-ml-model-interpreter:17.0.3'
    }
    
  3. 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 exibir 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 o fornecer em pacote com 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 seu modelo e seu aplicativo em um pacote, é 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 fazer referência a ele 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.

Agrupar modelos com um aplicativo

Para agrupar 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. Para isso, clique com o botão direito do mouse na pasta app/ e selecione Novo > Pasta > Pasta Assets.

Em seguida, adicione o seguinte ao arquivo build.gradle do seu aplicativo para garantir que o Gradle não comprima 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 aplicativo, primeiro configure o kit de ML com os locais onde seu modelo está disponível: na nuvem usando o Firebase, no armazenamento local ou em ambos. Se você especificar uma fonte de modelo do Cloud e uma fonte de modelo local, o kit de ML usará o modelo do Cloud se ele estiver disponível. Caso não esteja, o modelo local será usado.

Configurar uma fonte de modelo hospedada no Firebase

Se você hospedou seu modelo no Firebase, crie um objeto FirebaseCloudModelSource que especifica o nome que foi atribuído ao modelo durante o upload, as condições em que o Kit de ML precisa fazer o download do modelo inicialmente e quando as atualizações estarão disponíveis.

Java
Android

FirebaseModelDownloadConditions.Builder conditionsBuilder =
        new FirebaseModelDownloadConditions.Builder().requireWifi();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    // Enable advanced conditions on Android Nougat and newer.
    conditionsBuilder = conditionsBuilder
            .requireCharging()
            .requireDeviceIdle();
}
FirebaseModelDownloadConditions conditions = conditionsBuilder.build();

// Build a FirebaseCloudModelSource object by specifying the name you assigned the model
// when you uploaded it in the Firebase console.
FirebaseCloudModelSource cloudSource = new FirebaseCloudModelSource.Builder("my_cloud_model")
        .enableModelUpdates(true)
        .setInitialDownloadConditions(conditions)
        .setUpdatesDownloadConditions(conditions)
        .build();
FirebaseModelManager.getInstance().registerCloudModelSource(cloudSource);

Kotlin
Android

var conditionsBuilder: FirebaseModelDownloadConditions.Builder =
        FirebaseModelDownloadConditions.Builder().requireWifi()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    // Enable advanced conditions on Android Nougat and newer.
    conditionsBuilder = conditionsBuilder
            .requireCharging()
            .requireDeviceIdle()
}
val conditions = conditionsBuilder.build()

// Build a FirebaseCloudModelSource object by specifying the name you assigned the model
// when you uploaded it in the Firebase console.
val cloudSource = FirebaseCloudModelSource.Builder("my_cloud_model")
        .enableModelUpdates(true)
        .setInitialDownloadConditions(conditions)
        .setUpdatesDownloadConditions(conditions)
        .build()
FirebaseModelManager.getInstance().registerCloudModelSource(cloudSource)

Configurar uma fonte de modelo local

Se você agrupou o modelo com seu aplicativo, crie um objeto FirebaseLocalModelSource, especificando o nome do arquivo de modelo do TensorFlow Lite e atribuindo ao modelo um nome que você usará na próxima etapa.

Java
Android

FirebaseLocalModelSource localSource =
        new FirebaseLocalModelSource.Builder("my_local_model")  // Assign a name to this model
                .setAssetFilePath("my_model.tflite")
                .build();
FirebaseModelManager.getInstance().registerLocalModelSource(localSource);

Kotlin
Android

val localSource = FirebaseLocalModelSource.Builder("my_local_model") // Assign a name to this model
        .setAssetFilePath("my_model.tflite")
        .build()
FirebaseModelManager.getInstance().registerLocalModelSource(localSource)

Criar um interpretador usando suas fontes de modelo

Depois de configurar suas fontes de modelo, crie um objeto FirebaseModelOptions com a fonte do Cloud, a fonte local ou ambas, e use-o para chamar uma instância do FirebaseModelInterpreter:

Java
Android

FirebaseModelOptions options = new FirebaseModelOptions.Builder()
        .setCloudModelName("my_cloud_model")
        .setLocalModelName("my_local_model")
        .build();
FirebaseModelInterpreter firebaseInterpreter =
        FirebaseModelInterpreter.getInstance(options);

Kotlin
Android

val options = FirebaseModelOptions.Builder()
        .setCloudModelName("my_cloud_model")
        .setLocalModelName("my_local_model")
        .build()
val interpreter = FirebaseModelInterpreter.getInstance(options)

Especificar a entrada e saída do modelo

Em seguida, configure os formatos de entrada e saída do interpretador de 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 saída do seu modelo, pode usar o interpretador do TensorFlow Lite Python para inspecionar seu modelo. 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 de seu modelo, configure o interpretador de modelo do seu aplicativo criando um objeto FirebaseModelInputOutputOptions.

Por exemplo, um modelo de classificação de imagem de ponto flutuante pode utilizar como entrada uma matriz N x224x224x3 de valores float, representando um conjunto de imagens N 224x224 de três canais (RGB) e produzir como saída uma lista de 1.000 valores float, cada um representando a probabilidade de a imagem ser um membro de uma das 1.000 categorias previstas pelo modelo.

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

Java
Android

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
Android

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

Realizar inferências em dados de entrada

E 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 imagem 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
Android

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
Android

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 seus dados de entrada e transmita-o junto com a especificação de entrada e saída do modelo para o método run do interpretador de modelo:

Java
Android

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
Android

val inputs = FirebaseModelInputs.Builder()
        .add(input) // add() as many input arrays as your model requires
        .build()
firebaseInterpreter.run(inputs, inputOutputOptions)
        .addOnSuccessListener { result ->
            // ...
        }
        .addOnFailureListener(
                object : OnFailureListener {
                    override fun onFailure(e: Exception) {
                        // 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
Android

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

Kotlin
Android

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, como próxima etapa, será possível mapear os índices do resultado para os rótulos representados:

Java
Android

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
Android

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 Android API 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 Android API nível 20 ou versões mais antigas, o download do modelo é feito em um diretório chamado com.google.firebase.ml.custom.models no armazenamento interno app-private. Se você ativou o backup de arquivos usando o BackupAgent, poderá excluir esse diretório.

Enviar comentários sobre…

Precisa de ajuda? Acesse nossa página de suporte.