亚马逊AWS官方博客
在基于 AWS Inferentia 的 Inf1 实例上部署 TensorFlow OpenPose,借此显著提高资源性价比
在本文中,我们将使用AWS Neuron编译一套开源的TensorFlow版本的OpenPose,并针对基于AWS Inferentia的实例对推理性能进行调优。与基于GPU的实例相比,我们需要在本演练中设置基准测试环境、衡量图像处理管道的吞吐量,并量化该系统的性价比改进情况。
关于OpenPose
人体姿态估计,属于一类机器学习(ML)与计算机视觉(CV)相结合的技术应用方向,可用于支持行人意图预估、AR和体育赛事中的运动跟踪等多种用例。姿态估计的核心在于识别图像(关节与关键点)的具体坐标,将这些坐标串连起来以构成一个人的骨骼表示。对身体姿态的准确表达,将成为机器人交互设计乃至运动姿态量化等应用目标的实现前提。
目前市面上存在诸多可用于人体姿态估计的方法,其中OpenPose采取深度学习凭借自下而上的方法(由卡耐基梅隆大学认知计算实验室于2018年发布)吸引到众多拥趸。OpenPose是一种多人2D姿态估计模型,其中采用一种称为“部位亲和域”(PAF)的技术对身体各部位进行关联,并借此在图像上建立起多个单独的骨架结构。在这种自下而上的方法中,模型能够识别出各个关键点,并据此将骨架结构拼凑在一起。
为了实现这一目标,OpenPose使用了两步式流程。首先,它使用VGG-19模型提取图像特征,并将这些特征传递至并行运行的一对卷积神经网络(CNN)当中。
其中一套CNN负责计算置信度图,借此检测图像中的各个身体部位。另一套CNN则计算PAF,并将各个部分合并起来,构成人体骨骼结构。您可以多次重复这些并行分支,借此完善置信度图与PAF预测结果。
下图所示,为来自VGG的特征F,此特征被馈送至OpenPose模型的PAF与置信度图分支当中。(来源:使用部位亲和域进行实时多人2D姿态估计)
原始OpenPose代码依赖于Caffe模型与预统计的C++库。出于使用便捷性与可移植性的考虑,我们在本轮演练中使用GitHub repo中的tf-pose-estimation基于TensorFlow 1.15重新实现了OpenPose的神经网络。此repo还提供ML管道脚本,大家可以用OpenPose对图像及视频进行预处理与后处理。
先决条件
在本轮演练中,您需要准备一个有权访问AWS管理控制台的AWS账户,同时保证有权限使用公开IP创建 Amazon Elastic Compute Cloud (Amazon EC2)实例和创建Amazon Simple Storage Service Amazon S3)存储桶。
另外,如果您熟悉如何使用AWS Deep Learning AMI与Jupyter notebooks中的Conda环境则更好,但并非硬性要求。
关于AWS Inferentia与Neuron SDK
AWS Inferentia 芯片是一款由AWS定制构建,旨在提供更高推理性能的芯片方案,能够立足云端实现最低推理成本,并帮助大家轻松将ML集成至标准应用程序功能当中。
AWS Neuron 是一款软件开发套件(SDK),由编译器、运行时以及配置文件工具共同组成,这些工具可进一步优化Inferentia芯片的ML推理性能。Neuron已经与TensorFlow、PyTorch以及MXNet等流行ML框架顺畅集成,且预装在AWS Deep Learning AMI当中。在AWS Inferentia上部署深度学习模型的具体方式,与其他平台上的类似环境无甚区别,您可以轻松享受到这款芯片带来的性能提升与成本优化。
在AWS Neuron GitHub上发布的最新Neuron版本,增加了对更多模型(包括OpenPose)的支持选项,我们将在后文中重点加以介绍。此外,Neuron新版本还将Neuron PyTorch升级到最新稳定版(1.5.1),允许大家在AWS Inferentia上编译并运行更多模型。
使用Neuron SDK编译TensorFlow OpenPose模型
您可以在AWS中设置EC2实例,借此开启模型编译流程。本文建议大家使用z1d.xlarge实例类型,其拥有出色的单核性能与内存容量。在US East(北弗吉尼亚州)区域内,使用AWS Deep Learning AMI(Ubuntu 18.04)的29.0版本(ami-043f9aeaf108ebc37
)。此AMI预打包有Neuron SDK以及AWS Inferentia所需要的Neuron运行时。
关于在EC2实例上运行AWS Deep Learning AMI的更多详细信息,请参阅启动并配置DLAMI。
在通过SSH接入该实例时,您可以激活 aws_neuron_tensorflow_p36 Conda
环境,并将Neuron编译器更新至最新版本。编译脚本的正常运行,依赖于requirements-compile.txt
文件中列出的具体要求。关于编译脚本与需求文件,请参阅 GitHub repo。您可以使用以下代码,将其下载并安装至目标环境当中:
接下来,我们就可以开始编译过程。您可以编译 tf-pose-estimation
网络冻结图,在GitHub repo上可以找到。下面将下载到的原始脚本调整为单行wget
命令:
当下载完成之后,运行convert_graph_opt.py
脚本以为AWS Inferentia芯片编译。由于Neuron属于提前(AOT)编译器,大家需要在编译之前定义特定的图像大小。您可以使用 —net_resolution
参数(例如net_resolution=656x368
)来调整网络输入图像的分辨率。
编译后的模型可以在推理运行过程中接受任意批次大小的输入。此属性可以对模型的大规模部署进行基准测试;但是,示例 tf-pose-estimation repo
中用于图像及视频处理管道的批次大小设置为1。
要开始编译流程,请输入以下代码:
编译流程最多可能需要20分钟。在此期间,编译器会优化TensorFlow图运算并为已保存的模型生成AWS Inferentia版本。在编译过程中,系统会保留详尽的编译日志,例如:
在评估编译后的模型性能之前,您需要切换至由AWS Inferentia芯片提供支持的EC2 Inf1实例。要在两个实例之间共享编译完成的模型,请使用以下代码创建一个S3存储桶:
在AWS EC2 Inf1实例上使用Jupyter notebook对推理时间进行基准测试
在将编译完成的graph_model.pb
模型保存在S3存储桶后,我们需要修改GitHub repo上的ML管道脚本,借此根据图像与视频估计人体姿态。
要设置基准Inf1实例,您可以重复以上在z1d实例上配置编译的步骤。大家可以使用相同的AMI,只需要注意将实例类型更改为inf1.xlarge。本文建议大家使用g4dn.xlarge实例,在设置基本不变的情况下,这样的类型选择将帮助我们更直接地将运行在GPU上的基础 tf-pose-estimation
模型性能与AWS Inferentia编译模型进行比较。
通过本文,我们将使用Jupyter Lab服务器与目标实例及模型进行交互。关于在Amazon EC2上配置Jupyter Lab的更多详细信息,请参阅如何设置Jupyter Notebook服务器。
为tf-pose设置Conda环境
在登录至Jupyter Lab服务器之后,您即可克隆包含有TensorFlow版OpenPose的GitHub repo。
在Jupyter Launcher页面中的Other之下,选择Terminal。
在终端内,激活包含有Neuron SDK的aws_neuron_tensorflow_p36
环境。环境激活与克隆操作需要使用以下代码:
在克隆完成之后,我们建议您按照软件包安装说明进行操作,分步完成repo安装。在同一终端屏幕内,您可以通过安装GitHub repo中requirements.txt
文件所列出的opencv-python
与依赖项进行环境定制。
您需要运行两条pip命令:第一条命令负责处理opencv-python
;第二条负责完成 requirements.txt
中指定的安装步骤:
现在,大家可以开始构建notebook了。
在repo的root目录中,选择Notebook,Environment(conda_aws_neuron_tensorflow_p36
)以创建一个新的Jupyter notebook。在notebook的第一个单元格中,导入run.py
脚本中定义的库——此脚本将作为图像处理的参考管道。在后续单元格中,创建一个记录器以记录基准测试结果。详见以下代码:
定义主要推理函数main()
与辅助绘图函数plotter()
。这些函数是直接从run.py
处复制的OpenPose推理管道。我们需要对其做出简单修改,即添加repeats
参数,借此依次运行多个推理步骤并改进对平均模型吞吐量的度量能力(以每张图像的处理秒数为单位):
另外,如果您比较喜欢冒险,也可以根据run_video.py
或run_directory.py
修改用于推理视频或批量图像的代码结构。本轮演练仅供参考,大家不妨放松心态,尽情尝试!
其中main()
函数会将GitHub repo中Test Inference部分描述的参数字符串作为输入。要测试notebook的实现效果,请使用一组参考参数(请使用原始下载脚本以确保下载cmu
模型):
日志内容显示,您的第一个多人姿态分析任务已经完成:
可以看到,当前吞吐量每秒不足一帧(FPS),代表性能表现比较差。在这种情况下,我们实际是未使用GPU资源运行TesnorFlow图 –model cmu。很多朋友都清楚,这类模型在CPU上无法获得最佳运行性能。如果重复设置步骤,并选择使用搭载英伟达T4 GPU的g4dn.xlarge实例,则结果将大为不同:
结果显示5.85 FPS,性能得到了显著提升。
使用Neuron编译完成的CMU模型
到这里,我们已经使用了repo中自带的模型工件。接下来,我们换一种方式:不再使用原始下载脚本来获取CMU模型,而是将Neuron编译完成的模型复制到 ./models/graph/cmu/graph_model.pb
,而后再次运行测试:
如果您之前已经对未经Neuron编译的模型进行过测试,请确保在notebook上重新启动Python内核。重新启动内核,能够保证关闭所有TensorFlow会话,为基准测试提供完全等效的空白环境。再次运行同一notebook,您将得到以下日志条目:
结果显示,与g4dn.xlarge实例相比,新环境能够在降低约30%运营成本的前提下实现相同的帧速率。这意味着将工作负载转移至基于AWS Inferentia的实例虽然无法在吞吐量层面带来直观可见的性能提升,但却确实具有成本优势。例如,当迁移至AWS Inferentia时,Amazon Alexa文本到语音转换团队得以将推理成本降低达50%。
我们决定剖析已编译的图版本,并进一步寻求对OpenPose管道进行端到端推理性能调优的空间。Neuron与TensorFlow相集成之后,可实现对本地配置文件库的访问。要剖析Neuron编译图,我们使用TensorFlow Python分析器,在estimator方法上检测TensorFlow会话所运行的命令:
其中model_analyzer.profile
方法会在StdErr
上输出TensorFlow图上各项操作所对应的时间与内存消耗。使用原始代码,Neuron操作与平滑操作占据整个图运行时间中的大部分比例。以下StdErr
日志输出结果显示,总图运行时间为108.02毫秒,其中平滑操作耗费43.07毫秒:
其中,OpenPose中平滑方法提供针对置信度图计算得出的高斯模糊结果。通过对此项计算进行优化,我们能够从端到端姿态估计当中榨取出更好的性能。我们将estimator.py脚本上的平滑器filter参数从25修改为5。在使用新配置之后,总运行时间降低至67.44毫秒,其中平滑计算如今只需要2.37毫秒——时间节约量达37%!而在g4dn实例上,相同的优化对于运行时间几乎没有任何影响。大家还可以更改同一参数,并通过本地副本重新安装 tf-pose-estimation repo
的方式优化端到端管道版本。
我们使用七种不同的实例类型与大小运行了同一项基准测试,借此评估端到端图像处理管道的性能与推理成本优化效果。为了进行直观比较,我们还纳入了Amazon EC2按需实例的计费指标。
结果显示,即使是Inf1实例中最小的xlarge实例,其吞吐量也要比g4dn实例中最强劲的8xlarge实例高2倍,且处理1000张图像的成本仅为后者的十二分之一。对比最优的两个选项,inf1.xlarge和g4dn.xlarge(成本最低的GPU选项),Inf1能够将每1000张图像的处理成本较g4dn.xlarge降低72%,相当于性价比提升3.57倍。下表整理了此次基准测试的各项发现:
inf1.xlarge | inf1.2xlarge | inf1.6xlarge | g4dn.xlarge | g4dn.2xlarge | g4dn.4xlarge | g4dn.8xlarge | |
图像处理时间(秒/图像) | 0.0703 | 0.0677 | 0.0656 | 0.1708 | 0.1526 | 0.1477 | 0.1427 |
吞吐量(FPS) | 14.22 | 14.77 | 15.24 | 5.85 | 6.55 | 6.77 | 7.01 |
1000张图像处理时间(秒) | 70.3 | 67.7 | 65.6 | 170.8 | 152.6 | 147.7 | 142.7 |
按需实例价格 | $ 0.368 | $ 0.584 | $ 1.904 | $ 0.526 | $ 0.752 | $ 1.204 | $ 2.176 |
每1000张图像处理成本 | $ 0.007 | $ 0.011 | $ 0.035 | $ 0.025 | $ 0.032 | $ 0.049 | $ 0.086 |
以下图表,总结了xlarge与2xlarge实例在处理1000张图像时的吞吐量与成本情况。
通过在端到端管道中引入数据并行处理方法,我们又进一步降低了Inf1实例的图像处理成本并提升了 tf-pose-estimation
吞吐量。上表中列出的数值,与单一AWS Inferentia处理核心(即Neuron核心)的使用方式有关。基准实例中包含4个核心,因此仅使用其中1个显然会产生资源浪费。我们使用Python joblib库以比较粗糙的方式将main()函数调用实现并发扩展至四个线程当中。这种模式能够将吞吐量提升至56.88 FPS,并将每1000张图像的成本降低至0.002美元以下。这意味着更好的批处理策略可以让OpenPose在AWS Inferentia实例上的运行性价比得到更进一步的改善。
规模较大的CMU模型还能提供更好的姿态估计性能。例如,大家可以在包含多重景深与相应拍摄对象的场景下(参见下图),使用Neuron SDK编译模型进行多姿态检测。
安全关闭并进行资源清理
在Amazon EC2控制台上选择您的编译与推理实例,而后从Actions下拉菜单中选择Terminate。您编译之后的模型将被保存在s3://<MY_BUCKET_NAME> 当中以备后续重复使用。如果您对实例中的代码做出了更改,也请注意进行保存。实例终止仅会导致存储在实例主卷内的数据丢失。
总结
在本文中,我们分步完成了对OpenPose TensorFlow版开源模型的编译,更新自定义端到端图像处理管道,并体验了能够在EC2 Infi1实例之上对ML推理时间做出分析及深度优化的工具。在调优之后,Neuron编译的TensorFlow模型较现有费率最低的GPU实例实现72%的成本节约,且性能仍旧保持一致。本文中阐述的各项操作步骤,也适用于其他ML模型类型与框架。关于更多详细信息,请参阅AWS Neuron SDK GitHub repo。
感兴趣的朋友可以参阅关于AWS Inferentia芯片 与Amazon EC2 Inf1实例的更多详细信息,使用Neuron SDK在AWS Inferentia上运行您自己的定制化ML管道。