神经网络可以用torch.nn包来构建。之前的内容已经了解了 自动梯度,神经网络是基于自动梯度来定义一些模型。一个 nn.Module 包含层和返回输出的方法 forward(input)。例如,看看这个分类数字图像的网络:

神经网络模型怎么进行Shap计算 神经网络模型怎么用_pytorch

这是一个简单的前馈网络。 它接受输入,一个接一个地通过几个层,最后给出输出。

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

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

  1、定义网络

让我们定义这个网络结构:

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个输出通道,5x5卷积核
        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)  # 5*5 from image dimension
        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, 2))
        # 如果其尺寸是一个square只能指定一个数字
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        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)
)

我们只需要定义前向函数,因为后向函数(计算梯度的函数)会使用 autograd 自动定义。 我们可以在 forward 前向函数中使用任何张量操作。模型的可学习参数由 net.parameters() 返回:

params = list(net.parameters())#通过调用net.parameters()返回模型可训练的参数
print(len(params))
print(params[0].size())  # conv1的权重

 输出结果:

神经网络模型怎么进行Shap计算 神经网络模型怎么用_深度学习_02

尝试生成一个随机的 32x32 输入。 因为这个网络(LeNet)的预期输入大小是 32x32。 要在 MNIST 数据集上使用此网络,需要将数据集中的图像大小调整为 32x32。

input = torch.randn(1, 1, 32, 32)#迭代一个输入
out = net(input)
print(out)

 输出结果:

神经网络模型怎么进行Shap计算 神经网络模型怎么用_人工智能_03

 将所有参数的梯度缓冲区和具有随机梯度的反向传播归零:

net.zero_grad()#将所有参数梯度缓存器置零
out.backward(torch.randn(1, 10))#调用反向传播

2、损失函数

一个损失函数需要一对输入:模型输出和目标,然后计算一个值来评估输出距离目标多远。在nn包下有几个不同的损失函数。一个简单的是:nn.MSELoss,计算输出和目标之间的均方误差。

output = net(input)
target = torch.randn(10)  # 例如,一个虚拟目标
target = target.view(1, -1)  # 使其形状与输出相同
criterion = nn.MSELoss()

loss = criterion(output, target)
print(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 Tensor 都会随着梯度累积。

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

3、反向传播

要反向传播loss,我们所要做的就是使用 loss.backward()。 在这之前需要清除现有的梯度,否则梯度将累积到现有的梯度中。现在调用 loss.backward(),然后看一下con1的偏置项在反向传播之前和之后的变化:

net.zero_grad()     # 将所有参数的梯度缓冲区归零
print('conv1.bias.grad before backward')
print(net.conv1.bias.grad)
loss.backward()
print('conv1.bias.grad after backward')
print(net.conv1.bias.grad)

 运行结果:

神经网络模型怎么进行Shap计算 神经网络模型怎么用_2d_04

4、更新权重

实践中使用的最简单的更新规则是随机梯度下降(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
# 创建你的优化器,导入参数和学习率
optimizer = optim.SGD(net.parameters(), lr=0.01)
criterion = nn.CrossEntropyLoss()#定义交叉熵损失函数
# 在训练循环中:
optimizer.zero_grad()   # 将梯度缓冲归零
output = net(input)     # 数据正向传播
loss = criterion(output, target)#计算误差
loss.backward()       #误差反向传播
optimizer.step()    # 更新

笔记:torch.nn 仅支持小批量。 整个 torch.nn 包仅支持作为小批量样本的输入,而不是单个样本。例如,nn.Conv2d 将采用 nSamples x nChannels x Height x Width 的 4D 张量。如果您只有一个样本,只需使用 input.unsqueeze(0) 添加一个假的批次维度。