העבר מ-API של מודל מותאם אישית מדור קודם

גרסה 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:

לפני

מָהִיר

pod 'Firebase/MLModelInterpreter', '0.19.0'

Objective-C

pod 'Firebase/MLModelInterpreter', '0.19.0'

לאחר

מָהִיר

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

Objective-C

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

2. צור מתורגמן TensorFlow Lite במקום מתורגמן מודל Firebase

במקום ליצור Firebase 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)

Objective-C

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

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], בצע את השינויים הבאים:

לפני

מָהִיר

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

לאחר

מָהִיר

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. עדכן את קוד הטיפול בפלט

לבסוף, במקום לקבל את הפלט של המודל עם שיטת 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)")
    }
}

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

לאחר

מָהִיר

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