神經網絡的結構
神經網絡的網絡結構由輸入層,隱含層,輸出層組成。隱含層的個數+輸出層的個數=神經網絡的層數,也就是說神經網絡的層數不包括輸入層。下面是一個三層的神經網絡,包含了兩層隱含層,一個輸出層。其中第一層隱含層的節點數為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 }
(二) 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 }
在BP神經網絡中, 學習速率,訓練集,以及訓練次數,都會影響到最終模型的泛化能力。因此,在設計模型時,節點的個數,學習速率的大小,以及訓練次數都是需要考慮的。