Buka konsol

Menggunakan model TensorFlow Lite untuk melakukan inferensi dengan ML Kit di iOS

Anda dapat menggunakan ML Kit untuk melakukan inferensi di perangkat dengan model TensorFlow Lite.

ML Kit dapat menggunakan model TensorFlow Lite hanya pada perangkat yang menjalankan iOS 9 dan yang lebih baru.

Lihat sampel quickstart ML Kit di GitHub untuk contoh penggunaan API ini.

Sebelum memulai

  1. Jika Anda belum menambahkan Firebase ke aplikasi, lakukan dengan mengikuti langkah-langkahnya di panduan memulai.
  2. Sertakan library ML Kit di Podfile Anda:
    pod 'Firebase/Core'
    pod 'Firebase/MLModelInterpreter'
    
    Setelah Anda menginstal atau mengupdate Pod project, pastikan untuk membuka project Xcode menggunakan .xcworkspace-nya.
  3. Di aplikasi Anda, impor Firebase:

    Swift

    import Firebase

    Objective-C

    @import Firebase;
  4. Serta, impor modul FirebaseMLCommon:

    Swift

    import FirebaseMLCommon

    Objective-C

    @import FirebaseMLCommon;
  5. Konversikan model TensorFlow yang ingin Anda gunakan ke dalam format TensorFlow Lite. Baca bagian TOCO: Pengonversi Pengoptimalan TensorFlow Lite.

Menghosting atau membuat paket model Anda

Sebelum dapat menggunakan model TensorFlow Lite untuk inferensi di aplikasi, Anda harus membuat model tersedia untuk ML Kit. ML Kit dapat menggunakan model TensorFlow Lite yang dihosting dari jarak jauh menggunakan Firebase, dipaketkan dengan biner aplikasi, atau keduanya.

Dengan menghosting model di Firebase, Anda dapat mengupdate model tanpa merilis versi baru aplikasi, dan Anda dapat menggunakan Remote Config serta Pengujian A/B untuk melayani berbagai model secara dinamis ke kumpulan pengguna yang berbeda.

Jika Anda memilih untuk hanya menyediakan model dengan menghostingnya dengan Firebase, dan tidak memaketkannya dengan aplikasi Anda, Anda dapat mengurangi ukuran download awal aplikasi Anda. Namun, perlu diingat bahwa jika model tidak dipaketkan dengan aplikasi Anda, fungsi yang terkait dengan model tidak akan tersedia hingga aplikasi Anda mendownload model untuk pertama kalinya.

Dengan memaketkan model dengan aplikasi, Anda dapat memastikan bahwa fitur ML pada aplikasi Anda masih berfungsi jika model yang dihosting Firebase tidak tersedia.

Menghosting model di Firebase

Untuk menghosting model TensorFlow Lite di Firebase:

  1. Di bagian ML Kit pada Firebase console, klik tab Kustom.
  2. Klik Tambahkan model kustom (atau Tambahkan model lain).
  3. Tentukan nama yang akan digunakan untuk mengidentifikasi model Anda di project Firebase, lalu upload file model TensorFlow Lite (biasanya diakhiri dengan .tflite atau .lite ).

Setelah menambahkan model kustom ke project Firebase, Anda dapat mereferensikan model tersebut di aplikasi menggunakan nama yang Anda tentukan. Anda dapat mengupload file model TensorFlow Lite baru kapan saja untuk sebuah model, dan aplikasi Anda akan mendownload model baru itu dan mulai menggunakannya saat berikutnya aplikasi dimulai ulang. Anda dapat menentukan kondisi perangkat yang diperlukan aplikasi untuk mencoba mengupdate model (lihat di bawah ini).

Paketkan model dengan aplikasi

Untuk memaketkan model TensorFlow Lite dengan aplikasi Anda, tambahkan file model (biasanya diakhiri dengan .tflite atau .lite) ke project Xcode Anda. Berhati-hatilah saat memilih opsi Salin resource paket ketika Anda melakukannya. File model tersebut akan disertakan dalam paket aplikasi dan tersedia untuk ML Kit.

Memuat model

Untuk menggunakan model TensorFlow Lite Anda di aplikasi, pertama-tama konfigurasikan ML Kit dengan lokasi tempat model Anda tersedia: menggunakan Firebase dari jarak jauh, di penyimpanan lokal, atau keduanya. Jika Anda menetapkan model lokal dan jarak jauh, ML Kit akan menggunakan model jarak jauh jika tersedia, dan kembali ke model yang disimpan secara lokal jika model jarak jauh tidak tersedia.

Mengonfigurasi model yang dihosting oleh Firebase

Jika Anda menghosting model dengan Firebase, daftarkan objek RemoteModel yang menentukan nama yang ditetapkan pada model ketika menguploadnya, serta kondisi saat ML Kit harus mendownload model di awal dan saat update tersedia.

Swift

let initialConditions = ModelDownloadConditions(
  allowsCellularAccess: true,
  allowsBackgroundDownloading: true
)
let updateConditions = ModelDownloadConditions(
  allowsCellularAccess: false,
  allowsBackgroundDownloading: true
)
let remoteModel = RemoteModel(
  name: "my_remote_model",
  allowsModelUpdates: true,
  initialConditions: initialConditions,
  updateConditions: updateConditions
)
let isRegistered = ModelManager.modelManager().register(remoteModel)

Objective-C

FIRModelDownloadConditions *initialConditions =
    [[FIRModelDownloadConditions alloc] initWithAllowsCellularAccess:YES
                                         allowsBackgroundDownloading:YES];
FIRModelDownloadConditions *updateConditions =
    [[FIRModelDownloadConditions alloc] initWithAllowsCellularAccess:NO
                                         allowsBackgroundDownloading:YES];
FIRRemoteModel *remoteModel = [[FIRRemoteModel alloc] initWithName:@"my_remote_model"
                                                allowsModelUpdates:YES
                                                 initialConditions:conditions
                                                  updateConditions:conditions];
  BOOL isRegistered =
      [[FIRModelManager modelManager] registerRemoteModel:remoteModel];

Mengonfigurasi model lokal

Jika Anda memaketkan model dengan aplikasi, daftarkan objek LocalModel, yang menentukan nama file model TensorFlow Lite dan menetapkan nama pada model yang akan digunakan pada langkah berikutnya.

Swift

guard let modelPath = Bundle.main.path(forResource: "my_model", ofType: "tflite")
    else {
        // Invalid model path
        return
}
let localModel = LocalModel(name: "my_local_model", path: modelPath)
let registrationSuccessful = ModelManager.modelManager().register(localModel)

Objective-C

NSString *modelPath = [NSBundle.mainBundle pathForResource:@"my_model"
                                                    ofType:@"tflite"];
FIRLocalModel *localModel = [[FIRLocalModel alloc] initWithName:@"my_local_model"
                                                           path:modelPath];
BOOL registrationSuccess =
    [[FIRModelManager modelManager] registerLocalModel:localModel];

Membuat penafsir dari model Anda

Setelah mengonfigurasi lokasi model Anda, buat objek ModelOptions dengan model jarak jauh, model lokal, atau keduanya, dan gunakan untuk mendapatkan instance ModelInterpreter. Jika Anda hanya memiliki satu model, tetapkan nil untuk jenis model yang tidak digunakan.

Swift

let options = ModelOptions(remoteModelName: "my_remote_model",
                           localModelName: "my_local_model")
let interpreter = ModelInterpreter.modelInterpreter(options: options)

Objective-C

FIRModelOptions *options = [[FIRModelOptions alloc] initWithRemoteModelName:@"my_remote_model"
                                                             localModelName:@"my_local_model"];
FIRModelInterpreter *interpreter = [FIRModelInterpreter modelInterpreterWithOptions:options];

Menentukan masukan dan keluaran model

Selanjutnya, konfigurasikan format input dan output penafsir model.

Model TensorFlow Lite mengambil satu atau beberapa array multidimensi sebagai input dan menghasilkannya sebagai output. Array ini berisi nilai byte, int, long, atau float. Anda harus mengonfigurasi ML Kit dengan jumlah dan dimensi ("bentuk") array yang digunakan oleh model Anda.

Jika Anda tidak tahu bentuk dan tipe data dari input dan output model, Anda dapat menggunakan penafsir TensorFlow Lite Python untuk memeriksa model. Misalnya:

import tensorflow as tf

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

# Print input shape and type
print(interpreter.get_input_details()[0]['shape'])  # Example: [1 224 224 3]
print(interpreter.get_input_details()[0]['dtype'])  # Example: <class 'numpy.float32'>

# Print output shape and type
print(interpreter.get_output_details()[0]['shape'])  # Example: [1 1000]
print(interpreter.get_output_details()[0]['dtype'])  # Example: <class 'numpy.float32'>

Setelah Anda menentukan format input dan output model, konfigurasikan penafsir model aplikasi dengan membuat objek ModelInputOutputOptions.

Sebagai contoh, model klasifikasi gambar floating-point mungkin akan mengambil array Nx224x224x3 berisi nilai Float sebagai input, yang mewakili sekelompok gambar N 224x224 tiga saluran (RGB), dan menghasilkan output berupa daftar 1.000 nilai Float. Setiap nilai ini mewakili probabilitas bahwa gambar tersebut adalah anggota salah satu dari 1.000 kategori yang diprediksi oleh model.

Untuk model seperti itu, Anda akan mengonfigurasi masukan dan keluaran penafsir model, seperti yang ditunjukkan di bawah ini:

Swift

let ioOptions = ModelInputOutputOptions()
do {
    try ioOptions.setInputFormat(index: 0, type: .float32, dimensions: [1, 224, 224, 3])
    try ioOptions.setOutputFormat(index: 0, type: .float32, dimensions: [1, 1000])
} catch let error as NSError {
    print("Failed to set input or output format with error: \(error.localizedDescription)")
}

Objective-C

FIRModelInputOutputOptions *ioOptions = [[FIRModelInputOutputOptions alloc] init];
NSError *error;
[ioOptions setInputFormatForIndex:0
                             type:FIRModelElementTypeFloat32
                       dimensions:@[@1, @224, @224, @3]
                            error:&error];
if (error != nil) { return; }
[ioOptions setOutputFormatForIndex:0
                              type:FIRModelElementTypeFloat32
                        dimensions:@[@1, @1000]
                             error:&error];
if (error != nil) { return; }

Melakukan inferensi pada data masukan

Terakhir, untuk melakukan inferensi menggunakan model, dapatkan data masukan Anda, jalankan transformasi pada data yang mungkin diperlukan untuk model Anda, dan buat objek Data yang berisi data.

Misalnya, jika model Anda memproses gambar, dan model Anda memiliki dimensi masukan dari nilai-nilai floating-point [BATCH_SIZE, 224, 224, 3], Anda mungkin harus menyesuaikan skala nilai warna gambar ke rentang floating-point seperti pada contoh berikut ini :

Swift

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 }

let inputs = ModelInputs()
var inputData = Data()
do {
  for row in 0 ..< 224 {
    for col in 0 ..< 224 {
      let offset = 4 * (col * context.width + row)
      // (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)
    }
  }
  try inputs.addInput(inputData)
} catch let error {
  print("Failed to add input: \(error)")
}

Objective-C

CGImageRef image = // Your input image
long imageWidth = CGImageGetWidth(image);
long imageHeight = CGImageGetHeight(image);
CGContextRef context = CGBitmapContextCreate(nil,
                                             imageWidth, imageHeight,
                                             8,
                                             imageWidth * 4,
                                             CGColorSpaceCreateDeviceRGB(),
                                             kCGImageAlphaNoneSkipFirst);
CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight), image);
UInt8 *imageData = CGBitmapContextGetData(context);

FIRModelInputs *inputs = [[FIRModelInputs alloc] init];
NSMutableData *inputData = [[NSMutableData alloc] initWithCapacity:0];

for (int row = 0; row < 224; row++) {
  for (int col = 0; col < 224; col++) {
    long offset = 4 * (col * imageWidth + row);
    // 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.
    // (Ignore offset 0, the unused alpha channel)
    Float32 red = imageData[offset+1] / 255.0f;
    Float32 green = imageData[offset+2] / 255.0f;
    Float32 blue = imageData[offset+3] / 255.0f;

    [inputData appendBytes:&red length:sizeof(red)];
    [inputData appendBytes:&green length:sizeof(green)];
    [inputData appendBytes:&blue length:sizeof(blue)];
  }
}

[inputs addInput:inputData error:&error];
if (error != nil) { return nil; }

Setelah Anda menyiapkan input model, teruskan input tersebut dan opsi input/output ke metode run(inputs:options:) penafsir model.

Swift

interpreter.run(inputs: inputs, options: ioOptions) { outputs, error in
    guard error == nil, let outputs = outputs else { return }
    // Process outputs
    // ...
}

Objective-C

[interpreter runWithInputs:inputs
                   options:ioOptions
                completion:^(FIRModelOutputs * _Nullable outputs,
                             NSError * _Nullable error) {
  if (error != nil || outputs == nil) {
    return;
  }
  // Process outputs
  // ...
}];

Anda bisa mendapatkan keluaran dengan memanggil metode output(index:) dari objek yang ditampilkan. Misalnya:

Swift

// Get first and only output of inference with a batch size of 1
let output = try? outputs.output(index: 0) as? [[NSNumber]]
let probabilities = output??[0]

Objective-C

// Get first and only output of inference with a batch size of 1
NSError *outputError;
NSArray *probabilites = [outputs outputAtIndex:0 error:&outputError][0];

Cara penggunaan keluaran bergantung pada model yang Anda gunakan.

Misalnya, jika Anda melakukan klasifikasi, sebagai langkah berikutnya, Anda mungkin dapat memetakan indeks hasil ke label yang diwakilinya. Misalkan Anda memiliki file teks dengan string label untuk masing-masing kategori model Anda; Anda bisa memetakan string label ke probabilitas keluaran dengan melakukan hal berikut:

Swift

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 0 ..< labels.count {
  if let probability = probabilities?[i] {
    print("\(labels[i]): \(probability)")
  }
}

Objective-C

NSError *labelReadError = nil;
NSString *labelPath = [NSBundle.mainBundle pathForResource:@"retrained_labels"
                                                    ofType:@"txt"];
NSString *fileContents = [NSString stringWithContentsOfFile:labelPath
                                                   encoding:NSUTF8StringEncoding
                                                      error:&labelReadError];
if (labelReadError != nil || fileContents == NULL) { return; }
NSArray<NSString *> *labels = [fileContents componentsSeparatedByString:@"\n"];
for (int i = 0; i < labels.count; i++) {
    NSString *label = labels[i];
    NSNumber *probability = probabilites[i];
    NSLog(@"%@: %f", label, probability.floatValue);
}

Lampiran: Keamanan model

Terlepas dari bagaimana Anda menyediakan model TensorFlow Lite untuk ML Kit, ML Kit akan menyimpannya dalam format protobuf serial standar di penyimpanan lokal.

Secara teori, ini berarti bahwa siapa saja dapat menyalin model Anda. Namun, dalam praktiknya, sebagian besar model bersifat khusus aplikasi dan dikaburkan oleh pengoptimalan sehingga risikonya serupa dengan jika pesaing membongkar dan menggunakan kembali kode Anda. Meskipun demikian, Anda harus menyadari risiko ini sebelum menggunakan model kustom di aplikasi.