使用自动梯度搭建一个二层结构的神经网络模型
前面已经 搭建了一个简易神经网络模型,接下来使用自动梯度搭建一个二层结构的神经网络模型。
1 torch.autograd 和 Variable
通过使用 torch.autograd(主要功能是完成神经网络后向传播中的链式求导) 包,可以使模型参数自动计算在优化过程中需要用到的梯度值,在很大程度上帮助降低了实现后向传播代码的复杂度。
实现自动梯度功能的过程大致为:先通过输入的 Tensor 数据类型的变量在神经网络的前向传播过程中生成一张计算图,然后根据这个计算图和输出结果准确计算出每个参数需要更新的梯度,并通过完成后向传播完成对参数的梯度更新。
在实践中完成自动梯度需要用到 torch.autograd 包中的 Variable 类对我们定义的 Tensor数据类型变量进行封装,在封装后,计算图中的各个节点就是一个 Variable 对象,这样才能应用自动梯度的功能。
'''导入包'''
import torch
from torch.autograd import Variable
batch_n = 100
hidden_layer = 100
input_data = 1000
output_data = 10
'''用 Variable 类对 Tensor 数据类型变量进行封装'''
x = Variable(torch.randn(batch_n, input_data), requires_grad = False) #100*1000
y = Variable(torch.randn(batch_n, output_data), requires_grad = False) #100*10
w1 = Variable(torch.randn(input_data, hidden_layer), requires_grad = True) #1000*100
w2 = Variable(torch.randn(hidden_layer, output_data), requires_grad = True) #100*10
epoch_n = 20
learning_rate = 1e-6
'''新的模型训练和参数优化的代码'''
for epoch in range(epoch_n):
y_pred = x.mm(w1).clamp(min = 0).mm(w2)
loss = (y_pred - y).pow(2).sum()
print("Epoch:{}, Loss:{:.4f}".format(epoch,loss))
loss.backward()
w1.data -= learning_rate*w1.grad.data
w2.data -= learning_rate*w2.grad.data
w1.grad.data.zero_()
w2.grad.data.zero_()
Epoch:0, Loss:36054716.0000
Epoch:1, Loss:46005456.0000
Epoch:2, Loss:145356976.0000
Epoch:3, Loss:482522144.0000
Epoch:4, Loss:511481344.0000
Epoch:5, Loss:5381819.0000
Epoch:6, Loss:3527648.0000
Epoch:7, Loss:2548170.7500
Epoch:8, Loss:1980495.5000
Epoch:9, Loss:1623881.0000
Epoch:10, Loss:1383441.8750
Epoch:11, Loss:1210102.3750
Epoch:12, Loss:1077163.5000
Epoch:13, Loss:970541.5000
Epoch:14, Loss:881934.3750
Epoch:15, Loss:806076.7500
Epoch:16, Loss:740382.0000
Epoch:17, Loss:682604.8750
Epoch:18, Loss:631387.1875
Epoch:19, Loss:585655.7500
和之前的代码相比,当前的代码更简洁了,之前代码中的后向传播计算部分变成了新代码中的 loss.backward(),这个函数的功能在于让模型根据计算图自动计算每个节点的梯度值并根据需求进行保留。
2 自定义传播函数
还可以通过构建一个继承了 torch.nn.Module的新类,来完成对前向传播函数和后向传播函数的重写。
在这个新类中,我们使用 forward作为前向传播函数的关键字,使用 backward 作为后向传播函数的关键字。
2.1 搭建简易神经网络
'''导入包'''
import torch
from torch.autograd import Variable
batch_n = 64
hidden_layer = 100
input_data = 1000
output_data = 10
'''何定义我们的前向传播 forward 函数和后向传播backward 函数'''
class Model(torch.nn.Module): #类继承
def __init__(self): #类的初始化
super(Model, self).__init__()
def forward(self, input, w1, w2): #forward 函数,实现了模型的前向传播中的矩阵运算
x = torch.mm(input, w1)
x = torch.clamp(x, min = 0)
x =torch.mm(x, w2)
return x
def backward(self): #backward 函数,实现了模型的后向传播中的自动梯度计算,后向传播如果没有特别的需求,则在一般情况下不用进行调整
pass
model = Model() #调用类
2.2 训练模型、参数优化
'''对模型进行训练和对参数进行优化'''
x = Variable(torch.randn(batch_n, input_data), requires_grad = False)
y = Variable(torch.randn(batch_n, output_data), requires_grad = False)
w1 = Variable(torch.randn(input_data, hidden_layer), requires_grad = True)
w2 = Variable(torch.randn(hidden_layer, output_data) ,requires_grad = True)
epoch_n = 30
learning_rate = 1e-6
for epoch in range(epoch_n):
y_pred = model(x, w1, w2) #输出模型预测值
loss = (y_pred - y).pow(2).sum()
print("Epoch:{}, Loss:{:.4f}".format(epoch,loss))
loss.backward()
w1.data -= learning_rate * w1.grad.data
w2.data -= learning_rate * w2.grad.data
w1.grad.data.zero_()
w2.grad.data.zero_()
Epoch:0, Loss:30154982.0000
Epoch:1, Loss:27915354.0000
Epoch:2, Loss:27383836.0000
Epoch:3, Loss:24799186.0000
Epoch:4, Loss:19323030.0000
Epoch:5, Loss:12777702.0000
Epoch:6, Loss:7516660.0000
Epoch:7, Loss:4245948.0000
Epoch:8, Loss:2488885.0000
Epoch:9, Loss:1586501.0000
Epoch:10, Loss:1111118.8750
Epoch:11, Loss:840452.1875
Epoch:12, Loss:670228.2500
Epoch:13, Loss:552621.6875
Epoch:14, Loss:465184.7188
Epoch:15, Loss:396748.5625
Epoch:16, Loss:341388.4688
Epoch:17, Loss:295734.4375
Epoch:18, Loss:257580.7188
Epoch:19, Loss:225402.2812
Epoch:20, Loss:198073.2188
Epoch:21, Loss:174757.3594
Epoch:22, Loss:154748.5938
Epoch:23, Loss:137464.6094
Epoch:24, Loss:122470.6094
Epoch:25, Loss:109405.7500
Epoch:26, Loss:97988.0469
Epoch:27, Loss:87962.7266
Epoch:28, Loss:79135.0156
Epoch:29, Loss:71336.7109
从结果来看,对参数的优化同样在顺利进行,每次输出的 loss 值也在逐渐减小。