隨着深度學習技術的成熟和人工智能的發展,機器開始變得愈來愈「聰明」,愈來愈瞭解用戶的喜愛和習慣。php
近年來對於NLP的研究也在突飛猛進的變化,有趣的任務和算法更是層出不窮,百度提出知識加強的語義表示模型 ERNIE就是其中的佼佼者。ERNIE在語言推斷、語義類似度、命名實體識別、情感分析、問答匹配等各種NLP中文任務上的模型效果全面超越 Bert,成爲NLP中文任務中的主流模型,ERNIE 2.0的論文(https://arxiv.org/abs/1907.12412)也被國際人工智能頂級學術會議AAAI-2020收錄。html
然而在模型效果大幅度提高的同時,模型的計算複雜性也大大增長,這使得ERNIE在推理部署時出現延時高,推理速度不理想的狀況,給產業實踐帶來了極大的挑戰。c++
飛槳開源框架1.8版本中,Paddle Inference在算子融合、TensorRT子圖集成和半精度浮點數(Float 16)加速三個方面對ERNIE模型推理進行了全方位優化。git
實驗代表,在batch=32, layers=12, head_num=12, size_per_head=64的配置下,英偉達T4 ERNIE運行延時從224ms降至41.90ms,時延下降81.3%;在其餘配置不變,batch=1的狀況下,時延縮減到 2.72ms。進一步在Bert模型上的擴展實驗代表,一樣條件下,1.8版本相對Tensortflow也具有明顯的推理性能優點。github
新來的小夥伴可能會疑惑,啥是Paddle Inference?算法
Paddle Inference是飛槳深度學習框架的推理引擎,經過對不一樣平臺服務器應用場景的深度適配優化,下降時延,提高部署效率,詳情請參考:https://mp.weixin.qq.com/s/DX...docker
提高點一:算子融合優化,在減小模型計算量和調用次數的同時,下降訪存開銷。bash
Paddle Inference加載模型後,須要先將模型轉換爲由算子節點組成的拓撲圖,而後再進行模型圖分析。在此階段中,Paddle Inference會根據預先定義的模式對拓撲圖進行掃描,若是有子圖(由多個節點組成的結構)匹配到某一模式,則會對該子圖內的節點進行融合。服務器
接下來咱們來看下Paddle Inference中對Ernie模型的算子融合優化。下圖是ERNIE模型的網絡結構,包括兩個部分:模型輸入和多個重複的編碼結構。網絡
圖 ERNIE模型結構示意圖
對Ernie模型中的算子融合的優化包括模型輸入算子融合和編碼算子融合。
模型輸入算子融合的原理以下圖所示:
編碼部分算子融合的原理以下圖所示:
經過對輸入、編碼部分進行算子融合,標準的Ernie模型的算子數量從300個以上降到60個左右,給模型推理帶來了較大的性能提高,不但減小了模型計算量和計算核(kernel)調用的次數,還大大節省了訪存的開銷。與此同時,咱們對每個融合的算子針對性的進行了kernel優化,保證了GPU核心高度利用,極大的下降了預測延時。
提高點二:採用TensorRT子圖集成,擇優自動選擇最佳計算內核,提高運算速度
Paddle Inference 採用子圖方式集成了TRT,使用一個簡單例子展現了這一過程。
當模型加載後,模型表示爲由算子節點組成的拓撲圖。若是在運行前指定了TRT子圖模式,那在模型圖分析階段,Paddle Inference會找出可以被TRT運行的算子節點,同時將這些互相連接的OP融合成一個子圖並用一個TRT 算子代替,運行期間若是遇到TRT 算子,則調用TRT引擎執行。
在Paddle 1.8 版本中,咱們對Ernie模型進行了TRT子圖的集成,支持動態尺寸的輸入功能。預測期間,被TRT 引擎執行的算子會在初始化期間運行全部候選計算內核(kernel),並根據根據輸入的尺寸選擇出最佳的那一個出來,保證了模型的最佳推理性能。
提高點三:採用半精度浮點數,最大化提高訪存和計算效率
Float32對於咱們很是熟悉了,那Float16(又稱爲半精度浮點數)是什麼呢?以下圖所示,Float16是一種相對較新的浮點類型,在計算機中使用 2 字節(16 位)存儲,在 IEEE 754-2008 中,它被稱做 binary16。
能夠看出,Float16的指數位和尾數位的存儲單元數目都要少於計算中經常使用的單精度中的個數,使用 Float16 代替 Float32 來存儲數據,不可避免會形成必定的數值精度上的損失。若是將深度學習模型中的參數以及運算從Float32 替換成 Float16,會有什麼影響呢?
咱們知道,深度學習自己對精度不是太敏感,尤爲是在預測場景下,由於不會涉及參數的微調,不少時候採用低精度的存儲以及運算對模型的最終結果不會形成太大的影響。使用Paddle Inference上對Ernie模型進行Float 16測試,模型輸出數值精度和Float32相比,偏差在1e-3~1e-4之間,這樣的數值精度損失對模型的準確率幾乎無影響,徹底可接受。
在深度學習模型預測期間,採用Float16代替Float32,在節省存儲空間的同時,還能節省訪存的開銷,尤爲是可以提高預測的性能。特別地,當咱們使用Volta 架構的GPU時候,使用Float16能夠充分利用Tensor core的性能加速,最大限度的提高訪存和計算效率。
講了這麼多Paddle Inference關於ERNIE的優化點,小夥伴們是否是也躍躍欲試了呢?Paddle Inference提供了簡單靈活的C++和Python接口,下面咱們以Python接口爲例,演示一下ERNIE模型推理的具體操做方法,只須要完成以下三個步驟。(小祕密:沒有GPU的小夥伴別擔憂,能夠在AI Studio上蹭個算力試試手:))
第一步:環境準備
# 拉取鏡像,該鏡像預裝Paddle 1.8 Python環境,幷包含c++的預編譯庫,lib存放在默認用戶目錄 ~/ 下。 docker pull hub.baidubce.com/paddlepaddle/paddle:1.8.0-gpu-cuda10.0-cudnn7-trt6 export CUDA_SO="$(\ls /usr/lib64/libcuda* | xargs -I{} echo '-v {}:{}') $(\ls /usr/lib64/libnvidia* | xargs -I{} echo '-v {}:{}')" export DEVICES=$(\ls /dev/nvidia* | xargs -I{} echo '--device {}:{}') export NVIDIA_SMI="-v /usr/bin/nvidia-smi:/usr/bin/nvidia-smi" docker run $CUDA_SO $DEVICES $NVIDIA_SMI --name test_ernie --privileged --security-opt seccomp=unconfined --net=host -v $PWD:/paddle -it hub.baidubce.com/paddlepaddle/paddle:1.8.0-gpu-cuda10.0-cudnn7-trt6 /bin/bash # 下載Ernie預測模型 wget https://paddle-inference-dist.bj.bcebos.com/inference_demo/Ernie_inference_model.gz
第二步:建立AnalysisPredictor
AnalysisPredictor是Paddle Inference提供的推理引擎,它根據AnalysisConfig配置對象進行構造。所以咱們首先要建立AnalysisConfig對象,並設置一些推理引擎參數,這些參數包括模型的路徑、設備硬件類型,是否開啓顯存以及TensorRT子圖優化等。
def create_predictor(): # 配置模型路徑 config = AnalysisConfig('./ernie/model', ./ernie/params) config.switch_use_feed_fetch_ops(False) # 設置開啓內存/顯存複用 config.enable_memory_optim() # 設置開啓GPU config.enable_use_gpu(100, 0) # 設置使用TensorRT子圖,關於TensorRT子圖的更多信息請訪問: # https://paddle-inference.readthedocs.io/en/latest/optimize/paddle_trt.html config.enable_tensorrt_engine(workspace_size = 1<<30, max_batch_size=1, min_subgraph_size=5, precision_mode=AnalysisConfig.Precision.Half, # 開啓FP16 use_static=False, use_calib_mode=False) head_number = 12 names = ["placeholder_0", "placeholder_1", "placeholder_2", "stack_0.tmp_0"] min_input_shape = [1, 1, 1] max_input_shape = [100, 128, 1] opt_input_shape = [1, 128, 1] # 設置TensorRT動態shape運行模式,須要提供輸入的最小,最大,最優shape # 最優 shape處於最小最大shape之間,在預測初始化期間,會根據最優shape對OP選擇最優的kernel config.set_trt_dynamic_shape_info( {names[0]:min_input_shape, names[1]:min_input_shape, names[2]:min_input_shape, names[3] : [1, head_number, 1, 1]}, {names[0]:max_input_shape, names[1]:max_input_shape, names[2]:max_input_shape, names[3] : [100, head_number, 128, 128]}, {names[0]:opt_input_shape, names[1]:opt_input_shape, names[2]:opt_input_shape, names[3] : [args.batch, head_number, 128, 128]}); # 建立predictor predictor = create_paddle_predictor(config) return predictor
上述的代碼中,咱們根據Ernie模型須要的參數配置建立了predictor。
第三步:準備數據輸入,運行模型預測,獲取模型輸出
# 運行預測,其中data表示輸入的數據 def run(predictor, data): # copy data to input tensor input_names = predictor.get_input_names() # 將data數據設置到輸入tensor中 for i, name in enumerate(input_names): input_tensor = predictor.get_input_tensor(name) input_tensor.reshape(data[i].shape) input_tensor.copy_from_cpu(data[i].copy()) # 運行預測 predictor.zero_copy_run() results = [] # 獲取輸出數據 output_names = predictor.get_output_names() for i, name in enumerate(output_names): output_tensor = predictor.get_output_tensor(name) output_data = output_tensor.copy_to_cpu() results.append(output_data) return results pred = create_predictor() # 使用數值爲1的數據進行測試 in1 = np.ones((1, 128, 1)).astype(np.int64) in2 = np.ones((1, 128, 1)).astype(np.int64) in3 = np.ones((1, 128, 1)).astype(np.int64) in4 = np.ones((1, 128, 1)).astype(np.float32) results = run(pred, [in1, in2, in3, in4])
在這部分裏咱們使用了全1的數據做爲模型的輸入進行測試,不到20行代碼完成了預測過程,是否是很簡單呢?若是您還想了解更多關於Paddle Inference的 API信息和操做指導,請參考文檔:
https://paddle-inference.read...
爲了驗證Paddle Inference的升級效果,咱們選取了NLP領域中的兩個表明模型ERNIE和BERT,在英偉達T4卡上進行了性能測試。
ERNIE測試結果:
在`batch=32, layers=12, head_num=12, size_per_head=64`的配置下, ERNIE模型運行延時從224ms降至41.90ms,時延下降81.3%;在其餘配置不變,batch=1的狀況下,時延縮減到 2.72ms。(測試期間先預熱 100次,而後計算循環預測1000次的平均時間)
BERT測試結果:
同時咱們在Bert模型(該模型的預測結構同ERNIE總體上比較接近)下同Tensorflow在T4卡上進行了性能對比(針對每種測試狀況咱們選取了TF,TF-XLA,TF-TRT FP16中的最佳性能)。從下圖中能夠看到,在batch和seq_len較小的時候,飛槳的單位時間吞吐量爲TensorFlow的2-4倍,即便在batch和seq_len較大時(32, 128),飛槳的性能也比TensorFlow快20%-30%。
- Bert on T4 (layers=12, head_num=12, size_per_head=64)
以上就是飛槳1.8版本,Paddle Inference針對ERNIE模型推理效率的提高開發的關鍵能力。固然,這些技術不只僅針對ERNIE模型,transformer類的網絡結構都會受益,歡迎感興趣的夥伴試用,若是您還有其餘新穎的實踐,歡迎隨時和飛槳的夥伴聯繫。
如在使用過程當中有問題,可加入飛槳官方QQ羣進行交流:703252161。
若是您想詳細瞭解更多飛槳的相關內容,請參閱如下文檔。
Paddle Inference demo: