【源码】MatConvNets - eval.m Loss.m

本文深入解析MatConvNets库中的eval.m和Loss.m函数。eval.m用于执行前向传播和反向传播,通过DagNN对象进行有向无环图的计算,优化内存管理。Loss.m涉及损失计算,设定反向传播的终点,权重调整以及内存清理。两函数协同实现网络训练的高效与灵活。
摘要由CSDN通过智能技术生成

【源码】MatConvNets - eval.m Loss.m

eval.m

函数定义:
function eval(obj, inputs, derOutputs, stopLayer)

用法一:执行前向传播
eval(obj, inputs),使用指定的inputs执行DagNN,在整个网络上执行前向传播,计算所有变量的值,计算结果可以通过obj.vars(outputIndex)获得。

用法二:执行前向传播+反向传播
eval(obj, inputs, derOutputs),从inputs开始执行前向传播,完成后再从derOutputs执行反向传播。

obj是DagNN对象
inputs是一个元胞数组:{'inputName', inputValue, ...}
derOutputs是一个元胞数组:{'outputName', outputDerValue, ...}

准备工作

参数检查

确保inputs和derOutputs一定是cell数组类型

obj.computingDerivative = nargin > 2 && ~isempty(derOutputs) ;

if ~iscell(inputs), error('INPUTS is not a cell array.') ; end
if obj.computingDerivative && ~iscell(derOutputs), error('DEROUTPUTS is not a cell array.') ; end

if nargin < 4
  stopLayer = [];
end

前向传播

输入赋值

将输入inputs的值赋给对应的变量。

for i = 1:2:numel(inputs)
  v = obj.getVarIndex(inputs{i}) ;
  if ~isnan(v)
      switch obj.device
        case 'cpu', obj.vars(v).value = gather(inputs{i+1}) ;
        case 'gpu', obj.vars(v).value = gpuArray(inputs{i+1}) ;
      end
  end
end

前向传播

整个卷积神经网络可以用一个有向无环图(DagNN)表示,每个节点代表一层,每一层的inputs代表入边,outputs代表出边。obj.executionOrder则返回该有向无环图的拓扑序,按照这个顺序执行前向传播,可以确保在计算每一层的时候,输入都已经计算过。

forwardAdvanced执行该层的具体运算。

forwardTime记录该层执行时间。

前向传播到stopLayer则退出执行,不再继续传播。

inputs = [] ;
obj.numPendingVarRefs = [obj.vars.fanout] ;
for l = obj.executionOrder
  time = tic ;
  obj.layers(l).block.forwardAdvanced(obj.layers(l)) ;
  obj.layers(l).forwardTime = toc(time) ;
  
  if stopLayer == l
    break;
  end
end

类比Java的垃圾回收机制,Java内部维护着每个对象的引用次数,当对象的引用次数为0时,对象占用的内存就可以回收了。matconvnet使用了类似的机制来节约内存,当变量的引用次数为0时,将变量占用的内存回收。

numPendingVarRefs 记录每个变量的引用数,每一层执行完之后,对numPendingVarRefs维护,将该层引用的变量的计数器减一。当引用次数为0时,就可以将改变了占用的内存清空了。

参考Layer.m代码:

function forwardAdvanced(obj, layer)
  in = layer.inputIndexes ;
  out = layer.outputIndexes ;
  par = layer.paramIndexes ;
  net = obj.net ;

  inputs = {net.vars(in).value} ;

  % clear inputs if not needed anymore
  for v = in
	net.numPendingVarRefs(v) = net.numPendingVarRefs(v) - 1 ;
	if net.numPendingVarRefs(v) == 0
	  if ~net.vars(v).precious & ~net.computingDerivative & net.conserveMemory
		net.vars(v).value = [] ;
	  end
	end
  end     
  ...
end

反向传播

清空变量,节约内存

backpropDepth设置反向传播的终止层,当反向传播执行到这一层时就不再继续。

如果设置了backpropDepth,那该层前面的层在反向传播的时候就用不到。为了节约内存,把前面的层的变量都先置空。

if obj.backpropDepth
for l = obj.executionOrder
  [obj.vars(obj.layers(l).inputIndexes).value] = deal([]) ;
  if strcmp(obj.layers(l).name, obj.backpropDepth)    
    break;
  end
end
end

设置derOutputs权重

derOutputs代表反向传播的起点,并且可以有多个起点(多个优化目标),每个优化目标有一个权重。例如:
derOutputs = {'loss_spacial', 1, 'loss_temporal', 3} 有两个优化目标,并且权重比为1:3。derOutputs(1:2:end)代表变量名,derOutputs{2:2:end}代表对应权重。

v = obj.getVarIndex(derOutputs(1:2:end)) ;
[obj.vars(v).der] = deal(derOutputs{2:2:end}) ;
derOutputs = [] ;

反向传播

fliplr(obj.executionOrder)代表按前向传播相反的顺序执行反向传播。

backwardAdvanced 执行该层具体的反向传播。

backwardTime 记录反向传播时间。

执行到backprobDepth之后就不再继续执行反向传播。

obj.numPendingVarRefs = zeros(1, numel(obj.vars)) ;
obj.numPendingParamRefs = zeros(1, numel(obj.params)) ;
for l = fliplr(obj.executionOrder)
  time = tic ;
  if strcmp(obj.layers(l).name, obj.backpropDepth)    
    [obj.vars.value] = deal([]) ;
    [obj.vars.der] = deal([]) ;
    break;
  end
  obj.layers(l).block.backwardAdvanced(obj.layers(l)) ;
  obj.layers(l).backwardTime = toc(time) ;
end

总结

  1. 使用obj.executionOrder获得DagNN的拓扑序,按序执行前向传播和反向传播。
  2. stopLayerbackprobDepth决定了前向传播和反向传播的终点,可以更加细微地控制训练过程,在预训练的模型上进行二次训练时可能会用到,可以将参数改变限定在少数层上。
  3. forwardAdvancedbackwardAdvanced具体地实现了每一层的前向传播和反向传播的过程,这要看其他部分的源码。
  4. 借助numPendingVarRefs维护变量的引用数,将无引用的变量清空,尽可能地节约内存。

Loss.m

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值