Firebase/MLModelInterpreter
라이브러리 0.20.0 버전에는 커스텀 모델 기기의 위치를 가져오는 새로운 getLatestModelFilePath()
메서드가 도입되었습니다. 이 메서드를 사용하면 TensorFlow Lite Interpreter
객체를 직접 인스턴스화할 수 있습니다. 이 객체는 Firebase의 ModelInterpreter
래퍼 대신 사용할 수 있습니다.
앞으로는 이 방법이 권장됩니다. TensorFlow Lite 인터프리터 버전이 더 이상 Firebase 라이브러리 버전과 결합되어 있지 않으므로 필요할 경우 새 버전의 TensorFlow Lite로 자유롭게 업그레이드하거나 커스텀 TensorFlow Lite 빌드를 더 간편하게 사용할 수 있습니다.
이 페이지에서는 ModelInterpreter
에서 TensorFlow Lite Interpreter
로 마이그레이션하는 방법을 보여줍니다.
1. 프로젝트 종속 항목 업데이트
Firebase/MLModelInterpreter
라이브러리 0.20.0 이상 버전 및 TensorFlow Lite 라이브러리를 포함하도록 프로젝트의 Podfile을 업데이트합니다.
이전
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. Firebase ModelInterpreter 대신 TensorFlow Lite 인터프리터 만들기
Firebase ModelInterpreter
를 만드는 대신 getLatestModelFilePath()
을 사용하여 기기의 모델 위치를 가져와서 TensorFlow Lite Interpreter
를 만드는 데 사용합니다.
이전
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()
를 호출하여 모델의 입력과 출력을 위한 공간을 할당한 다음 입력 데이터를 입력 텐서에 복사합니다.
예를 들어 모델의 입력 모양이 [1 224 224 3] float
값이고 출력 모양이 [1 1000] float
인 경우 다음과 같이 변경합니다.
이전
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]);
}