学做网页的网站wordpress主题the

当前位置: 首页 > news >正文

学做网页的网站,wordpress主题the,邢台163信息港,做网站义乌编者按#xff1a;深度学习的飞速发展离不开硬件技术的突破#xff0c;而 GPU 的崛起无疑是其中最大的推力之一。但你是否曾好奇过#xff0c;为何一行简单的“.to(‘cuda’)”代码就能让模型的训练速度突飞猛进#xff1f;本文正是为解答这个疑问而作。 作者以独特的视角深度学习的飞速发展离不开硬件技术的突破而 GPU 的崛起无疑是其中最大的推力之一。但你是否曾好奇过为何一行简单的“.to(‘cuda’)”代码就能让模型的训练速度突飞猛进本文正是为解答这个疑问而作。 作者以独特的视角将复杂的 GPU 并行计算原理转化为通俗易懂的概念。从 CPU 与 GPU 的设计哲学对比到 CUDA 编程的核心要素再到具体的代码实现文章循序渐进地引领读者把握 GPU 并行计算的精髓。特别是文中巧妙的比喻 —— 将 CPU 比作法拉利GPU 比作公交车这一比喻生动形象地诠释了两种处理器的特性。 这篇文章不仅回答了为什么更指明了如何做在当前人工智能技术飞速发展的背景下理解底层技术原理的重要性不言而喻。这篇文章虽为入门级别的技术内容介绍但也提到了更高级的优化技术和工具库指明了进一步的学习方向具有一定的学习和参考价值。 作者 | Lucas de Lima Nogueira 编译 | 岳扬 Image by the author with the assistance of AI (https://copilot.microsoft.com/images/create) 现如今当我们提及深度学习时人们自然而然地会联想到通过 GPU 来增强其性能。 GPU图形处理器Graphical Processing Units起初是为了加速图像images及 2D、3D 图形graphics的渲染而生。但凭借其强大的并行运算能力GPU 的应用范围迅速拓展已扩展至深度学习deep learning等应用领域。 GPU 在深度学习模型中的应用始于 2000 年代中后期2012 年 AlexNet 的横空出世更是将这种趋势推向高潮。 AlexNet这款由 Alex Krizhevsky、Ilya Sutskever 和 Geoffrey Hinton 共同设计、研发的卷积神经网络在 2012 年的 ImageNet Large Scale Visual Recognition Challenge (ILSVRC) 上一鸣惊人。这一胜利具有里程碑式的意义它不仅证实了深度神经网络在图像分类领域image classification的卓越性能同时也彰显了使用 GPU 训练大型模型的有效性。 在这一技术突破之后GPU 在深度学习模型中的应用愈发广泛PyTorch 和 TensorFlow 等框架应运而生。 如今我们只需在 PyTorch 中轻敲 .to(“cuda”)即可将数据传递给 GPU从而加速模型的训练。但在实践中深度学习算法究竟是如何巧妙地利用 GPU 算力的呢让我们一探究竟吧 深度学习的核心架构如神经网络、CNNs、RNNs 和 transformer其本质都围绕着矩阵加法matrix addition、矩阵乘法matrix multiplication以及对矩阵应用函数applying a function a matrix等基本数学操作展开。因此优化这些核心运算便是提升深度学习模型性能的关键所在。 那么让我们从最基础的场景说起。想象一下你需要对两个向量执行相加操作 C A B。 可以用 C 语言简单实现这一功能 不难发现传统上计算机需逐一访问向量中的各个元素elements在每次迭代中按顺序对每对元素进行加法运算。但有一点需要注意各对元素间的加法操作互不影响即任意一对元素的加法不依赖于其它任何一对。那么若我们能同时执行这些数学运算实现所有元素对pairs of elements的并行相加效果会如何呢 直接做法是借助 CPU 的多线程功能并行执行所有数学运算。但在深度学习领域我们需要处理的向量规模巨大往往包含数百万个元素。通常情况下普通 CPU 只能同时处理十几条线程。此时GPU 的优势便凸显出来目前的主流 GPU 能够同时运行数百万个线程极大地提高了处理大规模向量中数学运算的效率。 01 GPU vs. CPU comparison 虽然从单次运算single operation的处理速度来看CPU 或许略胜 GPU 一筹但 GPU 的优势在于其卓越的并行处理能力。究其根源这一情况源于两者设计初衷的差异。CPU 的设计侧重于高效执行单一序列的操作即线程thread但一次仅能同时处理几十个相比之下GPU 的设计目标是实现数百万个线程的并行运算虽有所牺牲单个线程的运算速度却在整体并行性能上实现了质的飞跃。 打个比方你可以将 CPU 视作一辆炫酷的法拉利Ferrari跑车而 GPU 则如同一辆宽敞的公交车。倘若你的任务仅仅是运送一位乘客毫无疑问法拉利CPU是最佳选择。然而如若当前的运输需求是运送多位乘客即使法拉利CPU单程速度占优公交车GPU却能一次容纳全部乘客其集体运输效率远超法拉利多次单独接送的效率。由此可见CPU 更适于处理连续性的单一任务而 GPU 则在并行处理大量任务时展现出色的效能。 Image by the author with the assistance of AI (https://copilot.microsoft.com/images/create) 为了实现更出色的并行计算能力GPU 在设计上倾向于将更多晶体管资源transistors投入到数据处理中而非数据缓存data caching和流控机制flow contro这与 CPU 的设计思路大相径庭。CPU 为了优化单一线程的执行效率和复杂指令集的处理特意划拨了大量的晶体管来加强这些方面的性能。 下图生动地描绘了 CPU 与 GPU 在芯片资源分配上的显著差异。 Image by the author with inspiration from CUDA C Programming Guide (https://docs.nvidia.com/cuda/pdf/CUDA_C_Programming_Guide.pdf) CPU 配备了高性能内核powerful cores与更为精妙的缓存内存架构cache memory architecture消耗了相当多的晶体管资源这种设计方案能够极大地优化顺序任务的执行速度。而图形处理器GPU则着重于内核cores数量以实现更高的并行处理能力。 现在已经介绍完这些基础知识那么在实际应用中我们应如何有效利用并行计算的优势呢 02 Introduction to CUDA 当我们着手构建深度学习模型时很可能会倾向于采用诸如 PyTorch 或 TensorFlow 这类广受欢迎的 Python 开发库。尽管如此一个不争的事实是这些库的核心代码都是 C/C 代码。另外正如我们先前所提及的利用 GPU 加快数据的处理速度往往是一种主流优化方案。此时CUDA 的重要作用便凸显出来CUDA 是统一计算设备架构Compute Unified Device Architecture的缩写是英伟达NVIDIA为使 GPU 能够在通用计算领域大放光彩而精心打造的平台。与 DirectX 被游戏引擎用于图形运算graphical computation不同CUDA 使开发人员能够将英伟达NVIDIA的 GPU 计算能力集成到通用软件中而不仅仅局限于图形渲染。 为了实现这一目标CUDA 推出了一款基于 C/C 的简易接口CUDA C/C帮助开发者调用 GPU 虚拟指令集virtual intruction se及执行特定操作specific operations如在 CPU 与 GPU 间传输数据。 在继续深入技术细节之前我们有必要澄清几个 CUDA 编程的基础概念和专业术语 host特指 CPU 及其配套内存device对应 GPU 及其专属内存kernel指代在设备GPU上运行的函数代码 因此在一份使用 CUDA 撰写的基本代码basic code中程序主体在 host (CPU) 上执行随后将数据传递给 device (GPU) 并调用 kernels (functions) 在 device (GPU) 上并行运行。这些 kernels 由多条线程同时执行。运算完成后结果再从 device (GPU) 回传至 host (CPU) 。 话说回来让我们再次聚焦于两组向量相加这个具体问题 借助 CUDA C/C编程人员能够创建一种被称为 kernels 的 C/C 函数一旦这些 kernels 被调用 N 个不同的 CUDA 线程会并行执行 N 次。 若想定义这类 kernel可运用 global 关键字作为声明限定符declaration specifier而若欲设定执行该 kernel 的具体 CUDA 线程数目则需采用 … 来完成 每个 CUDA 线程在执行 kernel 时都会被赋予一个独一无二的线程 ID即 threadIdx它可以通过 kernel 中的预设变量获取。上述示例代码将两个长度size均为 N 的向量 A 和 B 相加并将结果保存到向量 C 中。值得我们注意的是相较于循环逐次处理成对加法的传统串行方式CUDA 的优势在于其能够并行利用 N 个线程一次性完成全部加法运算。 不过在运行上述这段代码前我们还需对其进行一次修改。切记kernel 函数的运行环境是 device (GPU) 这意味着所有相关数据均须驻留于 device 的内存之中。 要达到这一要求可以借助 CUDA 提供的以下内置函数 直接将变量 A、B 和 C 传入 kernel 的做法并不适用于本情况我们应当使用指针。在 CUDA 编程环境下host 数组比如示例中的 A、B 和 C无法直接用于 kernel 启动…。鉴于 CUDA kernels 的工作空间为 device 的内存device memory故需向 kernel 提供 device 指针device pointersd_A、d_B 和 d_C以确保其能在 device 的内存上运行。 除此之外我们还需通过调用 cudaMalloc 函数在 device 上划分内存空间并运用 cudaMemcpy 实现 host 和 device 之间的数据传输。 至此我们可在代码中实现向量 A 和 B 的初始化并在程序结尾处清理 CUDA 内存cuda memory。 另外调用 kernel 后务必插入 cudaDeviceSynchronize(); 这一行代码。该函数的作用在于协调 host 线程与 device 间的同步确保 host 线程在继续执行前device 已完成所有先前提交的 CUDA 操作。 此外CUDA 的错误检测机制同样不可或缺这种检测机制能协助我们及时发现并修正 GPU 上潜在的程序缺陷bugs。倘若忽略此环节device 线程CPU将持续运行而 CUDA 相关的故障排查则将变得异常棘手很难识别与 CUDA 相关的错误。 下面是这两种技术的具体实现方式 要编译和运行 CUDA 代码首先需要确保系统中已装有 CUDA 工具包CUDA toolkit。紧接着使用 nvcc —— NVIDIA CUDA 编译器完成相关代码编译工作。 然而当前的代码尚存优化空间。在前述示例中我们处理的向量规模仅为 N 1000这一数值偏小难以充分展示 GPU 强大的并行处理能力。特别是在深度学习场景下我们时常要应对含有数以百万计参数的巨型向量。然而倘若尝试将 N 的数值设为 500000并采用 1, 500000 的方式运行 kernel 上述代码便会抛出错误。因此为了完善代码使之能顺利执行此类大规模运算我们亟需掌握 CUDA 编程中的核心理念 —— 线程层级结构Thread hierarchy。 03 Thread hierarchy线程层级结构 调用 kernel 函数时采用的是 number_of_blocks, threads_per_block 这种格式notation。因此在上述示例中我们是以单个线程块的形式启动了 N 个 CUDA 线程。然而每个线程块所能容纳的线程数量都有限制这是因为所有处于同一线程块内的线程都被要求共存于同一流式多处理器核心streaming multiprocessor core并共同使用该核心的内存资源。 欲查询这一限制数量的具体数值可通过以下代码实现 就作者当前使用的 GPU 而言其单一线程块最多能承载 1024 个线程。因此为了有效处理示例中提及的巨型向量massive vector我们必须部署更多线程块以实现更大规模的线程并发执行。 同时这些线程块被精心布局成网格状结构grids如下图所展示 https://handwiki.org/wiki/index.php?curid1157670 (CC BY-SA 3.0) 现在我们可以通过以下途径获取线程 ID 于是该代码脚本更新为 04 性能对比分析 下表展示了在处理不同大小向量的加法运算时CPU 与 GPU 的计算性能对比情况。 Image by the author 显而易见GPU 的处理效能优势唯有在处理大规模向量时方能得以凸显。此外切勿忽视一件事此处的时间对比仅仅考量了 kernel/function 的执行耗时而未将 host 和 device 间数据传输所需的时间纳入考虑范围。尽管在大多数情况下数据传输的时间开销微不足道但就我们目前仅执行简易加法运算simple addition operation的情形而言这部分时间消耗却显得相对可观。因此我们应当铭记GPU 的计算性能仅在面对那些既高度依赖计算能力又适合大规模并行处理的任务时才能得以淋漓尽致地展现。 05 多维线程处理Multidimensional threads 现在我们已经知道如何提升简单数组操作simple array operation的性能了。然而在处理深度学习模型时必须要处理矩阵和张量运算matrix and tensor operations。在前文的示例中我们仅使用了内含 N 个线程的一维线程块one-dimensional blocks。然而执行多维线程块multidimensional thread blocks最高支持三维同样也是完全可行的。因此为了方便起见当我们需要处理矩阵运算时可运行一个由 N x M 个线程组成的线程块。还可以通过 row threadIdx.x 来确定矩阵的行索引而 col threadIdx.y 则可用来获取列索引。此外为了简化操作还可以使用 dim3 变量类型定义 number_of_blocks 和 threads_per_block。 下文的示例代码展示了如何实现两个矩阵的相加运算。 此外我们还可以将此示例进一步拓展实现对多个线程块的处理 此外我们也可以用同样的思路将这个示例扩展到三维运算3-dimensional operations操作的处理。 上文已经介绍了处理多维数据multidimensional data的方法接下来还有一个既重要又容易理解的概念值得我们学习如何在 kernel 中调用 functions。 一般可以通过使用 device 声明限定符declaration specifier来实现。这种限定符定义了可由 device GPU直接调用的函数functions。因此这些函数仅能在 global 或其他 device 函数中被调用。下面这个示例展示了如何对一个向量进行 sigmoid 运算这是深度学习模型中极其常见的一种运算方式。 至此我们已经掌握了 CUDA 编程的核心概念现在可以着手构建 CUDA kernels 了。对于深度学习模型而言其实质就是一系列涉及矩阵matrix与张量tensor的运算操作包括但不限于求和sum、乘法multiplication、卷积convolution以及归一化normalization 等。举个例子一个基础的矩阵乘法算法可以通过以下方式实现并行化 我们可以注意到在 GPU 版本的矩阵乘法算法中循环次数明显减少从而显著提升了运算处理速度。下面这张图表直观地展现了 N x N 矩阵乘法在 CPU 与 GPU 上的性能对比情况 Image by the author 我们会发现随着矩阵大小matrix size的增大GPU 在处理矩阵乘法运算时的性能提升幅度更大。 接下来让我们聚焦于一个基础的神经网络模型其核心运算通常表现为 y σ(Wx b)如下图所示 Image by the author 上述运算主要涉及矩阵乘法matrix multiplication、矩阵加法matrix addition以及对数组施加函数变换applying a function to an array。如若你已掌握这些并行化处理技术意味着你现在完全具备了从零构建、并在 GPU 上构建神经网络的能力 06 Conclusion 本文我们探讨了通过 GPU processing 译者注使用 GPU进行数据处理和计算。提升深度学习模型效能的入门概念。不过有一点还需要指出本文所介绍的内容仅仅是皮毛背后还隐藏着很多很多更深层次的东西。PyTorch 和 Tensorflow 等框架实现了诸多高级性能优化技术涵盖了 optimized memory access、batched operations 等复杂概念其底层利用了基于 CUDA 的 cuBLAS 和 cuDNN 等库。 但愿这篇文章能够让各位读者对使用 .to(“cuda”) 方法在 GPU 上构建、运行深度学习模型时的底层原理有个初步的了解。 Thanks so much for reading!
Lucas de Lima Nogueira https://www.linkedin.com/in/lucas-de-lima-nogueira/ END 原文链接 https://towardsdatascience.com/why-deep-learning-models-run-faster-on-gpus-a-brief-introduction-to-cuda-programming-035272906d66