java版本lstm_LSTM java 实现
由于實驗室事情緣故,需要將Python寫的神經(jīng)網(wǎng)絡轉成Java版本的,但是python中的numpy等啥包也不知道在Java里面對應的是什么工具,所以索性直接尋找一個現(xiàn)成可用的Java神經(jīng)網(wǎng)絡框架,于是就找到了JOONE,JOONE是一個神經(jīng)網(wǎng)絡的開源框架,使用的是BP算法進行迭代計算參數(shù),使用起來比較方便也比較實用,下面介紹一下JOONE的一些使用方法。
JOONE需要使用一些外部的依賴包,這在官方網(wǎng)站上有,也可以在這里下載。將所需的包引入工程之后,就可以進行編碼實現(xiàn)了。
首先看下完整的程序,這個是上面那個超鏈接給出的程序,應該是官方給出的一個示例吧,因為好多文章都用這個,這其實是神經(jīng)網(wǎng)絡訓練一個異或計算器:
import?org.joone.engine.*;
import?org.joone.engine.learning.*;
import?org.joone.io.*;
import?org.joone.net.*;
/*
*
*?JOONE實現(xiàn)
*
*?*/
public?class?XOR_using_NeuralNet?implements?NeuralNetListener
{
private?NeuralNet?nnet?=?null;
private?MemoryInputSynapse?inputSynapse,?desiredOutputSynapse;
LinearLayer?input;
SigmoidLayer?hidden,?output;
boolean?singleThreadMode?=?true;
//?XOR?input
private?double[][]?inputArray?=?new?double[][]
{
{?0.0,?0.0?},
{?0.0,?1.0?},
{?1.0,?0.0?},
{?1.0,?1.0?}?};
//?XOR?desired?output
private?double[][]?desiredOutputArray?=?new?double[][]
{
{?0.0?},
{?1.0?},
{?1.0?},
{?0.0?}?};
/**
*?@param?args
*????????????the?command?line?arguments
*/
public?static?void?main(String?args[])
{
XOR_using_NeuralNet?xor?=?new?XOR_using_NeuralNet();
xor.initNeuralNet();
xor.train();
xor.interrogate();
}
/**
*?Method?declaration
*/
public?void?train()
{
//?set?the?inputs
inputSynapse.setInputArray(inputArray);
inputSynapse.setAdvancedColumnSelector("?1,2?");
//?set?the?desired?outputs
desiredOutputSynapse.setInputArray(desiredOutputArray);
desiredOutputSynapse.setAdvancedColumnSelector("?1?");
//?get?the?monitor?object?to?train?or?feed?forward
Monitor?monitor?=?nnet.getMonitor();
//?set?the?monitor?parameters
monitor.setLearningRate(0.8);
monitor.setMomentum(0.3);
monitor.setTrainingPatterns(inputArray.length);
monitor.setTotCicles(5000);
monitor.setLearning(true);
long?initms?=?System.currentTimeMillis();
//?Run?the?network?in?single-thread,?synchronized?mode
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?}?};
//?set?the?inputs
inputSynapse.setInputArray(inputArray);
inputSynapse.setAdvancedColumnSelector("?1,2?");
Monitor?monitor?=?nnet.getMonitor();
monitor.setTrainingPatterns(4);
monitor.setTotCicles(1);
monitor.setLearning(false);
MemoryOutputSynapse?memOut?=?new?MemoryOutputSynapse();
//?set?the?output?synapse?to?write?the?output?of?the?net
if?(nnet?!=?null)
{
nnet.addOutputSynapse(memOut);
System.out.println(nnet.check());
nnet.getMonitor().setSingleThreadMode(singleThreadMode);
nnet.go();
for?(int?i?=?0;?i?
{
double[]?pattern?=?memOut.getNextPattern();
System.out.println("?Output?pattern?#?"?+?(i?+?1)?+?"?=?"
+?pattern[0]);
}
System.out.println("?Interrogating?Finished?");
}
}
/**
*?Method?declaration
*/
protected?void?initNeuralNet()
{
//?First?create?the?three?layers
input?=?new?LinearLayer();
hidden?=?new?SigmoidLayer();
output?=?new?SigmoidLayer();
//?set?the?dimensions?of?the?layers
input.setRows(2);
hidden.setRows(3);
output.setRows(1);
input.setLayerName("?L.input?");
hidden.setLayerName("?L.hidden?");
output.setLayerName("?L.output?");
//?Now?create?the?two?Synapses
FullSynapse?synapse_IH?=?new?FullSynapse();?/*?input?->?hidden?conn.?*/
FullSynapse?synapse_HO?=?new?FullSynapse();?/*?hidden?->?output?conn.?*/
//?Connect?the?input?layer?whit?the?hidden?layer
input.addOutputSynapse(synapse_IH);
hidden.addInputSynapse(synapse_IH);
//?Connect?the?hidden?layer?whit?the?output?layer
hidden.addOutputSynapse(synapse_HO);
output.addInputSynapse(synapse_HO);
//?the?input?to?the?neural?net
inputSynapse?=?new?MemoryInputSynapse();
input.addInputSynapse(inputSynapse);
//?The?Trainer?and?its?desired?output
desiredOutputSynapse?=?new?MemoryInputSynapse();
TeachingSynapse?trainer?=?new?TeachingSynapse();
trainer.setDesired(desiredOutputSynapse);
//?Now?we?add?this?structure?to?a?NeuralNet?object
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);
}
}
現(xiàn)在我會逐步解釋上面的程序。
【1】?從main方法開始說起,首先第一步新建一個對象:
XOR_using_NeuralNet?xor?=?new?XOR_using_NeuralNet();
【2】然后初始化神經(jīng)網(wǎng)絡:
xor.initNeuralNet();
初始化神經(jīng)網(wǎng)絡的方法中:
//?First?create?the?three?layers
input?=?new?LinearLayer();
hidden?=?new?SigmoidLayer();
output?=?new?SigmoidLayer();
//?set?the?dimensions?of?the?layers
input.setRows(2);
hidden.setRows(3);
output.setRows(1);
input.setLayerName("?L.input?");
hidden.setLayerName("?L.hidden?");
output.setLayerName("?L.output?");
上面代碼解釋:
input=new LinearLayer()是新建一個輸入層,因為神經(jīng)網(wǎng)絡的輸入層并沒有訓練參數(shù),所以使用的是線性層;
hidden = new SigmoidLayer();這里是新建一個隱含層,使用sigmoid函數(shù)作為激勵函數(shù),當然你也可以選擇其他的激勵函數(shù),如softmax激勵函數(shù)
output則是新建一個輸出層
之后的三行代碼是建立輸入層、隱含層、輸出層的神經(jīng)元個數(shù),這里表示輸入層為2個神經(jīng)元,隱含層是3個神經(jīng)元,輸出層是1個神經(jīng)元
最后的三行代碼是給每個輸出層取一個名字。
//?Now?create?the?two?Synapses
FullSynapse?synapse_IH?=?new?FullSynapse();?/*?input?->?hidden?conn.?*/
FullSynapse?synapse_HO?=?new?FullSynapse();?/*?hidden?->?output?conn.?*/
//?Connect?the?input?layer?whit?the?hidden?layer
input.addOutputSynapse(synapse_IH);
hidden.addInputSynapse(synapse_IH);
//?Connect?the?hidden?layer?whit?the?output?layer
hidden.addOutputSynapse(synapse_HO);
output.addInputSynapse(synapse_HO);
上面代碼解釋:
上面代碼的主要作用是將三個層連接起來,synapse_IH用來連接輸入層和隱含層,synapse_HO用來連接隱含層和輸出層
//?the?input?to?the?neural?net
inputSynapse?=?new?MemoryInputSynapse();
input.addInputSynapse(inputSynapse);
//?The?Trainer?and?its?desired?output
desiredOutputSynapse?=?new?MemoryInputSynapse();
TeachingSynapse?trainer?=?new?TeachingSynapse();
trainer.setDesired(desiredOutputSynapse);
上面代碼解釋:
上面的代碼是在訓練的時候指定輸入層的數(shù)據(jù)和目的輸出的數(shù)據(jù),
inputSynapse = new MemoryInputSynapse();這里指的是使用了從內存中輸入數(shù)據(jù)的方法,指的是輸入層輸入數(shù)據(jù),當然還有從文件輸入的方法,這點在文章后面再談。同理,desiredOutputSynapse = new MemoryInputSynapse();也是從內存中輸入數(shù)據(jù),指的是從輸入層應該輸出的數(shù)據(jù)
//?Now?we?add?this?structure?to?a?NeuralNet?object
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);
上面代碼解釋:
這段代碼指的是將之前初始化的構件連接成一個神經(jīng)網(wǎng)絡,NeuralNet是JOONE提供的類,主要是連接各個神經(jīng)層,最后一個nnet.addNeuralNetListener(this);這個作用是對神經(jīng)網(wǎng)絡的訓練過程進行監(jiān)聽,因為這個類實現(xiàn)了NeuralNetListener這個接口,這個接口有一些方法,可以實現(xiàn)觀察神經(jīng)網(wǎng)絡訓練過程,有助于參數(shù)調整。
【3】然后我們來看一下train這個方法:
inputSynapse.setInputArray(inputArray);
inputSynapse.setAdvancedColumnSelector("?1,2?");
//?set?the?desired?outputs
desiredOutputSynapse.setInputArray(desiredOutputArray);
desiredOutputSynapse.setAdvancedColumnSelector("?1?");
上面代碼解釋:
inputSynapse.setInputArray(inputArray);這個方法是初始化輸入層數(shù)據(jù),也就是指定輸入層數(shù)據(jù)的內容,inputArray是程序中給定的二維數(shù)組,這也就是為什么之前初始化神經(jīng)網(wǎng)絡的時候使用的是MemoryInputSynapse,表示從內存中讀取數(shù)據(jù)
inputSynapse.setAdvancedColumnSelector(" 1,2 ");這個表示的是輸入層數(shù)據(jù)使用的是inputArray的前兩列數(shù)據(jù)。
desiredOutputSynapse這個也同理
Monitor?monitor?=?nnet.getMonitor();
//?set?the?monitor?parameters
monitor.setLearningRate(0.8);
monitor.setMomentum(0.3);
monitor.setTrainingPatterns(inputArray.length);
monitor.setTotCicles(5000);
monitor.setLearning(true);
上面代碼解釋:
這個monitor類也是JOONE框架提供的,主要是用來調節(jié)神經(jīng)網(wǎng)絡的參數(shù),monitor.setLearningRate(0.8);是用來設置神經(jīng)網(wǎng)絡訓練的步長參數(shù),步長越大,神經(jīng)網(wǎng)絡梯度下降的速度越快,monitor.setTrainingPatterns(inputArray.length);這個是設置神經(jīng)網(wǎng)絡的輸入層的訓練數(shù)據(jù)大小size,這里使用的是數(shù)組的長度;monitor.setTotCicles(5000);這個指的是設置迭代數(shù)目;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?}?};
//?set?the?inputs
inputSynapse.setInputArray(inputArray);
inputSynapse.setAdvancedColumnSelector("?1,2?");
Monitor?monitor?=?nnet.getMonitor();
monitor.setTrainingPatterns(4);
monitor.setTotCicles(1);
monitor.setLearning(false);
MemoryOutputSynapse?memOut?=?new?MemoryOutputSynapse();
//?set?the?output?synapse?to?write?the?output?of?the?net
if?(nnet?!=?null)
{
nnet.addOutputSynapse(memOut);
System.out.println(nnet.check());
nnet.getMonitor().setSingleThreadMode(singleThreadMode);
nnet.go();
for?(int?i?=?0;?i?
{
double[]?pattern?=?memOut.getNextPattern();
System.out.println("?Output?pattern?#?"?+?(i?+?1)?+?"?=?"
+?pattern[0]);
}
System.out.println("?Interrogating?Finished?");
}
這個方法相當于測試方法,這里的inputArray是測試數(shù)據(jù), 注意這里需要設置monitor.setLearning(false);,因為這不是訓練過程,并不需要學習,monitor.setTrainingPatterns(4);這個是指測試的數(shù)量,4表示有4個測試數(shù)據(jù)(雖然這里只有一個)。這里還給nnet添加了一個輸出層數(shù)據(jù)對象,這個對象mmOut是初始測試結果,注意到之前我們初始化神經(jīng)網(wǎng)絡的時候并沒有給輸出層指定數(shù)據(jù)對象,因為那個時候我們在訓練,而且指定了trainer作為目的輸出。
接下來就是輸出結果數(shù)據(jù)了,pattern的個數(shù)和輸出層的神經(jīng)元個數(shù)一樣大,這里輸出層神經(jīng)元的個數(shù)是1,所以pattern大小為1.
【5】我們看一下測試結果:
Output?pattern?#?1?=?0.018303527517809233
表示輸出結果為0.01,根據(jù)sigmoid函數(shù)特性,我們得到的輸出是0,和預期結果一致。如果輸出層神經(jīng)元個數(shù)大于1,那么輸出值將會有多個,因為輸出層結果是0|1離散值,所以我們取輸出最大的那個神經(jīng)元的輸出值取為1,其他為0
【6】最后我們來看一下神經(jīng)網(wǎng)絡訓練過程中的一些監(jiān)聽函數(shù):
cicleTerminated:每個循環(huán)結束后輸出的信息
errorChanged:神經(jīng)網(wǎng)絡錯誤率變化時候輸出的信息
netStarted:神經(jīng)網(wǎng)絡開始運行的時候輸出的信息
netStopped:神經(jīng)網(wǎng)絡停止的時候輸出的信息
【7】好了,JOONE基本上內容就是這些。還有一些額外東西需要說明:
1,從文件中讀取數(shù)據(jù)構建神經(jīng)網(wǎng)絡
2.如何保存訓練好的神經(jīng)網(wǎng)絡到文件夾中,只要測試的時候直接load到內存中就行,而不用每次都需要訓練。
【8】先看第一個問題:
從文件中讀取數(shù)據(jù):
文件的格式:
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);
我們看下文件是如何輸出數(shù)據(jù)的:
private?File?inputFile?=?new?File(Constants.TRAIN_WORD_VEC_PATH);
fileInputSynapse.setInputFile(inputFile);
fileInputSynapse.setFirstCol(2);//使用文件的第2列到第3列作為輸出層輸入
fileInputSynapse.setLastCol(3);
fileDisireOutputSynapse.setInputFile(inputFile);
fileDisireOutputSynapse.setFirstCol(1);//使用文件的第1列作為輸出數(shù)據(jù)
fileDisireOutputSynapse.setLastCol(1);
其余的代碼和上文的是一樣的。
【9】然后看第二個問題:
如何保存神經(jīng)網(wǎng)絡
其實很簡單,直接序列化nnet對象就行了,然后讀取該對象就是java的反序列化,這個就不多做介紹了,比較簡單。但是需要說明的是,保存神經(jīng)網(wǎng)絡的時機一定是在神經(jīng)網(wǎng)絡訓練完畢后,可以使用下面代碼:
public?void?netStopped(NeuralNetEvent?e)?{
Monitor?mon?=?(Monitor)?e.getSource();
try?{
if?(mon.isLearning())?{
saveModel(nnet);?//序列化對象
}
}?catch?(IOException?ee)?{
//?TODO?Auto-generated?catch?block
ee.printStackTrace();
}
總結
以上是生活随笔為你收集整理的java版本lstm_LSTM java 实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 限制参数类型_java定义受限
- 下一篇: 取消"成为"