关于教程/2_supervised/4_train.lua中的 feval 函数

在Github上: https://github.com/torch/tutorials/blob/master/2_supervised/4_train.lua 我们有一个脚本定义了训练程序,我对该脚本中 feval 函数的构造很感兴趣。

-- 创建一个 closure 来评估 f(X) 和 df/dX
local feval = function(x)
      -- 获取新参数
      if x ~= parameters then
         parameters:copy(x)
      end

      -- 重置梯度
      gradParameters:zero()

      -- f 是所有 criterions 的平均值
      local f = 0

      -- 为整个 mini batch 评估函数
      for i = 1,#inputs do
          -- 评估 f
          local output = model:forward(inputs[i])
          local err = criterion:forward(output, targets[i])
          f = f + err

          -- 评估 df/dW
          local df_do = criterion:backward(output, targets[i])
          model:backward(inputs[i], df_do)

          -- 更新 confusion
          confusion:add(output, targets[i])
      end

      -- 标准化梯度和 f(X)
      gradParameters:div(#inputs)
      f = f/#inputs

      -- 返回 f 和 df/dX
      return f,gradParameters
      end

我尝试通过取消循环来修改此函数: for i = 1,#inputs do ... 因此,我不是针对每个输入单独对其进行前向和反向处理(inputs[i]),而是针对整个 mini batch(inputs)进行处理。这真正加快了处理速度。这是修改后的脚本:

-- 创建一个 closure 来评估 f(X) 和 df/dX
local feval = function(x)
      -- 获取新参数
      if x ~= parameters then
         parameters:copy(x)
      end

      -- 重置梯度
      gradParameters:zero()

      -- f 是所有 criterions 的平均值
      local f = 0
      -- 为整个 mini batch 评估函数

      -- 评估 f
      local output = model:forward(inputs)
      local f = criterion:forward(output, targets)

      -- 评估 df/dW
      local df_do = criterion:backward(output, targets)

      -- 更新权重
      model:backward(inputs, df_do)

      -- 更新 confusion
      confusion:batchAdd(output, targets)

      -- 返回 f 和 df/dX
      return f,gradParameters
      end

但是,当我详细检查给定 mini batch 的feval返回值(f,gradParameters)时,使用循环和不使用循环得到的结果不同。

所以我的问题是: 1 - 为什么要使用循环? 2 - 是否可能在不使用循环的情况下获得相同的结果?

敬礼 Sam

注:我是 Torch7 的初学者

点赞
用户6858358
用户6858358

我相信你一定注意到了要让第二种方法工作起来比仅仅改变feval要多做一些事情。在你的第二个例子中,inputs需要是一个4D的张量,而不是一个3D张量的表格(除非我最近更新时有所改变)。这些张量的大小取决于所使用的损失标准/模型。实现示例的人必须认为循环在这里是更容易的方法。此外,ClassNLLCriterion似乎不喜欢批处理(通常会使用CrossEntropy标准来处理这个问题)。

但是,两种方法应该给出相同的结果。唯一的小差异是,第一个例子使用平均误差/梯度,而第二个使用总和,如下所示:

                   gradParameters:div(inputs:size(1))
                   f = f/inputs:size(1)

在第二种情况下,f和gradParameters应该仅在一个因素opt.batchSize中与第一种情况有所不同。就优化目的而言,这些数学上是等价的。

2016-09-21 15:57:46