Windows ML 性能和内存

在本文中,我们将介绍如何在使用 Windows 机器学习时管理应用程序的性能。

线程处理和并发

从运行时公开的每个对象都是敏捷对象,这意味着可以从任何线程访问这些对象。 有关敏捷对象的详细信息,请参阅 C++/WinRT 中的敏捷对象

你将使用的一个关键对象是 LearningModelSession。 从任何线程调用此对象都始终是安全的。

  • 对于 GPU 会话:对象将锁定并同步并发调用。 如果需要并发,则需要创建多个会话才能实现它。

  • 对于 CPU 会话:对象不会锁定并且允许在单个会话上进行并发调用。 你必须负责管理自己的状态、缓冲区和绑定对象。

应注意并度量方案目标。 新式 GPU 体系结构的工作方式不同于 CPU。 例如,如果低延迟是目标,那么你可能想要管理如何使用管道(而非并发)来计划跨 CPU 和 GPU 引擎的工作。 有关多引擎同步的这篇文章是理想的入门文章。 如果吞吐量是目标(例如,每次尽可能多地处理映像),通常需要使用多个线程和并发来使 CPU 饱和。

当涉及到线程处理和并发时,需要运行试验并测量运行时间。 性能将基于目标和方案大幅变化。

内存利用率

LearningModelLearningModelSession 的每个实例在内存中都有一个模型副本。 如果使用小模型,你可能不在意这一点,但如果使用非常大的模型,则这一点会变得很重要。

若要释放内存,请在模型或会话上调用 Dispose。 不要直接删除它们,因为某些语言会执行延迟垃圾回收。

LearningModel 在内存中保留一份副本,以便能够创建新的会话。 释放 LearningModel 时,所有现有会话会继续运行。 但是,你将再也无法通过该 LearningModel 实例创建新会话。 对于大型模型,可以先创建模型和会话,然后释放模型。 如果对所有 Evaluate 调用使用单个会话,你就会在内存中拥有一个大型模型副本。

Float16 支持

为了提高性能并降低模型的内存占用量,可以使用 ONNXMLTools 将模型转换为 float16。

转换后,所有权重和输入都是 float16。 下面介绍如何使用 float16 输入和输出:

  • ImageFeatureValue

    • 建议用法。
    • 将颜色转换并张量化为 float16。
    • 支持 bgr8 和 8 位图像格式,此类格式可在不丢失数据的情况下安全地转换为 float16。
  • TensorFloat

    • 高级路径。
    • 将 float32 强制转换为 float16。
    • 如果是图像,则可以安全地进行强制转换,因为 bgr8 较小并且适合。
    • 如果不是图像,则 Bind 会失败,需改为传入 TensorFloat16Bit
  • TensorFloat16Bit

    • 高级路径。
    • 需要转换为 float16,并将输入作为 float32 传递,后者会被强制转换为 float16。

注意

大多数情况下,运算符仍执行 32 位数学运算。 溢出的风险较小,结果将截断为 float16。 但是,如果硬件播发 float16 支持,则运行时会利用它。

预处理输入数据

实际上,WinML 会执行一些预处理步骤,使处理输入数据更简单且更高效。 例如,给定的输入图像可以是各种颜色格式和形状,并且可能不同于模型的预期。 WinML 会对图像执行转换,对其进行匹配,降低开发人员的负荷。

WinML 还利用整个硬件堆栈(CPU、GPU 等)为特定设备和方案提供最有效的转换。

但在某些情况下,可能需要手动将输入数据张量化,因为你有一些特定的要求。 例如,你可能不希望对图像使用 VideoFrame,或者要将像素值从范围 0-255 规范化为范围 0-1。 在这些情况下,可以对数据执行你自己的自定义张量化操作。 如需此方面的示例,请参阅 Custom Tensorization Sample(自定义张量化示例)。

注意

使用以下资源可获取有关 Windows ML 的帮助:

  • 若要提出或回答有关 Windows ML 的技术问题,请在 Stack Overflow 上使用 windows-machine-learning 标记。
  • 若要报告 bug,请在 GitHub 上提交问题。