神经网络
卷积网 这是一个简单的前馈网络。 它获取输入,将其一层又一层地馈入,然后最终给出输出。
神经网络的典型训练过程如下:
定义具有一些可学习参数(或权重)的神经网络
遍历输入数据集
通过网络处理输入
计算损失(输出正确的距离有多远)
将梯度传播回网络参数
通常使用简单的更新规则来更新网络的权重:weight = weight - learning_rate * gradient
定义网络 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import torchimport torch.nn as nnimport torch.nn.functional as Fclass Net (nn.Module): def __init__ (self ): super (Net, self).__init__() self.conv1 = nn.Conv2d(1 , 6 , 5 ) 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 ): x = F.max_pool2d(F.relu(self.conv1(x)), (2 , 2 )) 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)
super() 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。
output:
1 2 3 4 5 6 7 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
函数,就可以使用autograd
自动定义backward
函数(计算梯度)。
模型的可学习参数由net.parameters()
返回
1 2 3 params = list (net.parameters()) print (len (params))print (params[0 ].size())
output:
1 2 10 torch.Size([6 , 1 , 5 , 5 ])
让我们尝试一个32x32
随机输入。
注意:该网络的预期输入大小(LeNet)为32x32
。 要在 MNIST 数据集 上使用此网络,请将图像从数据集中调整为32x32
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import torchimport torch.nn as nnimport torch.nn.functional as Fclass Net (nn.Module): def __init__ (self ): super (Net, self).__init__() self.conv1 = nn.Conv2d(1 , 6 , 5 ) 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 ): x = F.max_pool2d(F.relu(self.conv1(x)), (2 , 2 )) 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() input = torch.randn(1 , 1 , 32 , 32 )out = net(input ) print (out)
output:
1 2 tensor([[ 0.0265 , 0.0280 , 0.0228 , 0.1131 , 0.0270 , -0.1227 , 0.0928 , 0.0028 , 0.0389 , -0.0410 ]], grad_fn=<AddmmBackward>)
使用随机梯度将所有参数和反向传播的梯度缓冲区归零:
1 2 net.zero_grad() out.backward(torch.randn(1 , 10 ))
torch.nn
仅支持小批量。 整个torch.nn
包仅支持作为微型样本而不是单个样本的输入。
torch.Tensor
-一个多维数组 ,支持诸如backward()
的自动微分操作。 同样,保持相对于张量的梯度。
nn.Module
-神经网络模块。 封装参数 的便捷方法,并带有将其移动到 GPU,导出,加载等的帮助器。
nn.Parameter
-一种张量,即将其分配为Module
的属性时,自动注册为参数。
autograd.Function
-实现自动微分操作的正向和反向定义。 每个Tensor
操作都会创建至少一个Function
节点,该节点连接到创建Tensor
的函数,并且编码其历史记录。
损失函数 损失函数采用一对(输出,目标)输入,并计算一个值,该值估计输出与目标之间的距离。
nn
包下有几种不同的损失函数 。 一个简单的损失是:nn.MSELoss
,它计算输入和目标之间的均方误差。
1 2 3 4 5 6 7 output = net(input ) target = torch.randn(10 ) target = target.view(1 , -1 ) criterion = nn.MSELoss() loss = criterion(output, target) print (loss)
output:
1 tensor(1.1649, grad_fn=<MseLossBackward0>)
现在,如果使用.grad_fn
属性向后跟随loss
,您将看到一个计算图,如下所示:
1 2 3 4 input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d -> view -> linear -> relu -> linear -> relu -> linear -> MSELoss -> loss
当我们调用loss.backward()
时,整个图将被微分。图中具有requires_grad=True
的所有张量将随梯度累积其.grad
张量
让我们向后走几步:
1 2 3 print (loss.grad_fn) print (loss.grad_fn.next_functions[0 ][0 ]) print (loss.grad_fn.next_functions[0 ][0 ].next_functions[0 ][0 ])
output:
1 2 3 <MseLossBackward0 object at 0x7f71283dd048> <AddmmBackward0 object at 0x7f71283dd7f0> <AccumulateGrad object at 0x7f71283dd7f0>
反向传播 要反向传播误差,我们要做的只是对loss.backward()
。 不过,需要清除现有的梯度,否则梯度将累积到现有的梯度中。
现在,我们将其称为loss.backward()
,然后看一下向后前后conv1
的偏差梯度。
1 2 3 4 5 6 7 8 9 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)
output:
1 2 3 4 conv1.bias.grad before backward tensor([0., 0., 0., 0., 0., 0.]) conv1.bias.grad after backward tensor([ 0.0188, 0.0172, -0.0044, -0.0141, -0.0058, -0.0013])
更新权重 实践中使用的最简单的更新规则是 随机梯度下降(SGD)
:
weight = weight - learning_rate * gradient
下一次的权重 = 本次的权重值 - 学习率 * 梯度(误差)
实现 1 2 3 learning_rate = 0.01 for f in net.parameters(): f.data.sub_(f.grad.data * learning_rate)
在使用神经网络时,您希望使用各种不同的更新规则,如 SGD,Nesterov-SGD,Adam,RMSProp 等……
为实现此目的,我们构建了一个小包装:torch.optim
,可实现所有这些方法。
1 2 3 4 5 6 7 8 9 10 11 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()
将梯度缓冲区手动设置为零,这是因为反向传播 部分中所述累积了梯度。