BPニューラルネットワーク

10716 ワード

今日BPニューラルネットワークについて言えば、ニューラルネットワークは機械学習において広く応用されている.例えば、関数近似、モード識別、分類、データ圧縮、データ
発掘などの分野.次にBPニューラルネットワークの原理と実現を紹介する.
 
Contents
 
  1. BPニューラルネットワークの認識
  2. 抑制レイヤーの選択
  3. フォワードトランスファサブプロシージャ
  4. 逆転送サブプロシージャ
  5. BPニューラルネットワークの注意点
  6. BPニューラルネットワークのC++実現
 
 
1.BPニューラルネットワークの認識
 
BP(Back Propagation)ニューラルネットワークは2つのプロセスに分けられる
 
(1)作動信号順方向伝達サブプロセス
(2)誤差信号逆伝達サブプロセス
 
BPニューラルネットワークでは、単一のサンプルには入力があり、出力があり、入力層と出力層の間には通常いくつかの隠蔽層がある.実際
上で,1989年にRobert Hecht‐Nielsenは,どの閉領域内の連続関数に対しても1つの隠蔽層のBP網を用いてもよいことを実証した.
絡みが迫ってきて、これが万能が定理に迫っている.したがって、3階層のBPネットワークは、任意の次元から次元へのマッピングを完了することができます.すなわちこの3層分
入力層(I)、隠蔽層(H)、出力層(O)とは別に、以下のように示す
 
        
 
 
2.隠しレイヤーの選択
 
BPニューラルネットワークでは,入力層と出力層のノード数はいずれも決定されているが,隠蔽層のノード数は不確定である場合,どれだけに設定すべきか
ちょうどいいですか?実際,隠蔽層ノードの個数の多少はニューラルネットワークの性能に影響を及ぼし,隠蔽層を決定する経験式がある.
ノード数は、次のようになります.
 
                
 
ここで、暗黙層ノード数、入力層ノード数、出力層ノード数は、間の調整定数である.
 
 
3.順方向伝達サブプロセス
 
ノードとノードの間の重み値を、ノードのバルブ値を、各ノードの出力値を、各ノードの出力値を
値は、上位レベルのすべてのノードの出力値、現在のノードと上位レベルのすべてのノードの重み値、および現在のノードのバルブ値、およびアクティブ化関数に基づいて実現されます.
のです.具体的な計算方法は以下の通りです.
 
                    
 
ここではアクティブ化関数として、一般的にS型関数または線形関数を選択します.
 
順方向伝達の過程は比較的簡単で,上記の式に従って計算すればよい.BPニューラルネットワークでは,入力層ノードにバルブ値がない.
 
 
4.逆転送サブプロシージャ
 
BPニューラルネットワークでは,誤差信号の逆伝達サブプロセスが比較的複雑であり,これはWidrow‐Hoff学習則に基づいている.仮定出力レイヤ
のすべての結果は、誤差関数は次のとおりです.
 
                    
 
BPニューラルネットワークの主な目的は,誤差関数値が最小になるように重み値とバルブ値を繰り返し修正することである.Widrow-Hoff学習規則
相対誤差二乗和の最速降下方向に沿ってネットワークの重み値とバルブ値を連続的に調整することにより,勾配降下法に基づいて重みベクトル
の補正は、現在位置におけるE(w,b)の勾配に比例し、第1出力ノードに対して
 
                    
アクティブ化関数を
 
                    
 
アクティブ化関数を導出し、
 
                   
 
では次は
 
                   
 
中には
                            
 
同じように
 
                 
 
これが有名な学習規則であり,ニューロン間の接続重み値を変えることでシステムの実際の出力と所望の出力の誤差を低減する.
Widrow-Hoff学習規則または誤り訂正学習規則とも呼ばれる.
  
上記は、抑制層と出力層との間の重み値と出力層のバルブ値の調整量を計算し、入力層と抑制層と抑制層のバルブ値の調整
整数の計算はもっと複雑です.入力層k番目のノードと暗黙層i番目のノードとの間の重み値を仮定すると、
 
                
中には
 
                
 
これで学習ルールがより深く理解できるようになったでしょう.
 
上記の式によれば、勾配降下法によれば、隠蔽層と出力層との間の重み値とバルブ値は以下のように調整される.
 
                
 
入力層と隠し層の間の重み値とバルブ値の調整についても同様に
 
                
 
ここでBPニューラルネットワークの原理を基本的に述べる.
 
 
5.BPニューラルネットワークの注意点
 
BPニューラルネットワークは一般に問題の分類または近似に用いられる.分類に使用する場合、アクティブ化関数は一般的にSigmoid関数またはハードリミット関数を選択します.
数、関数近似に使用される場合、出力レイヤノードは線形関数、すなわち.
 
BPニューラルネットワークは,データを訓練する際に増分学習や一括学習を用いることができる.
 
インクリメンタル学習では、入力モードに十分なランダム性が必要であり、入力モードのノイズに敏感である.すなわち、急激に変化する入力モードに対して、訓練
練習の効果は比較的に悪くて、オンライン処理に適しています.ロット学習では入力モード順序の問題はなく,安定性はよいが,オフライン処理にのみ適している.
 
標準BPニューラルネットワークの欠陥:
 
(1)局所極小値が形成されやすく,グローバル最適値が得られない.
BPニューラルネットワークでは極小値が多いため,局所極小値に陥りやすいため,初期重み値とバルブ値に要求があり,
初期重み値とバルブ値のランダム性は十分に良く、複数回ランダムに実現することができる.
(2)訓練回数が多く学習効率が低く,収束速度が遅い.
(3)隠蔽層の選択は理論的指導に欠けている.
(4)訓練時に新しいサンプルを学習すると古いサンプルを忘れる傾向がある.
   
BPアルゴリズムの改良:
 
(1)増加運動量項
運動量項の導入はアルゴリズムの収束を加速させるためであり,すなわち次の式である.
 
       
 
運動量係数は一般的に選択されます.
 
(2)適応調整学習率
(3)急勾配因子の導入
 
通常、BPニューラルネットワークは、トレーニングの前にデータを正規化し、データを[0,1]または[−1,1]のようなより小さな区間にマッピングする.
 
 
6.BPニューラルネットワークのC++実現
 
BPニューラルネットワークのC++ファイルは以下の通りです.
 
   
 
BP.h:
#ifndef _BP_H_
#define _BP_H_
 
#include 
 
#define LAYER    3        //      
#define NUM      10       //        
 
#define A        30.0
#define B        10.0     //A B S      
#define ITERS    1000     //      
#define ETA_W    0.0035   //     
#define ETA_B    0.001    //     
#define ERROR    0.002    //         
#define ACCU     0.005    //         
 
#define Type double
#define Vector std::vector
 
struct Data
{
    Vector x;       //    
    Vector y;       //    
};
 
class BP{
 
public:
 
    void GetData(const Vector);
    void Train();
    Vector ForeCast(const Vector);
 
private:
 
    void InitNetWork();         //     
    void GetNums();             //    、         
    void ForwardTransfer();     //       
    void ReverseTransfer(int);  //       
    void CalcDelta(int);        //  w b    
    void UpdateNetWork();       //       
    Type GetError(int);         //         
    Type GetAccu();             //         
    Type Sigmoid(const Type);   //  Sigmoid  
 
private:
    int in_num;                 //      
    int ou_num;                 //      
    int hd_num;                 //      
 
    Vector data;          //      
 
    Type w[LAYER][NUM][NUM];    //BP     
    Type b[LAYER][NUM];         //BP       
     
    Type x[LAYER][NUM];         //        S          ,       
    Type d[LAYER][NUM];         //  delta     delta  
};
 
#endif  //_BP_H_

BP.cpp:
#include 
#include 
#include 
#include 
#include "BP.h"

//          
void BP::GetData(const Vector _data)
{
	data = _data;
}

//      
void BP::Train()
{
	printf("Begin to train BP NetWork!
"); GetNums(); InitNetWork(); int num = data.size(); for(int iter = 0; iter <= ITERS; iter++) { for(int cnt = 0; cnt < num; cnt++) { // for(int i = 0; i < in_num; i++) x[0][i] = data.at(cnt).x[i]; while(1) { ForwardTransfer(); if(GetError(cnt) < ERROR) // , break; ReverseTransfer(cnt); } } printf("This is the %d th trainning NetWork !
", iter); Type accu = GetAccu(); printf("All Samples Accuracy is %lf
", accu); if(accu < ACCU) break; } printf("The BP NetWork train End!
"); } // Vector BP::ForeCast(const Vector data) { int n = data.size(); assert(n == in_num); for(int i = 0; i < in_num; i++) x[0][i] = data[i]; ForwardTransfer(); Vector v; for(int i = 0; i < ou_num; i++) v.push_back(x[2][i]); return v; } // void BP::GetNums() { in_num = data[0].x.size(); // ou_num = data[0].y.size(); // hd_num = (int)sqrt((in_num + ou_num) * 1.0) + 5; // if(hd_num > NUM) hd_num = NUM; // } // void BP::InitNetWork() { memset(w, 0, sizeof(w)); // 0, memset(b, 0, sizeof(b)); } // void BP::ForwardTransfer() { // for(int j = 0; j < hd_num; j++) { Type t = 0; for(int i = 0; i < in_num; i++) t += w[1][i][j] * x[0][i]; t += b[1][j]; x[1][j] = Sigmoid(t); } // for(int j = 0; j < ou_num; j++) { Type t = 0; for(int i = 0; i < hd_num; i++) t += w[2][i][j] * x[1][i]; t += b[2][j]; x[2][j] = Sigmoid(t); } } // Type BP::GetError(int cnt) { Type ans = 0; for(int i = 0; i < ou_num; i++) ans += 0.5 * (x[2][i] - data.at(cnt).y[i]) * (x[2][i] - data.at(cnt).y[i]); return ans; } // void BP::ReverseTransfer(int cnt) { CalcDelta(cnt); UpdateNetWork(); } // Type BP::GetAccu() { Type ans = 0; int num = data.size(); for(int i = 0; i < num; i++) { int m = data.at(i).x.size(); for(int j = 0; j < m; j++) x[0][j] = data.at(i).x[j]; ForwardTransfer(); int n = data.at(i).y.size(); for(int j = 0; j < n; j++) ans += 0.5 * (x[2][j] - data.at(i).y[j]) * (x[2][j] - data.at(i).y[j]); } return ans / num; } // void BP::CalcDelta(int cnt) { // delta for(int i = 0; i < ou_num; i++) d[2][i] = (x[2][i] - data.at(cnt).y[i]) * x[2][i] * (A - x[2][i]) / (A * B); // delta for(int i = 0; i < hd_num; i++) { Type t = 0; for(int j = 0; j < ou_num; j++) t += w[2][i][j] * d[2][j]; d[1][i] = t * x[1][i] * (A - x[1][i]) / (A * B); } } // BP void BP::UpdateNetWork() { // for(int i = 0; i < hd_num; i++) { for(int j = 0; j < ou_num; j++) w[2][i][j] -= ETA_W * d[2][j] * x[1][i]; } for(int i = 0; i < ou_num; i++) b[2][i] -= ETA_B * d[2][i]; // for(int i = 0; i < in_num; i++) { for(int j = 0; j < hd_num; j++) w[1][i][j] -= ETA_W * d[1][j] * x[0][i]; } for(int i = 0; i < hd_num; i++) b[1][i] -= ETA_B * d[1][i]; } // Sigmoid Type BP::Sigmoid(const Type x) { return A / (1 + exp(-x / B)); }

Test.cpp:
#include 
#include 
#include 
 
#include "BP.h"
 
using namespace std;
 
double sample[41][4]= 
{ 
    {0,0,0,0}, 
    {5,1,4,19.020}, 
    {5,3,3,14.150}, 
    {5,5,2,14.360}, 
    {5,3,3,14.150}, 
    {5,3,2,15.390}, 
    {5,3,2,15.390}, 
    {5,5,1,19.680}, 
    {5,1,2,21.060}, 
    {5,3,3,14.150}, 
    {5,5,4,12.680}, 
    {5,5,2,14.360}, 
    {5,1,3,19.610}, 
    {5,3,4,13.650}, 
    {5,5,5,12.430}, 
    {5,1,4,19.020}, 
    {5,1,4,19.020}, 
    {5,3,5,13.390}, 
    {5,5,4,12.680}, 
    {5,1,3,19.610}, 
    {5,3,2,15.390}, 
    {1,3,1,11.110}, 
    {1,5,2,6.521}, 
    {1,1,3,10.190}, 
    {1,3,4,6.043}, 
    {1,5,5,5.242}, 
    {1,5,3,5.724}, 
    {1,1,4,9.766}, 
    {1,3,5,5.870}, 
    {1,5,4,5.406}, 
    {1,1,3,10.190}, 
    {1,1,5,9.545}, 
    {1,3,4,6.043}, 
    {1,5,3,5.724}, 
    {1,1,2,11.250}, 
    {1,3,1,11.110}, 
    {1,3,3,6.380}, 
    {1,5,2,6.521}, 
    {1,1,1,16.000}, 
    {1,3,2,7.219}, 
    {1,5,3,5.724} 
}; 
 
int main()
{
    Vector data;
    for(int i = 0; i < 41; i++)
    {
        Data t;
        for(int j = 0; j < 3; j++)
            t.x.push_back(sample[i][j]);
        t.y.push_back(sample[i][3]);
        data.push_back(t);
    }
    BP *bp = new BP();
    bp->GetData(data);
    bp->Train();
 
    while(1)
    {
        Vector in;
        for(int i = 0; i < 3; i++)
        {
            Type v;
            scanf("%lf", &v);
            in.push_back(v);
        }
        Vector ou;
        ou = bp->ForeCast(in);
        printf("%lf
", ou[0]); } return 0; }

 
Makefile:
Test : BP.h BP.cpp Test.cpp
    g++ BP.cpp Test.cpp -o Test
 
clean:
    rm Test