我自己的神经网络反向传播解决XOR无法正确收敛

时间:2018-04-05 15:45:00

标签: javascript machine-learning neural-network backpropagation gradient-descent

出于学习目的,我在JavaScript中从头开始实现自己的神经网络,作为第一个任务,我想解决XOR问题。 我已经可以解决OR和AND了,但是只要我需要隐藏图层,我的权重就不会正常收敛。

我使用3层网络,2个输入神经元+1偏置神经元,1个隐藏层,2个神经元+1个偏置神经元和1个输出神经元。

这种网络架构绝对应该能够解决任务。当我手动设置权重时

let W1 = new Matrix([ // weights for mapping between layer 1 and layer 2
    [-10, 20, 20], // OR
    [30, -20, -20] // NAND
]);
let W2 = new Matrix([ // weights for mapping between layer 2 and layer 3
    [-30, 20, 20] // AND
]); 

我得到了正确的输出(非常接近[0, 1, 1, 0])。

但是当我尝试学习XOR问题的权重时,我的输出总是接近[0.5, 0.5, 0.5, 0.5]而不是[0, 1, 1, 0]。我尝试了各种不同的权重初始化,学习率和梯度下降迭代次数,没有改进。

所以我很确定我的反向传播算法(W1grad的计算)有一个错误,但我无法找出错误的原因...... 任何帮助将不胜感激!

// X inputs, W1, W2 = weights, y = outputs, alpha = learning rate
function gradientDescent(X, W1, W2, y, alpha, n_iterations) {
    for (let i = 0; i < n_iterations; i++) {
        // forward propagate
        let a1 = addBias(X); // addBias just adds a column of 1's at the front of the matrix
        let z2 = a1.times(W1.t()); // t() = transpose
        let a2 = addBias(z2.map(sigmoid));
        let z3 = a2.times(W2.t());
        let a3 = z3.map(sigmoid);

        // calculate error
        let error = logCost(a3, y);

        // back propagate
        let outputDelta = a3.minus(y);
        let hiddenDelta = outputDelta.times(W2).etimes(addBias(z2.map(sigmoidGradient))); // etimes is element-wise multiplication
        let W2grad = outputDelta.t().times(a2).timess(1 / X.h); // timess (with 2 s) is scalar multiplication. this gradient seems to be right!
        let W1grad = hiddenDelta.cols(1, hiddenDelta.w - 1).t().times(a1).timess(1 / X.h); // TODO this seems to be wrong...

        // update weights
        W1 = W1.minus(W1grad.timess(alpha));
        W2 = W2.minus(W2grad.timess(alpha));
    }
    return [W1, W2];
}

可在此处找到完整代码(底部的相关部分,控制台中的输出):https://codepen.io/anon/pen/oqagqd

1 个答案:

答案 0 :(得分:1)

事实证明,这毕竟是重量初始化!

出于某种原因,我的算法似乎对权重的初始化非常敏感......

使用-2.5和+2.5之间以及5000+梯度下降迭代范围内的随机值,大多数可以为XOR问题提供正确的解决方案。许多其他范围根本不起作用......

使用

W1 = rand(2, 3).map(x => (x-.5)*5); // values between -2.5 and +2.5
W2 = rand(1, 3).map(x => (x-.5)*5);

返回输出

0.0676236578905123
0.9425132775668613
0.9095288663122072
0.05522288831217417

这是一个令人满意的XOR问题近似值(基础事实= [0, 1, 1, 0])。

而BTW:通过添加更多隐藏的神经元,可以更容易地获得良好的结果。