Użyj niestandardowego modelu TensorFlow Lite na platformach Apple

Jeśli Twoja aplikacja korzysta z niestandardowych modeli TensorFlow Lite , możesz użyć Firebase ML do wdrożenia swoich modeli. Wdrażając modele za pomocą Firebase, możesz zmniejszyć początkowy rozmiar pobieranej aplikacji i zaktualizować modele ML aplikacji bez wydawania nowej wersji aplikacji. A dzięki zdalnej konfiguracji i testom A/B możesz dynamicznie obsługiwać różne modele różnym grupom użytkowników.

Warunki wstępne

  • Biblioteka MLModelDownloader jest dostępna tylko dla Swift.
  • TensorFlow Lite działa tylko na urządzeniach z systemem iOS 9 i nowszym.

Modele TensorFlow Lite

Modele TensorFlow Lite to modele ML zoptymalizowane do działania na urządzeniach mobilnych. Aby uzyskać model TensorFlow Lite:

Zanim zaczniesz

Aby używać TensorFlowLite z Firebase, musisz użyć CocoaPods, ponieważ TensorFlowLite obecnie nie obsługuje instalacji za pomocą Swift Package Manager. Zobacz przewodnik instalacji CocoaPods, aby uzyskać instrukcje dotyczące instalowania MLModelDownloader .

Po zainstalowaniu zaimportuj Firebase i TensorFlowLite, aby z nich korzystać.

Szybki

import FirebaseMLModelDownloader
import TensorFlowLite

1. Wdróż swój model

Wdrażaj niestandardowe modele TensorFlow za pomocą konsoli Firebase lub pakietów Firebase Admin Python i Node.js SDK. Zobacz Wdrażanie modeli niestandardowych i zarządzanie nimi .

Po dodaniu niestandardowego modelu do projektu Firebase możesz odwoływać się do modelu w swoich aplikacjach, używając określonej przez siebie nazwy. W dowolnym momencie możesz wdrożyć nowy model TensorFlow Lite i pobrać nowy model na urządzenia użytkowników, wywołując getModel() (patrz poniżej).

2. Pobierz model na urządzenie i zainicjuj interpreter TensorFlow Lite

Aby użyć modelu TensorFlow Lite w swojej aplikacji, najpierw użyj pakietu Firebase ML SDK, aby pobrać najnowszą wersję modelu na urządzenie.

Aby rozpocząć pobieranie modelu, wywołaj metodę getModel() programu do pobierania modeli, określając nazwę przypisaną modelowi podczas jego przesyłania, czy chcesz zawsze pobierać najnowszy model oraz warunki, na jakich chcesz zezwolić na pobieranie.

Możesz wybrać spośród trzech zachowań pobierania:

Typ pobierania Opis
localModel Pobierz model lokalny z urządzenia. Jeśli nie ma dostępnego lokalnego modelu, działa to jak latestModel . Użyj tego typu pobierania, jeśli nie chcesz sprawdzać aktualizacji modelu. Na przykład używasz Zdalnej konfiguracji do pobierania nazw modeli i zawsze przesyłasz modele pod nowymi nazwami (zalecane).
localModelUpdateInBackground Pobierz model lokalny z urządzenia i zacznij aktualizować model w tle. Jeśli nie ma dostępnego lokalnego modelu, działa to jak latestModel .
latestModel Pobierz najnowszy model. Jeśli model lokalny to najnowsza wersja, zwraca model lokalny. W przeciwnym razie pobierz najnowszy model. To zachowanie będzie blokowane do momentu pobrania najnowszej wersji (niezalecane). Użyj tego zachowania tylko w przypadkach, w których wyraźnie potrzebujesz najnowszej wersji.

Należy wyłączyć funkcje związane z modelem — na przykład wyszarzanie lub ukrywanie części interfejsu użytkownika — dopóki nie potwierdzisz, że model został pobrany.

Szybki

let conditions = ModelDownloadConditions(allowsCellularAccess: false)
ModelDownloader.modelDownloader()
    .getModel(name: "your_model",
              downloadType: .localModelUpdateInBackground,
              conditions: conditions) { result in
        switch (result) {
        case .success(let customModel):
            do {
                // Download complete. Depending on your app, you could enable the ML
                // feature, or switch from the local model to the remote model, etc.

                // The CustomModel object contains the local path of the model file,
                // which you can use to instantiate a TensorFlow Lite interpreter.
                let interpreter = try Interpreter(modelPath: customModel.path)
            } catch {
                // Error. Bad model file?
            }
        case .failure(let error):
            // Download was unsuccessful. Don't enable ML features.
            print(error)
        }
}

Wiele aplikacji rozpoczyna zadanie pobierania w swoim kodzie inicjującym, ale możesz to zrobić w dowolnym momencie, zanim będziesz musiał użyć modelu.

3. Wykonaj wnioskowanie na danych wejściowych

Uzyskaj kształty wejściowe i wyjściowe swojego modelu

Interpreter modelu TensorFlow Lite przyjmuje jako dane wejściowe i generuje jako dane wyjściowe jedną lub więcej tablic wielowymiarowych. Te tablice zawierają wartości byte , int , long lub float . Zanim będziesz mógł przekazać dane do modelu lub wykorzystać jego wynik, musisz znać liczbę i wymiary ("kształt") tablic używanych przez Twój model.

Jeśli sam zbudowałeś model lub jeśli format danych wejściowych i wyjściowych modelu jest udokumentowany, możesz już mieć te informacje. Jeśli nie znasz kształtu i typu danych danych wejściowych i wyjściowych modelu, możesz użyć interpretera TensorFlow Lite do sprawdzenia modelu. Na przykład:

Pyton

import tensorflow as tf

interpreter = tf.lite.Interpreter(model_path="your_model.tflite")
interpreter.allocate_tensors()

# Print input shape and type
inputs = interpreter.get_input_details()
print('{} input(s):'.format(len(inputs)))
for i in range(0, len(inputs)):
    print('{} {}'.format(inputs[i]['shape'], inputs[i]['dtype']))

# Print output shape and type
outputs = interpreter.get_output_details()
print('\n{} output(s):'.format(len(outputs)))
for i in range(0, len(outputs)):
    print('{} {}'.format(outputs[i]['shape'], outputs[i]['dtype']))

Przykładowe dane wyjściowe:

1 input(s):
[  1 224 224   3] <class 'numpy.float32'>

1 output(s):
[1 1000] <class 'numpy.float32'>

Uruchom tłumacza

Po określeniu formatu danych wejściowych i wyjściowych modelu pobierz dane wejściowe i wykonaj na nich wszelkie przekształcenia, które są niezbędne do uzyskania danych wejściowych o odpowiednim kształcie dla modelu.

Na przykład, jeśli model przetwarza obrazy, a jego wymiary wejściowe wynoszą [1, 224, 224, 3] wartości zmiennoprzecinkowych, może być konieczne przeskalowanie wartości kolorów obrazu do zakresu zmiennoprzecinkowego, jak w poniższym przykładzie :

Szybki

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 }

var inputData = Data()
for row in 0 ..&lt; 224 {
  for col in 0 ..&lt; 224 {
    let offset = 4 * (row * context.width + col)
    // (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(&amp;bytes, &amp;normalizedRed, elementSize)
    inputData.append(&amp;bytes, count: elementSize)
    memcpy(&amp;bytes, &amp;normalizedGreen, elementSize)
    inputData.append(&amp;bytes, count: elementSize)
    memcpy(&ammp;bytes, &amp;normalizedBlue, elementSize)
    inputData.append(&amp;bytes, count: elementSize)
  }
}

Następnie skopiuj dane wejściowe NSData do interpretera i uruchom go:

Szybki

try interpreter.allocateTensors()
try interpreter.copy(inputData, toInputAt: 0)
try interpreter.invoke()

Możesz uzyskać dane wyjściowe modelu, wywołując metodę output(at:) interpretera. Sposób wykorzystania wyników zależy od używanego modelu.

Na przykład, jeśli przeprowadzasz klasyfikację, w następnym kroku możesz zmapować indeksy wyniku na etykiety, które reprezentują:

Szybki

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: "retrained_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])")
}

Załącznik: Zabezpieczenia modelu

Niezależnie od tego, w jaki sposób udostępniasz modele TensorFlow Lite w Firebase ML, Firebase ML przechowuje je w standardowym serializowanym formacie protobuf w pamięci lokalnej.

W teorii oznacza to, że każdy może skopiować Twój model. Jednak w praktyce większość modeli jest tak specyficzna dla aplikacji i zaciemniona przez optymalizacje, że ryzyko jest podobne do tego, jakie ma konkurencja w przypadku demontażu i ponownego użycia kodu. Niemniej jednak powinieneś być świadomy tego ryzyka, zanim użyjesz niestandardowego modelu w swojej aplikacji.