【问题标题】:(Java) Partial Derivatives for Back Propagation of Hidden Layer(Java) 隐藏层反向传播的偏导数
【发布时间】:2015-08-16 11:50:56
【问题描述】:

昨天我发布了一个关于first piece of the Back propagation aglorithm 的问题。

今天我正在努力了解隐藏层。

抱歉有很多问题,我已经阅读了几个关于这个主题的网站和论文,但是无论我阅读了多少,我仍然很难将它应用到实际代码中。

这是我正在分析的代码(我正在使用 Java,因此很高兴查看 Java 示例)

// update weights for the hidden layer
    for (Neuron n : hiddenLayer) {
        ArrayList<Connection> connections = n.getAllInConnections();
        for (Connection con : connections) {
            double output = n.getOutput();
            double ai = con.leftNeuron.getOutput();
            double sumKoutputs = 0;
            int j = 0;
            for (Neuron out_neu : outputLayer) {
                double wjk = out_neu.getConnection(n.id).getWeight();
                double desiredOutput = (double) expectedOutput[j];
                double ak = out_neu.getOutput();
                j++;
                sumKoutputs = sumKoutputs
                        + (-(desiredOutput - ak) * ak * (1 - ak) * wjk);
            }

            double partialDerivative = output * (1 - output) * ai * sumKoutputs;
            double deltaWeight = -learningRate * partialDerivative;
            double newWeight = con.getWeight() + deltaWeight;
            con.setDeltaWeight(deltaWeight);
            con.setWeight(newWeight + momentum * con.getPrevDeltaWeight());
        }
    }

这里的一个真正问题是,我不知道所有方法究竟是如何工作的。

这段代码会遍历隐藏层中的所有神经元,并逐个遍历隐藏层中每个神经元的每个连接。它抓取每个连接的输出?那么,这是传入连接的总和(可能通过 Sig 函数运行)然后 * 通过连接权重?那么“double ai”是获取这个特定节点的输入连接值吗?它是只得到一个还是神经元输入的总和?

然后第三个 for 循环几乎总结了一个我不太明白的“out_neu.getConnection(n.id).getWeight()”。那么,期望的输出是最终层节点的期望输出吗?那么ak是每个节点的实际输出(求和和激活函数)还是求和+激活*权重?

编辑

我开始编写自己的代码,有人可以看看吗?

公共类 BackProp {

public  int layers = 3; 
public int hiddenNeuronsNum = 5;
public int outputNeuronsNum = 1;
public static final double eta = .1;
public double[][][] weights; //holds the network -- weights[layer][neuron][forwardConnetion]


  public void Back(){
for(int neuron = 0; neuron < outputNeuronsNum; neuron++){
    for(int connection = 0; connection < hiddenNeuronsNum; connection++){

        double expOutput = expectedOutput[neuron]; //the expected output from the neuron we're on
        double actOutput = actualOutput[neuron];
        double previousLayerOutput = holdNeuronValues[layers-1][neuron];

        double delta = eta *(actOutput * (1-actOutput) *(expOutput - actOutput)* previousLayerOutput);
        weights[layers-1][neuron][connection] += delta; //OKAY M&M said YOU HAD THIS MESSED UP, 3rd index means end neuron, 2nd means start.. moving from left to right
    }
}


 //Hidden Layer..   

for(int neuron = 0; neuron < outputNeuronsNum; neuron++){
    for(int connection = 0; connection < hiddenNeuronsNum; connection++){

        double input = holdNeuronValues[layers-3][connection]; //what this neuron sends on, -2 for the next layer
        double output = holdNeuronValues[layers-2][connection];
        double sumKoutputs = 0;

        //for the output layer
        for (int outputNeurons = 0; outputNeurons < weights[layers].length; outputNeurons++) {      
            double wjk = weights[layers-2][neuron][outputNeurons]; //get the weight
            double expOutput = expectedOutput[outputNeurons];
            double out = actualOutput[outputNeurons];
            sumKoutputs += (-(expOutput - out) * wjk);
        }
        double partialDerivative = -eta * output * (1 - output) * input * sumKoutputs;

    }
}

} }

【问题讨论】:

    标签: java machine-learning artificial-intelligence neural-network


    【解决方案1】:

    这是标准的反向传播算法,它通过所有隐藏层反向传播误差。

    除非我们在输出层,否则隐藏层中神经元的误差取决于后续层。假设我们有一个特定的神经元 a,其突触将其连接到神经元 ijk在下一层。我们还假设神经元a的输出是oa。那么神经元a的误差等于下面的表达式(假设我们使用逻辑函数作为激活函数):

    δa = oa(1 - oa) × (δiwai + δjw aj + δkwak)

    这里,oa(1 - oa)是激活的导数的值功能。 δi是神经元i的误差,wai是分配的权重从ia的突触(连接);其余条款也是如此。

    请注意我们如何考虑 a 连接到的 next 层中的 每个 神经元的误差。另请注意,我们正在考虑赋予每个突触的权重。在不深入数学的情况下,直观地说,a 的错误不仅取决于 a 连接到的神经元上的错误,而且还取决于a 和下一层神经元之间的突触(连接)的权重。

    一旦出现错误,我们需要更新上一个连接到a的每个神经元的突触(连接)的权重(即,我们反向传播错误)。让我们假设我们有一个连接到 a 的单个神经元 z。然后我们要调整wza如下:

    wza = wza + (α × δa sub> × oz)

    如果前一层中有其他神经元(并且可能有)连接到a,我们也将使用相同的公式更新它们的权重。现在,如果您查看您的代码,您会发现这正是正在发生的事情。

    您正在对隐藏层中的每个神经元执行以下操作:

    • 您将获得将这个神经元连接到上一个层的突触(连接)列表。这是connections = n.getAllInConnections() 部分。
    • 对于每个连接,代码执行以下操作:
      • 它获取神经元的输出(这是上述公式中的 oa 项)。
      • 它获取连接到 this 神经元的神经元的输出(这是 oz 项)。
      • 然后对于输出层中的每个神经元,它计算每个输出神经元的误差之和乘以从隐藏层中的神经元到输出层中的神经元的权重。这里,sumKoutputs 与我们在表达式中所做的相同 (δiwai + δ jwaj + δkwak)。 δi 的值来自-(desiredOutput - ak) * ak * (1 - ak),因为这是计算输出层误差的方式;您可以简单地将输出层神经元的激活函数的导数乘以实际输出和预期输出之间的差异。最后,您可以看到我们将整个结果乘以 wjk;这与我们公式中的 wai 项相同。
      • 我们现在拥有了所有需要插入公式的值,以调整从前一层连接到我们的神经元的每个突触的权重。代码的问题在于它计算的一些东西有点不同:
        • 在我们的公式中,我们有 oa(1 - oa) × (δ iwai + δjwaj + δkwak) 表示神经元 a 的误差。但在代码中,它通过包含ai 来计算partialDerivative。用我们的话来说,这相当于 oa(1 - oa) × o z × (δiwai + δj sub>waj + δkwak)。数学上它是可行的,因为后来我们最终还是将它乘以学习率(α × δa × oz em>),所以完全一样;不同之处只是代码更早地执行了与 oz 的乘法。
        • 然后计算deltaWeight,即(α × δa × oz)我们的公式。在代码中,α 为learningRate
        • 然后我们通过将增量添加到当前权重来更新权重。这与 wza + (α × δa × oz )。
      • 现在情况有些不同了。可以看到代码并没有直接设置权重,而是处理momentum。您可以看到,通过使用momentum,我们将先前增量的一小部分添加到新权重中。这是神经网络中使用的一种技术,可确保网络不会陷入局部最小值。动量项给了我们一点“推动”以摆脱局部最小值(误差面中的“井”;使用神经网络,我们遍历误差面以找到误差最小的面,但我们可以得到卡在一个不如最佳解决方案“深”的“井”中),并确保我们可以“收敛”到一个解决方案上。但是你必须小心,因为如果你把这个设置得太高,你可能会超过你的最佳解决方案。一旦它使用动量计算出新的权重,它就会为连接(突触)设置它。

    我希望这个解释能让你更清楚。数学有点难学,但是一旦你弄清楚了,它就有意义了。我认为这里的主要问题是代码的编写方式略有不同。你可以看看我写的一些代码here,它实现了反向传播算法;我把它作为一个班级项目来做。它的运行方式与我上面描述的公式几乎相同,因此您应该能够轻松地完成它。你也可以看看this video我在解释反向传播算法的地方。

    【讨论】:

    • 哇,这太棒了,非常感谢,我正在重新阅读这篇文章,我正在编写自己的代码!希望我能让我自己的代码工作哈哈
    • 我无法理解这是一个多么棒的答案,再次感谢!
    • @NateCook3 很高兴我能提供帮助。 :)
    • 我开始编写自己的代码,你能快速看一下吗?我正在使用数组而不是对象。 @vivin-paliath
    • 我知道代码并不完美/完全正确,但我一直在尝试不参考其他人的代码。
    猜你喜欢
    • 2015-03-05
    • 2019-07-04
    • 2015-11-27
    • 2019-01-31
    • 2017-01-12
    • 2016-05-09
    • 2017-06-21
    • 1970-01-01
    • 2012-04-18
    相关资源
    最近更新 更多