از API مدل سفارشی قدیمی مهاجرت کنید

نسخه 0.20.0 کتابخانه Firebase/MLModelInterpreter یک متد جدید getLatestModelFilePath() معرفی می‌کند که موقعیت مکانی مدل‌های سفارشی را روی دستگاه دریافت می‌کند. می‌توانید از این متد برای نمونه‌سازی مستقیم یک شیء TensorFlow Lite Interpreter استفاده کنید که می‌توانید به جای پوشش ModelInterpreter فایربیس از آن استفاده کنید.

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

این صفحه نشان می‌دهد که چگونه می‌توانید از استفاده از ModelInterpreter به TensorFlow Lite Interpreter مهاجرت کنید.

۱. وابستگی‌های پروژه را به‌روزرسانی کنید

فایل Podfile پروژه خود را به‌روزرسانی کنید تا نسخه 0.20.0 کتابخانه Firebase/MLModelInterpreter (یا جدیدتر) و کتابخانه TensorFlow Lite را شامل شود:

قبل از

سویفت

pod 'Firebase/MLModelInterpreter', '0.19.0'

هدف-سی

pod 'Firebase/MLModelInterpreter', '0.19.0'

بعد از

سویفت

pod 'Firebase/MLModelInterpreter', '~> 0.20.0'
pod 'TensorFlowLiteSwift'

هدف-سی

pod 'Firebase/MLModelInterpreter', '~> 0.20.0'
pod 'TensorFlowLiteObjC'

۲. به جای Firebase ModelInterpreter، یک مفسر TensorFlow Lite ایجاد کنید

به جای ایجاد یک Firebase ModelInterpreter ، موقعیت مکانی مدل روی دستگاه را با getLatestModelFilePath() دریافت کنید و از آن برای ایجاد یک TensorFlow Lite Interpreter استفاده کنید.

قبل از

سویفت

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

هدف-سی

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

بعد از

سویفت

let remoteModel = CustomRemoteModel(
    name: "your_remote_model"  // The name you assigned in the Firebase console.
)
ModelManager.modelManager().getLatestModelFilePath(remoteModel) { (remoteModelPath, error) in
    guard error == nil, let remoteModelPath = remoteModelPath else { return }
    do {
        interpreter = try Interpreter(modelPath: remoteModelPath)
    } catch {
        // Error?
    }
}

هدف-سی

FIRCustomRemoteModel *remoteModel =
        [[FIRCustomRemoteModel alloc] initWithName:@"your_remote_model"];
[[FIRModelManager modelManager] getLatestModelFilePath:remoteModel
                                            completion:^(NSString * _Nullable filePath,
                                                         NSError * _Nullable error) {
    if (error != nil || filePath == nil) { return; }

    NSError *tfError = nil;
    interpreter = [[TFLInterpreter alloc] initWithModelPath:filePath error:&tfError];
}];

۳. کد آماده‌سازی ورودی و خروجی را به‌روزرسانی کنید

با استفاده از ModelInterpreter ، شما با ارسال یک شیء ModelInputOutputOptions به مفسر هنگام اجرای آن، شکل‌های ورودی و خروجی مدل را مشخص می‌کنید.

برای مفسر TensorFlow Lite، شما به جای آن، allocateTensors() برای اختصاص فضا برای ورودی و خروجی مدل فراخوانی می‌کنید، سپس داده‌های ورودی خود را در تانسورهای ورودی کپی می‌کنید.

برای مثال، اگر مدل شما شکل ورودی با مقادیر float [1 224 224 3] و شکل خروجی با مقادیر float [1 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)")
}

let inputs = ModelInputs()
do {
    let inputData = Data()
    // Then populate with input data.

    try inputs.addInput(inputData)
} catch let error {
    print("Failed to add input: \(error)")
}

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

هدف-سی

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

FIRModelInputs *inputs = [[FIRModelInputs alloc] init];
NSMutableData *inputData = [[NSMutableData alloc] initWithCapacity:0];
// Then populate with input data.

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

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

بعد از

سویفت

do {
    try interpreter.allocateTensors()

    let inputData = Data()
    // Then populate with input data.

    try interpreter.copy(inputData, toInputAt: 0)

    try interpreter.invoke()
} catch let err {
    print(err.localizedDescription)
}

هدف-سی

NSError *error = nil;

[interpreter allocateTensorsWithError:&error];
if (error != nil) { return; }

TFLTensor *input = [interpreter inputTensorAtIndex:0 error:&error];
if (error != nil) { return; }

NSMutableData *inputData = [[NSMutableData alloc] initWithCapacity:0];
// Then populate with input data.

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

[interpreter invokeWithError:&error];
if (error != nil) { return; }

۴. کد مدیریت خروجی را به‌روزرسانی کنید

در نهایت، به جای دریافت خروجی مدل با متد output() شیء ModelOutputs ، تانسور خروجی را از مفسر دریافت کرده و داده‌های آن را به هر ساختاری که برای مورد استفاده شما مناسب است تبدیل کنید.

برای مثال، اگر در حال طبقه‌بندی هستید، ممکن است تغییراتی مانند موارد زیر ایجاد کنید:

قبل از

سویفت

let output = try? outputs.output(index: 0) as? [[NSNumber]]
let probabilities = output?[0]

guard let labelPath = Bundle.main.path(
    forResource: "custom_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)")
    }
}

هدف-سی

// Get first and only output of inference with a batch size of 1
NSError *error;
NSArray *probabilites = [outputs outputAtIndex:0 error:&error][0];
if (error != nil) { return; }

NSString *labelPath = [NSBundle.mainBundle pathForResource:@"retrained_labels"
                                                    ofType:@"txt"];
NSString *fileContents = [NSString stringWithContentsOfFile:labelPath
                                                   encoding:NSUTF8StringEncoding
                                                      error:&error];
if (error != 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);
}

بعد از

سویفت

do {
    // After calling interpreter.invoke():
    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: "custom_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])")
    }
} catch let err {
    print(err.localizedDescription)
}

هدف-سی

NSError *error = nil;

TFLTensor *output = [interpreter outputTensorAtIndex:0 error:&error];
if (error != nil) { return; }

NSData *outputData = [output dataWithError:&error];
if (error != nil) { return; }

Float32 probabilities[outputData.length / 4];
[outputData getBytes:&probabilities length:outputData.length];

NSString *labelPath = [NSBundle.mainBundle pathForResource:@"custom_labels"
                                                    ofType:@"txt"];
NSString *fileContents = [NSString stringWithContentsOfFile:labelPath
                                                   encoding:NSUTF8StringEncoding
                                                      error:&error];
if (error != nil || fileContents == nil) { return; }

NSArray<NSString *> *labels = [fileContents componentsSeparatedByString:@"\n"];
for (int i = 0; i < labels.count; i++) {
    NSLog(@"%@: %f", labels[i], probabilities[i]);
}