zoukankan      html  css  js  c++  java
  • BP神经网络

      最近接触了不少机器学习的知识,我对人工智能领域相当感兴趣。人工神经网络是机器学习的一个重要领域;人工神经网络原理很简单,并没有什么深奥知识点和复杂的前导知识;于是自己动手实现一个简单的BP神经网络。

      人工神经网络:神经网络就是一个有向图,这个有向图的顶点分三部分,一部分用于接收外界输入(输入层),一部分用于内部处理(隐含层),一部分用于输出结果(输出层);隐含层一般只有一层,因为随着隐含层的层数增加,神经网络的复杂性会成指数增加;一般现在应用神经网络的层数都不多,一般取一层就够了,现在最复杂的人工神经网络——谷歌大脑,它的隐层数目也只有5层;人工神经网络解决的是线性可分问题,凡是线性可分的解空间都可以用人工神经网络进行处理。

      工作流程假设输入层神经元个数为d,隐层神经元个数为q,输出层神经元个数为l;输入层到隐层的权值为vij(0<=i<d,0<=j<q)隐层到输出层的连接权为wjk(0<=j<q,0<=k<l);隐层的阈值为aj 输出层的阈值为bk;

    输入层读入数据(xi),隐层把输入层的数据的加权值(xi*vij)累加,再减去阈值(aj)作为激发函数的输入值(Aj=f(∑(0=<i<d)Xi*vij-aj));输出层把隐层的数据加权(Aj*wjk)求和,输出层的值yk=f(∑(0<=j<q)Aj*wjk-bk)。

    现在假设输入层的神经元个数为2,隐层神经元个数为2,输出层神经元个数为2(如下图)。

    训练我们的神经网络:神经网络所学到的东西都包含在它的连接权与阈值中,训练一个神经网络也就是想办法调整它的各项连接权与阈值;调整阈值首先需要有一个目标,在这里我们的目标是使输出值尽可能的与样本标号(L)一致,我们采用均方误差Ek =1/2Σ(yj-Lj)2来衡量神经网络的分类效果。现在我们只需要使均方误差尽可能小就可以了,这样就把神经网络的参数调整转化为了最优化问题。最优化问题可以用梯度下降法进行求解。梯度下降法的推到过程在很多的机器学习书上都能找到,这里我就直接给出推导结果:

    输出层梯度变化值:  Δg[j] = y[j]*(1-y[j])*(L[k][j] - y[j]);

    隐层梯度变化值:    Δe[h] = A[h]*(1-A[h])*sum;

    Δv[i][j] = u*e[j]*x[i];

    Δa[i] = -u*e[i];

    Δw[i][j] = u*g[j]*A[i];

    Δb[i] = -u*g[i]; 

    以下给出误差逆传播算法:

    输入:训练集 D={(xk,yk)}mk=1

       学习率 u

    过程:在(0,1)范围内初始化网络中所有的连接权和阈值

    repeat:

      for all (Xk,yk) in D do

        根据当前参数计算当前样本的输出yk;

        计算输出层神经元梯度项gi;

        计算隐层神经元梯度项ei;

        更新连接权vij,aj,w,jk,bk;

      end for

    until 均方误差值达到要求精度或迭代次数超过允许计算的最大次数

    算法实现代码:

    public void train(int times,double er) {
        // TODO Auto-generated method stub
        int count=0;
        do{
            count++;
            int k = -1;
            Er =0.0f;
            for(double[] x:D){
                k++;//记录当前处理的数据的标号
                E[k] = 0.0;
                for(int h=0;h<q;h++){
                    sum = 0.0f;
                    for(int i=0;i<d;i++){//计算传入隐层每个节点的加权和
                        sum += v[i][h]*x[i];     
                    }
                    A[h] = (double) sigmoid.value(sum - a[h]);//计算隐层节点
                }
                for(int h=0;h<l;h++){
                    sum =0.0f;
                    for(int j =0;j<q;j++){//分别计算传到输出层的参数加权值
                        sum += w[j][h]*A[j];//sum += w[h][j]*A[h]; bug 1
                    }
                    y[h] = (double) sigmoid.value(sum - b[h]);//计算输出值//计算均方误差
                    E[k] +=(L[k][h] - y[h])*(L[k][h] - y[h]);
                }
                E[k] *= (1.0)/2;
                //计算输出层神经元的梯度项 g
                for(int j =0;j< l;j++){
                    g[j] = y[j]*(1-y[j])*(L[k][j] - y[j]);
                }
                //计算隐层神经元的梯度项 e
                for(int h =0;h<q;h++){
                    sum = 0.0f;
                    for(int j=0;j<l;j++){
                        sum += w[h][j]*g[j];
                    }
                    e[h] = A[h]*(1-A[h])*sum;
                }
                //更新连接权 w,v 与阈值 a,b
                for(int i=0;i<q;i++){
                    a[i] +=  -u*e[i];
                }
                for(int i=0;i<l;i++){
                    b[i] += -u*g[i]; 
                }
                for(int i=0;i<d;i++){
                    for(int j =0;j<q;j++){
                        v[i][j] += u*e[j]*x[i];
                    }
                }
                for(int i=0;i<q;i++){
                    for(int j=0;j<l;j++){
                        w[i][j] += u*g[j]*A[i];
                    }
                }
            }
        Er = 0.0f;
        for(int i=0;i<E.length;i++){
            Er += Math.abs(E[k]);
        }
        }while(Er >er&&count<times);
    }

    现在用BP神经网络解决一个小小的线性分类问题:

    double D[][] = {{0,0,},{0,1},{1,0},{1,1}};
    double L[][] = {{1,0},{1,0},{1,0},{0,1}};

    对平面上的四个点{0,0,},{0,1},{1,0},{1,1}进行分类,这里的输出标签为二元值:{1,0},{1,0},{1,0},{0,1}(y0 = 1表示标签为0 ,y1=1表示标签为1),输出层需要设计成两个神经元;如果输出标签是一元值{0,0,0,1},则输出层只需一个神经元。

    import java.io.FileNotFoundException;
    import java.io.PrintWriter;
    
    import org.apache.commons.math3.analysis.function.Sigmoid;
    public class BP {
        //数据集:四个点;
        double D[][] = {{0,0,},{0,1},{1,0},{1,1}};
        double L[][] = {{1,0},{1,0},{1,0},{0,1}};
        double u = 0.1f;//学习率
        Sigmoid sigmoid ;
        int   d ;//输入层节点个数;
        int   q ;//隐层节点个数;
        int   l ;//输出层节点个数
        //阈值
        double[] a ;
        double[] b ;
        //连接权
        double[][] v ;//输入到隐层
        double[][] w ;//隐层到输出层
        double sum = 0.0f;
        double[] A;
        double[] y;
        double[] e;
        double[] g;
        double[] E;
        double     Er;
        double     er;
        
        public double[][] getD(){
            return D;
        }
        public double[][] getL(){
            return L;
        }
        BP(int in ,int mid ,int out){
            d = in;//输入层节点个数
            q = mid;//隐层节点个数
            l = out;//输出层节点个数
            a = new double[q];
            b = new double[l];
            v = new double[d][q];//输入到隐层
            w = new double[q][l];//隐层到输出层
            sigmoid = new Sigmoid();
            A = new double[q];
            y = new double[l];
            e = new double[q];
            g = new double[l];
            E = new double[D.length];
            er = Er;
            iniRandom();
        }
    
        public static void main(String[] args) {
            BP bp = new BP(2,4,2);
            bp.iniRandom();
            bp.train(1000000,1.0E-1);
            bp.save("demo.txt");
        }
    
        public void iniRandom(){
            //在(0,1)范围内初始化网络中的所有阈值,连接权
            for(int i=0;i<q;i++){
                a[i] = (double) Math.random();
            }
            for(int i=0;i<l;i++){
                b[i] = (double) Math.random();
            }
            for(int i=0;i<d;i++){
                for(int j =0;j<q;j++){
                    v[i][j] = (double) Math.random();
                }
            }
            for(int i=0;i<q;i++){
                for(int j=0;j<l;j++){
                    w[i][j] = (double) Math.random();
                }
            }            
        }
        public void train(int times,double er) {
            // TODO Auto-generated method stub
            int count=0;
            do{
                count++;
                int k = -1;
                Er =0.0f;
                for(double[] x:D){
                    k++;//记录当前处理的数据的标号
                    E[k] = 0.0;
                    for(int h=0;h<q;h++){
                        sum = 0.0f;
                        for(int i=0;i<d;i++){//计算传入隐层每个节点的加权和
                            sum += v[i][h]*x[i];     
                        }
                        A[h] = (double) sigmoid.value(sum - a[h]);//计算隐层节点
                    }
                    for(int h=0;h<l;h++){
                        sum =0.0f;
                        for(int j =0;j<q;j++){//分别计算传到输出层的参数加权值
                            sum += w[j][h]*A[j];//sum += w[h][j]*A[h]; bug 1
                        }
                        y[h] = (double) sigmoid.value(sum - b[h]);//计算输出值
                        //计算均方误差
                        E[k] +=(L[k][h] - y[h])*(L[k][h] - y[h]);
                    }
                    E[k] *= (1.0)/2;
                    //计算输出层神经元的梯度项 g
                    for(int j =0;j< l;j++){
                        g[j] = y[j]*(1-y[j])*(L[k][j] - y[j]);
                    }
                    //计算隐层神经元的梯度项 e
                    for(int h =0;h<q;h++){
                        sum = 0.0f;
                        for(int j=0;j<l;j++){
                            sum += w[h][j]*g[j];
                        }
                        e[h] = A[h]*(1-A[h])*sum;
                    }
                    //更新连接权 w,v 与阈值 a,b
                    for(int i=0;i<q;i++){
                        a[i] +=  -u*e[i];
                    }
                    for(int i=0;i<l;i++){
                        b[i] += -u*g[i]; 
                    }
                    for(int i=0;i<d;i++){
                        for(int j =0;j<q;j++){
                            v[i][j] += u*e[j]*x[i];
                        }
                    }
                    for(int i=0;i<q;i++){
                        for(int j=0;j<l;j++){
                            w[i][j] += u*g[j]*A[i];
                        }
                    }
                }
            Er = 0.0f;
            for(int i=0;i<E.length;i++){
                Er += Math.abs(E[k]);
            }
            }while(Er >er&&count<times);
            System.out.println("y0	y1");
            for(int k =0;k<D.length;k++){
                double[]x = D[k];
                for(int h=0;h<q;h++){
                    sum = 0.0f;
                    for(int i=0;i<d;i++){//计算传入隐层每个节点的加权和
                        sum += v[i][h]*x[i];     
                    }
                    A[h] = (double) sigmoid.value(sum - a[h]);//计算隐层节点
                }    
                for(int h=0;h<l;h++){
                    sum =0.0f;
                    for(int j =0;j<q;j++){//分别计算传到输出层的参数加权值
                        sum += w[j][h]*A[j];//sum += w[h][j]*A[h]; bug 1
                    }
                    y[h] = (double) sigmoid.value(sum - b[h]);//计算输出值
                    System.out.print(Math.round(y[h])+"	");
                }
                System.out.println();
            }
                System.out.println(count+"-Er-"+Er);
                System.out.println();
        }
        public void save(String filename){
            PrintWriter out = null;
            try {
                out = new PrintWriter(filename);
                out.println(d+"	"+q+"	"+l);
                out.println("w[]:");
                for(int i=0;i<w.length;i++){
                    for(int j=0;j<w[i].length;j++){
                        out.print(w[i][j]+"	"); 
                    }
                    out.println();
                }
                out.println("a[]:");
                for(int i=0;i<a.length;i++){
                    out.print(a[i]+"	");
                }
                out.println();
                out.println("v[]:");
                for(int i=0;i<v.length;i++){
                    for(int j =0;j<v[i].length;j++){
                        out.print(v[i][j]+"	");
                    }
                    out.println();
                }
                out.println("b[]:");
                for(int i=0;i<b.length;i++){
                    out.print(b[i]+"	"); 
                }            
            } catch (FileNotFoundException e) {
                System.out.println("FileNotFound...");
            }finally{ 
                out.close();    
            }
        }
    }
    View Code

    运行结果:

    y0    y1
    1    0    
    1    0    
    1    0    
    0    1    
    1326-Er-0.09985477040289659

    神经网络参数值:

    2    4    2
    w[]:
    0.4734734334555844    0.4166439630989184    
    -0.16539393728354276    0.6905024328593287    
    -2.8796915544942725    2.43966901304918    
    -3.5478627812808825    3.948999341948662    
    a[]:
    0.32063821958753613    0.6786843360467054    3.13511814629679    4.05368024148439    
    v[]:
    0.8460297282417694    0.8832195045962835    2.18111507956042    2.759799840353552    
    -0.0373458145354313    0.933224930030448    2.152145920609395    2.7662226690924814    
    b[]:
    -3.296460577322894    4.123592764542515    
    View Code

    设计BackPropagation类:上面的例程已经完全实现了BP神经网络,现在需要把它封装成一个类,以便于日后使用方便。

  • 相关阅读:
    Excel.Application使用手册
    VMwareworkstationfull9.0.1894247+汉化补丁(2013.1.22)+有效密钥
    3个月ESET全系列产品试用用户名和密码
    各大安软官方卸载工具
    MDX语法学习filter与iif的使用
    SET XACT_ABORT 用法
    wcf传输List<t>
    存储过程中SELECT与SET对变量赋值
    SQL Server 定时备份数据库(作业)
    数据仓库MDX实际应用
  • 原文地址:https://www.cnblogs.com/yuanzhenliu/p/5644754.html
Copyright © 2011-2022 走看看