在PyTorch中提供了一种非常方便的方法,可以帮助我们实现对模 型中后向传播梯度的自动计算,避免了“重复造轮子”,这就是接下来要 重点介绍的torch.autograd包。通过使用 torch.autograd 包,可以使模型参 数自动计算在优化过程中需要用到的梯度值,在很大程度上帮助降低了 实现后向传播代码的复杂度。
torch.autograd包的主要功能是完成神经网络后向传播中的链式求 导,手动实现链式求导的代码会给我们带来很大的困扰,而 torch.autograd 包中丰富的类减少了这些不必要的麻烦。实现自动梯度功 能的过程大致为:先通过输入的Tensor数据类型的变量在神经网络的前 向传播过程中生成一张计算图,然后根据这个计算图和输出结果准确计 算出每个参数需要更新的梯度,并通过完成后向传播完成对参数的梯度 更新。
在实践中完成自动梯度需要用到torch.autograd包中的Variable类对 我们定义的Tensor数据类型变量进行封装,在封装后,计算图中的各个 节点就是一个Variable对象,这样才能应用自动梯度的功能。
如果已经按照如上方式完成了相关操作,则在选中了计算图中的某 个节点时,这个节点必定会是一个Variable对象,用X来代表我们选中的节点,那么X.data代表Tensor数据类型的变量,X.grad也是一个Variable对象,不过它表示的是X的梯度,在想访问梯度值时需要使用X.grad.data。
下面通过一个自动梯度的示例来看看如何使用
torch.autograd.Variable类和torch.autograd包。
import torch
from torch.autograd import Variable
batch_n = 100
hidden_layer = 100
input_data = 1000
output_data = 10
# requires_grad = False表示进行自动梯度计算过程中不会保留梯度值
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 = 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()
# loss or loss.item() or loss.data
print('Epoch:{},Loss:{}'.format(epoch, loss.data))
loss.backward()
w1.data -= learning_rate * w1.grad.data
w2.data -= learning_rate * w2.grad.data
w1.grad.data.zero_()
w2.grad.data.zero_()
和之前的代码相比,当前的代码更简洁了,之前代码中的后向传播 计算部分变成了新代码中的 loss.backward(),这个函数的功能在于让模 型根据计算图自动计算每个节点的梯度值并根据需求进行保留,有了这 一步,我们的权重参数 w1.data和 w2.data就可以直接使用在自动梯度过 程中求得的梯度值w1.data.grad和w2.data.grad,并结合学习速率来对现 有的参数进行更新、优化了。在代码的最后还要将本次计算得到的各个 参数节点的梯度值通过grad.data.zero_()全部置零,如果不置零,则计算 的梯度值会被一直累加,这样就会影响到后续的计算。同样,在整个模 型的训练和优化过程中,每个循环都加入了打印loss值的操作,所以最 后会得到20个loss值的打印输出,输出的结果如下:
Epoch:0,Loss:42446340.0
Epoch:1,Loss:62124656.0
Epoch:2,Loss:212499536.
Epoch:3,Loss:613557184.
Epoch:4,Loss:316774848.
Epoch:5,Loss:8154250.5
Epoch:6,Loss:5126338.
Epoch:7,Loss:3525115.
Epoch:8,Loss:2591381.
Epoch:9,Loss:2009312.
Epoch:10,Loss:1627536.0
Epoch:11,Loss:1365516.
Epoch:12,Loss:1177440.375
Epoch:13,Loss:1036691.
Epoch:14,Loss:927293.625
Epoch:15,Loss:839377.
Epoch:16,Loss:766661.
Epoch:17,Loss:705069.9375
Epoch:18,Loss:651869.875
Epoch:19,Loss:605188.0625
其实除了可以采用自动梯度方法,我们还可以通过构建一个继承了torch.nn.Module的新类,来完成对前向传播函数和后向传播函数的重写。在这个新类中,我们使用forward作为前向传播函数的关键字,使用backward作为后向传播函数的关键字。下面介绍如何使用自定义传播函数的方法,来调整之前具备自动梯度功能的简易神经网络模型。整个代码如下:
import torch
from torch.autograd import Variable
batch_n = 100
hidden_layer = 100
input_data = 1000
output_data = 10
class Model(torch.nn.Module):
def __init__(self):
super(Model, self).__init__()
def forward(self, input, w1, w2):
x = torch.mm(input, w1)
x = torch.clamp(x, min=0)
x = torch.mm(x, w2)
return x
def backward(self):
pass
model = Model()
# requires_grad = False表示进行自动梯度计算过程中不会保留梯度值
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 = 20
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:{}'.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_()
其中通过 class Model(torch.nn.Module)完成了类继承的操作,之后分别是类的初始 化,以及forward函数和backward函数。forward函数实现了模型的前向 传播中的矩阵运算,backward实现了模型的后向传播中的自动梯度计 算,后向传播如果没有特别的需求,则在一般情况下不用进行调整。在 定义好类之后,我们就可以对其进行调用了。
在20次训练后,20个loss值的打印输出如下:
Epoch:0,Loss:50420576.0
Epoch:1,Loss:106698128.
Epoch:2,Loss:380107904.0
Epoch:3,Loss:688367040.0
Epoch:4,Loss:55686864.
Epoch:5,Loss:18643374.0
Epoch:6,Loss:10126001.
Epoch:7,Loss:6374650.0
Epoch:8,Loss:4365133.
Epoch:9,Loss:3169498.
Epoch:10,Loss:2409720.
Epoch:11,Loss:1904333.75
Epoch:12,Loss:1554992.
Epoch:13,Loss:1305658.
Epoch:14,Loss:1122339.75
Epoch:15,Loss:983649.125
Epoch:16,Loss:875711.
Epoch:17,Loss:789469.5
Epoch:18,Loss:718763.
Epoch:19,Loss:659620.625