Agrega Firebase a tu app para iOS con tecnología TFLite

1. Descripción general

Objetivos

AA de Firebase te permite implementar tu modelo de manera inalámbrica. Esto te permite mantener un tamaño pequeño de la app y solo descargar el modelo de AA cuando sea necesario, experimentar con varios modelos o actualizar tu modelo de AA sin tener que volver a publicar toda la app.

En este codelab, convertirás una app para iOS con un modelo de TFLite estático en una app con un modelo entregado de forma dinámica desde Firebase. Aprenderás a hacer lo siguiente:

  1. Implementa modelos de TFLite en AA de Firebase y accede a ellos desde tu app
  2. Registra métricas relacionadas con el modelo con Analytics
  3. Selecciona el modelo que se cargará a través de Remote Config.
  4. Realiza pruebas A/B de diferentes modelos

Requisitos previos

Antes de comenzar este codelab, asegúrate de haber instalado lo siguiente:

  • Xcode 11 (o una versión posterior)
  • CocoaPods 1.9.1 (o una versión posterior)

2. Crea un proyecto de Firebase console

Agrega Firebase al proyecto

  1. Dirígete a Firebase console.
  2. Selecciona Crear proyecto nuevo y asígnale a tu proyecto el nombre "Codelab de iOS del AA de Firebase".

3. Obtén el proyecto de muestra

Descargue el código

Para comenzar, clona el proyecto de muestra y ejecuta pod update en el directorio del proyecto:

git clone https://github.com/FirebaseExtended/codelab-digitclassifier-ios.git
cd codelab-digitclassifier-ios
pod install --repo-update

Si no tienes Git instalado, también puedes descargar el proyecto de muestra desde su página de GitHub o haciendo clic en este vínculo. Una vez que hayas descargado el proyecto, ejecútalo en Xcode y prueba el clasificador de dígitos para comprender cómo funciona.

Configura Firebase

Sigue la documentación para crear un proyecto de Firebase nuevo. Cuando tengas tu proyecto, descarga el archivo GoogleService-Info.plist correspondiente desde Firebase console y arrástralo a la raíz del proyecto de Xcode.

f06cb08d48de7e10.png

Agrega Firebase a tu Podfile y ejecuta la instalación del Pod.

pod 'FirebaseMLModelDownloader', '9.3.0-beta'

En el método didFinishLaunchingWithOptions de tu AppDelegate, importa Firebase en la parte superior del archivo.

import FirebaseCore

Y agregar una llamada para configurar Firebase.

FirebaseApp.configure()

Vuelve a ejecutar el proyecto para asegurarte de que la app esté configurada correctamente y no falle durante el inicio.

4. Implementa un modelo en AA de Firebase

La implementación de un modelo en el AA de Firebase es útil por dos razones principales:

  1. Podemos mantener un tamaño de instalación de la app pequeño y solo descargar el modelo si es necesario.
  2. El modelo se puede actualizar con regularidad y con un ciclo de lanzamiento diferente al de toda la app.

Antes de que podamos reemplazar el modelo estático de nuestra app por un modelo descargado dinámicamente desde Firebase, debemos implementarlo en el AA de Firebase. El modelo se puede implementar a través de la consola o de manera programática con el SDK de Firebase Admin. En este paso, realizaremos la implementación a través de la consola.

Para simplificar, usaremos el modelo de TensorFlow Lite que ya está en nuestra app. Primero, abre Firebase y haz clic en Aprendizaje automático en el panel de navegación izquierdo. Luego navega a “Personalizado” y haz clic en el botón “Agregar modelo”.

Cuando se te solicite, asígnale un nombre descriptivo al modelo, como mnist_v1, y sube el archivo desde el directorio del proyecto del codelab.

3c3c50e6ef12b3b.png

5. Descarga el modelo de AA de Firebase

Elegir cuándo descargar el modelo remoto de Firebase a tu app puede ser complicado, ya que los modelos de TFLite pueden crecer relativamente grandes. Lo ideal es evitar cargar el modelo de inmediato cuando se inicia la app, ya que si nuestro modelo se usa para una sola función y el usuario nunca usa esa función, habremos descargado una cantidad significativa de datos sin motivo. También podemos configurar opciones de descarga, como recuperar solo los modelos cuando hay una conexión a Wi-Fi. Si quieres asegurarte de que el modelo esté disponible incluso sin una conexión de red, también debes empaquetar el modelo como parte de la app como copia de seguridad.

Para hacerlo más simple, quitaremos el modelo empaquetado predeterminado y siempre descargaremos un modelo de Firebase cuando se inicie la app. De esta manera, cuando ejecutas el reconocimiento de dígitos, puedes estar seguro de que la inferencia se ejecuta con el modelo proporcionado desde Firebase.

En la parte superior de ModelLoader.swift, importa el módulo de Firebase.

import FirebaseCore
import FirebaseMLModelDownloader

Luego, implementa el siguiente método.

static func downloadModel(named name: String,
                          completion: @escaping (CustomModel?, DownloadError?) -> Void) {
  guard FirebaseApp.app() != nil else {
    completion(nil, .firebaseNotInitialized)
    return
  }
  guard success == nil && failure == nil else {
    completion(nil, .downloadInProgress)
    return
  }
  let conditions = ModelDownloadConditions(allowsCellularAccess: false)
  ModelDownloader.modelDownloader().getModel(name: name, downloadType: .localModelUpdateInBackground, conditions: conditions) { result in
          switch (result) {
          case .success(let customModel):
                  // Download complete.
                  // The CustomModel object contains the local path of the model file,
                  // which you can use to instantiate a TensorFlow Lite classifier.
                  return completion(customModel, nil)
          case .failure(let error):
              // Download was unsuccessful. Notify error message.
            completion(nil, .downloadFailed(underlyingError: error))
          }
  }
}

En el elemento viewDidLoad de ViewController.swift, reemplaza la llamada de inicialización de DigitClassifier por nuestro nuevo método de descarga del modelo.

    // Download the model from Firebase
    print("Fetching model...")
    ModelLoader.downloadModel(named: "mnist_v1") { (customModel, error) in
      guard let customModel = customModel else {
        if let error = error {
          print(error)
        }
        return
      }

      print("Model download complete")
      
      // Initialize a DigitClassifier instance
      DigitClassifier.newInstance(modelPath: customModel.path) { result in
      switch result {
        case let .success(classifier):
          self.classifier = classifier
        case .error(_):
          self.resultLabel.text = "Failed to initialize."
        }
      }
    }

Vuelve a ejecutar tu app. Después de unos segundos, deberías ver un registro en Xcode que indique que el modelo remoto se descargó correctamente. Intenta dibujar un dígito y confirma que el comportamiento de la app no haya cambiado.

6. Haz un seguimiento de los comentarios y las conversiones de los usuarios para medir la precisión del modelo

Mediremos la exactitud del modelo a través del seguimiento de los comentarios de los usuarios sobre las predicciones del modelo. Si el usuario hace clic en “sí”, indicará que la predicción fue precisa.

Podemos registrar un evento de Analytics para hacer un seguimiento de la precisión de nuestro modelo. En primer lugar, debemos agregar Analytics al Podfile antes de poder usarlo en el proyecto:

pod 'FirebaseAnalytics'

Luego, en ViewController.swift, importa Firebase en la parte superior del archivo.

import FirebaseAnalytics

Agrega la siguiente línea de código al método correctButtonPressed.

Analytics.logEvent("correct_inference", parameters: nil)

Vuelve a ejecutar la app y dibuja un dígito. Presiona el botón “Yes” un par de veces para enviar comentarios que indiquen que la inferencia fue precisa.

Análisis de depuración

Por lo general, los eventos que registra la app se agrupan en lotes durante un período de aproximadamente una hora y se suben juntos. Con este enfoque, se conserva la batería de los dispositivos de los usuarios finales y se reduce el uso de datos de la red. Sin embargo, para validar tu implementación de estadísticas (y para ver tus estadísticas en el informe de DebugView), puedes habilitar el modo de depuración en tu dispositivo de desarrollo para subir eventos con un retraso mínimo.

Para habilitar el modo de depuración de Analytics en tu dispositivo de desarrollo, especifica el siguiente argumento de línea de comandos en Xcode:

-FIRDebugEnabled

Vuelve a ejecutar la app y dibuja un dígito. Presiona el botón “Yes” un par de veces para enviar comentarios que indiquen que la inferencia fue precisa. Ahora puedes ver los eventos de registro casi en tiempo real a través de la vista de depuración en Firebase console. Haz clic en Analytics > DebugView en la barra de navegación izquierda.

5276199a086721fd.png

7. Realiza un seguimiento del tiempo de inferencia con Firebase Performance

Cuando pruebas tu modelo, las métricas de rendimiento realizadas en dispositivos de desarrollo no son suficientes para capturar el rendimiento que tendrá el modelo en manos de los usuarios, ya que es difícil saber en qué hardware ejecutarán tu app los usuarios. Afortunadamente, puedes medir el rendimiento de tu modelo en los dispositivos de los usuarios con Firebase Performance para obtener un mejor panorama.

Para medir el tiempo que lleva ejecutar la inferencia, primero importa Firebase en DigitClassifier.swift:

import FirebasePerformance

Luego, inicia un registro de rendimiento en el método de clasificación y detén el seguimiento cuando se complete la inferencia. Asegúrate de agregar las siguientes líneas de código dentro del cierre de DispatchQueue.global.async y no directamente debajo de la declaración del método.

let inferenceTrace = Performance.startTrace(name: "tflite inference")
defer {
  inferenceTrace?.stop()
}

Si te interesa, puedes habilitar el registro de depuración siguiendo las instrucciones que se indican aquí para confirmar que se registran los seguimientos de rendimiento. Después de un tiempo, los seguimientos de rendimiento también se podrán ver en Firebase console.

8. Implementa un segundo modelo en el AA de Firebase

Cuando se te ocurre una nueva versión de tu modelo, como una con una mejor arquitectura de modelo o una entrenado con un conjunto de datos más grande o actualizado, podemos sentir la tentación de reemplazar nuestro modelo actual por la nueva versión. Sin embargo, un modelo que funciona bien en las pruebas no necesariamente tiene el mismo rendimiento en producción. Por lo tanto, hagamos pruebas A/B en producción para comparar nuestro modelo original y el nuevo.

Habilitar la API de Firebase Model Management

En este paso, habilitaremos la API de Firebase Model Management para implementar una versión nueva de nuestro modelo de TensorFlow Lite con código de Python.

Crea un bucket para almacenar tus modelos de AA

En Firebase console, ve a Storage y haz clic en Comenzar. fbbea78f0eb3dc9f.png

Sigue el diálogo para configurar tu bucket.

19517c0d6d2aa14d.png

Habilitar la API de AA de Firebase

Ve a la página de la API de AA de Firebase en la consola de Google Cloud y haz clic en Habilitar.

2414fd5cced6c984.pngSelecciona la app de Digit Classifier cuando se te solicite.

Ahora entrenaremos una nueva versión del modelo con un conjunto de datos más grande y, luego, la implementaremos de manera programática directamente desde el notebook de entrenamiento con el SDK de Firebase Admin.

Descarga la clave privada de la cuenta de servicio

Antes de poder usar el SDK de Firebase Admin, tendremos que crear una cuenta de servicio. Abre el panel de cuentas de servicio de Firebase console. Para ello, haz clic en este vínculo y, luego, en el botón a fin de crear una cuenta de servicio nueva para el SDK de Firebase Admin. Cuando se te solicite, haz clic en el botón Generate New Private Key. Usaremos la clave de la cuenta de servicio para autenticar nuestras solicitudes desde el notebook de Colab.

c3b95de1e5508516.png

Ahora podemos entrenar e implementar el modelo nuevo.

  1. Abre este notebook de Colab y haz una copia de él en tu propia Drive.
  2. Ejecuta la primera celda “Entrenar un modelo mejorado de TensorFlow Lite” haciendo clic en el botón de reproducción a la izquierda. Se entrenará un modelo nuevo, lo que puede tardar un poco.
  3. Si ejecutas la segunda celda, se creará una solicitud de carga de archivos. Sube el archivo JSON que descargaste de Firebase console cuando creaste tu cuenta de servicio.

71e847c6a85423b3.png

  1. Ejecuta las dos últimas celdas.

Después de ejecutar el notebook de Colab, deberías ver un segundo modelo en Firebase console. Asegúrate de que el segundo modelo se llame mnist_v2.

c316683bb4d75d57.png

9. Selecciona un modelo a través de Remote Config.

Ahora que tenemos dos modelos separados, agregaremos un parámetro para seleccionar el modelo que se descargará en el tiempo de ejecución. El valor del parámetro que reciba el cliente determinará qué modelo descargará. Primero, abre Firebase console y haz clic en el botón Remote Config en el menú de navegación izquierdo. Luego, haz clic en el botón "Agregar parámetro".

Asígnale el nombre model_name al parámetro nuevo y asígnale un valor predeterminado de mnist_v1. Haz clic en Publicar cambios para aplicar las actualizaciones. Al poner el nombre del modelo en el parámetro de Remote Config, podemos probar varios modelos sin agregar un parámetro nuevo para cada modelo que queramos probar.

Después de agregar el parámetro, deberías verlo en la consola:

699b3fd32acce887.png

En nuestro código, necesitaremos agregar una verificación cuando carguemos el modelo remoto. Cuando recibamos el parámetro de Remote Config, recuperaremos el modelo remoto con el nombre correspondiente. De lo contrario, intentaremos cargar mnist_v1. Antes de poder usar Remote Config, debemos agregarlo a nuestro proyecto especificándolo como una dependencia en el Podfile:

pod 'FirebaseRemoteConfig'

Ejecuta la instalación del Pod y vuelve a abrir el proyecto de Xcode. En ModelLoader.swift, implementa el método fetchParameterizedModel.

static func fetchParameterizedModel(completion: @escaping (CustomModel?, DownloadError?) -> Void) {
  RemoteConfig.remoteConfig().fetchAndActivate { (status, error) in
    DispatchQueue.main.async {
      if let error = error {
        let compositeError = DownloadError.downloadFailed(underlyingError: error)
        completion(nil, compositeError)
        return
      }

      let modelName: String
      if let name = RemoteConfig.remoteConfig().configValue(forKey: "model_name").stringValue {
        modelName = name
      } else {
        let defaultName = "mnist_v1"
        print("Unable to fetch model name from config, falling back to default \(defaultName)")
        modelName = defaultName
      }
      downloadModel(named: modelName, completion: completion)
    }
  }
}

Por último, en ViewController.swift, reemplaza la llamada downloadModel por el método nuevo que acabamos de implementar.

// Download the model from Firebase
print("Fetching model...")
ModelLoader.fetchParameterizedModel { (customModel, error) in
  guard let customModel = customModel else {
    if let error = error {
      print(error)
    }
    return
  }

  print("Model download complete")
  
  // Initialize a DigitClassifier instance
  DigitClassifier.newInstance(modelPath: customModel.path) { result in
  switch result {
    case let .success(classifier):
      self.classifier = classifier
    case .error(_):
      self.resultLabel.text = "Failed to initialize."
    }
  }
}

Vuelve a ejecutar la app y asegúrate de que siga cargando el modelo de forma correcta.

10. Realiza pruebas A/B de los dos modelos

Por último, podemos usar el comportamiento integrado de A/B Testing de Firebase para ver cuál de los dos modelos tiene el mejor rendimiento. Ve a Analytics -> Eventos en Firebase console. Si se muestra el evento correct_inference, márcalo como "Evento de conversión". De lo contrario, ve a Analytics -> Eventos de conversión y haz clic en "Crear un evento de conversión nuevo" y selecciona correct_inference..

Ahora ve a “Remote Config” en Firebase console y selecciona el botón “Prueba A/B” del menú de más opciones en el parámetro “model_name” que acabamos de agregar.

fad5ea36969d2aeb.png

En el menú que aparece a continuación, acepta el nombre predeterminado.

d7c006669ace6e40.png

Selecciona tu app en el menú desplegable y cambia los criterios de segmentación al 50% de los usuarios activos.

6246dd7c660b53fb.png

Si anteriormente pudiste establecer el evento correct_inference como una conversión, utiliza este evento como la métrica principal de seguimiento. De lo contrario, si no deseas esperar a que el evento aparezca en Analytics, puedes agregar correct_inference manualmente.

1ac9c94fb3159271.png

Por último, en la pantalla Variantes, configura la variante del grupo de control para que use mnist_v1 y el grupo Variante A para usar mnist_v2.

e4510434f8da31b6.png

Haz clic en el botón Revisar ubicado en la esquina inferior derecha.

Felicitaciones, creaste con éxito una prueba A/B para tus dos modelos independientes. Actualmente, la prueba A/B se encuentra en estado de borrador y puede iniciarse en cualquier momento haciendo clic en el botón "Iniciar experimento".

Para obtener información más detallada sobre las pruebas A/B, consulta la documentación sobre A/B Testing.

11. Conclusión

En este codelab, aprendiste a reemplazar un recurso de tflite empaquetado de forma estática en tu app por un modelo TFLite cargado de forma dinámica desde Firebase. Para obtener más información sobre TFLite y Firebase, consulta otras muestras de TFLite y las guías de introducción de Firebase.

¿Tienes alguna pregunta?

Informar problemas