概述
当我们要实现一个机器学习系统时,我们遵循以下的步骤:
1. 以一个我们能快速实现的简单算法作为开始,实现之后用我们的交叉验证集去测试。
2. 绘出学习曲线来判断更多的数据、特征等等是否有助于改善系统。
3. 误差分析:在算法出错的地方手动的选几个例子(在交叉验证集中)。
1.数据划分
首先我们会对数据进行划分,划分成三部分,训练集、开发集以及测试集。训练集我们用来训练模型,交叉验证集来用评估不同模型算法表现以用来选择合适的算法,测试集对我们最终选择的模型算法进行无偏评估。数据划分的比例根据实际数据量而定。为什么需要有三部分的数据,原因如下:
- 当用一部分数据取训练我们的模型时,这个部分数据就是我们的训练集,因此这是有“针对”的训练,结果通常是在训练集上的表现会较好,但是在实际中会如何,这个无法得知。
- 开发集将对不同的使用不同算法的模型进行评估,注意这一评估过程,存在拟合开发集的过程,因为我们总想要选择对开发表现较好的算法。
- 最后需要有测试集,测试集将对最后选定的模型进行评估,这一次评估将是无偏估计。
另外有一个训练集和测试集不同分布的问题, 这里的测试集可以理解成为实际应用中用户生成的数据。例如一个图片识别系统,通常我们的训练集都是从网上爬取的,这些图片基本都是高清和经过PS的,但是用户上传的照片也通常是比较模糊的。这个时候就存在了训练集和测试集不同分布的问题。这个时候要保持开发集和测试集同分布。原因很明显,我们最终是想要在实际应用中表现良好,而保持开发集和测试集同分布时,给我们最终选定的算法和模型的性能提供一定的保证,而不会说在另一分布的数据中表现的太糟糕。
2.偏差和方差
2.1 认识偏差和方差
前面有讲到,训练的过程其实就是一个拟合数据的过程,而偏差和方差分别是对训练集和开发集的拟合情况的一种描述,另外对现在的评估要建立在最优误差(贝叶斯误差)的基础上,他指示了性能最优的情况。先看下图:
1.可以看到对于上面的二元分类问题,第一条决策边界并不能很好的对训练数据进行分类,这就是属于高偏差问题,也称为欠拟合,欠拟合的决策边界通常都是不仅在训练集上表现槽糕,而且在面对新的数据集的表现也是不尽人意的。
2.对于第三条决策边界,决策边界很好的拟合了训练数据,意味着他在训练集上的误差几乎为0,但是这并不代表这是一个很好的边界,因为过度拟合会导致决策边界的泛化误差变大,这就导致面对新的数据集时,模型表现可能会与之前在训练集上的表现差别很大,这就称为高方差。
3.对于中间的第二条决策边界,并没有很好的拟合数据,但是也正确的分类了大部分数据,这样的决策边界具有较好的泛化误差,所以在面临新的数据集时,他的表现也会令人满意。
通过下图我们可以从模型的表现上认识偏差和方差问题:
可以发现,跟之前的对应,误差在训练集上低,验证集上高,为高方差;误差在训练集高,验证集高(相近)为高偏差;误差在训练集上高、验证集上高很多,为高偏差和高方差(意味着存在部分数据过度拟合);误差在两个训练集中都较低则为低偏差、低方差。
2.2 解决高方差
高方差通常是指我们的模型出现了过度拟合的情况,所以我们的目标是解决过度拟合的问题。
2.2.1 添加正规项
在目标函数后加上一个正规项,达到惩罚那些 W 很大的神经点,以减小高方差的问题。修改后的目标函数如下:
J
=
C
(
Y
,
Y
∗
)
+
λ
2
m
∑
(
w
i
)
2
J = C(Y, Y^*) + \frac{\lambda}{2m}\sum(w^i)^2
J=C(Y,Y∗)+2mλ∑(wi)2
其中
λ
\lambda
λ是我们需要调整的参数。如果是神经网络,W 是所有层中的 Wi。由导数的表达式可知
λ
\lambda
λ 控制了 W 的大小,
λ
\lambda
λ 越大使得W越小,甚至接近0,使得结点的影响变小,从而避免了高方差的情况(自然会对偏差有一定的影响)。
2.2.2 增加训练集大小
增加训练集,这个方法很简单,唯一的问题就是存在收集数据的代价。在这里,增加的训练集的分布的不同也会影响最终训练的结果,例如图像识别中,高像素图片和低像素图片就是不同的分布。
2.2.3 减少特征
特征越多其实就越能描述数据的特点(当然这里是指合适的特征),自然就可以很好的拟合训练集中的数据,手动减少部分特征,能够实现减少方差的目的,但是也会影响偏差的提高。
2.2.4 dropout (随机失活技术,神经网络)
因为神经网络中有多个神经元,多层神经元,所以我们可以令部分神经元失活,达到一种简化神经网络的目的。另外一种直观的理解就是随机失活使得任意一个结点都有可能失效,使得后面的结点不能过分的依赖前面的结点,以实现了正规化,达到解决高方差的目的。通常可以选择不同的隐藏层有着不同的失活率,例如某层很大时,失活率可以适当的提高。使用失活技术时,有下面的技术点需要注意:
- 前向传播时,使用了失活技术的隐藏层对应的输出 A 将除以对应的 keep_prob,恢复输出的值。
- 反向传播时,使与前向传播的对应,dA / Keep_prob,恢复输出的值。
- 失活技术不与梯度检查一起使用,但可以与添加正规项一起使用。
2.3 解决高偏差
高偏差是指模型欠拟合数据,这是比高方差更槽糕的问题。因为模型既不能在验证集上表现良好,在训练集上表现也很糟糕。
2.3.1 寻找更多特征
出现高偏差的原因之一是,特征过于简单,无法提供足够的特征来描述数据。所以寻找更多的特征是一种常见的方法。
2.3.2 减少 λ \lambda λ
如果使用了正规化技术中添加正规项, λ \lambda λ的过大会导致高方差问题,过小会导致高偏差问题。
2.3.3 加大网络规模
如果觉得一切都没有问题,但是偏差仍然居高不下,那有可能是网络过于简单了,就算线性函数想要拟合非线性的函数,结果可想而知。所以增加网络的隐藏层和结点个数以及选择合适的激活函数,能够解决此类问题。
2.4 总结
将上面两个问题及解决方案制成如下表格:
技术 | 高偏差 | 高方差 |
---|---|---|
增加训练集 | N | Y |
减少特征 | N | Y |
增加特征 | Y | N |
增大 λ \lambda λ | N | Y |
减小 λ \lambda λ | Y | N |
3. 优化过程中的问题
3.1 正规化输入
这种问题通常为输入的分布相差很大时使用,可以使输入的分布相同,以达到加速学习的过程。正规化输入公式如下:
X
N
o
r
m
=
X
−
X
M
e
a
n
X
S
t
d
X_{Norm} = \frac{X-X_{Mean}}{X_{Std}}
XNorm=XStdX−XMean
X
M
e
a
n
、
X
S
t
d
X_{Mean}、X_{Std}
XMean、XStd 分别代表X的均值、方差。
3.2 梯度爆炸/消失 与 初始化参数
梯度爆炸(消失)就是反向传播得过程中梯度过大(小)的问题,使得参数的变化过于激烈(微弱),导致学习过程出现问题。这通常在有较多层次的神经网络中会出现,这种问题似乎很难控制, 除非减少层数。 现在有一种初始化技术使得这种现象变化缓和一些。He 初始化技术:
W
=
n
p
.
r
a
n
d
o
m
.
r
a
n
d
n
(
.
.
,
.
.
.
)
∗
2
p
r
e
−
l
a
y
e
r
−
d
i
m
s
W = np.random.randn(.., ...) * \sqrt{\frac{2}{pre-layer-dims}}
W=np.random.randn(..,...)∗pre−layer−dims2
上面用了 Python 描述,随机初始化某一层的参数并乘以右边一个根式。大多数情况下能够加速学习的过程。
3.3 梯度检查(Gradient checking)
反向传播更新参数的过程中,我们使用的是梯度下降的方法,而且使用的是公式法。使用公式法的优势是快,但是存在一个问题就是导数的公式是否正确,为了判断梯度下降的过程是正确的,就会使用导数的定义法(极限的思想)来验证,而梯度检查也就是使用定义法计算出来的导数和公式法计算出来的导数做比较,若是相差较小,则认为公式是正确的,否则应该检查公式,当然这有个前提就是定义法得是正确的。具体使用梯度检查的过程如下:
- 用公式法计算导数 g r a d grad grad
- W p l u s = W + ϵ W_{plus} = W + \epsilon Wplus=W+ϵ ( ϵ \epsilon ϵ 应为一个很小的值,如 1 0 − 7 10^{-7} 10−7),用此参数计算代价,也就是一次前向传播得过程得 J p l u s J_{plus} Jplus
- W m i n u s = W − ϵ W_{minus}=W - \epsilon Wminus=W−ϵ,再一次前向传播 J m i n u s J_{minus} Jminus
- 得 g r a d a p p r o x = J p l u s − J m i n u s 2 ϵ gradapprox= \frac{J_{plus}-J_{minus}}{2\epsilon} gradapprox=2ϵJplus−Jminus
- 计算 d i f f = ∣ ∣ g r a d a p p r o x − g r a d ∣ ∣ ∣ ∣ g r a d ∣ ∣ + ∣ ∣ g r a d a p p r o x ∣ ∣ diff=\frac{||gradapprox-grad||}{{||grad||+||gradapprox||}} diff=∣∣grad∣∣+∣∣gradapprox∣∣∣∣gradapprox−grad∣∣
上面计算 dff 时分子、分母都是欧几里得距离,欧几里得距离描述的是两点之间的距离。通常计算出来的 d i f f diff diff 应该小于 1 0 − 7 10^{-7} 10−7,如若不然,则应该检查一下反向传播的过程是否存在问题。
4. 优化技术
4.1 小批量下降
当训练集的数量很大时,训练的速度就会明显下降,在神经网络中尤为明显,因为存在着双向传播。为了加速训练的速度,可以采用小批量训练。原本每次迭代使对整个训练集进行训练,现在则是将训练集分成若干个较小的训练集,再分别对这些分组的训练集进行训练,这就是小批量训练。与平常的训练十分类似,只是在训练时调整了数据的大小。下面一个Python例子:
for i in range(iterations_num):
for j in range(1, S):
forward_propagation(parameters, X[j])
...
两层循环,外层是熟悉的迭代次数,里面多了一层,内层 Loop 就是小批量训练。X[j]就是一小批训练集。在使用小批量下降时,有以下需要注意的地方:
- 训练集较小时(<2000),没有必要使用小批量下降。
- 选择合适的批大小,可能的是 2 的幂方,如 64、128、256、512、1024等
- 由于训练时,不总是单纯的拟合一个集合,而是多个小批量,因此代价函数会出现波动的情况,但是总体是下降的。
4.2 动量梯度下降优化算法(Momentum)
优化目标函数的方式有很多,动量梯度下降是一种比普通梯度下降快的算法,因为他减少了目标函数优化时的波动(通过计算梯度的指数加权平均,使得“导数”的变化变得缓和,可以使用较大了学习率),朝着最小值逼进。伪代码:
在这里,
β
\beta
β是我们需要调整的超参数,通常取值0.9.
v
d
w
和
v
d
b
v_{dw} 和 v_{db}
vdw和vdb通常置初始值为0.
4.3 均方根传递优化算法(RMSprop)
均方根传递与动量梯度下降的算法十分相似,见下伪代码:
S
d
W
=
β
S
d
W
+
(
1
−
β
)
d
W
2
S
d
b
=
β
S
d
b
+
(
1
−
β
)
d
b
2
W
=
W
−
α
d
W
S
d
W
,
b
=
b
−
α
d
b
S
d
b
S_{dW} = \beta S_{dW} + (1-\beta)dW^2 \\ S_{db} = \beta S_{db}+(1-\beta)db^2\\ W=W-\alpha \frac{dW}{\sqrt{S_{dW}}},b=b-\alpha \frac{db}{\sqrt{S_{db}}}
SdW=βSdW+(1−β)dW2Sdb=βSdb+(1−β)db2W=W−αSdWdW,b=b−αSdbdb
也是在每次更新W和b的时候作出了修改。可以发现当某些参数梯度很大时经过处理后就会变小,梯度小的着会相对变大,这就使得优化时出现的波动减小。从而可以使用更大的学习率,以加快学习速度。 S d W , S d b S_{dW},S_{db} SdW,Sdb的初始值为0.
4.4 Adam 优化算法
Adam算法是将上面Momentum和RMSprop算法结合起来使用的,通常在很多神经网络中都使用,而且表现得很好。见下面伪代码:
V
d
W
=
β
1
V
d
W
+
(
1
−
β
1
)
d
W
V
d
b
=
β
1
V
d
b
+
(
1
−
β
1
)
d
b
S
d
W
=
β
2
S
d
W
+
(
1
−
β
2
)
d
W
2
S
d
b
=
β
2
S
d
b
+
(
1
−
β
2
)
d
b
2
V
d
W
c
o
r
r
e
c
t
e
d
=
V
d
W
1
−
β
1
t
,
  
V
d
b
c
o
r
r
e
c
t
e
d
=
V
d
b
1
−
β
1
t
S
d
W
c
o
r
r
e
c
t
e
d
=
S
d
W
1
−
β
2
t
,
  
S
d
b
c
o
r
r
e
c
t
e
d
=
S
d
b
1
−
β
2
t
W
=
W
−
α
V
d
W
c
o
r
r
e
c
t
e
d
S
d
W
c
o
r
r
e
c
t
e
d
+
ϵ
b
=
b
−
α
V
d
b
c
o
r
r
e
c
t
e
d
S
d
b
c
o
r
r
e
c
t
e
d
+
ϵ
V_{dW}=\beta_1 V_{dW}+(1-\beta_1)dW\\ V_{db}=\beta_1 V_{db}+(1-\beta_1)db\\ S_{dW} = \beta_2 S_{dW} + (1-\beta_2)dW^2 \\ S_{db} = \beta_2 S_{db}+(1-\beta_2)db^2\\ V^{corrected}_{dW}=\frac{V_{dW}}{1-\beta_1^t},\;V^{corrected}_{db}=\frac{V_{db}}{1-\beta_1^t}\\ S^{corrected}_{dW}=\frac{S_{dW}}{1-\beta_2^t},\;S^{corrected}_{db}=\frac{S_{db}}{1-\beta_2^t}\\ W=W-\alpha \frac{V^{corrected}_{dW}}{\sqrt{S^{corrected}_{dW}}+\epsilon}\\ b=b-\alpha \frac{V^{corrected}_{db}}{\sqrt{S^{corrected}_{db}}+\epsilon}
VdW=β1VdW+(1−β1)dWVdb=β1Vdb+(1−β1)dbSdW=β2SdW+(1−β2)dW2Sdb=β2Sdb+(1−β2)db2VdWcorrected=1−β1tVdW,Vdbcorrected=1−β1tVdbSdWcorrected=1−β2tSdW,Sdbcorrected=1−β2tSdbW=W−αSdWcorrected+ϵVdWcorrectedb=b−αSdbcorrected+ϵVdbcorrected
其中,
β
1
默
认
0.9
,
θ
2
默
认
0.999
,
ϵ
默
认
1
0
−
8
\beta_1\ 默认\ 0.9\ ,\ \theta_2\ 默认\ 0.999\ ,\ \epsilon\ 默认\ 10^{-8}
β1 默认 0.9 , θ2 默认 0.999 , ϵ 默认 10−8,
α
\alpha
α是需要我们调试获得的,t 表示的是第几次迭代。上面的公式多了两项,这两项是偏差修正,使前期的估算更准确,偏差修正也可以放到对应的Momentum或RMSprop算法中。
4.5 学习率衰减
在学习的过程中,梯度(斜率)的变化是越来越小的,这意味着越来越靠近最优点了,如果我们的学习率
α
\alpha
α保持一个较大的值,可能会导致发散的情况发生。虽然梯度越来越小已经会自动调整了学习的步长,但是通过在迭代的过程中调整学习率,还是可以加上学习速度的。
常用的公式:
- α = 1 1 + d e c a y _ r a t e ∗ t ∗ α 0 \alpha = \frac{1}{1+decay\_rate\ *\ t}*\alpha_0 α=1+decay_rate ∗ t1∗α0
- α = 0.9 5 t ∗ α 0 \alpha = 0.95^t*\alpha_0 α=0.95t∗α0
- α = k t ∗ α 0 \alpha=\frac{k}{\sqrt{t}}*\alpha_0 α=tk∗α0
- 固定离散值
上面t为迭代的次数,decay_rate 和 k 都是需要调试的超参数。
5.误差分析
术语描述 Precision/Recall:
术语 | 解释 | 公式 |
---|---|---|
T r u e P o s i t i v e True\ Positive True Positive | 判断为真,实际也为真 | |
T r u e N e g a t i v e True\ Negative True Negative | 判断为假,实际也为假 | |
F a l s e P o s i t i v e False\ Positive False Positive | 判断为真,实际为假 | |
F a l s e N e g a t i v e False\ Negative False Negative | 判断为假,实际为真 | |
P r e c i s i o n Precision Precision | 在所有我们判断为真的数据中,True Positive 所占的比例 | T r u e P o s i t i v e T r u e P o s i t i v e + F a l s e P o s i t i v e \frac{True\ Positive}{True\ Positive + False\ Positive} True Positive+False PositiveTrue Positive |
R e c a l l Recall Recall | 在所有我们判断为真的数据中,True Negative 所占的比例 | T r u e N e g a t i v e T r u e N e g a t i v e + F a l s e N e g a t i v e \frac{True\ Negative}{True\ Negative+ False\ Negative} True Negative+False NegativeTrue Negative |
A c c u r a c y Accuracy Accuracy | 系统的准确性 | T r u e P o s i t i v e + T r u e N e g a t i v e m \frac{True\ Positive + True\ Negative}{m} mTrue Positive+True Negative |
也有以下表格:
\ | True | False |
---|---|---|
Predict True | True Positive | False Positive |
Predict False | False Negative | True Negative |
通常我们用 Precision 和 Recall 来判断系统的性能,我们希望Precision 和 Recall 都尽可能的高,因为这就意味着我们的系统表现得很好,但是实际上这两个不能同时都提高,原因很明显,当$h(\theta) $大于某一 t h r e h o l d threhold threhold时,我们判断为真,小于等于时,我们判断为假。见下面分析:
当我们调大 $threhold $ 时,导致的是系统判断为真的概率降低,自然 Predict 就上升了。相反我们降低 t h r e h o l d threhold threhold 时,系统判断为假的概率降低,Recall也会上升。所以这两个不能同时调优,我们需要的做的是权衡我们更希望哪一个更高。
通常,我们也要有一些数值来判断系统的性能:
术语 | 公式 |
---|---|
F 1 S c o r e F1\ Score F1 Score | 2 P R P + R 2\frac{PR}{P+R} 2P+RPR |