PyTorch教程001

第一节 基本的操作

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

什么是PyTorch?

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

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

我们开始吧~

Tensors

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

from __future__ import print_function
import 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 np
a = 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 torch
from 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 + 2
print(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 * 3
out = 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*2
count = 0
while y.data.norm() < 1000:
    y = y * 2#运行好多次,导致x有好多次的累加
    count = count + 1

print(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 torch
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__()
        #输入图像通道为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_features

net = Net()
print(net)
Net (
  (conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))
  (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
  (fc1): Linear (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,:])
10
torch.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,10
criterion = 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) # MSELoss
print(loss.creator.previous_functions[0][0])# Linear
print(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.01
for 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
# 创建自己的optimizer
optimizer = 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 torch
import torchvision
import 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 verified
Files already downloaded and verified

选几张图片做展示:

import matplotlib.pyplot as plt
import 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 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()

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

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

import torch.optim as optim
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(),lr=0.001,momentum=0.9)

训练神经网络

事情变得有趣起来。我们简单的迭代取得数据集中的数据然后喂给神将网络并进行优化。

for epoch in range(2):# 多次循环取出数据
    running_loss = 0.0
    for i, data in enumerate(trainloader,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.0

print("结束训练")
[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 = 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))
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] += 1


for 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
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值