本文由攜程技術(shù)中心投遞,ID:ctriptech桐玻。作者:潘鵬舉荆萤,攜程酒店研發(fā)BI經(jīng)理,負責(zé)酒店服務(wù)相關(guān)的業(yè)務(wù)建模工作偏竟,主要研究方向是用機器學(xué)習(xí)實現(xiàn)業(yè)務(wù)流程自動化敞峭、系統(tǒng)智能化、效率最優(yōu)化褪子,專注于算法實踐和應(yīng)用。
我們經(jīng)常會碰到一個問題:用了復(fù)雜的GBDT或者xgboost大大提升了模型效果呀枢,可是在上線的時候又犯難了裙秋,工程師說這個模型太復(fù)雜了缨伊,我沒法上線,滿足不了工程的要求枷恕,你幫我轉(zhuǎn)換成LR吧谭胚,直接套用一個公式就好了,速度飛速胡控,肯定滿足工程要求旁趟。這個時候你又屁顛屁顛用回了LR锡搜,重新訓(xùn)練了一下模型,心里默罵千百遍:工程能力真弱纷宇。
這些疑問蛾方,我們以前碰到過,通過不斷的摸索拓春,試驗出了不同的復(fù)雜機器學(xué)習(xí)的上線方法亚隅,來滿足不同場景的需求。在這里把實踐經(jīng)驗整理分享懂鸵,希望對大家有所幫助匆光。(我們的實踐經(jīng)驗更多是傾向于業(yè)務(wù)模型的上線流程,廣告和推薦級別的部署請自行繞道)夺巩。
首先在訓(xùn)練模型的工具上周崭,一般三個模型訓(xùn)練工具,Spark美澳、R摸航、Python忙厌。這三種工具各有千秋江咳,以后有時間,我寫一下三種工具的使用心得爹土。針對不同的模型使用場景踩身,為了滿足不同的線上應(yīng)用的要求挟阻,會用不同的上線方法。
一脱拼、總結(jié)來說坷备,大體會區(qū)分這三種場景,請大家對號入座赌蔑,酌情使用
如果是實時的、小數(shù)據(jù)量的預(yù)測應(yīng)用跷乐,則采用的SOA調(diào)用Rserve或者python-httpserve來進行應(yīng)用劈猿;這種應(yīng)用方式有個缺點是需要啟用服務(wù)來進行預(yù)測潮孽,也就是需要跨環(huán)境,從Java跨到R或者Python環(huán)境仗颈。對于性能挨决,基本上我們用Rserver方式订歪,針對一次1000條或者更少請求的預(yù)測,可以控制95%的結(jié)果在100ms內(nèi)返回結(jié)果盖高,100ms可以滿足工程上的實踐要求眼虱。更大的數(shù)據(jù)量捏悬,比如10000/次,100000/次的預(yù)測甥厦,我們目前評估下來滿足不了100ms的要求寇钉,建議分批進行調(diào)用或者采用多線程請求的方式來實現(xiàn)摧莽。
如果是實時、大數(shù)據(jù)量的預(yù)測應(yīng)用油够,則會采用SOA,訓(xùn)練好的模型轉(zhuǎn)換成PMML(關(guān)于如何轉(zhuǎn)換揩悄,我在下面會詳細描述)鬼悠,然后把模型封裝成一個類焕窝,用Java調(diào)用這個類來預(yù)測。用這種方式的好處是SOA不依賴于任何環(huán)境巴帮,任何計算和開銷都是在Java內(nèi)部里面消耗掉了榕茧,所以這種工程級別應(yīng)用速度很快客给、很穩(wěn)定。用此種方法也是要提供兩個東西蜻拨,模型文件和預(yù)測主類纵菌;
如果是Offline(離線)預(yù)測的,D+1天的預(yù)測笛辟,則可以不用考慮第1手幢、2中方式忱详,可以簡單的使用Rscript x.R或者python x.py的方式來進行預(yù)測。使用這種方式需要一個調(diào)度工具监透,如果公司沒有統(tǒng)一的調(diào)度工具,你用shell的crontab做定時調(diào)用就可以了胀蛮。
以上三種做法院刁,都會用SOA里面進行數(shù)據(jù)處理和變換,只有部分變換會在提供的Function或者類進行處理粪狼,一般性都建議在SOA里面處理好退腥,否則性能會變慢。
大概場景羅列完畢再榄,簡要介紹一下各不同工具的線上應(yīng)用的實現(xiàn)方式狡刘。
二、如何轉(zhuǎn)換PMML不跟,并封裝PMML
大部分模型都可以用PMML的方式實現(xiàn)颓帝,PMML的使用方法調(diào)用范例見:
jpmml的說明文檔:GitHub - jpmml/jpmml-evaluator: Java Evaluator API for PMML窝革;
Java調(diào)用PMML的范例(PPJUtils/java/pmml at master · pjpan/PPJUtils · GitHub)购城,此案例是我們的工程師寫的范例,大家可以根據(jù)此案例進行修改即可虐译;
Jpmml支持的轉(zhuǎn)換語言瘪板,主流的機器學(xué)習(xí)語言都支持了,深度學(xué)習(xí)類除外漆诽;
從下圖可以看到侮攀,它支持R、python和spark厢拭、xgboost等模型的轉(zhuǎn)換兰英,用起來非常方便。
三供鸠、接下來說一下各個算法工具的工程實踐
1.python模型上線:我們目前使用了模型轉(zhuǎn)換成PMML上線方法畦贸。
python-sklearn里面的模型都支持,也支持xgboost楞捂,并且PCA薄坏,歸一化可以封裝成preprocess轉(zhuǎn)換成PMML,所以調(diào)用起來很方便寨闹;
特別需要注意的是:缺失值的處理會影響到預(yù)測結(jié)果胶坠,大家可以可以看一下;
用PMML方式預(yù)測繁堡,模型預(yù)測一條記錄速度是1ms沈善,可以用這個預(yù)測來預(yù)估一下根據(jù)你的數(shù)據(jù)量乡数,整體的速度有多少。
2.R模型上線-這塊我們用的多闻牡,可以用R model轉(zhuǎn)換PMML的方式來實現(xiàn)瞳脓。
這里我介紹另一種的上線方式:Rserve。具體實現(xiàn)方式是:用SOA調(diào)用Rserve的方式去實現(xiàn)澈侠,我們會在服務(wù)器上部署好R環(huán)境和安裝好Rserve劫侧,然后用JAVA寫好SOA接口,調(diào)用Rserve來進行預(yù)測哨啃;
java調(diào)用Rserve方式見網(wǎng)頁鏈接:Rserve - Binary R server烧栋;
centos的Rserve搭建方法見:centos -Rserve的搭建,這里詳細描述了Rserve的搭建方式拳球。
Rserve方式可以批量預(yù)測审姓,跟PMML的單個預(yù)測方式相比,在少數(shù)據(jù)量的時候祝峻,PMML速度更快魔吐,但是如果是1000一次一批的效率上看,Rserve的方式會更快莱找;用Rserve上線的文件只需要提供兩個:
模型結(jié)果文件(XX.Rdata)酬姆;
預(yù)測函數(shù)(Pred.R)。
Rserve_1啟動把模型結(jié)果(XX.Rdata)常駐內(nèi)存奥溺。預(yù)測需要的輸入Feature都在Java里定義好不同的變量辞色,然后你用Java訪問Rserve_1,調(diào)用Pred.R進行預(yù)測浮定,獲取返回的List應(yīng)用在線上相满。最后把相關(guān)的輸入輸出存成log進行數(shù)據(jù)核對。
Pred.R <- function(x1,x2,x3){
data <- cbind(x1,x2,x3)
# feature engineering
score <- predict(modelname, data, type = 'prob')
return(list(score))
}
3.Spark模型上線-好處是脫離了環(huán)境桦卒,速度快立美。
Spark模型的上線就相對簡單一些,我們用Scala訓(xùn)練好模型(一般性我們都用xgboost訓(xùn)練模型)然后寫一個Java Class方灾,直接在JAVA中先獲取數(shù)據(jù)建蹄,數(shù)據(jù)處理,把處理好的數(shù)據(jù)存成一個數(shù)組迎吵,然后調(diào)用模型Class進行預(yù)測躲撰。模型文件也會提前l(fā)oad在內(nèi)存里面针贬,存在一個進程里面击费,然后我們?nèi)フ{(diào)用這個進程來進行預(yù)測。所以速度蠻快的桦他。
Spark模型上線蔫巩,放在spark集群谆棱,不脫離spark環(huán)境,方便圆仔,需要自己打jar包垃瞧;
我們這里目前還沒有嘗試過,有一篇博客寫到了如果把spark模型導(dǎo)出PMML,然后提交到spark集群上來調(diào)用坪郭,大家可以參考一下:Spark加載PMML進行預(yù)測个从。
四、只用Linux的Shell來調(diào)度模型的實現(xiàn)方法-簡單粗暴
因為有些算法工程師想快速迭代歪沃,把模型模擬線上線看一下效果嗦锐,所以針對離線預(yù)測的模型形式,還有一種最簡單粗暴的方法沪曙,這種方法開發(fā)快速方便奕污,具體做法如下:
寫一下R的預(yù)測腳本,比如predict.R液走,是你的主預(yù)測的模型碳默;
然后用shell封裝成xx.sh,比如predict.sh缘眶,shell里面調(diào)用模型嘱根,存儲數(shù)據(jù);
predict.sh的寫法如下:
# 數(shù)據(jù)導(dǎo)出
data_filename = xxx
file_date = xxx
result = xxx
updatedt = xxx
cd path
hive -e "USE tmp_xxxdb;SELECT * FROM db.table1;" > ${data_filname};
# R腳本預(yù)測
Rscript path/predict.R $file_date
if [ $? -ne 0 ]
then
echo "Running RScript Failure"
fi
# R預(yù)測的結(jié)果導(dǎo)入Hive表
list1="use tmp_htlbidb;
load data local inpath 'path/$result'
overwrite into table table2 partition(dt='${updatedt}');"
hive -e "$list1"
最后用Crontab來進行調(diào)度巷懈,很簡單儿子,如何設(shè)置crontab,度娘一下就好了砸喻。
>crontab -e
-------------------------
### 每天5點進行預(yù)測模型柔逼;
0 5 * * * sh predict.sh
五、說完了部署上線割岛,說一下模型數(shù)據(jù)流轉(zhuǎn)的注意事項
區(qū)分offline和realtime數(shù)據(jù)愉适,不管哪種數(shù)據(jù),我們根據(jù)key和不同的更新頻次癣漆,把數(shù)據(jù)放在redis里面去维咸,設(shè)置不同的key和不同的過期時間;
大部分redis數(shù)據(jù)都會存放兩個批次的數(shù)據(jù)惠爽,用來預(yù)防無法取到最新的數(shù)據(jù)癌蓖,則用上一批次的數(shù)據(jù)來進行填充;
針對offline數(shù)據(jù)婚肆,用調(diào)度工具做好依賴租副,每天跑數(shù)據(jù),并生成信號文件讓redis來進行讀冉闲浴用僧;
針對realtime數(shù)據(jù)结胀,我們區(qū)分兩種類型,一種是歷史+實時责循,比如最近30天的累計訂單量糟港,則我們會做兩步,第一部分是D+1之前的數(shù)據(jù)院仿,存成A表秸抚,今天產(chǎn)生的實時數(shù)據(jù),存儲B表歹垫,A和B表表結(jié)構(gòu)相同耸别,時效性不同;我們分別把A表和B表的數(shù)據(jù)放在Redis上去县钥,然后在SOA里面對這兩部分?jǐn)?shù)據(jù)實時進行計算秀姐;
模型的輸入輸出數(shù)據(jù)進行埋點,進行數(shù)據(jù)跟蹤若贮,一是用來校驗數(shù)據(jù)省有,二來是用來監(jiān)控API接口的穩(wěn)定性,一般性我們會用ES來進行l(wèi)og的查看和性能方面的監(jiān)控谴麦;
任何接口都需要有容災(zāi)機制蠢沿,如果接口超時,前端需要進行容災(zāi)匾效,立即放棄接口調(diào)用數(shù)據(jù)舷蟀,返回一個默認(rèn)安全的數(shù)值,這點對于工程上非常重要面哼。
以上就是我們在模型部署的經(jīng)驗分享野宜,歡迎大家來找我一起探討相關(guān)工程上的最佳實踐。
攜程技術(shù)中心現(xiàn)開放大數(shù)據(jù)相關(guān)崗位魔策,包括資深NLP科學(xué)家/算法工程師/大數(shù)據(jù)底層架構(gòu)工程師/大數(shù)據(jù)平臺開發(fā)工程師/機器學(xué)習(xí)工程師/自然語言處理工程師/圖像識別工程師等匈子,有意者可砸簡歷至tech@ctrip.com。