參考https://github.com/chenyuntc/pytorch-book/tree/v1.0html
但願你們直接到上面的網址去查看代碼,下面是本人的筆記git
pytorch的設計遵循tensor-> variable(autograd)-> nn.Module三個由低到高的抽象層次,分別表明高維數組(張量)、自動求導(變量)和神經網絡(層/模塊)。這三個抽象之間聯繫緊密,能夠同時進行修改和操做github
在IPython和Jupyter notebook兩個工具中使用了Jupyter notebook算法
更多安裝內容可見:深度學習PyTorch環境安裝——mac數組
1.tensor網絡
Tensor是pytorch中的重要數據結構,可認爲是一個高維數組。它能夠是一個數(標量)、一維數組(向量)、二維數組(矩陣)或更高維的數組數據結構
其簡單的用法可見pytorch學習-WHAT IS PYTORCHide
1)導入包模塊化
from __future__ import print_function import torch as t
2)僅分配空間,不初始化函數
#構建5*3矩陣,只是分配了空間,未初始化 x = t.Tensor(5,3) x
返回:
tensor([[ 0.0000e+00, 2.0000e+00, -1.0696e+12], [-1.5849e+29, 2.4158e-12, 1.1625e+33], [ 8.9605e-01, 1.1632e+33, 5.6003e-02], [ 7.0374e+22, 5.7453e-44, 0.0000e+00], [ 0.0000e+00, 2.0000e+00, -9.4044e+11]])
3)初始化
#使用[0,1]均勻分佈隨機初始化二維數組 x = t.rand(5, 3) x
返回:
tensor([[0.0136, 0.1579, 0.7545], [0.0127, 0.3194, 0.0126], [0.7604, 0.0289, 0.4808], [0.3374, 0.8478, 0.4047], [0.2441, 0.4211, 0.5480]])
4)得到size
print(x.size()) #兩種寫法,獲得二維數組的行列數 #支持該寫法的緣由是由於torch.Size是tuple對象的子類,所以其支持tuple的全部操做,如索引等 x.size()[0], x.size(1)
返回:
torch.Size([5, 3]) (5, 3)
5)加法的幾種寫法
y = t.rand(5,3) y
返回:
tensor([[0.6840, 0.5716, 0.3848], [0.8492, 0.5449, 0.3555], [0.4052, 0.5689, 0.1948], [0.2308, 0.6252, 0.8855], [0.0487, 0.2880, 0.9232]])
1>第一種
x + y
返回:
tensor([[0.6975, 0.7295, 1.1394], [0.8619, 0.8643, 0.3681], [1.1656, 0.5978, 0.6756], [0.5681, 1.4729, 1.2902], [0.2928, 0.7091, 1.4712]])
2>第二種
t.add(x,y)
結果相同
3》第三種:指定加法結果的輸出目標爲result
result = t.Tensor(5,3) #預分配空間存儲結果 t.add(x, y, out=result) result
結果相同
4》第四種:改變y的值
y.add_(x) y
y變爲上面的加法結果
函數名後面帶下劃線_的函數會修改tensor自己
2.自動微分
深度學習算法的本質是經過反向函數求導數,pytorch的Autograd模塊實現了此功能。在Tensor上的全部操做,Autograd都可以爲他們自動提供微分,避免手動計算的複雜過程
其中Autograd.Variable是Autograd的核心類。Tensor被封裝成Variable後,能夠調用它的.backward實現反向傳播,自動計算全部梯度
1)導入包
from __future__ import print_function
import torch as t
from torch.autograd import Variable
2)新建
#使用Tensor新建一個Variable x = Variable(t.ones(2, 2),requires_grad = True) x
返回:
tensor([[1., 1.], [1., 1.]], requires_grad=True)
此時查看該值的grad和grad_fn是沒有返回值的,由於沒有進行任何操做
x.grad_fn x.grad
3)進行求和操做,查看梯度
y = x.sum() y
返回:
tensor(4., grad_fn=<SumBackward0>)
這時候可查看:
y.grad_fn
返回:
<SumBackward0 at 0x122782978>
可知y是變量Variable x進行sum操做求來的,可是這個時候y.grad是沒有返回值的,由於沒有使用y進行別的操做
這個時候的x.grad也是沒有值的,雖然使用x進行了sum操做,可是尚未對y反向傳播來計算梯度
y.backward()#反向傳播,計算梯度
而後再查看:
#由於y = x.sum() = (x[0][0] + x[0][1] + x[1][0] + x[1][1]) #每一個值的梯度都爲1 x.grad
返回:
tensor([[1., 1.], [1., 1.]])
可是須要注意的一點就是grad在反向求和的過程當中是累加的,因此反向傳播前要把梯度清零
好比再運行一遍反向傳播:
y.backward() x.grad
可見返回:
tensor([[2., 2.], [2., 2.]])
清零操做:
x.grad.data.zero_()#清零
再運行便可:
y.backward() x.grad
返回:
tensor([[1., 1.], [1., 1.]])
在這裏簡單解釋一下梯度的計算,如圖示:
下面咱們使用代碼實現一下看看是否是這樣:
x = Variable(t.ones(2,2), requires_grad = True) y = x +1 z = y*y k = z.mean()
而後進行反向傳播,得到x的梯度:
k.backward() x.grad
返回果真是:
tensor([[1., 1.], [1., 1.]])
4)Variable和Tensor有近乎一致的接口,在實際中能夠無縫切換
x = Variable(t.ones(4,5)) y = t.cos(x)#傳入的是Variable x_tensor_cos = t.cos(x.data)#傳入的是Tensor print(y) x_tensor_cos
返回:
tensor([[0.5403, 0.5403, 0.5403, 0.5403, 0.5403], [0.5403, 0.5403, 0.5403, 0.5403, 0.5403], [0.5403, 0.5403, 0.5403, 0.5403, 0.5403], [0.5403, 0.5403, 0.5403, 0.5403, 0.5403]]) tensor([[0.5403, 0.5403, 0.5403, 0.5403, 0.5403], [0.5403, 0.5403, 0.5403, 0.5403, 0.5403], [0.5403, 0.5403, 0.5403, 0.5403, 0.5403], [0.5403, 0.5403, 0.5403, 0.5403, 0.5403]])
可見這樣子獲得的值並無什麼區別,返回的都是Tensor,而不是Variable
3.神經網絡
torch.nn是專門爲神經網絡設計的模塊化接口
nn構建於Autograd之上,可用來定義和運行神經網絡。nn.Module是nn中最重要的類,能夠把它看做一個網絡的封裝,包含網絡各層定義和forward方法,調用forward(input)可返回前向傳播的結果
舉例最先的卷積神經網絡LeNet(判斷圖片中的字符),來看看如何使用nn.Module實現
1)定義網絡
繼承nn.Module,並實現它的forward方法,把網絡中具備可學習參數的層放在構造函數__init__中。
若是某一層(如ReLu)不具備可學習的參數,那麼可放在構造函數中,也能夠不放
import torch.nn as nn import torch.nn.functional as F class Net(nn.Module): def __init__(self): #nn.Module子類的函數必須在構造函數中執行父類的構造函數 #下式等價於nn.Module.__init__(self) super(Net, self).__init__() #卷積層1,參數1表示輸入圖片爲單通道,即灰度圖像,爲1*32*32,6表示輸出通道數,即過濾器的通道數,5表示過濾器爲5*5 #因此下一層獲得的值爲6*28*28,做爲輸入 self.conv1 = nn.Conv2d(1, 6, 5) #卷積層2 self.conv2 = nn.Conv2d(6, 16, 5) #全鏈接層,y = Wx + b self.fc1 = nn.Linear(16*5*5, 120) self.fc2 = nn.Linear(120, 84) self.fc3 = nn.Linear(84, 10) #最後的輸出是10,是由於判斷的數值分紅了10類 def forward(self, x): #先conv1卷積->再relu激活->再max_pool池化 x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2)) #(2, 2)至關於池化將高度和寬度縮減一半,獲得結果爲6*14*14 x = F.max_pool2d(F.relu(self.conv2(x)), 2) #激活後爲16*10*10,池化後16*5*5,由於大小是正方形,因此第二個參數能夠只有一個值,效果和上面的(2,2)是相同的 #reshape,將圖形扁平化,使其由16*5*5的三維立體變成1*(16*5*5)的二維矩陣 x = x.view(x.size()[0], -1) x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) x = self.fc3(x) return x net = Net() print(net)
返回:
Net( (conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1)) (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1)) (fc1): Linear(in_features=400, out_features=120, bias=True) (fc2): Linear(in_features=120, out_features=84, bias=True) (fc3): Linear(in_features=84, out_features=10, bias=True) )
只要在nn.Module的子類中定義了forward函數,backward函數就會被自動實現(利用Autograd)
在forward函數中可使用任何Variable支持的函數
上面定義的網絡的可學習參數可經過net.Parameters()返回,net,named_parameters()可同時返回可學習的參數及名稱
params = list(net.parameters()) print(len(params)) #返回10
若是想要查看參數的名字等信息,可見:
for name, parameter in net.named_parameters(): print(name, ':', parameter.size())
返回:
conv1.weight : torch.Size([6, 1, 5, 5]) conv1.bias : torch.Size([6]) conv2.weight : torch.Size([16, 6, 5, 5]) conv2.bias : torch.Size([16]) fc1.weight : torch.Size([120, 400]) fc1.bias : torch.Size([120]) fc2.weight : torch.Size([84, 120]) fc2.bias : torch.Size([84]) fc3.weight : torch.Size([10, 84]) fc3.bias : torch.Size([10])
forward函數的輸入和輸出都是Variable,只有Variable才具備自動求導功能,Tensor是沒有的。因此在輸入時,須要把Tensor封裝成Variable
這裏nn.Conv2d的輸入必須是四維的,即樣本數*通道數*高*寬
input = Variable(t.randn(1, 1, 32, 32)) #即輸入是一張灰度的32*32大小的圖片 output = net(input) output.size()
返回:
torch.Size([1, 10])
net.zero_grad() #全部參數的梯度清零 output.backward(Variable(t.ones(1, 10))) #反向傳播
這裏的backward()中爲何須要傳入參數Variable(t.ones(1, 10))呢?沒有傳入就會報錯:
RuntimeError: grad can be implicitly created only for scalar outputs
緣由可見:pytorch的backward
這時候就能夠查看參數的梯度了:
params = list(net.parameters()) print(params[0].grad)
返回:
tensor([[[[ 1.0658e-01, -3.3146e-02, 3.7709e-03, -3.0634e-02, 1.1791e-02], [ 3.1975e-02, 4.4254e-02, -1.1230e-02, 8.9412e-02, 7.1382e-02], [-5.6357e-02, 9.1797e-05, 1.8966e-02, -4.4844e-03, 5.7771e-02], [-1.2467e-03, -6.6853e-02, -6.9389e-02, 1.6258e-02, 7.9231e-02], [-3.1562e-02, 2.0557e-02, 2.1569e-02, 8.2486e-02, -1.1457e-01]]], [[[-4.3394e-02, -4.5307e-03, 2.7980e-02, 1.8965e-03, 1.0381e-01], [-2.9320e-02, 2.8249e-02, -3.9046e-02, 1.8078e-02, -4.6308e-02], [-3.2397e-03, -1.8470e-02, -6.8865e-03, 5.4334e-03, -6.9593e-02], [-3.7685e-02, -1.2786e-01, -5.6480e-03, -5.7637e-02, -2.2518e-02], [ 6.1855e-03, 2.5202e-02, 3.0603e-02, 2.8058e-02, -6.9573e-02]]], [[[-9.2159e-02, 8.4124e-02, -7.6889e-02, -4.0251e-03, -2.3461e-02], [ 1.3258e-01, -4.1542e-02, 1.5377e-02, -2.0014e-02, -1.3080e-02], [-1.8869e-02, 7.1884e-02, -5.8952e-02, 2.0899e-02, 1.5558e-02], [ 2.7937e-02, 7.8165e-02, -4.8216e-02, -1.6983e-02, 4.5583e-02], [-7.2376e-02, 5.4218e-03, 7.5949e-02, -6.5286e-02, 3.4859e-03]]], [[[ 9.8840e-02, -9.1584e-02, 4.7770e-02, 6.2509e-03, 8.5981e-03], [ 4.2380e-02, 1.8999e-02, 2.4990e-02, 8.1884e-03, -3.5326e-02], [-1.2791e-02, 9.0136e-02, 8.5160e-06, 2.5163e-02, 7.3440e-03], [-8.7427e-02, 4.0755e-02, -1.1036e-02, 5.3797e-02, 7.8865e-03], [-3.1478e-02, 6.5078e-02, 3.5277e-02, -3.8831e-02, -8.0675e-02]]], [[[-7.8461e-03, 5.7134e-02, 5.0355e-02, -1.2885e-02, -1.3467e-02], [-1.3372e-02, -1.9114e-02, -9.7893e-02, -8.6965e-03, 1.4722e-02], [ 1.9918e-02, 2.6398e-02, 1.5144e-02, 4.7445e-02, 6.1139e-03], [-4.0234e-02, -3.6375e-02, 5.7755e-02, -1.3112e-02, -6.8822e-02], [-2.2237e-02, -3.2318e-02, 1.3830e-03, -1.1355e-02, -2.0060e-02]]], [[[-9.9372e-02, -6.5939e-02, 5.9860e-02, 1.4672e-02, -3.6069e-02], [ 3.5409e-02, -8.1433e-02, 3.1477e-03, -2.8791e-02, 5.8678e-02], [ 3.1001e-02, 5.9960e-02, -7.9541e-02, -1.0646e-01, 1.2691e-02], [ 2.8374e-02, -1.0153e-01, 4.3227e-02, 7.5082e-02, -1.0772e-02], [-4.9453e-02, -9.9464e-03, -4.2608e-02, -4.2708e-02, 5.5077e-02]]]])
⚠️torch.nn只支持mini_batches,不支持一次只輸入一個樣本,即一次必須是一個batch
若是隻想輸入一個樣本,則使用input.unsqueeze(0)將batch_size設爲1
4.損失函數
nn實現了神經網絡中大多數的損失函數,例如nn.MSELoss用來計算均方偏差,nn.CrossEntropyLoss用來計算交叉熵損失
output = net(input) print(output) target = Variable(t.arange(0, 10))
print(target) criterion = nn.MSELoss() loss = criterion(output, target) loss
運行時遇到下面的錯誤:
RuntimeError: Expected object of scalar type Float but got scalar type Long for argument #2 'target'
解決辦法就是轉換target類別爲float便可:
output = net(input) print(output) target = Variable(t.arange(0, 10)).float() #更改
print(target) criterion = nn.MSELoss() loss = criterion(output, target) loss
返回:
tensor([[-0.0552, -0.0790, 0.0176, -0.1695, -0.1261, -0.0572, 0.0072, -0.1686, 0.0739, -0.0895]], grad_fn=<AddmmBackward>) tensor([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.]) tensor(29.0489, grad_fn=<MseLossBackward>)
若是對該loss進行反向傳播溯源,即便用grad_fn屬性
首先從以前的網絡可知其整個計算圖爲:
input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d -> view -> linear -> relu -> linear -> relu -> linear -> MSELoss -> loss
當咱們使用loss.backward()時,該圖會動態生成並自動微分,也會自動計算途中參數(parameter)的導數
net.zero_grad() #把net中全部可學習的參數的梯度清零 print("反向傳播前conv1.bias的梯度:") print(net.conv1.bias.grad) loss.backward() print("反向傳播後conv1.bias的梯度:") print(net.conv1.bias.grad)
返回:
反向傳播前conv1.bias的梯度: tensor([0., 0., 0., 0., 0., 0.]) 反向傳播後conv1.bias的梯度: tensor([-0.0503, 0.0551, 0.0052, 0.0081, 0.0084, 0.0665])
5.優化器
torch.optim中實現了深度學習中絕大多數的優化方法,例如RMSProp,Adam,SGD等
import torch.optim as optim #新建一個優化器,指定要調整的參數和學習率 optimizer = optim.SGD(net.parameters(), lr = 0.01) #在訓練的過程當中 #先將梯度清零(和net.zero_grad()效果同樣) optimizer.zero_grad() #計算損失 output = net(input) loss = criterion(output, target) #反向傳播 loss.backward() #更新參數 optimizer.step()
6.數據加載和預處理
torchvision實現了經常使用的圖像數據加載功能,例如Imagenet,CIFAR10,MNIST等,以及經常使用的數據轉換操做,這極大方便了數據加載
小試牛刀:CIFAR-10分類