|
劇照 | 《怦然心動(dòng)》 作為目前越來(lái)越受歡迎的深度學(xué)習(xí)框架,pytorch 基本上成了新人進(jìn)入深度學(xué)習(xí)領(lǐng)域最常用的框架。相比于 TensorFlow,pytorch 更易學(xué),更快上手,也可以更容易的實(shí)現(xiàn)自己想要的 demo。今天的文章就從 pytorch 的基礎(chǔ)開(kāi)始,幫助大家實(shí)現(xiàn)成功入門。 首先,本篇文章需要大家對(duì)深度學(xué)習(xí)的理論知識(shí)有一定的了解,知道基本的 CNN,RNN 等概念,知道前向傳播和反向傳播等流程,畢竟本文重點(diǎn)是一篇實(shí)操性的教程。 其次,這篇文章我更想從一個(gè)總體性的視角展開(kāi),大家在學(xué)習(xí)的過(guò)程中更注重的應(yīng)該是在接觸新知識(shí)時(shí),如何設(shè)計(jì)學(xué)習(xí)路線的一種思路分享。這種思路不一定適合所有人,但是肯定可以對(duì)你有所借鑒,你也可以基于此總結(jié)出來(lái)更適合自己的方法。 接下來(lái)我們從以下幾個(gè)步驟去幫助大家入門 pytorch 的實(shí)戰(zhàn)教程。 這八個(gè)步驟,對(duì)應(yīng)了我的八篇學(xué)習(xí)筆記的文章,本文是從一個(gè)串講的思路來(lái)介紹學(xué)習(xí)路徑,對(duì)應(yīng)步驟的更多細(xì)節(jié)會(huì)在具體的文章中展示。在每個(gè)步驟介紹的最后和全文的結(jié)尾,我們也會(huì)給出文章的鏈接,大家可以針對(duì)性食用~ 1. 開(kāi)始一個(gè)簡(jiǎn)單的分類器我個(gè)人在學(xué)習(xí)一門新語(yǔ)言,一個(gè)新框架,一個(gè)新技術(shù)時(shí),最優(yōu)先要保證的就是成就感反饋。以學(xué)習(xí) pytorch 為例,很多教程從張量開(kāi)始。我自己也按照這種教程學(xué)習(xí)過(guò),的確內(nèi)容非常全盡,但是有兩個(gè)原因,我自己不太推薦以這種方式入門:1)前期學(xué)習(xí)過(guò)于枯燥,沒(méi)有成就感;2)有的知識(shí)內(nèi)容屬于深度學(xué)習(xí)的基本功,過(guò)于贅述。 所以我覺(jué)得入門一個(gè)新知識(shí)的知識(shí),最好是先搭起來(lái)結(jié)構(gòu),然后再去慢慢補(bǔ)充細(xì)節(jié)。因此我在這篇文章的第一部分,先選擇構(gòu)建一個(gè)簡(jiǎn)單的分類器,讓大家知道一個(gè) pytorch 下的代碼流程應(yīng)該是什么樣子。 學(xué)過(guò) c 語(yǔ)言的朋友肯定知道,我們先學(xué)第一個(gè)代碼的時(shí)候,肯定是先來(lái)一個(gè) hello world,而不是去研究第一行的 #include。 對(duì)于第一個(gè) pytorch 程序而言,我們要做的是首先跑通整個(gè)流程,如果是一個(gè)簡(jiǎn)單的分類器,數(shù)據(jù)集也就不能太復(fù)雜。因此,我們從三方面考慮:1)自定義生成一些點(diǎn),分為兩類;2)學(xué)習(xí)如何構(gòu)建一個(gè)淺層的神經(jīng)網(wǎng)絡(luò);3)嘗試 pytorch 中的訓(xùn)練和測(cè)試過(guò)程。 1.1 自定義生成數(shù)據(jù)集首先,自定義生成我們的數(shù)據(jù)集。利用 torch 自帶的 zeros,ones 這些方法,我們生成一些隨機(jī)的點(diǎn),分為兩類。比如分別以(2,2)和(-2,-2)為均值,隨機(jī)生成一些隨機(jī)數(shù),作為兩類,這樣子我們就得到了我們想要的數(shù)據(jù)集。 1.2 學(xué)會(huì)構(gòu)建網(wǎng)絡(luò)的流程其次,就是構(gòu)建一個(gè)淺層的神經(jīng)網(wǎng)絡(luò),這里我們給出一個(gè)代碼示例,大家了解一下最基礎(chǔ)的 pytorch 的網(wǎng)絡(luò)應(yīng)該如何構(gòu)建: class Net(torch.nn.Module): def __init__(self, n_feature, n_hidden, n_output): super(Net, self).__init__() self.n_hidden = torch.nn.Linear(n_feature, n_hidden) self.out = torch.nn.Linear(n_hidden, n_output) def forward(self, x_layer): x_layer = torch.relu(self.n_hidden(x_layer)) x_layer = self.out(x_layer) x_layer = torch.nn.functional.softmax(x_layer) return x_layernet = Net(n_feature=2, n_hidden=10, n_output=2)# print(net)optimizer = torch.optim.SGD(net.parameters(), lr=0.02)loss_func = torch.nn.CrossEntropyLoss()這個(gè) Net 類,就是我們構(gòu)建的代碼框架,用它生成的對(duì)象就是一個(gè)我們可以用來(lái)訓(xùn)練和測(cè)試的網(wǎng)絡(luò)。這個(gè)類中,初始化函數(shù)中表示了每個(gè)網(wǎng)絡(luò)層的結(jié)構(gòu)設(shè)置,而 forward() 方法表示了每個(gè)層之間的交互順序和關(guān)系。 而 optimizer 就是優(yōu)化器,包含了需要優(yōu)化的參數(shù)有哪些,loss_func 就是我們?cè)O(shè)置的損失函數(shù)。 這個(gè)就像我們寫一個(gè) hello,world 一樣,我們只需要知道自己該如何構(gòu)造一個(gè)網(wǎng)絡(luò)。當(dāng)我們需要調(diào)整的時(shí)候,就將其中對(duì)應(yīng)的模塊替換掉。 1.3 訓(xùn)練與測(cè)試接下來(lái)就是訓(xùn)練與測(cè)試的階段。訓(xùn)練我們需要知道三句代碼是核心:
這里的核心思路是,梯度清空,反向傳播,參數(shù)更新。分別對(duì)應(yīng)了這三句代碼的作用。在pytorch 中,梯度會(huì)保留,所以需要用 zero_grad() 來(lái)清空,然后利用損失函數(shù)反向傳播計(jì)算梯度,最后就是用我們定義的優(yōu)化器將每個(gè)需要優(yōu)化的參數(shù)進(jìn)行更新。 測(cè)試階段就很簡(jiǎn)單了,直接將輸入丟進(jìn)去就可以看到預(yù)測(cè)結(jié)果?,F(xiàn)在我們重新隨機(jī)生成一些數(shù)據(jù)點(diǎn)作為測(cè)試集,可以看到訓(xùn)練集對(duì)它的分類結(jié)果就很明顯。 至此,我們就完成了一個(gè)對(duì)于簡(jiǎn)單分類器的描述。當(dāng)然,如果你對(duì)前面的文章沒(méi)有任何了解,可能會(huì)覺(jué)得這部分不夠入門。那么可以看一下我的第一篇筆記:pytorch學(xué)習(xí)筆記(1):開(kāi)始一個(gè)簡(jiǎn)單的分類器,這里詳細(xì)的介紹了如何實(shí)現(xiàn)一個(gè)簡(jiǎn)單分類器的細(xì)節(jié)介紹。 2. 在 MNIST 上實(shí)現(xiàn)一個(gè) cnn完成了一個(gè)線性分類器后,我們的學(xué)習(xí)路線應(yīng)該是什么樣子呢?我覺(jué)得比較合適的做法是先改動(dòng) “hello,world” 的部分,讓我們看看把最直觀的部分進(jìn)行修改,會(huì)有哪些變化。并且可以得到很直接的成就反饋。 做深度學(xué)習(xí),肯定最熟悉的就是 CNN 做圖片分類。在一張圖片上,通過(guò)卷積來(lái)一層層的提取特征,最終實(shí)現(xiàn)分類的效果。那么我們既然已經(jīng)知道如何實(shí)現(xiàn)一個(gè)分類器,接下來(lái)就來(lái)看看如何用 CNN 完成圖片的分類。 這里的數(shù)據(jù)集我們選擇 mnist,是大家經(jīng)常用來(lái)作為入門的圖片分類數(shù)據(jù)集,內(nèi)容是各種手寫數(shù)字的展示。在安裝 torch 的時(shí)候,大家參考的教程一般也會(huì)推薦安裝 torchvision。在這個(gè)之中給出了一個(gè) dataset 的集合,其中包括了各種各樣的常見(jiàn)數(shù)據(jù)集,mnist 自然也是其中之一。 對(duì)于這些數(shù)據(jù)集的使用方法,主要是 root,transform 等幾個(gè)參數(shù),并不是很難。然后對(duì)應(yīng)的有一個(gè) torch.data 中的 DataLoader 方法,可以用來(lái)讓數(shù)據(jù)按自己想要的 batch 生成。具體的如何并行式生成數(shù)據(jù),在本文的最后一部分會(huì)進(jìn)行介紹。這里我們只需要知道可以使用 DataLoader 并行式按批生成數(shù)據(jù)。 核心的是如何構(gòu)建一個(gè) CNN 網(wǎng)絡(luò)。我們前面學(xué)會(huì)了分類器,只使用了一個(gè)隱藏層進(jìn)行 embedding 操作就可以了。那么如果要實(shí)現(xiàn) CNN,我們自然要加入卷積層,激活層,池化層這些操作。 class CNN(nn.Module): def __init__(self): super(CNN, self).__init__() self.conv1 = nn.Sequential( nn.Conv2d( in_channels=1, out_channels=16, kernel_size=5, stride=2, padding=2, ), nn.ReLU(), nn.MaxPool2d(2) ) self.conv2 = nn.Sequential( nn.Conv2d(16, 32, 5, 1, 2), nn.ReLU() ) self.out = nn.Linear(32 * 7 * 7, 10) def forward(self, x): x = self.conv1(x) x = self.conv2(x) x = x.view(x.size(0), -1) output = self.out(x) return output所以按照這個(gè)樣子,我們就可以改造前面簡(jiǎn)單分類器的結(jié)構(gòu),生成我們現(xiàn)在的 CNN 結(jié)構(gòu)。從代碼中可以看到,將第一個(gè)卷積層設(shè)計(jì)為:卷積+激活+最大池化;第二個(gè)卷積層設(shè)計(jì)為:卷積+激活。最后跟上一個(gè)全連接層,實(shí)現(xiàn)整個(gè) CNN 的網(wǎng)絡(luò)結(jié)構(gòu)設(shè)計(jì)。 最終的網(wǎng)絡(luò)運(yùn)行結(jié)果可以對(duì) mnist 數(shù)據(jù)集達(dá)到 97% 以上的分類精度,可見(jiàn) CNN 在圖片分類領(lǐng)域的確有獨(dú)到的優(yōu)勢(shì)。 現(xiàn)在我們通過(guò)適當(dāng)?shù)母脑鞂?shí)現(xiàn)了一個(gè) CNN 在圖片分類上的應(yīng)用,具體的更多細(xì)節(jié)可以參考:pytorch學(xué)習(xí)筆記(2):在 MNIST 上實(shí)現(xiàn)一個(gè) cnn 那么在完成了這一步的操作后,我們可能需要思考一點(diǎn):如果我自己想去做一些更自定義的網(wǎng)絡(luò)結(jié)構(gòu)出來(lái),該如何實(shí)現(xiàn)呢?我又怎么知道去修改哪里,以及修改成什么樣子呢?所以接下來(lái)需要了解的是 torch 都提供了哪些集成好的常用網(wǎng)絡(luò)層。 3. 常用網(wǎng)絡(luò)層介紹通過(guò)兩個(gè)遞進(jìn)的例子,我們已經(jīng)知道了該如何實(shí)現(xiàn)一個(gè)基本的 CNN 網(wǎng)絡(luò)結(jié)構(gòu)。但是如前面提到的問(wèn)題一樣,如果想改某一部分,應(yīng)該怎么改呢? 所以從學(xué)習(xí)的角度出發(fā)的話,現(xiàn)在應(yīng)該考慮的是介紹常用的網(wǎng)絡(luò)層都有哪些。然后我們就可以(成為一個(gè)調(diào)包俠。哈哈,入門肯定要從調(diào)包開(kāi)始嘛~)開(kāi)始針對(duì)自己想要設(shè)計(jì)的網(wǎng)絡(luò)結(jié)構(gòu)選擇合適的模塊啦~ 在這部分我們從以下幾個(gè)方面去對(duì) pytorch 提供的網(wǎng)絡(luò)層進(jìn)行了介紹: 卷積層:自帶了一維,二維,三維等卷積函數(shù);池化層:可選的有最大池化,平均池化等;Dropout:有一維,二維等選擇;BN層:是否加入 BN 層的操作;激活函數(shù):elu,relu,sigmoid,tanh,softmax 等層可供選擇;損失函數(shù):mse,CrossEntropy 等可供選擇。總體來(lái)說(shuō)這部分內(nèi)容,我簡(jiǎn)單的給一個(gè)大綱,就不過(guò)多贅述了,具體的每部分的細(xì)節(jié)參數(shù)設(shè)置,以及一些幫助大家理解的實(shí)例都可以在這篇文章中進(jìn)一步查看:pytorch學(xué)習(xí)筆記(3):常用網(wǎng)絡(luò)層介紹。 4. tensorboard 可視化現(xiàn)在我們具備了初步構(gòu)建自定義網(wǎng)絡(luò)結(jié)構(gòu)的能力,也可以完成在自帶數(shù)據(jù)集上進(jìn)行訓(xùn)練和測(cè)試的操作。那么如何讓我們對(duì)訓(xùn)練過(guò)程中的性能有一個(gè)更直觀的認(rèn)識(shí)呢?對(duì)網(wǎng)絡(luò)結(jié)構(gòu)如何進(jìn)行可視化呢?數(shù)據(jù)集的內(nèi)容是什么樣子的? 這些功能我們都可以用一個(gè)名為 tensorboard 的工具來(lái)實(shí)現(xiàn),這個(gè)工具在 TensorFlow 中也很常用。 如何學(xué)習(xí)使用 tensorboard 呢?這部分我們建議從如下幾個(gè)步驟去進(jìn)行:首先舉一個(gè)簡(jiǎn)單的例子,讓代碼示例跑起來(lái);然后將整個(gè)訓(xùn)練過(guò)程可視化出來(lái);最后再展示如何可視化數(shù)據(jù)集的內(nèi)容以及網(wǎng)絡(luò)結(jié)構(gòu)流程。 4.1 run一個(gè)例子這里我們選擇先運(yùn)行一個(gè)官方教程給出的例子,了解如何使用 tensorboard 的基本流程:
從這個(gè)流程中我們可以看到,引入了一個(gè) SummaryWriter 類,然后生成一個(gè) writer 對(duì)象,在 for 循環(huán)中,每次調(diào)用 add_scalar() 方法,往進(jìn)添加內(nèi)容。 在完成這個(gè)代碼后,如果我們?cè)诮K端中輸入: tensorboard --logdir='runs'我們會(huì)得到一副 y=2x 的斜線,這就相當(dāng)于揭示了 tensorboard 的本質(zhì)。每次將一個(gè)值傳入 ‘runs’ 文件夾中的文件中,然后在終端中去調(diào)用保存的數(shù)據(jù),產(chǎn)生我們想要的圖形。 這一步我們主要是理解上面的這個(gè)流程,那么我們就來(lái)看看該怎么替換想要換掉的模塊,來(lái)生成我們想要生成的圖形。 4.2 可視化 CNN 的訓(xùn)練數(shù)據(jù)前面第二部分,我們定義了一個(gè) CNN 來(lái)實(shí)現(xiàn)對(duì)圖片的分類效果。那么在訓(xùn)練過(guò)程中的 accuracy 和 loss 是如何變化的呢?
這里我們可以看出來(lái)是訓(xùn)練部分的內(nèi)容,只是在后面加上了我們前面的一個(gè)步驟:添加了兩行 add_scalar() 方法。其實(shí)就是訓(xùn)練時(shí)每隔 50 步都進(jìn)行一次測(cè)試,并將測(cè)試結(jié)果記錄下來(lái),并且每一步的 loss 也都會(huì)保存下來(lái)。 所以到最后,我們?cè)诮K端中輸入上面提到的:tensorboard —logdir=dir,就可以看到下面這幅圖: 4.3 圖片和模型的可視化除了上面對(duì)數(shù)值的記錄,tensorboard 還提供了諸如圖片和模型等的可視化,相比于使用 add_scalar(),這里我們使用 add_image() 和 add_graph() 來(lái)實(shí)現(xiàn)對(duì)應(yīng)的功能。 add_image() 對(duì)圖片數(shù)據(jù)進(jìn)行保存,每次輸入一個(gè) batch 的數(shù)據(jù),也就是說(shuō) batch 有多大,其實(shí)相當(dāng)于可視化了多少的 image。 add_graph() 則是對(duì)模型結(jié)構(gòu)的保存,在可視化的時(shí)候,就可以對(duì)這些內(nèi)容進(jìn)行自動(dòng)展示。 這里我們主要是介紹概括性的方法與學(xué)習(xí)流程,具體的關(guān)于 tensorboard 的內(nèi)容,每個(gè)方法的參數(shù)設(shè)置,包括數(shù)據(jù)保存的路徑等內(nèi)容,大家可以進(jìn)一步參考:pytorch學(xué)習(xí)筆記(4):tensorboard 可視化。 5. vgg 及一些 tricks這一部分的內(nèi)容就比較簡(jiǎn)單了,找一個(gè)比較經(jīng)典的深層網(wǎng)絡(luò)來(lái)實(shí)現(xiàn)一下,驗(yàn)證一下我們之前的基礎(chǔ)。此外,再介紹一種方法來(lái)簡(jiǎn)化深層網(wǎng)絡(luò)的構(gòu)造方法。 首先實(shí)現(xiàn)一個(gè) vgg 本身并沒(méi)有太多難度,我們看一看 paper,就可以知道網(wǎng)絡(luò)的結(jié)構(gòu)設(shè)置。我們不拘泥于 vgg,而是說(shuō)一個(gè)深層次的網(wǎng)絡(luò)的構(gòu)成。 實(shí)現(xiàn)一個(gè)長(zhǎng)的網(wǎng)絡(luò),本質(zhì)上還是按照前面的思路,一層層的把網(wǎng)絡(luò)堆疊起來(lái)。我們先看使用 Sequential 構(gòu)建一個(gè)卷積層的樣子: 這是一層的網(wǎng)絡(luò)樣子,我們根據(jù)自己要實(shí)現(xiàn)的網(wǎng)絡(luò)定義,比如參考 vgg 的 paper 內(nèi)容,定義了卷積層的各個(gè)參數(shù),加上 BN 層,加上 relu 進(jìn)行激活。 整體就是我們定義好的一層,其它層以此類推,用我們前面介紹的常用網(wǎng)絡(luò)層就可以像搭積木一樣,把它們搭建起來(lái)。 所以按照前面的教程思路,一個(gè)深層的神經(jīng)網(wǎng)絡(luò),例如 vgg,本質(zhì)上是可以通過(guò)簡(jiǎn)單的堆疊來(lái)實(shí)現(xiàn)的。最后我們?cè)?forward 函數(shù)中,定義好如下內(nèi)容: def forward(self, x): x = self.conv1(x) x = self.conv2(x) x = self.conv3(x) x = self.conv4(x) x = self.conv5(x) x = self.conv6(x) x = self.conv7(x) x = self.conv8(x) x = self.conv9(x) x = self.conv10(x) x = self.conv11(x) x = self.conv12(x) x = self.conv13(x) x = x.view(x.size(0), -1) output = self.out(x) return output可以看到從內(nèi)容上就是前面的 CNN 的擴(kuò)展,沒(méi)有技術(shù)上的新東西。但是也顯然易見(jiàn),有點(diǎn)丑陋,寫這么長(zhǎng)個(gè) forward,而且還看起來(lái)都是重復(fù)的東西,程序員當(dāng)然不能容忍重復(fù)的內(nèi)容一直出現(xiàn)。 所以這里分為兩步我們?nèi)タ紤]如何簡(jiǎn)化一個(gè)模型:sequential 以 list 的形式輸入各層的網(wǎng)絡(luò)結(jié)構(gòu);更加方便的生成各層網(wǎng)絡(luò)結(jié)構(gòu)的 list。具體的意思是什么呢?我們簡(jiǎn)單的展開(kāi)來(lái)講一下。
這里就可以很直接的看到,最終生成的 layers 這個(gè) list,就是我們想要的內(nèi)容,其中包含了我們需要的每個(gè)網(wǎng)絡(luò)層結(jié)構(gòu)。在 for 循環(huán)中就是我們生成的方式,按照在參數(shù) cfg 中定義的內(nèi)容,依次往 layers 中添加我們需要的內(nèi)容。數(shù)字表示該卷積層的輸出通道,字母 ‘M' 表示最大池化。 可以看到,以這種方式,我們就可以通過(guò)只調(diào)整 cfg 這個(gè) list,最終實(shí)現(xiàn)目標(biāo)的深層次的神經(jīng)網(wǎng)絡(luò)的設(shè)計(jì)。更詳細(xì)的介紹和設(shè)計(jì),可以參考我們之前的學(xué)習(xí)筆記:pytorch學(xué)習(xí)筆記(5):vgg 實(shí)現(xiàn)以及一些 tricks。 6. GPU 和如何保存加載模型到了這一步,我們的網(wǎng)絡(luò)深度也加上來(lái)了,是時(shí)候考慮一下 GPU 加速的問(wèn)題了。GPU 在深度學(xué)習(xí)中是無(wú)論如何也繞不過(guò)去的一個(gè)話題,好在 pytorch 在 GPU 的使用方面給了非常友好的接口,下面我們就看一下如何使用 GPU 加速,以及如何保存訓(xùn)練好的模型,到測(cè)試時(shí)再加載出來(lái)。 6.1 先看看 GPU 咋用吧我們就來(lái)說(shuō)一下 GPU 在 pytorch 中有多么簡(jiǎn)單易用吧。首先如下簡(jiǎn)單命令: torch.cuda.is_available()這條命令可以判讀你是否安裝好了 GPU 版本的 pytorch,或者你的顯卡是否可以使用,如果結(jié)果顯示 GPU 的使用在 pytorch 中,我們就記住三部分:遷移數(shù)據(jù),遷移模型,遷回?cái)?shù)據(jù)。 首先遷移數(shù)據(jù)是指,我們需要將數(shù)據(jù)遷移到 GPU 上,這個(gè)時(shí)候就體現(xiàn)出顯存的重要性,顯存越大,就可以往進(jìn)遷移的數(shù)據(jù)越多; 其次是遷移模型,也就是說(shuō)將我們定義好的網(wǎng)絡(luò)模型也遷移到 GPU 上,這個(gè)時(shí)候就可以在 GPU 上對(duì)給定模型,利用遷移進(jìn)來(lái)的數(shù)據(jù)進(jìn)行訓(xùn)練和測(cè)試; 最后是遷回?cái)?shù)據(jù),也就是說(shuō)將測(cè)試好的結(jié)果再返回 CPU,進(jìn)行下一步的其它處理,比如計(jì)算精度之類。 這里給一個(gè)小栗子來(lái)為大家看一下這三步:
通過(guò)這個(gè)例子,我們可以很清晰的看到如何使用 GPU 完成我們上所述的三個(gè)步驟。只要保證了將這三部分加入到你的代碼中,中間的訓(xùn)練和測(cè)試依然保持原樣,我們就實(shí)現(xiàn)了利用 GPU 加速的目的。 6.2 訓(xùn)練好的模型如何保存和加載呢?關(guān)于 pytorch 中的模型保存,一般有兩種途徑:只保存網(wǎng)絡(luò)參數(shù),保存整個(gè)網(wǎng)絡(luò)。 首先要知道的一點(diǎn)是,在 pytorch 中所有的網(wǎng)路參數(shù)數(shù)據(jù)都是一個(gè) dict,也就是網(wǎng)絡(luò)對(duì)象的 state_dict() 參數(shù)。那么我們?nèi)绻氡4嫦聛?lái)需要的內(nèi)容,其實(shí)在底層操作方面并不復(fù)雜。 現(xiàn)在來(lái)看如何保存模型,其實(shí)就一條語(yǔ)句:torch.save(content,path),就可以將需要的 content 保存到目標(biāo)的 path 中。這里唯一的需要思考的是如何區(qū)分只保存網(wǎng)絡(luò)參數(shù),還是保存整個(gè)網(wǎng)絡(luò)。 只保存網(wǎng)絡(luò)參數(shù)時(shí),我們的 content 就是 cnn.state_dict(),如果保存整個(gè)網(wǎng)絡(luò),content 就是 cnn。下面兩行代碼分別是只保存參數(shù)和保存整個(gè)網(wǎng)絡(luò): torch.save(cnn.state_dict(), PATH)torch.save(cnn, PATH)可以看到保存的方式非常方便,一個(gè)函數(shù)就可以完成。那么對(duì)應(yīng)的,讀取的方式是什么呢? 分別用兩個(gè)不同的方法來(lái)進(jìn)行讀?。簂oad_state_dict() 和 load()。 只看名稱也可以想到前者是讀取參數(shù),后者是讀取整個(gè)網(wǎng)絡(luò)。但是只讀取參數(shù)的話,我們需要提前定義好對(duì)應(yīng)的網(wǎng)絡(luò)對(duì)象,然后通過(guò)讀取參數(shù)的方式,為網(wǎng)絡(luò)的結(jié)構(gòu)中填充相應(yīng)的參數(shù)。 具體的如何使用 GPU 加速模型,如何存儲(chǔ)和讀取訓(xùn)練好的網(wǎng)絡(luò),細(xì)節(jié)的代碼和例子可以看:pytorch學(xué)習(xí)筆記(6):GPU 和如何保存加載模型。 7. RNN 回歸前面我們介紹了 CNN 的創(chuàng)建方式,常用的網(wǎng)絡(luò)層,基于此的基礎(chǔ)上,又介紹了一些其它的相關(guān)操作,比如 GPU 加速等?,F(xiàn)在我們來(lái)看系列教程的最后一部分,就是如何使用 RNN。 以 RNN 為例,我們構(gòu)建一個(gè)回歸器,以此來(lái)介紹 RNN 在 pytorch 中的使用方法,幫助大家入門 RNN 的操作過(guò)程。 7.1 RNN 參數(shù)我們這里不再贅述 RNN 的定義和內(nèi)容,在本節(jié)后面的文章鏈接中,詳細(xì)的介紹了這一部分。我們?cè)谶@里只說(shuō)一下在 pytorch 中的 RNN 類可以設(shè)置的參數(shù)。 input_size:這個(gè)參數(shù)表示的輸入數(shù)據(jù)的維度。比如輸入一個(gè)句子,這里表示的就是每個(gè)單詞的詞向量的維度。hidden_size :可以理解為在 CNN 中,一個(gè)卷積層的輸出維度一樣。這里表示將前面的 input_size 映射到一個(gè)什么維度上。num_layers:表示循環(huán)的層數(shù)。舉個(gè)栗子,將 num_layers 設(shè)置為 2,也就是將兩個(gè) RNN 堆疊在一起,第一層的輸出作為第二層的輸入。默認(rèn)為 1。nonlinearity:這個(gè)參數(shù)對(duì)激活函數(shù)進(jìn)行選擇,目前 pytorch 支持 tanh 和 relu,默認(rèn)的激活函數(shù)是 tanh。bias:這個(gè)參數(shù)表示是否需要偏置項(xiàng),默認(rèn)為 True。batch_first:這個(gè)是我們數(shù)據(jù)的格式描述,在 pytorch 中我們經(jīng)常以 batch 分組來(lái)訓(xùn)練數(shù)據(jù)。這里的 batch_size 表示 batch 是否在輸入數(shù)據(jù)的第一個(gè)維度,如果在第一個(gè)維度則為 True,默認(rèn)為 False,也就是第二個(gè)維度。dropout:這里就是對(duì)每一層的輸出是否加一個(gè) dropout 層,如果參數(shù)非 0,那么就會(huì)加上這個(gè) dropout 層。值得注意的是,對(duì)最后的輸出層并不會(huì)加,也就是這個(gè)參數(shù)只有在 num_layers 參數(shù)大于 1 的時(shí)候才有意義。默認(rèn)為 0。bidirectional:如果為 True,則表示 RNN 網(wǎng)絡(luò)為雙向結(jié)構(gòu),默認(rèn)為 False。這些參數(shù)的給定,我們就可以輕松的去設(shè)置我們想要的 RNN 結(jié)構(gòu)。此處 input_size 和 hidden_size 是兩個(gè)必須傳入的參數(shù),需要讓網(wǎng)絡(luò)知道將什么維度的輸入映射到什么維度上去。其余的參數(shù)都給了比較常用的默認(rèn)值。 7.2 回歸器:用 sin 預(yù)測(cè) cos在這里我們舉一個(gè)非常容易理解的例子。也不去折騰什么復(fù)雜數(shù)據(jù)集,我們同樣使用一個(gè)簡(jiǎn)單的自定義數(shù)據(jù)集:sin 函數(shù)作為 data,cos 函數(shù)作為 label。因?yàn)橹攸c(diǎn)是學(xué)習(xí) RNN 的使用,所以我們無(wú)需測(cè)試集,只看訓(xùn)練的擬合程度,判斷是否成功收斂就可以了。 首先給出來(lái)我們定義的 RNN 結(jié)構(gòu),再對(duì)其中的細(xì)節(jié)進(jìn)行解讀:
我們先看第一部分 RNN 的結(jié)構(gòu)上,定義了三個(gè)參數(shù):input_size,hidden_size,batch_first。 input_size 我們?cè)O(shè)置為 1,是因?yàn)槊看屋斎氲臄?shù)據(jù)上,只有一個(gè)點(diǎn)的位置,數(shù)據(jù)是一維數(shù)據(jù); hidden_size 設(shè)置為 32,表示我們想要將這個(gè)數(shù)據(jù)映射到 32 維的隱空間上,這個(gè)值由自己進(jìn)行選擇,不要太小,也不要太大(太小會(huì)導(dǎo)致擬合能力較差,太大會(huì)導(dǎo)致計(jì)算資源消耗過(guò)多); batch_first 設(shè)為 最終,根據(jù)前面對(duì)參數(shù)的介紹,可以得知,我們構(gòu)建了一個(gè)單層的 RNN 網(wǎng)絡(luò),輸入的每個(gè) time_step 上的數(shù)據(jù)都是一維的,通過(guò)將其映射到 32 維的隱空間上,來(lái)發(fā)掘?qū)?biāo)簽數(shù)據(jù)的擬合關(guān)系。 接下來(lái)我們看一下 forward 函數(shù)中的內(nèi)容,與 CNN 的 forward 中有些不一樣。在 CNN 中,我們直接將對(duì)應(yīng)的網(wǎng)絡(luò)結(jié)構(gòu)往一起拼接就可以,這里多了一些奇怪的參數(shù)。這是為什么呢? 從第一行開(kāi)始看起,首先 RNN 我們都知道,每個(gè) time_step 的循環(huán)中,都是將上一個(gè)循環(huán)的隱狀態(tài)和當(dāng)前的輸入結(jié)合起來(lái)作為輸入。那么 r_out 和 h_state 就是當(dāng)前狀態(tài)的輸出和隱狀態(tài)。 第二行的 outs 是一個(gè)空列表,用來(lái)存儲(chǔ)什么內(nèi)容呢?我們往下看。 后面是一個(gè) for 循環(huán),循環(huán)的次數(shù)取決于 r_out.size(1)。這個(gè)參數(shù)表示什么呢?r_out 我們知道是輸出,這個(gè)輸出的格式應(yīng)該和輸入是相同格式(batch,time_step,hidden_size),所以 r_out.size(1) 表示了這批數(shù)據(jù)的 time_step 的大小,也就是這批數(shù)據(jù)有多少個(gè)點(diǎn)。將對(duì)應(yīng)的數(shù)據(jù)進(jìn)行 self.out() 操作,也就是將 32 維的數(shù)據(jù)再映射到 1 維,并將結(jié)果 append 到 outs 中。 這里我們就知道前面定義的 outs 列表用來(lái)裝什么數(shù)據(jù)了,最后將結(jié)果 stack 起來(lái),作為 forward 的返回值。 這里看一下訓(xùn)練過(guò)程中的擬合情況: 藍(lán)色線條展示了模型的擬合過(guò)程,可以看到最終逐漸擬合到了目標(biāo)的 cos 曲線上(紅色線條)。 本部分更多的細(xì)節(jié),包括 RNN 原理的簡(jiǎn)單介紹,對(duì)應(yīng)的網(wǎng)絡(luò)的運(yùn)行細(xì)節(jié)都在:第七篇文章 中可以看到。除此之外,這篇文章中還進(jìn)一步給出了一個(gè)利用 LSTM 實(shí)現(xiàn)一個(gè)對(duì) mnist 數(shù)據(jù)集進(jìn)行分類的例子,幫助我們可以學(xué)習(xí) pytorch 中 LSTM 的使用方法,非常建議看一下這篇文章:pytorch學(xué)習(xí)筆記(7):RNN 和 LSTM 實(shí)現(xiàn)分類和回歸。 番外篇:如何進(jìn)階番外篇不是說(shuō)不重要的一步,而是更多的想表達(dá)我對(duì)學(xué)習(xí)流程為何這么設(shè)置的思路。通過(guò)前面的文章,大家肯定可以算是基本入門了 pytorch 的使用,至少不會(huì)出現(xiàn)想要實(shí)現(xiàn)一個(gè)網(wǎng)絡(luò)時(shí),手足無(wú)措的情況。但是如果從學(xué)好這個(gè)框架出發(fā),這肯定是遠(yuǎn)遠(yuǎn)不夠的。 那么我們應(yīng)該如何去學(xué)好這個(gè)非常流行的框架呢?我們首先應(yīng)該是按照前面文章的思路一樣,將整體流程的思維架設(shè)起來(lái),知道應(yīng)該怎么入手,可能大神還會(huì)給你說(shuō)如何底層加速,如何優(yōu)化細(xì)節(jié),如何并行式加載數(shù)據(jù)等。但是我們?nèi)绻簧蟻?lái)就學(xué)的那么細(xì),可能現(xiàn)在還云里霧里,不知道那種細(xì)節(jié)性的文章在說(shuō)什么。 所以這里我們給出一個(gè)并行式加載數(shù)據(jù)的例子,讓大家知道,在架設(shè)起來(lái)對(duì) pytorch 的整體性認(rèn)知以后,我們就可以很輕松的去針對(duì)性補(bǔ)充自己需要學(xué)會(huì)的內(nèi)容。 這里給出一篇文章的例子,講解了如何提高數(shù)據(jù)加載的速度,讓我們可以利用 pytorch 自帶的 DataLoader 類,自定義設(shè)置自己的數(shù)據(jù)加載類型,讓你的數(shù)據(jù)生成不再成為訓(xùn)練的瓶頸。具體的細(xì)節(jié)可以看:一個(gè)例子告訴你,在 pytorch 中應(yīng)該如何并行生成數(shù)據(jù)。 重點(diǎn)不是這篇文章,而是授之以漁。按照這類方法,大家就可以進(jìn)一步去優(yōu)化自己的知識(shí)體系,補(bǔ)充對(duì)細(xì)節(jié)上的提升。大家通過(guò)本篇系列文章的匯總教程以后,就可以很輕松的去學(xué)習(xí)其它對(duì) pytorch 技能進(jìn)行優(yōu)化的進(jìn)階文章了。 總結(jié)這是一篇對(duì) pytorch 進(jìn)行入門教程的文章,不僅僅是對(duì)框架的學(xué)習(xí),這樣的學(xué)習(xí)方法也可以借鑒到其它的框架,編程語(yǔ)言等中去。 |
|
|