??本文分享自華為云社區(qū)《如何使用ModelBox快速提升AI應(yīng)用性能》,作者: panda。
隨著AI技術(shù)和計(jì)算能力的發(fā)展,越來越多的開發(fā)者學(xué)會(huì)用tensorflow、pytorch等引擎訓(xùn)練模型并開發(fā)成AI應(yīng)用以解決各種生產(chǎn)問題。在開發(fā)初期開發(fā)者往往聚焦在模型的精度上,性能關(guān)注較少,但隨著業(yè)務(wù)量不斷增加,AI應(yīng)用的性能往往成為瓶頸,此時(shí)對(duì)于沒有性能優(yōu)化經(jīng)驗(yàn)的開發(fā)者來說往往需要耗費(fèi)大量精力做優(yōu)化性能,本文為開發(fā)者介紹一些常用的優(yōu)化方法和經(jīng)驗(yàn)。本文首先介紹什么是AI應(yīng)用性能優(yōu)化,以及常用的性能優(yōu)化手段,然后介紹華為云ModelBox開源框架,最后結(jié)合實(shí)際業(yè)務(wù)為例,詳細(xì)講解如何利用ModelBox框架進(jìn)行快速的性能優(yōu)化以及背后的原理。
什么是AI應(yīng)用性能優(yōu)化? AI應(yīng)用性能優(yōu)化是保證結(jié)果正確的情況下,提升AI推理應(yīng)用執(zhí)行效率。AI應(yīng)用性能優(yōu)化的目的一般分為兩方面:一方面可以提升用戶體驗(yàn),如門禁系統(tǒng)刷臉場景,對(duì)推理時(shí)延比較敏感,識(shí)別速度直接影響用戶感官,再比如自動(dòng)駕駛場景,對(duì)時(shí)延要求非常高;另一方面可以降低硬件成本,相同的硬件設(shè)備可以支撐更多的業(yè)務(wù),當(dāng)部署節(jié)點(diǎn)數(shù)具備一定規(guī)模時(shí),節(jié)省的硬件成本就相當(dāng)可觀了。
如何去衡量性能的好壞?我們通常使用吞吐量和時(shí)延來衡量。 吞吐量在不同場景也有不同衡量指標(biāo),比如圖片請求場景,一般使用qps作為吞吐量的指標(biāo),即每秒種處理的請求個(gè)數(shù)。在視頻流場景,則一般使用視頻并發(fā)路數(shù)來衡量。 時(shí)延是指數(shù)據(jù)輸入到結(jié)果輸出中間的處理時(shí)間差。正常來講吞吐量越大越好,時(shí)延越小越好,在不同場景對(duì)吞吐量和時(shí)延的要求不一樣, 對(duì)于某些時(shí)延不敏感的場景,我們可以犧牲時(shí)延來提升吞吐量。所以我們在做性能優(yōu)化前需要先明確優(yōu)化指標(biāo)是吞吐量還是時(shí)延。
另外除此之外,在性能優(yōu)化過程中,還需要重點(diǎn)關(guān)注一些系統(tǒng)資源指標(biāo),如內(nèi)存、顯存、CPU占用率、GPU占用率等。這些指標(biāo)可以幫忙我們輔助判斷當(dāng)前資源使用情況,為我們做性能優(yōu)化提供思路,如GPU利用率較低時(shí),就需要針對(duì)性想辦法充分利用GPU資源。
一個(gè)AI應(yīng)用可以分為模型和工程邏輯,AI應(yīng)用的優(yōu)化我們也可以從上到下進(jìn)行劃分,其中應(yīng)用流程優(yōu)化和應(yīng)用工程優(yōu)化為工程方面的優(yōu)化,模型編譯優(yōu)化和模型算法優(yōu)化則為模型優(yōu)化。
應(yīng)用流程優(yōu)化:主要是對(duì)業(yè)務(wù)邏輯進(jìn)行調(diào)整,減少一些不必要的操作以到達(dá)性能提升的效果,業(yè)務(wù)邏輯的優(yōu)化有時(shí)是最快捷最有效的,往往會(huì)有事半功倍的效果。但需要具體場景具體分析。
應(yīng)用工程優(yōu)化:主要是軟件工程方面的優(yōu)化,如多線程、內(nèi)存池、硬件加速等等, 對(duì)上層 ,此外模型batching也是最常見的優(yōu)化手段,通過共享隊(duì)列組batch以充分利用模型的batching性能。方法較通用。ModelBox框架提供的主要為應(yīng)用工程優(yōu)化能力。
模型編譯優(yōu)化:常用手段有低精度量化、混合精度等、算子融合等,此類優(yōu)化會(huì)影響模型精度。
模型算法優(yōu)化:對(duì)模型結(jié)構(gòu)進(jìn)行優(yōu)化,減少模型計(jì)算量,如模型剪枝、模型蒸餾等,需要重新訓(xùn)練。
本文重點(diǎn)介紹AI應(yīng)用工程優(yōu)化的常用手段,常用優(yōu)化手段如下:
模型Batching: 原理主要是將多次推理數(shù)據(jù)合并成一批數(shù)據(jù)進(jìn)行GPU推理,相比單數(shù)據(jù)推理,batching推理可以降低Gpu Kernel Launch次數(shù),充分利用多個(gè)GPU計(jì)算單元并發(fā)計(jì)算,從而提高整體吞吐量。 一次推理的數(shù)據(jù)個(gè)數(shù)叫Batchsize,Batchsize不一定是越大越好,往往和模型結(jié)構(gòu)的稀疏程度有關(guān)系 ,所以需要具體模型具體分析。
Pipeline并行:將業(yè)務(wù)的處理劃分為幾個(gè)階段,通過流水線的方式讓不同數(shù)據(jù)并行起來。如下圖所示,同一時(shí)間數(shù)據(jù)1在執(zhí)行操作C的同時(shí),數(shù)據(jù)2在執(zhí)行操作B,數(shù)據(jù)3在執(zhí)行操作A。
多線程并發(fā):某個(gè)操作單線程處理成為瓶頸時(shí),可以采用多線程并發(fā)執(zhí)行。但一般還需要對(duì)多線程執(zhí)行的結(jié)果做保序操作。
硬件加速:使用硬件的加速能力如Cuda、Ascend 、 SIMD等,與此同時(shí)硬件的加速會(huì)帶來額外的主機(jī)到硬件設(shè)備的內(nèi)存拷貝開銷。
顯存拷貝/顯存申請: 不同與內(nèi)存,硬件上顯存的拷貝和申請耗時(shí)較長,頻繁的申請和拷貝會(huì)影響整體性能,可以通過顯存池的管理減少內(nèi)存申請的時(shí)間,還可以調(diào)整業(yè)務(wù)邏輯,盡量減少HtoD,DtoH的拷貝次數(shù)。
Cuda/Ascend Stream 異步: 基于cuda或者ascend硬件時(shí),可以使用帶Stream的異步接口進(jìn)行加速。
異構(gòu)計(jì)算加速:可以使用多個(gè)或者多種硬件進(jìn)行加速,如使用多GPU進(jìn)行推理,再比如使用cpu+gpu多硬件同時(shí)推理,并且能做到負(fù)載均衡。
以上這些常用的應(yīng)用工程優(yōu)化需要根據(jù)當(dāng)前業(yè)務(wù)瓶頸合理選擇。同時(shí)上述方法的實(shí)現(xiàn)實(shí)現(xiàn)往往需要耗費(fèi)大量工作,同時(shí)對(duì)軟件能力要求較高。為此華為云開源了ModelBox框架,集成了上述優(yōu)化手段,能夠幫忙開發(fā)者快速提升性能。
一個(gè)典型場景AI算法的商用落地除了模型訓(xùn)練外,還需要進(jìn)行視頻圖片解碼、HTTP服務(wù)、預(yù)處理、后處理、多模型復(fù)雜業(yè)務(wù)串聯(lián)、運(yùn)維、打包等工程開發(fā),往往需要耗費(fèi)比模型訓(xùn)練多得多的時(shí)間,同時(shí)算法的性能和可靠性通常隨開發(fā)人員的工程能力水平高低而參差不齊,嚴(yán)重影響AI算法的上線效率。
ModelBox是一套專門為AI開發(fā)者提供的易于使用,高效,高擴(kuò)展的AI推理開發(fā)框架,它可以幫助AI開發(fā)者快速完成從模型文件到AI推理應(yīng)用的開發(fā)和上線工作,降低AI算法落地門檻,同時(shí)帶來AI應(yīng)用的高穩(wěn)定性和極致性能。ModelBox是一套易用、高效、高擴(kuò)展的AI推理開發(fā)框架,幫助開發(fā)者快速完成算法工程化,并帶來高性能,一次開發(fā)端邊云部署等好處。ModelBox框架當(dāng)前已經(jīng)開源,可詳見https://modelbox-ai.com。
ModelBox框架主要特點(diǎn)有:
ModolBox框架采用圖編排的方式開發(fā)業(yè)務(wù),將應(yīng)用執(zhí)行邏輯通過有向圖的方式表達(dá)出來,而圖上的每個(gè)節(jié)點(diǎn)叫做ModelBox功能單元,是應(yīng)用的基本組成部分,也是ModelBox的執(zhí)行單元。在ModelBox中,內(nèi)置了大量的高性能基礎(chǔ)功能單元庫,開發(fā)者可以直接復(fù)用這些功能單元減少開發(fā)工作。除內(nèi)置功能單元外,ModelBox支持功能單元的自定義開發(fā),支持的功能單元形式多樣,如C/C++動(dòng)態(tài)庫、Python腳本、模型+模型配置文件等。除此之外,ModolBox提供了運(yùn)維安全、開發(fā)調(diào)試等配套的組件用于快速服務(wù)化。
ModolBox邏輯架構(gòu)如下圖:
ModelBox提供了兩個(gè)開發(fā)模式:標(biāo)準(zhǔn)模式和SDK模式。
標(biāo)準(zhǔn)模式:這種模式下AI應(yīng)用的主入口由ModelBox進(jìn)程管理,應(yīng)用的全部邏輯承載編排在流程圖中,開發(fā)者首先通過流程圖配置文件描述整個(gè)應(yīng)用的數(shù)據(jù)處理過程,然后實(shí)現(xiàn)流程圖中缺少的功能單元,完成整個(gè)應(yīng)用。此模式優(yōu)點(diǎn)是并發(fā)度高,性能好,配套組件豐富,缺點(diǎn)是需要把全部業(yè)務(wù)邏輯拆分為圖,在存量復(fù)雜業(yè)務(wù)場景切換工作量大。
SDK模式:這種模式下,開發(fā)者業(yè)務(wù)進(jìn)程通過ModelBox SDK提供的API管理流程圖的初始化、啟動(dòng)及數(shù)據(jù)交互, 此模式可以選擇性的將部分邏輯切換為ModelBox圖編排。優(yōu)點(diǎn)是改動(dòng)少,優(yōu)化工作量少,可以逐步優(yōu)化。缺點(diǎn)相對(duì)于標(biāo)準(zhǔn)模式只能獲得部分性能收益。
兩種模式適用于不同場景。標(biāo)準(zhǔn)模式適用于整體業(yè)務(wù)邏輯清晰,比較容易通過流程圖方式表達(dá)的場景,和新開發(fā)業(yè)務(wù)場景。SDK模式適用場景于應(yīng)用邏輯不能全部進(jìn)入流程圖中,控制邏輯較為復(fù)雜的場景;已有業(yè)務(wù)遷移場景等。本文后續(xù)講解的AI應(yīng)用性能優(yōu)化實(shí)踐主要通過SDK模式進(jìn)行優(yōu)化。
下面以一個(gè)圖像分類的AI應(yīng)用為樣例,介紹如何使用ModelBox框架進(jìn)行性能優(yōu)化。該業(yè)務(wù)原始代碼使用Python語言開發(fā),采用flask框架作為Http Server提供Restful API 對(duì)輸入圖像進(jìn)行識(shí)別分類,模型為ResNet101網(wǎng)絡(luò),訓(xùn)練引擎為tensorflow 。具體業(yè)務(wù)邏輯和性能情況如下圖所示:
從當(dāng)前業(yè)務(wù)場景和性能測試情況看,推理階段耗時(shí)占比大,導(dǎo)致整體性能較差。對(duì)照前面講解的AI應(yīng)用軟件工程優(yōu)化方法,我們可以從以下幾個(gè)方面嘗試做優(yōu)化:
1)一次請求攜帶一張圖片,只能單batch推理,多個(gè)請求多次單bacth推理,算然gpu利用率100%,但效率低,可通過模型batching優(yōu)化推理性能。
2)如果模型推理時(shí)間優(yōu)化后,預(yù)處理、推理、后處理可以通過pipeline并發(fā)優(yōu)化。
3)圖片decode、resize、mean等cpu的預(yù)處理操作可以通過cuda、多線程加速。
我們使用ModelBox框架可以快速嘗試上述模型和預(yù)處理優(yōu)化,測試效果。
我們首先嘗試使用ModelBox 框架SDK API優(yōu)化模型推理部分性能,針對(duì)純模型優(yōu)化,ModelBox 提供了Model接口,只需幾行代碼即可完成優(yōu)化。
1) 環(huán)境準(zhǔn)備下載tensorflow引擎的ModelBox開發(fā)鏡像。下載方法可見ModelBox文檔,在代碼中引入modelbox包,設(shè)置日志級(jí)別。
# modelbox
import modelbox
modelbox.set_log_level(modelbox.Log.Level.DEBUG)
2) 配置推理功能單元新建classify_infer.toml配置文件,根據(jù)模型實(shí)際情況填寫模型配置,如模型文件路徑、推理引擎類型、輸入Tensor名稱、輸出Tensor名稱等。配置如下:
# 基礎(chǔ)配置
[base]
name = "classify_infer" # 功能單元名稱
device = "cuda" # 功能單元運(yùn)行的設(shè)備類型,cpu,cuda,ascend等。
version = "0.0.1" # 功能單元組件版本號(hào)
description = "description" # 功能單元功能描述信息
entry = "../model/resnet_v1_101.pb" # 模型文件路徑
type = "inference" #推理功能單元時(shí),此處為固定值
virtual_type = "tensorflow" # 指定推理引擎, 可以時(shí)tensorflow, tensorrt, atc
[config]
plugin = "" # 推理引擎插件
# 輸入端口描述
[input]
[input.input1] # 輸入端口編號(hào),格式為input.input[N]
name = "input" # 輸入端口名稱
# 輸出端口描述
[output]
[output.output1] # 輸出端口編號(hào),格式為output.output[N]
name = "resnet_v1_101/predictions/Softmax" # 輸出端口名稱
3) 模型初始化在業(yè)務(wù)初始化階段使用Model接口進(jìn)行模型推理實(shí)例初始化,接口如下:
modelbox.Model(path, node_name, batch_size, device_type, device_id)
輸入?yún)?shù)說明:path: 推理功能單元配置文件路徑,即classify_infer.toml路徑node_name:實(shí)例名稱batch_size:一次batching推理的batchsize最大值,當(dāng)不足時(shí),采用動(dòng)態(tài)batch。device_type:加速硬件類型,可取值cuda、cpu、ascend等,也可設(shè)置多硬件,如” cuda:0,1,2;cpu:0” 等device_id:單加速類型時(shí),加速設(shè)備號(hào)
Model實(shí)例初始化成功后啟動(dòng),同時(shí)注釋掉原有tensorflow不再使用的代碼,初始化代碼如下:
def __init__(self)
...
# modelbox
self.model = modelbox.Model("/home/code/image_classify/classify_infer/", "classify_infer", ["input"], ["resnet_v1_101/predictions/Softmax"], 8, "cuda", "0")
self.model.start()
4) 模型推理替換使用Model.infer 接口替換掉原始tensorflow的session.run接口,接口說明如下:
output = model.infer([input_port1_data, input_port2_data, … ])
輸入?yún)?shù)說明:input_port1_data、 input_port2_data : 模型每個(gè)輸入Tensor數(shù)據(jù)輸出參數(shù)說明:output: 模型的推理結(jié)果列表,,可以通過下標(biāo)獲取每個(gè)Tensor輸出結(jié)果。結(jié)果類型為modelbox::Buffer,通常需要通過numpy接口轉(zhuǎn)換numpy類型進(jìn)行后處理。
具體代碼修改如下:
def process(self, img_file):
image = self.preprocess(img_file)
...
# 對(duì)image進(jìn)行推理,batch為1
# infer_output = self.sess.run(self.output,feed_dict={self.input: np.expand_dims( image,0 )})
# probabilities = infer_output[0, 0:]
#modelbox
output_list = self.model.infer([image.astype(np.float32)])
output_buffer = output_list[0]
probabilities = np.array(output_buffer)
...
self.postprocess(probabilities, resp)
至此,推理的優(yōu)化代碼已修改完畢,進(jìn)行功能調(diào)試后,即可對(duì)性能進(jìn)行測試。通過還可以通過ModelBox 性能Profiling工具進(jìn)行性能數(shù)據(jù)打點(diǎn)分析推理執(zhí)行性能詳細(xì)情況,具體使用方法可見官方文檔。前面我們講到bacth_size并不是越大越好,我們可以通過調(diào)整bacth_size參數(shù)測試性能情況。該業(yè)務(wù)實(shí)測數(shù)據(jù)如下:
從當(dāng)前業(yè)務(wù)場景和性能測試情況看,推理階段耗時(shí)占比大,導(dǎo)致整體性能較差。對(duì)照前面講解的AI應(yīng)用軟件工程優(yōu)化方法,我們可以從以下幾個(gè)方面嘗試做優(yōu)化:
1)一次請求攜帶一張圖片,只能單batch推理,多個(gè)請求多次單bacth推理,算然gpu利用率100%,但效率低,可通過模型batching優(yōu)化推理性能。
2)如果模型推理時(shí)間優(yōu)化后,預(yù)處理、推理、后處理可以通過pipeline并發(fā)優(yōu)化。
3)圖片decode、resize、mean等cpu的預(yù)處理操作可以通過cuda、多線程加速。
我們使用ModelBox框架可以快速嘗試上述模型和預(yù)處理優(yōu)化,測試效果。
我們首先嘗試使用ModelBox 框架SDK API優(yōu)化模型推理部分性能,針對(duì)純模型優(yōu)化,ModelBox 提供了Model接口,只需幾行代碼即可完成優(yōu)化。
1) 環(huán)境準(zhǔn)備下載tensorflow引擎的ModelBox開發(fā)鏡像。下載方法可見ModelBox文檔,在代碼中引入modelbox包,設(shè)置日志級(jí)別。
# modelbox
import modelbox
modelbox.set_log_level(modelbox.Log.Level.DEBUG)
2) 配置推理功能單元新建classify_infer.toml配置文件,根據(jù)模型實(shí)際情況填寫模型配置,如模型文件路徑、推理引擎類型、輸入Tensor名稱、輸出Tensor名稱等。配置如下:
# 基礎(chǔ)配置
[base]
name = "classify_infer" # 功能單元名稱
device = "cuda" # 功能單元運(yùn)行的設(shè)備類型,cpu,cuda,ascend等。
version = "0.0.1" # 功能單元組件版本號(hào)
description = "description" # 功能單元功能描述信息
entry = "../model/resnet_v1_101.pb" # 模型文件路徑
type = "inference" #推理功能單元時(shí),此處為固定值
virtual_type = "tensorflow" # 指定推理引擎, 可以時(shí)tensorflow, tensorrt, atc
[config]
plugin = "" # 推理引擎插件
# 輸入端口描述
[input]
[input.input1] # 輸入端口編號(hào),格式為input.input[N]
name = "input" # 輸入端口名稱
# 輸出端口描述
[output]
[output.output1] # 輸出端口編號(hào),格式為output.output[N]
name = "resnet_v1_101/predictions/Softmax" # 輸出端口名稱
3) 模型初始化在業(yè)務(wù)初始化階段使用Model接口進(jìn)行模型推理實(shí)例初始化,接口如下:
modelbox.Model(path, node_name, batch_size, device_type, device_id)
輸入?yún)?shù)說明:path: 推理功能單元配置文件路徑,即classify_infer.toml路徑node_name:實(shí)例名稱batch_size:一次batching推理的batchsize最大值,當(dāng)不足時(shí),采用動(dòng)態(tài)batch。device_type:加速硬件類型,可取值cuda、cpu、ascend等,也可設(shè)置多硬件,如” cuda:0,1,2;cpu:0” 等device_id:單加速類型時(shí),加速設(shè)備號(hào)
Model實(shí)例初始化成功后啟動(dòng),同時(shí)注釋掉原有tensorflow不再使用的代碼,初始化代碼如下:
def __init__(self)
...
# modelbox
self.model = modelbox.Model("/home/code/image_classify/classify_infer/", "classify_infer", ["input"], ["resnet_v1_101/predictions/Softmax"], 8, "cuda", "0")
self.model.start()
4) 模型推理替換使用Model.infer 接口替換掉原始tensorflow的session.run接口,接口說明如下:
output = model.infer([input_port1_data, input_port2_data, … ])
輸入?yún)?shù)說明:input_port1_data、 input_port2_data : 模型每個(gè)輸入Tensor數(shù)據(jù)輸出參數(shù)說明:output: 模型的推理結(jié)果列表,,可以通過下標(biāo)獲取每個(gè)Tensor輸出結(jié)果。結(jié)果類型為modelbox::Buffer,通常需要通過numpy接口轉(zhuǎn)換numpy類型進(jìn)行后處理。
具體代碼修改如下:
def process(self, img_file):
image = self.preprocess(img_file)
...
# 對(duì)image進(jìn)行推理,batch為1
# infer_output = self.sess.run(self.output,feed_dict={self.input: np.expand_dims( image,0 )})
# probabilities = infer_output[0, 0:]
#modelbox
output_list = self.model.infer([image.astype(np.float32)])
output_buffer = output_list[0]
probabilities = np.array(output_buffer)
...
self.postprocess(probabilities, resp)
至此,推理的優(yōu)化代碼已修改完畢,進(jìn)行功能調(diào)試后,即可對(duì)性能進(jìn)行測試。通過還可以通過ModelBox 性能Profiling工具進(jìn)行性能數(shù)據(jù)打點(diǎn)分析推理執(zhí)行性能詳細(xì)情況,具體使用方法可見官方文檔。前面我們講到bacth_size并不是越大越好,我們可以通過調(diào)整bacth_size參數(shù)測試性能情況。該業(yè)務(wù)實(shí)測數(shù)據(jù)如下:
優(yōu)化前每個(gè)請求單獨(dú)處理, 每次推理一份數(shù)據(jù),使用ModelBox后,會(huì)有單獨(dú)ModelBox線程和隊(duì)列將多個(gè)線程的推理請求合并,通過bacthing推理一組數(shù)據(jù)。
推理模型切換到ModelBox后,除了收獲性能收益外,還可以獲得如下收益: 軟硬件引擎適配能力, 修改到其他引擎或者硬件無需修改代碼,只需要修改模型配置文件即可; 多卡、多硬件能力:可以通過配置至此單進(jìn)程多卡,或者多類型硬件異構(gòu)能力。
模型優(yōu)化完成后,如果瓶頸轉(zhuǎn)移到模型預(yù)處理,我們還可以通過ModelBox對(duì)AI應(yīng)用的預(yù)處理進(jìn)行優(yōu)化。下面介紹下如果通過ModelBox SDK API進(jìn)行推理加預(yù)處理優(yōu)化。
1) 構(gòu)造流程圖基于上一章節(jié)推理優(yōu)化步驟的環(huán)境準(zhǔn)備、配置推理功能單元后,我們需要將預(yù)處理和推理流程構(gòu)造為ModelBox流程圖。 原始業(yè)務(wù)邏輯中:圖片解碼、resize、mean、推理 以上這些操作都是相對(duì)耗時(shí),并且通過GPU加速的。本次我們對(duì)上述操作進(jìn)行流程圖構(gòu)建如下:
代碼層面,ModelBox可以通過兩種方式構(gòu)建流程圖:
a)通過圖配置文件構(gòu)建創(chuàng)建graph.toml, 并編寫配置文件如下:
[driver]
skip-default = false
dir=["/home/code/image_classify/classify_infer/"] # path for user c++ flowuint, python flowuint, infer flowunit
[profile]
profile=false
trace=false
dir="/home/code/image_classify/test/"
[log]
level="INFO"
[graph]
format = "graphviz"
graphconf = '''digraph weibo_sample {
queue_size=64
batch_size=8
input[type=input, device=cpu]
img_decoder[type=flowunit, flowunit=image_decoder, device=cpu, deviceid=0,batch_size=8]
img_resize[type=flowunit, flowunit=resize, device=cpu, deviceid=0, image_height=224, image_width=224,batch_size=18]
img_mean[type=flowunit, flowunit=mean, device=cpu, deviceid=0, mean="123.68,116.78,103.94",batch_size=8]
classify_infer[type=flowunit, flowunit=classify_infer, device=cuda, deviceid="1",batch_size=16]
output[type=output, device=cpu]
input -> img_decoder:in_encoded_image
img_decoder:out_image -> img_resize:in_image
#input -> img_resize:in_image
img_resize:out_image -> img_mean:in_data
img_mean:out_data -> classify_infer:input
classify_infer:"resnet_v1_101/predictions/Softmax" -> output
}
'''
配置文件編寫后通過ModelBox Flow接口加載并運(yùn)行:
def __init__(self)
...
# modelbox
self.flow = modelbox.Flow()
self.flow.init("/home/code/image_classify/graph/image_classify.toml")
self.flow.start_run()
b)通過代碼構(gòu)建通過FlowGraphDesc對(duì)象構(gòu)建流程圖,并加載運(yùn)行。
def __init__(self)
...
# modelbox
self.graph_desc = modelbox.FlowGraphDesc()
self.graph_desc.set_drivers_dir(["/home/code/image_classify/classify_infer/"])
self.graph_desc.set_queue_size(64)
self.graph_desc.set_batch_size(8)
input = self.graph_desc.add_input("input")
img_decoder = self.graph_desc.add_node("image_decoder", "cpu",input)
img_resize = self.graph_desc.add_node("resize", "cpu", ["image_height=224", "image_width=224"],img_decoder)
img_mean = self.graph_desc.add_node("mean", "cpu",["mean=123.68,116.78,103.94" ], img_resize)
classify_infer = self.graph_desc.add_node("classify_infer", "cuda", ["batch_size=32"], img_mean)
self.graph_desc.add_output("output", classify_infer)
self.flow = modelbox.Flow()
self.flow.init(self.graph_desc)
self.flow.start_run()
需要說明的是,本業(yè)務(wù)需要優(yōu)化的功能單元圖片解碼、resize、mean都是ModelBox預(yù)置功能單元,并且支持硬件加速,如果不在預(yù)置庫中時(shí),可以通過功能單元注冊接口注冊為功能單元。
不管哪種方式,我們都可以通過配置調(diào)整每個(gè)功能單元的batch_size、queue_size、設(shè)備類型,設(shè)備ID等功能參數(shù)來調(diào)整執(zhí)行策略。如通過設(shè)置device=cuda則指定改功能單元通過GPU加速,batch_size=8, 則表示一次處理8個(gè)數(shù)據(jù),queue_size =32 ,則代表非功能單元會(huì)使用queue_size/batch_size = 4個(gè)線程同時(shí)并行計(jì)算。
2) 業(yè)務(wù)邏輯替換將原有預(yù)處理和推理的代碼替換為ModelBox Flow的運(yùn)行接口。
def process(self, img_file):
# image = self.preprocess(img_file)
# 對(duì)image進(jìn)行推理,batch為1
# infer_output = self.sess.run(self.output,feed_dict={self.input: np.expand_dims( image,0 )})
# probabilities = infer_output[0, 0:]
#modelbox
stream_io = self.flow.create_stream_io()
buffer = stream_io.create_buffer(img_file)
stream_io.send("input", buffer)
output_buffer = stream_io.recv("output")
probabilities = np.array(output_buffer)
...
self.postprocess(probabilities, resp)
send()輸入?yún)?shù)說明: 圖的輸入端口名稱,輸入bufferrecv()輸出參數(shù)說明: output: 圖的輸出buffer
至此,預(yù)處理加推理的優(yōu)化代碼已修改完畢,進(jìn)行功能調(diào)試后,即可對(duì)性能進(jìn)行測試。同樣可以通過ModelBox 性能Profiling工具進(jìn)行性能分析。我們分別設(shè)置預(yù)處理全為cpu、 預(yù)處理全為gpu進(jìn)行性能測試,測試結(jié)果如下:
可以看到同為batchsize為16時(shí),通過預(yù)處理性能較純模型推理優(yōu)化性能有提升,同時(shí)全為cpu預(yù)處理時(shí)反而比gpu預(yù)處理性能好。這是因?yàn)橐环矫鎐pu預(yù)處理采用了多線程并發(fā)處理,另一方面GPU預(yù)處理搶占了GPU資源,影響了推理速度,從而影響整體性能。所以并不推薦所有操作都使用硬件加速,需要具體場景具體分析,保證資源計(jì)算的合理分配。
通過ModelBox優(yōu)化后的數(shù)據(jù)執(zhí)行情況如上,優(yōu)化收益主要如下:
AI應(yīng)用的性能優(yōu)化是一個(gè)循序漸進(jìn)的過程,并不是所有方法都有效,開發(fā)者需要結(jié)果自身業(yè)務(wù)具體問題具體分析,才能到達(dá)事半功倍的效果。經(jīng)過實(shí)際業(yè)務(wù)的優(yōu)化實(shí)踐,希望大家對(duì)如果使用ModeBox框架優(yōu)化AI應(yīng)用性能有一些初步了解,同時(shí)也能理解優(yōu)化原理。如果對(duì)ModelBox感興趣可以進(jìn)入ModelBox官網(wǎng)詳細(xì)了解。