Google se compromete a impulsar la igualdad racial para las comunidades afrodescendientes. Obtén información al respecto.

Usa un modelo personalizado de TensorFlow Lite en Android

Si tu app usa modelos personalizados de TensorFlow Lite, puedes usar AA de Firebase para implementarlos. Si implementas modelos con Firebase, puedes reducir el tamaño de la descarga inicial de tu app y actualizar sus modelos de AA sin lanzar una nueva versión. Además, con Remote Config y A/B Testing, puedes entregar de manera dinámica diferentes modelos a conjuntos distintos de usuarios.

Modelos de TensorFlow Lite

Los modelos de TensorFlow Lite son modelos de AA optimizados para ejecutarse en dispositivos móviles. Deberás realizar lo siguiente para obtener un modelo de TensorFlow Lite:

Antes de comenzar

  1. Si aún no lo hiciste, agrega Firebase a tu proyecto de Android.
  2. En tu archivo build.gradle de nivel de proyecto, asegúrate de incluir el repositorio Maven de Google en las secciones buildscript y allprojects.
  3. Usa la BoM de Firebase para Android a fin de declarar la dependencia de la biblioteca de Android de modelos de personalizados de AA de Firebase en el archivo Gradle (generalmente app/build.gradle) de tu módulo (nivel de app).

    Además, como parte de la configuración de los modelos personalizados de AA de Firebase, debes agregar el SDK de TensorFlow Lite a tu app.

    dependencies {
        // Import the BoM for the Firebase platform
        implementation platform('com.google.firebase:firebase-bom:25.12.0')
    
        // Declare the dependency for the Firebase ML Custom Models library
        // When using the BoM, you don't specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-ml-model-interpreter'
    // Also declare the dependency for the TensorFlow Lite library and specify its version implementation 'org.tensorflow:tensorflow-lite:2.3.0'
    }

    Si usas la BoM de Firebase para Android, tu app siempre utilizará versiones compatibles de las bibliotecas de Firebase para Android.

    (Alternativa) Declara las dependencias de la biblioteca de Firebase sin usar la BoM.

    Si eliges no usar la BoM de Firebase, debes especificar cada versión de la biblioteca de Firebase en su línea de dependencia.

    Ten en cuenta que, si usas varias bibliotecas de Firebase en tu app, te recomendamos que utilices la BoM para administrar las versiones de las bibliotecas, lo que garantiza que todas las versiones sean compatibles.

    dependencies {
        // Declare the dependency for the Firebase ML Custom Models library
        // When NOT using the BoM, you must specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-ml-model-interpreter:22.0.4'
    // Also declare the dependency for the TensorFlow Lite library and specify its version implementation 'org.tensorflow:tensorflow-lite:2.3.0'
    }
  4. En el manifiesto de tu app, declara que se requiera el permiso de INTERNET:
    <uses-permission android:name="android.permission.INTERNET" />

1. Implementa tu modelo

Implementa tus modelos personalizados de TensorFlow con Firebase console o los SDK de Firebase Admin Python y de Node.js. Consulta la sección sobre cómo implementar y administrar modelos personalizados.

Después de agregar un modelo personalizado al proyecto de Firebase, podrás usar el nombre que especificaste para hacer referencia al modelo en tus apps. Puedes subir un nuevo modelo de TensorFlow Lite en cualquier momento. Tu app descargará el nuevo modelo y comenzará a usarlo cuando se reinicie. Podrás definir las condiciones del dispositivo que tu app requiera para intentar actualizar el modelo (ver a continuación).

2. Descarga el modelo en el dispositivo

Para usar tu modelo de TensorFlow Lite en tu app, primero usa el SDK de AA de Firebase a fin de descargar la última versión del modelo en el dispositivo.

Para iniciar la descarga del modelo, llama al método download() del administrador de modelos y especifica el nombre que le asignaste cuando lo subiste y las condiciones en las que deseas permitir la descarga. Si el modelo no está en el dispositivo o si hay una versión más reciente de este, la tarea descargará el modelo de Firebase de forma asíncrona.

Debes inhabilitar la funcionalidad relacionada con el modelo, por ejemplo, ocultar o inhabilitar parte de la IU, hasta que confirmes que se descargó el modelo.

Java

FirebaseCustomRemoteModel remoteModel =
      new FirebaseCustomRemoteModel.Builder("your_model").build();
FirebaseModelDownloadConditions conditions = new FirebaseModelDownloadConditions.Builder()
        .requireWifi()
        .build();
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

val remoteModel = FirebaseCustomRemoteModel.Builder("your_model").build()
val conditions = FirebaseModelDownloadConditions.Builder()
    .requireWifi()
    .build()
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.
    }

Muchas apps comienzan la tarea de descarga en su código de inicialización, pero puedes hacerlo en cualquier momento antes de usar el modelo.

3. Inicializa un intérprete de TensorFlow Lite

Después de descargar el modelo en el dispositivo, puedes obtener la ubicación del archivo de modelos mediante una llamada al método getLatestModelFile() del administrador del modelo. Usa este valor para crear una instancia de un intérprete de TensorFlow Lite:

Java

FirebaseCustomRemoteModel remoteModel = new FirebaseCustomRemoteModel.Builder("your_model").build();
FirebaseModelManager.getInstance().getLatestModelFile(remoteModel)
        .addOnCompleteListener(new OnCompleteListener<File>() {
            @Override
            public void onComplete(@NonNull Task<File> task) {
                File modelFile = task.getResult();
                if (modelFile != null) {
                    interpreter = new Interpreter(modelFile);
                }
            }
        });

Kotlin+KTX

val remoteModel = FirebaseCustomRemoteModel.Builder("your_model").build()
FirebaseModelManager.getInstance().getLatestModelFile(remoteModel)
    .addOnCompleteListener { task ->
        val modelFile = task.result
        if (modelFile != null) {
            interpreter = Interpreter(modelFile)
        }
    }

4. Realiza inferencias sobre los datos de entrada

Obtén las formas de entrada y salida de tu modelo

El intérprete del modelo de TensorFlow Lite toma como entrada y produce como salida uno o más arreglos multidimensionales. Estos arreglos contienen valores byte, int, long o float. Antes de pasar datos a un modelo o utilizar su resultado, debes conocer el número y las dimensiones (“forma”) de los arreglos que usa tu modelo.

Si compilaste el modelo tú mismo o si el formato de entrada y salida del modelo está documentado, es posible que ya tengas esta información. Si no conoces la forma y el tipo de datos de la entrada y la salida del modelo, puedes usar el intérprete de TensorFlow Lite para inspeccionarlo. Por ejemplo:

Python

import tensorflow as tf

interpreter = tf.lite.Interpreter(model_path="your_model.tflite")
interpreter.allocate_tensors()

# Print input shape and type
inputs = interpreter.get_input_details()
print('{} input(s):'.format(len(inputs)))
for i in range(0, len(inputs)):
    print('{} {}'.format(inputs[i]['shape'], inputs[i]['dtype']))

# Print output shape and type
outputs = interpreter.get_output_details()
print('\n{} output(s):'.format(len(outputs)))
for i in range(0, len(outputs)):
    print('{} {}'.format(outputs[i]['shape'], outputs[i]['dtype']))

Resultado de ejemplo:

1 input(s):
[  1 224 224   3] <class 'numpy.float32'>

1 output(s):
[1 1000] <class 'numpy.float32'>

Ejecuta el intérprete

Después de determinar el formato de entrada y salida del modelo, obtén los datos de entrada y realiza las transformaciones necesarias a fin de obtener una entrada con la forma correcta para tu modelo.

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

Java

Bitmap bitmap = Bitmap.createScaledBitmap(yourInputImage, 224, 224, true);
ByteBuffer input = ByteBuffer.allocateDirect(224 * 224 * 3 * 4).order(ByteOrder.nativeOrder());
for (int y = 0; y < 224; y++) {
    for (int x = 0; x < 224; x++) {
        int px = bitmap.getPixel(x, y);

        // Get channel values from the pixel value.
        int r = Color.red(px);
        int g = Color.green(px);
        int b = Color.blue(px);

        // Normalize channel values to [-1.0, 1.0]. This requirement depends
        // on the model. For example, some models might require values to be
        // normalized to the range [0.0, 1.0] instead.
        float rf = (r - 127) / 255.0f;
        float gf = (g - 127) / 255.0f;
        float bf = (b - 127) / 255.0f;

        input.putFloat(rf);
        input.putFloat(gf);
        input.putFloat(bf);
    }
}

Kotlin+KTX

val bitmap = Bitmap.createScaledBitmap(yourInputImage, 224, 224, true)
val input = ByteBuffer.allocateDirect(224*224*3*4).order(ByteOrder.nativeOrder())
for (y in 0 until 224) {
    for (x in 0 until 224) {
        val px = bitmap.getPixel(x, y)

        // Get channel values from the pixel value.
        val r = Color.red(px)
        val g = Color.green(px)
        val b = Color.blue(px)

        // Normalize channel values to [-1.0, 1.0]. This requirement depends on the model.
        // For example, some models might require values to be normalized to the range
        // [0.0, 1.0] instead.
        val rf = (r - 127) / 255f
        val gf = (g - 127) / 255f
        val bf = (b - 127) / 255f

        input.putFloat(rf)
        input.putFloat(gf)
        input.putFloat(bf)
    }
}

Luego, asigna un ByteBuffer lo suficientemente grande para contener los datos de salida del modelo y pasar el búfer de entrada y de salida al método run() del intérprete de TensorFlow Lite. Por ejemplo, para una forma de salida de valores de punto flotante [1 1000]:

Java

int bufferSize = 1000 * java.lang.Float.SIZE / java.lang.Byte.SIZE;
ByteBuffer modelOutput = ByteBuffer.allocateDirect(bufferSize).order(ByteOrder.nativeOrder());
interpreter.run(input, modelOutput);

Kotlin+KTX

val bufferSize = 1000 * java.lang.Float.SIZE / java.lang.Byte.SIZE
val modelOutput = ByteBuffer.allocateDirect(bufferSize).order(ByteOrder.nativeOrder())
interpreter?.run(input, modelOutput)

La manera de usar el resultado depende del modelo que uses.

Por ejemplo, si realizas una clasificación, el paso siguiente puede ser asignar los índices del resultado a las etiquetas que representan:

Java

modelOutput.rewind();
FloatBuffer probabilities = modelOutput.asFloatBuffer();
try {
    BufferedReader reader = new BufferedReader(
            new InputStreamReader(getAssets().open("custom_labels.txt")));
    for (int i = 0; i < probabilities.capacity(); i++) {
        String label = reader.readLine();
        float probability = probabilities.get(i);
        Log.i(TAG, String.format("%s: %1.4f", label, probability));
    }
} catch (IOException e) {
    // File not found?
}

Kotlin+KTX

modelOutput.rewind()
val probabilities = modelOutput.asFloatBuffer()
try {
    val reader = BufferedReader(
            InputStreamReader(assets.open("custom_labels.txt")))
    for (i in probabilities.capacity()) {
        val label: String = reader.readLine()
        val probability = probabilities.get(i)
        println("$label: $probability")
    }
} catch (e: IOException) {
    // File not found?
}

Apéndice: Recurre a un modelo empaquetado localmente

Cuando alojas tu modelo con Firebase, cualquier funcionalidad relacionada con el modelo no estará disponible hasta que tu app descargue el modelo por primera vez. En el caso de algunas apps, esto podría estar bien, pero si tu modelo habilita la funcionalidad principal, es posible que desees empaquetar una versión de tu modelo con la mejor versión disponible. De esta manera, puedes asegurarte de que las funciones de AA de tu app funcionen cuando el modelo alojado en Firebase no esté disponible.

Para agrupar tu modelo de TensorFlow Lite con tu app, haz lo siguiente:

  1. Copia el archivo del modelo (que generalmente termina en .tflite o .lite) en la carpeta assets/ de tu app. (Es posible que primero debas crear la carpeta. Para ello, haz clic con el botón derecho en la carpeta app/ y, luego, en Nuevo > Carpeta > Carpeta de elementos).

  2. Agrega lo siguiente al archivo build.gradle de tu app para asegurarte de que Gradle no comprima los modelos cuando se compile la app:

    android {
    
        // ...
    
        aaptOptions {
            noCompress "tflite", "lite"
        }
    }
    

A continuación, usa el modelo empaquetado localmente cuando no esté disponible el modelo alojado:

Java

FirebaseCustomRemoteModel remoteModel =
        new FirebaseCustomRemoteModel.Builder("your_model").build();
FirebaseModelManager.getInstance().getLatestModelFile(remoteModel)
        .addOnCompleteListener(new OnCompleteListener<File>() {
            @Override
            public void onComplete(@NonNull Task<File> task) {
                File modelFile = task.getResult();
                if (modelFile != null) {
                    interpreter = new Interpreter(modelFile);
                } else {
                    try {
                        InputStream inputStream = getAssets().open("your_fallback_model.tflite");
                        byte[] model = new byte[inputStream.available()];
                        inputStream.read(model);
                        ByteBuffer buffer = ByteBuffer.allocateDirect(model.length)
                                .order(ByteOrder.nativeOrder());
                        buffer.put(model);
                        interpreter = new Interpreter(buffer);
                    } catch (IOException e) {
                        // File not found?
                    }
                }
            }
        });

Kotlin+KTX

val remoteModel = FirebaseCustomRemoteModel.Builder("your_model").build()
FirebaseModelManager.getInstance().getLatestModelFile(remoteModel)
    .addOnCompleteListener { task ->
        val modelFile = task.result
        if (modelFile != null) {
            interpreter = Interpreter(modelFile)
        } else {
            val model = assets.open("your_fallback_model.tflite").readBytes()
            val buffer = ByteBuffer.allocateDirect(model.size).order(ByteOrder.nativeOrder())
            buffer.put(model)
            interpreter = Interpreter(buffer)
        }
    }

Apéndice: Seguridad del modelo

Independientemente de cómo hagas que tus modelos de TensorFlow Lite estén disponibles para AA de Firebase, AA de Firebase los almacena en forma local en el formato estándar de protobuf serializado.

En teoría, eso significa que cualquier persona puede copiar tu modelo. Sin embargo, en la práctica, la mayoría de los modelos son tan específicos para la aplicación, y ofuscados por las optimizaciones, que el riesgo es comparable a que alguien de la competencia desensamble y vuelva a usar tu código. No obstante, debes estar al tanto de ese riesgo antes de usar un modelo personalizado en tu app.

En la API de Android nivel 21 (Lollipop) o posterior, el modelo se descarga en un directorio excluido de las copias de seguridad automáticas.

En una API de Android nivel 20 o anterior, el modelo se descarga en un directorio llamado com.google.firebase.ml.custom.models en el almacenamiento interno privado de la app. Si habilitas la copia de seguridad con BackupAgent, tienes la opción de excluir este directorio.