OneFlow 并行特色
OneFlow 并行特色
在 Consistent 與 Mirrored 視角中,已經(jīng)知道 OneFlow 提供了 mirrored 與 consistent 兩種看待分布式系統(tǒng)的視角,并且提前知道了 OneFlow 的 consistent 視角頗具特色。
因?yàn)樵?consistent_view 下,OneFlow 提供了邏輯上統(tǒng)一的視角,分布式訓(xùn)練時(shí),用戶可以自由選擇數(shù)據(jù)并行、模型并行還是是混合并行。
本文繼續(xù)深入介紹 OneFlow 獨(dú)具特色的 consistent 視角,包括:
? OneFlow 在 consistent_view 下純數(shù)據(jù)并行流程示意
? OneFlow 在 consistent_view 下混合并行流程示意
? 混合并行的優(yōu)勢(shì)及適用場(chǎng)景
? OneFlow 混合并行實(shí)例
網(wǎng)絡(luò)模型訓(xùn)練的邏輯圖
先設(shè)定一個(gè)簡(jiǎn)單的多層網(wǎng)絡(luò),作為討論并行方式的載體,其結(jié)構(gòu)如下圖所示:
各層中,有 樣本 (灰色矩形)、 模型 (藍(lán)色矩形),以及作用在兩者之上的 op (圓形),為了簡(jiǎn)化討論,也可將樣本與模型限定為 矩陣 ,作用在它們之上的op為 矩陣乘法 。
對(duì)照上圖,很容易梳理出該網(wǎng)絡(luò)模型的邏輯:
? 第0層的輸入為 Data 0 矩陣與 Model 0 矩陣,它們進(jìn)行 op (矩陣乘法)運(yùn)算后,輸出 Data 1
? 第1層的輸入為 Data 1 矩陣與 Model 1 矩陣,它們進(jìn)行 op 運(yùn)算后,輸出 output
? 第2層為 output 層,Data 2 作為整個(gè)網(wǎng)絡(luò)的輸出;當(dāng)然,在更深的網(wǎng)絡(luò)中,它也可以作為下一層的輸入繼續(xù)參與訓(xùn)練
consistent 視角下支持?jǐn)?shù)據(jù)并行、模型并行與混合并行,將依次進(jìn)行介紹,其中混合并行是重點(diǎn)。
Consistent 視角下的并行特色
純數(shù)據(jù)并行
已經(jīng)知道,consistent 視角下,默認(rèn)的并行方式是數(shù)據(jù)并行;而如果選擇 mirrored 視角,則只能采用數(shù)據(jù)并行;若在調(diào)用作業(yè)函數(shù)時(shí)直接傳遞 numpy 數(shù)據(jù)(而不是使用 OneFlow 的 DataLoader 及相關(guān)算子),兩者的區(qū)別在于:
? mirrored 視角下,采用純數(shù)據(jù)并行,需要自己根據(jù)參與訓(xùn)練的卡數(shù)對(duì)數(shù)據(jù)進(jìn)行切分、重組,使用 list 傳遞和接收數(shù)據(jù);
? 而 consistent 視角下提供了邏輯上的統(tǒng)一看待,數(shù)據(jù)的切分和重組交給了 OneFlow 框架完成。
下圖是 consistent 視角下,采用純數(shù)據(jù)并行的方式,實(shí)現(xiàn)原邏輯網(wǎng)絡(luò)模型的流程示意圖:
在純數(shù)據(jù)并行中,采用了2張顯卡進(jìn)行并行訓(xùn)練,因?yàn)椴捎昧?純數(shù)據(jù)并行 ,可以看到,對(duì)于原邏輯模型中的每一層,樣本數(shù)據(jù)都被平均分配到了各個(gè)卡上,每張卡上都擁有 完整的模型,與切分的數(shù)據(jù)進(jìn)行 op 運(yùn)算,最后組合各個(gè)卡上的樣本,得到完整的輸出。
純模型并行
在 consistent 視角下,也可以通過選擇純模型并行(設(shè)置方式在下文實(shí)例中會(huì)介紹),其流程示意圖為:
在純模型并行中,同樣是2張顯卡進(jìn)行并行訓(xùn)練,原邏輯模型中的每一層中,都是 部分模型 與 完整的數(shù)據(jù) 進(jìn)行 op 運(yùn)算,最后組合得到完整的輸出。
值得一提的是,從上圖可以看出,各個(gè)卡上第0層的輸出,并 不能 直接作為第1層的輸入:因?yàn)槟P筒⑿兄?#xff0c;為完成 op 操作,需要部分的模型與 完整的 數(shù)據(jù);為了解決這個(gè)問題,OneFlow 中使用了 boxing 機(jī)制。
boxing 機(jī)制會(huì)統(tǒng)籌分布式訓(xùn)練中各個(gè)節(jié)點(diǎn)的數(shù)據(jù),并合理切分、合并到對(duì)應(yīng)的卡上,除了模型并行過程中的數(shù)據(jù)重組問題外,數(shù)據(jù)并行中的反向梯度同步,也使用 boxing 機(jī)制解決。
boxing 的內(nèi)部機(jī)制雖然復(fù)雜,但是對(duì)于用戶而言是透明的,僅僅是防止讀者產(chǎn)生迷惑才加入了 boxing 的圖示,對(duì)于本文而言,只需要了解:OneFlow 會(huì)自動(dòng)協(xié)調(diào)好分布式中數(shù)據(jù)的同步問題。
選擇最優(yōu)的并行方式
數(shù)據(jù)并行與模型并行的優(yōu)劣并不是一成不變的,樣本規(guī)模、模型規(guī)模及模型結(jié)構(gòu)決定了分布式訓(xùn)練中的綜合表現(xiàn),需要具體情況具體分析。
概括而言:
? 數(shù)據(jù)并行情況下,需要同步的信息是反向傳播過程的 梯度,因此應(yīng)該確保各個(gè)訓(xùn)練節(jié)點(diǎn)之間的信息同步速度要比節(jié)點(diǎn)內(nèi)部的計(jì)算速度快,比如說 卷積層 的參數(shù)較少,但是計(jì)算量大,就比較適合使用數(shù)據(jù)并行;
? 模型并行情況下,因?yàn)榭梢詫⑦壿嬌献鳛檎w的模型 切分到各個(gè)物理卡 上,能夠解決“模型太大,一張卡裝不下”的問題,因此,對(duì)于參數(shù)量大的神經(jīng)網(wǎng)絡(luò)層(如最后的全連接層),可以考慮使用模型并行。
實(shí)際上,也可以使用 混合并行,在同一個(gè)分布式訓(xùn)練的不同部分,組合使用數(shù)據(jù)并行、模型并行。比如,對(duì)于神經(jīng)網(wǎng)絡(luò)中靠前的參數(shù)較少、計(jì)算量大的層,采用數(shù)據(jù)并行;在最終的參數(shù)眾多的全連接層,則采用模型并行,以下是針對(duì)本文最開始的網(wǎng)絡(luò)模型邏輯圖的 混合并行 實(shí)現(xiàn)方案的示意圖:
目前,其它的主流框架對(duì)于混合并行或者不支持,或者需要深度定制,而 OneFlow 中可以通過簡(jiǎn)單的設(shè)置,配置混合并行的分布式訓(xùn)練,還可以用自由度超高的流水并行,深度優(yōu)化分布式系統(tǒng)。
混合并行實(shí)例
代碼
以下腳本,在 consistent 視角下,對(duì) MLP 模型采用了混合并行方案:輸入層與隱藏層采用(默認(rèn)的)數(shù)據(jù)并行;輸出層采用模型并行并進(jìn)行列切分。
代碼:hybrid_parallelism_mlp.py
更具體的解析在后文“代碼解讀”可見。
代碼解讀
以上腳本修改自3分鐘快速上手中的示例代碼,比較兩份代碼,也可以體會(huì)到在 OneFlow 的 consistent_view 下進(jìn)行各種并行方案的配置是多么的簡(jiǎn)單,只需要在單機(jī)的程序上稍加修改即可。
以上程序的關(guān)鍵部分有:
? 通過 oneflow.config.gpu_device_num 接口設(shè)置參與訓(xùn)練的GPU數(shù)目:
? flow.config.gpu_device_num(2)
? reshape 及 hidden 采用默認(rèn)的數(shù)據(jù)并行,不需要修改;輸出層通過設(shè)置 model_distribute 為 flow.distribute.split(axis=0) 變?yōu)槟P筒⑿?#xff1a;
? def mlp(data):
? initializer = flow.truncated_normal(0.1)
? reshape = flow.reshape(data, [data.shape[0], -1])
? hidden = flow.layers.dense(
? reshape,
? 512,
? activation=flow.nn.relu,
? kernel_initializer=initializer,
? name=“dense1”,
? )
? return flow.layers.dense(
? hidden,
? 10,
? kernel_initializer=initializer,
? # dense為列存儲(chǔ),進(jìn)行split(0)切分
? model_distribute=flow.distribute.split(axis=0),
? name=“dense2”,
? )
有讀者可能好奇為什么split(axis=0)是列切分?需要說明的是,OneFlow 中的 dense 內(nèi)部采用列存儲(chǔ),因此以上代碼的flow.distribute.split(axis=0)確實(shí)是在做列切分。
此外,flow.layers.dense 使用 model_distribute 形參設(shè)置并行方式,其內(nèi)部調(diào)用了底層更通用的 get_variable 接口創(chuàng)建 blob, get_variable 接口設(shè)置并行方式的形參名為 distribute。
可以看到,通過極少量的修改,就能將單機(jī)訓(xùn)練程序改為分布式、混合并行的程序,這是 OneFlow 區(qū)別于其它框架的一大特色。
流水并行實(shí)例
在模型并行之外,OneFlow 還提供了一種靈活度更高的“流水并行”的并行方式,可以讓用戶使用 scope.placement 接口顯式指定用來運(yùn)行邏輯 op的 物理硬件。
在流水并行中,整個(gè)神經(jīng)網(wǎng)絡(luò)有的層次在一組物理設(shè)備上,另外一些層次在另外一組物理設(shè)備上,接力協(xié)同工作,分多個(gè)階段,在設(shè)備之間流水執(zhí)行。
在以下示例中,對(duì) Consistent 與 Mirrored 視角中的“在 OneFlow 中使用 consistent 視角”代碼進(jìn)行簡(jiǎn)單修改,展示了流水并行模式。
代碼
完整代碼:hybrid_parallelism_lenet.py
更詳細(xì)的討論可見后文的“代碼解讀”。
代碼解讀
以上關(guān)鍵的代碼只有2行,且他們的本質(zhì)作用是類似的:
? 通過 oneflow.scope.placement ,指定 hidden 層的 op 計(jì)算運(yùn)行在0號(hào) GPU 上
? with flow.scope.placement(“gpu”, “0:0”):
? hidden = flow.layers.dense(
? reshape,
? 512,
? activation=flow.nn.relu,
? kernel_initializer=initializer,
? name=“hidden”,
? )
? 通過 oneflow.scope.placement ,指定 output 層的op計(jì)算運(yùn)行在第0號(hào)主機(jī)的1號(hào) GPU 上
? with flow.scope.placement(“gpu”, “0:1”):
? output = flow.layers.dense(
? hidden, 10, kernel_initializer=initializer, name=“outlayer”
? )
scope.placement 的具體使用,可參閱 API 文檔。
流水并行,使得用戶可以為每個(gè) op 指定物理設(shè)備,非常適合對(duì)網(wǎng)絡(luò)模型及分布式情況都很熟悉的用戶進(jìn)行 深度優(yōu)化 。
此外,OneFlow 提供的 API oneflow.unpack、oneflow.pack 等,結(jié)合了 OneFlow 自身任務(wù)調(diào)度的特點(diǎn),使得流水并行更易用、高效,將在另外的文章中專門介紹。
總結(jié)
以上是生活随笔為你收集整理的OneFlow 并行特色的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 作业函数的定义与调用
- 下一篇: OFRecord 数据格式