【PyTorch笔记】60分钟入门PyTorch——神经网络(用PyTorch构建一个神经网络)

Neural Networks

1. 神经网络

torch.nn包可以用来构建神经网络,nn包依赖于autograd包去定义模型并对其进行求导

一个nn.Module包含很多层,和一个以input作为输入的前向方法forward,并返回输出output

下面是一个分类数字图像的网络

convnet

卷积网络

这是一个简单的前馈神经网络,网络接受输入数据,一层接着一层的传输输入数据,最后给出输出结果。

一个神经网络的典型训练过程如下:

  • 定义包括可学习参数(或权重)的网络,
  • 用数据集做迭代
  • 计算损失(输出值和真实值之间的差异)
  • 将梯度反向传播进网络的参数
  • 更新网络的参数,经典的一个简单的更新规则: w e i g h t = w e i g h t − l e a r n i n g _ r a t e ∗ g r a d i e n t weight = weight - learning\_rate *gradient weight=weightlearning_rategradient

2. 定义网络

定义网络

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

class Net(nn.Module):

    def __init__(self):
        super(Net,self).__init__()
        # 输入通道:1,输出通道:6,卷积核:3*3
        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):
        # 最大池化层(2,2)
        x = F.max_pool2d(F.relu(self.conv1(x)),2)
        # 如果是方形,可以使用单个数字定义
        x = F.max_pool2d(F.relu(self.conv2(x)),2)
        # 除batch维度外,展开所有维度
        x = torch.flatten(x,1)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

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, bias=True)
  (fc2): Linear(in_features=120, out_features=84, bias=True)
  (fc3): Linear(in_features=84, out_features=10, bias=True)
)

必须要定义forward函数,计算梯度的backward函数会使用autograd自动定义,在forward函数中可以使用任何针对Tensor的操作

net.parameters()返回一个模型需要学习的参数

params = list(net.parameters())
print(len(params))
print(params[0].size()) # conv1的权重
10
torch.Size([6, 1, 5, 5])

随机构造32×32大小的输入数据,这个网络结构(LeNet)期待的输入数据是32×32。如果要在MINIST数据集上使用这个网络,需要把数据集中的图像调整到32×32

import torch
input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)
tensor([[ 0.0242, -0.0113, -0.1382, -0.1108,  0.1062,  0.0663,  0.0473,  0.0761, 0.1049, -0.0412]], grad_fn=<AddmmBackward0>)

将所有参数的梯度缓存清零,然后进行随机梯度的反向传播

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

torch.nn只支持小批量输入,整个torch.nn包都只支持小样本的输入,而不是单个样本

例如:nn.Conv2d将接受一个四维的张量:nSamples×nChannels×Height×Width

如果有单个样本,使用input.unsqueeze(0)来添加一个假batch维度

回归一下到目前为止见过的所有类

  • torch.Tensor:支持autograd操作(比如backward())的多维数组,同时保存梯度(tensor形式)
  • nn.Module:神经网络模块,方便封装参数,帮助移到到GPU上运行、导出和加载等
  • nn.Parameter:一种张量,当把它赋值给Module的属性时,会自动注册为参数
  • autograd.Function:实现autograd操作的前向和后向定义,每个Tensor操作会创造至少一个Function节点,这个节点连接到创建tensor并对其历史进行编码的函数

现在,我们已覆盖如下内容

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

还剩下:

  • 计算损失
  • 更新网络的权重参数

3. 损失函数

损失函数接受(output,target)作为输入(output为网络的输出,target为实际值),计算值来评估output和target的误差

nn包下有几种不同的损失函数,一个简单的损失是:nn.MSELoss,计算output和target之间的均方误差

output = net(input)
target = torch.randn(10)
target = target.view(1,-1) # 保持和output输出形状相同
criterion = nn.MSELoss()

loss = criterion(output,target)
print(loss)
tensor(0.3965, grad_fn=<MseLossBackward0>)

现在,如果你反向跟踪loss,使用.grad_fn属性,将会看到下面这样的计算图

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

所以,当调用loss.backward()的时候,整个图被分为神经网络参数和图中设置requires_grad=True的所有tensor,帮助.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
<MseLossBackward0 object at 0x0000019DABCB18B0>
<AddmmBackward0 object at 0x0000019DABCB1250>
<AccumulateGrad object at 0x0000019DABCB18B0>

4. 反向传播

为了反向传播误差,要调用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
tensor([0., 0., 0., 0., 0., 0.])
conv1.bias.grad after backward
tensor([-0.0079,  0.0134,  0.0143,  0.0054,  0.0081, -0.0033])

现在,我们已经知道如何使用损失函数

接下来最后一件要学习的是:更新网络权重

5. 更新权重

在实践中最简单的更新规则是随机梯度下降(SGD)

w e i g h t = w e i g h t − l e a r n i n g _ r a t e ∗ g r a d i e n t weight=weight−learning\_rate∗gradient weight=weightlearning_rategradient

使用Python代码简单实现:

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

torch.optim包里面有各种更新方法的实现

import torch.optim as optim

# 创建优化器
optimizer = optim.SGD(net.parameters(),lr=0.01)

# 训练循环中
optimizer.zero_grad()
output = net(input)
loss = criterion(output,target)
loss.backward()
optimizer.step()  # 更新参数

梯度是可以累积的,所以要使用optimizer.zero_grad()手动将梯度缓冲区设置为零

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值