Task02 PyTorch進階訓(xùn)練技巧

參考鏈接:https://github.com/datawhalechina/thorough-pytorch

本task注重于pytorch在實際使用中的一些操作~較為實用

1.自定義損失函數(shù)

PyTorch在torch.nn模塊為我們提供了許多常用的損失函數(shù),比如:MSELoss,L1Loss,BCELoss...... 但是隨著深度學(xué)習(xí)的發(fā)展,出現(xiàn)了越來越多的非官方提供的Loss,比如DiceLoss,HuberLoss,SobolevLoss...... 這些Loss Function專門針對一些非通用的模型,PyTorch不能將他們?nèi)刻砑拥綆熘腥?,因此這些損失函數(shù)的實現(xiàn)則需要我們通過自定義損失函數(shù)來實現(xiàn)。另外,在科學(xué)研究中,我們往往會提出全新的損失函數(shù)來提升模型的表現(xiàn),這時我們既無法使用PyTorch自帶的損失函數(shù),也沒有相關(guān)的博客供參考,此時自己實現(xiàn)損失函數(shù)就顯得更為重要了。

1.1 以函數(shù)方式定義

損失函數(shù)定義

1.2 以類方式定義

雖然以函數(shù)定義的方式很簡單,但是以類方式定義更加常用,在以類方式定義損失函數(shù)時,我們?nèi)绻疵恳粋€損失函數(shù)的繼承關(guān)系我們就可以發(fā)現(xiàn)Loss函數(shù)部分繼承自_loss, 部分繼承自_WeightedLoss, 而_WeightedLoss繼承自_loss,?_loss繼承自?nn.Module。我們可以將其當(dāng)作神經(jīng)網(wǎng)絡(luò)的一層來對待,同樣地,我們的損失函數(shù)類就需要繼承自nn.Module類,下面以DiceLoss為例。

Dice Loss函數(shù)

除此之外,常見的損失函數(shù)還有BCE-Dice Loss,Jaccard/Intersection over Union (IoU) Loss,F(xiàn)ocal Loss等。

BCE-Dice Loss
Jaccard/Intersection over Union (IoU)
Focal Loss

Focal loss主要是為了解決one-stage目標(biāo)檢測中正負(fù)樣本比例嚴(yán)重失衡的問題。該損失函數(shù)降低了大量簡單負(fù)樣本在訓(xùn)練中所占的權(quán)重,也可理解為一種困難樣本挖掘。是 Kaiming 大神團隊在論文Focal Loss for Dense Object Detection?提出來的損失函數(shù),利用它改善了圖像物體檢測的效果。

2.動態(tài)調(diào)整學(xué)習(xí)率

學(xué)習(xí)率的選擇是深度學(xué)習(xí)中一個困擾人們許久的問題,學(xué)習(xí)速率設(shè)置過小,會極大降低收斂速度,增加訓(xùn)練時間;學(xué)習(xí)率太大,可能導(dǎo)致參數(shù)在最優(yōu)解兩側(cè)來回振蕩。但是當(dāng)我們選定了一個合適的學(xué)習(xí)率后,經(jīng)過許多輪的訓(xùn)練后,可能會出現(xiàn)準(zhǔn)確率震蕩或loss不再下降等情況,說明當(dāng)前學(xué)習(xí)率已不能滿足模型調(diào)優(yōu)的需求。此時我們就可以通過一個適當(dāng)?shù)膶W(xué)習(xí)率衰減策略來改善這種現(xiàn)象,提高我們的精度。這種設(shè)置方式在PyTorch中被稱為scheduler。

2.1 使用官方scheduler

在訓(xùn)練神經(jīng)網(wǎng)絡(luò)的過程中,學(xué)習(xí)率是最重要的超參數(shù)之一,作為當(dāng)前較為流行的深度學(xué)習(xí)框架,PyTorch已經(jīng)在torch.optim.lr_scheduler為我們封裝好了一些動態(tài)調(diào)整學(xué)習(xí)率的方法供我們使用,如下面列出的這些scheduler。

官方scheduler

關(guān)于如何使用這些動態(tài)調(diào)整學(xué)習(xí)率的策略,PyTorch官方也很人性化的給出了使用實例代碼,我們在使用官方給出的torch.optim.lr_scheduler時,需要將scheduler.step()放在optimizer.step()后面進行使用。

使用官方API

2.2?自定義scheduler

雖然PyTorch官方給我們提供了許多的API,但是在實驗中也有可能碰到需要我們自己定義學(xué)習(xí)率調(diào)整策略的情況,而我們的方法是自定義函數(shù)adjust_learning_rate來改變param_group中l(wèi)r的值,在下面的敘述中會給出一個簡單的實現(xiàn)。

假設(shè)我們現(xiàn)在正在做實驗,需要學(xué)習(xí)率每30輪下降為原來的1/10,假設(shè)已有的官方API中沒有符合我們需求的,那就需要自定義函數(shù)來實現(xiàn)學(xué)習(xí)率的改變。

自定義scheduler

3.模型微調(diào)

參考鏈接:https://blog.csdn.net/qq_42250789/article/details/108832004

微調(diào)定義:?給定預(yù)訓(xùn)練模型(Pre_trained model),基于模型進行微調(diào)(Fine Tune)。相對于從頭開始訓(xùn)練(Training a model from scatch),微調(diào)為你省去大量計算資源和計算時間,提高了計算效率,甚至提高準(zhǔn)確率。

預(yù)訓(xùn)練模型:(1) 預(yù)訓(xùn)練模型就是已經(jīng)用數(shù)據(jù)集訓(xùn)練好了的模型。(2) 現(xiàn)在我們常用的預(yù)訓(xùn)練模型就是他人用常用模型,比如VGG16/19,Resnet等模型,并用大型數(shù)據(jù)集來做訓(xùn)練集,比如Imagenet, COCO等訓(xùn)練好的模型參數(shù)。 (3)? 正常情況下,我們常用的VGG16/19等網(wǎng)絡(luò)已經(jīng)是他人調(diào)試好的優(yōu)秀網(wǎng)絡(luò),我們無需再修改其網(wǎng)絡(luò)結(jié)構(gòu)。

不同數(shù)據(jù)集下使用微調(diào):

數(shù)據(jù)集1 - 數(shù)據(jù)量少,但數(shù)據(jù)相似度非常高 - 在這種情況下,我們所做的只是修改最后幾層或最終的softmax圖層的輸出類別。

數(shù)據(jù)集2 - 數(shù)據(jù)量少,數(shù)據(jù)相似度低 - 在這種情況下,我們可以凍結(jié)預(yù)訓(xùn)練模型的初始層(比如k層),并再次訓(xùn)練剩余的(n-k)層。由于新數(shù)據(jù)集的相似度較低,因此根據(jù)新數(shù)據(jù)集對較高層進行重新訓(xùn)練具有重要意義。

數(shù)據(jù)集3? - 數(shù)據(jù)量大,數(shù)據(jù)相似度低 - 在這種情況下,由于我們有一個大的數(shù)據(jù)集,我們的神經(jīng)網(wǎng)絡(luò)訓(xùn)練將會很有效。但是,由于我們的數(shù)據(jù)與用于訓(xùn)練我們的預(yù)訓(xùn)練模型的數(shù)據(jù)相比有很大不同。使用預(yù)訓(xùn)練模型進行的預(yù)測不會有效。因此,最好根據(jù)你的數(shù)據(jù)從頭開始訓(xùn)練神經(jīng)網(wǎng)絡(luò)(Training from scatch)

數(shù)據(jù)集4? - 數(shù)據(jù)量大,數(shù)據(jù)相似度高 - 這是理想情況。在這種情況下,預(yù)訓(xùn)練模型應(yīng)該是最有效的。使用模型的最好方法是保留模型的體系結(jié)構(gòu)和模型的初始權(quán)重。然后,我們可以使用在預(yù)先訓(xùn)練的模型中的權(quán)重來重新訓(xùn)練該模型。

3.1 模型微調(diào)的流程

在源數(shù)據(jù)集(如ImageNet數(shù)據(jù)集)上預(yù)訓(xùn)練一個神經(jīng)網(wǎng)絡(luò)模型,即源模型。

創(chuàng)建一個新的神經(jīng)網(wǎng)絡(luò)模型,即目標(biāo)模型。它復(fù)制了源模型上除了輸出層外的所有模型設(shè)計及其參數(shù)。我們假設(shè)這些模型參數(shù)包含了源數(shù)據(jù)集上學(xué)習(xí)到的知識,且這些知識同樣適用于目標(biāo)數(shù)據(jù)集。我們還假設(shè)源模型的輸出層跟源數(shù)據(jù)集的標(biāo)簽緊密相關(guān),因此在目標(biāo)模型中不予采用。

為目標(biāo)模型添加一個輸出?小為?標(biāo)數(shù)據(jù)集類別個數(shù)的輸出層,并隨機初始化該層的模型參數(shù)。

在目標(biāo)數(shù)據(jù)集上訓(xùn)練目標(biāo)模型。我們將從頭訓(xùn)練輸出層,而其余層的參數(shù)都是基于源模型的參數(shù)微調(diào)得到的。

3.2 使用已有模型結(jié)構(gòu)

這里我們以torchvision中的常見模型為例,列出了如何在圖像分類任務(wù)中使用PyTorch提供的常見模型結(jié)構(gòu)和參數(shù)。對于其他任務(wù)和網(wǎng)絡(luò)結(jié)構(gòu),使用方式是類似的:

實例化網(wǎng)格:

實例化網(wǎng)格

傳遞pretrained參數(shù):通過True或者False來決定是否使用預(yù)訓(xùn)練好的權(quán)重,在默認(rèn)狀態(tài)下pretrained = False,意味著我們不使用預(yù)訓(xùn)練得到的權(quán)重,當(dāng)pretrained = True,意味著我們將使用在一些數(shù)據(jù)集上預(yù)訓(xùn)練得到的權(quán)重。

傳遞pretrained參數(shù)

注意事項:

通常PyTorch模型的擴展為.pt或.pth,程序運行時會首先檢查默認(rèn)路徑中是否有已經(jīng)下載的模型權(quán)重,一旦權(quán)重被下載,下次加載就不需要下載了。

一般情況下預(yù)訓(xùn)練模型的下載會比較慢,我們可以直接通過迅雷或者其他方式去?這里?查看自己的模型里面model_urls,然后手動下載,預(yù)訓(xùn)練模型的權(quán)重在Linux和Mac的默認(rèn)下載路徑是用戶根目錄下的.cache文件夾。在Windows下就是C:\Users\<username>\.cache\torch\hub\checkpoint。我們可以通過使用?torch.utils.model_zoo.load_url()設(shè)置權(quán)重的下載地址。

如果覺得麻煩,還可以將自己的權(quán)重下載下來放到同文件夾下,然后再將參數(shù)加載網(wǎng)絡(luò)。

self.model=models.resnet50(pretrained=False)

self.model.load_state_dict(torch.load('./model/resnet50-19c8e357.pth'))

如果中途強行停止下載的話,一定要去對應(yīng)路徑下將權(quán)重文件刪除干凈,要不然可能會報錯。

3.3 訓(xùn)練特定層

在默認(rèn)情況下,參數(shù)的屬性.requires_grad = True,如果我們從頭開始訓(xùn)練或微調(diào)不需要注意這里。但如果我們正在提取特征并且只想為新初始化的層計算梯度,其他參數(shù)不進行改變。那我們就需要通過設(shè)置requires_grad = False來凍結(jié)部分層。在PyTorch官方中提供了這樣一個例程。

在下面我們?nèi)耘f使用resnet18為例的將1000類改為4類,但是僅改變最后一層的模型參數(shù),不改變特征提取的模型參數(shù);注意我們先凍結(jié)模型參數(shù)的梯度,再對模型輸出部分的全連接層進行修改,這樣修改后的全連接層的參數(shù)就是可計算梯度的。

之后在訓(xùn)練過程中,model仍會進行梯度回傳,但是參數(shù)更新則只會發(fā)生在fc層。通過設(shè)定參數(shù)的requires_grad屬性,我們完成了指定訓(xùn)練模型的特定層的目標(biāo),這對實現(xiàn)模型微調(diào)非常重要。

4. 半精度訓(xùn)練

我們提到PyTorch時候,總會想到要用硬件設(shè)備GPU的支持,也就是“卡”。GPU的性能主要分為兩部分:算力和顯存,前者決定了顯卡計算的速度,后者則決定了顯卡可以同時放入多少數(shù)據(jù)用于計算。在可以使用的顯存數(shù)量一定的情況下,每次訓(xùn)練能夠加載的數(shù)據(jù)更多(也就是batch size更大),則也可以提高訓(xùn)練效率。另外,有時候數(shù)據(jù)本身也比較大(比如3D圖像、視頻等),顯存較小的情況下可能甚至batch size為1的情況都無法實現(xiàn)。因此,合理使用顯存也就顯得十分重要。

我們觀察PyTorch默認(rèn)的浮點數(shù)存儲方式用的是torch.float32,小數(shù)點后位數(shù)更多固然能保證數(shù)據(jù)的精確性,但絕大多數(shù)場景其實并不需要這么精確,只保留一半的信息也不會影響結(jié)果,也就是使用torch.float16格式。由于數(shù)位減了一半,因此被稱為“半精度”。

顯然半精度能夠減少顯存占用,使得顯卡可以同時加載更多數(shù)據(jù)進行計算。本節(jié)會介紹如何在PyTorch中設(shè)置使用半精度計算。

4.1?半精度訓(xùn)練的設(shè)置

在PyTorch中使用autocast配置半精度訓(xùn)練,同時需要在下面三處加以設(shè)置:

注意:

半精度訓(xùn)練主要適用于數(shù)據(jù)本身的size比較大(比如說3D圖像、視頻等)。當(dāng)數(shù)據(jù)本身的size并不大時(比如手寫數(shù)字MNIST數(shù)據(jù)集的圖片尺寸只有28*28),使用半精度訓(xùn)練則可能不會帶來顯著的提升。

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

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

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