參考: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估算出來的均值和方差肯定更精准一些。