Intégration Swift de Core ML
Chargez, configurez et exécutez des modèles Core ML dans des applications iOS. Ce skill couvre le côté Swift : chargement de modèles, prédiction, MLTensor, profilage et déploiement. Cible iOS 26+ avec Swift 6.3, rétrocompatible jusqu'à iOS 14 sauf indication contraire.
Limite de périmètre : La conversion côté Python, l'optimisation (quantification, palettisation, élagage) et la sélection de framework sont gérées dans le skill
apple-on-device-ai. Ce skill ne concerne que l'intégration Swift.
Consultez references/coreml-swift-integration.md pour des modèles de code complets, y compris la mise en cache basée sur les acteurs, l'inférence par lots, le prétraitement d'images et les tests.
Sommaire
- Chargement de Modèles
- Configuration du Modèle
- Réalisation de Prédictions
- MLTensor (iOS 18+)
- Travailler avec MLMultiArray
- Prétraitement d'Images
- Pipelines Multi-Modèles
- Intégration Vision
- Profilage des Performances
- Déploiement du Modèle
- Gestion de la Mémoire
- Erreurs Courantes
- Liste de Vérification
- Références
Chargement de Modèles
Classes Auto-Générées
Lorsque vous glissez un fichier .mlpackage ou .mlmodelc dans Xcode, il génère une classe Swift avec des entrées/sorties typées. Utilisez-la autant que possible.
import CoreML
let config = MLModelConfiguration()
config.computeUnits = .all
let model = try MyImageClassifier(configuration: config)
Chargement Manuel
Chargez à partir d'une URL lorsque le modèle est téléchargé à l'exécution ou stocké en dehors du bundle.
let modelURL = Bundle.main.url(
forResource: "MyModel", withExtension: "mlmodelc"
)!
let model = try MLModel(contentsOf: modelURL, configuration: config)
Chargement Asynchrone (iOS 16+)
Chargez les modèles sans bloquer le thread principal. Préférez cette méthode pour les gros modèles.
let model = try await MLModel.load(
contentsOf: modelURL,
configuration: config
)
Compilation à l'Exécution
Compilez un fichier .mlpackage ou .mlmodel en .mlmodelc sur l'appareil. Utile pour les modèles téléchargés depuis un serveur.
let compiledURL = try await MLModel.compileModel(at: packageURL)
let model = try MLModel(contentsOf: compiledURL, configuration: config)
Mettez en cache l'URL compilée — recompiler à chaque lancement gaspille du temps. Copiez compiledURL vers un emplacement persistant (par exemple, Application Support).
Configuration du Modèle
MLModelConfiguration contrôle les unités de calcul, l'accès au GPU et les paramètres du modèle.
Tableau de Décision des Unités de Calcul
| Valeur | Utilise | Quand Choisir |
|---|---|---|
.all | CPU + GPU + Neural Engine | Par défaut. Laissez le système décider. |
.cpuOnly | CPU | Tâches en arrière-plan, sessions audio, ou lorsque le GPU est occupé. |
.cpuAndGPU | CPU + GPU | Besoin du GPU mais le modèle a des opérations non supportées par l'ANE. |
.cpuAndNeuralEngine | CPU + Neural Engine | Meilleure efficacité énergétique pour les modèles compatibles. |
let config = MLModelConfiguration()
config.computeUnits = .cpuAndNeuralEngine
// Permet une inférence en arrière-plan à faible priorité
config.computeUnits = .cpuOnly
Propriétés de Configuration
let config = MLModelConfiguration()
config.computeUnits = .all
config.allowLowPrecisionAccumulationOnGPU = true // plus rapide, légère perte de précision
Réalisation de Prédictions
Avec des Classes Auto-Générées
La classe générée fournit des structures d'entrée/sortie typées.
let model = try MyImageClassifier(configuration: config)
let input = MyImageClassifierInput(image: pixelBuffer)
let output = try model.prediction(input: input)
print(output.classLabel) // "golden_retriever"
print(output.classLabelProbs) // ["golden_retriever": 0.95, ...]
Avec MLDictionaryFeatureProvider
Utilisez lorsque les entrées sont dynamiques ou inconnues à la compilation.
let inputFeatures = try MLDictionaryFeatureProvider(dictionary: [
"image": MLFeatureValue(pixelBuffer: pixelBuffer),
"confidence_threshold": MLFeatureValue(double: 0.5),
])
let output = try model.prediction(from: inputFeatures)
let label = output.featureValue(for: "classLabel")?.stringValue
Prédiction Asynchrone (iOS 17+)
let output = try await model.prediction(from: inputFeatures)
Prédiction par Lots
Traitez plusieurs entrées en un seul appel pour un meilleur débit.
let batchInputs = try MLArrayBatchProvider(array: inputs.map { input in
try MLDictionaryFeatureProvider(dictionary: ["image": MLFeatureValue(pixelBuffer: input)])
})
let batchOutput = try model.predictions(from: batchInputs)
for i in 0..<batchOutput.count {
let result = batchOutput.features(at: i)
print(result.featureValue(for: "classLabel")?.stringValue ?? "unknown")
}
Prédiction avec État (iOS 18+)
Utilisez MLState pour les modèles qui maintiennent un état entre les prédictions (modèles séquentiels, LLM, accumulateurs audio). Créez l'état une fois et passez-le à chaque appel de prédiction.
let state = model.makeState()
// Chaque prédiction fait avancer l'état interne du modèle
for frame in audioFrames {
let input = try MLDictionaryFeatureProvider(dictionary: [
"audio_features": MLFeatureValue(multiArray: frame)
])
let output = try await model.prediction(from: input, using: state)
let classification = output.featureValue(for: "label")?.stringValue
}
L'état n'est pas Sendable — utilisez-le à partir d'un seul acteur ou d'une seule tâche. Appelez model.makeState() pour créer un état indépendant pour des flux concurrents.
MLTensor (iOS 18+)
MLTensor est un tableau multidimensionnel natif Swift pour le prétraitement/post-traitement. Les opérations s'exécutent paresseusement — appelez .shapedArray(of:) pour matérialiser les résultats.
import CoreML
// Création
let tensor = MLTensor([1.0, 2.0, 3.0, 4.0])
let zeros = MLTensor(zeros: [3, 224, 224], scalarType: Float.self)
// Remodelage
let reshaped = tensor.reshaped(to: [2, 2])
// Opérations mathématiques
let softmaxed = tensor.softmax()
let normalized = (tensor - tensor.mean()) / tensor.standardDeviation()
// Interopérabilité avec MLMultiArray
let multiArray = try MLMultiArray([1.0, 2.0, 3.0, 4.0])
let fromMultiArray = MLTensor(multiArray)
let backToArray = tensor.shapedArray(of: Float.self)
Travailler avec MLMultiArray
MLMultiArray est le principal type d'échange de données pour les entrées et sorties de modèles non-images. Utilisez-le lorsque la classe auto-générée attend des caractéristiques de type tableau.
// Créer un tableau 3D : [lot, séquence, caractéristiques]
let array = try MLMultiArray(shape: [1, 128, 768], dataType: .float32)
// Écrire des valeurs
for i in 0..<128 {
array[[0, i, 0] as [NSNumber]] = NSNumber(value: Float(i))
}
// Lire des valeurs
let value = array[[0, 0, 0] as [NSNumber]].floatValue
// Créer à partir d'un pointeur de données pour une interopérabilité sans copie
let data: [Float] = [1.0, 2.0, 3.0]
let fromData = try MLMultiArray(dataPointer: UnsafeMutableRawPointer(mutating: data),
shape: [3],
dataType: .float32,
strides: [1])
Consultez references/coreml-swift-integration.md pour des modèles avancés de MLMultiArray, y compris la tokenisation NLP et l'extraction de caractéristiques audio.
Prétraitement d'Images
Les modèles d'images attendent une entrée CVPixelBuffer. Utilisez la conversion CGImage pour les photos provenant de l'appareil photo ou de la bibliothèque de photos. VNCoreMLRequest de Vision gère cela automatiquement ; la conversion manuelle n'est nécessaire que pour les prédictions directes avec MLModel.
import CoreVideo
func createPixelBuffer(from cgImage: CGImage, width: Int, height: Int) -> CVPixelBuffer? {
var pixelBuffer: CVPixelBuffer?
let attrs: [CFString: Any] = [
kCVPixelBufferCGImageCompatibilityKey: true,
kCVPixelBufferCGBitmapContextCompatibilityKey: true,
]
CVPixelBufferCreate(kCFAllocatorDefault, width, height,
kCVPixelFormatType_32ARGB, attrs as CFDictionary, &pixelBuffer)
guard let buffer = pixelBuffer else { return nil }
CVPixelBufferLockBaseAddress(buffer, [])
let context = CGContext(
data: CVPixelBufferGetBaseAddress(buffer),
width: width, height: height,
bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(buffer),
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue
)
context?.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height))
CVPixelBufferUnlockBaseAddress(buffer, [])
return buffer
}
Pour des modèles de prétraitement supplémentaires (normalisation, recadrage central), consultez references/coreml-swift-integration.md.
Pipelines Multi-Modèles
Enchaînez des modèles lorsque le prétraitement ou le post-traitement nécessite un modèle séparé.
// Inférence séquentielle : préprocesseur -> modèle principal -> postprocesseur
let preprocessed = try preprocessor.prediction(from: rawInput)
let mainOutput = try mainModel.prediction(from: preprocessed)
let finalOutput = try postprocessor.prediction(from: mainOutput)
Pour les pipelines gérés par Xcode, utilisez le type de modèle de pipeline dans le fichier .mlpackage. Chaque sous-modèle s'exécute sur son unité de calcul optimale.
Intégration Vision
Utilisez Vision pour exécuter des modèles d'images Core ML avec un prétraitement automatique des images (redimensionnement, normalisation, espace colorimétrique, orientation).
Moderne : CoreMLRequest (iOS 18+)
import Vision
import CoreML
let model = try MLModel(contentsOf: modelURL, configuration: config)
let request = CoreMLRequest(model: .init(model))
let results = try await request.perform(on: cgImage)
if let classification = results.first as? ClassificationObservation {
print("\(classification.identifier): \(classification.confidence)")
}
Legacy : VNCoreMLRequest
let vnModel = try VNCoreMLModel(for: model)
let request = VNCoreMLRequest(model: vnModel) { request, error in
guard let results = request.results as? [VNRecognizedObjectObservation] else { return }
for observation in results {
let label = observation.labels.first?.identifier ?? "unknown"
let confidence = observation.labels.first?.confidence ?? 0
let boundingBox = observation.boundingBox // coordonnées normalisées
print("\(label): \(confidence) at \(boundingBox)")
}
}
request.imageCropAndScaleOption = .scaleFill
let handler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer)
try handler.perform([request])
Pour des modèles complets du framework Vision (reconnaissance de texte, détection de codes-barres, numérisation de documents), consultez le skill
vision-framework.
Profilage des Performances
MLComputePlan (iOS 17.4+)
Inspectez quel dispositif de calcul chaque opération utilisera avant d'exécuter les prédictions.
let computePlan = try await MLComputePlan.load(
contentsOf: modelURL, configuration: config
)
guard case let .program(program) = computePlan.modelStructure else { return }
guard let mainFunction = program.functions["main"] else { return }
for operation in mainFunction.block.operations {
let deviceUsage = computePlan.deviceUsage(for: operation)
let estimatedCost = computePlan.estimatedCost(of: operation)
print("\(operation.operatorName): \(deviceUsage?.preferredComputeDevice ?? "unknown")")
}
Instruments
Utilisez le modèle d'instrument Core ML dans Instruments pour profiler :
- Temps de chargement du modèle
- Latence de prédiction (répartition par opération)
- Répartition par dispositif de calcul (CPU/GPU/ANE par opération)
- Allocation mémoire
Exécutez en dehors du débogueur pour des résultats précis (Xcode : Product > Profile).
Déploiement du Modèle
Bundle vs Ressources à la Demande
| Stratégie | Avantages | Inconvénients |
|---|---|---|
| Dans le bundle | Disponibilité immédiate, fonctionne hors ligne | Augmente la taille de téléchargement de l'application |
| Ressources à la demande | Téléchargement initial plus petit | Nécessite un téléchargement avant la première utilisation |
| Ressources en arrière-plan (iOS 16+) | Téléchargement anticipé | Configuration plus complexe |
| CloudKit / serveur | Flexibilité maximale | Nécessite un réseau, configuration plus longue |
Considérations de Taille
- Limite de l'App Store : 4 Go pour le bundle de l'application
- Limite de téléchargement cellulaire : 200 Mo (peut demander une exception)
- Utilisez les tags ODR pour les modèles > 50 Mo
- Pré-compilez en
.mlmodelcpour éviter la compilation sur l'appareil
// Chargement des ressources à la demande
let request = NSBundleResourceRequest(tags: ["ml-model-v2"])
try await request.beginAccessingResources()
let modelURL = Bundle.main.url(forResource: "LargeModel", withExtension: "mlmodelc")!
let model = try await MLModel.load(contentsOf: modelURL, configuration: config)
// Appelez request.endAccessingResources() lorsque vous avez terminé
Gestion de la Mémoire
- Décharger en arrière-plan : Libérez les références de modèle lorsque l'application entre en arrière-plan pour libérer la mémoire GPU/ANE. Rechargez au retour au premier plan.
- Utilisez
.cpuOnlypour les tâches en arrière-plan : Le traitement en arrière-plan ne peut pas utiliser le GPU ou l'ANE ; définir.cpuOnlyévite les replis silencieux et les conflits de ressources. - Partagez les instances de modèle : Ne créez jamais plusieurs instances
MLModelà partir du même modèle compilé. Utilisez un acteur pour fournir un accès partagé. - Surveillez la pression mémoire : Les gros modèles (>100 Mo) peuvent déclencher des avertissements de mémoire. Enregistrez-vous pour
UIApplication.didReceiveMemoryWarningNotificationet libérez les modèles mis en cache en cas de pression.
Consultez references/coreml-swift-integration.md pour un gestionnaire de modèle basé sur les acteurs avec chargement sensible au cycle de vie et éviction du cache.
Erreurs Courantes
NE FAITES PAS : Charger des modèles sur le thread principal.
FAITES : Utilisez l'API asynchrone MLModel.load(contentsOf:configuration:) ou chargez sur un acteur en arrière-plan.
Pourquoi : Les gros modèles peuvent prendre des secondes à charger, ce qui bloque l'interface utilisateur.
NE FAITES PAS : Recompiler .mlpackage en .mlmodelc à chaque lancement de l'application.
FAITES : Compilez une fois avec MLModel.compileModel(at:) et mettez en cache l'URL compilée de manière persistante.
Pourquoi : La compilation est coûteuse. Mettez en cache le fichier .mlmodelc dans Application Support.
NE FAITES PAS : Coder en dur .cpuOnly sauf si vous avez une raison spécifique.
FAITES : Utilisez .all et laissez le système choisir l'unité de calcul optimale.
*
… [tronqué pour la traduction]