BP神經網絡—java實現


神經網絡的結構

神經網絡的網絡結構由輸入層,隱含層,輸出層組成。隱含層的個數+輸出層的個數=神經網絡的層數,也就是說神經網絡的層數不包括輸入層。下面是一個三層的神經網絡,包含了兩層隱含層,一個輸出層。其中第一層隱含層的節點數為3,第二層的節點數為2,輸出層的節點數為1;輸入層為樣本的兩個特征X1,X2.

圖1 三層神經網絡

在神經網絡中每一個節點的都與上一層的所有節點相連,稱為全連接。神經網絡的上一層輸出的數據是下一層的輸入數據。在圖中的神經網絡中,原始的輸入數據,通過第一層隱含層的計算得出的輸出數據,會傳到第二層隱含層。而第二層的輸出,又會作為輸出層的輸入數據。

神經網絡中的每一層(除了輸入層)都是由神經元組成,也稱為節點。每一個神經元都相當於一個感知器。如下圖:

圖2 單個神經元

 在神經網絡中,每個節點都將計算出特征矩陣X與權值矩陣的加權和,得到凈輸入e,然后通過激勵函數f(e)得到該節點的輸出y。在圖1中,每條連線都可以看做是一個權值。

在神經網絡中,可以添加輸出層節點的個數來解決多分類問題。有四個類別需要分類則,則輸出層的節點個數可以設為4個節點,每一個節點代表一個類別。

BP神經網絡的訓練過程

神經網絡的訓練過程分為兩個過程:1、向前傳播得到預測數據;2、反向傳播更新權重。如下圖所示:

圖3 神經網絡的訓練過程

第一步、向前傳播得到預測數據:向前傳播的過程,即數據從輸入層輸入,經過隱含層,輸出層的計算得到預測值,預測值為輸出層的輸出結果。網絡層的輸出即,該層中所有節點(神經元)的輸出值的集合。我們以圖一的神經網絡結構為例,分析向前傳播過程。

1.得到隱含層的輸出y1,y2,y3

 

2.獲取到第二層的隱含層輸出y4,y5,輸入的數據也就是第一層隱含層的輸出數據y1,y2,y3

 

3、通過輸出層,得到最后的預測值y。

 

第二步、反向傳播更新權重:根據樣本的真實類標,計算模型預測的結果與真實類標的誤差。然后將該誤差反向傳播到各個隱含層。計算出各層的誤差,再根據各層的誤差,更新權重。

1.計算輸出層的誤差:其中z為該樣本的類標

2計算第二層隱含層的誤差

3.計算第一次隱含層的誤差:

4、更新權重:新的權值=原權值+學習速率×該節點的誤差×激勵函數的導函數的值(f(e)的倒數)×與該節點相連的輸入值

 4.1更新輸入層與第一層隱含層之間的權值:

 

 

4.2更新第一層隱含層與第二層隱含層之間的權值

4.3更新第二層隱含層與輸出層之間的權值

以上就是反向傳播的過程。誤差從輸出層反向的傳到輸入層,然后再從輸入層向前更新權值。

BP神經網絡的設計與實現

  (一) BP神經網絡的設計

1.設計網絡的結構:

本次實驗采用java語言實現。設計了包含一個隱含層的神經網絡,即一個2層的神經網絡。

每層都含有一個一維X特征矩陣即為輸入數據,一個二維W權值矩陣,一個一維的誤差矩陣error,同時該神經網絡中還包含了一個一維的目標矩陣target,記錄樣本的真實類標。

X特征矩陣:第一層隱含層的X矩陣的長度為輸入層輸入數據的特征個數+1,隱含層的X矩陣的長度則是上一層的節點的個數+1,X[0]=1。

W權值矩陣:第一維的長度設計為節點(即神經元)的個數,第二維的長度設計為上一層節點的個數+1;W[0][0]為該節點的偏置量

error誤差矩陣:數組長度設計為該層的節點個數。

目標矩陣target:輸出層的節點個數與其一致。

激活函數:采用sigmoid函數:1/1+e-x

2.神經網絡的計算過程

按照以上的設計,向前傳播得到下一層的輸出結果,如圖所示:

求誤差過程,如圖所示:

反向傳播過程,調整權值,如圖所示:

(二) BP神經網絡的實現

一、向前傳播得到預測數據:

1.初始化權值
2.訓練數據集:
  2.1、導入訓練數據集和目標值;
  2.2、向前傳播得到輸出值;
    2.2.1、獲取隱含層的輸出
    2.2.2、獲取輸出層的輸出
二、反向傳播更新權重
 1、獲取輸出層的誤差;
 2、獲取隱含層的誤差;
 3、更新隱含層的權值;
 4、更新輸出層的權值;
三.測試神經網絡
  3.3 向前傳播得到預測值;

代碼如下:

 1 public class Bp {
  2 
  3     private double[] hide1_x;//// 輸入層即第一層隱含層的輸入;hide1_x[數據的特征數目+1], hide1_x[0]為1
  4     private double[][] hide1_w;// 隱含層權值,hide1_w[本層的節點的數目][數據的特征數目+1];hide_w[0][0]為偏置量
  5     private double[] hide1_errors;// 隱含層的誤差,hide1_errors[節點個數]
  6 
  7     private double[] out_x;// 輸出層的輸入值即第二次層隱含層的輸出 out_x[上一層的節點數目+1], out_x[0]為1
  8     private double[][] out_w;// 輸出層的權值 hide1_w[節點的數目][上一層的節點數目+1]//
  9                                 // out_w[0][0]為偏置量
 10     private double[] out_errors;// 輸出層的誤差 hide1_errors[節點個數]
 11 
 12     private double[] target;// 目標值,target[輸出層的節點個數]
 13 
 14     private double rate;// 學習速率
 15 
 16     public Bp(int input_node, int hide1_node, int out_node, double rate) {
 17         super();
 18 
 19         // 輸入層即第一層隱含層的輸入
 20         hide1_x = new double[input_node + 1];
 21 
 22         // 第一層隱含層
 23         hide1_w = new double[hide1_node][input_node + 1];
 24         hide1_errors = new double[hide1_node];
 25 
 26         // 輸出層
 27         out_x = new double[hide1_node + 1];
 28         out_w = new double[out_node][hide1_node + 1];
 29         out_errors = new double[out_node];
 30 
 31         target = new double[out_node];
 32 
 33         // 學習速率
 34         this.rate = rate;
 35         init_weight();// 1.初始化網絡的權值
 36     }
 37 
 38     /**
 39      * 初始化權值
 40      */
 41     public void init_weight() {
 42 
 43         set_weight(hide1_w);
 44         set_weight(out_w);
 45     }
 46 
 47     /**
 48      * 初始化權值
 49      * 
 50      * @param w
 51      */
 52     private void set_weight(double[][] w) {
 53         for (int i = 0, len = w.length; i != len; i++)
 54             for (int j = 0, len2 = w[i].length; j != len2; j++) {
 55                 w[i][j] = 0;
 56             }
 57     }
 58 
 59     /**
 60      * 獲取原始數據
 61      * 
 62      * @param Data
 63      *            原始數據矩陣
 64      */
 65     private void setHide1_x(double[] Data) {
 66         if (Data.length != hide1_x.length - 1) {
 67             throw new IllegalArgumentException("數據大小與輸出層節點不匹配");
 68         }
 69         System.arraycopy(Data, 0, hide1_x, 1, Data.length);
 70         hide1_x[0] = 1.0;
 71     }
 72 
 73     /**
 74      * @param target
 75      *            the target to set
 76      */
 77     private void setTarget(double[] target) {
 78         this.target = target;
 79     }
 80 
 81     /**
 82      * 2.訓練數據集
 83      * 
 84      * @param TrainData
 85      *            訓練數據
 86      * @param target
 87      *            目標
 88      */
 89     public void train(double[] TrainData, double[] target) {
 90         // 2.1導入訓練數據集和目標值
 91         setHide1_x(TrainData);
 92         setTarget(target);
 93 
 94         // 2.2:向前傳播得到輸出值;
 95         double[] output = new double[out_w.length + 1];
 96         forword(hide1_x, output);
 97 
 98         // 2.3、方向傳播:
 99         backpropagation(output);
100 
101     }
102 
103     /**
104      * 反向傳播過程
105      * 
106      * @param output
107      *            預測結果
108      */
109     public void backpropagation(double[] output) {
110 
111         // 2.3.1、獲取輸出層的誤差;
112         get_out_error(output, target, out_errors);
113         // 2.3.2、獲取隱含層的誤差;
114         get_hide_error(out_errors, out_w, out_x, hide1_errors);
115         //// 2.3.3、更新隱含層的權值;
116         update_weight(hide1_errors, hide1_w, hide1_x);
117         // * 2.3.4、更新輸出層的權值;
118         update_weight(out_errors, out_w, out_x);
119     }
120 
121     /**
122      * 預測
123      * 
124      * @param data
125      *            預測數據
126      * @param output
127      *            輸出值
128      */
129     public void predict(double[] data, double[] output) {
130 
131         double[] out_y = new double[out_w.length + 1];
132         setHide1_x(data);
133         forword(hide1_x, out_y);
134         System.arraycopy(out_y, 1, output, 0, output.length);
135 
136     }
137 
138     
139     public void update_weight(double[] err, double[][] w, double[] x) {
140 
141         double newweight = 0.0;
142         for (int i = 0; i < w.length; i++) {
143             for (int j = 0; j < w[i].length; j++) {
144                 newweight = rate * err[i] * x[j];
145                 w[i][j] = w[i][j] + newweight;
146             }
147 
148         }
149     }
150 
151     /**
152      * 獲取輸出層的誤差
153      * 
154      * @param output
155      *            預測輸出值
156      * @param target
157      *            目標值
158      * @param out_error
159      *            輸出層的誤差
160      */
161     public void get_out_error(double[] output, double[] target, double[] out_error) {
162         for (int i = 0; i < target.length; i++) {
163             out_error[i] = (target[i] - output[i + 1]) * output[i + 1] * (1d - output[i + 1]);
164         }
165 
166     }
167 
168     /**
169      * 獲取隱含層的誤差
170      * 
171      * @param NeLaErr
172      *            下一層的誤差
173      * @param Nextw
174      *            下一層的權值
175      * @param output 下一層的輸入
176      * @param error
177      *            本層誤差數組
178      */
179     public void get_hide_error(double[] NeLaErr, double[][] Nextw, double[] output, double[] error) {
180 
181         for (int k = 0; k < error.length; k++) {
182             double sum = 0;
183             for (int j = 0; j < Nextw.length; j++) {
184                 sum += Nextw[j][k + 1] * NeLaErr[j];
185             }
186             error[k] = sum * output[k + 1] * (1d - output[k + 1]);
187         }
188     }
189 
190     /**
191      * 向前傳播
192      * 
193      * @param x
194      *            輸入值
195      * @param output
196      *            輸出值
197      */
198     public void forword(double[] x, double[] output) {
199 
200         // 2.2.1、獲取隱含層的輸出
201         get_net_out(x, hide1_w, out_x);
202         // 2.2.2、獲取輸出層的輸出
203         get_net_out(out_x, out_w, output);
204 
205     }
206 
207     /**
208      * 獲取單個節點的輸出
209      * 
210      * @param x
211      *            輸入矩陣
212      * @param w
213      *            權值
214      * @return 輸出值
215      */
216     private double get_node_put(double[] x, double[] w) {
217         double z = 0d;
218 
219         for (int i = 0; i < x.length; i++) {
220             z += x[i] * w[i];
221         }
222         // 2.激勵函數
223         return 1d / (1d + Math.exp(-z));
224     }
225 
226     /**
227      * 獲取網絡層的輸出
228      * 
229      * @param x
230      *            輸入矩陣
231      * @param w
232      *            權值矩陣
233      * @param net_out
234      *            接收網絡層的輸出數組
235      */
236     private void get_net_out(double[] x, double[][] w, double[] net_out) {
237 
238         net_out[0] = 1d;
239         for (int i = 0; i < w.length; i++) {
240             net_out[i + 1] = get_node_put(x, w[i]);
241         }
242 
243     }
244 
245 }
View Code

(二) BP神經網絡的測試

用上面實現的BP神經網絡來訓練模型,自動判斷它是正數還是復數,奇數還是偶數.

 1 public class Test {
 2 
 3     /**
 4      * @param args
 5      * @throws IOException
 6      */
 7     public static void main(String[] args) throws IOException {
 8         
 9     
10         Bp bp = new Bp(32, 15, 4, 0.05);
11 
12         Random random = new Random();
13         
14         List<Integer> list = new ArrayList<Integer>();
15         for (int i = 0; i != 6000; i++) {
16             int value = random.nextInt();
17             list.add(value);
18         }
19 
20         for (int i = 0; i !=25; i++) {
21             for (int value : list) {
22                 double[] real = new double[4];
23                 if (value >= 0)
24                     if ((value & 1) == 1)
25                         real[0] = 1;
26                     else
27                         real[1] = 1;
28                 else if ((value & 1) == 1)
29                     real[2] = 1;
30                 else
31                     real[3] = 1;
32                 
33                 double[] binary = new double[32];
34                 int index = 31;
35                 do {
36                     binary[index--] = (value & 1);
37                     value >>>= 1;
38                 } while (value != 0);
39 
40                 bp.train(binary, real);
41                
42                 
43 
44             }
45         }
46         
47 
48         
49         
50         System.out.println("訓練完畢,下面請輸入一個任意數字,神經網絡將自動判斷它是正數還是復數,奇數還是偶數。");
51 
52         while (true) {
53             
54             byte[] input = new byte[10];
55             System.in.read(input);
56             Integer value = Integer.parseInt(new String(input).trim());
57             int rawVal = value;
58             double[] binary = new double[32];
59             int index = 31;
60             do {
61                 binary[index--] = (value & 1);
62                 value >>>= 1;
63             } while (value != 0);
64 
65             double[] result =new double[4];
66              bp.predict(binary,result);
67 
68              
69             double max = -Integer.MIN_VALUE;
70             int idx = -1;
71 
72             for (int i = 0; i != result.length; i++) {
73                 if (result[i] > max) {
74                     max = result[i];
75                     idx = i;
76                 }
77             }
78 
79             switch (idx) {
80             case 0:
81                 System.out.format("%d是一個正奇數\n", rawVal);
82                 break;
83             case 1:
84                 System.out.format("%d是一個正偶數\n", rawVal);
85                 break;
86             case 2:
87                 System.out.format("%d是一個負奇數\n", rawVal);
88                 break;
89             case 3:
90                 System.out.format("%d是一個負偶數\n", rawVal);
91                 break;
92             }
93         }
94     }
95 }
View Code

在BP神經網絡中, 學習速率,訓練集,以及訓練次數,都會影響到最終模型的泛化能力。因此,在設計模型時,節點的個數,學習速率的大小,以及訓練次數都是需要考慮的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM