神经网络反向传播算法陷入XOR训练模式

时间:2012-02-20 22:11:49

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

概述

所以我试图掌握神经网络的机制。我仍然没有完全掌握它背后的数学,但我想我理解如何实现它。我目前有一个可以学习AND,OR和NOR训练模式的神经网络。但是,我似乎无法实现XOR模式。我的前馈神经网络由 2个输入,3个隐藏和1个输出组成。权重和偏差在 -0.5和0.5 之间随机设置,并使用 sigmoidal激活函数

生成输出

算法

到目前为止,我猜我在训练算法中犯了一个错误,如下所述:

  1. 对于输出图层中的每个神经元,提供error的{​​{1}}值 - 转到第3步
  2. 对于隐藏或输入图层中的每个神经元(向后工作),提供desiredOutput - actualOutput值,该值是所有error的总和 - 转到第3步
  3. 对于每个神经元,使用提供的forward connection weights * the errorGradient of the neuron at the other end of the connection值生成等于error的{​​{1}}。 - 转到第4步
  4. 对于每个神经元,将偏差调整为等于error gradient。然后将每个后向连接的权重调整为等于output * (1-output) * error
  5. 我正在网上训练我的神经网络,所以这在每个训练样本之后运行。

    代码

    这是运行神经网络的主要代码:

    current bias + LEARNING_RATE * errorGradient

    现在current weight + LEARNING_RATE * output of neuron at other end of connection * this neuron's errorGradient功能:

    private void simulate(double maximumError) {
    
        int errorRepeatCount = 0;
        double prevError = 0;
    
        double error; // summed squares of errors
        int trialCount = 0;
    
        do {
    
            error = 0;
    
            // loop through each training set
            for(int index = 0; index < Parameters.INPUT_TRAINING_SET.length; index++) {
    
                double[] currentInput = Parameters.INPUT_TRAINING_SET[index];
                double[] expectedOutput = Parameters.OUTPUT_TRAINING_SET[index];
                double[] output = getOutput(currentInput);
    
                train(expectedOutput);
    
                // Subtracts the expected and actual outputs, gets the average of those outputs, and then squares it.
                error += Math.pow(getAverage(subtractArray(output, expectedOutput)), 2); 
    
    
    
            }
    
        } while(error > maximumError);
    

    输出图层train()功能:

    public void train(double[] expected) {
    
        layers.outputLayer().calculateErrors(expected);
    
        for(int i = Parameters.NUM_HIDDEN_LAYERS; i >= 0; i--) {
            layers.allLayers[i].calculateErrors();
        }
    
    }
    

    普通(隐藏和输入)图层calculateErrors()功能:

    public void calculateErrors(double[] expectedOutput) {
    
        for(int i = 0; i < numNeurons; i++) {
    
            Neuron neuron = neurons[i];
            double error = expectedOutput[i] - neuron.getOutput();
            neuron.train(error);
    
        }
    
    }
    

    Full Neuron课程:

    calculateErrors()

    结果

    当我训练AND,OR或NOR时,网络通常可以在大约1000个时期内收敛,但是当我用XOR训练时,输出变得固定并且它永远不会收敛。那么,我做错了什么?有什么想法吗?

    修改

    根据其他人的建议,我开始实施我的神经网络,没有课程......而且它有效。我仍然不确定我的问题出在上面的代码中,但是它存在于某个地方。

8 个答案:

答案 0 :(得分:7)

这是令人惊讶的,因为您正在使用足够大的网络(几乎没有)来学习XOR。你的算法看起来正确,所以我真的不知道发生了什么。了解如何生成训练数据可能会有所帮助:您是否只是一遍又一遍地重复样本(1,0,1),(1,1,0),(0,1,1),(0,0,0)或类似的事情?也许问题在于随机梯度下降会导致你跳过稳定的最小值。您可以尝试一些方法来解决这个问题:也许从您的训练示例中随机抽样而不是重复它们(如果这就是您正在做的事情)。或者,您也可以修改学习算法:

目前你有相当于的东西:

weight(epoch) = weight(epoch - 1) + deltaWeight(epoch)
deltaWeight(epoch) = mu * errorGradient(epoch)

其中mu是学习率

一种选择是非常慢慢减少mu的值。

另一种方法是将deltaWeight的定义更改为包含“动量”

deltaWeight(epoch) = mu * errorGradient(epoch) + alpha * deltaWeight(epoch -1)

其中alpha是动量参数(介于0和1之间)。

在视觉上,您可以将梯度下降看作是通过在该表面上放置一个物体来尝试找到曲面的最小点,然后逐步移动该物体,其中任何指向都是基于哪里向下倾斜它目前位于。问题在于你并不真正做渐变下降:相反,你会做随机梯度下降,你可以通过从一组训练向量中采样来移动方向,然后移动到样本看起来像是向下的任何方向。平均在整个训练数据中,随机梯度下降应该起作用,但不能保证,因为你可以进入一个你来回跳跃而不能取得进步的情况。慢慢降低学习速度意味着每次采取越来越小的步骤,因此不能陷入无限循环。

另一方面,动量使算法变成类似于滚动橡皮球的东西。作为球的角色,它倾向于沿着向下的方向前进,但它也倾向于继续前进的方向,并且如果它一直处于下坡与下一个方向相同的一段时间它会加速。因此,球会跳过一些局部最小值,并且它会更有弹性抵抗在目标上来回踩踏,因为这样做意味着抵抗动量的力量。


有了一些代码并且更多地考虑了这一点,很明显你的问题在于训练早期层。您已经成功学习的函数都是线性可分的,因此只有单个层才能正确学习才有意义。虽然你的方法应该有效,但我同意LiKao的一般实施策略。我对如何调试这个问题的建议是弄清楚输入层和输出层之间连接的权重进展是什么样的。

您应该发布Neuron的其余实现。

答案 1 :(得分:5)

我很久以前遇到了同样的问题。最后我找到了解决方案,如何使用MLP算法编写解决XOR的代码。

XOR问题似乎是一个容易学习的问题,但它不适用于MLP,因为它不是线性可分的。因此,即使你的MLP没问题(我的意思是你的代码中没有错误),你必须找到能够学习XOR问题的好参数。

两个隐藏和一个输出神经元很好。你要设置的两个主要内容是:

  • 虽然你只有4个训练样本,但你必须进行数千个时期的训练。
  • 如果您使用sigmoid隐藏层但线性输出网络会更快收敛

以下是详细说明和示例代码:http://freeconnection.blogspot.hu/2012/09/solving-xor-with-mlp.html

答案 2 :(得分:3)

小提示 - 如果NN的输出似乎偏向 0.5 ,那么一切都还行!

仅使用学习速率和偏差的算法太简单,无法快速学习XOR。您可以增加纪元数或更改算法。

我的建议是使用动力:

  • 1000 epochs
  • learningRate = 0.3
  • 动量= 0.8
  • 从[0,1]
  • 中抽取的权重
  • 偏向绘制形式[-0.5,0.5]

一些关键的伪代码(假设后向和前向传播有效):

for every edge:
    previous_edge_weight_change = -1 * learningRate * edge_source_neuron_value * edge_target_neuron_delta + previous_edge_weight * momentum

    edge_weight += previous_edge_weight_change

for every neuron:
    previous_neuron_bias_change = -1 * learningRate * neuron_delta + previous_neuron_bias_change * momentum

    bias += previous_neuron_bias_change

答案 3 :(得分:1)

我建议您生成一个网格(例如从[-5,-5]到[5,5],步长为0.5),在XOR上学习你的MLP并将其应用到网格中。用彩色绘图你可以看到某种边界。 如果你在每次迭代时都这样做,你会看到边界的演变并且可以控制学习。

答案 4 :(得分:1)

自从我上次实施神经网络以来已经有一段时间了,但我认为你的错误就在于:

bias += Parameters.LEARNING_RATE * errorGradient;

connection.weight += Parameters.LEARNING_RATE * connection.input.getOutput() * errorGradient;

这些行中的第一行根本不应该存在。偏差最好建模为神经元的输入,固定为1.这将使您的代码更简单,更清晰,因为您不必以任何特殊方式处理偏差。

另一点是,我认为这两个表达中的符号都是错误的。想想这样:

  1. 你的渐变指向最陡峭的方向,所以如果你走向那个方向,你的错误就会变大。

  2. 你在这里做的是在权重中添加一些东西,以防错误已经是正面的,即你正在使它更积极。如果它是负数,那么你会减少某些东西,即你使它更负面。

  3. 除非我遗漏了关于错误定义或渐变计算的内容,否则应将这些行更改为:

    bias -= Parameters.LEARNING_RATE * errorGradient;
    

    connection.weight -= Parameters.LEARNING_RATE * connection.input.getOutput() * errorGradient;
    

    我在早期的一个实现中遇到了类似的错误,它导致了完全相同的行为,即它导致了一个在简单情况下学习的网络,但是一旦训练数据变得更复杂就不再存在。

答案 5 :(得分:1)

LiKao评论简化我的实现并摆脱面向对象的方面解决了我的问题。如上所述,算法中的缺陷是未知的,但是现在我的工作神经网络要小得多。

随时可以继续提供有关我之前实施的问题的见解,因为其他人可能会在将来遇到同样的问题。

答案 6 :(得分:0)

我在神经网络上有点生疏,但我认为用一个感知器实现XOR存在问题:基本上神经元能够通过直线分离两组解,但是一条直线不是足以解决 XOR问题 ......

Here应该是答案!

答案 7 :(得分:0)

我看不出代码有什么问题,但是我的网络遇到了类似的问题而没有为XOR收敛,所以我想发布我的工作配置。

3个输入神经元(其中一个是1.0的固定偏差) 3个隐藏的神经元
1输出神经元

在-0.5和0.5之间随机选择的重量 Sigmoid激活功能。

学习率= 0.2
动量= 0.4
时期= 50,000

融合了10/10次。

我所犯的一个错误是没有将偏置输入连接到输出神经元,这意味着相同的配置它只会收敛10次,而其他8次失败,因为1和1会输出0.5

另一个错误是没有做足够的时代。如果我只做了1000,那么每个测试用例的输出往往大约为0.5。对于每个测试用例,对于epochs> = 8000所以2000次,它开始看起来可能正在工作(但仅在使用动量时)。

当做50000个时期时,是否使用动量并不重要。

我尝试的另一件事是不将sigmoid函数应用于输出神经元输出(我认为这是早先的帖子所建议的),但这破坏了网络,因为输出*(1输出)部分错误方程现在可能是否定意味着权重的更新方式导致错误增加。