coreml

coreml

热门

在 iOS 应用中集成 Core ML 模型,实现设备端机器学习推理。涵盖模型加载(.mlmodel、.mlpackage、.mlmodelc)、使用自动生成类和 MLFeatureProvider 进行预测、计算单元配置(CPU、GPU、神经网络引擎)、MLTensor、VNCoreMLRequest、MLComputePlan、多模型流水线以及部署策略。适用于加载 Core ML 模型、进行预测、配置计算单元或分析模型性能的场景。

936Star
47Fork
更新于 2026/7/15
SKILL.md
readonly只读
name
coreml
description

在 iOS 应用中集成 Core ML 模型,实现设备端机器学习推理。涵盖模型加载(.mlmodel、.mlpackage、.mlmodelc)、使用自动生成类和 MLFeatureProvider 进行预测、计算单元配置(CPU、GPU、神经网络引擎)、MLTensor、VNCoreMLRequest、MLComputePlan、多模型流水线以及部署策略。适用于加载 Core ML 模型、进行预测、配置计算单元或分析模型性能的场景。

Core ML Swift 集成

在 iOS 应用中加载、配置和运行 Core ML 模型。本技能涵盖 Swift 端的模型加载、预测、MLTensor、性能分析和部署。

范围边界: Python 端的模型转换、优化(量化、调色板化、剪枝)和框架选择属于 apple-on-device-ai 技能。本技能仅负责 Swift 集成。

有关完整的代码模式,包括基于 actor 的缓存、批量推理、图像预处理和测试,请参阅 references/coreml-swift-integration.md

目录

加载模型

自动生成类

当您将 .mlmodel.mlpackage 添加到应用目标时,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+,并且编译后的模型必须缓存,以免应用每次启动都重新编译。

模型配置

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()、直接惰性缓冲区访问或同步提取等示例;在张量流水线外部执行不受支持的 DSP/统计操作,或使用来源确认的张量操作。

使用 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)。

模型部署

捆绑小型离线关键模型。对于新的较大或可更新资产,优先使用 Background Assets;仅对现有 ODR 项目保留 On-Demand Resources。编译下载的源模型一次,按版本持久化 .mlmodelc,并在最低支持的物理设备上测试加载、首次/重复预测、生命周期转换和内存。加载 部署模式 以获取实现细节。

内存管理

  • 后台卸载: 当应用进入后台时释放模型引用以释放 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+)
  • [ ] 处理多个输入时使用批量预测
  • [ ] 模型大小适合部署策略(捆绑、Background Assets、ODR)
  • [ ] 在目标设备上测试内存(尤其是 RAM 较少的旧设备)
  • [ ] 在调试器外部运行预测以获得准确的性能测量

参考资料