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

1. 概要

目標

Firebase ML を使用すると、モデルを無線でデプロイできます。これにより、アプリサイズを小さく抑え、必要なときにのみ 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

Firebase を Podfile に追加し、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 に追加する必要があります。

pod 'FirebaseAnalytics'

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

import FirebaseAnalytics

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

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

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

デバッグ分析

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

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

-FIRDebugEnabled

アプリを再度実行し、数字を描画します。[はい] を押します。ボタンを数回押して、推論が正確だったというフィードバックを送信します。Firebase コンソールのデバッグビューを使用して、ログイベントをほぼリアルタイムで表示できるようになりました。[アナリティクス] をクリック >DebugView にアクセスすることもできます。

5276199a086721fd.png

7. Firebase Performance を使用して推論時間を追跡する

モデルをテストする際に、開発デバイスで作成されたパフォーマンス指標だけでは、ユーザーの環境でのモデルのパフォーマンスを把握するのに十分ではありません。ユーザーがどのハードウェアでアプリを実行するかを見分けるのは困難です。幸いなことに、ユーザーの分布に対するモデルのパフォーマンスをFirebase Performance を使用して、モデルのパフォーマンスをより詳しく把握します。

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

import FirebasePerformance

次に、classification メソッドでパフォーマンス トレースを開始し、推論が完了したらトレースを停止します。メソッド宣言の直下ではなく、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求められたら、数字分類器アプリを選択します。

より大きなデータセットを使用して新しいバージョンのモデルをトレーニングし、Firebase Admin SDK を使用してトレーニング ノートブックから直接プログラムでモデルをデプロイします。

サービス アカウントの秘密鍵をダウンロードする

Firebase Admin SDK を使用するには、サービス アカウントを作成する必要があります。こちらのリンクをクリックして Firebase コンソールの [サービス アカウント] パネルを開き、ボタンをクリックして Firebase Admin SDK 用の新しいサービス アカウントを作成します。プロンプトが表示されたら、[Generate New Private Key(新しい秘密鍵を生成)] ボタンをクリックします。サービス アカウント キーを使用して、Colab ノートブックからリクエストを認証します。

c3b95de1e5508516.png

これで、新しいモデルをトレーニングしてデプロイできます。

  1. この Colab ノートブックを開き、自分のドライブの下にコピーを作成します。
  2. 最初のセル「Train animprove TensorFlow Lite model」を実行します。左側の再生ボタンをクリックしますこれにより新しいモデルがトレーニングされます。これには時間がかかることがあります。
  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 で依存関係として指定してプロジェクトに追加する必要があります。

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. 2 つのモデルの A/B テストを行う

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

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

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 スタートガイドをご覧ください。

質問がある場合

問題を報告する