Build dynamic iOS apps with the Create ML framework
Machine Learning & AI 进阶 26m

使用 Create ML 框架构建动态 iOS app

Build dynamic iOS apps with the Create ML framework

2021年6月11日

在 Apple 官方观看视频

一句话判断

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 支持自定义的 EstimatorTransformer,可以扩展框架的能力。
  • MLTask 支持 checkpoint/resume,训练被中断后可以从上次保存的权重继续。
  • 如果设备端训练效果不理想,可以考虑用 Core ML 的 on-device update 功能做增量更新。
WWDC 2021