写在前面
在爱奇艺视频图像增强项目中上线视频推理服务,通过优化将处理速率提升了10倍,GPU利用率稳定在90%以上。本文通过分享视频推理服务解决方案,帮助为解决利用率低的问题相关从业者提供一些思路。
背 景
和业内专家交流过程中,我们发现视频推理是一个共同的热点需求和难点需求,普遍反应是深度模型推广速度快,优化工作跟不上模型上线的要求,表现出来是集群的整体利用率不尽如人意。在爱奇艺AI推理服务中,图像类业务占了最大的比例,图像的主要来源是视频。业务的服务形式表现为:对视频流数据进行模型推理(后文简称视频推理)。
视频推理服务中视频分类业务这一大类最为广泛应用,通常是采用多个分类模型进行推理,得到一些分类的概率数值或者特征数值表示。这类服务典型业务有:视频审核,版权检查,人脸识别等等。
而视频推理服务中的另一大类服务,则是视频变换服务。简单来讲,就是输入一个视频,再输出一个经过变形的视频。这类算法常常带有更多的主观性。图像增强,超分辨率的转换,是目前行业中的代表性业务。
视频变换服务与对比分类服务,有两个特点:
第一,是计算需求量常常比分类服务高一个或几个数量级。分类服务通常只需要对视频中的一小部分有代表性信息进行处理,帧率可以缩减,输入图像分辨率可以降低。视频变换服务则需要针对视频的每一帧图像进行处理,图像分辨率常常保持不变,甚至会增大。
第二,是多数算法需要手动集成。视频变换算法通常包含比较复杂的前后处理操作,既包含深度模型,又有传统CV操作。不同视频变换算法差异性较大,更不容易抽象成统一服务。
这两个特点在上线视频推理服务时,我们常常遇到两个麻烦。第一,性能优化不足。如果算法全量上线,需要的计算资源成本太高;第二,推理功能需要和编解码功能在一起集成,针对每个模型都要进行手动调整测试。上线周期基本上由平台工程师的个数决定。
本文重点将放在视频变换服务上,同时会简单分享在短视频推理服务的优化工作。
已有方案
对于视频变换服务,出现在大多数工程师脑中的第一个方案,通常是先将视频拆成图像,然后调用原有的图像推理服务,最后将推理结果汇总处理,返回汇总结果。这个汇总结果可能是一系列图片,也可能是编码后的视频。
在视频图像增强项目中,编码团队改造了ffmpeg,完成解码→推理转换→编码整个过程。其中推理转换过程,采用了一个自定义的filter,用于前后处理和模型推理。速度比写硬盘方案快了不少,资源消耗也少了很多。
实际运行过程中,发现GPU利用率较低。主要原因是,CPU编码速度比GPU推理速度要慢。于是采用GPU硬件编码器,速度大大提升,GPU利用率有所提升,但是距离GPU计算极限还有较大差距。
这里需要指出一个普遍存在对GPU利用率(GPU utilization)的误解。手册中,GPU utilization含义是:
“Percent of time over the past sample period during which one or more kernels was executing on the GPU.”
也就是说,计算时间占总时间的比例。简单来讲,也就是GPU在干活时间中的比例。至于GPU干得累不累,需要用另一个指标(Occupancy)来衡量。可惜的是这个指标不是那么容易拿到。
那么要怎么让GPU工作更加饱满呢?有一个直接办法,就是加大计算任务规模。原来一次处理一张图片,现在一次处理16张图片。通常把一次处理样本的数量叫做batch size。对于多数图像模型来说,batch size大于16之后,处理速度不会有显著增加。调大batch size之后,吞吐量可能增加2-4倍,单张图片的处理延迟略有增加。
回到视频图像增强项目上,ffmpeg filter API一次只允许处理一张图片。所以,要另外想办法。在解释具体优化工作之前,先来分析一些整个任务执行路径。
一个典型的推理过程如下:
解码 → NV12转RGB → 前处理 → 模型推理 → 后处理 → RGB转NV12 → 编码
如果编解码在CPU上完成,推理在GPU完成,还会多出在内存和显存之间搬移数据的环节。1080p一帧图片RGB原数据大小为5.93MB。按两倍视频速率计算,一秒钟需要在内存和显存之间传输的数据量为:2x24x5.93x2=569.28MB。按照3GB/s的传输速度(Pageable Data Transform),大约需要190ms。也就是说大约20%时间,会花费在数据传输上。
为了解决数据搬移造成计算断流的问题,Nvidia提供了一个视频推理解决方案:DeepStream。
DeepStream采用TensorRT(TRT)作为推理引擎,支持多路视频输入,采用GPU硬编解码单元进行,视频到图像再到视频的转换。在整个转换过程中,所有数据都可以保留在GPU显存中,没有额外换进换出开销。调优过的DeepStream的任务,速度快,吞吐量接近硬件计算理论峰值。
于是我们做实验,实验中TensorRT对不同分辨率模型多次执行优化过程,模型转换时间长。不同batch size也需要使用不同的优化后模型。如果模型中有不支持的操作,需要编写对应的插件。此外,有特殊的前后处理的业务逻辑在DeepStream上集成起来比较困难。
DeepStream在颜色空间转换时,会发生取整误差,对色彩比较敏感的模型,结果会出现较大差异。DeepStream使用的图像缩放算法,也与实际训练抽帧算法有差异,造成模型结果不一致。
基于上述原因,我们在视频图像增强项目上,没有选用DeepStream。
优化方案介绍
最终我们方案如下图所示:
一个典型的请求流程如下:
请求方发送任务请求到服务入口;
服务入口程序将任务信息,如视频来源地址/目标地址、回调地址等信息记录到数据库中;
工作程序(worker)读取任务数据库,执行推理逻辑,最后将处理结果上传到云端存储,并发送处理结果到回调地址。
其中,工作程序的推理环节是我们优化的重点和难点。
这个方案有三个特点:
所有操作都在GPU完成,并且支持批量推理;
前后处理及推理过程都通过python脚本完成;
完整方案包括视频下载,处理,上传流水线并行服务。
第一个特点与DeepStream类似,性能参数也和DeepStream相当,就不赘述。
细节见下图:
本方案的第二特点是最独特的亮点,解决DeepStream内部黑盒操作方式,最大化使用自由度。我们采用用户自定义python脚本,来加载模型和执行推理,大大增加了业务适配和验证工作的速度。本方案提供的python环境预装了Tensorflow,PyTorch,cupy,opencv等常见的算法库,覆盖了大多数基本功能。同时也支持自定义CUDA kernel调用,解决长尾和性能需求。
一些模型还有复杂的前后处理要求。开始部署时,对资源的需求量比较少,通过脚本快速开发,将一些基础操作组合起来,实现业务快速落地,尽早评估模型效果,加快迭代速度。如果反馈结果比较好,业务扩大服务规模,资源需求变得庞大。这时我们可以针对计算瓶颈,使用CUDA API编写计算kernel,提高计算效率,节省大量成本。
我们最终的期望是,算法工程师可以自行使用SDK来调试推理功能,确认结果,以减少平台工程师介入。同时,SDK还提供了操作计时功能,可用于分析性能瓶颈。
常见使用场景是,一个服务通常会使用多个模型进行推理,然后使用boosting算法取得更加可靠的结果。本方案可以轻松支持多模型推理,只要显存容量满足需求,甚至可以同时对Tensorflow和PyTorch的模型进行推理。
在不同计算框架间,进行数据交换,是这个方案面临的重要问题。深度学习开发社区提出了DLPack协议,来解决这个问题。因为DLPack没有做数据搬移,性能损失可以忽略。这个协议一推出就得到PyTorch,mxnet,cupy等框架的支持。但是在项目开发时,Tensorflow还没有不支持DLPack功能。Google工程师建议,将显存数据搬移到内存中,生成一个numpy对象后,再发送到Tensorflow Session。这样一来,把数据保持在显存的设计就被打破了。我们通过编写了特殊的Tensorflow OP,解决了这个问题。这个OP将显存中的数据复制到指定变量中,推理时使用指定变量作为输入。但在使用这个特殊OP时,调用者需要保证传递的数据不能超过目标变量的空间。
值得庆幸的是,社区开发了tfdlpack-gpu来完成这个目标。这个特殊OP也完成了它的历史使命。
仅仅“算得快”还不能满足业务实际需求。我们将整个计算过程封装成一个统一服务:从开发调试,模型部署,到运行监控,形成了完整功能闭环。为了更好的提供开箱即用体验,我们提供了任务管理、监控、日志等API。算法工程师只需要提交模型和推理脚本,就可以在云端部署服务。调度系统可以根据待运行任务的多寡进行动态的扩缩容,也可以按照预先设定完成定时扩缩容。
在推广该方案中,我们遇到的一个最常见的问题是:推理时预处理结果和训练的样本不一致。视频解码完之后,拿到的结果通常是NV12编码,通过自定义的CUDA kernel转换成RGB编码。但是,因为不同运算单元的计算方式和取整误差的影响,CUDA kernel得到RGB编码与ffmepg拿到的结果存在1%-2%的误差。虽然人眼看过去没有太大区别,但是对模型结果却有不同程度的影响。此外,不同的图像缩放算法也给结果带来不小的影响。
解决预处理结果不一致问题,最直接的办法是使用视频推理服务对训练素材进行处理,用同一个预处理结果进行训练。这样方式,保证了训练和推理时的输入一致性。
短视频推理服务
在解决长视频变换问题之后,我们尝试将这个服务扩展到短视频推理业务上。短视频推理服务多用于审核、打标签等业务中,使用场景多为同步在线服务。这就对整个请求延迟要一定的要求。业务愿意用更多的资源来换取更低的请求延迟,所以这类服务在线上的GPU利用率比长视频服务更低。
要达到低延迟的目标,常见的办法是降低处理数据量。其中一个典型的方法是,只对短视频的关键帧做处理。一两分钟的视频,关键帧的数量为10到20个。这方式可以通过一次推理就能取得结果。
但是为长视频构建的流水线,在短视频上表现得并不好。因为在开始解码前,我们需要对硬件解码器做初始化,通常会花费大约110ms。推理的耗时大概也在100ms左右,实际端到端延迟是纯推理耗时的大约两倍。经过仔细阅读手册,我们发现一个解决办法:对硬件编码器进行重新配置,而不是重新初始化。拿到第一帧数据的时间从110ms缩短到15ms。最终得到的结果是,推理时间占比大于85%。
总 结
经过爱奇艺技术产品团队的细致调优后,视频图像增强项目最终吞吐速率比ffmpeg方案提升了10倍,相同的任务使用的资源量只有原来的10%。这样因为资源使用量减少了,所以视频图像增强项目中,运营同学就可以把节约的资源更多的用于扩大视频增强的覆盖范围。
值得注意的是,V100实际吞吐量是T4的2.57倍。但是,T4标称的fp16处理能力是V100的一半。猜测可能原因是,T4的内存带宽是V100的1/3。综合考虑,我们最终还是使用了V100来处理视频。使得实际运行过程中,视频图像增强服务GPU利用率始终保持在90%以上。
随着深度学习模型不断快速迭代发展、应用范围不断拓展、业务模式不断的更新变化,单一种服务方式已经不能满足全部需求。本项目针对视频推理服务,通过将由GPU执行推理服务全流程,支持自定义推理脚本,将推理和数据传输并行等方式,达到了良好计算效率与优化工作量的平衡,实现了视频推理类模型的快速落地,同时节省了大量成本。我们希望通过平台化标准化视频推理服务,解决模型上线难和视频推理上线利用率低的问题,让算法工程师从繁琐的工程实践中得到解放。
也许你还想看
让AI“读懂”短视频视频与编解码的技术邂逅,碰撞出的高清罗曼史
扫一扫下方二维码,更多精彩内容陪伴你!