吴恩达深度学习课程笔记-6
02. 改善深度神经网络:超参数调试、正则化和优化
第三周 超参数调试、Batch Norm和程序框架
3.1 & 3.2 & 3.3 选择超参数
在训练神经网络时我们有很多超参数要进行调试:学习率、动量、Adam的参数、学习率衰减、batch size、隐藏层数、隐藏单元数等等。吴恩达老师建议较为重要,可以考虑调节的参数顺序为:学习率、动量( \( \beta 一般取0.9 \) )、batch size、隐藏单元数、其它。
在进行实验时,两个参数调试要在一个矩形数组内搜索最优组合,三个参数则要在一个立方数组内搜索最优组合,建议是不要采取网格搜索,而是随机搜索,进一步可以采取coarse to fine策略,及在前一步随机搜索的基础上,选择结果较好的参数组合附近进一步细化搜索。
注意在进行参数搜索时,并不总是在参数范围内随机均匀采样,有时候要选择合适的尺度。
像隐藏层数 \( 2, 3, 4 \)、隐藏单元数 \( 50, \dots, 100 \) 等可以进行均匀采样,而像学习率 \( \alpha = 0.0001, \dots, 1 \) 直接进行均匀采样就不太合适,这时候不再使用线性分度,而是对数分度:
\( \alpha \in [0.0001, 1] \)
\( a = \lg(0.0001) = -4, \quad, b = \lg(1) = 0 \),设 \( r \in [a, b] \)
对 \( r \) 在 \( [a,b] \) 范围内进行均匀采样,然后取对应的学习率 \( \alpha = 10^r \)
再比如,Momentum算法中指数加权平均的参数 \( \beta = 0.9, \dots, 0.999 \),也不能简单的均匀采样,你看 \( \beta = 0.9 \rightarrow 0.9005 \) 和 \( \beta = 0.999 \rightarrow 0.9995 \) 同样的范围显然对结果的影响不一样(平均的长度变化大),这时候也要做一定的尺度转换:
\( 1-\beta = 0.1, \dots, 0.001, \quad r \in [-3, -1] \)
然后对 \(r\) 均匀采样,\( \beta = 1 - 10^r \)
最后在调试超参数的时候,如果你的计算资源充足,可以同时运行不同设置的训练程序,这样不需要对某一个实验进行特殊照顾;如果计算资源不够,就只能一次跑一个实验,在整个过程中悉心照料。
3.4 & 3.5 & 3.6 & 3.7 Batch Norm
前面的课程提到归一化模型的输入会加速训练,对于深度神经网络来说,归一化隐藏层的变量也许会产生同样的效果,但 Batch Norm 不只是简单的进行归一化,如果只是归一化中间变量,那么学习到的各种各样分布就没用了。以神经网络的其中一层为例:
- 为了简化表示,将 \( z^{[l]}(i) \) 记为\( z^{(1)}, \dots, z^{(m)} \)
- \( \begin{gathered} \mu = \frac{1}{m} \sum_{i=1}^m z^{(i)} \end{gathered} \)
- \( \begin{gathered} \sigma^2 = \frac{1}{m} \sum_{i=1}^m (z^{(i)} - \mu)^2 \end{gathered} \)
- \( \begin{gathered} z^{(i)}_{\text{norm}} = \frac{z^{(i)} - \mu}{\sqrt{\sigma^2 + \epsilon}} \end{gathered} \)
- \( \begin{gathered} \tilde{z}^{(i)} = \gamma z^{(i)}_{\text{norm}} + \beta \end{gathered} \)
- 然后用 \( \tilde{z}^{(i)} \) 代替 \( z^{(i)} \)
有几点需要注意:
- 归一化的是激活函数的输入值,也有归一化激活函数输出值 \( a^{[l]} \) 的,吴老师推荐前者;
- 是基于一批样本对每个中间变量进行归一化,而不是基于一组中间变量进行归一化,经常和mini-batch结合起来使用;
- 直观理解,先归一化各神经元的输入,然后统一学习新的均值方差,\( \gamma,\beta \) 是可学习参数;
- 由于神经元的输入要先进行归一化,因此 \( z^{[l]} = W^{[l]}a^{[l-1]} + b^{[l]} \) 中的 \( b^{[l]} \) 其实可以直接省略掉;
- 参数的维度 \( \beta^{[l]}, \gamma^{[l]} \) 都是 \( (n^{[l]},1) \)
Batch Norm 为什么有用?
常见的说法是Batch Norm解决了variance shift问题,简单来说就是一个深层网络在学习时,每一层的参数都是在不停变化的,而如果前面几层网络的参数发生变化,后面的参数都要相应地变化,导致整个网络非常难学。Batch Norm减小了每一层的变化,相当于使每一层的参数都能进行一定程度的独立学习,因此训练速度加快。
此外,Batch Norm还起到一定的正则化作用,因为均值和方差都是在 mini-batch 上计算的,相当于向 \( z^{[l]} \) 中加入了噪声,和 dropout 类似。
在测试阶段,样本是一个一个进行处理的,没有条件计算均值和方差,因此,可以在训练的过程中计算mini-batch上均值方差的指数加权平均,以此作为测试阶段的均值方差。
3.8 & 3.9 Softmax回归
前面的问题都是针对二分类问题,输出只有一个,用来表示归属某一类“是”的概率。
对于多分类问题,可以采用 softmax 回归。Softmax 是相对于 hard max 而言的,hard max 就是取最大值,在输入向量最大值的位置输出1,其它位置输出0,softmax 的处理相对比较温和,接受一个向量输入,然后输出一个归一化的概率向量,值越大的位置对应越大的概率。
\( \begin{gathered} a^{[L]}_i = \frac{ \exp\{z^{[L]}_i\} }{ \sum_{i=1}^{n^{[L]}} \exp\{z^{[L]}_i\} } \end{gathered} \)
其中 \( n^{[L]} = C \) 即总的类别数,输出向量的每个值表示归属每个类的概率,加起来等于1. 当softmax回归中\( C=2 \)时,其实就是logistic回归。
在训练一个softmax分类器,也就是多分类器时:
损失函数 \( \begin{gathered} L(\hat{y}, y) = - \sum_{j=1}^{C}y_j log \hat{y}_j \end{gathered} \)
代价函数 \( \begin{gathered} J(W, b) = \frac{1}{m} \sum_{i=1}^{m} L(\hat{y}^{(i)}, y^{(i)}) \end{gathered} \)
由于多分类问题的标签形式如 \( y = \begin{bmatrix} 0 \\ 1 \\ \vdots \\ 0 \\ \end{bmatrix} \),所以最小化损失函数的直观理解就是选出正确类别并使其概率最大化。
梯度反向传播时,只要求出 \( dz^{[L]} = \hat{y} - y \) 即可。
3.10 & 3.11 深度学习框架
深度学习框架是一定要用的嘛,大家又不可能自己去造轮子。
现在最火的肯定是Pytorch,当然之前还是TensorFlow的天下。吴恩达这门课只介绍了TensorFlow的简单知识,重要的还是看文档写代码啦,毕竟TensorFlow都到2.0时代了,知识要不断更新啊。
import numpy as np import tensorflow as tf coefficients = np.array([[1], [-20], [25]]) w = tf.Variable([0], dtype=tf.float32)
# 将来进行赋值的量 x = tf.placeholder(tf.float32, [3,1])
# tensorflow 重载了计算符,可以求导 cost = x[0][0]*w**2 + x[1][0]*w + x[2][0] train = tf.train.GradientDescentOptimizer(0.01).minimize(cost) init = tf.global_variables_initializer()
with tf.Session() as session:
# 构建计算图 session.run(init) print(session.run(w))
for i in range(1000): session.run(train, feed_dict={x:coefficients}) print(session.run(w))