梯度累積

1. gradient_accumulation_steps

  • 如果顯存不足,我們可以通過梯度累積(gradient_accumulation_steps)來解決。

  • 假設(shè)原來的batch size=10,數(shù)據(jù)總量為1000,那么一共需要100 train steps,同時(shí)一共進(jìn)行100次梯度更新。
    若是顯存不夠,我們需要減小batch size,我們?cè)O(shè)置gradient_accumulation_steps=2,那么我們新的batch size=10/2=5,我們需要運(yùn)行兩次,才能在內(nèi)存中放入10條數(shù)據(jù),梯度更新的次數(shù)不變?yōu)?00次,那么我們的train steps=200

    1. 輸入數(shù)據(jù)和標(biāo)簽,計(jì)算loss
    1. loss.backward() 反向傳播,計(jì)算當(dāng)前梯度
    1. 多次循環(huán)步驟1-2,不清空梯度,使梯度累加在已有梯度上
    1. 梯度累積到一定的次數(shù)(gradient_accumulation_steps )之后,optimizer.step() 根據(jù)累計(jì)的梯度更新網(wǎng)絡(luò)參數(shù),然后model.zero_grad() 清空之前的梯度,為下一波梯度累加做準(zhǔn)備
  • 梯度累加就是,每次獲取1個(gè)batch的數(shù)據(jù),計(jì)算1次梯度,梯度不清空,不斷累加,累加一定次數(shù)后,根據(jù)累加的梯度更新網(wǎng)絡(luò)參數(shù),然后清空梯度,進(jìn)行下一次循環(huán)

for step, batch in enumerate(tqdm(train_dataloader, desc="Iteration")):
     batch = tuple(t.to(device) for t in batch)
     input_ids, input_mask, segment_ids, label_ids = batch
     outputs = model(input_ids, label_ids, segment_ids, input_mask)
     loss = outputs#r如果沒有調(diào)用任何函數(shù),那么返回的是forward函數(shù)中的返回值
     if n_gpu > 1:
        loss = loss.mean() # mean() to average on multi-gpu.
        if args.gradient_accumulation_steps > 1:##所以loss應(yīng)該是間隔指定梯度累積步的均值
            loss = loss / args.gradient_accumulation_steps
       loss.backward()
       tr_loss += loss.item()##設(shè)置經(jīng)過多少個(gè) 梯度累積步 之后才更新網(wǎng)絡(luò)的參數(shù)
       if (step + 1) % args.gradient_accumulation_steps == 0:#設(shè)定多少batch時(shí)更新神經(jīng)網(wǎng)絡(luò)的參數(shù)
           optimizer.step()
           scheduler.step()  # Update learning rate schedule
           model.zero_grad()
           global_step += 1      
  • 一定條件下,batchsize越大訓(xùn)練效果越好,梯度累加則實(shí)現(xiàn)了batchsize的變相擴(kuò)大,如果accumulation_steps為8,則batchsize '變相' 擴(kuò)大了8倍,是實(shí)驗(yàn)室解決顯存受限的一個(gè)不錯(cuò)的trick,使用時(shí)需要注意,學(xué)習(xí)率也要適當(dāng)放大

  • 如果一個(gè)模型是需要多卡并行訓(xùn)練以開大batchsize,而你沒有這么多卡。那可以利用梯度累加的性質(zhì),在每次反向傳播后,先不進(jìn)行優(yōu)化器的迭代,多累積幾個(gè)batch的梯度后,再進(jìn)行優(yōu)化器迭代、梯度清零的操作。這樣的話,即使使用單卡也可以達(dá)到多卡開大batch_size的效果,雖然訓(xùn)練會(huì)慢一點(diǎn)就是了,但是對(duì)卡的要求大大降低了。

  • 一個(gè)例子:

gradient_accumulation_steps通過累計(jì)梯度來解決本地顯存不足問題。
假設(shè)原來的batch_size=6,樣本總量為24,gradient_accumulation_steps=2
那么參數(shù)更新次數(shù)=24/6=4
現(xiàn)在,減小batch_size=6/2=3,參數(shù)更新次數(shù)不變=24/3/2=4

  • num_train_optimization_steps :模型參數(shù)的總更新次數(shù)
num_train_optimization_steps = int(total_train_examples / args.train_batch_size / args.gradient_accumulation_steps)

  • 在梯度反傳時(shí),每gradient_accumulation_steps次進(jìn)行一次梯度更新,之前照常利用loss.backward()計(jì)算梯度。

一般說batch_size越大越好,現(xiàn)在降低為batch_size=3 的話,一共24個(gè)樣本,需要每?jī)蓚€(gè)batch 更新一次參數(shù),因?yàn)橐肓颂荻壤鄯e,
3+3——>1
3+3——>1
3+3——>1
3+3——>1
總共更新4次數(shù)
如果batch_size=6,沒有梯度累計(jì)引入,那么每個(gè)batch 更新一次梯度:
6——>1
6——>1
6——>1
6——>1

  • 同樣更新4次參數(shù),這樣對(duì)比起來,引入梯度了累積trick,參數(shù)更新次數(shù)不會(huì)改變,但是可以達(dá)到類似于 增大了 batch_size 的效果,因?yàn)橐话銇碚f是 每個(gè)batch 更新一次網(wǎng)絡(luò)參數(shù),上面是兩個(gè)batch 一起才更新,類似于是一次batch=6的效果,即增大了batch.因?yàn)轱@存不夠的時(shí)候通常需要將batch_size 減小,一般就減小為 batch_size/gradient_accumulation_steps,因?yàn)榭梢园凑丈鲜龅囊?guī)則達(dá)到?jīng)]有減小batch_size 的效果.

參考:
gradient_accumulation_steps
gradient_accumulation_steps-CSDN博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容