深度學習之PyTorch實戰(1)——基礎學習及搭建環境
最近在學習PyTorch框架,買了一本《深度學習之PyTorch實戰計算機視覺》,從學習開始,小編會整理學習筆記,並部落格記錄,希望自己好好學完這本書,最後能熟練應用此框架。
PyTorch是美國網際網路巨頭Facebook在深度學習框架Torch的基礎上使用Python重寫的一個全新的深度學習框架,它更像NumPy的替代產物,不僅繼承了NumPy的眾多優點,還支援GPUs計算,在計算效率上要比NumPy有更明顯的優勢;不僅如此,PyTorch還有許多高階功能,比如擁有豐富的API,可以快速完成深度神經網路模型的搭建和訓練。所以 PyTorch一經發布,便受到了眾多開發人員和科研人員的追捧和喜愛,成為AI從業者的重要工具之一。
一:PyTorch中的Tensor
首先,我們需要學會使用PyTorch中的Tensor。Tensor在PyTorch中負責儲存基本資料,PyTorch針對Tensor也提供了相對豐富的函式和方法,所以PyTorch中的Tensor與NumPy的陣列具有極高的相似性。Tensor是一種高層次架構,也不要明白什麼是深度學習,什麼是後向傳播,如何對模型進行優化,什麼是計算圖等技術細節。更重要的是,在PyTorch中定義的Tensor資料型別可以在GPUs上進行運算,而且只需要對變數做一些簡單的型別轉換就能輕易實現。
1.1 Tensor的資料型別
在使用Tensor時,我們首先要掌握如何使用Tensor來定義不同資料型別的變數。和Numpy差不多,PyTorch中的Tensor也有自己的資料型別定義方式,常用的如下:
1.1.1 torch.FloatTensor
此變數用於生成資料型別為浮點型的Tensor,傳遞給torch.FloatTensor的引數可以是一個列表,也可以是一個維度值。
>>> import torch >>> a = torch.FloatTensor(2,3) >>> print(a) -0.11710.0000 -0.1171 0.00000.00000.0000 [torch.FloatTensor of size 2x3] >>> b = torch.FloatTensor([2,3,4,5]) >>> print(b) 2 3 4 5 [torch.FloatTensor of size 4]
可以看到,列印輸出的兩組變數資料型別都顯示為浮點型,不同的是,前一組的是按照我們指定的維度隨機生成的浮點型Tensor,而另外一組是按照我們給定的列表生成的浮點型Tensor。
1.1.2 torch.IntTensor
用於生成資料型別為整型的Tensor,傳遞給torch.IntTensor的引數可以是一個列表,也可以是一個維度值。
>>> import torch >>> a = torch.IntTensor(2,3) >>> print(a) 0 0 4 0 1 0 [torch.IntTensor of size 2x3] >>> b = torch.IntTensor([2,3,4,5]) >>> print(b) 2 3 4 5 [torch.IntTensor of size 4]
可以看到,以上生成的兩組Tensor最後顯示的資料型別都為整型。
1.1.3 torch.rand
用於生成資料型別為浮點型且維度指定的隨機Tensor,和在Numpy中使用numpy.rand生成隨機數的方法類似,隨機生成的浮點資料在0~1區間均勻分佈。
>>> import torch >>> a = torch.rand(2,3) >>> print(a) 0.55280.69950.5719 0.45830.57450.1114 [torch.FloatTensor of size 2x3]
1.1.4 torch.randn
用於生成資料型別為浮點型且維度指定的隨機Tensor,和在Numpy中使用numpy.randn生成隨機數的方法類似,隨機生成的浮點數的取值滿足均值為0,方差為1的正太分佈。
>>> import torch >>> a = torch.randn(2,3) >>> print(a) -0.53411.2267 -1.0884 0.4008 -1.81401.6335 [torch.FloatTensor of size 2x3]
1.1.5 torch.range
用於生成資料型別為浮點型且自定義其實範圍和結束範圍的Tensor,所以傳遞給torch.range的引數有三個,分別是範圍的起始值,範圍的結束值和步長,其中,步長用於指定從起始值到結束值的每步的資料間隔。
>>> import torch >>> a = torch.range(2,8,1) >>> print(a) 2 3 4 5 6 7 8 [torch.FloatTensor of size 7]
1.1.6 torch.zeros
用於生成資料型別為浮點型且維度指定的Tensor,不過這個浮點型的Tensor中的元素值全部為0.
>>> import torch >>> a = torch.zeros(2,3) >>> print(a) 000 000 [torch.FloatTensor of size 2x3]
1.2 Tensor的運算
這裡通常對Tensor資料型別的變數進行運算,來組合一些簡單或者複雜的演算法,常用的Tensor運算如下:
1.2.1 torch.abs
將引數傳遞到torch.abs後返回輸入引數的絕對值作為輸出,輸出引數必須是一個Tensor資料型別的變數。
import torch a = torch.randn(2,3) print(a) b = torch.abs(a) print(b) tensor([[-0.4098,0.5198,0.2362], [ 0.1903,0.5537,0.2249]]) tensor([[0.4098, 0.5198, 0.2362], [0.1903, 0.5537, 0.2249]]) Process finished with exit code 0
1.2.2 torch.add
將引數傳遞到torch.add後返回輸入引數的求和結果作為輸出,輸入引數既可以全部是Tensor資料型別的變數,也可以是一個Tensor資料型別的變數,另一個是標量。
import torch a = torch.randn(2,3) print(a) b = torch.randn(2,3) print(b) c = torch.add(a,b) print(c) d = torch.randn(2,3) print(d) e = torch.add(d,10) print(e) tensor([[-1.4372, -1.3911,0.5531], [ 1.2329, -0.1978,1.1220]]) tensor([[-1.2755,0.7442,1.3189], [-0.0558, -1.0597, -0.5731]]) tensor([[-2.7127, -0.6468,1.8720], [ 1.1771, -1.2575,0.5489]]) tensor([[ 0.7636, -0.1948,2.3720], [ 0.8740,0.2431, -0.1906]]) tensor([[10.7636,9.8052, 12.3720], [10.8740, 10.2431,9.8094]]) Process finished with exit code 0
如上所示,無論是呼叫torch.add對兩個Tensor資料型別的變數進行計算,還是完成Tensor資料型別的變數和標量的計算,計算方式都和NumPy中的陣列的加法運算如出一轍。
1.2.3 torch.clamp
對輸入引數按照自定義的範圍進行裁剪,最後將引數裁剪的結果作為輸出。所以輸入引數一共有三個,分別是需要進行裁剪的Tensor資料型別的變數、裁剪的上邊界和裁剪的下邊界,具體的裁剪過程是:使用變數中的每個元素分別和裁剪的上邊界及裁剪的下邊界的值進行比較,如果元素的值小於裁剪的下邊界的值,該元素就被重寫成裁剪的下邊界的值;同理,如果元素的值大於裁剪的上邊界的值,該元素就被重寫成裁剪的上邊界的值。
import torch a = torch.randn(2,3) print(a) b = torch.clamp(a,-0.1,0.1) print(b) tensor([[ 0.0251,1.8832,1.5243], [-0.1365,1.2307,0.0640]]) tensor([[ 0.0251,0.1000,0.1000], [-0.1000,0.1000,0.0640]]) Process finished with exit code 0
1.2.4 torch.div
將引數傳遞到torch.div後返回輸入引數的求商結果作為輸出,同樣,參與運算的引數可以全部是Tensor資料型別的變數,也可以是Tensor資料型別的變數和標量的組合。
import torch a = torch.randn(2,3) print(a) b = torch.randn(2,3) print(b) c = torch.div(a,b) print(c) d = torch.randn(2,3) print(d) e = torch.div(d,10) print(e) tensor([[-0.1323,0.3262, -0.1542], [-0.7933,1.9173,0.3522]]) tensor([[-1.3476,1.1644, -0.8035], [-0.4051,1.1651,0.4930]]) tensor([[0.0981, 0.2801, 0.1919], [1.9582, 1.6455, 0.7143]]) tensor([[-0.2241, -0.1561,0.8274], [ 1.9453,0.3524,0.3677]]) tensor([[-0.0224, -0.0156,0.0827], [ 0.1945,0.0352,0.0368]]) Process finished with exit code 0
1.2.5 torch.mul
將引數傳遞到 torch.mul後返回輸入引數求積的結果作為輸出,參與運算的引數可以全部是Tensor資料型別的變數,也可以是Tensor資料型別的變數和標量的組合。
import torch a = torch.randn(2,3) print(a) b = torch.randn(2,3) print(b) c = torch.mul(a,b) print(c) d = torch.randn(2,3) print(d) e = torch.mul(d,10) print(e) tensor([[-0.7182,1.2282,0.0594], [-1.2675,0.0491,0.3961]]) tensor([[-0.9145,1.0164, -1.1200], [ 1.0187,0.7591, -2.1201]]) tensor([[ 0.6568,1.2483, -0.0666], [-1.2912,0.0373, -0.8399]]) tensor([[-1.2548,0.2213, -1.0233], [ 0.9986,0.1143, -0.5950]]) tensor([[-12.5477,2.2128, -10.2334], [9.9864,1.1433,-5.9500]]) Process finished with exit code 0
1.2.6 torch.pow
將引數傳遞到torch.pow後返回輸入引數的求冪結果作為輸出,參與運算的引數可以全部是Tensor資料型別的變數,也可以是Tensor資料型別的變數和標量的組合。
import torch a = torch.randn(2,3) print(a) b = torch.pow(a,2) print(b) tensor([[ 0.1484, -0.5102, -0.4332], [ 0.9905,0.5156,2.8043]]) tensor([[0.0220, 0.2603, 0.1877], [0.9811, 0.2658, 7.8641]]) Process finished with exit code 0
1.2.7 torch.mm
將引數傳遞到 torch.mm後返回輸入引數的求積結果作為輸出,不過這個求積的方式和之前的torch.mul運算方式不太樣,torch.mm運用矩陣之間的乘法規則進行計算,所以被傳入的引數會被當作矩陣進行處理,引數的維度自然也要滿足矩陣乘法的前提條件,即前一個矩陣的行數必須和後一個矩陣的列數相等,否則不能進行計算。
import torch a = torch.randn(2,3) print(a) b = torch.randn(3,2) print(b) b = torch.mm(a,b) print(b) tensor([[ 1.0980, -0.8971,0.6445], [ 0.3750,1.8396, -0.8509]]) tensor([[-0.4458,1.1413], [-0.3940,0.9038], [ 1.0982,0.7131]]) tensor([[ 0.5718,0.9020], [-1.8263,1.4838]]) Process finished with exit code 0
1.2.8 torch.mv
將引數傳遞到torch.mv後返回輸入引數的求積結果作為輸出,torch.mv運用矩陣與向量之間的乘法規則進行計算,被傳入的引數中的第1個引數代表矩陣,第2個引數代表向量,順序不能顛倒。
import torch a = torch.randn(2,3) print(a) b = torch.randn(3) print(b) c = torch.mv(a,b) print(c) tensor([[-1.1866,0.1514,0.8387], [-0.1865, -1.5696, -2.4197]]) tensor([-0.7359,0.6183,0.5907]) tensor([ 1.4623, -2.2623]) Process finished with exit code 0
1.3 搭建一個簡易神經網路
下面通過一個例項來看看如何使用已經掌握的知識,搭建出一個基於PyTorch架構的簡易神經網路模型。
搭建神經網路模型的具體程式碼如下,這裡講完整的程式碼分為幾部分進行詳細介紹,以便大家瞭解。
1.3.1 匯入包
程式碼的開始處是相關包的匯入:
import torch batch_n = 100 hidden_layer = 100 input_data = 1000 output_data = 10
我們先通過import torch 匯入必要的包,然後定義4個整型變數,其中:batch_n是在一個批次中輸入資料的數量,值是100,這意味著我們在一個批次中輸入100個數據,同時,每個資料包含的資料特徵有input_data個,因為input_data的值是1000,所以每個資料的特徵就是1000個,hidden_layer用於定義經過隱藏層後保留的資料特徵的個數,這裡有100個,因為我們的模型只考慮一層隱藏層,所以在程式碼中僅僅定義了一個隱藏層的引數;output_data是輸出的資料,值是10,我們可以將輸出的資料看作一個分類結果值得數量,個數10表示我們最後要得到10個分類結果值。
一個批次的資料從輸入到輸出的完整過程是:先輸入100個具有1000個特徵的資料,經過隱藏層後變成100個具有100個特徵的資料,再經過輸出層後輸出100個具有10個分類結果值的資料,在得到輸出結果之後計算損失並進行後向傳播,這樣一次模型的訓練就完成了,然後訓練這個流程就可以完成指定次數的訓練,並達到優化模型引數的目的。
1.3.2 初始化權重
x = torch.randn(batch_n,input_data) y = torch.randn(batch_n,output_data) w1 = torch.randn(input_data,hidden_layer) w2 = torch.randn(hidden_layer,output_data)
在以上的程式碼中定義的從輸入層到隱藏層,從隱藏層到輸出層對應的權重引數,同在之前說到的過程中使用的引數維度是一致的,由於我們現在並沒有好的權重引數的初始化方法,儘管這並不是一個好主意,可以看到,在程式碼中定義的輸入層維度為(100,1000),輸出層維度為(100,10),同時,從輸入層到隱藏層的權重引數維度為(1000,100),從隱藏層到輸出層的權重引數維度為(100,10),這裡我們可能會好奇權重引數的維度是如何定義下來的,其實,只要我們把整個過程看作矩陣連續的乘法運算,九自然能夠明白了,在程式碼中我們的真實值y也是通過隨機的方式生成的,所以一開始在使用損失函式計算損失值時得到的結果會較大。
1.3.3 定義訓練次數和學習效率
在定義好輸入,輸出和權重引數值之後,就可以開始訓練模型和優化權重引數了,在此之前,我們還需要明確訓練的總次數和學習效率,程式碼如下:
epoch_n = 20 learning_rate = 1e-6
由於接下來會使用梯度下降的方法來優化神經網路的引數,所以必須定義後向傳播的次數和梯度下降使用的學習效率。在以上程式碼中使用了epoch_n定義訓練的次數,epoch_n的值為20,所以我們需要通過迴圈的方式讓程式進行20次訓練,來完成對初始化權重引數的優化和調整。在優化的過程中使用的學習效率learning_rate的值為1e-6,表示0.000001,接下來對模型進行正式訓練並對引數進行優化。
1.1.4 梯度下降優化神經網路的引數
下面程式碼通過最外層的一個大迴圈來保證我們的模型可以進行20次訓練,迴圈內的是神經網路模型具體的前向傳播和後向傳播程式碼。引數的優化和更新使用梯度
for epoch in range(epoch_n): h1 = x.mm(w1)# 100*1000 h1 = h1.clamp(min=0) y_pred = h1.mm(w2)# 100*10 # print(y_pred) loss = (y_pred - y).pow(2).sum() print("Epoch:{} , Loss:{:.4f}".format(epoch, loss)) gray_y_pred = 2 * (y_pred - y) gray_w2 = h1.t().mm(gray_y_pred) grad_h = gray_y_pred.clone() grad_h = grad_h.mm(w2.t()) grad_h.clamp_(min=0) grad_w1 = x.t().mm(grad_h) w1 -= learning_rate * grad_w1 w2 -= learning_rate * gray_w2
以上程式碼通過最外層的一個大迴圈來保證我們的模型可以進行20層訓練,迴圈內的是神經網路模型具體的前向傳播和後向傳播程式碼,引數的優化和更新使用梯度下降來完成。在這個神經網路的前向傳播中,通過兩個連續的矩陣乘法計算出預測結果,在計算的過程中還對矩陣乘積的結果使用clamp方法進行裁剪,將小於零的值全部重新賦值於0,這就像加上了一個ReLU啟用函式的功能。
前向傳播得到的預測結果通過 y_pred來表示,在得到了預測值後就可以使用預測值和真實值來計算誤差值了。我們用loss來表示誤差值,對誤差值的計算使用了均方誤差函式。之後的程式碼部分就是通過實現後向傳播來對權重引數進行優化了,為了計算方便,我們的程式碼實現使用的是每個節點的鏈式求導結果,在通過計算之後,就能夠得到每個權重引數對應的梯度分別是grad_w1和grad_w2。在得到引數的梯度值之後,按照之前定義好的學習速率對w1和w2的權重引數進行更新,在程式碼中每次訓練時,我們都會對loss的值進行列印輸出,以方便看到整個優化過程的效果,所以最後會有20個loss值被列印顯示。
1.1.5 列印結果及分析
Epoch:0 , Loss:55005852.0000 Epoch:1 , Loss:131827080.0000 Epoch:2 , Loss:455499616.0000 Epoch:3 , Loss:633762304.0000 Epoch:4 , Loss:23963018.0000 Epoch:5 , Loss:10820027.0000 Epoch:6 , Loss:6080145.5000 Epoch:7 , Loss:3903527.5000 Epoch:8 , Loss:2783492.7500 Epoch:9 , Loss:2160689.0000 Epoch:10 , Loss:1788741.0000 Epoch:11 , Loss:1549332.1250 Epoch:12 , Loss:1383139.6250 Epoch:13 , Loss:1259326.3750 Epoch:14 , Loss:1161324.7500 Epoch:15 , Loss:1080014.2500 Epoch:16 , Loss:1010260.2500 Epoch:17 , Loss:949190.7500 Epoch:18 , Loss:894736.6875 Epoch:19 , Loss:845573.3750 Process finished with exit code 0
可以看出,loss值從之前的巨大誤差逐漸縮減,這說明我們的模型經過二十次訓練和權重引數優化之後,得到的預測的值和真實值之間的差距越來越小了。
1.1.6 完整的程式碼如下:
# coding:utf-8 import torch batch_n = 100 hidden_layer = 100 input_data = 1000 output_data = 10 x = torch.randn(batch_n, input_data) y = torch.randn(batch_n, output_data) w1 = torch.randn(input_data, hidden_layer) w2 = torch.randn(hidden_layer, output_data) epoch_n = 20 learning_rate = 1e-6 for epoch in range(epoch_n): h1 = x.mm(w1)# 100*1000 h1 = h1.clamp(min=0) y_pred = h1.mm(w2)# 100*10 # print(y_pred) loss = (y_pred - y).pow(2).sum() print("Epoch:{} , Loss:{:.4f}".format(epoch, loss)) gray_y_pred = 2 * (y_pred - y) gray_w2 = h1.t().mm(gray_y_pred) grad_h = gray_y_pred.clone() grad_h = grad_h.mm(w2.t()) grad_h.clamp_(min=0) grad_w1 = x.t().mm(grad_h) w1 -= learning_rate * grad_w1 w2 -= learning_rate * gray_w2
二:自動梯度
我們在上面基於PyTorch深度學習框架搭建了一個簡易神經網路模型,並通過在程式碼中使用前向傳播和後向傳播實現了對這個模型的訓練和對權重引數的額優化,不過該模型在結構上很簡單,而且神經網路的程式碼也不復雜。我們在實踐中搭建的網路模型都是層次更深的神經網路模型,即深度神經網路模型,結構會有所變化,程式碼也會更復雜。對於深度的神經網路模型的前向傳播使用簡單的程式碼就能實現,但是很難實現涉及該模型中後向傳播梯度計算部分的程式碼,其中最苦難的就是對模型計算邏輯的梳理。
在PyTorch中提供了一種非常方便的方法,可以幫助我們實現對模型中後向傳播梯度的自動計算,避免了“重複造輪子”,這就是接下來要學習的torch.autograd包,通過torch.autograd包,可以使模型引數自動計算在優化過程中需要用到的梯度值,在很大程度上幫助降低了實現後向傳播程式碼的複雜度。
2.1 torch.autograd和Variable
torch.autograd包的主要功能是完成神經網路後向傳播中的鏈式求導,手動實現鏈式求導的程式碼會給我們造成很大的困擾,而torch.autograd包中豐富的類減少了這些不必要的麻煩。
實現自動梯度功能的過程大概分為以下幾步:
1 通過輸入的Tensor資料型別的變數在神經網路的前向傳播過程中生成一張計算圖 2 根據這個計算圖和輸出結果準確計算出每個引數需要更新的梯度 3 通過完成後向傳播完成對引數梯度的更新
在實踐中完成自動梯度需要用到torch.autograd包中的Variable類對我們定義的Tensor資料型別變數進行封裝,在封裝後,計算圖中的各個節點就是一個variable 物件,這樣才能應用自動梯度的功能。
如果已經按照如上的方式完成了相關操作,則在選中了計算圖中的某個節點時,這個節點必定是一個Variable物件,用X表示我們選中的節點,那麼X.data代表Tensor資料型別 的變數,X.grad也是一個Variable物件,不過他代表的是X的梯度,在想訪問梯度值的時候需要X.grad.data
下面通過一個自動剃度的例項來看看如何使用torch.autograd.Variable類和torch.autograd包,我們同樣搭建一個二層結構的神經網路模型,這有利於我們之前搭建的簡易神經網路模型的訓練和優化過程進行對比,重新實現。
2.1.1 匯入包
程式碼的開始處是相關包的匯入,但是在程式碼中增加一行, from torch.autograd import Variable,之前定義的不變 :
import torch from torch.autograd import Variable # 批量輸入的資料量 batch_n = 100 # 通過隱藏層後輸出的特徵數 hidden_layer = 100 # 輸入資料的特徵個數 input_data = 1000 # 最後輸出的分類結果數 output_data = 10
其中:batch_n是在一個批次中輸入資料的數量,值是100,這意味著我們在一個批次中輸入100個數據,同時,每個資料包含的資料特徵有input_data個,因為input_data的值是1000,所以每個資料的特徵就是1000個,hidden_layer用於定義經過隱藏層後保留的資料特徵的個數,這裡有100個,因為我們的模型只考慮一層隱藏層,所以在程式碼中僅僅定義了一個隱藏層的引數;output_data是輸出的資料,值是10,我們可以將輸出的資料看作一個分類結果值得數量,個數10表示我們最後要得到10個分類結果值。
2.1.2 初始化權重
x = Variable(torch.randn(batch_n , input_data) , requires_grad = False) y = Variable(torch.randn(batch_n , output_data) , requires_grad = False) w1 = Variable(torch.randn(input_data,hidden_layer),requires_grad = True) w2 = Variable(torch.randn(hidden_layer,output_data),requires_grad = True)
“Variable(torch.randn(batch_n, input_data), requires_grad = False)”這段程式碼就是之前講到的用 Variable類對 Tensor資料型別變數進行封裝的操作。在以上程式碼中還使用了一個requires_grad引數,這個引數的賦值型別是布林型,如果requires_grad的值是False,那麼表示該變數在進行自動梯度計算的過程中不會保留梯度值。我們將輸入的資料x和輸出的資料y的requires_grad引數均設定為False,這是因為這兩個變數並不是我們的模型需要優化的引數,而兩個權重w1和w2的requires_grad引數的值為True
2.1.3 定義訓練次數和學習效率
在定義好輸入,輸出和權重引數值之後,就可以開始訓練模型和優化權重引數了,在此之前,我們還需要明確訓練的總次數和學習效率,程式碼如下:
epoch_n = 20 learning_rate = 1e-6
和之前一樣,在以上程式碼中使用了epoch_n定義訓練的次數,epoch_n的值為20,所以我們需要通過迴圈的方式讓程式進行20次訓練,來完成對初始化權重引數的優化和調整。在優化的過程中使用的學習效率learning_rate的值為1e-6,表示0.000001,接下來對模型進行正式訓練並對引數進行優化。
2.1.4 新的模型訓練和引數優化
下面程式碼通過最外層的一個大迴圈來保證我們的模型可以進行20次訓練,迴圈內的是神經網路模型具體的前向傳播和後向傳播程式碼。引數的優化和更新使用梯度
for epoch in range(epoch_n): y_pred = x.mm(w1).clamp(min= 0 ).mm(w2) loss = (y_pred - y).pow(2).sum() print("Epoch:{} , Loss:{:.4f}".format(epoch, loss.data[0])) loss.backward() w1.data -= learning_rate * w1.grad.data w2.data -= learning_rate * w2.grad.data w1.grad.data.zero_() w2.grad.data.zero_()
和之前的程式碼相比,當前的程式碼更簡潔了,之前程式碼中的後向傳播計算部分變成了新程式碼中的 loss.backward(),這個函式的功能在於讓模型根據計算圖自動計算每個節點的梯度值並根據需求進行保留,有了這一步,我們的權重引數 w1.data和 w2.data就可以直接使用在自動梯度過程中求得的梯度值w1.data.grad和w2.data.grad,並結合學習速率來對現有的引數進行更新、優化了。在程式碼的最後還要將本次計算得到的各個引數節點的梯度值通過grad.data.zero_()全部置零,如果不置零,則計算的梯度值會被一直累加,這樣就會影響到後續的計算。同樣,在整個模型的訓練和優化過程中,每個迴圈都加入了列印loss值的操作,所以最後會得到20個loss值的列印輸出。
2.1.5 列印結果及分析
Epoch:0 , Loss:51193236.0000 Epoch:1 , Loss:118550784.0000 Epoch:2 , Loss:451814400.0000 Epoch:3 , Loss:715576704.0000 Epoch:4 , Loss:21757992.0000 Epoch:5 , Loss:11608872.0000 Epoch:6 , Loss:7414747.5000 Epoch:7 , Loss:5172238.5000 Epoch:8 , Loss:3814624.2500 Epoch:9 , Loss:2930500.2500 Epoch:10 , Loss:2325424.0000 Epoch:11 , Loss:1895581.7500 Epoch:12 , Loss:1581226.2500 Epoch:13 , Loss:1345434.7500 Epoch:14 , Loss:1164679.7500 Epoch:15 , Loss:1023319.6875 Epoch:16 , Loss:910640.1875 Epoch:17 , Loss:819365.0625 Epoch:18 , Loss:743999.4375 Epoch:19 , Loss:680776.3750 Process finished with exit code 0
可以看出,對引數的優化在順利進行,因為loss值也越來越低。
2.2 自定義傳播函式
其實除了可以採用自動梯度方法,我們還可以通過構建一個繼承了torch.nn.Module的新類,來完成對前向傳播函式和後向傳播函式的重寫。在這個新類中,我們使用forward作為前向傳播函式的關鍵字,使用backward作為後向傳播函式的關鍵字。下面介紹如何使用自定義傳播函式的方法,來調整之前具備自動梯度功能的簡易神經網路模型。整個程式碼的開始部分如下:
#_*_coding:utf-8_*_ import torch from torch.autograd import Variable # 批量輸入的資料量 batch_n = 100 # 通過隱藏層後輸出的特徵數 hidden_layer = 100 # 輸入資料的特徵個數 input_data = 1000 # 最後輸出的分類結果數 output_data = 10
和之前的程式碼一樣,在程式碼的開始部分同樣是匯入必要的包、類,並定義需要用到的4 個變數。下面看看新的程式碼部分是如何定義我們的前向傳播 forward 函式和後向傳播backward函式的:
class Model(torch.nn.Module): def __init__(self): super(Model,self).__init__() def forward(self,input,w1,w2): x = torch.mm(input,w1) x = torch.clamp(x,min=0) x = torch.mm(x,w2) return x def backward(self): pass
以上程式碼展示了一個比較常用的Python類的構造方式:首先通過class Model(torch.nn.Module)完成了類繼承的操作,之後分別是類的初始化,以及forward函式和backward函式。forward函式實現了模型的前向傳播中的矩陣運算,backward實現了模型的後向傳播中的自動梯度計算,後向傳播如果沒有特別的需求,則在一般情況下不用進行調整。在定義好類之後,我們就可以對其進行呼叫了,程式碼如下:
model = Model()
這一系列操作相當於完成了對簡易神經網路的搭建,然後就只剩下對模型進行訓練和對引數進行優化的部分了,程式碼如下:
#_*_coding:utf-8_*_ import torch from torch.autograd import Variable # 批量輸入的資料量 batch_n = 100 # 通過隱藏層後輸出的特徵數 hidden_layer = 100 # 輸入資料的特徵個數 input_data = 1000 # 最後輸出的分類結果數 output_data = 10 x = Variable(torch.randn(batch_n , input_data) , requires_grad = False) y = Variable(torch.randn(batch_n , output_data) , requires_grad = False) w1 = Variable(torch.randn(input_data,hidden_layer),requires_grad = True) w2 = Variable(torch.randn(hidden_layer,output_data),requires_grad = True) # 訓練次數設定為20 epoch_n = 20 # 將學習效率設定為0.000001 learning_rate = 1e-6 for epoch in range(epoch_n): y_pred = x.mm(w1).clamp(min= 0 ).mm(w2) loss = (y_pred - y).pow(2).sum() print("Epoch:{} , Loss:{:.4f}".format(epoch, loss.data[0])) loss.backward() w1.data -= learning_rate * w1.grad.data w2.data -= learning_rate * w2.grad.data w1.grad.data.zero_() w2.grad.data.zero_()
這裡,變數的賦值、訓練次數和學習速率的定義,以及模型訓練和引數優化使用的程式碼,和在 6.2.1節中使用的程式碼沒有太大的差異,不同的是,我們的模型通過“y_pred =model(x, w1, w2)”來完成對模型預測值的輸出,並且整個訓練部分的程式碼被簡化了。在20次訓練後,20個loss值的列印輸出如下:
Epoch:0 , Loss:39938204.0000 Epoch:1 , Loss:49318664.0000 Epoch:2 , Loss:147433040.0000 Epoch:3 , Loss:447003584.0000 Epoch:4 , Loss:418029088.0000 Epoch:5 , Loss:4521120.5000 Epoch:6 , Loss:3043527.5000 Epoch:7 , Loss:2294490.0000 Epoch:8 , Loss:1862741.6250 Epoch:9 , Loss:1583754.6250 Epoch:10 , Loss:1384331.2500 Epoch:11 , Loss:1230558.5000 Epoch:12 , Loss:1105670.1250 Epoch:13 , Loss:1000757.3750 Epoch:14 , Loss:910758.0625 Epoch:15 , Loss:832631.0625 Epoch:16 , Loss:764006.6250 Epoch:17 , Loss:703198.6875 Epoch:18 , Loss:649156.8125 Epoch:19 , Loss:600928.8750 Process finished with exit code 0
三:windows上安裝pytorch
PyTorch官網: ofollow,noindex" target="_blank">http://pytorch.org/
3.1,進入官網
安裝該庫就要到官網上去找安裝方法,開啟官網頁面如下:
3.2 點選Get Started
在官網頁面向下劃,滑到Get Started頁面如下所示,run this command後面對應的命令即安裝命令,右側區域為電腦系統以及CUDA版本的選擇,通過選擇你電腦的配置來決定run this command後面對應的安裝命令。
3.3 按照自己Windows的配置進行點選
下圖是我的電腦的配置,我選擇的是Windows系統,此處要根據你的選擇,之後選擇你的命令方式,我是使用pip命令的,Python的環境選擇,根據的python環境配置來選擇。
3.4 進入pip
開啟命令視窗,輸入如下程式碼,然後回車,如下圖所示開始安裝
(記得認真看自己的python版本和型號,要是32位的,就無法安裝,這是坑!!)
pip3 install http://download.pytorch.org/whl/cpu/torch-0.4.1-cp37-cp37m-win_amd64.whl pip3 install torchvision
安裝好如下(由於是國外網站,所以下載速度比較慢):
3.5 安裝torchvision
pip install torchvision