1.代码参照吴岸城老师所著的《神经网络与深度学习》第二章内容(与书中代码稍有不同,如果代码报错,请参考另一篇文章[对代码实现第一个感知机心得的再一次解读])
2.我也是写这篇博客时才发现书的前言有吴老师的GitHub,里边有
Book_Neural_network_and_deep_learning仓库,说是有部分书中代码,不过我现在怀疑买了本盗版书,书中链接都是错的
3.书中代码截图都是某个方法的片段,所以照着敲的时候会碰上各种缺失的方法或者this关键字的异常,可以百度去比较一下,这里我将提供我的可运行代码,并写一写自己从建Demo到运行中的一些体会
4.可运行代码如下:
package bzu.tjw;
import org.neuroph.core.Layer;
import org.neuroph.core.NeuralNetwork;
import org.neuroph.core.data.DataSet;
import org.neuroph.core.data.DataSetRow;
import org.neuroph.core.events.LearningEvent;
import org.neuroph.core.events.LearningEventListener;
import org.neuroph.core.learning.LearningRule;
import org.neuroph.core.learning.SupervisedLearning;
import org.neuroph.nnet.Perceptron;
import org.neuroph.nnet.comp.neuron.ThresholdNeuron;
import org.neuroph.nnet.learning.BinaryDeltaRule;
import org.neuroph.util.*;
import java.util.Arrays;
public class FirstPerceptron extends NeuralNetwork {
public static void main(String[] args){
//建立AND训练集,有两个输入和一个输出
DataSet trainAndSet = new DataSet(2, 1);
trainAndSet.addRow(new DataSetRow(new double[]{0, 0}, new double[]{0}));
trainAndSet.addRow(new DataSetRow(new double[]{0, 1}, new double[]{0}));
trainAndSet.addRow(new DataSetRow(new double[]{1, 0}, new double[]{0}));
trainAndSet.addRow(new DataSetRow(new double[]{1, 1}, new double[]{1}));
// 建立OR训练集
DataSet trainOrSet = new DataSet(2, 1);
trainOrSet.add(new DataSetRow(new double[]{0, 0}, new double[]{0}));
trainOrSet.add(new DataSetRow(new double[]{0, 1}, new double[]{1}));
trainOrSet.add(new DataSetRow(new double[]{1, 0}, new double[]{1}));
trainOrSet.add(new DataSetRow(new double[]{1, 1}, new double[]{1}));
// 建立XOR训练集
DataSet trainXorSet = new DataSet(2, 1);
trainXorSet.addRow(new DataSetRow(new double[]{0, 0}, new double[]{0}));
trainXorSet.addRow(new DataSetRow(new double[]{0, 1}, new double[]{1}));
trainXorSet.addRow(new DataSetRow(new double[]{1, 0}, new double[]{1}));
trainXorSet.addRow(new DataSetRow(new double[]{1, 1}, new double[]{0}));
// 建立感知机
NeuralNetwork myPerceptron = new Perceptron(2, 1);
LearningRule lr = myPerceptron.getLearningRule();
lr.addListener(new LearningEventListener() {
@Override
public void handleLearningEvent(LearningEvent event) {
SupervisedLearning bp = (SupervisedLearning)event.getSource();
if(event.getEventType() != LearningEvent.Type.LEARNING_STOPPED){
System.out.println(bp.getCurrentIteration()+ ". iteration : "+ bp.getTotalNetworkError());
}
}
});
// 训练AND集
myPerceptron.learn(trainAndSet);
System.out.println("测试感知机AND集训练结果:");
testNeuralNetwork(myPerceptron, trainAndSet);
// 训练OR集
// myPerceptron.learn(trainOrSet);
// System.out.println("测试感知机Or集训练结果:");
// testNeuralNetwork(myPerceptron, trainOrSet);
// 训练XOR集
// 由于XOR输入输出情况线性不可分,将无法完成训练
// myPerceptron.learn(trainXorSet);
// System.out.println("测试感知机Xor集训练结果:");
// testNeuralNetwork(myPerceptron, trainXorSet);
}
/**
* 感知机代码实现
*/
private void createNetwork(int inputNeuronsCount, int outputNeuronsCount, TransferFunctionType transferFunctionType){
//设置神经网络类型,这里我们将类型设置为感知器
this.setNetworkType(NeuralNetworkType.PERCEPTRON);
//初始化神经元输入刺激设置
NeuronProperties inputNeuronProperties = new NeuronProperties();
inputNeuronProperties.setProperty("transferFunction",TransferFunctionType.LINEAR);
//创建输入刺激
Layer inputLayer = LayerFactory.createLayer(inputNeuronsCount,inputNeuronProperties);
this.addLayer(inputLayer);
NeuronProperties outputNeuronProperties = new NeuronProperties();
outputNeuronProperties.setProperty("neuronType", ThresholdNeuron.class);
outputNeuronProperties.setProperty("thresh",new Double(Math.abs(Math.random())));
outputNeuronProperties.setProperty("transferFunction",transferFunctionType);
//为TransferFunctionType.LINEAR传输函数设置倾斜属性
outputNeuronProperties.setProperty("transferFunction.slop",new Double(1));
//create一个神经元的输出
Layer outputLayer = LayerFactory.createLayer(outputNeuronsCount,outputNeuronProperties);
this.addLayer(outputLayer);
//在输入和输出层中建立全连接
ConnectionFactory.fullConnect(inputLayer,outputLayer);
//为神经网络设置默认输入输出
NeuralNetworkFactory.setDefaultIO(this);
this.setLearningRule(new BinaryDeltaRule());
}
public static void testNeuralNetwork(NeuralNetwork nnet, DataSet tset) {
for (DataSetRow dataRow : tset.getRows()) {
nnet.setInput(dataRow.getInput());
nnet.calculate();
double[ ] networkOutput = nnet.getOutput();
System.out.print("Input: " + Arrays.toString(dataRow.getInput()) );
System.out.println(" Output: " + Arrays.toString(networkOutput) );
}
}
}
5.这段代码将课本的AND、OR、XOR学习放到了一起,为了结果明显建议运行时单独对某一个规则进行测试,可以看到经过四条训练数据的训练,感知机可以无误地对AND和OR运算训练集中的输入做出响应但是XOR运算的学习却无法完成,迭代不止。这与运算本身性质有关,XOR的结果表现为线性不可分,用简单的感知机模型是无法完成训练的,当采用更多层的网络。
6.项目构建时,直接把所有neuroph-2.94的包和doc导入就好了,13M也不大,以避免项目中可能出现的包缺失
7.如果只导入neuroph-2.94相关的包,项目运行时会报错
Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory at org.ap
还需要导入需要 log4j 和slf4 两个JAR包
自己导包的时候如果报
java.lang.NoClassDefFoundError: org/apache/log4j/Level
可能是多导入了slf4j-log4j12-1.7.26.jar包
8.至于里边涉及到的方法,字面意思比较容易理解,去官方的开发手册查了查,但是介绍写的都很简洁,一般就只是一句话的描述,跟踪到源码里脑子就直接炸了
9.实现后的心得感悟:根据书中的代码注释和讲解,感觉代码的思路比较清晰,但是对代码为什么要这么写,结果又是怎么得出的就比较迷惑了,而且既然是逻辑运算,举一反三就仿照着加了个NOT试了试
改后的代码如下,采用Junit测试
package bzu.tjw;
import org.junit.Test;
import org.neuroph.core.Layer;
import org.neuroph.core.NeuralNetwork;
import org.neuroph.core.data.DataSet;
import org.neuroph.core.data.DataSetRow;
import org.neuroph.core.events.LearningEvent;
import org.neuroph.core.events.LearningEventListener;
import org.neuroph.core.learning.LearningRule;
import org.neuroph.core.learning.SupervisedLearning;
import org.neuroph.nnet.Perceptron;
import org.neuroph.nnet.comp.neuron.ThresholdNeuron;
import org.neuroph.nnet.learning.BinaryDeltaRule;
import org.neuroph.util.*;
import java.util.Arrays;
public class FirstPerceptron extends NeuralNetwork implements LearningEventListener{
@Test
public void test(){
//建立AND训练集,有两个输入和一个输出
DataSet trainAndSet = new DataSet(2, 1);
trainAndSet.addRow(new DataSetRow(new double[]{0, 0}, new double[]{0}));
trainAndSet.addRow(new DataSetRow(new double[]{0, 1}, new double[]{0}));
trainAndSet.addRow(new DataSetRow(new double[]{1, 0}, new double[]{0}));
trainAndSet.addRow(new DataSetRow(new double[]{1, 1}, new double[]{1}));
// 建立OR训练集
DataSet trainOrSet = new DataSet(2, 1);
trainOrSet.add(new DataSetRow(new double[]{0, 0}, new double[]{0}));
trainOrSet.add(new DataSetRow(new double[]{0, 1}, new double[]{1}));
trainOrSet.add(new DataSetRow(new double[]{1, 0}, new double[]{1}));
trainOrSet.add(new DataSetRow(new double[]{1, 1}, new double[]{1}));
// 建立XOR训练集
DataSet trainXorSet = new DataSet(2, 1);
trainXorSet.addRow(new DataSetRow(new double[]{0, 0}, new double[]{0}));
trainXorSet.addRow(new DataSetRow(new double[]{0, 1}, new double[]{1}));
trainXorSet.addRow(new DataSetRow(new double[]{1, 0}, new double[]{1}));
trainXorSet.addRow(new DataSetRow(new double[]{1, 1}, new double[]{0}));
// 建立NOT训练集
DataSet trainNotSet = new DataSet(1, 1);
trainNotSet.addRow(new DataSetRow(new double[]{0}, new double[]{1}));
trainNotSet.addRow(new DataSetRow(new double[]{1}, new double[]{0}));
// 建立感知机
NeuralNetwork myPerceptron = new Perceptron(2, 1);
NeuralNetwork myPerceptron_not = new Perceptron(1, 1);
LearningRule lr = myPerceptron.getLearningRule();
lr.addListener(this);
LearningRule lr_tjw = myPerceptron_not.getLearningRule();
lr_tjw.addListener(this);
// 训练AND集
// myPerceptron.learn(trainAndSet);
// System.out.println("测试感知机AND集训练结果:");
// testNeuralNetwork(myPerceptron, trainAndSet);
// 训练OR集
// myPerceptron.learn(trainOrSet);
// System.out.println("测试感知机Or集训练结果:");
// testNeuralNetwork(myPerceptron, trainOrSet);
// 训练XOR集
// 由于XOR输入输出情况线性不可分,将无法完成训练
// myPerceptron.learn(trainXorSet);
// System.out.println("测试感知机Xor集训练结果:");
// testNeuralNetwork(myPerceptron, trainXorSet);
// 训练NOT集
myPerceptron_not.learn(trainNotSet);
System.out.println("测试感知机Not集训练结果:");
testNeuralNetwork(myPerceptron_not, trainNotSet);
}
/**
* 感知机代码实现
*/
private void createNetwork(int inputNeuronsCount, int outputNeuronsCount, TransferFunctionType transferFunctionType){
//设置神经网络类型,这里我们将类型设置为感知器
this.setNetworkType(NeuralNetworkType.PERCEPTRON);
//初始化神经元输入刺激设置
NeuronProperties inputNeuronProperties = new NeuronProperties();
inputNeuronProperties.setProperty("transferFunction",TransferFunctionType.LINEAR);
//创建输入刺激
Layer inputLayer = LayerFactory.createLayer(inputNeuronsCount,inputNeuronProperties);
this.addLayer(inputLayer);
NeuronProperties outputNeuronProperties = new NeuronProperties();
outputNeuronProperties.setProperty("neuronType", ThresholdNeuron.class);
outputNeuronProperties.setProperty("thresh",new Double(Math.abs(Math.random())));
outputNeuronProperties.setProperty("transferFunction",transferFunctionType);
//为TransferFunctionType.LINEAR传输函数设置倾斜属性
outputNeuronProperties.setProperty("transferFunction.slop",new Double(1));
//create一个神经元的输出
Layer outputLayer = LayerFactory.createLayer(outputNeuronsCount,outputNeuronProperties);
this.addLayer(outputLayer);
//在输入和输出层中建立全连接
ConnectionFactory.fullConnect(inputLayer,outputLayer);
//为神经网络设置默认输入输出
NeuralNetworkFactory.setDefaultIO(this);
this.setLearningRule(new BinaryDeltaRule());
}
public static void testNeuralNetwork(NeuralNetwork nnet, DataSet tset) {
for (DataSetRow dataRow : tset.getRows()) {
nnet.setInput(dataRow.getInput());
nnet.calculate();
double[ ] networkOutput = nnet.getOutput();
System.out.print("Input: " + Arrays.toString(dataRow.getInput()) );
System.out.println(" Output: " + Arrays.toString(networkOutput) );
}
}
@Override
public void handleLearningEvent(LearningEvent event) {
SupervisedLearning bp = (SupervisedLearning)event.getSource();
if(event.getEventType() != LearningEvent.Type.LEARNING_STOPPED){
System.out.println(bp.getCurrentIteration()+ ". iteration : "+ bp.getTotalNetworkError());
}
}
}
10.文章参照:
Neuroph训练简单逻辑运算感知机
另附:
Neuroph多层感知机训练XOR