前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从零开始深度学习(十一):浅层神经网络

从零开始深度学习(十一):浅层神经网络

作者头像
我是管小亮
发布2020-04-20 16:16:42
5370
发布2020-04-20 16:16:42
举报

1、神经网络的梯度下降

我们这一次讲的浅层神经网络——单隐层神经网络,那么什么是浅层神经网络呢?浅层神经网络其实就是一个单隐层神经网络!!!会有 ,,, 这些个参数,还有个 表示输入特征的个数, 表示隐藏单元个数, 表示输出单元个数。

好了,这就是全部的符号参数了。那么具体参数的维度如下:

  • 矩阵 的维度就是(), 就是 维向量,可以写成 ,就是一个的列向量。
  • 矩阵 的维度就是(), 的维度和 一样,就是写成 。

此外,还有一个神经网络的 代价函数(Cost function),在之前的博客中讲过,不过是基于逻辑回归的。假设现在是在做二分类任务,那么代价函数等于:

训练参数之后,需要做梯度下降,然后进行参数更新,进而网络优化。所以,每次梯度下降都会循环,并且计算以下的值,也就是网络的输出:

  • 前向传播(forward propagation) 方程如下(之前讲过):

(1)

(2)

(3)

(4)

  • 反向传播(back propagation) 方程如下:

(1)

(2)

(3)

(4)

(5)

(6)

:反向传播的这些公式都是针对所有样本,进行过向量化的。

其中, 是 的矩阵;这里 np.sumpythonnumpy 命令,axis=1 表示水平相加求和,keepdims 是防止 python 输出那些古怪的秩数 ,加上这个确保阵矩阵 这个向量的输出的维度为 这样标准的形式。

编程操作看这个博客——神经网络编程基础。

  • 参数更新 方程如下:

(1)

(1)

其中 ,。

如果你跟着咱们系列下来的话,应该发现了到目前为止,计算的都和 Logistic 回归十分的相似,但当你开始 计算 反向传播的时候,你会发现,是需要计算隐藏层和输出层激活函数的导数的,在这里(二元分类)使用的是 sigmoid 函数。

如果你想认真的推导一遍反向传播,深入理解反向传播的话,欢迎看一下这个博客——深度学习100问之深入理解Back Propagation(反向传播),只要你跟着推导一遍,反向传播基本没什么大问题了。或者如果你觉得自己数学不太好的话,也可以和许多成功的深度学习从业者一样直接实现这个算法,不去了解其中的知识,这就是深度学习相较于机器学习最大的优势,我猜是的。

2、随机初始化

当训练神经网络时,权重随机初始化 是很重要的,简单来说,参数初始化 就是 决定梯度下降中的起始点。对于逻辑回归,把权重初始化为0当然也是可以的,但是对于一个神经网络,如果权重或者参数都初始化为0,那么梯度下降将不会起作用。你一定想问为什么?

慢慢来看,假设现在有两个输入特征,即 ,2个隐藏层单元,即 等于2,因此与一个隐藏层相关的矩阵,或者说 就是一个 2*2 的矩阵。我们再假设,在权重随机初始化的时候,把它初始化为 0 的 2*2 矩阵, 也等于 (把偏置项 初始化为0是合理的),但是把 初始化为 0 就有问题了。你会发现,如果按照这样进行参数初始化的话,总是发现 和 相等,这两个激活单元就会一样了!!!为什么会这样呢?

因为在反向传播时,两个隐含单元计算的是相同的函数,都是来自 的梯度变化,也就是 和 是一样的,由 可得 ,学习率 一样,梯度变化 一样,这样更新后的输出权值也会一模一样,由此 也等于 。

你可能觉得这也没啥啊,大惊小怪的,但是如果这样初始化这整个神经网络的话,那么这两个隐含单元就完全一样了,因此它们两个完全对称,也就意味着计算同样的函数,并且肯定的是最终经过每次训练的迭代,这两个隐含单元仍然是同一个函数,令人困惑。

由此可以推导,由于隐含单元计算的是同一个函数,所有的隐含单元对输出单元有同样的影响。一次迭代后,同样的表达式结果仍然是相同的,即 隐含单元仍是对称的。那么两次、三次、无论多少次迭代,不管网络训练多长时间,隐含单元仍然计算的是同样的函数。因此这种情况下超过1个隐含单元也没什么意义,因为计算的是同样的东西。当然无论是多大的网络,比如有3个特征,还是有相当多的隐含单元。

那么这个问题的解决方法是什么?其实很简单,就是 随机初始化参数

你应该这么做:把 设为 np.random.randn(2,2)(生成标准正态分布),通常再乘上一个较小的数,比如 0.01,这样就把它初始化为一个很小的随机数。然后 本来就没有这个对称的问题(叫做symmetry breaking problem),所以可以把 初始化为0,因为只要随机初始化 ,就有不同的隐含单元计算不同的东西,就不会有 symmetry breaking 问题了。相似地,对于 也随机初始化, 可以初始化为0。

举一个随机初始化的例子,比如:

你也许会疑惑,这个常数从哪里来?为什么是0.01,而不是100或者1000?

这是因为我们 通常倾向于初始化为很小的随机数

这么想,如果你用 tanh 或者 sigmoid 激活函数,或者说只在输出层有一个 sigmoid 激活函数,这种情况下,如果(数值)波动太大,在计算激活值时 ,如果 很大, 就会很大或者很小,这种情况下很可能停在 tanh / sigmoid 函数的平坦的地方(甚至在训练刚刚开始的时候),而这些平坦的地方对应导数函数图像中梯度很小的地方,也就意味着梯度下降会很慢(因为梯度小),因此学习也就很慢,这显然是不好的。

sigmoid 函数图像和导数函数图像:

tanh 函数图像和导数函数图像:

如果你没有使用 sigmoid / tanh 激活函数在整个的神经网络里,就不成问题。但如果做二分类并且输出单元是 Sigmoid 函数,那么你一定不会想让你的初始参数太大,因此这就是为什么乘上 0.01 或者其他一些小数是合理的尝试,对 也是一样。

关于浅层神经网络的代码,可以手撕一下,欢迎看一下这个博客——深度学习之手撕神经网络代码(基于numpy)。

未完待续。。。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-02-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员管小亮 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档