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

برای استنباط با ML Kit در iOS از یک مدل TensorFlow Lite استفاده کنید

شما می توانید ML کیت به انجام بر روی دستگاه استنتاج با استفاده TensorFlow بازگشت به محتوا | مدل.

ML Kit می تواند از مدلهای TensorFlow Lite فقط در دستگاههایی که iOS 9 و جدیدتر دارند استفاده کند.

قبل از اینکه شروع کنی

  1. اگر شما در حال حاضر در Firebase به برنامه شما اضافه نشده است، این کار را با دنبال کردن مراحل در راهنمای شروع به کار .
  2. شامل کتابخانه ML Kit در Podfile شما:
    pod 'Firebase/MLModelInterpreter', '6.25.0'
    
    شما بعد از نصب یا به روز رسانی غلاف پروژه خود را، مطمئن شوید که برای باز کردن پروژه Xcode متعلق به خود را با استفاده از آن .xcworkspace .
  3. در برنامه خود ، Firebase را وارد کنید:

    سریع

    import Firebase

    هدف-ج

    @import Firebase;
  4. مدل TensorFlow را که می خواهید استفاده کنید به فرمت TensorFlow Lite تبدیل کنید. مشاهده TensorFlow بازگشت به محتوا | بهینه سازی تبدیل: TOCO .

مدل خود را میزبانی یا بسته بندی کنید

قبل از استفاده از مدل TensorFlow Lite برای نتیجه گیری در برنامه خود ، باید مدل را در دسترس ML Kit قرار دهید. ML Kit می تواند از مدلهای TensorFlow Lite که از راه دور با استفاده از Firebase میزبانی شده اند ، همراه با برنامه باینری یا هر دو استفاده کند.

با میزبانی یک مدل در Firebase ، می توانید مدل را بدون انتشار نسخه جدید برنامه به روز کنید ، و می توانید از Remote Config و A/B Testing برای ارائه پویا مدل های مختلف به مجموعه های مختلف کاربران استفاده کنید.

اگر می خواهید مدل را فقط با میزبانی Firebase ارائه دهید و آن را با برنامه خود همراه نکنید ، می توانید حجم بارگیری اولیه برنامه خود را کاهش دهید. با این حال ، به خاطر داشته باشید که اگر مدل با برنامه شما همراه نشده باشد ، تا زمانی که برنامه شما مدل را برای اولین بار بارگیری نکند ، هیچ ویژگی مرتبط با مدل در دسترس نخواهد بود.

با ترکیب مدل خود با برنامه خود ، می توانید اطمینان حاصل کنید که ویژگی های ML برنامه شما همچنان کار می کند وقتی مدل میزبانی Firebase در دسترس نیست.

مدلهای میزبان در Firebase

برای میزبانی مدل TensorFlow Lite خود در Firebase:

  1. در بخش ML کیت از فایربیس کنسول ، روی زبانه های سفارشی.
  2. کلیک کنید اضافه کردن مدل های سفارشی (و یا اضافه کردن مدل های دیگری).
  3. تعیین نام که استفاده می شود برای شناسایی مدل خود را در پروژه فایربیس خود را آپلود فایل مدل TensorFlow بازگشت به محتوا | (معمولا در پایان دادن به .tflite یا .lite ).

پس از افزودن یک مدل سفارشی به پروژه Firebase خود ، می توانید مدل را در برنامه های خود با استفاده از نامی که مشخص کرده اید ارجاع دهید. در هر زمان ، می توانید یک مدل جدید TensorFlow Lite بارگذاری کنید ، و برنامه شما مدل جدید را بارگیری می کند و با راه اندازی مجدد برنامه ، استفاده از آن را شروع می کند. شما می توانید شرایط دستگاه مورد نیاز برای برنامه خود را برای تلاش برای به روز رسانی مدل تعریف کنید (به پایین مراجعه کنید).

بسته بندی مدلها با یک برنامه

به بسته نرم افزاری مدل TensorFlow آرشیو خود را با برنامه خود، اضافه کردن فایل مدل (که معمولا در پایان دادن به .tflite یا .lite ) به پروژه Xcode متعلق به خود، مراقبت را انتخاب کنید کپی بسته نرم افزاری منابع زمانی که شما این کار را. فایل مدل در بسته نرم افزاری گنجانده شده و در دسترس ML Kit خواهد بود.

مدل را بارگذاری کنید

برای استفاده از مدل TensorFlow Lite خود در برنامه ، ابتدا ML Kit را با مکانهایی که مدل شما در دسترس است پیکربندی کنید: از راه دور با استفاده از Firebase ، در ذخیره سازی محلی یا هر دو. اگر هم مدل محلی و هم راه دور را مشخص کنید ، می توانید از مدل از راه دور در صورت موجود بودن آن استفاده کنید و اگر مدل از راه دور در دسترس نیست دوباره به مدل ذخیره شده محلی بازگردید.

پیکربندی مدل میزبانی شده Firebase

اگر شما مدل خود را با Firebase میزبانی، ایجاد یک CustomRemoteModel شی، تعیین نام شما مدل اختصاص زمانی که شما آن را منتشر:

سریع

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

هدف-ج

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

سپس ، کار بارگیری مدل را مشخص کنید ، شرایطی را که می خواهید اجازه بارگیری را مشخص کنید مشخص کنید. اگر مدل روی دستگاه نباشد یا نسخه جدیدتری از مدل موجود باشد ، کار به صورت ناهمزمان مدل را از Firebase بارگیری می کند:

سریع

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

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

هدف-ج

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

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

بسیاری از برنامه ها وظیفه بارگیری را در کد اولیه خود شروع می کنند ، اما قبل از استفاده از مدل می توانید این کار را در هر نقطه انجام دهید.

یک مدل محلی را پیکربندی کنید

اگر شما این مدل را با برنامه خود را، ایجاد یک CustomLocalModel شی، تعیین نام فایل از مدل TensorFlow مطلب:

سریع

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

هدف-ج

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

از مدل خود یک مترجم ایجاد کنید

بعد از اینکه شما منابع مدل خود را پیکربندی کنید، ایجاد یک ModelInterpreter شی از یکی از آنها.

اگر شما فقط یک مدل به صورت محلی همراه داشته باشد، فقط تصویب CustomLocalModel شی به modelInterpreter(localModel:) :

سریع

let interpreter = ModelInterpreter.modelInterpreter(localModel: localModel)

هدف-ج

FIRModelInterpreter *interpreter =
    [FIRModelInterpreter modelInterpreterForLocalModel:localModel];

اگر مدل میزبانی از راه دور دارید ، قبل از اجرا باید بررسی کنید که بارگیری شده است. شما می توانید وضعیت از کار مدل دانلود با استفاده از مدیر مدل را بررسی کنید isModelDownloaded(remoteModel:) روش.

اگرچه شما فقط مجبور به اعلام این قبل از اجرای مترجم، اگر شما هر دو یک مدل کنترل از راه دور به میزبانی و یک مدل به صورت محلی همراه، ممکن است احساس کنند برای انجام این چک که با نمونه سازی ModelInterpreter : ایجاد یک مترجم از مدل کنترل از راه دور اگر آن را بارگیری شده است ، و در غیر این صورت از مدل محلی.

سریع

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

هدف-ج

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

اگر فقط یک مدل میزبانی از راه دور دارید ، باید عملکرد مربوط به مدل را غیرفعال کنید-به عنوان مثال ، خاکستری شدن یا پنهان کردن بخشی از UI خود-تا زمانی که تأیید کنید مدل بارگیری شده است.

با پیوستن ناظران به مرکز اطلاع رسانی پیش فرض ، می توانید وضعیت بارگیری مدل را دریافت کنید. مطمئن باشید که برای استفاده از یک مرجع ضعیف به self در بلوک ناظر، از دانلود می توانید برخی از زمان را، و شی منشاء می توان با زمان آزاد پس از اتمام دانلود. مثلا:

سریع

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

هدف-ج

__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];
            }];

ورودی و خروجی مدل را مشخص کنید

بعد ، فرمت های ورودی و خروجی مترجم مدل را پیکربندی کنید.

یک مدل TensorFlow Lite ورودی می گیرد و یک یا چند آرایه چند بعدی را به عنوان خروجی تولید می کند. این آرایه های حاوی byte ، int ، long ، و یا float ارزش. شما باید ML Kit را با تعداد و ابعاد ("شکل") آرایه هایی که مدل شما استفاده می کند پیکربندی کنید.

اگر شکل و نوع داده ورودی و خروجی مدل خود را نمی دانید ، می توانید از مترجم TensorFlow Lite Python برای بررسی مدل خود استفاده کنید. مثلا:

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

پس از فرمت های ورودی و خروجی مدل خود را تعیین، مترجم مدل برنامه خود را با ایجاد یک پیکربندی ModelInputOutputOptions شی.

به عنوان مثال، یک تصویر مدل طبقه بندی ممیز شناور ممکن است به عنوان ورودی یک را N x224x224x3 مجموعه ای از Float ارزش ها، به نمایندگی از یک دسته ای از N 224x224 سه کانال (RGB) تصاویر و تولید به عنوان خروجی یک لیست از 1000 Float ارزش ها، هر یک نماینده ی احتمالاً تصویر یکی از 1000 دسته ای است که مدل پیش بینی کرده است.

برای چنین مدلی ، ورودی و خروجی مترجم را مطابق شکل زیر پیکربندی کنید:

سریع

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

هدف-ج

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

استنتاج در مورد داده های ورودی را انجام دهید

در نهایت، به انجام استنتاج با استفاده از مدل، دریافت داده های ورودی خود را، انجام هر گونه تحولات در داده هایی را که ممکن است برای مدل خود را لازم باشد، و ساخت یک Data شی که شامل داده.

برای مثال، اگر فرآیندهای مدل عکس ها، و مدل خود را دارای ابعاد ورودی [BATCH_SIZE, 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 }

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

هدف-ج

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

پس از ورودی مدل خود را آماده (و پس از تایید شما مدل در دسترس است)، عبور ورودی و ورودی / خروجی گزینه به شما مترجم مدل را run(inputs:options:completion:) روش.

سریع

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

هدف-ج

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

شما می توانید خروجی را با تماس با گرفتن output(index:) روش از جسم است که بازگشت. مثلا:

سریع

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

هدف-ج

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

نحوه استفاده از خروجی به مدل مورد استفاده شما بستگی دارد.

به عنوان مثال ، اگر در حال انجام طبقه بندی هستید ، در مرحله بعد ، ممکن است نمایه های نتیجه را به برچسب هایی که نشان می دهند ترسیم کنید. فرض کنید یک فایل متنی با رشته های برچسب برای هر یک از دسته های مدل خود دارید. با انجام موارد زیر می توانید رشته های برچسب را به احتمالات خروجی ترسیم کنید:

سریع

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

هدف-ج

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

پیوست: امنیت مدل

صرف نظر از اینکه چگونه مدلهای TensorFlow Lite خود را در دسترس ML Kit قرار می دهید ، ML Kit آنها را در قالب استاندارد protobuf سریالی در ذخیره سازی محلی ذخیره می کند.

از لحاظ تئوری ، این بدان معناست که هرکسی می تواند مدل شما را کپی کند. با این حال ، در عمل ، اکثر مدلها آنقدر خاص برنامه هستند و با بهینه سازی ها مبهم می شوند که خطر شبیه به جداسازی و استفاده مجدد کد شما توسط رقبا است. با این وجود ، قبل از استفاده از مدل سفارشی در برنامه خود ، باید از این خطر آگاه باشید.