MXNet深度學習實戰》">《MXNet深度學習實戰》
614
2025-04-01
3.3 Module
在MXNet框架中,Module是一個高級的封裝模塊,可用來執行通過Symbol模塊定義的網絡模型的訓練,與Module相關的接口介紹都可以參考Module的官方文檔地址:https://mxnet.apache.org/api/python/module/module.html。Module接口提供了許多非常方便的方法用于模型訓練,只需要將準備好的數據、超參數等傳給對應的方法就能啟動訓練。
在3.2節,我們用Symbol接口定義了一個網絡結構sym,接下來我們將基于這個網絡結構介紹Module模塊,首先來看看如何通過Module模塊執行模型的預測操作。通過mxnet.module.Module()接口初始化一個Module對象,在初始化時需要傳入定義好的網絡結構sym并指定運行環境,這里設置為GPU環境。然后執行Module對象的bind操作,這個bind操作與Symbol模塊中的bind操作類似,目的也是將網絡結構添加到執行器,使得定義的靜態圖能夠真正運行起來,因為這個過程涉及顯存分配,因此需要提供輸入數據和標簽的維度信息才能執行bind操作,讀者可以在命令行通過“$ watch nvidia-smi”命令查看執行bind前后,顯存的變化情況。bind操作中還存在一個重要的參數是for_training,這個參數默認是True,表示接下來要進行的是訓練過程,因為我們這里只需要進行網絡的前向計算操作,因此將該參數設置為False。最后調用Module對象的init_params()方法初始化網絡結構的參數,初始化的方式是可以選擇的,這里采用默認方式,至此,一個可用的網絡結構執行器就初始化完成了。初始化網絡結構執行器的代碼具體如下:
mod = mx.mod.Module(symbol=sym, context=mx.gpu(0))
mod.bind(data_shapes=[('data',(8,3,28,28))],
label_shapes=[('softmax_label',(8,))],
for_training=False)
mod.init_params()
接下來隨機初始化一個4維的輸入數據,該數據的維度需要與初始化Module對象時設定的數據維度相同,然后通過mxnet.io.DataBatch()接口封裝成一個批次數據,之后就可以作為Module對象的forward()方法的輸入了,執行完前向計算后,調用Module對象的get_outputs()方法就能得到模型的輸出結果,具體代碼如下:
data = mx.nd.random.uniform(0,1,shape=(8,3,28,28))
mod.forward(mx.io.DataBatch([data]))
print(mod.get_outputs()[0])
輸出結果如下,因為輸入數據的批次大小是8,網絡的全連接層輸出節點數是2,因此輸出的維度是8*2:
[[ 0.50080067? 0.4991993 ]
[ 0.50148612? 0.49851385]
[ 0.50103837? 0.4989616 ]
[ 0.50171131? 0.49828872]
[ 0.50254387? 0.4974561 ]
[ 0.50104254? 0.49895743]
[ 0.50223148? 0.49776852]
[ 0.49780959? 0.50219035]]
接下來介紹如何通過Module模塊執行模型的訓練操作,代碼部分與預測操作有較多地方是相似的,具體代碼見代碼清單3-1(本書中的代碼清單都可以在本書的項目代碼地址中找到:https://github.com/miraclewkf/MXNet-Deep-Learning-in-Action),接下來詳細介紹代碼內容。
1)使用mxnet.io.NDArrayIter()接口初始化得到訓練和驗證數據迭代器,這里為了演示采用隨機初始化的數據,實際應用中要讀取有效的數據,不論讀取的是什么樣的數據,最后都需要封裝成數據迭代器才能提供給模型訓練。
2)用mxnet.module.Module()接口初始化得到一個Module對象,這一步至少要輸入一個Symbol對象,另外這一步還可以指定訓練環境是CPU還是GPU,這里采用GPU。
3)調用Module對象的bind()方法將準備好的數據和網絡結構連接到執行器構成一個完整的計算圖。
4)調用Module對象的init_params()方法初始化網絡的參數,因為前面定義的網絡結構只是一個架子,里面沒有參數,因此需要執行參數初始化。
5)調用Module對象的init_optimizer()方法初始化優化器,默認采用隨機梯度下降法(stochastic gradient descent,SGD)進行優化。
6)調用mxnet.metric.create()接口創建評價函數,這里采用的是準確率(accuracy)。
7)執行5次循環訓練,每次循環都會將所有數據過一遍模型,因此在循環開始處需要執行評價函數的重置操作、數據的初始讀取等操作。
8)此處的while循環只有在讀取完訓練數據之后才會退出,該循環首先會調用Module對象的forward()方法執行模型的前向計算,這一步就是輸入數據通過每一個網絡層的參數進行計算并得到最后結果。
9)調用Module對象的backward()方法執行模型的反向傳播計算,這一步將涉及損失函數的計算和梯度的回傳。
10)調用Module對象的update()方法執行參數更新操作,參數更新的依據就是第9步計算得到的梯度,這樣就完成了一個批次(batch)數據對網絡參數的更新。
11)調用Module對象的update_metric()方法更新評價函數的計算結果。
12)讀取下一個批次的數據,這里采用了Python中的try和except語句,表示如果try包含的語句執行出錯,則執行except包含的語句,這里用來標識是否讀取到了數據集的最后一個批次。
13)調用評價對象的get_name_value()方法并打印此次計算的結果。
14)調用Module對象的get_params()方法讀取網絡參數,并利用這些參數初始化Module對象了。
15)調用數據對象的reset()方法進行重置,這樣在下一次循環中就可以從數據的最初始位置開始讀取了。
代碼清單3-1 通過Module模塊訓練模型
import mxnet as mx
import logging
data = mx.sym.Variable('data')
conv = mx.sym.Convolution(data=data, num_filter=128, kernel=(3,3), pad=(1,1),
name='conv1')
bn = mx.sym.BatchNorm(data=conv, name='bn1')
relu = mx.sym.Activation(data=bn, act_type='relu', name='relu1')
pool = mx.sym.Pooling(data=relu, kernel=(2,2), stride=(2,2), pool_type='max',
name='pool1')
fc = mx.sym.FullyConnected(data=pool, num_hidden=2, name='fc1')
sym = mx.sym.SoftmaxOutput(data=fc, name='softmax')
data = mx.nd.random.uniform(0,1,shape=(1000,3,224,224))
label = mx.nd.round(mx.nd.random.uniform(0,1,shape=(1000)))
train_data = mx.io.NDArrayIter(data={'data':data},
label={'softmax_label':label},
batch_size=8,
shuffle=True)
print(train_data.provide_data)
print(train_data.provide_label)
mod = mx.mod.Module(symbol=sym,context=mx.gpu(0))
mod.bind(data_shapes=train_data.provide_data,
label_shapes=train_data.provide_label)
mod.init_params()
mod.init_optimizer()
eval_metric = mx.metric.create('acc')
for epoch in range(5):
end_of_batch = False
eval_metric.reset()
data_iter = iter(train_data)
next_data_batch = next(data_iter)
while not end_of_batch:
data_batch = next_data_batch
mod.forward(data_batch)
mod.backward()
mod.update()
mod.update_metric(eval_metric, labels=data_batch.label)
try:
next_data_batch = next(data_iter)
mod.prepare(next_data_batch)
except StopIteration:
end_of_batch = True
eval_name_vals = eval_metric.get_name_value()
print("Epoch:{} Train_Acc:{:.4f}".format(epoch, eval_name_vals[0][1]))
arg_params, aux_params = mod.get_params()
mod.set_params(arg_params, aux_params)
train_data.reset()
假設你拉取了本書的項目代碼,項目代碼的根目錄用“~/”表示,因為該腳本保存在“~/chapter3-baseKnowledge-of-MXNet/Module_code3-1.py”中,因此可以通過如下命令運行該腳本:
$ cd ~/chapter3-baseKnowledge-of-MXNet
$ python Module_code3-1.py
成功運行時可以得到如下結果:
Epoch:0 Train_Acc:0.5090
Epoch:1 Train_Acc:0.7010
Epoch:2 Train_Acc:0.9620
Epoch:3 Train_Acc:0.9860
Epoch:4 Train_Acc:0.9950
mx.mod是mxnet.module常用的縮寫,后續篇章默認采用縮寫形式。
代碼清單3-1中的代碼其實從mod.bind()方法這一行到最后都可以用Module模塊中的fit()方法來實現。fit()方法不僅封裝了上述的bind操作、參數初始化、優化器初始化、模型的前向計算、反向傳播、參數更新和計算評價指標等操作,還提供了保存訓練結果等其他操作,因此fit()方法將是今后使用MXNet訓練模型時經常調用的方法。下面這段代碼就演示了fit()方法的調用,前面兩行設置命令行打印訓練信息,這三行代碼可以直接替換代碼清單3-1中從mod.bind()那一行到最后的所有代碼。在fit()方法的輸入參數中,train_data參數是訓練數據,num_epoch參數是訓練時整個訓練集的迭代次數(也稱epoch數量)。需要注意的是,將所有train_data過一遍模型才算完成一個epoch,因此這里設定為將這個訓練集數據過5次模型才完成訓練。
logger = logging.getLogger()
logger.setLevel(logging.INFO)
mod.fit(train_data=train_data, num_epoch=5)
簡化版的代碼如代碼清單3-2所示。
代碼清單3-2 通過Module模塊訓練模型(簡化版)
import mxnet as mx
import logging
data = mx.sym.Variable('data')
conv = mx.sym.Convolution(data=data, num_filter=128, kernel=(3,3), pad=(1,1),
name='conv1')
bn = mx.sym.BatchNorm(data=conv, name='bn1')
relu = mx.sym.Activation(data=bn, act_type='relu', name='relu1')
pool = mx.sym.Pooling(data=relu, kernel=(2,2), stride=(2,2), pool_type='max',
name='pool1')
fc = mx.sym.FullyConnected(data=pool, num_hidden=2, name='fc1')
sym = mx.sym.SoftmaxOutput(data=fc, name='softmax')
data = mx.nd.random.uniform(0,1,shape=(1000,3,224,224))
label = mx.nd.round(mx.nd.random.uniform(0,1,shape=(1000)))
train_data = mx.io.NDArrayIter(data={'data':data},
label={'softmax_label':label},
batch_size=8,
shuffle=True)
print(train_data.provide_data)
print(train_data.provide_label)
mod = mx.mod.Module(symbol=sym,context=mx.gpu(0))
logger = logging.getLogger()
logger.setLevel(logging.INFO)
mod.fit(train_data=train_data, num_epoch=5)
該腳本代碼保存在“~/chapter3-baseKnowledge-of-MXNet/Module_code3-2.py”中,下面使用如下命令運行該腳本:
$ cd ~/chapter3-baseKnowledge-of-MXNet
$ python Module_code3-2.py
從下面打印出來的訓練結果可以看到,輸出結果與代碼清單3-1的輸出結果基本吻合:
INFO:root:Epoch[0] Train-accuracy=0.515000
INFO:root:Epoch[0] Time cost=4.618
INFO:root:Epoch[1] Train-accuracy=0.700000
INFO:root:Epoch[1] Time cost=4.425
INFO:root:Epoch[2] Train-accuracy=0.969000
INFO:root:Epoch[2] Time cost=4.428
INFO:root:Epoch[3] Train-accuracy=0.988000
INFO:root:Epoch[3] Time cost=4.410
INFO:root:Epoch[4] Train-accuracy=0.999000
INFO:root:Epoch[4] Time cost=4.425
上面的演示代碼中只設定了fit()方法的幾個輸入,其實fit()方法的輸入還有很多,實際使用中可根據具體要求設定不同的輸入參數,本書后面的章節還會進行詳細介紹。
得益于MXNet的靜態圖設計和對計算過程的優化,你會發現MXNet的訓練速度相較于大部分深度學習框架要快,而且顯存占用非常少!這使得你能夠在單卡或單機多卡上使用更大的batch size訓練相同的模型,這對于復雜模型的訓練非常有利,有時候甚至還會影響訓練結果。
機器學習 深度學習
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。