因为实验室事情缘故,须要将Python写的神经网络转成Java版本的,可是python中的numpy等啥包也不知道在Java里面对应的是什么工具,因此索性直接寻找一个现成可用的Java神经网络框架,因而就找到了JOONE,JOONE是一个神经网络的开源框架,使用的是BP算法进行迭代计算参数,使用起来比较方便也比较实用,下面介绍一下JOONE的一些使用方法。java
JOONE须要使用一些外部的依赖包,这在官方网站上有,也能够在这里下载。将所需的包引入工程以后,就能够进行编码实现了。python
首先看下完整的程序,这个是上面那个超连接给出的程序,应该是官方给出的一个示例吧,由于好多文章都用这个,这实际上是神经网络训练一个异或计算器:算法
- import org.joone.engine.*;
- import org.joone.engine.learning.*;
- import org.joone.io.*;
- import org.joone.net.*;
-
-
- public class XOR_using_NeuralNet implements NeuralNetListener
- {
- private NeuralNet nnet = null;
- private MemoryInputSynapse inputSynapse, desiredOutputSynapse;
- LinearLayer input;
- SigmoidLayer hidden, output;
- boolean singleThreadMode = true;
-
-
- private double[][] inputArray = new double[][]
- {
- { 0.0, 0.0 },
- { 0.0, 1.0 },
- { 1.0, 0.0 },
- { 1.0, 1.0 } };
-
-
- private double[][] desiredOutputArray = new double[][]
- {
- { 0.0 },
- { 1.0 },
- { 1.0 },
- { 0.0 } };
-
-
- public static void main(String args[])
- {
- XOR_using_NeuralNet xor = new XOR_using_NeuralNet();
-
- xor.initNeuralNet();
- xor.train();
- xor.interrogate();
- }
-
-
- public void train()
- {
-
-
- inputSynapse.setInputArray(inputArray);
- inputSynapse.setAdvancedColumnSelector(" 1,2 ");
-
- desiredOutputSynapse.setInputArray(desiredOutputArray);
- desiredOutputSynapse.setAdvancedColumnSelector(" 1 ");
-
-
- Monitor monitor = nnet.getMonitor();
-
-
- monitor.setLearningRate(0.8);
- monitor.setMomentum(0.3);
- monitor.setTrainingPatterns(inputArray.length);
- monitor.setTotCicles(5000);
- monitor.setLearning(true);
-
- long initms = System.currentTimeMillis();
-
- nnet.getMonitor().setSingleThreadMode(singleThreadMode);
- nnet.go(true);
- System.out.println(" Total time= "
- + (System.currentTimeMillis() - initms) + " ms ");
- }
-
- private void interrogate()
- {
-
- double[][] inputArray = new double[][]
- {
- { 1.0, 1.0 } };
-
- inputSynapse.setInputArray(inputArray);
- inputSynapse.setAdvancedColumnSelector(" 1,2 ");
- Monitor monitor = nnet.getMonitor();
- monitor.setTrainingPatterns(4);
- monitor.setTotCicles(1);
- monitor.setLearning(false);
- MemoryOutputSynapse memOut = new MemoryOutputSynapse();
-
-
- if (nnet != null)
- {
- nnet.addOutputSynapse(memOut);
- System.out.println(nnet.check());
- nnet.getMonitor().setSingleThreadMode(singleThreadMode);
- nnet.go();
-
- for (int i = 0; i < 4; i++)
- {
- double[] pattern = memOut.getNextPattern();
- System.out.println(" Output pattern # " + (i + 1) + " = "
- + pattern[0]);
- }
- System.out.println(" Interrogating Finished ");
- }
- }
-
-
- protected void initNeuralNet()
- {
-
-
- input = new LinearLayer();
- hidden = new SigmoidLayer();
- output = new SigmoidLayer();
-
-
- input.setRows(2);
- hidden.setRows(3);
- output.setRows(1);
-
- input.setLayerName(" L.input ");
- hidden.setLayerName(" L.hidden ");
- output.setLayerName(" L.output ");
-
-
- FullSynapse synapse_IH = new FullSynapse();
- FullSynapse synapse_HO = new FullSynapse();
-
-
- input.addOutputSynapse(synapse_IH);
- hidden.addInputSynapse(synapse_IH);
-
-
- hidden.addOutputSynapse(synapse_HO);
- output.addInputSynapse(synapse_HO);
-
-
- inputSynapse = new MemoryInputSynapse();
-
- input.addInputSynapse(inputSynapse);
-
-
- desiredOutputSynapse = new MemoryInputSynapse();
-
- TeachingSynapse trainer = new TeachingSynapse();
-
- trainer.setDesired(desiredOutputSynapse);
-
-
- nnet = new NeuralNet();
-
- nnet.addLayer(input, NeuralNet.INPUT_LAYER);
- nnet.addLayer(hidden, NeuralNet.HIDDEN_LAYER);
- nnet.addLayer(output, NeuralNet.OUTPUT_LAYER);
- nnet.setTeacher(trainer);
- output.addOutputSynapse(trainer);
- nnet.addNeuralNetListener(this);
- }
-
- public void cicleTerminated(NeuralNetEvent e)
- {
- }
-
- public void errorChanged(NeuralNetEvent e)
- {
- Monitor mon = (Monitor) e.getSource();
- if (mon.getCurrentCicle() % 100 == 0)
- System.out.println(" Epoch: "
- + (mon.getTotCicles() - mon.getCurrentCicle()) + " RMSE: "
- + mon.getGlobalError());
- }
-
- public void netStarted(NeuralNetEvent e)
- {
- Monitor mon = (Monitor) e.getSource();
- System.out.print(" Network started for ");
- if (mon.isLearning())
- System.out.println(" training. ");
- else
- System.out.println(" interrogation. ");
- }
-
- public void netStopped(NeuralNetEvent e)
- {
- Monitor mon = (Monitor) e.getSource();
- System.out.println(" Network stopped. Last RMSE= "
- + mon.getGlobalError());
- }
-
- public void netStoppedError(NeuralNetEvent e, String error)
- {
- System.out.println(" Network stopped due the following error: "
- + error);
- }
-
- }
如今我会逐步解释上面的程序。数组
【1】 从main方法开始提及,首先第一步新建一个对象:网络
- XOR_using_NeuralNet xor = new XOR_using_NeuralNet();
【2】而后初始化神经网络:数据结构
初始化神经网络的方法中:多线程
- input = new LinearLayer();
- hidden = new SigmoidLayer();
- output = new SigmoidLayer();
-
-
- input.setRows(2);
- hidden.setRows(3);
- output.setRows(1);
-
- input.setLayerName(" L.input ");
- hidden.setLayerName(" L.hidden ");
- output.setLayerName(" L.output ");
上面代码解释:框架
input=new LinearLayer()是新建一个输入层,由于神经网络的输入层并无训练参数,因此使用的是线性层;函数
hidden = new SigmoidLayer();这里是新建一个隐含层,使用sigmoid函数做为激励函数,固然你也能够选择其余的激励函数,如softmax激励函数工具
output则是新建一个输出层
以后的三行代码是创建输入层、隐含层、输出层的神经元个数,这里表示输入层为2个神经元,隐含层是3个神经元,输出层是1个神经元
最后的三行代码是给每一个输出层取一个名字。
- FullSynapse synapse_IH = new FullSynapse();
- FullSynapse synapse_HO = new FullSynapse();
-
-
- input.addOutputSynapse(synapse_IH);
- hidden.addInputSynapse(synapse_IH);
-
-
- hidden.addOutputSynapse(synapse_HO);
- output.addInputSynapse(synapse_HO);
上面代码解释:
上面代码的主要做用是将三个层链接起来,synapse_IH用来链接输入层和隐含层,synapse_HO用来链接隐含层和输出层
- inputSynapse = new MemoryInputSynapse();
-
- input.addInputSynapse(inputSynapse);
-
-
- desiredOutputSynapse = new MemoryInputSynapse();
-
- TeachingSynapse trainer = new TeachingSynapse();
-
- trainer.setDesired(desiredOutputSynapse);
上面代码解释:
上面的代码是在训练的时候指定输入层的数据和目的输出的数据,
inputSynapse = new MemoryInputSynapse();这里指的是使用了从内存中输入数据的方法,指的是输入层输入数据,固然还有从文件输入的方法,这点在文章后面再谈。同理,desiredOutputSynapse = new MemoryInputSynapse();也是从内存中输入数据,指的是从输入层应该输出的数据
- nnet = new NeuralNet();
-
- nnet.addLayer(input, NeuralNet.INPUT_LAYER);
- nnet.addLayer(hidden, NeuralNet.HIDDEN_LAYER);
- nnet.addLayer(output, NeuralNet.OUTPUT_LAYER);
- nnet.setTeacher(trainer);
- output.addOutputSynapse(trainer);
- nnet.addNeuralNetListener(this);
上面代码解释:
这段代码指的是将以前初始化的构件链接成一个神经网络,NeuralNet是JOONE提供的类,主要是链接各个神经层,最后一个nnet.addNeuralNetListener(this);这个做用是对神经网络的训练过程进行监听,由于这个类实现了NeuralNetListener这个接口,这个接口有一些方法,能够实现观察神经网络训练过程,有助于参数调整。
【3】而后咱们来看一下train这个方法:
- inputSynapse.setInputArray(inputArray);
- inputSynapse.setAdvancedColumnSelector(" 1,2 ");
-
- desiredOutputSynapse.setInputArray(desiredOutputArray);
- desiredOutputSynapse.setAdvancedColumnSelector(" 1 ");
上面代码解释:
inputSynapse.setInputArray(inputArray);这个方法是初始化输入层数据,也就是指定输入层数据的内容,inputArray是程序中给定的二维数组,这也就是为何以前初始化神经网络的时候使用的是MemoryInputSynapse,表示从内存中读取数据
inputSynapse.setAdvancedColumnSelector(" 1,2 ");这个表示的是输入层数据使用的是inputArray的前两列数据。
desiredOutputSynapse这个也同理
- Monitor monitor = nnet.getMonitor();
-
-
- monitor.setLearningRate(0.8);
- monitor.setMomentum(0.3);
- monitor.setTrainingPatterns(inputArray.length);
- monitor.setTotCicles(5000);
- <span style="line-height: 1.5;">monitor.setLearning(true);
上面代码解释:
这个monitor类也是JOONE框架提供的,主要是用来调节神经网络的参数,monitor.setLearningRate(0.8);是用来设置神经网络训练的步长参数,步长越大,神经网络梯度降低的速度越快,monitor.setTrainingPatterns(inputArray.length);这个是设置神经网络的输入层的训练数据大小size,这里使用的是数组的长度;monitor.setTotCicles(5000);这个指的是设置迭代数目;monitor.setLearning(true);这个true表示是在训练过程。
- nnet.getMonitor().setSingleThreadMode(singleThreadMode);
- nnet.go(true);
上面代码解释:
nnet.getMonitor().setSingleThreadMode(singleThreadMode);这个指的是是否是使用多线程,可是我不太清楚这里的多线程指的是什么意思
nnet.go(true)表示的是开始训练。
【4】最后来看一下interrogate方法
- double[][] inputArray = new double[][]
- {
- { 1.0, 1.0 } };
-
- inputSynapse.setInputArray(inputArray);
- inputSynapse.setAdvancedColumnSelector(" 1,2 ");
- Monitor monitor = nnet.getMonitor();
- monitor.setTrainingPatterns(4);
- monitor.setTotCicles(1);
- monitor.setLearning(false);
- MemoryOutputSynapse memOut = new MemoryOutputSynapse();
-
-
- if (nnet != null)
- {
- nnet.addOutputSynapse(memOut);
- System.out.println(nnet.check());
- nnet.getMonitor().setSingleThreadMode(singleThreadMode);
- nnet.go();
-
- for (int i = 0; i < 4; i++)
- {
- double[] pattern = memOut.getNextPattern();
- System.out.println(" Output pattern # " + (i + 1) + " = "
- + pattern[0]);
- }
- System.out.println(" Interrogating Finished ");
- }
这个方法至关于测试方法,这里的inputArray是测试数据, 注意这里须要设置monitor.setLearning(false);,由于这不是训练过程,并不须要学习,monitor.setTrainingPatterns(4);这个是指测试的数量,4表示有4个测试数据(虽然这里只有一个)。这里还给nnet添加了一个输出层数据对象,这个对象mmOut是初始测试结果,注意到以前咱们初始化神经网络的时候并无给输出层指定数据对象,由于那个时候咱们在训练,并且指定了trainer做为目的输出。
接下来就是输出结果数据了,pattern的个数和输出层的神经元个数同样大,这里输出层神经元的个数是1,因此pattern大小为1.
【5】咱们看一下测试结果:
- Output pattern # 1 = 0.018303527517809233
表示输出结果为0.01,根据sigmoid函数特性,咱们获得的输出是0,和预期结果一致。若是输出层神经元个数大于1,那么输出值将会有多个,由于输出层结果是0|1离散值,因此咱们取输出最大的那个神经元的输出值取为1,其余为0
【6】最后咱们来看一下神经网络训练过程当中的一些监听函数:
cicleTerminated:每一个循环结束后输出的信息
errorChanged:神经网络错误率变化时候输出的信息
netStarted:神经网络开始运行的时候输出的信息
netStopped:神经网络中止的时候输出的信息
【7】好了,JOONE基本上内容就是这些。还有一些额外东西须要说明:
1,从文件中读取数据构建神经网络
2.如何保存训练好的神经网络到文件夹中,只要测试的时候直接load到内存中就行,而不用每次都须要训练。
【8】先看第一个问题:
从文件中读取数据:
文件的格式:
0;0;0
1;0;1
1;1;0
0;1;1
中间使用分号隔开,使用方法以下,也就是把上文的MemoryInputSynapse换成FileInputSynapse便可。
- fileInputSynapse = new FileInputSynapse();
- input.addInputSynapse(fileInputSynapse);
- fileDisireOutputSynapse = new FileInputSynapse();
- TeachingSynapse trainer = new TeachingSynapse();
- trainer.setDesired(fileDisireOutputSynapse);
咱们看下文件是如何输出数据的:
- private File inputFile = new File(Constants.TRAIN_WORD_VEC_PATH);
- fileInputSynapse.setInputFile(inputFile);
- fileInputSynapse.setFirstCol(2);
- fileInputSynapse.setLastCol(3);
- fileDisireOutputSynapse.setInputFile(inputFile);
- fileDisireOutputSynapse.setFirstCol(1);
- fileDisireOutputSynapse.setLastCol(1);
其他的代码和上文的是同样的。
【9】而后看第二个问题:
如何保存神经网络
其实很简单,直接序列化nnet对象就好了,而后读取该对象就是java的反序列化,这个就很少作介绍了,比较简单。可是须要说明的是,保存神经网络的时机必定是在神经网络训练完毕后,可使用下面代码:
- public void netStopped(NeuralNetEvent e) {
- Monitor mon = (Monitor) e.getSource();
- try {
- if (mon.isLearning()) {
- saveModel(nnet);
- }
- } catch (IOException ee) {
-
- ee.printStackTrace();
- }