『開發技術』GPU訓練加速原理(附KerasGPU訓練技巧)
0.深入理解GPU訓練加速原理
我們都知道用GPU可以加速神經神經網路訓練(相較於CPU),具體的速度對比可以參看我之前寫的速度對比博文: [深度應用]·主流深度學習硬體速度對比(CPU,GPU,TPU)
GPU是如何加速的呢?
我打算從兩個方面來解答:
- 單個GPU較於CPU加速:
在訓練網路中,其實大量的運算資源都消耗在了數值計算上面,大部分網路訓練的過程都是1.計算loss,2.根據loss求梯度,3.再根據梯度更新引數(梯度下降原理)。無論在GPU還是CPU中,都是不斷重複123步。但是由於CPU是通用計算單元(並不擅長數值執行),而GPU特長是影象處理(數值計算)。所以GPU更加適合訓練網路,從而起到加速效果。
- 多GPU較於單GPU加速:
一般在GPU訓練中,同一個GPU中,batch_size的大小,決定訓練的速度,batch_size越小,訓練一輪所需的步數(data_len/batch_size)就會越大,從而花費時間越多。
下面介紹下使用多GPU資料並行加速原理:
假設一臺機器上有k塊GPU。給定需要訓練的模型,每塊GPU及其相應的視訊記憶體將分別獨立維護一份完整的模型引數。在模型訓練的任意一次迭代中,給定一個隨機小批量,我們將該批量中的樣本劃分成k份並分給每塊顯示卡的視訊記憶體一份。然後,每塊GPU將根據相應視訊記憶體所分到的小批量子集和所維護的模型引數分別計算模型引數的本地梯度。接下來,我們把k塊顯示卡的視訊記憶體上的本地梯度相加,便得到當前的小批量隨機梯度。之後,每塊GPU都使用這個小批量隨機梯度分別更新相應視訊記憶體所維護的那一份完整的模型引數。下圖描繪了使用2塊GPU的資料並行下的小批量隨機梯度的計算。
使用2塊GPU的資料並行下的小批量隨機梯度的計算
我們回憶下梯度下降的過程,1.計算loss,2.根據loss求梯度,3.再根據梯度更新引數。
使用上述的多GPU資料並行方法,可以理解為把batch_size擴大了k倍,從而總的時間縮短為了k分之1,實現了多GPU計算訓練。
其實每一個GPU上網路的引數都是相同的,因為都是從相同的loss做的更新。
1.如何在 GPU 上執行 Keras?
如果你以 TensorFlow 或 CNTK 後端執行,只要檢測到任何可用的 GPU,那麼程式碼將自動在 GPU 上執行。
如果你以 Theano 後端執行,則可以使用以下方法之一:
方法 1: 使用 Theano flags。
THEANO_FLAGS=device=gpu,floatX=float32 python my_keras_script.py
"gpu" 可能需要根據你的裝置識別符號(例如gpu0,gpu1等)進行更改。
方法 2: 建立 .theanorc
: 指導教程
方法 3: 在程式碼的開頭手動設定 theano.config.device
, theano.config.floatX
:
import theano
theano.config.device = 'gpu'
theano.config.floatX = 'float32'
2.如何在多 GPU 上執行 Keras 模型?
我們建議使用 TensorFlow 後端來執行這項任務。有兩種方法可在多個 GPU 上執行單個模型:資料並行和裝置並行。
在大多數情況下,你最需要的是資料並行。
資料並行
資料並行包括在每個裝置上覆制一次目標模型,並使用每個模型副本處理不同部分的輸入資料。Keras 有一個內建的實用函式 keras.utils.multi_gpu_model
,它可以生成任何模型的資料並行版本,在多達 8 個 GPU 上實現準線性加速。
有關更多資訊,請參閱 multi_gpu_model 的文件。這裡是一個快速的例子:
from keras.utils import multi_gpu_model
# 將 `model` 複製到 8 個 GPU 上。
# 假定你的機器有 8 個可用的 GPU。
parallel_model = multi_gpu_model(model, gpus=8)
parallel_model.compile(loss='categorical_crossentropy',
optimizer='rmsprop')
# 這個 `fit` 呼叫將分佈在 8 個 GPU 上。
# 由於 batch size 為 256,每個 GPU 將處理 32 個樣本。
parallel_model.fit(x, y, epochs=20, batch_size=256)
裝置並行
裝置並行性包括在不同裝置上運行同一模型的不同部分。對於具有並行體系結構的模型,例如有兩個分支的模型,這種方式很合適。
這種並行可以通過使用 TensorFlow device scopes 來實現。這裡是一個簡單的例子:
# 模型中共享的 LSTM 用於並行編碼兩個不同的序列
input_a = keras.Input(shape=(140, 256))
input_b = keras.Input(shape=(140, 256))
shared_lstm = keras.layers.LSTM(64)
# 在一個 GPU 上處理第一個序列
with tf.device_scope('/gpu:0'):
encoded_a = shared_lstm(tweet_a)
# 在另一個 GPU上 處理下一個序列
with tf.device_scope('/gpu:1'):
encoded_b = shared_lstm(tweet_b)
# 在 CPU 上連線結果
with tf.device_scope('/cpu:0'):
merged_vector = keras.layers.concatenate([encoded_a, encoded_b],
axis=-1)
3.參考
1.http://zh.d2l.ai/chapter_computational-performance/multiple-gpus.html
2.https://keras.io/zh/getting-started/faq/#how-can-i-run-a-keras-model-on-multiple-