Utilizzare un modello TensorFlow Lite personalizzato sulle piattaforme Apple

Se la tua app utilizza modelli TensorFlow Lite, puoi utilizzare Firebase ML per eseguire il deployment dei modelli. Di eseguendo il deployment di modelli con Firebase, puoi ridurre le dimensioni l'app e aggiorna i modelli ML dell'app senza rilasciare una nuova versione la tua app. Inoltre, con Remote Config e A/B Testing, puoi eseguire dinamicamente per distribuire modelli diversi a insiemi di utenti differenti.

Prerequisiti

  • La raccolta MLModelDownloader è disponibile solo per Swift.
  • TensorFlow Lite funziona solo sui dispositivi che utilizzano iOS 9 e versioni successive.

Modelli TensorFlow Lite

I modelli TensorFlow Lite sono modelli di ML ottimizzati per l'esecuzione sui dispositivi mobili dispositivi mobili. Per ottenere un modello TensorFlow Lite:

Prima di iniziare

Per utilizzare TensorFlowLite con Firebase, devi utilizzare CocoaPods perché TensorFlowLite attualmente non supporta l'installazione con Swift Package Manager. Consulta la guida all'installazione di CocoaPods per istruzioni su come installare MLModelDownloader.

Dopo l'installazione, importa Firebase e TensorFlowLite per utilizzarli.

Swift

import FirebaseMLModelDownloader
import TensorFlowLite

1. Esegui il deployment del modello

Esegui il deployment dei tuoi modelli TensorFlow personalizzati utilizzando la console Firebase oppure gli SDK Firebase Admin e Node.js. Consulta Distribuire e gestire modelli personalizzati.

Dopo aver aggiunto un modello personalizzato al tuo progetto Firebase, puoi fare riferimento alla tuo modello nelle tue app utilizzando il nome specificato. Puoi eseguire il deployment in qualsiasi momento un nuovo modello TensorFlow Lite e scaricarlo sul sito web degli utenti dispositivi per chiamata al numero getModel() (vedi sotto).

2. Scarica il modello sul dispositivo e inizializza un interprete TensorFlow Lite

Per utilizzare il modello TensorFlow Lite nella tua app, usa prima l'SDK Firebase ML per scaricare l'ultima versione del modello sul dispositivo.

Per avviare il download del modello, chiama il metodo getModel() del downloader del modello, specificando il nome assegnato al modello al momento del caricamento, se vuoi scaricare sempre il modello più recente e le condizioni in cui vuoi consentire il download.

Puoi scegliere fra tre tipi di comportamento di download:

Tipo di download Descrizione
localModel Recupera il modello locale dal dispositivo. Se non è disponibile alcun modello locale, si comporta come latestModel. Usa questa tipo di download se non ti interessa controllare la disponibilità di aggiornamenti del modello. Ad esempio: stai utilizzando Remote Config per recuperare dei modelli, ma i modelli vengono sempre caricati con nuovi nomi (consigliato).
localModelUpdateInBackground Recupera il modello locale dal dispositivo e iniziare ad aggiornare il modello in background. Se non è disponibile alcun modello locale, si comporta come latestModel.
latestModel Scarica il modello più recente. Se il modello locale è all'ultima versione, restituisce il token un modello di machine learning. Altrimenti, scarica l'ultima versione un modello di machine learning. Questo comportamento bloccherà fino a quando viene scaricata l'ultima versione (non consigliato). Utilizza questo comportamento solo in nei casi in cui è necessaria esplicitamente la versione completamente gestita.

Devi disattivare le funzionalità correlate al modello, ad esempio disattivare o nascondere parte dell'interfaccia utente, finché non confermi che il modello è stato scaricato.

Swift

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)
        }
}

Molte app avviano l'attività di download nel codice di inizializzazione, ma puoi farlo quindi in qualsiasi momento prima di dover usare il modello.

3. Esegui l'inferenza sui dati di input

Ottenere le forme di input e output del modello

L'interprete di modelli TensorFlow Lite prende come input e lo produce come output uno o più array multidimensionali. Questi array contengono byte, int, long o float e i relativi valori. Prima di poter passare i dati a un modello o utilizzare il suo risultato, devi conoscere il numero e le dimensioni ("forma") degli array utilizzati dal modello.

Se il modello è stato creato da te o se il formato di input e output del modello è potrebbero essere già state documentate, potresti già avere queste informazioni. Se non conosci la forma e il tipo di dati di input e output del modello, puoi utilizzare Interprete TensorFlow Lite per ispezionare il modello. Ad esempio:

Python

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']))

Output di esempio:

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

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

Esegui l'interprete

Dopo aver determinato il formato di input e output del modello, ottieni il di input ed eseguire le trasformazioni necessarie per ottenere un input della forma giusta per il tuo modello.

Ad esempio, se il tuo modello elabora immagini e ha dimensioni di input di valori di tipo [1, 224, 224, 3] a virgola mobile, potresti dover scalare i valori di colore dell'immagine a un intervallo a virgola mobile come nell'esempio seguente:

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 }

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)
  }
}

Quindi, copia il tuo input NSData nell'interprete ed eseguilo:

Swift

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

Puoi ottenere l'output del modello chiamando il metodo output(at:) dell'interprete. Il modo in cui utilizzi l'output dipende dal modello utilizzato.

Ad esempio, se esegui la classificazione, come passaggio successivo, mappa gli indici del risultato alle etichette che rappresentano:

Swift

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

Appendice: Sicurezza del modello

Indipendentemente da come rendi disponibili i tuoi modelli TensorFlow Lite Firebase ML, Firebase ML li archivia nel formato protobuf serializzato standard in archiviazione locale.

In teoria, ciò significa che chiunque può copiare il tuo modello. Tuttavia, in pratica, la maggior parte dei modelli è così specifica per l'applicazione e offuscata dalle ottimizzazioni che il rischio è simile a quello dei concorrenti che smontano e riutilizzano il codice. Tuttavia, è necessario essere consapevoli di questo rischio prima di utilizzare un modello personalizzato nella tua app.