大多数浏览器和
Developer App 均支持流媒体播放。
-
使用异步预测改进 Core ML 集成
了解如何使用最新的 Core ML 执行引擎改进来加速 App 中的机器学习功能,以及积极的资源缓存是如何帮助推理和更快的模型加载。我们将会向你展示最新的异步预测选项,并探讨平衡性能和总体内存使用率的多种考量,从而帮助你打造反应敏捷的 App。深入了解 API 以帮助你理解并最大程度地提高模型中的硬件利用率。想要进一步了解有关优化 Core ML 模型使用的内容,请查看 WWDC23 中的”使用 Core ML Tools 进行机器学习模型压缩”。
资源
相关视频
WWDC22
WWDC21
-
下载
♪ ♪
Ben:大家好 我是 Ben Levine Core ML 团队的一名工程师 今天 我将谈谈 在 App 中 集成 Core ML 的新功能 在 App 中构建 智能体验从未如此简单 Xcode SDK 为你提供了 坚实的基础 让你可以利用和部署 由机器学习驱动的功能 而一系列特定领域框架 通过简单的 API 为你提供内置的智能功能 这些功能均由 Apple 训练和优化的模型驱动 并且这些模型 均通过 Core ML 执行 Core ML 框架为设备中运行的 机器学习模型提供引擎 从而 你可以轻松 为 App 部署个性化模型 Core ML 借助 Accelerate 和 Metal 系列框架 充分利用 Apple 芯片的 高性能计算能力 同时还可以抽象硬件细节 Core ML 的目的是帮你 在 App 中集成机器学习模型 今年 我们将关注 Core ML 的性能和灵活性 并对工作流程、API 接口 以及底层推断引擎进行了改进 在我们开始谈论工作流程并强调优化 Core ML 集成的 潜在发展机会之前 我们先来了解一下 你可以通过 更新至最新的操作系统 自动获得潜在性能优势
在比较 iOS 16 和 17 之间的相对预测时间时 你会发现 iOS 17 在你的大部分模型中速度都更快 推理引擎中的 加速方式附带操作系统 而且不需要你重新编译模型 或对代码进行修改 对于其他平台来说也是如此 当然 加速的程度 共同取决于模型和硬件 接下来我们进入正题 首先 我会回顾一下 在 App 中集成 Core ML 的工作流程 在回顾过程中 我会强调工作流程中 各部分的优化机会 接着 我会重点介绍 模型集成 并谈谈新 API 及其在计算可用性、模型生命周期 和异步预测中的表现 我先来回顾一下 Core ML 的工作流程 在 App 中集成 Core ML 一共包含两步 第一步 开发模型 第二步 在 App 中使用该模型 对于模型开发 你有以下几种选择 要开发属于自己的模型 最简单的方式之一就是使用 Create ML Create ML 不仅提供了多种 适用于常见机器学习任务的模板 同时还可以充分利用内置于 操作系统中高度优化的模型 该框架不仅可以指导你 完成模型开发工作流程 还可让你以交互的方式评估结果 如果你想进一步了解 请查看今年的 Create ML 视频
另一种模型开发的方式是 利用一种 python 机器学习框架 来训练模型 接着使用 CoreMLTools python 包 以将其转换为 Core ML 模型格式 最后 从 Apple 硬件的 精度和性能两方面 对你的模型进行评估 这一步非常重要 借助评估中的反馈 你可以重新审视其中部分步骤 以便进一步优化模型 在这些步骤中 有许多优化机会 对于训练而言 收集和选择 训练数据的方法十分重要 这些数据应与部署模型时传递的数据 以及用户手中的数据保持一致 你所选择的模型架构也十分重要 你可能会探索多个选项 并且每个选项 在训练数据要求、精度、大小 和性能之间都存在权衡 在训练时 这些权衡 可能无法完全体现 并需要对完整的 开发流程进行多次迭代 接下来是模型转换 CoreMLTools 提供了多种选择 来帮助你优化转换后模型的 精度、占用空间以及计算成本 你可以选择与 App 数据流 最为匹配的输入和输出格式 以避免产生不必要的副本 如果你的输入形状可以改变 那么你可以指定特定改变 不要只选择一种形状 或是在多种特定形状的 模型中来回切换 你可以对整个模型或单独操作 设置明确的计算精度 float32 和 float16 都能使用 除了计算精度 你还可以在一定程度上 控制模型参数的表示方式 CoreMLTools 附带了一组实用程序 用于训练后权重量化和压缩 这些使用程序可以帮助你 显著降低模型的占用空间 并提升其在设备上的性能 但为了发挥这些优势 模型精度可能会有所损失 针对该问题 一些新工具可以为你提供帮助 CoreMLTools 包中 有一个新的优化子模块 该子模块可以整合并更新 训练后的压缩实用程序 并可为 PyTorch 添加 新的量化感知训练扩展 这样 你便可以访问 由数据驱动的优化 从而在训练时保持量化模型的精度 并且在 Core ML 的 ML Program 模型类型中 该子模块还可以与 支持激活量化的新操作进行结合 想要进一步了解 请查看今年的使用 Core ML 压缩机器学习模型讲座
接下来是评估 在评估模型时 一种选择就是在 python 代码中 使用 CoreMLTools 直接在转换后的模型上运行预测 该过程会使用与 App 代码 相同的 Core ML 推理 stack 以便你可以快速检查 在模型转换过程中 你的选择 是如何影响模型精度和性能的 在评估和探索模型方面 Xcode 也提供了一些实用的工具 由于模型预览适用于 大多数常见的模型类型 因此 你无需编写代码 即可向模型提供示例输入 并预览预测的输出 Core ML 性能报告 可以为你提供所有连接设备上 有关负载、预测和编译时间的 模型计算性能分析 值得注意的是 即便是在你训练模型框架前 该报告也可以有效评估这些框架 现在 让我们回到整个工作流程 接下来 我来讲讲模型集成 模型集成是开发 App 的一部分 与你在 App 中 使用的其它资源一样 你也希望可以仔细管理和优化 使用 Core ML 模型的方式
模型集成共包含 3 个步骤 首先 你要编写 App 代码以应用模型 这段代码需要包括 加载模型的位置和时间 准备模型输入数据的方式、 进行预测以及使用结果
接着 你要编译该代码和模型 最后 你要测试、运行 并分析 App 中运行的模型 在进行分析时 Core ML 以及神经网络引擎工具 可能会对你有所帮助 并且 这也是一个 设计和优化的迭代过程 并一直持续到你准备发布为止 今年 我们推出了几个 用于优化模型集成的新功能 第一个是计算可用性 所有 Apple 平台 均支持 Core ML 并且在默认情况下 Core ML 会考虑所有可用计算 来优化其执行 其中包括中央处理器、图形处理器 以及神经网络引擎 如果有的话 然而 这些计算设备的 性能特点和可用性 因运行 App 的支持硬件而异 从而可能会影响用户 使用 ML 驱动功能的体验 或你对模型和配置的选择 例如 某些体验可能需要模型 在神经网络引擎上运行 以达到性能和功能的要求 现在我们有一个对计算设备 可用性进行运行时检查的新 API MLComputeDevice 可以枚举 捕获到的计算设备类型 及其关联值中特定计算设备的属性 借助 MLModel 中的 availableComputeDevices 属性 你便可以检查 哪些设备可用于 Core ML 例如 该代码会检查 是否存在可用的神经网络引擎 说得更具体点 就是该代码会检查 可用计算设备集合中 是否包含神经网络引擎类型 接下来 我们再来了解一下 模型集成中的模型生命周期 首先 我来回顾一下 各种模型资源类型 主要有两种类型:源模型和编译模型 源模型的文件扩展名为 MLModel 或 MLPackage 它是一种用于 构建和编辑的开放格式 编译模型的 文件扩展名为 MLModelC 专用于运行时访问 在大多数情况下 你需要 将源模型添加至 App 目标中 接着 Xcode 会编译该模型 并将其放入 App 的资源中 在运行时 你需要实例化 一个 MLModel 来使用模型
实例化将一个 URL 转换为其编译后的形式 以及一个可选配置 生成的 MLModel 会根据特定配置 和特定设备的硬件功能 加载最佳推理所需的所有必要资源 接着我们来深入了解一下 加载过程中发生了什么 首先 Core ML 会检查缓存 以查看其是否根据配置和设备 对模型进行了专门化 如果完成了 它便会 从缓存中加载所需资源并返回 这个过程称为缓存加载 如果缓存中没有配置 其便会触发特定设备的编译 来完成模型的专门化 该过程完成后 编译就会将输出添加到缓存 并在此结束加载 这个过程称为非缓存加载 对于特定模型而言 非缓存加载需要耗费大量时间 但是该类加载关注为设备优化模型 从而加快后续的加载
在设备专门化过程中 Core ML 首先会解析模型 并对其进行通用优化处理 接着 Core ML 会根据 估计的性能和硬件可用性 对特定计算设备的操作链进行分段 并且该分段内容会得到缓存 最后 每段内容都会 针对分配到的计算设备 进行计算设备特定的编译 该编译包括针对 特定计算设备的进一步优化 并输出计算设备可以运行的制品 该过程完成后 Core ML 便会缓存这些制品 以供后续加载模型使用
Core ML 会将专门化的资源 缓存在磁盘上 这些资源与模型的 路径和配置关联在一起 并且会在启动 App 和 重启设备时持久存在 若设备的可用磁盘空间不足 或系统更新 又或是编译模型被删除或被修改 操作系统都会删除缓存 如果发生这些情况 下次模型加载便会再次对设备进行专门化
为了了解模型是否使用了缓存 你可以使用 Core ML Instrument 来追踪 App 并查看加载事件 如果标签为“准备和缓存” 则表示模型为非缓存加载 所以 Core ML 已经 对设备进行了专门化 并缓存了结果 如果加载事件标签为“缓存” 那么模型为加载缓存 并且未对设备进行专门化 以上是针对 MLProgram 模型的新功能 此外 Core ML 性能报告 还可以让你了解 加载的成本 默认情况下 这里会 显示缓存加载的中位数
现在还可以显示非缓存加载时间 由于加载模型在反应时间 和内存方面的成本较高 这里我为你提供了 一些通用的最佳实践
首先 在 App 启动时 不要在 UI 线程上加载模型 相反 可以考虑使用异步加载 API 或延迟加载模型 接着 如果 App 连续运行多次预测 请让模型处于加载状态 而不是按顺序 在每次预测中重新加载模型 最后 如果你的 App 暂不需要使用模型 卸载模型即可 这样有助于减轻内存压力 并且由于缓存的存在 后续的加载会更快 模型加载完成后 你便需要考虑 使用模型运行预测 我会在一个示例中 为你演示新的异步选项
为了展示新的异步预测 API 我会使用一个 App 该 App 会显示图像库 并允许对图像应用滤镜 我会重点介绍 使用 Core ML 模型的着色滤镜 该模型会将灰度图像作为输入 并输出着色后的图像 这里是一个该 App 的应用实例 首先 该 App 会 加载原始的灰度图像 在我选择彩色图像模式时 它便会利用 Core ML 对图形进行着色 当我向下滚动时 模型一定处于运行状态 但是速度比我预期的要慢一点 并且 如果我向下滚动得远些 我就会看到图像着色 需要较长的时间
当我再向上滚动 所有的图像 似乎又花时间逐个着色了一遍 在我的 SwiftUI 代码中 我使用 LazyVGrid 来保持图像 所以当视图离开屏幕时 应该取消所有任务 我们来看一下当前实现 以了解性能不佳 以及其不重视被取消任务的原因 这里就是实现的代码 由于同步预测 API 不是线性安全的 因此 App 需要确保 模型上的预测是串行执行的 这一点可以通过将 ColorizingService 设置为 actor 来实现 actor 每次只允许 调用一次 colorize 方法 该 actor 拥有 colorizerModel 这是一个与 App 捆绑在一起的 自动生成的模型接口 Colorize 方法 当前会执行两个操作 首先 准备模型的输入 包括调整图像大小 以匹配模型输入尺寸 接着 通过模型运行输入 并获取经过着色的输出 接下来 我会使用 Core ML 的 Instruments 模板 来捕获 App 的 Instruments 追踪
在查看 Instruments 追踪时 我们可以看到预测是串行执行的 而这一点是由 actor 的隔离机制保证的 但是 在下一个预测开始运行之前 每个预测之间都存在着间隔 从而导致 App 性能不佳 而这些间隔之所以存在 是因为 actor 的隔离机制 不仅包裹了预测模型 同时还包裹了输入准备过程 其中一种改进措施就是 将输入准备标记为非隔离方法 这样它便不会阻塞 下一个 colorize 请求进入 actor 虽然这么做可能有用 但是 Core ML 预测本身 仍然是串行的 而这就是处理程序的瓶颈 为了利用 Core ML 预测 本身的并发性 我可以考虑使用批量预测 API 这样预测便会接收一批输入 然后通过模型运行这些输入 在底层原理中 Core ML 也会在可能时利用并发性 创建一个 colorize 方法的批量版本 十分简单 而困难的地方在于弄清楚 如何收集输入并将其形成一批 然后将其传递给该方法 事实上 本用例的很多方面 都会给使用 批量预测 API 造成困难 在需要处理的工作量已知时 批量 API 可以发挥最佳使用效果 但在本例中 需要处理的 图像数量并不是固定的 而是取决于一个屏幕大小的函数 和滚动完成的数量 虽然我可以自行选择 批量大小 但是我还要处理 批量大小与我的设定不符 但仍需处理的情况 并且 我将获得不同的 UI 体验 即图像分批着色 最后 即使用户滚动到其他位置 我也无法取消那部分的批量
由于这些问题的存在 我更愿意使用 一次只能处理一个预测的 API 而这就是新的异步预测 API 可以发挥作用的地方 它不仅是线性安全的 并且结合使用 Swift 并发性与 Core ML 的效果也很好 为了将这段代码切换为异步设计 首先 我会将 colorize 方法改为 async 然后 我会在调用预测前 添加 await 关键词 该关键词是使用 新异步版本 API 所必须的 接着 我会将 ColorizingService 改为类而不是 actor 这样便可以同时 对多个图像进行着色 最后 我会在方法开始处 添加取消检查 异步预测 API 会尽力响应取消 尤其是在同时请求多个预测时 但在这种情况下 最好还是 在开头部分加入额外的检查 这样 即使任务 在输入 colorize 方法前被取消 也可以避免准备输入 现在我来进行这些更改 并重新运行 App
和之前一样 我将其 设置为 Colorized 模式 可以看到 图像着色的速度大大提升了 并且如果我快速滚动到底部 图像几乎立刻加载了出来 稍微向上滚动一点 可以确认 当我向上滚动时 图像也得到了着色 这就意味着 在我快速轻扫滑动到底部时 第一次成功 取消了 colorize 调用 在使用这种新异步设计查看跟踪时 我们便可以看到预测 同时在多个图像上运行 并且会通过垂直堆叠的 多个预测区间来显示 由于该模型部分 在神经网络引擎上运行 因此 我们也可以 在神经网络工具中观察该模型 初始实现是对图像进行串行着色 因此 在没有滚动的情况下 着色图像的初始视图大概需要 2 秒
在切换到异步实现后 图像会被并行着色 着色时间减少了一半 大约只有 1 秒 总的来说 通过利用异步预测 API 和 Colorizer 模型的并发性 我大约能够将 总数据吞吐能力提高 2 倍 但需要注意的是 给定模型和用例 能从并发设计中获益多少 很大程度上取决于这几个因素 其中包括模型的操作、 运算单元和硬件组合 以及计算设备可能 正在处理的其他任务 同时 ML 程序和管线模型类型 也可以在同时运行预测方面 提供最佳性能改进
总而言之 在向 App 中添加并发性时 你应当仔细分析工作负荷 以确保你的用例确实可以从中受益
在向 App 中添加并发性时 你还需要注意 内存使用情况 如果内存中同时加载 大量模型输入和输出 App 的峰值内存使用量 便会大幅增加 你可以结合 Core ML Instrument 和 Allocations Instrument 来对此进行分析
在追踪中可以看到 当我将大量输入加载到内存中 以通过 colorizer 模式运行时 App 的内存使用率正在迅速上升
其中存在的潜在问题便是 我代码中的 colorize 方法没有流量控制 因此并行着色的 图像数量也没有固定限制 如果模型输入和输出都较少 这可能不成问题 但如果输入输出都很多 那么同时在内存中 设置这些输入和输出 便会导致 App 的 峰值内存使用率大大增加
其中一种改进方法就是添加逻辑 来限制正在处理的预测最大数量 这样便可以减少内存中 同时加载的输入和输出 从而在运行预测时 降低峰值内存使用率 在本示例中 如果当前 已经有两个正在处理的项目 其便会推迟新的项目 直到前一个项目完成 最佳策略取决于你的用例 例如 在从相机中 获取流式传输数据时 你可能只希望 丢弃任务而非延迟任务 这样 你就可以 避免累计 frame 并避免做那些 暂时不再具有相关性的工作 稍微回顾一下 这里是针对 何时使用不同预测 API 的 几点通用建议
如果你处于同步上下文中 并且每个输入之间的时间间隔 相较于模型反应时间较长 那么异步预测 API 的 运行效果会更好 如果你的输入可用于批量形式 那么批量预测 API 几乎就是为你量身定制的
如果你处于异步上下文中 拥有大量的输入 并且 随着时间变化 这些输入可以单独使用 那么这时候使用异步 API 可以取得最佳效果 总之 在使用 Core ML 工作流程时 你在模型开发和集成过程中 有许多优化机会 新的计算可用性 API 可以帮助你 根据设备上的可用硬件 在运行时进行决策 了解模型的生命周期以及缓存操作 则可帮助你对加载和卸载模型的 时机和位置做出最佳决定 最后 异步预测 API 不仅可以帮助你 将 Core ML 与其他的 异步 Swift 代码进行集成 还可以通过支持并发预测 来提高数据吞吐能力 我是 Core ML 团队的 Ben 我可不是人工智能哦!
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。