1. 程式人生 > >【Tensorflow】並行GPU計算

【Tensorflow】並行GPU計算

宣告:

0.簡介

在很多情況下,我們僅僅依靠CPU去訓練深度學習程式是十分耗時間的,所以我們需要將深度學習框架在GPU上進行模型訓練。但是,對於更加複雜的神經網路或者更加龐大的資料集,單個GPU已經無法滿足我們的計算量需求,所以需要將訓練過程並行在多個GPU上。

1.TensorFlow使用GPU

Tensorflow通過呼叫tf.device()來指定執行下一步操作的裝置,這個裝置可以是本地的CPU或者是GPU,也可以是遠端的伺服器。
tf.device()可以通過裝置號進行裝置選擇。對於GPU而言,通過/gpu:0/gpu:1進行選擇,但是對於CPU而言,所有的CPU都是通過/cpu:0

的方式進行選擇。
CPU
測試一:

import tensorflow as tf
with tf.device('/cpu:0'):
    a = tf.constant([1.0, 2.0, 3.0], shape=[3], name='a')
    b = tf.constant([1.0, 2.0, 3.0], shape=[3], name='b')
    c = a + b
with tf.Session(config=tf.ConfigProto(log_device_placement=True)) as sess:
    print(sess.run(c))

測試一輸出:

add: (Add): /job:localhost/replica:0/task:0/cpu:0
b: (Const): /job:localhost/replica:0/task:0/cpu:0
a: (Const): /job:localhost/replica:0/task:0/cpu:0

測試二:

import tensorflow as tf
with tf.device('/cpu:1'):
    a = tf.constant([1.0, 2.0, 3.0], shape=[3], name='a')
    b = tf.constant([1.0, 2.0, 3.0], shape=[3], name='b')
    c = a
+ b with tf.Session(config=tf.ConfigProto(log_device_placement=True)) as sess: print(sess.run(c))

測試二輸出(出現錯誤):

available devices are [ /job:localhost/replica:0/task:0/cpu:0, /job:localhost/replica:0/task:0/gpu:0, /job:localhost/replica:0/task:0/gpu:1 ].

因此對於GPU而言,我們可以通過編號進行選擇,但是對於CPU而言,所有的CPU都需要作為/cpu:0使用。

GPU

import tensorflow as tf
with tf.device('/gpu:0'):
    a = tf.constant([1.0, 2.0, 3.0], shape=[3], name='a')
with tf.device('/gpu:1'):
    b = tf.constant([1.0, 2.0, 3.0], shape=[3], name='b')
    c = a + b
with tf.Session(config=tf.ConfigProto(log_device_placement=True)) as sess:
    print(sess.run(c))

輸出:

add: (Add): /job:localhost/replica:0/task:0/gpu:1
b: (Const): /job:localhost/replica:0/task:0/gpu:1
a: (Const): /job:localhost/replica:0/task:0/gpu:0

輸出操作日誌
Tensorflow通過在構建Session中設定引數log_device_placement來打印出執行每一個操作的裝置的資訊(參見上面的程式)。

裝置優先順序
對於有GPU的裝置而言(安裝的是Tensorflow的GPU版本),程式會優先將運算自動部署在GPU上進行計算,如果是多GPU環境,Tensoflow只會將運算優先放在/gpu:0上進行計算。因此很多時候我們需要對不同的運算制定特定的裝置進行執行。

非法指定裝置
對於Tensorflow而言,並不是所有的操作都可以放置在GPU上進行運算,如果強制將某些操作制定在GPU上執行的話,會導致程式錯誤。對於不同版本的Tensorflow而言,對GPU的支援程度也是不一致的,如果在程式中全部使用強制指定裝置的方式會降低程式的可移植性。在Tensorflow的kernel中定義了哪些操作可以執行在GPU裝置上。

import tensorflow as tf
with tf.device('/gpu:0'):
    a = tf.Variable(1., name='a')
with tf.device('/gpu:0'):
    b = tf.Variable(1, name='b')
with tf.Session(config=tf.ConfigProto(log_device_placement=True)) as sess:
    print(sess.run(tf.global_variables_initializer()))

輸出:

Cannot assign a device for operation 'b': Could not satisfy explicit device specification '/device:GPU:0' because no supported kernel for GPU devices is available.

如上例,對於變數b因為是整數型別,因此在GPU上執行出現了錯誤。其實在GPU上,tf.Variable只支援實數型引數(float16, float32, double)。為避免這個問題,TensorFlow在生成會話時可以指定allow_soft_placement引數。當allow_soft_placement引數設定為True時,如果運算無法由GPU執行,那麼TensorFlow會自動將它放到CPU上執行。

import tensorflow as tf
with tf.device('/gpu:0'):
    a = tf.Variable(1., name='a')
with tf.device('/gpu:0'):
    b = tf.Variable(1, name='b')
with tf.Session(config=tf.ConfigProto(allow_soft_placement=True,log_device_placement=True)) as sess:
    print(sess.run(tf.global_variables_initializer()))

雖然GPU可以加速TensorFlow的計算,但一般來說不會把所有的操作全部都放在GPU上。一般來說,CPU進行流控,GPU進行密集型計算任務,將資料轉入或轉出GPU都會消耗時間,而且當GPU與記憶體進行資料交換的時候也會消耗時間。

2.深度學習並行模式

常見的並行化深度學習模型訓練方式有兩種:同步模式和非同步模式。深度學習的模型訓練過程是一個迭代過程,在每一輪的迭代過程,前向傳播演算法會根據當前的引數取值計算出一小部分訓練資料上的預測值,然後根據反向傳播演算法去優化損失函式從而更新網路層引數。因此在不同的裝置上進行並行訓練時,只需要將資料分發到多個裝置上即可,核心問題在於如何進行引數更新。

非同步方式
這裡寫圖片描述
在訓練的過程中,不同的裝置訓練進度是不一致的。因此,不同的裝置會在不同的時間去獲取引數的最新值,裝置與裝置之間完全獨立,每個裝置都會根據當前時間的引數值然後取小部分訓練資料進行訓練,並獨立的去反向傳播更新引數值。
這裡寫圖片描述
可以看到每一個裝置之間是互相獨立進行的。(但是在非同步處理過程中,要注意引數的訪問,避免出現資源爭奪等問題)

這裡寫圖片描述
但是在非同步模式下,容易導致模型無法優化的最優解。如圖所示,假設在t0點,裝置ab同時獲取到網路層引數,現在根據梯度下降的原理,需要將值向左迭代,此時如果是一個裝置更新引數,可能會達到極小值點,但是如果兩個裝置都對過去的引數進行更新,就會導致更新過度,更新到t1點。

同步方式
這裡寫圖片描述
在同步模式下,所有的裝置同時讀取引數的取值,並且當反向傳播演算法完成之後同步更新引數的取值。單個裝置不會單獨對引數進行更新,而會等待所有裝置都完成反向傳播之後再統一更新引數 。在每一輪迭代時,不同裝置首先統一讀取當前引數的取值,並隨機獲取一小部分資料。然後在不同裝置上執行反向傳播過程得到在各自訓練資料上引數的梯度。注意雖然所有裝置使用的引數是一致的,但是因為訓練資料不同,所以得到引數的梯度就可能不一樣。當所有裝置完成反向傳播的計算之後,需要計算出不同裝置上引數梯度的平均值,最後再根據平均值對引數進行更新。

總結

  • 現在常見的隨機梯度下降本身就是梯度下降演算法的一個近似解法,即使對於梯度下降演算法而言,也不一定會得到全域性最優解。
  • 在很多環境中,非同步模式的結果不一定比同步模式的結果差。
  • 同步模式耗時間。

3.多GPU並行測試