BP神经网络代码和原理

来源:互联网 发布:单片机的时钟电路图 编辑:程序博客网 时间:2024/06/05 07:54

神经网络的结构

神经网络的网络结构由输入层,隐含层,输出层组成。隐含层的个数+输出层的个数=神经网络的层数,也就是说神经网络的层数不包括输入层。下面是一个三层的神经网络,包含了两层隐含层,一个输出层。其中第一层隐含层的节点数为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 output107      *            预测结果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 data125      *            预测数据126      * @param output127      *            输出值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 output155      *            预测输出值156      * @param target157      *            目标值158      * @param out_error159      *            输出层的误差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 NeLaErr172      *            下一层的误差173      * @param Nextw174      *            下一层的权值175      * @param output 下一层的输入176      * @param error177      *            本层误差数组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 x194      *            输入值195      * @param output196      *            输出值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 x211      *            输入矩阵212      * @param w213      *            权值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 x230      *            输入矩阵231      * @param w232      *            权值矩阵233      * @param net_out234      *            接收网络层的输出数组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                     else27                         real[1] = 1;28                 else if ((value & 1) == 1)29                     real[2] = 1;30                 else31                     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神经网络中, 学习速率,训练集,以及训练次数,都会影响到最终模型的泛化能力。因此,在设计模型时,节点的个数,学习速率的大小,以及训练次数都是需要考虑的。

原创粉丝点击