從基礎概念到實現,小白如何快速入門PyTorch

文章選自analyticsvidhya,機器之心編譯
PyTorch 是一個有潛力能改變深度學習實現面貌的 Python 庫,它的使用很是靈活與輕鬆。在本文中,咱們將以更實用的方式探索 PyTorch,包括基礎知識和案例研究等。此外,本文還將比較使用 NumPy 和 PyTorch 從頭構建神經網絡的方式,以瞭解它們在實現中的類似之處。
PyTorch 的構建者代表,PyTorch 的哲學是解決當務之急,也就是說即時構建和運行咱們的計算圖。這剛好適合 Python 的編程方法,由於咱們不需等待整個代碼都被寫入才能知道是否起做用。咱們很容易運行部分代碼,並實時檢查它。
PyTorch 是一個基於 Python 的庫,旨在爲深度學習提供一個靈活的開發平臺。PyTorch 的工做流程很是接近於 Python 的科學計算庫 NumPy。那麼爲何咱們須要使用 PyTorch 構建深度學習模型?如下做者根據實際經驗提供了三個理由:
  • 便於使用的 API:它的使用如同 Python 那樣簡單。
  • 支持 Python:正如上文所述,PyTorch 能夠平滑地與 Python 數據科學棧相結合。它與 NumPy 同樣簡單,甚至咱們都感受不出它們的區別。
  • 動態計算圖:PyTorch 再也不採用特定的函數預約義計算圖,而是提供構建動態計算圖的框架,甚至咱們能夠在運行時修正它們。這種動態框架在咱們不知道所構建的神經網絡須要多少內存時很是有用。
其它一些使用 PyTorch 的優勢還有多 GPU 支持、自定義數據加載器和極簡的預處理過程等。自從它在 2016 年 1 月份發佈以來,許多研究者將其採用爲標準的實現庫,由於它構建新穎的、極其複雜的計算圖一樣很是簡單。即便這樣,PyTorch 被主流數據科學家和研究員接收仍是花了很長時間,由於它目前仍然是新的項目,且還有不少地方須要構建與完善。

PyTorch 基礎

在討論 PyTorch 的各個組件前,咱們須要瞭解它的工做流。PyTorch 使用一種稱之爲 imperative / eager 的範式,即每一行代碼都要求構建一個圖以定義完整計算圖的一個部分。即便完整的計算圖尚未完成構建,咱們也能夠獨立地執行這些做爲組件的小計算圖,這種動態計算圖被稱爲「define-by-run」方法。
更多介紹請查看: pytorch.org/about/
安裝 PyTorch 很是簡單,咱們能夠按照本身的系統跟隨官方文檔的步驟輕鬆完成。例如如下選擇在 Linux、Python 3.5 和 CUDA 9.1 的環境下安裝 PyTorc
conda install pytorch torchvision cuda91 -c pytorch
咱們在基礎部分主要須要瞭解的 PyTorch 元素有 PyTorch 張量、數學運算、自動求導模塊、最優化模塊和神經網絡模塊。下面本文會依次對這些模塊進行簡要的介紹:

PyTorch 張量

正如 PyTorch 文檔所說,若是咱們熟悉 NumPy 的多維數組,那麼 Torch 張量的不少操做咱們能輕易地掌握。PyTorch 提供了 CPU 張量和 GPU 張量,而且極大地加速了計算的速度。

從張量的構建與運行就能體會到 PyTorch 相比 TensorFLow 須要聲明張量、初始化張量要簡潔地多。如下語句將隨機初始化一個 5×3 的二維張量,由於 PyTorch 是一種動態圖,因此它聲明和真實賦值是同時進行的。
torch.Tensor(5, 3)

---------------------------------------

2.4878e+04  4.5692e-41  2.4878e+04

4.5692e-41 -2.9205e+19  4.5691e-41

1.2277e-02  4.5692e-41 -4.0170e+19

4.5691e-41  1.2277e-02  4.5692e-41

0.0000e+00  0.0000e+00  0.0000e+00

[torch.FloatTensor of size 5x3]複製代碼
若咱們但願隨機初始化的張量服從某些分佈,那麼咱們能夠直接對張量對象使用一些方法。以下初始化的張量將服從均勻分佈:
torch.Tensor(5, 3).uniform_(-1, 1)

---------------------------------------------

-0.2767 -0.1082 -0.1339

-0.6477  0.3098  0.1642

-0.1125 -0.2104  0.8962

-0.6573  0.9669 -0.3806

0.8008 -0.3860  0.6816

[torch.FloatTensor of size 5x3]複製代碼
在 PyTorch 中,torch.Tensor 是一種多維矩陣,其中每一個元素都是一個單一的數據類型,且該構造函數默認的爲 torch.FloatTensor。如下是具體張量的類型:
除了直接定義維度,通常咱們還能夠從 Python 列表或 NumPy 數組中建立張量。並且根據 Python 列表和元組等數據結構的習慣,咱們可使用類似的索引方式進行取值或賦值等。如下經過 Python 列表建立一個 Torch 張量,並經過索引賦值:
>>> torch.FloatTensor([[1, 2, 3], [4, 5, 6]])

1  2  3

4  5  6

[torch.FloatTensor of size 2x3]

>>> print(x[1][2])

6.0

>>> x[0][1] = 8

>>> print(x)

1  8  3

4  5  6

[torch.FloatTensor of size 2x3]複製代碼
若 x 爲咱們定義的 5×3 Torch 張量,且初始化數值服從-1 到 1 的均勻分佈,那麼咱們能夠執行不少基礎的數學運算。如下執行了一個簡單的矩陣間對應元素乘積。
x = torch.Tensor(5, 3).uniform_(-1, 1)

y = x * torch.randn(5, 3)

print(y)

---------------------------------------------

0.2200 -0.0368  0.4494

-0.2577 -0.0343  0.1587

-0.7503 -0.1729  0.0453

0.9296 -0.1067 -0.6402

-0.3276  0.0158 -0.0552

[torch.FloatTensor of size 5x3]複製代碼
PyTorch 一樣支持廣播(Broadcasting)操做,通常它會隱式地把一個數組的異常維度調整到與另外一個算子相匹配的維度以實現維度兼容。爲了定義兩個形狀是不是可兼容的,PyTorch 會從最後開始往前逐個比較它們的維度大小。在這個過程當中,若是二者的對應維度相同,或者其一(或者全是)等於 1,則繼續進行比較,直到最前面的維度。若不知足這兩個條件,程序就會報錯。以下展現了 PyTorch 的廣播操做:
print (x.size())

y = x + torch.randn(5, 1)

print(y)

---------------------------------------------

torch.Size([5, 3])

0.1919 -0.5006 -1.2410

-0.8080  0.1407 -0.6193

-1.6629 -0.1580 -0.3921

1.0395  0.7069 -0.1459

1.9027  1.4343  1.2299

[torch.FloatTensor of size 5x3]複製代碼
正如 PyTorch 在官網上所說,PyTorch 是一個張量和動態神經網絡 Python 庫,它有着極其強大的 GPU 加速性能。咱們通常能夠直接定義 GPU 張量,也能夠由 CPU 張量轉化爲 GPU 張量。以下,咱們定義了兩個 GPU 張量,並對這兩個張量執行矩陣乘法。固然,咱們也能夠以下所示將 CPU 張量轉換爲 GPU 張量。
x = torch.cuda.HalfTensor(5, 3).uniform_(-1, 1)

y = torch.cuda.HalfTensor(3, 5).uniform_(-1, 1)

torch.matmul(x, y)

-----------------------------------------------------

0.2456  1.1543  0.5376  0.4358 -0.0369

0.8247 -0.4143 -0.7188  0.3953  0.2573

-0.1346  0.7329  0.5156  0.0864 -0.1349

-0.3555  0.3135  0.3921 -0.1428 -0.1368

-0.4385  0.5601  0.6533 -0.2793 -0.5220

[torch.cuda.HalfTensor of size 5x5 (GPU 0)]

# 如下轉化CPU張量爲GPU張量

x = torch.FloatTensor(5, 3).uniform_(-1, 1)

print(x)

x = x.cuda(device=0)

print(x)

x = x.cpu()

print(x)複製代碼

數學運算

如 NumPy 同樣,高效地實現數學函數對於科學計算庫相當重要。PyTorch 提供了一個簡單的接口,並支持 200 多種數學運算,如下是 PyTorch 實現簡單加運算的過程:
a = torch.FloatTensor([2])
b = torch.FloatTensor([3])

a + b
 5
[torch.FloatTensor of size 1]複製代碼
這種運算與 Python 很是像,咱們能夠在定義的 PyTorch 張量上執行多種矩陣運算。例如咱們能夠轉置二維張量:
matrix = torch.randn(3, 3)
matrix
-1.3531 -0.5394  0.8934
 1.7457 -0.6291 -0.0484
-1.3502 -0.6439 -1.5652
[torch.FloatTensor of size 3x3]
matrix.t()
-2.1139  1.8278  0.1976
 0.6236  0.3525  0.2660
-1.4604  0.8982  0.0428
[torch.FloatTensor of size 3x3]複製代碼

AutoGrad 模塊

TensorFlow、Caffe 和 CNTK 等大多數框架都是使用的靜態計算圖,開發者必須創建或定義一個神經網絡,並重復使用相同的結構來執行模型訓練。改變網絡的模式就意味着咱們必須從頭開始設計並定義相關的模塊。
但 PyTorch 使用的技術爲自動微分(automatic differentiation)。在這種機制下,系統會有一個 Recorder 來記錄咱們執行的運算,而後再反向計算對應的梯度。這種技術在構建神經網絡的過程當中十分強大,由於咱們能夠經過計算前向傳播過程當中參數的微分來節省時間。
from torch.autograd import Variable

x = Variable(train_x)
y = Variable(train_y, requires_grad=False)複製代碼
從概念上來講,Autograd 會維護一個圖並記錄對變量執行的全部運算。這會產生一個有向無環圖,其中葉結點爲輸入向量,根結點爲輸出向量。經過從根結點到葉結點追蹤圖的路徑,咱們能夠輕易地使用鏈式法則自動計算梯度。
在內部,Autograd 將這個圖表徵爲 Function 對象的圖,而且能夠應用 apply() 計算評估圖的結果。在計算前向傳播中,當 Autograd 在執行請求的計算時,它還會同時構建一個表徵梯度計算的圖,且每一個 Variable 的 .grad_fn 屬性就是這個圖的輸入單元。在前向傳播完成後,咱們能夠在後向傳播中根據這個動態圖來計算梯度。
如下展現了經過 backward() 和 torch.autograd.grad 計算梯度的方法,其中 torch.eq() 評估表達式是否是相等,即 x.grad 的計算結果是否是等於 2x。
x = Variable(torch.Tensor(5, 3).uniform_(-1, 1), requires_grad=True)

y = Variable(torch.Tensor(5, 3).uniform_(-1, 1), requires_grad=True)

z = x ** 2 + 3 * y

z.backward(gradient=torch.ones(5, 3))

# eq computes element-wise equality

torch.eq(x.grad, 2 * x)

----------------------------------------------------------------------

Variable containing:

1  1  1

1  1  1

1  1  1

1  1  1

1  1  1

[torch.ByteTensor of size 5x3]複製代碼
如下展現了對 y 求導的結果,即 dz/dy。從上面 z 的定義可知結果應該是 3,那麼如下展現了該計算過程:
y.grad

-------------------------------

Variable containing:

3  3  3

3  3  3

3  3  3

3  3  3

3  3  3

[torch.FloatTensor of size 5x3]複製代碼
前面是使用 backward() 求解變量的梯度,後面咱們也可使用 torch.autograd.grad 計算梯度。以下所示,咱們使用另一種方式求解同一個函數的梯度。
x = Variable(torch.Tensor(5, 3).uniform_(-1, 1), requires_grad=True)

y = Variable(torch.Tensor(5, 3).uniform_(-1, 1), requires_grad=True)

z = x ** 2 + 3 * y


dz_dx = torch.autograd.grad(z, x, grad_outputs=torch.ones(5, 3))

dz_dy = torch.autograd.grad(z, y, grad_outputs=torch.ones(5, 3))複製代碼

最優化模塊

torch.optim 是實現神經網絡中多種優化算法的模塊,它目前已經支持大多數通常的方法,因此咱們不須要從頭構建優化算法。如下展現了使用 Adam 優化器的基本代碼:
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)複製代碼

神經網絡模塊

PyTorch AutoGrad 使得計算圖的定義和梯度的計算十分簡單,但原版的 AutoGrad 可能對定義複雜的神經網絡顯得太底層,所以咱們須要神經網絡模塊幫助簡化工做。該 nn 包定義了一組函數,咱們能夠將其視爲有一些可訓練權重的神經網絡層級。咱們也能夠將該神經網絡模塊視爲相似於 Keras 的 PyTorch 組件。
咱們通常可使用 torch.nn 包構建神經網絡,下面提供了一些 API 的表達及意義:
  • 線性層- nn.Linear、nn.Bilinear
  • 卷積層 - nn.Conv1d、nn.Conv2d、nn.Conv3d、nn.ConvTranspose2d
  • 非線性激活函數- nn.Sigmoid、nn.Tanh、nn.ReLU、nn.LeakyReLU
  • 池化層 - nn.MaxPool1d、nn.AveragePool2d
  • 循環網絡 - nn.LSTM、nn.GRU
  • 歸一化 - nn.BatchNorm2d
  • Dropout - nn.Dropout、nn.Dropout2d
  • 嵌入 - nn.Embedding
  • 損失函數 - nn.MSELoss、nn.CrossEntropyLoss、nn.NLLLoss
import torch

# define model
model = torch.nn.Sequential(
 torch.nn.Linear(input_num_units, hidden_num_units),
 torch.nn.ReLU(),
 torch.nn.Linear(hidden_num_units, output_num_units),
)
loss_fn = torch.nn.CrossEntropyLoss()複製代碼
以上就是 PyTorch 的基本組件,咱們可使用它們快速構建神經網絡。固然以上只是簡單的概念介紹,每個模塊都有很是多的函數與方法,讀者可詳細查閱 PyTorch 文檔瞭解更多。

構建神經網絡(NumPy vs. PyTorch)

在這一部分中,咱們分別使用 NumPy 和 PyTorch 構建簡單的神經網絡以實現二元分類問題,本文的後面會對這一部分的代碼進行解釋。
## Neural network in numpy

import numpy as np

#Input array
X=np.array([[1,0,1,0],[1,0,1,1],[0,1,0,1]])

#Output
y=np.array([[1],[1],[0]])

#Sigmoid Function
def sigmoid (x):
 return 1/(1 + np.exp(-x))

#Derivative of Sigmoid Function
def derivatives_sigmoid(x):
 return x * (1 - x)

#Variable initialization
epoch=5000 #Setting training iterations
lr=0.1 #Setting learning rate
inputlayer_neurons = X.shape[1] #number of features in data set
hiddenlayer_neurons = 3 #number of hidden layers neurons
output_neurons = 1 #number of neurons at output layer

#weight and bias initialization
wh=np.random.uniform(size=(inputlayer_neurons,hiddenlayer_neurons))
bh=np.random.uniform(size=(1,hiddenlayer_neurons))
wout=np.random.uniform(size=(hiddenlayer_neurons,output_neurons))
bout=np.random.uniform(size=(1,output_neurons))

for i in range(epoch):
  #Forward Propogation
  hidden_layer_input1=np.dot(X,wh)
  hidden_layer_input=hidden_layer_input1 + bh
  hiddenlayer_activations = sigmoid(hidden_layer_input)
  output_layer_input1=np.dot(hiddenlayer_activations,wout)
  output_layer_input= output_layer_input1+ bout
  output = sigmoid(output_layer_input)

  #Backpropagation
  E = y-output
  slope_output_layer = derivatives_sigmoid(output)
  slope_hidden_layer = derivatives_sigmoid(hiddenlayer_activations)
  d_output = E * slope_output_layer
  Error_at_hidden_layer = d_output.dot(wout.T)
  d_hiddenlayer = Error_at_hidden_layer * slope_hidden_layer
  wout += hiddenlayer_activations.T.dot(d_output) *lr
  bout += np.sum(d_output, axis=0,keepdims=True) *lr
  wh += X.T.dot(d_hiddenlayer) *lr
  bh += np.sum(d_hiddenlayer, axis=0,keepdims=True) *lr

print('actual :\n', y, '\n')
print('predicted :\n', output)複製代碼

如今,咱們會發現使用 PyTorch 實現相同的網絡會很是簡單。如下的代碼一樣也用粗體表示出它與 NumPy 的不一樣之處:
## neural network in pytorch*import torch*

#Input array
X = *torch.Tensor*([[1,0,1,0],[1,0,1,1],[0,1,0,1]])

#Output
y = *torch.Tensor*([[1],[1],[0]])

#Sigmoid Function
def sigmoid (x):
  return 1/(1 + *torch.exp*(-x))

#Derivative of Sigmoid Function
def derivatives_sigmoid(x):
  return x * (1 - x)

#Variable initialization
epoch=5000 #Setting training iterations
lr=0.1 #Setting learning rate
inputlayer_neurons = X.shape[1] #number of features in data set
hiddenlayer_neurons = 3 #number of hidden layers neurons
output_neurons = 1 #number of neurons at output layer

#weight and bias initialization
wh=*torch.randn*(inputlayer_neurons, hiddenlayer_neurons)*.type(torch.FloatTensor)*
bh=*torch.randn*(1, hiddenlayer_neurons)*.type(torch.FloatTensor)*
wout=*torch.randn*(hiddenlayer_neurons, output_neurons)
bout=*torch.randn*(1, output_neurons)

for i in range(epoch):

  #Forward Propogation
  hidden_layer_input1 = *torch.mm*(X, wh)
  hidden_layer_input = hidden_layer_input1 + bh
  hidden_layer_activations = sigmoid(hidden_layer_input)

  output_layer_input1 = *torch.mm*(hidden_layer_activations, wout)
  output_layer_input = output_layer_input1 + bout
  output = sigmoid(output_layer_input1)

  #Backpropagation
  E = y-output
  slope_output_layer = derivatives_sigmoid(output)
  slope_hidden_layer = derivatives_sigmoid(hidden_layer_activations)
  d_output = E * slope_output_layer
  Error_at_hidden_layer = *torch.mm*(d_output, wout.t())
  d_hiddenlayer = Error_at_hidden_layer * slope_hidden_layer
  wout += *torch.mm*(hidden_layer_activations.t(), d_output) *lr
  bout += d_output.sum() *lr
  wh += *torch.mm*(X.t(), d_hiddenlayer) *lr
  bh += d_output.sum() *lr

print('actual :\n', y, '\n')
print('predicted :\n', output)複製代碼

對比其它深度學習庫

在一份基準腳本中,它展現出 PyTorch 在訓練長短時間記憶(LSTM)網絡上比其它主要框架的表現都要好,由於它運行一個 Epoch 有最少的中位數時間。
PyTorch 中的數據加載 API 通過了優良的設計,接口是針對特定數據集、採樣器和數據加載器而構建的。對比於 TensorFlow 的數據加載工具(readers, queues 等),我發現 PyTorch 的數據加載模塊更易於使用。同時它們還能無縫對接神經網絡構建模塊,因此咱們不須要第三方高級庫。
然而,我並不推薦使用使用 PyTorch 部署模型,由於 PyTorch 仍然不是那麼成熟。正如 PyTorch 開發者所說:「咱們常常看到用戶首先建立一個 PyTorch 模型來測試是否可行,而後當須要部署模型到生產中時,他們會轉化爲 Caffe 2 等其餘框架,並將其部署到移動端或其它平臺。」

案例研究

前面咱們已經瞭解了PyTorch的基本組成元素與特性,下面咱們會經過線性迴歸與手寫字體識別兩個具體的案例探討如何使用 PyTorch 構建高效地模型。

PyTorch 線性迴歸

定義數據:
import torch

from torch.autograd import Variable

x_data = Variable(torch.Tensor([[1.0], [2.0], [3.0]]))

y_data = Variable(torch.Tensor([[2.0], [4.0], [6.0]]))複製代碼
定義模型,在 PyTorch 中,咱們可使用高級 API 來定義相關的模型或層級。以下定義了「torch.nn.Linear(1, 1)」,即一個輸入變量和一個輸出變量。
class Model(torch.nn.Module):

   def __init__(self):

       """ In the constructor we instantiate two nn.Linear module """

       super(Model, self).__init__()

       self.linear = torch.nn.Linear(1, 1)  # One in and one out

   def forward(self, x):

       """ In the forward function we accept a Variable of input data and we must return a Variable of output data. We can use Modules defined in the constructor as well as arbitrary operators on Variables. """

       y_pred = self.linear(x)

       return y_pred複製代碼
構建損失函數和優化器,構建損失函數也能夠直接使用「torch.nn.MSELoss(size_average=False)」調用均方根偏差函數。優化器可使用「torch.optim.SGD()」提到用隨機梯度降低,其中咱們須要提供優化的目標和學習率等參數。
# Construct our loss function and an Optimizer. The call to model.parameters()

# in the SGD constructor will contain the learnable parameters of the two

# nn.Linear modules which are members of the model.

criterion = torch.nn.MSELoss(size_average=False)

optimizer = torch.optim.SGD(model.parameters(), lr=0.01)複製代碼
訓練模型,執行前向傳播計算損失函數,並優化參數:
# Training loop

for epoch in range(500):

       # Forward pass: Compute predicted y by passing x to the model

   y_pred = model(x_data)

   # Compute and print loss

   loss = criterion(y_pred, y_data)

   print(epoch, loss.data[0])

   # Zero gradients, perform a backward pass, and update the weights.

   optimizer.zero_grad()

   loss.backward()

   optimizer.step()複製代碼

用 PyTorch 解決圖像識別問題

爲了進一步熟悉 PyTorch,咱們將使用它解決 Analytics Vidhya 的深度學習實踐問題:識別手寫數字。咱們的問題是給定一張 28 x 28 的圖像,利用模型識別其所表明的手寫數字。
因此首先咱們須要下載訓練集與測試集,數據集包含了一個壓縮文件以儲存全部的圖像。其中 train.csv 和 test.csv 分別儲存了訓練和測試圖像,且圖像的格式爲 png。下面咱們將一步步構建簡單的神經網絡以實現手寫數字識別功能。

第 0 步:準備工做

a)導入必要的函數庫
# import modules
%pylab inline
import os
import numpy as np
import pandas as pd
from scipy.misc import imread
from sklearn.metrics import accuracy_score複製代碼
b)設置隨機的 Seed,所以咱們能控制模型產生的隨機數基本不變(僞隨機數)。
# To stop potential randomness
seed = 128
rng = np.random.RandomState(seed)複製代碼
c)設置工做目錄的路徑。
root_dir = os.path.abspath('.')
data_dir = os.path.join(root_dir, 'data')

# check for existence
os.path.exists(root_dir), os.path.exists(data_dir)複製代碼

第 1 步:加載與預處理數據

a)如今讀取 CSV 格式的數據集,並獲取文件名與對應的標註。
# load dataset
train = pd.read_csv(os.path.join(data_dir, 'Train', 'train.csv'))
test = pd.read_csv(os.path.join(data_dir, 'Test.csv'))

sample_submission = pd.read_csv(os.path.join(data_dir, 'Sample_Submission.csv'))

train.head(複製代碼
b)接下來能夠打印準備好的圖片。
# print an image
img_name = rng.choice(train.filename)
filepath = os.path.join(data_dir, 'Train', 'Images', 'train', img_name)

img = imread(filepath, flatten=True)

pylab.imshow(img, cmap='gray')
pylab.axis('off')
pylab.show()複製代碼
c)對於更簡單的數據操做,咱們能夠儲存全部的圖像做爲 NumPy 數組。
# load images to create train and test set
temp = []
for img_name in train.filename:
  image_path = os.path.join(data_dir, 'Train', 'Images', 'train', img_name)
  img = imread(image_path, flatten=True)
  img = img.astype('float32')
  temp.append(img)

train_x = np.stack(temp)

train_x /= 255.0
train_x = train_x.reshape(-1, 784).astype('float32')

temp = []
for img_name in test.filename:
  image_path = os.path.join(data_dir, 'Train', 'Images', 'test', img_name)
  img = imread(image_path, flatten=True)
  img = img.astype('float32')
  temp.append(img)

test_x = np.stack(temp)

test_x /= 255.0
test_x = test_x.reshape(-1, 784).astype('float32')

train_y = train.label.values複製代碼
d)由於這個是一個典型的機器學習問題,因此咱們能夠建立驗證集以監控模型的運行狀況。下面咱們以 7:3 的比例分割訓練集與驗證集。
# create validation set
split_size = int(train_x.shape[0]*0.7)

train_x, val_x = train_x[:split_size], train_x[split_size:]
train_y, val_y = train_y[:split_size], train_y[split_size:]複製代碼

第 2 步:構建模型

a)下面是模型的主體,咱們定義的神經網絡共有三層,即輸入層、隱藏層和輸出層。輸入層和輸出層的神經元數量是固定的,即 28 x 28 和 10 x 1,它們分別表明了輸入圖像的像素和類別。咱們在隱藏層採用了 50 個神經元,並採用 Adam 做爲最優化算法。
import torch
from torch.autograd import Variable
# number of neurons in each layer
input_num_units = 28*28
hidden_num_units = 500
output_num_units = 10

# set remaining variables
epochs = 5
batch_size = 128
learning_rate = 0.001複製代碼
b)如下將開始訓練模型。
# define model
model = torch.nn.Sequential(
  torch.nn.Linear(input_num_units, hidden_num_units),
  torch.nn.ReLU(),
  torch.nn.Linear(hidden_num_units, output_num_units),
)
loss_fn = torch.nn.CrossEntropyLoss()

# define optimization algorithm
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
## helper functions
# preprocess a batch of dataset
def preproc(unclean_batch_x):
  """Convert values to range 0-1"""
  temp_batch = unclean_batch_x / unclean_batch_x.max()

  return temp_batch

# create a batch
def batch_creator(batch_size):
  dataset_name = 'train'
  dataset_length = train_x.shape[0]

  batch_mask = rng.choice(dataset_length, batch_size)

  batch_x = eval(dataset_name + '_x')[batch_mask]
  batch_x = preproc(batch_x)

  if dataset_name == 'train':
    batch_y = eval(dataset_name).ix[batch_mask, 'label'].values

  return batch_x, batch_y
# train network
total_batch = int(train.shape[0]/batch_size)

for epoch in range(epochs):
  avg_cost = 0
  for i in range(total_batch):
    # create batch
    batch_x, batch_y = batch_creator(batch_size)

    # pass that batch for training
    x, y = Variable(torch.from_numpy(batch_x)), Variable(torch.from_numpy(batch_y), requires_grad=False)
    pred = model(x)

    # get loss
    loss = loss_fn(pred, y)

    # perform backpropagation
    loss.backward()
    optimizer.step()
    avg_cost += loss.data[0]/total_batch

  print(epoch, avg_cost)
# get training accuracy
x, y = Variable(torch.from_numpy(preproc(train_x))), Variable(torch.from_numpy(train_y), requires_grad=False)
pred = model(x)

final_pred = np.argmax(pred.data.numpy(), axis=1)

accuracy_score(train_y, final_pred)
# get validation accuracy
x, y = Variable(torch.from_numpy(preproc(val_x))), Variable(torch.from_numpy(val_y), requires_grad=False)
pred = model(x)
final_pred = np.argmax(pred.data.numpy(), axis=1)

accuracy_score(val_y, final_pred)複製代碼

  • 訓練準確度爲:0.8779008746355685
  • 測試準確度爲:0.867482993197279
這些分數很是使人滿意,由於咱們只是用簡單的神經網絡訓練了 5 個 Epoch。以上,本文介紹了簡單的 PyTorch 入門概念,並利用簡單的案例熟悉 PyTorch 的使用。讀者能夠繼續閱讀 PyTorch 的文檔以瞭解更多信息。


相關文章
相關標籤/搜索