1. 內存爆炸問題
1.1 loss.item或 float(loss)
首先附上主要代碼
下圖接上圖
上面爲定義的TextCNN模型,下圖爲主要的訓練及預測測試集的過程
下圖接上圖
可以從第三張圖片看到綠框部分有一個tr_loss = float(tr_loss)
。在pytorch中涉及需要求導/梯度變量的計算將保存在內存中,如果不釋放指向計算圖形的指針,這些變量在循環訓練中就會超出你內存。因此千萬不要在循環中累積歷史記錄。
如果不加tr_loss = float(tr_loss)
,tr_loss(可求導變量,一直會保留副本在內存中)就會一直有副本,並保留在內存中,經過多個epoch後,就會撐爆內存。一般釋放副本的方式爲tr_loss.item()
上面提到的tr_loss = float(tr_loss)
也可以,但更推薦使用tr_loss.item()
。如果沒有釋放副本(內存),就會出現以下錯誤:
看似是內存問題,其實使我們代碼寫的有問題。
有時我們需要算累積的loss,也是一樣的方式,如下所示:
total_loss = 0
for i in range(10000):
optimizer.zero_grad()
output = model(input)
loss = criterion(output)
loss.backward()
optimizer.step()
total_loss += loss.item()
仔細想一想,pytorch訓練的時候有一個optimizer.zero_grad()似乎也是釋放計算圖,因此不會撐爆內存
1.2 with torch.no_grad()
第三張圖片還有一個綠框部分即with torch.no_grad()
這個作用也是防止內存爆炸。
模型訓練階段,會存儲一些用於反向傳播的內部信息。當需要由訓練階段(反向傳播)轉至預測階段時,需要使用model.eval()
和with torch.no_grad()
。model.eval()
是告訴計算機,我現在不需要訓練了(反向傳播),我要開始測試了,這時,計算機就會固定住dropout和batch normalization(不會取平均,而是用訓練好的值),簡單理解就是不需要更新參數了,而是用訓練好的參數進行預測。由於不需要更新參數,故不會產生用於反向傳播的內部信息,更不會存儲到內存了。with torch.no_grad()
是爲了釋放訓練過程中產生的用於反向傳播的內部信息,從而釋放內存。不管是對訓練集進行預測,還是對測試集進行預測,都需要使用with torch.no_grad()
2. 如何觀察內存變化
第一章提到內存爆炸,那麼如何觀察內存的變化呢
2.1 cpu
當使用cpu訓練模型時,可在命令行輸入top然後按大寫的M即可看到佔用內存最大的程序。
圖片中紅框部分就是cpu佔用率,說明我們現在使用了7.5%的內存
2.2 GPU
當我們使用cuda時,使用命令watch -n 0.1 nvidia-smi
觀察內存變化
上圖顯示我們共有4塊GPU,一塊大約16G(圖中綠框),目前,我們僅使用了其中一塊GPU,利用率爲96%(紅框部分,該數表示程序96%的部分都在GPU上跑,剩餘4%在cpu跑),目前共佔用GPU的顯存爲4G左右(藍框部分)
3. 多塊GPU(數據並行),lstm參數展平
當我們有多塊GPU,並且全部使用時,訓練lstm,需要進行參數展平的操作,否則會有一堆提示信息(這不算報錯吧,warning)
由於多次循環,滿屏基本都是上圖中的warning了。
那麼怎樣進行參數展平,只需要在lstm cell前加lstm.flatten_parameters(),如下圖所示
這個問題好像有時候出現,有時候不出現。。。且僅使用多塊GPU時出現(如果只使用一塊GPU,還沒有出現過)
4. 多塊GPU與DataLoder
pytroch中有DataLoader,可以將數據劃分成多個batch,DataLodaer有一個參數num_workers,這是pytroch加快速度的一種方法,其允許批量並行加載。所以,你可以一次加載許多批量,而不是一次加載一個。但是當既使用DataLoder又使用多塊GPU時,就可能出現segmentation faultmentation fault
的問題,看了很多博客,大概是DataLoder是多線程,與多塊GPU發生了衝突,故有人建議將num_worker設成1,然而我不管設成1還是0還是會出現segmentation fault的問題。有一篇大神的博客https://meteorix.github.io/2019/04/30/pytorch-coredump/從c++的角度去debug問題,其最終使用了線程加鎖,解決了問題,不過,我嘗試在DataLoder加鎖,還是沒有解決問題(可能是加鎖位置不對)。最終採用了最極端的方式,放棄DataLoder,換用其他方式,最終不再報錯。
由於上述問題是多線程與多GPU衝突的問題,故僅僅使用一塊GPU應該不會報錯,即使使用多塊GPU,上述問題也不一定出現,因爲這個問題是隨機性的。。。我在測試中,有一次程序的確跑通了。
棄用了紅框的DataLoder,換用綠框的方式
5. pytroch訓練及預測部分怎麼寫
回顧第一章的TextCNN,我的代碼是在一個epoch訓練結束後,再對整個訓練集和測試集進行預測,獲得loss和acc並存儲在列表中。此外,還有另外一種寫法。訓練集和測試集都分成很多batch,一個epoch中的每個batch都記錄下loss和acc,當一個epoch結束後,求一個epoch下n個batch的平均值(loss和acc)作爲一個epoch的loss和acc。
參考鏈接:https://blog.csdn.net/weixin_40446557/article/details/103387629