梯度累加實現 「視訊記憶體擴大「

2021-10-14 06:40:21 字數 4618 閱讀 8323

pytorch中在反向傳播前為什麼要手動將梯度清零? - pascal的回答 - 知乎

這種模式可以讓梯度玩出更多花樣,比如說梯度累加(gradient accumulation)

傳統的訓練函式,乙個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

() # reset gradient

loss.

backward()

optimizer.

step

()

獲取loss:輸入影象和標籤,通過infer計算得到**值,計算損失函式;

optimizer.zero_grad()清空過往梯度;

loss.backward()反向傳播,計算當前梯度

optimizer.step()根據梯度更新網路引數

簡單的說就是進來乙個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.1 loss regularization

loss = loss/accumulation_steps

# 2.2 back propagation

loss.

backward()

# 3. update parameters of net

if((i+1)

%accumulation_steps)==0

:# optimizer the net

optimizer.

step

() # update parameters of net

optimizer.

zero_grad

() # reset gradient

獲取loss:輸入影象和標籤,通過infer計算得到**值,計算損失函式;

loss.backward()?反向傳播,計算當前梯度

多次迴圈步驟1-2,不清空梯度,使梯度累加在已有梯度上;

梯度累加了一定次數後,先optimizer.step()?根據累計的梯度更新網路引數,然後optimizer.zero_grad()?清空過往梯度,為下一波梯度累加做準備;

總結來說:梯度累加就是,每次獲取1個batch的資料,計算1次梯度,梯度不清空,不斷累加,累加一定次數後,根據累加的梯度更新網路引數,然後清空梯度,進行下一次迴圈。

一定條件下,batchsize越大訓練效果越好,梯度累加則實現了batchsize的變相擴大,如果accumulation_steps?為8,則batchsize 『變相『 擴大了8倍,是我們這種乞丐實驗室解決視訊記憶體受限的乙個不錯的trick,使用時需要注意,學習率也要適當放大。

更新1:關於bn是否有影響,之前有人是這麼說的:

as far as i know, batch norm statistics get updated on each forward pass, so no problem if you don『t do.backward()?every time.

bn的估算是在forward階段就已經完成的,並不衝突,只是accumulation_steps=8?和真實的batchsize放大八倍相比,效果自然是差一些,畢竟八倍batchsize的bn估算出來的均值和方差肯定更精準一些。

更新2:根據李韶華的分享,可以適當調低bn自己的momentum引數:

bn自己有個momentum引數:x_new_running = (1 - momentum) * x_running + momentum * x_new_observed. momentum越接近0,老的running stats記得越久,所以可以得到更長序列的統計資訊

我簡單看了下pytorch 1.0的原始碼:類裡面momentum這個屬性預設為0.1,可以嘗試調節下。

pytorch中在反向傳播前為什麼要手動將梯度清零? - forever123的回答 - 知乎

原因在於在pytorch中,計算得到的梯度值會進行累加。

而這樣的好處可以從記憶體消耗的角度來看。

在pytorch中,multi-task任務乙個標準的train from scratch流程為:

for idx, data in enumerate

(train_loader)

: xs, ys = data

pred1 =

model1

(xs)

pred2 =

model2

(xs)

loss1 =

loss_fn1

(pred1, ys)

loss2 =

loss_fn2

(pred2, ys)**

****

loss = loss1 + loss2

optmizer.

zero_grad()

loss.

backward()

++++

++ optmizer.

step

()

從pytorch的設計原理上來說,在每次進行前向計算得到pred時,會產生乙個用於梯度回傳的計算圖,這張圖儲存了進行back propagation需要的中間結果,當呼叫了**.backward()**?後,會從記憶體中將這張圖進行釋放。為了減小每次的記憶體消耗,借助梯度累加,又有

for idx, data in enumerate

(train_loader)

: xs, ys = data

optmizer.

zero_grad()

# 計算d

(l1)/d

(x) pred1 =

model1

(xs) #生成graph1

loss =

loss_fn1

(pred1, ys)

loss.

backward

() #釋放graph1

# 計算d

(l2)/d

(x) pred2 =

model2

(xs)#生成graph2

loss2 =

loss_fn2

(pred2, ys)

loss.

backward

() #釋放graph2

# 使用d

(l1)/d

(x)+

d(l2)/d

(x)進行優化

optmizer.

step

()

可以從**中看出,利用梯度累加,可以在最多儲存一張計算圖的情況下進行multi-task任務的訓練。

另外乙個理由就是在記憶體大小不夠的情況下疊加多個batch的grad作為乙個大batch進行迭代,因為二者得到的梯度是等價的。

綜上可知,這種梯度累加的思路是對記憶體的極大友好,是由fair的設計理念出發的。

TF實現多minibatch梯度累加及反向更新

tf中optimizor原始碼 如何累加梯度進行反向 在顯示卡資源有限的情況下,想要增加batch size的大小,於是可以想到將乙個大的batch分解成許多的mini batch前傳,並同時將梯度累加,達到batch size大小之後反向跟新梯度。def minimize self,loss,gl...

pytorch梯度累加反向傳播

傳統的訓練函式,乙個batch是這麼訓練的 for i,images,target in enumerate train loader 1.input output images images.cuda non blocking true target torch.from numpy np.arr...

矩陣累加(指標實現)

描述 程式設計計算k個整型矩陣的累加,並輸出結果。若給定的矩陣不能相加,則直接終止程式並輸出 matrix can t add 若計算機記憶體不足以計算給定矩陣的累加,則直接終止程式並輸出 not enough memory 輸入 第1行輸入1個整數k,表示待累加矩陣的個數。第2行輸入2個整數,表示...