在 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。
目录
- 加载模型
- 模型配置
- 进行预测
- MLTensor(iOS 18+)
- 使用 MLMultiArray
- 图像预处理
- 多模型流水线
- Vision 集成
- 性能分析
- 模型部署
- 内存管理
- 常见错误
- 审查清单
- 参考资料
加载模型
自动生成类
当您将 .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:)。仅在同时传递 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()、直接惰性缓冲区访问或同步提取等示例;在张量流水线外部执行不受支持的 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 流水线(
CoreMLRequestiOS 18+ 或VNCoreMLRequest)进行正确预处理 - [ ] 检查
MLComputePlan以验证计算设备调度(iOS 17.4+) - [ ] 处理多个输入时使用批量预测
- [ ] 模型大小适合部署策略(捆绑、Background Assets、ODR)
- [ ] 在目标设备上测试内存(尤其是 RAM 较少的旧设备)
- [ ] 在调试器外部运行预测以获得准确的性能测量
参考资料
- 模式和代码:references/coreml-swift-integration.md
- 模型转换和优化(Python 端):涵盖在
apple-on-device-ai技能中 - Apple 文档:Core ML |
MLModel |
MLTensor |
MLComputePlan |
Background Assets






