استفاده از مدل TensorFlow Lite سفارشی در پلاتفرم‌های Apple

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

پیش‌نیازها

  • کتابخانه MLModelDownloader فقط برای Swift دردسترس است.
  • ‫TensorFlow Lite فقط در دستگاه‌هایی که از iOS 9 و نسخه‌های جدیدتر استفاده می‌کنند اجرا می‌شود.

مدل‌های TensorFlow Lite

مدل‌های TensorFlow Lite مدل‌های ML هستند که برای اجرا در دستگاه‌های همراه بهینه‌سازی شده‌اند. برای دریافت مدل TensorFlow Lite:

قبل از شروع

برای استفاده از TensorFlowLite با Firebase، باید از CocoaPods استفاده کنید زیرا TensorFlowLite درحال‌حاضر از نصب با «مدیر بسته Swift» پشتیبانی نمی‌کند. برای دریافت دستورالعمل‌های مربوط به نحوه نصب MLModelDownloader، راهنمای نصب CocoaPods را ببینید.

پس‌از نصب، Firebase و TensorFlowLite را وارد کنید تا بتوانید از آن‌ها استفاده کنید.

Swift

import FirebaseMLModelDownloader
import TensorFlowLite

۱. مدلتان را مستقر کنید

مدل‌های TensorFlow سفارشی‌تان را بااستفاده از کنسول Firebase یا کیت‌های توسعه نرم‌افزار Firebase Admin Python و Node.js مستقر کنید. استقرار و مدیریت مدل‌های سفارشی را ببینید.

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

۲. مدل را در دستگاه بارگیری کنید و مفسر TensorFlow Lite را مقداردهی اولیه کنید

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

برای شروع بارگیری مدل، روش getModel() بارگیری‌کننده مدل را فراخوانی کنید، نامی را که هنگام بارگذاری مدل به آن اختصاص دادید مشخص کنید، و مشخص کنید آیا می‌خواهید همیشه جدیدترین مدل بارگیری شود و تحت چه شرایطی می‌خواهید بارگیری مجاز باشد.

می‌توانید از بین سه رفتار بارگیری انتخاب کنید:

نوع بارگیری شرح
localModel مدل محلی را از دستگاه دریافت کنید. اگر مدل محلی دردسترس نباشد، این مانند latestModel عمل می‌کند. اگر علاقه‌ای به بررسی به‌روزرسانی‌های مدل ندارید، از این نوع بارگیری استفاده کنید. برای مثال، از «پیکربندی از دور» برای بازیابی نام‌های مدل استفاده می‌کنید و همیشه مدل‌ها را تحت نام‌های جدید بارگذاری می‌کنید (توصیه می‌شود).
localModelUpdateInBackground مدل محلی را از دستگاه دریافت کنید و به‌روزرسانی مدل را در پس‌زمینه شروع کنید. اگر مدل محلی دردسترس نباشد، این مانند latestModel عمل می‌کند.
latestModel جدیدترین مدل را دریافت کنید. اگر مدل محلی جدیدترین نسخه باشد، مدل محلی را برمی‌گرداند. درغیراین‌صورت، جدیدترین مدل را بارگیری کنید. این رفتار تا زمانی که جدیدترین نسخه بارگیری شود مسدود خواهد شد (توصیه نمی‌شود). فقط در مواردی که صریحاً به جدیدترین نسخه نیاز دارید از این رفتار استفاده کنید.

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

Swift

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

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

۳. انجام استنباط روی داده‌های ورودی

دریافت شکل‌های ورودی و خروجی مدل

تفسیرگر مدل TensorFlow Lite یک یا چند آرایه چندبعدی را به‌عنوان ورودی می‌گیرد و به‌عنوان خروجی تولید می‌کند. این آرایه‌ها حاوی مقادیر byte،‏ int،‏ long، یا float هستند. قبل‌از اینکه بتوانید داده‌ها را به مدل منتقل کنید یا از نتیجه آن استفاده کنید، باید تعداد و ابعاد («شکل») آرایه‌هایی را که مدل شما استفاده می‌کند بدانید.

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

پایتون

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] مقدار ممیز شناور دارد، ممکن است لازم باشد مقادیر رنگ تصویر را به محدوده ممیز شناور مقیاس کنید، مانند مثال زیر:

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 }

var inputData = Data()
for row in 0 ..&lt; 224 {
  for col in 0 ..&lt; 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(&amp;bytes, &amp;normalizedRed, elementSize)
    inputData.append(&amp;bytes, count: elementSize)
    memcpy(&amp;bytes, &amp;normalizedGreen, elementSize)
    inputData.append(&amp;bytes, count: elementSize)
    memcpy(&ammp;bytes, &amp;normalizedBlue, elementSize)
    inputData.append(&amp;bytes, count: elementSize)
  }
}

سپس ورودی NSData را در مفسر کپی کنید و آن را اجرا کنید:

Swift

try interpreter.allocateTensors()
try interpreter.copy(inputData, toInputAt: 0)
try interpreter.invoke()

با فراخوانی روش output(at:) مفسر می‌توانید برونداد مدل را دریافت کنید. نحوه استفاده از برونداد به مدلی که استفاده می‌کنید بستگی دارد.

برای مثال، اگر طبقه‌بندی انجام می‌دهید، به‌عنوان گام بعدی می‌توانید شاخص‌های نتیجه را به برچسب‌هایی که نشان می‌دهند نگاشت کنید:

Swift

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

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

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

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