1. 設為首頁   文化中國網歡迎您~!

          機器學習框架上的一些實踐

          機器學習工程實踐

          過去半年,我們團隊在機器學習平臺上做過一些工作,因為最近看到幾篇關于機器學習算法與工程方面的的文章,覺得十分有道理,萌發了總結一下這塊的一些工作的念頭,我最近工作主要分為兩塊:1,機器學習框架的研發、機器學習平臺的搭建;2,基礎NLP能力的業務支持。本篇文章會總結下在機器學習框架這部分系統工作上的一些工作,主要也分為兩部分:1,經典框架的支持;2,自研框架的工作;

          ?

          經典框架的支持

          這里經典框架其實就是TensorFlow,目前TensorFlow在我司場景上主要集中在兩部分場景:

          1. 搜索、推薦、廣告等比較傳統業務場景,提供包括召回、粗排、精排等核心流程的算法支持;
          2. 新興業務如直播、社交等業務基礎的算法能力的支持,構建內容生態, 如各業務內容審核、曲庫、歌單、直播體系建設等方面;

          具體一個case

          算法業務有一個場景,根據用戶過去session內的若干次(限制為定長)的訪問記錄,預測下一個訪問內容,業務同學設計了一個DNN來召回這部分內容,然后,在精排階段去排序。但是問題在于召回的整體候選集特別大,大概為30萬, 因此,這個DNN模型就有了如下的結構:

          初看,沒有任何問題,設計一個多層mlp,來訓練召回模型,且保證輸入限制為定長K,通過過去K次瀏覽記錄來召回下一次可能的內容, 很合理,且在業務上效果挺不錯的。從算法業務同學的視角里,這完全沒有任何問題,相信很多小伙伴在業務初期都會有類似的嘗試,但是問題是當候選集為30萬大小,或者更大時,想想這時候會發生什么?(感謝之前在騰訊手機qq瀏覽器的經驗,yuhao做歧義消解的時候討論過這個問題)每一輪的迭代,必須有兩個過程forward、backward, forward主要邏輯是基于預測值,backward主要邏輯是根據預測值和對應標簽信息,然后更新梯度信息,如此大的輸出節點數,每一次forward會計算30萬的softmax然后計算loss,通過bp更新梯度,這其中的耗時可想而知,,相信很多小伙伴看到這里會突然想到word2vec針對這塊的優化:Negative Sampling和Hierarchical Softmax, 專門用來解決輸出維度過大的情況;google也發表了On Using Very Large Target Vocabulary for Neural Machine Translation,用于在Very Large Target Vocabulary部分的loss的計算,TensorFlow官方也支持https://www.tensorflow.org/api_docs/python/tf/nn/sampled_softmax_loss,改造sampled_softmax_loss之后,速度提升將近30%。另外在支持業務的時候,還發現一個很有意思的東西,業務同學因為要在訓練過程中看到一些預測的結果是否符合預期, 因此在每次sess.run()的時候都塞進去predict的op。但是呢,訓練過程本身又是使用的softmax_cross_entropy,這就造成了一次sess.run()其實跑了兩輪softmax,之前沒有考慮這樣的細節,在某天和業務同學一起優化時,猛然看到,修改后,速度直接提升了一倍,也就是說上述所有的計算其實都是在softmax相關的計算,其實真實的模型的更新可能95%以上的計算都在softmax,加上本身使用TensorFlow靈活性確實夠大,predict、train又計算了兩次softmax,耗時可想而知。基于上述兩個點優化之后,速度整體提升明顯,但是回到算法模塊的設計上,DNN在如此大的候選集上真的合適嗎,在我看來,其實設計是可以更好的,微軟在2013年的的文章有提到DSSM的工作https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/cikm2013_DSSM_fullversion.pdf, 后來業界優化dssm支持lstm、cnn子模塊,用于推薦系統的召回,相信會是更好的方法,不存在輸出空間太大的問題。

          上述類似的問題應該出現在很多團隊中,尤其是在新興業務中的快速落地,無可厚非,設計了一套業務數據十分好看的模型,除了耗時多一些、內存多了一些,但是呢,對工程同學呢,這個是無法忍受的。不需要的地方,一點點的算力、一點點的比特的浪費都不能讓,這是工程同學、尤其是機器學習工程同學基本的堅持。

          hdfs小文件讀取的優化

          另外一塊關于TensorFlow的優化是讀取hdfs數據時,小文件的影響,場景是這樣的,業務同學收集好數據之后,轉為tfrecord,存到hdfs,然后本地通過TFRecordDataset去讀取hdfs文件,速度很慢,通過一些工具分析,主要耗時集中在數據拉取過程中,但是其他業務場景下也不會有問題,后來拿到數據看了下,因為復用了部分代碼,在spark上轉tfrecord的時候默認partition為5000,而本身該場景數據量比較小,分割為5000后,每個文件特別小,而TensorFlow在讀取tfrecord時,遇到小文件時,效率會特別低,其實不僅僅是在hdfs上,在ceph上也是,筆者之前也遇到小文件造成的數據讀取的耗時嚴重影響模型訓練的問題。

          分布式方案如何選擇

          當單機無法滿足性能之后,自然而然選擇了分布式方案,那么分布式方案如何選擇呢,業界有兩套比較成熟的方案:

          1. 基于parameter server的分布式方案,能夠有效支持模型并行、數據并行;
          2. 基于ring allreduce的分布式方案,能夠有效支持數據并行; 兩者之間差別在哪兒呢 ? 回答這個問題之前,我們先做一個算術題: 若一個場景,每200個batch耗時21秒左右,即一個batch約為0.1s,假設模型傳參時間為一半,整體模型大小約為100M,如果僅做數據并行,也就是說每0.05s需要將整個模型通過網絡傳到另一臺機器上,也就是要獎金2GB/s的帶寬,換算成遠遠超過現在很多10Gb網卡的性能,而大家會存疑了,為啥每個batch 計算時間為啥僅有0.1s呢,這個可能嗎,其實在推薦、廣告這類場景下,這種情況極有可能,在推薦、廣告這類場景下模型的特點在于embedding維度極大,但是本身消耗的算力比較小,耗時也很小,embedding僅僅是lookup 然后到較小維度的embedding向量,剩余的參數更新量極小。 所以要保證此類模型的并行效率,ring allreduce這類分布式方案,并不可行,網絡必將成為瓶頸,那么如何選擇呢? parameter server目前看來是一套比較好的方案,模型并行,模型分布能夠有效利用多個worker的網卡帶寬,達到較高的加速效率。 而ring allreduce適合那些 model_size/batch耗時 較小的場景,比如cv場景下cnn model,其加速比幾乎可以達到線性:

          ?

          當然也有很多手段來在推薦場景上也使用ring allreduce,比如并不是每個batch都更新所有的梯度的信息,可以選擇性的去傳輸部分梯度,通過合理的策略選擇,也能達到很好的加速效率,這里就不詳細展開了。

          線上模型部署優化

          模型部署這塊的工作,因為涉及到線上,在我們看來更加重要。由于業務系統大部分基于Java構建,而機器學習框架本身大部分采用c/c++實現,因此我們采用jni的方式來打通java業務系統到c++模型的調用,將包括spark lgb、tensorflow還有我們自研的框架,進行封裝,業務只需要指定模型引擎、寫好模型出入處理,即可快速上線,這塊后續會有團隊小伙專門文章介紹,這里只描述一點可能算不上優化的優化,就是在TensorFlow框架中引入SIMD的支持,起先由于缺少這塊的經驗,并沒有想到SIMD對于性能的提升,但業務RT過高時,發現原先TensorFlow CPU的線上的編譯按TensorFlow默認教程,少了AVX、SSE的支持, 在引入AVX、SSE之后,線上性能提升明顯,A場景從40ms降到了20ms,B場景從70+ms降到了40ms,讀者里面有部署沒有引入SIMD的,可以快速嘗試下,很香,命令如下:

          bazel build -c opt --copt=-msse3 --copt=-msse4.1 --copt=-msse4.2 --copt=-mavx --copt=-mavx2 --copt=-mfma //tensorflow/tools/pip_package:build_pip_package

          自研框架

          ?如上圖,是自研框架的一個邏輯抽象圖,整體框架分為三個角色:scheduler、Server、Worker,通過計算與存儲分離,合理編排任務,達到高性能的分布式機器學習框架,這里不詳細描述這塊的設計,后續感興趣會有專門的文章來描述,這里僅討論下在自研框架上的幾道坎。

          自研框架路上的幾道坎

          部署工作

          項目之初,因為基于Parameter Server的自研框架,不像Spark、Hadoop有現成的作業提交系統,團隊開發了一套簡單的實驗工具,用于支持框架的開發:具體是基于docker作為環境的配置以及隔離工具, 自研deploy工具,發布多節點訓練任務,鏡像內打通線上大數據環境,可以任務實驗環境發布后直接拉取節點來訓練模型,現階段已有較好的任務發布、資源調度系統,相信隨著后續迭代會更加的合理以及完全。

          其實這個就是一個雞生蛋、蛋生雞的問題,有的人認為要自研框架,需要先考慮支持工作,如何提交、如何監控, 連部署工具、任務調度都沒有,怎么做框架?這是個特別好的問題,基建無法滿足的情況應該多多少少會出現在很多團隊上,怎么辦?基建無法滿足,開發就沒辦法進行下去嗎?當然不是,作為工程師,完全可以開發一個極簡版本,支持你的項目開發,記住這時你的目的是框架開發而非業務支持,框架開發過程中自然會找到解決方案,以前老大經常和我們提項目之初不能過度設計,我覺得還要加上一條,項目之初要抓住關鍵需求,然后來扣,一個復雜的系統永遠不是完美的,也不是一個團隊可以支持的,要聯合可以聯合的團隊一起成長、一起攻克。

          資源瓶頸

          不管何時,資源的瓶頸或許說資源的限制一定會存在,對于一個好的系統一定是不斷磨合不同流程、不同模塊之間的性能來達到的,自研框架過程中,我們學習到一些經驗:定制數據處理邏輯分布式機器學習框架,尤其是大規模離散場景下,單batch的樣本稀疏程度十分大, 有值特征通常不到萬分之一,在一輪迭代中僅僅只更新很小一部分參數,如下圖

          ?如圖中粉紅圓圈

          原則上,但數據reader去解析數據文件中的數據時,理論上一次遍歷即可拿到所有數據,此處考慮到計算能力,采用生產-消費者模式,配置好合適的cache,用來保存待消費的數據序列。放入cache的數據文件分片單位,如支持4個part,即表明cache內數據條數為4*part內條數據,讀取文件數據時,應用format_parser來解釋訓練數據格式,然后進入cache, cache內部分進行shuffle,切分batch,切分batch過程中會計算每一個batch的nnz、key_set,用于后面分配計算空間以及向server拉取參數,參數拉取完成夠, 每一個batch喂給計算模塊去計算,shuffle batch on the fly。

          可能各位大佬看到這里覺得不太高效,為什么是分塊的載入cache,為啥不直接使用流式處理呢 ? 流式處理是不是會更高效,因為這里考慮到shuffle這塊的邏輯,流式上的shuffle設計會十分復雜,這里其實我們也考慮過,比如在cache上配置一個計時器,定時進行cache內數據的shuffle,理論上可以增加一定的shuffle邏輯,但其實也無法嚴格保證, 當然之前我們也考慮過直接在前面讀取數據時,做全局的shuffle,類似于現在圖像的讀取邏輯,比如類似于lmdb的存儲結構,其實質在于每個樣本配置一個指針用于指定數據內存塊,但是在推薦場景下,一般單個樣本1k-1.5k大小,樣本量十分大, 如果使用lmdb這套邏輯,理論上我可以通過指針序列進行全局的shuffle,快速定位到指針位置來取樣本數據, 但是如此多的指針,本身的內存占用就變得很大了,不像圖像,單個指針相對整個圖像內存來說幾乎忽略不計,我們在嘗試之后,發現樣本空間變得十分巨大, 拉取數據的增長遠遠超過我們的預期, 而在推薦場景下這個是我們沒有采用的,而是采用分數據塊讀取,然后local shuffle的邏輯。

          拒絕數據拷貝,減少內存壓力起初框架開發時,盡快我們考慮到性能問題,但多多稍稍還是沒注意很多內存空間的拷貝以及不及時釋放的問題,這塊在單worker,或者worker數量較少的情況下,影響可忽略,但是當我們要將一臺機器壓到極致性能時,這塊我們重新梳理了下,通過更改邏輯以及使用move操作去除 parser 等函數中不必要的數據拷(此處沒有嚴格對比),預估能提升將近1/10的性能,尤其是訓練樣本數據塊的拷貝,占用過多內存。

          磁盤IO瓶頸我們沒有想到磁盤IO瓶頸來的如此快,反而一直擔心網絡IO, 后來查了下機器,就釋然了,實驗拿到的機器竟然是很老的機械磁盤(這里真的想吐槽規劃這批機器的同事),磁盤速率極低,磁盤IO的等待遠遠超出預期,尤其是在第一個epoch從hdfs拉到本地緩存數據和讀取數據塊到內存時,磁盤IO被打滿了。計算耗時在最嚴峻時,連整體耗時的五分之一都不到,磁盤IO成為了系統計算的瓶頸,減少了cache內存區大小也只不過減緩了這部分的壓力,磁盤還是在大部分時間被打的滿滿的。

          我們嘗試過,編排數據讀取部分平攤到整體任務計算的過程中,減少磁盤IO壓力, 發現效果并不明顯。最后我們通過將業務部分原始樣本數據:大概480G的文本數據,通過Protobuf+gzip之后,壓縮到差不多100G不到,單個文件大小從492M,轉換后一個文件大小為 106M,相對降低了 78%。而讀取單個文件的性能從原來的平均40s縮短至8s,相對減少了80%;,在數據讀取部分進行反序列化,本以為反序列化會增加部分耗時,但發現在經過第一部分的優化之后,反序列化不增加額外耗時,且由于整體樣本量減少到了1/5,磁盤IO完全不成問題了,也加上第一步的優化改造,整體的IO曲線很平穩且健康。至此,磁盤IO等待符合預期,不再用磁盤IO瓶頸。

          網絡瓶頸,由于現在是比較簡單的模型,暫時沒有看到,本個季度應該會遇到,到時候再看。

          特殊需求優化

          考慮到部分業務,并沒有實時化部署線上服務,需要預先離線計算結果,然后放到線上去做推薦,我們的分布式機器學習框架也做了一些離線的inference的優化,單臺機器從30萬/s的處理速度優化到170萬/s的速度,用5臺機器,200個cpu計算核70分鐘完成370億的樣本的離線計算,整體內存占用僅180G。具體優化包括以下幾個方面:

          1, 數據壓縮,如前面提到采用protobuf+gzip后,提升明顯;2, 實現local_inference函數,因為此業務場景模型單機完全可以載入,去掉pull參數邏輯,直接從內存中拿到對應key,local inference時,每個worker載入全部參數;3, 修改batch inference改為單條去查詢,然后多線程計算結果,這里比較違反常識,理論上同事多個樣本進行計算,向量化計算效率肯定更高,但是這里因為在local inference場景下,不像訓練時,組成batch的matrix效率更高,local inference計算只有一個forward,計算耗時極小,整體耗時瓶頸并不在計算上,相反由于要組成一個batch的matrix增加的耗時要大于整體計算的耗時,而單個單個可以直接查詢key來進行forward計算,且這里通過openmp,可以達到多線程加速的效果。

          業務溝通

          和業務交流溝通,永遠是做底層同學最大的一道坎,彼此視角不同、技術方向不同、愿景也有差異,在暫不成熟的業務上,業務同學永遠有1000種以上的方法去提升日活、留存、轉化率,技術也許只是最后一個選擇。服務意識,是系統,尤其是像ml system這類并不是足夠成熟的行業上必須要具備的,其實想想TensorFlow也就釋然了,如此牛的一套東西,也還必須要全世界去pr,去培養用戶使用機器學習的習慣。

          未來規劃

          自研框架這套大概經歷了四個多月的時間,也培養了兩個比較給力的小伙伴,后續規劃主要是向業務看齊,先滿足業務,能預期的主要包括以下幾個方面

          實時化支持改造業務離線模型,支持實時化,這套框架本身已經支持增量訓練,更重要的改造是:1,利用現有大數據框架進行特征實時化;2,模型小時級訓練(實時化其實也支持到位了,但目前業務需求不明顯);3,模型校驗機制:需要有一套合適的機器判斷小時級更新的模型是否應該上線。

          參數通信模塊優化前面提到網絡目前還沒看到瓶頸,但是在涉及到更復雜一些的模型,更大維度的參數空間時,網絡必將成為瓶頸,目前業界在大規模分布式框架上有一些減緩網絡帶寬壓力的措施:1,梯度裁剪;2,梯度壓縮;3,混合精度訓練;

          其他框架兼容由于計算算子目前在很多現有的機器學習框架支持已經夠豐富了, 后續會考慮支持TensorFlow、Pytorch, 參考xdl、byteps這類框架,也會看看能否支持統一的模型部署格式如onnx,目前團隊正在調研這部分工作,相信今年會在這塊有一定的突破。

          代碼結構優化目前團隊每周一會進行code review,后續會進行幾輪代碼大范圍重構,更加抽象一些邏輯,更加強調代碼的復用:如增加register各類操作機制、更改layer到op層等等操作;


          也歡迎大家關注我的同名微信公眾號 小石頭的碼瘋窩(xiaoshitou_ml_tech),或者通過公眾號加我的個人微信進行討論

          推薦閱讀:蘋果x好還是xr
          好运来 www.convites-casamento.com:蕲春县| www.ericagarliebphotography.com:集安市| www.crowdcomputingblog.com:龙井市| www.wzxj888.com:宁化县| www.chaoren555.com:罗甸县| www.heritage-academy.org:资源县| www.irenecroce.com:察哈| www.implantdentalve.com:囊谦县| www.tea778.com:内丘县| www.iforoz.com:马尔康县| www.szhnbot.com:南通市| www.fusheng1bet.com:温泉县| www.kitagaya.com:竹山县| www.360cityvisit.com:唐海县| www.qpjmw.com:枝江市| www.gz577.com:称多县| www.vns4393.com:东光县| www.cillianmurphy.net:泰安市| www.myphotoestate.com:台安县| www.testingtutorials.net:白玉县| www.blueknightspavi.com:南昌县| www.madisonkungfu.com:柳州市| www.ikemax.com:六枝特区| www.heritage-academy.org:海伦市| www.1pshouhui.com:阿巴嘎旗| www.auburnoysterbar.com:娄底市| www.dong000.com:宁明县| www.estadonacionalespanol.com:大城县| www.elegooo.com:志丹县| www.199bongo.com:惠水县| www.tagged-login.com:仙居县| www.cqgspclaw.com:贺兰县| www.jyodhisham.com:虞城县| www.g08488.com:鞍山市| www.ungms.com:新化县| www.xpresspropane2u.com:五常市| www.game-football.com:嵩明县| www.zhgtymodel.com:兴城市| www.huidenhd.com:宁津县| www.bikeleads.com:铜梁县| www.benhvienungthu.com:昔阳县| www.cursosrioja.com:政和县| www.52okcar.com:长沙县| www.92top10.com:萨嘎县| www.fsbaohu.com:赞皇县| www.akaeno.com:体育| www.atlanteventuresmezzogiorno.com:南昌市| www.eccacaceres.org:沂南县| www.lomachihuahuas.com:仙居县| www.msliver.com:富平县| www.shkef.com:丽水市| www.djosephoto.com:桃园县| www.hg39199.com:桂东县| www.im-mould.com:杭锦旗| www.faribaba.com:尼木县| www.ircdzone.net:达拉特旗| www.pianfang120.com:郓城县| www.makpad.com:田东县| www.opfci.com:安多县| www.jamesstephenshurling.com:怀化市| www.lbgnjy.com:韶关市| www.jyxjbj.com:永德县| www.ssxnshz.com:交口县| www.hongdachen.com:婺源县| www.zuluanimazione.com:丘北县| www.shermantheband.com:石棉县| www.taikunco.com:社旗县| www.spoiledrottencatsociety.com:贡嘎县| www.foteng888.com:富宁县| www.9zmm.com:兴安县| www.bazardasminas.net:陆川县| www.chengsekeji.com:峨眉山市| www.cafeconsolas.com:蒙城县| www.022tjhj.com:辉县市| www.bestsuprashop.com:新余市| www.stranded-deep.net:根河市| www.smartmobilelab.com:和平县| www.wfhtdr.com:莱芜市| www.wateric-valve.com:涞水县| www.oopsireadabookagain.com:深泽县| www.jingpindj.com:四子王旗| www.w-wha.org:林口县| www.shhupai.com:太仆寺旗| www.hk-zhenda.com:怀安县|