WWDC Quick Look 💓 By SwiftGGTeam
Control training in Create ML with Swift

Control training in Create ML with Swift

观看原视频

Highlight

Create ML 在 macOS Big Sur 引入异步训练、checkpoint 和 Combine publisher API,让开发者能在 Swift 中观察训练指标、暂停并恢复长时间训练、从中间 checkpoint 导出模型。

核心内容

训练机器学习模型经常卡在等待上。以前在 Create ML 里调用模型构造器,代码会等到训练结束才返回模型。遇到耗时数小时的 object detection(物体检测)训练时,如果达到最大 iteration(迭代次数)后 loss(损失值)还在下降,开发者只能重新设置更大的训练次数,再从头开始。(02:54

macOS Big Sur 的 Create ML framework 把训练流程拆成可观察、可中断、可恢复的 MLJob。开发者调用新的 train 方法启动异步训练,然后通过 resultprogresscheckpoints 三个 publisher 处理最终模型、训练指标和中间 checkpoint。cancel() 可以停止训练,同一个 sessionDirectory 会在下一次训练时恢复已有进度。(04:46

这套 API 的价值在 Style Transfer(风格迁移)里很直观。风格迁移的好坏带有主观判断,内置评估指标不一定能替你决定何时停止。Session 把 checkpoint 中的 stylized validation image(风格化验证图片)取出来,再用 SwiftUI 放进 Playground Live View。训练仍在继续,开发者已经能看到模型每隔几轮产生的真实效果。(16:02

详细内容

用 sessionParameters 定义训练会话

04:58)新的训练入口接收 MLTrainingSessionParameters。它把训练会话保存在哪、多久报告一次进度、多久保存一次 checkpoint、最多训练多少轮,都放在同一个参数对象里。

// Session parameters can be provided to `train` method.
let sessionParameters = MLTrainingSessionParameters(
    sessionDirectory: sessionDirectory,
    reportInterval: 10,
    checkpointInterval: 100,
    iterations: 1000
)

let job = try MLStyleTransfer.train(trainingData: dataSource,
                                    parameters: trainingParameters,
                                    sessionParameters: sessionParameters)

关键点:

  • sessionDirectory 是训练状态的落盘位置。目录存在时,Create ML 会用它恢复已有 session。
  • reportInterval 控制 progress report(进度报告)的频率。间隔越小,反馈越密集,也可能带来额外开销。
  • checkpointInterval 控制 checkpoint 保存频率。它决定你能以多细的粒度回看训练过程。
  • iterations 是本轮训练的目标迭代次数。后续调用 train 时可以把这个数字调大,让训练继续往前走。
  • train 返回 MLJob,训练从同步等待变成异步任务。

用 Combine 取回结果和进度

06:21MLJobresult publisher 在训练结束时返回模型,失败时返回错误。Session 使用 Combine 的 sink 接收完成状态和模型值。

// Register a sink to receive the resulting model.
job.result.sink { result in
    // Handle errors
}
receiveValue: { model in
    // Use model
}
.store(in: &subscriptions)

关键点:

  • result 的 completion closure 负责处理成功或失败状态。
  • receiveValue closure 拿到最终训练好的模型。
  • .store(in: &subscriptions) 保留订阅,否则异步训练还没结束,订阅就可能被释放。

07:07)训练中的 progress 仍然是 Foundation 的 Progress。Create ML 提供 MLProgress helper type(辅助类型),用来读取 loss、accuracy 等模型相关指标。

// Observing progress details
job.progress.publisher(for: \.fractionCompleted)
    .sink { [weak job] fractionCompleted in
        guard let job = job, let progress = MLProgress(progress: job.progress) else {
            return
        }
        print("Progress: \(fractionCompleted)")
        print("Iteration: \(progress.itemCount) of \(progress.totalItemCount ?? 0)")
        print("Accuracy: \(progress.metrics[.accuracy] ?? 0.0)")
    }
    .store(in: &subscriptions)

关键点:

  • publisher(for: \.fractionCompleted) 监听 Foundation Progress 的完成比例。
  • [weak job] 避免订阅闭包长期持有训练任务。
  • MLProgress(progress:) 在新 session 刚创建或切换 phase(阶段)时可能初始化失败,所以代码提前返回。
  • itemCount 在训练 phase 代表当前 iteration,在 feature extraction(特征提取)phase 代表已处理的文件或记录。
  • progress.metrics[.accuracy] 读取任务相关指标。不同训练任务可能提供 accuracy、loss 或验证集指标。

暂停并恢复长时间训练

12:04)停止训练只需要调用 cancel()。恢复训练时,再次调用同一个模型类型的 train,并传入相同的 sessionDirectory

job.cancel()

let resumedJob = try MLStyleTransfer.train(
    trainingData: dataSource,
    parameters: trainingParameters,
    sessionParameters: sessionParameters)

resumedJob.progress
    .publisher(for: \.fractionCompleted)
    .sink { completed in
        _ = completed

        guard let progress = MLProgress(progress: resumedJob.progress) else { return }
        if let styleLoss = progress.metrics[.styleLoss] { _ = styleLoss }
        if let contentLoss = progress.metrics[.contentLoss] { _ = contentLoss }
    }
    .store(in: &subscriptions)

resumedJob.result.sink { result in
    print(result)
}
receiveValue: { model in
    try? model.write(to: sessionDirectory)
}
.store(in: &subscriptions)

关键点:

  • cancel() 让开发者在达到固定 iteration 前主动停止训练。
  • sessionParameters 里的目录决定恢复位置。要开始新训练,就删除旧目录或换一个路径。
  • 恢复后的 resumedJob 仍然提供 progress 和 result publisher。
  • styleLosscontentLoss 来自 Style Transfer 任务,说明 metrics 会随模型类型变化。
  • model.write(to:) 把完成后的模型写入 session 目录,方便后续检查或部署。

从 checkpoint 观察和导出模型

14:26)异步训练会自动生成 checkpoint。checkpoints publisher 给开发者一个处理新 checkpoint 的入口,可以记录自定义指标、生成模型,或按自己的规则停止训练。

// Register for receiving checkpoints.
job.checkpoints.sink { checkpoint in
    // Process checkpoint
}
.store(in: &subscriptions)

关键点:

  • checkpoint 会保存到 session folder(会话目录)中。
  • publisher 在新 checkpoint 产生时触发。
  • feature extraction checkpoint 和 training checkpoint 都可能出现,后续处理要区分 phase。

14:50)只有 training checkpoint 能直接生成模型。Session 在代码里先检查 phase,再把 checkpoint 转成 MLActivityClassifier

// Generate a model from a checkpoint
guard checkpoint.phase == .training else {
    // Not a training checkpoint, can't create model yet.
    return
}

let model = try MLActivityClassifier(checkpoint: checkpoint)
try model.write(to: url)

关键点:

  • checkpoint.phase == .training 是导出模型前的必要检查。
  • MLActivityClassifier(checkpoint:) 从某个训练中间态创建模型。
  • model.write(to:) 能把中间模型保存下来,用于比较或提前试用。
  • transcript 明确提到 training checkpoint 可用于 action classification、object detection、Style Transfer 和 activity classification。

管理历史 session 和 checkpoint 空间

15:40MLTrainingSession 是 checkpoint 和 metadata(元数据)的集合。开发者可以恢复一个 session,查看创建时间、当前 phase、iteration,以及历史 metrics。

let session = MLObjectDetector.restoreTrainingSession(sessionParameters: sessionParameters)

let losses = session.checkpoints.compactMap { $0.metrics[.loss] as? Double }

关键点:

  • restoreTrainingSession 用同一组 sessionParameters 找回训练会话。
  • session.checkpoints 让你离线分析整段训练历史。
  • metrics[.loss] 可以用来画 loss 曲线,判断是否需要延长训练。

15:48)checkpoint 会占用磁盘。Create ML 允许按条件删除不再需要的 checkpoint。

let session = MLObjectDetector.restoreTrainingSession(sessionParameters: sessionParameters)

// Save space by removing some checkpoints
session.removeCheckpoints { $0.iteration < 500 }

关键点:

  • removeCheckpoints 接收一个判断闭包。
  • $0.iteration < 500 会删除较早的 checkpoint。
  • 保留关键节点、删除早期节点,能在可追溯性和磁盘占用之间做取舍。

在 Playground 里可视化训练效果

16:24)Style Transfer checkpoint 会携带 stylizedImageURL。Session 把它转成 NSImage,再用 SwiftUI 拼成 Playground Live View。

job.checkpoints
    .compactMap { $0.metrics[.stylizedImageURL] as? URL }
    .receive(on: DispatchQueue.main)
    .map { NSImage(byReferencing: $0) }
    .sink { image in
        let _ = image

        let view = VStack {
            Image(nsImage: image)
                .resizable()
                .aspectRatio(contentMode: .fit)
            Image(nsImage: style)
                .resizable()
                .aspectRatio(contentMode: .fit)
            Image(nsImage: validation)
                .resizable()
                .aspectRatio(contentMode: .fit)
        }.frame(maxHeight: 1400)

        PlaygroundSupport.PlaygroundPage.current.setLiveView(view)
    }
    .store(in: &subscriptions)

关键点:

  • .compactMap 只保留带有 stylizedImageURL 的 checkpoint。
  • .receive(on: DispatchQueue.main) 把 UI 更新切回主线程。
  • VStack 同时展示当前风格化结果、style image(风格图)和 validation image(验证图)。
  • setLiveView 让训练过程直接出现在 Xcode Playground 中。

核心启发

训练进度面板

做什么:为内部模型训练工具加一个实时进度面板,显示 fraction completed、iteration、loss、accuracy 和验证集指标。

为什么值得做:Session 展示了 MLJob.progressMLProgress,训练指标可以在训练中读取,不必等最终模型生成后才判断质量。

怎么开始:在调用 train 后订阅 job.progress.publisher(for: \.fractionCompleted),用 MLProgress(progress:) 读取 metrics,再把数据写到日志、图表或 SwiftUI 界面。

可恢复的长时间训练任务

做什么:给 object detection、Style Transfer 或 activity classification 训练加上暂停、恢复和延长训练能力。

为什么值得做:transcript 提到 object detection 训练可能耗时五小时。若 loss 还在下降,复用 sessionDirectory 能继续已有训练进度。

怎么开始:把 sessionDirectory 固定到某个实验目录,创建 MLTrainingSessionParameters,停止时调用 job.cancel(),恢复时用同一目录再次调用 train

checkpoint 模型对比库

做什么:把训练过程中关键 checkpoint 导出成多个候选模型,让团队比较中间版本的实际效果。

为什么值得做:Create ML 会自动保存 training checkpoint,并允许通过 MLActivityClassifier(checkpoint:) 这类 initializer 生成模型。

怎么开始:订阅 job.checkpoints,先检查 checkpoint.phase == .training,再从 checkpoint 创建模型并写入带 iteration 编号的目录。

训练历史清理工具

做什么:写一个脚本扫描 session 目录,保留关键 iteration,删除早期或无价值 checkpoint。

为什么值得做:training checkpoint 会持续保存,长期实验会消耗磁盘。MLTrainingSession 提供恢复 session 和删除 checkpoint 的 API。

怎么开始:调用 restoreTrainingSession(sessionParameters:),读取 session.checkpoints 中的 loss 或 iteration,再用 removeCheckpoints 清理旧 checkpoint。

Playground 可视化实验台

做什么:把 Style Transfer 的 checkpoint 图片实时放进 Playground Live View,对比风格图、原验证图和当前输出。

为什么值得做:风格迁移质量带有主观判断,session 演示了通过 stylizedImageURL 直接观察中间效果。

怎么开始:从 checkpoint metrics 取出 .stylizedImageURL,转成 NSImage,在主线程用 SwiftUI 的 VStack 展示三张图。

关联 Session

评论

GitHub Issues · utterances