英文原文連結: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%的精度
訓練結果