大多数浏览器和
Developer App 均支持流媒体播放。
-
使用 Swift 在 Create ML 中进行控制训练
有了 Create ML 框架,开发模型和自动化工作流程将比以往更容易。 我们将向你展示如何在训练机器学习模型的同时探索它们并与之交互,从而帮助你快速开发更优良的模型。了解 Create ML 中的训练控制功能如何利用 checkpointing API 暂停、保存、恢复和扩展训练过程,从而自定义训练工作流程。 并了解如何使用 Combine API 程序化监视进度。 如果你还不熟悉 Create ML 并且有兴趣了解训练机器学习模型,建议观看“介绍Create ML APP”。
资源
相关视频
WWDC21
WWDC20
WWDC19
-
下载
(你好 WWDC 2020) 你好 欢迎来到 WWDC (使用 SWIFT 在 CREATE ML 中进行控制训练) (LIZI OTTENS 机器学习经理) 你好 我叫 Lizi 是 Create ML 团队的一名 机器学习经理 今天 我很高兴地介绍一组用于 Create ML 中 进行检查点和异步训练的新 API 为了确保我们步调一致 让我们讨论一下 Create ML app 和 Create ML 框架 Create ML app 是非常棒的 如果你想轻松地创建模型 而不需要编写一行代码 我建议你从这里开始
在后台 该 app 是由一个 丰富且易于使用的 API 驱动的 即 Create ML 框架 对于 macOS Big Sur 我们将为你带来一套强大而灵活的 API 用于控制你的训练工作流程 这将使你 能够定制创建机器学习模型的方式 并利用所有驱动该 app 的相同技术 下面让我们先回顾一下训练过程
如果你想训练一个模型 你首先要做的是收集一些数据
你还需要查看所有可用的训练选项 如果你正在使用 Create ML 那么你总是可以接受默认的参数设置 但是理解这些选项的含义并没有什么坏处
接下来 预处理 有些任务需要提取特性 或将数据转换为正确的结构 例如 对于原始音频 可以通过获取它的光谱图进行转换 或者对于视频 可以从每一帧中提取其关键点 然后 训练 这是个有趣的部分 我们会花一些时间深入研究这个 所以我们现在先不讨论它 (设置、预处理、训练、评估) 接下来 我们通过向模型提供新数据来评估它 这里 重要的是使用模型在训练中 没有看到的不同数据集 这将帮助你确定你的模型是否在归纳 而不是记住了训练时的例子 此时 你可能意识到 你的模型并不完全是你所期望的 也许它需要更多的数据 也许它需要一套不同的训练参数 或者它只是需要多训练一些时间 模型开发是一个迭代的过程 所以你可以返回设置或训练 直到你的模型满足你的需求
(部署) 一旦你满意了 你就可以使用训练过的模型来执行预测 此时 你已经准备好部署了 说到训练过程 我们来谈谈控制的问题 训练控制主要是要有暂停和恢复的能力 也要有观察和改变训练过程的能力
你可以在任何时候停止训练
为什么这很重要? 训练一个机器学习模型需要时间 特别是 训练一个 对象检测模型可能需要 5 个小时 假设你正在训练 几个小时后 训练停止了 因为它达到了最大的迭代次数
但是你会发现损失仍然在减少 仍然有空间趋近到一个更好的模型 如果没有训练控制 你将不得不从头开始 并选择更大数量的最大迭代 但是现在你可以从训练停止的地方继续
这一点很重要的另一个原因是 结果是特定于你试图达到的目标 使用内置机制来评估模型 并不一定会 为你的特定用例提供最好的结果 在风格转换中尤其如此 因为模型的质量是主观的 例如 在这里 模型将鱼马赛克风格 应用到舞者的照片上 由你决定什么时候是足够好的 并且停止训练 此外 选择什么时候停止是特定于你的需要 也许你需要一个原型的模型 而不想在它上面花费太多时间 或者这是一个很重要的模型 并且你愿意花费额外的时间来训练 以达到最好的效果 使用训练控制 你可以定义自定义停止条件 这意味着你不受迭代次数的限制 你可能有一个图像分类器的目标精度 或者你可能有一个特定的训练时间
训练控制在 Create ML app 中可用 但是使用 API 你可以根据需要自定义此行为 让我来为你展示
在此之前 你将调用一个模型构造函数 时间将停止 并返回一个经过训练的模型
使用新的异步 API 你现在可以调用返回作业的训练方法
训练方法采用常规训练参数 以及 sessionParameters 实际参数 该实际参数 可让你指定报告的步频和迭代总数 你可以省略实际参数 使用默认值
sessionParameters 的 第一个参数是 sessionDirectory 如果该目录不存在 则将创建该目录 在随后的调用中 目录的内容 将用于恢复前一个会话 如果你想启动一个新的会话 只需删除目录或选择一个新位置
接下来的两个参数 控制进度报告和检查点的步频 更小的更新间隔将提供更频繁的更新 但可能会以性能为代价
最后一个参数是迭代的总次数 训练将在这个迭代数字停止 但是不要担心 你可以在随后的训练调用中 增加这个数字 并且你可以在达到这个迭代数之前停止
从训练返回的作业是 MLJob 的一个实例 它代表一个活跃的训练作业 它包含进度发布器 检查点发布器 和结果发布器
这些利用了 Combine 框架 你可以在 WWDC 19 的 “Combine 介绍”和“Combine 实践”中 了解更多
MLJob 还提供了一个取消方法 这样你可以在任何时候轻松地停止训练 为了让你有所了解 下面是你如何使用结果发布器 来获得一个训练好的模型 首先 在结果发布器上注册一个接收器 它的第一个闭包在完成时被调用 并收到成功或失败的结果 在失败的情况下 它还会收到一个错误 所以你应该在这里处理错误
第二个闭包在训练完成时 收到最终的模型
最后 存储订阅 直到完成订阅
你可以观察进度 比如完成的分数 当前的训练迭代、迭代的总次数 以及定制的指标 比如当前的损失或精度值
例如 你可以通过访问 MLJob 上的 进度发布器来观察和打印进度 注意 这是 “从基础开始的进度”的标准实例 要访问自定义指标 我们首先注册一个接收器闭包 为防患于未然 我们对作业进行弱捕捉 以避免保留周期
接下来 我们确保作业仍然存在 并创建一个 MLProgress 实例 MLProgress 帮助程序类型 允许我们从“基础进度”实例 访问自定义指标 注意 在某些情况下可能无法初始化 MLProgress 的实例 比如刚刚 创建了一个新会话或开始了一个新阶段 我们通过提前返回来处理
然后我们可以访问所有的进度信息 如当前项目和项目总数 在特性提取中 项目是指当前被处理的文件 或记录 对于训练来说 它是指当前的迭代次数 最后 我们可以得到训练指标 指标仅在训练阶段可用 并且特定于每个任务 有些任务具有精确性 而其他任务具有损失或其他指标 如果你提供了验证数据 那么你还可以访问验证指标 现在让我们看看如何使用新的异步 API 训练模型
在观看了“在 Create ML 中 构建图像和视频风格转换模型”之后 我对训练一些风格转换模型非常感兴趣 所以我开始在 Playgrounds 上 设置我的模型创建工作流 这里 我有一些不同风格的图片 可以在我的资源包中使用 我也有一些不同艺术家的验证图片 我想将其风格化
在我的 Playground 中 我首先指定了 我要使用的风格图像和验证图像的 URL
关于 Playgrounds 的一个好处是 它允许我 在运行的过程中在内部可视化我的数据 例如 我可以看到我指定的风格 和我们将使用的验证图像
接下来我要做的是指定一个 dataSource 来定义我们的训练数据 我将添入 styleImageURL 和 contentDirectory
然后初始化我们设置会话所需的参数
我先初始化 MLTrainingSessionParameters 它需要一个 sessionDirectory 我还可以提供一个报告间隔 来指定进度报告之间的迭代次数 一个检查点间隔 来指定检查点之间的迭代次数 以及扩展训练的迭代
接下来 我将设置模型参数
现在我们准备开始训练
为了开始训练过程 我将对 我想要创建的模型类型调用训练方法
这里 我将提供我的 dataSource
trainingParameters…
和 sessionParameters
它返回一个 MLJob 的实例
MLJob 包含一个结果发布器 我们可以使用结果发布器 来接收训练期间发生的错误 并在完成之后访问模型 我还可以利用作业 来控制和观察训练的进行 例如 如果我想以编程方式访问进度 我可以通过使用进度发布器来实现 由于这是“基础进度”的一个实例 所以我将通过提供该属性的主要路径 来指定 我们想要 fractionCompleted 属性
接下来 我将调用接收器 它将返回一个可撤销性 稍后我们可以使用它来终止订阅 除了 fractionCompleted 之外 我还可以访问 特定于我试图训练的 ML 模型的指标 为此 我将从“基础进度”的实例中 初始化一个 MLProgress 的实例 然后 我可以访问特定的指标 如风格损失和内容在迭代中的损失 借助 Xcode Playgrounds 的强大功能 当我点击“训练”时 我可以将正在发生的进度可视化
而在 Playgrounds 中 我可以查看以最新的值或图形显示的进度 因为它的训练跨越迭代 这非常酷
新版本还提供了控制训练过程的能力 通过暂停、恢复和扩展来进行控制 如果要停止 我只需要调用取消就可以了
要恢复 只需使用与前面 相同的会话目录调用训练就可以了
它几乎是自动的 (检查点) 既然你已经看到了 如何使用新的异步 API 训练模型 那么我想介绍一下检查点 有了检查点 你就可以随着时间捕捉模型的状态 你可以使用它们来查看训练的进展情况 并轻松地与以前的结果进行比较 此外 如果你认为某个检查点需要更多的训练 你可以使用它来恢复你中断的地方
MLCheckpoint 是表示训练 或特性提取检查点的结构体
与其他框架不同的是 MLCheckpoints 是在 使用新的异步 API 时自动创建的
并且很容易从中恢复 你所需要做的 就是提供一个 sessionDirectory 在其中启动新的会话或继续现有的会话 这在比如 Playgrounds 这样的 环境中是非常好的 所以你可以 在不失去进度的情况下进行迭代
让我们暂时回到训练过程上来 我想聚焦于两个阶段 即预处理和训练 这是生成检查点的两个阶段
让我们从预处理开始
在这个阶段中 我们获取数据元素 比如表中的单个文件或行 并处理每个元素 衡量进度的标准是 这些元素已经处理了多少
每隔几个元素 我们就将当前进程存储为一个检查点 我们只保留最后一个预处理检查点 因为这个阶段是严格的加法
训练阶段也是一个迭代的过程 该模型通过称为迭代的分立步骤进行改进 一次迭代包括通过网络运行一批数据 计算损失和更新权值
训练检查点是模型在特定迭代中的状态 与预处理不同 训练保留了你创建的所有检查点 随着训练的进行 我们也会得到损失或准确率等指标 这些指标与检查点一起存储 以帮助你识别它们
让我们看一些代码 下面是使用检查点发布器的示例 所有检查点都会自动保存到会话文件夹中 但是这个发布器提供了生成自定义指标 生成模型或完全停止训练的机会 这是你在创建新检查点时 采取行动的一个机会
例如 这就是从检查点生成模型的方法 一旦有了模型 就可以将其保存到磁盘 或使用它进行预测
注意 你只能从训练检查点创建模型 而不能从特性提取检查点创建 因此你应该检查它的训练阶段
你可以在特性提取阶段对图像分类 声音分类和动作分类 设置检查点 训练检查点可用于动作分类 对象检测、风格转换和活动分类 现在我们来谈谈会话 可以将会话 视为所有检查点及其元数据的聚合 这由 MLTrainingSession 类型表示 它允许你查询诸如何时创建的会话 当前训练阶段和当前迭代编号等细节
如果你想直接访问会话 你可以对一种模型类型 使用 restoreTrainingSession 方法
然后 你可以做一些事情 比如访问损失值 你还可以删除不再需要的检查点 释放磁盘空间 现在让我们看看检查点的实际操作
我们已经看到了如何在 Playgrounds 中 训练风格转换模型时 观察 MLJob 实例的进展和结果 回到我们相同的 Playground 中 我们可以看到 MLJob 包含一个检查点发布器 我们可以用它来观察不同时间的检查点 对于风格转换 我可以访问 每个检查点上的风格化验证图像 以帮助我们 可视化我们的模型在训练时的进展情况
为此 我可以使用 compactMap 将检查点转换为我们需要的表单 然后将图像 URL 映射到 NSImages 中
然后 我将调用接收器 将订阅服务器附加到检查点发布服务器 并将 AnyCancellable 返回类型存储到订阅列表中
现在我们可以看到 经过一段时间的风格化验证图像 随着模型的训练 直接在代码页就可以看到 但如果我们能更进一步 不是更好吗? 现在 使用 SwiftUI 我们可以轻松地在 Xcode Playgrounds Live View 中渲染更多
我要做的第一件事 是找到一个新的视图对象 并向它传递风格图像、风格化验证图像 和原始验证 这样我们就能比较模型随时间的运行情况 由于我们使用 UI 我将在主队列上接收这个 然后我将设置 Playground Live View 以便我们能在那里填充结果
现在 Live View 栩栩如生了 现在 我们可以在模型训练时将它可视化 与我们的参考风格和验证图像一起 这要归功于新的检查点和异步训练 API
让我们回顾一下这次视频的内容 我们学习了如何使用 Create ML 中 可用的新异步 API 训练模型 我们学习了检查点以及如何使用它们 来生成模型和恢复训练 最后 我们看到了 如何使用 Combine 发布器 来获得进度报告和处理结果 那么接下来你要做什么呢? 如果你需要自定义你的训练工作流程 我鼓励你利用这些新工具 训练控制让你不仅可以控制你的模型质量 而且可以控制整个训练过程 不管你是在开发模型、自动化工作流 还是在 macOS apps 中按需创建模型 谢谢大家 请享受剩下的 WWDC
(你好 WWDC 2020)
-
-
4:39 - Synchronous training
let model = try MLActivityClassifier(...)
-
4:47 - Asynchronous Training
let job = try MLActivityClassifier.train(..., sessionParameters: sessionParameters)
-
4:58 - Setting up training parameters
// Session parameters can be provided to `train` method. let sessionParameters = MLTrainingSessionParameters( sessionDirectory: sessionDirectory, reportInterval: 10, checkpointInterval: 100, iterations: 1000 ) let job = try MLActivityClassifier.train(..., sessionParameters: sessionParameters)
-
6:21 - Register a sink to receive model
// Register a sink to receive the resulting model. job.result.sink { result in // Handle errors } receiveValue: { model in // Use model } .store(in: &subscriptions)
-
7:07 - Getting training progress
// 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)
-
8:55 - Demo 1: Setup
let style = NSImage(byReferencing: styleImageURL) let validation = NSImage(byReferencing: validationImageURL) var iterations = 500 var progressInterval = 5 var checkpointInterval = 5 let sessionDirectory = URL(fileURLWithPath: "\(NSHomeDirectory())/\(experimentID)") let sessionParameters = MLTrainingSessionParameters(sessionDirectory: sessionDirectory, reportInterval: progressInterval, checkpointInterval: checkpointInterval, iterations: iterations) let trainingParameters = MLStyleTransfer.ModelParameters( algorithm: .cnn, validation: .content(validationImageURL), maxIterations: iterations, textelDensity: 416, styleStrength: 5)
-
10:03 - Demo 1: Training
var subscriptions = [AnyCancellable]() let job = try MLStyleTransfer.train(trainingData: dataSource, parameters: trainingParameters, sessionParameters: sessionParameters) job.result.sink { result in print(result) } receiveValue: { model in try? model.write(to: sessionDirectory) } .store(in: &subscriptions)
-
10:51 - Demo 1: Progress
job.progress .publisher(for: \.fractionCompleted) .sink { completed in _ = completed guard let progress = MLProgress(progress: job.progress) else { return } if let styleLoss = progress.metrics[.styleLoss] { _ = styleLoss } if let contentLoss = progress.metrics[.contentLoss] { _ = contentLoss } } .store(in: &subscriptions)
-
12:04 - Demo 1: Cancel & Resume
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)
-
14:26 - Observing checkpoints
let job = try MLActivityClassifier.train(..., sessionParameters: sessionParameters) // Register for receiving checkpoints. job.checkpoints.sink { checkpoint in // Process checkpoint } .store(in: &subscriptions)
-
14:50 - Generating a model from a checkpoint
// 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)
-
15:40 - Working with a session
let session = MLObjectDetector.restoreTrainingSession(sessionParameters: sessionParameters) let losses = session.checkpoints.compactMap { $0.metrics[.loss] as? Double }
-
15:48 - Removing checkpoints from a session
let session = MLObjectDetector.restoreTrainingSession(sessionParameters: sessionParameters) // Save space by removing some checkpoints session.removeCheckpoints { $0.iteration < 500 }
-
16:13 - Demo 2: Visualizing Style Transfer Checkpoints
job.checkpoints .compactMap { $0.metrics[.stylizedImageURL] as? URL } .map { NSImage(byReferencing: $0) } .sink { image in let _ = image } .store(in: &subscriptions)
-
16:24 - Demo 2: Visualizing Checkpoints with SwiftUI + 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)
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。