Core ML Swift-Integration
Laden, konfigurieren und ausführen von Core ML-Modellen in iOS-Apps. Dieser Skill behandelt die Swift-Seite: Modellladung, Vorhersage, MLTensor, Profilerstellung und Bereitstellung. Ziel iOS 26+ mit Swift 6.3, rückwärtskompatibel bis iOS 14, sofern nicht anders angegeben.
Abgrenzung: Die Python-Seite der Modellkonvertierung, Optimierung (Quantisierung, Palettisierung, Pruning) und Framework-Auswahl befinden sich im Skill
apple-on-device-ai. Dieser Skill umfasst ausschließlich die Swift-Integration.
Siehe references/coreml-swift-integration.md für vollständige Code-Muster, einschließlich actor-basiertem Caching, Batch-Inferenz, Bildvorverarbeitung und Tests.
Inhaltsverzeichnis
- Modelle laden
- Modellkonfiguration
- Vorhersagen durchführen
- MLTensor (iOS 18+)
- Arbeiten mit MLMultiArray
- Bildvorverarbeitung
- Multi-Modell-Pipelines
- Vision-Integration
- Leistungsprofilierung
- Modellbereitstellung
- Speicherverwaltung
- Häufige Fehler
- Review-Checkliste
- Referenzen
Modelle laden
Automatisch generierte Klassen
Wenn Sie ein .mlpackage oder .mlmodelc in Xcode ziehen, generiert es eine Swift-Klasse mit typisierter Ein-/Ausgabe. Verwenden Sie diese wann immer möglich.
import CoreML
let config = MLModelConfiguration()
config.computeUnits = .all
let model = try MyImageClassifier(configuration: config)
Manuelles Laden
Laden von einer URL, wenn das Modell zur Laufzeit heruntergeladen oder außerhalb des Bundles gespeichert wird.
let modelURL = Bundle.main.url(
forResource: "MyModel", withExtension: "mlmodelc"
)!
let model = try MLModel(contentsOf: modelURL, configuration: config)
Asynchrones Laden (iOS 16+)
Modelle laden, ohne den Hauptthread zu blockieren. Bevorzugen Sie dies für große Modelle.
let model = try await MLModel.load(
contentsOf: modelURL,
configuration: config
)
Zur Laufzeit kompilieren
Ein .mlpackage oder .mlmodel auf dem Gerät zu .mlmodelc kompilieren. Nützlich für Modelle, die von einem Server heruntergeladen wurden.
let compiledURL = try await MLModel.compileModel(at: packageURL)
let model = try MLModel(contentsOf: compiledURL, configuration: config)
Zwischenspeichern Sie die kompilierte URL – erneutes Kompilieren bei jedem Start verschwendet Zeit. Kopieren Sie compiledURL an einen persistenten Ort (z. B. Application Support).
Modellkonfiguration
MLModelConfiguration steuert Compute Units, GPU-Zugriff und Modellparameter.
Entscheidungstabelle für Compute Units
| Wert | Verwendung | Wann auswählen |
|---|---|---|
.all | CPU + GPU + Neural Engine | Standard. Das System entscheiden lassen. |
.cpuOnly | CPU | Hintergrundaufgaben, Audio-Sessions oder wenn die GPU ausgelastet ist. |
.cpuAndGPU | CPU + GPU | GPU benötigt, aber das Modell hat Operationen, die von ANE nicht unterstützt werden. |
.cpuAndNeuralEngine | CPU + Neural Engine | Beste Energieeffizienz für kompatible Modelle. |
let config = MLModelConfiguration()
config.computeUnits = .cpuAndNeuralEngine
// Low-Priority-Hintergrundinferenz zulassen
config.computeUnits = .cpuOnly
Konfigurationseigenschaften
let config = MLModelConfiguration()
config.computeUnits = .all
config.allowLowPrecisionAccumulationOnGPU = true // schneller, geringer Präzisionsverlust
Vorhersagen durchführen
Mit automatisch generierten Klassen
Die generierte Klasse stellt typisierte Ein-/Ausgabe-Strukturen bereit.
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, ...]
Mit MLDictionaryFeatureProvider
Verwenden, wenn die Eingaben dynamisch oder zur Kompilierzeit nicht bekannt sind.
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
Asynchrone Vorhersage (iOS 17+)
let output = try await model.prediction(from: inputFeatures)
Batch-Vorhersage
Mehrere Eingaben in einem Aufruf verarbeiten, um den Durchsatz zu verbessern.
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 ?? "unbekannt")
}
Zustandsbehaftete Vorhersage (iOS 18+)
MLState für Modelle verwenden, die den Zustand über mehrere Vorhersagen hinweg beibehalten (Sequenzmodelle, LLMs, Audio-Akkumulatoren). Einmal einen Zustand erstellen und bei jedem Vorhersageaufruf übergeben.
let state = model.makeState()
// Jede Vorhersage führt den internen Modellzustand weiter
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
}
Der Zustand ist nicht Sendable – verwenden Sie ihn aus einem einzelnen Actor oder Task. Rufen Sie model.makeState() auf, um unabhängige Zustände für gleichzeitige Streams zu erstellen.
MLTensor (iOS 18+)
MLTensor ist ein nativ-Swift mehrdimensionales Array für die Vor-/Nachbearbeitung. Operationen werden träge ausgeführt – rufen Sie .shapedArray(of:) auf, um Ergebnisse zu materialisieren.
import CoreML
// Erstellung
let tensor = MLTensor([1.0, 2.0, 3.0, 4.0])
let zeros = MLTensor(zeros: [3, 224, 224], scalarType: Float.self)
// Umformen
let reshaped = tensor.reshaped(to: [2, 2])
// Mathematische Operationen
let softmaxed = tensor.softmax()
let normalized = (tensor - tensor.mean()) / tensor.standardDeviation()
// Interoperabilität mit MLMultiArray
let multiArray = try MLMultiArray([1.0, 2.0, 3.0, 4.0])
let fromMultiArray = MLTensor(multiArray)
let backToArray = tensor.shapedArray(of: Float.self)
Arbeiten mit MLMultiArray
MLMultiArray ist der primäre Datenaustauschtyp für Nicht-Bild-Modelleingaben und -ausgaben. Verwenden Sie es, wenn die automatisch generierte Klasse Array-artige Merkmale erwartet.
// Erstellen eines 3D-Arrays: [batch, sequence, features]
let array = try MLMultiArray(shape: [1, 128, 768], dataType: .float32)
// Werte schreiben
for i in 0..<128 {
array[[0, i, 0] as [NSNumber]] = NSNumber(value: Float(i))
}
// Werte lesen
let value = array[[0, 0, 0] as [NSNumber]].floatValue
// Aus Datenzeiger für Zero-Copy-Interop erstellen
let data: [Float] = [1.0, 2.0, 3.0]
let fromData = try MLMultiArray(dataPointer: UnsafeMutableRawPointer(mutating: data),
shape: [3],
dataType: .float32,
strides: [1])
Siehe references/coreml-swift-integration.md für fortgeschrittene MLMultiArray-Muster, einschließlich NLP-Tokenisierung und Audio-Merkmalsextraktion.
Bildvorverarbeitung
Bildmodelle erwarten CVPixelBuffer-Eingabe. Verwenden Sie CGImage-Konvertierung für Fotos von der Kamera oder der Fotobibliothek. Die Vision VNCoreMLRequest erledigt dies automatisch; manuelle Konvertierung ist nur bei direkter MLModel-Vorhersage erforderlich.
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
}
Weitere Vorverarbeitungsmuster (Normalisierung, Center-Cropping) finden Sie in references/coreml-swift-integration.md.
Multi-Modell-Pipelines
Modelle verketten, wenn die Vor- oder Nachbearbeitung ein separates Modell erfordert.
// Sequentielle Inferenz: Vorverarbeiter -> Hauptmodell -> Nachverarbeiter
let preprocessed = try preprocessor.prediction(from: rawInput)
let mainOutput = try mainModel.prediction(from: preprocessed)
let finalOutput = try postprocessor.prediction(from: mainOutput)
Für Xcode-verwaltete Pipelines verwenden Sie den Pipeline-Modelltyp im .mlpackage.
Jedes Untermodell läuft auf seiner optimalen Compute Unit.
Vision-Integration
Verwenden Sie Vision, um Core ML-Bildmodelle mit automatischer Bildvorverarbeitung (Skalierung, Normalisierung, Farbraum, Ausrichtung) auszuführen.
Modern: 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 ?? "unbekannt"
let confidence = observation.labels.first?.confidence ?? 0
let boundingBox = observation.boundingBox // normalisierte Koordinaten
print("\(label): \(confidence) bei \(boundingBox)")
}
}
request.imageCropAndScaleOption = .scaleFill
let handler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer)
try handler.perform([request])
Vollständige Vision-Framework-Muster (Texterkennung, Barcode-Erkennung, Dokumentenscan) finden Sie im Skill
vision-framework.
Leistungsprofilierung
MLComputePlan (iOS 17.4+)
Überprüfen, welches Compute-Gerät jede Operation verwenden wird, bevor Vorhersagen ausgeführt werden.
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 ?? "unbekannt")")
}
Instruments
Verwenden Sie die Core ML-Instrumentvorlage in Instruments zur Profilierung von:
- Modellladezeit
- Vorhersage-Latenz (Aufschlüsselung pro Operation)
- Compute-Geräteverteilung (CPU/GPU/ANE pro Operation)
- Speicherzuweisung
Führen Sie außerhalb des Debuggers aus, um genaue Ergebnisse zu erhalten (Xcode: Produkt > Profil).
Modellbereitstellung
Bundle vs. On-Demand-Ressourcen
| Strategie | Vorteile | Nachteile |
|---|---|---|
| Im Bundle | Sofortige Verfügbarkeit, funktioniert offline | Erhöht die App-Download-Größe |
| On-Demand-Ressourcen | Kleinerer initialer Download | Erfordert Download vor der ersten Verwendung |
| Hintergrund-Assets (iOS 16+) | Lädt im Voraus herunter | Komplexere Einrichtung |
| CloudKit / Server | Maximale Flexibilität | Erfordert Netzwerk, längere Einrichtung |
Größenerwägungen
- App Store-Grenze: 4 GB für das App-Bundle
- Mobilfunk-Download-Limit: 200 MB (Ausnahmen können beantragt werden)
- Verwenden Sie ODR-Tags für Modelle > 50 MB
- Vorabkompilierung zu
.mlmodelc, um die Kompilierung auf dem Gerät zu vermeiden
// On-Demand-Ressourcen laden
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)
// request.endAccessingResources() aufrufen, wenn fertig
Speicherverwaltung
- Im Hintergrund entladen: Modellreferenzen freigeben, wenn die App in den Hintergrund wechselt, um GPU/ANE-Speicher freizugeben. Bei Rückkehr in den Vordergrund neu laden.
.cpuOnlyfür Hintergrundaufgaben verwenden: Die Hintergrundverarbeitung kann GPU oder ANE nicht nutzen; die Einstellung von.cpuOnlyvermeidet stilles Fallback und Ressourcenkonflikte.- Modellinstanzen teilen: Niemals mehrere
MLModel-Instanzen aus demselben kompilierten Modell erstellen. Einen Actor für gemeinsamen Zugriff verwenden. - Speicherdruck überwachen: Große Modelle (>100 MB) können Speicherwarnungen auslösen. Registrieren Sie sich für
UIApplication.didReceiveMemoryWarningNotificationund geben Sie zwischengespeicherte Modelle bei Druck frei.
Siehe references/coreml-swift-integration.md für einen actor-basierten Modellmanager mit lebenszyklusbewusstem Laden und Cache-Eviction.
Häufige Fehler
NICHT: Modelle auf dem Hauptthread laden.
TUN: Die asynchrone API MLModel.load(contentsOf:configuration:) verwenden oder auf einem Hintergrund-Actor laden.
Grund: Große Modelle können Sekunden zum Laden benötigen und die UI einfrieren.
NICHT: .mlpackage bei jedem App-Start zu .mlmodelc kompilieren.
TUN: Einmal mit MLModel.compileModel(at:) kompilieren und die kompilierte URL dauerhaft zwischenspeichern.
Grund: Kompilierung ist teuer. Das .mlmodelc im Application Support zwischenspeichern.
NICHT: .cpuOnly hartcodieren, es sei denn, Sie haben einen bestimmten Grund.
TUN: .all verwenden und das System die optimale Compute Unit wählen lassen.
NICHT: MLState zwischen gleichzeitigen Tasks teilen.
TUN: Einen unabhängigen Zustand pro parallelem Stream mit model.makeState() erstellen.
Grund: MLState ist nicht Sendable und kann zu Datenrennen führen.
NICHT: Kompilierte URL-Referenzen über Starts hinweg verlieren.
TUN: Die URL von MLModel.compileModel(at:) in einem persistenten Verzeichnis (z. B. Application Support) zwischenspeichern, da temporäre Verzeichnisse möglicherweise gelöscht werden.
Review-Checkliste
- Asynchrones Laden (
MLModel.load) verwendet, wo immer möglich? - Compute Units sind für den Anwendungsfall angemessen (keine unnötige
.cpuOnly-Einschränkung)? - Bildvorverarbeitung korrigiert Pixelformat, Größe, Farbraum und Ausrichtung?
- Leistung wird mit
MLComputePlanoder Instruments profiliert? - Speicher wird bei Hintergrundereignissen und bei Druck entladen?
-
MLStatewird nicht zwischen Tasks geteilt? - On-Demand-Ressourcen für große Modelle, die das Bundle-Limit überschreiten?
- Persistente Zwischenspeicherung der kompilierten Modell-URL, um wiederholte Kompilierung zu vermeiden?
Referenzen
- references/coreml-swift-integration.md – vollständige Code-Muster
apple-on-device-aiSkill – Python-Seite der Modellkonvertierungvision-frameworkSkill – Vollständige Vision-Framework-Muster