在 iOS App 中整合 Core ML 模型,進行裝置端機器學習推論。涵蓋模型載入(.mlmodel、.mlpackage、.mlmodelc)、使用自動生成類別與 MLFeatureProvider 進行預測、運算單元配置(CPU、GPU、神經網路引擎)、MLTensor、VNCoreMLRequest、MLComputePlan、多模型管線以及部署策略。適用於載入 Core ML 模型、進行預測、配置運算單元或分析模型效能時使用。
Core ML Swift 整合
在 iOS App 中載入、配置並執行 Core ML 模型。本技能涵蓋 Swift 端的模型載入、預測、MLTensor、效能分析與部署。
範圍邊界: Python 端的模型轉換、最佳化(量化、調色盤化、剪枝)以及框架選擇屬於
apple-on-device-ai技能。本技能僅負責 Swift 整合。
完整程式碼模式(包含基於 Actor 的快取、批次推論、影像前處理與測試)請參閱 references/coreml-swift-integration.md。
目錄
- 載入模型
- 模型配置
- 進行預測
- MLTensor(iOS 18+)
- 使用 MLMultiArray
- 影像前處理
- 多模型管線
- Vision 整合
- 效能分析
- 模型部署
- 記憶體管理
- 常見錯誤
- 審查清單
- 參考資料
載入模型
自動生成類別
當您將 .mlmodel 或 .mlpackage 加入 App target 時,Xcode 會自動生成一個帶有型別化輸入/輸出的 Swift 類別。請盡可能使用此方式。
import CoreML
let config = MLModelConfiguration()
config.computeUnits = .all
let model = try MyImageClassifier(configuration: config)
手動載入
當模型在執行時期下載或儲存在 bundle 外部時,從 URL 載入。
let modelURL = Bundle.main.url(
forResource: "MyModel", withExtension: "mlmodelc"
)!
let model = try MLModel(contentsOf: modelURL, configuration: config)
非同步載入(iOS 15+)
在不阻塞主執行緒的情況下載入模型。大型模型建議使用此方式。
let model = try await MLModel.load(
contentsOf: modelURL,
configuration: config
)
執行時期編譯(iOS 16+)
在裝置上將 .mlpackage 或 .mlmodel 編譯為 .mlmodelc。適用於從伺服器下載的模型。每個模型版本僅需編譯一次,而非每次啟動都編譯。
let compiledURL = try await MLModel.compileModel(at: packageURL)
let model = try await MLModel.load(contentsOf: compiledURL, configuration: config)
快取編譯後的 URL——每次啟動都重新編譯是一個錯誤。請將 compiledURL 複製到持久化位置(例如 Application Support)。審查執行時期載入的模型時,應同時指出兩件事:非同步 MLModel.compileModel(at:) 需要 iOS 16+,且編譯後的模型必須快取,以免 App 每次啟動都重新編譯。
模型配置
MLModelConfiguration 控制運算單元、GPU 存取以及模型參數。
運算單元決策表
| 值 | 使用 | 選擇時機 |
|---|---|---|
.all |
CPU + GPU + 神經網路引擎 | 預設值。讓系統決定。 |
.cpuOnly |
CPU | 確定性測試、僅 CPU 的備援方案,或在分析後發現加速器政策、資源競爭、熱狀態或能源預算為限制因素時,用於受限工作。 |
.cpuAndGPU |
CPU + GPU | 需要 GPU 但模型包含 ANE 不支援的運算。 |
.cpuAndNeuralEngine(iOS 16+) |
CPU + 神經網路引擎 | 相容模型的最佳能源效率。 |
let config = MLModelConfiguration()
config.computeUnits = .cpuAndNeuralEngine
// 在分析與政策審查後,可選擇用於受限工作的備援方案
config.computeUnits = .cpuOnly
配置屬性
let config = MLModelConfiguration()
config.computeUnits = .all
config.allowLowPrecisionAccumulationOnGPU = true // 更快,但些微精度損失
進行預測
使用自動生成類別
生成的類別提供型別化的輸入/輸出結構。
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, ...]
使用 MLDictionaryFeatureProvider
當輸入為動態或在編譯時期未知時使用。
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
非同步工作流程中的預測
MLModel.prediction(...) 是同步的。在非同步管線中,保持模型載入為非同步,然後從 actor 或非主執行緒的任務中執行預測,無需在預測呼叫前加上 await。
let output = try model.prediction(from: inputFeatures)
批次預測
一次處理多個輸入以獲得更好的吞吐量。
let batchInputs = try MLArrayBatchProvider(array: inputs.map { input in
try MLDictionaryFeatureProvider(dictionary: ["image": MLFeatureValue(pixelBuffer: input)])
})
let batchOutput = try model.predictions(fromBatch: batchInputs)
for i in 0..<batchOutput.count {
let result = batchOutput.features(at: i)
print(result.featureValue(for: "classLabel")?.stringValue ?? "unknown")
}
當批次處理不需要明確的 MLPredictionOptions 時,使用 predictions(fromBatch:)。僅在同時傳入 MLBatchProvider 和 MLPredictionOptions 時才使用 predictions(from:options:);predictions(from:) 本身並非無選項的批次 API。
在批次處理前,先驗證一個代表性的單一輸入。然後確認批次輸出的數量/順序、特徵類型、領域不變量,以及與單一輸入結果的一致性。若失敗,請先修正確定性輸入、形狀、模型或配置問題,再重新執行測試案例與實體裝置分析。
狀態化預測(iOS 18+)
對於需要在預測之間維持狀態的模型(序列模型、LLM、音訊累加器),請使用 MLState。建立狀態一次,並在每次預測呼叫時傳入。
let state = model.makeState()
// 每次同步預測都會攜帶內部的模型狀態
for frame in audioFrames {
let input = try MLDictionaryFeatureProvider(dictionary: [
"audio_features": MLFeatureValue(multiArray: frame)
])
let output = try model.prediction(from: input, using: state)
let classification = output.featureValue(for: "label")?.stringValue
}
MLState 是 Sendable,但 Sendable 並不表示一個狀態可以安全地進行並行推論。使用相同狀態的預測必須序列化;請勿在預測進行中讀取或寫入狀態緩衝區。為每個獨立的並行串流呼叫 model.makeState()。如果您需要 MLPredictionOptions,iOS 18+ 也提供了非同步的 prediction(from:using:options:) 多載;每個狀態同時只能有一個預測的規則仍然適用。
MLTensor(iOS 18+)
MLTensor 是 Swift 原生的多維陣列,用於前/後處理。運算會延遲執行——呼叫 await tensor.shapedArray(of:) 來具體化結果。
import CoreML
// 建立
let tensor = MLTensor([1.0, 2.0, 3.0, 4.0])
let zeros = MLTensor(zeros: [3, 224, 224], scalarType: Float.self)
// 重塑
let reshaped = tensor.reshaped(to: [2, 2])
// 數學運算
let softmaxed = tensor.softmax(alongAxis: -1)
let centered = tensor - tensor.mean()
// 與 MLShapedArray / MLMultiArray 互通
let shaped = await tensor.shapedArray(of: Float.self)
let multiArray = try MLMultiArray(shaped)
let shapedAgain = MLShapedArray<Float>(multiArray)
請勿自行發明用於統計或橋接的 MLTensor API。避免使用如 MLTensor(multiArray)、tensor.std()、tensor.standardDeviation()、直接存取延遲緩衝區或同步提取等範例;請在 tensor 管線外部執行不支援的 DSP/統計運算,或使用經來源確認的 tensor 運算。
使用 MLMultiArray
MLMultiArray 是非影像模型輸入與輸出的主要資料交換類型。當自動生成類別預期陣列型特徵時使用。
// 建立一個 3D 陣列:[batch, sequence, features]
let array = try MLMultiArray(shape: [1, 128, 768], dataType: .float32)
// 寫入數值
for i in 0..<128 {
array[[0, i, 0] as [NSNumber]] = NSNumber(value: Float(i))
}
// 讀取數值
let value = array[[0, 0, 0] as [NSNumber]].floatValue
let data: [Float] = [1.0, 2.0, 3.0]
let shaped = MLShapedArray(scalars: data, shape: [3])
let fromShaped = try MLMultiArray(shaped)
進階的 MLMultiArray 模式(包含 NLP 分詞與音訊特徵提取)請參閱 references/coreml-swift-integration.md。
影像前處理
影像模型預期 CVPixelBuffer 作為輸入。對於來自相機或照片圖庫的照片,請使用 CGImage 轉換。Vision 的 VNCoreMLRequest 會自動處理此轉換;僅在直接使用 MLModel 預測時才需要手動轉換。
完整的 CVPixelBuffer 轉換檢查以及額外的正規化或裁切模式,請載入 影像前處理。
多模型管線
當前處理或後處理需要單獨的模型時,可以串聯模型。
// 順序推論:前處理器 -> 主模型 -> 後處理器
let preprocessed = try preprocessor.prediction(from: rawInput)
let mainOutput = try mainModel.prediction(from: preprocessed)
let finalOutput = try postprocessor.prediction(from: mainOutput)
對於 Xcode 管理的管線,請使用 .mlpackage 中的管線模型類型。每個子模型會在其最佳運算單元上執行。
Vision 整合
使用 Vision 來執行 Core ML 影像模型,並自動進行影像前處理(調整大小、正規化、色彩空間、方向)。
現代方式: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)")
}
傳統方式: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 // 正規化座標
print("\(label): \(confidence) at \(boundingBox)")
}
}
request.imageCropAndScaleOption = .scaleFill
let handler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer)
try handler.perform([request])
完整的 Vision 框架模式(文字辨識、條碼偵測、文件掃描)請參閱
vision-framework技能。
效能分析
MLComputePlan(iOS 17.4+)
在執行預測之前,檢查每個運算將使用哪個運算裝置。載入 MLComputePlan 詳細用法 以了解模型結構遍歷、裝置使用情況與預估成本檢查。
Instruments
使用 Instruments 中的 Core ML 儀器模板來分析:
- 模型載入時間
- 預測延遲(每個運算的細部分解)
- 運算裝置分派(每個運算的 CPU/GPU/ANE)
- 記憶體配置
請在除錯器外執行以獲得準確結果(Xcode:Product > Profile)。
模型部署
將小型且離線關鍵的模型打包進 bundle。對於新的大型或可更新資產,建議使用 Background Assets;僅在現有 ODR 專案中保留 On-Demand Resources。將下載的原始模型編譯一次,按版本持久化 .mlmodelc,並在支援的最低階實體裝置上測試載入、首次/重複預測、生命週期轉換與記憶體。實作細節請載入 部署模式。
記憶體管理
- 進入背景時卸載: 當 App 進入背景時釋放模型參考,以釋放 GPU/ANE 記憶體。返回前景時重新載入。
- 共享模型實例: 永遠不要從同一個編譯模型建立多個
MLModel實例。使用 actor 提供共享存取。 - 監控記憶體壓力: 大型模型(>100 MB)可能觸發記憶體警告。註冊
UIApplication.didReceiveMemoryWarningNotification,並在記憶體壓力下釋放快取的模型。
基於 actor 的模型管理器(包含生命週期感知載入與快取回收)請參閱 references/coreml-swift-integration.md。
常見錯誤
不要: 在主執行緒上載入模型。
應該: 使用 MLModel.load(contentsOf:configuration:) 非同步 API 或在背景 actor 上載入。
原因: 大型模型可能需要數秒鐘載入,導致 UI 凍結。
不要: 忽略輸入與模型預期之間的 MLFeatureValue 類型不匹配。
應該: 精確匹配類型——影像使用 MLFeatureValue(pixelBuffer:),而非原始資料。
原因: 類型不匹配會導致難以理解的執行時期崩潰或靜默的錯誤結果。
不要: 每次預測都建立新的 MLModel 實例。
應該: 載入一次並重複使用。使用 actor 管理模型生命週期。
原因: 模型載入會配置大量記憶體與運算資源。
不要: 跳過模型載入與預測的錯誤處理。
應該: 捕捉錯誤並在模型失敗時提供備援行為。
原因: 模型在較舊的裝置或資源受限時可能載入失敗。
不要: 假設所有運算都在神經網路引擎上執行。
應該: 使用 MLComputePlan(iOS 17.4+)驗證每個運算的裝置分派。
原因: 不支援的運算會退回 CPU,可能造成管線瓶頸。
不要: 在傳遞給 Vision + Core ML 之前手動處理影像。
應該: 使用 CoreMLRequest(iOS 18+)或 VNCoreMLRequest(傳統)讓 Vision 處理前處理。
原因: Vision 能正確處理方向、縮放與像素格式轉換。
審查清單
- [ ] 模型非同步載入(不阻塞主執行緒)
- [ ]
MLModelConfiguration.computeUnits根據使用情境適當設定 - [ ] 模型實例跨預測重複使用(不每次重新建立)
- [ ] 盡可能使用自動生成類別(型別化輸入/輸出)
- [ ] 模型載入與預測失敗的錯誤處理
- [ ] 若在執行時期編譯,編譯後的模型已持久化快取
- [ ] 影像輸入使用 Vision 管線(
CoreMLRequestiOS 18+ 或VNCoreMLRequest)以獲得正確的前處理 - [ ] 已檢查
MLComputePlan以驗證運算裝置分派(iOS 17.4+) - [ ] 處理多個輸入時使用批次預測
- [ ] 模型大小適合部署策略(bundle、Background Assets、ODR)
- [ ] 已在目標裝置上測試記憶體(尤其是 RAM 較少的舊裝置)
- [ ] 預測在除錯器外執行以獲得準確的效能測量
參考資料
- 模式與程式碼:references/coreml-swift-integration.md
- 模型轉換與最佳化(Python 端):涵蓋於
apple-on-device-ai技能 - Apple 文件:Core ML |
MLModel |
MLTensor |
MLComputePlan |
Background Assets




