PyTorch學習筆記


【PyTorch深度學習60分鍾快速入門 】Part1:PyTorch是什么?來源:https://www.cnblogs.com/leejack/p/8370634.html
import torch 
import numpy as np #用於替代NumPy(torch對象叫張量,帶前綴和括號,並用逗號分隔,numpy對象叫數組,用空格分隔),#torch可以使用GPU的計算力,一種深度學習研究平台,可以提供最大的靈活性和速度
x = torch.Tensor(5, 3) #創建一個5x3且未初始化的矩陣,如果將首字母大寫的Tensor改成小寫tensor,會報錯
#print(x)
x = torch.rand(5, 3)#創建一個隨機初始化的矩陣rand表示0~1之間均勻分布的隨機數
#print(x)
#print(x.size())
y = torch.rand(5, 3)
#print(x + y)
#print(torch.add(x, y))  
result = torch.Tensor(5, 3)
#print(result)
torch.add(x, y, out=result)
#print(result)
y.add_(x) #原地替換,任何原地改變張量值的操作后綴都是固定的_,例如:x.copy_(y)、x.t_(),都將原地改變x的值。
#print(y)
x = torch.randn(4, 4)
y = x.view(16) #調整或重塑張量形狀,使用torch.view,相當於numpy中的reshape
z = x.view(-1, 8)  # the size -1 is inferred from other dimensions,-1和2結果一樣,可以從其size中看出來(2,8)
print(y,z)
print(x.size(), y.size(), z.size())

a = torch.ones(5)#ones和numpy中的ones一樣
print("a:",a)
b = a.numpy()#將torch張量對象轉換為numpy中的數組對象
print("b:",b)

a = np.ones(5)
b = torch.from_numpy(a)#將numpy中的數組對象轉換為torch張量對象,b=a.torch()不可以,只能用torch.from_numpy(?)

#在CPU上的所有的張量,除了CharTensor之外,都支持轉換成NumPy對象,也支持反向轉換。
np.add(a, 1, out=a)#將a +1后 再回傳給a
print('a:',a)
print('b:',b)
if torch.cuda.is_available():#使用.cuda函數可以將張量移到GPU上進行計算
    x = x.cuda()
    y = y.cuda()
    x + y
else:
    print('cuda is not available')
tensor([ 0.3856, -0.3865, -0.5026,  0.8776, -2.5368, -1.7295,  0.0219,  1.2241,
         1.4038, -0.8838, -0.1019,  2.1651, -0.3457, -0.5027,  0.0651,  0.1814]) tensor([[ 0.3856, -0.3865, -0.5026,  0.8776, -2.5368, -1.7295,  0.0219,  1.2241],
        [ 1.4038, -0.8838, -0.1019,  2.1651, -0.3457, -0.5027,  0.0651,  0.1814]])
torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])
a: tensor([1., 1., 1., 1., 1.])
b: [1. 1. 1. 1. 1.]
a: [2. 2. 2. 2. 2.]
b: tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
cuda is not available

 

 
         

#【PyTorch深度學習60分鍾快速入門 】Part2:Autograd自動化微分,

 
         

#在PyTorch中,集中於所有神經網絡的是autograd包。
#autograd.Variable是autograd包的核心類,它封裝了一個張量,並支持幾乎所有在該張量上定義的操作。
#一旦完成了你的計算,你可以調用.backward(),它會自動計算所有梯度。你可以通過.data屬性訪問原始的張量,而梯度w.r.t.這個變量被累積到.grad。
#還有一個類對於autograd的實現非常重要——一個函數。變量和函數是相互聯系的,並建立一個非循環圖,它編碼了計算的一個完整歷史。
#每個變量都有一個.grad_fn屬性,該屬性引用了一個創建了該變量的函數(除了由用戶創建的變量之外,它們的grad_fn是None)。
#如果你想計算導數,你可以在一個變量上調用.backward()。如果變量是一個標量(也就是說它包含一個元素數據),那么你不需要為backward()指定任何參數,
#但是如果它是矢量,有更多元素,那么你需要指定一個grad_output參數,該參數是一個匹配形狀的張量。


import
torch from torch.autograd import Variable x = Variable(torch.ones(2, 2), requires_grad=True)#創建一個變量: y = x + 2 z = y * y * 3#對y做更多的操作 out = z.mean() print(x,y,z,out)#在打印出來的結果中,x值的后面攜帶了是否要求梯度的信息,y,z,out的值后面則各攜帶了一個grad_fn的編號,按計算公式名稱和順序編號 print(y.grad_fn,z.grad_fn,out.grad_fn)#y,z,out是由於操作而創建的,所以它各自都有一個grad_fn,相應的編號和地址參見打印結果 #現在我們介紹后向傳播 #backward函數是反向求導數,使用鏈式法則求導,如果是標量則可以直接求導, #對矢量(即多維變量)y求導,需要額外指定grad_tensors,grad_tensors的shape必須和y的相同 #y.backward(torch.Tensor(2,2)) #y可以和out或者z同時求梯度,沒有沖突 #z.backward(torch.Tensor(2,2)) #z和out不能同時求梯度會報錯,有沖突 out.backward()#out.backward()等效於做out.backward(torch.Tensor([1.0])),若括號里面改成torch.Tensor([1.0,1.0]結果會依次增加4.5,為什么? print(x.grad)#打印梯度d(out)/dx, 梯度(Gradients)即導數或偏導數, z=3(x+2)^2,out對xi的偏導數=3(xi+2)/2,xi=1,所以結果=4.5 print("*********************************************************") #backward函數中還有retain_graph參數 ,使用retain_graph參數的時候,再次求導的時候,會對之前的導數進行累加 #如果默認不設置即requires_grad=None,則會報錯,無法反向求導2次 x=Variable(torch.Tensor([1,5,6,10,16]),requires_grad=True) #需要求導數 y=x*x weights0=torch.ones(5) y.backward(weights0,retain_graph=True) print(x.grad) weights1=torch.FloatTensor([0.1,0.1,0.1,0.1,0.1]) y.backward(weights1,retain_graph=True) print(x.grad) weights2=torch.FloatTensor([0.5,0.1,0.1,0.1,0.2]) y.backward(weights2,retain_graph=True) print(x.grad) print("*********************************************************") #你可以利用梯度做很多瘋狂的事情! x = torch.randn(3)#生成均值為0,標准差為1的3個標准正態分布,一維張量 x = Variable(x, requires_grad=True) y = x * 2 while y.data.norm() < 1000:#括號未寫內容默認為求2范數,關於范數的解釋見下面代碼 y = y * 2 #2的n次方,當n達到10的時候,y=1024 print(y) gradients = torch.FloatTensor([0.1, 1.0, 10])#因為x=torch.randn(3)是3個數,所以需要給出3個權重 y.backward(gradients) print(x.grad) #y對x的導數就是2的n次方 print("*********************************************************") #范數(norm)是數學中的一種基本概念。在泛函分析中,它定義在賦范線性空間中,並滿足一定的條件,即①非負性;②齊次性;③三角不等式。 #它常常被用來度量某個向量空間(或矩陣)中的每個向量的長度或大小。 #常用的三種p-范數推導出的矩陣范數: #1-范數:║A║1 = max{ ∑|ai1|,∑|ai2|,……,∑|ain| } (列和范數,A每一列元素絕對值之和的最大值) #(其中∑|ai1|第一列元素絕對值的和∑|ai1|=|a11|+|a21|+...+|an1|,其余類似); #2-范數:║A║2 = A的最大奇異值 = (max{ λi(AH*A) }) 1/2 (譜范數,即A^H*A特征值λi中最大者λ1的平方根,其中AH為A的轉置共軛矩陣); #∞-范數:║A║∞ = max{ ∑|a1j|,∑|a2j|,...,∑|amj| } (行和范數,A每一行元素絕對值之和的最大值)(其中∑|a1j| 為第一行元素絕對值的和,其余類似); #其它的p-范數則沒有很簡單的表達式。 a = torch.ones((2,3)) #建立tensor a1 = torch.norm(a,p=1) #指定求1范數,等價於a1 = a.data.norm(p=1) a2 = torch.norm(a) #默認求2范數,等價於a2 = a.data.norm(p=2) print(a) print(a1) print(a2) #求指定維度上的范數,返回輸入張量給定維dim 上每行的p 范數 a = torch.tensor([[1, 2, 3, 4],[1, 2, 3, 4]]).float() #norm僅支持floatTensor,a是一個2*4的Tensor a0 = torch.norm(a,p=2,dim=0) #按0維度求2范數,按列 a1 = torch.norm(a,p=2,dim=1) #按1維度求2范數,按行 print(a0) print(a1) #可以看輸出,dim=0是對0維度上的一個向量求范數,返回結果數量等於其列的個數,也就是說有多少個0維度的向量,將得到多少個范數。dim=1同理。 #再看keepdim,其含義是保持輸出的維度,挺抽象的,我們還是通過具體例子來看吧。 a = torch.rand((2,3,4)) at = torch.norm(a,p=2,dim=1,keepdim=True) #保持維度 af = torch.norm(a,p=2,dim=1,keepdim=False) #不保持維度,默認為false,經過計算后3維降為2維 print(a.shape) print(at.shape) print(af.shape)
tensor([[1., 1.],
        [1., 1.]], requires_grad=True) tensor([[3., 3.],
        [3., 3.]], grad_fn=<AddBackward0>) tensor([[27., 27.],
        [27., 27.]], grad_fn=<MulBackward0>) tensor(27., grad_fn=<MeanBackward1>)
<AddBackward0 object at 0x00000000061D8278> <MulBackward0 object at 0x000000000995FC50> <MeanBackward1 object at 0x000000000995FDA0>
tensor([[4.5000, 4.5000],
        [4.5000, 4.5000]])
*********************************************************
tensor([ 2., 10., 12., 20., 32.])
tensor([ 2.2000, 11.0000, 13.2000, 22.0000, 35.2000])
tensor([ 3.2000, 12.0000, 14.4000, 24.0000, 41.6000])
*********************************************************
tensor([-810.8790, -148.9206, -800.3726], grad_fn=<MulBackward0>)
tensor([  51.2000,  512.0000, 5120.0000])
*********************************************************
tensor([[1., 1., 1.],
        [1., 1., 1.]])
tensor(6.)
tensor(2.4495)
tensor([1.4142, 2.8284, 4.2426, 5.6569])
tensor([5.4772, 5.4772])
torch.Size([2, 3, 4])
torch.Size([2, 1, 4])
torch.Size([2, 4])

#【PyTorch深度學習60分鍾快速入門 】Part3:神經網絡
#神經網絡可以通過使用torch.nn包來構建。
#既然你已經了解了autograd,而nn依賴於autograd來定義模型並對其求微分。一個nn.Module包含多個網絡層,以及一個前向函數forward(input)
#例如,查看下圖中,對圖片分類的網絡:圖片參見https://www.cnblogs.com/leejack/p/8387771.html
#這是一個簡單的前饋網絡。它接受輸入,並將輸入依次通過多個層,然后給出輸出結果。對於神經網絡來說,一個經典的訓練過程包括以下步驟:
#定義一個包含一些可學習的參數(或權重)的神經網絡,對輸入數據集進行迭代,通過網絡處理輸入,計算損失函數(即輸出距離正確值差多遠)
#將梯度傳播回網絡參數,更新網絡的權重,通常使用一個簡單的更新規則:weight = weight - learning_rate * gradient
#下面,我們定義該網絡:

import torch
from torch.autograd import Variable
import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module): #所定義的Net是從nn.Module框架中繼承而來的,而不是一個新玩意

    def __init__(self):#定義一個初始化函數,當調用這個Net的時候,內部會進行初始化
        
        super(Net, self).__init__() #等價於nn.Module.__init__(self)這里首先初始化(清空)所定義的網絡
        
        #以下是配置網絡內容及參數:本例中配置了兩次卷積和三次全連接(前饋神經網絡也稱為全連接或多層感知器),前饋函數單獨定義,不含在初始化里面。
        
        # 1 input image channel, 6 output channels, 5x5 square convolution,# kernel 
        self.conv1 = nn.Conv2d(1, 6, 5) #"1"是圖像通道,kernel 5(自定義邊長5*5),經卷積后size變成((n-(5-1)),(n-(5-1))),6層是自定義的嗎?
        self.conv2 = nn.Conv2d(6, 16, 5)#16層是自定義的嗎?
        # an affine operation: y = Wx + b 全連接層,又名仿射層,輸出y和輸入x滿足y=Wx +b,W和b是可以學習的參數,后續可以調用參數來獲得W和b的值
        self.fc1 = nn.Linear(16 * 5 * 5, 120) #原圖片32*32像素經過兩次卷積和兩次池化后大小變成了5*5,共有16個。120是自定義的嗎?
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):#定義前向forward函數,引出自變量x,返回計算結果return x,反向(backward)函數使用autograd自動定義,在前向函數中可使用任何張量操作。
        # Max pooling over a (2, 2) window 通過(2,2)的最大池化操作以后,行數和列數就變成了原來的一半(因為是2個中選擇一個大的)
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2)) #先調用kernal卷積conv1計算一次,再由激活函數relu計算一次,最后再經過最大池化計算一次
        # If the size is a square you can only specify a single number
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)#接上一步,將經過一輪計算的x再調用conv2計算一次,再relu一次,再最大池化一次
        x = x.view(-1, self.num_flat_features(x)) #將x的size統一變成二維,其中用-1表示組數,可以自適應原先的組數,其余的合並,具體參見下方解釋,
        x = F.relu(self.fc1(x))#調用上面定義好size的線性函數fc1計算一次,然后再relu一次
        x = F.relu(self.fc2(x))#繼續調用fc2計算一次,然后再relu一次
        x = self.fc3(x)#最后再做一次線性fc3,最終的結果就是二維的,反向求導時盡管是10個特征值,out.backward(torch.randn(1, 10)),若寫(10)不行
        return x

    def num_flat_features(self, x): #特征扁平化處理,這段函數的目的就是為了將特征重構
        size = x.size()[1:]  #[1:],其中的1:就是將除了第一維的組數保留外,其余全部通過下面的乘法計算總數,並利用上面的view(-1,)合並成2維
        # all dimensions except the batch dimension, x.size()[開始值:結尾值],反饋x有幾維,取其中幾維的值
        #比如x=torch.rand((2,3,4)),則x.size()等效於x.size()[0:3]為[2,3,4],x.size()[1:]為[3,4],x.size()[1:3]為[3,4]
        num_features = 1
        for s in size:
            num_features *= s
        return num_features

net = Net()
print(net)

print("***********************************************************")

def num_flat_features(x):
        size = x.size()[1:]  
        # all dimensions except the batch dimension, x.size()[開始值:結尾值],反饋x有幾維,取其中幾維的值
        #比如x=torch.rand((2,3,4)),則x.size()等效於x.size()[0:3]為[2,3,4],x.size()[1:]為[3,4],x.size()[1:3]為[3,4]
        num_features = 1
        for s in size:
            num_features *= s
        return num_feature
    
x=torch.rand(2,3,5)
x.view(-1,num_flat_features(x))
print(x,num_flat_features(x),x.size())
print(x.view(-1,num_flat_features(x)))

print("***********************************************************")
#一個模型的可學習參數由net.parameters()返回
params = list(net.parameters())
print(len(params)) #上面構造的前饋神經網絡共計包含10個參數,net return x的結果中恰好也是10個值
print(params[1])  
#params[0].size()四維[6,1,5,5],表示6個大組,每個大組有1個小組(增加一個方括號而已),每小組內有25個(5*5)數據conv1's .weight, 每次運行值不一樣,值是如何設定的?
#params[1].size()一維[6],表示6個值,tensor([ 0.1707,  0.1464, -0.0306, -0.1523, -0.1115,  0.0629],requires_grad=True)值每次運行也會變,值是如何設定的?
#params[2].size()四維[16,6,5,5],表示16個大組,每個大組有6個小組,每小組內有25個(5*5)數據conv2's .weight,每次運行值不一樣,值是如何設定的?
#params[3].size()一維[16],表示16個值,tensor([-0.0175,  0.0498, -0.0686,  ..., -0.0419],requires_grad=True),每次運行值不一樣,值是如何設定的?
#params[4].size()二維[120,400],表示120個組,每組400個數據的列表:W1
#params[5].size()一維[120],表示120個值:b1
#params[6].size()二維[84,120],表示84個組,每組120個數據的列表:W2
#params[7].size()一維[84],表示84個值:b2
#params[8].size()二維[10,84],表示10個組,每組84個數據的列表:W3
#params[9].size()一維[10],表示10個值:b3,tensor([-0.0595, -0.0891, 0.0139, ..., -0.0213], requires_grad=True),每次運行值不一樣

print("***********************************************************")
#前向輸入是一個autograd.Variable,輸出也是如此。
#注意:該網絡(LeNet)的預期輸入大小為32x32。要在MNIST數據集上使用該網絡,需要將該數據集中的圖片大小調整為32x32。
input = Variable(torch.randn(1, 1, 32, 32))
out = net(input)
print(out)

print("***********************************************************")
#將所有參數的梯度緩沖區置為零,並使用隨機梯度進行后向傳播:
net.zero_grad()
out.backward(torch.randn(1, 10))
#注意: torch.nn只支持小批量,整個torch.nn包都只支持小批量樣本的輸入,而不支持單個樣本。
#例如,nn.Conv2d將接受一個4維的張量nSamples x nChannels x Height x Width。
#如果你只有單個樣本,那么只需要使用input.unsqueeze(0)來添加一個假的批量維度。
#簡要回顧:
#torch.Tensor:一個多維數組。
#autograd.Variable:封裝了一個張量和對該張量操作的記錄歷史。除了與張量具有相同的API外,還擁有一些如backward()等的操作。
#此外,還持有對張量的梯度w.r.t.。
#nn.Module:神經網絡模塊。一種封裝參數的便捷方式,並含有將參數移到GPU、導出、加載等的輔助功能。
#nn.Parameter:一種變量,當作為一個屬性分配給一個模塊時,它會自動注冊為一個參數。
#autograd.Function:實現autograd操作的前向和后向定義。每個變量操作,至少創建一個單獨的函數節點,連接到創建了一個變量的函數,並對其歷史進行編碼。

#0x02 損失函數(Loss Function)損失函數接受(輸出,目標)輸入對,並計算一個值,該值能夠評估輸出與目標的偏差大小。
#nn包中有幾個不同的損失函數。一個簡單的損失函數是nn.MSELoss,它會計算輸入和目標之間的均方誤差。
output = net(input)
target = Variable(torch.arange(1., 11.))  
# 虛擬目標舉個例子 a dummy target, for example,arange函數中的數字要加小數點,否則會報錯,左開右閉,output有10個所以target也10個
criterion = nn.MSELoss()#MSE就是兩者差的平方和除以樣本數量10,這里沒有用自由度即10-1=9這個值,不能直接將參數output和target輸入MSEloss()的括號中
loss = criterion(output, target)
print("loss:",loss)
#現在,如果你沿着后向跟蹤損失,那么可以使用它的`.grad_fn屬性,你將會看到一個這樣的計算圖:
#input ->conv2d ->relu ->maxpool2d ->conv2d ->relu ->maxpool2d ->view ->linear ->relu ->linear ->relu ->linear ->MSELoss ->loss
#所以,當我們調用loss.backward()時,整個計算圖是對損失函數求微分后的,並且圖中所有的變量將使自己的.grad變量與梯度進行累積。
#為了便於展示,我們反向跟隨幾步:
print(loss.grad_fn)  # MSELoss
print(loss.grad_fn.next_functions[0][0])  # Linear 為什么要加參數[0][0],去掉一個[0]也可以得到一個值,但是會多出一個“(,0)”
print(loss.grad_fn.next_functions[0][0].next_functions[0][0])  # ReLU
#0x03 反向傳播(Backprop)
#為了反向傳播誤差,我們所要做的就是調用loss.backward()。不過,你需要清除現有的梯度,否則梯度就會累積到已有的梯度上。
#現在我們應該調用loss.backward(),並在反向之前和之后查看conv1的偏差梯度。
net.zero_grad()     # zeroes the gradient buffers of all parameters
print('conv1.bias.grad before backward')
print(net.conv1.bias.grad) #conv1和bias可以換成其它函數(relu,linear),bias可以換成weight
loss.backward()
print('conv1.bias.grad after backward')
print(net.conv1.bias.grad)

#0x04 更新權重
#在實踐中使用的最簡單的更新規則是隨機梯度下降法(Stochastic Gradient Descent,SGD):
#weight = weight - learning_rate * gradient
#我們可以使用簡單的python代碼實現這一點:
learning_rate = 0.01
for f in net.parameters():
    f.data.sub_(f.grad.data * learning_rate) #Variable里面的值調用使用data?

#然而,當你使用神經網絡時,你可能想使用各種不同的更新規則,如SGD、Nesterov-SGD、Adam、RMSProp等等。
#為了實現這一點,我們構建了一個小的工具包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

print("***********************************************************")
print("loss after optimizer:",loss)#經過優化以后打印損失觀察一下
print("params[1] after optimizer:",params[1])#經過優化以后打印conv1的偏倚bias和上面未經優化的偏倚bias可以進行對比
#優化以后如何再進一步優化,優化到什么程度算是ok了?
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)
)
***********************************************************
***********************************************************
10
Parameter containing:
tensor([ 0.1703, -0.1637,  0.1829, -0.1471, -0.0758, -0.0585],
       requires_grad=True)
***********************************************************
tensor([[ 0.1154,  0.0158,  0.0087, -0.1210, -0.0249,  0.0537, -0.0099,  0.0766,
         -0.0059, -0.0395]], grad_fn=<AddmmBackward>)
***********************************************************
loss: tensor(38.5074, grad_fn=<MseLossBackward>)
<MseLossBackward object at 0x00000000096CCD68>
<AddmmBackward object at 0x00000000096CCF28>
<AccumulateGrad object at 0x00000000096CCD68>
conv1.bias.grad before backward
tensor([0., 0., 0., 0., 0., 0.])
conv1.bias.grad after backward
tensor([-0.0713,  0.1072,  0.1073, -0.0711,  0.0175, -0.1034])
***********************************************************
loss after optimizer: tensor(37.9959, grad_fn=<MseLossBackward>)
params[1] after optimizer: Parameter containing:
tensor([ 0.1711, -0.1658,  0.1826, -0.1456, -0.0762, -0.0577],
       requires_grad=True)
#【PyTorch深度學習60分鍾快速入門 】Part4:訓練一個分類器
#太棒啦!到目前為止,你已經了解了如何定義神經網絡、計算損失,以及更新網絡權重。不過,現在你可能會思考以下幾個方面:

#0x01 訓練數據集 https://www.cnblogs.com/leejack/p/8388776.html
#通常,當你需要處理圖像、文本、音頻或視頻數據時,你可以使用標准的python包將數據加載到numpy數組中。然后你可以將該數組轉換成一個torch.*Tensor。
#對於圖像,Pillow、OpenCV這些包將有所幫助。
#對於音頻,可以使用scipy和librosa包。
#對於文本,無論是基於原始的Python還是Cython的加載,或者NLTK和SpaCy都將有所幫助。
#具體對於圖像來說,我們已經創建了一個名為torchvision的包,它為像Imagenet、CIFAR10、MNIST等公共數據集提供了數據加載器,並為圖像提供了數據轉換器,即torchvision.datasets和torch.utils.data.DataLoader。
#這提供了極大的便利,避免了編寫樣板代碼。
#對於本教程,我們將使用CIFAR10數據集。它包含以下10個分類:飛機、汽車、鳥、貓、鹿、狗、青蛙、馬、輪船和卡車。
#CIFAR-10數據集中的圖像大小為3x32x32,即大小為32x32像素的3通道彩色圖像。

#0x02 訓練一個圖像分類器,我們將按順序執行以下步驟:
#使用torchvision加載並歸一化CIFAR10訓練和測試數據集,定義一個卷積神經網絡,定義一個損失函數,利用訓練數據來訓練網絡,利用測試數據來測試網絡
#1. 加載和歸一化CIFAR10,使用torchvision可以很容易地加載CIFAR10。

import torch
import torchvision #需要在pytorch官網上安裝帶cuda版本的torch,否則這一步會報錯,cuda可能需要事先安裝
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')

#為了增添一些樂趣,我們來展示一些訓練圖片:
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)))


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

#2. 定義一個卷積神經網絡
#從前面“神經網絡”一節中拷貝神經網絡並對其進行修改,使它接受3通道的圖像(而不是原先定義的單通道圖像)。
from torch.autograd import Variable
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(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 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()

#3. 定義損失函數和優化器
#讓我們用一個分類交叉熵的損失函數,以及帶動量的SGD:
import torch.optim as optim
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

#4. 訓練網絡
#這里正是事情開始變得有趣的地方。我們只需循環遍歷我們的數據迭代器,並將輸入量輸入到網絡並進行優化:
for epoch in range(2):  # loop over the dataset multiple times

    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        # get the inputs
        inputs, labels = data

        # wrap them in Variable
        inputs, labels = Variable(inputs), Variable(labels)

        # zero the parameter gradients
        optimizer.zero_grad()

        # forward + backward + optimize
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        # print statistics
        running_loss += loss.item() ##需要將原代碼中的loss.data[0] 改為loss.item()
        if i % 2000 == 1999:    # print every 2000 mini-batches
            print('[%d, %5d] loss: %.3f' %
                  (epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0

print('Finished Training')

#5. 在測試數據上測試網絡
#我們已經利用訓練數據集對網絡訓練了2次。但是,我們需要檢查網絡是否已經學到了什么。
#我們將通過預測神經網絡輸出的類標簽來檢查它,並根據實際情況對其進行檢查。如果預測是正確的,那么我們將該樣本添加到正確的預測列表中。
#OK!第一步,讓我們展示測試集中的一個圖像,以便於我們熟悉它。
dataiter = iter(testloader)
images, labels = dataiter.next()

# print images
imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))

#現在讓我們看看神經網絡認為上面例子中的對象是什么:
outputs = net(Variable(images))
#輸出結果是10個類的能量值。如果一個類的能量值越高,那么網絡就越可能認為圖像是該特定類。所以,我們來獲取最高能量值對應的索引:
_, predicted = torch.max(outputs.data, 1)

print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]
                              for j in range(4)))
#結果看起來相當不錯。下面,我們看一下該網絡在整個數據集上的表現。

correct = 0
total = 0
for data in testloader:
    images, labels = data
    outputs = net(Variable(images))
    _, predicted = torch.max(outputs.data, 1)
    total += labels.size(0)
    correct += (predicted == labels).sum()

print('Accuracy of the network on the 10000 test images: %d %%' % (
    100 * correct / total))
#結果看起來比隨機概率要好,隨機概率為10%的准確率(隨機從10個類中挑選一個類)。看起來似乎該網絡學到了一些東西。
#下面,我們看一下到底是哪些類別表現的很好,哪些類別表現的不好:
class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
for data in testloader:
    images, labels = data
    outputs = net(Variable(images))
    _, predicted = torch.max(outputs.data, 1)
    c = (predicted == labels).squeeze()
    for i in range(4):
        label = labels[i]
        class_correct[label] += c[i]
        class_total[label] += 1


for i in range(10):
    print('Accuracy of %5s : %2d %%' % (
        classes[i], 100 * class_correct[i] / class_total[i]))
#Ok,下一步我們將學習如何在GPU上運行神經網絡。
#0x03 在GPU上訓練
#將神經網絡轉移到GPU上,就像將一個張量轉移到GPU上一樣。這將遞歸地遍歷所有模塊,並將它們的參數和緩沖器轉換為CUDA張量:
##net.cuda()
#記住,你還必須將每一步的輸入和目標都發送到GPU上:
##inputs, labels = Variable(inputs.cuda()), Variable(labels.cuda())
#為什么與CPU相比,我沒有看到速度的明顯提升?那是因為你的網絡實在是太小了。
#練習: 嘗試增加網絡的寬度(第一個nn.Conv2d的參數2,以及第二個nn.Conv2d的參數1,它們必須為同一個數字),然后看下速度提升效果。
#實現的目標:以更高的角度理解PyTorch的Tensor庫和神經網絡,訓練一個小型的神經網絡來對圖像進行分類
#0x04 在多個GPU上訓練
#如果你想使用所有GPU來得到速度更大的提升,可以閱讀下一節“數據並行性”。

#0x05 擴展閱讀
#Train neural nets to play video games
#Train a state-of-the-art ResNet network on imagenet
#Train a face generator using Generative Adversarial Networks
#Train a word-level language model using Recurrent LSTM networks
#More examples
#More tutorials
#Discuss PyTorch on the Forums
#Chat with other users on Slack
Files already downloaded and verified
Files already downloaded and verified
 ship truck  deer  deer
[1,  2000] loss: 2.232
[1,  4000] loss: 1.889
[1,  6000] loss: 1.703
[1,  8000] loss: 1.608
[1, 10000] loss: 1.536
[1, 12000] loss: 1.443
[2,  2000] loss: 1.409
[2,  4000] loss: 1.370
[2,  6000] loss: 1.339
[2,  8000] loss: 1.327
[2, 10000] loss: 1.309
[2, 12000] loss: 1.291
Finished Training
GroundTruth:    cat  ship  ship plane
Predicted:    cat  ship  ship plane
Accuracy of the network on the 10000 test images: 54 %
Accuracy of plane :  0 %
Accuracy of   car :  0 %
Accuracy of  bird :  0 %
Accuracy of   cat :  0 %
Accuracy of  deer :  0 %
Accuracy of   dog :  0 %
Accuracy of  frog :  0 %
Accuracy of horse :  0 %
Accuracy of  ship :  1 %
Accuracy of truck :  0 %

#【PyTorch深度學習60分鍾快速入門 】Part5:數據並行化
#在本節中,我們將學習如何利用DataParallel使用多個GPU。在PyTorch中使用多個GPU非常容易,你可以使用下面代碼將模型放在GPU上:
#model.gpu()
#然后,你可以將所有張量拷貝到GPU上:
#mytensor = my_tensor.gpu()
#請注意,僅僅調用my_tensor.gpu()並不會將張量拷貝到GPU上,你需要將它指派給一個新的張量,然后在GPU上使用這個新張量。
#在多個GPU上執行你的前向和后向傳播是一件很自然的事情。然而,PyTorch默認情況下只會使用一個GPU。
#不過,通過利用DataParallel使你的模型並行地運行,這樣你就能很容易地將操作運行在多個GPU上:
#model = nn.DataParallel(model)
#這正是本節的核心。下面,我們將更詳細地分析該技術。
#0x01 導入和參數,下面,導入PyTorch模塊並定義相關參數:
import torch
import torch.nn as nn
from torch.autograd import Variable
from torch.utils.data import Dataset, DataLoader
# Parameters and DataLoaders
input_size = 5
output_size = 2
batch_size = 30
data_size = 100
#0x02 虛擬數據集,創建一個虛擬的(隨機的)數據集。你只需要實現__getitem__方法:
class RandomDataset(Dataset):

    def __init__(self, size, length):
        self.len = length
        self.data = torch.randn(length, size)

    def __getitem__(self, index):
        return self.data[index]

    def __len__(self):
        return self.len

rand_loader = DataLoader(dataset=RandomDataset(input_size, 100),batch_size=batch_size, shuffle=True)
#0x03 簡單模型
#在該demo中,我們的模型只會接受一個輸入,並進行一個線性操作,然后給出輸出結果。
#然而,你可以在任何模型(CNN、RNN、Capsule Net等等)上使用DataParallel。
#在模型內部我們添加了一個打印語句,以監控輸入和輸出的張量大小。請注意在rank 0批次時打印的內容:
class Model(nn.Module):
    # Our model

    def __init__(self, input_size, output_size):
        super(Model, self).__init__()
        self.fc = nn.Linear(input_size, output_size)

    def forward(self, input):
        output = self.fc(input)
        print("  In Model: input size", input.size(),
              "output size", output.size())

        return output
#0x04 創建模型和數據並行化
#這是本教程的核心部分。首先,我們需要創建一個模型實例,並檢查我們是否擁有多個GPU。
#如果擁有多個GPU,那么可以使用nn.DataParallel來封裝我們的模型。然后,利用model.gpu()將模型放到GPU上:
model = Model(input_size, output_size)
if torch.cuda.device_count() > 1:
  print("Let's use", torch.cuda.device_count(), "GPUs!")
  # dim = 0 [30, xxx] -> [10, ...], [10, ...], [10, ...] on 3 GPUs
  model = nn.DataParallel(model)

if torch.cuda.is_available():
   model.cuda()

#0x05 運行模型,現在,我們可以查看輸入與輸出張量的大小:
for data in rand_loader:
    if torch.cuda.is_available():
        input_var = Variable(data.cuda())
    else:
        input_var = Variable(data)

    output = model(input_var)
    print("Outside: input size", input_var.size(),
          "output_size", output.size())
#0x06 結果
#當我們將輸入和輸出都以30個作為批量大小時,正常情況下該模型會得到30並輸出30。但如果你有多個GPU,那么你將會得到下面的結果:
#2個GPU,如果你有2個GPU,你將看到:
# on 2 GPUs
"""
Let's use 2 GPUs!
    In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
    In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
    In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
    In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
    In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
    In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
    In Model: input size torch.Size([5, 5]) output size torch.Size([5, 2])
    In Model: input size torch.Size([5, 5]) output size torch.Size([5, 2])
Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])
"""

#3個GPU,如果你有3個GPU,你將看到:

"""
Let's use 3 GPUs!
    In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
    In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
    In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
    In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
    In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
    In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
    In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
    In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
    In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
    In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
    In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])
"""

#8個GPU,如果你有8個GPU,你將看到:
"""
Let's use 8 GPUs!
    In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
    In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
    In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
    In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
    In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
    In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
    In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
    In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
    In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
    In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
    In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
    In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
    In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])
"""

#0x07 總結
#DataParallel會自動分割數據,並將作業順序發送給多個GPU上的多個模型。
#在每個模型完成它們的作業之后,DataParallel會收集並合並結果,然后再返回給你。
In Model: input size torch.Size([30, 5]) output size torch.Size([30, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
  In Model: input size torch.Size([30, 5]) output size torch.Size([30, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
  In Model: input size torch.Size([30, 5]) output size torch.Size([30, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
  In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])

 
        
 


免責聲明!

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



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