将 Firebase 添加到由 TFLite 提供支持的 iOS 应用

1. 概览

目标

借助 Firebase ML,您可以通过无线方式部署模型。这样一来,您可以缩减应用大小,仅在需要时下载机器学习模型、对多个模型进行实验或更新机器学习模型,而无需重新发布整个应用。

在此 Codelab 中,您将使用 Firebase 动态提供的模型将使用静态 TFLite 模型的 iOS 应用转换为应用。您将了解如何:

  1. 将 TFLite 模型部署到 Firebase ML 并从您的应用访问这些模型
  2. 使用 Analytics 记录模型相关指标
  3. 选择要通过 Remote Config 加载的模型
  4. 对不同的模型进行 A/B 测试

前提条件

在开始此 Codelab 之前,请确保您已安装:

  • Xcode 11(或更高版本)
  • CocoaPods 1.9.1(或更高版本)

2. 创建 Firebase 控制台项目

将 Firebase 添加到项目中

  1. 前往 Firebase 控制台
  2. 选择创建新项目,并将您的项目命名为“Firebase ML iOS Codelab”。

3. 获取示例项目

下载代码

首先克隆示例项目,并在项目目录中运行 pod update

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

如果您未安装 git,也可以从其 GitHub 页面或点击此链接下载示例项目。下载项目后,在 Xcode 中运行它并试用数字分类器,以了解其工作方式。

设置 Firebase

按照文档中的说明创建一个新的 Firebase 项目。获得项目后,从 Firebase 控制台下载项目的 GoogleService-Info.plist 文件,并将其拖到 Xcode 项目的根目录。

F06cb08d48de7e10.png

将 Firebase 添加到您的 Podfile 并运行 pod install。

pod 'FirebaseMLModelDownloader', '9.3.0-beta'

AppDelegatedidFinishLaunchingWithOptions 方法中,在文件顶部导入 Firebase

import FirebaseCore

然后添加一个用于配置 Firebase 的调用。

FirebaseApp.configure()

再次运行项目,确保应用配置正确,不会在启动时崩溃。

4.将模型部署到 Firebase ML

将模型部署到 Firebase ML 非常有用,主要有两个原因:

  1. 我们可以保持较小的应用安装大小,并且仅在需要时下载模型
  2. 模型可以定期更新,并且采用不同于整个应用的发布周期

在将应用中的静态模型替换为从 Firebase 动态下载的模型之前,我们需要将该模型部署到 Firebase ML。您可以通过控制台部署模型,也可以使用 Firebase Admin SDK 以编程方式部署模型。在此步骤中,我们将通过控制台进行部署。

为简单起见,我们将使用应用中已有的 TensorFlow Lite 模型。首先,打开 Firebase,然后点击左侧导航面板中的“机器学习”。然后,转到“自定义”并点击“添加模型”按钮。

出现提示时,为模型指定一个描述性名称(如 mnist_v1),然后从 Codelab 项目目录中上传文件。

3c3c50e6ef12b3b

5. 从 Firebase ML 下载模型

选择何时从 Firebase 将远程模型下载到应用可能会很困难,因为 TFLite 模型可能会变得相对较大。理想情况下,我们希望避免在应用启动时立即加载模型,因为如果模型仅用于一项功能,而用户从未使用该功能,我们就会无缘无故下载大量数据。我们还可以设置下载选项,例如在连接到 Wi-Fi 时仅提取模型。如果希望确保模型在没有网络连接时也可使用,还应将模型捆绑为应用的一部分作为备用。

为简单起见,我们将移除默认的捆绑模型,并始终在应用启动时从 Firebase 下载模型。这样,在运行数字识别时,您可以确保推断是使用 Firebase 提供的模型运行。

ModelLoader.swift 的顶部,导入 Firebase 模块。

import FirebaseCore
import FirebaseMLModelDownloader

然后,实现以下方法。

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

ViewController.swiftviewDidLoad 中,将 DigitClassifier 初始化调用替换为新的模型下载方法。

    // 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."
        }
      }
    }

重新运行您的应用。几秒钟后,您应该会在 Xcode 中看到一条日志,表明远程模型已成功下载。试着画一个数字,并确认应用的行为没有改变。

6. 跟踪用户反馈和转化情况,以衡量模型准确率

我们将通过跟踪用户对模型预测的反馈来衡量模型的准确性。如果用户点击“是”,则表示预测是准确的。

我们可以记录 Analytics 事件来跟踪模型的准确性。首先,我们必须先将 Analytics(分析)添加到 Podfile 中,然后才能在项目中使用它:

pod 'FirebaseAnalytics'

然后在 ViewController.swift 中,在文件顶部导入 Firebase

import FirebaseAnalytics

correctButtonPressed 方法中添加以下代码行。

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

再次运行应用并绘制一个数字。按几次“是”按钮,以发送关于推断准确的反馈。

调试分析

通常,应用程序所记录的事件会在大约一小时的时间内一起批量上传,并一起上传。这种方法可以节省最终用户设备的电量并减少网络流量消耗。不过,为了验证您的分析实现效果(以及为了在 DebugView 报告中查看您的分析),您可以在开发设备上启用调试模式,以尽可能减少延迟上传事件。

要在开发设备上启用 Google Analytics(分析)“调试”模式,请在 Xcode 中指定以下命令行参数:

-FIRDebugEnabled

再次运行应用并绘制一个数字。按几次“是”按钮,以发送关于推断准确的反馈。现在,您可以通过 Firebase 控制台中的调试视图近乎实时地查看日志事件。在左侧导航栏中,依次点击“Analytics”(分析)>“DebugView”。

5276199a086721fd.png

7. 通过 Firebase Performance 跟踪推理时间

测试模型时,在开发设备上提供的性能指标不足以捕获用户手中模型的性能,因为很难判断用户将通过哪种硬件运行您的应用。幸运的是,您可以使用 Firebase 性能来衡量模型在用户设备上的表现,以更好地了解模型的性能。

如需测量运行推理所需的时间,请先在 DigitClassifier.swift 中导入 Firebase:

import FirebasePerformance

然后,在分类方法中启动性能跟踪,并在推断完成时停止跟踪。请确保在 DispatchQueue.global.async 闭包内(而不是在方法声明正下方)添加以下代码行。

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

如果您想了解其他相关信息,可以按照此处的说明启用调试日志记录,以确认您的性能跟踪记录是否已记录下来。一段时间后,您还可以在 Firebase 控制台中看到性能跟踪记录。

8. 将第二个模型部署到 Firebase ML

在想出模型的新版本(例如,具有更好的模型架构的版本,或使用较大或更新的数据集训练的版本)时,我们可能会想用新版本替换当前的模型。但是,一个在测试中表现良好的模型不一定在生产环境中表现同样出色。因此,让我们在生产环境中进行 A/B 测试,以比较原始模型和新模型。

启用 Firebase Model Management API

在此步骤中,我们将启用 Firebase Model Management API,以使用 Python 代码部署 TensorFlow Lite 模型的新版本。

创建存储分区以存储机器学习模型

在 Firebase 控制台中,点击“Storage”(开始),然后点击“Get started”(开始使用)。fbbea78f0eb3dc9f.png

按照对话框设置存储分区。

19517c0d6d2aa14d.png

启用 Firebase ML API

转到 Google Cloud 控制台上的 Firebase ML API 页面,然后点击“启用”。

2414fd5cced6c984.png当系统询问时,选择 Digit Classifier 应用。

现在,我们将使用较大的数据集来训练模型的新版本,然后使用 Firebase Admin SDK 直接从训练笔记本以编程方式部署该模型。

下载服务帐号的私钥

在使用 Firebase Admin SDK 之前,我们需要创建一个服务帐号。点击此链接,打开 Firebase 控制台的“服务帐号”面板,然后点击该按钮,即可为 Firebase Admin SDK 创建新的服务帐号。出现提示时,点击“Generate New Private Key”(生成新私钥)按钮。我们将使用该服务帐号密钥对来自 Colab 笔记本的请求进行身份验证。

c3b95de1e5508516

现在,我们可以训练和部署新模型了。

  1. 打开此 Colab 笔记本,并在您自己的云端硬盘下创建一份副本。
  2. 点击第一个单元左侧的播放按钮,运行第一个单元“训练改进的 TensorFlow Lite 模型”。这将训练新模型,可能需要一些时间。
  3. 运行第二个单元格将会创建一个文件上传提示。上传您在创建服务帐号时从 Firebase 控制台下载的 JSON 文件。

71e847c6a85423b3

  1. 运行最后两个单元格。

运行 Colab 笔记本后,您应该会在 Firebase 控制台中看到第二个模型。确保第二个模型的名称为 mnist_v2

c316683bb4d75d57

9. 通过 Remote Config 选择模型

现在我们有了两个单独的模型,接下来将添加一个参数,用于选择要在运行时下载哪个模型。客户端收到的参数值将决定客户端下载哪个模型。首先,打开 Firebase 控制台,点击左侧导航菜单中的“Remote Config”按钮。然后,点击“添加参数”按钮。

将新参数命名为 model_name,并将其默认值设为 mnist_v1。点击发布更改以应用更新。通过将模型名称放入 Remote Config 参数中,我们可以测试多个模型,而无需为要测试的每个模型添加新参数。

添加该参数后,您应该会在 Console 中看到它:

699b3fd32acce887.png

在我们的代码中,我们需要在加载远程模型时添加一项检查。当我们从 Remote Config 收到参数时,我们将提取具有相应名称的远程模型;否则,我们将尝试加载 mnist_v1。在使用 Remote Config 之前,我们必须先在 Podfile 中将其指定为依赖项,以将其添加到我们的项目中:

pod 'FirebaseRemoteConfig'

运行 pod install 并重新打开 Xcode 项目。在 ModelLoader.swift 中,实现 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)
    }
  }
}

最后,在 ViewController.swift 中,将 downloadModel 调用替换为我们刚刚实现的新方法。

// 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."
    }
  }
}

重新运行应用,并确保它仍然正确加载模型。

10. 对两种模型进行 A/B 测试

最后,我们可以使用 Firebase 的内置 A/B Testing 行为来了解两个模型中哪个效果更好。在 Firebase 控制台中依次点击“分析”->“事件”。如果显示的是 correct_inference 事件,请将其标记为“转化事件”。如果没有,您可以依次转到“Google Analytics(分析)”->“转化事件”并点击“创建新的转化事件”,然后放置 correct_inference.

现在,前往 Firebase 控制台中的“Remote Config”,从我们刚刚添加的“model_name”参数的“更多选项”菜单中选择“A/B test”按钮。

fad5ea36969d2aeb

在接下来出现的菜单中,接受默认名称。

d7c006669ace6e40.png

从下拉菜单中选择您的应用,然后将定位条件更改为活跃用户的 50%。

6246dd7c660b53fb.png

如果您之前能够将 correct_inference 事件设为转化,请将此事件用作要跟踪的主要指标。如果您不想等待事件显示在 Google Analytics(分析)中,可以手动添加 correct_inference

1ac9c94fb3159271.png

最后,在“变体”屏幕上,将对照组变体设为使用“mnist_v1”,将变体 A 组设为使用“mnist_v2”。

e4510434f8da31b6

点击右下角的“审核”按钮。

恭喜,您已成功为两个单独的模型创建了 A/B 测试!此 A/B 测试目前处于草稿状态,可随时通过点击“启动实验”按钮来启动。

如需详细了解 A/B 测试,请参阅 A/B Testing 文档

11. 总结

在此 Codelab 中,您学习了如何将应用中的静态捆绑的 tflite 资源替换为 Firebase 中动态加载的 TFLite 模型。如需详细了解 TFLite 和 Firebase,请查看其他 TFLite 示例和 Firebase 入门指南。

有疑问?

报告问题