Pytorch原生AMP支持使用方法(1.6版本)


AMP:Automatic mixed precision,自動混合精度,可以在神經網絡推理過程中,針對不同的層,采用不同的數據精度進行計算,從而實現節省顯存和加快速度的目的。

在Pytorch 1.5版本及以前,通過NVIDIA出品的插件apex,可以實現amp功能。

從Pytorch 1.6版本以后,Pytorch將amp的功能吸收入官方庫,位於torch.cuda.amp模塊下。

本文為針對官方文檔主要內容的簡要翻譯和自己的理解。

1. Introduction

torch.cuda.amp提供了對混合精度的支持。為實現自動混合精度訓練,需要結合使用如下兩個模塊:

2. Typical Mixed Precision Training

一個典型的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()

3. Working with Unscaled Gradients

待更新

4. Working with Scaled Gradients

待更新

5. Working with Multiple Models, Losses, and Optimizers

如果模型的Loss計算部分輸出多個loss,需要對每一個loss值執行scaler.scale

如果網絡具有多個優化器,對任一個優化器執行scaler.unscale_,並對每一個優化器執行scaler.step

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()

6. Working with Multiple GPUs

針對多卡訓練的情況,只影響autocast的使用方法,GradScaler的用法與之前一致。

6.1 DataParallel in a single process

在每一個不同的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)

6.2 DistributedDataParallel, one GPU per process

torch.nn.parallel.DistributedDataParallel在官方文檔中推薦每個GPU執行一個實例的方法,以達到最好的性能表現。

在這種模式下,DistributedDataParallel內部並不會再啟動子進程,因此對於autocastGradScaler的使用都沒有影響,與典型示例保持一致。

6.3 DistributedDataParallel, multiple GPUs per process

DataParallel 的使用相同,在模型構建時,對forward函數的定義方式進行修改,保證autocast在進程內部生效。


免責聲明!

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



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