AMP:Automatic mixed precision,自動混合精度,能夠在神經網絡推理過程當中,針對不一樣的層,採用不一樣的數據精度進行計算,從而實現節省顯存和加快速度的目的。html
在Pytorch 1.5版本及之前,經過NVIDIA出品的插件apex,能夠實現amp功能。python
從Pytorch 1.6版本之後,Pytorch將amp的功能吸取入官方庫,位於torch.cuda.amp
模塊下。git
本文爲針對官方文檔主要內容的簡要翻譯和本身的理解。github
torch.cuda.amp
提供了對混合精度的支持。爲實現自動混合精度訓練,須要結合使用以下兩個模塊:網絡
torch.cuda.amp.autocast
:autocast
主要用做上下文管理器或者裝飾器,來肯定使用混合精度的範圍。torch.cuda.amp.GradScalar
:GradScalar
主要用來完成梯度縮放。一個典型的amp應用示例以下:函數
# 定義模型和優化器 model = Net().cuda() optimizer = optim.SGD(model.parameters(), ...) # 在訓練最開始定義GradScalar的實例 scaler = GradScaler() for epoch in epochs: for input, target in data: optimizer.zero_grad() # 利用with語句,在autocast實例的上下文範圍內,進行模型的前向推理和loss計算 with autocast(): output = model(input) loss = loss_fn(output, target) # 對loss進行縮放,針對縮放後的loss進行反向傳播 # (此部分計算在autocast()做用範圍之外) scaler.scale(loss).backward() # 將梯度值縮放回原尺度後,優化器進行一步優化 scaler.step(optimizer) # 更新scalar的縮放信息 scaler.update()
待更新性能
待更新優化
若是模型的Loss計算部分輸出多個loss,須要對每個loss值執行scaler.scale
。插件
若是網絡具備多個優化器,對任一個優化器執行scaler.unscale_
,並對每個優化器執行scaler.step
。scala
而scaler.update
只在最後執行一次。
應用示例以下:
scaler = torch.cuda.amp.GradScaler() for epoch in epochs: for input, target in data: optimizer0.zero_grad() optimizer1.zero_grad() with autocast(): output0 = model0(input) output1 = model1(input) loss0 = loss_fn(2 * output0 + 3 * output1, target) loss1 = loss_fn(3 * output0 - 5 * output1, target) scaler.scale(loss0).backward(retain_graph=True) scaler.scale(loss1).backward() # 選擇其中一個優化器執行顯式的unscaling scaler.unscale_(optimizer0) # 對每個優化器執行scaler.step scaler.step(optimizer0) scaler.step(optimizer1) # 完成全部梯度更新後,執行一次scaler.update scaler.update()
針對多卡訓練的狀況,隻影響autocast
的使用方法,GradScaler
的用法與以前一致。
在每個不一樣的cuda設備上,torch.nn.DataParallel
在不一樣的進程中執行前向推理,而autocast只在當前進程中生效,所以,以下方式的調用是不生效的:
model = MyModel() dp_model = nn.DataParallel(model) # 在主進程中設置autocast with autocast(): # dp_model的內部進程並不會對autocast生效 output = dp_model(input) # loss的計算在主進程中執行,autocast能夠生效,但因爲前面執行推理時已經失效,所以總體上是不正確的 loss = loss_fn(output)
有效的調用方式以下所示:
# 方法1:在模型構建中,定義forwar函數時,採用裝飾器方式 MyModel(nn.Module): ... @autocast() def forward(self, input): ... # 方法2:在模型構建中,定義forwar函數時,採用上下文管理器方式 MyModel(nn.Module): ... def forward(self, input): with autocast(): ... # DataParallel的使用方式不變 model = MyModel().cuda() dp_model = nn.DataParallel(model) # 在模型執行推理時,因爲前面模型定義時的修改,在各cuda設備上的子進程中autocast生效 # 在執行loss計算是,在主進程中,autocast生效 with autocast(): output = dp_model(input) loss = loss_fn(output)
torch.nn.parallel.DistributedDataParallel
在官方文檔中推薦每一個GPU執行一個實例的方法,以達到最好的性能表現。
在這種模式下,DistributedDataParallel
內部並不會再啓動子進程,所以對於autocast
和GradScaler
的使用都沒有影響,與典型示例保持一致。
與DataParallel
的使用相同,在模型構建時,對forward函數的定義方式進行修改,保證autocast在進程內部生效。