基于TensorRT的BERT推斷加速與服務(wù)部署
BERT的出現(xiàn)真是廣大NLPer的福音,在很多任務(wù)上能取得顯著提升。不例外,作者在工作過程中也使用了BERT進(jìn)行下游任務(wù)訓(xùn)練,但在感嘆BERT真香的時候,它及其漫長的推斷時間讓人感到很為難。本文就記錄了在使用tensorRT部署B(yǎng)ERT時候的各種坑。話不多說,先給下最終模型推斷時間對比(如下面表格所示),然后開始我們的填坑記。
| max_seq | batch_size | 1 | 5 | 10 | 20 | 40 | 100 |
|---|---|---|---|---|---|---|---|
| 128 | Tensorflow(ms) | 20 | 24 | 45 | 76 | 130 | 300 |
| 128 | tensorRT(ms) | 2.2 | 5.2 | 7.2 | 10.1 | 19 | 51 |
實(shí)戰(zhàn)系列篇章中主要會分享,解決實(shí)際問題時的過程、遇到的問題或者使用的工具等等。如問題分解、bug排查、模型部署等等。已更新的文章根據(jù)篇章目錄查看,相關(guān)代碼實(shí)現(xiàn)開源在:https://github.com/wellinxu/nlp_store ,更多內(nèi)容關(guān)注知乎專欄(或微信公眾號):NLP雜貨鋪。
基本依賴 自尋坑路 TensorRT 'UFF'模型轉(zhuǎn)換錯誤 BERT in TensorRT 'time out'demo模型特難下載 順利運(yùn)行demo推斷 'additional_dict'模型中包含訓(xùn)練時的參數(shù) 'cls_squad_output_weights'模型中參數(shù)名稱不一致 'ascii'中文讀取報錯 模型結(jié)構(gòu)與demo不同 'nan'batch大小的配置問題 tensorflow與tensorRT的推斷時間對比 tensorRT server for BERT 'segmentation'cuda與flask的沖突 tensorrt模型文件的確認(rèn) 'deserialize'tensorrtserver版本問題 'CustomEmbLayerNormPluginDynamic'插件缺失 config.pbtxt配置文件不對 Client tensorrtserver.api下載安裝 'dimension'batch大小的配置問題 上線 Serialization Error 總結(jié) 參考
基本依賴
[ ] python [ ] git [ ] gpu [ ] nvidia-docker [ ] bert的checkout模型
自尋坑路
那天接口調(diào)用方告訴我,我的接口超時了要優(yōu)化下。從使用BERT開始我就知道,總會有這一天的,而現(xiàn)在終于來了。在一開始部署服務(wù)的時候,就直接上了GPU,對于一般調(diào)用,雖然慢了點(diǎn),但也不至于超時。但被新業(yè)務(wù)調(diào)用后,處理的樣本量明顯增加,特別容易出現(xiàn)超時的現(xiàn)象。沒辦法,自己約的,呸,自己選的路,再艱難也得先看看別人趟的水,然后再決定走不走。既然需求已經(jīng)提了,我當(dāng)然立刻行動起來,根據(jù)項目特性,先優(yōu)化了一波流程,讓整體速度提升了4倍左右,之前timeout的樣例都通過了,先部署起來給下游用。雖然提升了4倍速度,但大樣本依然游走在timeout的邊緣,如果遇到更大的樣本,就...。
為了看看前人趟的水,就來知乎進(jìn)行了搜索,然后得到了下面幾篇文章。
《從零開始學(xué)習(xí)自然語言處理(NLP)》-BERT推理加速實(shí)踐(6)【1】
《從零開始學(xué)習(xí)自然語言處理(NLP)》-BERT模型推理加速總結(jié)(5)【2】
加速 BERT 模型有多少種方法?從架構(gòu)優(yōu)化、模型壓縮到模型蒸餾,最新進(jìn)展詳解!【3】
NVIDIA發(fā)布TensorRT 6,突破BERT-Large推理10毫秒大關(guān)【4】
從結(jié)果看,有這么幾種方式:縮短max_seq,合并請求組成大batch,替換模型(蒸餾/縮減層數(shù)等),換成float16精度,使用tensorflow的xla,使用tensorRT??s短max_seq首先被排除了,因?yàn)檫@個項目在處理過程中,要對所有的文本進(jìn)行處理,縮短seq等于增加了樣本量,所以不適合我們項目。組成大batch也不行,我們的一次請求都至少40個樣本,在樣本量超過40之后,batch的增加與時間的增加基本上是線性的,所以也不適合。至于替換模型,之前嘗試過tiny版的albert,速度肯定有提升,但準(zhǔn)確率降了5個點(diǎn),接受不了。后面的幾個方法,tensorRT同時包含了所有優(yōu)點(diǎn),so,基于tensorRT部署B(yǎng)ERT服務(wù),坑從此開始。
TensorRT
tensorRT【5】是什么,不知道,沒聽過,不管了,先按照說明【6】把tensorrt安裝下,在我tensorflow14的docker容器中一頓操作,哎,木報錯,順利安裝完。然后需要把模型轉(zhuǎn)為tensorRT形式,順利找到轉(zhuǎn)tensorflow模型的文檔【7】,當(dāng)然checkout模型是不能直接轉(zhuǎn)的,事先要轉(zhuǎn)為‘frozen TensorFlow model’格式,這個在【7】中也有提示,也可以自行百度/google尋找教程。
'UFF'模型轉(zhuǎn)換錯誤
順利轉(zhuǎn)完模型后,需要轉(zhuǎn)為uff文件,這時坑開始來了,出現(xiàn)了一個它認(rèn)識我我不認(rèn)識它的錯誤(這個錯誤到寫文章時都沒解決,有一個原因是后來沒有走這條路)。只能再去找前人,文章【8】中顯示,nvidia專門對BERT進(jìn)行過優(yōu)化,是有demo的,一翻波折之后,終于找到了官方demo。
BERT in TensorRT
在tensortRT的官方github上一開始并沒有找到BERT的demo,后來發(fā)現(xiàn)在5.1與6.0分支【9】中都有BERT的demo,聰明的我認(rèn)為新版本應(yīng)該更好些(也許選擇5.1,會少走一些彎路,但我不想再去找坑了),于是重新開始了尋坑之旅,這樣第一行代碼出現(xiàn)了:
git?clone?-b?release/6.0?https://github.com/NVIDIA/TensorRT.git
根據(jù)【9】中python的提示,依次運(yùn)行:
cd?TensorRT/demo/BERT
sh?python/create_docker_container.sh
'time out'demo模型特難下載
在TensorRT/demo/BERT/python目錄下有readme說明文檔,根據(jù)說明準(zhǔn)備環(huán)境,第一步克隆代碼已經(jīng)完成,第二步建立鏡像,時間稍微有點(diǎn)久,但還算順利,第三步在鏡像中編譯插件/下載調(diào)試過的模型(因?yàn)槭峭暾膁emo,所以模型都是有demo的),其中模型下載可以根據(jù)需求選擇base/large,max_seq的大小,以及float32/float16精度,插件編譯得還比較順利,就是模型下載得太慢了,十幾k每秒,也不知道多大,那就等著,自己再去看看不知道是什么的TensorRT。直到下班,還沒下好,沒事明天早上再來看,結(jié)果第二天過來看,告訴我下載失敗,好吧重新下,5個小時后又下載失敗,好吧重新下......終于下載了3天得到了base_fp16_384的模型。反應(yīng)慢的我這時候想起,我們項目中的長度是128,最好弄個128的模型,這樣方便對比推斷的時間,所以在后臺重新下載128的模型,自己先用384模型跑完demo的后續(xù)流程(事實(shí)上,一直到部署結(jié)束,都沒能再成功下載過一個模型,想想當(dāng)時能成功下載384的模型,真的是幸運(yùn))。
順利運(yùn)行demo推斷
然后根據(jù)文檔,順利執(zhí)行了"Building an Engine"與"Running Inference"兩個步驟,運(yùn)行時間有些波動,最快大概再2.4ms一個,鑒于長度是384,與官方說的128長度2.2ms一個基本吻合了。這個時候的我,仿佛已經(jīng)完成了模型加速,幸福的表情難以言表。
'additional_dict'模型中包含訓(xùn)練時的參數(shù)
雖然說128長度的demo模型一直下不成功,但發(fā)現(xiàn)384的模型其實(shí)就是個checkout形式的tensorflow模型,所以就直接拿我們自己訓(xùn)練好的checkout模型來轉(zhuǎn)換。開始轉(zhuǎn)化模型:
python?python/bert_builder.py?-m?/workspace/models/fine-tuned/bert_tf_v2_base_fp32_128_v2/model.ckpt-6001?-o?bert_base_128.engine?-b?1?-s?128?-c?/workspace/models/fine-tuned/bert_tf_v2_base_fp16_128_v2
在短暫的等待之后,就迎來了ERROR!
報錯表示,在模型數(shù)據(jù)加載過程中,出現(xiàn)了不支持的數(shù)據(jù)類型,于是在bert_builder.py的252/253行加入了代碼:
print(pn)????#?打印參數(shù)名稱
print(type(tensor))????#?打印參數(shù)類型
再次運(yùn)行后發(fā)現(xiàn),一個叫"signal_early_stopping/STOP"的參數(shù)是布爾形式,這是在訓(xùn)練過程中用到的,所以將237行改為:
param_names?=?[key?for?key?in?sorted(tensor_dict)?if?'early_stop'?not?in?key?and?'adam'?not?in?key?and?'global_step'?not?in?key?and?'pooler'?not?in?key]?
'cls_squad_output_weights'模型中參數(shù)名稱不一致
順利解決了上面的bug,再次運(yùn)行后,比上次多等待了一眨眼,就得到了一個全新的ERROR!
報錯表示,參數(shù)中沒有一個叫"cls_squad_output_weights"的,demo是squad的一個樣例,而我的是二分類下游任務(wù),最后輸出層參數(shù)名稱不一致,根據(jù)上面打印的參數(shù)名字(不同的下游任務(wù)或者訓(xùn)練代碼,最后層的參數(shù)名字都可能不同),將217/218行代碼修改為:
W_out?=?init_dict["output_weights"]
B_out?=?init_dict["output_bias"]
然后再次運(yùn)行,沒有報錯,進(jìn)入了相對較長的等待。當(dāng)"Saving Engine to bert_base_128.engine ?Done."出現(xiàn)的時候,這個時候的我,仿佛已經(jīng)完成了模型加速,幸福的表情難以言表。
'ascii'中文讀取報錯
趕緊以葫蘆畫瓢(將文檔與問題都用文件的形式提供),也運(yùn)行一次推斷:
python?python/bert_inference.py?-e?bert_base_128.engine?-pf?"p.txt"?-qf?"q.txt"?-v?/workspace/models/fine-tuned/bert_tf_v2_base_fp32_128_v2/vocab.txt
很快,就報了一個"ascii"編碼的錯誤(因?yàn)樽x取中文的緣故),在百度之后,執(zhí)行了下面一行得以解決(本容器中可使用C.UTF-8):
export?LANG=C.UTF-8
繼續(xù)運(yùn)行后,得到"Running inference in 437.362 Sentences/Sec"(2.286ms),后面還有一個squad相關(guān)的錯誤,直接被我忽略了。這個時候的我,仿佛已經(jīng)完成了模型加速,幸福的表情難以言表。
模型結(jié)構(gòu)與demo不同
后面我修改了bert_inference.py文件(根據(jù)自己需要,自行修改),打印出模型分類結(jié)果,發(fā)現(xiàn)結(jié)果是[128,2,1,1]維度的,并且沒有做最后的softmax操作,獲取第一行數(shù)據(jù)并softmax,然后發(fā)現(xiàn),結(jié)果是錯的,什么鬼,摔!
這個錯誤讓我茶飯不思(那兩天吃得可好了),以為距離模型加速成功就一步之遙(其實(shí)還有好遠(yuǎn)),結(jié)果結(jié)果是錯的,根本不能用!對著代碼查這查那,用著google找這找那,絲毫沒有頭緒。一股神秘的力量,讓我回去看了tensorflow訓(xùn)練BERT下游任務(wù)的代碼,在我粗略的論文閱讀中,以及網(wǎng)絡(luò)/同事的介紹中,得到的信息都是,取BERT最后一層[CLS]的編碼直接進(jìn)行下游二分類訓(xùn)練(單層全連接+softmax)。但代碼中卻是,先經(jīng)過一層全連接+tanh,再接全連接+softmax。根據(jù)代碼得知,第一層的兩參數(shù)名叫"bert_pooler_dense_kernel","bert_pooler_dense_bias",所以將bert_build.py原237行修改為:
param_names?=?[key?for?key?in?sorted(tensor_dict)?if?'early_stop'?not?in?key?and?'adam'?not?in?key?and?'global_step'?not?in?key]
在tensorrt-api文檔【10】的幫助下,將bert_build.py中的squad_output函數(shù)修改為:
def?squad_output(prefix,?config,?init_dict,?network,?input_tensor):
?????"""
?????Create?the?squad?output
?????"""
??????
?????idims?=?input_tensor.shape
?????assert?len(idims)?==?5
?????B,?S,?hidden_size,?_,?_?=?idims
??????
?????p_w?=?init_dict["bert_pooler_dense_kernel"]
?????p_b?=?init_dict["bert_pooler_dense_bias"]
?????#這里其實(shí)可以直接取[CLS]的向量進(jìn)行后續(xù)運(yùn)算,但是沒能實(shí)現(xiàn)相關(guān)功能,就計算了所有的
?????pool_output?=?network.add_fully_connected(input_tensor,?hidden_size,?p_w,?p_b)
?????pool_data?=?pool_output.get_output(0)
?????tanh?=?network.add_activation(pool_data,?trt.tensorrt.ActivationType.TANH)
?????tanh_output?=?tanh.get_output(0)
??????
?????W_out?=?init_dict["output_weights"]
?????B_out?=?init_dict["output_bias"]
??????
?????W?=?network.add_constant((1,?hidden_size,?2),?W_out)
?????dense?=?network.add_fully_connected(tanh_output,?2,?W_out,?B_out)???????????????
?????set_layer_name(dense,?prefix,?"dense")
?????return?dense
在相對較長的等待中,獲得了新的engine模型。在運(yùn)行了自己修改的推斷腳本后,得到了正確結(jié)果(使用了fp16,所以最后結(jié)果在萬分位上有所不同,但基本一致了)。這個時候的我,仿佛已經(jīng)完成了模型加速,幸福的表情難以言表。
'nan'batch大小的配置問題
為了對比下速度,多預(yù)測了幾個樣本,發(fā)現(xiàn)報錯,可能是因?yàn)闃?gòu)建engine的時候batch設(shè)置的是1,重新設(shè)置為20,再次運(yùn)行:
python?python/bert_builder.py?-m?/workspace/models/fine-tuned/bert_tf_v2_base_fp32_128_v2/model.ckpt-6001?-o?bert_base_128.engine?-b?20?-s?128?-c?/workspace/models/fine-tuned/bert_tf_v2_base_fp16_128_v2
得到新模型后,推斷多個樣本時,依然報錯,得到的都是nan結(jié)果。什么情況,這個batch size參數(shù)是擺設(shè)嗎!(后來我查看過5.1分支的代碼,配置有所不同,也許在5.1分支上,直接使用batch size是有作用的)天知道在經(jīng)過怎樣的過程之后,發(fā)現(xiàn)engine構(gòu)建過程中,有過配置設(shè)置:
bs1_profile?=?builder.create_optimization_profile()
set_profile_shape(bs1_profile,?1)
builder_config.add_optimization_profile(bs1_profile)
為了模型速度,構(gòu)建過程中只設(shè)置了batch size為1,8以及參數(shù)值這三個。在google一翻之后,一位大佬說,再讀取engine之后,設(shè)置使用第二個配置(就是以傳入?yún)?shù)為batch size的配置)就可以了,也就是添加一行代碼"context.active_optimization_profile = 1",很有道理,一運(yùn)行發(fā)現(xiàn),"out of index"!還是要暴力處理,后來直接注釋了其他兩個配置,終于運(yùn)行成功了,batch size在[1,20] 之間都得到了正確結(jié)果。這個時候的我,仿佛已經(jīng)完成了模型加速,幸福的表情難以言表。
tensorflow與tensorRT的推斷時間對比
然后就得到了一開始的那張時間對比表,tensorflow是1.14版,float32精度,使用estimator進(jìn)行預(yù)測,tensorrt是6.0版,直接使用python調(diào)用:
| max_seq | batch_size | 1 | 5 | 10 | 20 | 40 | 100 |
|---|---|---|---|---|---|---|---|
| 128 | Tensorflow(ms) | 20 | 24 | 45 | 76 | 130 | 300 |
| 128 | tensorRT(ms) | 2.2 | 5.2 | 7.2 | 10.1 | 19 | 51 |
tensorRT server for BERT
'segmentation'cuda與flask的沖突
python調(diào)用成功后,就簡單把bert_inference.py程序改成了一個python服務(wù)程序,順利啟動服務(wù)之后,調(diào)用服務(wù)接口,驚喜來了:
一個光禿禿的提示(報錯位置都沒有),反正沒得結(jié)果。后來一查百度,說這個多為內(nèi)存不當(dāng)操作造成,這讓我一個半路出家的程序員怎么辦!后來仔細(xì)排查了報錯位置,發(fā)現(xiàn)是cuda報出的錯,結(jié)合這個信息,在面向google編程之后,得知pycuda上下文與http上下文有一些沖突(超過我知識范圍了),在初始化cuda的時候不能使用autoinit,得調(diào)用一次init一次,
#import?pycuda.autoinit?#注釋掉自動初始化
#?初始化cuda
cuda.init()
device?=?cuda.Device(0)
ctx?=?device.make_context()
#?中間所有處理程序
#?結(jié)束上下文
ctx.pop()
如上所示,每次調(diào)用的時候都得先初始化,經(jīng)過這樣的修改以后,果然跑通了。但耗時了3000ms一個樣本,時間足足多了上百倍,還能不能好好地玩耍了。這還不是最關(guān)鍵的,最關(guān)鍵的是,所有的返回結(jié)果全部變成了[0.5,0.5],好吧在服務(wù)中用python直接調(diào)用模型是行不通了(并不是真正的行不通,只是我這個渣渣,cuda也不懂,解決不了這兩個問題)。
tensorrt模型文件的確認(rèn)
這個時候,只能去使用tensorrt server【11】了。根據(jù)【11】文檔里所說,只要求一個配置文件和模型文件,就可以啟動相應(yīng)的docker服務(wù)了。但發(fā)現(xiàn)這個server能識別的tensorrt模型文件是一個以.plan結(jié)尾的文件,但我只有一個以.engine結(jié)尾的模型文件。后面我查閱了百度/gooogle相關(guān)文件,都沒找到如何將engine轉(zhuǎn)為plan文件,后來只找到一個在tensorrt6.0已經(jīng)被棄用的方法"write_engine_to_file()",但這不能用,而且也不知道寫進(jìn)去的文件是啥類型。后來我搜索了【5】中所有的"plan"字符,終于找到一句話:
Write out the inference engine in a serialized format. This is also called a plan file.
這句話的意思應(yīng)該就是說,engine跟plan是一個東東(后面確實(shí)讀取成功了,應(yīng)該是一個東西,當(dāng)時還是有猜的成分)。
'deserialize'tensorrtserver版本問題
按照【11】中所講,我認(rèn)為下載官方提供的docker來部署最為方便,最主要的是,我發(fā)現(xiàn)服務(wù)器上已經(jīng)有一個tensorrt server的鏡像了,應(yīng)該是同事之前下的,下鏡像的過程都省掉了。根據(jù)【11】中Model相關(guān)內(nèi)容,將模型文件重命名為model.plan,將配置文件修改為(有問題,后面要改):
name:?"bert"
platform:?"tensorrt_plan"
max_batch_size:?20
input?[
????{??
??????name:?"input0"
??????data_type:?TYPE_INT32
??????dims:?[128]
????},?
????{??
??????name:?"input1"
??????data_type:?TYPE_INT32
??????dims:?[128]
????},?
????{??
??????name:?"input2"
??????data_type:?TYPE_INT32
??????dims:?[128]
?????}??
]??????
output?[
????{??
??????name:?"output0"
??????data_type:?TYPE_FP16
???dims:[128,2]?????????????????????????????????????????????????????????????????????????????????????????????????????????
????}??
]?
根據(jù)【11】中的命令,直接運(yùn)行:
NV_GPU=1?nvidia-docker?run?--rm?--shm-size=1g?--ulimit?memlock=-1?--ulimit?stack=67108864?-p50014:8000?-p50015:8001?-p50016:8002?-v?/自己的路徑/models:/models?nvcr.io/nvidia/tensorrtserver:19.08-py3?trtserver?--model-repository=/models
直接一個報錯"trtserver: unrecognized option '--model-repository=/models'",根據(jù)提示,將參數(shù)名"--model-repository"改為了"--model-store",然后再運(yùn)行,就得到了又一個錯誤:
在一個不知道在哪里的文件,報了一個c++的錯誤,一籌莫展。根據(jù)提示,好像是batch size的問題,也可能是engine文件并不是plan文件導(dǎo)致讀錯了,也可能是版本問題(tensorrt server跟tensorrt的版本不統(tǒng)一【12】)。針對前面兩種情況,試了各種姿勢,依然是報這個錯,沒辦法,只能重新下載個docker鏡像了。
docker?pull?nvcr.io/nvidia/tensorrtserver:19.09-py3
當(dāng)然下載不會一帆風(fēng)順的,會有權(quán)限錯誤提示,在【11】中也多次提到,下載docker容器得先有NGC 權(quán)限【13】。
'CustomEmbLayerNormPluginDynamic'插件缺失
兩個小時之后,下載完畢,然后運(yùn)行:
NV_GPU=1?nvidia-docker?run?--rm?--shm-size=1g?--ulimit?memlock=-1?--ulimit?stack=67108864?-p50014:8000?-p50015:8001?-p50016:8002?-v?/自己的路徑/models:/models?nvcr.io/nvidia/tensorrtserver:19.09-py3?trtserver?--model-repository=/models
大概等了2秒鐘,有點(diǎn)開心,已經(jīng)過了上個bug出現(xiàn)的時間,又過了1秒,果然來了個新bug:
讀取"CustomEmbLayerNormPluginDynamic"插件錯誤,這個插件有點(diǎn)眼熟,在bert_build.py文件里面出現(xiàn)過,在咨詢了一波google之后,發(fā)現(xiàn)一個類似的問題【14】,在根據(jù)bert_build.py文件,將libbert_plugins.so/libcommon.so(在【9】中build文件夾中,編譯之后的)文件拷進(jìn)docker容器里。進(jìn)入容器:
NV_GPU=1?nvidia-docker?run?-it?--shm-size=1g?--ulimit?memlock=-1?--ulimit?stack=67108864?-p50014:8000?-p50015:8001?-p50016:8002?-v?/自己的路徑/models:/models?nvcr.io/nvidia/tensorrtserver:19.09-py3?/bin/bash
然后運(yùn)行:
export?LD_PRELOAD=/opt/tensorrtserver/libbert_plugins.so:/opt/tensorrtserver/libcommon.so
so文件位置是自己確定的,然后運(yùn)行:
trtserver?--model-repository=/models
config.pbtxt配置文件不對
這次運(yùn)行時間更長了,好開心,然后:
根據(jù)這個提示,是配置文件有問題,修改后又報錯再修改再報錯,這樣循環(huán)幾次后,配置文件變成了:
name:?"bert"
platform:?"tensorrt_plan"
max_batch_size:?20
input?[
????{
??????name:?"input_ids"
??????data_type:?TYPE_INT32
??????dims:?[128]
????},
????{
??????name:?"segment_ids"
??????data_type:?TYPE_INT32
??????dims:?[128]
????},
????{
??????name:?"input_mask"
??????data_type:?TYPE_INT32
??????dims:?[128]
????}
]
output?[
????{
??????name:?"cls_dense"
??????data_type:?TYPE_FP32
??????dims:?[128,?2,?1,?1]
????}
]
再次運(yùn)行之后,一直沒報錯,好像成功了,根據(jù)【11】中狀態(tài)檢查,運(yùn)行了下:
curl?localhost:50014/api/status
然后得到了:
哦耶,好像加載成功了,這個時候的我,仿佛已經(jīng)完成了模型加速,幸福的表情難以言表。
Client
tensorrtserver.api下載安裝
看似tensorrt的服務(wù)已經(jīng)成功部署了,現(xiàn)在就需要在我自己的服務(wù)內(nèi)部調(diào)用成功了。根據(jù)【11】當(dāng)中關(guān)于client的介紹,client的環(huán)境可以自己打鏡像/編譯/下載鏡像/下載編譯好的結(jié)果,琢磨著下載編譯好的結(jié)果最簡單了,所以在【15】中找到了自己需要的版本,直接運(yùn)行:
wget?https://github.com/NVIDIA/tensorrt-inference-server/releases/download/v1.6.0/v1.6.0_ubuntu1804.clients.tar.gz
tar?-zxvf?v1.6.0_ubuntu1804.clients.tar.gz
然后安裝一下:
cd?python
pip?install?tensorrtserver-1.6.0-py2.py3-none-linux_x86_64.whl
'dimension'batch大小的配置問題
有了依賴環(huán)境之后,根據(jù)【11】中的python api,成功地將模型調(diào)用添加到我的服務(wù)中去,最后調(diào)用測試,這個時候的我,仿佛已經(jīng)完成了模型加速,幸福的表情難以言:
根據(jù)報錯提示,是batch size不對,將樣本數(shù)量換成20后,果然運(yùn)行正確了。為什么直接用python調(diào)用模型的時候,樣本數(shù)只要小于batch size就可以了!后來我將bert_build.py文件的"set_profile_shape"函數(shù)改為了:
def?set_profile_shape(profile,?batch_size):
????maxshape?=?(batch_size,?S)
????minshape?=?(1,?S)
????optshape?=?(batch_size,?S)?#?這個batch的大小在最大和最小之間就可以,可以相等
????profile.set_shape("input_ids",?min=shape,?opt=shape,?max=shape)
????profile.set_shape("segment_ids",?min=shape,?opt=shape,?max=shape)
????profile.set_shape("input_mask",?min=shape,?opt=shape,?max=shape)
其中"set_shape"等函數(shù)的含義可以在文檔【10】中找到,修改后,又將全流程走了一遍,構(gòu)建engine,部署tensorrt server,運(yùn)行client,然后我真的完成了了模型加速,幸福的表情一言難盡!最后測試20個樣本調(diào)用服務(wù)的時間是15ms左右,比python直接調(diào)用延遲了5ms左右。
上線
Serialization Error
果然,我還是笑得太早,太年輕了。剛把服務(wù)推到測試上,就來了驚喜:
一看錯誤,模型加載錯誤,什么情況,我不是已經(jīng)跑通了嗎?經(jīng)過一系列查詢之后,發(fā)現(xiàn)tensorrt在不同GPU(主要根據(jù)計算能力分類)上編譯的模型是不能通用的,我之前在調(diào)研機(jī)上跑的,GPU型號是V100(計算能力7.0),而測試機(jī)上是P4(計算能力6.1),在V100上編譯的模型不能再P4使用,根據(jù)github上前輩提示,在CMakeLists.txt文件的第21行添加:
-gencode?arch=compute_60,code=sm_60?\
這樣就可以在P4上進(jìn)行編譯了,后來驗(yàn)證P4上的模型可以在P100的機(jī)器上跑通。當(dāng)然編譯的過程顯然不能一帆風(fēng)順,中間出現(xiàn)了out of memory的問題,后來測試發(fā)現(xiàn),大概需要5800M的顯存才能編譯成功。線上的GPU是P100的,順利運(yùn)行了新編譯的模型,心里終于微微一笑;bert在V100上的運(yùn)行速度大概是P100的5倍,而且nvidia主要在T4跟V100上優(yōu)化bert推斷的,而且模型轉(zhuǎn)換時P100上不能使用float16精度模式(只能用float32),所以新編譯的模型效率提高有限;想到新編譯的模型比tensorflow的效率只提高了30%左右,感覺之前的努力突然不香了。
總結(jié)
壓力/需求使人進(jìn)步,如果沒有"time out"報錯,我在部署完gpu版的BERT模型就結(jié)束了,這不,硬著頭皮也要上,從完全沒有聽過tensorrt到成功部署bert,大概用了兩周多時間。雖然現(xiàn)在對tensorrt依然只是了解皮毛,對cuda編程更是兩眼一抹黑,但沒關(guān)系,有了這個開端,后面慢慢學(xué)。
在部署過程中,百度/google/知乎各種找教程,都沒有找到部署B(yǎng)ERT的詳細(xì)過程,也為了提升自身,寫下自己遇到的坑,以及部分解決辦法,供大家一起交流進(jìn)步。
參考(閱讀原文可查看鏈接)
【1】《從零開始學(xué)習(xí)自然語言處理(NLP)》-BERT推理加速實(shí)踐(6)
【2】《從零開始學(xué)習(xí)自然語言處理(NLP)》-BERT模型推理加速總結(jié)(5)
【3】加速 BERT 模型有多少種方法?從架構(gòu)優(yōu)化、模型壓縮到模型蒸餾,最新進(jìn)展詳解!
【4】NVIDIA發(fā)布TensorRT 6,突破BERT-Large推理10毫秒大關(guān)
【5】What Is TensorRT?
【6】tensorRT的debian方式安裝
【7】使用python將tensorflow模型轉(zhuǎn)為tensorRT模型
【8】Real-Time Natural Language Understanding with BERT Using TensorRT
【9】tensorRT6.0分支BERT官方demo
【10】tensorrt-api
【11】NVIDIA TensorRT Inference Server
【12】TensorRT inference server documentation versions
【13】NGC guide
【14】CustomEmbLayerNormPluginDynamic插件加載問題
【15】tensorrt-inference-server/releases
【16】DeepLearningExamples
