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