日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

深度神经网络模型训练时GPU显存不足怎么办?

發(fā)布時間:2023/12/8 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深度神经网络模型训练时GPU显存不足怎么办? 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

最近跑的模型都比較大,尤其是Bert, 這真的是難為我 1080ti 了, 在Bert的Example中,官方提供了一些 Trick 來幫助我們加速訓(xùn)練,很良心, 但感覺還不夠,于是花費一些時間整理出一個 Trick 集合,來幫助我們在顯存不足的時候來嘿嘿嘿。

本文分為兩大部分,第一部分引入一個主題:如何估計模型所需顯存, 第二個主題:GPU顯存不足時的各種 Trick 。

監(jiān)控 GPU

監(jiān)控GPU最常用的當(dāng)然是 nvidia-smi ,但有一個工具能夠更好的展示信息:gpustat 。

nvidia-smi watch --color -n1 gpustat -cpu # 動態(tài)事實監(jiān)控GPU

推薦在配置文件中配置別名,反正我每次 gpu 一下,信息就全出來了,很方便。

下面有同學(xué)推薦nvtop, 我簡單試了試,的確挺好的,展現(xiàn)出現(xiàn)的信息很豐富 , 推薦試一試。

如何估計模型顯存 [1]
首先,思考一個問題: 模型中的哪些東西占據(jù)了我的顯存,咋就動不動就 out of memory?

其實一個模型所占用的顯存主要包含兩部分: 模型自身的參數(shù), 優(yōu)化器參數(shù), 模型每層的輸入輸出。

模型自身參數(shù)

模型自身的參數(shù)指的就是各個網(wǎng)絡(luò)層的 Weight 和Bias,這部分顯存在模型加載完成之后就會被占用, 注意到的是,有些層是有參數(shù)的,如CNN, RNN; 而有些層是無參數(shù)的, 如激活層, 池化層等。

從Pytorch 的角度來說,當(dāng)你執(zhí)行 model.to(device) 是, 你的模型就加載完畢,此時你的模型就已經(jīng)加載完成了。

對于Pytorch來說,模型參數(shù)存儲在 model.parameters() 中,因此,我們不需要自己計算,完全可以通過Pytorh來直接打印:

print('Model {} : params: {:4f}M'.format(model._get_name(), para * type_size / 1000 / 1000))

優(yōu)化器參數(shù)

優(yōu)化器參數(shù)指的是模型在優(yōu)化過程即反向傳播中所產(chǎn)生的參數(shù), 這部分參數(shù)主要指的就是 dw, 即梯度,在SGD中, 其大小與參數(shù)一樣, 因此在優(yōu)化期間, 模型的參數(shù)所占用的顯存會翻倍。

值得注意的是,不同的優(yōu)化器其所需保存的優(yōu)化參數(shù)不同, 對于 Adam, 由于其還需要保存其余參數(shù), 模型的參數(shù)量會在優(yōu)化區(qū)間翻 4 倍。

模型每層的輸入輸出

首先,第一點是輸入數(shù)據(jù)所占用的顯存, 這部分所占用的顯存其實并不大,這是因為我們往往采用迭代器的方式讀取數(shù)據(jù),這意味著我們其實并不是一次性的將所有數(shù)據(jù)讀入顯存,而這保證每次輸入所占用的顯存與整個網(wǎng)絡(luò)參數(shù)來比是微不足道的。

然后,在模型進行前向傳播與反向傳播時, 一個很重要的事情就是計算并保存每一層的輸出以及其對應(yīng)的梯度, 這意味著,這也占據(jù)了很大一部分顯存。

最后, 模型輸出的顯存占用可以總結(jié)為:

  • 每一層的輸出(多維數(shù)組), 其對應(yīng)的梯度, 值得注意的是,模型輸出不需要存儲相應(yīng)的動量信息(即此處如果使用Adam, 模型輸出的參數(shù)量依舊是2倍而不是4倍, 我也不知道為啥??求大佬指教)
  • 輸出的顯存占用與 batch size 成正比

那么有沒有辦法通過Pytorch來計算這部分參數(shù)量呢? 答案是有的,我們可以假設(shè)一個batch的樣本,然后通過 model.modules() 來對每一層進行遍歷,獲得每一層的輸出shape, 然后就能夠獲得一個batch的數(shù)據(jù)的輸出參數(shù)量。[2]

所有的顯存占用計算

顯存占用 = 模型自身參數(shù) × n + batch size × 輸出參數(shù)量 × 2 + 一個batch的輸入數(shù)據(jù)(往往忽略)

其中,n是根據(jù)優(yōu)化算法來定的,如果選用SGD, 則 n = 2, 如果選擇Adam, 則 n = 4.

一個很棒的實現(xiàn)如下, 我懶得再重新寫了,你可以根據(jù)這個改一改,問題不大。

# 模型顯存占用監(jiān)測函數(shù) # model:輸入的模型 # input:實際中需要輸入的Tensor變量 # type_size 默認為 4 默認類型為 float32 def modelsize(model, input, type_size=4):para = sum([np.prod(list(p.size())) for p in model.parameters()])print('Model {} : params: {:4f}M'.format(model._get_name(), para * type_size / 1000 / 1000))input_ = input.clone()input_.requires_grad_(requires_grad=False)mods = list(model.modules())out_sizes = []for i in range(1, len(mods)):m = mods[i]if isinstance(m, nn.ReLU):if m.inplace:continueout = m(input_)out_sizes.append(np.array(out.size()))input_ = outtotal_nums = 0for i in range(len(out_sizes)):s = out_sizes[i]nums = np.prod(np.array(s))total_nums += numsprint('Model {} : intermedite variables: {:3f} M (without backward)'.format(model._get_name(), total_nums * type_size / 1000 / 1000))print('Model {} : intermedite variables: {:3f} M (with backward)'.format(model._get_name(), total_nums * type_size*2 / 1000 / 1000))

GPU 顯存不足時的Trick [2]

此處不討論多GPU, 分布式計算等情況,只討論一些常規(guī)的 Trick, 會不定時進行更新。

降低batch size

這應(yīng)該很好理解,適當(dāng)降低batch size, 則模型每層的輸入輸出就會成線性減少, 效果相當(dāng)明顯。這里需要注意的一點是, dev batch size 的調(diào)整也有助于降低顯存, 同時,不要將 dev 或 test 的batch size 設(shè)置為樣本集長度, 我最近就干了這個傻事,害的我調(diào)試了一天才調(diào)出來是這個問題。

選擇更小的數(shù)據(jù)類型

一般默認情況下, 整個網(wǎng)絡(luò)中采用的是32位的浮點數(shù),如果切換到 16位的浮點數(shù),其顯存占用量將接近呈倍數(shù)遞減。

精簡模型

在設(shè)計模型時,適當(dāng)?shù)木喣P?#xff0c;如原來兩層的LSTM轉(zhuǎn)為一層; 原來使用LSTM, 現(xiàn)在使用GRU; 減少卷積核數(shù)量; 盡量少的使用 Linear 等。

數(shù)據(jù)角度

對于文本數(shù)據(jù)來說,長序列所帶來的參數(shù)量是呈線性增加的, 適當(dāng)?shù)目s小序列長度可以極大的降低參數(shù)量。

total_loss

考慮到 loss 本身是一個包含梯度信息的 tensor, 因此,正確的求損失和的方式為:

total_loss += loss.item()

釋放不需要的張量和變量

采用del釋放你不再需要的張量和變量,這也要求我們在寫模型的時候注意變量的使用,不要隨心所欲,漫天飛舞。

Relu 的 inplace 參數(shù)

激活函數(shù) Relu() 有一個默認參數(shù) inplace ,默認為Flase, 當(dāng)設(shè)置為True的時候,我們在通過relu() 計算得到的新值不會占用新的空間而是直接覆蓋原來的值,這表示設(shè)為True, 可以節(jié)省一部分顯存。

梯度累積

首先, 要了解一些Pytorch的基本知識:

  • 在Pytorch 中,當(dāng)我們執(zhí)行 loss.backward() 時, 會為每個參數(shù)計算梯度,并將其存儲在 paramter.grad 中, 注意到, paramter.grad 是一個張量, 其會累加每次計算得到的梯度。
  • 在 Pytorch 中, 只有調(diào)用 optimizer.step()時才會進行梯度下降更新網(wǎng)絡(luò)參數(shù)。

我們知道, batch size 與占用顯存息息相關(guān),但有時候我們的batch size 又不能設(shè)置的太小,這咋辦呢? 答案就是梯度累加

我們先來看看傳統(tǒng)訓(xùn)練:

for i,(feature,target) in enumerate(train_loader):outputs = model(feature) # 前向傳播loss = criterion(outputs,target) # 計算損失optimizer.zero_grad() # 清空梯度loss.backward() # 計算梯度optimizer.step() # 反向傳播, 更新網(wǎng)絡(luò)參數(shù)

而加入梯度累加之后,代碼是這樣的:

for i,(features,target) in enumerate(train_loader):outputs = model(images) # 前向傳播loss = criterion(outputs,target) # 計算損失loss = loss/accumulation_steps # 可選,如果損失要在訓(xùn)練樣本上取平均loss.backward() # 計算梯度if((i+1)%accumulation_steps)==0:optimizer.step() # 反向傳播,更新網(wǎng)絡(luò)參數(shù)optimizer.zero_grad() # 清空梯度

比較來看, 我們發(fā)現(xiàn),梯度累加本質(zhì)上就是累加 accumulation_steps 個batch 的梯度, 再根據(jù)累加的梯度來更新網(wǎng)絡(luò)參數(shù),以達到類似batch_size 為 accumulation_steps * batch_size 的效果。在使用時,需要注意適當(dāng)?shù)臄U大學(xué)習(xí)率。

更詳細來說, 我們假設(shè) batch size = 32, accumulation steps = 8 , 梯度積累首先在前向傳播的時候講 batch 分為 accumulation steps 份, 然后得到 size=4 的小份batch , 每次就以小 batch 來計算梯度,但是不更新參數(shù),將梯度積累下來,直到我們計算了 accumulation steps 個小 batch, 我們再更新參數(shù)。

梯度積累能很大程度上緩解GPU顯存不足的問題,推薦使用。

在Bert的倉庫中,就使用了這個Trick,十分實用,簡直是我們這種乞丐實驗室的良心Trick。

梯度檢查點

這個Trick我沒用過,畢竟模型還沒有那么那么大。

等我用過再更新吧,先把坑挖下。

最后

哎, 如果你看完了這篇文章,就說明了一件事情: **小伙子,你卡也不夠啊。**哎, 乞丐實驗室不配深度學(xué)習(xí),哭了。

Reference

[1]科普帖:深度學(xué)習(xí)中GPU和顯存分析

[2]如何在Pytorch中精細化利用顯存

[3]GPU捉襟見肘還想訓(xùn)練大批量模型?誰說不可以

[4]PyTorch中在反向傳播前為什么要手動將梯度清零?

[5]From zero to research — An introduction to Meta-learning

[6]Training Neural Nets on Larger Batches: Practical Tips for 1-GPU, Multi-GPU & Distributed setups

總結(jié)

以上是生活随笔為你收集整理的深度神经网络模型训练时GPU显存不足怎么办?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。