ML Kit vous permet d'effectuer une inférence sur l'appareil TensorFlow Lite.
ML Kit ne peut utiliser les modèles TensorFlow Lite que sur les appareils exécutant iOS 9 ou une version ultérieure.
Avant de commencer
- Si vous n'avez pas encore ajouté Firebase à votre application, suivez les les étapes décrites dans le guide de démarrage.
- Incluez les bibliothèques ML Kit dans votre Podfile:
Après avoir installé ou mis à jour les pods de votre projet, ouvrez votre Xcode projet à l'aide de sonpod 'Firebase/MLModelInterpreter', '6.25.0'
.xcworkspace
. - Dans votre application, importez Firebase:
Swift
import Firebase
Objective-C
@import Firebase;
- Convertissez le modèle TensorFlow que vous souhaitez utiliser au format TensorFlow Lite. Voir TOCO: convertisseur d'optimisation TensorFlow Lite.
Hébergez ou regroupez votre modèle
Avant de pouvoir utiliser un modèle TensorFlow Lite pour l'inférence dans votre application, vous devez doit mettre le modèle à la disposition de ML Kit. ML Kit peut utiliser TensorFlow Lite des modèles hébergés à distance à l'aide de Firebase, groupés avec le binaire de l'application, ou les deux.
En hébergeant un modèle sur Firebase, vous pouvez le mettre à jour sans publier de nouvelle version de l'appli. Vous pouvez utiliser Remote Config et A/B Testing pour diffuser dynamiquement différents modèles à différents ensembles d'utilisateurs.
Si vous choisissez de fournir le modèle uniquement en l'hébergeant avec Firebase, et non de l'intégrer à votre application, vous pouvez réduire la taille de téléchargement initiale de votre application. Gardez toutefois à l'esprit que si le modèle n'est pas fourni avec votre application, liées au modèle ne seront pas disponibles tant que votre application n'aura pas téléchargé pour la première fois.
En regroupant votre modèle avec votre application, vous pouvez vous assurer que ses fonctionnalités de ML continuent de fonctionner lorsque le modèle hébergé sur Firebase n'est pas disponible.
Héberger des modèles sur Firebase
Pour héberger votre modèle TensorFlow Lite sur Firebase:
- Dans la section ML Kit de la console Firebase, cliquez sur l'onglet Personnalisé.
- Cliquez sur Ajouter un modèle personnalisé (ou Ajouter un autre modèle).
- Spécifiez un nom qui permettra d'identifier votre modèle dans Firebase
projet, puis importez le fichier de modèle TensorFlow Lite (qui se termine généralement par
.tflite
ou.lite
).
Après avoir ajouté un modèle personnalisé à votre projet Firebase, vous pouvez référencer le dans vos applications en utilisant le nom spécifié. À tout moment, vous pouvez importer un nouveau modèle TensorFlow Lite, et votre application le téléchargera commencer à l'utiliser au prochain redémarrage de l'application. Vous pouvez définir l'appareil requises pour que votre application tente de mettre à jour le modèle (voir ci-dessous).
Regrouper les modèles avec une application
Pour empaqueter le modèle TensorFlow Lite avec votre application, ajoutez le fichier de modèle (généralement
se terminant par .tflite
ou .lite
) vers votre projet Xcode, en veillant à sélectionner
Dans ce cas, copiez les ressources du groupe. Le fichier de modèle sera inclus dans
app bundle et disponible pour ML Kit.
Charger le modèle
Pour utiliser votre modèle TensorFlow Lite dans votre application, configurez d'abord ML Kit avec les emplacements où votre modèle est disponible : à distance à l'aide de Firebase, dans l'espace de stockage local ou les deux. Si vous spécifiez à la fois un modèle local et un modèle distant, vous pouvez utilisez le modèle distant s'il est disponible et utilisez stocké en local si le modèle distant n'est pas disponible.
Configurer un modèle hébergé sur Firebase
Si vous avez hébergé votre modèle avec Firebase, créez un objet CustomRemoteModel
,
en spécifiant le nom que vous avez attribué au modèle lors de sa publication:
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"];
Ensuite, démarrez la tâche de téléchargement du modèle, en spécifiant les conditions dans lesquelles si vous souhaitez autoriser le téléchargement. Si le modèle ne figure pas sur l'appareil, ou si un modèle plus récent du modèle est disponible, la tâche téléchargera de manière asynchrone depuis Firebase:
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];
De nombreuses applications lancent la tâche de téléchargement dans leur code d'initialisation, mais vous pouvez avant de devoir utiliser le modèle.
Configurer un modèle local
Si vous avez regroupé le modèle avec votre application, créez un objet CustomLocalModel
.
en spécifiant le nom de fichier du modèle TensorFlow Lite:
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];
Créer un interpréteur à partir de votre modèle
Après avoir configuré les sources de votre modèle, créez
objet ModelInterpreter
de l'un d'entre eux.
Si vous ne disposez que d'un modèle groupé localement, il vous suffit de transmettre le CustomLocalModel
à modelInterpreter(localModel:)
:
Swift
let interpreter = ModelInterpreter.modelInterpreter(localModel: localModel)
Objective-C
FIRModelInterpreter *interpreter =
[FIRModelInterpreter modelInterpreterForLocalModel:localModel];
Si vous disposez d'un modèle hébergé à distance, vous devez vérifier qu'il a été
téléchargée avant
de l’exécuter. Vous pouvez vérifier l'état du téléchargement du modèle
à l'aide de la méthode isModelDownloaded(remoteModel:)
du gestionnaire de modèles.
Il vous suffit de confirmer cela avant d'exécuter l'interpréteur, mais si vous
un modèle hébergé à distance et un modèle groupé localement, cela peut rendre
d'effectuer cette vérification lors de l'instanciation de ModelInterpreter
: créez une
de l'interpréteur du modèle distant s'il a été téléchargé, et à partir du modèle local
dans le cas contraire.
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];
}
Si vous ne disposez que d'un modèle hébergé à distance, vous devez désactiver les paramètres (par exemple, griser ou masquer une partie de l'interface utilisateur), vous confirmez que le modèle a été téléchargé.
Vous pouvez obtenir l'état du téléchargement du modèle en associant des observateurs au modèle
Centre de notifications. Veillez à utiliser une référence faible à self
dans l'observateur
, car les téléchargements peuvent prendre un certain temps et que l'objet d'origine peut être
libérées une fois le téléchargement terminé. Exemple :
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]; }];
Spécifier l'entrée et la sortie du modèle
Configurez ensuite les formats d'entrée et de sortie de l'interpréteur de modèle.
Un modèle TensorFlow Lite prend en entrée et produit en sortie une ou plusieurs
des tableaux multidimensionnels. Ces tableaux contiennent byte
,
Valeurs int
, long
ou float
. Vous devez configurer ML Kit avec le nombre et les dimensions ("forme") des tableaux utilisés par votre modèle.
Si vous ne connaissez pas la forme ni le type de données des entrées et sorties de votre modèle, vous pouvez utiliser l'interpréteur Python TensorFlow Lite pour inspecter votre modèle. Exemple :
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'>
Une fois que vous avez déterminé le format d'entrée et de sortie de votre modèle, configurez
l'interpréteur de modèle d'une application en créant
objet ModelInputOutputOptions
.
Par exemple, un modèle de classification d'images à virgule flottante peut prendre comme entrée
NTableau x224 x 224x3 de valeurs Float
, représentant un lot de
N des images à trois canaux (RVB) 224 x 224 et produisent en sortie une liste de
1 000 valeurs Float
, chacune représentant la probabilité à laquelle l'image fait partie
l'une des 1 000 catégories prédites par le modèle.
Pour un tel modèle, vous devez configurer les entrées et sorties de l'interpréteur de modèle comme indiqué ci-dessous:
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; }
Effectuer des inférences sur les données d'entrée
Enfin, pour effectuer des inférences à l'aide du modèle, obtenir vos données d'entrée, exécuter
des transformations sur les données qui pourraient être nécessaires pour votre modèle, et créer
Objet Data
contenant les données.
Par exemple, si votre modèle traite des images et que ses dimensions d'entrée sont des valeurs à virgule flottante [BATCH_SIZE, 224, 224, 3]
, vous devrez peut-être mettre à l'échelle les valeurs de couleur de l'image à une plage à virgule flottante, comme dans l'exemple suivant :
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; }
Après avoir préparé l'entrée de votre modèle (et confirmé que le modèle est bien
sont disponibles), transmettez les options d'entrée et d'entrée/sortie
la fonction run(inputs:options:completion:)
de votre interpréteur de modèle ;
.
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 // ... }];
Vous pouvez obtenir le résultat en appelant la méthode output(index:)
de l'objet que
est renvoyé. Exemple :
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];
La manière dont vous utilisez la sortie dépend du modèle que vous utilisez.
Par exemple, si vous effectuez une classification, vous pourriez mapper les index du résultat avec les étiquettes qu'ils représentent. Supposons que vous ayez fichier texte contenant des chaînes d'étiquettes pour chacune des catégories de votre modèle ; vous pourriez cartographier les chaînes d'étiquette aux probabilités de sortie. suivantes:
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); }
Annexe : Sécurité des modèles
Quelle que soit la façon dont vous rendez vos modèles TensorFlow Lite disponibles ML Kit et ML Kit les stocke au format protobuf sérialisé standard dans stockage local.
En théorie, cela signifie que n'importe qui peut copier votre modèle. Toutefois, Dans la pratique, la plupart des modèles sont tellement spécifiques à une application et obscurcis par des optimisations dont le risque est similaire à celui des concurrents en démontrant et en réutilisant votre code. Néanmoins, vous devez être conscient de ce risque avant d'utiliser un modèle personnalisé dans votre application.