美文网首页
一个Java实现的BP神经网络

一个Java实现的BP神经网络

作者: Lairai | 来源:发表于2018-06-19 16:01 被阅读0次

使用iris数据集通过(?)测试,部分测试结果如下

Sample 30 output: [0.9173913048441509 ,0.07218233216540289 ,0.05744858265348781]
Sample 31 output: [0.917292984920966 ,0.07226427474570203 ,0.05751372081755422]
Sample 32 output: [0.918114384748762 ,0.0713477300259459 ,0.0567037735359584]
Sample 33 output: [0.9189481821904941 ,0.07067531962504378 ,0.05611338236094786]
Sample 34 output: [0.9191395178042765 ,0.07041061573734159 ,0.05586669511083175]
Sample 35 output: [0.9172532110788241 ,0.07228931702502635 ,0.05748128484860246]
Sample 36 output: [0.9172752653761954 ,0.07211471985313571 ,0.057250110770381404]
Sample 37 output: [0.9180905113382464 ,0.07134885843883981 ,0.05662172416767537]
Sample 38 output: [0.9172532110788241 ,0.07228931702502635 ,0.05748128484860246]
Sample 39 output: [0.9165688820679151 ,0.07287697667570102 ,0.05794994983627105]
Sample 40 output: [0.9179057763850749 ,0.07162805170331658 ,0.056929321524857794]
Sample 41 output: [0.9178717765050967 ,0.07156232654619624 ,0.05682984883273152]
Sample 42 output: [0.9153313393388919 ,0.07398825937078152 ,0.058912503398660616]
Sample 43 output: [0.9169313882658655 ,0.07253471108301074 ,0.05765933287066486]
Sample 44 output: [0.9181687199702597 ,0.07130231976989787 ,0.056738514755495524]
Sample 45 output: [0.9188397803666218 ,0.0708361637887942 ,0.056398973166296724]
Sample 46 output: [0.9170014434581761 ,0.07243067392965044 ,0.057605319231257564]
Sample 47 output: [0.9185858497855375 ,0.07102838137303531 ,0.0564522737320248]
Sample 48 output: [0.9171664939908034 ,0.07233088463353576 ,0.05751266356066004]
Sample 49 output: [0.9184613071029186 ,0.07109378649317799 ,0.056471769599150436]
Sample 50 output: [0.9176069934031237 ,0.07187964526042434 ,0.057114905318048644]
Sample 51 output: [0.056661448715799256 ,0.9197971680666066 ,0.07034273604934331]
Sample 52 output: [0.05703994046881898 ,0.9193897373001655 ,0.07070184135267996]
Sample 53 output: [0.056972124673542596 ,0.9195511345944515 ,0.07059235324621554]
Sample 54 output: [0.0588559895075485 ,0.9171656902574303 ,0.07268532097165667]
Sample 55 output: [0.057536347679306876 ,0.9188198438194021 ,0.07121395582717005]
Sample 56 output: [0.05817266763576755 ,0.9182205234491182 ,0.07182467026894204]
Sample 57 output: [0.05713037213247033 ,0.9194162321584641 ,0.07071321494288134]
Sample 58 output: [0.058875908321747314 ,0.916832848600374 ,0.07294902583476662]
Sample 59 output: [0.05725757115032023 ,0.9191174308410729 ,0.07096289636406912]
Sample 60 output: [0.058499003167677165 ,0.9176086193746652 ,0.07227789562302502]

使用姿势如下

public class Test {
    public static void main(String[] args) {
        /*两个隐含层,隐含层4个元素*/
        /*目标误差0.009,最大学习次数40000,学习效率0.25,
        /*动量因子0.9*/
        /*设有两层隐含层*/
        BP net = new BP(0.25, 0.9, 1, 0.09, 4000, 2, 4);
        net.train();
        net.test();
    }
}

累计变量的收敛情况


1.1.png

实现代码如下

package BP;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;

/*
一个用于分类的BP神经网络
 */
public class BP {

    /*********************/
    /*对训练样本文件的要求:*/
    /*第一行格式为*/
    /*输入层神经元个数 输出层神经元个数*/
    /*样本值的格式要求为*/
    /*样本特征向量 期望输出向量 (空格分开)*/
    /*期望输出向量格式:(0 |1.0 )*,只能有一个1.0*/
    /*********************/
    Scanner scanner;

    private final String TRAINING_FILE_PATH = "src/BP/training_samples.txt";
    private final String TEST_FILE_PATH = "src/BP/test_samples.txt";
    private final String OUTPUT_FILE_PATH = "C:\\Coding\\JFreeChart\\src\\chart_source.txt"; //输出数据用于JFreeChar绘图,绘图代码略
    private final String OUTPUT_FILE_PATH_2 = "C:\\Coding\\JFreeChart\\src\\chart_source_2.txt"; //意义同上
    private final String OUTPUT_FILE_PATH_3 = "C:\\Coding\\JFreeChart\\src\\chart_source_3.txt"; //意义同上

    //private double training_sample_count; //训练样本数
    private double eta;                     //学习因子
    private double alpha;                   //动量因子
    private double gain;                    //sigmoid参数
    private double minError;                //最小误差
    private double outputError;

    private long maxTrainingTime;           //最大训练次数
    private int hiddenLayerCount;           //隐含层层数
    private int neuronCountHiddenLayer;     //假定每个隐含层都有相同的神经元数
    private int neuronCountInputLayer;      //输入层神经元个数
    private int neuronCountOutputLayer;     //输出层神经元个数

    Layer inputLayer;                       //输入层
    Layer outputLayer;                      //输出层
    ArrayList<Layer> hiddenLayerList;       //隐含层

    private double[][] wxh;                 //输入层和第一个隐含层的权值
    private double[][][] whh;               //隐含层之间的权值
    private double[][] why;                 //隐含层和输出层之间的权值

    private double[][] dwxh;                //dWeight
    private double[][][] dwhh;
    private double[][] dwhy;

    private double[] target;                // target output

    /*生成训练好的BP神经网络*/
    public BP(double eta, double alpha, double gain, double minError, long maxTrainingTime, int hiddenLayerCount, int neuronCountHiddenLayer) {
        this.eta = eta;
        this.alpha = alpha;
        this.gain = gain;
        this.minError = minError;
        this.maxTrainingTime = maxTrainingTime;
        this.hiddenLayerCount = hiddenLayerCount;
        this.neuronCountHiddenLayer = neuronCountHiddenLayer;
        try {
            /*读取第一行*/
            scanner = new Scanner(new File(TRAINING_FILE_PATH));
            String input_line = scanner.nextLine();
            String[] temp = input_line.split(" ");
            neuronCountInputLayer = Integer.parseInt(temp[0]);
            neuronCountOutputLayer = Integer.parseInt(temp[1]);

            /*分配内存*/
            inputLayer = new Layer(neuronCountInputLayer);
            outputLayer = new Layer(neuronCountOutputLayer);
            hiddenLayerList = new ArrayList<>(hiddenLayerCount);
            for (int i = 0; i < hiddenLayerCount; ++i)
                hiddenLayerList.add(new Layer(neuronCountHiddenLayer));

            wxh = new double[neuronCountInputLayer][neuronCountHiddenLayer];
            dwxh = new double[neuronCountInputLayer][neuronCountHiddenLayer];

            whh = new double[hiddenLayerCount - 1][neuronCountHiddenLayer][neuronCountHiddenLayer];
            dwhh = new double[hiddenLayerCount - 1][neuronCountHiddenLayer][neuronCountHiddenLayer];

            why = new double[neuronCountHiddenLayer][neuronCountOutputLayer];
            dwhy = new double[neuronCountHiddenLayer][neuronCountOutputLayer];

            target = new double[neuronCountOutputLayer];

            /*初始化权值和阈值信息*/
            for (int i = 0; i < wxh.length; ++i) {
                for (int j = 0; j < wxh[0].length; ++j) {
                    wxh[i][j] = Functions.random(-0.5, 0.5);
                }
            }

            for (int i = 0; i < whh.length; ++i) {
                for (int j = 0; j < neuronCountHiddenLayer; ++j) {
                    for (int k = 0; k < neuronCountHiddenLayer; ++k) {
                        whh[i][j][k] = Functions.random(-0.5, 0.5);
                    }
                    hiddenLayerList.get(i).thresh[j] = Functions.random(-0.5, 0.5);
                }
            }

            for (int i = 0; i < neuronCountOutputLayer; ++i) {
                for (int j = 0; j < neuronCountHiddenLayer; ++j) {
                    why[j][i] = Functions.random(-0.5, 0.5);
                }
                outputLayer.thresh[i] = Functions.random(-0.5, 0.5);
            }

            //train();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    /*训练网络*/
    public void train() {
        String s;
        double accumulatedError;            //累计
        int sample = 1;
        try {
            FileWriter writer = new FileWriter(new File(OUTPUT_FILE_PATH));
            while (true) {
                s = scanner.nextLine();
                if (s == null) break;
                accumulatedError = 0;

                /*对单个样本进行训练*/
                int turn = 0;
                while (true) {
                    ++turn;
                    outputError = 0;
                    train(s);
                    accumulatedError += outputError;
                    /*去掉后面一项即为去掉评估函数*/
                    if (turn > maxTrainingTime || outputError < minError)
                        break;
                }
                writer.write(sample + " " +accumulatedError + '\n');
                ++sample;
                if (!scanner.hasNextLine()) break;
            }
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /*利用文本中的一行信息进行训练*/
    private void train (String sample) {

        /*实例化输入层数据和该向量对应的*/
        String[] temp = sample.split(" ");
        for (int i = 0; i < neuronCountInputLayer; ++i)
            inputLayer.output[i] = Double.parseDouble(temp[i]);

        target = new double[neuronCountOutputLayer];
        for (int i = neuronCountInputLayer; i < temp.length; ++i)
            target[i - neuronCountInputLayer] = Double.parseDouble(temp[i]);

        feedForward();

        outputError = 0;
        propagateBackward();

        updateWeights();
    }

    /*测试训练完成的神经网络*/
    public void test() {
        try {
            Scanner scanner = new Scanner(new File(TEST_FILE_PATH));
            int sample = 30;
            while (scanner.hasNextLine()) {
                String[] temp = scanner.nextLine().split(" ");
                for (int i = 0; i < neuronCountInputLayer; ++i) {
                    this.inputLayer.output[i] = Double.parseDouble(temp[i]);
                }
                this.outputError = 0;
                feedForward();
                ++sample;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /*Propagate the inputs forward to compute the outputs*/
    private void feedForward() {

        /*输入层到第一隐含层*/
        Layer firstHiddenLayer = hiddenLayerList.get(0);
        for (int i = 0; i < firstHiddenLayer.neuronNumber; ++i) {
            double net = 0;
            for (int j = 0; j < inputLayer.neuronNumber; ++j) {
                net += (inputLayer.output[j] * wxh[j][i]);
            }
            net -= firstHiddenLayer.thresh[i];
            firstHiddenLayer.output[i] = Functions.sigmoid(net, gain);
        }

        /*计算隐含层之间的输出值,从第二层开始*/
        for (int i = 1; i < hiddenLayerCount; ++i) {
            Layer thisLayer = hiddenLayerList.get(i);
            Layer formerLayer = hiddenLayerList.get(i - 1);
            for (int j = 0; j < thisLayer.neuronNumber; ++j) {
                double net = 0;
                for (int k = 0; k < formerLayer.neuronNumber; ++k) {
                    net += (formerLayer.output[k] * whh[i-1][k][j]);
                }
                net -= thisLayer.thresh[j];
                thisLayer.output[j] = Functions.sigmoid(net, gain);
            }
        }

        /*最后一层到输出层*/
        Layer lastLayer = hiddenLayerList.get(hiddenLayerList.size() - 1);
        for (int i = 0; i < outputLayer.neuronNumber; ++i) {
            double net = 0;
            for (int j = 0; j < lastLayer.neuronNumber; ++j) {
                net += (lastLayer.output[j] * why[j][i]);
            }
            net -= outputLayer.thresh[i];
            outputLayer.output[i] = Functions.sigmoid(net, gain);
        }
    }

    /* Propagate deltas backward from output layer to input layer*/
    private void propagateBackward () {

        /*Computes deltas in the output layer*/
        for (int i = 0; i < outputLayer.neuronNumber; ++i) {
            double error = target[i] - outputLayer.output[i];
            outputLayer.delta[i] = gain
                    * outputLayer.output[i]
                    * (1 - outputLayer.output[i])
                    * error;
            this.outputError = (error * error) / 2;
        }

        /*Computes deltas in the last hidden layer*/
        Layer lastLayer = hiddenLayerList.get(hiddenLayerList.size() - 1);
        for (int i = 0; i < lastLayer.neuronNumber; ++i) {
            double temp = 0;
            for (int j = 0; j < outputLayer.neuronNumber; ++j)
                temp += (outputLayer.delta[j] * why[i][j]);
            lastLayer.delta[i] = (1 - lastLayer.output[i]) * lastLayer.output[i] * temp;
        }

        /*Computes deltas among the hidden layer*/
        for (int i = hiddenLayerList.size() - 2; i >= 0; --i) {
            Layer thisLayer = hiddenLayerList.get(i);
            Layer sucLayer = hiddenLayerList.get(i + 1);
            for (int j = 0; j < thisLayer.neuronNumber; ++j) {
                double temp = 0;
                for (int k = 0; k < sucLayer.neuronNumber; ++k)
                    temp += (sucLayer.delta[k] * whh[i][j][k]);
                thisLayer.delta[j] = (1 - thisLayer.output[i]) * thisLayer.output[i] * temp;
            }
        }
    }

    private void updateWeights() {
        /*更新输入层和第一层的权值和阈值*/
        Layer firstLayer = hiddenLayerList.get(0);
        for (int j = 0; j < firstLayer.neuronNumber; ++j) {
            for (int i = 0; i < inputLayer.neuronNumber; ++i) {
                wxh[i][j] += (eta * firstLayer.delta[j] * inputLayer.output[i] + alpha * dwxh[i][j]);
                dwxh[i][j] = eta * inputLayer.delta[j] * firstLayer.output[i];
            }
            firstLayer.thresh[j] = eta * firstLayer.delta[j];
        }

        /*更新隐含层之间的权值和阈值*/
        for (int i = 1; i < hiddenLayerList.size(); ++i) {
            Layer thisLayer = hiddenLayerList.get(i);
            Layer formerLayer = hiddenLayerList.get(i - 1);
            for (int j = 0; j < thisLayer.neuronNumber; ++j) {
                for (int k = 0; k < formerLayer.neuronNumber; ++k) {
                    whh[i - 1][k][j] += (eta * thisLayer.delta[j] * formerLayer.output[k] + alpha * dwhh[i - 1][k][j]);
                    dwhh[i-1][k][j] = eta * thisLayer.delta[j] * formerLayer.output[k];
                }
                thisLayer.thresh[j] = eta * thisLayer.delta[j];
            }
        }

        /*更新输出层的权值和阈值*/
        Layer lastLayer = hiddenLayerList.get(hiddenLayerList.size() - 1);
        for (int j = 0; j < outputLayer.neuronNumber; ++j) {
            for (int i = 0; i < lastLayer.neuronNumber; ++i) {
                why[i][j] += (eta * outputLayer.delta[j] * lastLayer.output[i] + alpha * dwhy[i][j]);
                dwhy[i][j] = eta * outputLayer.delta[j] * lastLayer.output[i];
            }
            outputLayer.thresh[j] = eta * outputLayer.delta[j];
        }
    }

    static class Layer {
        int neuronNumber;
        double[] output;
        double[] delta;
        double[] thresh;
        public Layer(int neuronNumber) {
            this.neuronNumber = neuronNumber;
            output = new double[neuronNumber];
            delta = new double[neuronNumber];
            thresh = new double[neuronNumber];
        }
    }
}

附用JFreeChart做的参数对累计误差收敛影响的图
[alpha]


1.2.png

[eta]


1.3.png
[gain]
1.4.png
[层数]
1.5.png

相关文章

网友评论

      本文标题:一个Java实现的BP神经网络

      本文链接:https://www.haomeiwen.com/subject/qqnaeftx.html