程序如何在两个gpu卡上并行运行_深度学习分布式训练相关介绍 - Part 1 多GPU训练...
本篇文章主要是對深度學(xué)習(xí)中運用多GPU進(jìn)行訓(xùn)練的一些基本的知識點進(jìn)行的一個梳理
文章中的內(nèi)容都是經(jīng)過認(rèn)真地分析,并且盡量做到有所考證
拋磚引玉,希望可以給大家有更多的啟發(fā),并能有所收獲
介紹
大多數(shù)時候,梯度下降算法的訓(xùn)練需要較大的Batch Size才能獲得良好性能。而當(dāng)我們選擇比較大型的網(wǎng)絡(luò)時候,由于GPU資源有限,我們往往要減少樣本數(shù)據(jù)的Batch Size。
當(dāng)GPU無法存儲足夠的訓(xùn)練樣本時,我們該如何在更大的batch size上進(jìn)行訓(xùn)練?面對這個問題,事實上我們有幾種工具、技巧可以選擇,它們也是下文中將介紹的內(nèi)容。
在這篇文章中,我們將探討:
- 多GPU訓(xùn)練和單GPU訓(xùn)練有什么區(qū)別
- 如何最充分地使用多GPU機(jī)器
- 如何進(jìn)行多機(jī)多卡訓(xùn)練?
更多關(guān)于多機(jī)多卡的分布式訓(xùn)練的詳細(xì)架構(gòu)理解和實踐請參考我的下一篇文章:
Zhang Bin:深度學(xué)習(xí)分布式訓(xùn)練相關(guān)介紹 - Part 2 詳解分布式訓(xùn)練架構(gòu)PS-Worker與Horovod?zhuanlan.zhihu.com本文章介紹的內(nèi)容在框架間是通用的,代碼示例為:在不借助外部框架的情況下,將單GPU訓(xùn)練TensorFlow代碼改為支持多GPU的訓(xùn)練代碼
單GPU訓(xùn)練 vs 多GPU訓(xùn)練
單GPU訓(xùn)練 一般代碼比較簡單,并且能滿足我們的基本需求,通常做法是設(shè)定變量CUDA_VISIBLE_DEVICES的值為某一塊GPU來Mask我們機(jī)器上的GPU設(shè)備,雖然有時當(dāng)我們忘了設(shè)定該變量時程序會自動占用所有的GPU資源,但如果沒有相應(yīng)的代碼去分配掌控GPU資源的使用的話,程序還是只會利用到第一張卡的計算資源,其他的資源則僅是占用浪費狀態(tài)。
多GPU訓(xùn)練 則可以從兩個方面提升我們模型訓(xùn)練的上限:1. 超過單卡顯存上限的模型大小, 2. 更大的Batch Size和更快訓(xùn)練速度。相應(yīng)的,目前各大主流框架的多GPU訓(xùn)練一般存在兩種模式:
- 模型并行 :分布式系統(tǒng)中的不同GPU負(fù)責(zé)網(wǎng)絡(luò)模型的不同部分,進(jìn)而可以 構(gòu)建超過單卡顯存容量大小的模型 。比如,可以將神經(jīng)網(wǎng)絡(luò)的不同層分配到不同的GPU設(shè)備,或者將不同的參數(shù)變量分配到不同的GPU設(shè)備。
- 數(shù)據(jù)并行 :不同的 GPU設(shè)備有同一模型的多個副本,將數(shù)據(jù)分片并分配到每個GPU上,然后將所有GPU的計算結(jié)果按照某種方式合并,進(jìn)而可以增加訓(xùn)練數(shù)據(jù)的Batch Size。
此外,從主機(jī)的數(shù)量的角度來講還存在 單機(jī)多卡和多機(jī)多卡(分布式)的區(qū)別:
- 單機(jī)多卡:只需運行一份代碼,由該代碼分配該臺機(jī)器上GPU資源的使用
- 多機(jī)多卡:每臺機(jī)器上都需要運行一份代碼,機(jī)器之間需要互相通信傳遞梯度,并且模型參數(shù)的更新也存在同步訓(xùn)練模式和異步訓(xùn)練模式的區(qū)別
多GPU機(jī)器的充分利用
這一節(jié),將詳細(xì)探討一下多GPU的機(jī)器該如何利用。
對于多GPU訓(xùn)練,一般我們需要用數(shù)據(jù)并行的模式比較多,通過增大 Batch Size 并輔以較高的 Learning Rate 可以加快模型的收斂速度。
由于我們模型是通過若干步梯度反向傳播來迭代收斂模型的參數(shù),而每一步的梯度由一個Batch內(nèi)樣本數(shù)據(jù)的損失情況(Loss)得到,因此當(dāng) Batch Size 比較大時, 可以減少 Batch樣本的分布和整體樣本的分布偏差太大的風(fēng)險,進(jìn)而使得每一步的梯度更新方向更加準(zhǔn)確。
比如,單卡訓(xùn)練 InceptionResNet 網(wǎng)絡(luò)最大Batch Size為100, 學(xué)習(xí)率為0.001。采用4張卡去訓(xùn)練時,可以設(shè)置Batch Size為400, 學(xué)習(xí)率為 0.002。在代碼中對每一個Batch 400 切分成 4*100,然后給到不同GPU卡上的模型上去訓(xùn)練。
下面主要介紹一下單機(jī)多卡訓(xùn)練的細(xì)節(jié)及部分Tensorflow代碼:
- 數(shù)據(jù)切片
- 模型構(gòu)建
- 梯度反傳
1. 數(shù)據(jù)分片
對于多GPU訓(xùn)練,我們首先要做的就是對訓(xùn)練數(shù)據(jù)進(jìn)行分片,對于單機(jī)多卡模型,其數(shù)據(jù)的分片可以在代碼的內(nèi)部進(jìn)行, 而對于多機(jī)多卡模型,多機(jī)之間沒有必要進(jìn)行數(shù)據(jù)切分方面的通信,建議的做法是先在本地做好數(shù)據(jù)的分配,然后再由不同的機(jī)器讀取不同的數(shù)據(jù)。
Tensorflow 單機(jī)多卡的數(shù)據(jù)切片代碼如下,其中 training_iterator 為采用tf.data.Dataset 構(gòu)建的訓(xùn)練數(shù)據(jù)pipeline, num_tower 為當(dāng)前機(jī)器上可見的(CUDA_VISIBLE_DEVICES)GPU數(shù)量。
tower 指模型的副本。
mnist_input, mnist_label = training_iterator.get_next() tower_inputs = tf.split(mnist_input, num_towers) tower_labels = tf.split(mnist_label, num_towers)通過 tf.split 函數(shù),將輸入的數(shù)據(jù)按照GPU的數(shù)量平分。
2. 構(gòu)建模型
有了分好的數(shù)據(jù),下一步驟則是需要將模型放置在我們所有的GPU上,并將切片好的數(shù)據(jù)傳入。
Tensorflow可以指定將不同的任務(wù)分配到不同的設(shè)備上, GPU支持大量并行計算, 因此可以將模型的運算、梯度的計算分配到GPU上來,而變量的存儲、梯度的更新則可以由CPU來執(zhí)行,如下圖所示
下面我們來看一下TensorFlow的相關(guān)代碼
最外層的 for i in range(num_towers): 使我們需要構(gòu)建模型 num_towers次。
下一層的 with tf.device(device_string % i): 代表著每次我們構(gòu)建模型時,模型放置的設(shè)備名稱, 如果為GPU機(jī)器device_string = '/gpu:%d' ,如果為CPU機(jī)器 device_string = '/cpu:%d'
再下一層 with (tf.variable_scope(("tower"), reuse=True if i > 0 else None)): 通過 scope 的reuse 使得我們再后續(xù)放置模型的時候,不同GPU之間的模型參數(shù)共享。
最后一層 with (slim.arg_scope([slim.model_variable, slim.variable], device="/cpu:0" if num_gpus != 1 else "/gpu:0")): 使得我們將構(gòu)建模型中的變量放到CPU上來, 而運算則仍保留在該GPU上。
tower_gradients = [] tower_predictions = [] tower_label_losses = [] optimizer = tf.train.AdamOptimizer(learning_rate=1E-3)for i in range(num_towers):with tf.device(device_string % i):with (tf.variable_scope(("tower"), reuse=True if i > 0 else None)):with (slim.arg_scope([slim.model_variable, slim.variable],device="/cpu:0" if num_gpus != 1 else "/gpu:0")):x = tf.placeholder_with_default(tower_inputs[i], [None, 224,224,3], name="input")y_ = tf.placeholder_with_default(tower_labels[i], [None, 1001], name="label")# logits = tf.layers.dense(x, 10)logits = build_model(x)predictions = tf.nn.softmax(logits, name="predictions")loss = tf.losses.softmax_cross_entropy(onehot_labels=y_, logits=logits)grads = optimizer.compute_gradients(loss)tower_gradients.append(grads)tower_predictions.append(predictions)tower_label_losses.append(loss)3. 梯度反傳
上一步已經(jīng)得到了各個tower上的梯度,下面則需要將這些梯度結(jié)合起來并進(jìn)行反向傳播以更新模型的參數(shù)。
梯度結(jié)合的代碼可以參考下面的函數(shù)
def combine_gradients(tower_grads):"""Calculate the combined gradient for each shared variable across all towers.Note that this function provides a synchronization point across all towers.Args:tower_grads: List of lists of (gradient, variable) tuples. The outer listis over individual gradients. The inner list is over the gradientcalculation for each tower.Returns:List of pairs of (gradient, variable) where the gradient has been summedacross all towers."""filtered_grads = [[x for x in grad_list if x[0] is not None] for grad_list in tower_grads]final_grads = []for i in range(len(filtered_grads[0])):grads = [filtered_grads[t][i] for t in range(len(filtered_grads))]grad = tf.stack([x[0] for x in grads], 0)grad = tf.reduce_sum(grad, 0)final_grads.append((grad, filtered_grads[0][i][1],))return final_grads通過 combine_gradients 來計算合并梯度,再通過 optimizer.apply_gradients 對得到的梯度進(jìn)行反向傳播
merged_gradients = combine_gradients(tower_gradients) train_op = optimizer.apply_gradients(merged_gradients, global_step=global_step)最后再通過 sess.run(train_op) 對執(zhí)行。
多機(jī)多卡 分布式訓(xùn)練
多機(jī)多卡相比較于單機(jī)多卡,其使得模型訓(xùn)練的上限進(jìn)一步突破。一般我們一臺服務(wù)器只支持8張GPU卡,而采用分布式的多機(jī)多卡訓(xùn)練方式,可以將幾十甚至幾百臺服務(wù)器調(diào)度起來一起訓(xùn)練一個模型。
但相比于單機(jī)多卡,多機(jī)多卡分布式訓(xùn)練方式的配置更復(fù)雜一些,不僅要保證多臺機(jī)器之間是可以互相通信的,還需要配置不同機(jī)器之間的角色以及不同機(jī)器之間梯度傳遞。
- TensorFlow 原生 PS架構(gòu)
在Parameter server架構(gòu)(PS架構(gòu))中,集群中的節(jié)點被分為兩類:parameter server和worker。其中parameter server存放模型的參數(shù),而worker負(fù)責(zé)計算參數(shù)的梯度。在每個迭代過程,worker從parameter sever中獲得參數(shù),然后將計算的梯度返回給parameter server,parameter server聚合從worker傳回的梯度,然后更新參數(shù),并將新的參數(shù)廣播給worker。 - Uber 開發(fā)的Horovod架構(gòu) - 支持 TensorFlow, Keras, PyTorch, and Apache MXNet
Horovod 是一套面向 TensorFlow 的分布式訓(xùn)練框架,由 Uber 構(gòu)建并開源,目前已經(jīng)運行于 Uber 的 Michelangelo 機(jī)器學(xué)習(xí)即服務(wù)平臺上。Horovod 能夠簡化并加速分布式深度學(xué)習(xí)項目的啟動與運行。
Horovod架構(gòu)采用全新的梯度同步和權(quán)值同步算法,叫做 ring-allreduce。此種算法各個節(jié)點之間只與相鄰的兩個節(jié)點通信,并不需要參數(shù)服務(wù)器。因此,所有節(jié)點都參與計算也參與存儲。
關(guān)于分布式訓(xùn)練的更多介紹,請參考我的下一篇文章
Zhang Bin:深度學(xué)習(xí)分布式訓(xùn)練相關(guān)介紹 - Part 2 詳解分布式訓(xùn)練架構(gòu)PS-Worker與Horovod?zhuanlan.zhihu.com相關(guān)參考鏈接
TensorFlow - Multi GPU Computation
分布式tensorflow(一)
TensorFlow分布式全套(原理,部署,實例)
distributeTensorflowExample
總結(jié)
以上是生活随笔為你收集整理的程序如何在两个gpu卡上并行运行_深度学习分布式训练相关介绍 - Part 1 多GPU训练...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 摩尔庄园怎么成为邻居
- 下一篇: 扩容原理_硬核丨一文读懂以太坊扩容方案