侧边栏壁纸
  • 累计撰写 5 篇文章
  • 累计创建 0 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

Pytroch学习-神经网络

Administrator
2022-07-18 / 0 评论 / 1 点赞 / 66 阅读 / 9152 字 / 正在检测是否收录...

Pytorch神经网络

一、Pytorch神经网络介绍

神经网络可以通过torch.nn包来创建。我们之前简单的了解了autograd,而nn会使用autograd来定义模型以及求梯度。一个nn.Module对象包括了许多网络层(layer),并且有一个forward(input)方法来返回output。

训练一个神经网络需要如下的步骤:

  • 定义一个神经网络,它通常有一些可以训练的参数
  • 迭代一个数据集
  • 处理网络的输入
  • 计算loss(会调用Module对象的forward方法)
  • 计算loss对参数的梯度
  • 更新参数,通常会使用如下的梯度下降方法来更新:
    $$
    weight=weight-learning_rate*gradient
    $$

二、定义网络

首先给出一个创建网络的例子:

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


class Net(nn.Module):

    def __init__(self):
        super(Net, self).__init__()
        # 输入是1个通道的灰度图,输出6个通道(feature map),使用5x5的卷积核
        self.conv1 = nn.Conv2d(1, 6, 5)
        # 第二个卷积层也是5x5,有16个通道
        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):
        # 32x32 -> 28x28 -> 14x14
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
        # 14x14 -> 10x10 -> 5x5
        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(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))
# 10
print(params[0].size())  # conv1的weight
# torch.Size([6, 1, 5, 5])

三、测试网络

接着我们尝试一个随机的32x32的输入来检验(sanity check)网络定义没有问题。注意:这个网络(LeNet)期望的输入大小是32x32。如果使用MNIST数据集(28x28),我们需要缩放到32x32。

input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)
# tensor([[-0.0021,  0.0369,  0.0975, -0.0612,  0.1577,  0.0053,  0.1004, -0.0794,
#           0.1761,  0.0336]], grad_fn=<AddmmBackward0>)

默认的梯度会累加,因此我们通常在backward之前清除掉之前的梯度值:

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

注意:torch.nn只支持mini-batches的输入。整个torch.nn包的输入都必须第一维是batch,即使只有一个样本也要弄成batch是1的输入。

比如,nn.Conv2d的输入是一个4D的Tensor,shape是nSamples x nChannels x Height x Width。如果你只有一个样本(nChannels x Height x Width),那么可以使用input.unsqueeze(0)来增加一个batch维。

四、损失函数

损失函数的参数是(output, target)对,output是模型的预测,target是实际的值。损失函数会计算预测值和真实值的差别,损失越小说明预测的越准。
PyTorch提供了这里有许多不同的损失函数: http://pytorch.org/docs/nn.html#loss-functions。最简单的一个损失函数是:nn.MSELoss,它会计算预测值和真实值的均方误差。比如:

output = net(input)
target = torch.arange(1, 11)  # 随便伪造的一个“真实值” 
target = target.view(1, -1)  # 把它变成output的shape(1, 10) 
criterion = nn.MSELoss()

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

如果从loss往回走,需要使用tensor的grad_fn属性,我们Negative看到这样的计算图:

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

因此当调用loss.backward()时,PyTorch会计算这个图中所有requires_grad=True的tensor关于loss的梯度。

print(loss.grad_fn)  # MSELoss
print(loss.grad_fn.next_functions[0][0])  # Add
print(loss.grad_fn.next_functions[0][0].next_functions[0][0])  # Expand
# <MseLossBackward0 object at 0x000001643BE00220>
# <AddmmBackward0 object at 0x000001643BDD2EB0>
# <AccumulateGrad object at 0x000001643BE00220>

五、计算梯度

在调用loss.backward()之前,我们需要清除掉tensor里之前的梯度,否则会累加进去。


net.zero_grad()     # 清掉tensor里缓存的梯度值。

print('conv1.bias.grad before backward')
print(net.conv1.bias.grad)

loss.backward()

print('conv1.bias.grad after backward')
print(net.conv1.bias.grad)

六、更新参数

更新参数最简单的方法是使用随机梯度下降(SGD):
$$ weight=weight−learningrate∗gradientweight=weight−learningrate∗gradient
$$
我们可以使用如下简单的代码来实现更新:

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

# 创建optimizer,需要传入参数和learning rate
optimizer = optim.SGD(net.parameters(), lr=0.01)

# 清除梯度
optimizer.zero_grad()  
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step()    # optimizer会自动帮我们更新参数

1

评论区