ไลบรารี Firebase/MLModelInterpreter
เวอร์ชัน 0.20.0 มีgetLatestModelFilePath()
เมธอดใหม่ที่ใช้รับตำแหน่งในอุปกรณ์ของโมเดลที่กำหนดเอง คุณสามารถใช้วิธีนี้เพื่อสร้างอินสแตนซ์ของออบเจ็กต์ TensorFlow Lite
Interpreter
โดยตรง ซึ่งคุณสามารถใช้แทน Wrapper ModelInterpreter
ของ Firebase ได้
นับจากนี้ไป เราขอแนะนำให้ใช้วิธีนี้ เนื่องจากเวอร์ชันของตัวแปล TensorFlow Lite ไม่ได้เชื่อมโยงกับเวอร์ชันของไลบรารี Firebase อีกต่อไป คุณจึงมีความยืดหยุ่นมากขึ้นในการอัปเกรดเป็น TensorFlow Lite เวอร์ชันใหม่เมื่อต้องการ หรือใช้บิลด์ TensorFlow Lite ที่กำหนดเองได้ง่ายขึ้น
หน้านี้แสดงวิธีย้ายข้อมูลจากการใช้ ModelInterpreter
ไปยัง
TensorFlow Lite Interpreter
1. อัปเดตทรัพยากร Dependency ของโปรเจ็กต์
อัปเดต Podfile ของโปรเจ็กต์ให้มีไลบรารี
Firebase/MLModelInterpreter
เวอร์ชัน 0.20.0 (หรือใหม่กว่า) และไลบรารี TensorFlow Lite
ดังนี้
ก่อน
pod 'Firebase/MLModelInterpreter', '0.19.0'
pod 'Firebase/MLModelInterpreter', '0.19.0'
หลัง
pod 'Firebase/MLModelInterpreter', '~> 0.20.0'
pod 'TensorFlowLiteSwift'
pod 'Firebase/MLModelInterpreter', '~> 0.20.0'
pod 'TensorFlowLiteObjC'
2. สร้างตัวแปล TensorFlow Lite แทน Firebase ModelInterpreter
แทนที่จะสร้าง ModelInterpreter
ของ Firebase ให้รับตำแหน่งของโมเดลในอุปกรณ์ด้วย getLatestModelFilePath()
แล้วใช้เพื่อสร้าง TensorFlow Lite
Interpreter
ก่อน
let remoteModel = CustomRemoteModel(
name: "your_remote_model" // The name you assigned in the Firebase console.
)
interpreter = ModelInterpreter.modelInterpreter(remoteModel: remoteModel)
// 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?
}
}
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
ค่า ให้ทําการเปลี่ยนแปลงต่อไปนี้
ก่อน
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
// ...
}
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)
}
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()
ให้รับเอาต์พุตเทนเซอร์จากอินเทอร์พรีเตอร์ แล้วแปลงข้อมูล
เป็นโครงสร้างใดก็ได้ที่สะดวกสำหรับกรณีการใช้งานของคุณ
เช่น หากคุณกำลังทำการจัดประเภท คุณอาจทำการเปลี่ยนแปลงดังต่อไปนี้
ก่อน
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)")
}
}
// 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)
}
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]);
}