最近在學習PyTorch框架,買了一本《深度學習之PyTorch實戰計算機視覺》,從學習開始,小編會整理學習筆記,並博客記錄,但願本身好好學完這本書,最後能熟練應用此框架。python
PyTorch是美國互聯網巨頭Facebook在深度學習框架Torch的基礎上使用Python重寫的一個全新的深度學習框架,它更像NumPy的替代產物,不只繼承了NumPy的衆多優勢,還支持GPUs計算,在計算效率上要比NumPy有更明顯的優點;不只如此,PyTorch還有許多高級功能,好比擁有豐富的API,能夠快速完成深度神經網絡模型的搭建和訓練。因此 PyTorch一經發布,便受到了衆多開發人員和科研人員的追捧和喜好,成爲AI從業者的重要工具之一。算法
Tensorflow中數據的核心單元就是Tensor。張量包含了一個數據集合,這個數據集合就是原始值變形而來的,它能夠是一個任何維度的數據。tensor的rank就是其維度。windows
Rank本意是矩陣的秩,不過Tensor Rank和Matrix Rank的意義不太同樣,這裏就還叫Rank。Tensor Rank的意義看起來更像是維度,好比Rank =1就是向量,Rank=2 就是矩陣了,Rank = 0 就是一個值。數組
首先,咱們須要學會使用PyTorch中的Tensor。Tensor在PyTorch中負責存儲基本數據,PyTorch針對Tensor也提供了相對豐富的函數和方法,因此PyTorch中的Tensor與NumPy的數組具備極高的類似性。Tensor是一種高層次架構,也不要明白什麼是深度學習,什麼是後向傳播,如何對模型進行優化,什麼是計算圖等技術細節。更重要的是,在PyTorch中定義的Tensor數據類型能夠在GPUs上進行運算,並且只須要對變量作一些簡單的類型轉換就能輕易實現。網絡
在使用Tensor時,咱們首先要掌握如何使用Tensor來定義不一樣數據類型的變量。和Numpy差很少,PyTorch中的Tensor也有本身的數據類型定義方式,經常使用的以下:架構
此變量用於生成數據類型爲浮點型的Tensor,傳遞給torch.FloatTensor的參數能夠是一個列表,也能夠是一個維度值。框架
>>> import torch >>> a = torch.FloatTensor(2,3) >>> print(a) -0.1171 0.0000 -0.1171 0.0000 0.0000 0.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。函數
用於生成數據類型爲整型的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最後顯示的數據類型都爲整型。學習
用於生成數據類型爲浮點型且維度指定的隨機Tensor,和在Numpy中使用numpy.rand生成隨機數的方法相似,隨機生成的浮點數據在0~1區間均勻分佈。
>>> import torch >>> a = torch.rand(2,3) >>> print(a) 0.5528 0.6995 0.5719 0.4583 0.5745 0.1114 [torch.FloatTensor of size 2x3]
用於生成數據類型爲浮點型且維度指定的隨機Tensor,和在Numpy中使用numpy.randn生成隨機數的方法相似,隨機生成的浮點數的取值知足均值爲0,方差爲1的正太分佈。
>>> import torch >>> a = torch.randn(2,3) >>> print(a) -0.5341 1.2267 -1.0884 0.4008 -1.8140 1.6335 [torch.FloatTensor of size 2x3]
用於生成數據類型爲浮點型且自定義其實範圍和結束範圍的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]
用於生成數據類型爲浮點型且維度指定的Tensor,不過這個浮點型的Tensor中的元素值所有爲0.
>>> import torch >>> a = torch.zeros(2,3) >>> print(a) 0 0 0 0 0 0 [torch.FloatTensor of size 2x3]
這裏一般對Tensor數據類型的變量進行運算,來組合一些簡單或者複雜的算法,經常使用的Tensor運算以下:
將參數傳遞到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
將參數傳遞到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中的數組的加法運算一模一樣。
對輸入參數按照自定義的範圍進行裁剪,最後將參數裁剪的結果做爲輸出。因此輸入參數一共有三個,分別是須要進行裁剪的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
將參數傳遞到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
將參數傳遞到 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
將參數傳遞到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
將參數傳遞到 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
將參數傳遞到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
下面經過一個實例來看看如何使用已經掌握的知識,搭建出一個基於PyTorch架構的簡易神經網絡模型。
搭建神經網絡模型的具體代碼以下,這裏講完整的代碼分爲幾部分進行詳細介紹,以便你們瞭解。
代碼的開始處是相關包的導入:
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個分類結果值的數據,在獲得輸出結果以後計算損失並進行後向傳播,這樣一次模型的訓練就完成了,而後訓練這個流程就能夠完成指定次數的訓練,並達到優化模型參數的目的。
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也是經過隨機的方式生成的,因此一開始在使用損失函數計算損失值時獲得的結果會較大。
在定義好輸入,輸出和權重參數值以後,就能夠開始訓練模型和優化權重參數了,在此以前,咱們還須要明確訓練的總次數和學習效率,代碼以下:
epoch_n = 20 learning_rate = 1e-6
因爲接下來會使用梯度降低的方法來優化神經網絡的參數,因此必須定義後向傳播的次數和梯度降低使用的學習效率。在以上代碼中使用了epoch_n定義訓練的次數,epoch_n的值爲20,因此咱們須要經過循環的方式讓程序進行20次訓練,來完成對初始化權重參數的優化和調整。在優化的過程當中使用的學習效率learning_rate的值爲1e-6,表示0.000001,接下來對模型進行正式訓練並對參數進行優化。
下面代碼經過最外層的一個大循環來保證咱們的模型能夠進行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值被打印顯示。
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值從以前的巨大偏差逐漸縮減,這說明咱們的模型通過二十次訓練和權重參數優化以後,獲得的預測的值和真實值之間的差距愈來愈小了。
# 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包,可使模型參數自動計算在優化過程當中須要用到的梯度值,在很大程度上幫助下降了實現後向傳播代碼的複雜度。
torch.autograd包的主要功能是完成神經網絡後向傳播中的鏈式求導,手動實現鏈式求導的代碼會給咱們形成很大的困擾,而torch.autograd包中豐富的類減小了這些沒必要要的麻煩。
實現自動梯度功能的過程大概分爲如下幾步:
1 經過輸入的Tensor數據類型的變量在神經網絡的前向傳播過程當中生成一張計算圖 2 根據這個計算圖和輸出結果準確計算出每一個參數須要更新的梯度 3 經過完成後向傳播完成對參數梯度的更新
在實踐中完成自動梯度須要用到torch.autograd包中的Variable類對咱們定義的Tensor數據類型變量進行封裝,在封裝後,計算圖中的各個節點就是一個variable 對象,這樣才能應用自動梯度的功能。autograd package是PyTorch中全部神經網絡的核心。先了解一些基本知識,而後開始訓練第一個神經網絡。autograd package提供了Tensors上全部運算的自動求導功能。它是一個按運行定義(define-by-run)的框架,這意味着反向傳播是依據代碼運行狀況而定義的,而且每個單次迭代均可能不相同。
autograd.Variable 是這個package的中心類。它打包了一個Tensor,而且支持幾乎全部運算。一旦你完成了你的計算,能夠調用.backward(),全部梯度就能夠自動計算。
你可使用.data屬性來訪問原始tensor。相對於變量的梯度值能夠被積累到.grad中。
這裏還有一個類對於自動梯度的執行是很重要的:Function(函數)
變量和函數是相互關聯的,而且創建一個非循環圖。每個變量有一個.grad_fn屬性,它能夠引用一個建立了變量的函數(除了那些用戶建立的變量——他們的grad_fn是空的)。
若是想要計算導數,能夠調用Variable上的.backward()。若是變量是標量(只有一個元素),你不須要爲backward()肯定任何參數。可是,若是它有多個元素,你須要肯定grad_output參數(這是一個具備匹配形狀的tensor)。
若是已經按照如上的方式完成了相關操做,則在選中了計算圖中的某個節點時,這個節點一定是一個Variable對象,用X表示咱們選中的節點,那麼X.data表明Tensor數據類型 的變量,X.grad也是一個Variable對象,不過他表明的是X的梯度,在想訪問梯度值的時候須要X.grad.data
下面經過一個自動剃度的實例來看看如何使用torch.autograd.Variable類和torch.autograd包,咱們一樣搭建一個二層結構的神經網絡模型,這有利於咱們以前搭建的簡易神經網絡模型的訓練和優化過程進行對比,從新實現。
代碼的開始處是相關包的導入,可是在代碼中增長一行,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個分類結果值。
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
在定義好輸入,輸出和權重參數值以後,就能夠開始訓練模型和優化權重參數了,在此以前,咱們還須要明確訓練的總次數和學習效率,代碼以下:
epoch_n = 20 learning_rate = 1e-6
和以前同樣,在以上代碼中使用了epoch_n定義訓練的次數,epoch_n的值爲20,因此咱們須要經過循環的方式讓程序進行20次訓練,來完成對初始化權重參數的優化和調整。在優化的過程當中使用的學習效率learning_rate的值爲1e-6,表示0.000001,接下來對模型進行正式訓練並對參數進行優化。
下面代碼經過最外層的一個大循環來保證咱們的模型能夠進行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)) 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值的打印輸出。
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值也愈來愈低。
其實除了能夠採用自動梯度方法,咱們還能夠經過構建一個繼承了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)) 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
PyTorch官網:https://pytorch.org/
安裝該庫就要到官網上去找安裝方法,打開官網頁面以下:
在官網頁面向下劃,滑到Get Started頁面以下所示,run this command後面對應的命令即安裝命令,右側區域爲電腦系統以及CUDA版本的選擇,經過選擇你電腦的配置來決定run this command後面對應的安裝命令。
下圖是個人電腦的配置,我選擇的是Windows系統,此處要根據你的選擇,以後選擇你的命令方式,我是使用pip命令的,Python的環境選擇,根據的python環境配置來選擇。
打開命令窗口,輸入以下代碼,而後回車,以下圖所示開始安裝
(記得認真看本身的python版本和型號,要是32位的,就沒法安裝,這是坑!!)
pip3 install http://download.pytorch.org/whl/cpu/torch-0.4.1-cp37-cp37m-win_amd64.whl pip3 install torchvision
安裝好以下(因爲是國外網站,因此下載速度比較慢):
pip install torchvision