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- 輸入數(shù)據(jù)和標(biāo)簽,計(jì)算loss
- loss.backward() 反向傳播,計(jì)算當(dāng)前梯度
- 多次循環(huán)步驟1-2,不清空梯度,使梯度累加在已有梯度上
- 梯度累積到一定的次數(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博客