因爲實驗室事情緣故,須要將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();
- }