2012-08-16 101 views
3

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

// 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值)。我检查了代码。唉,我没能解决我的问题。 我会非常感谢任何想尝试帮助我的人。

+0

你是否在其中规定了'精确'问题?我的意思是,你确定这不仅仅是一个浮点问题吗? 除此之外,我猜你的背篮或隐藏的体重不能正确计算。除非你在较小的样本上测试这个NN并证明它工作正常。 – Shark 2012-08-16 16:33:58

+0

我认为这不是浮点问题。我尝试了一个例子,运行9000次的算法。输出仍然是一个数组NaN值。在第5次迭代之后,值变得无限。我无法理解为什么会发生这种情况。 – 2012-08-16 16:58:09

+0

它是否正确地学习XOR问题?调试这样的东西非常简单。 – 2012-08-16 17:51:55

回答

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

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

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

在情况下(2)您应该先尝试解决一个更简单的问题。以一个非常简单的仿真数据集,然后采取standard benchmark,然后与您的数据一起尝试。验证反向传播实现的有效方法是与numerical differentation method进行比较。

0

我还没有测试过你的代码,但我几乎可以肯定,你开始与大权重。 这些主题的大部分介绍都将它放在“初始权值随机值”,而忽略了算法实际上对某些初始值发散(转到Inf)。

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

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

0

我有一个神经网络处理灰度图像类似的问题。您有960个输入值,范围介于0和255之间。即使在初始权重较小的情况下,您也可能输入大量的神经元,反向传播算法陷入困境。

尝试将每个像素值除以255,然后将其传递到神经网络中。这对我来说很有用。刚开始使用非常小的初始权重还不够,我相信由于评论中提出了浮点精度问题。

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

而对于它的价值,隐层3个神经元是很多我的目的(识别人脸图像的性别)

0

我写了一个完整的新的神经网络库,它的工作原理。可以肯定的是,在我以前的尝试中,我错过了使用传递函数及其派生物的想法。谢谢你们!