吴恩达改善深层神经网络章节笔记(三)——参数调试和Batch Normalize
- 1. 调试处理 (Tuning Process)
- 2. 为超参数选择合适的范围 (Using an Appropriate Scale to Pick Hyperparameters)
- 3. 超参数训练实战:Pandas vs. Caviar (Hyperparameters Tuning in Practice: Pandas vs. Caviar)
- 4. 归一化网络的激活函数 (Normalizing Activations in a Network)
- 5. 将Batch归一化拟合进神经网络 (Fitting Batch Norm into a Neural Network)
- 6. Batch Norm 为什么起作用? (Why does Batch Norm work?)
- 7. 测试时的 Batch Norm (Batch Norm at Test Time)
- 8. Softmax 回归 (Softmax Regression)
- 9. 训练一个 Softmax 分类器 (Training a Softmax Classifier)
视频课程链接:
https://www.bilibili.com/video/BV1FT4y1E74V?
笔记参考链接:
https://blog.csdn.net/weixin_36815313/article/details/105728919
1. 调试处理 (Tuning Process)
神经网络的改变会涉及到许多不同超参数的设置。超参数调试的优先级如下:
- 第一优先级
- 学习率 α \alpha α
- 第二优先级
- Momentum参数 β \beta β(一般取0.9)
- mini-batch的大小
- 隐藏单元的数量
- 第三优先级
- 神经网络的层数
- 学习率衰减系数
- 第四优先级
- Adam算法参数 β 1 \beta1 β1(一般取0.9)
- Adam算法参数 β 2 \beta2 β2(一般取0.999)
- 偏置项 ϵ \epsilon ϵ(一般取 1 0 − 8 10^{-8} 10−8)
在早一代的机器学习算法中,如果你有两个超参数,这里称之为超参1,超参2,常见的做法是在网格中取样点,然后系统地研究这些数值(如上图)。
在深度学习领域,我们常用的方法是随机选择点(如上图),你可以选择同等数量的点,接着用这些随机取的点试验超参数的效果。之所以这么做是因为,对于你要解决的问题而言,你很难提前知道哪个超参数最重要。
实际上,你搜索的超参数可能不止两个。假如你有三个超参数,这时搜索的不是一个方格,而是一个立方体(如上图),超参3代表第三维,接着在三维立方体中取值,你会试验更多的值。
当你给超参数取值时,另一个惯例是采用由粗糙到精细的策略。
以二维的超参数为例,对超参数进行取值,也许你会发现效果最好的某个点,也许这个点周围的其他一些点效果也很好,那在接下来要做的是放大这块小区域(小蓝色方框),然后在其中更密集得取值或随机取值,聚集更多的资源,在这个蓝色的方格中搜索。如果你怀疑这些超参数在这个区域的最优结果,那在整个的方格中进行粗略搜索后,你会知道接下来应该聚焦到更小的方格中。在更小的方格中,你可以更密集地取点,因此这种从粗到细的搜索也经常使用。
通过试验超参数的不同取值,你可以选择对训练集目标而言的最优值,或对于开发集而言的最优值,或在超参搜索过程中你最想优化的东西。
2. 为超参数选择合适的范围 (Using an Appropriate Scale to Pick Hyperparameters)
在超参数范围中,随机取值可以提升你的搜索效率,但随机取值并不是在有效范围内的随机均匀取值,而是选择合适的标尺,用于探究这些超参数。
2.1 线性轴标度
假设你要选取隐藏单元的数量
n
[
l
]
n^{[l]}
n[l],超参数的取值范围是
[
50
,
100
]
[50,100]
[50,100]。在这种情况下,做一条从50-100的数轴,对其随机取点,这是一个搜索特定超参数较为直观的方式。
如果你要选取神经网络的层数,假设层数为2到4中的某个值,因此可以按顺序2、3、4随机均匀取样,或者应用网格搜索,你会觉得2、3、4这三个数值是合理的。
这是几个在你考虑范围内随机均匀取值的例子,这些取值还比较合理,但对某些超参数而言不适用。
2.2 对数轴标度
假设你在搜索超参数
α
\alpha
α(学习率),其取值范围可能在
[
0.0001
,
1
]
[0.0001,1]
[0.0001,1]内。如果画一条从0.0001到1的数轴,沿其随机均匀取值,那90%的数值将会落在0.1到1之间,即在0.1到1之间,应用了90%的资源,而在0.0001到0.1之间,只有10%的搜索资源。
因此这里使用对数标尺搜索超参数会更合理,而不使用线性轴,分别依次取0.0001,0.001,0.01,0.1,1,在对数轴上均匀随机取点,这样,在0.0001到0.001之间,就会有更多的搜索资源可用。
对数标尺转化在Python中的实现方法:
r = -4 * np.random.rand()
a = np.power(10,r)
np.random.rand()的作用是返回一个取值范围在
[
0
,
1
)
[0,1)
[0,1)内的随机样本值,从而可以得到
r
∈
[
−
4
,
0
)
r\in[-4,0)
r∈[−4,0),那么
a
∈
[
1
0
−
4
,
1
0
0
)
a\in[10^{-4},10^0)
a∈[10−4,100),即
a
∈
[
0.0001
,
1
)
a\in[0.0001,1)
a∈[0.0001,1)。
如果你在
1
0
a
10^a
10a和
1
0
b
10^b
10b之间取值,在此例中,
1
0
a
=
0.0001
10^a=0.0001
10a=0.0001,因此可以通过
a
=
l
g
0.0001
a=lg{0.0001}
a=lg0.0001算出
a
a
a的值,即-4,同理可以算出
b
b
b的值
b
=
l
g
1
b=lg{1}
b=lg1,即0。你要做的就是在
[
a
,
b
]
[a,b]
[a,b]区间随机均匀地给
r
r
r取值,然后设置
a
a
a的值。
另一个例子是给
β
\beta
β取值,用于计算指数的加权平均值。假设
β
\beta
β的取值范围是
[
0.9
,
0.999
]
[0.9,0.999]
[0.9,0.999]。当计算指数的加权平均值时,
β
\beta
β取0.9就相当于在10个值中计算平均值,而取0.999则相当于在1000个值中取平均。因此我们要探究的是
1
−
β
\pmb{1-\beta}
1−β1−β1−β,这个值的取值范围在
[
0.1
,
0.001
]
[0.1,0.001]
[0.1,0.001]内,因此使用对数标尺,0.1对应的是
1
0
−
1
10^{-1}
10−1,0.001对应的是
1
0
−
3
10^{-3}
10−3,所以你要做的就是在
[
−
3
,
−
1
]
[-3,-1]
[−3,−1]里随机均匀的给
r
r
r取值,设定
1
−
β
=
1
0
r
1-\beta=10^r
1−β=10r,即
β
=
1
−
1
0
r
\beta=1-10^r
β=1−10r,这就变成了在特定的选择范围内超参数随机取值。用这种方式得到想要的结果,你在0.9到0.99区间搜索超参数所耗费的资源,和在0.99到0.999区间搜索所耗费的一样多。
至于为什么不使用线性轴,这是因为当
β
\pmb{\beta}
βββ接近1时,所得结果的灵敏度会变化,即使
β
\pmb{\beta}
βββ有微小的变化。假设
β
\beta
β在0.9到0.9005之间取值,根据公式
1
1
−
β
\frac{1}{1-\beta}
1−β1可知,在这个范围内取值就相当于大概10个值取平均,因此你的结果几乎不会变化。而
β
\beta
β值如果在0.999到0.9995之间,这会对你的算法产生巨大影响。当
β
\beta
β取0.999,这相当于对1000个值取平均,当
β
\beta
β取0.9995,这就相当于对2000个值取平均。所以整个取值过程中,你需要更加密集地取值。
3. 超参数训练实战:Pandas vs. Caviar (Hyperparameters Tuning in Practice: Pandas vs. Caviar)
关于如何搜索超参数的问题,大概有两种重要的思想流派,或人们通常采用的两种重要但不同的方式。
3.1 照看一个模型 (Babysitting one model)
一种是你照看一个模型,通常是在有庞大的数据组,但没有许多计算资源或足够的CPU和GPU的前提下,基本而言,你只可以一次负担起试验一个模型或一小批模型,在这种情况下,即使当它在试验时,你也可以逐渐改良。比如,在第0天随机初始化参数,然后开始试验,然后你逐渐观察自己的学习曲线,可以是损失函数
J
J
J、数据设置误差或其它的东西,在第1天里逐渐减少,那这一天末的时候,你可能会试着增加一点学习率,看看它会怎样,也许第2天会发现它变得更好。两天后,它依旧做得不错,也许可以填充下Momentum或减少变量。然后每天你都会观察它,并且不断调整你的参数。也许有一天,你会发现你的学习率太大了,所以可能又会回归到之前的模型,但你可以说是在每天花时间照看此模型,即使是它在许多天或许多星期的试验过程中。
所以这是一个人们照料一个模型的方法,观察它的表现,耐心地调试学习率,但那通常是因为你没有足够的计算能力,不能在同一时间试验大量模型时才采取的办法。
3.2 同时训练多个模型 (Training many models in parallel)
另一种方法则是同时试验多种模型,你设置了一些超参数,尽管让它自己运行,或者是一天甚至好几天,然后你会获得像上图这样的学习曲线(蓝色曲线),这可以是损失函数J,实验误差,或数据误差的损失,但都是曲线轨迹的度量。同时你可以开始一个有着不同超参数设定的不同模型,所以你的第二个模型会生成一个不同的学习曲线(紫色曲线),也许这条看起来更好些。与此同时,你可以试验第三种模型,其可能产生一条新的学习曲线,或者其他任意的学习曲线。或者你可以同时平行试验许多不同的模型,不同的橙色曲线就是对应不同的模型。用这种方式你可以试验许多不同的参数设定,最后快速选择工作效果最好的那个。
4. 归一化网络的激活函数 (Normalizing Activations in a Network)
4.1 Batch归一化的作用
在深度学习兴起后,最重要的一个思想是它的一种算法,叫做Batch归一化。Batch归一化会使你的参数搜索问题变得很容易,使神经网络对超参数的选择更加稳定,超参数的范围会更加庞大,工作效果也很好,也会使你的训练更加容易,甚至是深层网络。
当你训练一个神经网络模型时,归一化输入特征可以加快学习过程。通过计算平均值
μ
=
1
m
∑
x
(
i
)
\mu=\frac{1}{m}\sum{x^{(i)}}
μ=m1∑x(i),然后从训练集中减去平均值
x
=
x
−
μ
x=x-\mu
x=x−μ,计算方差
σ
2
=
1
m
∑
(
x
(
i
)
)
2
\sigma^2=\frac{1}{m}\sum{(x^{(i)})^2}
σ2=m1∑(x(i))2,接着根据方差归一化你的输入数据集
x
=
x
/
σ
2
x=x / \sigma^2
x=x/σ2。在之前的章节中了解到,通过归一化可以将学习问题的轮廓从扁平状变成圆形,更易于算法优化。所以对于logistic回归和神经网络的归一化输入特征值而言,这是非常有效的。
但是我们以前在神经网络训练中,只是对输入层数据进行归一化处理,却没有在中间层进行归一化处理。虽然我们对输入数据进行了归一化处理,但是输入数据经过
σ
(
W
T
X
+
b
)
σ(W^TX+b)
σ(WTX+b)这样的矩阵乘法以及非线性运算之后,其数据分布很可能被改变,而随着深度网络的多层运算之后,数据分布的变化将越来越大。
因此如果你想训练这些参数,比如
w
[
3
]
,
b
[
3
]
w^{[3]},b^{[3]}
w[3],b[3],那么归一化
a
[
2
]
a^{[2]}
a[2]的平均值和方差,从而使
w
[
3
]
,
b
[
3
]
w^{[3]},b^{[3]}
w[3],b[3]的训练更有效率。尽管严格来说,我们真正归一化的不是
a
[
2
]
a^{[2]}
a[2],而是
z
[
2
]
z^{[2]}
z[2]。深度学习文献中有一些争论,关于在激活函数之前是否应该将值
z
[
2
]
z^{[2]}
z[2]归一化,或是否应该在应用激活函数
a
[
2
]
a^{[2]}
a[2]后再规范值。实践中,经常做的是归一化
z
[
2
]
z^{[2]}
z[2],我推荐其为默认选择。
4.2 Batch归一化的使用方法
在神经网络中,假设隐藏层单元
z
[
l
]
(
i
)
z^{[l](i)}
z[l](i)已知,其中
i
∈
[
1
,
m
]
i\in[1,m]
i∈[1,m]。下面的公式都是针对第
l
l
l层单元,但为了简化符号,因此省略
[
l
]
[l]
[l]。
首先,取每个
z
(
i
)
z^{(i)}
z(i)值,使其规范化。方法如下,计算
z
(
i
)
z^{(i)}
z(i)的均值,再将每个
z
(
i
)
z^{(i)}
z(i)值减去均值,除以标准差。为了使数值稳定,通常在分母加上
ϵ
\epsilon
ϵ,以防
σ
=
0
\sigma=0
σ=0的情况。
μ
=
1
m
∑
i
=
0
m
z
(
i
)
\mu=\frac{1}{m}\sum_{i=0}^{m} {z^{(i)}}
μ=m1i=0∑mz(i)
σ
2
=
1
m
∑
i
=
0
m
(
z
(
i
)
−
μ
)
2
\sigma^2=\frac{1}{m} \sum_{i=0}^{m} {(z^{(i)}-\mu)^2}
σ2=m1i=0∑m(z(i)−μ)2
z
n
o
r
m
(
i
)
=
z
(
i
)
−
μ
σ
2
+
ϵ
z_{norm}^{(i)}=\frac{z^{(i)}-\mu}{\sqrt{\sigma^2+\epsilon}}
znorm(i)=σ2+ϵz(i)−μ 该批次的数据
z
(
i
)
z^{(i)}
z(i)经过规范化后,满足正态分布,此时
z
z
z的每一个分量都满足均值为0和方差为1。但是归一化后的
z
z
z基本会被限制在正态分布下,使得网络的表达能力下降,所以接下来我们所要做的就是尺度变换和偏移。
z
~
(
i
)
=
γ
z
n
o
r
m
(
i
)
+
β
\tilde{z}^{(i)}=\gamma z^{(i)}_{norm}+\beta
z~(i)=γznorm(i)+β 它的直观作用是,将
z
n
o
r
m
(
i
)
z_{norm}^{(i)}
znorm(i)乘以
γ
γ
γ调整数值大小,再加上
β
β
β增加偏移后得到
z
~
(
i
)
\tilde{z}^{(i)}
z~(i),这里的
γ
γ
γ是尺度因子,
β
β
β是平移因子。如果
γ
=
σ
2
+
ϵ
\gamma=\sqrt{\sigma^2+\epsilon}
γ=σ2+ϵ(即
z
n
o
r
m
(
i
)
=
z
(
i
)
−
μ
σ
2
+
ϵ
z^{(i)}_{norm}=\frac{z^{(i)}-\mu}{\sqrt{\sigma^2+\epsilon}}
znorm(i)=σ2+ϵz(i)−μ中的分母项),
β
\beta
β等于
μ
\mu
μ(这里的
μ
\mu
μ就是
z
n
o
r
m
(
i
)
=
z
(
i
)
−
μ
σ
2
+
ϵ
z^{(i)}_{norm}=\frac{z^{(i)}-\mu}{\sqrt{\sigma^2+\epsilon}}
znorm(i)=σ2+ϵz(i)−μ中的
μ
\mu
μ),那么
z
~
(
i
)
=
z
(
i
)
\tilde{z}^{(i)}=z^{(i)}
z~(i)=z(i)。因此,
γ
z
n
o
r
m
(
i
)
+
β
γ
\gamma z^{(i)}_{norm}+\betaγ
γznorm(i)+βγ的实际作用在于,通过对
γ
\pmb{\gamma}
γγγ和
β
\pmb{\beta}
βββ合理设定,可以构造满足其它均值和方差的隐藏单元值
z
(
i
)
\pmb{z^{(i)}}
z(i)z(i)z(i)。
5. 将Batch归一化拟合进神经网络 (Fitting Batch Norm into a Neural Network)
实践中,Batch归一化通常和训练集的mini-batch一起使用。假设有一个神经网络,你应用Batch归一化的方式就是,将第一个mini-batch
X
{
1
}
X^{\{1\}}
X{1}作为输入,然后应用参数
w
[
1
]
w^{[1]}
w[1]和
b
[
1
]
b^{[1]}
b[1]计算
z
[
1
]
z^{[1]}
z[1],接着Batch归一化会减去均值,除以标准差,由
β
[
1
]
\beta^{[1]}
β[1]和
γ
[
1
]
\gamma^{[1]}
γ[1]重新缩放,这样就得到了
z
~
[
1
]
\tilde{z}^{[1]}
z~[1],再应用激活函数
g
[
1
]
(
z
~
[
1
]
)
g^{[1]}(\tilde{z}^{[1]})
g[1](z~[1])得到
a
[
1
]
a^{[1]}
a[1]。然后使用参数
w
[
2
]
w^{[2]}
w[2]和
b
[
2
]
b^{[2]}
b[2]计算
z
[
2
]
z^{[2]}
z[2],Batch归一化得到
z
~
[
2
]
\tilde{z}^{[2]}
z~[2],中间再引入两个参数
β
[
2
]
\beta^{[2]}
β[2]和
γ
[
2
]
\gamma^{[2]}
γ[2],最后应用激活函数
g
[
2
]
(
z
~
[
2
]
)
g^{[2]}(\tilde{z}^{[2]})
g[2](z~[2])得到
a
[
2
]
a^{[2]}
a[2]。后面以此类推,第二个mini-batch
X
{
2
}
X^{\{2\}}
X{2},第三个mini-batch
X
{
3
}
X^{\{3\}}
X{3}等等也按照这样的方式继续训练。
关于Batch归一化需要强调的是
- Batch归一化是发生在计算 z z z和 a a a之间的。
- 这里的 β [ 1 ] , β [ 2 ] \beta^{[1]},\beta^{[2]} β[1],β[2]等和Momentum超参数 β \beta β没有任何关系。
- z z z的计算方式是 z ( i ) = w T a ( i ) + b z^{(i)}=w^Ta^{(i)}+b z(i)=wTa(i)+b,而Batch归一化中均值的计算方式 μ = 1 m ∑ i = 0 m z ( i ) = E ( w T a ( i ) ) + b \mu=\frac{1}{m}\sum_{i=0}^{m} {z^{(i)}}=E(w^Ta^{(i)})+b μ=m1∑i=0mz(i)=E(wTa(i))+b。因此 z n o r m ( i ) = z ( i ) − μ σ 2 + ϵ z_{norm}^{(i)}=\frac{z^{(i)}-\mu}{\sqrt{\sigma^2+\epsilon}} znorm(i)=σ2+ϵz(i)−μ中 z ( i ) − μ = ( w T a ( i ) + b ) − ( E ( w T a ( i ) ) + b ) = w T a ( i ) − E ( w T a ( i ) z^{(i)}-\mu=(w^Ta^{(i)}+b)-(E(w^Ta^{(i)})+b)=w^Ta^{(i)}-E(w^Ta^{(i)} z(i)−μ=(wTa(i)+b)−(E(wTa(i))+b)=wTa(i)−E(wTa(i)。这意味着,无论偏置项 b b b的值是多少,都是会被消除的。因此在使用Batch归一化时可以不添加偏置项 b \pmb{b} bbb,或将其置为0。
总结一下如何使用Batch归一化来应用梯度下降法。假设你在使用mini-batch梯度下降法,运行 t t t从1到batch数量的for循环,在for循环中执行如下操作:
- 对mini-batch X { t } X^{\{t\}} X{t}应用前向传播,并且对每个隐藏层都应用正向传播,计算得到 z [ l ] z^{[l]} z[l];
- 用Batch归一化计算得到 z ~ [ l ] \tilde{z}^{[l]} z~[l],从而替代 z [ l ] z^{[l]} z[l];
- 用反向传播计算第 l l l层所有的参数,即 d w [ l ] dw^{[l]} dw[l]、 d b [ l ] db^{[l]} db[l]、 d β [ l ] d\beta^{[l]} dβ[l]和 d γ [ l ] d\gamma^{[l]} dγ[l] ;
- 更新参数:
w
[
l
]
=
w
[
l
]
−
α
d
w
[
l
]
w^{[l]}=w^{[l]}-\alpha dw^{[l]}
w[l]=w[l]−αdw[l],
β
[
l
]
=
β
[
l
]
−
α
d
β
[
l
]
\beta^{[l]}=\beta^{[l]}-\alpha d\beta^{[l]}
β[l]=β[l]−αdβ[l],
γ
[
l
]
=
γ
[
l
]
−
α
d
γ
[
l
]
\gamma^{[l]}=\gamma^{[l]}-\alpha d\gamma^{[l]}
γ[l]=γ[l]−αdγ[l]。
(Momentum、RMSprop、Adam等优化算法在这里也同样适用)
6. Batch Norm 为什么起作用? (Why does Batch Norm work?)
6.1 Covariate shift
假设有一个神经网络,建立在猫的识别检测上。假设你已经在所有黑猫的图像上训练了数据集,但是现在将此网络应用于有色猫的识别。在这种情况下,正样本中不只有黑猫,还有其它颜色的猫。
假设左图是黑猫的训练集正负样本分布情况,右图是黑猫和有色猫混合的训练集正负样本分布情况。在实际中,使用左图这样的训练集作为输入而训练结果不错的神经网络,同样给右图这样的训练集运行,却并不见得会好。即使存在运行都很好的同一个函数,也不希望去使用。
Covariate shift的概念就是由于训练集和测试集,即输入数据存在分布的差异性,给网络的泛化性和训练速度带来了影响。
6.2 Covariate shift如何影响神经网络?
现在有一个上图这样的深层神经网络,以隐藏层第三层为例,假设此网络已经学习了参数
w
[
3
]
w^{[3]}
w[3]和
b
[
3
]
b^{[3]}
b[3]。
然后遮住左边的部分,从隐藏层第三层的角度来看,它从前一层获得一些值,即
a
1
[
2
]
,
a
2
[
2
]
,
a
3
[
2
]
,
a
4
[
2
]
a_1^{[2]},a_2^{[2]},a_3^{[2]},a_4^{[2]}
a1[2],a2[2],a3[2],a4[2],这些值也可以看作是输入值
x
1
,
x
2
,
x
3
,
x
4
x_1,x_2,x_3,x_4
x1,x2,x3,x4。隐藏层第三层的工作是找到一种方式,使这些值映射到
y
^
\hat{y}
y^。
现在我们把网络的左边揭开,这个网络还有参数 w [ 1 ] , b [ 1 ] w^{[1]},b^{[1]} w[1],b[1]和 w [ 2 ] , b [ 2 ] w^{[2]},b^{[2]} w[2],b[2],每一次参数迭代更新后,经过隐藏层第一层和第二层的网络计算后, a 1 [ 2 ] , a 2 [ 2 ] , a 3 [ 2 ] , a 4 [ 2 ] a_1^{[2]},a_2^{[2]},a_3^{[2]},a_4^{[2]} a1[2],a2[2],a3[2],a4[2]的值也会改变,对于隐藏层第三层来说,输入数据的分布会发生变化,因此它就有了Covariate shift问题。
6.3 Batch归一化如何解决Covariate shift问题?
将第二层的隐藏单元值的分布绘制出来(为了便于理解,这里仅考虑
z
1
[
2
]
z^{[2]}_1
z1[2]和
z
2
[
2
]
z^{[2]}_2
z2[2]两个隐藏单元值),因为
z
1
[
2
]
z^{[2]}_1
z1[2]和
z
2
[
2
]
z^{[2]}_2
z2[2]的值会变化,因此其数据分布也会有所变化。
Batch归一化所做的是,限制这些隐藏单元值分布变化的程度。当神经网络在前一层中更新参数,Batch归一化可以确保无论其怎样变化
z
1
[
2
]
z^{[2]}_1
z1[2]和
z
2
[
2
]
z^{[2]}_2
z2[2]的均值和方差保持不变(均值和方差可以分别为0和1,即符合正态分布,也可以由参数
β
\beta
β和
γ
\gamma
γ决定)。
Batch归一化减少了输入值改变的问题,使输入值变得更稳定,神经网络中后面的层就会有更坚实的基础。即使输入分布改变了一些,也会改变得更少。Batch归一化让当前层保持学习,当输入分布改变时,迫使后面的层对其适应的程度减小了,或者说它减弱了前层参数的作用与后层参数的作用之间的联系,使得网络中每一层都可以自己学习,稍稍独立于其它层,这有助于加速整个网络的学习。
6.4 Batch归一化的其他作用
Batch归一化还有一个作用,它有轻微的正则化效果。Batch归一化通常与mini-batch梯度下降法一起使用,由于每个mini-batch
X
{
t
}
X^{\{t\}}
X{t}相当于不同的输入数据集,且mini-batch size较小,因此在mini-batch上计算出的均值和方差会有一些小的噪声。同时从
z
[
l
]
z^{[l]}
z[l]到
z
~
[
l
]
\tilde{z}^{[l]}
z~[l]的缩放过程中也有一些噪音,因为它是用本身存在噪音的均值和方差计算得出的。
所以和dropout相似,Batch归一化给每个隐藏层的激活值上增加了噪音,这迫使后面的单元不过分依赖任何一个隐藏单元。对于隐藏单元来说,向输入添加方差极小的噪声等价于对权重施加范数惩罚,因此相当于有正则化的作用。但是因为添加的噪音很微小,所以正则化的效果并不是很大。
如果你想得到dropout更强大的正则化效果,你可以将Batch归一化和dropout一起使用。另外,通过应用较大的min-batch,可以减少噪音,同时也减少正则化效果。但事实上,不建议把Batch归一化当作正则化。
7. 测试时的 Batch Norm (Batch Norm at Test Time)
Batch归一化将你的数据以mini-batch的形式逐一处理,但在测试时,你可能需要对每个样本逐一处理。
首先回顾一下在训练阶段,以mini-batch的形式执行Batch归一化的过程。在一个mini-batch中,首先将mini-batch中所有的样本
z
(
i
)
z^{(i)}
z(i)值累加求和,计算均值,这里用
m
m
m来表示这个mini-batch中的样本数量,而不是整个训练集。然后计算方差,再计算
z
n
o
r
m
(
i
)
z^{(i)}_{norm}
znorm(i),即用均值和标准差来调整,分母加上
ϵ
\epsilon
ϵ是为了数值稳定性。最后是用
γ
\gamma
γ和
β
\beta
β再次调整
z
n
o
r
m
z_{norm}
znorm得到
z
~
\tilde{z}
z~。
请注意在训练阶段用于调节计算的均值
μ
\mu
μ和方差
σ
2
\sigma^2
σ2是在整个mini-batch上进行计算,但是在测试阶段,你也许不能对一个mini-batch中所有的样本同时处理,因此你需要用其它方式来得到均值
μ
\mu
μ和方差
σ
2
\sigma^2
σ2,而且如果你只有一个样本,一个样本的均值和方差没有意义。在典型的Batch归一化应用中,你需要用一个指数加权平均来估算,这个平均数涵盖了所有mini-batch。
假设在第
l
l
l层,首先训练第一个mini-batch
X
{
1
}
X^{\{1\}}
X{1},得到第一个mini-batch的均值
μ
{
1
}
[
l
]
\mu^{\{1\}[l]}
μ{1}[l]。然后在这一层继续训练第二个mini-batch
X
{
2
}
X^{\{2\}}
X{2},得到均值
μ
{
2
}
[
l
]
\mu^{\{2\}[l]}
μ{2}[l]。接着在这一层训练第三个mini-batch
X
{
3
}
X^{\{3\}}
X{3},得到均值
μ
{
3
}
[
l
]
\mu^{\{3\}[l]}
μ{3}[l]。正如之前用的指数加权平均来计算
θ
1
,
θ
2
,
θ
3
\theta_1,\theta_2,\theta_3
θ1,θ2,θ3的均值,这里也可以使用指数加权平均来估计这一隐藏层的各隐藏单元
z
z
z的均值。同样的,你还可以用指数加权平均来追踪这一层的各个mini-batch中的方差
σ
2
\sigma^2
σ2。因此在用不同的mini-batch训练神经网络的同时,能够得到你所查看的每一层的
μ
\mu
μ和
σ
2
\sigma^2
σ2的平均数的实时数值。
最后在测试时,对应等式
z
n
o
r
m
(
i
)
=
z
(
i
)
−
μ
σ
2
+
ϵ
z_{norm}^{(i)}=\frac{z^{(i)}-\mu}{\sqrt{\sigma^2+\epsilon}}
znorm(i)=σ2+ϵz(i)−μ,你只需要用你的
z
z
z值来计算
z
n
o
r
m
(
i
)
z_{norm}^{(i)}
znorm(i),用
μ
\mu
μ和
σ
2
\sigma^2
σ2的指数加权平均值来做调整,然后再用刚算出来的
z
n
o
r
m
z_{norm}
znorm和你在神经网络训练过程中得到的参数
β
\beta
β和
γ
\gamma
γ来计算那个测试样本的
z
~
\tilde{z}
z~值。
8. Softmax 回归 (Softmax Regression)
到目前为止,我们遇到的所有分类的案例都使用的是二分分类,这种分类只有两种可能的标记,即0或1。有一种logistic回归的一般形式,叫做Softmax回归,用于解决多分类的问题。
假设你需要识别猫,狗和小鸡,这里我把猫设为类别1,狗为类别2,小鸡为类别3,如果不属于以上任何一类,就设为类别0(如上图)。这里用符号
C
C
C来表示分类的类别总个数。
首先建立一个神经网络,其输出层有4个,或者说有
C
C
C个输出单元,因此
n
[
L
]
n^{[L]}
n[L],即输出层单元数量等于4,或者一般来说等于
C
C
C。
输出层的每个输出单元会给出每一个类别的概率分别有多大(如上图),即在输入为
X
X
X时,第一个节点对应的是输出为“其他”类(类别0)的概率
P
(
o
t
h
e
r
∣
X
)
P(other|X)
P(other∣X),第二个节点对应的是输出为猫(类别1)的概率
P
(
c
a
t
∣
X
)
P(cat|X)
P(cat∣X),第三个节点对应的是输出为狗(类别2)的概率
P
(
d
o
g
∣
X
)
P(dog|X)
P(dog∣X),第四个节点对应的是输出为小鸡(类别3)的概率
P
(
b
a
b
y
c
h
i
c
k
∣
X
)
P(baby\ chick|X)
P(baby chick∣X)。因此
y
^
\hat{y}
y^将是一个4×1维向量,且四个输出的概率加起来应该等于1。
让你的网络实现上述功能需要用到Softmax层,以及输出层来生成输出。在神经网络的最后一层,你将会像往常一样计算各层的线性部分,计算流程如下:
(1) 计算
z
[
L
]
=
W
[
L
]
a
[
L
−
1
]
+
b
[
L
]
z^{[L]}=W^{[L]}a^{[L-1]}+b^{[L]}
z[L]=W[L]a[L−1]+b[L]
(2) 应用Softmax激活函数:
- 计算一个临时变量 t = e z [ L ] t=e^{z^{[L]}} t=ez[L],这是对所有元素求幂。其中 z [ L ] z^{[L]} z[L]的维度是4×1,因此 t = e z [ l ] t=e^{z^{[l]}} t=ez[l]是一个4×1维向量;
- 对 t t t进行归一化,使输出的和为1,然后输出 a [ L ] a^{[L]} a[L]。因此 a [ L ] = e z [ L ] ∑ j = 1 4 t i a^{[L]}=\frac{e^{z^{[L]}}}{\sum_{j=1}^4t_i} a[L]=∑j=14tiez[L],换句话说, a [ L ] a^{[L]} a[L]也是一个4×1维向量,而这个四维向量的第 i i i个元素 a i [ L ] = t i ∑ j = 1 4 t i a^{[L]}_i=\frac{t_i}{\sum_{j=1}^4t_i} ai[L]=∑j=14titi。
举一个具体的例子来说,假设输出层中 z [ L ] = [ 5 2 − 1 3 ] z^{[L]}=\begin{bmatrix} 5\\ 2\\ -1\\ 3\\ \end{bmatrix} z[L]=⎣⎢⎢⎡52−13⎦⎥⎥⎤,然后用元素取幂方法来计算得到 t = e z [ L ] = [ e 5 e 2 e − 1 e 3 ] = [ 148.4 7.4 0.4 20.1 ] t=e^{z^{[L]}}=\begin{bmatrix} e^5\\ e^2\\ e^{-1}\\ e^3\\ \end{bmatrix}=\begin{bmatrix} 148.4\\ 7.4\\ 0.4\\ 20.1\\ \end{bmatrix} t=ez[L]=⎣⎢⎢⎡e5e2e−1e3⎦⎥⎥⎤=⎣⎢⎢⎡148.47.40.420.1⎦⎥⎥⎤。从向量 t t t到向量 a [ l ] a^{[l]} a[l]只需要将这些元素归一化,使总和为1,即把向量 t t t中的所有元素加起来,得到176.3,最终 a [ l ] = y ^ = t 176.3 = [ 0.841 0.041 0.002 0.114 ] a^{[l]}=\hat{y}=\frac{t}{176.3}=\begin{bmatrix} 0.841\\ 0.041\\ 0.002\\ 0.114\\ \end{bmatrix} a[l]=y^=176.3t=⎣⎢⎢⎡0.8410.0410.0020.114⎦⎥⎥⎤。因此在输出层,第一个节点的输出是0.842,即输入 X X X为类别0的概率是84.2%。同理,第二个节点的输出是0.041,即输入 X X X为类别1的概率是4.1%。第三个节点的输出是0.002,即输入 X X X为类别2的概率是0.2%。第四个节点的输出是0.114,即输入 X X X为类别3的概率是11.4%。
9. 训练一个 Softmax 分类器 (Training a Softmax Classifier)
关于训练带有Softmax输出层的神经网络,具体而言,我们先定义训练神经网络会用到的损失函数。以上一章节为例,假设输入是一张猫(类别1)的图片,即真实标签是
y
=
[
0
1
0
0
]
y=\begin{bmatrix} 0\\ 1\\ 0\\ 0\\ \end{bmatrix}
y=⎣⎢⎢⎡0100⎦⎥⎥⎤。假设你的神经网络输出的是
y
^
=
a
[
L
]
=
[
0.3
0.2
0.1
0.4
]
\hat{y}=a^{[L]}=\begin{bmatrix} 0.3\\ 0.2\\ 0.1\\ 0.4\\ \end{bmatrix}
y^=a[L]=⎣⎢⎢⎡0.30.20.10.4⎦⎥⎥⎤,其中
y
^
\hat{y}
y^是一个元素总和为1的向量。对于这个样本神经网络的表现不佳,这实际上是一只猫,但却只分配到20%是猫的概率,所以在本例中表现不佳。
在Softmax分类中,我们一般用到的损失函数是
L
(
y
^
,
y
)
=
−
∑
j
=
1
4
y
j
log
y
^
j
L(\hat{y},y)=-\sum_{j=1}^4y_j\log\hat{y}_j
L(y^,y)=−j=1∑4yjlogy^j 在这个样本中
y
1
=
y
3
=
y
4
=
0
y_1=y_3=y_4=0
y1=y3=y4=0,
y
2
=
1
y_2=1
y2=1,因此损失函数
L
(
y
^
,
y
)
=
−
y
2
t
log
y
^
2
=
−
log
y
^
2
L(\hat{y},y)=-y_2t\log\hat{y}_2=-\log\hat{y}_2
L(y^,y)=−y2tlogy^2=−logy^2。
L
(
y
^
,
y
)
=
−
∑
j
=
1
4
y
j
log
y
^
j
=
−
y
2
log
y
^
2
=
−
log
y
^
2
L(\hat{y},y)=-\sum_{j=1}^4y_j\log\hat{y}_j=-y_2\log\hat{y}_2=-\log\hat{y}_2
L(y^,y)=−∑j=14yjlogy^j=−y2logy^2=−logy^2。这就意味着,如果你的学习算法试图将它变小,因为梯度下降法是用来减少训练集的损失的,要让损失函数
L
(
y
^
,
y
)
L(\hat{y},y)
L(y^,y)变小,就是让
−
log
y
^
2
−
-\log\hat{y}_2−
−logy^2−变小,而
y
^
2
\hat{y}_2
y^2要尽可能大。
概括来讲,损失函数所做的就是找到你的训练集中的真实类别,然后试图使该类别相应的概率尽可能地高。
这是单个训练样本的损失,整个训练集的损失
J
J
J就是将所有训练样本的预测都加起来,即
J
(
w
[
1
]
,
b
[
1
]
,
⋯
)
=
1
m
∑
i
=
1
m
L
(
y
^
(
i
)
,
y
(
i
)
)
J(w^{[1]},b^{[1]},\cdots)=\frac1m\sum_{i=1}^mL(\hat{y}^{(i)},y^{(i)})
J(w[1],b[1],⋯)=m1i=1∑mL(y^(i),y(i)) 因此你要做的就是用梯度下降法,使这里的损失最小化。在有Softmax输出层的神经网络中,实现梯度下降法的流程如下:
- 前向传播过程:
- ① 输出层会计算 z [ l ] z^{[l]} z[l],其维度是C×1。
- ② 用Softmax激活函数来得到 a [ l ] a^{[l]} a[l],或者说 y ^ \hat{y} y^。
- ③ 计算损失。
- 反向传播过程:
- ① d z [ l ] = y ^ − y dz^{[l]}=\hat{y}-y dz[l]=y^−y,这是对 z [ l ] z^{[l]} z[l]损失函数的偏导数 d z [ l ] = ∂ J ∂ z [ l ] dz^{[l]}=\frac{\partial J}{\partial z^{[l]}} dz[l]=∂z[l]∂J。
- ② 计算整个神经网络中所需要的所有导数。