【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])
来源:https://www.cnblogs.com/brave-sailor/p/11588733.html