最近在試著尋找ML + sys可做的方向,發現涉及到的坑太多了,有點眼花繚亂的感覺......不如寫點東西總結一哈,幫自己理一下思路。
個人感覺MLsys不能算是一種方向,而是一種思路......比如對於system研究者來說,可以把ML作為我們開發的系統要適配的一種benchmark,就像transaction對於資料庫、某種檔案場景對於File System的意義一樣。這樣一想可做的空間就寬廣多了。就算ML哪天又進入寒冬,之前所學的技術也仍然是可持續的。傳統的system研究者也應該適應這個潮流,不能簡單的把MLsys一律歸為大水漫灌..
有很多topic我也是初次接觸,還不是很熟悉。如有錯誤還請批評指點~
1. 分散式機器學習(Distributed DNN Training)
這個又可以分為兩個方面:from ML / system perspective。安利一下劉鐵巖老師的《分散式機器學習》這本書([ch_]表示引用這本書中的一些章節),還有UCB cs294 19fall的這一節。
ML
從ML的角度做,主要是發明或改進分散式訓練演算法[ch4] [ch5],保證在分散式加速的同時,仍然能達到原來的學習效果(loss/accuracy)。因此很多工作也被投在像ICML、NIPS這種專業ML會議上。主要用到的方法包括最佳化(optimization)和統計學習理論(statistical learning theory)。
還有一類工作涉及到如何把單機演算法改造成分散式[ch9],比如同步/非同步SGD等。這裡主要涉及到的問題是如何降低分散式環境下的通訊開銷,提高加速比。
這方面瞭解不多就少寫點了... 可以參考這裡。
System
還有一個就是從System的角度做。從分散式計算的角度來看,可以把相關工作分為以下幾類:
對於計算量太大的場景(計算並行),可以多執行緒/多節點平行計算,多節點共享公共的儲存空間。常用的一個演算法就是同步隨機梯度下降(synchronous stochastic gradient descent),含義大致相當於K個(K是節點數)mini-batch SGD [ch6.2]
對於訓練資料太多,單機放不下的場景(資料並行,也是最主要的場景),需要將資料劃分到多個節點上訓練。每個節點先用本地的資料先訓練出一個子模型,同時和其他節點保持通訊(比如更新引數)以保證最終可以有效整合來自各個節點的訓練結果,並得到全域性的ML模型。[ch6.3]
對於模型太大的場景,需要把模型(例如NN中的不同層)劃分到不同節點上進行訓練。此時不同節點之間可能需要頻繁的sync。這個叫做模型並行。[ch6.4]
Pipeline Parallelism:這是去年(SOSP19 PipeDream)才出現的概念,參考這裡的第90、95頁 以及這裡的簡介。Pipeline Parallelism相當於把資料並行和模型並行結合起來,把資料劃分成多個chunk,也把訓練模型的過程分成了Forward Pass和Backward Pass兩個stage。然後用流水線的思想進行計算。
另外,分散式ML本質上還是分散式系統嘛,所以像傳統分散式系統裡的一些topic(比如一致性、fault tolerance、通訊、load balance等等)也可以放到這個背景下進行研究。
最近挖的比較多的坑大致涉及以下幾個點:
1.1. 分散式ML系統設計
[ch7.3] 最著名的就是幾大分散式DL模型:Parameter Server / AllReduce等。
個人感覺這裡面一個可以挖的坑是Decentralized Training。地裡一位大佬也在做這個方向。
1.2 Edge Computing
很多ML模型是需要在手機上執行的(比如毀圖秀秀)。針對這一場景,一個是要對手機這種低功耗裝置對ML model進行裁剪加速(後面會提到),還有一個要做的就是執行在多個device上的分散式ML。
這裡有個最近非常火的概念:Federated Learning。其實本質還是炒資料並行的冷飯...不過應用場景比較不一樣。FL更多是為了Privacy的考慮,而分散式加速訓練在這裡倒是個次要目標。FL還涉及到了模型聚合[ch8],也就是如何把多個device本地訓練出的模型合併到一起。
1.3 大量計算資源的Scheduling / device placement
UCB的CS294 19spring對這一節有過介紹。
這裡的計算資源的數量級是很大的......比如工業界會有萬臺CPU伺服器 / 上千臺GPU伺服器搭建的DL平臺。這個小方向要解決的問題就是如何充分利用它們的效能。比如在阿里PAI組的JD裡就有這麼一條:“設計探索高效的分散式Placement演算法,以更系統化的方式來解決大規模深度學習高效訓練的問題”。
這方面比較早的工作大概是這篇paper,說的是如何為TensorFlow計算圖裡的不同運算元分配不同的device,最後用強化學習實現了這個目標。這個工作看起來有點prototype,但提出了一個新的思路。
另外還有很多猛如虎的類似Train XX model in y minutes的工作。這種就不僅是placement好就能完成的了,還需要涉及系統拓撲的設計、降低communication開銷等等。
1.4 communication相關
[ch3.5] [ch7]介紹了一些宏觀上的通訊模型,但深入進去還有很多可搞的坑。傳統搞網路/分散式系統的組比較契合這個小方向。
例如我校的分散式組原來有一些geo-distributed system的工作,現在也可以往ML上裝。
1.5 其他sys for ML可做的坑
工業界的一個ML pipeline不僅僅是訓練,還涉及到很多其他的坑。這些是目前被挖的還比較少的:
儲存 / Data Management:
1) 訓練資料的規模是很大的。如何為ML設計一個專用的檔案系統(類似大資料界的HDFS)或者資料庫來加速讀資料呢?類似的工作有管理ML model的ModelDB.
2) 在ML framework中,以及Parameter Server中,需要用一個KV storage system來儲存引數。可不可以針對ML的場景最佳化這個KV儲存系統呢?關於這個可以參考neopenx大神的blog。
2. 深度學習模型壓縮/加速
這方面和architecture結合比較緊密。CS229有這一節,也可以參考NIPS19上的這個talk。
對DL model進行壓縮主要考慮兩個角度:減少計算量(例如conv層的計算量) / 記憶體佔用(NN的引數數量)。不僅要考慮ML上的metric,也要考慮system層面的performance(例如latency / throughput / 功耗。有時候這些比ML模型的accuracy還重要)。具體的方式大概有以下幾種:
1. Architectural Compression
Layer Design -> Typically using factorization techniques to reduce storage and computation
Pruning(剪枝) -> Eliminating weights, layers, or channels to reduce storage and computation from large pre-trained models. 減少卷積核大小 / 通道數等等
2. Weight Compression
Low Bit Precision Arithmetic -> Weights and activations are stored and computed using low bit precision
Quantized(量化) Weight Encoding -> Weights are quantized and stored using dictionary encodings.
很多相關的工作是在ML的角度來壓縮模型的(也就是Arch Compression,特別是針對CNN和RNN。比如很著名的MobileNet)。這裡我們先(kan)略(bu)過(dong),來看從System的角度是如何加速的。
2.1 透過Quantized(量化)降低計算精度要求
量化的含義是將卷積層(the weights and / or activations of a CNN)通常要用到的32位浮點數用更低位的數來表示,如int32, int16, int8等等,來降低資源佔用(float32無論是計算還是儲存都是很吃資源的..)。量化之後無疑會損失一部分精度,但神經網路對噪聲並不是特別敏感,因此控制好量化的程度之後對ML任務的影響可以很小。
一種常用的量化方法是train in floating point and then quantize the resulting weights,訓練時還是用float32(因為要涉及到反向傳播和梯度下降,全是int就很難搞了..),但在inference的階段就可以加速啦。一個直觀的方法是事先找好一般網路引數的min / max值,然後將訓練好的網路引數乘一個scala factor來對映到[MIN_INT, MAX_INT]區間內的整數存起來。在inference時先按int來計算,最後結果再轉換回float32。這一過程中其實加速了大量的卷積計算。比如這篇paper就實現了float32到int8的量化。
混合精度計算:上面講的方法是用在inference階段的,其實在模型訓練時也可以用類似的方法來加速,只不過再用int就不大行了。一種比較新的方法是用float16(也就是俗稱的半精度),fp16佔用空間是單精度(fp32)的一半,雙精度(double,也就是fp64)的1/4。
量化的具體實現方法可以參考這裡。NVIDIA專門推出了針對inference階段量化加速的工具包TensorRT
2.2 新硬體 / DL Acclerator
在純硬體方面針對DL workload的工作也有很多,這裡來看幾個parallel相關的技術。最近Data-Level Parallelism不僅在深度學習中,在其他一些領域(比如資料庫)也有了越來越多的應用。
CPU:儘管GPU已經成了深度學習計算的標配,有時候仍然是需要CPU運算的。例如要在手機等辣雞裝置上進行inference。
SIMD:SIMD的含義是同一條指令在多個資料流上操作,和在向量處理器中一樣。在具體實現中(例如SSE指令集)是把一個128位SSE暫存器(這是新增加的SIMD專用暫存器,和早期借用FPU暫存器的MMX不同。在SSE指令集中是增加了8個這種暫存器)劃分成4個塊,同時存放4個float32單精度浮點數,4個塊可以同時進行運算(有多個運算單元,作用於不同的地址),這樣就提高了並行度。後來的SSE2 / SSE3 / SSE4 / AVX指令集在此基礎上又增加對float64 / 更多運算的支援,以及擴充套件了SIMD專用暫存器的位數,但本質上還是一樣的。 另外,SIMD帶來的並行和超標量處理器的並行性(一個週期issue多個指令,用於instruction level parallelism)不是一個概念。非超標量處理器也可以SIMD,而超標量處理器可以更並行issue多個SIMD操作。
VLIW:和一次issue多條指令,然後靠硬體進行ILP排程(也叫動態多發射。需要硬體實現亂序執行、分支預測等操作)的超標量處理器不同,VLIW(Very Large Instruction Width,採用這種技術的處理器也叫做靜態多發射處理器)的含義是一次只issue一條可以完成多個操作的複雜長指令(也叫發射包,其實從軟體的角度看是多條指令的集合)。因此一條指令的位寬可以很大。VLIW是透過編譯器來進行指令級並行排程的(比如一個常用的方法是迴圈展開,透過識別出可並行的重疊跨迴圈體指令塊來實現ILP)。VLIW的本意是希望在編譯階段就識別出程式中的依賴關係(靜態排程),得到可以並行執行的發射包,硬體只需要根據排程好的發射包直接執行即可,這樣就簡化了硬體實現,從而實現更大寬度發射包的並行執行。intel Itanium的IA64指令集就使用了這個技術,但它在當年並沒有取得成功。一個重要的原因是它只適合計算密集、演算法固定可控的workload。傳統的通用應用程式可能很難具備這個屬性(有很多run-time才能確定的值,另外cache訪問也是不確定的),但深度學習任務具備這些性質。
GPU:GPU的本質可以看做SIMT(Single Instruction Multiple Threads)。
等8205課上講完GPGPU的topic再補充吧...
GPU叢集:
系統結構:這個和純計算關係不是很大,可能暫時和ML加速也沒啥關係(事實上目前在計算機網路研究中用的還多一些)......但對於最佳化整體效能會有幫助
NUMA:當單個CPU效能已經到瓶頸時,多處理器就成了比較好的解決方案。為了方便程式設計,需要保證能為應用程式提供跨越所有處理器的單一實體地址空間,這種也叫做共享記憶體處理器(Shared Memory Processor)。SMP又可以分為兩種型別:1) 任何處理器訪問任何地址的仿存時間都是相同的,叫做統一儲存訪問(Uniform Memory Access)。2) 對於每個核心,訪問某些字會比訪問其他字快一些,整個記憶體空間被分割並分配給不同處理器 / 記憶體控制器,這叫做非統一儲存訪問(NonUniform Memory Access,NUMA)。NUMA雖然看起來複雜,但可以支援更大的規模(更多的核心),並且訪問附近的儲存器時具有較低的延遲。在過去記憶體控制器還在北橋的時代,多處理器用的是UMA(所有處理器都透過FSB匯流排連線北橋,再訪問記憶體)。後來隨著核心越來越多,為提高訪存速度,記憶體處理器被做到了CPU內,每個CPU有(或者很少的幾個核心共享)一個記憶體控制器,然後直連一部分記憶體空間,這些核心就被歸為一個NUMA node。而跨NUMA node之間的記憶體訪問需要走QPI匯流排。可以參考這裡的圖解。在一些涉及many core的工作中會經常用到NUMA的概念
RDMA:在網路環境中會用到。RDMA全稱是Remote Direct Memory Access,用於實現不需要OS參與的遠端記憶體訪問(因為message passing through kernel會浪費本來很大的記憶體和網路頻寬)。具體的技術細節可以參考這裡。
專用硬體:CPU效能太菜,GPU又太龐大,於是人們開發了AI專用晶片
FPGA:全稱是Field Programmable Gate Array,是可以多次燒寫的。因為本質上屬於軟體所以可以快速開發 / 迭代。
ASIC:全稱是application-specific integrated circuits,出廠後電路就不可以改變了(需要流片)。但是效能比FPGA高。
2.3 矩陣運算元最佳化
神經網路中的很多運算本質上就是對矩陣運算,因此可以用一些矩陣乘法最佳化方案來加速。比如cublas就是封裝好的針對矩陣和向量運算的加速庫,而對於神經網路加速則會使用cudnn
運算元最佳化是個非常貼近hardware的工作,對多種裝置都人工調優這些運算元其實是比較難的...如果能簡化一部分工作就最好啦。於是就有了下面會提到的深度學習編譯器
2.4 AutoML
這個嚴格來說可能不算MLsys了...但它的思路在很多MLsys問題中也會被用到
AutoML最早只能調很有限的幾種引數,用的方法也比較暴力(啟發式搜尋)。後來能調的東西越來越多,方法也更加猛如虎...一個里程碑是NAS,標誌著神經網路結構也可以Auto了。
常用的調參方法大致可以分為這幾種:
隨機搜尋,或者說叫啟發式搜尋。包括 GridSearch 和 RandomSearch。這種方法的改進空間主要體現在使用不同的取樣方法生成配置,但本質上仍然是隨機試驗不同的配置,沒有根據跑出來的結果來反饋指導取樣過程,效率比較低。
Multi-armed Bandit。這種方法綜合考慮了“探索”和“利用”兩個問題,既可以配置更多資源(也就是取樣機會)給搜尋空間中效果更優的一部分,也會考慮嘗試儘量多的可能性。Bandit 結合貝葉斯最佳化,就構成了傳統的 AutoML 的核心。
深度強化學習。強化學習在 AutoML 中最著名的應用就是 NAS,用於自動生成神經網路結構。另外它在 深度學習引數調優 中也有應用。它的優點是從“從資料中學習”轉變為“從動作中學習”(比如某個引數從小調到大),既可以從效能好的樣本中學習,也可以從效能壞的樣本中學習。但強化學習的坑也比較多,體現在訓練可能比較困難,有時結果比較難復現。
之所以把AutoML也列出來,是因為這些方法在下面提到的ML for system問題中會很有用。比如之前做過的AutoTiKV就應用了一種貝葉斯最佳化方法來調節資料庫引數。
cs294中給出了幾個可提高的方向:
Accelerate data collection and preparation
Automatic data discovery
Distributed data processing, esp. for image and video data
Data cleaning and schema driven auto-featurization
Accelerate model selection and hyper-parameter search
Parallel and distributed execution
Data and feature caching across training runs
Provenance
Track previous model development to inform future decisions
Connect errors in production with decisions in model development
3. 深度學習框架/系統設計
和Distributed Training的區別是這裡更關注一些工程上的東西(框架設計、API設計等等)。一個Deep Learning Framework大致需要以下幾個元素:
支援各種運算元(op) 和 tensor (data)
計算圖的定義方式(動態 v.s. 靜態)
Auto Diff
Optimizer(例如Adam)
各種加速和最佳化的庫:cudnn, openblas,mkl等
3.1 Deep Learning Framework
這一節重點關注這幾個方向:
Differentiable Programming:如果用過Keras或者PyTorch就會記得它可以簡單得像搭積木一樣摞一個NN出來,只需要定義一個一個的層(前向傳播邏輯)和損失函式就行了。而NN的訓練需要Backward Propagation / Forward Propagation,也就是計算微分,運算時framework可以根據定義好的計算圖自動求導算梯度。只要可微分就可以保證這個積木能摞出來,然後使用鏈式法則就可以自動計算微分(Automatic Differentiation)。如果一個語言或者framework具備了Differentiable Programming的性質,就可以更簡單的在它上面開發Deep Learning應用(可以類比python手寫NN和Keras的區別)。這篇文章對Auto Diff的實現做了很詳細的介紹。
Embedded Domain Specific Languages:DSL的概念我們都知道,比如SQL就是資料庫系統中的DSL,但這已經相當於一個全新的語言了。Embedded DSL是在現有語言上(例如Python)針對某個特定任務做的擴充套件。比如為了讓Python做矩陣計算更方便發明了numpy;為了進行機器學習就有了TensorFlow / PyTorch等等。Embedded DSL的作用是完成 Linear Algebra -> Pipelines -> Differentiable Programs 的轉化。
根據計算圖的定義方式,可以分為Declarative Abstraction(Embedded DSL先生成靜態計算圖,類似編譯執行 define-and-run,例如Tensorflow、Caffe)和Imperative(Embedded DSL生成動態計算圖並直接輸出結果,類似解釋執行 define-by-run,例如PyTorch、Tensorflow Eager)
對於具體的DL框架來說,雖然很多公司都開始自研框架了,但最流行的基本就TensorFlow、PyTorch、mxnet等等那幾家了。不過最近又出現了分散式強化學習框架Ray,也具有很好的落地潛能。
3.2 Inference / Model Serving
之前關注了很多訓練ML模型中會遇到的問題。但實際應用場景裡,inference(直接使用訓練好的模型predict)的次數會比training多很多,因此inference的效能也很重要。
Inference可以再分為以下兩種:
Offline: Pre-Materialize Predictions:所有可能的query都是已知的,就事先predict好存起來。一般沒有這麼玩的...
Online: Compute Predictions on the fly:根據使用者的輸入實時predict。這才是最常見的場景
一個典型的ML inference pipeline大致涉及到以下工序:
input data
-> Preprocessing(比如圖片要resize)
-> model prediction(有時候會同時用很多model,還要ensemble起來)
-> 輸出結果,有時候還要處理一下
這個pipeline的衡量指標包括Latency、Throughput等(和傳統的system問題一樣呀)。cs294裡列出了幾個最近的工作,可以參考這裡的paper解讀。個人感覺這裡可做的坑不多....大多是修修補補...
3.3深度學習編譯器
這裡值得提一下TVM。這篇文章對TVM進行了非常詳細的介紹。
簡單的說TVM是在把訓練好的ML model部署在不同裝置上時用的,重點關注的是Inference而不是Training(也就是推理引擎)。在這一過程中,模型本身可能用了不同的framework來寫(比如tensorflow / PyTorch / MXNet,本質區別在於使用的運算元型別可能不一樣),而要部署到的裝置也可能有不同的硬體架構(比如x86 / ARM / GPU / FPGA)。inference的過程也就是將framework X寫出來的model放在硬體Y上執行的過程,這一過程和編譯器是非常相似的(將語言X寫的程式編譯到硬體Y上執行),這也就是深度學習編譯器的含義。
為了設計一個高效的深度學習編譯器,TVM借鑑了傳統編譯器LLVM的設計思想:抽象出編譯器前端[ 高階語言C/java -> IR ],編譯器中端[ 最佳化IR,這種是不同編譯器平臺共享的 ],編譯器後端[ IR -> 目標硬體上的binary ]等概念,引入IR (Intermediate Representation。深度學習問題中可以將計算圖作為IR,稱為Graph IR)。這樣不同硬體/framework都對標同一套IR,就避免了需要對每種硬體和framework排列組合適配的問題。TVM主要解決的是後端的問題[在目標硬體上高效執行IR]。而前端的問題[生成和最佳化IR]就交給深度學習框架們完成(針對這一步,在TVM stack中提供了NNVM,作用是represent workloads from different frameworks into standardized computation graphs)。
TVM是和硬體深度整合的,也就是需要針對每種硬體平臺實現相關的AI運算元(類似NVIDIA GPU上的cuDNN)。然而人工調優這些運算元的實現是很費精力的(特別是要針對不同形狀的業務模型),這裡面也有一些knob需要調整。為了讓這個過程也能ML化,於是後來有了AutoTVM。
cs294 sp19還提出了幾個可能的future work:
- Compilers are great at Ahead of Time scheduling, what about Just-In-Time scheduling?
- Any way we can share GPU in predictable way and maximize utilization for DNN inference?
- Can we optimize for “fitness” of the kernel when it’s executed along with other kernels instead of its latency?
4. 用ML最佳化傳統的system問題
這裡面的花樣就更多了...在上學期Jon的ML system課上有過較詳細的接觸。大部分是用ML去最佳化一個傳統system問題中,一些需要人工經驗調整、或者說可以從歷史情況learn到一些東西的模組。比如資料庫引數、作業系統頁表、資料庫索引等等。一個模組可以被ML化的前提是它必須是empirical的,參考它在頁表(OS的工作集原理)、資料庫(DBA是個很吃經驗的活...)中的應用。如果人工都看不出來啥規律就別指望它能ML了...
一般認為用ML最佳化system的思想是起源於Jeff Dean在NIPS2017的workshop。這方面的工作很多發表在純system的頂級會議以及下屬的AI for xxx workshop上,另外一些AI會議的workshop也會收錄一些這方面的工作,比如nips 2018的MLsys workshop。從2017年開始已經有很多坑被做過了,但個人感覺還是有一些搞頭的。感覺可以從下面兩個角度再來搞:
同樣的scenario,使用更合適的ML演算法。注意這裡是更合適,而不是更高大上猛如虎。
比如這篇ML+Database的paper,使用了LSTM來預測未來的workload pattern,還要用GPU訓練,但生產環境上要求資料庫伺服器也安個顯示卡是不現實的。工程上的一個解決方案是搞個集中式的訓練叢集(類似OtterTune),在DBaaS的情況下這種方法倒是行得通,但在對外發布的資料庫產品中就不行了。
這裡感覺可以參考早期AutoML的一些工作,因為它們本質是很類似的(都是調參嘛...)。傳統方法有啟發式搜尋/貝葉斯最佳化。最近也有很多人用強化學習去搞,但還是存在太吃資源的問題...
這方面對ML知識的要求高一點。
尋找system界更多可以ML化的場景。這個更適合專業的system researcher來做,對ML倒是略有了解即可。
有一類思路是把ML深度整合到系統設計中,比如Andy在2019年的15-721課上提到過Self-Driving Database的概念,和之前用ML最佳化資料庫的工作不同的是,Self-Driving DB更關注如何把ML和DB深度整合,而不是搞一個又一個外掛的模組了。
另外一個類似的工作是在OS領域:engineering.purdue.edu/ 。
5. 其他
方向不是很契合就先不看了...等用到了再填坑
ML pipeline / lifecycle:ucbrise.github.io/cs294
Privacy:ucbrise.github.io/cs294
需要的技能樹
這是從一些公司ML System Research Scientist崗位的招聘要求中整理出來的,更側重system一些。
System:
工程基礎:C/C++、OO programming。閱讀原始碼是個很好的學習方式
OS
分散式系統
編譯原理。特別是編譯器最佳化技術、LLVM、memory optimization。Parser之類不喜歡也可以不看
Computer Architecture。另外還需要了解:1.GPU架構,例如視訊記憶體分配機制、CPU與GPU互動。2.CPU、儲存系統相關的新技術。3.有條件可以瞭解下深度學習專用硬體。
常見的平行計算框架,例如MPI/OpenMP/CUDA
ML framework的底層原理,扒原始碼
工業界的一些新東西:例如k8s、KubeFlow、ElasticDL
ML:
機器學習基礎
常見的分散式機器學習演算法、DL模型壓縮、模型加速方法(根據具體方向而定)
數理基礎不要太菜…不要被人吐槽像沒學過高中數學…