當(dāng)我們千辛萬苦完成了前面的數(shù)據(jù)獲取、數(shù)據(jù)清洗枪蘑、模型訓(xùn)練损谦、模型評(píng)估等等步驟之后,終于等到老大說“上線”啦腥寇。想到辛苦訓(xùn)練出來的模型要被調(diào)用還有點(diǎn)小激動(dòng)呢成翩,可是真當(dāng)下手的時(shí)候就有點(diǎn)懵了:模型要怎么部署?部署在哪里赦役?有什么限制或要求麻敌?
本文基于以上常見的部署困惑,介紹一下深度學(xué)習(xí)中TensorFlow(Keras)模型部署時(shí)需要考慮的問題以及常用部署方法掂摔,并通過實(shí)際的模型部署來進(jìn)行方案比較术羔。
問題分析
筆者較為推薦的思考路徑是從問題和限制出發(fā)赢赊,思考好了這些限制條件之后再去搜索對(duì)應(yīng)的解決方案,這樣就可以避免限于方案過多挑不過來的窘境级历,并且若是挑不到完美的方案释移,還可以根據(jù)條件對(duì)現(xiàn)有方案進(jìn)行改造。
現(xiàn)在就來看看部署模型我們通常需要考慮什么問題寥殖。
首先是模型的使用場(chǎng)景玩讳,根據(jù)使用場(chǎng)景就可以獲取到對(duì)應(yīng)的性能等要求。舉個(gè)例子來說嚼贡,如果是要上線給用戶使用的熏纯,哦吼,恭喜你粤策,這塊最起碼速度要求就很高樟澜,畢竟用戶可不像自己人,沒有多少耐心一直等你的加載圈轉(zhuǎn)呀轉(zhuǎn)叮盘,具體的指標(biāo)根據(jù)場(chǎng)景和應(yīng)用而定秩贰,不過一般會(huì)在數(shù)毫秒 最多可以到數(shù)十毫秒每條的樣子。此外柔吼,如果是基于網(wǎng)絡(luò)調(diào)用那還好說毒费,機(jī)器在自己手上,土豪就可以各顯神通了嚷堡,如果是離線的手機(jī)APP蝗罗,那就還有大小的要求,畢竟動(dòng)不動(dòng)讓你下個(gè)幾百M(fèi)的app看著也煩呀蝌戒。
接下來串塑,如果是基于網(wǎng)絡(luò)調(diào)用的,還要看看自己手里的資源夠不夠北苟,是壕到有專門的GPU用于部署呢桩匪,還是只能苦逼地使用CPU呢,如果是GPU部署友鼻,那可以偷笑了傻昙,GPU部署服務(wù)方便的同時(shí)性能又好,畢竟各種各樣的優(yōu)化都是苦于資源不夠彩扔,對(duì)于大伙兒來說可以躺賺誰愿意坐著賺錢啊妆档。
好吧,CPU就CPU虫碉,然而CPU服務(wù)器里頭也是可以區(qū)別的贾惦,是選擇有裝好例如python、TensorFlow等環(huán)境的呢,還是赤條條的要么自己裝要么另尋出路的须板。
方案介紹
接下來碰镜,本文會(huì)根據(jù)幾種常見的限制組合來列出對(duì)應(yīng)的解決方案,需要部署到手機(jī)端的話TensorFlow已經(jīng)給出了TensorFlow Lite的解決方案习瑰,筆者暫未接觸到手機(jī)部署相關(guān)的需求绪颖,就不深入介紹了。接下來就以最常見的服務(wù)提供方部署模型甜奄,通過網(wǎng)絡(luò)調(diào)用方式通信介紹兩套方案柠横。
基于預(yù)測(cè)改造的方案
模型在測(cè)試集上進(jìn)行測(cè)試知道吧?讀取文件后調(diào)用模型對(duì)每條case進(jìn)行預(yù)測(cè)贺嫂,最后將所有預(yù)測(cè)結(jié)果和正確答案對(duì)比滓鸠,計(jì)算出各種指標(biāo)。整個(gè)流程如下圖:
而在提供服務(wù)時(shí)第喳,輸入是由外部傳入的,因此不需要前面的讀取文件操作踱稍,最后提供預(yù)測(cè)結(jié)果即可曲饱,也不需要最后的對(duì)比、計(jì)算指標(biāo)的步驟珠月。為了處理接收外部輸入扩淀、返回預(yù)測(cè)結(jié)果,并做一些業(yè)務(wù)邏輯相關(guān)的處理啤挎,需要引入一個(gè)處理中心驻谆,這個(gè)處理中心通常是web框架 如 flask、tornado庆聘、Django等胜臊,其作用是搭建http服務(wù),將外部輸入傳給模型伙判,將模型預(yù)測(cè)結(jié)果返回象对。流程如下圖:
再來梳理一下流程,在同一臺(tái)機(jī)器上通過web框架如flask等搭建好對(duì)外服務(wù)宴抚,初始化時(shí)加載好模型勒魔。當(dāng)外部應(yīng)用服務(wù)例如搜索應(yīng)用的輸入到來時(shí),例如傳來一句話菇曲,或者上傳了一張圖片冠绢,對(duì)外服務(wù)就將輸入傳給預(yù)處理函數(shù),將輸入處理成可以給模型預(yù)測(cè)的樣子常潮,例如做標(biāo)準(zhǔn)化弟胀、去噪等等,隨后模型進(jìn)行預(yù)測(cè),并將結(jié)果返回給對(duì)外服務(wù)邮利,服務(wù)將結(jié)果包裝一下返回給搜索應(yīng)用回去弥雹。
一個(gè)搜索應(yīng)用會(huì)有很多個(gè)模型和子模塊構(gòu)成,那么對(duì)于每個(gè)模型都需要有一個(gè)對(duì)外服務(wù)來對(duì)模型進(jìn)行封裝并提供通信接口延届。
整個(gè)過程并沒有多少難點(diǎn)剪勿,搭建http服務(wù)使用簡(jiǎn)單的demo即可,然后將預(yù)測(cè)函數(shù)改造改造就可以啦方庭,最終的性能視乎部署的設(shè)備及模型深度而定厕吉。
基于TF-Serving的方案
如果在要部署的服務(wù)器上不愿或無法裝和訓(xùn)練一樣的環(huán)境,也可以使用TensorFlow推出的Serving工具械念,此處對(duì)小白來說更推薦基于docker的安裝方式头朱,只需要安好對(duì)應(yīng)鏡像即可,否則得自己編譯安裝龄减∠钆ィ基于docker安裝的話需要提前想好是用cpu版本還是gpu版,根據(jù)版本拉取對(duì)應(yīng)的鏡像希停。
那這個(gè)TF-Serving是什么樣的呢烁巫?先來看看基于TF-Serving后最終整個(gè)流程是怎么工作的:
如圖,在TF-Serving流程上一般會(huì)用到兩臺(tái)機(jī)器(或更多)宠能,其中一臺(tái)作為TF-Serving的服務(wù)器亚隙,專門給模型用來部署并預(yù)測(cè),應(yīng)用服務(wù)放在另外的服務(wù)器违崇,跟其他服務(wù)共享環(huán)境阿弃。
我們將模型部署在TF-Serving的服務(wù)器上,TF-Serving會(huì)自動(dòng)根據(jù)傳入的端口和模型路徑進(jìn)行部署羞延,模型所在的服務(wù)器不需要python環(huán)境(以python訓(xùn)練為例)渣淳,隨后應(yīng)用服務(wù)直接對(duì)模型所在服務(wù)器發(fā)起服務(wù)調(diào)用,調(diào)用可以通過java或python的grpc進(jìn)行調(diào)用肴楷,具體調(diào)用方式參考當(dāng)前使用版本的手冊(cè)水由。
不過TF-Serving一個(gè)很大的坑在于:數(shù)據(jù)預(yù)處理的部分需要交給應(yīng)用服務(wù)做,TF-Serving只接收張量輸入赛蔫,如文本分類的模型砂客,它就只接收序列的id,而非句子本身呵恢,并且輸出返回的是id而非文本化的標(biāo)簽鞠值,因此也需要額外一層轉(zhuǎn)換。
然后來看看怎么將模型部署到TF-Serving上:
流程比較簡(jiǎn)單渗钉,首先在GPU服務(wù)器上訓(xùn)練好模型后彤恶,將模型保存好钞钙,再根據(jù)網(wǎng)上的轉(zhuǎn)換腳本轉(zhuǎn)換成TF-Serving接受的格式,不論使用Tensorflow還是Keras都不影響(pytorch麻煩出門右轉(zhuǎn))声离,只是保存的函數(shù)不同而已芒炼,查好具體用哪個(gè)函數(shù)即可,最后將生成出來的文件上傳到安裝了TF-Serving的服務(wù)器术徊,啟動(dòng)服務(wù)即可本刽。
方案對(duì)比
接下來本文先以一個(gè)實(shí)際的BERT-base+Fasttext文本二分類模型為例子,分別用以上兩個(gè)方案用GPU和CPU進(jìn)行部署赠涮,看看他們性能之間的差異子寓,最后在總結(jié)一下不同方案的適用場(chǎng)景和差異。
部署性能對(duì)比
GPU 基于 模型預(yù)測(cè)改造方案
首先上GPU:
Tesla V100 訓(xùn)練起來很給力笋除,那么預(yù)測(cè)起來時(shí)間怎么樣呢斜友?筆者以1500個(gè)句子作為輸入,在服務(wù)器上搭建好http服務(wù)后遠(yuǎn)程調(diào)用垃它,最終平均時(shí)間是0.0102s/條鲜屏,10毫秒每條,對(duì)于大多數(shù)線上使用來說算是還可以了国拇,畢竟BERT的體積之大和推理之慢但效果之好是大家有目共睹的墙歪。
CPU 基于 模型預(yù)測(cè)改造方案
那么把這個(gè)模型移到CPU上又會(huì)怎么樣呢?在Intel(R) Xeon(R) Gold 61xx CPU 2*32 上采用同樣的改造和服務(wù)搭建方式贝奇,同樣的1.5k句子跑起來,最終平均時(shí)間是0.390s/條靠胜。
看到這個(gè)結(jié)果掉瞳,筆者一開始瞪大了眼睛,仔細(xì)確認(rèn)了好幾遍數(shù)量級(jí)浪漠,最終無可奈何的接受了這個(gè)現(xiàn)實(shí)陕习,390ms/條的速度,拿給內(nèi)部自己人調(diào)用都要嫌慢吧址愿。然后回頭去某東上搜了下V100该镣,好吧。响谓。人家快是有道理的损合。
CPU 基于 TF-Serving 方案
那TensorFlow官方強(qiáng)推的Serving工具表現(xiàn)怎么樣呢?或許經(jīng)過推理優(yōu)化之后運(yùn)行速度或許會(huì)快一些吧娘纷?實(shí)驗(yàn)結(jié)果也證實(shí)了這個(gè)假設(shè)嫁审,cpu版本的TF-Serving平均耗時(shí)為0.292 s/條, 比直接調(diào)用足足快了100ms赖晶,看來強(qiáng)推還是有道理的律适。
部署優(yōu)化
雖然知道業(yè)界對(duì)BERT推理過慢廣泛詬病,但在GPU和CPU下差距如此之大還是有些出乎意料。吃驚歸吃驚捂贿,還得看看有沒有優(yōu)化的方法纠修。如果我們把CPU下耗時(shí)極大的BERT放到GPU會(huì)怎么樣?將原始的BERT作為服務(wù)放到GPU上厂僧,這樣平時(shí)訓(xùn)練和線上預(yù)測(cè)取embedding都從這里取扣草,這樣我們只需要部署接在BERT后面的小模型即可,哪怕是用CPU也扛得住吧吁系?雖然會(huì)犧牲微調(diào)BERT帶來的提升德召,但是畢竟可以上線呀,比因性能太差而放棄要好多了汽纤。
在GPU上部署B(yǎng)ERT服務(wù)上岗,這里就要用到筆者非常喜歡的 bert-as-service了,它是python的一個(gè)bert庫蕴坪,能夠讓bert作為服務(wù)肴掷,接收句子輸入返回bert編碼的句子向量。
部署之后測(cè)試一下性能背传,不使用batch的單條預(yù)測(cè)下平均耗時(shí)是0.0073s/條呆瞻,7ms,可以接受径玖。那分離開來的fasttext模型呢痴脾?部署在CPU的TF-Serving后平均耗時(shí)是0.0028s/條,3ms梳星,也可以接受赞赖,總耗時(shí)竟然跟整個(gè)模型放在GPU上是一樣的,瓶頸果然還是在BERT呀冤灾。
這套方案同樣可以遷移到其他場(chǎng)景前域,例如將用戶的各種信息訓(xùn)練好Embedding后作為公共資源由模型各自調(diào)用,或者可以將耗內(nèi)存較大的word2vec embedding文件放到有較多內(nèi)存的機(jī)器上韵吨,再通過網(wǎng)絡(luò)進(jìn)行數(shù)據(jù)傳輸匿垄。
方案總結(jié)
現(xiàn)在來總結(jié)一下上邊的實(shí)驗(yàn),先看一下以上三種方案的流程圖(每個(gè)黑色框代表一臺(tái)機(jī)器):
然后來看一下方案分別的耗時(shí):
首先要說的是归粉,能上GPU一定要上GPU椿疗!鈔能力節(jié)省非常多的時(shí)間和精力。然后我們挨個(gè)方案來說說優(yōu)缺點(diǎn)盏浇。
第一套方案就是簡(jiǎn)單地將測(cè)試模式改造成連續(xù)預(yù)測(cè)模式变丧,通過web框架進(jìn)行轉(zhuǎn)發(fā)和包裝。
好處:修改簡(jiǎn)單易于上手绢掰。
壞處:
推理性能為三種方案之內(nèi)最慢的痒蓬。
推理服務(wù)器需要有python環(huán)境童擎。
第二套方案利用TF-Serving進(jìn)行部署,TF-Serving自動(dòng)會(huì)進(jìn)行部署和端口監(jiān)聽攻晒。
好處:
速度較方案一快顾复。
不需要python環(huán)境。
壞處:
安裝較麻煩鲁捏,最好使用docker芯砸。
輸入輸出都需要轉(zhuǎn)換和處理。
第三套方案是在方案二上的優(yōu)化给梅,將耗費(fèi)資源較多的部分放到性能較好的機(jī)器上假丧,可以作為公共資源給多個(gè)小網(wǎng)絡(luò)共同使用。
好處(其他同方案二):
- 速度最快动羽。
壞處(其他同方案二):
需要有性能好的機(jī)器存放耗資源的網(wǎng)絡(luò)包帚,這塊需要協(xié)調(diào)。
多一次網(wǎng)絡(luò)通信运吓,需要承受網(wǎng)絡(luò)波動(dòng)的影響渴邦,如果TF-Serving和GPU機(jī)器屬于不同網(wǎng)絡(luò)環(huán)境則更為麻煩,需要借助VPN等手段拘哨。
每個(gè)對(duì)外服務(wù)調(diào)用模型需要配置專門的邏輯:從GPU服務(wù)器取Embedding谋梭,作為輸入給TF-Serving,才能得到TF-Serving的輸出倦青。
縱觀所有方案瓮床,發(fā)現(xiàn)其實(shí)我們可以做一些小融合,以方案三(或方案二)為基礎(chǔ)产镐,結(jié)合方案一的包裝纤垂,來去除TF-Serving輸入輸出需要轉(zhuǎn)換,以及方案三中每個(gè)服務(wù)需要配置專門邏輯的缺點(diǎn)磷账,流程如下圖:
以方案三為例,在TF-Serving的服務(wù)器上再增加一層中介贾虽,主要做輸入輸出的轉(zhuǎn)換逃糟,再承擔(dān)中轉(zhuǎn)的作用。
當(dāng)外部輸入到來時(shí)蓬豁,對(duì)外服務(wù)接收請(qǐng)求绰咽,處理后傳給GPU機(jī)器,得到embedding地粪,而后將embedding傳給TF-Serving的模型取募,得到預(yù)測(cè)結(jié)果后轉(zhuǎn)換成外部需要的格式,最后打包結(jié)果返回給其他服務(wù)蟆技。
這樣玩敏,方案四既擁有了方案三速度最快的優(yōu)點(diǎn)斗忌,也避免了TF-Serving需要做輸出輸出轉(zhuǎn)換,以及在TF-Serving與GPU Embedding服務(wù)器中來回跑的缺點(diǎn)旺聚。
最后
本文列舉了基于網(wǎng)絡(luò)調(diào)用的幾個(gè)模型部署的方案织阳,并且根據(jù)實(shí)際場(chǎng)景進(jìn)行優(yōu)化和變型,如果要在此之上要追求極致的性能砰粹,就只能自己動(dòng)手豐衣足食了唧躲,例如用C++或Go重寫推理部分,或者利用大公司自研的框架進(jìn)行部署和推理碱璃。
想看更多實(shí)戰(zhàn)文章弄痹,歡迎關(guān)注微信公眾號(hào)【AI實(shí)戰(zhàn)派】