我编写了一个神经网络程序。它适用于逻辑门,但当我尝试用它来识别手写数字时,它根本无法学习。
请查看下面的代码:
// 这是一个单神经元;理解剩余代码可能需要这个
typedef struct SingleNeuron{ double outputValue; std::vector<double> weight; std::vector<double> deltaWeight; double gradient; double sum;}SingleNeuron;
然后我初始化网络。我将权重设置为-0.5到+0.5之间的随机值,将sum设置为0,deltaWeight设置为0
接下来是前馈过程:
for (unsigned i = 0; i < inputValues.size(); ++i){ neuralNet[0][i].outputValue = inputValues[i]; neuralNet[0][i].sum = 0.0; // std::cout << "o/p Val = " << neuralNet[0][i].outputValue << std::endl;}for (unsigned i = 1; i < neuralNet.size(); ++i){ std::vector<SingleNeuron> prevLayerNeurons = neuralNet[i - 1]; unsigned j = 0; double thisNeuronOPVal = 0; // std::cout << std::endl; for (j = 0; j < neuralNet[i].size() - 1; ++j) { double sum = 0; for (unsigned k = 0; k < prevLayerNeurons.size(); ++k) { sum += prevLayerNeurons[k].outputValue * prevLayerNeurons[k].weight[j]; } neuralNet[i][j].sum = sum; neuralNet[i][j].outputValue = TransferFunction(sum); // std::cout << neuralNet[i][j].outputValue << "\t"; } // std::cout << std::endl;}
我的传递函数及其导数在最后提到。
之后我尝试使用以下方法进行反向传播:
// 计算输出层的梯度for (unsigned i = 0; i < outputLayer.size() - 1; ++i){ double delta = actualOutput[i] - outputLayer[i].outputValue; outputLayer[i].gradient = delta * TransferFunctionDerivative(outputLayer[i].sum);}// std::cout << "Found Output gradients "<< std::endl;// 计算隐藏层的梯度for (unsigned i = neuralNet.size() - 2; i > 0; --i){ std::vector<SingleNeuron>& hiddenLayer = neuralNet[i]; std::vector<SingleNeuron>& nextLayer = neuralNet[i + 1]; for (unsigned j = 0; j < hiddenLayer.size(); ++j) { double dow = 0.0; for (unsigned k = 0; k < nextLayer.size() - 1; ++k) { dow += nextLayer[k].gradient * hiddenLayer[j].weight[k]; } hiddenLayer[j].gradient = dow * TransferFunctionDerivative(hiddenLayer[j].sum); }}// std::cout << "Found hidden layer gradients "<< std::endl;// 从输出层到第一隐藏层,更新所有权重for (unsigned i = neuralNet.size() - 1; i > 0; --i){ std::vector <SingleNeuron>& currentLayer = neuralNet[i]; std::vector <SingleNeuron>& prevLayer = neuralNet[i - 1]; for (unsigned j = 0; j < currentLayer.size() - 1; ++j) { for (unsigned k = 0; k < prevLayer.size(); ++k) { SingleNeuron& thisNeueon = prevLayer[k]; double oldDeltaWeight = thisNeueon.deltaWeight[j]; double newDeltaWeight = ETA * thisNeueon.outputValue * currentLayer[j].gradient + (ALPHA * oldDeltaWeight); thisNeueon.deltaWeight[j] = newDeltaWeight; thisNeueon.weight[j] += newDeltaWeight; } }}
这些是传递函数及其导数:
double TransferFunction(double x){ double val; //val = tanh(x); val = 1 / (1 + exp(x * -1)); return val;}double TransferFunctionDerivative(double x){ //return 1 - x * x; double val = exp(x * -1) / pow((exp(x * -1) + 1), 2); return val;}
我观察到一件事情,如果我使用标准的Sigmoid函数作为我的传递函数,并且将神经元的输出传递给传递函数,结果是无穷大。但是tanh(x)在这个值上工作得很好
所以如果我使用1/1+e^(-x)作为传递函数,我必须传递网络输入的总和
,而如果tanh
是我的传递函数,我必须传递当前神经元的输出
。
我不完全理解为什么会这样,这可能需要一个不同的问题来回答。
但这个问题实际上是关于其他事情的:网络对逻辑门有效,但对字符识别无效
我已经尝试了许多学习率
和加速度
以及隐藏层数
和它们的尺寸
的组合变化。请查看下面的结果:
AvgErr: 0.299399 #Pass799AvgErr : 0.305071 #Pass809AvgErr : 0.303046 #Pass819AvgErr : 0.299569 #Pass829AvgErr : 0.30413 #Pass839AvgErr : 0.304165 #Pass849AvgErr : 0.300529 #Pass859AvgErr : 0.302973 #Pass869AvgErr : 0.299238 #Pass879AvgErr : 0.304708 #Pass889AvgErr : 0.30068 #Pass899AvgErr : 0.302582 #Pass909AvgErr : 0.301767 #Pass919AvgErr : 0.303167 #Pass929AvgErr : 0.299551 #Pass939AvgErr : 0.301295 #Pass949AvgErr : 0.300651 #Pass959AvgErr : 0.297867 #Pass969AvgErr : 0.304221 #Pass979AvgErr : 0.303702 #Pass989
看了这些结果后,您可能会觉得这个家伙只是陷入了局部最小值,但请稍等并继续阅读:
Input = [0, 0, 0, 0, 0, 0, 1, 0, 0, 0] Output = 0.0910903, 0.105674, 0.064575, 0.0864824, 0.128682, 0.0878434, 0.0946296, 0.154405, 0.0678767, 0.0666924Input = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]Output = 0.0916106, 0.105958, 0.0655508, 0.086579, 0.126461, 0.0884082, 0.110953, 0.163343, 0.0689315, 0.0675822Input = [0, 0, 0, 1, 0, 0, 0, 0, 0, 0] Output = 0.105344, 0.105021, 0.0659517, 0.0858077, 0.123104, 0.0884107, 0.116917, 0.161911, 0.0693426, 0.0675156Input = [0, 0, 0, 0, 0, 0, 1, 0, 0, 0] Output = 0.107113, 0.101838, 0.0641632, 0.0967766, 0.117149, 0.085271, 0.11469, 0.153649, 0.0672772, 0.0652416
以上是第996、997、998和999次迭代的输出
所以网络根本没有学习。对于这个例子,我使用了ALPHA = 0.4,ETA = 0.7,10个隐藏层,每层100个神经元,平均值为10个迭代。如果您担心学习率为0.4或如此多的隐藏层,我已经尝试了它们的不同组合。例如,学习率为0.1和4个隐藏层,每层16个神经元
回答:
这个回答是从问题发布者的评论中复制过来的。
我解开了谜团。我犯了最糟糕的错误。我给出了错误的输入。我使用opencv扫描图像,而不是使用reshape
,我使用了resize
,所以输入是图像的线性插值。因此,我的输入是错误的。代码没有任何问题。我的网络是784 - 65 - 10
,准确率为96.43%。