昨天我发布了有关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;
}
}
}
}
最佳答案
这是标准的反向传播算法,它在所有隐藏层中反向传播错误。
除非我们在输出层中,否则隐藏层中神经元的错误取决于后续层。假设我们有一个特定的神经元a,其突触将其连接到下一层的神经元i,j和k。我们还假设神经元a的输出是oa。然后,神经元a的误差等于以下表达式(假设我们将逻辑函数用作激活函数):
δa= oa(1-oa)×(δiwai+δjwaj+δkwak)
此处,oa(1-oa)是激活函数的导数的值。 δi是神经元i的误差,wai是分配给从i到a的突触(连接)的权重;其余条款也是如此。
请注意,我们如何考虑a连接到的下一层中每个神经元的错误。还要注意,我们正在考虑每个突触的权重。无需进行数学运算,直观上就可以理解a的错误不仅取决于a连接到的神经元上的错误,而且还取决于a和下一个神经元之间的突触(连接)的权重。层。
出现错误后,我们需要更新连接到的前一层中每个神经元的突触(连接)权重(即,向后传播错误)。让我们假设我们有一个连接到a的单个神经元z。然后我们必须如下调整wza:
wza = wza +(α×δa×盎司)
如果在上一层中还有其他神经元(可能还有)连接到a,我们也将使用相同的公式来更新它们的权重。现在,如果您看一下代码,您将看到这正是正在发生的情况。
您正在对隐藏层中的每个神经元执行以下操作:
您将获得将神经元连接到上一层的突触(连接)列表。这是connections = n.getAllInConnections()
部分。
对于每个连接,代码然后执行以下操作:
它获取神经元的输出(这是上述公式中的oa术语)。
它获取连接到该神经元的神经元的输出(这是盎司术语)。
然后,对于输出层中的每个神经元,它计算每个输出神经元的误差乘以从隐藏层中的神经元到输出层中神经元的权重之和。在这里,sumKoutputs
与我们在表达式(δiwai+δjwaj+δkwak)中所做的相同。 δi的值来自-(desiredOutput - ak) * ak * (1 - ak)
,因为这是您计算输出层误差的方式。您只需将输出层神经元的激活函数的导数乘以实际输出与预期输出之间的差即可。最后,您可以看到我们将整个事情乘以wjk;这与我们公式中的wai术语相同。
现在,我们有了所有需要插入公式的值,以调整从上一层连接到神经元的每个突触的权重。该代码的问题在于它计算某些内容时有些不同:
在我们的公式中,对于神经元a的误差,我们具有oa(1- oa)×(δiwai+δjwaj+δkwak)。但是在代码中,它通过包含partialDerivative
来计算ai
。用我们的术语来说,这相当于oa(1- oa)×盎司×(δiwai+δjwaj+δkwak)。从数学上讲,它是可行的,因为后来我们最终将其乘以学习率(α×δa×oz),因此完全相同。所不同的是,代码较早执行了oz的乘法运算。
然后计算deltaWeight
,在我们的公式中为(α×δa×oz)。在代码中,α是learningRate
。
然后,我们通过将增量添加到当前权重来更新权重。这与wza +(α×δa×oz)相同。
现在情况有所不同。您可以看到该代码没有直接设置权重,而是处理了momentum
。您可以看到,通过使用momentum
,我们将先前增量的一小部分添加到了新的权重中。这是一种用于神经网络的技术,可确保网络不会陷入局部最小值。动量项使我们略微“推”出局部极小值(误差面中的“井”;通过神经网络,我们遍历误差面以找到误差最小的面,但是我们可以得到卡在“井”中,而不是最佳解决方案的“深度”),并确保我们可以“收敛”在解决方案上。但是您必须小心,因为如果将其设置得太高,您可能会超出最佳解决方案。一旦使用动量计算了新的权重,就将其设置为连接(突触)。
我希望这个解释对您来说更清楚。数学有点难以理解,但是一旦弄清楚,它就会有意义。我认为这里的主要问题是代码是以稍微不同的方式编写的。您可以看一下我编写的实现反向传播算法的代码here;我是作为一个班级项目来做的。它的运行方式与我上面描述的公式大致相同,因此您应该可以轻松地遵循它。您也可以看看我在解释反向传播算法时所做的this video。
关于java - (Java)用于隐层反向传播的偏导数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30600284/