武功再高,也怕菜刀。
当我们的模型主体结构确定了之后,或者说我们由于数据量等现实问题已经无法再提已有模型的能力,我们可以使用很多简单的优化算法,把我们现在的数据模型的能力大幅提高。这一部分我们会分别介绍多层网络,激活函数,损失函数,梯度下降,反向传播,动态学习率以及具体的实现
多层网络
首先我们在第一章就讨论了深度学习的惊人效果,所以首先我们要做的就是要把上一张的模型变成神经网络结构,即在输入层和输出层之间加入隐藏层。
隐藏层可以包含很多层,上一层的处理结果又是下一次的输入值,经过每一层的处理最终得到输出结果。可以理解为每一层我们通过参数的设置,把上一层的处理结果抽象成更高级的特征。
层数越深越好?
随着隐藏层的层数越来越多,我们最终结果的准确率也会提升。然而随着深度越来越深,我们发现了这些问题:
1. 过拟合(Overfitting) 原因是随着层数的增多,对正确结果有影响的“噪音”也和真正所需的特征一起被放大,抽象。这就导致了我们所训练的模型对于训练集数据可以高度的匹配,但是不具备普遍实用性,所以这样的模型会在测试集上表现不尽人意。
如图所示,第一种是拟合不足,导致模型在训练集和测试集上都没有好的表现;恰当的拟合如图二,则会在训练集和测试集上都有比较好的结果;图三则是过拟合,它在训练集上过度的拟合,虽然可以在训练集上取得很高的准确率,却会在测试集上获得较低的训练集。
对于如何在深度网络中解决过拟合的问题,我们会专门开一章讲解,有很多有趣的想法。
2. 梯度消失(Gradient Vanishing Problem)
梯度消失是指在神经网络训练过程中,权重更新的梯度消失,也就是权重参数无法再通过训练更新。现在对于梯度的修正方法也很多了,我们会在后面的讲解中带出。其中一个简单实用的就是使用非线性激活函数。
3. 梯度爆炸(Gradient Exploding Problem)
和梯度消失相反,梯度爆炸是指在神经网络训练过程中,权重更新的梯度变大,使得权重多次以大于1的倍数迭代更新。我们也会在后面的讲解中提出解决方案。
激活函数
简单概括,激活函数是用来实现函数的去线性化。想象一下你用一堆直线想要画出一个圆,就必须用无数的直线一点点修正。但是如果你可以话曲线就可以一次性解决问题。如果没有激活函数,所有的输入和输出其实都是线性关系,这就像用直线画圆一样,同样的训练成本下只能实现粗糙的效果。
sigmoid是最为普遍的激活函数,但是现在在我们的实际应用中已经很少使用了,一个重要的原因就是它在深层网络中容易引起梯度消失,所以我们一般在隐藏层中使用ReLU作为激活函数。但是使用ReLU我们一样有需要注意的是学习率的设定,因为从它的特征我们也能发现,它很容易造成死神经元,也就是所有神经元的输入经过激活后变成了0。所以我们也可以使用Leaky ReLU替代。
具体使用什么样的激活函数其实和我们所训练的数据以及模型有关,大家在实践中发现模型有问题发生在激活函数前后时,可以尝试其他的激活函数。
损失函数
我们训练神经网络的最终目的就是最小化我们的损失函数,所以损失函数即我们用来衡量模型好坏的标准。正如我们前面提到的,不同的模型有着目的所以一般有着不一样的损失函数。但是我们也有着一些通用的评判方法,比如交叉熵函数(Cross Entropy)。p表示真实标记的分布,q则为训练后的模型的预测标记分布,交叉熵损失函数可以衡量p与q的相似性。
自定义损失函数
我们当然可以根据实际需要自定义损失函数。比如我是一个销售商,我进货时需要考虑我能卖多少。假如一件商品的成本是1,利润是2,那么我多进货一个,损失是1,但是少进一个就会损失2,所以我的损失函数就会变成
loss = sum(f(x, y))
f(x, y) = 1 * (x - y) 如果x > y
f(x, y) = 2 * (y - x) 如果y > x
这些都是我们在实际应用中必然会使用的方法,每一项都可以展开说很多内容,我们在这里先进行简单介绍,等我们在操作中遇到时会继续展开。有兴趣的同学们可以自己去搜索一下相关知识。
另外欢迎大家关注留言,我们的更新可以及时推送,一起学习吧骚年们~