快速上手笔记,PyTorch模型训练实用教程(附代码)
機器之心發布
作者:余霆嵩
前言
自 2017 年 1 月 PyTorch 推出以來,其熱度持續上升,一度有趕超 TensorFlow 的趨勢。PyTorch 能在短時間內被眾多研究人員和工程師接受并推崇是因為其有著諸多優點,如采用 Python 語言、動態圖機制、網絡構建靈活以及擁有強大的社群等。因此,走上學習 PyTorch 的道路已刻不容緩。
本教程以實際應用、工程開發為目的,著重介紹模型訓練過程中遇到的實際問題和方法。如上圖所示,在機器學習模型開發中,主要涉及三大部分,分別是數據、模型和損失函數及優化器。本文也按順序的依次介紹數據、模型和損失函數及優化器,從而給大家帶來清晰的機器學習結構。
通過本教程,希望能夠給大家帶來一個清晰的模型訓練結構。當模型訓練遇到問題時,需要通過可視化工具對數據、模型、損失等內容進行觀察,分析并定位問題出在數據部分?模型部分?還是優化器?只有這樣不斷的通過可視化診斷你的模型,不斷的對癥下藥,才能訓練出一個較滿意的模型。
為什么寫此教程
前幾年一直在用 Caffe 和 MatConvNet,近期轉 PyTorch。當時只想快速地用上 PyTorch 進行模型開發,然而搜了一圈 PyTorch 的教程,并沒有找到一款適合的。很多 PyTorch 教程是從學習機器學習 (深度學習) 的角度出發,以 PyTorch 為工具進行編寫,里面介紹很多模型,并且附上模型的 demo。
然而,工程應用開發中所遇到的問題并不是跑一個模型的 demo 就可以的,模型開發需要對數據的預處理、數據增強、模型定義、權值初始化、模型 Finetune、學習率調整策略、損失函數選取、優化器選取、可視化等等。鑒于此,我只能自己對著官方文檔,一步一步地學習。
起初,只是做了一些學習筆記,后來覺得這些內容應該對大家有些許幫助,畢竟在互聯網上很難找到這類內容的分享,于是此教程就誕生了。
本教程內容及結構
本教程內容主要為在 PyTorch 中訓練一個模型所可能涉及到的方法及函數,并且對 PyTorch 提供的數據增強方法(22 個)、權值初始化方法(10 個)、損失函數(17 個)、優化器(6 個)及 tensorboardX 的方法(13 個)進行了詳細介紹。
本教程分為四章,結構與機器學習三大部分一致:
第一章,介紹數據的劃分,預處理,數據增強;
第二章,介紹模型的定義,權值初始化,模型 Finetune;
第三章,介紹各種損失函數及優化器;
第四章,介紹可視化工具,用于監控數據、模型權及損失函數的變化。
本教程適用讀者:
想熟悉 PyTorch 使用的朋友;
想采用 PyTorch 進行模型訓練的朋友;
正采用 PyTorch,但無有效機制去診斷模型的朋友;
干貨直達:
1.6 transforms 的二十二個方法
2.2 權值初始化的十種方法
3.1 PyTorch 的十七個損失函數
3.3 PyTorch 的十個優化器
3.4 PyTorch 的六個學習率調整方法
4.1 TensorBoardX
項目代碼:https://github.com/tensor-yu/PyTorch_Tutorial
為了展示該教程的內容,讀者可試讀第二章的第一小節,了解PyTorch如何搭建模型:
第二章 模型
第二章介紹關于網絡模型的一系列內容,包括模型的定義,模型參數初始化方法,模型的保存和加載,模型的 finetune(本質上還是模型權值初始化),首先介紹模型的定義。
2.1 模型的搭建
2.1.1 模型定義的三要
首先,必須繼承 nn.Module 這個類,要讓 PyTorch 知道這個類是一個 Module。
其次,在__init__(self) 中設置好需要的「組件"(如 conv、pooling、Linear、BatchNorm 等)。
最后,在 forward(self, x) 中用定義好的「組件」進行組裝,就像搭積木,把網絡結構搭建出來,這樣一個模型就定義好了。
接下來,請看代碼,在/Code/main_training/main.py 中可以看到定義了一個類 class Net(nn.Module),先看__init__(self) 函數:
????????super(Net,?self).__init__()
????????self.conv1?=?nn.Conv2d(3,?6,?5)
????????self.pool1?=?nn.MaxPool2d(2,?2)
????????self.conv2?=?nn.Conv2d(6,?16,?5)
????????self.pool2?=?nn.MaxPool2d(2,?2)
????????self.fc1?=?nn.Linear(16?*?5?*?5,?120)
????????self.fc2?=?nn.Linear(120,?84)
????????self.fc3?=?nn.Linear(84,?10)
第一行是初始化,往后定義了一系列組件,如由 Conv2d 構成的 conv1,有 MaxPool2d 構成的 poo1l,這些操作均由 torch.nn 提供,torch.nn 中的操作可查看文檔:https://PyTorch.org/docs/stable/nn.html#。
當這些組件定義好之后,就可以定義 forward() 函數,用來搭建網絡結構,請看代碼:
????????x?=?self.pool1(F.relu(self.conv1(x)))
????????x?=?self.pool2(F.relu(self.conv2(x)))
????????x?=?x.view(-1,?16?*?5?*?5)
????????x?=?F.relu(self.fc1(x))
????????x?=?F.relu(self.fc2(x))
????????x?=?self.fc3(x)
????????return?x
x 為模型的輸入,第一行表示,x 經過 conv1,然后經過激活函數 relu,再經過 pool1 操作;
第二行于第一行一樣;第三行,表示將 x 進行 reshape,為了后面做為全連接層的輸入;
第四,第五行的操作都一樣,先經過全連接層 fc,然后經過 relu;
第六行,模型的最終輸出是 fc3 輸出。
至此,一個模型定義完畢,接著就可以在后面進行使用。例如,實例化一個模型 net = Net(),然后把輸入 inputs 扔進去,outputs = net(inputs),就可以得到輸出 outputs。
2.1.2 模型定義多說兩句
上面只是介紹了模型定義的要素和過程,但是在工程應用中會碰到各種各樣的網絡模型,這時,我們就需要一些實用工具來幫助我們定義模型了。
這里以 Resnet34 為例介紹「復雜」模型的定義,這部分代碼從 github 上獲取。
地址:https://github.com/yuanlairuci110/PyTorch-best-practice-master/blob/master/models/ResNet34.py
????'''
????實現子module:?Residual?Block
????'''
????def?__init__(self,?inchannel,?outchannel,?stride=1,?shortcut=None):
????????super(ResidualBlock,?self).__init__()
????????self.left?=?nn.Sequential(
????????????????nn.Conv2d(inchannel,?outchannel,?3,?stride,?1,?bias=False),
????????????????nn.BatchNorm2d(outchannel),
????????????????nn.ReLU(inplace=True),
????????????????nn.Conv2d(outchannel,?outchannel,?3,?1,?1,?bias=False),
????????????????nn.BatchNorm2d(outchannel)?)
????????self.right?=?shortcut
????def?forward(self,?x):
????????out?=?self.left(x)
????????residual?=?x?if?self.right?is?None?else?self.right(x)
????????out?+=?residual
????????return?F.relu(out)
class?ResNet34(BasicModule):
????'''
????實現主module:ResNet34
????ResNet34包含多個layer,每個layer又包含多個Residual?block
????用子module來實現Residual?block,用_make_layer函數來實現layer
????'''
????def?__init__(self,?num_classes=2):
????????super(ResNet34,?self).__init__()
????????self.model_name?=?'resnet34'
????????#?前幾層:?圖像轉換
????????self.pre?=?nn.Sequential(
????????????????nn.Conv2d(3,?64,?7,?2,?3,?bias=False),
????????????????nn.BatchNorm2d(64),
????????????????nn.ReLU(inplace=True),
????????????????nn.MaxPool2d(3,?2,?1))
????????#?重復的layer,分別有3,4,6,3個residual?block
????????self.layer1?=?self._make_layer(?64,?128,?3)
????????self.layer2?=?self._make_layer(?128,?256,?4,?stride=2)
????????self.layer3?=?self._make_layer(?256,?512,?6,?stride=2)
????????self.layer4?=?self._make_layer(?512,?512,?3,?stride=2)
????????#分類用的全連接
????????self.fc?=?nn.Linear(512,?num_classes)
????def?_make_layer(self,??inchannel,?outchannel,?block_num,?stride=1):
????????'''
????????構建layer,包含多個residual?block
????????'''
????????shortcut?=?nn.Sequential(
????????????????nn.Conv2d(inchannel,outchannel,1,stride,?bias=False),
????????????????nn.BatchNorm2d(outchannel))
????????layers?=?[]
????????layers.append(ResidualBlock(inchannel,?outchannel,?stride,?shortcut))
????????for?i?in?range(1,?block_num):
????????????layers.append(ResidualBlock(outchannel,?outchannel))
????????return?nn.Sequential(*layers)
????def?forward(self,?x):
????????x?=?self.pre(x)
????????x?=?self.layer1(x)
????????x?=?self.layer2(x)
????????x?=?self.layer3(x)
????????x?=?self.layer4(x)
????????x?=?F.avg_pool2d(x,?7)
????????x?=?x.view(x.size(0),?-1)
????????return?self.fc(x)
還是從三要素出發看看是怎么定義 Resnet34 的。
首先,繼承 nn.Module;
其次,看__init__() 函數,在__init__() 中,定義了這些組件,self.pre,self.layer1-4, self.fc ;
最后,看 forward(),分別用了在__init__() 中定義的一系列組件,并且用了 torch.nn.functional.avg_pool2d 這個操作。
至此,網絡定義完成。
以為就完了?怎么可能,__init__() 函數中的組件是怎么定義的,在__init__() 中出現了 torch.nn.Sequential。
組件定義還調用函數_make_layer(),其中也用到了 torch.nn.Sequential,其中還調用了 ResidualBlock(nn.Module),在 ResidualBlock(nn.Module) 中有一次調用了 torch.nn.Sequential。
torch.nn.Sequential 到底是什么呢?為什么都在用呢?
2.1.3 nn.Sequetial
torch.nn.Sequential 其實就是 Sequential 容器,該容器將一系列操作按先后順序給包起來,方便重復使用。例如 Resnet 中有很多重復的 block,就可以用 Sequential 容器把重復的地方包起來。
官方文檔中給出兩個使用例子:
model?=?nn.Sequential(
??????????nn.Conv2d(1,20,5),
??????????nn.ReLU(),
??????????nn.Conv2d(20,64,5),
??????????nn.ReLU()
????????)
#?Example?of?using?Sequential?with?OrderedDict
model?=?nn.Sequential(OrderedDict([
??????????('conv1',?nn.Conv2d(1,20,5)),
??????????('relu1',?nn.ReLU()),
??????????('conv2',?nn.Conv2d(20,64,5)),
??????????('relu2',?nn.ReLU())
????????]))
小結:
模型的定義就是先繼承,再構建組件,最后組裝。
其中基本組件可從 torch.nn 中獲取,或者從 torch.nn.functional 中獲取,同時為了方便重復使用組件,可以使用 Sequential 容器將一系列組件包起來,最后在 forward() 函數中將這些組件組裝成你的模型。
獲取方式一:
獲取方式二:
鏈接: https://pan.baidu.com/s/11hvPGusAopXNwuCsuLilCA?
提取碼: anw5?
機器之心CES 2019專題報道即將到來,歡迎大家積極關注。
點擊「閱讀原文」查看機器之心專題頁。
總結
以上是生活随笔為你收集整理的快速上手笔记,PyTorch模型训练实用教程(附代码)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 这种有序神经元,像你熟知的循环神经网络吗
- 下一篇: 新手福利:免费百页机器学习入门书