第一个感知机的代码解读
- 第三遍看这个代码了,以前只是根据注释和命名规则去看每条代码是要干嘛,这次读了部分源码,找到了很多疑问的答案,也有还没解决的
- 先上看代码前的基础链接,基础不牢地动山摇啊
java中Set的用法
JAVA 8 :: 关键字
java泛型中<?>和有什么区别?
神经网络的 Delta 学习规则(learning rule)
delta法则(梯度下降) - 对代码的解读都在代码的注释里了(代码这里MarkDown格式有点问题,但是没检查出为什么,百度说版本bug,凑合一下看吧)
`import java.util.Arrays;
import org.junit.Test;
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;
public class FirstPerceptron extends NeuralNetwork implements LearningEventListener {
/**
* 序列化
*/
private static final long serialVersionUID = 1L;
@Test
public void test() {
// 建立AND训练集,有两个输入和一个输出
DataSet trainAndSet = new DataSet(2, 1);
/**
* 这里add有重写的add(double[] input, double[] output)方法
* 但是为什么非要new一个DataSetRow,因为对应testNeuralNetwork()方法中的for循环遍历
* 但是实测即使写成trainAndSet.add(new double[] { 0, 0 }, new double[] { 0 });程序也正常输出了,但是还是写规范的好
*/
trainAndSet.add(new DataSetRow(new double[] { 0, 0 }, new double[] { 0 }));
trainAndSet.add(new DataSetRow(new double[] { 0, 1 }, new double[] { 0 }));
trainAndSet.add(new DataSetRow(new double[] { 1, 0 }, new double[] { 0 }));
trainAndSet.add(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.add(new DataSetRow(new double[] { 0, 0 }, new double[] { 0 }));
trainXorSet.add(new DataSetRow(new double[] { 0, 1 }, new double[] { 1 }));
trainXorSet.add(new DataSetRow(new double[] { 1, 0 }, new double[] { 1 }));
trainXorSet.add(new DataSetRow(new double[] { 1, 1 }, new double[] { 0 }));
// 建立NOT训练集
DataSet trainNotSet = new DataSet(1, 1);
trainNotSet.add(new DataSetRow(new double[] { 0 }, new double[] { 1 }));
trainNotSet.add(new DataSetRow(new double[] { 1 }, new double[] { 0 }));
// 建立感知机
/**
* 为什么要向上转型而不是直接定义成Perceptron呢
* 因为下边学习规则是NeuralNetwork父类里的私有方法,子类无法访问到,子类只是拥有,但是不能使用
*/
NeuralNetwork myPerceptron = new Perceptron(2, 1);
NeuralNetwork myPerceptron_not = new Perceptron(1, 1);
/**
* 这里的学习规则到底是什么呢?get到的到底是什么东西,也没人为指定学习规则是什么啊?
* 这里学习规则在new一个Perceptron的时候以及设置了setLearningRule(new BinaryDeltaRule())
* 所以get的时候并不是空
* BinaryDeltaRule规则是什么呢?看我发的链接
*/
LearningRule lr = myPerceptron.getLearningRule();
lr.addListener(this);
LearningRule lr_tjw = myPerceptron_not.getLearningRule();
/**
* 监听这里不好理解啊,就像Android的监听,知道干嘛的但是不知道内部如何实现的
*/
lr_tjw.addListener(this);
// 训练AND集
/**
* learn()这个抽象方法暂时没有找到在哪里去实现的
*/
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);
}
public static void testNeuralNetwork(NeuralNetwork<?> nnet, DataSet test) {
for (DataSetRow dataRow : test.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());
}
}
}
`
4. 问题1:在Perceptron类中提到了BinaryDeltaRule,百度了一下文章中有这么一段话尽管当训练样例线性可分时,感知器法则可以成功地找到一个权向量,但如果样例不是线性可分时它将不能收敛。因此,人们设计了另一个训练法则来克服这个不足,称为 delta 法则(delta rule)。如果训练样本不是线性可分的,那么 delta 法则会收敛到目标概念的最佳 近似。但是关于XOR的问题又有博客说XOR的结果表现为线性不可分,应采用更多层的网络,但是上文不是说到针对线性不可分时delta可以解决的吗?这个留待以后吧
5. 线性不可分是下一节的内容,包含XOR问题的解决
6. 问题2:同样是最初提到的基础链接文章里的一句话显然 delta规则要求转移函数可导这里的转移函数应该就是书上说的传输函数(transferFunction)吧,在Perceptron类中找到了下图代码,这些类型还不了解是什么
7. 问题3:关于代码中learn()抽象方法的实现也有必要再去源码里找找看
8. 代码的流程给我的感觉应该是创建训练集=>创建感知机=>学习规则是配置在感知机上的=>监听是设置在学习规则上个的=>感知机开始学习=>输出监听结果和最后的学习结果
9. 补充:针对传输函数问题,这个枚举中的函数,参考吴岸城老师的《神经网络与深度学习》第28页,就会明白了,而且表格十分详细