前言
目前自己在项目中移动端常用的推理框架有3个:
- TensorFlow Lite:谷歌开发维护,通用平台;
- MNN:阿里开发维护,通用平台
- Core ML:苹果开发维护,iOS/MacOS平台;
具体差异和优劣可以直接大模型这里直接给出快速结论:
- 选 TensorFlow Lite:项目需要跨平台(Android+iOS + 其他)、通用场景(如普通图像分类 / 识别)、新手开发,优先考虑。
- 选 MNN:项目聚焦移动端(尤其是 Android)、追求极致性能 / 轻量化、国内场景(如鸿蒙 / 短视频),或需要高定制化,优先考虑。
- 选 Core ML:项目仅面向 iOS/macOS 平台,追求最优性能和原生集成体验,优先考虑(可搭配 TFLite 做跨平台兜底)。
本篇基于iOS/macOS开发主要针对Core ML的高级特性
Core ML的高级特性
一、特性: Model Auto-Update -- 无需发版,C端自动静默更新模型
痛点场景:
苹果的审核周期大家都知道,发一次版本大概审核完需要很长时间;因此自动更新训练好的新版本模型很重要
技术原理:
有两种方式,一个是通过.mlmodelc(编译后的模型文件夹) 一个是直接使用.mlmodel
- 推荐使用
.mlmodelc(编译后的模型文件夹),加载速度比.mlmodel快,且无需客户端编译; - 若使用
.mlmodel,需在客户端调用MLModel.compileModel(at:)编译后再加载(会增加首次使用的耗时)。
这里只介绍使用mlmodelc的方式:苹果并没有直接提供自动更新模型的API,但是可以通过动态加载编译后的.mlmodelc文件,可实现运行时模型替换。实现步骤如下
- 将新模型(.mlmodel)在服务器端预编译为.mlmodelc(使用coremlcompiler)
- App启动时检查本地模型版本,若服务器有新版本则下载.mlmodelc包
- 使用MLModel.compileModel(at:)验证并加
// 1. 从服务器下载.mlmodelc压缩包并解压到
// Documents目录
let modelURL = FileManager.**default**.urls(**for**: .documentDirectory, **in**: . userDomainMask).first!.appendingPathComponent("updated_model.mlmodelc")
// 2. 编译(实际是验证完整性)
let compiledURL = try MLModel.compileModel(at: modelURL)
// 3. 动态加载新模型
let newMode = try MLModel(contentsOf: compiledURL)
// 替换全局模型实例
currentRecommendationModel = newModel
注意事项
- 权限问题:需确保.mlmodelc文件位于App沙盒内(如Documents或Caches目录)
- 版本校验:务必在下载后校验模型哈希值,防止中间人攻击
- 回滚机制:保留旧模型,新模型加载失败时自动回退
二、特性:Flexible Input Shapes — 动态适配任意分辨率
痛点场景:
你的图像识别App需兼容前置/后置摄像头(不同分辨率),甚至用户上传的网络图片。硬编码固定输入尺寸(如224x224)会导致拉伸失真或裁剪丢失关键信息。
技术原理:
两种解决方式:1、做好对应模型的图像前处理,处理成模型需要的对应分辨率和颜色模式。2、在模型层进行处理 第一种最常用这里主要讲第二种
在模型转换阶段,通过coremltools声明可变维度(RangeDim或EnumeratedShapes),使CoreML运行时能接受指定范围内的任意尺寸
import coremitools as ct
# 定义动态输入:高度和宽度可在128~1024间任意取值
input_shape = ct.Shape(
shape=(1, 3, ct.RangeDim(128, 1024), ct.RangeDim(128, 1024))
)
#转换模型
mlmodel = ct.convert(
source_model, inputs=[ct.lmageType(name="input",shape=input_shape)]
)
mlmodel.save("dynamic_model.mlmodel")
// 直接传入原始图像(如640×480)
let prediction = try model.prediction(from:originalPixelBuffer)
// CoreML自动处理内部缩放/填充,无需预处理
兼容性提示
- iOS 13+ 支持RangeDim,iOS 12及以下仅支持固定尺寸
- 某些层(如全连接层)要求固定输入,需在模型设计阶段规避
三、特性:GPU/Neural Engine优先级控制—精细调度硬件资源
痛点场景:
后台运行的低功耗语音唤醒模型,若默认使用NeuralEngine(ANE)会显著增加电量消耗;而前台实时AR应用则需全力调用ANE以保证帧率。
技术原理:
通过MLModelConfiguration.computeUnits显式指定计算单元:
- .cpuOnly:仅CPU,功耗最低
- .cpuAndGPU:CPU+GPU,平衡性能与功耗
- .all:CPU+GPU+ANE,极致性能
let config = MLModelConfiguration()
//根据场景切换
if isInBackground {
config.computeUnits = .cpuOnly // 后台省电模式
} else if UIDevice.current.isCharging {
confg.computeUnits =.all // 充电时全速运行
} else {
config.computeUnits =.cpuAndGPU // 平衡模式
}
let model = try MyModel(configuration: config)
性能对比 (iPhone 14 Pro)
模式 | ANE占用率| 电池消耗(%/min)| 推理延迟(ms) | .all | 95% | 1.8 | 12 | .cpuAndGPU | 0% | 0.9 | 28 | .cpuOnly | 0% | 0.5| 65| 收益:后台任务续航延长2倍,前台交互流畅度达60FPS.
常见错误与解决方案
一、动态设定shape崩溃
- 错误:未在转换时声明RangeDim,运行时传入非标准尺寸
- 解决:严格校验输入尺寸范围,或使用flexible_shape_utils工具
二、自动更新权限拒绝
- 错误:尝试从tmp目录加载.mlmodelc
- 解决:必须使用FileManager的合法目录(如Documents)
三、自定义层精度丢失
- 错误:在Swift中用Float32处理,而CoreML内部为Float16
- 解决:转换时指定compute_precision=ct.precision.FLOAT32
总结CoreML深度优化
- 是否有模型需要频繁更新?一尝试Auto-Update (特性1)
- 是否处理多尺寸输入?一 启用Flexible Shapes(特性2)
- 是否有领域特定预处理?一构建Custom Layer (暂时没深入)
- 是否需个性化体验?一 探索On-Device Learning (暂时没深入)
- 是否在意电池消耗?一 配置Compute Units(特性3)