TensorFlow Lite 推論

推論という用語は、入力データに基づいて予測を行うためにデバイス上で TensorFlow Lite モデルを実行するプロセスを指します。TensorFlow Lite モデルで推論を行うには、インタープリタを使用して推論を行う必要があります。TensorFlow Lite インタープリタは、無駄のない高速に設計されています。インタープリタは、静的なグラフの順序付けとカスタムの(動的でない)メモリ アロケータを使用して、負荷、初期化、実行のレイテンシを最小限に抑えます。

このページでは、TensorFlow Lite インタープリタにアクセスし、C++、Java、Python を使用して推論を実行する方法と、サポートされている各プラットフォームの他のリソースへのリンクについて説明します。

重要なコンセプト

TensorFlow Lite 推論は通常、次の手順を実行します。

  1. モデルの読み込み

    .tflite モデルは、モデルの実行グラフを含むメモリに読み込む必要があります。

  2. データの変換

    モデルの未加工の入力データが、通常、モデルが期待する入力データの形式と一致しません。たとえば、モデルに合わせて画像のサイズ変更や、画像形式の変更が必要になることがあります。

  3. 推論の実行

    このステップでは、TensorFlow Lite API を使用してモデルを実行します。次のセクションで説明するように、インタープリタの構築やテンソルの割り当てなどのいくつかの手順が含まれます。

  4. 出力の解釈

    モデル推論から結果を受け取ったら、アプリケーションに役立つ意味のある方法でテンソルを解釈する必要があります。

    たとえば、確率のリストのみを返すモデルがあるとします。確率を関連カテゴリにマッピングし、エンドユーザーに提示するかどうかは任意です。

対応プラットフォーム

TensorFlow 推論 API は、AndroidiOSLinux などの最も一般的なモバイル/組み込みプラットフォーム向けに、複数のプログラミング言語で提供されています。

ほとんどの場合、API の設計は使いやすさよりもパフォーマンスを重視しています。TensorFlow Lite は小型デバイスでの高速推論向けに設計されているため、API が利便性を犠牲にして不要なコピーを回避しようとするのは当然です。同様に、TensorFlow API との整合性は明示的な目標ではなく、言語間で多少の差異が想定されます。

すべてのライブラリで TensorFlow Lite API を使用して、モデルの読み込み、入力へのフィード、推論出力の取得を行うことができます。

Android プラットフォーム

Android では、TensorFlow Lite 推論は Java または C++ API を使用して実行できます。Java API は便利であり、Android アクティビティ クラス内で直接使用できます。C++ API は柔軟性と速度に優れていますが、Java レイヤと C++ レイヤの間でデータを移動するには JNI ラッパーの作成が必要になる場合があります。

C++Java の使用方法については、以下をご覧ください。チュートリアルとサンプルコードについては、Android クイックスタートをご覧ください。

TensorFlow Lite Android ラッパーコード生成ツール

メタデータで拡張された TensorFlow Lite モデルの場合、デベロッパーは TensorFlow Lite Android ラッパーコード生成ツールを使用して、プラットフォーム固有のラッパーコードを作成できます。ラッパーコードにより、Android 上で ByteBuffer を直接操作する必要がなくなります。代わりに、デベロッパーは BitmapRect などの型付きオブジェクトを使用して TensorFlow Lite モデルを操作できます。詳細については、TensorFlow Lite Android ラッパーコード生成ツールをご覧ください。

iOS プラットフォーム

iOS では、SwiftObjective-C で記述されたネイティブ iOS ライブラリで TensorFlow Lite を利用できます。また、Objective-C コードで直接 C API を使用することもできます。

SwiftObjective-CC API の使用方法については、下記をご覧ください。チュートリアルとサンプルコードについては、iOS クイックスタートをご覧ください。

Linux プラットフォーム

Linux プラットフォーム(Raspberry Pi を含む)では、次のセクションで説明するように、C++Python で利用可能な TensorFlow Lite API を使用して推論を実行できます。

モデルの実行

TensorFlow Lite モデルを実行するには、いくつかの簡単なステップが必要です。

  1. モデルをメモリに読み込みます。
  2. 既存のモデルに基づいて Interpreter を作成します。
  3. 入力テンソル値を設定する。(事前定義されたサイズが必要ない場合は、必要に応じて入力テンソルのサイズを変更します)。
  4. 推論を呼び出す。
  5. 出力テンソル値を読み取ります。

以降のセクションでは、これらの手順を各言語で行う方法について説明します。

Java でモデルを読み込んで実行する

プラットフォーム: Android

TensorFlow Lite で推論を実行するための Java API は、主に Android で使用するために設計されているため、Android ライブラリの依存関係 org.tensorflow:tensorflow-lite として使用できます。

Java では、Interpreter クラスを使用してモデルを読み込み、モデルの推論を行います。多くの場合、必要な API はこれだけです。

Interpreter を初期化するには、.tflite ファイルを使用します。

public Interpreter(@NotNull File modelFile);

MappedByteBuffer を使用する場合:

public Interpreter(@NotNull MappedByteBuffer mappedByteBuffer);

どちらの場合も、有効な TensorFlow Lite モデルを指定する必要があります。指定しないと、API は IllegalArgumentException をスローします。MappedByteBuffer を使用して Interpreter を初期化する場合は、Interpreter の存続期間中は変更されないままにする必要があります。

モデルで推論を実行する場合におすすめの方法は、署名を使用することです。Tensorflow 2.5 以降に変換されたモデルで使用可能

try (Interpreter interpreter = new Interpreter(file_of_tensorflowlite_model)) {
  Map<String, Object> inputs = new HashMap<>();
  inputs.put("input_1", input1);
  inputs.put("input_2", input2);
  Map<String, Object> outputs = new HashMap<>();
  outputs.put("output_1", output1);
  interpreter.runSignature(inputs, outputs, "mySignature");
}

runSignature メソッドは次の 3 つの引数を取ります。

  • 入力 : シグネチャの入力名から入力オブジェクトへの入力をマッピングします。

  • 出力 : シグネチャの出力名から出力データへの出力マッピングをマッピングします。

  • シグネチャ名(省略可): シグネチャ名(モデルにシグネチャが 1 つしかない場合は空のままにしておくことができます)。

モデルにシグネチャが定義されていない場合に推論を実行するもう 1 つの方法。Interpreter.run() を呼び出すだけです。次に例を示します。

try (Interpreter interpreter = new Interpreter(file_of_a_tensorflowlite_model)) {
  interpreter.run(input, output);
}

run() メソッドは入力を 1 つだけ受け取り、出力を 1 つだけ返します。したがって、モデルに複数の入力または複数の出力がある場合は、代わりに次のコマンドを使用します。

interpreter.runForMultipleInputsOutputs(inputs, map_of_indices_to_outputs);

この場合、inputs の各エントリは入力テンソルに対応し、map_of_indices_to_outputs は出力テンソルのインデックスを対応する出力データにマッピングします。

どちらの場合も、テンソル インデックスは、モデルの作成時に TensorFlow Lite コンバータに指定した値に対応している必要があります。input のテンソルの順序は、TensorFlow Lite コンバータに指定された順序と一致する必要があることに注意してください。

Interpreter クラスには、オペレーション名を使用してモデル入出力のインデックスを取得できる便利な関数も用意されています。

public int getInputIndex(String opName);
public int getOutputIndex(String opName);

opName がモデル内の有効な演算でない場合、IllegalArgumentException がスローされます。

また、Interpreter がリソースを所有していることにも注意してください。メモリリークを回避するには、次の方法でリソースの使用後にリソースを解放する必要があります。

interpreter.close();

Java を使用したプロジェクトの例については、Android 画像分類サンプルをご覧ください。

サポートされているデータ型(Java)

TensorFlow Lite を使用するには、入力テンソルと出力テンソルのデータ型が次のいずれかのプリミティブ型である必要があります。

  • float
  • int
  • long
  • byte

String 型もサポートされていますが、プリミティブ型とは異なる方法でエンコードされます。特に、文字列テンソルの形状によってテンソル内の文字列の数と配置が決まり、各要素自体は可変長の文字列になります。この意味で、テンソルの(バイト)サイズは形状と型だけで計算できないため、文字列を単一のフラットな ByteBuffer 引数として指定することはできません。

IntegerFloat などのボックス化された型を含む、他のデータ型が使用されている場合は、IllegalArgumentException がスローされます。

入力

各入力は、サポートされているプリミティブ型の配列または多次元配列、または適切なサイズの未加工の ByteBuffer である必要があります。入力が配列または多次元配列の場合、関連する入力テンソルは推論時に配列の次元に暗黙的に変更されます。入力が ByteBuffer の場合、呼び出し元は推論を実行する前に、まず関連する入力テンソルを手動で(Interpreter.resizeInput() を介して)サイズ変更する必要があります。

ByteBuffer を使用する場合は、Interpreter で不要なコピーを回避できるため、直接バイトバッファを使用することをおすすめします。ByteBuffer がダイレクト バイトバッファの場合、その順序は ByteOrder.nativeOrder() である必要があります。モデルの推論に使用した後は、モデルの推論が終了するまで変更しないままにする必要があります。

出力

各出力は、サポートされているプリミティブ型の配列または多次元配列、または適切なサイズの ByteBuffer です。一部のモデルには動的な出力があり、出力テンソルの形状は入力によって異なる場合があります。既存の Java 推論 API でこれを処理する簡単な方法はありませんが、計画されている拡張機能によって可能になります。

Swift でモデルを読み込んで実行する

プラットフォーム: iOS

Swift API は、Cocoapods の TensorFlowLiteSwift Pod で使用できます。

まず、TensorFlowLite モジュールをインポートする必要があります。

import TensorFlowLite
// Getting model path
guard
  let modelPath = Bundle.main.path(forResource: "model", ofType: "tflite")
else {
  // Error handling...
}

do {
  // Initialize an interpreter with the model.
  let interpreter = try Interpreter(modelPath: modelPath)

  // Allocate memory for the model's input `Tensor`s.
  try interpreter.allocateTensors()

  let inputData: Data  // Should be initialized

  // input data preparation...

  // Copy the input data to the input `Tensor`.
  try self.interpreter.copy(inputData, toInputAt: 0)

  // Run inference by invoking the `Interpreter`.
  try self.interpreter.invoke()

  // Get the output `Tensor`
  let outputTensor = try self.interpreter.output(at: 0)

  // Copy output to `Data` to process the inference results.
  let outputSize = outputTensor.shape.dimensions.reduce(1, {x, y in x * y})
  let outputData =
        UnsafeMutableBufferPointer<Float32>.allocate(capacity: outputSize)
  outputTensor.data.copyBytes(to: outputData)

  if (error != nil) { /* Error handling... */ }
} catch error {
  // Error handling...
}

Objective-C でモデルを読み込んで実行する

プラットフォーム: iOS

Objective-C API は、Cocoapods の TensorFlowLiteObjC Pod で利用できます。

まず、TensorFlowLite モジュールをインポートする必要があります。

@import TensorFlowLite;
NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
                                                      ofType:@"tflite"];
NSError *error;

// Initialize an interpreter with the model.
TFLInterpreter *interpreter = [[TFLInterpreter alloc] initWithModelPath:modelPath
                                                                  error:&error];
if (error != nil) { /* Error handling... */ }

// Allocate memory for the model's input `TFLTensor`s.
[interpreter allocateTensorsWithError:&error];
if (error != nil) { /* Error handling... */ }

NSMutableData *inputData;  // Should be initialized
// input data preparation...

// Get the input `TFLTensor`
TFLTensor *inputTensor = [interpreter inputTensorAtIndex:0 error:&error];
if (error != nil) { /* Error handling... */ }

// Copy the input data to the input `TFLTensor`.
[inputTensor copyData:inputData error:&error];
if (error != nil) { /* Error handling... */ }

// Run inference by invoking the `TFLInterpreter`.
[interpreter invokeWithError:&error];
if (error != nil) { /* Error handling... */ }

// Get the output `TFLTensor`
TFLTensor *outputTensor = [interpreter outputTensorAtIndex:0 error:&error];
if (error != nil) { /* Error handling... */ }

// Copy output to `NSData` to process the inference results.
NSData *outputData = [outputTensor dataWithError:&error];
if (error != nil) { /* Error handling... */ }

Objective-C コードでの C API の使用

現在、Objective-C API はデリゲートをサポートしていません。Objective-C コードでデリゲートを使用するには、基盤となる C API を直接呼び出す必要があります。

#include "tensorflow/lite/c/c_api.h"
TfLiteModel* model = TfLiteModelCreateFromFile([modelPath UTF8String]);
TfLiteInterpreterOptions* options = TfLiteInterpreterOptionsCreate();

// Create the interpreter.
TfLiteInterpreter* interpreter = TfLiteInterpreterCreate(model, options);

// Allocate tensors and populate the input tensor data.
TfLiteInterpreterAllocateTensors(interpreter);
TfLiteTensor* input_tensor =
    TfLiteInterpreterGetInputTensor(interpreter, 0);
TfLiteTensorCopyFromBuffer(input_tensor, input.data(),
                           input.size() * sizeof(float));

// Execute inference.
TfLiteInterpreterInvoke(interpreter);

// Extract the output tensor data.
const TfLiteTensor* output_tensor =
    TfLiteInterpreterGetOutputTensor(interpreter, 0);
TfLiteTensorCopyToBuffer(output_tensor, output.data(),
                         output.size() * sizeof(float));

// Dispose of the model and interpreter objects.
TfLiteInterpreterDelete(interpreter);
TfLiteInterpreterOptionsDelete(options);
TfLiteModelDelete(model);

C++ でモデルを読み込んで実行する

プラットフォーム: Android、iOS、Linux

C++ では、モデルは FlatBufferModel クラスに格納されます。TensorFlow Lite モデルをカプセル化しており、モデルが保存されている場所に応じて、いくつかの方法でビルドできます。

class FlatBufferModel {
  // Build a model based on a file. Return a nullptr in case of failure.
  static std::unique_ptr<FlatBufferModel> BuildFromFile(
      const char* filename,
      ErrorReporter* error_reporter);

  // Build a model based on a pre-loaded flatbuffer. The caller retains
  // ownership of the buffer and should keep it alive until the returned object
  // is destroyed. Return a nullptr in case of failure.
  static std::unique_ptr<FlatBufferModel> BuildFromBuffer(
      const char* buffer,
      size_t buffer_size,
      ErrorReporter* error_reporter);
};

モデルを FlatBufferModel オブジェクトとして取得できたので、Interpreter を使用して実行できます。1 つの FlatBufferModel を複数の Interpreter で同時に使用できます。

Interpreter API の重要な部分を以下のコード スニペットに示します。次の点に注意してください。

  • テンソルは、文字列の比較(および文字列ライブラリへの固定依存関係)を避けるために、整数で表されます。
  • 同時実行スレッドからインタープリタにアクセスすることはできません。
  • 入力テンソルと出力テンソルのメモリ割り当ては、テンソルのサイズ変更の直後に AllocateTensors() を呼び出してトリガーする必要があります。

C++ で TensorFlow Lite を使用する最も簡単な方法は次のとおりです。

// Load the model
std::unique_ptr<tflite::FlatBufferModel> model =
    tflite::FlatBufferModel::BuildFromFile(filename);

// Build the interpreter
tflite::ops::builtin::BuiltinOpResolver resolver;
std::unique_ptr<tflite::Interpreter> interpreter;
tflite::InterpreterBuilder(*model, resolver)(&interpreter);

// Resize input tensors, if desired.
interpreter->AllocateTensors();

float* input = interpreter->typed_input_tensor<float>(0);
// Fill `input`.

interpreter->Invoke();

float* output = interpreter->typed_output_tensor<float>(0);

他のサンプルコードについては、minimal.cclabel_image.cc をご覧ください。

Python でモデルを読み込んで実行する

プラットフォーム: Linux

推論を実行するための Python API では、ほとんどの場合、モデルを読み込んで推論を実行するために必要なのは tf.lite.Interpreter だけです。

次の例は、Python インタープリタを使用して .tflite ファイルを読み込み、ランダムな入力データを使用して推論を実行する方法を示しています。

この例は、SignatureDef が定義された SavedModel から変換する場合に推奨されます。TensorFlow 2.5 以降で利用可能

class TestModel(tf.Module):
  def __init__(self):
    super(TestModel, self).__init__()

  @tf.function(input_signature=[tf.TensorSpec(shape=[1, 10], dtype=tf.float32)])
  def add(self, x):
    '''
    Simple method that accepts single input 'x' and returns 'x' + 4.
    '''
    # Name the output 'result' for convenience.
    return {'result' : x + 4}


SAVED_MODEL_PATH = 'content/saved_models/test_variable'
TFLITE_FILE_PATH = 'content/test_variable.tflite'

# Save the model
module = TestModel()
# You can omit the signatures argument and a default signature name will be
# created with name 'serving_default'.
tf.saved_model.save(
    module, SAVED_MODEL_PATH,
    signatures={'my_signature':module.add.get_concrete_function()})

# Convert the model using TFLiteConverter
converter = tf.lite.TFLiteConverter.from_saved_model(SAVED_MODEL_PATH)
tflite_model = converter.convert()
with open(TFLITE_FILE_PATH, 'wb') as f:
  f.write(tflite_model)

# Load the TFLite model in TFLite Interpreter
interpreter = tf.lite.Interpreter(TFLITE_FILE_PATH)
# There is only 1 signature defined in the model,
# so it will return it by default.
# If there are multiple signatures then we can pass the name.
my_signature = interpreter.get_signature_runner()

# my_signature is callable with input as arguments.
output = my_signature(x=tf.constant([1.0], shape=(1,10), dtype=tf.float32))
# 'output' is dictionary with all outputs from the inference.
# In this case we have single output 'result'.
print(output['result'])

もう一つの例は、モデルに SignatureDefs が定義されていない場合です。

import numpy as np
import tensorflow as tf

# Load the TFLite model and allocate tensors.
interpreter = tf.lite.Interpreter(model_path="converted_model.tflite")
interpreter.allocate_tensors()

# Get input and output tensors.
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()

# Test the model on random input data.
input_shape = input_details[0]['shape']
input_data = np.array(np.random.random_sample(input_shape), dtype=np.float32)
interpreter.set_tensor(input_details[0]['index'], input_data)

interpreter.invoke()

# The function `get_tensor()` returns a copy of the tensor data.
# Use `tensor()` in order to get a pointer to the tensor.
output_data = interpreter.get_tensor(output_details[0]['index'])
print(output_data)

事前に変換された .tflite ファイルとしてモデルを読み込む代わりに、コードを TensorFlow Lite Converter Python API と組み合わせて、Keras モデルを TensorFlow Lite 形式に変換してから推論を実行できます。

import numpy as np
import tensorflow as tf

img = tf.keras.Input(shape=(64, 64, 3), name="img")
const = tf.constant([1., 2., 3.]) + tf.constant([1., 4., 4.])
val = img + const
out = tf.identity(val, name="out")

# Convert to TF Lite format
converter = tf.lite.TFLiteConverter.from_keras_model(tf.keras.models.Model(inputs=[img], outputs=[out]))
tflite_model = converter.convert()

# Load the TFLite model and allocate tensors.
interpreter = tf.lite.Interpreter(model_content=tflite_model)
interpreter.allocate_tensors()

# Continue to get tensors and so forth, as shown above...

その他の Python サンプルコードについては、label_image.py をご覧ください。

動的シェイプモデルを使用して推論を実行する

動的な入力シェイプを持つモデルを実行する場合は、推論を実行する前に入力シェイプのサイズを変更します。それ以外の場合、TensorFlow モデルの None シェイプは、TFLite モデルでは 1 のプレースホルダに置き換えられます。

次の例は、別の言語で推論を実行する前に入力シェイプのサイズを変更する方法を示しています。すべての例では、入力シェイプが [1/None, 10] として定義されており、[3, 10] にサイズ変更する必要があることを前提としています。

C++ の例

// Resize input tensors before allocate tensors
interpreter->ResizeInputTensor(/*tensor_index=*/0, std::vector<int>{3,10});
interpreter->AllocateTensors();

Python の例:

# Load the TFLite model in TFLite Interpreter
interpreter = tf.lite.Interpreter(model_path=TFLITE_FILE_PATH)

# Resize input shape for dynamic shape model and allocate tensor
interpreter.resize_tensor_input(interpreter.get_input_details()[0]['index'], [3, 10])
interpreter.allocate_tensors()

# Get input and output tensors.
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()

サポートされているオペレーション

TensorFlow Lite は TensorFlow オペレーションのサブセットをサポートしていますが、いくつかの制限があります。オペレーションと制限事項の一覧については、TF Lite Ops ページをご覧ください。