简易神经网络搭建与训练
实例代码及注释如下
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
# 构建神经网络卷积核,映射函数,前向传播
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
# 设置2D卷积核 1 input -> 6 output 3*3 kernel
# 6 input -> 16 output 3*3 kernel
self.conv1 = nn.Conv2d(1, 6, 3)
self.conv2 = nn.Conv2d(6, 16, 3)
# an affine operation: y = Wx + b
# 设置从16*6*6到10的映射函数
self.fc1 = nn.Linear(16 * 6 * 6, 120) # 6*6 from image dimension
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
# 基于卷积结果的relu结果,最大值池化至 2*2 窗口
# 尺寸变化:n*n -> (n-1)*(n-1) -> (n-1)*(n-1) -> 2*2
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
# 如果大小是正方形,则只声明一个数字
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)
# 可学习参数
params = list(net.parameters())
print(len(params))
print(params[0].size()) # conv1's .weight
# 尝试一个随机的32x32输入
input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)
# 使用随机梯度将所有参数和反向的梯度缓冲区归零:
net.zero_grad()
out.backward(torch.randn(1, 10))
# 计算均方误差
output = net(input)
target = torch.randn(10)
target = target.view(1, -1) # 使尺寸相同
criterion = nn.MSELoss()
loss = criterion(output, target)
print(loss)
# 后退
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
# 清除现有梯度
# 看一下conv1在向后之前和之后的偏差梯度
net.zero_grad() # zeroes the gradient buffers of all parameters
print('conv1.bias.grad before backward')
print(net.conv1.bias.grad)
loss.backward()
print('conv1.bias.grad after backward')
print(net.conv1.bias.grad)
# 随机梯度下降 learning rate = 0.01
optimizer = optim.SGD(net.parameters(), lr=0.01)
# 训练100次
for num in range(1,100):
optimizer.zero_grad() # 清楚现有梯度
output = net(input)
loss = criterion(output, target)
print(loss)
loss.backward()
optimizer.step() # Does the update