梯度累加實現 “顯存擴大"


參考:PyTorch中在反向傳播前為什么要手動將梯度清零? - Pascal的回答 - 知乎
pytorch會在每一次backward()后進行梯度計算,但是梯度不會自動歸零,如果不進行手動歸零的話,梯度會不斷累加

1.1 傳統的訓練一個 batch 的流程如下:

for i, (images, target) in enumerate(train_loader):
    # 1. input output
    images = images.cuda(non_blocking=True)
    target = torch.from_numpy(np.array(target)).float().cuda(non_blocking=True)
    outputs = model(images)
    loss = criterion(outputs, target)
    
    # 2. backward
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
  • 獲取loss: 輸入圖像和標簽,通過infer計算得到預測值,計算損失函數
  • optimizer.zero_grad()清空過往梯度
  • loss.backward()反向傳播,計算當前梯度
  • optimizer.step()根據梯度更新網絡參數

即進來一個batch的數據,就計算一次梯度,更新一次網絡

1.2 使用梯度累加

for i, (images, target) in enumerate(train_loader):
    # 1. input output
    images = images.cuda(non_blocking=True)
    target = torch.from_numpy(np.array(target)).float().cuda(non_blocking=True)
    outputs = model(imgaes)
    loss = criterion(outputs, target)

    # 2.1 loss regularization
    loss = loss / accumulation_steps # loss每次都會更新,因此每次都除以steps再加到原來的梯度上面去
    
    # 2.2 backward propagation
    loss.backward()

    # 3. update parameters of net
    if ((i+1)%accumulation)==0:
        # optimizer the net
        optimizer.step()
        optimizer.zero_grad() # reset grdient
  • 獲取loss: 輸入圖像和標簽,通過infer計算得到預測值,計算損失函數
  • loss.backward()反向傳播,計算當前梯度
  • 多次循環步驟 1-2, 不清空梯度,使梯度累加在已有梯度上
  • 梯度累加一定次數后,先optimizer.step()根據累積的梯度更新網絡參數,然后optimizer.zero_grad()清空過往梯度,為下一波梯度累加做准備

總結來說:梯度累加就是,每次獲取1個batch的數據,計算1次梯度,梯度不清空,不斷累加,累加一定次數后,根據累加的梯度更新網絡參數,然后清空梯度,進行下一次循環。
一定條件下,batchsize越大訓練效果越好,梯度累加則實現了batchsize的變相擴大,如果accumulation_steps為8,則batchsize '變相' 擴大了8倍,是我們這種乞丐實驗室解決顯存受限的一個不錯的trick,使用時需要注意,學習率也要適當放大。
BN的估算是在forward階段就已經完成的,並不沖突,只是accumulation_steps=8和真實的batchsize放大八倍相比,效果自然是差一些,畢竟八倍Batchsize的BN估算出來的均值和方差肯定更精准一些。


免責聲明!

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



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