前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何分析机器学习中的性能瓶颈

如何分析机器学习中的性能瓶颈

作者头像
GPUS Lady
发布2021-05-07 10:35:55
2.4K0
发布2021-05-07 10:35:55
举报
文章被收录于专栏:GPUS开发者GPUS开发者

本文参考编译自NVIDIA Blog

软件性能分析是达到系统最佳效能的关键,数据科学和机器学习应用程序也是如此。在 GPU 加速深度学习的时代,当剖析深度神经网络时,必须了解 CPU、GPU,甚至是可能会导致训练或推理变慢的内存瓶颈

01

nvidia-smi

使用 GPU 的第一个重要工具是 nvidia-smi Linux 命令。此命令会显示出与 GPU 有关的实用统计数据,例如内存用量、功耗以及在 GPU 上执行的进程。目的是查看是否有充分利用 GPU 执行模型。

首先,是检查利用了多少 GPU 内存。通常是希望看到模型使用了大部分的可用 GPU 内存,尤其是在训练深度学习模型时,因为表示已充分利用GPU。功耗是 GPU 利用率的另一个重要指标。通常,启动的 CUDA 或 Tensor 核心越多,消耗的 GPU 功率越高。

如图 1 所示,未充分利用GPU。此结论是根据两个指标获得:

  • 功耗:142 W / 300 W
  • 内存用量:2880 MB / 16160 MB

GPU-Util显示利用率为 62%,证实了此结论。解决方法之一是增加批次大小。启动更多核心,以处理更大的批次。于此情形下,即可充分利用 GPU。

增加批次大小及进行相同的 Python 程序呼叫。如图 2 所示,GPU 利用率为 98%。检查功耗和内存用量,即可证实此结果,它们已接近极限。

您已经完成初步优化,使用较大的批次大小,即几乎占用所有 GPU 内存的批次大小,是在深度学习领域中提高 GPU 利用率最常使用的优化技术。

nvidia-smi 显示的不是仅有功耗和内存用量。您也可以尝试 nvidia-smi dmon,以滚动方式列出更多的 GPU 统计数据,如图 3。

每一个 GPU 都有多个串流多处理器(streaming multiprocessors),执行 CUDA 核心。使用众多串流多处理器表示已充分利用 GPU。如图 3 所示,串流多处理器利用率在呼叫开始时大约为 0%,之后在实际训练开始时上升至 90 几。除串流多处理器利用率外,nvidia-smi dmon 也会列出下列统计资料:

  • 功耗(pwr)
  • GPU 温度(gtemp)
  • 记忆体温度(mtemp)
  • 内存利用率(mem)
  • 编码器利用率(enc)
  • 译码器利用率(dec)
  • 内存时钟速率(mclk)
  • 处理器时钟速率(pclk)

截至目前为止,范例仅使用一个 GPU。在有多个 GPU 的情况下,nvidia-smi 和 nvidia-smi dmon 会分别显示出各个 GPU 的指标。在有多个 GPU 时,可以利用的另一个工具是 nvidia-topo -m。此呼叫会显示出 GPU 装置的拓扑以及彼此连接的方式。

图 4 所示为 DGX A100 系统的拓扑配置,有 8 个 A100 GPU 与 NVLink 连接。选择特定 GPU 执行工作负载时,建议选择与 NVLink 连接的 GPU,因为它们具有较高的带宽,尤其是在 DGX-1 系统上。

截至目前为止,我们已经示范如何使用 nvidia-smi 工具分析 GPU 的利用率。这些指标系指出是否有充分利用 GPU。在建模时,应始终以彻底利用 GPU 为目标,以充分利用加速运算。

(Lady注:Jetson产品不支持nvidia-smi命令。)

02

TensorFlow 和 DLProf

GPU 利用率是进行剖析和优化之极佳的起点。您可以采用 DLProf、PyProf 等工具,进行更多详细的建模分析。您也可以利用使用者介面目视检查程序代码。Deep Learning Profiler(DLProf)支持 TensorBoard,让您可以目视检查模型。

DLProf更详细使用说明可以参考:

https://docs.nvidia.com/deeplearning/frameworks/dlprof-user-guide/index.html

以下程序代码范例是使用 TensorFlow 1.15 训练 ResNet50 模型。其同时可链接 DLProf 参数,在训练模型时执行剖析。

代码语言:javascript
复制
dlprof --nsys_opts="--sample=cpu --trace 'nvtx,cuda,osrt,cudnn'" \
--profile_name=/ecan/tf_a100_profiling --nsys_base_name=resnet50_tf_fp32_b408 \
--output_path=/ecan/tf_a100_profiling --tb_dir=resnet50_tf_fp32_b408 \
--force=true --iter_start=20 --iter_stop=40 \
python main.py \
    --arch resnet50 \
    --mode train \
    --data_dir /ecan/tfr \
    --export_dir /ecan/results \
    --batch_size 256 \
    --num_iter 100 \
    --iter_unit batch \
    --results_dir /ecan/results \
    --display_every 100 \
    --lr_init 0.01 \
    --seed 12345

(可以左右拖动代码)

python main.py 以后的程序代码,是开始针对 ResNet50 模型(取自 NVIDIA DeepLearningExamples GitHub 储存库)进行训练。开头的 dlprof 命令设定,是用于进行剖析的 DLProf 参数。下列 DLProf 参数是用于设定输出档案和文件夹名称:

  • profile_name
  • base_name
  • output_path
  • tb_dir

force 参数设为 true,以覆盖现有的输出档案。iter_startiter_stop 参数指定剖析工具注意的迭代范围。如果是较大的模型,请限制剖析量,因为产生的档案会快速变大。

DLProf 使用内部的 NVIDIA Nsight Systems 剖析器,而 nsys_opts 参数可用于传递 NVIDIA Nsight 参数。sample 参数用于指定是否收集 CPU 样本。trace 参数用于选择追踪的呼叫。

在此设定中,我们选择收集 nvtx API、CUDA API、操作系统运行时间,以及 CUDNN API 呼叫。DLProf 可以与预设参数搭配使用,例如 dlprof python main.py,预设参数可以提供良好的涵盖范围。我们在此处使用更多选项,示范如何透过 DLProf 自定义 NVIDIA Nsight 参数,并获得更详细的剖析输出。

DLProf 呼叫产生两个档案,sqliteqdrep,以及 events_folder。这些档案包含剖析器追踪的所有运算。您可以将 Qdrep 档案馈入 Nsight Systems,在其中目视检查剖析输出。您可以从命令行以及透过具有可视化用户接口的应用程序,使用 Nsight Systems 剖析器。使用以下命令启动 TensorBoard:

代码语言:javascript
复制
tensorboard --logdir events_folder

图 5 所示为 DLProf 插件 TensorBoard 范例。

DLProf 插件 TensorBoard 提供大量的模型信息,从迭代花费的平均时间,到前 10 名的耗时核心。若需要更多与 DLProf 用户接口有关的信息,请参阅:

https://docs.nvidia.com/deeplearning/frameworks/tensorboard-plugin-user-guide/#features

图 6 所示为与训练有关的运行时间指标。20 次迭代花费的总时间为 12.3 秒,该命令定义从第 20 次迭代开始,到第 40 次迭代停止,每一次迭代平均 588 毫秒。

每一次迭代平均花费 588 毫秒时,表示未利用 A100 支持的新精度类型 TF32。TF32 在矩阵乘法中使用较少的位,同时提供相同的模型准确度,因此可加快迭代速度。除需要处理的位较少外,TF32 同时利用了 Tensor 核心,一种深度学习的专用硬件,有助于加快矩阵乘法和累加运算。Volta (V100)、Turing (T4) 和 Ampere (A100) 世代 GPU 皆具有 Tensor 核心。

TF32 在 NVIDIA NGC TensorFlow 和 PyTorch 容器中是预设为启用,并由 NVIDIA_TF32_OVERRIDE=0NVIDIA_TF32_OVERRIDE=1 环境变量控制。

在启用 TF32 后,进行相同的呼叫,而不变更任何参数。图 7 呈现出前 10 名 GPU 运算以及是否使用 Tensor 核心(TC)。

您可以看到某些运算已经使用Tensor 核心,非常好。查看每一次迭代花费的平均时间,以检查是否有加速效果。

在切换至TF32 精度时,平均迭代时间从588 毫秒缩短成399 毫秒。切换一个环境变量即可大幅加速。重点在于是否可以做到比399 毫秒更好。您知道是由DLProf 提出此建议,因此可以做到比588 毫秒更好。

DLProf 不仅提供大量的模型信息,同时会提出改进建议。在此范例中,其建议启用 XLA 和 AMP(automatic mixed precision,自动混合精度)。XLA 是以加快线性代数运算为目标的线性代数编译程序。数值精度描述是用于表示值的位数。混合精度是以运算方法结合不同的数值精度。将某些张量上的储存的需求和内存流量减半,能以较低的精度训练深度学习网络,以达到高传输量。混合精度可以加快大型矩阵到矩阵乘加运算的训练速度。

想要启用 XLA 和 AMP,请在 NVIDIA 容器中设定以下环境变量 container:

代码语言:javascript
复制
export TF_XLA_FLAGS="--tf_xla_auto_jit=2"
export TF_ENABLE_AUTO_MIXED_PRECISION=1

最近,大多数储存库皆已内建XLA 和AMP,通常只需传递相关参数。在此范例中,它们是 use_xlause_tf_amp。在启用 XLA 和 AMP 之后,可以让模型有效率地使用 Tensor 核心、减少需要的内存数量,并利用更快的线性代数运算。

如图10 所示,几乎所有符合Tensor 核心条件的运算,皆已使用Tensor 核心,圆饼图中没有粉红色分类。这是理想的情况,然而更重要的是有助于缩短训练时间。

平均迭代时间从 399 毫秒以及 588 毫秒缩短成 341 毫秒。使用半精度产生的内存用量较少。为了进行公平的比较,请勿变更混合精度的批次大小。启用 AMP 可以使模型的批次大小比全浮点精度高出一倍,并进一步缩短训练时间。

总结来说,首先采用 TF32 精度及缩短训练时间。然后,启用 AMP 和 XLA,并进一步缩短使用 DLProf 辅助剖析时的训练时间。

03

PyTorch 和 PyProf

本节示范如何在使用 PyTorch 建立模型时进行剖析。截至目前为止,我们已经示范数种优化技术。在 PyTorch 中,使用 TF32 和 AMP优化模型。

接着遵循更进阶的途径,在程序代码基础中加入额外的程序代码。此外,直接使用 PyProf 和 Nsight Systems 剖析器,无须呼叫 DLProf。您仍可以使用 DLProf 和 TensorBoard 剖析 PyTorch 模型,因为 DLProf 亦可支持 PyTorch。但是,我们想要示范替代的剖析方式。

您可以挑选需要剖析的项目,例如仅剖析第 17 次迭代。在资料迭代循环中,检查是否处于第 17 次迭代。如果是,则使用剖析器,开始和结束标记包围执行正向传递、损失计算、梯度计算(反向)及更新参数(步进)的程序代码行。

从相同的储存库取用 ResNet50 训练程序代码。在训练程序代码中变更剖析,并增加 pyprof 参数,以针对唯一的正向传递启用剖析。您可以留下反向传播,并任意设定范围,然后推送至此分支,以供参考。在变更之后进行呼叫,以执行 PyTorchResNet50 训练及剖析:

代码语言:javascript
复制
nsys profile --trace 'nvtx,cuda,osrt,cudnn' -c cudaProfilerApi --stop-on-range-end true \
--show-output true --sample=cpu --export=sqlite \
-o /ecan/pytorch_a100_profiling/resnet50_pytorch_fp32_b256 \
python main.py /ecan/imagenet_small \
--raport-file raport.json -j16 -p 100 --lr 2.048 \
--optimizer-batch-size 256 --warmup 8 --arch resnet50 \
 -c fanin --label-smoothing 0.1 \
 --lr-schedule cosine --training-only --mom 0.875 --wd 3.0517578125e-05 -b 256\
--epochs 1 --workspace /ecan/results \
--pyprof

(左右滑动代码)

这一次,直接呼叫 Nsight Systems 剖析器。您已经知道 tracesampleoutput (-o) 参数。增加 -c cudaProfilerApi –stop-on-range-endtrue 参数以通知剖析器,已导入开始和停止标记,使剖析器仅剖析两者之间的事件。将 –show-output 参数设为 true 时,会将目标进程 stdoutstderr 数据流打印至控制台。

此呼叫会产生两个档案:qdrepsqlite。在 TensorFlow 中已使用 TensorBoard 的 event_files 文件夹,但是未碰触 qdrep 档案。这一次是使用 qdrep,在 Nsight Systems 应用程序中目视检查剖析结果。

以下程序代码范例是使用 PyProf 呼叫,分析核心:

代码语言:javascript
复制
python -m pyprof.parse (resulting_sqlite_file_from_our_call) > a_file
python -m pyprof.prof a_file -w 100 -c idx,trace,sil,tc,flops,bytes,kernel \
| (read -r; printf “%s\n” “$REPLY”; sort -k5 -n -r)

w 参数可设定字段宽度,以及 c 参数可指定需要印出的选项。有多个选项,且我们选择了这些选项,完整列表如下。我们同依据浮点运算次数排序,进行更好的分析,否则,依据执行顺序排序。

我们提供一些来自清单顶部的核心。前几个是批次正规化核心。您也可以识别呼叫档案的行号,例如 resnet50.py:201。有助于进一步了解这些核心统计数据,因为模型中可能有多个批次正规化。最后一行是使用半精度的矩阵乘法。它同时使用 Tensor 核心,非常好。

变更先前之PyProf 呼叫的最后一行,以取得花在迭代正向传递上的总奈秒数:

代码语言:javascript
复制
python -m pyprof.prof a_file -w 100 -c idx,trace,sil,tc,flops,bytes,kernel \
| awk ‘{total+=$3}END{print total}’

呼叫的结果为 188,388,811 ns (188.4 ms)。截至目前为止,已使用 FP32 精度类型完成剖析。您已经知道切换至 TF32 精度类型,可以将程序代码优化。切换 NVIDIA_TF32_OVERRIDE 环境变量,即可利用 TF32 精度类型。

如果训练和剖析呼叫相同,但是这一次是启用 TF32 精度类型时,总时间为 110,250,534 ns (110.25 ms)。在切换至 TF32 之后,运行时间几乎减半。

您已习惯在 TensorFlow 上进行优化,现在可以在 PyTorch 上,将程序代码优化。还有一个步骤:启用混合精度,并检查是否可以进一步将程序代码优化。

代码语言:javascript
复制
nsys profile --trace 'nvtx,cuda,osrt,cudnn' -c cudaProfilerApi --stop-on-range-end true \
--show-output true --sample=cpu --export=sqlite \
-o /ecan/pytorch_a100_profiling/resnet50_pytorch_amp_b256 \
python main.py /ecan/imagenet_small \
--raport-file raport.json -j16 -p 100 --lr 2.048 \
 --optimizer-batch-size 256 --warmup 8 --arch resnet50 \
 -c fanin --label-smoothing 0.1 \
 --lr-schedule cosine --training-only --mom 0.875 --wd 3.0517578125e-05 -b 256 \
--amp --static-loss-scale 128 \
--epochs 1 --workspace /ecan/results \
--pyprof

大部分参数都与先前的呼叫相同,除 ampstatic-loss-scale 参数外。amp 参数启用 AMP,因为程序代码基础可为其提供支持。static-loss-scale 参数调整损失。若需要更多与 ResNet50 训练参数有关的信息,请参阅 ResNet50 v1.5For PyTorch 指南中的命令行选项一节。

在开启AMP 模式之情况下,执行呼叫的程序代码范例时,获得72,860,695 ns (72.86 ms)。这是好消息,因为已使用混合精度进一步将程序代码优化。在TensorFlow 上可以获得类似的改善。虽然TensorFlow 已进行额外的优化(XLA),也可以仅使用AMP,在PyTorch 上获得进一步的改善。

04

使用 Nsight Systems 进行剖析

截至目前为止,您已经使用透过剖析器呼叫从训练中收集的统计资料。您同时已利用 PyProf 快速浏览模型中使用的核心。您使用 TensorBoard 和 DLProf 插件产生绝佳的可视化。在本文开头,您是使用 nvidia-smi 检查 GPU 利用率。如果您认为还不足够,而想要深入探索时,无须担心,我们还有更多的内容。

在完成包含剖析器呼叫的训练之后,取得 qdrep 档案。现在,让我们透过 NVIDIA Nsight Systems 剖析器的用户接口,更深入地分析模型。若需要更多信息,请参阅 Nsight Systems 使用指南。https://docs.nvidia.com/nsight-systems/UserGuide/index.html

即使将剖析限制为仅限迭代正向传播,也可以使用Nsight Systems 剖析器目视检查大量信息。有时候我们会放大此画面的特定区域,以进行进一步分析。想要仔细查看,请将训练的开头放大,并聚焦于几毫秒。

首先看到一些绿色的内存运算,接着是卷积运算。然后,开始将批次正规化。不出所料,下一步就是启用函式。于此范例中,它是ReLU。最后,看到执行最大池化。这是在程序代码基础和大多数ResNet 模型中看到的顺序。您也可以查看堆栈追踪,以取得更多与选择之运算有关的信息,在选取时会变成青绿色。

在结束本篇文章之前,我们想要示范另一种优化方法。在PyTorch 中,可以变更记忆体格式。通常是使用以下格式储存数据:

代码语言:javascript
复制
[ number of elements in the batch, number of channels (depth or number of filters), height, width ]

PyTorch 以 [n, h, w, c] 格式运作。以 [n,h, w, c] 格式处理类批次正规化层的速度较快。最耗时的运算是批次正规化,如图 14 所示。此外,Tensor 核心原生采用 [n,h, w, c] 格式。基本上,透过变更记忆体格式,可以在处理类批次正规化层时节省一些时间,且可在CUDNN 核心中避免一些格式转换的时间。

在先前的呼叫中增加 –memoryformat nchw 即可,且让您可以使用 [n,c, h, w] 记忆体格式。在采用 [n,c, h, w] 记忆体格式之后,训练不再需要记忆体格式转换操作,例如 nhwcToNchwKernelnchwToNhwcKernel,请参见图 18。因此可以节省更多时间。换言之,您已透过变更记忆体格式,再次完成优化。为了确认这一点,请计算花在核心的总时间。我们的结果是 45,631,828 ns(45.6 ms)。在采用 [n,c, h, w] 记忆体格式时,大约为 70 毫秒。利用记忆体格式优化技术进一步缩短运行时间。

总结

本文详细介绍了如何使用各种工具剖析深度学习模型:nvidia-smi、DLProf 和 PyProf,以及 NVIDIA Nsight Systems 剖析器。每一个工具都可以指出不同层级的效能改善机会。剖析是使用两个常见的深度学习框架执行:PyTorch 和 TensorFlow。DeepLearningExamples GitHub 储存库中提供了程序代码范例,同时有 PyProf 和 PyTorch 呼叫的程序代码变更。建议您复制这些步骤,以便能更熟悉剖析工具。

https://github.com/ethem-kinginthenorth/DeepLearningExamples

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-05-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 GPUS开发者 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • nvidia-smi
  • TensorFlow 和 DLProf
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档