PyTorch教程001

来源:互联网 发布:布鲁克林法学院 知乎 编辑:程序博客网 时间:2024/06/04 17:53

第一节 基本的操作

点我,所有内容翻译于此链接

什么是PyTorch?

它是基于Python语言的科学计算工具包,其设计目的包括以下两个方面:

  • 替代Numpy从而可以使用强大的GPU进行计算
  • 一个深读学习的研究平台,致力于提供最大的灵活性与速度

我们开始吧~

Tensors

Tensors和Numpy的ndarrays很像,与之不同之处在于Tensor可以在GPU上进行运算从而获得更快的计算速度。

from __future__ import print_functionimport torch

利用pyTorch构造一个未初始化的5×3的矩阵:

x = torch.Tensor(5,3)print(x)
-9.3921e+17  4.5628e-41 -9.3921e+17 4.5628e-41  0.0000e+00  0.0000e+00 0.0000e+00  0.0000e+00  0.0000e+00 0.0000e+00  0.0000e+00  0.0000e+00 0.0000e+00  0.0000e+00  0.0000e+00[torch.FloatTensor of size 5x3]

类似于上一步,构造一个随机(值域为[0,1])初始化的矩阵:

x = torch.rand(5,3)print(x)
 0.9144  0.5597  0.7737 0.4661  0.0467  0.1902 0.7286  0.9863  0.7427 0.5697  0.6273  0.0305 0.5571  0.3872  0.0925[torch.FloatTensor of size 5x3]

获得其尺寸:

print(x.size())
torch.Size([5, 3])

* Note: * Torch.Size实际上是一个tuple,因此支持相同的操作。

操作

用于数据操作的语法有很多,下面来看加法的操作:

Addition:语法一

y = torch.rand(5,3)print(x+y)
 1.5705  0.7976  1.5798 0.8943  0.2229  0.4182 0.8277  1.7598  1.7178 0.8377  0.9944  0.0811 0.9289  1.3111  0.7678[torch.FloatTensor of size 5x3]

Addition:语法二

print(torch.add(x,y))
 1.5705  0.7976  1.5798 0.8943  0.2229  0.4182 0.8277  1.7598  1.7178 0.8377  0.9944  0.0811 0.9289  1.3111  0.7678[torch.FloatTensor of size 5x3]

Addition:给一个输出的Tensor

result = torch.Tensor(5,3)torch.add(x,y,out=result)print(result)
 1.5705  0.7976  1.5798 0.8943  0.2229  0.4182 0.8277  1.7598  1.7178 0.8377  0.9944  0.0811 0.9289  1.3111  0.7678[torch.FloatTensor of size 5x3]

Addition:替换

#把x的值加到y上y.add_(x)print(y)
 1.5705  0.7976  1.5798 0.8943  0.2229  0.4182 0.8277  1.7598  1.7178 0.8377  0.9944  0.0811 0.9289  1.3111  0.7678[torch.FloatTensor of size 5x3]

注意,每个用于替换的操作其后都会接一个 _。例如x.copy_(y),x.t_(),都会改变x的值。

你可以使用标准的numpy的操作来使用数据:

print(x[:,1])#打印矩阵的所有行及第二列
 0.5597 0.0467 0.9863 0.6273 0.3872[torch.FloatTensor of size 5]

Numpy Bridge:Tensor与Numpy的相互转换

Tensor和Numpy在转换后会共享数据所占的内存区域,改变两者的任何一个的值,两者的值都会同时发生改变。

将Torch的Tensor转换为Numpy的Array

a = torch.ones(5)print(a)
 1 1 1 1 1[torch.FloatTensor of size 5]
b = a.numpy()print(b)
[ 1.  1.  1.  1.  1.]

验证:共享内存的机制

a.add_(1)print('a is :\n',a)print('b is :\n',b)
a is : 2 2 2 2 2[torch.FloatTensor of size 5]b is : [ 2.  2.  2.  2.  2.]

将numpy数组转换为torch的Tensor

import numpy as npa = np.ones(5)b = torch.from_numpy(a)np.add(a,1,out=a)print(a)print(b)
[ 2.  2.  2.  2.  2.] 2 2 2 2 2[torch.DoubleTensor of size 5]

除了CharTensor外,运算在CPU模式的所有Tensor都支持与Numpy的相互转换。

Autograd: 自动求导数

PyTorch的所有神经网络的核心功能都在于autograd类。该类提供所有对Tensor操作的求导。它是一个define-by-run的框架,这意味着你的反向传播是有你的代码在运行时决定的,因而每个单一的迭代可以不同。

Variable

autograd.Variable是该类的一个核心类,它包含着Tensor并且支持几乎所有相关的操作。一旦你完成了前向传播的计算任务,你可以调用backward()使得所有的梯度自动的进行计算。

你可以使用Tensor.data访问原始Tensor的数据,与此同时,梯度的值贝类加到属性Tensor.grad中。
这里写图片描述
图示为Variable的内部组成

import torchfrom torch.autograd import Variable

创建一个Variable

x = Variable(torch.ones(2,2),requires_grad=True)print(x)
Variable containing: 1  1 1  1[torch.FloatTensor of size 2x2]

对变量进行操作:

y = x + 2print(y)
Variable containing: 3  3 3  3[torch.FloatTensor of size 2x2]

y是由一个操作的结果所生成的,因此它拥有一个creator。

print(y.creator)
<torch.autograd._functions.basic_ops.AddConstant object at 0x7f31d7e47828>

对y进行更多的操作:

z = y * y * 3out = z.mean()print(z,out)
Variable containing: 27  27 27  27[torch.FloatTensor of size 2x2] Variable containing: 27[torch.FloatTensor of size 1]

梯度

让反向传播 out.backward() 等价于操作out.backward(torch.Tensor([1.0]))

out.backward()

打印求导结果d(out)dx

print(x.grad)
Variable containing: 4.5000  4.5000 4.5000  4.5000[torch.FloatTensor of size 2x2]

得出上面结果的原因:

out=3(x+2)2d(out)dx=32(x+2)x=[1111]d(out)dx=[4.54.54.54.5]

你可以利用autograd做很多疯狂的事情。

x = torch.rand(3)x = Variable(x,requires_grad=True)y = x*2count = 0while y.data.norm() < 1000:    y = y * 2#运行好多次,导致x有好多次的累加    count = count + 1print(y)print(count)
Variable containing:  279.9745 1013.4999  346.2041[torch.FloatTensor of size 3]10
gradients = torch.FloatTensor([0.1,1.0,0.0001])y.backward(gradients)print(x.grad)
Variable containing:  204.8000 2048.0000    0.2048[torch.FloatTensor of size 3]

神经网络

神经网络可以通过torch.nn包来实现。
现在我们对于autograd有了一个大概的印象,nn依赖于autograd来定义模型以及对它们求导数。一个nn.Module包含layers和方法forward(input),返回的结果为output

例如,观看下面分类数字手写识别的卷积神经网络:
<img src="mnist.png">
它是一个简单的feed-forward网络。他将输入一层一层的传递下去,最终给出一个输出结果。
一个典型的神经网络的训练流程如下所下:

  • 定义一个拥有可学习参数的神经网络
  • 遍历数据集,将其内容作为输入
  • 处理输入数据
  • 计算代价(神经网络的输出与正确值的距离)
  • 反向传播
  • 更新神经网络中的参数,一般形式如下:
    weight=weight+learningrategradient

定义神经网络

让我们来定义一个神经网络:

import torchfrom torch.autograd import Variableimport torch.nn as nnimport torch.nn.functional as Fclass Net(nn.Module):    def __init__(self):        super(Net,self).__init__()        #输入图像通道为1,6个输出通道,5×5的卷积核        self.conv1 = nn.Conv2d(1,6,5)        # 输入图像通道为6,16个输出通道,5×5的卷积核        self.conv2 = nn.Conv2d(6,16,5)        #y = Wx + b 的仿射变换        self.fc1 = nn.Linear(16*5*5,120)        self.fc2 = nn.Linear(120,84)        self.fc3 = nn.Linear(84,10)    def forward(self,x):        # 2×2的最大池化窗口        x = F.max_pool2d(F.relu(self.conv1(x)),(2,2))        # 若是一个正方形,可以用一个数来表示        x = F.max_pool2d(F.relu(self.conv2(x)),2)        x = x.view(-1,self.num_flat_features(x))        x = F.relu(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:]# 取得出batch外的所有维度        num_features = 1        for s in size:            num_features *= s        return num_featuresnet = 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 (400 -> 120)  (fc2): Linear (120 -> 84)  (fc3): Linear (84 -> 10))

我们必须自定义forward函数,backward函数(用于计算梯度)使用autograd自动定义,你可以在forward函数中使用任何Tensor的操作

用于学习的网络参数可以通过net.parameters()获取。

params = list(net.parameters())print(len(params))print(params[0].size())# 卷积层1的参数print(params[0][1,:])
10torch.Size([6, 1, 5, 5])Variable containing:(0 ,.,.) =  -0.1029  0.0501 -0.1202  0.0168 -0.0679 -0.1803  0.1550 -0.1125 -0.0424 -0.0865  0.0836  0.0452 -0.0619  0.1352  0.0567  0.1265 -0.1011 -0.0643 -0.1561 -0.1950 -0.0530  0.1887  0.1961 -0.0866  0.0307[torch.FloatTensor of size 1x5x5]

forward函数的输入是一个autograd.Variable,输出同样也是。

input = Variable(torch.randn(1,1,32,32))out = net(input)print(out)
Variable containing:-0.1454  0.0262  0.0150 -0.0715 -0.1060  0.0319 -0.0391 -0.0759 -0.0447 -0.1261[torch.FloatTensor of size 1x10]

清空所有参数的梯度的缓存,并且使用随机梯度进行反步操作:

net.zero_grad()out.backward(torch.randn(1,10))

注意:torch.nn只支持mini-batches,完整的torch.nn包只支持样本的mini-batch作为输入,而不是单个的样本。
例如,nn.Conv2d会接收一个4维的Tensor作为输入,包括nSamples x nChannels x Height x Width
如果只有一个样本,可以通过使用 input.unsqueeze(0)来添加一个伪造的batch维度。

在进行更深入的研究之前,让我们概括一下目前所见到的一些类。

torch.Tensor:一个多维数组
autograd.Variable:包含一个Tensor并接受应用在其上的历史操作。拥有和Tensor一样的API,额外的有像是backward()。同样包含着Tensor中的梯度。
nn.Mosule:神经网络模型,便于封装参数,易于转到GPU进行计算,导出,加载等。
nn.Parameter:一种类型的Variable,当作为Module的一个属性时,自动的注册为一个参数。
autograd.Function:实现用于自动求导操作的前向传播和反向传播。每一个Variable操作,创造至少一个单一的Function节点,用于创建一个Variable并编码其历史。

目前为止我们学习了:

  • 定义一个神经网络
  • 处理输入,调用反向传播

接下来我们还要学习:

  • 计算代价函数
  • 更新神经网络权重

代价函数(Loss Function)

代价函数通过计算来自于input的(output,target),计算一个值然后估计输出值偏离于目标值的程度。
nn包中有很多种loss function,一个简单的loss是:nn.MSELoss,该loss用于计算输出和目标之间的均方误差。
例如:

output = net(input)target = Variable(torch.arange(1,11))#假设的target :1,2,3,4,5,6,7,8,9,10criterion = nn.MSELoss()loss = criterion(output,target)print(loss)
Variable containing: 39.1498[torch.FloatTensor of size 1]

现在,如果你沿着反向传播的方向,使用其creator属性,你会看到一个计算图:

input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d      -> view -> linear -> relu -> linear -> relu -> linear      -> MSELoss      -> loss

因此,当我们调用loss.backward()的时候。完整的图就是对loss求导后的结果,每一个图中的Variable将会拥有使用gradient累加的它们自己的.grad Variable。
为了更好的说明,进行一些反向传播的操作:

print(loss.creator) # MSELossprint(loss.creator.previous_functions[0][0])# Linearprint(loss.creator.previous_functions[0][0].previous_functions[0][0])# Relu
<torch.nn._functions.thnn.auto.MSELoss object at 0x7f319130cf28><torch.nn._functions.linear.Linear object at 0x7f319130cd68><torch.nn._functions.thnn.auto.Threshold object at 0x7f319130cc88>

Backprop(反向传播)

为了将误差进行反向传播,我们只需要运行loss.backward()。你需要清空已经存在的梯度信息,否则现存的梯度值会被累加到下一步要计算的梯度当中去。
现在,我们调用一下loss.backward(),然后看一下conv1的偏置梯度在反向传播前后的变化。

net.zero_grad() # 清空梯度的缓存print("反向传播前的conv1.bias.grad:")print(net.conv1.bias.grad)loss.backward()print("反向传播后的conv1.bias.grad:")print(net.conv1.bias.grad)
反向传播前的conv1.bias.grad:Variable containing: 0 0 0 0 0 0[torch.FloatTensor of size 6]反向传播后的conv1.bias.grad:Variable containing:1.00000e-02 *  1.6163 -1.4633 -1.4045 -0.7913  4.5722  1.9762[torch.FloatTensor of size 6]

至此,我们明白了如何使用loss functions

Update the weights

最简单的权重更新规则被称为Stochastic Gradient Descent (SGD):

weight=weightlearningrategradient

使用简单的python代码实现上述功能:

learning_rate = 0.01for f in net.parameters():    f.data.sub_(f.grad.data * learning_rate)

当然,PyTorch同样提供了很多的类似函数包括SGD,Nesterov-SGD, Adam, RMSProp等等。所有的这些方法都被封装到包torch.optim中。

import torch.optim as optim# 创建自己的optimizeroptimizer = optim.SGD(net.parameters(),lr=0.01)# 在训练的循环中optimizer.zero_grad() # 清空梯度缓存output = net(input)loss = criterion(output,target)loss.backward()optimizer.step() # 更新操作

训练一个图像分类器

我们将从以下几个步骤来进行:

  • 使用torchvision加载并且正则化数据集CIFAR10
  • 定义一个卷积神经网络
  • 定义一个损失函数
  • 在training data 上训练网络
  • 在test data上测试神经网络

加载并正则化CIFAR10

使用torchvision,它用于加载CIFAR10时十分方便

import torchimport torchvisionimport torchvision.transforms as transforms

输出的torchvision数据集是PILImages的格式,范围为[0,1],我们需要将它转化为正则化的Tensor,范围是[-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('./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')
Files already downloaded and verifiedFiles already downloaded and verified

选几张图片做展示:

import matplotlib.pyplot as pltimport numpy as np%matplotlib inline#用于显示图像的函数def imshow(img):    img = img / 2 + 0.5    npimg = img.numpy()    plt.imshow(np.transpose(npimg,(1,2,0)))# 随机的获取一些训练图像dataiter = iter(trainloader)images,labels = dataiter.next()#显示图像imshow(torchvision.utils.make_grid(images))#打印标签print(' '.join('%10s'%classes[labels[j]] for j in range(4)))
      frog        dog      plane        car

png这里写图片描述

定义一个卷积神经网络

复制之前神经网络的定义,修改网络的1个通道输入为3个通道

from torch.autograd import Variableimport torch.nn as nnimport torch.nn.functional as Fclass 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 xnet = Net()

定义Loss函数和优化器(optimizer)

这里我们使用交叉熵损失函数和SGD做为优化器

import torch.optim as optimcriterion = 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,0):        # 获得输入        inputs,labels = data        # 使用Variable包装这些数据        inputs,labels = Variable(inputs),Variable(labels)        # 清空缓存的梯度信息        optimizer.zero_grad()        #forward + backward + optimize        outputs = net(inputs)        loss = criterion(outputs,labels)        loss.backward()        optimizer.step()        #打印统计信息        running_loss += loss.data[0]        if i%2000 == 1999:# 打印没2000 mini-batches            print('[%d,%5d] loss :%.3f'%                  (epoch+1,i+1,running_loss / 2000))            running_loss = 0.0print("结束训练")
[1, 2000] loss :2.184[1, 4000] loss :1.821[1, 6000] loss :1.644[1, 8000] loss :1.548[1,10000] loss :1.498[1,12000] loss :1.456[2, 2000] loss :1.380[2, 4000] loss :1.345[2, 6000] loss :1.323[2, 8000] loss :1.329[2,10000] loss :1.297[2,12000] loss :1.266结束训练

在测试集上测试神经网络

我们已经把神经网络在训练集上训练了2遍,现在我们需要检验一下神经网络是否学到了什么。
我们根据神经网络输出的类标签与实际的类标签进行比较,将神经网络预测准确的样本归类到correct predictions中去。
首先,我们先是一张测试及中的图片来熟悉一下数据:

dataiter = iter(testloader)images,labels = dataiter.next()# 显示图片imshow(torchvision.utils.make_grid(images))print("GroundTruth:",''.join('%10s'%classes[labels[j]] for j in range(4)))
GroundTruth:        cat      ship      ship     plane

这里写图片描述

好了,让我们看一下神经网络是如何识别上面的图片的:

outputs = net(Variable(images))

输出是10个类的能量。高的能量集中到一个类标签中,则神经网络则会将数据归类为该类。因此,让我们找出最高的能量的索引。

_,predicted = torch.max(outputs.data,1)print('Predicted:',' '.join('%5s'%classes[predicted[j][0]]                           for j in range(4)))
Predicted:  ship   car   car  ship

接下来看看该神经网络在整个数据集上的表现如何。

correct = 0total = 0for 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))
Accuracy of the network on the 10000 test images: 55 %

看起来不错哦,因为它比随机的结果好很多(随机的准确率为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] += 1for i in range(10):    print('Accuracy of %5s : %2d %%' % (        classes[i], 100 * class_correct[i] / class_total[i]))
Accuracy of plane : 66 %Accuracy of   car : 81 %Accuracy of  bird : 33 %Accuracy of   cat : 44 %Accuracy of  deer : 36 %Accuracy of   dog : 54 %Accuracy of  frog : 59 %Accuracy of horse : 55 %Accuracy of  ship : 73 %Accuracy of truck : 44 %

目标达成

  • Understanding PyTorch’s Tensor library and neural networks at a high level.
  • Train a small neural network to classify images
原创粉丝点击