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

Use un modelo TensorFlow Lite para la inferencia con ML Kit en Android

Puedes usar ML Kit para realizar inferencias en el dispositivo con un modelo TensorFlow Lite .

Esta API requiere Android SDK nivel 16 (Jelly Bean) o más reciente.

Antes de que empieces

  1. Si aún no lo ha hecho, agregue Firebase a su proyecto de Android .
  2. Agregue las dependencias para las bibliotecas de Android ML Kit a su archivo Gradle de módulo (nivel de aplicación) (generalmente 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. Convierta el modelo de TensorFlow que desea usar al formato TensorFlow Lite. Consulte TOCO: Convertidor de optimización de TensorFlow Lite .

Aloja o agrupa tu modelo

Antes de poder usar un modelo de TensorFlow Lite para la inferencia en su aplicación, debe hacer que el modelo esté disponible para ML Kit. ML Kit puede usar modelos de TensorFlow Lite alojados de forma remota con Firebase, integrados con el binario de la aplicación o ambos.

Al alojar un modelo en Firebase, puede actualizar el modelo sin lanzar una nueva versión de la aplicación, y puede usar Remote Config y A/B Testing para entregar dinámicamente diferentes modelos a diferentes conjuntos de usuarios.

Si elige proporcionar el modelo solo alojándolo con Firebase y no incluirlo en su aplicación, puede reducir el tamaño de descarga inicial de su aplicación. Tenga en cuenta, sin embargo, que si el modelo no está incluido con su aplicación, ninguna funcionalidad relacionada con el modelo estará disponible hasta que su aplicación descargue el modelo por primera vez.

Al agrupar su modelo con su aplicación, puede asegurarse de que las funciones de ML de su aplicación sigan funcionando cuando el modelo alojado en Firebase no esté disponible.

Aloja modelos en Firebase

Para alojar su modelo TensorFlow Lite en Firebase:

  1. En la sección Kit de aprendizaje automático de Firebase console , haga clic en la pestaña Personalizado .
  2. Haga clic en Agregar modelo personalizado (o Agregar otro modelo ).
  3. Especifique un nombre que se usará para identificar su modelo en su proyecto de Firebase, luego cargue el archivo del modelo de TensorFlow Lite (generalmente termina en .tflite o .lite ).
  4. En el manifiesto de su aplicación, declare que se requiere permiso de INTERNET:
    <uses-permission android:name="android.permission.INTERNET" />
    

Después de agregar un modelo personalizado a su proyecto de Firebase, puede hacer referencia al modelo en sus aplicaciones con el nombre que especificó. En cualquier momento, puede cargar un nuevo modelo de TensorFlow Lite y su aplicación descargará el nuevo modelo y comenzará a usarlo la próxima vez que se reinicie la aplicación. Puede definir las condiciones del dispositivo requeridas para que su aplicación intente actualizar el modelo (consulte a continuación).

Paquete de modelos con una aplicación

Para agrupar su modelo de TensorFlow Lite con su aplicación, copie el archivo del modelo (que generalmente termina en .tflite o .lite ) en la carpeta assets/ de su aplicación. (Es posible que primero deba crear la carpeta haciendo clic con el botón derecho en la app/ carpeta y luego haciendo clic en Nuevo > Carpeta > Carpeta de activos ).

Luego, agrega lo siguiente al archivo build.gradle de tu aplicación para asegurarte de que Gradle no comprima los modelos al compilar la aplicación:

android {

    // ...

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

El archivo del modelo se incluirá en el paquete de la aplicación y estará disponible para ML Kit como recurso sin procesar.

Carga el modelo

Para usar su modelo TensorFlow Lite en su aplicación, primero configure ML Kit con las ubicaciones donde su modelo está disponible: de forma remota usando Firebase, en almacenamiento local o ambos. Si especifica un modelo tanto local como remoto, puede usar el modelo remoto si está disponible y recurrir al modelo almacenado localmente si el modelo remoto no está disponible.

Configurar un modelo alojado en Firebase

Si alojó su modelo con Firebase, cree un objeto FirebaseCustomRemoteModel y especifique el nombre que le asignó al modelo cuando lo cargó:

Java

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

Kotlin+KTX

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

Luego, inicie la tarea de descarga del modelo, especificando las condiciones bajo las cuales desea permitir la descarga. Si el modelo no está en el dispositivo, o si hay disponible una versión más reciente del modelo, la tarea descargará el modelo de forma asíncrona desde 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.
    }

Muchas aplicaciones inician la tarea de descarga en su código de inicialización, pero puede hacerlo en cualquier momento antes de que necesite usar el modelo.

Configurar un modelo local

Si empaquetaste el modelo con tu aplicación, crea un objeto FirebaseCustomLocalModel y especifica el nombre de archivo del modelo de TensorFlow Lite:

Java

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

Kotlin+KTX

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

Crea un intérprete a partir de tu modelo.

Después de configurar las fuentes de su modelo, cree un objeto FirebaseModelInterpreter a partir de una de ellas.

Si solo tiene un modelo empaquetado localmente, simplemente cree un intérprete a partir de su 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)

Si tiene un modelo alojado de forma remota, deberá verificar que se haya descargado antes de ejecutarlo. Puede comprobar el estado de la tarea de descarga del modelo mediante el método isModelDownloaded() del administrador de modelos.

Aunque solo tiene que confirmar esto antes de ejecutar el intérprete, si tiene un modelo alojado de forma remota y un modelo empaquetado localmente, podría tener sentido realizar esta comprobación al instanciar el intérprete del modelo: cree un intérprete a partir del modelo remoto si se ha descargado y, en caso contrario, del 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)
}

Si solo tiene un modelo alojado de forma remota, debe deshabilitar la funcionalidad relacionada con el modelo, por ejemplo, atenuar u ocultar parte de su interfaz de usuario, hasta que confirme que el modelo se ha descargado. Puede hacerlo adjuntando un oyente al método download() del administrador 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.
    }

Especificar la entrada y salida del modelo

A continuación, configure los formatos de entrada y salida del intérprete del modelo.

Un modelo de TensorFlow Lite toma como entrada y produce como salida una o más matrices multidimensionales. Estas matrices contienen valores byte , int , long o float . Debe configurar ML Kit con el número y las dimensiones ("forma") de las matrices que usa su modelo.

Si no conoce la forma y el tipo de datos de la entrada y la salida de su modelo, puede usar el intérprete de Python de TensorFlow Lite para inspeccionar su modelo. Por ejemplo:

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

Una vez que haya determinado el formato de entrada y salida de su modelo, puede configurar el intérprete de modelo de su aplicación creando un objeto FirebaseModelInputOutputOptions .

Por ejemplo, un modelo de clasificación de imágenes de coma flotante podría tomar como entrada una matriz N x224x224x3 de valores float , que representa un lote de N 224x224 imágenes de tres canales (RGB), y producir como salida una lista de 1000 valores float , cada uno de los cuales representa el probabilidad de que la imagen sea miembro de una de las 1000 categorías que predice el modelo.

Para tal modelo, configuraría la entrada y la salida del intérprete del modelo como se muestra a continuación:

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 inferencias sobre datos de entrada

Finalmente, para realizar inferencias utilizando el modelo, obtenga sus datos de entrada y realice las transformaciones en los datos que sean necesarias para obtener una matriz de entrada con la forma adecuada para su modelo.

Por ejemplo, si tiene un modelo de clasificación de imágenes con una forma de entrada de [1 224 224 3] valores de punto flotante, podría generar una matriz de entrada a partir de un objeto de Bitmap de bits como se muestra en el siguiente ejemplo:

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

Luego, crea un objeto FirebaseModelInputs con tus datos de entrada y pásalo junto con la especificación de entrada y salida del modelo al método de run del intérprete del 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
            // ...
        }

Si la llamada tiene éxito, puede obtener el resultado llamando al método getOutput() del objeto que se pasa al detector de éxito. Por ejemplo:

Java

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

Kotlin+KTX

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

La forma en que utilice la salida depende del modelo que esté utilizando.

Por ejemplo, si está realizando una clasificación, como siguiente paso, puede asignar los índices del resultado a las etiquetas que representan:

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: Modelo de seguridad

Independientemente de cómo haga que sus modelos de TensorFlow Lite estén disponibles para ML Kit, ML Kit los almacena en el formato protobuf serializado estándar en el almacenamiento local.

En teoría, esto significa que cualquiera puede copiar su modelo. Sin embargo, en la práctica, la mayoría de los modelos son tan específicos de la aplicación y están tan ofuscados por las optimizaciones que el riesgo es similar al de los competidores que desensamblan y reutilizan su código. No obstante, debe tener en cuenta este riesgo antes de utilizar un modelo personalizado en su aplicación.

En el nivel 21 de la API de Android (Lollipop) y posteriores, el modelo se descarga en un directorio que está excluido de la copia de seguridad automática .

En el nivel de API de Android 20 y anteriores, el modelo se descarga en un directorio llamado com.google.firebase.ml.custom.models en el almacenamiento interno privado de la aplicación. Si habilitó la copia de seguridad de archivos mediante BackupAgent , puede optar por excluir este directorio.