获取我们在 Firebase 峰会上发布的所有信息,了解 Firebase 可如何帮助您加快应用开发速度并满怀信心地运行应用。了解详情

Migrieren Sie von der Legacy-API für benutzerdefinierte Modelle

Version 0.20.0 der Firebase/MLModelInterpreter Bibliothek führt eine neue getLatestModelFilePath() -Methode ein, die den Speicherort benutzerdefinierter Modelle auf dem Gerät abruft. Sie können diese Methode verwenden, um ein TensorFlow Lite Interpreter -Objekt direkt zu instanziieren, das Sie anstelle des ModelInterpreter -Wrappers von Firebase verwenden können.

Für die Zukunft ist dies der bevorzugte Ansatz. Da die TensorFlow Lite-Interpreterversion nicht mehr mit der Firebase-Bibliotheksversion gekoppelt ist, haben Sie mehr Flexibilität, um auf neue Versionen von TensorFlow Lite zu aktualisieren, wann immer Sie möchten, oder einfacher benutzerdefinierte TensorFlow Lite-Builds zu verwenden.

Diese Seite zeigt, wie Sie von der Verwendung von ModelInterpreter zum TensorFlow Lite Interpreter migrieren können.

1. Projektabhängigkeiten aktualisieren

Aktualisieren Sie die Podfile Ihres Projekts so, dass sie Version 0.20.0 der Firebase/MLModelInterpreter Bibliothek (oder neuer) und der TensorFlow Lite-Bibliothek enthält:

Vor

Schnell

pod 'Firebase/MLModelInterpreter', '0.19.0'

Ziel c

pod 'Firebase/MLModelInterpreter', '0.19.0'

Nach

Schnell

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

Ziel c

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

2. Erstellen Sie einen TensorFlow Lite-Interpreter anstelle eines Firebase ModelInterpreter

Anstatt einen Firebase ModelInterpreter zu erstellen, rufen Sie den Standort des Modells auf dem Gerät mit getLatestModelFilePath() und verwenden Sie ihn, um einen TensorFlow Lite Interpreter zu erstellen.

Vor

Schnell

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

Ziel c

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

Nach

Schnell

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

Ziel 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. Aktualisieren Sie den Eingabe- und Ausgabevorbereitungscode

Mit ModelInterpreter geben Sie die Eingabe- und Ausgabeformen des Modells an, indem Sie ein ModelInputOutputOptions Objekt an den Interpreter übergeben, wenn Sie ihn ausführen.

Für den TensorFlow Lite-Interpreter rufen Sie stattdessen allocateTensors() auf, um Platz für die Eingabe und Ausgabe des Modells zuzuweisen, und kopieren dann Ihre Eingabedaten in die Eingabetensoren.

Wenn Ihr Modell beispielsweise ein Eingabe-Shape von [1 224 224 3] float -Werten und ein Ausgabe-Shape von [1 1000] float -Werten hat, nehmen Sie diese Änderungen vor:

Vor

Schnell

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

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

Nach

Schnell

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

Ziel 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. Aktualisieren Sie den Ausgabehandhabungscode

Anstatt schließlich die Ausgabe des Modells mit der Methode output() des ModelOutputs -Objekts abzurufen, holen Sie sich den Ausgabetensor vom Interpreter und konvertieren Sie seine Daten in eine beliebige Struktur, die für Ihren Anwendungsfall geeignet ist.

Wenn Sie beispielsweise eine Klassifizierung vornehmen, können Sie Änderungen wie die folgenden vornehmen:

Vor

Schnell

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

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

Nach

Schnell

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

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