La versión 0.20.0 de la biblioteca de Firebase/MLModelInterpreter
incluye un nuevo método getLatestModelFilePath()
, que obtiene la ubicación de los modelos personalizados en el dispositivo. Puedes usar este método para crear una instancia directa de un objeto Interpreter
de TensorFlow Lite, que puedes usar en lugar del wrapper ModelInterpreter
de Firebase.
En el futuro, este será el enfoque preferido. Debido a que la versión del intérprete de TensorFlow Lite ya no está vinculada a la versión de biblioteca de Firebase, tienes más flexibilidad para actualizar a las nuevas versiones de TensorFlow Lite cuando lo deseas, o usar con facilidad compilaciones personalizadas de TensorFlow Lite.
En esta página, se muestra cómo migrar de usar ModelInterpreter
a usar el Interpreter
de TensorFlow Lite.
1. Actualiza las dependencias del proyecto
Actualiza el Podfile de tu proyecto para que incluya la versión 0.20.0 de la biblioteca Firebase/MLModelInterpreter
(o posterior) y la biblioteca de TensorFlow Lite:
Antes
Swift
pod 'Firebase/MLModelInterpreter', '0.19.0'
Objective-C
pod 'Firebase/MLModelInterpreter', '0.19.0'
Después
Swift
pod 'Firebase/MLModelInterpreter', '~> 0.20.0'
pod 'TensorFlowLiteSwift'
Objective-C
pod 'Firebase/MLModelInterpreter', '~> 0.20.0'
pod 'TensorFlowLiteObjC'
2. Crea un intérprete de TensorFlow Lite en lugar de un ModelInterpreter de Firebase
En lugar de crear un ModelInterpreter
de Firebase, obtén la ubicación del modelo en el dispositivo con getLatestModelFilePath()
y úsalo para crear un Interpreter
de TensorFlow Lite.
Antes
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];
Después
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. Actualiza el código de preparación de entrada y salida
Con ModelInterpreter
, especificas las formas de entrada y salida del modelo si pasas un objeto ModelInputOutputOptions
al intérprete cuando lo ejecutas.
En el caso del intérprete de TensorFlow Lite, en su lugar, debes llamar a allocateTensors()
a fin de asignar espacio para los datos de entrada y salida del modelo y, luego, copiar los datos de entrada a los tensores de entrada.
Por ejemplo, si tu modelo tiene una forma de entrada de valores de float
[1 224 224 3] y una forma de salida de valores de float
[1 1,000], realiza estos cambios:
Antes
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
// ...
}];
Después
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. Actualiza el código de administración de salida
Por último, en lugar de obtener los datos de salida del modelo con el método output()
del objeto ModelOutputs
, obtén el tensor de salida del intérprete y convierte sus datos en la estructura que sea conveniente para tu caso de uso.
Por ejemplo, si realizas la clasificación, puedes hacer cambios como el siguiente:
Antes
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);
}
Después
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]);
}