神经网络和深度学习-第三周-吴恩达 Deep Learning Specialization 学习笔记

激活函数(Activation Function)

这一周前几节的课程主要是通过细致的讲解如何向量化参数及变量,以及如何通过矩阵进行运算,这里没有太多需要额外讲解的部分,因此从 3.6 节的激活函数开始写起。

前面做 Logistic 回归的时候激活函数选择的是 Sigmoid 函数:σ(z) = 1 / (1 + e-z),但这个函数并不是激活函数的唯一选择,这里提到另一个非常常用的函数是 tanh 函数,其数学表示为:

a = tanh(x) = (ex - e-x) / (ex + e-x)

def tanh(x):
    return (np.exp(x) - np.exp(-x)) / (np.exp(x) + np.exp(-x))

对应的函数图形为:

tanh(x)

从图形的相似之处也可以看出 tanh 函数是 sigmoid 函数图形沿 y 轴的一个平移和拉伸,其值域为 ( -1, 1) ,这一对称值域的好处就是如果使用这个函数作为激活函数,那么经过其激活的输出值就会呈现一个更加对称的分布(均值更接近于 0),也更加便于后续层的处理。

所以 Andrew 讲到依照他个人的经验,除了二元分类(y 取值 0 或 1)的输出层,在中间层他本人一般都用 tanh 函数而非 sigmoid 函数。sigmoid 函数和 tanh 函数的共同缺点是当自变量非常大的时候,函数过于平缓,这会减慢梯度下降法迭代的速度,并且会产生 梯度消失 现象,所以在中间层比 tanh 更好的一个函数则是 ReLU 函数(Rectified Linear Unit):a = max(0, z) ,其对应的函数图形如下图:

ReLU

这个函数的特点是当 z > 0 的时候其导数为 1,当 z < 0 的时候导数为 0,这里如果严格按照导数的定义,z = 0 这一点无导数,但实际计算中 z 能够真正取得 0 的概率非常小,因此可以不去计较。 并且,ReLU 是目前为止所接触的激活函数里最好的选择,如果在实践中不知道如何选取,可以先从 ReLU 开始。在这里 Andrew 也对为何除了实数域的输出层外激活函数基本都要是非线性函数做了证明,这一点一定要记住。

激活函数的导数

  • Sigmoid 函数:a = σ(z) = 1 / (1 + e-z) ,导数为 σ(z)' = σ(z)(1 - σ(z)) = a(1 - a)

  • tanh 函数 a = tanh(x) = (ex - e-x) / (ex + e-x),导数为 tanh(x)' = 1 - (tanh(x))2 = 1 - a2

  • ReLU 函数 a = max(0, z),当 z > 0 的时候其导数为 1,当 z < 0 的时候导数为 0

此处之所以将求导的结果展开成包含函数本身的形式是因为当 a 已知的前提下导数的计算更加方便。

参数的随机初始化

在之前的 Logistic 回归神经网络训练中,参数的初始值设置为 0,这种做法并不是总是可行的:因为这会使同一层里的多个隐藏单元里的函数都完全相同,使得多个单元的存在失去意义,因此比较合理的做法是在开始的时候采用随机的方式赋予初值。

2 hidden layer neural network

Numpy 中的 random 模块下包含很多可以生成随机采样的函数,在神经网络的构建中,也常被用于参数的随机初始化,通常的参数随机初始化所使用的函数主要有以下两个:

  • np.random.random()

  • np.random.normal()

默认的 np.random.random() 生成 [0.0, 1.0) 之间的一个随机数,如果想生成 [a, b) 之间的一个均匀分布,则可以采用 (b - a) * np.random.random((m, n, k)) + a 的形式,其中 (m, n, k) 是最终生成的随机数数组的形状。以下代码生成取值范围在 [-1, 1) 之间的 (3, 4) 的一个数组:

  • weights = 2 * np.random.random((3, 4)) - 1

进一步地,在使用 sigmoid 函数或 tanh 函数时,为了使得 w 的初值在随机初始化时取一个较小的值,使得激活函数的输入值处于函数梯度较大的位置,进而加快学习的速度,避免梯度消失现象,可以将 w 设置成服从方差为输入特征的数量的反比的正态分布的形式。

下面这个行代码给出服从均值为 0,标准差为 1 / n_features**.5 的正态分布的多个参数值:

weights = np.random.normal(scale=1 / n_features**.5, size=n_features)

代码中 scale 为正态分布的均方差,n_features 为一个输入中包含的特征的数量。

另外需要注意的是,在神经网络调试过程中,为了减少干扰,可以在随机初始化之前使用 np.random.seed(1) 来使得每一次运行程序产生的随机数都相同。

参考阅读

  1. 如果你和我一样好奇为何 e 总是频繁的出现在数学里,请阅读更加直观的理解e

  2. 激活函数和损失函数

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容