日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

開源項(xiàng)目 | 五分鐘搭建BERT服務(wù),實(shí)現(xiàn)1000 QPS

 LZS2851 2019-08-13

作者丨劉欣

單位丨香儂科技算法架構(gòu)負(fù)責(zé)人

研究方向丨NLP工程化、算法平臺(tái)架構(gòu)

深度學(xué)習(xí)模型在訓(xùn)練和測試時(shí),通常使用小批量(mini-batch)的方式將樣本組裝在一起,這樣能充分利用 GPU 的并行計(jì)算特性,加快運(yùn)算速度。 

但在將使用了深度學(xué)習(xí)模型的服務(wù)部署上線時(shí),由于用戶請(qǐng)求通常是離散和單次的,若采取傳統(tǒng)的循環(huán)服務(wù)器或多線程服務(wù)器,在短時(shí)間內(nèi)有大量請(qǐng)求時(shí),會(huì)造成 GPU 計(jì)算資源閑置,用戶等待時(shí)間線性變長。 

基于此,我們開發(fā)了 service-streamer,它是一個(gè)中間件,將服務(wù)請(qǐng)求排隊(duì)組成一個(gè)完整的 batch,再送進(jìn) GPU 運(yùn)算。這樣可以犧牲最小的時(shí)延(默認(rèn)最大 0.1s),提升整體性能,極大優(yōu)化 GPU 利用率。

Github開源鏈接:


https://github.com/ShannonAI/service-streamer


功能特色


  • 簡單易用:只需添加兩三行代碼即可讓模型服務(wù)提速上數(shù)十倍。

  • 處理高速:高 QPS、低延遲,專門針對(duì)速度做了優(yōu)化,見基準(zhǔn)測試。

  • 擴(kuò)展性好:可輕松擴(kuò)展到多 GPU 場景,處理大量請(qǐng)求,見分布式。 

  • 適用性強(qiáng):中間件,適用于所有深度學(xué)習(xí)框架和 web 框架。


安裝步驟


可通過 pip 安裝,要求 Python>=3.5:
pip installservice_streamer

五分鐘搭建BERT服務(wù)

為了演示 API 使用方法,service-streamer 提供了一個(gè)完整的教程和示例代碼。如何在五分鐘搭建起基于 BERT 模型的完形填空服務(wù),每秒處理 1000+ 請(qǐng)求。點(diǎn)擊文章左下角“閱讀原文”查看完整代碼。

1. 首先我們定義一個(gè)完型填空模型(bert_model.py),其 predict 方法接受批量的句子,并給出每個(gè)句子中 [MASK] 位置的預(yù)測結(jié)果。
class TextInfillingModel(object);
       ...
batch=['twinkle twinkle [MASKstar',
            'Happy birthday to [MASK]',
            'the answer to lifethe [MASK], and everything']
model=TextaInfillingModel()
outputs=model.predict(batch)
print(outputs)
#['little', 'you', 'universe' ]

2. 然后使用 Flask 將模型封裝成 web 服務(wù) flask_example.py。這時(shí)候你的 web 服務(wù)每秒鐘只能完成 12 句請(qǐng)求。

model=TextInfillingModel()
@app.route('/naive', methods=['POST'])
def naive_predict( ):
      inputs = request.form.getlist('s')
      outputs = model.predict(inputs)
      return jsonify(outputs)

app.run(port=5005)

3. 下面我們通過 service_streamer 封裝你的模型函數(shù),三行代碼使 BERT 服務(wù)的預(yù)測速度達(dá)到每秒 200+ 句(16 倍 QPS)。

from service_streamer import ThreadStreamer streamer= ThreadedStreamer (model.predict,batch_size=64, max_latency=0.1)

@app.route('/stream', methods=['POST'])
def stream_predict( ):
     inputs = request.form.getlist('s')
    outputs = streamer.predict(inputs)
    return isonify(outputs)

app.run(port=5005, debug=False)
4. 最后,我們利用 Streamer 封裝模型,啟動(dòng)多個(gè) GPU worker,充分利用多卡性能實(shí)現(xiàn)每秒 1000+ 句(80 倍 QPS)。

import multiprocessing
from service_streamer import ManagedModel, Streamer
multiprocessing.set_start_method('spawn', force=True)

class ManagedBertModel(ManagedModel):

     def init_model(self):
           self.model = TextInfillingModel( )

      def predict(self, batch):
            return self.model.predict(batch)

streamer =Streamer(ManagedBertModel, batch_size=64, max_latency=0.1
worker_num = 8, cuda_devices=(0,1,2,3))

app.run(port=5005, debug=False)
運(yùn)行 flask_multigpu_example.py 這樣即可啟動(dòng) 8 個(gè) GPU worker,平均分配在 4 張卡上。

更多指南


除了上面的 5 分鐘教程,service-streamer 還提供了: 


  • 分布式 API 使用方法,可以配合 gunicorn 實(shí)現(xiàn) web server 和 gpu worker 的分布式; 

  • 異步 Future API,在本地高頻小 batch 調(diào)用的情形下如何利用 service-streamer 加速;

  • 性能 Benchmark,利用 wrk 進(jìn)行單卡和多卡的性能測試數(shù)據(jù)。

API介紹


快速入門

通常深度學(xué)習(xí)的 inference 按 batch 輸入會(huì)比較快。

outputs = model.predict(batch_inputs)
用 service_streamer 中間件封裝 predict 函數(shù),將 request 排隊(duì)成一個(gè)完整的 batch,再送進(jìn) GPU。犧牲一定的時(shí)延(默認(rèn)最大 0.1s),提升整體性能,極大提高 GPU 利用率。

from service_streamer import ThreadedStreamer

# 用Streamer封裝batch_predict函數(shù)
streamer = ThreadedStreamer(model.predict, batch_size=64, max_latency=0.1)

# 用Streamer異步調(diào)用predict函數(shù)
outputs = streamer.predict(batch_inouts)

然后你的 web server 需要開啟多線程(或協(xié)程)即可。 

短短幾行代碼,通??梢詫?shí)現(xiàn)數(shù)十(batch_size/batch_per_request)倍的加速。

分布式GPU worker 

上面的例子是在 web server 進(jìn)程中,開啟子線程作為 GPU worker 進(jìn)行 batch predict,用線程間隊(duì)列進(jìn)行通信和排隊(duì)。 

實(shí)際項(xiàng)目中 web server 的性能(QPS)遠(yuǎn)高于 GPU 模型的性能,所以我們支持一個(gè) web server 搭配多個(gè) GPU worker 進(jìn)程。

import multiprocessing; 
multiprocessing.set_start_method('spawn', force=True)
from service_streamer import Streamer

# spawn出4個(gè)gpu worker進(jìn)程
streamer = Streamer(model.predict, 640.1, worker_num=4
outputs = streamer.redict(batch)

Streamer 默認(rèn)采用 spawn 子進(jìn)程運(yùn)行 gpu worker,利用進(jìn)程間隊(duì)列進(jìn)行通信和排隊(duì),將大量的請(qǐng)求分配到多個(gè) worker 中處理,再將模型 batch predict 的結(jié)果傳回到對(duì)應(yīng)的 web server,并且返回到對(duì)應(yīng)的 http response。


上面這種方式定義簡單,但是主進(jìn)程初始化模型,多占了一份顯存,并且模型只能運(yùn)行在同一塊 GPU 上,所以我們提供了 ManageModel 類,方便模型 lazy 初始化和遷移,以支持多 GPU。

from service_streamer import ManagedModel

class ManagedBertModel(ManagedModel):

   def init_model(self):
       self.model = Model( )

  def predict(self, batch):
      return self.model.predict(batch) 

# spawn出4個(gè)gpu worker進(jìn)程,平均分?jǐn)?shù)在0/1/2/3號(hào)GPU上
streamer = Streamer(ManagedBertModel, 640.1, worker_num=4,cuda_devices=(0,1,2,3))
outputs = streamer.predict(batch)
分布式web server 

有時(shí)候,你的 web server 中需要進(jìn)行一些 CPU 密集型計(jì)算,比如圖像、文本預(yù)處理,再分配到 GPU worker 進(jìn)入模型。CPU 資源往往會(huì)成為性能瓶頸,于是我們也提供了多 web server 搭配(單個(gè)或多個(gè))GPU worker 的模式。

使用 RedisStreamer 指定所有 web server 和 GPU worker 共用的 redis broker 地址。

# 默認(rèn)參數(shù)可以省略,使用localhost:6379
streamer = RedisStreamer
(redis_broker='172.22.22.22:6379')
然后跟任意 python web server 的部署一樣,用 gunicorn 或 uwsgi 實(shí)現(xiàn)反向代理和負(fù)載均衡。

cd example
gunicorn -c redis_streamer_gunicorn.py flask_example:app

這樣每個(gè)請(qǐng)求會(huì)負(fù)載均衡到每個(gè) web server 中進(jìn)行 CPU 預(yù)處理,然后均勻的分布到 GPU worke 中進(jìn)行模型 predict。

Future API

如果你使用過任意 concurrent 庫,應(yīng)該對(duì) future 不陌生。當(dāng)你的使用場景不是 web service,又想使用 service_streamer 進(jìn)行排隊(duì)或者分布式 GPU 計(jì)算,可以直接使用 Future API。

from service_streamer import ThreadedStreamer
streamer = ThreadedStreamer(model.predict, 640.1)

xs ={}
for i in range(200):
    future = streamer.submit(['Happy birthday to [MASK]'
                                           'Today is my lucky [MASK]'])
     xs.append(future)

# 先拿到所有future對(duì)象,再等待異步返回
for future in xs:
     outputs = future.result()
     print(outputs)

基準(zhǔn)測試

如何做基準(zhǔn)測試

我們使用 wrk 來使做基準(zhǔn)測試。 

環(huán)境

  • GPU : Titan Xp

  • cuda : 9.0

  • pytorch : 1.1 


單個(gè)GPU進(jìn)程

# start flask threaded server
python example/flask_example.py

# benchmark naive api without service_streamer
./wrk -t 4 -c 128 -d 20s --timeout=10s -s scripts/streamer.lua http://127.0.0.1:5005/naive

# benchmark stream api with service_streamer
./wrk -t 4 -c 128 -d 20s --timeout=10s -s scripts/streamer.lua http://127.0.0.1:5005/naive


多個(gè)GPU進(jìn)程 

這里對(duì)比單 web server 進(jìn)程的情況下,多 GPU worker 的性能,驗(yàn)證通過和負(fù)載均衡機(jī)制的性能損耗。Flask 多線程 server 已經(jīng)成為性能瓶頸,故采用 gevent server。


利用Future API使用多個(gè)GPU 

為了規(guī)避 web server 的性能瓶頸,我們使用底層 Future API 本地測試多 GPU worker 的 benchmark。


可以看出 service_streamer 的性能跟 GPUworker 數(shù)量及乎成線性關(guān)系,其中進(jìn)程間通信的效率略高于 redis 通信。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多