Sie können ML Kit verwenden, um auf dem Gerät Inferenzen mit einer TensorFlow Lite-Modell verfügbar.
ML Kit kann TensorFlow Lite-Modelle nur auf Geräten mit iOS 9 und höher verwenden.
Hinweis
- Wenn Sie Firebase Ihrer App noch nicht hinzugefügt haben, folgen Sie der Anleitung im Einstiegsleitfaden.
- Fügen Sie die ML Kit-Bibliotheken in Ihre Podfile ein:
Nachdem Sie die Pods Ihres Projekts installiert oder aktualisiert haben, öffnen Sie unbedingt Ihren Xcode mithilfe derpod 'Firebase/MLModelInterpreter', '6.25.0'
.xcworkspace
zu erstellen. - Importieren Sie Firebase in Ihre App:
Swift
import Firebase
Objective-C
@import Firebase;
- Konvertieren Sie das gewünschte TensorFlow-Modell in das TensorFlow Lite-Format. Weitere Informationen finden Sie unter TOCO: TensorFlow Lite Optimization Converter.
Modell hosten oder bündeln
Bevor Sie ein TensorFlow Lite-Modell für Inferenzen in Ihrer App verwenden können, müssen Sie muss das Modell für ML Kit verfügbar gemacht werden. ML Kit kann TensorFlow Lite verwenden Modelle, die remote mit Firebase gehostet werden und mit dem App-Binärprogramm gebündelt sind, oder beides.
Wenn Sie ein Modell auf Firebase hosten, können Sie es aktualisieren, ohne ein neue App-Version und du kannst Remote Config und A/B Testing für Folgendes verwenden: verschiedenen Gruppen von Nutzern dynamisch verschiedene Modelle bereitstellen.
Wenn Sie das Modell nur durch das Hosting mit Firebase und nicht mit Ihrer App bündeln, können Sie die anfängliche Downloadgröße Ihrer App reduzieren. Wenn das Modell nicht in Ihrer App enthalten ist, modellbezogene Funktionen sind erst verfügbar, wenn Ihre App die um ein neues Modell zu erstellen.
Wenn Sie Ihr Modell mit Ihrer App bündeln, können Sie dafür sorgen, dass die ML-Features Ihrer App funktionieren auch, wenn das von Firebase gehostete Modell nicht verfügbar ist.
Modelle in Firebase hosten
So hosten Sie Ihr TensorFlow Lite-Modell in Firebase:
- Klicken Sie in der Firebase-Konsole im Bereich ML Kit auf den Tab Benutzerdefiniert.
- Klicken Sie auf Benutzerdefiniertes Modell hinzufügen oder Weiteres Modell hinzufügen.
- Geben Sie einen Namen an, mit dem das Modell in Firebase identifiziert wird
und laden Sie dann die TensorFlow Lite-Modelldatei hoch, die in der Regel auf
.tflite
oder.lite
).
Nachdem Sie Ihrem Firebase-Projekt ein benutzerdefiniertes Modell hinzugefügt haben, können Sie auf das Modell in Ihren Apps unter Verwendung des von Ihnen angegebenen Namens. Sie können jederzeit ein neues TensorFlow Lite-Modell an. Ihre App lädt das neue Modell verwenden, wenn die App das nächste Mal neu gestartet wird. Sie können festlegen, Bedingungen erfüllt, damit Ihre App versucht, das Modell zu aktualisieren (siehe unten).
Modelle mit einer App bündeln
Wenn Sie Ihr TensorFlow Lite-Modell mit Ihrer App bündeln möchten, fügen Sie die Modelldatei (normalerweise endet sie auf .tflite
oder .lite
) Ihrem Xcode-Projekt hinzu. Achten Sie dabei darauf, Bundle-Ressourcen kopieren auszuwählen. Die Modelldatei wird in das App-Bundle aufgenommen und für ML Kit verfügbar gemacht.
Modell laden
Um Ihr TensorFlow Lite-Modell in Ihrer App zu verwenden, konfigurieren Sie zuerst ML Kit mit Standorte, an denen Ihr Modell verfügbar ist: remote mit Firebase, in lokalen Speicher oder beides. Wenn Sie sowohl ein lokales als auch ein Remote-Modell angeben, können Sie das Remote-Modell verwenden, wenn es verfügbar ist. Andernfalls wird das lokal gespeicherte Modell verwendet.
Von Firebase gehostetes Modell konfigurieren
Wenn Sie Ihr Modell bei Firebase gehostet haben, erstellen Sie ein CustomRemoteModel
-Objekt.
Geben Sie den Namen an, den Sie dem Modell bei der Veröffentlichung zugewiesen haben:
Swift
let remoteModel = CustomRemoteModel(
name: "your_remote_model" // The name you assigned in the Firebase console.
)
Objective-C
// Initialize using the name you assigned in the Firebase console.
FIRCustomRemoteModel *remoteModel =
[[FIRCustomRemoteModel alloc] initWithName:@"your_remote_model"];
Starten Sie dann den Modelldownload und geben Sie die Bedingungen an, unter denen Sie den Download erlauben möchten. Wenn das Modell nicht auf dem Gerät installiert ist oder ein neueres Modell Version des Modells verfügbar ist, lädt die Aufgabe asynchron das aus Firebase verwenden:
Swift
let downloadConditions = ModelDownloadConditions(
allowsCellularAccess: true,
allowsBackgroundDownloading: true
)
let downloadProgress = ModelManager.modelManager().download(
remoteModel,
conditions: downloadConditions
)
Objective-C
FIRModelDownloadConditions *downloadConditions =
[[FIRModelDownloadConditions alloc] initWithAllowsCellularAccess:YES
allowsBackgroundDownloading:YES];
NSProgress *downloadProgress =
[[FIRModelManager modelManager] downloadRemoteModel:remoteModel
conditions:downloadConditions];
Viele Apps starten die Downloadaufgabe in ihrem Initialisierungscode, Sie können dies aber auch jederzeit tun, bevor Sie das Modell verwenden müssen.
Lokales Modell konfigurieren
Wenn Sie das Modell mit Ihrer App gebündelt haben, erstellen Sie ein CustomLocalModel
-Objekt.
und geben den Dateinamen des TensorFlow Lite-Modells an:
Swift
guard let modelPath = Bundle.main.path(
forResource: "your_model",
ofType: "tflite",
inDirectory: "your_model_directory"
) else { /* Handle error. */ }
let localModel = CustomLocalModel(modelPath: modelPath)
Objective-C
NSString *modelPath = [NSBundle.mainBundle pathForResource:@"your_model"
ofType:@"tflite"
inDirectory:@"your_model_directory"];
FIRCustomLocalModel *localModel =
[[FIRCustomLocalModel alloc] initWithModelPath:modelPath];
Interpreter aus dem Modell erstellen
Nachdem Sie die Modellquellen konfiguriert haben, erstellen Sie
ModelInterpreter
-Objekt aus einem von ihnen.
Wenn Sie nur ein lokal gebündeltes Modell haben, müssen Sie nur die CustomLocalModel
Objekt für modelInterpreter(localModel:)
:
Swift
let interpreter = ModelInterpreter.modelInterpreter(localModel: localModel)
Objective-C
FIRModelInterpreter *interpreter =
[FIRModelInterpreter modelInterpreterForLocalModel:localModel];
Wenn Sie ein extern gehostetes Modell haben, müssen Sie prüfen,
die Sie vor der Ausführung heruntergeladen haben. Sie können den Status des Modelldownloads
mit der Methode isModelDownloaded(remoteModel:)
des Modellmanagers.
Sie müssen dies nur vor der Ausführung des Dolmetschers bestätigen. Wenn Sie
ein remote gehostetes und ein lokal gebündeltes Modell haben,
sinnvoll, diese Prüfung bei der Instanziierung von ModelInterpreter
durchzuführen:
Interpreter aus dem Remote-Modell, falls es heruntergeladen wurde, und aus dem
modellieren.
Swift
var interpreter: ModelInterpreter
if ModelManager.modelManager().isModelDownloaded(remoteModel) {
interpreter = ModelInterpreter.modelInterpreter(remoteModel: remoteModel)
} else {
interpreter = ModelInterpreter.modelInterpreter(localModel: localModel)
}
Objective-C
FIRModelInterpreter *interpreter;
if ([[FIRModelManager modelManager] isModelDownloaded:remoteModel]) {
interpreter = [FIRModelInterpreter modelInterpreterForRemoteModel:remoteModel];
} else {
interpreter = [FIRModelInterpreter modelInterpreterForLocalModel:localModel];
}
Wenn Sie nur ein remote gehostetes Modell haben, sollten Sie die modellbezogenen wie z. B. das Ausgrauen oder Ausblenden eines Teils der Benutzeroberfläche, bis bestätigen Sie, dass das Modell heruntergeladen wurde.
Sie können den Downloadstatus des Modells abrufen, indem Sie Beobachter an das Standard-Benachrichtigungscenter anhängen. Achten Sie darauf, im Beobachter einen schwachen Verweis auf self
zu verwenden
-Block, da Downloads einige Zeit in Anspruch nehmen können und das ursprüngliche Objekt
wird erst wieder freigegeben, wenn der Download abgeschlossen ist. Beispiel:
Swift
NotificationCenter.default.addObserver( forName: .firebaseMLModelDownloadDidSucceed, object: nil, queue: nil ) { [weak self] notification in guard let strongSelf = self, let userInfo = notification.userInfo, let model = userInfo[ModelDownloadUserInfoKey.remoteModel.rawValue] as? RemoteModel, model.name == "your_remote_model" else { return } // The model was downloaded and is available on the device } NotificationCenter.default.addObserver( forName: .firebaseMLModelDownloadDidFail, object: nil, queue: nil ) { [weak self] notification in guard let strongSelf = self, let userInfo = notification.userInfo, let model = userInfo[ModelDownloadUserInfoKey.remoteModel.rawValue] as? RemoteModel else { return } let error = userInfo[ModelDownloadUserInfoKey.error.rawValue] // ... }
Objective-C
__weak typeof(self) weakSelf = self; [NSNotificationCenter.defaultCenter addObserverForName:FIRModelDownloadDidSucceedNotification object:nil queue:nil usingBlock:^(NSNotification *_Nonnull note) { if (weakSelf == nil | note.userInfo == nil) { return; } __strong typeof(self) strongSelf = weakSelf; FIRRemoteModel *model = note.userInfo[FIRModelDownloadUserInfoKeyRemoteModel]; if ([model.name isEqualToString:@"your_remote_model"]) { // The model was downloaded and is available on the device } }]; [NSNotificationCenter.defaultCenter addObserverForName:FIRModelDownloadDidFailNotification object:nil queue:nil usingBlock:^(NSNotification *_Nonnull note) { if (weakSelf == nil | note.userInfo == nil) { return; } __strong typeof(self) strongSelf = weakSelf; NSError *error = note.userInfo[FIRModelDownloadUserInfoKeyError]; }];
Eingabe und Ausgabe des Modells angeben
Konfigurieren Sie als Nächstes die Ein- und Ausgabeformate des Modellinterpreters.
Ein TensorFlow Lite-Modell übernimmt als Eingabe und erzeugt als Ausgabe eine oder mehrere
multidimensionale Arrays. Diese Arrays enthalten entweder byte
-, int
-, long
- oder float
-Werte. Sie müssen ML Kit mit der Anzahl und den Dimensionen („Form“) der Arrays konfigurieren, die in Ihrem Modell verwendet werden.
Wenn Sie die Form und den Datentyp der Eingabe und Ausgabe Ihres Modells nicht kennen, können Sie Ihr Modell mit dem Python-Interpreter von TensorFlow Lite überprüfen. Beispiel:
import tensorflow as tf interpreter = tf.lite.Interpreter(model_path="my_model.tflite") interpreter.allocate_tensors() # Print input shape and type print(interpreter.get_input_details()[0]['shape']) # Example: [1 224 224 3] print(interpreter.get_input_details()[0]['dtype']) # Example: <class 'numpy.float32'> # Print output shape and type print(interpreter.get_output_details()[0]['shape']) # Example: [1 1000] print(interpreter.get_output_details()[0]['dtype']) # Example: <class 'numpy.float32'>
Nachdem Sie das Format der Eingabe und Ausgabe Ihres Modells festgelegt haben, konfigurieren Sie den Modellinterpreter Ihrer App, indem Sie ein ModelInputOutputOptions
-Objekt erstellen.
Ein Gleitkomma-Bildklassifizierungsmodell kann beispielsweise
Nx224x224x3-Array mit Float
-Werten, die eine Gruppe von
N 224 x 224 Drei-Kanal-Bilder (RGB) und produzieren als Ausgabe eine Liste mit
1.000 Float
-Werte, die jeweils die Wahrscheinlichkeit darstellen, zu der das Bild gehört
einer der 1.000 Kategorien,
die das Modell vorhersagt.
Für ein solches Modell konfigurieren Sie die Eingabe und Ausgabe des Modellinterpreters wie unten gezeigt:
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)") }
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; }
Inferenzen auf Eingabedaten durchführen
Um schließlich eine Inferenz mit dem Modell durchzuführen, rufen Sie Ihre Eingabedaten ab, führen Sie
Transformationen der Daten, die für Ihr Modell erforderlich sein könnten, und erstellen ein
Data
-Objekt, das die Daten enthält.
Beispiel: Ihr Modell verarbeitet Bilder und hat Eingabeabmessungen
von [BATCH_SIZE, 224, 224, 3]
Gleitkommawerten ist, müssen Sie möglicherweise skalieren
die Farbwerte des Bildes in einen Gleitkommabereich, wie im folgenden Beispiel gezeigt:
Swift
let image: CGImage = // Your input image guard let context = CGContext( data: nil, width: image.width, height: image.height, bitsPerComponent: 8, bytesPerRow: image.width * 4, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue ) else { return false } context.draw(image, in: CGRect(x: 0, y: 0, width: image.width, height: image.height)) guard let imageData = context.data else { return false } let inputs = ModelInputs() var inputData = Data() do { for row in 0 ..< 224 { for col in 0 ..< 224 { let offset = 4 * (col * context.width + row) // (Ignore offset 0, the unused alpha channel) let red = imageData.load(fromByteOffset: offset+1, as: UInt8.self) let green = imageData.load(fromByteOffset: offset+2, as: UInt8.self) let blue = imageData.load(fromByteOffset: offset+3, as: UInt8.self) // Normalize channel values to [0.0, 1.0]. This requirement varies // by model. For example, some models might require values to be // normalized to the range [-1.0, 1.0] instead, and others might // require fixed-point values or the original bytes. var normalizedRed = Float32(red) / 255.0 var normalizedGreen = Float32(green) / 255.0 var normalizedBlue = Float32(blue) / 255.0 // Append normalized values to Data object in RGB order. let elementSize = MemoryLayout.size(ofValue: normalizedRed) var bytes = [UInt8](repeating: 0, count: elementSize) memcpy(&bytes, &normalizedRed, elementSize) inputData.append(&bytes, count: elementSize) memcpy(&bytes, &normalizedGreen, elementSize) inputData.append(&bytes, count: elementSize) memcpy(&ammp;bytes, &normalizedBlue, elementSize) inputData.append(&bytes, count: elementSize) } } try inputs.addInput(inputData) } catch let error { print("Failed to add input: \(error)") }
Objective-C
CGImageRef image = // Your input image long imageWidth = CGImageGetWidth(image); long imageHeight = CGImageGetHeight(image); CGContextRef context = CGBitmapContextCreate(nil, imageWidth, imageHeight, 8, imageWidth * 4, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaNoneSkipFirst); CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight), image); UInt8 *imageData = CGBitmapContextGetData(context); FIRModelInputs *inputs = [[FIRModelInputs alloc] init]; NSMutableData *inputData = [[NSMutableData alloc] initWithCapacity:0]; for (int row = 0; row < 224; row++) { for (int col = 0; col < 224; col++) { long offset = 4 * (col * imageWidth + row); // Normalize channel values to [0.0, 1.0]. This requirement varies // by model. For example, some models might require values to be // normalized to the range [-1.0, 1.0] instead, and others might // require fixed-point values or the original bytes. // (Ignore offset 0, the unused alpha channel) Float32 red = imageData[offset+1] / 255.0f; Float32 green = imageData[offset+2] / 255.0f; Float32 blue = imageData[offset+3] / 255.0f; [inputData appendBytes:&red length:sizeof(red)]; [inputData appendBytes:&green length:sizeof(green)]; [inputData appendBytes:&blue length:sizeof(blue)]; } } [inputs addInput:inputData error:&error]; if (error != nil) { return nil; }
Nachdem Sie Ihre Modelleingabe vorbereitet und bestätigt haben, dass das Modell
verfügbar), übergeben Sie die Eingabe- und
Eingabe-/Ausgabeoptionen an
run(inputs:options:completion:)
Ihres Modellinterpreters
.
Swift
interpreter.run(inputs: inputs, options: ioOptions) { outputs, error in guard error == nil, let outputs = outputs else { return } // Process outputs // ... }
Objective-C
[interpreter runWithInputs:inputs options:ioOptions completion:^(FIRModelOutputs * _Nullable outputs, NSError * _Nullable error) { if (error != nil || outputs == nil) { return; } // Process outputs // ... }];
Sie können die Ausgabe erhalten, indem Sie die Methode output(index:)
des Objekts aufrufen,
zurückgegeben. Beispiel:
Swift
// Get first and only output of inference with a batch size of 1 let output = try? outputs.output(index: 0) as? [[NSNumber]] let probabilities = output??[0]
Objective-C
// Get first and only output of inference with a batch size of 1 NSError *outputError; NSArray *probabilites = [outputs outputAtIndex:0 error:&outputError][0];
Wie Sie die Ausgabe verwenden, hängt vom verwendeten Modell ab.
Bei der Klassifizierung könnten Sie als nächsten Schritt ordnen Sie die Indexe des Ergebnisses den von ihnen dargestellten Labels zu. Angenommen, Sie haben eine Textdatei mit Beschriftungszeichenfolgen für jede Kategorie Ihres Modells könnten Sie kartografieren, den Ausgabewahrscheinlichkeiten hinzu, indem Sie eine Methode wie Folgendes:
Swift
guard let labelPath = Bundle.main.path(forResource: "retrained_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
NSError *labelReadError = nil; NSString *labelPath = [NSBundle.mainBundle pathForResource:@"retrained_labels" ofType:@"txt"]; NSString *fileContents = [NSString stringWithContentsOfFile:labelPath encoding:NSUTF8StringEncoding error:&labelReadError]; if (labelReadError != 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); }
Anhang: Modellsicherheit
Unabhängig davon, wie Sie Ihre TensorFlow Lite-Modelle für ML Kit verfügbar machen, speichert ML Kit sie im standardmäßigen serialisierten Protobuf-Format im lokalen Speicher.
Theoretisch kann also jeder Ihr Modell kopieren. Sie können jedoch in der Praxis sind die meisten Modelle so anwendungsspezifisch Optimierungen vorzunehmen, bei denen das Risiko dem der Konkurrenz beim Auseinanderbauen und Ihren Code wiederverwenden. Sie sollten sich jedoch über dieses Risiko im Klaren sein, bevor Sie ein benutzerdefiniertes Modell in Ihrer App erstellen.