【问题标题】:Neural Network Backpropagation does not compute weights correctly神经网络反向传播无法正确计算权重
【发布时间】:2012-08-13 01:37:08
【问题描述】:

目前,我遇到了反向传播算法的问题。 我正在尝试实现它并用它来识别人脸的方向(左、右、下、直)。 基本上,我有 N 个图像,读取像素并将其值(0 到 255)更改为从 0.0 到 1.0 的值。所有图像均为 32*30。 我有一个 960 个神经元的输入层、一个 3 个神经元的隐藏层和一个 4 个神经元的输出层。例如,输出 表示该人向右看。 我遵循了伪代码。但是,它不能正常工作 - 它没有计算正确的权重,因此它无法处理训练和测试示例。 以下是部分代码:

    // main function - it runs the algorithm
     private void runBackpropagationAlgorithm() {
        for (int i = 0; i < 900; ++i) {
            for (ImageUnit iu : images) {
                double [] error = calcOutputError(iu.getRatioMatrix(), iu.getClassification());
                changeHiddenUnitsOutWeights(error);
                error = calcHiddenError(error);
                changeHiddenUnitsInWeights(error,iu.getRatioMatrix());
            }
        }
    }

  // it creates the neural network
    private void createNeuroneNetwork() {
            Random generator = new Random();
            for (int i = 0; i < inHiddenUnitsWeights.length; ++i) {
                for (int j = 0; j < hiddenUnits; ++j) {
                    inHiddenUnitsWeights[i][j] = generator.nextDouble();
                }
            }
            for (int i = 0; i < hiddenUnits; ++i) {
                for (int j = 0; j < 4; ++j) {
                    outHddenUnitsWeights[i][j] = generator.nextDouble();
                }
            }
        }
   // Calculates the error in the network. It runs through the whole network.
private double [] calcOutputError(double[][] input, double [] expectedOutput) {
        int currentEdge = 0;
        Arrays.fill(hiddenUnitNodeValue, 0.0);
        for (int i = 0; i < input.length; ++i) {
            for (int j = 0; j < input[0].length; ++j) {
                for (int k = 0; k < hiddenUnits; ++k) {
                    hiddenUnitNodeValue[k] += input[i][j] * inHiddenUnitsWeights[currentEdge][k];
                }
                ++currentEdge;
            }
        }
        double[] out = new double[4];
        for (int j = 0; j < 4; ++j) {
            for (int i = 0; i < hiddenUnits; ++i) {
                out[j] += outHddenUnitsWeights[i][j] * hiddenUnitNodeValue[i];
            }
        }
        double [] error = new double [4];
        Arrays.fill(error, 4);
        for (int i = 0; i < 4; ++i) {
            error[i] = ((expectedOutput[i] - out[i])*(1.0-out[i])*out[i]);
            //System.out.println((expectedOutput[i] - out[i]) + " " + expectedOutput[i] + " " +  out[i]);
        }
        return error;
    }

// Changes the weights of the outgoing edges of the hidden neurons
private void changeHiddenUnitsOutWeights(double [] error) {
        for (int i = 0; i < hiddenUnits; ++i) {
            for (int j = 0; j < 4; ++j) {
                outHddenUnitsWeights[i][j] += learningRate*error[j]*hiddenUnitNodeValue[i];
            }
        }
    }

// goes back to the hidden units to calculate their error.
private double [] calcHiddenError(double [] outputError) {
        double [] error = new double[hiddenUnits];
        for (int i = 0; i < hiddenUnits; ++i) {
            double currentHiddenUnitErrorSum = 0.0;
            for (int j = 0; j < 4; ++j) {
                currentHiddenUnitErrorSum += outputError[j]*outHddenUnitsWeights[i][j];
            }
            error[i] = hiddenUnitNodeValue[i] * (1.0 - hiddenUnitNodeValue[i]) * currentHiddenUnitErrorSum;
        }
        return error;
    }

// changes the weights of the incomming edges to the hidden neurons. input is the matrix of ratios
private void changeHiddenUnitsInWeights(double [] error, double[][] input) {
        int currentEdge = 0;
        for (int i = 0; i < input.length; ++i) {
            for (int j = 0; j < input[0].length; ++j) {
                for (int k = 0; k < hiddenUnits; ++k) {
                    inHiddenUnitsWeights[currentEdge][k] += learningRate*error[k]*input[i][j];
                }
                ++currentEdge;
            }
        }
    }

随着算法的工作,它计算越来越大的权重,最终接近无穷大(NaN 值)。我检查了代码。唉,我没有设法解决我的问题。 我会非常感谢任何愿意帮助我的人。

【问题讨论】:

  • 您排除了“精度”问题吗?我的意思是,你确定这不仅仅是一个浮点问题吗?除此之外,我猜你的反向传播或隐藏的超重计算不正确。除非您在较小的样本上测试此 NN 并证明它有效。
  • 我认为这不是浮点问题。我尝试了一个示例并运行了该算法 9000 次。输出仍然是一个数组 NaN 值。就在第 5 次迭代之后,值变得无限。我不明白为什么会这样。
  • 它是否正确学习了 XOR 问题?调试这样的东西非常整洁。
  • 我试过了,但权重仍然上升到无穷大......我明天去尝试调试它
  • 你应该减去梯度以达到最小值。

标签: java machine-learning neural-network backpropagation


【解决方案1】:

我编写了一个全新的神经网络库,它可以工作。可以肯定的是,在我之前的尝试中,我错过了使用传递函数及其导数的想法。谢谢大家!

【讨论】:

    【解决方案2】:

    您的代码缺少传递函数。听起来您想要具有 softmax 输出的逻辑函数。您需要在 calcOutputError 中包含以下内容

    // Logistic transfer function for hidden layer. 
    for (int k = 0; k < hiddenUnits; ++k) {
        hiddenUnitNodeValue[k] = logistic(hiddenUnitNodeValue[k]);
    }
    

    // Softmax transfer function for output layer.
    sum = 0;
    for (int j = 0; j < 4; ++j) {
        out[j] = logistic(out[j]);
        sum += out[j];
    }
    for (int j = 0; j < 4; ++j) {
        out[j] = out[j] / sum;
    }
    

    逻辑函数在哪里

    public double logistic(double x){
        return (1/(1+(Math.exp(-x)));
    }
    

    请注意,softmax 传递函数为您提供总和为 1 的输出,因此可以将它们解释为概率。

    另外,您对输出层的误差梯度的计算不正确。它应该只是

    for (int i = 0; i < 4; ++i) {
        error[i] = (expectedOutput[i] - out[i]);
    } 
    

    【讨论】:

      【解决方案3】:

      我在处理灰度图像的神经网络中遇到了类似的问题。您有 960 个输入值,范围在 0 到 255 之间。即使初始权重很小,您的神经元最终也会得到非常大的输入,并且反向传播算法会卡住。

      尝试将每个像素值除以 255,然后再将其传递到神经网络。这对我有用。仅仅从极小的初始权重开始是不够的,我相信这是由于 cmets 中提出的浮点精度问题。

      正如另一个答案中所建议的,测试算法的一个好方法是查看您的网络是否可以学习 XOR 等简单函数。

      不管怎样,隐藏层中的 3 个神经元对于我的目的来说已经足够了(识别面部图像的性别)

      【讨论】:

        【解决方案4】:

        我还没有测试过你的代码,但我几乎可以肯定你一开始就使用了大权重。 大多数关于该主题的介绍都将其保留在“使用随机值初始化权重”,而忽略了算法对于某些起始值实际上是发散的(转到 Inf)。

        尝试使用较小的起始值,例如在 -1/5 和 1/5 之间并将其缩小。

        另外做了一个矩阵乘法的方法,你(只)用了4次,更容易看出那里是否有问题。

        【讨论】:

          【解决方案5】:

          我没有检查您的所有代码。我只是想给你一些一般性的建议。我不知道您的目标是(1)学习人脸的方向还是(2)实现自己的神经网络。

          在情况 (1) 中,您应该考虑使用 those 库之一。它们只是工作并为您提供更灵活的配置选项。例如,标准反向传播是神经网络最差的优化算法之一。收敛取决于学习率。我看不出您在实现中选择了哪个值,但它可能太高了。还有其他优化算法不需要学习率或在训练期间对其进行调整。此外,隐藏层中的 3 个神经元很可能是不够的。大多数用于图像的神经网络都有数百个甚至数千个隐藏单元。我建议您首先尝试使用完全开发的库来解决您的问题。如果它确实有效,请尝试实施您自己的 ANN 或开心。 :)

          在情况 (2) 中,您应该首先尝试解决一个更简单的问题。取一个非常简单的人工数据集,然后取一个standard benchmark,然后用你的数据进行尝试。验证反向传播实现是否有效的一个好方法是与numerical differentation method 进行比较。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-01-22
            • 2017-09-28
            • 2019-11-06
            • 2013-12-17
            • 2015-03-03
            • 2012-02-21
            • 2011-01-05
            • 1970-01-01
            相关资源
            最近更新 更多