觀遠AI實戰 | 機器學習系統的工程實踐

觀遠資料發表於2019-01-16

觀遠AI實戰 | 機器學習系統的工程實踐

「觀遠AI實戰」 欄目文章由觀遠演算法天團傾力打造,觀小編整理編輯。這裡將不定期推送關於機器學習資料探勘,特徵重要性等乾貨分享。

本文8千多字,約需要16分鐘閱讀時間。

機器學習作為時下最為火熱的技術之一受到了廣泛的關注。我們每天開啟公眾號都能收到各種前沿進展、論文解讀、最新教程的推送。這些文章中絕大多數內容都跟酷炫的新模型、高大上的數學推導有關。但是Peter Norvig說過,“We don’t have better algorithms. We just have more data.”。在實際機器學習應用中,對最終結果起到決定性作用的往往是精心收集處理的高質量資料。

從表面看好像也不難,但略微深究就會發現機器學習系統與傳統的軟體工程專案有著非常大的差異。除了廣受矚目的模型演算法,良好的工程化思考與實現是最終達到機器學習專案成功的另一大關鍵因素。

谷歌在2015年發表的論文《Hidden Technical Debt in Machine Learning Systems》中就很好的總結了機器學習工程中的各種不佳實踐導致的技術債問題。主要有以下幾種:

系統邊界模糊

在傳統的軟體工程中,一般會進行細緻的設計和抽象,對於系統的各個組成部分進行良好的模組劃分,這樣整個系統的演進和維護都會處於一個比較可控的狀態。但機器學習系統天然就與資料存在一定程度的耦合,加上天然的互動式、實驗性開發方式,很容易就會把資料清洗、特徵工程、模型訓練等模組耦合在一起,牽一髮而動全身,導致後續新增新特徵,做不同的實驗驗證都會變得越來越慢,越來越困難。

資料依賴難以管理

傳統的軟體工程開發中,可以很方便的通過編譯器,靜態分析等手段獲取到程式碼中的各種依賴關係,快速發現不合理的耦合設計,然後藉助於單元測試等手段快速重構改進。在機器學習系統中這類程式碼耦合分析同樣不可或缺。除此之外還多了資料依賴問題。

比如銷售預測系統可能會對接終端POS系統資料,也會引入市場部門的營銷資料,還有倉儲、運輸等等多種資料來源。在大多數情況下這些資料來源都是不同部門維護的,不受資料演算法團隊的控制,指不定哪天就悄悄做了一個變更。如果變更很大,可能在做資料處理或者模型訓練時會直接丟擲錯誤,但大多數情況下你的系統還是能正常執行,而得到的訓練預測結果很可能就有問題了。

在一些複雜業務系統中,這些資料本身還會被加工成各種中間資料集,同時被幾個資料分析預測任務共享,形成複雜的依賴關係網,進一步加大了資料管理的難度。

機器學習系統的反模式

膠水程式碼:隨著各種開源專案的百花齊放,很多機器學習專案都會呼叫各種開源庫來做資料處理、模型訓練、引數調優等環節。於是自然而然在整個專案中大量的程式碼都是為了把這些不同的開源庫粘合在一起的膠水程式碼,同樣會導致之前提到過的邊界模糊,與特定的庫緊耦合,難以替換模組快速演進等問題。

流水線叢林:在資料處理特徵工程與模型調優的迭代演進過程中,稍不注意你的整個系統流水線就會變得無比冗長,各種中間結果的寫入和前後依賴極其複雜。這時候想新增一個新特徵,或是除錯某個執行失敗都變得如此困難,逐漸迷失在這混亂的叢林中……如果只具備機器學習知識而缺少工程經驗,用這種做實驗的方式來開發系統顯然是不靠譜的,必須有良好的工程化思維,從總體上把控程式碼模組結構,才能更好的平衡實驗的靈活性與系統開發效率,保證整體的高效運作。 

失效的實驗性程式碼路徑:這一點也是承接前面,很多時候如果以跑實驗的心態來給系統“添磚加瓦”,很可能到後面各種小徑交叉的程式碼庫就這麼出現了,誰都搞不清楚哪些程式碼有用哪些是不會執行到的。如何復現別人的實驗結果,要用哪些資料集和特徵,設定哪些變數、做哪些微調都會成為難解之謎。

缺乏好的系統抽象:個人覺得sklearn的各種API設計還算蠻好的,現在很多其它庫的高層API都參考了這個準業界標準來實現。文中主要提到在分散式訓練中缺乏一個很好的業界標準,比如MapReduce顯然是不行的,Parameter Server看起來還算靠譜但也還未成為標準。沒有好的抽象標準也就導致了各種庫在功能、介面設計時不一致,從而有了以上提到的一系列邊界模糊,膠水程式碼等問題。

配置項技術債

相對於傳統軟體系統,機器學習系統的配置項往往會更多更復雜。比如要使用哪些特徵、各種資料選擇的規則、複雜的預處理和後置處理、模型本身的各種引數設定等等。因此除了工程程式碼外,配置項的精心設計、評審也成了一個不容忽視的點。否則很容易造成系統在實際執行中頻繁出錯,難以使用。

變化無常的外部世界

機器學習系統很多時候都是直接與外部世界的資料做互動,而外部世界總是變化無常。而且機器學習系統本身的輸出也會影響到外部世界,從而進一步回饋到機器學習系統的輸入中來。比如推薦系統給使用者展示的內容會影響使用者的點選行為,而這些點選瀏覽行為又會成為訓練資料輸入到推薦系統來。如何獲取到外部世界變化的資訊,進而及時改進甚至自動更新演算法模型就成了一個非常重要的問題。

在谷歌的這篇原始論文中對各種坑都給了一些解決的建議,歸納總結一下,總體上來說就是要轉變團隊整體的文化氛圍,強調良好的工程思維和實踐。一個設計良好的機器學習專案系統中往往真正跟機器學習相關的程式碼只佔了很小的一部分。

觀遠AI實戰 | 機器學習系統的工程實踐

新模型固然酷炫,但是機器學習工程實踐的總結與推廣與它在整個專案中扮演的角色的重要性顯然是不成正比的。所以今天我們要著重來講一下這個方面:機器學習系統的最佳工程實踐是什麼樣的?

這時候就要請出谷歌的另外一篇論文《The ML Test Score》了。前一篇論文在具體實踐落地方面缺乏細節,在這篇論文裡,谷歌總結了28個非常具體的機器學習系統相關工程實踐準則,可謂是乾貨滿滿,十分接地氣。

文中給出的28個建議都是針對機器學習系統的,沒有包含通用軟體工程裡那些單元測試,釋出流程等內容,在實踐中這些傳統最佳實踐也同樣非常重要。這些實踐在谷歌內部團隊廣泛使用,但沒有一個團隊執行的覆蓋率超過80%,因此這些測試點都是非常值得關注並有一定的實踐難度的。

特徵與資料測試

特徵期望值編寫到schema中:很多特徵的分佈情況或數值期望是有一些先驗知識可以去校驗的。比如一般人身高都在0-3米的範圍內、英語中最常見的詞是”the”、整體的詞頻一般服從冪律分佈等。我們可以把這些先驗領域知識,或是從訓練集中計算出的數學期望值編寫在資料schema檔案中,後續對於新的輸入資料,構建完特徵後的模型訓練資料以及最終上線使用模型時都能進行自動化的檢查,避免因為資料不符合預期而導致的錯誤預測情況。

確保所有的特徵都是有用的:在之前的機器學習技術債論文中也有提到研發人員總是傾向於不斷往系統中新增新的特徵,尤其在上線時間比較緊迫的情況下,缺少細緻的特徵選擇和有效性驗證工作。這會導致特徵數量越來越多,構建訓練集需要花費的時間也越來越長,後續的維護成本也會更高。所以跟業務程式碼一樣,沒有幫助的特徵也要及時清理,輕裝前行。文中給出的方法基本是常見的特徵選擇法,比如計算特徵相關度,使用單獨或小批量特徵來跑模型看是否有預測能力等。

去除價效比低的特徵:計算新增任何一個特徵都需要消耗資源,包括生成和訓練模型開銷,模型預測開銷,甚至還要考慮到對上游資料的依賴,額外的庫函式引入,特徵本身的不穩定性等等。對於任何一個特徵的新增,都要綜合考慮這些開銷與它能帶來的效能提升來決定是否引入。如果只是非常有限的效果提升,我們應該果斷放棄那些過於複雜的特徵。

特徵必須遵循業務規範需求:不同的專案對機器學習系統可以使用的資料可能有不同的規範需求,比如可能有些業務禁止我們使用從使用者資料中推演出來的特徵。所以我們也需要從程式碼工程層面把這些規範需求進行實現,以避免訓練與線上特徵出現不一致或違反了業務規範等問題。

資料流水線必須有完善的隱私控制:與上一條類似,機器學習系統從資料來源獲取使用者相關隱私資料時已經通過了相應的控制校驗,後續在系統內部流水線做處理時我們也要時刻注意對隱私資料的訪問控制。比如各種中間資料集,資料字典的存放與訪問控制,上游系統的使用者資料刪除能夠級聯更新到機器學習系統的整個鏈路中,諸如此類需要特別注意的問題。

能夠快速開發新特徵:一個新特徵從提出到實現,測試,上線的整個流程所需要花費的時間決定了整個機器系統迭代演進,響應外部變化的速度。要實現這一點,良好的工程結構、不同模組的抽象設計都是非常重要的。文中沒有給具體的例子,不過我們可以借鑑sklearn中pipeline模組設計的思想,以及類似FeatureHub這樣的開源系統的實現來不斷優化完善特徵工程實踐。

特徵工程程式碼寫相應的測試:在實驗探索階段,我們經常會寫完一個特徵之後,粗略地取樣一些資料,大致驗證通過後就認為這個特徵基本沒有問題了。但這其中可能就隱藏了不少bug,而且不像業務程式碼中的錯誤,要發現這些bug極其困難。所以必須養成良好的習慣,在特徵開發階段就寫好相應的測試程式碼,確保特徵的正確性,後續應對各種系統變更也都能很快通過測試來進行快速驗證。

觀遠AI實戰 | 機器學習系統的工程實踐

模型開發測試

模型說明必須通過review並記錄在案:隨著機器學習模型技術的發展,各種複雜模型,大量的引數配置往往讓模型訓練和執行變得無比複雜。加上在多人協同的專案中,很多時候需要使用不同的資料,或者做一些特定的調整來重新評估模型效果,這時候有詳細的模型說明記錄就顯得尤為重要了。除了簡單的文字記錄,市面上也有不少開源專案(比如ModelDB,MLflow等)專注於幫助開發者管理模型,提高實驗的可復現性。

模型優化指標與業務指標一致:很多機器學習的應用業務中,實際的業務指標並不能直接拿來作為目標函式優化,比如業務營收,使用者滿意度等等。因此大多數模型在優化時都會選擇一個“代理指標”,比如使用者點選率的logloss之類。因此在建模,評估過程中必須要考慮到這個代理指標與真實業務指標是否有比較強的正相關性。我們可以通過各種A/B測試來進行評估,如果代理指標的改進無法提升真正的業務指標,就需要及時進行調整。

調優模型超引數這點相信大家都會做,畢竟各種機器學習教程中都會有很大篇幅講解如何進行調參來提升模型效果。值得注意的是除了暴力的網格搜尋隨機搜尋同樣簡單而效果往往更好。另外還有許多更高階的演算法例如貝葉斯優化,SMAC等也可以嘗試使用。對於同一個資料集,在使用不同的特徵組合,資料抽樣手段的時候理論上來說都應該進行引數調優以達到最佳效果。這部分的工作也是比較容易通過自動化工具來實現的。

對模型時效性有感知對於很多輸入資料快速變化的業務例如推薦系統,金融應用等,模型的時效性就顯得極其重要了。如果沒有及時訓練更新模型的機制,整個系統的執行效果可能會快速下降。我們可以通過保留多個版本的舊模型,使用A/B測試等手段來推演模型效果與時間推移的關係,並以此來制定整體模型的更新策略。

模型應該優於基準測試:對於我們開發的複雜模型,我們應該時常拿它與一些簡單的基準模型進行測試比較。如果需要花費大量精力調優的模型效果相比簡單的線性模型甚至統計預測都沒有明顯提升的話,我們就要仔細權衡一下使用複雜模型或做進一步研發改進的必要性了。

模型效果在不同資料切片上表現都應達標:在驗證模型效果時,我們不能僅依賴於一個總體的驗證集或是交叉驗證指標。應該在不同維度下對資料進行切分後分別驗證,便於我們對模型效果有更細粒度上的理解。否則一些細粒度上的問題很容易被總體統計指標所掩蓋,同時這些更細粒度的模型問題也能指導我們做進一步細化模型的調優工作。例如將預測效果根據不同國家的使用者,不同使用頻率的使用者,或者各種類別的電影等方式來分組分析。具體劃分方式可以根據業務特點來制定,並同時考察多個重要的維度。我們可以把這些測試固化到釋出流程中,確保每次模型更新不會在某些資料子集中有明顯的效果下降。

將模型的包容性列入測試範圍:近些年來也有不少關於模型包容性,公平性相關問題的研究。例如我們在做NLP相關問題時通常會使用預訓練的word embedding表達,如果這些預訓練時使用的語料與真實應用的場景有偏差,就會導致類似種族,性別歧視的公平性問題出現。我們可以檢查輸入特徵是否與某些使用者類別有強關聯性,還可以通過模型輸出的切分分析,去判斷是否對某些使用者組別有明顯的差異對待。當發現存在這個問題時,我們可以通過特徵預處理(例如文中提到的embedding對映轉換來消除例如性別維度的差異),模型開發中的各種後置處理,收集更多資料以確保模型能學習到少數群體的特性等方式來解決這個問題。

觀遠AI實戰 | 機器學習系統的工程實踐

機器學習基礎設施測試

模型訓練是可復現的:在理想情況下,我們對同一份資料以同樣的引數進行模型訓練應該能獲取到相同的模型結果。這樣對於我們做特徵工程重構驗證等都會非常有幫助。但在實際專案中做到穩定復現卻非常困難。例如模型中用到的隨機數種子,模型各部分的初始化順序,多執行緒/分散式訓練導致的訓練資料使用順序的不確定性等都會導致無法穩定復現的問題。因此我們在實際工程中對於這些點都要額外注意。比如凡是用到了隨機數的地方都應該暴露介面方便呼叫時進行隨機數種子的設定。除了儘量寫能確定性執行的程式碼外,模型融合也能在一定程度上減輕這個問題。

模型說明程式碼需要相應測試:雖然模型說明程式碼看起來很像“配置檔案”,但其中也可能存在bug,導致模型訓練沒有按預期方式執行。而且要測試這種模型說明程式碼也非常困難,因為模型訓練往往牽涉到非常多的外部輸入資料,而且通常耗時較長。文中提到谷歌將會開源一些相關的框架來幫助做相關的測試,一些具體的測試方法如下:

1. 用工具去生成一些隨機資料,在模型中執行一個訓練步驟,檢驗梯度更新是否符合預期。模型需要支援從任意checkpoint中恢復,繼續執行訓練。

2. 針對演算法特性做簡單的檢驗,比如RNN在執行過程中每次應該會接受輸入序列中的一個元素來進行處理。

3. 執行少量訓練步驟,觀察training loss變化,確定loss是按預期呈現不斷下降的趨勢。

4. 用簡單資料集讓模型去過擬合,迅速達到在訓練集上100%的正確率,證明模型的學習能力。

5. 儘量避免傳統的”golden tests”,就是把之前一個模型跑的結果存下來,以此為基準去測試後面新模型是否達到了預期的效果。從長期來看由於輸入資料的變化,訓練本身的不穩定性都會導致這個方法的維護成本很高。即使發現了效能下降,也難以提供有用的洞察。

機器學習pipeline的整合測試:一個完整的機器學習pipeline一般會包括訓練資料的收集,特徵生成,模型訓練,模型驗證,部署和服務釋出等環節。這些環節前後都會有一定互動影響,因此需要整合測試來驗證整個流程的正確性。這些測試應該包括在持續整合和釋出上線環節。為了節約執行時間,對於需要頻繁執行的持續整合測試,我們可以選擇少量資料進行驗證,或者是使用較為簡單的模型以便於開發人員快速驗證其它環節的修改不會引起問題。而在釋出流程中還是需要使用與生產環境儘可能一致的配置來執行整體的整合測試。 

模型上線前必須驗證其效果:這點看起來應該是大家都會遵守的原則。唯一要注意的是需要同時驗證模型的長期效果趨勢(是否有緩慢效能下降),以及與最近一個版本對比是否有明顯的效能下降。

模型能夠對單個樣本做debug:這個就有點厲害了,當你發現一個奇怪的模型輸出時,你怎麼去尋找問題的原因?這時候如果有一個方便的工具能讓你把這個樣本輸入到模型中去,單步執行去看模型訓練/預測的過程,那將對排查問題帶來極大的便利和效率提升。文中提到TensorFlow自帶了一個debugger,在實際工作中我們也會使用eli5,LIME之類的工具來做黑盒模型解釋,但從方便程度和效果上來說肯定還是比不上框架自帶的排查工具。

模型上線前的金絲雀測試:這點在傳統軟體工程中基本也是標配,儘管我們有測試環境,預釋出環境的各種離線測試,模型在正式上線時還是需要在生產環境中跑一下簡單的驗證測試,確保部署系統能夠成功載入模型併產出符合預期的預測結果。在此基礎上還可以進一步使用灰度釋出的方式,逐漸把流量從舊模型遷移到新模型上來,增加一層保障。

模型能夠快速回滾:與其它傳統軟體服務一樣,模型上線後如果發現有問題應該能夠快速,安全回滾。要做到這點必須在開發時就確保各種上下游依賴的相容性。並且回滾演練本身也應該成為常規釋出測試的一環,而不是到出了問題才去手毛腳亂的操作,引出更多意料之外的問題。

觀遠AI實戰 | 機器學習系統的工程實踐監控測試

依賴變更推送:機器學習系統一般都會大量依賴於各種外部系統提供的資料,如果這些外部系統的資料格式,欄位含義等發生了變化而我們沒有感知,很容易就會導致模型訓練和預測變得不符合預期。因此我們必須訂閱這些依賴系統的變更推送,並確保其它系統的維護者知曉我們在使用他們提供的資料。 

訓練與線上輸入資料分佈的一致性:雖然模型內部運作過程極為複雜,難以直接監控其執行時正確性,但是模型的輸入資料這塊還是比較容易監控的。我們可以用之前定義的特徵資料schema檔案來對線上輸入資料進行檢測,當出現較大偏差時自動觸發告警,以便及時發現外部資料的變化情況。

訓練與線上服務時生成的特徵值一致:在機器學習專案中經常會出現同一個特徵在訓練時和線上上使用時所採用的計算生成方式是不一樣的。比如在訓練時特徵是通過處理之前的歷史日誌計算出來的,而到了線上的時候同樣的特徵可能來自於使用者實時的反饋。或者是訓練時採用的特徵生成函式是非常靈活,易於做各種實驗嘗試的,而到了線上則改用固化的優化計算效能版本以降低服務響應時間。在理想情況下,雖然採用了不同的計算方式,但生成的特徵值應該是相同的,否則就會出現訓練和預測使用的資料有偏移,導致模型效果變差等問題。所以我們需要通過各種手段來監控線上線下資料的一致性。

例如可以通過對線上資料進行取樣打標記錄,來與訓練集中的對應條目進行直接比較,計算出有偏差的特徵數量,及這些特徵中相應的有偏差的樣本佔比數量。另外也可以通過計算線上線下各個特徵的統計分佈的差別來衡量是否有這類問題的產生。

模型的時效性:這一點與之前的模型測試中的時效性類似,我們可以直接使用其中獲取到的模型效果與時間推移關係來推斷理想情況下模型訓練更新的頻率,並以此來對模型持續執行時間進行監控和告警。要注意過長的更新週期會提升模型的維護難度。另外哪怕模型本身更新比較可控,但是模型依賴的資料來源也有類似的時效性問題,我們同樣需要對其進行監控以免出現資料過期的問題。

數值穩定性:在模型訓練中可能會在沒有任何報錯的情況下出現奇怪的NaN,inf值,導致非預期的引數更新甚至最終得到不可用的模型。因此我們需要在訓練中監控任何訓練資料中包含NaN或者inf的情況進行適當的處理。同時對於各模型引數的取值範圍,ReLU層後輸出為0的單元數量佔比等指標進行檢測,一旦超出預期範圍就進行告警,便於及時定位排查相關問題。

模型效能相關監控:機器學習模型的訓練速度,預測響應時間,系統吞吐量,以及記憶體佔用等效能指標對於整體系統的可擴充套件性來說都是至關重要的。尤其是隨著資料量的越來越大,越來越複雜模型的引入,更加劇了各種效能問題的出現。在模型演進,資料變化以及基礎架構/計算庫的更迭中,需要我們謹慎的評估模型效能變化,進行快速響應。在做效能監控時不但要注意不同程式碼元件,版本的表現,也要關注資料和模型版本的影響。而且除了容易檢測到的效能突變,長期,緩慢的效能下降也需要引起足夠的重視。

模型預測質量的迴歸問題:總體的目標是希望新部署的模型相對於過去的模型在預測效能上沒有下降。但驗證集相對於線上的真實情況來說總是有所區別的,只能作為一個效能評估的參考。文中列舉了幾種手段來做監控:

1. 衡量預測的統計偏差,正常情況下模型的預測偏差應該為0,如果不是則說明其中有一定問題。

2. 對於能在作出預測後很快得到正確與否反饋的任務型別,我們可以以此進行實時監控,迅速判斷模型效果相比之前是否有下降。

3. 對於在模型提供服務時無法快速獲取到正確標籤型別的任務型別,我們可以使用人工標記的驗證資料來比較模型效果的變化。

最後總結一下前面提到的各種監控,基本上還有兩個共通點,一是各種告警的閾值要做精心選擇,過低的閾值容易出現警報氾濫導致根本沒人去管的情況,而過高的閾值又會掩蓋系統中已經存在的各種問題。二是除了短時間內明顯的指標急劇下降外,同時也要關注長期的緩慢的下降,後者更難以發現,應該引起足夠的重視。

觀遠AI實戰 | 機器學習系統的工程實踐

文章後續還給出了這28個測試指標的具體評分標準,幫助大家在實踐中更好的對照,應用這些最佳實踐。還有很多在谷歌內部使用這套評分系統的各種反饋,以及他們各個團隊的平均得分情況等。

觀遠AI實戰 | 機器學習系統的工程實踐

對於這個平均得分情況,作者強力安利了一把谷歌自家的TFX。 

觀遠AI實戰 | 機器學習系統的工程實踐

比如基礎架構的整合測試,谷歌內部的得分也很低(滿分為1的情況下平均為0.2分)。TFX可以方便的實現整個工程的pipeline,自然也很容易在此基礎上完成相應的整合測試了。除了TFX,像Uber的Michelangelo,Facebook的FBLearner Flow也都是類似的機器學習平臺,雖然沒有開源,但都有比較詳細的介紹文章可以學習參考。

觀遠AI實戰 | 機器學習系統的工程實踐

這些框架基本可以看作各家公司做機器學習工程的最佳實踐總結,總體架構基本都遵循“資料管理->模型訓練->模型測試驗證->部署上線”這樣的流程。不過他們在具體設計實現上各有千秋,例如文中作者認為“線下線上訓練資料分佈偏差監控”是最為重要但卻鮮有實現的一個測試點,在很多專案組都曾經因為缺少這個監控而出現過多次線上故障。因此TFX提供了資料驗證模組來專門解決這個問題。而像Uber的Michelangelo則側重快速開發特徵這個點引入了Feature Store模組。在實際應用中我們可以根據不同的業務特點靈活的選擇工程方案來進行實現。

參考資料:https://papers.nips.cc/paper/5656-hidden-technical-debt-in-machine-learning-systems.pdf

https://ai.google/research/pubs/pub46555

相關文章