Adicionar o Firebase ao seu app iOS com TFLite

1. Visão geral

Gols

O Firebase ML permite que você implante seu modelo over the air. Assim, é possível manter o app pequeno e fazer o download do modelo de ML somente quando necessário, testar vários modelos ou atualizar seu modelo de ML sem precisar republicar o app inteiro.

Neste codelab, você converterá um app iOS que usa um modelo estático do TFLite em um app usando um modelo disponibilizado dinamicamente pelo Firebase. Você vai aprender o seguinte:

  1. Implante modelos do TFLite para o Firebase ML e acesse-os pelo seu app
  2. Registrar métricas relacionadas ao modelo com o Google Analytics
  3. Selecione qual modelo é carregado pela Configuração remota
  4. Faça testes A/B em diferentes modelos

Pré-requisitos

Antes de iniciar este codelab, verifique se você instalou:

  • Xcode 11 (ou versão mais recente)
  • CocoaPods 1.9.1 (ou mais recente)

2. Criar projeto do Console do Firebase

Adicionar o Firebase ao projeto

  1. Acesse o Console do Firebase.
  2. Selecione Criar novo projeto e dê a ele o nome "Firebase ML iOS Codelab".

3. Acesse o projeto de amostra

Faça o download do código

Comece clonando o projeto de amostra e executando pod update no diretório do projeto:

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

Se o git não estiver instalado, faça o download do projeto de exemplo na página do GitHub ou clique neste link. Depois de fazer o download do projeto, execute-o no Xcode e teste o classificador de dígitos para ter uma ideia de como ele funciona.

Configurar o Firebase

Siga a documentação para criar um novo projeto do Firebase. Quando tiver seu projeto, faça o download do arquivo GoogleService-Info.plist dele no Console do Firebase e arraste-o para a raiz do projeto Xcode.

f06cb08d48de7e10.png

Adicione o Firebase ao seu Podfile e execute a instalação do pod.

pod 'FirebaseMLModelDownloader', '9.3.0-beta'

No método didFinishLaunchingWithOptions do AppDelegate, importe o Firebase na parte de cima do arquivo.

import FirebaseCore

E adicionar uma chamada para configurar o Firebase.

FirebaseApp.configure()

Execute o projeto novamente para garantir que o app esteja configurado corretamente e não falhe após a inicialização.

4. Implantar um modelo no Firebase ML

A implantação de um modelo no Firebase ML é útil por dois motivos principais:

  1. Podemos manter o tamanho da instalação do app pequeno e fazer o download do modelo apenas se necessário
  2. O modelo pode ser atualizado regularmente e com um ciclo de lançamento diferente do que todo o app

Antes de substituir o modelo estático no app por um modelo baixado dinamicamente do Firebase, é preciso implantá-lo no Firebase ML. O modelo pode ser implantado por meio do console ou de forma programática com o SDK Admin do Firebase. Nesta etapa, vamos implantar pelo console.

Para simplificar, usaremos o modelo do TensorFlow Lite que já está no nosso app. Primeiro, abra o Firebase e clique em "Machine Learning" no painel de navegação à esquerda. Depois navegue até "Personalizado" e clique no botão "Adicionar modelo".

Quando solicitado, dê um nome descritivo ao modelo, como mnist_v1, e faça upload do arquivo do diretório do projeto do codelab.

3c3c50e6ef12b3b.png

5. Fazer o download do modelo do Firebase ML

Escolher quando fazer o download do modelo remoto do Firebase para seu aplicativo pode ser complicado, já que os modelos do TFLite podem crescer relativamente. O ideal é evitar o carregamento do modelo imediatamente após a inicialização do app, já que se o modelo for usado para apenas um recurso e o usuário nunca utilizar esse recurso, uma quantidade significativa de dados será baixada sem motivo. Também é possível definir opções de download, como buscar modelos apenas quando conectado a uma rede Wi-Fi. Para garantir que o modelo esteja disponível mesmo sem uma conexão de rede, agrupe-o como parte do app como backup.

Para simplificar, removeremos o modelo de pacote padrão e sempre faremos o download de um modelo do Firebase quando o app for iniciado. Dessa forma, ao executar o reconhecimento de dígitos, você tem certeza de que a inferência está sendo executada com o modelo fornecido pelo Firebase.

Na parte superior de ModelLoader.swift, importe o módulo do Firebase.

import FirebaseCore
import FirebaseMLModelDownloader

Em seguida, implemente o método a seguir.

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

No viewDidLoad do ViewController.swift, substitua a chamada de inicialização do DigitClassifier pelo novo método de download do 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."
        }
      }
    }

Execute o app novamente. Após alguns segundos, você verá um registro no Xcode indicando que o download do modelo remoto foi concluído. Tente desenhar um dígito e confirme se o comportamento do app não mudou.

6. Acompanhe o feedback e a conversão dos usuários para medir a precisão do modelo

Vamos medir a acurácia do modelo rastreando o feedback dos usuários sobre as previsões do modelo. Se um usuário clicar em "sim", isso indicará que a previsão foi precisa.

Podemos registrar um evento do Analytics para acompanhar a acurácia do nosso modelo. Primeiro, é preciso adicionar o Analytics ao Podfile para poder usá-lo no projeto:

pod 'FirebaseAnalytics'

Em seguida, em ViewController.swift, importe o Firebase na parte de cima do arquivo.

import FirebaseAnalytics

E adicione a seguinte linha de código ao método correctButtonPressed.

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

Execute o app novamente e desenhe um dígito. Pressione o botão "Yes" algumas vezes para enviar feedback informando que a inferência foi precisa.

Análise de depuração

Em geral, os eventos registrados pelo seu aplicativo são agrupados ao longo de aproximadamente uma hora e enviados juntos. Essa abordagem economiza a bateria dos dispositivos dos usuários finais e reduz o uso de dados de rede. No entanto, para validar sua implementação de análise (e para consultar sua análise no Relatório do DebugView), você pode ativar o modo de depuração no seu dispositivo de desenvolvimento para fazer upload de eventos com um atraso mínimo.

Para ativar o modo de depuração do Analytics no seu dispositivo de desenvolvimento, especifique o seguinte argumento de linha de comando no Xcode:

-FIRDebugEnabled

Execute o app novamente e desenhe um dígito. Pressione o botão "Yes" algumas vezes para enviar feedback informando que a inferência foi precisa. Agora é possível exibir os eventos de registro quase em tempo real pela visualização de depuração no Console do Firebase. Clique em Analytics > DebugView na barra de navegação à esquerda.

5276199a086721fd.png

7. Acompanhar o tempo de inferência com o Firebase Performance

Ao testar o modelo, as métricas de desempenho feitas em dispositivos de desenvolvimento não são suficientes para capturar o desempenho do modelo nas mãos dos usuários, já que é difícil dizer em qual hardware o app será executado. Felizmente, é possível medir o desempenho do seu modelo nos dispositivos dos usuários com o Firebase Performance para ter uma visão melhor do desempenho dele.

Para medir o tempo necessário para executar a inferência, primeiro importe o Firebase para o DigitClassifier.swift:

import FirebasePerformance

Em seguida, inicie um rastreamento de desempenho no método de classificação e interrompa-o quando a inferência for concluída. Adicione as seguintes linhas de código no fechamento DispatchQueue.global.async, e não diretamente abaixo da declaração do método.

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

Se quiser, ative a geração de registros de depuração seguindo estas instruções para confirmar se os traces de desempenho estão sendo registrados. Depois de um tempo, os traces de desempenho também ficarão visíveis no console do Firebase.

8. Implantar um segundo modelo no Firebase ML

Ao elaborar uma nova versão do seu modelo, como uma versão com melhor arquitetura ou treinada com um conjunto de dados maior ou atualizado, podemos nos sentir tentados a substituir o modelo atual pela versão nova. No entanto, um modelo com bom desempenho em testes não necessariamente terá um desempenho tão bom na produção. Portanto, vamos fazer testes A/B na produção para comparar o modelo original e o novo.

Ativar a API Firebase Model Management

Nesta etapa, ativaremos a API Firebase Model Management para implantar uma nova versão do nosso modelo do TensorFlow Lite usando código Python.

Crie um bucket para armazenar seus modelos de ML

No Console do Firebase, acesse Storage e clique em "Começar". fbbea78f0eb3dc9f.png

Siga as mensagens para configurar o bucket.

19517c0d6d2aa14d.png

Ativar a API Firebase ML

Acesse a página da API Firebase ML no console do Google Cloud e clique em "Ativar".

2414fd5cced6c984.pngQuando solicitado, selecione o app Digit Classifier.

Agora vamos treinar uma nova versão do modelo usando um conjunto de dados maior. Depois, vamos fazer a implantação de maneira programática diretamente do notebook de treinamento usando o SDK Admin do Firebase.

Fazer o download da chave privada da conta de serviço

Para podermos usar o SDK Admin do Firebase, precisamos criar uma conta de serviço. Clique neste link para abrir o painel "Contas de serviço" do Console do Firebase e crie uma nova conta de serviço para o SDK Admin do Firebase. Quando solicitado, clique no botão Gerar nova chave privada. Usaremos a chave da conta de serviço para autenticar nossas solicitações do notebook do Colab.

c3b95de1e5508516.png

Agora podemos treinar e implantar o novo modelo.

  1. Abra este bloco do Colab e faça uma cópia dele no seu Drive.
  2. Execute a primeira célula "Treinar um modelo aprimorado do TensorFlow Lite" clicando no botão de reprodução à esquerda. Isso vai treinar um novo modelo e pode levar algum tempo.
  3. Executar a segunda célula cria um prompt de upload de arquivo. Faça upload do arquivo JSON que você baixou do console do Firebase ao criar sua conta de serviço.

71e847c6a85423b3.png

  1. Execute as duas últimas células.

Depois de executar o notebook do Colab, um segundo modelo será exibido no Console do Firebase. Verifique se o nome do segundo modelo é mnist_v2.

c316683bb4d75d57.png

9. Selecione um modelo usando a Configuração remota

Agora que temos dois modelos separados, vamos adicionar um parâmetro para selecionar o modelo que será transferido por download no tempo de execução. O valor do parâmetro que o cliente recebe determina de qual modelo o cliente faz o download. Primeiro, abra o Console do Firebase e clique no botão Configuração remota no menu de navegação à esquerda. Depois, clique no botão "Adicionar parâmetro".

Nomeie o novo parâmetro como model_name e atribua a ele o valor padrão de mnist_v1. Clique em Publicar alterações para aplicar as atualizações. Ao colocar o nome do modelo no parâmetro da Configuração remota, é possível testar vários modelos sem adicionar um novo parâmetro para cada um deles.

Depois de adicionar o parâmetro, ele aparecerá no Console:

699b3fd32acce887.png

Em nosso código, precisaremos adicionar uma verificação ao carregar o modelo remoto. Quando recebermos o parâmetro da Configuração remota, buscaremos o modelo remoto com o nome correspondente. Caso contrário, tentaremos carregar mnist_v1. Antes de usar a Configuração remota, precisamos adicioná-la ao nosso projeto especificando-a como uma dependência no Podfile:

pod 'FirebaseRemoteConfig'

Execute a instalação do pod e abra novamente o projeto Xcode. Em ModelLoader.swift, implemente o 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 fim, em ViewController.swift, substitua a chamada downloadModel pelo novo método 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."
    }
  }
}

Execute o app novamente e verifique se ele ainda carrega o modelo corretamente.

10. Faça testes A/B nos dois modelos

Por fim, podemos usar o comportamento integrado do Teste A/B do Firebase para ver qual dos nossos dois modelos tem o melhor desempenho. Acesse Analytics -> Eventos no Console do Firebase. Se o evento correct_inference estiver aparecendo, marque-o como "Evento de conversão". Caso contrário, acesse "Analytics -> Eventos de conversão", clique em "Criar um novo evento de conversão" e salve o correct_inference..

Agora acesse "Configuração remota no Console do Firebase" e selecione o botão "Teste A/B" no menu "Mais opções" no parâmetro "model_name" que acabamos de adicionar.

fad5ea36969d2aeb.png

No menu a seguir, aceite o nome padrão.

d7c006669ace6e40.png

Selecione seu app no menu suspenso e mude os critérios de segmentação para 50% de usuários ativos.

6246dd7c660b53fb.png

Se você conseguiu definir o evento correct_inference como uma conversão antes, use-o como a métrica principal de acompanhamento. Caso contrário, se você não quiser esperar o evento aparecer no Analytics, adicione correct_inference manualmente.

1ac9c94fb3159271.png

Por fim, na tela "Variantes", defina a variante do grupo de controle como mnist_v1 e o grupo da variante A como mnist_v2.

e4510434f8da31b6.png

Clique no botão "Revisar" no canto inferior direito.

Parabéns! Você criou um teste A/B para os dois modelos diferentes. No momento, o teste A/B está em estado de rascunho e pode ser iniciado a qualquer momento clicando no botão "Iniciar experimento".

Para saber mais sobre o teste A/B, consulte a documentação relacionada.

11. Conclusão

Neste codelab, você aprendeu a substituir um recurso tflite estaticamente agrupado no seu app por um modelo TFLite carregado dinamicamente no Firebase. Para saber mais sobre o TFLite e o Firebase, confira outros exemplos do TFLite e os guias de iniciação do Firebase.

Perguntas?

Informar problemas