深度學習之PyTorch實戰(1)——基礎學習及搭建環境


  最近在學習PyTorch框架,買了一本《深度學習之PyTorch實戰計算機視覺》,從學習開始,小編會整理學習筆記,並博客記錄,希望自己好好學完這本書,最后能熟練應用此框架。

  PyTorch是美國互聯網巨頭Facebook在深度學習框架Torch的基礎上使用Python重寫的一個全新的深度學習框架,它更像NumPy的替代產物,不僅繼承了NumPy的眾多優點,還支持GPUs計算,在計算效率上要比NumPy有更明顯的優勢;不僅如此,PyTorch還有許多高級功能,比如擁有豐富的API,可以快速完成深度神經網絡模型的搭建和訓練。所以 PyTorch一經發布,便受到了眾多開發人員和科研人員的追捧和喜愛,成為AI從業者的重要工具之一。

知識儲備——深度學習中的常見概念

張量Tensor

  Tensorflow中數據的核心單元就是Tensor。張量包含了一個數據集合,這個數據集合就是原始值變形而來的,它可以是一個任何維度的數據。tensor的rank就是其維度。

  Rank本意是矩陣的秩,不過Tensor Rank和Matrix Rank的意義不太一樣,這里就還叫Rank。Tensor Rank的意義看起來更像是維度,比如Rank =1就是向量,Rank=2 就是矩陣了,Rank = 0 就是一個值。

一: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.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。

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.5528  0.6995  0.5719
 0.4583  0.5745  0.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.5341  1.2267 -1.0884
 0.4008 -1.8140  1.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)

 0  0  0
 0  0  0
[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 對象,這樣才能應用自動梯度的功能。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包,我們同樣搭建一個二層結構的神經網絡模型,這有利於我們之前搭建的簡易神經網絡模型的訓練和優化過程進行對比,重新實現。

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))

    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)
    y_pred = model(x, w1, 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官網:https://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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM