第一个感知机的代码解读

  1. 第三遍看这个代码了,以前只是根据注释和命名规则去看每条代码是要干嘛,这次读了部分源码,找到了很多疑问的答案,也有还没解决的
  2. 先上看代码前的基础链接,基础不牢地动山摇啊
    java中Set的用法
    JAVA 8 :: 关键字
    java泛型中<?>和有什么区别?
    神经网络的 Delta 学习规则(learning rule)
    delta法则(梯度下降)
  3. 对代码的解读都在代码的注释里了(代码这里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页,就会明白了,而且表格十分详细
第一个感知机的代码解读
第一个感知机的代码解读