PyTorch学习笔记之神经网络

torch.nn是专门为神经网络设计的模块化接口。nn构建于Autograd之上,可用来定义和运行神经网络。nn.Module是nn中最重要的类,可以把它看做一个网络的封装,包含网络各层定义及forward方法,forward方法可返回前向传播的结果。

定义网络时,需要继承nn.Module,并实现它的forward方法,把网络中具有可学习参数的层放在构造函数__init__中。如果某一层(如ReLU)不具有可学习的参数,则既可以放在构造函数中,也可以不放。

import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):
    def __init__(self):
        # nn.Module子类的函数必须在构造函数中执行父类的构造函数
        super(Net, self).__init__()
        # 卷积层‘1’表示输入图片为单通道,‘6’表示输出通道数
        # ‘5’表示卷积核为5*5
        self.conv1 = nn.Conv2d(1, 6, 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):
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        # reshape,‘-1’表示自适应
        x = x.view(x.size()[0], -1)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

net = Net()

只要在nn.Module的子类中定义了forward函数,backward函数就会被自动实现(利用Autograd)。在forward函数中可以使用任何Variable支持的函数,还可以使用if、for、print、log等Python语法。

网络的可学习参数通过net.parameters()返回,net.named_parameters可同时返回可学习的参数及名称。

params = list(net.parameters())

for name, parameters in net.named_parameters():
    print(name, ':', parameters.size())

forward函数的输入和输出都是Variable,只有Variable才具有自动求导功能,Tensor是没有的,所以在输入时,需要把Tensor封装成Variable

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

# 所有参数的梯度清零
net.zero_grad()

torch.nn只支持mini-batches,不支持一次只输入一个样本,即一次必须是一个batch。如果只想输入一个样本,则用input.unsqueeze(0)将batch_size设为1。

nn实现了神经网络中大多数的损失函数,例如nn.MSELoss用来计算均方误差,nn.CrossEntropyLoss用来计算交叉熵损失。

output = net(input)
target = Variable(t.arange(0, 10))
criterion = nn.MSELoss()
loss = criterion(output, target)

计算图如下:

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

当调用loss.backward()时,该图会动态生成并自动微分,也会自动计算图中参数的导数。

在反向传播计算完所有参数的梯度后,还需要使用优化方法更新网络的权重和参数。例如,随机梯度下降法(SGD)的更新策略如下

weight = weight - learning_rate * gradient

torch.optim中实现了深度学习中绝大多数的优化方法,例如RMSProp、Adam、SGD等,更便于使用。

import torch.optim as optim
optimizer = optim.SGD(net.parameters(), lr=0.01)
# 在训练过程中
# 先梯度清零
optimizer.zero_grad()

# 计算损失
output = net(input)
less = criterion(output, target)

# 反向传播
loss.backward()

# 更新参数
optimizer.step()

对于常用的数据集,PyTorch也提供了封装好的借口供用户快速调用,这些数据集主要保存在torchvision中。

torchvision实现了常用的图像数据加载功能,例如Imagenet、CIFAR10、MNIST等,以及常用的数据转换操作,这极大地方便了数据加载。

常用的图像相关层

  • 卷积层(Conv)
    • 前向卷积
    • 逆卷积
  • 池化层(Pool)
    • 平均池化(AvgPool)
    • 最大值池化(MaxPool)
    • 自适应池化(AdaptiveAvgPool)

池化层可以看作是一种特殊的卷积层,用来下采样。但池化层没有可学习参数,其weight是固定的。

其它的深度学习常用层

  • Linear:全连接层
  • BatchNorm:批规范化层
  • Dropout:dropout层,用来防止过拟合

对于前馈神经网络,如果每次都写复杂的forward函数会有些麻烦,可以用Sequential进行简化。Sequential是一个特殊的Module,它包含几个子module,前向传播时会将输入一层接一层地传递下去。

# Sequential的三种写法
net1 = nn.Sequential()
net1.add_module('conv', nn.Conv2d(3, 3, 3))
net1.add_module('batchnorm', nn.BatchNorm2d(3))
net1.add_module('activation_layer', nn.ReLU())

net2 = nn.Sequential(
    nn.Conv2d(3, 3, 3),
    nn.BatchNorm2d(3),
    nn.ReLU()
    )
    
from collections import OrderedDict
net3 = nn.Sequential(OrderedDict([
        ('conv1', nn.Conv2d(3, 3, 3)),
        ('bn1', nn.BatchNorm2d(3)),
        ('relu1', nn.ReLU())
    )

# 可根据名字或序号取出子module
net1.conv, net2[0], net3.conv1

PyTorch将深度学习中常用的优化方法全部封装在torch.optim中。

所有的优化方法都是继承基类optim.Optimizer,并实现了自己的优化步骤。

SGD的使用示例:

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 6, 5),
            nn.ReLU(),
            nn.MaxPool2d(2, 2),
            nn.Conv2d(6, 16, 5),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )
        self.classifier = nn.Sequential(
            nn.Linear(16 * 5 * 5, 120),
            nn.ReLU(),
            nn.Linear(120, 84),
            nn.ReLU(),
            nn.Linear(84, 10)
        )
    
    def forward(self, x):
        x = self.features(x)
        x = x.view(-1, 16 * 5 * 5)
        x = self.classifier(x)
        return x
    
net = Net()

from torch import optim
optimizer = optim.SGD(params=net.parameters(), lr=1)
# 梯度清零
optimizer.zero_grad()

input = V(t.randn(1, 3, 32, 32))
output = net(input)
output.backward(output)
# 执行优化
optimizer.step()

# 为不同子网络设置不同的学习率,在finetune中经常用到
# 如果对某个参数不指定学习率,就使用默认学习率
optimizer = optim.SGD([
        {'params' : net.features.parameters()},
        {'params' : net.classifier.parameters(), 'lr' : 1e-2}
        ], lr=1e-5
    )

nn中还有一个很常用的模块:nn.functional。nn中大多数layer在functional中都有一个与之相对应的函数。nn.functional中的函数和nn.Module的主要区别在于,用nn.Module实现的layers是一个特殊的类,都是由class Layer(nn.Module)定义,会自动提取可学习的参数;而nn.functional中的函数更像是纯函数,由def function(input)定义。

如果模型有可学习的参数,最好用nn.Module,否则既可以使用nn.functional也可以使用nn.Module,二者在性能上没有太大差异。

dropout建议使用nn.Dropout而不是nn.functional.dropout,因为dropout在训练和测试两个阶段的行为有所差别,使用nn.Module对象能够通过model.eval操作加以区分。

PyTorch中的nn.init模块专门为初始化设计,实现了常用的初始化策略。

nn.Module基类的构造函数的源代码:

def __init__(self):
    self._parameters = OrderedDict()
    self._modules = OrderedDict()
    self._buffers = OrderedDict()
    self._backward_hooks = OrderedDict()
    self._forward_hooks = OrderedDict()
    self.training = True

属性的解释:

  • _parameters:字典,保存用户直接设置的parameter
  • _modules:子module
  • _buffers:缓存。如batchnorm使用momentum机制,每次前向传播需用到上一次前向传播的结果
  • _backward_hooks与_forward_hooks:钩子技术,用来提取中间变量
  • training:BarchNorm与Dropout层在训练阶段和测试阶段中采取的策略不同,通过判断training值决定前向传播策略

在PyTorch中保存模型十分简单,所有的Module对象都具有state_dict()函数,返回当前Module所有的状态数据。将这些状态数据保存后,下次使用模型时即可利用model.load_state_dict()函数将状态加载进来。

# 保存模型
t.save(net.state_dict(), 'net.pth')

# 加载已保存的模型
net2 = Net()
net2.load_state_dict(t.load('net.pth'))

将Module放在GPU上运行

  • model = model.cuda():将模型的所有参数转存到GPU
  • input.cuda():将输入数据放置到GPU上

在多个GPU上并行计算:

class torch.nn.DataParallel(module, device_ids=None, output_device=None, dim=0)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值