Fügen Sie Firebase zu Ihrer TFLite-basierten iOS-App hinzu

1. Übersicht

Ziele

Mit Firebase ML können Sie Ihr Modell drahtlos bereitstellen. Auf diese Weise können Sie die App-Größe klein halten und das ML-Modell nur bei Bedarf herunterladen, mit mehreren Modellen experimentieren oder Ihr ML-Modell aktualisieren, ohne die gesamte App erneut veröffentlichen zu müssen.

In diesem Codelab konvertieren Sie eine iOS-App mit einem statischen TFLite-Modell in eine App mit einem Modell, das dynamisch von Firebase bereitgestellt wird. Du wirst lernen wie:

  1. Stellen Sie TFLite-Modelle in Firebase ML bereit und greifen Sie über Ihre App darauf zu
  2. Protokollieren Sie modellbezogene Metriken mit Analytics
  3. Wählen Sie aus, welches Modell über Remote Config geladen wird
  4. A/B-Test verschiedener Modelle

Voraussetzungen

Bevor Sie dieses Codelab starten, vergewissern Sie sich, dass Sie Folgendes installiert haben:

  • Xcode 11 (oder höher)
  • CocoaPods 1.9.1 (oder höher)

2. Erstellen Sie ein Firebase-Konsolenprojekt

Fügen Sie dem Projekt Firebase hinzu

  1. Gehen Sie zur Firebase-Konsole .
  2. Wählen Sie „ Neues Projekt erstellen“ und nennen Sie Ihr Projekt „Firebase ML iOS Codelab“.

3. Holen Sie sich das Beispielprojekt

Laden Sie den Code herunter

Beginnen Sie mit dem Klonen des Beispielprojekts und dem Ausführen von pod update im Projektverzeichnis:

git clone https://github.com/FirebaseExtended/codelab-digitclassifier-ios.git
cd codelab-digitclassifier-ios
pod install --repo-update

Wenn Sie Git nicht installiert haben, können Sie das Beispielprojekt auch von der GitHub-Seite herunterladen oder auf diesen Link klicken. Nachdem Sie das Projekt heruntergeladen haben, führen Sie es in Xcode aus und spielen Sie mit dem Ziffernklassifizierer herum, um ein Gefühl dafür zu bekommen, wie es funktioniert.

Firebase einrichten

Folgen Sie der Dokumentation , um ein neues Firebase-Projekt zu erstellen. Nachdem Sie Ihr Projekt erstellt haben, laden Sie die Datei GoogleService-Info.plist Ihres Projekts von der Firebase-Konsole herunter und ziehen Sie sie in das Stammverzeichnis des Xcode-Projekts.

f06cb08d48de7e10.png

Fügen Sie Ihrer Pod-Datei Firebase hinzu und führen Sie die Pod-Installation aus.

pod 'Firebase/MLCommon'
pod 'FirebaseMLModelInterpreter', '0.20.0'

Importieren Sie in der Methode didFinishLaunchingWithOptions Ihres AppDelegate Firebase am Anfang der Datei

import Firebase

Und fügen Sie einen Aufruf hinzu, um Firebase zu konfigurieren.

FirebaseApp.configure()

Führen Sie das Projekt erneut aus, um sicherzustellen, dass die App richtig konfiguriert ist und beim Start nicht abstürzt.

4. Stellen Sie ein Modell in Firebase ML bereit

Das Bereitstellen eines Modells in Firebase ML ist aus zwei Hauptgründen nützlich:

  1. Wir können die App-Installationsgröße klein halten und das Modell nur bei Bedarf herunterladen
  2. Das Modell kann regelmäßig und mit einem anderen Release-Zyklus als die gesamte App aktualisiert werden

Bevor wir das statische Modell in unserer App durch ein dynamisch heruntergeladenes Modell von Firebase ersetzen können, müssen wir es in Firebase ML bereitstellen. Das Modell kann entweder über die Konsole oder programmgesteuert mithilfe des Firebase Admin SDK bereitgestellt werden. In diesem Schritt werden wir über die Konsole bereitstellen.

Der Einfachheit halber verwenden wir das TensorFlow Lite-Modell, das bereits in unserer App enthalten ist. Öffnen Sie zunächst Firebase und klicken Sie im linken Navigationsbereich auf Machine Learning. Navigieren Sie dann zu „Benutzerdefiniert“ und klicken Sie auf die Schaltfläche „Modell hinzufügen“.

Wenn Sie dazu aufgefordert werden, geben Sie dem Modell einen aussagekräftigen Namen wie mnist_v1 und laden Sie die Datei aus dem Codelab-Projektverzeichnis hoch.

3c3c50e6ef12b3b.png

5. Laden Sie das Modell von Firebase ML herunter

Die Entscheidung, wann das Remote-Modell von Firebase in Ihre App heruntergeladen werden soll, kann schwierig sein, da TFLite-Modelle relativ groß werden können. Idealerweise möchten wir vermeiden, das Modell sofort beim Start der App zu laden, denn wenn unser Modell nur für eine Funktion verwendet wird und der Benutzer diese Funktion nie verwendet, haben wir ohne Grund eine erhebliche Menge an Daten heruntergeladen. Wir können auch Download-Optionen festlegen, z. B. Modelle nur abrufen, wenn sie mit WLAN verbunden sind. Wenn Sie sicherstellen möchten, dass das Modell auch ohne Netzwerkverbindung verfügbar ist, sollten Sie das Modell als Backup auch als Teil der App bündeln.

Der Einfachheit halber entfernen wir das standardmäßig gebündelte Modell und laden immer ein Modell von Firebase herunter, wenn die App gestartet wird. Auf diese Weise können Sie beim Ausführen der Ziffernerkennung sicher sein, dass die Inferenz mit dem von Firebase bereitgestellten Modell ausgeführt wird.

Importieren Sie oben in ModelDownloader.swift das Firebase-Modul.

import Firebase

Implementieren Sie dann die folgenden Methoden.

static func downloadModel(named name: String,
                          completion: @escaping (RemoteModel?, DownloadError?) -> Void) {
  guard FirebaseApp.app() != nil else {
    completion(nil, .firebaseNotInitialized)
    return
  }
  guard success == nil && failure == nil else {
    completion(nil, .downloadInProgress)
    return
  }

  let remoteModel = CustomRemoteModel(name: name)
  let conditions = ModelDownloadConditions(allowsCellularAccess: true,
                                           allowsBackgroundDownloading: true)

  success = NotificationCenter.default.addObserver(forName: .firebaseMLModelDownloadDidSucceed,
                                                   object: nil,
                                                   queue: nil) { (notification) in
    defer { success = nil; failure = nil }
    guard let userInfo = notification.userInfo,
        let model = userInfo[ModelDownloadUserInfoKey.remoteModel.rawValue] as? RemoteModel
    else {
      completion(nil, .downloadReturnedEmptyModel)
      return
    }
    guard model.name == name else {
      completion(nil, .downloadReturnedWrongModel)
      return
    }
    completion(model, nil)
  }
  failure = NotificationCenter.default.addObserver(forName: .firebaseMLModelDownloadDidFail,
                                                   object: nil,
                                                   queue: nil) { (notification) in
    defer { success = nil; failure = nil }
    guard let userInfo = notification.userInfo,
        let error = userInfo[ModelDownloadUserInfoKey.error.rawValue] as? Error
    else {
      completion(nil, .mlkitError(underlyingError: DownloadError.unknownError))
      return
    }
    completion(nil, .mlkitError(underlyingError: error))
  }
  ModelManager.modelManager().download(remoteModel, conditions: conditions)
}

// Attempts to fetch the model from disk, downloading the model if it does not already exist.
static func fetchModel(named name: String,
                       completion: @escaping (String?, DownloadError?) -> Void) {
  let remoteModel = CustomRemoteModel(name: name)
  if ModelManager.modelManager().isModelDownloaded(remoteModel) {
    ModelManager.modelManager().getLatestModelFilePath(remoteModel) { (path, error) in
      completion(path, error.map { DownloadError.mlkitError(underlyingError: $0) })
    }
  } else {
    downloadModel(named: name) { (model, error) in
      guard let model = model else {
        let underlyingError = error ?? DownloadError.unknownError
        let compositeError = DownloadError.mlkitError(underlyingError: underlyingError)
        completion(nil, compositeError)
        return
      }
      ModelManager.modelManager().getLatestModelFilePath(model) { (path, pathError) in
        completion(path, error.map { DownloadError.mlkitError(underlyingError: $0) })
      }
    }
  }
}

Ersetzen Sie in ViewController.swift von viewDidLoad den DigitClassifier-Initialisierungsaufruf durch unsere neue Methode zum Herunterladen des Modells.

// Download the model from Firebase
print("Fetching model...")
ModelDownloader.fetchModel(named: "mnist_v1") { (filePath, error) in
  guard let path = filePath else {
    if let error = error {
      print(error)
    }
    return
  }
  print("Model download complete")

  // Initialize a DigitClassifier instance
  DigitClassifier.newInstance(modelPath: path) { result in
    switch result {
    case let .success(classifier):
      self.classifier = classifier
    case .error(_):
      self.resultLabel.text = "Failed to initialize."
    }
  }
}

Führen Sie Ihre App erneut aus. Nach einigen Sekunden sollten Sie ein Protokoll in Xcode sehen, das angibt, dass das Remote-Modell erfolgreich heruntergeladen wurde. Versuchen Sie, eine Ziffer zu zeichnen, und vergewissern Sie sich, dass sich das Verhalten der App nicht geändert hat.

6. Verfolgen Sie das Feedback und die Konvertierung von Benutzern, um die Modellgenauigkeit zu messen

Wir messen die Genauigkeit des Modells, indem wir das Benutzerfeedback zu Modellvorhersagen verfolgen. Wenn ein Benutzer auf „Ja“ klickt, zeigt dies an, dass die Vorhersage korrekt war.

Wir können ein Analytics-Ereignis protokollieren, um die Genauigkeit unseres Modells zu verfolgen. Zuerst müssen wir Analytics zum Podfile hinzufügen, bevor es im Projekt verwendet werden kann:

pod 'Firebase/Analytics'

Importieren Sie dann in ViewController.swift Firebase am Anfang der Datei

import Firebase

Fügen Sie die folgende Codezeile in der Methode correctButtonPressed .

Analytics.logEvent("correct_inference", parameters: nil)

Führen Sie die App erneut aus und zeichnen Sie eine Ziffer. Drücken Sie einige Male auf die Schaltfläche "Ja", um eine Rückmeldung zu senden, dass die Schlussfolgerung korrekt war.

Debug-Analyse

Im Allgemeinen werden von Ihrer App protokollierte Ereignisse über einen Zeitraum von etwa einer Stunde zusammengefasst und zusammen hochgeladen. Dieser Ansatz schont den Akku auf den Geräten der Endbenutzer und reduziert die Netzwerkdatennutzung. Um Ihre Analyseimplementierung zu validieren (und um Ihre Analyse im DebugView-Bericht anzuzeigen), können Sie jedoch den Debug-Modus auf Ihrem Entwicklungsgerät aktivieren, um Ereignisse mit minimaler Verzögerung hochzuladen.

Um den Analytics-Debug-Modus auf Ihrem Entwicklungsgerät zu aktivieren, geben Sie das folgende Befehlszeilenargument in Xcode an:

-FIRDebugEnabled

Führen Sie die App erneut aus und zeichnen Sie eine Ziffer. Drücken Sie einige Male auf die Schaltfläche "Ja", um eine Rückmeldung zu senden, dass die Schlussfolgerung korrekt war. Jetzt können Sie die Protokollereignisse nahezu in Echtzeit über die Debug-Ansicht in der Firebase-Konsole anzeigen. Klicken Sie in der linken Navigationsleiste auf Analytics > DebugView.

5276199a086721fd.png

7. Verfolgen Sie die Inferenzzeit mit Firebase Performance

Beim Testen Ihres Modells reichen Leistungsmetriken auf Entwicklungsgeräten nicht aus, um zu erfassen, wie das Modell in den Händen Ihrer Benutzer funktionieren wird, da es schwierig ist zu sagen, auf welcher Hardware Benutzer Ihre App ausführen werden. Glücklicherweise können Sie mit Firebase Performance die Leistung Ihres Modells auf den Geräten der Benutzer messen, um sich ein besseres Bild von der Leistung Ihres Modells zu machen.

Um die Zeit zu messen, die zum Ausführen der Inferenz benötigt wird, importieren Sie zuerst Firebase in DigitClassifier.swift:

import Firebase

Starten Sie dann eine Leistungsverfolgung in der Methode classify und stoppen Sie die Verfolgung, wenn die Inferenz abgeschlossen ist. Stellen Sie sicher, dass Sie die folgenden Codezeilen innerhalb des Abschlusses DispatchQueue.global.async und nicht direkt unterhalb der Methodendeklaration hinzufügen.

let inferenceTrace = Performance.startTrace(name: "tflite inference")
defer {
  inferenceTrace?.stop()
}

Wenn Sie neugierig sind, können Sie die Debug-Protokollierung über die Anweisungen hier aktivieren, um zu bestätigen, dass Ihre Leistungsspuren protokolliert werden. Nach einer Weile werden die Leistungsspuren auch in Firebase Console sichtbar.

8. Stellen Sie ein zweites Modell in Firebase ML bereit

Wenn Sie eine neue Version Ihres Modells entwickeln, z. B. eine mit einer besseren Modellarchitektur oder eine, die auf einem größeren oder aktualisierten Datensatz trainiert wurde, fühlen wir uns möglicherweise versucht, unser aktuelles Modell durch die neue Version zu ersetzen. Ein Modell, das beim Testen gut abschneidet, muss jedoch nicht unbedingt genauso gut in der Produktion abschneiden. Lassen Sie uns daher A/B-Tests in der Produktion durchführen, um unser ursprüngliches Modell und das neue zu vergleichen.

Aktivieren Sie die Firebase Model Management API

In diesem Schritt aktivieren wir die Firebase Model Management API, um eine neue Version unseres TensorFlow Lite-Modells mithilfe von Python-Code bereitzustellen.

Erstellen Sie einen Bucket zum Speichern Ihrer ML-Modelle

Gehen Sie in Ihrer Firebase-Konsole zu Speicher und klicken Sie auf Erste Schritte. fbbea78f0eb3dc9f.png

Folgen Sie dem Dialog, um Ihren Bucket einzurichten.

19517c0d6d2aa14d.png

Aktivieren Sie die Firebase ML-API

Gehen Sie in der Google Cloud Console zur Firebase ML API-Seite und klicken Sie auf Aktivieren.

2414fd5cced6c984.png Wählen Sie die Digit Classifier-App aus, wenn Sie dazu aufgefordert werden.

Jetzt trainieren wir eine neue Version des Modells mithilfe eines größeren Datasets und stellen es dann programmgesteuert direkt aus dem Trainingsnotebook mithilfe des Firebase Admin SDK bereit.

Laden Sie den privaten Schlüssel für das Dienstkonto herunter

Bevor wir das Firebase Admin SDK verwenden können, müssen wir ein Dienstkonto erstellen. Öffnen Sie das Bedienfeld „Dienstkonten“ der Firebase-Konsole, indem Sie auf diesen Link klicken, und klicken Sie auf die Schaltfläche, um ein neues Dienstkonto für das Firebase Admin SDK zu erstellen. Wenn Sie dazu aufgefordert werden, klicken Sie auf die Schaltfläche Neuen privaten Schlüssel generieren. Wir verwenden den Dienstkontoschlüssel zum Authentifizieren unserer Anforderungen aus dem Colab-Notebook.

c3b95de1e5508516.png

Jetzt können wir das neue Modell trainieren und bereitstellen.

  1. Öffnen Sie dieses Colab-Notizbuch und erstellen Sie eine Kopie davon in Ihrem eigenen Drive.
  2. Führen Sie die erste Zelle „Trainieren eines verbesserten TensorFlow Lite-Modells“ aus, indem Sie auf die Wiedergabeschaltfläche links davon klicken. Dadurch wird ein neues Modell trainiert, was einige Zeit in Anspruch nehmen kann.
  3. Wenn Sie die zweite Zelle ausführen, wird eine Aufforderung zum Hochladen einer Datei erstellt. Laden Sie die JSON-Datei hoch, die Sie beim Erstellen Ihres Dienstkontos von der Firebase Console heruntergeladen haben.

71e847c6a85423b3.png

  1. Führen Sie die letzten beiden Zellen aus.

Nachdem Sie das Colab-Notebook ausgeführt haben, sollten Sie ein zweites Modell in der Firebase-Konsole sehen. Stellen Sie sicher, dass das zweite Modell mnist_v2 .

c316683bb4d75d57.png

9. Wählen Sie ein Modell über Remote Config aus

Da wir nun zwei separate Modelle haben, fügen wir einen Parameter hinzu, um auszuwählen, welches Modell zur Laufzeit heruntergeladen werden soll. Der Wert des Parameters, den der Client erhält, bestimmt, welches Modell der Client herunterlädt. Öffnen Sie zunächst die Firebase-Konsole und klicken Sie im linken Navigationsmenü auf die Schaltfläche Remote Config. Klicken Sie dann auf die Schaltfläche "Parameter hinzufügen".

Benennen Sie den neuen Parameter model_name und geben Sie ihm den Standardwert mnist_v1 . Klicken Sie auf Änderungen veröffentlichen , um die Aktualisierungen anzuwenden. Indem wir den Namen des Modells in den Remote-Konfigurationsparameter einfügen, können wir mehrere Modelle testen, ohne für jedes Modell, das wir testen möchten, einen neuen Parameter hinzuzufügen.

Nachdem Sie den Parameter hinzugefügt haben, sollten Sie ihn in der Konsole sehen:

699b3fd32acce887.png

In unserem Code müssen wir beim Laden des Remote-Modells eine Überprüfung hinzufügen. Wenn wir den Parameter von Remote Config erhalten, rufen wir das Remote-Modell mit dem entsprechenden Namen ab; Andernfalls versuchen wir, mnist_v1 zu laden. Bevor wir Remote Config verwenden können, müssen wir es unserem Projekt hinzufügen, indem wir es als Abhängigkeit in der Poddatei angeben:

pod 'Firebase/RemoteConfig'

Führen Sie die Pod-Installation aus und öffnen Sie das Xcode-Projekt erneut. Implementieren Sie in ModelDownloader.swift die Methode fetchParameterizedModel .

static func fetchParameterizedModel(completion: @escaping (String?, DownloadError?) -> Void) {
  RemoteConfig.remoteConfig().fetchAndActivate { (status, error) in
    DispatchQueue.main.async {
      if let error = error {
        let compositeError = DownloadError.mlkitError(underlyingError: error)
        completion(nil, compositeError)
        return
      }

      let modelName: String
      if let name = RemoteConfig.remoteConfig().configValue(forKey: "model_name").stringValue {
        modelName = name
      } else {
        let defaultName = "mnist_v1"
        print("Unable to fetch model name from config, falling back to default \(defaultName)")
        modelName = defaultName
      }

      fetchModel(named: modelName, completion: completion)
    }
  }
}

Ersetzen Sie schließlich in ViewController.swift den Aufruf fetchModel durch die neue Methode, die wir gerade implementiert haben.

// Download the model from Firebase
print("Fetching model...")
ModelDownloader.fetchParameterizedModel { (filePath, error) in
  guard let path = filePath else {
    if let error = error {
      print(error)
    }
    return
  }
  print("Model download complete")

  // Initialize a DigitClassifier instance
  DigitClassifier.newInstance(modelPath: path) { result in
    switch result {
    case let .success(classifier):
      self.classifier = classifier
    case .error(_):
      self.resultLabel.text = "Failed to initialize."
    }
  }
}

Führen Sie die App erneut aus und stellen Sie sicher, dass das Modell immer noch korrekt geladen wird.

10. A/B-Test der beiden Modelle

Schließlich können wir das integrierte A/B-Testverhalten von Firebase verwenden, um zu sehen, welches unserer beiden Modelle besser abschneidet. Gehen Sie in der Firebase-Konsole zu Analytics -> Events. Wenn das correct_inference Ereignis angezeigt wird, markieren Sie es als „Conversion-Ereignis“, wenn nicht, können Sie zu Analytics -> Conversion-Ereignisse gehen und auf „Neues Conversion-Ereignis erstellen“ klicken und correct_inference.

Gehen Sie nun zu „Remote Config“ in der Firebase-Konsole, wählen Sie die Schaltfläche „A/B-Test“ aus dem Menü „Weitere Optionen“ für den Parameter „model_name“, den wir gerade hinzugefügt haben.

fad5ea36969d2aeb.png

Übernehmen Sie im folgenden Menü den Standardnamen.

d7c006669ace6e40.png

Wählen Sie Ihre App in der Dropdown-Liste aus und ändern Sie die Ausrichtungskriterien auf 50 % der aktiven Benutzer.

6246dd7c660b53fb.png

Wenn Sie das Ereignis correct_inference zuvor als Conversion festlegen konnten, verwenden Sie dieses Ereignis als primäre zu verfolgende Metrik. Andernfalls, wenn Sie nicht warten möchten, bis das Ereignis in Analytics angezeigt wird, können Sie correct_inference manuell hinzufügen.

1ac9c94fb3159271.png

Stellen Sie schließlich auf dem Bild Varianten Ihre Kontrollgruppenvariante auf die Verwendung von mnist_v1 und Ihre Variante A-Gruppe auf die Verwendung von mnist_v2 .

e4510434f8da31b6.png

Klicken Sie auf die Schaltfläche Überprüfen in der unteren rechten Ecke.

Herzlichen Glückwunsch, Sie haben erfolgreich einen A/B-Test für Ihre beiden separaten Modelle erstellt! Der A/B-Test befindet sich derzeit im Entwurfsstadium und kann jederzeit durch Klicken auf die Schaltfläche "Experiment starten" gestartet werden.

Weitere Informationen zu A/B-Tests finden Sie in der Dokumentation zu A/B-Tests .

11. Fazit

In diesem Codelab haben Sie gelernt, wie Sie ein statisch gebündeltes TFLite-Asset in Ihrer App durch ein dynamisch geladenes TFLite-Modell von Firebase ersetzen. Um mehr über TFLite und Firebase zu erfahren, werfen Sie einen Blick auf andere TFLite-Beispiele und die Firebase-Erste-Schritte-Leitfäden.

Eine Frage haben?

Probleme melden