使用 Create ML 框架构建动态 iOS app
Build dynamic iOS apps with the Create ML framework
2021年6月11日
一句话判断
Create ML 框架在 iOS 15 可以直接在设备上训练模型了——这意味着你的 app 可以根据用户个人数据做个性化适配,而不用把隐私数据上传到云端。
这场 Session 讲了什么
Create ML 之前只能在 Mac 上训练模型,然后把训练好的 .mlmodel 文件打包到 app 中。iOS 15 打破了这个限制:CreateMLComponents 框架允许你在 iOS 设备上直接训练和推理机器学习模型。
Session 展示了几个使用场景。照片分类 app 可以根据用户的相册训练一个专属的分类器。健身 app 可以根据用户的动作数据优化姿态检测模型。文本输入 app 可以学习用户的打字习惯来改进自动补全。
核心 API 是 MLPipeline,它把数据预处理、特征提取和模型训练串成一条流水线。配合 MLUpdateTask,你可以在后台执行训练任务,不会阻塞 UI。训练完成后,模型直接保存在设备的 Application Support 目录中,下次启动 app 时加载即可。
值得深挖的点
设备端训练的硬件要求
设备端训练依赖 Apple Neural Engine(ANE)和 GPU。在 A12 及更新的芯片上,训练速度可以接受——一个图片分类任务(几千张图)通常几分钟内完成。但在 A11 及更老的设备上,训练可能需要几十分钟甚至更久。
Session 建议:对于计算密集型的训练任务,使用 MLUpdateTask 的异步模式,并通过 progressHandler 报告训练进度。如果训练被中断(比如 app 进入后台),可以从 checkpoint 恢复,不需要从头开始。
数据收集与隐私的平衡
设备端训练的最大优势是隐私保护——用户数据不出设备。但这也带来了数据量的问题:单个用户的数据可能不足以训练一个好的模型。Session 建议使用「基础模型 + 个性化微调」的策略:app 内置一个预训练好的基础模型,然后根据用户数据在设备上做 fine-tune。这样即使用户数据很少,模型也能有一个不错的起点。
代码片段
// 在设备上训练一个图片分类器
import CreateMLComponents
import Vision
@MainActor
class ImageClassifierTrainer: ObservableObject {
@Published var trainingProgress: Double = 0
@Published var isTraining = false
func trainModel(
positiveImages: [URL],
negativeImages: [URL]
) async throws -> MLImageClassifierModel {
isTraining = true
defer { isTraining = false }
// 准备训练数据
var trainingData: [(URL, Int)] = []
trainingData += positiveImages.map { ($0, 1) } // 正样本标记为 1
trainingData += negativeImages.map { ($0, 0) } // 负样本标记为 0
// 配置训练参数
let featureExtractor = ImageFeatureExtractor()
.set(\.modelType, to: .vision(print: .none))
let classifier = ImageClassifier()
.set(\.maxIterations, to: 50)
.set(\.augmentationOptions, to: [.flip, .rotate])
// 构建训练流水线
let pipeline = featureExtractor.appending(classifier)
// 执行训练
let model = try await pipeline.fitted(
to: trainingData,
eventHandler: { event in
if case .trainingProgress(let progress) = event {
DispatchQueue.main.async {
self.trainingProgress = progress
}
}
}
)
return model
}
}
// 使用训练好的模型进行推理
func classify(image: CGImage) async throws -> Int {
let model = try await loadOrTrainModel()
let prediction = try model.prediction(from: image)
return prediction
}
// 持久化保存模型
func saveModel(_ model: MLImageClassifierModel) throws {
let url = getModelSaveURL()
try model.write(to: url)
}
func loadOrTrainModel() async throws -> MLImageClassifierModel {
let url = getModelSaveURL()
if FileManager.default.fileExists(atPath: url.path) {
return try MLImageClassifierModel(contentsOf: url)
}
// 首次使用,用基础数据训练
return try await trainModel(
positiveImages: loadBundledPositiveImages(),
negativeImages: loadBundledNegativeImages()
)
}
最佳实践
设备端训练不应该在用户首次启动 app 时就执行。应该在用户产生了足够的数据(比如使用了几天)后再提示「是否启用个性化功能」。训练过程要在后台进行,并且允许用户取消。
模型文件的大小需要关注。一个 fine-tune 后的图片分类模型可能在 10-50MB。如果你的 app 有多个模型,考虑用 MLModelCollection 来管理,系统会在存储空间不足时自动清理不常用的模型。
还有什么值得关注
- Create ML Components 支持自定义的
Estimator和Transformer,可以扩展框架的能力。 MLTask支持 checkpoint/resume,训练被中断后可以从上次保存的权重继续。- 如果设备端训练效果不理想,可以考虑用
Core ML的 on-device update 功能做增量更新。