神经网络可以通过使用torch.nn包来构建。

既然你已经了解了autograd,而nn依赖于autograd来定义模型并对其求微分。一个nn.Module包含多个网络层,以及一个返回输出的方法forward(input)

例如,查看下图中的对数字图片分类的网络:

这是一个简单的前馈网络。它接受输入,并将输入依次通过多个层,然后给出输出结果。

对于神经网络来说,一个经典的训练过程包括以下步骤:

  • 定义一个包含一些可学习的参数(或权重)的神经网络
  • 对输入数据集进行迭代
  • 通过网络处理输入
  • 计算损失函数(即输出距离正确值差多远)
  • 将梯度传播回网络参数
  • 更新网络的权重,通常使用一个简单的更新规则:weight = weight - learning_rate * gradient

0x01 定义网络

下面,我们定义该网络:

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 input image channel, 6 output channels, 5x5 square convolution
# kernel
self.conv1 = nn.Conv2d(1, 6, 5)
self.conv2 = nn.Conv2d(6, 16, 5)
# an affine operation: 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):
# Max pooling over a (2, 2) window
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
# If the size is a square you can only specify a single number
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:] # all dimensions except the batch dimension
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(in_features=400, out_features=120)
(fc2): Linear(in_features=120, out_features=84)
(fc3): Linear(in_features=84, out_features=10)
)

你只需要定义前向(forward)函数,而反向(backward)函数(梯度计算的位置)会使用autograd自动为你定义。你可以在前向函数中使用任何张量操作。

一个模型的可学习参数由net.parameters()返回。

params = list(net.parameters())
print(len(params))
print(params[0].size()) # conv1's .weight

输出结果:

10
torch.Size([6, 1, 5, 5])

前向输入是一个autograd.Variable,输出也是如此。注意:该网络(LeNet)的预期输入大小为32x32。要在MNIST数据集上使用该网络,需要将该数据集中的图片大小调整为32x32。

input = Variable(torch.randn(1, 1, 32, 32))
out = net(input)
print(out)

输出结果:

Variable containing:
0.0023 -0.0613 -0.0397 -0.1123 -0.0397 0.0330 -0.0656 -0.1231 0.0412 0.0162
[torch.FloatTensor of size 1x10]

将所有参数的梯度缓冲区置为零,并使用随机梯度进行后向传播:

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
criterion = nn.MSELoss() loss = criterion(output, target)
print(loss)

输出结果:

Variable containing:
38.8243
[torch.FloatTensor of size 1]

现在,如果你沿着后向跟踪损失,那么可以使用它的``.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
print(loss.grad_fn.next_functions[0][0].next_functions[0][0]) # ReLU

输出结果:

<MseLossBackward object at 0x7fe4c18539e8>
<AddmmBackward object at 0x7fe3f5498550>
<ExpandBackward object at 0x7fe4c18539e8>

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) loss.backward() print('conv1.bias.grad after backward')
print(net.conv1.bias.grad)

输出结果:

conv1.bias.grad before backward
Variable containing:
0
0
0
0
0
0
[torch.FloatTensor of size 6] conv1.bias.grad after backward
Variable containing:
1.00000e-02 *
7.4571
-0.4714
-5.5774
-6.2058
6.6810
3.1632
[torch.FloatTensor of size 6]

现在,我们已经看到了如何使用损失函数。

扩展阅读:

神经网络包中包含各种各样的模块和损失函数,它们构成了深度神经网络的构造块。完整的文档列表可以在这里查看。

唯一剩下的待学习的知识点:

  • 更新网络的权重

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)

然而,当你使用神经网络时,你可能想使用各种不同的更新规则,如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

注意:

要观察梯度是如何缓存的,需要手动调用optimizer.zero_grad()将缓冲器设置为0。这是因为梯度会累积,正如在“反向传播”一节中解释的那样。

脚本总运行时间:0分0.129秒。

【PyTorch深度学习60分钟快速入门 】Part3:神经网络的更多相关文章

  1. 【PyTorch深度学习60分钟快速入门 】Part4:训练一个分类器

      太棒啦!到目前为止,你已经了解了如何定义神经网络.计算损失,以及更新网络权重.不过,现在你可能会思考以下几个方面: 0x01 数据集 通常,当你需要处理图像.文本.音频或视频数据时,你可以使用标准 ...

  2. 【PyTorch深度学习60分钟快速入门 】Part1:PyTorch是什么?

      0x00 PyTorch是什么? PyTorch是一个基于Python的科学计算工具包,它主要面向两种场景: 用于替代NumPy,可以使用GPU的计算力 一种深度学习研究平台,可以提供最大的灵活性 ...

  3. 【PyTorch深度学习60分钟快速入门 】Part0:系列介绍

      说明:本系列教程翻译自PyTorch官方教程<Deep Learning with PyTorch: A 60 Minute Blitz>,基于PyTorch 0.3.0.post4 ...

  4. 【PyTorch深度学习60分钟快速入门 】Part5:数据并行化

      在本节中,我们将学习如何利用DataParallel使用多个GPU. 在PyTorch中使用多个GPU非常容易,你可以使用下面代码将模型放在GPU上: model.gpu() 然后,你可以将所有张 ...

  5. 【PyTorch深度学习60分钟快速入门 】Part2:Autograd自动化微分

      在PyTorch中,集中于所有神经网络的是autograd包.首先,我们简要地看一下此工具包,然后我们将训练第一个神经网络. autograd包为张量的所有操作提供了自动微分.它是一个运行式定义的 ...

  6. pytorch深度学习60分钟闪电战

    https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html 官方推荐的一篇教程 Tensors #Construct a ...

  7. Vue.js 60 分钟快速入门

    Vue.js 60 分钟快速入门 转载 作者:keepfool 链接:http://www.cnblogs.com/keepfool/p/5619070.html Vue.js介绍 Vue.js是当下 ...

  8. 不会几个框架,都不好意思说搞过前端: Vue.js - 60分钟快速入门

    Vue.js——60分钟快速入门   Vue.js是当下很火的一个JavaScript MVVM库,它是以数据驱动和组件化的思想构建的.相比于Angular.js,Vue.js提供了更加简洁.更易于理 ...

  9. Vue.js——60分钟快速入门(转)

    vue:Vue.js——60分钟快速入门 <!doctype html> <html lang="en"> <head> <meta ch ...

随机推荐

  1. 【APP测试(Android)】--性能测试

  2. 【转】C#单元测试,带你快速入门

    [转]C#单元测试,带你快速入门 注:本文示例环境 VS2017 XUnit 2.2.0 单元测试框架 xunit.runner.visualstudio 2.2.0 测试运行工具 Moq 4.7.1 ...

  3. Chapter3_操作符_方法调用中的别名问题

    接下来展示方法调用中的别名问题,方法调用中的别名问题指的是,将一个对对象的引用传递给某一个方法时,方法操作的是这一个特定的引用而不是这个引用的拷贝. class Person{ float heigh ...

  4. iOS 开发之 KVC - setValuesForKeysWithDictionary 解析

    从字典映射到一个对象,这是KVC中的一个方法所提供的,这个方法就是 setValuesForKeysWithDictionary:,非常好用,不需要你来一一的给对象赋值而直接从字典初始化即可,但用的不 ...

  5. ActiveMQ_7JMX

    activemq配置jmx 配置activemq中的jmx可以用于监控activemq信息. activemq.xml配置 修改broker属性 添加节点managementContext <m ...

  6. 大数据 Spark 架构

    一.Spark的产生背景起源 1.spark特点 1.1轻量级快速处理 Saprk允许传统的hadoop集群中的应用程序在内存中已100倍的速度运行即使在磁盘上也比传统的hadoop快10倍,Spar ...

  7. 【pycharm 警告】unittest RuntimeWarning: Parent module ” not found while handling absolute import

    Pycharm 2016.2执行单元测试遇到如下问题: RuntimeWarning: Parent module ‘YOUR_MODULE_HERE’ not found while handlin ...

  8. 9.7 翻译系列:EF数据注解特性之--InverseProperty【EF 6 Code-First系列】

    原文链接:https://www.entityframeworktutorial.net/code-first/inverseproperty-dataannotations-attribute-in ...

  9. Learning WCF:Fault Handling

    There are two types of Execptions which can be throwed from the WCF service. They are Application ex ...

  10. hotmail 收不到邮件的问题

    之前用 hotmail 注册过一个 aws 账号,起了一个 ec2 的免费一年的 VPS,然后没怎么用,不久就把这事忘了. 直到有一天手机收到信用卡扣款消息,我就马上去登账号,却怎么也想不起密码来了, ...