Pytorch|官方入门教程-学习笔记(二)-神经网络

背景介绍

Pytorch是torch的一个衍生品,在Python语言中可以替代numpy的一个强大的科学计算库。

Pytorch与TensorFlow的主要区别:

  • TensorFlow是基于静态计算图,需要先定义再运行,一次定义多次运行;
  • Pytorch基于动态计算图,在运行过程中进行定义,可以实现多次构建多次运行。

Pytorch官方文档传送门

神经网络

torch.nn是神经网络的模块化接口,nn构建于Autograd之上,可用来定义和运行神经网络。torch.nn.module是nn一个重要的类,既可以表示神经网络的某一层(layer),也可以表示一个包含很多层的神经网络。一般在使用中,可以继承nn.module,编写自己的网络层。

一个典型的神经网络训练过程包括以下几点:

  • 定义一个包含可训练参数(或权重)的神经网络;
  • 迭代整个输入;
  • 通过神经网络处理输入;
  • 计算损失(当前输出的准确程度);
  • 反向传播梯度到神经网络的参数;
  • 更新网络的参数;

定义网络

现以Lenet网络为示例,演示一个前馈神经网络的定义过程:
LeNet网络结构

该网络的代码描述分为三个部分:卷积->激活->池化。

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

class Net(nn.Module):
    def __init__(self):
        # nn.Module的子类函数必须在构造函数中执行父类的构造函数
        super(Net, self).__init__() # 等价于nn.Module.__init__(self)
        
        # 卷积层
        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)
        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()
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)
)

网络的可训练参数可通过net.parameters()返回,net.named_parameters()可同时返回名称与参数:

param = list(net.parameters())
print(len(param))

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

输出:

10
conv1.weight : torch.Size([6, 1, 5, 5])
conv1.bias : torch.Size([6])
conv2.weight : torch.Size([16, 6, 5, 5])
conv2.bias : torch.Size([16])
fc1.weight : torch.Size([120, 400])
fc1.bias : torch.Size([120])
fc2.weight : torch.Size([84, 120])
fc2.bias : torch.Size([84])
fc3.weight : torch.Size([10, 84])
fc3.bias : torch.Size([10])

对此的x尝试32*32的输入。由于torch.nn只支持mini-batches,一次输入必须以一个batch为单位。比如nn.Conv2d的输入需要4维数据,形如nSamples × nChannels × Height × Width,可将样本数设为1:

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

net.zero_grad()
out.backward(t.rand(1, 10))

输出:

tensor([[-0.0379, 0.0291, -0.0321, 0.1000, -0.0400, -0.0975, -0.0263, 0.0553, 0.0381, -0.0784]], grad_fn=)

损失函数

nn库实现了神经网络中的大多数损失函数。例如nn.MSELoss用来计算均方误差,nn.CrossEntropyLoss用来计算交叉熵损失。对上例中的x定义一个随机指定的目标向量:

output = net(input)
target = t.randn(10)
target = target.view(1, -1)
criterion = nn.MSELoss()

loss = criterion(output, target)
print(loss)

print(loss.grad_fn)
print(net.conv1.bias.grad)

输出:

tensor(1.0112, grad_fn=)

tensor([-0.0333, -0.0038, -0.0306, 0.0085, 0.0234, -0.0376])

反向传播

如果对loss进行反向传播溯源,可观察到其计算图的序列如下:input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d -> view -> linear -> relu -> linear -> relu -> linear -> MSELoss -> loss,当调用loss.backward(),该图会自动生成并自动微分,也会自动计算图中parameters的导数,而且所有在图中requires_grad = True的张量也会让他们的grad张量累计梯度。

net.zero_grad() # 将net中所有可学习参数的梯度清零
print('反向传播之前conv1.bias的梯度')
print(net.conv1.bias.grad)

loss.backward()

print('反向传播之后conv1.bias的梯度')
print(net.conv1.bias.grad)

反向传播之前conv1.bias的梯度
tensor([0., 0., 0., 0., 0., 0.])
反向传播之后conv1.bias的梯度
tensor([ 0.0040, 0.0052, 0.0036, 0.0031, 0.0193, -0.0061])

优化器

反向传播计算完所有参数的梯度后,需要使用优化方法来更新网络的权重和参数。一般采用随机梯度下降(SGD):

torch.optim实现了神经网络中的绝大多数优化方法,例如SGD,Adam,RMSProp等。

import torch.optim as optim

# 新建一个优化器,指定要调整的参数和学习率
optimizer = optim.SGD(net.parameters(), lr = 0.01)

# 训练之前先进行梯度清零(等同于net.zreo_grad)
optimizer.zero_grad()

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

# 反向传播
loss.backward()

# 更新参数
optimizer.step()

print(net.conv1.bias.grad)

tensor([ 0.0018, 0.0017, 0.0028, 0.0059, 0.0082, -0.0086])

你可能感兴趣的:(Pytorch|官方入门教程-学习笔记(二)-神经网络)