深度学习 -- 神经网络 2
目录
1. 深度神经网络 vs 神经网络
如上图所示:
当数据量比较少的时候,传统机器学习的性能提升很快,甚至是优于基于神经网络的机器学习的。但是随着数据量的越来越大,传统机器学习的性能逐渐趋于平坦,而基于神经网络的机器学习的性能却越来越好。而且随着神经网路的规模越来越大,性能也是越来越好。
所以说是规模推动深度学习快速发展,不仅仅是神经网络的规模,还有数据的规模。当下,要想达到高性能,要么训练一个大的网络,要么有大量的训练数据。然而这个在一定程度上也会达到瓶颈,比如用光了所有数据,或者网络太大需要太长的训练时间。不过目前条件下,光是规模就可以促进深度学习前进一大步。
当数据量在Small Training set这个区间时,不同的算法性能是不同的。如果没有很大的数据集,那么手工提取出来的feature在很大程度上决定了算法的性能。比如SVM的性能可能就会优于神经网络的性能,这个性能更多的是依赖提取feature的能力和算法的细节。
但是随着数据量的增大以及计算能力的加强,神经网络的性能表现的越来越好。当然除了数据和计算力,算法的发展也增强了神经网络的性能,比如**函数从sigmoid换成了ReLU,使得神经网络有了重大的突***决了sigmoid算法中存在的梯度消失的问题(梯度几乎为0时,学习的进度会非常得缓慢),而ReLU的梯度始终为1,梯度永远不会消失,这使得梯度下降算法的速度很快。
支持深度学习快速发展的三要素:
- Data
- Computation
- Algorithm
在上一篇文章中,https://www.zybuluo.com/hummingbird2018/note/1265140,介绍了最简单的一层神经网络,接下来循序渐进地介绍深层神经网络。
2. 单层隐藏层的神经网络
这是一个2层的神经网络,其中:输入层2个神经元(代表2个feature),1层隐藏层4个神经元,**函数为tanh,输出层1个神经元,**函数为sigmoid
2.1 **函数
在神经网络中,目前经常使用的**函数主要是以下几种:
- sigmoid::S曲线
a的范围在(0, 1)之间,一般当a>=0.5时认为输出为1,a<0.5时为0。
该**函数在神经网络中基本不再使用,因为下面的tanh完全能够替换掉它,而且效果比它还要好。唯一使用sigmoid的场景就是当需要输出一个二分类的结果时,因为这时要求的输出为 ,正好是sigmoid的范围。
- tanh:双正切曲线
它和sigmoid的差别就在于做了纵向平移和比例放大,使得它能够通过原点,范围在(-1, 1)之间。之所以比sigmoid的效果好在于:经过该**函数输出的数据平均值更加接近于0,而不是0.5,使得数据中心化,当作为下一层的输入数据进行计算时更加的简单。在之前的机器学习中也曾经讲过,作为输入数据时,一般要做的预处理动作就有scale normalization和mean normalization(zero mean),目的都是为了计算效果更好。
但这两种**函数有一个共同的缺点:当Z越大或者越小时,a的值越接近1或者-1,变化的幅度越来越小,这就会导致斜率会变得很小,接近于0,这会使得梯度下降变得缓慢,最终导致梯度消失的严重问题。为解决此问题,产生了一个新的**函数:ReLU
- ReLU:Rectified Linear Unit
当z<0时,a = 0;当z >= 0 时,a = 1,此时斜率永远为1,所以不会发生梯度消失的问题。ReLU现在已经被广泛地应用在神经网络上,所以如果你不确定该选用什么**函数时,那么就选择ReLU。
当然ReLU也有一个缺点:就是当z为负数时,其导数为0,虽然这在实际应用中并不是什么大的问题,因为通常隐藏单元输出的z都会大于0。不过为了解决这个问题,有个改进的版本:Leaky ReLU
- Leaky ReLU
它和ReLU的区别在于当z为负数的时候有一个很小的斜率,比如0.01,公式为:
2.2 为什么需要非线性的**函数
**函数的作用就是去线性,如果没有**函数会怎么样?
扩展开来仍然得到一个如下格式的结果:
$
它仍然是线性的,所以不管神经网络有多少隐藏层,有多少隐藏单元,这些最终都没有任何意义。所以在神经网络中,至少在隐藏层是必须要去线性的。唯一不需要**函数的只有在输出层,当要求输出为一个连续的变量时,比如线性回归问题。
2.3 **函数的导数
因为在反向传播中需要使用**函数的导数,所以这里顺便做一个简单的介绍。如果需要了解详细的推导过程,请参考微积分的链式法则,这里只列出结果。
-
sigmoid的导数
-
tanh的导数
-
ReLU的导数
KaTeX parse error: Expected & or \\ or \cr or \end at position 54: …{cases} 0, & \̲m̲b̲o̲x̲{if }z < 0 \\ 1… -
Leaky ReLU的导数
KaTeX parse error: Expected & or \\ or \cr or \end at position 57: …ses} 0.01, & \̲m̲b̲o̲x̲{if }z < 0 \\ 1…
2.4 前向传播Forward propagration
前向传播的目的就在于求出每一层的和最后的。以上图的神经网络为例,结果如下:
2.5 反向传播Backward propagation
它的目的就是通过最小化,更新每一层的参数和。而最小化的过程就是梯度下降的过程。首先以为例解释推导过程,因为是反向开始的,所以首先从开始,根据微积分链式法则
对同理。为了简化,令:
那么针对该例,求导的结果如下:
最后更新参数:
Summary
通过Forward/Backward Propagation,就能够不断地优化参数
3. 单层隐藏层神经网络的构建实例
问题:对下面图中花瓣形状的两种颜色的数据进行分类
如果使用简单的线性分类,比如:
clf = sklearn.linear_model.LogisticRegressionCV();
clf.fit(X.T, Y.T);
那么得到的结果为:
准确率只有47%。下面我们只使用一层隐藏层的神经网络来分类。
步骤如下:
The general methodology to build a Neural Network is to:
-
Define the neural network structure ( # of input units, # of hidden units, etc).
input layer: n_x = 2 hidden layer: n_h = 4, activation: tanh output layer: n_y = 2, activation: sigmoid
-
Initialize the model’s parameters
def initialize_parameters(n_x, n_h, n_y):
-
Loop:
-
Implement forward propagation
def forward_propagation(X, parameters):
-
Compute loss
def compute_cost(A2, Y, parameters):
-
Implement backward propagation to get the gradients
def backward_propagation(parameters, cache, X, Y):
-
Update parameters (gradient descent)
def update_parameters(parameters, grads, learning_rate = 1.2):
-
通过上述的几个重要的步骤,以及对超参learning_rate和hidden layer层数的调试,最终发现下图是个最佳的结果。在下面的代码实例中有详细的实现过程和讲解