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

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

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

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

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

حدِّث ملف Podfile الخاص بمشروعك لتضمين الإصدار 0.20.0 من مكتبة Firebase/MLModelInterpreter (أو إصدار أحدث) وTensorFlow Lite المكتبة:

قبل

Swift

pod 'Firebase/MLModelInterpreter', '0.19.0'

Objective-C

pod 'Firebase/MLModelInterpreter', '0.19.0'

بعد

Swift

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

Objective-C

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

2- أنشئ مترجمًا فوريًا من TensorFlow Lite بدلاً من مترجم نماذج Firebase.

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

قبل

Swift

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

Objective-C

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

بعد

Swift

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

Objective-C

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، يمكنك بدلاً من ذلك طلب allocateTensors() من أجل تخصيص مساحة لمدخلات النموذج ومخرجاته، ثم نسخ بيانات الإدخال إلى مترابطات المدخلات.

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

قبل

Swift

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

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

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

بعد

Swift

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

Objective-C

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. تعديل رمز معالجة الإخراج

وأخيرًا، بدلاً من الحصول على ناتج النموذج باستخدام دالة ModelOutputs output()، احصل على متّصِل الإخراج من المترجم الفوري وحوّله. البيانات إلى أي هيكل ملائم لحالة الاستخدام لديك.

على سبيل المثال، إذا كنت بصدد التصنيف، يمكنك إجراء تغييرات مثل التالي:

قبل

Swift

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

Objective-C

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

بعد

Swift

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

Objective-C

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