数据科学

机器学习框架互操作性,第 3 部分:使用 E2E 管道实现零拷贝

介绍

高效的管道设计对数据科学家至关重要。在编写复杂的端到端工作流时,您可以从各种构建块中进行选择,每种构建块都专门用于特定任务。不幸的是,在数据格式之间重复转换容易出错,而且会降低性能。让我们改变这一点!

在本系列博客中,我们将讨论高效框架互操作性的不同方面:

  • 第一个职位中,我们讨论了不同内存布局以及异步内存分配的内存池的优缺点,以实现零拷贝功能。
  • 第二职位中,我们强调了数据加载/传输过程中出现的瓶颈,以及如何使用远程直接内存访问( RDMA )技术缓解这些瓶颈。
  • 在本文中,我们将深入讨论端到端管道的实现,展示所讨论的跨数据科学框架的最佳数据传输技术。

要了解有关框架互操作性的更多信息,请查看我们在 NVIDIA 的 GTC 2021 年会议上的演示。

让我们深入了解以下方面的全功能管道的实现细节:

  • 从普通 CSV 文件解析 20 小时连续测量的电子 CTR 心电图( ECG )。
  • 使用传统信号处理技术将定制 ECG 流无监督分割为单个心跳。
  • 用于异常检测的变分自动编码器( VAE )的后续培训。
  • 结果的最终可视化。

对于前面的每个步骤,都使用了不同的数据科学库,因此高效的数据转换是一项至关重要的任务。最重要的是,在将数据从一个基于 GPU 的框架复制到另一个框架时,应该避免昂贵的 CPU 往返。

零拷贝操作:端到端管道

说够了!让我们看看框架的互操作性。在下面,我们将逐步讨论端到端管道。如果你是一个不耐烦的人,你可以直接在这里下载完整的 Jupyter 笔记本。源代码可以在最近的RAPIDS docker 容器中执行。

Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
view raw Cell00.ipynb hosted with ❤ by GitHub

步骤 1 :数据加载

在第一步中,我们下载 20 小时的 ele CTR 心电图作为 CSV 文件,并将其写入磁盘(见单元格 1 )。之后,我们解析 CSV 文件中的 500 MB 标量值,并使用 RAPIDS “ blazing fast CSV reader ”(参见单元格 2 )将其直接传输到 GPU 。现在,数据驻留在 GPU 上,并将一直保留到最后。接下来,我们使用cuxfilter( ku 交叉滤波器)框架绘制由 2000 万个标量数据点组成的整个时间序列(见单元格 3 )。

A table of floating-point numbers in CSV format, the API call for cudf.io.csv.read_csv, as well as the resulting data frame in RAPIDS.
图 1 :使用 RAPIDS CSV 解析器解析逗号分隔值( CSV )。
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

步骤 2 :数据分割

在下一步中,我们使用传统的信号处理技术将 20 小时的 ECG 分割成单个心跳。我们通过将 ECG 流与高斯分布的二阶导数(也称为里克尔小波)进行卷积来实现这一点,以便分离原型心跳中初始峰值的相应频带。使用 CuPy (一种 CUDA 加速的密集线性代数和阵列运算库)可以方便地进行小波采样和基于 FFT 的卷积运算。直接结果是,存储 ECG 数据的 RAPIDS cuDF 数据帧必须使用 DLPack 作为零拷贝机制转换为 CuPy 阵列。

The left panel depicts a single heartbeat and a Ricker wavelet. The right panel depicts 13 continuously measured heartbeats and the corresponding output of the convolution with the Ricker wavelet.
图 2 :使用 CuPy 将 ele CTR 心图( ECG )流与固定宽度的 Ricker 小波卷积。

卷积的特征响应(结果)测量流中每个位置的固定频率内容的存在。请注意,我们选择小波的方式使局部最大值对应于心跳的初始峰值。

Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

步骤 3 :局部极大值检测

在下一步中,我们使用非最大抑制( NMS )的 1D 变体将这些极值点映射到二进制门。 NMS 确定流中每个位置的对应值是否为预定义窗口(邻域)中的最大值。这个令人尴尬的并行问题的 CUDA 实现非常简单。在我们的示例中,我们使用即时编译器 Numba 实现无缝的 Python 集成。 Numba 和 Cupy 都将 CUDA 阵列接口实现为零拷贝机制,因此可以完全避免从 Cupy 阵列到 Numba 设备阵列的显式转换。

The left panel depicts the segmentation gates for 13 continuously measured heartbeats coming from the 1D non-maximum suppression. The right panel visualizes a few approximately aligned heartbeats embedded in a vector of length 256.
图 3 :使用 Numba JIT 的 1D 非最大抑制和嵌入心跳。

每个心跳的长度是通过计算门位置的相邻差分(有限阶导数)来确定的。我们通过使用谓词门== 1 过滤索引域,然后调用 cupy . diff ()来实现这一点。得到的直方图描述了长度分布。

Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
view raw Cell0910.ipynb hosted with ❤ by GitHub

步骤 4 :候选修剪和嵌入

我们打算使用固定长度的输入矩阵在心跳集上训练(卷积)变分自动编码器( VAE )。用 CUDA 内核可以实现心跳信号在零向量中的嵌入。在这里,我们再次使用 Numba 进行候选修剪和嵌入。

Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
view raw Cell1112.ipynb hosted with ❤ by GitHub

步骤 5 :异常值检测

在这一步中,我们在 75% 的数据上训练 VAE 模型。 DLPack 再次用作零拷贝机制,将 CuPy 数据矩阵映射到 PyTorch 张量。

The network topology of a variational autoencoder with an approximately isotropic point cloud below the (middle) latent layer.
图 4 :使用 PyTorch 训练可变自动编码器。
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

步骤 6 :结果可视化

在最后一步中,我们可视化剩余 25% 数据的潜在空间。

An approximately isotropic Gaussian point cloud in the 2D plane and 9 (fake) heart beats generated with the decoder of the autoencoder.
图 5 :使用 RAPIDS cuxfilter 对潜在空间进行采样和可视化。
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
view raw Cell1718.ipynb hosted with ❤ by GitHub
结论

从这篇和前面的博文中可以看出,互操作性对于设计高效的数据管道至关重要。在不同的框架之间复制和转换数据是一项昂贵且极其耗时的任务,它为数据科学管道增加了零价值。数据科学工作负载变得越来越复杂,多个软件库之间的交互是常见的做法。 DLPack 和 CUDA 阵列接口是事实上的数据格式标准,保证了基于 GPU 的框架之间的零拷贝数据交换。

对外部内存管理器的支持是一个很好的特点,在评估您的管道将使用哪些软件库时要考虑。例如,如果您的任务同时需要数据帧和数组数据操作,那么最好选择 RAPIDS cuDF + CuPy 库。它们都受益于 GPU 加速,支持 DLPack 以零拷贝方式交换数据,并共享同一个内存管理器 RMM 。或者, RAPIDS cuDF + JAX 也是一个很好的选择。然而,后一种组合 或许需要额外的开发工作来利用内存使用,因为 JAX 缺乏对外部内存分配器的支持。

在处理大型数据集时,数据加载和数据传输瓶颈经常出现。 NVIDIA GPU 直接技术起到了解救作用,它支持将数据移入或移出 GPU 内存,而不会加重 CPU 的负担,并将不同节点上 GPU 之间传输数据时所需的数据副本数量减少到一个。

 

Tags