原文地址:https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.htmlhtml
什麼是pytorch?python
pytorch是一個基於python語言的的科學計算包,主要分爲兩種受衆:數組
開始網絡
Tensors框架
Tensors與numpy的ndarray類似,且Tensors能使用GPU進行加速計算。dom
建立5 * 3的未初始化矩陣:ide
建立並隨機初始化矩陣:函數
建立一個類型爲long且值全爲0的矩陣:oop
直接賦值建立tensor:學習
使用已有的tensor建立一個tensor,這個方法能使用已有tensor的屬性如類型:
new_* 方法的參數是張量形狀大小
重寫類型,結果具備相同的形狀大小
獲取張量的尺寸大小:
注:torch.Size其實是一個元組,因此它支持全部的元組操做。
pytorch有多種運算的語法。在下列例子中,咱們看一下加法運算:
加法1
加法2:
加法:提供一個輸出tensor做爲參數
加法:in-place ( 至關於+= ,將結果直接賦值到左側的變量)
注:任何方法名中使用「_」的都是in-place操做,好比x.copy_(y),x.t_(),都會使x的值發生改變。
同時,你也可使用標準NumPy風格的索引操做:
Resizing:若是你想要resize或reshape一個張量,可使用方法 torch.view :
若是張量只有一個元素,可使用.item()來獲取一個python數值
Torch的張量和NumPy數組的相互轉換,他們共享底層的內存位置,更改一個另外一個也會改變。
張量轉換爲NumPy數組:
當張量的值改變時,numpy數組的值也同時改變:
Numpy數組轉換爲張量:
除了字符張量,全部在CPU上的張量都能與NumPy數組相互轉換。
張量可以被轉移到任何設備經過使用 .to 方法。
autograd包在pytorch中是全部神經網絡的核心,咱們先看一下而後將會訓練本身的神經網絡。
autograd包給全部張量運算提供了自動微分。他是一個運行定義框架(define-by-run framework),意爲着被你的代碼如何運行所定義,每次的單個迭代都是不一樣的。
讓咱們用簡單的術語和一些例子來看下:
torch.Tensor是核心類。若是你設置了它的屬性 .requires_grad爲True, 它就開始跟蹤全部的運算。當完成全部的計算能夠經過 .backward() 而後就能夠自動進行全部的梯度計算。該張量的全部梯度將會累加到.grad() 屬性。
經過調用 .detach() 方法能夠中止一個張量對計算記錄的追蹤。
另外一種阻止張量計算追蹤的方法是,將代碼塊寫入 with torch.no_grad(): ,這在訓練中評價測試一個模型的時候尤爲有用,由於咱們設置了requires_grad =True,而此時並不須要梯度。
自動微分的另外一個重要實現是類Function。
Tensor和Function是相互聯繫的並創建了一個非循環圖,記錄了計算的完整歷史。每一個張量有.grad_fn屬性,這個屬性與建立張量的Function相關聯(除了用戶本身建立張量,該屬性grad_fn is None)
若是你想要計算導數,你能夠調用張量的.backward()方法,若是張量是一個標量,如只有一個元素的數據,則backward()不須要指定任何參數;若是其有更多參數,則須要指定一個具備匹配張量大小的gradient參數。
建立一個張量並設置 requires_grad = True來追蹤計算:
張量運算:
y是做爲運算的結果所建立的,因此它有屬性 grad_fn。
y的更多運算:
.require_grad_()方法能夠改變張量requires_grad的值,若是沒指定的話,默認值爲False。
開始反向傳播。由於 out 包含一個單一的標量,out.backward() 至關於 out.backward((torch.tensor(1.))) 。
打印梯度d(out)/dx:
x = [ [1, 1], [1, 1] ] y = x + 2 z = y * y * 3 out = z.mean d(out)/dx = 3/2(x + 2)
當.requires_grad=True 時,你也能夠經過寫入代碼塊with torch.no_grad()中止追蹤歷史和自動微分:
torch.nn能夠建立神經網絡,nn依賴於 autograd 來定義模型和微分。nn.Moudle包括各類層和返回output的方法forward(input)。
例如,如下爲對數字圖片進行分類的網絡:
一個簡單的前饋網絡,得到輸入並通過一個個網絡層,最終獲得輸出。
如下是一個神經網絡的訓練步驟:
#encoding:utf-8 import torch import torch.nn as nn import torch.nn.functional as F class Net(nn.Module): def __init__(self): super(Net,self).__init__() #定義2個卷積層 self.conv1=nn.Conv2d(in_channels=1,out_channels=6,kernel_size=5) self.conv2=nn.Conv2d(in_channels=6,out_channels=16,kernel_size=5) #定義了3個線性層,即y=wx+b self.fc1=nn.Linear(in_features=16*5*5,out_features=120) self.fc2=nn.Linear(120,84) self.fc3=nn.Linear(84,10) def forward(self, x): #在第一層卷積網絡和第二層之間增長了relu激活函數和池化層 x=F.max_pool2d(input=F.relu(input=self.conv1(x)),kernel_size=(2,2)) #若是池化層是方塊形的能夠用一個number指定 x=F.max_pool2d(input=F.relu(input=self.conv2(x)),kernel_size=2) x=x.view(-1,self.num_flat_features(x)) x=F.relu(input=self.fc1(x)) x=F.relu(self.fc2(x)) x=self.fc3(x) return x def num_flat_features(self,x): size=x.size()[1:]#切片0裏面放的是當前訓練batch的number,不是特徵信息 num_features=1 for s in size: num_features*=s return num_features 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) )
你只要定義前向函數,當你使用 autograd 時,後向傳播函數將會自動定義。在前向函數中你可使用任何張量運算。
模型的可學習參數由 net.parameters() 返回。
params = list(net.parameters()) print(len(params)) print(params[0].size()) # conv1's .weight
這個網絡適合32*32的數據集(1*32*32通過一個核心爲5的卷積層,大小變爲6*28*28;通過一個核心爲2的池化層,大小變爲6*14*14;再通過一個核心爲5的卷積層,大小變爲16*10*10;池化層,16*5*5,正好是下一個線性層的輸入特徵數),咱們隨機一個輸入數據集,運行一下咱們的模型吧:
input = torch.randn(1, 1, 32, 32) out = net(input) print(out)
tensor([[-8.2934e-03, -1.9684e-02, -7.6836e-02, 5.3187e-02, 1.1083e-01, -2.5156e-02, -6.1870e-05, -8.3843e-03, 4.1401e-02, -5.8330e-02]], grad_fn=<AddmmBackward>)
將全部參數的梯度設爲0,使用隨機梯度進行反向傳播:
net.zero_grad()
out.backward(torch.randn(1, 10))
注:torch.nn只支持批量數據的處理,而不是單個樣本。例如,nn.Conv2d 將會使用 4D張量,nSamples x nChannels x Height x Width,若是隻有單個樣本,可使用input.unsqueeze(0)來增長一個假的batch維度。
回顧下剛學到的幾個類:
損失函數計算輸出和目標之間的值,來衡量二者的差距。在nn中有多種不一樣的損失函數,一個簡單的損失函數是:nn.MSELoss,它返回的是均方差,即每個份量相減的平方累計最後除份量的個數。
output = net(input) target = torch.randn(10) # a dummy target, for example target = target.view(1, -1) # make it the same shape as output criterion = nn.MSELoss() loss = criterion(output, target) print(loss)
tensor(0.9531, grad_fn=<MseLossBackward>)
若是你沿着loss反向傳播的方向,用.grad_fn屬性能夠看到計算圖:
input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d -> view -> linear -> relu -> linear -> relu -> linear -> MSELoss -> loss
因此,當你調用loss.backward()時,在整個計算圖都會進行微分,全部requires_grad=True的張量的.grad屬性都會累加。
爲了說明,打印以下少數幾步的反向傳播:
print(loss.grad_fn) # MSELoss print(loss.grad_fn.next_functions[0][0]) # Linear print(loss.grad_fn.next_functions[0][0].next_functions[0][0]) # ReLU
<MseLossBackward object at 0x7f6e48d5ee48> <AddmmBackward object at 0x7f6e48d5eac8> <AccumulateGrad object at 0x7f6e48d5eac8>
爲了反向傳播偏差咱們須要作的只要使用 loss.backward() ,同時也須要清除已有的梯度,不然梯度會累加到已經存在的梯度。
調用 loss.backward() ,觀察conv1 層bias的梯度反向傳播先後的變化
net.zero_grad() # zeroes the gradient buffers of all parameters print('conv1.bias.grad before backward') print(net.conv1.bias.grad) loss.backward() print('conv1.bias.grad after backward') print(net.conv1.bias.grad)
conv1.bias.grad before backward
tensor([0., 0., 0., 0., 0., 0.])
conv1.bias.grad after backward
tensor([-0.0022, 0.0022, -0.0139, 0.0072, 0.0029, 0.0035])
如今咱們已經知道了如何使用損失函數。
在實際中使用的最簡單的更新規則是隨即梯度降低SGD:
weight = weight - learning_rate * gradient
咱們能夠簡單地使用python代碼進行實現:
learning_rate = 0.01 for f in net.parameters(): f.data.sub_(f.grad.data * learning_rate)
若是須要使用其餘的更新規則,好比SGD,Nesterov-SGD, Adam, RMSProp等等,pytorch也提供了相關的包:torch.optim ,實現了這些方法。
使用實例:
import torch.optim as optim # create your optimizer optimizer = optim.SGD(net.parameters(), lr=0.01) # in your training loop: optimizer.zero_grad() # zero the gradient buffers output = net(input) loss = criterion(output, target) loss.backward() optimizer.step() # Does the update
注:代碼中咱們手動將梯度設置爲0,optimizer.zero_grad(),這是由於梯度不置0的話將會累加之前的值,在反向傳播那一節咱們也提到過。
咱們已經知道了如何定義網絡,計算損失和更新網絡權重,那咱們將會思考,那數據呢?
一般來說,當你處理圖片,文本,語音和視頻數據,可使用標準的python包來加載數據爲numpy數組,而後轉換爲張量torch.*Tensor。
對於視覺,咱們專門建立了一個名爲 torchvision 的包,其擁有加載普通數據集(Imagenet, CIFAR10, MNIST)的加載器和圖片數據轉換器,即torchvision.datasets 和
torch.utils.data.DataLoader。
在本教程中,咱們將使用CIFAR10數據集。它有10個類別:‘airplane’, ‘automobile’, ‘bird’, ‘cat’, ‘deer’, ‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’. CIFAR10中的圖像的大小爲3*32*32,即大小爲32*32的有3個通道的彩色圖像。
接下來咱們要作一步步完成如下內容:
import torch import torchvision import torchvision.transforms as transforms
torchvision數據集的輸出是在[0,1]範圍的]PILImage,咱們將其轉化爲歸一化範圍[-1, 1]的張量。
transform = transforms.Compose( [transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform) trainloader = torch.utils.data.DataLoader(trainset, batch_size=4, shuffle=True, num_workers=2) testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform) testloader = torch.utils.data.DataLoader(testset, batch_size=4, shuffle=False, num_workers=2) classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./data/cifar-10-python.tar.gz Files already downloaded and verified
使用matplotlib庫查看訓練數據:
import matplotlib.pyplot as plt import numpy as np # functions to show an image def imshow(img): img = img / 2 + 0.5 # unnormalize npimg = img.numpy() plt.imshow(np.transpose(npimg, (1, 2, 0))) plt.show() # get some random training images dataiter = iter(trainloader) images, labels = dataiter.next() # show images imshow(torchvision.utils.make_grid(images)) # print labels print(' '.join('%5s' % classes[labels[j]] for j in range(4)))
frog bird truck ship
複製神經網絡那一節的代碼,而後將輸入圖像的通道改成3個,
import torch.nn as nn import torch.nn.functional as F class Net(nn.Module): def __init__(self): super(Net,self).__init__() self.conv1=nn.Conv2d(3,6,5) self.pool=nn.MaxPool2d(kernel_size=2,stride=2) self.conv2=nn.Conv2d(6,16,5) self.fc1=nn.Linear(16*5*5,120) self.fc2=nn.Linear(in_features=120,out_features=84) self.fc3 = nn.Linear(in_features=84, out_features=10) def forward(self, x): x=self.pool(F.relu(self.conv1(x))) x=self.pool(F.relu(self.conv2(x))) x=x.view(-1,16*5*5) 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(3, 6, kernel_size=(5, 5), stride=(1, 1)) (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (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) )
使用分類的交叉熵損失函數和帶動量的隨機梯度降低。
import torch.optim as optim criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
for epoch in range(2):#循環整個數據集多少遍 running_loss=0.0 for i,data in enumerate(trainloader,start=0): #get the inputs inputs, labels = data # 梯度清0 optimizer.zero_grad() #forward + backword + optimize outputs = net(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() #打印統計信息 running_loss += loss.item() if i % 2000 == 1999: #每2000個mini-batches打印一次 print("[%d, %d] loss: %.3f" % (epoch+1, i+1, running_loss/2000)) running_loss=0.0 print('Finished Training')