TFLite を利用した iOS アプリに Firebase を追加する

1. 概要

目標

Firebase ML を使用すると、モデルを無線(OTA)でデプロイできます。これにより、アプリのサイズを小さく保ち、必要なときだけ ML モデルをダウンロードしたり、複数のモデルをテストしたり、アプリ全体を再公開することなく ML モデルを更新したりできます。

この Codelab では、静的な TFLite モデルを使用する iOS アプリを、Firebase から動的に提供されるモデルを使用するアプリに変換します。ここでは以下について学びます。

  1. TFLite モデルを Firebase ML にデプロイしてアプリからアクセス
  2. アナリティクスでモデル関連の指標を記録する
  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

Podfile に Firebase を追加し、pod install を実行します。

pod 'FirebaseMLModelDownloader', '9.3.0-beta'

AppDelegatedidFinishLaunchingWithOptions メソッドで、ファイルの先頭で Firebase をインポートします。

import FirebaseCore

Firebase を構成する呼び出しを追加します。

FirebaseApp.configure()

プロジェクトを再度実行して、アプリが正しく構成され、起動時にクラッシュしないことを確認します。

4. Firebase ML にモデルをデプロイする

Firebase ML へのモデルのデプロイは、主に次の 2 つの理由で有用です。

  1. アプリのインストール サイズを小さく保ち、必要な場合にのみモデルをダウンロードできます。
  2. モデルは定期的に更新でき、アプリ全体とは異なるリリース サイクルで更新できる

アプリの静的モデルを Firebase から動的にダウンロードされたモデルに置き換える前に、Firebase ML にデプロイする必要があります。このモデルは、コンソールから、または Firebase Admin SDK を使ってプログラムによってデプロイできます。このステップでは、コンソールからデプロイします。

シンプルにするために、アプリにすでに組み込まれている TensorFlow Lite モデルを使用します。まず、Firebase を開き、左側のナビゲーション パネルで [ML] をクリックします。[カスタム] に移動し、[モデルを追加] ボタンをクリックします。

プロンプトが表示されたら、モデルにわかりやすい名前(mnist_v1 など)を付けて、Codelab プロジェクト ディレクトリからファイルをアップロードします。

3c3c50e6ef12b3b.png

5. Firebase ML からモデルをダウンロードする

TFLite モデルは比較的大きくなる可能性があるため、Firebase からリモートモデルをアプリにダウンロードするタイミングの選択は難しい場合があります。アプリの起動時にすぐにモデルを読み込むのは避けるのが理想的です。モデルが 1 つの機能にのみ使用され、ユーザーがその機能を使用することがないと、理由もなく大量のデータがダウンロードされることになるためです。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. ユーザー フィードバックとコンバージョンを追跡してモデルの精度を測定する

モデルの予測に対するユーザー フィードバックを追跡することで、モデルの精度を測定します。ユーザーが [はい] をクリックすると、予測が正確であったことを示します。

アナリティクスのイベントをログに記録して、モデルの精度を追跡できます。まず、プロジェクトで使用する前に Podfile に Analytics を追加する必要があります。

pod 'FirebaseAnalytics'

次に、ViewController.swift で、ファイルの先頭に Firebase をインポートします。

import FirebaseAnalytics

次のコード行を correctButtonPressed メソッドに追加します。

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

アプリを再度実行して数字を描画します。[はい] ボタンを数回押して、推論が正確であることに関するフィードバックを送信します。

分析をデバッグする

通常、アプリによってログに記録されたイベントは、約 1 時間にわたってバッチ処理され、まとめてアップロードされます。これにより、エンドユーザーのデバイスのバッテリーを節約し、ネットワーク データ使用量を削減できます。ただし、アナリティクスの実装を検証する(および DebugView レポートにアナリティクスを表示する)目的では、開発デバイスでデバッグモードを有効にすると、最小限の遅延でイベントをアップロードできます。

開発デバイスでアナリティクスのデバッグモードを有効にするには、Xcode で次のコマンドライン引数を指定します。

-FIRDebugEnabled

アプリを再度実行して数字を描画します。[はい] ボタンを数回押して、推論が正確であることに関するフィードバックを送信します。Firebase コンソールのデバッグビューから、ログイベントをほぼリアルタイムで確認できるようになりました。左側のナビゲーション バーで [Analytics] > [DebugView] をクリックします。

5276199a086721fd.png

7. Firebase Performance で推論時間を追跡する

モデルをテストする際、開発デバイスで作成したパフォーマンス指標では、ユーザーがどのハードウェアでアプリを実行するのか判断することが難しいため、ユーザーの手でモデルがどのように動作するかを把握するには不十分です。幸い、Firebase Performance を使用すると、ユーザーのデバイスでのモデルのパフォーマンスを測定し、モデルのパフォーマンスをより正確に把握できます。

推論の実行にかかる時間を測定するには、まず Firebase を DigitClassifier.swift にインポートします。

import FirebasePerformance

次に、classify メソッドでパフォーマンス トレースを開始し、推論が完了したらトレースを停止します。次のコード行は、メソッド宣言の直下ではなく DispatchQueue.global.async クロージャ内に追加してください。

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

ご興味がある場合は、こちらの手順に沿ってデバッグ ロギングを有効にして、パフォーマンス トレースがログに記録されていることを確認できます。しばらくすると、Firebase コンソールにもパフォーマンス トレースが表示されるようになります。

8. Firebase ML に 2 つ目のモデルをデプロイする

より優れたモデル アーキテクチャを持つモデルや、大規模なデータセットや更新されたデータセットでトレーニングされたモデルなど、モデルの新しいバージョンを思いついたら、現在のモデルを新しいバージョンに置き換えたくなるかもしれません。ただし、テストで良好なパフォーマンスが得られるモデルが、必ずしも本番環境で同じように機能するわけではありません。そこで、本番環境で A/B テストを実施して、元のモデルと新しいモデルを比較しましょう。

Firebase Model Management API を有効にする

このステップでは、Firebase Model Management API を有効にし、Python コードを使用して TensorFlow Lite モデルの新しいバージョンをデプロイします。

ML モデルを保存するバケットを作成する

Firebase コンソールで [Storage] に移動し、[始める] をクリックします。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.png

これで、新しいモデルをトレーニングしてデプロイできるようになりました。

  1. この Colab ノートブックを開き、自分のドライブ内にコピーを作成します。
  2. 最初のセルの左にある再生ボタンをクリックして、「トレーニングされた TensorFlow Lite モデルのトレーニング」を実行します。新しいモデルをトレーニングするため、しばらく時間がかかることがあります。
  3. 2 番目のセルを実行すると、ファイルのアップロードを求めるプロンプトが表示されます。サービス アカウントの作成時に Firebase コンソールからダウンロードした JSON ファイルをアップロードします。

71e847c6a85423b3.png

  1. 最後の 2 つのセルを実行します。

Colab ノートブックを実行すると、Firebase コンソールに 2 つ目のモデルが表示されます。2 番目のモデルの名前が mnist_v2 であることを確認します。

c316683bb4d75d57.png

9. Remote Config でモデルを選択する

2 つの異なるモデルを作成できたので、実行時にダウンロードするモデルを選択するためのパラメータを追加します。クライアントが受け取るパラメータの値によって、クライアントがダウンロードするモデルが決まります。まず、Firebase コンソールを開き、左側のナビゲーション メニューで [Remote Config] ボタンをクリックします。[パラメータを追加] ボタンをクリックします。

新しいパラメータに model_name という名前を付け、デフォルト値の mnist_v1 を設定します。[変更を公開] をクリックして更新を適用します。Remote Config パラメータにモデルの名前を指定することで、テストするモデルごとに新しいパラメータを追加することなく、複数のモデルをテストできます。

パラメータを追加すると、コンソールに表示されます。

699b3fd32acce887.png

今回のコードでは、リモートモデルを読み込むときにチェックを追加する必要があります。Remote Config からパラメータを受信すると、対応する名前でリモートモデルがフェッチされます。そうでない場合は、mnist_v1 の読み込みが試行されます。Remote Config を使用する前に、Podfile で依存関係として指定して、Remote Config をプロジェクトに追加する必要があります。

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.swiftdownloadModel 呼び出しを、先ほど実装した新しいメソッドに置き換えます。

// 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. 2 つのモデルの A/B テストを実施する

最後に、Firebase に組み込まれた A/B Testing 動作を使用して、2 つのモデルのどちらのパフォーマンスが優れているかを確認できます。Firebase コンソールで [アナリティクス] -> [イベント] に移動します。correct_inference イベントが表示されている場合は、それを「コンバージョン イベント」としてマークします。表示されていない場合は、[アナリティクス] -> [コンバージョン イベント] に移動して、[新しいコンバージョン イベントを作成] をクリックし、correct_inference. をドロップします。

Firebase コンソールの [Remote Config] に移動し、先ほど追加した「model_name」パラメータのその他のオプション メニューから [A/B テスト] ボタンを選択します。

fad5ea36969d2aeb.png

表示されるメニューで、デフォルト名をそのまま使用します。

d7c006669ace6e40.png

プルダウンでアプリを選択し、ターゲティング条件をアクティブ ユーザーの 50% に変更します。

6246dd7c660b53fb.png

以前に correct_inference イベントをコンバージョンとして設定できていた場合は、このイベントをメインの指標としてトラッキングします。アナリティクスにイベントが表示されるまで待ちたくない場合は、correct_inference を手動で追加します。

1ac9c94fb3159271.png

最後に、[パターン] 画面で、コントロール グループのパターンには mnist_v1 を、パターン A のグループは mnist_v2 を使用するように設定します。

e4510434f8da31b6.png

右下にある [確認] ボタンをクリックします。

これで、2 つのモデルの A/B テストを作成できました。A/B テストは現在、下書き状態になっています。[テストを開始] ボタンをクリックするといつでも開始できます。

A/B Testing について詳しくは、A/B Testing のドキュメントをご覧ください。

11. まとめ

この Codelab では、アプリ内の静的にバンドルされた tflite アセットを、Firebase から動的に読み込まれる TFLite モデルに置き換える方法を学習しました。TFLite と Firebase の詳細については、他の TFLite サンプルと Firebase スタートガイドをご覧ください。

質問がある場合

問題を報告する