这里有一个玩具模型。我在调用backward
之前打印了模型参数,仅此一次,然后再次打印模型参数。参数没有变化。如果我在调用backward
之后添加一行model:updateParameters(<learning_rate>)
,我会看到参数更新。
但是在我见过的示例代码中,例如https://github.com/torch/demos/blob/master/train-a-digit-classifier/train-on-mnist.lua,没有人实际调用updateParameters
。此外,看起来optim.sgd
、optim.adam
或nn.StochasticGradient
也从未调用updateParameters
。我在这里错过了什么?参数是如何自动更新的?如果我必须调用updateParameters
,为什么没有示例这样做?
require 'nn'require 'optim'local model = nn.Sequential()model:add(nn.Linear(4, 1, false))local params, grads = model:getParameters()local criterion = nn.MSECriterion()local inputs = torch.randn(1, 4)local labels = torch.Tensor{1}print(params)model:zeroGradParameters()local output = model:forward(inputs)local loss = criterion:forward(output, labels)local dfdw = criterion:backward(output, labels)model:backward(inputs, dfdw)-- 如果取消下面的注释行,参数将被更新:-- model:updateParameters(1000)print(params)
回答:
backward()
不应该改变参数,它只是计算误差函数相对于网络所有参数的导数。
一般来说,训练过程是以下步骤的序列:
repeat local output = model:forward(input) --查看模型的预测 local loss = criterion:forward(output, answer) --查看误差有多大 local loss_grad = criterion:backward(output, answer) --查看误差最大的地方 model:backward(input,loss_grad) --查看网络的每个特定参数对误差的责任有多大 model:updateParameters(learningRate) --根据参数的错误程度修正参数 model:zeroGradParameters() --网络参数现在已经不同,所以旧的梯度现在没有用了until is_user_satisfied()
updateParameters
在这里实现了最简单的优化算法(梯度下降)。如果你愿意,你可以使用自己的函数来代替。理论上,你可以通过网络存储进行显式的循环来更新它们的值。在实践中,你通常会调用getParameters()
local model_parameters,model_parameters_gradient=model:getParameters()
这会给你所有值和梯度的同质张量。这些张量是网络内部的视图,因此对它们的更改会影响网络。你可能不知道网络的哪个点对应哪个值,但大多数优化器并不关心这一点。
optim.sgd
使用的示例如下:
optim.sgd( function_to_return_error_and_its_gradients, model_parameters, optimizer_special_settings)
具体细节在示例中有所介绍,但这里重要的是优化器接收model_parameters
作为参数,这使其可以对网络进行写操作。虽然文档中没有明确说明,但在源代码中可以看到,优化器会更改其输入张量的值(另外,请注意,它返回的正是它接收到的同一个张量)。