文章目錄
- 第二章 一個案例吃透深度學習(下)
-
- 資源配置
-
- CPU還是GPU?
- 分布式訓練
- 完整代碼
- 訓練調試與優化
-
- 計算模型的分類準确率
- 檢查模型訓練過程,識别潛在訓練問題
- 加入校驗或測試,更好評價模型效果
- 加入正則化項,避免模型過拟合
- 可視化分析
第二章 一個案例吃透深度學習(下)
接着上一章的四個部分的優化方法,今天學習另外三部分的優化。
資源配置
CPU還是GPU?
飛槳動态圖通過fluid.dygraph.guard(place=None)裡的place參數,設定在GPU上訓練還是CPU上訓練。
with fluid.dygraph.guard(place=fluid.CPUPlace()) #設定使用CPU資源訓神經網絡。
with fluid.dygraph.guard(place=fluid.CUDAPlace(0)) #設定使用GPU資源訓神經網絡,預設使用伺服器的第一個GPU卡。"0"是GPU卡的編号,比如一台伺服器有的四個GPU卡,編号分别為0、1、2、3。
分布式訓練
分布式訓練有兩種實作模式:模型并行和資料并行。
模型并行的方式一般适用于如下兩個場景:
- 模型架構過大: 完整的模型無法放入單個GPU。如2012年ImageNet大賽的冠軍模型AlexNet是模型并行的典型案例,由于當時GPU記憶體較小,單個GPU不足以承擔AlexNet,是以研究者将AlexNet拆分為兩部分放到兩個GPU上并行訓練。
-
網絡模型的結構設計相對獨立: 當網絡模型的設計結構可以并行化時,采用模型并行的方式。如在計算機視覺目标檢測任務中,一些模型(如YOLO9000)的邊界框回歸和類别預測是獨立的,可以将獨立的部分放到不同的裝置節點上完成分布式訓練。
資料并行的方式與衆人拾柴火焰高的道理類似,如果把訓練資料比喻為磚頭,把一個裝置(GPU)比喻為一個人,那單GPU訓練就是一個人在搬磚,多GPU訓練就是多個人同時搬磚,每次搬磚的數量倍數增加,效率呈倍數提升。值得注意的是,每個裝置的模型是完全相同的,但是輸入資料不同,是以每個裝置的模型計算出的梯度是不同的。如果每個裝置的梯度隻更新目前裝置的模型,就會導緻下次訓練時,每個模型的參數都不相同。是以我們還需要一個梯度同步機制,保證每個裝置的梯度是完全相同的。
完整代碼
def train_multi_gpu():
##修改1-從環境變量擷取使用GPU的序号
place = fluid.CUDAPlace(fluid.dygraph.parallel.Env().dev_id)
with fluid.dygraph.guard(place):
##修改2-對原模型做并行化預處理
strategy = fluid.dygraph.parallel.prepare_context()
model = MNIST()
model = fluid.dygraph.parallel.DataParallel(model, strategy)
model.train()
#調用加載資料的函數
train_loader = load_data('train')
##修改3-多GPU資料讀取,必須確定每個程序讀取的資料是不同的
train_loader = fluid.contrib.reader.distributed_batch_reader(train_loader)
optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.01, parameter_list=model.parameters())
EPOCH_NUM = 5
for epoch_id in range(EPOCH_NUM):
for batch_id, data in enumerate(train_loader()):
#準備資料
image_data, label_data = data
image = fluid.dygraph.to_variable(image_data)
label = fluid.dygraph.to_variable(label_data)
predict = model(image)
loss = fluid.layers.cross_entropy(predict, label)
avg_loss = fluid.layers.mean(loss)
# 修改4-多GPU訓練需要對Loss做出調整,并聚合不同裝置上的參數梯度
avg_loss = model.scale_loss(avg_loss)
avg_loss.backward()
model.apply_collective_grads()
# 最小化損失函數,清除本次訓練的梯度
optimizer.minimize(avg_loss)
model.clear_gradients()
if batch_id % 200 == 0:
print("epoch: {}, batch: {}, loss is: {}".format(epoch_id, batch_id, avg_loss.numpy()))
#儲存模型參數
fluid.save_dygraph(model.state_dict(), 'mnist')
訓練調試與優化
訓練過程優化思路主要有如下五個關鍵環節:1. 計算分類準确率,觀測模型訓練效果。2. 檢查模型訓練過程,識别潛在問題。3. 加入校驗或測試,更好評價模型效果。4. 加入正則化項,避免模型過拟合。5. 可視化分析。
計算模型的分類準确率
檢查模型訓練過程,識别潛在訓練問題
加入校驗或測試,更好評價模型效果
加入正則化項,避免模型過拟合
具體來說,在模型的優化目标(損失)中人為加入對參數規模的懲罰項。當參數越多或取值越大時,該懲罰項就越大。通過調整懲罰項的權重系數,可以使模型在“盡量減少訓練損失”和“保持模型的泛化能力”之間取得平衡。泛化能力表示模型在沒有見過的樣本上依然有效。正則化項的存在,增加了模型在訓練集上的損失。
飛槳支援為所有參數加上統一的正則化項,也支援為特定的參數添加正則化項。前者的實作如下代碼所示,僅在優化器中設定regularization參數即可實作。使用參數regularization_coeff調節正則化項的權重,權重越大時,對模型複雜度的懲罰越高。
可視化分析
訓練模型時,經常需要觀察模型的評價名額,分析模型的優化過程,以確定訓練是有效的。可選用這兩種工具:Matplotlib庫和VisualDL。
- Matplotlib庫:Matplotlib庫是Python中使用的最多的2D圖形繪圖庫,它有一套完全仿照MATLAB的函數形式的繪圖接口,使用輕量級的PLT庫(Matplotlib)作圖是非常簡單的。
-
VisualDL:如果期望使用更加專業的作圖工具,可以嘗試VisualDL,飛槳可視化分析工具。VisualDL能夠有效地展示飛槳在運作過程中的計算圖、各種名額變化趨勢和資料資訊。
Matplotlib示例代碼:
#引入matplotlib庫
import matplotlib.pyplot as plt
with fluid.dygraph.guard(place):
model = MNIST()
model.train()
optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.01, parameter_list=model.parameters())
EPOCH_NUM = 10
iter=0
iters=[]
losses=[]
for epoch_id in range(EPOCH_NUM):
for batch_id, data in enumerate(train_loader()):
#準備資料,變得更加簡潔
image_data, label_data = data
image = fluid.dygraph.to_variable(image_data)
label = fluid.dygraph.to_variable(label_data)
#前向計算的過程,同時拿到模型輸出值和分類準确率
predict, acc = model(image, label)
#計算損失,取一個批次樣本損失的平均值
loss = fluid.layers.cross_entropy(predict, label)
avg_loss = fluid.layers.mean(loss)
#每訓練了100批次的資料,列印下目前Loss的情況
if batch_id % 100 == 0:
print("epoch: {}, batch: {}, loss is: {}, acc is {}".format(epoch_id, batch_id, avg_loss.numpy(), acc.numpy()))
iters.append(iter)
losses.append(avg_loss.numpy())
iter = iter + 100
#後向傳播,更新參數的過程
avg_loss.backward()
optimizer.minimize(avg_loss)
model.clear_gradients()
#儲存模型參數
fluid.save_dygraph(model.state_dict(), 'mnist')
#畫出訓練過程中Loss的變化曲線
plt.figure()
plt.title("train loss", fontsize=24)
plt.xlabel("iter", fontsize=14)
plt.ylabel("loss", fontsize=14)
plt.plot(iters, losses,color='red',label='train loss')
plt.grid()
plt.show()
VisualDL示例代碼:
#引入VisualDL庫,并設定儲存作圖資料的檔案位置
from visualdl import LogWriter
log_writer = LogWriter(logdir="./log")
with fluid.dygraph.guard():
model = MNIST()
model.train()
optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.01, parameter_list=model.parameters())
EPOCH_NUM = 10
iter = 0
for epoch_id in range(EPOCH_NUM):
for batch_id, data in enumerate(train_loader()):
#準備資料,變得更加簡潔
image_data, label_data = data
image = fluid.dygraph.to_variable(image_data)
label = fluid.dygraph.to_variable(label_data)
#前向計算的過程,同時拿到模型輸出值和分類準确率
predict, avg_acc = model(image, label)
#計算損失,取一個批次樣本損失的平均值
loss = fluid.layers.cross_entropy(predict, label)
avg_loss = fluid.layers.mean(loss)
#每訓練了100批次的資料,列印下目前Loss的情況
if batch_id % 100 == 0:
print("epoch: {}, batch: {}, loss is: {}, acc is {}".format(epoch_id, batch_id, avg_loss.numpy(), avg_acc.numpy()))
log_writer.add_scalar(tag = 'acc', step = iter, value = avg_acc.numpy())
log_writer.add_scalar(tag = 'loss', step = iter, value = avg_loss.numpy())
iter = iter + 100
#後向傳播,更新參數的過程
avg_loss.backward()
optimizer.minimize(avg_loss)
model.clear_gradients()
#儲存模型參數
fluid.save_dygraph(model.state_dict(), 'mnist')
在指令行 輸入 $ visualdl --logdir ./log --port 8080,查閱的網址在第三步的啟動指令後會列印出來(如http://127.0.0.1:8080/),将該網址輸入浏覽器位址欄重新整理頁面的效果如下圖所示。除了右側對資料點的作圖外,左側還有一個控制闆,可以調整諸多作圖的細節。