1 梯度检验
写了这么多篇笔记,我发现反向传播算法是目前来说,我了解到最复杂的算法。
为了完整地理解算法,我还复习了导数方面的功课,花了不少时间。
这个算法,有太多的细节,实现起来非常容易出错。
有时候,你使用梯度下降算法进行迭代,虽然每次代价函数 J(θ) 的值都在下降,但最终得到的结果却又有很大的误差,这很可能代码中依然存在一些问题。
对于这样的情况,应该怎么处理呢?
有一个叫梯度检验(Gradient Checking)的方法,可以减少这种错误的概率。
那么梯度检验到底是什么呢?
我们求的梯度,就是一个曲线的导数。
直观点,我们可以看看下图这样一个二阶模型:
![](https://i-blog.csdnimg.cn/blog_migrate/109cbb75c4e907f706137bc5e08d1a3d.jpeg)
在这里,导数就是图中红色切线的斜率。
以切点为中心,自变量分别增减一个ε,那么代价函数对应的就是 J(θ+ε) 和 J(θ-ε)。
两个点连接得到的绿色直线,其斜率应该和红色切线斜率是相近的:
![](https://i-blog.csdnimg.cn/blog_migrate/a68447e0bee681c80c4d58ffb6baf316.jpeg)
因为根据导数的定义:
![](https://i-blog.csdnimg.cn/blog_migrate/3e525b2a199ee94562fe381160658824.jpeg)
x 接近于0的时候,得到的 f'(x0) 就是导数。
所以,当 ε 无限小的时候,绿色的直线就是切线。
当然,如果 ε 取值太小的话,计算时可能会存在问题。
一般来说, ε = 10-4 会比较合适。
上面所说的是一个参数的情况,如果对于很多参数 θ1,θ2,θ3,…,θn ,我们就需要对每个 θ 进行计算:
![](https://i-blog.csdnimg.cn/blog_migrate/d7ddd66d599f390a40f96d838d344a61.jpeg)
最终我们求得的计算得到的结果,应该和算法求得的导数是相近的。
通过这样的方法进行检验,你对自己的模型就能更有信心。
由于这个计算,不是向量化的计算,而是数字化的计算,在迭代的过程中,会非常慢。
如果我们经过检查,确认实现的算法没有问题,那么记得要把梯度检验(Gradient Checking)关闭掉,否则我们会极大地影响计算速度。
2 随机初始化
我们对参数 θ 的初始化,一般来说,都是全部设置为 0 ,或者全部设置为 1 。
但这对于神经网络来说,会存在问题。
因为实现逻辑的关键,就在于参数的选择上。
同样节点的神经网络模型,参数的不一致,实现的效果就不一样,例如 y = x1 AND x2 :
![](https://i-blog.csdnimg.cn/blog_migrate/f76bb57d2951a89100f15b8b5d561393.jpeg)
和 y = x1 OR x2 :
![](https://i-blog.csdnimg.cn/blog_migrate/f7c4c093a2d4c26b15f46442a7070ac1.jpeg)
还记得前面《神经网络算法》为了实现前面将蓝圈和红叉区分开来,我们对于 y = x1 XNOR x2 的实现么?
![](https://i-blog.csdnimg.cn/blog_migrate/3f1aef01ed7a4fd564800d81268f6a5d.jpeg)
如果我们将初始的参数全部都设置为 0 或者 1 ,因为特征完全相同,参数也完全相同,对于下一个单元而言,每一个结果是一样的。
对于每一个训练数据,都有:
![](https://i-blog.csdnimg.cn/blog_migrate/9f211fae0ab4eea85eb778f28e7632e4.jpeg)
这样的话,得到的结果同步变化,多个神经单元其实只是相当于一个神经单元。
这不是我们想要的结果,所以我们需要设置随机的初始参数。