手把手教你使用Java实现一个神经网络

首先看一下运行效果:

下面是项目整体目录:


0.实现神经网络总览

神经网络由层、神经元、权重、激活函数和偏置组成。每层都有一个或者多个神经元,每一个神经元都和神经输入/输出连接,这些连接就是权重。
需要重点强调一下,一个神经网络可能有很多隐含层,也可能一个没有,因为每层的神经元数目也可能不同。然而,输入输出层的神经元个数分别等于神经输入/输出的个数。
我们为了实现,需要定义以下的类:

  • Neuron: 定义人工神经元
  • NeuralLayer: 抽象类,定义一个神经元层。
  • InputLayer: 定义神经输入层
  • HiddenLayer:定义输入层和输出层之间的层
  • OutputLayer: 定义神经输出层。
  • InputNeuron: 定义神经网络输入中出现的神经元。
  • NeuralNet:将前面定义的所有类组成一个ANN结构。

除了以上的类,我们还需要为激活函数定义一个IActivationFunction接口。这是必要的,因为激活函数与方法类似,需要作为神经元的一个属性进行分配。所以要为激活函数定义类,这些类需要实现IActivationFunction接口:

  • Linear
  • Sigmoid
  • Step
  • HyperTan

第一章的编码基本完成。除此之外,还需要定义俩个类。一个用于异常处理(NeuralException),另一个用于产生随机数(RandomNumberGenerator)。最后,将这些类分别放到俩个包。


1.神经元Neuron类

神经元类是本章代码的基础类。根据理论,人工神经元有如下属性

  • 输入
  • 权重
  • 偏置
  • 激活函数
  • 输出

首先定义神经元的各种属性:

public class Neuron {
    //神经元相关的权重
    protected ArrayList<Double> weight;
    //神经元的输入
    private ArrayList<Double> input;
    //这个神经元的输出,由激活函数产生
    private Double output;
    //传递给激活函数的值
    private Double outputBeforeActivation;
    //输入的数量。如果为0,则表示神经元尚未初始化。
    private int numberOfInputs = 0;
    //神经元的偏差。除了第一层,其他都应该是1.0。
    protected Double bias = 1.0;
    //神经元的激活函数
    private IActivationFunction activationFunction;
}

当实例化神经元时,需要指定输入数据的个数以及激活函数。构造函数如下:

    public Neuron(int numberofinputs,IActivationFunction iaf){
        numberOfInputs=numberofinputs;
        weight=new ArrayList<>(numberofinputs+1);
        input=new ArrayList<>(numberofinputs);
        activationFunction=iaf;
    }

注意,为偏置定义另一个权重。一个重要的步骤是初始化神经元,也就是为权重赋初始值。这主要在init()方法中完成,通过随机数生成器静态类RandomNumberGenerator生成随机数,赋值权重。注意:设置权重值时需要防止权重数组越界。

public void init(){
        if(numberOfInputs>0){
            for(int i=0;i<=numberOfInputs;i++){
                double newWeight = RandomNumberGenerator.GenerateNext();
                try{
                    this.weight.set(i, newWeight);
                }
                catch(IndexOutOfBoundsException iobe){
                    this.weight.add(newWeight);
                }
            }
        }
    }
    

最后,在calc()方法中计算输出值:

public void calc(){
        outputBeforeActivation=0.0;
        if(numberOfInputs>0){
            if(input!=null && weight!=null){
                for(int i=0;i<=numberOfInputs;i++){
                    outputBeforeActivation+=(i==numberOfInputs?bias:input.get(i))*weight.get(i);
                }
            }
        }
        output=activationFunction.calc(outputBeforeActivation);
    }

首先需要对所有输入和权重的成绩进行求和(偏置乘最后一个权重,i==Number-OfInputs),然后将得出的结果保存在属性outputBeforeActivation中。激活函数用这个值计算神经元的输出。

总代码如下:

package neuralnet;

import java.util.ArrayList;

public class Neuron {
    //神经元相关的权重
    protected ArrayList<Double> weight;
    //神经元的输入
    private ArrayList<Double> input;
    //这个神经元的输出,由激活函数产生
    private Double output;
    //传递给激活函数的值
    private Double outputBeforeActivation;
    //输入的数量。如果为0,则表示神经元尚未初始化。
    private int numberOfInputs = 0;
    //神经元的偏差。除了第一层,其他都应该是1.0。
    protected Double bias = 1.0;
    //神经元的激活函数
    private IActivationFunction activationFunction;
    public Neuron(){

    }
    public Neuron(int numberofinputs){
        numberOfInputs=numberofinputs;
        weight=new ArrayList<>(numberofinputs+1);
        input=new ArrayList<>(numberofinputs);
    }
    public Neuron(int numberofinputs,IActivationFunction iaf){
        numberOfInputs=numberofinputs;
        weight=new ArrayList<>(numberofinputs+1);
        input=new ArrayList<>(numberofinputs);
        activationFunction=iaf;
    }
    public void init(){
        if(numberOfInputs>0){
            for(int i=0;i<=numberOfInputs;i++){
                double newWeight = RandomNumberGenerator.GenerateNext();
                try{
                    this.weight.set(i, newWeight);
                }
                catch(IndexOutOfBoundsException iobe){
                    this.weight.add(newWeight);
                }
            }
        }
    }
    public void setInputs(double [] values){
        if(values.length==numberOfInputs){
            for(int i=0;i<numberOfInputs;i++){
                try{
                    input.set(i, values[i]);
                }
                catch(IndexOutOfBoundsException iobe){
                    input.add(values[i]);
                }
            }
        }
    }
    public void setInputs(ArrayList<Double> values){
        if(values.size()==numberOfInputs){
            input=values;
        }
    }
    public ArrayList<Double> getArrayInputs(){
        return input;
    }
    public double[] getInputs(){
        double[] inputs = new double[numberOfInputs];
        for (int i=0;i<numberOfInputs;i++){
            inputs[i]=this.input.get(i);
        }
        return inputs;
    }
    public void setInput(int i,double value){
        if(i>=0 && i<numberOfInputs){
            try{
                input.set(i, value);
            }
            catch(IndexOutOfBoundsException iobe){
                input.add(value);
            }
        }
    }
    public double getInput(int i){
        return input.get(i);
    }
    public double[] getWeights(){
        double[] weights = new double[numberOfInputs+1];
        for(int i=0;i<=numberOfInputs;i++){
            weights[i]=weight.get(i);
        }
        return weights;
    }
    public ArrayList<Double> getArrayWeights(){
        return weight;
    }
    public void updateWeight(int i, double value){
        if(i>=0 && i<=numberOfInputs){
            weight.set(i, value);
        }
    }
    public int getNumberOfInputs(){
        return this.numberOfInputs;
    }
    public void setWeight(int i,double value) throws NeuralException{
        if(i>=0 && i<numberOfInputs){
            this.weight.set(i, value);
        }
        else{
            throw new NeuralException("Invalid weight index");
        }
    }

    public double getOutput(){
        return output;
    }

    public void calc(){
        outputBeforeActivation=0.0;
        if(numberOfInputs>0){
            if(input!=null && weight!=null){
                for(int i=0;i<=numberOfInputs;i++){
                    outputBeforeActivation+=(i==numberOfInputs?bias:input.get(i))*weight.get(i);
                }
            }
        }
        output=activationFunction.calc(outputBeforeActivation);
    }

    public void setActivationFunction(IActivationFunction iaf){
        this.activationFunction=iaf;
    }

    public double getOutputBeforeActivation(){
        return outputBeforeActivation;
    }
}

2.NeuralLayer类

在这个类中,将把在同一层中对齐的神经元分成一组。因为一层需要将值传递给另一层,也需要定义层与层之间的连接。类的属性定义如下:

//这一层的神经元数量
    protected int numberOfNeuronsInLayer;
    //这一层的神经元
    private ArrayList<Neuron> neuron;
    //激励函数
    protected IActivationFunction activationFnc;
    //将值提供给此层的前一层
    protected NeuralLayer previousLayer;
    protected NeuralLayer nextLayer;
    protected ArrayList<Double> input;
    protected ArrayList<Double> output;
    protected int numberOfInputs;

这个类是抽象的,整整可实例化的层类是InputLayer、HiddenLayer和Outp-utLayer。创建一个类是,必须使用另一个类的构造函数,这几个类具有相似的构造函数。

而层的初始化和计算都和神经元一样,他们也实现了init()方法和calc() 方法。生命欸protected类型,确保了只有子类可以调用或覆盖这些方法。

全部的代码如下:

package neuralnet;

import java.util.ArrayList;

public abstract class NeuralLayer {
    //这一层的神经元数量
    protected int numberOfNeuronsInLayer;
    //这一层的神经元
    private ArrayList<Neuron> neuron;
    //激励函数
    protected IActivationFunction activationFnc;
    //将值提供给此层的前一层
    protected NeuralLayer previousLayer;
    protected NeuralLayer nextLayer;
    protected ArrayList<Double> input;
    protected ArrayList<Double> output;
    protected int numberOfInputs;

    public NeuralLayer(int numberofneurons){
        this.numberOfNeuronsInLayer=numberofneurons;
        neuron = new ArrayList<>(numberofneurons);
        output = new ArrayList<>(numberofneurons);
    }

    public NeuralLayer(int numberofneurons,IActivationFunction iaf){
        this.numberOfNeuronsInLayer=numberofneurons;
        this.activationFnc=iaf;
        neuron = new ArrayList<>(numberofneurons);
        output = new ArrayList<>(numberofneurons);
    }
    public int getNumberOfNeuronsInLayer(){
        return numberOfNeuronsInLayer;
    }

    public ArrayList<Neuron> getListOfNeurons(){
        return neuron;
    }

    protected NeuralLayer getPreviousLayer(){
        return previousLayer;
    }

    protected NeuralLayer getNextLayer(){
        return nextLayer;
    }

    protected void setPreviousLayer(NeuralLayer layer){
        previousLayer=layer;
    }

    protected void setNextLayer(NeuralLayer layer){
        nextLayer=layer;
    }
    protected void init(){
        if(numberOfNeuronsInLayer>=0){
            for(int i=0;i<numberOfNeuronsInLayer;i++){
                try{
                    neuron.get(i).setActivationFunction(activationFnc);
                    neuron.get(i).init();
                }
                catch(IndexOutOfBoundsException iobe){
                    neuron.add(new Neuron(numberOfInputs,activationFnc));
                    neuron.get(i).init();
                }
            }
        }
    }

    protected void setInputs(ArrayList<Double> inputs){
        this.numberOfInputs=inputs.size();
        this.input=inputs;
    }

    protected void calc(){
        if(input!=null && neuron!=null){
            for(int i=0;i<numberOfNeuronsInLayer;i++){
                neuron.get(i).setInputs(this.input);
                neuron.get(i).calc();
                try{
                    output.set(i,neuron.get(i).getOutput());
                }
                catch(IndexOutOfBoundsException iobe){
                    output.add(neuron.get(i).getOutput());
                }
            }
        }
    }

    protected ArrayList<Double> getOutputs(){
        return output;
    }

    protected Neuron getNeuron(int i){
        return neuron.get(i);
    }

    protected void setNeuron(int i, Neuron _neuron){
        try{
            this.neuron.set(i, _neuron);
        }
        catch(IndexOutOfBoundsException iobe){
            this.neuron.add(_neuron);
        }
    }

}

3.ActivationFunction接口

在定义NeerualNetwork类之前,先看接口的Java代码示例:

public interface IActivationFunction {
    double calc(double x);
    public enum ActivationFunctionENUM {
        STEP, LINEAR, SIGMOID, HYPERTAN
    }
}

其中calc()方法属于实现IActivationFunction接口的特定的激活函数类,例如Sigmoid函数。

public class Sigmoid implements IActivationFunction {
    private double a=1.0;
    public Sigmoid(){

    }
    public Sigmoid(double value){
        this.setA(value);
    }
    public void setA(double value){
        this.a=value;
    }
    @Override
    public double calc(double x){
        return 1.0/(1.0+Math.exp(-a*x));
    }
}

这也是多态性的一个示例,即在相同的函数名下,类和方法呈现不同的行为,产生灵活的应用。

4.神经网络(NeuralNet)类

最后,定义神经网络类。到目前为止,我们已经知道,神经网络在神经层中组织神经元,且每个神经网络至少有俩层,一个用来接收收入,一个用来处理输出,还有一个数量可变的隐含层。因此,除了具有和神经元以及NeuralLary类相似的属性之外,Neural还将拥有这几个属性,如numberOfInputs,numberOfOutputs等。

 private InputLayer inputLayer;
    private ArrayList<HiddenLayer> hiddenLayer;
    private OutputLayer outputLayer;
    private int numberOfHiddenLayers;
    private int numberOfInputs;
    private int numberOfOutputs;
    private ArrayList<Double> input;
    private ArrayList<Double> output;
   

这个类的构造函数比前面类的参数更多:

 public NeuralNet(int numberofinputs,int numberofoutputs,
            int [] numberofhiddenneurons,IActivationFunction[] hiddenAcFnc,
            IActivationFunction outputAcFnc)

如果隐含层的数量是可变的,我们还应该考虑到可能有多个隐含层或0个隐含层,且对每个隐含层来说,隐藏神经元的数量也是可变的。处理这种可变性的最好方法就是把每个隐含层中的神经元数量表示为一个整数向量(参数 numberofhiddenlayers)。此外,需要为每个隐含层定义激活函数,包括输出层,完成这个目标所需要的参数分别为hiddenActivationFnc和outputAcFnc。
完整实现如下:

public NeuralNet(int numberofinputs,int numberofoutputs,
            int [] numberofhiddenneurons,IActivationFunction[] hiddenAcFnc,
            IActivationFunction outputAcFnc){
        numberOfHiddenLayers=numberofhiddenneurons.length;
        numberOfInputs=numberofinputs;
        numberOfOutputs=numberofoutputs;
        if(numberOfHiddenLayers==hiddenAcFnc.length){
            input=new ArrayList<>(numberofinputs);
            inputLayer=new InputLayer(numberofinputs);
            if(numberOfHiddenLayers>0){
                hiddenLayer=new ArrayList<>(numberOfHiddenLayers);
            }
            for(int i=0;i<numberOfHiddenLayers;i++){
                if(i==0){
                    try{
                        hiddenLayer.set(i,new HiddenLayer(numberofhiddenneurons[i],
                            hiddenAcFnc[i],
                            inputLayer.getNumberOfNeuronsInLayer()));
                    }
                    catch(IndexOutOfBoundsException iobe){
                        hiddenLayer.add(new HiddenLayer(numberofhiddenneurons[i],
                            hiddenAcFnc[i],
                            inputLayer.getNumberOfNeuronsInLayer()));
                    }
                    inputLayer.setNextLayer(hiddenLayer.get(i));
                }
                else{
                    try{
                        hiddenLayer.set(i, new HiddenLayer(numberofhiddenneurons[i],
                             hiddenAcFnc[i],hiddenLayer.get(i-1)
                            .getNumberOfNeuronsInLayer()
                            ));
                    }
                    catch(IndexOutOfBoundsException iobe){
                        hiddenLayer.add(new HiddenLayer(numberofhiddenneurons[i],
                             hiddenAcFnc[i],hiddenLayer.get(i-1)
                            .getNumberOfNeuronsInLayer()
                            ));
                    }
                    hiddenLayer.get(i-1).setNextLayer(hiddenLayer.get(i));
                }
            }
            if(numberOfHiddenLayers>0){
                outputLayer=new OutputLayer(numberofoutputs,outputAcFnc,
                        hiddenLayer.get(numberOfHiddenLayers-1)
                        .getNumberOfNeuronsInLayer()
                        );
                hiddenLayer.get(numberOfHiddenLayers-1).setNextLayer(outputLayer);
            }
            else{
                outputLayer=new OutputLayer(numberofinputs, outputAcFnc,
                        numberofoutputs);
                inputLayer.setNextLayer(outputLayer);
            }
        }
    }

5.运行程序

代码如下:

package neuralnet;

import neuralnet.math.IActivationFunction;
import neuralnet.math.Linear;
import neuralnet.math.RandomNumberGenerator;
import neuralnet.math.Sigmoid;

public class NeuralNetConsoleTest {
    public static void main(String[] args){

        RandomNumberGenerator.seed=0;

        int numberOfInputs=2;
        int numberOfOutputs=1;
        int[] numberOfHiddenNeurons= { 3 };
        IActivationFunction[] hiddenAcFnc = { new Sigmoid(1.0) } ;
        Linear outputAcFnc = new Linear(1.0);
        System.out.println("Creating Neural Netword...");
        NeuralNet nn = new NeuralNet(numberOfInputs,numberOfOutputs,
                numberOfHiddenNeurons,hiddenAcFnc,outputAcFnc);
        System.out.println("Neural Network Network...");

        double [] neuralInput = { 1.5 , 0.5 };
        System.out.println("Feeding the values {1.5;0.5} to the neural network");
        double [] neuralOutput;
        nn.setInputs(neuralInput);
        nn.calc();
        neuralOutput=nn.getOutputs();
        System.out.println("OutPut 1:" + neuralOutput[0]);
        neuralInput[0] = 1.0;
        neuralInput[1] = 2.1;
        System.out.println("Feeding the values {1.0;2.1} to the neural network");
        nn.setInputs(neuralInput);
        nn.calc();
        neuralOutput=nn.getOutputs();
        System.out.println("OutPut 2:" + neuralOutput[0]);

    }
}

到此就完成了我们神经网络的全部代码:下面是源代码压缩包。有需要的同学可以下载运行。

下载链接

原文地址:https://www.cnblogs.com/godoforange/p/11567090.html

时间: 2024-11-05 16:40:51

手把手教你使用Java实现一个神经网络的相关文章

手把手教你使用FineUI开发一个b/s结构的取送货管理信息系统系列博文索引

近阶段接到一些b/s类型的软件项目,但是团队成员之前大部分没有这方面的开发经验,于是自己选择了一套目前网上比较容易上手的开发框架(FineUI),计划录制一套视频讲座,来讲解如何利用FineUI快速开发一个小型的b/s结构的管理系统,已达到帮助团队成员快速掌握b/s结构信息系统的开发方法. 源码位置:https://github.com/kamiba/FineUIDemo 源码下载办法:安装SVN客户端(本文最后提供下载地址),然后checkout以下的地址:https://github.com

手把手教你用Java制作原生态的报表

今天下午没事干.主要是论文没有什么进展,唉!读研苦逼啊.突然想玩玩其他的,记得上次用JSP和Servlet是几个月前的事了.于是就任性的玩一回报表. 首先我是默认你不是菜鸟了,而且用过JSP和Servlet,会Java Web的基础知识,会使用Jdbc连接数据库.这些都是必要的知识,否则接下来的内容将没法讲解. 一.报表生成的关键要素 1.后台数据的抽取 2.数据项的逻辑运算 3.前台使用表格展示 二.使用流程图的形式展示3大关键要素 1.后台数据的抽取流程 我们来谈谈这个流程所需要的技术. 获

手把手教你实现Java权限管理系统 后端篇(十二):解决跨域问题

什么是跨域? 同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源. 同源策略是浏览器安全的基石. 如果一个请求地址里面的协议.域名和端口号都相同,就属于同源. 举个栗子 判断下面URL是否和 http://www.a.com/a/a.html 同源 http://www.a.com/b/b.html 同源 http://www.b.com/a/a.html 不同源,域名不相同 https://www.a.com/b/b.html 不同源,协议不相同 htt

手把手教你用java实现syslog消息的收发,学不会你打我喽!

大家好,我是道哥,专注于后端java开发,喜欢写作和分享.如果觉得文章对你有用,那就点个赞呗!如果能转发那是对道哥最大的支持! syslog的定义 见文知义,syslog,从英文名字上可以看出是指系统日志. 以下内容摘自百度百科: Syslog常被称为系统日志或系统记录,是一种用来在互联网协议(TCP/IP)的网上中传递记录档消息的标准.这个词汇常用来指涉实际的syslog 协议,或者那些提交syslog消息的应用程序或数据库. syslog协议属于一种主从式协议:syslog发送端会发送出一个

手把手教你用Docker部署一个MongoDB集群

MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中最像关系数据库的.支持类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引.本文介绍了如何使用Docker搭建MongoDB集群. 本文我会向大家介绍如何使用Docker部署一个MongoDB集群,具体如下: 2.6.5版本的MongoDB 有3个节点的副本集(Replica set) 身份验证 持久化数据到本地文件系统 首先要准备三个运行的Docker服务器,这意味着你

手把手教你实现Java权限管理系统 前端篇(十四):菜单功能实现

菜单功能实现 菜单接口封装 菜单管理是一个对菜单树结构的增删改查操作. 提供一个菜单查询接口,查询整颗菜单树形结构. http/modules/menu.js 添加 findMenuTree 接口. import axios from '../axios' /* * 菜单管理模块 */ // 保存 export const save = (data) => { return axios({ url: '/menu/save', method: 'post', data }) } // 删除 ex

手把手教你实现Java权限管理系统 前端篇(十六):系统备份还原

系统备份还原 在很多时候,我们需要系统数据进行备份还原.我们这里就使用MySql的备份还原命令实现系统备份还原的功能. 后台接口准备 系统备份还原是对数据库的备份还原,所以必须有后台接口的支持,我们准备好了接口,相关内容可以查阅后台篇. backup:系统备份创建接口,会在服务端_backup目录下生成以时间戳相关的备份目录,目录下有MySQL的备份SQL. delete:系统备份删除接口,传入页面查询得到的备份名称作为参数,删除服务端备份记录. findRecord:系统备份查询接口,查询所有

手把手教你实现Java权限管理系统 前端篇(十五):嵌套外部网页

嵌套外部网页 在有些时候,我们需要在我们的内容栏主区域显示外部网页.如查看服务端提供的SQL监控页面,接口文档页面等. 这个时候就要求我们的导航菜单能够解析嵌套网页的URL,并根据URL路由到相应的嵌套组件.接下来我们就讲解具体实现方案. 实现原理 1. 给菜单URL添加嵌套网页前缀,如果是服务端网页,除内部URL外,以iframe:前缀开头,外部网页直接以http[s]完整路径开头. 2. 路由导航守卫在动态加载路由时,检测到如果是外部嵌套网页,则绑定IFrame嵌套组件,最后用IFrame来

手把手教你打造一个Material Design风格的App(二)

--接上文. 3.1添加ToolBar(ActionBar) 添加ToolBar非常简单,你需要做的仅仅是为toolbar创建一个单独的layout布局,如果你想在哪里展示toolbar,只要在对应布局里将toolbar的布局文件include进来即可. (8)在res-->layout文件夹下创建一个名为toolbar.xml的文件,然后在里面添加一个android.support.v7.widget.Toolbar元素,这样就创建了一个具有特定高度和主题的toolbar. toolbar.x