DL4J源码阅读(五):误差反传和网络测试

    backprop()得到全网梯度设置gradientepsilon。流程回到computeGradientAndScore()方法中。下面是计算得分:score = ((IOutputLayer) getOutputLayer()).computeScore(calcL1(true), calcL2(true), true)BaseOutputLayer中的computeScore()方法调用了preOutput2d()。这个方法在计算梯度时调用过了。如果当时保存了这个结果,就不用再计算了。调用损失函数computeScore()计算分数。算出来的分数除以批次50就是真正的分数。

    computeGradientAndScore()方法结束,流程回到BaseOptimizergradientAndScore()方法中。这里有个地方可以优化一下,不需要下面条语句Pair<Gradient, Double> pair = model.gradientAndScore(),在model.computeGradientAndScore()语句就返回Pair<Gradient, Double> pair更好些。下面的语句updateGradientAccordingToParams(pair.getFirst(), model, model.batchSize())是对梯度进行整理。

    BaseMultiLayerUpdater中的public void update(Gradient gradient, int iteration, int batchSize)方法首先将梯度按层、Wb的结构保存到layerGradients中。如果一开始就用这种结构更好些。结构层次清晰,而且也不用再整理。调用ub.update(iteration)更新梯度。本例中使用的是NESTEROVS,会调用NesterovsUpdater中的applyUpdater方法更新梯度。得到的梯度再除以批次50getFlattenedGradientsView().divi(batchSize)。由于梯度都很小,除以50后,非常小,几乎等于0了,但其实不是0

    流程一路返回到StochasticGradientDescent.optimize()方法中,Pair<Gradient, Double> pair保存的是全网梯度和得分。stepFunction.step(params, gradient.gradient())语句更新全网参数。更新时比较简单,全网参数行向量减去全网梯度行向量。这就是扁平化会提高效率的所在。model.setParams(params)语句将更新过的参数设置到网络中。然后评估是否可以终止和迭代数加1。流程回到MultiLayerNetwork.fit()方法。因为在循环中,会再获取下50行数据,继续训练网络。

 

网络测试

    MLPClassifierLinear中,从Evaluation eval = new Evaluation(numOutputs)语句开始往后,都是网络测试部分。加载测试数据时,没有使用异步加载。INDArray predicted = model.output(features, false)语句是执行测试数据,并返回测试结果。MultiLayerNetworkoutput()方法中调用INDArray ret = silentOutput(input, train).detach()语句进行测试,流程会调到feedForwardToLayer(layers.length - 1, train)方法。这里可以看到和训练时调用feedForwardToLayer(layers.length - 2, true)语句时,方法的第一个参数是不同的。训练时没有包括输出层,测试时包括了。当然第二个参数也不同。


梯度、误差计算过程

输出层激活函数Softmax,损失函数是NegativeLogLikelihoodMCXENT)梯度、误差计算过程:

1、误差矩阵=激活函数Softmax的输出矩阵减去标签矩阵:delta = output.subi(labels)

2、梯度矩阵=本层的输入矩阵转置乘以误差矩阵:Nd4j.gemm(input, delta, weightGradView, true, false, 1.0, 0.0);

3、上层误差矩阵=本层权重矩阵乘以delta矩阵的转置的结果再转置:INDArray epsilonNext = params.get(DefaultParamInitializer.WEIGHT_KEY).mmul(delta.transpose()).transpose();

 

隐藏层激活函数ReLU梯度、误差计算过程:

1、误差矩阵=激活函数ReLU的输出矩阵求导,然后乘以误差矩阵:

        INDArray delta = Nd4j.getExecutioner().execAndReturn(new RectifedLinear(in).derivative());

        delta.muli(epsilon);

2、梯度矩阵=本层的输入矩阵转置乘以误差矩阵:Nd4j.gemm(input, delta, weightGrad, true, false, 1.0, 0.0);

3、上层误差矩阵=本层权重矩阵乘以delta矩阵的转置的结果再转置:INDArray epsilonNext = params.get(DefaultParamInitializer.WEIGHT_KEY).mmul(delta.transpose()).transpose();

 

输出层和隐藏层计算过程,误差矩阵的计算方式不同,其它两步类似。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值