Se la tua app utilizza modelli TensorFlow Lite personalizzati, puoi utilizzare Firebase ML per distribuire i tuoi modelli. Distribuendo i modelli con Firebase, puoi ridurre le dimensioni del download iniziale della tua app e aggiornare i modelli ML della tua app senza rilasciare una nuova versione della tua app. Inoltre, con Remote Config e A/B Testing, puoi servire in modo dinamico diversi modelli a diversi gruppi di utenti.
Prerequisiti
- La libreria
MLModelDownloader
è disponibile solo per Swift. - TensorFlow Lite funziona solo su dispositivi che utilizzano iOS 9 e versioni successive.
Modelli TensorFlow Lite
I modelli TensorFlow Lite sono modelli ML ottimizzati per l'esecuzione su dispositivi mobili. Per ottenere un modello TensorFlow Lite:
- Usa un modello pre-costruito, come uno dei modelli ufficiali di TensorFlow Lite .
- Converti un modello TensorFlow, un modello Keras o una funzione concreta in TensorFlow Lite.
Prima di iniziare
Per utilizzare TensorFlowLite con Firebase, devi utilizzare CocoaPods poiché TensorFlowLite attualmente non supporta l'installazione con Swift Package Manager. Consulta la guida all'installazione di CocoaPods per istruzioni su come installare MLModelDownloader
.
Una volta installati, importa Firebase e TensorFlowLite per usarli.
Veloce
import FirebaseMLModelDownloader
import TensorFlowLite
1. Distribuisci il tuo modello
Distribuisci i tuoi modelli TensorFlow personalizzati utilizzando la console Firebase o gli SDK Firebase Admin Python e Node.js. Vedere Distribuire e gestire modelli personalizzati .
Dopo aver aggiunto un modello personalizzato al tuo progetto Firebase, puoi fare riferimento al modello nelle tue app usando il nome che hai specificato. In qualsiasi momento, puoi distribuire un nuovo modello TensorFlow Lite e scaricare il nuovo modello sui dispositivi degli utenti chiamando getModel()
(vedi sotto).
2. Scaricare il modello sul dispositivo e inizializzare un interprete TensorFlow Lite
Per utilizzare il tuo 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 che hai assegnato al modello quando lo hai caricato, se vuoi scaricare sempre il modello più recente e le condizioni in cui vuoi consentire il download.
Puoi scegliere tra tre comportamenti di download:
Tipo di download | Descrizione |
---|---|
localModel | Ottieni il modello locale dal dispositivo. Se non è disponibile un modello locale, si comporta come latestModel . Usa questo tipo di download se non sei interessato a controllare gli aggiornamenti del modello. Ad esempio, stai utilizzando Remote Config per recuperare i nomi dei modelli e carichi sempre i modelli con nuovi nomi (opzione consigliata). |
localModelUpdateInBackground | Ottieni il modello locale dal dispositivo e inizia ad aggiornare il modello in background. Se non è disponibile un modello locale, si comporta come latestModel . |
latestModel | Prendi l'ultimo modello. Se il modello locale è la versione più recente, restituisce il modello locale. Altrimenti, scarica l'ultimo modello. Questo comportamento si bloccherà fino al download dell'ultima versione (non consigliato). Utilizzare questo comportamento solo nei casi in cui è necessaria esplicitamente l'ultima versione. |
Dovresti disabilitare la funzionalità relativa al modello, ad esempio disattivare o nascondere parte dell'interfaccia utente, fino a quando non confermi che il modello è stato scaricato.
Veloce
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 in qualsiasi momento prima di dover utilizzare il modello.
3. Eseguire l'inferenza sui dati di input
Ottieni le forme di input e output del tuo modello
L'interprete del modello TensorFlow Lite prende come input e produce come output uno o più array multidimensionali. Questi array contengono valori byte
, int
, long
o float
. Prima di poter passare i dati a un modello o utilizzarne il risultato, è necessario conoscere il numero e le dimensioni ("forma") degli array utilizzati dal modello.
Se hai creato il modello da solo o se il formato di input e output del modello è documentato, potresti già avere queste informazioni. Se non si conosce la forma e il tipo di dati dell'input e dell'output del modello, è possibile utilizzare l'interprete TensorFlow Lite per ispezionare il modello. Per esempio:
Pitone
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']))
Esempio di output:
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 dell'input e dell'output del modello, ottenere i dati di input ed eseguire le trasformazioni sui dati necessarie per ottenere un input della forma corretta per il modello. Ad esempio, se il tuo modello elabora le immagini e il tuo modello ha dimensioni di input di [1, 224, 224, 3]
valori a virgola mobile, potresti dover ridimensionare i valori di colore dell'immagine in un intervallo a virgola mobile come nell'esempio seguente :
Veloce
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 ..< 224 {
for col in 0 ..< 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(&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)
}
}
Quindi, copia il tuo input NSData
sull'interprete ed eseguilo:
Veloce
try interpreter.allocateTensors()
try interpreter.copy(inputData, toInputAt: 0)
try interpreter.invoke()
È possibile ottenere l'output del modello chiamando il metodo output(at:)
dell'interprete. La modalità di utilizzo dell'output dipende dal modello in uso.
Ad esempio, se stai eseguendo la classificazione, come passaggio successivo potresti mappare gli indici del risultato alle etichette che rappresentano:
Veloce
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 a Firebase ML, Firebase ML li archivia nel formato protobuf serializzato standard nell'archiviazione locale.
In teoria, questo significa che chiunque può copiare il tuo modello. Tuttavia, in pratica, la maggior parte dei modelli sono così specifici dell'applicazione e offuscati dalle ottimizzazioni che il rischio è simile a quello dei concorrenti che smontano e riutilizzano il codice. Tuttavia, dovresti essere consapevole di questo rischio prima di utilizzare un modello personalizzato nella tua app.