PyTorch学习之 神经网络
学习网站
http://pytorch123.com/SecondSection/neural_networks/
神经网络
神经网络可以通过torch.nn包来构建,它是基于自动梯度来构建一些模型,一个nn.Module包括层和一个方法forward(input),同时也会返回一个输出(output)。
下图是一个简单的前馈神经网络LeNet,一个简单的神经网络包括一下几点:
- 定义一个包含可训练参数的神经网络
- 迭代整个输入
- 通过神经网络处理输入
- 计算损失函数值
- 反向传播梯度到神经网络的参数
- 更新网络的参数,典型的一个简单的更新方法: weight = weight - learning_rate*gradient·
使用torch编写上面的神经网络
# -*- coding: UTF-8 -*-
"""
Modify: 2019-12-14
"""
import torch
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
# super(Net,self) 首先找到 Net 的父类(就是类 nn.Module),
# 然后把类 Net 的对象转换为类 FooParent 的对象
super(Net, self).__init__()
#构建卷积
self.conv1 = nn.Conv2d(1, 6, 5) # 输入 1, 输出 6, 卷积 5*5,即6个不同的5*5卷积
self.conv2 = nn.Conv2d(6, 16, 5) # 输入 6, 输出 16, 卷积 5*5
#构建全连接 y = wx + b
self.fc1 = nn.Linear(16*5*5, 120) # 因为有16张 5*5 map, 所以有 16*5*5 = 400个输入,因此有120个400*1的向量
self.fc2 = nn.Linear(120, 84) # 84 个 120 * 1 个向量
self.fc3 = nn.Linear(84, 10) # 10个 84 * 1 个向量
def forward(self, x):
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2)) # 2*2 的最大值池化, relu为激活函数
x = F.max_pool2d(F.relu(self.conv2(x)), 2) # 如果池化窗口是矩形的,则可以只设定为2
x = x.view(-1, self.num_flat_features(x)) # view函数将张量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:] # 单张图像的size
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)
# )
# 一个模型的训练参数可以通过调用 net.parameterss()返回
params = list(net.parameters())
print(len(params))
print(params[0].size())
# 输出
# 10
# torch.Size([6, 1, 5, 5])
#下面尝试随机生成一个32*32的输入。注意:期望的输入维度是32*32
# 为了使用这个网络在MNIST数据上使用,需要把数据集中的图像维度修改为32*32
input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)
#输出
# tensor([[ 0.0469, 0.0975, 0.0686, 0.0793, 0.0673, 0.0325, -0.0455, -0.0428,
# # -0.0671, -0.0067]], grad_fn=<AddmmBackward>)
#把所有参数梯度缓存器置0, 用随机的梯度来反向传播
net.zero_grad()
out.backward(torch.randn(1, 10))
上面的代码定义了一个神经而桑落,并且处理了输入以及调用反向传播。
下面开始,计算损失值和更新网络中的权重:
- 损失函数:一个损失函数需要一对输入:模型输出和目标,然后计算一个值来评估输出距离目标有多远。nn包中包含着一些不同的损失函数,一个简单的损失函数就是 nn.MSELoss ,即均方误差。
output = net(input)
target = torch.randn(10) # 目标值
target = target.view(1, -1) # 将其变换成与输出值相同的尺寸格式
criterion = nn.MSELoss() # MSELoss
loss = criterion(output, target) #计算输出与目标之间的损失函数
print(loss)
#输出
#tensor(0.3130, grad_fn=<MseLossBackward>)
- 反向传播:为了使损失进行反向传播,需要做的仅仅是使用loss.backward()。首先要清空现存的梯度,否则会和现存的梯度累计到一起
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)
#输出
# conv1.bias.grad before backward
# tensor([0., 0., 0., 0., 0., 0.])
# conv1.bias.grad after backward
# tensor([-0.0040, -0.0041, 0.0244, -0.0020, -0.0054, -0.0084])
- 更新神经网络参数
最简单的更新计算方式就是使用随机梯度下降 weight = weight - learning_rate * gradient
python实现方式
learning_rate = 0.01
for f in net.parameters():
f.data.sub_(f.grad.data * learning_rate)
torch.optim中内置了多种权重更新规则,类似于SGD, Nesterov-SGD,Adam, RMSProp等。
使用方式如下:
import torch.optim as optim
# 创建优化器
optimizer = optim.SGD(net.parameters(), lr=0.01)
# 在训练的循环中加入一下代码
optimizer.zero_grad() # 将梯度置0
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step() # 更新权重