欢迎参加我们将于 2022 年 10 月 18 日举办的 Firebase 峰会(线上线下同时进行),了解 Firebase 如何帮助您加快应用开发速度、满怀信心地发布应用并在之后需要时轻松地扩大应用规模。立即报名

الترحيل من واجهة برمجة التطبيقات للنموذج المخصص القديم

يقدم الإصدار 0.20.0 من مكتبة Firebase/MLModelInterpreter طريقة getLatestModelFilePath() الجديدة ، والتي تحصل على الموقع على جهاز النماذج المخصصة. يمكنك استخدام هذه الطريقة لإنشاء مثيل مباشر لكائن TensorFlow Lite Interpreter ، والذي يمكنك استخدامه بدلاً من غلاف ModelInterpreter الخاص بـ ModelInterpreter .

للمضي قدمًا ، هذا هو النهج المفضل. نظرًا لأن إصدار مترجم TensorFlow Lite لم يعد مقترنًا بإصدار مكتبة Firebase ، فلديك المزيد من المرونة للترقية إلى إصدارات جديدة من TensorFlow Lite عندما تريد ، أو استخدام تصميمات TensorFlow Lite المخصصة بسهولة أكبر.

توضح هذه الصفحة كيف يمكنك الانتقال من استخدام ModelInterpreter إلى Interpreter TensorFlow Lite.

1. تحديث تبعيات المشروع

قم بتحديث 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'

2. قم بإنشاء مترجم TensorFlow Lite بدلاً من Firebase ModelInterpreter

بدلاً من إنشاء ModelInterpreter ، احصل على موقع النموذج على الجهاز باستخدام getLatestModelFilePath() واستخدمه لإنشاء Interpreter TensorFlow Lite.

قبل

سويفت

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

3. تحديث كود إعداد المدخلات والمخرجات

باستخدام ModelInterpreter ، يمكنك تحديد أشكال المدخلات والمخرجات للنموذج عن طريق تمرير كائن ModelInputOutputOptions إلى المترجم الفوري عند تشغيله.

بالنسبة لمترجم TensorFlow Lite ، يمكنك بدلاً من ذلك استدعاء customateTensors allocateTensors() لتخصيص مساحة لإدخال وإخراج النموذج ، ثم انسخ بيانات الإدخال إلى موتر الإدخال.

على سبيل المثال ، إذا كان نموذجك يحتوي على شكل إدخال [1 224 224 3] قيم float وشكل إخراج [1100] قيم float ، فقم بإجراء هذه التغييرات:

قبل

سويفت

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

4. تحديث كود معالجة الإخراج

أخيرًا ، بدلاً من الحصول على مخرجات النموذج باستخدام طريقة 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]);
}