Bert模型網絡結構較深,參數量龐大,將Bert模型部署成在線服務在實時性和吞吐上面臨巨大挑戰。本文主要介紹360搜索將Bert模型部署成在線服務的過程當中碰到的一些困難以及作的工程方面的優化。
在360搜索場景下對在線Bert服務的延遲和吞吐有極高的要求。通過前期的調研探索和試驗,將Bert模型作成在線服務主要有如下3個挑戰:算法
基於以上幾個困難點,咱們前期調研了TF-Serving、OnnxRuntime、TorchJIT、TensorRT等幾個熱門的推理框架,在比較了是否支持量化、是否須要預處理、是否支持變長、穩定性和性能以及社區活躍度等幾個維度後,最終選用了Nvidia開源的TensorRT。肯定了框架選型以後,咱們針對Bert在線服務作了幾個不一樣層面的優化。緩存
TensorRT推理框架自己提供的優化有:網絡
12層Bert模型的線上延遲不能知足性能要求,咱們將其蒸餾至6層的輕量級小模型。作完知識蒸餾後,在下降計算量的同時也保證了預測效果,6層模型能夠達到12層模型精度的99%。通過試驗驗證,Bert模型層數和在線服務的性能呈正相關的關係,6層模型TP99指標相對於12層模型性能提高1倍。架構
在Bert模型結構中,大部分Tensor都是fp32精度,可是在推理時不須要反向傳播,此時能夠下降精度,在保證模型效果的基礎上大幅提升模型的吞吐。通過FP16量化以後,模型推理延遲變爲原先1/3的同時,吞吐提高爲原先的3倍,此時顯存佔用也爲原先模型的1/2。可是相比較原先模型,fp16量化後的模型在萬分位後有損失,在360搜索的場景下通過驗證,量化後的模型對最終效果幾乎無影響。權衡之下,量化後的收益遠大於損失。框架
上圖中H2D和D2H分別表示從內存往顯存中拷貝數據和從顯存往內存中拷貝數據,Kernel表示正在執行核函數。線上請求推理時所作的三個動做爲首先將請求數據由內存拷貝至顯存,而後GPU發起核函數調用作推理計算,最後將計算結果由顯存拷貝至內存。GPU真正執行計算的部分是執行核函數的部分(上圖中藍色部分),數據拷貝時GPU是空閒的(上圖中白色部分),此時不管壓測壓力多大GPU都會有空閒時間,所以利用率不會壓滿。機器學習
解決上述問題的一個方法是增長一條Stream,使得兩條Stream的核函數計算部分能夠交替執行,增長GPU有效工做時間佔比,GPU利用率能夠壓到98%以上。Stream能夠理解爲任務隊列,H2D能夠理解爲一次任務,多增長一條Stream不會增長額外的顯存佔用,多條Stream是共享模型權重的。函數
上圖描述了一個佔有2張GPU卡的單個Bert服務進程的運行架構。從左至右依次解釋出現的名詞,task表示待處理的預測請求,context用來存儲這條請求的上下文信息,stream表示任務流,profile描述了模型輸入的限制(好比限制輸入的最大batch size),engine是TensorRT將原始模型編譯優化後的模型。每張GPU卡上加載一個模型,每一個模型會有2條Stream共享模型權重對外提供預測服務。性能
每當Bert服務收到來自客戶端的預測請求,這個請求將會被放入任務隊列。上圖線程池中的4個工做線程每當空閒時會從任務隊列中取出一條預測任務,保存好上下文信息後便將請求數據經過Stream拷貝到顯存,GPU調用核函數作完推理後再將結果經過Stream傳回到內存,此時工做線程將結果存入指定位置後通知上層,一條完整的請求預測流程就完成了。學習
在搜索場景下,當天的搜索內容會有一部分熱詞出現,加上緩存能夠有效減輕一部分計算量。在搜索系統加入請求級別的緩存以後,平均緩存命中率可達35%,極大地緩解了Bert在線服務的壓力。測試
最開始的在線服務是採用輸入維度固定的方式,即輸入shape的最後一個維度爲離線統計出現過的最大sequence長度,通過線上小流量驗證而且統計線上請求以後,發現線上請求長度超過70的sequence佔比不到10%。因而咱們採起了動態sequence長度的優化方式,即採用一個請求batch中長度最長的sequence爲輸入長度,對其他的sequence作補零操做,通過這一優化線上性能提高7%。
作完上述優化後在測試以及小流量驗證的過程當中,咱們也碰到了一些問題,分享給你們。
在搜索場景下,有一個多版本模型熱加載的需求。開發完上線後觀測到一個現象,在熱加載新模型的時候,會出現TP99升高的現象,後來通過定位分析找到了緣由。
在Bert在線服務作預測的時候,會有一個將模型輸入數據從內存拷貝到顯存的操做。而Bert服務動態加載模型的時候,也會有一個將模型權重數據從內存拷貝到顯存的動做,拷貝模型到顯存的時候佔據了PCI總線,這時候預測請求數據從內存拷貝到顯存就會受到影響,從而TP99就會升高。模型權重拷貝持續約幾秒的時間,此時TP95正常,經統計僅有幾條請求會有延遲升高,對業務基本無影響。
在前期開發過程當中,咱們觀測到相同的sequence輸入模型,在不一樣的batch size下返回的結果老是不盡相同,而是在某一固定的區間內震盪,例如返回的結果老是介於0.93-0.95之間且不固定。這個現象在TensorRT 7.1.3.4下穩定復現,後與Nvidia的同事溝通反饋,在7.2.2.3這個版本下已經修復。
單個Bert模型僅佔用幾百MB的顯存,可是上了多版本模型的功能後,Bert在線服務有可能加載5-8個模型,若是處理很差有可能會出現OOM的問題。目前咱們的處理手段是若是因顯存不夠而沒法正常加載模型,僅僅會提示模型加載失敗不會影響正常服務。加載一個新的模型的顯存佔用量是能夠提早判斷出來的,主要依據有3個:
通過前期框架調研驗證,模型優化,工程架構優化以及部署探索過程後,最終Bert在線服務在360搜索場景下正式上線了。目前通過優化後的6層模型單張T4卡每秒可計算1500條qt,線上高峯期TP99爲13ms。工程方面Bert在線服務穩定性和性能獲得了保障的基礎上,業務效果上相較於baseline也取得了可觀的收益。
咱們後續會持續探索推動Bert在360的應用落地,目前在搜索場景下工程方面還有一些亟需優化點: