BP(backward propogation)神经网络是广泛使用的一种神经网络。要我说,神经网络就是一种高端的插值技术。相应比较好的实现教程有:
- Matlab工具箱版本(使用简便,但是不适用于理解原理):漫谈ANN(2):BP神经网络;
- Matlab原理实现(根据原理实现的版本,未使用神经网络工具箱):简单易学的机器学习算法——神经网络之BP神经网络;
- C++原理实现(根据原理实现):BP神经网络原理及C++实战
三篇文章,第2、3篇适用于理解原理。详细的数学推导已经在里面了,就不赘述了。下面记录一下我在实现过程中碰到的一些错误与总结:
神经网络的过程。见下图:
简单说,就是我有一堆已知的输入向量(每个向量可能有多维),每次读取一个向量(可能会有多维度),每个维度成为上图的输入层中一个输入节点。
每个维度的数值,将自己的一部分(按照权值分配、自己选用的传递函数)送给隐藏层。写程序的时候很容易问:第一次的权值怎么办呢?其实(-1,1)随机化就好了,后续会逐渐修正。
这样一来,隐藏层每个节点同样有了属于自己的数值,同样道理,传给输出节点。其中每个输出节点的每个值对应输出向量的一个维度。
至此,我们完成了一次 forward pass 过程,方向是:输入层 => 输出层。
熟悉神经网络的同学肯定知道,神经网络使用时有“训练”、“测试”两部分。我们现在考虑训练过程。每次 forward pass 过程之后,输出层的值与真实值之间,存在一个差,这个差记为误差
error。 此时我们根据公式(参考上文中的链接教程,使用不同的传递函数),将误差作为参数传给隐藏层节点。
这些误差有什么用呢?还记得我们各个层之间的随机化的权值么?就是用来修正这个权值的,具体推导过程见上文中的链接教程。同理,修改输入层与隐藏层之间的权值,我们的视角到达输入层。
至此,我们完成了一次 backward pass 过程,方向是:输入层 <= 输出层。
第一个样本的一套做完了,即 forward pass + backward pass。接下来呢?再做第二个样本的一套,并把这层的误差与上一层的误差相加。
然后,第三个样本的一套,加误差;……第N个样本的一套,加误差。这时候,所有样本都过了一遍,看看误差和是否小于一个可以接受的值(这个值根据实际情况,自由设定)时。如果不小于,则进行下一套大保健,即:
清零误差;第一个样本的一套,加误差;第二个样本的一套,加误差;……第N个样本的一套,加误差。误差和误差和是否小于一个可以接受的值。如果不小于,则进行下一套大保健。
误差和达到要求了,妥了,不训练了。此时再用测试数据,输入一个测试样本,将各个维度的数值输入到节点,进行一次 forward pass(测试样本只有输入,无法校对输出的正确性与否)。得到的输出值就是我们的预测值。
神经网络神奇之处在这么几个地方:
- 传递函数的选取:把上一层各个节点的数值按照权重汇聚之后,作为传递函数的输入,得到函数输出值就是当前层当前节点的数值。这个传递函数,是线性函数?二次函数?还是一元函数?二元函数?都有着不同的性质,适用不同的场景。
- 隐藏层节点数:全靠蒙啊……选多了算着慢,选少了不能充分传递消息。
- 隐藏层层数:虽然图例中说的是只有一层的情况,但不是说不可以多层。多层自有多层的好处,比如 Deep Learning 中,一般就是七层或者以上。
既然这么通俗易懂,为什么还是在实现中会出现错误呢?下面我说说几个我遇到的错误:
- 输入节点,究竟是每个向量的维度一个节点?还是一个样本向量一个节点?尤其是上图中的情况,特别容易造成误解。以为一个样本对应一个输出节点,其实是错误的。对于一个多维度的输入数据,例如下图:
- 对待误差究竟是每次训练一个样本,调整到最优,是错误的。误差应该站在整个学习样本的观点来看,需要整个训练样本的累积误差,然后所有样本的多次循环。
最后给大家推荐三篇 IBM DeveloperWorks 上面的相关原理性文章,讲的很清楚: