1、
用optim来减少损失
首先需要给出一个函数,给定当前的weight作为参数,可以输出损失和损失对应的梯度
optim希望他们优化的weight和对应的梯度是一个一维的Tensor,但是整个网络有很多层,每一层都有很多的参数,怎么做才能成为一维的Tensor呢?用getParameters()函数即可
params, gradParams = model:getParameters()
这个函数返回params,包含了weight和bias,组成一个全新的Tensor。对于梯度则是由gradParams来承担,组成一个全新的连续的Tensor。我们想要优化的参数全都包含在了这个one-dimension的Tensor里面了
print(self.params:size())
print(self.gradParams:size())
require 'optim'
for epoch = 1, 50 do
-- local function we give to optim
-- it takes current weights as input, and outputs the loss
-- and the gradient of the loss with respect to the weights
-- gradParams is calculated implicitly by calling 'backward',
-- because the model's weight and bias gradient tensors
-- are simply views onto gradParams
function feval(params)
gradParams:zero()
local outputs = model:forward(batchInputs)
local loss = criterion:forward(outputs, batchLabels)
local dloss_doutputs = criterion:backward(outputs, batchLabels)
model:backward(batchInputs, dloss_doutputs)
return loss, gradParams
end
optim.sgd(feval, params, optimState)
end
现在分析一下optim的具体使用细节
feval是一个函数,接受的参数是weight和bias的一维Tensor,返回loss,和参数梯度
optim本身需要接受weight和bias的一维Tensor
optimState是关于更新的一系列的参数
2、
解析一下通常的训练流程运用optim
local function evalFn(x) return criterion.output, gradparam end
local output = model:forward(input)
local err = criterion:forward(output, label)
model:zeroGradParameters()
model:backward(input, criterion:backward(output, label))
optfn(evalFn, param, optimState)
在torch里面有两个状态变量(
output
and
gradInput
.)
1)定义evalFn
2) 前传计算输出,更新output状态变量
3)利用criterion来计算损失,self.output更新(criterion的作用是给定输入和目标,根据损失计算梯度)
4)将参数对应的梯度全部置零
5)model反传,给定input和gradOutput,这里的gradOutput是由criterion进行backward计算得来的,计算关于output的梯度,backward函数接受input和gradOutput,计算关于input的梯度,也即gradInput,这时候会调用两个函数
6)然后用文章一开始的optim进行优化,evalFn返回criterion.output,也即损失,gradparam也即梯度,这是evalFn对参数的要求,然后进行更性参数即可
注:需要注意的是,criterion也有output和gradinput两个状态变量,只不过引用的时候要用criterion.output...和model的output是分开的
另外一种写法,和上文表达的意思是相同的
local function feval() return self.criterion.output, self.gradParams end local output = self.model:forward(self.input) local loss = self.criterion:forward(self.model.output, self.target) self.model:zeroGradParameters() self.criterion:backward(self.model.output, self.target) --这一步计算的结果保存在
self.model:backward(self.input, self.criterion.gradInput) --利用上一步计算出来的损失,计算相对于输入的梯度 optim[self.opt.optMethod](feval, self.params, self.optimState)self.criterion.gradInput中
下面以输入为3*3*256*256输入为例,看看这几个梯度和到底是什么样子
output和loss
print(output)
print(loss)
print(self.model.output)
print(self.criterion.output)
print(self.criterion.gradInput) #gradInput里面计算的是关于output的梯度
print(self.model.gradInput:size()) #model里面的gradInput则是关于图像输入的梯度
几个概念:gradInput,gradOutput,gradParam
https://zhuanlan.zhihu.com/p/21550685,里面有详细介绍,再添加新的层的时候必须要做的几个工作
我们平时可以调用的四个变量,model.output, model.gradInput, criterion.output, criterion.gradInput
那么gradParam是通过getParameters来获得的,他不是状态变量,那他是什么时候更新的呢???
前面提到model.backward的时候会计算两个梯度,关于输入的梯度和关于参数的梯度
这里的更新的gradParameters会保留下供未来使用,用于更新参数
当然如果不用optim,可以直接用updateParameters来更新参数的
function gradUpdate(mlp, x, y, criterion, learningRate)
local pred = mlp:forward(x)
local err = criterion:forward(pred, y)
local gradCriterion = criterion:backward(pred, y)
mlp:zeroGradParameters()
mlp:backward(x, gradCriterion)
mlp:updateParameters(learningRate)
end