Catch up on everthing we announced at this year's Firebase Summit. Learn more

AppleプラットフォームでカスタムTensorFlowLiteモデルを使用する

あなたのアプリがカスタム使用している場合はTensorFlowライトモデルを、あなたのモデルを展開するFirebase MLを使用することができます。 Firebaseを使用してモデルをデプロイすることで、アプリの新しいバージョンをリリースすることなく、アプリの初期ダウンロードサイズを縮小し、アプリのMLモデルを更新できます。また、RemoteConfigとA / B Testingを使用すると、さまざまなモデルをさまざまなユーザーセットに動的に提供できます。

前提条件

  • MLModelDownloaderライブラリは、スウィフトのためにのみ使用可能です。
  • TensorFlow Liteは、iOS9以降を使用するデバイスでのみ実行されます。

TensorFlowLiteモデル

TensorFlow Liteモデルは、モバイルデバイスで実行するように最適化されたMLモデルです。 TensorFlow Liteモデルを取得するには:

あなたが始める前に

あなたはすでにあなたのアプリにFirebaseを追加していない場合は、の手順に従って、それを行うセットアップガイドを

Swift Package Managerを使用して、Firebaseの依存関係をインストールおよび管理します。

  1. Xcodeで、アプリの開いているプロジェクト、ナビゲートしてファイル>スウィフトパッケージ>パッケージの依存関係を追加します
  2. プロンプトが表示されたら、FirebaseAppleプラットフォームSDKリポジトリを追加します。
  3.   https://github.com/firebase/firebase-ios-sdk
      
  4. FirebaseMLライブラリを選択します。
  5. 完了すると、Xcodeは自動的に依存関係の解決とダウンロードをバックグラウンドで開始します。

次に、アプリ内セットアップを実行します。

  1. アプリで、Firebaseをインポートします。

    迅速

    import Firebase
    import TensorFlowLite
    

1.モデルをデプロイします

FirebaseコンソールまたはFirebaseAdminPythonおよびNode.jsSDKを使用して、カスタムTensorFlowモデルをデプロイします。参照展開をしてカスタムモデルを管理します

Firebaseプロジェクトにカスタムモデルを追加した後、指定した名前を使用してアプリでモデルを参照できます。いつでも、あなたは新しいTensorFlow Liteのモデルを展開することができますし、呼び出すことにより、ユーザーのデバイス上に新しいモデルをダウンロードgetModel()下記参照します)。

2.モデルをデバイスにダウンロードし、TensorFlowLiteインタープリターを初期化します

アプリでTensorFlowLiteモデルを使用するには、まずFirebase MLSDKを使用してモデルの最新バージョンをデバイスにダウンロードします。

モデルのダウンロードを開始するには、モデルのダウンローダの呼び出しgetModel()あなたは、あなたがそれをアップロードするときに、常に最新のモデルをダウンロードするかどうか、モデルを割り当てられ、あなたがダウンロードできるようにしたい条件名を指定する方法を、。

次の3つのダウンロード動作から選択できます。

ダウンロードタイプ説明
localModelデバイスからローカルモデルを取得します。以下のような使用可能なローカルモデルが存在しない場合、この振る舞うlatestModel 。モデルの更新を確認することに興味がない場合は、このダウンロードタイプを使用してください。たとえば、Remote Configを使用してモデル名を取得していて、常に新しい名前でモデルをアップロードしている(推奨)。
localModelUpdateInBackgroundデバイスからローカルモデルを取得し、バックグラウンドでモデルの更新を開始します。以下のような使用可能なローカルモデルが存在しない場合、この振る舞うlatestModel
latestModel最新モデルを入手してください。ローカルモデルが最新バージョンの場合は、ローカルモデルを返します。それ以外の場合は、最新モデルをダウンロードしてください。この動作は、最新バージョンがダウンロードされるまでブロックされます(推奨されません)。この動作は、最新バージョンが明示的に必要な場合にのみ使用してください。

モデルがダウンロードされたことを確認するまで、モデル関連の機能(UIのグレーアウトや非表示など)を無効にする必要があります。

迅速

let conditions = ModelDownloadConditions(allowsCellularAccess: false)
ModelDownloader.modelDownloader()
    .getModel(name: "your_model",
              downloadType: .localModelUpdateInBackground,
              conditions: conditions) { result in
        switch (result) {
        case .success(let customModel):
            do {
                // Download complete. Depending on your app, you could enable the ML
                // feature, or switch from the local model to the remote model, etc.

                // The CustomModel object contains the local path of the model file,
                // which you can use to instantiate a TensorFlow Lite interpreter.
                let interpreter = try Interpreter(modelPath: customModel.path)
            } catch {
                // Error. Bad model file?
            }
        case .failure(let error):
            // Download was unsuccessful. Don't enable ML features.
            print(error)
        }
}

多くのアプリは初期化コードでダウンロードタスクを開始しますが、モデルを使用する前であればいつでもダウンロードできます。

3.入力データに対して推論を実行します

モデルの入力形状と出力形状を取得します

TensorFlow Liteモデルインタープリターは入力として受け取り、出力として1つ以上の多次元配列を生成します。これらの配列は、どちらか含まれているbyteintlong 、またはfloat値を。モデルにデータを渡したり、その結果を使用したりする前に、モデルが使用する配列の数と次元(「形状」)を知っておく必要があります。

モデルを自分で作成した場合、またはモデルの入力と出力の形式が文書化されている場合は、すでにこの情報を持っている可能性があります。モデルの入力と出力の形状とデータ型がわからない場合は、TensorFlowLiteインタープリターを使用してモデルを検査できます。例えば:

Python

import tensorflow as tf

interpreter = tf.lite.Interpreter(model_path="your_model.tflite")
interpreter.allocate_tensors()

# Print input shape and type
inputs = interpreter.get_input_details()
print('{} input(s):'.format(len(inputs)))
for i in range(0, len(inputs)):
    print('{} {}'.format(inputs[i]['shape'], inputs[i]['dtype']))

# Print output shape and type
outputs = interpreter.get_output_details()
print('\n{} output(s):'.format(len(outputs)))
for i in range(0, len(outputs)):
    print('{} {}'.format(outputs[i]['shape'], outputs[i]['dtype']))

出力例:

1 input(s):
[  1 224 224   3] <class 'numpy.float32'>

1 output(s):
[1 1000] <class 'numpy.float32'>

インタプリタを実行する

モデルの入力と出力の形式を決定したら、入力データを取得し、モデルに適切な形状の入力を取得するために必要なデータに対して変換を実行します。

モデルは、画像を処理し、モデルに入力大きされている場合、例えば、 [1, 224, 224, 3]の浮動小数点値を、次の例のように、浮動小数点の範囲に画像の色値をスケーリングする必要がある場合があります:

迅速

let image: CGImage = // Your input image
guard let context = CGContext(
  data: nil,
  width: image.width, height: image.height,
  bitsPerComponent: 8, bytesPerRow: image.width * 4,
  space: CGColorSpaceCreateDeviceRGB(),
  bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue
) else {
  return false
}

context.draw(image, in: CGRect(x: 0, y: 0, width: image.width, height: image.height))
guard let imageData = context.data else { return false }

var inputData = Data()
for row in 0 ..&lt; 224 {
  for col in 0 ..&lt; 224 {
    let offset = 4 * (row * context.width + col)
    // (Ignore offset 0, the unused alpha channel)
    let red = imageData.load(fromByteOffset: offset+1, as: UInt8.self)
    let green = imageData.load(fromByteOffset: offset+2, as: UInt8.self)
    let blue = imageData.load(fromByteOffset: offset+3, as: UInt8.self)

    // Normalize channel values to [0.0, 1.0]. This requirement varies
    // by model. For example, some models might require values to be
    // normalized to the range [-1.0, 1.0] instead, and others might
    // require fixed-point values or the original bytes.
    var normalizedRed = Float32(red) / 255.0
    var normalizedGreen = Float32(green) / 255.0
    var normalizedBlue = Float32(blue) / 255.0

    // Append normalized values to Data object in RGB order.
    let elementSize = MemoryLayout.size(ofValue: normalizedRed)
    var bytes = [UInt8](repeating: 0, count: elementSize)
    memcpy(&amp;bytes, &amp;normalizedRed, elementSize)
    inputData.append(&amp;bytes, count: elementSize)
    memcpy(&amp;bytes, &amp;normalizedGreen, elementSize)
    inputData.append(&amp;bytes, count: elementSize)
    memcpy(&ammp;bytes, &amp;normalizedBlue, elementSize)
    inputData.append(&amp;bytes, count: elementSize)
  }
}

次に、あなたの入力コピーNSDataインタプリタにし、それを実行します。

迅速

try interpreter.allocateTensors()
try interpreter.copy(inputData, toInputAt: 0)
try interpreter.invoke()

あなたは、通訳者の呼び出しによって、モデルの出力に含まを得ることができますoutput(at:)方法。出力の使用方法は、使用しているモデルによって異なります。

たとえば、分類を実行している場合、次のステップとして、結果のインデックスをそれらが表すラベルにマップできます。

迅速

let output = try interpreter.output(at: 0)
let probabilities =
        UnsafeMutableBufferPointer<Float32>.allocate(capacity: 1000)
output.data.copyBytes(to: probabilities)

guard let labelPath = Bundle.main.path(forResource: "retrained_labels", ofType: "txt") else { return }
let fileContents = try? String(contentsOfFile: labelPath)
guard let labels = fileContents?.components(separatedBy: "\n") else { return }

for i in labels.indices {
    print("\(labels[i]): \(probabilities[i])")
}

付録:モデルのセキュリティ

TensorFlowLiteモデルをFirebaseMLで利用できるようにする方法に関係なく、FirebaseMLはそれらを標準のシリアル化されたprotobuf形式でローカルストレージに保存します。

理論的には、これは誰でもモデルをコピーできることを意味します。ただし、実際には、ほとんどのモデルはアプリケーション固有であり、最適化によって難読化されているため、リスクは競合他社がコードを分解して再利用する場合と同様です。それでも、アプリでカスタムモデルを使用する前に、このリスクに注意する必要があります。