Você pode usar o ML Kit para realizar inferência no dispositivo com um modelo do TensorFlow Lite .
Esta API requer Android SDK nível 16 (Jelly Bean) ou mais recente.
Antes de você começar
- Se ainda não o fez, adicione o Firebase ao seu projeto Android .
- Adicione as dependências para as bibliotecas Android do ML Kit 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' }
- Converta o modelo TensorFlow que você deseja usar para o formato TensorFlow Lite. Consulte TOCO: TensorFlow Lite Optimizing Converter .
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, agrupados 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 atender dinamicamente diferentes modelos a diferentes conjuntos de usuários.
Se você optar por fornecer apenas o modelo hospedando-o com o Firebase e não empacote-o com seu aplicativo, poderá reduzir o tamanho inicial do download de seu aplicativo. Lembre-se, no entanto, de que, se o modelo não estiver incluído em seu aplicativo, qualquer funcionalidade relacionada ao modelo não estará disponível até que seu aplicativo baixe o modelo pela primeira vez.
Ao agrupar seu modelo com seu aplicativo, você pode garantir que os recursos de ML do seu aplicativo ainda funcionem quando o modelo hospedado no Firebase não estiver disponível.
Modelos de hospedagem no Firebase
Para hospedar seu modelo do TensorFlow Lite no Firebase:
- Na seção Kit de ML do console do Firebase , clique na guia Personalizado .
- Clique em Adicionar modelo personalizado (ou Adicionar outro modelo ).
- Especifique um nome que será usado para identificar seu modelo em seu projeto do Firebase e, em seguida, faça upload do arquivo de modelo do TensorFlow Lite (geralmente terminando em
.tflite
ou.lite
). - No manifesto do seu aplicativo, declare que a permissão de 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 especificado. A qualquer momento, você pode carregar um novo modelo do TensorFlow Lite, e seu aplicativo fará o download do novo modelo e começará a usá-lo na próxima vez que o aplicativo for reiniciado. Você pode definir as condições do dispositivo necessárias para que seu aplicativo tente atualizar o modelo (veja abaixo).
Agrupar modelos com um aplicativo
Para agrupar seu modelo do TensorFlow Lite com seu aplicativo, copie o arquivo do 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, em seguida, 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 compilar 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 seu modelo do TensorFlow Lite em seu aplicativo, primeiro configure o ML Kit com os locais onde seu 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 no Firebase
Se você hospedou seu modelo com o Firebase, crie um objeto FirebaseCustomRemoteModel
, especificando o nome que atribuiu 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 assíncrono do modelo 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 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 as origens do modelo, crie um objeto FirebaseModelInterpreter
a partir de uma delas.
Se você tiver apenas um modelo empacotado localmente, basta criar um interpretador de 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, será necessário verificar se ele 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 modelos.
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 executar esta verificação ao instanciar o interpretador de modelo: crie um interpretador a partir do modelo remoto se 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, desabilite a funcionalidade relacionada ao modelo, por exemplo, esmaeça ou oculte parte de sua IU, até confirmar que o modelo foi baixado. Você pode fazer isso anexando um ouvinte ao método download()
do gerenciador de modelo:
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") dos arrays que seu modelo usa.
Se você não conhece a forma e o tipo de dados de entrada e saída do seu modelo, pode usar o interpretador 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 modelo, você pode configurar o interpretador de modelo do 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 N 224x224 de três canais (RGB) e produzir como saída uma lista de 1.000 valores float
, cada um representando o probabilidade de que a imagem seja membro de uma das 1000 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()
Executar inferência nos dados de entrada
Por fim, para executar a inferência usando o modelo, obtenha seus dados de entrada e execute as transformações necessárias nos dados para obter uma matriz de entrada com a forma correta 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 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 junto com a especificação de entrada e saída do modelo para o método run
do interpretador do 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 ouvinte 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óximo passo, você pode 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 é tão específica do aplicativo e ofuscada por otimizações que o risco é semelhante ao de concorrentes desmontando e reutilizando seu código. No entanto, você deve estar ciente desse risco antes de usar um modelo personalizado em seu aplicativo.
No Android API nível 21 (Lollipop) e mais recente, o download do modelo é feito em um diretório excluído do backup automático .
Na API do Android nível 20 e anteriores, o download do modelo é feito em um diretório chamado com.google.firebase.ml.custom.models
no armazenamento interno privado do aplicativo. Se você ativou o backup de arquivo usando BackupAgent
, pode optar por excluir esse diretório.