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

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

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

Esta API requer o Android SDK nível 16 (Jelly Bean) ou mais recente.

Antes de você começar

  1. Se ainda não o fez, adicione o Firebase ao seu projeto Android .
  2. Adicione as dependências das bibliotecas do ML Kit Android ao arquivo Gradle do módulo (nível do aplicativo) (geralmente 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. Converta o modelo TensorFlow que você deseja usar para o formato TensorFlow Lite. Consulte TOCO: conversor de otimização do TensorFlow Lite .

Hospede ou agrupe seu modelo

Antes de usar um modelo do TensorFlow Lite para inferência em seu aplicativo, você deve disponibilizar o modelo para o ML Kit. O kit de ML pode usar modelos do TensorFlow Lite hospedados remotamente usando o Firebase, empacotados com o binário do aplicativo ou ambos.

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

Se você optar por fornecer apenas o modelo hospedando-o com o Firebase e não agrupá-lo 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 empacotado com seu aplicativo, nenhuma funcionalidade relacionada ao modelo estará disponível até que o aplicativo baixe o modelo pela primeira vez.

Ao agrupar seu modelo com seu aplicativo, você garante que os recursos de ML do seu aplicativo ainda funcionem quando o modelo hospedado pelo Firebase não estiver disponível.

Modelos de host no Firebase

Para hospedar seu modelo do TensorFlow Lite no Firebase:

  1. Na seção ML Kit do Firebase console , clique na guia Custom .
  2. Clique em Adicionar modelo personalizado (ou Adicionar outro modelo ).
  3. Especifique um nome que será usado para identificar seu modelo no projeto do Firebase e faça upload do arquivo de modelo do TensorFlow Lite (geralmente terminando em .tflite ou .lite ).
  4. No manifesto do seu aplicativo, declare 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 ao modelo em seus aplicativos usando o nome que você especificou. A qualquer momento, você pode fazer upload de um novo modelo do TensorFlow Lite, e seu aplicativo fará o download do novo modelo e começará a usá-lo na próxima reinicialização do aplicativo. Você pode definir as condições do dispositivo necessárias para que seu aplicativo tente atualizar o modelo (veja abaixo).

Agrupe modelos com um aplicativo

Para agrupar seu modelo do TensorFlow Lite com seu aplicativo, copie o arquivo de modelo (geralmente terminando em .tflite ou .lite ) para a pasta assets/ do seu aplicativo. (Talvez seja necessário criar a pasta primeiro clicando com o botão direito do mouse no app/ pasta e clicando em Novo > Pasta > Pasta de ativos .)

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 de modelo será incluído no pacote do aplicativo e estará disponível para o ML Kit como um ativo bruto.

Carregar o modelo

Para usar o modelo do TensorFlow Lite no aplicativo, primeiro configure o ML Kit com os locais em que o modelo está disponível: remotamente usando o Firebase, no armazenamento local ou ambos. Se você especificar um modelo local e remoto, poderá usar o modelo remoto se estiver disponível e retornar ao modelo armazenado localmente se o modelo remoto não estiver disponível.

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 carregá-lo:

Java

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

Kotlin+KTX

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

Em seguida, inicie a tarefa de download do modelo, especificando as condições sob as quais você deseja 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 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+KTX

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

Muitos aplicativos iniciam a tarefa de download em seu 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ê empacotou o modelo com seu aplicativo, 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+KTX

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

Crie um interpretador a partir do seu modelo

Depois de configurar suas fontes de modelo, crie um objeto FirebaseModelInterpreter a partir de uma delas.

Se você tiver apenas um modelo empacotado localmente, basta criar um interpretador do seu objeto 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 você tiver um modelo hospedado remotamente, precisará verificar se foi baixado antes de executá-lo. Você pode verificar o status da tarefa de download do modelo usando o método isModelDownloaded() do gerenciador de modelo.

Embora você só precise confirmar isso antes de executar o interpretador, se você tiver um modelo hospedado remotamente e um modelo empacotado localmente, pode fazer sentido realizar essa verificação ao instanciar o interpretador de modelo: crie um interpretador do modelo remoto se ele foi baixado e, caso contrário, do modelo local.

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 você tiver apenas um modelo hospedado remotamente, deverá desabilitar a funcionalidade relacionada ao modelo — por exemplo, acinzentado ou ocultar parte de sua interface do usuário — até confirmar que o modelo foi baixado. Você pode fazer isso anexando um ouvinte 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+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.
    }

Especifique a entrada e a saída do modelo

Em seguida, configure os formatos de entrada e saída do interpretador de modelo.

Um modelo do TensorFlow Lite recebe como entrada e produz como saída um ou mais arrays multidimensionais. Essas matrizes contêm valores byte , int , long ou float . Você deve configurar o ML Kit com o número e as dimensões ("forma") das matrizes que seu modelo usa.

Se você não souber a forma e o tipo de dados da entrada e saída do seu modelo, poderá usar o interpretador do TensorFlow Lite 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 de entrada e saída do seu modelo, você pode configurar o interpretador de modelo do seu aplicativo criando um objeto FirebaseModelInputOutputOptions .

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

Para tal modelo, você configuraria a entrada e a saída do interpretador de modelo conforme mostrado 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+KTX

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

Realizar inferência nos dados de entrada

Por fim, para realizar a inferência usando o modelo, obtenha seus dados de entrada e execute quaisquer transformações nos dados que sejam necessárias para obter uma matriz de entrada da forma correta para seu modelo.

Por exemplo, se você tiver um modelo de classificação de imagem com uma forma de entrada de valores de ponto flutuante [1 224 224 3], poderá gerar uma matriz de entrada de 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+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
    }
}

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

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 a chamada for bem-sucedida, você poderá obter a saída chamando o método getOutput() do objeto que é passado para o listener de sucesso. Por exemplo:

Java

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

Kotlin+KTX

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, poderá mapear os índices do resultado para os rótulos que eles representam:

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

Apêndice: Segurança do modelo

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

Em teoria, isso significa que qualquer pessoa pode copiar seu modelo. No entanto, na prática, a maioria dos modelos são tão específicos do aplicativo e ofuscados por otimizações que o risco é semelhante ao dos concorrentes desmontando e reutilizando seu código. No entanto, você deve estar ciente desse risco antes de usar um modelo personalizado em seu aplicativo.

Na API do Android de nível 21 (Lollipop) e mais recente, o modelo é baixado para um diretório que é excluído do backup automático .

Na API do Android de nível 20 e anterior, o modelo é baixado em um diretório chamado com.google.firebase.ml.custom.models no armazenamento interno privado do aplicativo. Se você ativou o backup de arquivos usando BackupAgent , poderá optar por excluir esse diretório.