Check out what’s new from Firebase@ Google I/O 2021, and join our alpha program for early access to the new Remote Config personalization feature. Learn more

iOS'ta ML Kit ile çıkarım için bir TensorFlow Lite modeli kullanın

Bir TensorFlow Lite modeliyle cihaz üzerinde çıkarım yapmak için ML Kit'i kullanabilirsiniz.

ML Kit, TensorFlow Lite modellerini yalnızca iOS 9 ve daha yenisini çalıştıran cihazlarda kullanabilir.

Sen başlamadan önce

  1. Henüz uygulamanıza Firebase'i eklemediyseniz, başlangıç kılavuzundaki adımları izleyerek bunu yapın.
  2. ML Kit kitaplıklarını Pod dosyanıza ekleyin:
    pod 'Firebase/MLModelInterpreter', '6.25.0'
    
    Projenizin Pod'larını yükledikten veya güncelledikten sonra, Xcode projenizi .xcworkspace kullanarak açtığınızdan emin olun.
  3. Uygulamanızda Firebase'i içe aktarın:

    hızlı

    import Firebase

    Amaç-C

    @import Firebase;
  4. Kullanmak istediğiniz TensorFlow modelini TensorFlow Lite formatına dönüştürün. Bkz. TOCO: TensorFlow Lite Optimize Edici Dönüştürücü .

Modelinizi barındırın veya paketleyin

Uygulamanızda çıkarım için bir TensorFlow Lite modeli kullanmadan önce modeli ML Kit'e sunmalısınız. ML Kit, uygulama ikili programıyla birlikte verilen Firebase kullanılarak uzaktan barındırılan TensorFlow Lite modellerini veya her ikisini kullanabilir.

Firebase'de bir model barındırarak, yeni bir uygulama sürümü yayınlamadan modeli güncelleyebilir ve farklı modelleri farklı kullanıcı gruplarına dinamik olarak sunmak için Uzaktan Yapılandırma ve A/B Testini kullanabilirsiniz.

Modeli yalnızca Firebase ile barındırarak sağlamayı seçerseniz ve uygulamanızla birlikte paket yapmazsanız, uygulamanızın ilk indirme boyutunu küçültebilirsiniz. Bununla birlikte, model uygulamanızla birlikte paketlenmemişse, uygulamanız modeli ilk kez indirene kadar modelle ilgili herhangi bir işlevin kullanılamayacağını unutmayın.

Modelinizi uygulamanızla bir araya getirerek, Firebase tarafından barındırılan model mevcut olmadığında uygulamanızın ML özelliklerinin çalışmaya devam etmesini sağlayabilirsiniz.

Firebase'deki modelleri barındırın

TensorFlow Lite modelinizi Firebase'de barındırmak için:

  1. Firebase konsolunun ML Kiti bölümünde Özel sekmesini tıklayın.
  2. Özel model ekle (veya Başka bir model ekle ) öğesine tıklayın.
  3. Firebase projenizde modelinizi tanımlamak için kullanılacak bir ad belirtin, ardından TensorFlow Lite model dosyasını yükleyin (genellikle .tflite veya .lite ).

Firebase projenize özel bir model ekledikten sonra, belirttiğiniz adı kullanarak uygulamalarınızda modele başvurabilirsiniz. İstediğiniz zaman yeni bir TensorFlow Lite modeli yükleyebilirsiniz; uygulamanız yeni modeli indirecek ve uygulama yeniden başlatıldığında kullanmaya başlayacaktır. Uygulamanızın modeli güncellemeye çalışması için gereken cihaz koşullarını tanımlayabilirsiniz (aşağıya bakın).

Modelleri bir uygulamayla paketleyin

TensorFlow Lite modelinizi uygulamanızla birlikte paketlemek için, model dosyasını (genellikle .tflite veya .lite biten) Xcode projenize ekleyin ve bunu yaparken Paket kaynaklarını kopyala'yı seçmeye dikkat edin. Model dosyası, uygulama paketine dahil edilecek ve ML Kit'e sunulacak.

modeli yükle

TensorFlow Lite modelinizi uygulamanızda kullanmak için önce ML Kit'i modelinizin bulunduğu konumlarla yapılandırın: Firebase'i uzaktan kullanarak, yerel depolamada veya her ikisini birden. Hem yerel hem de uzak model belirtirseniz, varsa uzak modeli kullanabilir ve uzak model yoksa yerel olarak depolanan modele dönebilirsiniz.

Firebase tarafından barındırılan bir modeli yapılandırın

Modelinizi Firebase ile barındırdıysanız, yayınladığınızda modele atadığınız adı belirterek bir CustomRemoteModel nesnesi oluşturun:

hızlı

let remoteModel = CustomRemoteModel(
  name: "your_remote_model"  // The name you assigned in the Firebase console.
)

Amaç-C

// Initialize using the name you assigned in the Firebase console.
FIRCustomRemoteModel *remoteModel =
    [[FIRCustomRemoteModel alloc] initWithName:@"your_remote_model"];

Ardından, indirmeye izin vermek istediğiniz koşulları belirterek model indirme görevini başlatın. Model cihazda değilse veya modelin daha yeni bir sürümü mevcutsa görev, modeli Firebase'den eşzamansız olarak indirir:

hızlı

let downloadConditions = ModelDownloadConditions(
  allowsCellularAccess: true,
  allowsBackgroundDownloading: true
)

let downloadProgress = ModelManager.modelManager().download(
  remoteModel,
  conditions: downloadConditions
)

Amaç-C

FIRModelDownloadConditions *downloadConditions =
    [[FIRModelDownloadConditions alloc] initWithAllowsCellularAccess:YES
                                         allowsBackgroundDownloading:YES];

NSProgress *downloadProgress =
    [[FIRModelManager modelManager] downloadRemoteModel:remoteModel
                                             conditions:downloadConditions];

Birçok uygulama indirme görevini başlatma kodunda başlatır, ancak bunu modeli kullanmanız gerekmeden önce herhangi bir noktada yapabilirsiniz.

Yerel bir model yapılandırın

Modeli uygulamanızla paketlediyseniz, TensorFlow Lite modelinin dosya adını belirterek bir CustomLocalModel nesnesi oluşturun:

hızlı

guard let modelPath = Bundle.main.path(
  forResource: "your_model",
  ofType: "tflite",
  inDirectory: "your_model_directory"
) else { /* Handle error. */ }
let localModel = CustomLocalModel(modelPath: modelPath)

Amaç-C

NSString *modelPath = [NSBundle.mainBundle pathForResource:@"your_model"
                                                    ofType:@"tflite"
                                               inDirectory:@"your_model_directory"];
FIRCustomLocalModel *localModel =
    [[FIRCustomLocalModel alloc] initWithModelPath:modelPath];

Modelinizden bir tercüman oluşturun

Model kaynaklarınızı yapılandırdıktan sonra, bunlardan birinden bir ModelInterpreter nesnesi oluşturun.

Yalnızca yerel olarak paketlenmiş bir modeliniz varsa, CustomLocalModel nesnesini modelInterpreter(localModel:) :

hızlı

let interpreter = ModelInterpreter.modelInterpreter(localModel: localModel)

Amaç-C

FIRModelInterpreter *interpreter =
    [FIRModelInterpreter modelInterpreterForLocalModel:localModel];

Uzaktan barındırılan bir modeliniz varsa, çalıştırmadan önce indirildiğini kontrol etmeniz gerekir. Model yöneticisinin isModelDownloaded(remoteModel:) yöntemini kullanarak model indirme görevinin durumunu kontrol edebilirsiniz.

Bunu yalnızca yorumlayıcıyı çalıştırmadan önce onaylamanız gerekse de, hem uzaktan barındırılan hem de yerel olarak paketlenmiş bir modeliniz varsa, ModelInterpreter örneğini başlatırken bu kontrolü yapmak mantıklı olabilir: uzak modelden bir yorumlayıcı oluşturun. indirildi ve aksi takdirde yerel modelden.

hızlı

var interpreter: ModelInterpreter
if ModelManager.modelManager().isModelDownloaded(remoteModel) {
  interpreter = ModelInterpreter.modelInterpreter(remoteModel: remoteModel)
} else {
  interpreter = ModelInterpreter.modelInterpreter(localModel: localModel)
}

Amaç-C

FIRModelInterpreter *interpreter;
if ([[FIRModelManager modelManager] isModelDownloaded:remoteModel]) {
  interpreter = [FIRModelInterpreter modelInterpreterForRemoteModel:remoteModel];
} else {
  interpreter = [FIRModelInterpreter modelInterpreterForLocalModel:localModel];
}

Yalnızca uzaktan barındırılan bir modeliniz varsa, modelin indirildiğini onaylayana kadar modelle ilgili işlevleri (örneğin, kullanıcı arayüzünüzün grileştirilmesi veya bir kısmının gizlenmesi) devre dışı bırakmanız gerekir.

Model indirme durumunu, varsayılan Bildirim Merkezi'ne gözlemciler ekleyerek alabilirsiniz. İndirmeler biraz zaman alabileceğinden ve indirme bittiğinde kaynak nesne serbest bırakılabileceğinden, gözlemci bloğunda self için zayıf bir referans kullandığınızdan emin olun. Örneğin:

hızlı

NotificationCenter.default.addObserver(
    forName: .firebaseMLModelDownloadDidSucceed,
    object: nil,
    queue: nil
) { [weak self] notification in
    guard let strongSelf = self,
        let userInfo = notification.userInfo,
        let model = userInfo[ModelDownloadUserInfoKey.remoteModel.rawValue]
            as? RemoteModel,
        model.name == "your_remote_model"
        else { return }
    // The model was downloaded and is available on the device
}

NotificationCenter.default.addObserver(
    forName: .firebaseMLModelDownloadDidFail,
    object: nil,
    queue: nil
) { [weak self] notification in
    guard let strongSelf = self,
        let userInfo = notification.userInfo,
        let model = userInfo[ModelDownloadUserInfoKey.remoteModel.rawValue]
            as? RemoteModel
        else { return }
    let error = userInfo[ModelDownloadUserInfoKey.error.rawValue]
    // ...
}

Amaç-C

__weak typeof(self) weakSelf = self;

[NSNotificationCenter.defaultCenter
    addObserverForName:FIRModelDownloadDidSucceedNotification
                object:nil
                 queue:nil
            usingBlock:^(NSNotification *_Nonnull note) {
              if (weakSelf == nil | note.userInfo == nil) {
                return;
              }
              __strong typeof(self) strongSelf = weakSelf;

              FIRRemoteModel *model = note.userInfo[FIRModelDownloadUserInfoKeyRemoteModel];
              if ([model.name isEqualToString:@"your_remote_model"]) {
                // The model was downloaded and is available on the device
              }
            }];

[NSNotificationCenter.defaultCenter
    addObserverForName:FIRModelDownloadDidFailNotification
                object:nil
                 queue:nil
            usingBlock:^(NSNotification *_Nonnull note) {
              if (weakSelf == nil | note.userInfo == nil) {
                return;
              }
              __strong typeof(self) strongSelf = weakSelf;

              NSError *error = note.userInfo[FIRModelDownloadUserInfoKeyError];
            }];

Modelin giriş ve çıkışını belirtin

Ardından, model yorumlayıcının giriş ve çıkış biçimlerini yapılandırın.

Bir TensorFlow Lite modeli girdi olarak alır ve çıktı olarak bir veya daha fazla çok boyutlu dizi üretir. Bu diziler byte , int , long veya float değerler içerir. Modelinizin kullandığı dizilerin sayısı ve boyutları ("şekil") ile ML Kit'i yapılandırmanız gerekir.

Modelinizin giriş ve çıkışının şeklini ve veri türünü bilmiyorsanız, modelinizi incelemek için TensorFlow Lite Python yorumlayıcısını kullanabilirsiniz. Örneğin:

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'>

Modelinizin giriş ve çıkış biçimini belirledikten sonra, bir ModelInputOutputOptions nesnesi oluşturarak uygulamanızın model yorumlayıcısını yapılandırın.

Örneğin, bir kayan noktalı görüntü sınıflandırma modeli girdi olarak bir alabilir N arasında x224x224x3 dizisi Float bir toplu temsil değerleri N 1000 listesini çıkışı olarak 224x224 üç kanal (RGB) görüntü ve üretim Float her birini temsil eden değerler görüntünün, modelin öngördüğü 1000 kategoriden birinin üyesi olma olasılığı.

Böyle bir model için, model yorumlayıcısının giriş ve çıkışını aşağıda gösterildiği gibi yapılandırırsınız:

hızlı

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)")
}

Amaç-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; }

Giriş verileri üzerinde çıkarım gerçekleştirin

Son olarak, modeli kullanarak çıkarım yapmak için giriş verilerinizi alın, modeliniz için gerekli olabilecek veriler üzerinde tüm dönüşümleri gerçekleştirin ve verileri içeren bir Data nesnesi oluşturun.

Örneğin, modeliniz görüntüleri [BATCH_SIZE, 224, 224, 3] ve modeliniz [BATCH_SIZE, 224, 224, 3] kayan nokta değerlerinin giriş boyutlarına [BATCH_SIZE, 224, 224, 3] görüntünün renk değerlerini aşağıdaki örnekte olduğu gibi bir kayan nokta aralığına ölçeklendirmeniz gerekebilir. :

hızlı

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)")
}

Amaç-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; }

Model girişinizi hazırladıktan sonra (ve modelin mevcut olduğunu onayladıktan sonra), giriş ve giriş/çıkış seçeneklerini model yorumlayıcınızın run(inputs:options:completion:) yöntemine iletin.

hızlı

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

Amaç-C

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

Döndürülen nesnenin output(index:) yöntemini çağırarak çıktıyı alabilirsiniz. Örneğin:

hızlı

// 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]

Amaç-C

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

Çıktıyı nasıl kullandığınız, kullandığınız modele bağlıdır.

Örneğin, sınıflandırma yapıyorsanız, sonraki adım olarak sonucun dizinlerini temsil ettikleri etiketlerle eşleyebilirsiniz. Modelinizin kategorilerinin her biri için etiket dizeleri içeren bir metin dosyanız olduğunu varsayalım; aşağıdakine benzer bir şey yaparak etiket dizelerini çıktı olasılıklarıyla eşleştirebilirsiniz:

hızlı

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)")
  }
}

Amaç-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);
}

Ek: Model güvenliği

TensorFlow Lite modellerinizi ML Kit için nasıl kullanılabilir hale getirdiğinizden bağımsız olarak, ML Kit bunları yerel depolamada standart serileştirilmiş protobuf formatında saklar.

Teoride bu, herkesin modelinizi kopyalayabileceği anlamına gelir. Bununla birlikte, pratikte, çoğu model uygulamaya o kadar özeldir ve optimizasyonlarla karıştırılmıştır ki, risk rakiplerinizin kodunuzu söküp yeniden kullanma riskine benzer. Yine de, uygulamanızda özel bir model kullanmadan önce bu riskin farkında olmalısınız.