英文原文链接:http://www.codeproject.com/KB/recipes/Backprop_ANN.aspx
作者:Sacha Barber
翻译:本站(http://hi.baidu.com/ebfok)原创
说明:翻译力求保持原貌,删去了一些无关紧要的内容;水平有限,错误难免;转载请注明出处。
-------------------------------------------------------------------------------------
什么是XOR问题,如下面的真值表所示:
新的神经网络是什么样的
能解决XOR问题的新的神经网络看起来就像是一个单层网络。我们面对的仍然是输入/权值/输出。新东西是隐含层。
通过使用输入和权值我们能计算出给定节点的激活状态。对隐含层来说这很容易办到,因为它与输入层是直接相连的。
输出层与输入层是隔离的,所以为了计算出输出节点的激活状态,需要利用隐含层的输出,也就是输出层节点的输入。
上面的整个过程可以看成是从一层到下一层的前向传播过程。
这和单层网络仍是类似的,任何给定节点的激活状态也仍按下面的方法计算:
wi 是 weight(i), Ii 是input(i)
学习的类型
有两种学习类型:加强型和监督型
加强型学习
在加强型学习中,当训练时,一组输入提交到神经网络,输出是0.75,期望输出是1.0。
误差(1.0 - 0.75)被用来训练。
当有2个输出,总误差是2个输出误差的和,这等于是说“所有输出上的总误差是1.76”
注意这是说你错得有多么严重,而不是说你错再什么方向。
使用这种方法是用也得不到结果的。
监督型学习
监督型学习中网络被提供更多的信息。不只是告诉网络错得有多严重,而且还告诉错再什么方向。
学习算法
训练神经网络的步骤如下:
- 随机生成权值(和偏置)
- 测试训练集中的数据,看误差有多大
- 微调权值,以便改善输出
- 尝试新的训练集或者重复训练原来的训练集
- 重复以上过程直到得到精确的输出
本文就是按以上步骤来解决XOR问题的,这也被叫做“反向传播”(BP或BackProp)
反向传播利用输出误差来调节输出层的权值,不仅如此,还能计算上一层的误差,并用这个误差来调整那里的权值,余类推。
采用S函数作为非线性传递函数是个技巧,之所以使用S函数,是因为它是可微分的。
S函数完美地可微分,所以有
即
delta_outputs[i] = outputs[i] * (1.0 - outputs[i]) * (targets[i] - outputs[i])
正是使用这种算法才使得权值增量能在网络中反向传播。
值得注意的地方
存在着这样的凹部,两边十分陡峭,而往底部则倾斜得较轻微,梯度下降时,时间浪费在在凹部的两边上上下下的过程中。(想想球吧!)
所以应该怎么办呢?可以加个动量项,就能抵消上面的那种来回往复的运动,并且加强任何一致的方向,这样就能更加快速的下降到谷底。
开始训练
从下面这段代码开始:
/// 主训练过程。期望输出值以参数形式传入。神经网络通过微调权值而更新。加入了动量项以确保训练/// 朝正确的方向进行。我设法避免出现上面所说的凹部。
/// 参数:一个 double[] 数组,包含了期望输出值
private void train_network(double[] target)
{
//得到动量值
double[] delta_hidden = new double[nn.NumberOfHidden + 1];
double[] delta_outputs = new double[nn.NumberOfOutputs];
// 得到输出层的delta值
for (int i = 0; i < nn.NumberOfOutputs; i++)
{
delta_outputs[i] =
nn.Outputs[i] * (1.0 - nn.Outputs[i]) * (target[i] - nn.Outputs[i]);
}
//得到隐含层的delta值
for (int i = 0; i < nn.NumberOfHidden + 1; i++)
{
double error = 0.0;
for (int j = 0; j < nn.NumberOfOutputs; j++)
{
error += nn.HiddenToOutputWeights[i, j] * delta_outputs[j];
}
delta_hidden[i] = nn.Hidden[i] * (1.0 - nn.Hidden[i]) * error;
}
// 更新隐含层和输出层之间的权值
for (int i = 0; i < nn.NumberOfOutputs; i++)
{
for (int j = 0; j < nn.NumberOfHidden + 1; j++)
{
//使用动量项,确保朝着正确的方向移动
nn.HiddenToOutputWeights[j, i] += nn.LearningRate * delta_outputs[i] * nn.Hidden[j];
}
}
//更新输入层和隐含层之间的权值
for (int i = 0; i < nn.NumberOfHidden; i++)
{
for (int j = 0; j < nn.NumberOfInputs + 1; j++)
{
//使用动量项,确保朝着正确的方向移动
nn.InputToHiddenWeights[j, i] += nn.LearningRate * delta_hidden[i] * nn.Inputs[j];
}
}
}
最终的代码
本文中的代码如下面的类图所示(Visual Studio 2005 C#, .NET v2.0)
值得人们花点时间研究下的主要的类有:
- NN_Trainer_XOR : 训练神经网络以解决XOR问题
- TrainerEventArgs : 训练事件参数,用于GUI
- NeuralNetwork : 可调的神经网络
- NeuralNetworkEventArgs : 训练事件参数,用于GUI
- SigmoidActivationFunction :S型激活函数
程序演示
由图可见,XOR问题几乎被解决了,但是不能达到100%的精度
训练结果