深度学习
Author: shikanon
CreateTime: 2017/2/13
一、基础篇
神经网络中的每个神经元 对其所有的输入进行加权求和,并添加一个被称为偏置(bias) 的常数,然后通过一些非线性激活函数来反馈结果。
数据集我们采用深度学习界的Hello-Word———— MNIST手写数字数据集,学习从第一个softmax开始。
1. softmax
softmax主要用来做多分类问题,是logistic回归模型在多分类问题上的推广,softmax 公式:
当k=2时,转换为逻辑回归形式。
softmax一般作为神经网络最后一层,作为输出层进行多分类,Softmax的输出的每个值都是>=0,并且其总和为1,所以可以认为其为概率分布。
softmax 示意图
softmax 输出层示意图
%pylab inline |
Populating the interactive namespace from numpy and matplotlib
from IPython.display import SVG |
Using TensorFlow backend.
#设置随机数种子,保证实验可重复 |
原数据结构:
((60000, 28, 28), (60000,))
((10000, 28, 28), (10000,))
变换后的数据结构:
((60000, 784), (60000, 10))
((10000, 784), (10000, 10))
# 构建一个softmax模型 |
____________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
====================================================================================================
dense_1 (Dense) (None, 10) 7850 dense_input_1[0][0]
____________________________________________________________________________________________________
activation_1 (Activation) (None, 10) 0 dense_1[0][0]
====================================================================================================
Total params: 7,850
Trainable params: 7,850
Non-trainable params: 0
____________________________________________________________________________________________________
SVG(model_to_dot(model).create(prog='dot', format='svg')) |
from keras.callbacks import Callback, TensorBoard |
tensorboard = TensorBoard(log_dir='/home/tensorflow/log/softmax/epoch') |
<keras.callbacks.History at 0xa86d650>
损失函数
损失函数(loss function),是指一种将一个事件(在一个样本空间中的一个元素)映射到一个表达与其事件相关的经济成本或机会成本的实数上的一种函数,在统计学中损失函数是一种衡量损失和错误(这种损失与“错误地”估计有关,如费用或者设备的损失)程度的函数。
交叉熵(cross-entropy)就是神经网络中常用的损失函数。
交叉熵性质:
(1)非负性。
(2)当真实输出a与期望输出y接近的时候,代价函数接近于0.(比如y=0,a~0;y=1,a~1时,代价函数都接近0)。
一个比较简单的理解就是使得 预测值Yi和真实值Y’ 对接近,即两者的乘积越大,coss-entropy越小。
交叉熵和准确度变化图像可以看 TensorBoard 。
梯度下降
如果对于所有的权重和所有的偏置计算交叉熵的偏导数,就得到一个对于给定图像、标签和当前权重和偏置的「梯度」,如图所示:
我们希望损失函数最小,也就是需要到达交叉熵最小的凹点的低部。在上图中,交叉熵被表示为一个具有两个权重的函数。
而学习速率,即在梯度下降中的步伐大小。
#模型的测试误差指标 |
['loss', 'acc']
9800/10000 [============================>.] - ETA: 0s
[0.87580669939517974, 0.94387999653816224]
上面,我们探索了softmax对多分类的支持和理解,知道softmax可以作为一个输出成层进行多分类任务。
但是,这种分类任务解决的都是线性因素形成的问题,对于非线性的,特别是异或问题,如何解决呢?
这时,一种包含多层隐含层的深度神经网络的概念被提出。
3. 激活函数
激活函数(activation function)可以使得模型加入非线性因素的。
解决非线性问题有两个办法:线性变换、引入非线性函数。
(1)线性变换(linear transformation)
原本一个线性不可分的模型如:X^2 + Y^2 = 1
其图形如下图所示:
fig = plt.figure(0) |
[<matplotlib.lines.Line2D at 0x86c7510>]
将坐标轴进行高维变换,横坐标变成X^2,纵坐标变成 Y^2,这是表达式变为了 X + Y = 1,这样,原来的非线性问题,就变成了一个线性可分的问题,变成了一个简单的一元一次方程了。
详细可以参见下图:
fig2 = plt.figure(1) |
[<matplotlib.lines.Line2D at 0x984bf10>]
(2)引入非线性函数
异或是一种基于二进制的位运算,用符号XOR 表示(Python中的异或操作符为 ^ ),其运算法则是对运算符两侧数的每一个二进制位,同值取0,异值取1。
下面是一个典型的异或表:
table = {'x':[1,0,1,0],'y':[1,0,0,1]} |
x | y | z | |
---|---|---|---|
0 | 1 | 1 | 0 |
1 | 0 | 0 | 0 |
2 | 1 | 0 | 1 |
3 | 0 | 1 | 1 |
x = 1, y = 1, 则 z = 0
x = 0, y = 0, 则 z = 0
x = 1, y = 0, 则 z = 1
x = 0, y = 1, 则 z = 1
…
其图形如下:
fig3 = plt.figure(2) |
那么如果可以构建一个函数拟合这样的图形呢?即如何构建一个f(),使得:f(x,y)=z呢?
为了解决问题,我们来构建一个两层的神经网络,该神经网络有两个激活函数,F(x,y)和 H(x,y), 具体如下图所示:
F(x,y)为一个阈值为1的阈值函数:
即:当AX+BY>1时候,F(x,y) = 1;否则为0;if AX+BY > 1:
F = 1
else:
F = 0
H(x,y)为一个阈值为0的阈值函数:if AX+BY > 0:
H = 1
else:
H = 0
图中线的数字表示权重值,
- 对于(1,1)的点,第二层从左到右隐藏层的值分别为(1,1,1),最后输出为(1,1,1)*(1,-2,1)=0; |
first_hidder_layer_table = {'x':[1,0,1,0],'y':[1,0,0,0],'z':[1,0,0,1],'output':[0,0,1,1]} |
output | x | y | z | |
---|---|---|---|---|
0 | 0 | 1 | 1 | 1 |
1 | 0 | 0 | 0 | 0 |
2 | 1 | 1 | 0 | 0 |
3 | 1 | 0 | 0 | 1 |
这样我们就构建出了一个可以计算拟合的函数了。
我们观察一下第一个隐含层,其总共有三个维度,三个权重值,从输入层到第一层,实际上,就是从将一个二维的数组变成一个三维数组,从而实现线性切分。
图形化解释:
from mpl_toolkits.mplot3d import Axes3D |
<matplotlib.text.Text at 0xb7b0d90>
经过变换后的数据是线性可分的(n维,比如本例中可以用平面将两个不同颜色的点切分)
更多的操作可以参考tensorflow提供的一个神经网络的网页小程序,通过自己调整程序参数可以更深刻理解神经网络、激活函数的作用。
演示网址:
http://playground.tensorflow.org/
可以自己建立一个小型神经网络帮助理解。
4. sigmoid
sigmoid是一个用来做二分类的”S”形逻辑回归曲线
sigmoid公式:
sigmoid图像:
其抑制两头,对中间细微变化敏感,因此sigmoid函数作为最简单常用的神经网络激活层被使用。
优点:
(1)输出范围(0,1),数据在传递的过程中不容易发散
(2)单向递增
(3)易求导
sigmod有个缺点,sigmoid函数反向传播时,很容易就会出现梯度消失,在接近饱和区的时候,导数趋向0,会变得非常缓慢。因此,在优化器选择时选用Adam优化器。
Adam 也是基于梯度下降的方法,但是每次迭代参数的学习步长都有一个确定的范围,不会因为很大的梯度导致很大的学习步长,参数的值比较稳定。有利于降低模型收敛到局部最优的风险,而SGD容易收敛到局部最优,如果下面代码中的optimizer改成SGD的化,在一次epoch后就acc值不会改变了,陷入局部最优
|
____________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
====================================================================================================
dense_23 (Dense) (None, 200) 157000 dense_input_7[0][0]
____________________________________________________________________________________________________
activation_23 (Activation) (None, 200) 0 dense_23[0][0]
____________________________________________________________________________________________________
dense_24 (Dense) (None, 100) 20100 activation_23[0][0]
____________________________________________________________________________________________________
activation_24 (Activation) (None, 100) 0 dense_24[0][0]
____________________________________________________________________________________________________
dense_25 (Dense) (None, 60) 6060 activation_24[0][0]
____________________________________________________________________________________________________
activation_25 (Activation) (None, 60) 0 dense_25[0][0]
____________________________________________________________________________________________________
dense_26 (Dense) (None, 30) 1830 activation_25[0][0]
____________________________________________________________________________________________________
activation_26 (Activation) (None, 30) 0 dense_26[0][0]
____________________________________________________________________________________________________
dense_27 (Dense) (None, 10) 310 activation_26[0][0]
____________________________________________________________________________________________________
activation_27 (Activation) (None, 10) 0 dense_27[0][0]
====================================================================================================
Total params: 185,300
Trainable params: 185,300
Non-trainable params: 0
____________________________________________________________________________________________________
SVG(model_to_dot(model).create(prog='dot', format='svg')) |
tensorboard2 = TensorBoard(log_dir='/home/tensorflow/log/five_layer_sigmoid/epoch', histogram_freq=0) |
<keras.callbacks.History at 0xf868a90>
#模型的测试误差指标 |
['loss', 'acc']
9800/10000 [============================>.] - ETA: 0s
[0.036339853547979147, 0.98736999988555907]
根据上面,我们可以看出,深度越深,效果越好。
但是,对于深层网络,sigmoid函数反向传播时,很容易就会出现梯度消失的情况从而无法完成深层网络的训练。在sigmoid接近饱和区时,变换非常缓慢,导数趋于0,减缓收敛速度。
5. ReLu
ReLu来自于对人脑神经细胞工作时的稀疏性的研究,在 Lennie,P.(2003)提出人脑神经元有95%-99%是闲置的,而更少工作的神经元意味着更小的计算复杂度,更不容易过拟合
修正线性单元(Rectified linear unit,ReLU)公式:
$$ReLU\left(x\right)=
\left\lbrace
\begin{align}
x, \quad x \gt 0 \cr
0, \quad x \le 0
\end{align}
\right .$$
其图像:
ReLU具有线性、非饱和性,而其非饱和性使得网络可以自行引入稀疏性。
ReLU的使用解决了sigmoid梯度下降慢,深层网络的信息丢失的问题。
ReLU在训练时是非常脆弱的,并且可能会“死”。例如,经过ReLU神经元的一个大梯度可能导致权重更新后该神经元接收到任何数据点都不会再激活。如果发生这种情况,之后通过该单位点的梯度将永远是零。也就是说,ReLU可能会在训练过程中不可逆地死亡,并且破坏数据流形。如果学习率太高,大部分网络将会“死亡”(即,在整个训练过程中神经元都没有激活)。而设置一个适当的学习率,可以在一定程度上避免这一问题。
6. 学习速率
上面说梯度下降的时候,说过学习速率其实就是梯度下降的步伐。因此,为了到达山谷,需要控制步伐的大小,即学习速率。
学习速率大小的调节一般取决于 loss 的变化幅度。
# neural network with 5 layers |
____________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
====================================================================================================
dense_16 (Dense) (None, 200) 157000 dense_input_4[0][0]
____________________________________________________________________________________________________
activation_16 (Activation) (None, 200) 0 dense_16[0][0]
____________________________________________________________________________________________________
dense_17 (Dense) (None, 100) 20100 activation_16[0][0]
____________________________________________________________________________________________________
activation_17 (Activation) (None, 100) 0 dense_17[0][0]
____________________________________________________________________________________________________
dense_18 (Dense) (None, 60) 6060 activation_17[0][0]
____________________________________________________________________________________________________
activation_18 (Activation) (None, 60) 0 dense_18[0][0]
____________________________________________________________________________________________________
dense_19 (Dense) (None, 30) 1830 activation_18[0][0]
____________________________________________________________________________________________________
activation_19 (Activation) (None, 30) 0 dense_19[0][0]
____________________________________________________________________________________________________
dense_20 (Dense) (None, 10) 310 activation_19[0][0]
____________________________________________________________________________________________________
activation_20 (Activation) (None, 10) 0 dense_20[0][0]
====================================================================================================
Total params: 185,300
Trainable params: 185,300
Non-trainable params: 0
____________________________________________________________________________________________________
SVG(model_to_dot(model).create(prog='dot', format='svg')) |
tensorboard3 = TensorBoard(log_dir='/home/tensorflow/log/five_layer_relu/epoch', histogram_freq=0) |
<keras.callbacks.History at 0xe3c6d50>
#模型的测试误差指标 |
['loss', 'acc']
9600/10000 [===========================>..] - ETA: 0s
[0.017244604945910281, 0.99598000288009647]
7.Dropout
运行目录下的mnist_2.1_five_layers_relu_lrdecay.py
随着迭代次数的增加,我们可以发现测试数据的loss值和训练数据的loss存在着巨大的差距, 随着迭代次数增加,train loss 越来越好,但test loss 的结果确越来越差,test loss 和 train loss 差距越来越大,模型开始过拟合。
Dropout是指对于神经网络单元按照一定的概率将其暂时从网络中丢弃,从而解决过拟合问题。
可以对比mnist_2.1_five_layers_relu_lrdecay.py 和 加了dropout的/mnist_2.2_five_layers_relu_lrdecay_dropout.py的结果
# neural network with 5 layers |
____________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
====================================================================================================
dense_171 (Dense) (None, 200) 157000 dense_input_35[0][0]
____________________________________________________________________________________________________
activation_171 (Activation) (None, 200) 0 dense_171[0][0]
____________________________________________________________________________________________________
dense_172 (Dense) (None, 100) 20100 activation_171[0][0]
____________________________________________________________________________________________________
activation_172 (Activation) (None, 100) 0 dense_172[0][0]
____________________________________________________________________________________________________
dropout_100 (Dropout) (None, 100) 0 activation_172[0][0]
____________________________________________________________________________________________________
dense_173 (Dense) (None, 60) 6060 dropout_100[0][0]
____________________________________________________________________________________________________
activation_173 (Activation) (None, 60) 0 dense_173[0][0]
____________________________________________________________________________________________________
dropout_101 (Dropout) (None, 60) 0 activation_173[0][0]
____________________________________________________________________________________________________
dense_174 (Dense) (None, 30) 1830 dropout_101[0][0]
____________________________________________________________________________________________________
activation_174 (Activation) (None, 30) 0 dense_174[0][0]
____________________________________________________________________________________________________
dropout_102 (Dropout) (None, 30) 0 activation_174[0][0]
____________________________________________________________________________________________________
dense_175 (Dense) (None, 10) 310 dropout_102[0][0]
____________________________________________________________________________________________________
activation_175 (Activation) (None, 10) 0 dense_175[0][0]
====================================================================================================
Total params: 185,300
Trainable params: 185,300
Non-trainable params: 0
____________________________________________________________________________________________________
SVG(model_to_dot(model).create(prog='dot', format='svg')) |
tensorboard4 = TensorBoard(log_dir='/home/tensorflow/log/five_layer_relu_dropout/epoch') |
<keras.callbacks.History at 0x27819610>
#模型的测试误差指标 |
['loss', 'acc']
9900/10000 [============================>.] - ETA: 0s
[0.025450729207368569, 0.99462999999523161]