coreml

coreml

熱門

在 iOS App 中整合 Core ML 模型,進行裝置端機器學習推論。涵蓋模型載入(.mlmodel、.mlpackage、.mlmodelc)、使用自動生成類別與 MLFeatureProvider 進行預測、運算單元配置(CPU、GPU、神經網路引擎)、MLTensor、VNCoreMLRequest、MLComputePlan、多模型管線以及部署策略。適用於載入 Core ML 模型、進行預測、配置運算單元或分析模型效能時使用。

936星標
47分支
更新於 2026/7/15
SKILL.md
唯讀
名稱
coreml
描述

在 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

目錄

載入模型

自動生成類別

當您將 .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:)。僅在同時傳入 MLBatchProviderMLPredictionOptions 時才使用 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
}

MLStateSendable,但 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 管線(CoreMLRequest iOS 18+ 或 VNCoreMLRequest)以獲得正確的前處理
  • [ ] 已檢查 MLComputePlan 以驗證運算裝置分派(iOS 17.4+)
  • [ ] 處理多個輸入時使用批次預測
  • [ ] 模型大小適合部署策略(bundle、Background Assets、ODR)
  • [ ] 已在目標裝置上測試記憶體(尤其是 RAM 較少的舊裝置)
  • [ ] 預測在除錯器外執行以獲得準確的效能測量

參考資料