java实现bp神经网络mnist手写数字识别(代码片段)

七仔的博客 七仔的博客     2023-02-01     787

关键词:

Java实现BP神经网络,内含BP神经网络类,采用MNIST数据集,包含服务器和客户端程序,可在服务器训练后使客户端直接使用训练结果,界面有画板,可以手写数字

Java实现BP神经网络MNIST手写数字识别

如果需要源码,请在下方评论区留下邮箱,我看到就会发过去

一、神经网络的构建

(1):构建神经网络层次结构

由训练集数据可知,手写输入的数据维数为784维,而对应的输出结果为分别为0-9的10个数字,所以根据训练集的数据可知,在构建的神经网络的输入层的神经元的节点个数为784个,而对应的输出层的神经元个数为10个。隐层可选择单层或多层。

(2):确定隐层中的神经元的个数

因为对于隐层的神经元个数的确定目前还没有什么比较完美的解决方案,所以对此经过自己查阅书籍和上网查阅资料,有以下的几种经验方式来确定隐层的神经元的个数,方式分别如下所示:

  1. 一般取(输入+输出)/2

  2. 隐层一般小于输入层

3)(输入层+1)/2

  1. log(输入层)

  2. log(输入层)+10

实验得到以第五种的方式得到的测试结果相对较高。

(3):设置神经元的激活函数

在《机器学习》的书中介绍了两种比较常用的函数,分别是阶跃函数和Sigmoid函数。最后自己采用了后者函数。

(4):初始化输入层和隐层之间神经元间的权值信息

采用的是使用简单的随机数分配的方法,并且两层之间的神经元权值是通过二维数组进行保留,数组的索引就代表着两层对应的神经元的索引信息

(5):初始化隐层和输出层之间神经元间的权值信息

采用的是使用简单的随机数分配的方法,并且两层之间的神经元权值是通过二维数组进行保留,数组的索引就代表着两层对应的神经元的索引信息

(6):读取CSV测试集表格信息,并加载到程序用数据保存,其中将每个维数的数据都换成了0和1的二进制数进行处理。

(7):读取CSV测试集结果表格信息,并加载到程序用数据保存

(8):计算输入层与隐层中隐层神经元的阈值

这里主要是采用了下面的方法:

Sum=sum+weight[i][j] * layer0[i];

参数的含义:将每个输入层中的神经元与神经元的权值信息weight[i][j]乘以对应的输入层神经元的阈值累加,然后再调用激活函数得到对应的隐层神经元的阈值。

(9):计算隐层与输出层中输出层的神经元的阈值

方法和上面的类似,只是相对应的把权值信息进行了修改即可。

(10):计算误差逆传播(输出层的逆误差)

采用书上P103页的方法(西瓜书)

(11):计算误差传播(隐层的逆误差)

采用书上P103页的方法(西瓜书)

(12):更新各层神经元之间的权值信息

double newVal = momentum * prevWeight[j][i] + eta * delta[i] * layer[j];

参数:其中设置momentum 为0.9,设置eta 为0.25,prevWeight[j][i]表示神经元之间的权值,layer[j]和delta[i]表示两层不同神经元的阈值。

(13):循环迭代训练5次

(14):输入测试集数据

(15):输出测试集预测结果和实际结果进行比较,得到精确度

此处放一个多隐层BP神经网络的类(自己写的,有错误请指出):

/**
 * BP神经网络类
 * 使用了附加动量法进行优化
 * 主要使用方法:
 *     初始化:   BP bp = new BP(new int[]int,int*n,int)  //第一个int表示输入层,中间n个int表示隐藏层,最后一个int表示输出层
 *     训练: bp.train(double[],double[])               //第一个double[]表示输入,第二个double[]表示期望输出
 *     测试       int result = bp.test(double[])            //参数表示输入,返回值表示输出层最大权值
 *     另有设置学习率和动量参数方法
 */
import java.util.Random;
 
public class BP 
    
    private final double[][] layers;//输入层、隐含层、输出层
    private final double[][] deltas;//每层误差
    private final double[][][] weights;//权值
    private final double[][][] prevUptWeights;//更新之前的权值信息
    private final double[] target;   //预测的输出内容
    
    private double eta;        //学习率
    private double momentum;    //动量参数
    
    private final Random random;  //主要是对权值采取的是随机产生的方法
    
    //初始化
    public BP(int[] size, double eta, double momentum) 
       int len = size.length;
       //初始化每层
       layers = new double[len][];
       for(int i = 0; i<len; i++) 
           layers[i] = new double[size[i] + 1];
       
       //初始化预测输出
        target = new double[size[len - 1] + 1];
        
       //初始化隐藏层和输出层的误差
       deltas = new double[len - 1][];
       for(int i = 0; i < (len - 1); i++) 
           deltas[i] = new double[size[i + 1] + 1];
       
       
       //使每次产生的随机数都是第一次的分配,这是有参数和没参数的区别
        random = new Random(100000);
       //初始化权值
       weights = new double[len - 1][][];
       for(int i = 0; i < (len - 1); i++) 
           weights[i] = new double[size[i] + 1][size[i + 1] + 1];
       
       randomizeWeights(weights);
       
       //初始化更新前的权值
       prevUptWeights = new double[len - 1][][];
       for(int i = 0; i < (len - 1); i++) 
           prevUptWeights[i] = new double[size[i] + 1][size[i + 1] + 1];
       
       
        this.eta = eta;             //学习率
        this.momentum = momentum;   //动态量
    
    
    //随机产生神经元之间的权值信息  
    private void randomizeWeights(double[][][] matrix) 
        for (int i = 0, len = matrix.length; i != len; i++) 
            for (int j = 0, len2 = matrix[i].length; j != len2; j++) 
               for(int k = 0, len3 = matrix[i][j].length; k != len3; k++) 
                   double real = random.nextDouble();    //随机分配着产生0-1之间的值  
                   matrix[i][j][k] = random.nextDouble() > 0.5 ? real : -real;
               
            
        
    
    
    //初始化输入层,隐含层,和输出层  
    public BP(int[] size) 
        this(size, 0.25, 0.9);
    
    
    //训练数据
    public void train(double[] trainData, double[] target) 
       loadValue(trainData,layers[0]);       //加载输入的数据
       loadValue(target,this.target);         //加载输出的结果数据
        forward();                  //向前计算神经元权值(先算输入到隐含层的,然后再算隐含到输出层的权值)
        calculateDelta();           //计算误差逆传播值 
        adjustWeight();             //调整更新神经元的权值
    
 
    //加载数据
    private void loadValue(double[] value,double [] layer) 
        if (value.length != layer.length - 1)
            throw new IllegalArgumentException("Size Do Not Match.");
        System.arraycopy(value, 0, layer, 1, value.length);  //调用系统复制数组的方法(存放输入的训练数据)
    
    
    //向前计算(先算输入到隐含层的,然后再算隐含到输出层的权值)
    private void forward() 
       //计算隐含层到输出层的权值
       for(int i = 0; i < (layers.length - 1); i++) 
           forward(layers[i], layers[i+1], weights[i]);
        
    
    
    //计算每一层的误差(因为在BP中,要达到使误差最小)(就是逆传播算法,书上有P101)
    private void calculateDelta() 
        outputErr(deltas[deltas.length-1],layers[layers.length - 1],target);   //计算输出层的误差(因为要反过来算,所以先算输出层的)
        
        for(int i = (layers.length - 1); i > 1; i--) 
            hiddenErr(deltas[i - 2/*输入层没有误差*/],layers[i - 1],deltas[i - 1],weights[i - 1]);   //计算隐含层的误差
        
    
    
     //更新每层中的神经元的权值信息
    private void adjustWeight() 
       for(int i = (layers.length - 1); i > 0; i--) 
            adjustWeight(deltas[i - 1], layers[i - 1], weights[i - 1], prevUptWeights[i - 1]);
       
    
    
    //向前计算各个神经元的权值(layer0:某层的数据,layer1:下一层的内容,weight:某层到下一层的神经元的权值)
    private void forward(double[] layer0, double[] layer1, double[][] weight) 
        layer0[0] = 1.0;//给偏置神经元赋值为1(实际上添加了layer1层每个神经元的阙值)简直漂亮!!!
        for (int j = 1, len = layer1.length; j != len; ++j) 
            double sum = 0;//保存权值
            for (int i = 0, len2 = layer0.length; i != len2; ++i) 
               sum += weight[i][j] * layer0[i];
            
            layer1[j] = sigmoid(sum);  //调用神经元的激活函数来得到结果(结果肯定是在0-1之间的)
        
    
    
    //计算输出层的误差(delte:误差,output:输出,target:预测输出)
    private void outputErr(double[] delte, double[] output,double[] target) 
        for (int idx = 1, len = delte.length; idx != len; ++idx) 
            double o = output[idx];
            delte[idx] = o * (1d - o) * (target[idx] - o);
        
    
    
    //计算隐含层的误差(delta:本层误差,layer:本层,delta1:下一层误差,weights:权值)
    private void hiddenErr(double[] delta, double[] layer, double[] delta1, double[][] weights) 
        for (int j = 1, len = delta.length; j != len; ++j) 
            double o = layer[j];  //神经元权值
            double sum = 0;
            for (int k = 1, len2 = delta1.length; k != len2; ++k)  //由输出层来反向计算
                sum += weights[j][k] * delta1[k];
            delta[j] = o * (1d - o) * sum;
        
    
    
    //更新每层中的神经元的权值信息(这也就是不断的训练过程)
    private void adjustWeight(double[] delta, double[] layer, double[][] weight, double[][] prevWeight) 
        layer[0] = 1;
        for (int i = 1, len = delta.length; i != len; ++i) 
            for (int j = 0, len2 = layer.length; j != len2; ++j) 
               //通过公式计算误差限=(动态量*之前的该神经元的阈值+学习率*误差*对应神经元的阈值),来进行更新权值
                double newVal = momentum * prevWeight[j][i] + eta * delta[i] * layer[j];
                weight[j][i] += newVal;  //得到新的神经元之间的权值
                prevWeight[j][i] = newVal;  //保存这一次得到的权值,方便下一次进行更新
            
        
    
    
    //我这里用的是sigmoid激活函数,当然也可以用阶跃函数,看自己选择吧 
    private double sigmoid(double val) 
        return 1d / (1d + Math.exp(-val));
    
    
    //测试神经网络
    public int test(double[] inData) 
        if (inData.length != layers[0].length - 1)
            throw new IllegalArgumentException("Size Do Not Match.");
        System.arraycopy(inData, 0, layers[0], 1, inData.length);
        forward();
        return getNetworkOutput();
    
    
    //返回最后的输出层的结果
    private int getNetworkOutput() 
        int len = layers[layers.length - 1].length;
        double[] temp = new double[len - 1];
        for (int i = 1; i != len; i++)
            temp[i - 1] = layers[layers.length - 1][i];
        //获得最大权值下标
        double max = temp[0];
        int idx = -1;
        for (int i = 0; i <temp.length; i++) 
            if (temp[i] >= max) 
                max = temp[i];
                idx = i;
            
        
        return idx;
    
    
    //设置学习率
    public void setEta(double eta) 
       this.eta = eta;
    
    
    //设置动量参数
    public void setMomentum(double momentum)
       this.momentum = momentum;
    

二、系统架构

由于BP神经网络训练过程时间较长,所以采用客户端服务器(C/S)的形式,在服务器进行训练,在客户端直接进行识别,使用套接字进行通讯。

服务器:

客户端:

采用MVC架构:

  1. Model(模型)表示应用程序核心。

  2. View(视图)显示数据。

  3. Controller(控制器)处理输入。

MNIST数字集经过整理存储在CSV文件中。

以下是系统架构:

三、源码

如果需要源码,请在下方评论区留下邮箱,我看到就会发过去

基于tensorflow2.x实现bp神经网络,实践mnist手写数字识别(代码片段)

一、MNIST数据集MNIST是一个非常有名的手写数字识别数据集,在很多资料中都会被用作深度学习的入门样例。在Tensorflow2.x中该数据集已被封装在了tf.keras.datasets工具包下,如果没有指定数据集的位置,并先前也没有使... 查看详情

bp神经网络(手写数字识别)

1实验环境实验环境:CPU[email protected],内存8G,windows1064位操作系统实现语言:python实验数据:Mnist数据集程序使用的数据库是mnist手写数字数据库,数据库有两个版本,一个是别人做好的.mat格式,训练数据有60000条,每条是... 查看详情

基于tensorflow2.x实现多层卷积神经网络,实践mnist手写数字识别(代码片段)

...、MNIST数据集上篇文章中使用了Tensorflow2.x搭建了对层的BP神经网络,经过训练后发现准确率只有96.8%对于单环境的图片识别场景来说,还是有点偏低,本文使用多层的卷积代替BP网络中的隐藏层对模型进行优化。下面是... 查看详情

基于tensorflow2.x实现多层卷积神经网络,实践mnist手写数字识别(代码片段)

...、MNIST数据集上篇文章中使用了Tensorflow2.x搭建了对层的BP神经网络,经过训练后发现准确率只有96.8%对于单环境的图片识别场景来说,还是有点偏低,本文使用多层的卷积代替BP网络中的隐藏层对模型进行优化。下面是... 查看详情

java实现bp神经网络mnist手写数字识别

一、神经网络的构建(1):构建神经网络层次结构由训练集数据可知,手写输入的数据维数为784维,而对应的输出结果为分别为0-9的10个数字,所以根据训练集的数据可知,在构建的神经网络的输入层的神经元的... 查看详情

基于tensorflow2.x实现bp神经网络,实践mnist手写数字识别(代码片段)

一、MNIST数据集MNIST是一个非常有名的手写数字识别数据集,在很多资料中都会被用作深度学习的入门样例。在Tensorflow2.x中该数据集已被封装在了tf.keras.datasets工具包下,如果没有指定数据集的位置,并先前也没有使... 查看详情

pytorch使用mnist数据集实现手写数字识别(代码片段)

...写数字识别是入门必做吧。这里使用pyTorch框架进行简单神经网络的搭建。首先导入需要的包。1importtorch2importtorch.nnasnn3importtorch.utils.dataasData4importtorchvision 接下来需要下载mnist数据集。我们创建train_data。使用torchvision.datasets.MN... 查看详情

基于tensorflow2.x实现多层卷积神经网络,实践mnist手写数字识别(代码片段)

...、MNIST数据集上篇文章中使用了Tensorflow2.x搭建了对层的BP神经网络,经过训练后发现准确率只有96.8%对于单环境的图片识别场景来说,还是有点偏低,本文使用多层的卷积代替BP网络中的隐藏层对模型进行优化。下面是... 查看详情

深度学习100例-卷积神经网络(cnn)实现mnist手写数字识别|第1天

...模型五、预测六、知识点详解1.MNIST手写数字数据集介绍2.神经网络程序说明3.网络结构说明我的环境:语言环境:Python3.6.5编译器:jupyternotebook深度学习环境:TensorFlow2来自专栏:【深度学习100例 查看详情

tensorflow入门实战|第1周:实现mnist手写数字识别(代码片段)

...模型五、预测六、知识点详解1.MNIST手写数字数据集介绍2.神经网络程序说明3.网络结构说明一、前期工作1.设置GPU( 查看详情

全连接神经网络实现识别手写数据集mnist(代码片段)

全连接神经网络实现识别手写数据集MNISTMNIST是一个由美国由美国邮政系统开发的手写数字识别数据集。手写内容是0~9,一共有60000个图片样本,我们可以到MNIST官网免费下载。总共4个文件,该文件是二进制内容。train-images-idx3-uby... 查看详情

tensorflow入门实战|第1周:实现mnist手写数字识别(代码片段)

...模型五、预测六、知识点详解1.MNIST手写数字数据集介绍2.神经网络程序说明3.网络结构说明一、前期工作1.设置GPU(如果使用的是CPU可以忽略这步) 查看详情

第一周:mnist手写数字识别|365天深度学习训练营

...深度学习是什么参考文章:🔗深度学习100例-卷积神经网络(CNN)实现mnist手写数字识别|第1天 查看详情

第一周:mnist手写数字识别|365天深度学习训练营

...深度学习是什么参考文章:🔗深度学习100例-卷积神经网络(CNN)实现mnist手写数字识别|第1天 查看详情

android+tensorflow+cnn+mnist手写数字识别实现

SyncHere  查看详情

android+tensorflow+cnn+mnist手写数字识别实现

SyncHere  查看详情

bp神经网络-手写数字的识别-机器学习实验二

文末也可直接获取实验文档以及相关数据。BP神经网络-手写数字的识别-机器学习实验二实验目的:(1)了解前馈神经网络、反馈神经网络、自组织神经网络(2)掌握神经网络的应用使用BP神经网络,完成... 查看详情

用bp人工神经网络识别手写数字

http://wenku.baidu.com/link?url=HQ-5tZCXBQ3uwPZQECHkMCtursKIpglboBHq416N-q2WZupkNNH3Gv4vtEHyPULezDb50ZcKor41PEikwv5TfTqwrsQ4-9wmH06L7bYD04u用BP人工神经网络识别手写数字yzw20091201上传于2013-01-31|暂无评价|356人阅读|13次下载|暂无简 查看详情