隨著機(jī)器學(xué)習(xí)的廣泛應(yīng)用趟脂,如何高效的把訓(xùn)練好的機(jī)器學(xué)習(xí)的模型部署到生產(chǎn)環(huán)境,正在被越來越多的工具所支持。我們今天就來看一看不同的工具是如何解決這個(gè)問題的。
上圖的過程是一個(gè)數(shù)據(jù)科學(xué)項(xiàng)目所要經(jīng)歷的典型的過程体谒。從數(shù)據(jù)采集開始,經(jīng)歷數(shù)據(jù)分析臼婆,數(shù)據(jù)變形抒痒,數(shù)據(jù)驗(yàn)證,數(shù)據(jù)拆分颁褂,訓(xùn)練故响,模型創(chuàng)建傀广,模型驗(yàn)證,大規(guī)模訓(xùn)練彩届,模型發(fā)布伪冰,到提供服務(wù),監(jiān)控和日志惨缆。諸多的機(jī)器學(xué)習(xí)工具如Scikt-Learn糜值,Spark, Tensorflow, MXnet, PyTorch提供給數(shù)據(jù)科學(xué)家們不同的選擇,同時(shí)也給模型的部署帶來了不同的挑戰(zhàn)坯墨。
我們先來簡(jiǎn)單的看一看機(jī)器學(xué)習(xí)的模型是如何部署寂汇,它又會(huì)遇到那些挑戰(zhàn)。
模型持久化
模型部署一般就是把訓(xùn)練的模型持久化捣染,然后運(yùn)行服務(wù)器加載模型骄瓣,并提供REST或其它形式的服務(wù)接口。我們以RandomForestClassification為例耍攘,看一下Sklearn榕栏,Spark和Tensorflow是如何持久化模型。
Sklearn
我們使用Iris數(shù)據(jù)集蕾各,利用RandomForestClassifier分類扒磁。
訓(xùn)練的代碼如上。這里模型導(dǎo)出的代碼在最后一句式曲。joblib.dump()妨托,參考這里。Sklearn的模型到處本質(zhì)上是利用Python的Pickle機(jī)制吝羞。Python的函數(shù)進(jìn)行序列化兰伤,也就是說把訓(xùn)練好的Transformer函數(shù)序列化并存為文件。
要加載模型也很簡(jiǎn)單钧排,只要調(diào)用joblib.load()就好了敦腔。
Sklearn對(duì)Pickle做了一下封裝和優(yōu)化,但這并不能解決Pickle本身的一些限制恨溜,例如:
版本兼容問題符衔,不同的Python,Pickle糟袁,Sklearn的版本柏腻,生成的序列化文件并不兼容
安全性問題,例如序列化的文件中被人注入惡意代碼
擴(kuò)展問題系吭,你自己寫了一個(gè)擴(kuò)展類,無法序列化颗品,或者你在Python中調(diào)用了C函數(shù)
模型的管理肯尺,如果我生成了不同版本的模型沃缘,該如何管理
Spark
Spark的Pipeline和Model都支持Save到文件,然后可以很方便的在另一個(gè)Context中加載则吟。
訓(xùn)練的代碼如下:
模型加載的代碼如下:
調(diào)用model的toDebugString方法可以看到分類器的內(nèi)部細(xì)節(jié)槐臀。
下圖是Spark存儲(chǔ)的Piple模型的目錄結(jié)構(gòu):
我們可以看到,它包含了元數(shù)據(jù)Pipeline的五個(gè)階段的數(shù)據(jù)氓仲,這里的文件都是二進(jìn)制的數(shù)據(jù)水慨,只有Spark自己可以加載。
Tensorflow
最后我們來看一下Tensorflow敬扛。Tensorflow提供了tf.train.Saver來導(dǎo)出他的模型到元圖(MetaGraph)晰洒。
導(dǎo)出的模型會(huì)包含以下文件:
其中checkpoint是元數(shù)據(jù),包含其它文件的路徑信息啥箭。還包含了一個(gè)Pickle文件和其它幾個(gè)checkpiont文件谍珊。可以看出急侥,Tensorflow也利用了Python的Pickle機(jī)制來存儲(chǔ)模型砌滞,并在這之外加入了額外的元數(shù)據(jù)。
模型加載的代碼如下:
這里要注意的是坏怪,RandomForest不是tensforflow的核心包贝润,所以在模型加載的時(shí)候必須tensorflow.contrib.tensor_forest.python.tensor_forest, 否則模型是無法成功加載的。因?yàn)椴患虞d的話tensor_forest中定義的一些屬性會(huì)缺失铝宵。
另外就是Tensorflow也可以存儲(chǔ)計(jì)算圖打掘,調(diào)用tf.train.write_graph()方法可以把圖定義存儲(chǔ)下來。當(dāng)然也可以在TesnsorBoard中展示該圖捉超。
好了胧卤,我們看到,Sklearn拼岳,Spark和Tensorflow都提供了自己的模型持久化的方法枝誊,那么簡(jiǎn)單來說,只要使用一個(gè)web服務(wù)器例如Flask惜纸,加一些模型加載和管理的方法叶撒,然后暴露REST API就可以提供預(yù)測(cè)服務(wù)了,是不是很簡(jiǎn)單呢耐版?
其實(shí)要在生產(chǎn)環(huán)境下提供服務(wù)祠够,還需要面對(duì)很多其它的挑戰(zhàn),例如:
在云上如何擴(kuò)展和伸縮
如何進(jìn)行性能調(diào)優(yōu)
如何管理模型的版本
安全性
如何持續(xù)集成和持續(xù)部署
如何支持AB測(cè)試
為了解決模型部署的挑戰(zhàn)粪牲,不同的組織開發(fā)了一些開源的工具古瓤,例如:Clipper,Seldon,MFlow落君,MLeap穿香,Oracle Graphpipe,MXnet model server?等等绎速,我們就選其中幾個(gè)看個(gè)究竟皮获。
Clipper
Clipper是由UC BerkeleyRISE Lab開發(fā)的, 在用戶應(yīng)用和機(jī)器學(xué)習(xí)模型之間的一個(gè)提供預(yù)測(cè)服務(wù)的系統(tǒng)纹冤,通過解耦合用戶應(yīng)用和機(jī)器學(xué)習(xí)系統(tǒng)的方式洒宝,簡(jiǎn)化部署流程。
它有以下功能:
利用簡(jiǎn)單標(biāo)準(zhǔn)化的REST接口來簡(jiǎn)化機(jī)器學(xué)習(xí)系統(tǒng)的集成萌京,支持主要的機(jī)器學(xué)習(xí)框架雁歌。
使用開發(fā)模型相同的庫(kù)和環(huán)境簡(jiǎn)化模型部署
利用可適配的Batching,緩存等技術(shù)改善吞吐量
通過智能選擇和合并模型來改善預(yù)測(cè)的準(zhǔn)確率
Clipper的架構(gòu)如下圖:
Clipper使用了容器和微服務(wù)技術(shù)來構(gòu)架架構(gòu)枫夺。使用Redis來管理配置将宪,Prometheus來進(jìn)行監(jiān)控。Clipper支持使用Kubernetes或者本地的Docker來管理容器橡庞。
Clipper支持以下幾種模型:
純Python函數(shù)
PyShark
PyTorch
Tensorflow
MXnet
自定義
Clipper模型部署的基本過程如下较坛,大家可以參考我的這個(gè)notebook
創(chuàng)建Clipper集群(使用K8s或者本地Docker)
創(chuàng)建一個(gè)應(yīng)用
訓(xùn)練模型
調(diào)用Clipper提供的模型部署方法部署模型,這里不同的工具需要調(diào)用不同的部署方法扒最。部署時(shí)丑勤,會(huì)把訓(xùn)練好的Estimator利用CloudPickle之久化,本地構(gòu)建一個(gè)容器鏡像吧趣,部署到Docker或者K8s法竞。
把模型和應(yīng)用關(guān)聯(lián)到一起,相當(dāng)于發(fā)布模型强挫。然后就可以調(diào)用對(duì)應(yīng)的REST API來做預(yù)測(cè)了岔霸。
我試著把之前的三種工具的RomdomForest的例子用Clipper發(fā)布到我的Kubernetes集群,踩到了以下的坑坑:
我本地的Cloudpickle的版本太新俯渤,導(dǎo)致模型不能反序列化呆细,參考這個(gè)Issue
Tensorflow在Pickle的時(shí)候失敗,應(yīng)該是調(diào)用了C的code
我的K8s運(yùn)行在AWS上八匠,我在K8S上使用內(nèi)部IP失敗絮爷,clipper連接一直在使用外部的域名,導(dǎo)致無法部署PySpark的模型梨树。
總之坑夯,除了Sklearn成功部署之外,Tensorflow和Spark都失敗了抡四。
Seldon
Seldon是一家創(chuàng)辦于倫敦的公司柜蜈,致力于提供對(duì)于基于開源軟件的機(jī)器學(xué)習(xí)系統(tǒng)的控制仗谆。Seldon Core是該公司開源的提供在Kubernetes上部署機(jī)器學(xué)習(xí)模型的工具。它擁有以下功能:
Python/Spark/H2O/R 的模型支持
REST API和gRPC接口
部署基于Model/Routers/Combiner/Transformers的圖的微服務(wù)
利用K8S來提供擴(kuò)展跨释,安全性胸私,監(jiān)控等等DevOps的功能
Seldon的使用過程如上圖,
首先在K8s上安裝Seldon Core鳖谈,Seldon利用ksonnet,以CRD的形式安裝seldon core
利用S2i(s2i是openshift開源的一款工具阔涉,用于把代碼構(gòu)建成容器鏡像)缆娃,構(gòu)建運(yùn)行時(shí)模型容器,并注冊(cè)到容器注冊(cè)表
編寫你的運(yùn)行圖瑰排,并提交到K8s來部署你的模型
Seldon支持基于四種基本單元贯要,Model,Transformer椭住, Router崇渗, Combiner來構(gòu)建你的運(yùn)行圖,并按照該圖在K8s創(chuàng)建對(duì)應(yīng)的資源和實(shí)例京郑,來獲得AB測(cè)試宅广,模型ensemble的功能。
例如下圖的幾個(gè)例子:
AB 測(cè)試
模型ensemble
復(fù)雜圖
圖模式是Seldon最大的亮點(diǎn)些举,可以訓(xùn)練不同的模型跟狱,然后利用圖來組合出不同的運(yùn)行時(shí),非常方便户魏。
筆者嘗試在K8S上利用Seldon部署之前提到的三種工具生成的模型驶臊,都獲得了成功(代碼在這里)。這里分享一下遇到的幾個(gè)問題:
Seldon支持Java的Python叼丑,然而用運(yùn)行PySpark关翎,這兩個(gè)都需要,所以我不得不自己構(gòu)建了一個(gè)鏡像鸠信,手工在Python鏡像上安裝Java纵寝。
因?yàn)槭褂肅DR的原因,我沒有找到有效改變?nèi)萜鞯膌iveness和readiness的設(shè)置症副,因?yàn)镾park初始化模型在Hadoop上店雅,加載模型需要時(shí)間,總是readiness超時(shí)導(dǎo)致容器無法正常啟動(dòng)贞铣,K8s不斷的重啟容器闹啦。所以我只好修改代碼,讓模型加載變成Lazy Load辕坝,但是這樣第一次REST Call會(huì)比較耗時(shí)窍奋,但是容器和服務(wù)總算是能夠正常啟動(dòng)。
MLflow
MLflow是Databricks開發(fā)的開源系統(tǒng),用于管理機(jī)器學(xué)習(xí)的端到端的生命周期琳袄。
MLflow提供跟蹤江场,項(xiàng)目管理和模型管理的功能。使用MLFlow來提供一個(gè)基于Sklearn的模型服務(wù)非常簡(jiǎn)單窖逗,
調(diào)用mlflow.sklearn.log_model(), MLflow創(chuàng)建以下的目錄來管理模型:
我們看到在artifacts目錄下有Python的pickle文件和另一個(gè)元數(shù)據(jù)文件址否,MLModel。
使用 mlflow sklearn serve -m model 就可以很方便的提供基于sklearn的模型服務(wù)了碎紊。
雖然MLFlow也號(hào)稱支持Spark和Tensorflow佑附,但是他們都是基于Python來做,我嘗試使用仗考,但是文檔和例子比較少音同,所以沒能成功。但原理上都是使用Pickle?元數(shù)據(jù)的方式秃嗜。大家有興趣的可以嘗試一下权均。
關(guān)于部署功能,MLFlow的一個(gè)亮點(diǎn)是和Sagemaker锅锨,AzureML的支持叽赊。
MLeap
MLeap的目標(biāo)是提供一個(gè)在Spark和Sklearn之間可移植的模型格式,和運(yùn)行引擎橡类。它包含:
????基于JSON的序列化
????運(yùn)行引擎
????Benchmark
MLeap的架構(gòu)如下圖:
這是一個(gè)使用MLeap導(dǎo)出Sklearn模型的例子:
導(dǎo)出的模型結(jié)構(gòu)如下圖所示:
這個(gè)是randonforest的模型json
我們可以看出MLeap把模型完全序列化成與代碼無關(guān)的JSON文件蛇尚,這樣就可以在不同的運(yùn)行時(shí)工具Spark/Sklearn之間做到可移植。
MLeap對(duì)模型提供服務(wù)顾画,不需要依賴任何Sklearn或者Spark的代碼取劫。只要啟動(dòng)MLeap的Server,然后提交模型就好了研侣。
下面的代碼用Scala在Spark 上訓(xùn)練一個(gè)同樣的Randonforest分類模型谱邪,并利用MLeap持久化模型。
導(dǎo)出的模型和之前的Sklearn具有相同的格式庶诡。
MLeap的問題在于要支持所有的算法惦银,對(duì)于每一個(gè)算法都要實(shí)現(xiàn)對(duì)應(yīng)的序列化,這也使得它的需要很多的開發(fā)來支持客戶自定義的算法
總結(jié)
Seldon Core和K8S結(jié)合的很好末誓,它提供的運(yùn)行圖的方式非常強(qiáng)大扯俱,它也是我實(shí)驗(yàn)中唯一一個(gè)能夠成功部署Sklearn,Spark和Tensorflow三種模型的工具喇澡,非常推薦迅栅!
Clipper提供基于K8s和Docker的模型部署,它的模型版本管理做得不錯(cuò)晴玖,但是代碼不太穩(wěn)定读存,小問題不少为流,基于CloudPickle也有不少的限制,只能支持Python也是個(gè)問題让簿。推薦給數(shù)據(jù)科學(xué)家有比較多的本地交互的情況敬察。
MLFlow能夠提供很方便的基于Python的模型服務(wù),但是缺乏和容器的結(jié)合尔当。但是它能夠支持和Sagemaker莲祸,AzureML等云的支持。推薦給已經(jīng)在使用這些云的玩家椭迎。
MLeap的特色是支持模型的可交互性虫给,也就是說我可以把sklearn訓(xùn)練的模型導(dǎo)出在Spark上運(yùn)行,這的功能很有吸引力侠碧,但是要支持全部的算法,它還有很長(zhǎng)的路要走缠黍。關(guān)于機(jī)器學(xué)習(xí)模型標(biāo)準(zhǔn)化的問題弄兜,大家也可以關(guān)注PMML。現(xiàn)階段各個(gè)工具對(duì)PMML的支持比較有限瓷式,隨著深度學(xué)習(xí)的廣泛應(yīng)用替饿,PMML何去何從還未可知。
下表是對(duì)以上幾個(gè)工具的簡(jiǎn)單總結(jié)贸典,供大家參考
?Model PersistentML ToolsKubernetest IntegrationVersionLicenseImplementation
Seldon CoreS2i + PickleTensorflow, SKlearn, Keras, R, H2O, Nodejs, PMMLYes0.3.2ApacheDocker + K8s CRD
ClipperPicklePython, PySpark, PyTorch, Tensorflow, MXnet, Customer ContainerYes0.3.0ApacheCPP / Python
MLFlowDirectory + MetadataPython, H2O, Kera, MLeap, PyTorch, Sklearn, Spark, Tensorflow, RNoAlphaApachePython
MLeap?JSONSpark,Sklearn, TensorflowNo0.12.0ApacheScala/Java