アプリでカスタムTensorFlowLiteモデルを使用している場合は、FirebaseMLを使用してモデルをデプロイできます。 Firebaseを使用してモデルをデプロイすることで、アプリの新しいバージョンをリリースすることなく、アプリの初期ダウンロードサイズを縮小し、アプリのMLモデルを更新できます。また、RemoteConfigとA/ B Testingを使用すると、さまざまなモデルをさまざまなユーザーセットに動的に提供できます。
前提条件
MLModelDownloader
ライブラリは、Swiftでのみ使用できます。- TensorFlow Liteは、iOS9以降を使用するデバイスでのみ実行されます。
TensorFlowLiteモデル
TensorFlow Liteモデルは、モバイルデバイスで実行するように最適化されたMLモデルです。 TensorFlow Liteモデルを取得するには:
- 公式のTensorFlowLiteモデルの1つなど、事前に構築されたモデルを使用します。
- TensorFlowモデル、Kerasモデル、または具象関数をTensorFlowLiteに変換します。
あなたが始める前に
TensorFlowLiteは現在SwiftPackageManagerでのインストールをサポートしていないため、FirebaseでTensorFlowLiteを使用するには、CocoaPodsを使用する必要があります。 MLModelDownloaderのインストール方法については、 MLModelDownloader
インストールガイドを参照してください。
インストールしたら、FirebaseとTensorFlowLiteをインポートして使用します。
迅速
import FirebaseMLModelDownloader
import TensorFlowLite
1.モデルをデプロイします
FirebaseコンソールまたはFirebaseAdminPythonおよびNode.jsSDKを使用して、カスタムTensorFlowモデルをデプロイします。カスタムモデルの展開と管理を参照してください。
Firebaseプロジェクトにカスタムモデルを追加した後、指定した名前を使用してアプリでモデルを参照できます。 getModel()
を呼び出すことで、いつでも新しいTensorFlow Liteモデルをデプロイし、新しいモデルをユーザーのデバイスにダウンロードできます(以下を参照)。
2.モデルをデバイスにダウンロードし、TensorFlowLiteインタープリターを初期化します
アプリでTensorFlowLiteモデルを使用するには、まずFirebaseMLSDKを使用してモデルの最新バージョンをデバイスにダウンロードします。モデルのダウンロードを開始するには、モデルダウンローダーの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つ以上の多次元配列を生成します。これらの配列には、 byte
、 int
、 long
、または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 ..< 224 {
for col in 0 ..< 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(&bytes, &normalizedRed, elementSize)
inputData.append(&bytes, count: elementSize)
memcpy(&bytes, &normalizedGreen, elementSize)
inputData.append(&bytes, count: elementSize)
memcpy(&ammp;bytes, &normalizedBlue, elementSize)
inputData.append(&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形式でローカルストレージに保存します。
理論的には、これは誰でもモデルをコピーできることを意味します。ただし、実際には、ほとんどのモデルはアプリケーション固有であり、最適化によって難読化されているため、リスクは競合他社がコードを分解して再利用する場合と同様です。それでも、アプリでカスタムモデルを使用する前に、このリスクに注意する必要があります。