使用Keras進行多GPU訓練 multi_gpu_model


使用Keras訓練具有多個GPU的深度神經網絡(照片來源:Nor-Tech.com)。

摘要

在今天的博客文章中,我們學習了如何使用多個GPU來訓練基於Keras的深度神經網絡。

使用多個GPU使我們能夠獲得准線性加速。

為了驗證這一點,我們在CIFAR-10數據集上訓練了MiniGoogLeNet。

使用單個GPU,我們能夠獲得63秒的時間段,總訓練時間為74分10秒

然而,通過使用Keras和Python的多GPU訓練,我們將訓練時間減少到16秒,總訓練時間為19m3s

使用Keras啟用多GPU培訓就像單個函數調用一樣簡單 - 我建議您盡可能使用多GPU培訓。在未來,我想象 multi_gpu_model   將演變,讓我們進一步定制專門其中的GPU應該用於訓練,最終實現多系統的訓練也是如此。

方法:使用Keras,Python和深度學習進行多GPU培訓

當我第一次開始使用Keras時,我愛上了API。它簡單而優雅,類似於scikit-learn。然而它非常強大,能夠實施和訓練最先進的深度神經網絡。

然而,我對Keras的最大挫折之一是在多GPU環境中使用它可能有點不重要。

如果您使用Theano,請忘掉它 - 多GPU培訓不會發生。
TensorFlow是一種可能性,但它可能需要大量的樣板代碼和調整才能使您的網絡使用多個GPU進行訓練。

我更喜歡在執行多GPU培訓時使用mxnet后端(甚至是mxnet庫直接)到Keras,但這引入了更多的配置來處理。

所有這一切都與改變弗朗索瓦CHOLLET宣布,使用TensorFlow后端多GPU的支持,現在在烤Keras V2.0.9大部分功勞歸功於@ kuza55和他們的keras-extras回購

我已經使用並測試了這個多GPU功能已近一年了,我非常高興看到它作為官方Keras發行版的一部分。

在今天博客文章的剩余部分中,我將演示如何使用Keras,Python和深度學習訓練卷積神經網絡進行圖像分類。

MiniGoogLeNet深度學習架構

圖1: MiniGoogLeNet架構是它的大兄弟GoogLeNet / Inception的一個小版本。圖片來自@ ericjang11@pluskid

上面的圖1中,我們可以看到單個卷積(),初始()和下采樣()模塊,然后是從這些構建塊構建的整體MiniGoogLeNet架構(底部)。我們將在本文后面的多GPU實驗中使用MiniGoogLeNet架構。

MiniGoogLenet中的Inception模塊是Szegedy等人設計的原始Inception模塊的變體

我首先從@ ericjang11@pluskid的推文中了解了這個“Miniception”模塊,它們可以很好地可視化模塊和相關的MiniGoogLeNet架構。

在做了一些研究后,我發現這張圖片來自張等人的2017年出版物“ 理解深度學習需要重新思考泛化”

然后我開始在Keras + Python中實現MiniGoogLeNet架構 - 我甚至將它作為使用Python進行計算機視覺深度學習的一部分

對MiniGoogLeNet Keras實現的全面審查超出了本博文的范圍,因此如果您對網絡的工作原理(以及如何編碼)感興趣,請參閱我的書

否則,您可以使用此博客文章底部“下載”部分下載源代碼。

使用Keras和多個GPU訓練深度神經網絡

讓我們繼續使用Keras和多個GPU開始培訓深度學習網絡。

首先,您需要確保  在虛擬環境中安裝和更新Keras 2.0.9(或更高版本)(我們  在本書中使用名為dl4cv的虛擬環境 ): 

1
2
$ workon dl4cv
$ pip install -- upgrade keras

從那里,打開一個新文件,將其命名為 train .py  ,並插入以下代碼: 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# set the matplotlib backend so figures can be saved in the background
# (uncomment the lines below if you are using a headless server)
# import matplotlib
# matplotlib.use("Agg")
 
# import the necessary packages
from pyimagesearch . minigooglenet import MiniGoogLeNet
from sklearn . preprocessing import LabelBinarizer
from keras . preprocessing . image import ImageDataGenerator
from keras . callbacks import LearningRateScheduler
from keras . utils . training_utils import multi_gpu_model
from keras . optimizers import SGD
from keras . datasets import cifar10
import matplotlib . pyplot as plt
import tensorflow as tf
import numpy as np
import argparse

如果您使用的是無頭服務器,則需要通過取消注釋行來配置第3行和第4行的matplotlib后端這樣可以將matplotlib圖保存到磁盤。如果您沒有使用無頭服務器(即,您的鍵盤+鼠標+顯示器已插入系統,則可以將線條注釋掉)。

從那里我們導入這個腳本所需的包。

第7行從我的pyimagesearch   模塊導入MiniGoogLeNet (包含在“下載”部分中提供的下載)。

另一個值得注意的導入是在  第13行,我們導入CIFAR10數據集這個輔助函數將使我們能夠只用一行代碼從磁盤加載CIFAR-10數據集。

現在讓我們解析命令行參數: 

19
20
21
22
23
24
25
26
27
28
# construct the argument parse and parse the arguments
ap = argparse . ArgumentParser ( )
ap . add_argument ( "-o" , "--output" , required = True ,
help = "path to output plot" )
ap . add_argument ( "-g" , "--gpus" , type = int , default = 1 ,
help = "# of GPUs to use for training" )
args = vars ( ap . parse_args ( ) )
 
# grab the number of GPUs and store it in a conveience variable
G = args [ "gpus" ]

我們使用 argparse   解析一個需要和一個可選的參數線20-25

  • - 輸出  :訓練完成后輸出圖的路徑。
  • - gpus  :用於培訓的GPU數量。

加載命令行參數后,  為方便起見,我們將GPU的數量存儲為 G第28行)。

從那里,我們初始化用於配置我們的訓練過程的兩個重要變量,然后定義 poly_decay  ,一個等同於Caffe的多項式學習速率衰減的學習速率調度函數 

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# definine the total number of epochs to train for along with the
# initial learning rate
NUM_EPOCHS = 70
INIT_LR = 5e - 3
 
def poly_decay ( epoch ) :
# initialize the maximum number of epochs, base learning rate,
# and power of the polynomial
maxEpochs = NUM_EPOCHS
baseLR = INIT_LR
power = 1.0
 
# compute the new learning rate based on polynomial decay
alpha = baseLR * ( 1 - ( epoch / float ( maxEpochs ) ) ) * * power
 
# return the new learning rate
return alpha

我們設置  NUM _ EPOCHS  = 70   - 這是我們的訓練數據將通過網絡的次數(時期)(第32行)。

我們還初始化學習率 INIT_LR = 5e - 3 ,這是在之前的試驗(第33行)中通過實驗發現的值

從那里,我們定義 poly_decay   函數,它相當於Caffe的多項式學習速率衰減(第35-46行)。本質上,此功能可在訓練期間更新學習速率,並在每個時期后有效地減少學習速度。設置  功率= 1.0   會將衰減從多項式更改線性

接下來我們將加載我們的訓練+測試數據並將圖像數據從整數轉換為浮點數: 

48
49
50
51
52
53
# load the training and testing data, converting the images from
# integers to floats
print ( "[INFO] loading CIFAR-10 data..." )
( ( trainX , trainY ) , ( testX , testY ) ) = cifar10 . load_data ( )
trainX = trainX . astype ( "float" )
testX = testX . astype ( "float" )

從那里我們對數據應用平均減法 

55
56
57
58
# apply mean subtraction to the data
mean = np . mean ( trainX , axis = 0 )
trainX -= mean
testX -= mean

在  第56行,我們計算所有訓練圖像的平均值,然后是  第57和58行,其中我們從訓練和測試集中的每個圖像中減去平均值。

然后,我們執行“one-hot encoding”,這是我在本書中更詳細討論的編碼方案: 

60
61
62
63
# convert the labels from integers to vectors
lb = LabelBinarizer ( )
trainY = lb . fit_transform ( trainY )
testY = lb . transform ( testY )

單熱編碼將分類標簽從單個整數轉換為向量,因此我們可以應用分類交叉熵損失函數。我們已經在第61-63行處理了這個問題  

接下來,我們創建一個數據增強器和一組回調: 

65
66
67
68
69
70
# construct the image generator for data augmentation and construct
# the set of callbacks
aug = ImageDataGenerator ( width_shift_range = 0.1 ,
height_shift_range = 0.1 , horizontal_flip = True ,
fill_mode = "nearest" )
callbacks = [ LearningRateScheduler ( poly_decay ) ]

67-69行,我們構建了用於數據增強的圖像生成器。

數據增強覆蓋在里面詳細執業捆綁深度學習計算機視覺與Python ; 然而,暫時理解這是一種在訓練過程中使用的方法,我們通過對它們進行隨機變換來隨機改變訓練圖像。

由於這些改變,網絡不斷地看到增強的示例 - 這使得網絡能夠更好地概括驗證數據,同時可能在訓練集上表現更差。在大多數情況下,這些權衡是值得的。

我們在第70行創建了一個回調函數, 它允許我們的學習率在每個時代之后衰減 - 注意我們的函數名稱 poly_decay  。

我們接下來檢查GPU變量:

72
73
74
75
76
# check to see if we are compiling using just a single GPU
if G <= 1 :
print ( "[INFO] training with 1 GPU..." )
model = MiniGoogLeNet . build ( width = 32 , height = 32 , depth = 3 ,
classes = 10 )

如果GPU計數小於或等於1,我們  通過使用 初始化 模型構建   函數(第73-76行),否則我們將在訓練期間並行化模型:

78
79
80
81
82
83
84
85
86
87
88
89
90
# otherwise, we are compiling using multiple GPUs
else :
print ( "[INFO] training with {} GPUs..." . format ( G ) )
 
# we'll store a copy of the model on *every* GPU and then combine
# the results from the gradient updates on the CPU
with tf . device ( "/cpu:0" ) :
# initialize the model
model = MiniGoogLeNet . build ( width = 32 , height = 32 , depth = 3 ,
classes = 10 )
 
# make the model parallel
model = multi_gpu_model ( model , gpus = G )

在Keras中創建一個多GPU模型需要一些額外的代碼,但不多!

首先,您將在第84行注意到我們已指定使用CPU(而不是GPU)作為網絡上下文。

為什么我們需要CPU?

好吧,CPU負責處理任何開銷(例如在GPU內存上移動和移動訓練圖像),而GPU本身則負擔繁重。

在這種情況下,CPU實例化基本模型。

然后我們可以  在第90行調用 multi_gpu_model此功能將模型從CPU復制到我們所有的GPU,從而獲得單機,多GPU數據並行性。

在訓練我們的網絡時,圖像將被批量分配到每個GPU。CPU將從每個GPU獲得梯度,然后執行梯度更新步驟。

然后我們可以編譯我們的模型並啟動培訓過程:

92
93
94
95
96
97
98
99
100
101
102
103
104
105
# initialize the optimizer and model
print ( "[INFO] compiling model..." )
opt = SGD ( lr = INIT_LR , momentum = 0.9 )
model . compile ( loss = "categorical_crossentropy" , optimizer = opt ,
metrics = [ "accuracy" ] )
 
# train the network
print ( "[INFO] training network..." )
H = model . fit_generator (
aug . flow ( trainX , trainY , batch_size = 64 * G ) ,
validation_data = ( testX , testY ) ,
steps_per_epoch = len ( trainX ) / / ( 64 * G ) ,
epochs = NUM_EPOCHS ,
callbacks = callbacks , verbose = 2 )

在  第94行,我們構建了一個隨機梯度下降(SGD)優化器。

隨后,我們使用SGD優化器和分類的交叉熵損失函數編譯模型。

我們現在准備訓練網絡了!

為了啟動培訓過程,我們打電話給 模型fit_generator   並提供必要的參數。

我們希望每個GPU上的批量大小為64,因此由batch_size = 64 * G 指定    。

我們的培訓將持續70個時期(我們之前指定)。

梯度更新的結果將在CPU上組合,然后在整個訓練過程中應用於每個GPU。

現在培訓和測試已經完成,讓我們繪制損失/准確度,以便我們可以看到培訓過程:

107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# grab the history object dictionary
H = H . history
 
# plot the training loss and accuracy
N = np . arange ( 0 , len ( H [ "loss" ] ) )
plt . style . use ( "ggplot" )
plt . figure ( )
plt . plot ( N , H [ "loss" ] , label = "train_loss" )
plt . plot ( N , H [ "val_loss" ] , label = "test_loss" )
plt . plot ( N , H [ "acc" ] , label = "train_acc" )
plt . plot ( N , H [ "val_acc" ] , label = "test_acc" )
plt . title ( "MiniGoogLeNet on CIFAR-10" )
plt . xlabel ( "Epoch #" )
plt . ylabel ( "Loss/Accuracy" )
plt . legend ( )
 
# save the figure
plt . savefig ( args [ "output" ] )
plt . close ( )

最后一個塊僅使用matplotlib繪制訓練/測試損失和准確度(第112-121行),然后將數字保存到磁盤(第124行)。

如果您想了解有關培訓過程(以及內部工作原理)的更多信息,請參閱使用Python進行計算機視覺深度學習

Keras多GPU結果

讓我們檢查一下我們辛勤工作的結果。

首先,使用本文底部“下載”部分從本課程中獲取代碼然后,您就可以按照結果進行操作

讓我們在單個GPU上訓練以獲得基線:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ python train .py -- output single_gpu .png
[ INFO ] loading CIFAR - 10 data . . .
[ INFO ] training with 1 GPU . . .
[ INFO ] compiling model . . .
[ INFO ] training network . . .
Epoch 1 / 70
- 64s - loss : 1.4323 - acc : 0.4787 - val_loss : 1.1319 - val_acc : 0.5983
Epoch 2 / 70
- 63s - loss : 1.0279 - acc : 0.6361 - val_loss : 0.9844 - val_acc : 0.6472
Epoch 3 / 70
- 63s - loss : 0.8554 - acc : 0.6997 - val_loss : 1.5473 - val_acc : 0.5592
. . .
Epoch 68 / 70
- 63s - loss : 0.0343 - acc : 0.9898 - val_loss : 0.3637 - val_acc : 0.9069
Epoch 69 / 70
- 63s - loss : 0.0348 - acc : 0.9898 - val_loss : 0.3593 - val_acc : 0.9080
Epoch 70 / 70
- 63s - loss : 0.0340 - acc : 0.9900 - val_loss : 0.3583 - val_acc : 0.9065
Using TensorFlow backend .
 
real      74m10.603s
user      131m24.035s
sys      11m52.143s

圖2:在單個GPU上使用Keras在CIFAR-10上訓練和測試MiniGoogLeNet網絡架構的實驗結果。

對於這個實驗,我在我的NVIDIA DevBox上使用單個Titan X GPU進行了訓練。每個時期花費約63秒,總訓練時間為74分10秒

然后我執行以下命令來訓練  我的所有四個Titan X GPU:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ python train .py -- output multi_gpu .png -- gpus 4
[ INFO ] loading CIFAR - 10 data . . .
[ INFO ] training with 4 GPUs . . .
[ INFO ] compiling model . . .
[ INFO ] training network . . .
Epoch 1 / 70
- 21s - loss : 1.6793 - acc : 0.3793 - val_loss : 1.3692 - val_acc : 0.5026
Epoch 2 / 70
- 16s - loss : 1.2814 - acc : 0.5356 - val_loss : 1.1252 - val_acc : 0.5998
Epoch 3 / 70
- 16s - loss : 1.1109 - acc : 0.6019 - val_loss : 1.0074 - val_acc : 0.6465
. . .
Epoch 68 / 70
- 16s - loss : 0.1615 - acc : 0.9469 - val_loss : 0.3654 - val_acc : 0.8852
Epoch 69 / 70
- 16s - loss : 0.1605 - acc : 0.9466 - val_loss : 0.3604 - val_acc : 0.8863
Epoch 70 / 70
- 16s - loss : 0.1569 - acc : 0.9487 - val_loss : 0.3603 - val_acc : 0.8877
Using TensorFlow backend .
 
real      19m3.318s
user      104m3.270s
sys      7m48.890s

圖3:在CIFAR10數據集上使用Keras和MiniGoogLeNet的多GPU培訓結果(4個Titan X GPU)。訓練結果類似於單GPU實驗,而訓練時間減少了約75%。

在這里你可以看到訓練中的准線性加速:使用四個GPU,我能夠將每個時期減少到只有  16秒整個網絡在19分3秒內完成了培訓  

正如您所看到的,不僅可以輕松地訓練具有Keras和多個GPU的深度神經網絡  ,它也是  高效的

注意:在這種情況下,單GPU實驗獲得的精度略高於多GPU實驗。在訓練任何隨機機器學習模型時,會有一些差異。如果你要在數百次運行中平均這些結果,它們將(大致)相同。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM