【数据极客】Week3 任务
- 学习率(Learning Rate) η 下降步长
−∇L(θ)=[−∂L(θ1)/∂(θ1)∂L(θ2)/∂(θ2)] 负梯度方向
∇L(θ0) 代表了在 θ0 点处的等高线的法线方向, 也是该梯度是最大上升方向, 前面加上符号就成了, 最大下降方向, 即: −∇L(θ0)
从 θ0 点, 沿着在该点的负梯度方向上, 移动 −η∇L(θ0) 的距离, 最终到达 θ1 点。
t
代表时间, 随着时间的增长, 学习率在不断减小。Adagrad
上面提到的普通的随机梯度下降算法对于所有参数都使用了同一个更新速率。但是同一个更新速率不一定适合所有参数。比如有的参数可能已经到了仅需要微调的阶段,但又有些参数由于对应样本少等原因,还需要较大幅度的调动。
而在Adagrad的更新规则中,学习率 η 会随着每次迭代根据历史梯度的变化而变化。
ηt=ηt+1√
σt=1t+1∑ti=0‾‾‾‾‾‾‾‾‾√(gi)2
wt+1=wt−ηtσtgt
分子分布约分之后得到 wt+1=wt−η∑ti=0(gi)2√gt
∑ti=0(gi)2 代表前 t
步梯度平方的累加和。
这里分母可能为0, 所以在分母位置加上一个极小值 ϵ , 变为 wt+1=wt−η∑ti=0(gi)2+ϵ√gt
可以看到算法不断的迭, 分母会越来越大, 整体的学习率会越来越小。
随机梯度下降
θi=θi−1−η∇Ln(θi−1)
迭代得到下一个点。
在我们面对多特征问题时,我们要保证这些特征都具有相似的尺度,这将帮助梯度下降算法更快的收敛。
特征放缩之后所有纬度的均值为0, 方差为1
梯度下降背后的数学
Keras
的底层库使用TensorFlow
或 Theano
,这两个库也称为Keras
的后端。
pip install keras
batch_size 太小会导致训练慢,过拟合等问题,太大会导致欠拟合。所以要适当选择
Hello World of Deep Learning - Mnist Works with Keras
import tensorflow as tf
import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Activation
from keras import backend as K
import numpy
Using TensorFlow backend.
# Training Data
# MNIST Data: http://yann.lecun.com/exdb/mnist/
# Keras provides data sets loading function: http://keras.io/datasets/
# 训练集,测试集
(X_train, y_train), (X_test, y_test) = mnist.load_data() # 使用Keras自带的mnist工具读取数据(第一次需要联网)
X_train = X_train.reshape(X_train.shape[0], X_train.shape[1] * X_train.shape[2]) # 由于mist的输入数据维度是(num, 28, 28),这里需要把后面的维度直接拼起来变成784维
X_test = X_test.reshape(X_test.shape[0], X_test.shape[1] * X_test.shape[2])
Y_train = (numpy.arange(10) == y_train[:, None]).astype(int) #把index转换成一个one hot的矩阵
Y_test = (numpy.arange(10) == y_test[:, None]).astype(int)
print(X_train.shape)
print(Y_train.shape)
print(X_test.shape)
print(Y_test.shape)
print('Rows: %d, columns: %d' % (X_train.shape[0], X_train.shape[1]))
(60000, 784)
(60000, 10)
(10000, 784)
(10000, 10)
Rows: 60000, columns: 784
# 1. 构建网络
model = Sequential()
# 对所有像素使用全连接层 Dense
model.add(Dense(input_dim=784, output_dim=500)) # 第一层
model.add(Activation('sigmoid')) # 激活函数 sigmoid
model.add(Dense(output_dim=500)) # 第二层
model.add(Activation('sigmoid')) # 激活函数 sigmoid
model.add(Dense(output_dim=10)) # 第三层的输出为 10个类别 对应0到9
model.add(Activation('softmax'))# 激活函数 softmax
/Users/anthony/anaconda3/lib/python3.6/site-packages/ipykernel/__main__.py:4: UserWarning: Update your `Dense` call to the Keras 2 API: `Dense(input_dim=784, units=500)`
/Users/anthony/anaconda3/lib/python3.6/site-packages/ipykernel/__main__.py:7: UserWarning: Update your `Dense` call to the Keras 2 API: `Dense(units=500)`
/Users/anthony/anaconda3/lib/python3.6/site-packages/ipykernel/__main__.py:10: UserWarning: Update your `Dense` call to the Keras 2 API: `Dense(units=10)`
model.summary()
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense_1 (Dense) (None, 500) 392500
_________________________________________________________________
activation_1 (Activation) (None, 500) 0
_________________________________________________________________
dense_2 (Dense) (None, 500) 250500
_________________________________________________________________
activation_2 (Activation) (None, 500) 0
_________________________________________________________________
dense_3 (Dense) (None, 10) 5010
_________________________________________________________________
activation_3 (Activation) (None, 10) 0
=================================================================
Total params: 648,010
Trainable params: 648,010
Non-trainable params: 0
_________________________________________________________________
# 2. 构建损失方法 loss
# loss:字符串(预定义损失函数名)或目标函数,参考损失函数
# 3.1 优化方法 optimizer
#optimizer:字符串(预定义优化器名)或优化器对象,参考优化器
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
#metrics:列表,包含评估模型在训练和测试时的网络性能的指标
# 3.2 训练 得到最优参数
# batch_size 太小会导致训练慢,过拟合等问题,太大会导致欠拟合。所以要适当选择
# nb_epoch就是最多迭代的次数, shuffle就是是否把数据随机打乱之后再进行训练
# verbose是屏显模式,官方这么说的:verbose: 0 for no logging to stdout, 1 for progress bar logging, 2 for one log line per epoch.
# 就是说0是不屏显,1是显示一个进度条,2是每个epoch都显示一行数据
# validation_split就是拿出百分之多少用来做交叉验证
model.fit(X_train, Y_train, batch_size=200, nb_epoch=100, shuffle=True, verbose=1, validation_split=0.3)
model.evaluate(X_test, Y_test, batch_size=200, verbose=1)
# nb_epoch 在新的API里面变为 epochs
/Users/anthony/anaconda3/lib/python3.6/site-packages/keras/models.py:848: UserWarning: The `nb_epoch` argument in `fit` has been renamed `epochs`.
warnings.warn('The `nb_epoch` argument in `fit` '
Train on 42000 samples, validate on 18000 samples
Epoch 1/100
42000/42000 [==============================] - 3s - loss: 0.4860 - acc: 0.8681 - val_loss: 0.2842 - val_acc: 0.9185
Epoch 2/100
42000/42000 [==============================] - 3s - loss: 0.2586 - acc: 0.9250 - val_loss: 0.2413 - val_acc: 0.9299
Epoch 3/100
42000/42000 [==============================] - 3s - loss: 0.2245 - acc: 0.9341 - val_loss: 0.2152 - val_acc: 0.9372
省略多行
Epoch 99/100
42000/42000 [==============================] - 2s - loss: 0.0401 - acc: 0.9867 - val_loss: 0.1058 - val_acc: 0.9693
Epoch 100/100
42000/42000 [==============================] - 2s - loss: 0.0400 - acc: 0.9858 - val_loss: 0.1086 - val_acc: 0.9674
9000/10000 [==========================>...] - ETA: 0s
[0.095627619931474325, 0.97119999766349796]
常规的神经网络输入层input layer
,会经过多个隐层, 每个隐层有多个神经元组成, 每个神经元都与前一层的神经元两两相连接, 最后的输出层output layer
是全连接层, 实现分类, 输出每个类别的分值, 作为分类的判断依据。
全连接在隐层节点很多的时候,会使得权重数量过多,会存在过拟合问题。
在该网络中以三维的方式排列其神经元,三个维度分别为长、宽、深度,作为卷积层的输入是三维的(3D), 输出则也是三维的(3D).
如果输入是图片, 长、宽为图片的长与宽, 深度对应于图片的三个颜色通道(红、绿、蓝)。
与普通神经网络中的全连接层相比, 卷积神经网络通过过滤器Filter,得到特征图Feature Map, 其次通过池化 Max Pooling操作, 起到了下采样的效果。
卷积神经网络由一组有序的n个(卷积层+激励层+池化层)+ Full Connected Layer
上图展示了 5∗5∗3 的卷积核在 32∗32∗3 的图像上卷积的过程,将filter
在整个空间位置滑动并做内积(卷积)运算。最后得到结果是Feature Map 特征图谱。
输入大小 N=7 , 过滤器大小 F=3 , 滑动步长为 stride=1 , 计算该层卷积操作之后的输出结果为多少?
此时 (7−3)/1+1=3 , 即 7∗7 大小的图片, 经过大小为3的过滤器, 将得到 5∗5 大小的特征图Feature Map。
步长 stride
每次滑动数据窗口filter的单位长度。
0填充边缘 Zero-Padding
比如现在有一个 4∗4 的数据块, 现在要用 3∗3 的窗口,使用 stride=2 进行滑动,显然是不行的。
使用0来填充边缘, 使得满足滑动要求。
将卷积层结果做非线性映射
更多的激活函数 Activation Function 请见上周作业。
在每个颜色对应的数据窗口中取最大值。
和普通的神经网络中的全连接层的作用和效果是一样的, 不再赘述了。位于卷积神经网络的尾部。
因为深层神经网络在做非线性变换前的激活输入值(就是那个x=WU+B,U是输入)随着网络深度加深或者在训练过程中,其分布逐渐发生偏移或者变动,之所以训练收敛慢,一般是整体分布逐渐往非线性函数的取值区间的上下限两端靠近(对于Sigmoid函数来说,意味着激活输入值WU+B是大的负值或正值),所以这导致后向传播时低层神经网络的梯度消失,这是训练深层神经网络收敛越来越慢的本质原因。
benefit:
BN的目的就是把每层神经网络任意神经元这个输入值的分布强行拉回到【均值为0方差为1的标准正太分布】而不是【正态分布】,其实就是把越来越偏的分布强制拉回比较标准的分布。
这样使得激活输入值落在非线性函数对输入比较敏感的区域,这样输入的小变化就会导致损失函数较大的变化,意思是这样让梯度变大,避免梯度消失问题产生,而且梯度变大意味着学习收敛速度快,能大大加快训练速度。
卷积核(Convolution Kernel)具有的一个属性就是局部性。即它只关注局部特征,局部的程度取决于卷积核的大小,目的是提取图像邻近像素的相似性。
目的: 减少了参数量,降低过拟合。
目的:池化的目的是识别同类不同表现,最大池化的原理就是取出每个滑动窗口中最大的值(Max Pooling),这样就可以适应每个窗口内的变化,也能降低输出参数量。
可能会造成反复震荡, 无法到达局部最小点。
训练速度太慢。
L1是向量各元素的绝对值之和。
L1正则化往往会使学到的模型很稀疏(系数权重经常为0), 可以用来做特征选择。
L2是向量各元素的平方和。
由于L2惩罚想中的系数是二次方的,L2正则化会让系数的取值变得平均。系数之和为常数时, 各系数相等时惩罚是最小的,所以L2系数趋于相同; 表征能力强的特征对应的系数是非零的。
L1和L2的异同点
Dropout是指在模型训练时随机让网络某些隐含层节点的权重不工作,不工作的那些节点可以暂时认为不是网络结构的一部分,但是它的权重得保留下来(只是暂时不更新而已),因为下次样本输入时它可能又得工作了。
由于每次用输入网络的样本进行权值更新时,隐含节点都是以一定概率随机出现,因此不能保证每2个隐含节点每次都同时出现,这样权值的更新不再依赖于有固定关系隐含节点的共同作用,阻止了某些特征仅仅在其它特定特征下才有效果的情况。
- Covariance shift
when the input distribution to a learning system changes, it is said to experience covariance shift.
假设x是属于特征空间的某一样本点,y是标签。covariate这个词,其实就是指这里的x,那么covariate shift可以直接根据字面意思去理解:样本点x的变化。
- Internal Covariate Shift
当这种现象发生在多层神经网络之间时,即前面层参数的改变,使得后面层的输入分布发生改变时,就叫Internal covariance shift。
- 总结
对于深度学习这种包含很多隐层的网络结构,在训练过程中,因为各层参数老在变,所以每个隐层都会面临covariate shift的问题,也就是在训练过程中,隐层的输入分布老是变来变去,这就是所谓的“Internal Covariate Shift”,Internal指的是深层网络的隐层,是发生在网络内部的事情,而不是covariate shift问题只发生在输入层。
前面层的参数变化会影响当前层的输入分布,缺点:
因此我们想到,如果前面层的输出activation(即当前层的输入input)的分布是一定的呢?那样就会减少下一层输入陷入sigmoid饱和非线性区域的可能,梯度不再容易消失,从而加快训练速度。
解决办法:
对某一层的输出activations进行白化操作(零均值、单位方差、去相关),可以使得这些activations的分布保持恒定,从而减小internal covariance shift对下一层的影响。
具体可以参照以下资料:
https://zhuanlan.zhihu.com/p/26352087
http://blog.csdn.net/mao_xiao_feng/article/details/53150037
http://blog.csdn.net/mao_xiao_feng/article/details/54317852
http://blog.csdn.net/panglinzhuo/article/details/77531913
http://blog.csdn.net/zhikangfu/article/details/53391840
https://www.zhihu.com/question/24529483
因为神经网络具有很多参数,而训练过程中,如果对这些参数不加以限制,它们很可能会在训练出的模型中含带有训练数据的特性。我觉得可以说成是神经网络参数权重记住了训练数据的样子。
将数据集拆分成多个批次(batch),随机抽取一个批次计算并更新参数。
收敛速度快。
在梯度下降法中的一种加速算法。
更新时候会保留之前的更新方向,利用当前的批次再微调本次的更新参数, 因此引入一个新的变量v速度, 作为前几次梯度的累加。【参考《Tensorflow技术解析与实战》】
对于一般的SGD, 表达式可以写为 x=x−α∗dx , x 沿着负梯度方向下降。 而带monmentum项的SGD形式如下: v=β∗v−α∗dx
即: x=x+v
其中系数 β 就是动量系数, 如果上一次的动量(即 v )与这次的负梯度方向是相同的,同方向, 相当于在相同行驶方向的汽车, 踩了一脚油门, 下降速度就会加快。
能够为哥哥参数分配不同的学习率, 能够控制每个维度的梯度方向。如果本次更新梯度大, 学习率就衰减得快一些,如果更新时候梯度较小, 学习率衰减就慢一点。
机器之心:Adam优化算法
LeNet5 就是 LeCun 网络(Net), 共有5层网络。
输入图像大小为 32∗32 的灰度图像
该层有6个卷积核, 卷积核大小为 5∗5
共有 6∗(5∗5+1)=156 个 w 参数, 其中 1 代表一个 bias
, 经过卷积操作之后, 得到 28∗28∗6 的特征图谱。
(N−F)/stride+1=卷积后的特征图谱大小
这里 N=32 F=5 stride=1 得到 (32-5)/1 + 1 = 28
所以特征图谱尺寸为 28∗28
该层是大小的 2∗2 的平均池化层。
通过该层降采样之后,得到特征图谱尺寸变为上一层的 12 , 即: 14∗14∗6 , 再使用非线性 Sigmod 激活函数进行映射。
该层有16个卷积核, 大小为 5∗5 ,经过计算可以得到特征图谱为 10∗10∗16
该层同S2层一样, 同样使用 2∗2 的平均池化层,通过该层降采样之后,得到特征图谱尺寸变为上一层的 12 ,
即: 5∗5∗16
该层有120个大小为 5∗5 的卷积核,和前面一层的输出是同样大小的, 所以相当于构成全连接层,就是卷积核在进行卷积的过程中, 只需要遍历一次, 就能将同样大小 5∗5 的数据块卷积完成。
F6
全连接层, 含有84个隐层节点, 激活函数 Sigmoid
OUTPUT
输出10类分类结果, 0——>9 共10个分类结果。
总结
Lenet5 含有三个卷积层,两个池化层,再加上1个全连接层。
每个卷积层都包含: 卷积、池化、非线性激活函数映射
有关 LeNet5的介绍,请参照【思考问题】中的4.10回答
在MNIST 写数字数据集上,使 LeNet5模型进 训练和测试,评估准确率。
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt
%matplotlib inline
#Mnist数据集
# one_hot=True会使得 label由1维变位10维
mnist = input_data.read_data_sets("MNIST_Data/data/", one_hot=True)
Extracting MNIST_Data/data/train-images-idx3-ubyte.gz
Extracting MNIST_Data/data/train-labels-idx1-ubyte.gz
Extracting MNIST_Data/data/t10k-images-idx3-ubyte.gz
Extracting MNIST_Data/data/t10k-labels-idx1-ubyte.gz
img = mnist.train.images[66].reshape(28,28);
plt.imshow(img)
sess = tf.InteractiveSession()
#构造权重函数,标准差0.1, 给权重制造一些随机的噪声
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1);
return tf.Variable(initial)
#给偏置bias增加0.1, 防止死亡节点
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
# 构造二维卷积函数
# tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None, name=None)
# input 参数
# an input tensor of shape [batch, in_height, in_width, in_channels]
# [一个batch的图片数量, 图片高度, 图片宽度, 图像通道数],这是一个4维的Tensor,要求类型为float32和float64其中之一
# filter 参数
# 相当于CNN中的卷积核, [filter_height, filter_width, in_channels, out_channels],
# [卷积核的高度,卷积核的宽度,图像通道数,卷积核个数]
# 要求类型与参数input相同,有一个地方需要注意,第三维in_channels,就是参数input的第四维
# strides 参数
# 卷积时在图像每一维的步长,这是一个一维的向量,长度4
# padding 参数
# 边界的处理方式,SAME代表边界加上padding让卷积的输入和输出保持同样的尺寸。
def conv2d(x, W):
return tf.nn.conv2d(x, W, strides=[1,1,1,1], padding='SAME')
# ksize 使用 2*2 最大化池, 保留像素值最大的点
# ksize 池化窗口的大小,取一个四维向量,一般是[1, height, width, 1],
# 因为我们不想在batch和channels上做池化,所以这两个维度设为了1,即横纵两个方向上步长为2进行遍历
def max_pool_2x2(x):
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
#定义 Input 向量格式
#变形函数tf.reshape将数据转换为 28*28 ,单通道颜色
# -1 代表样本数量未知 , 1 代表单通道颜色
_X = tf.placeholder(tf.float32, [None, 784])
_y = tf.placeholder(tf.float32, [None, 10])
X_image = tf.reshape(_X, [-1,28,28,1]); # 将 1*784 转换为 28*28 结构
W_conv1 = weight_variable([5,5,1,32]) # 卷积核的尺寸;1代表单通道颜色;6代表卷积核数量,就是最后提取特征的类别6特种
b_conv1 = bias_variable([32]) # 偏置
h_conv1 = tf.nn.relu(conv2d(X_image, W_conv1) + b_conv1)# 卷积 + 非线性
print(h_conv1.get_shape()) #第一次卷积之后输出数据格式
h_pool1 = max_pool_2x2(h_conv1)#第一次最大池化
(?, 28, 28, 32)
查看第一最大池化之后, 可以看到图像大小由 28∗28∗32 边为 14∗14∗32
print(h_pool1.get_shape())
(?, 14, 14, 32)
W_conv2 = weight_variable([5, 5, 32,64]) # 使用64个卷积核
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)# 第二次卷积 + 非线性
print(h_conv2.get_shape()) # 第二次卷积之后输出数据格式
h_pool2 = max_pool_2x2(h_conv2)
print(h_pool2.get_shape()) # 第二次最大池化
(?, 14, 14, 64)
(?, 7, 7, 64)
隐含节点1024个
W_fc1 = weight_variable([7*7*64, 1024])
print(W_fc1.get_shape())
b_fc1 = bias_variable([1024])
h_pool2_flatten = tf.reshape(h_pool2, [-1, 7*7*64]) # 将第二次最大池化后的结果重构为 一维向量
print(h_pool2_flatten.get_shape())
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flatten, W_fc1) + b_fc1) # 全连接层 非线性
(3136, 1024)
(?, 3136)
print(h_fc1.get_shape())
(?, 1024)
训练时随机丢弃一部分节点的数据, 减轻过拟合
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
W_fc2 = weight_variable([1024,10])
b_fc2 = bias_variable([10])
y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
cross_entropy = tf.reduce_mean(-tf.reduce_sum(_y * tf.log(y_conv), reduction_indices=[1]))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
# accuracy函数
correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(_y, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
tf.global_variables_initializer().run()
# 训练20000次 每次大小为50的mini-batch 每100次训练查看训练结果 用以实时监测模型性能
for i in range(20000):
batch = mnist.train.next_batch(50)
if i % 100 == 0:
train_accuracy = accuracy.eval(feed_dict={_X: batch[0], _y: batch[1], keep_prob: 1.0})
print("step %d, train_accuracy %g" % (i, train_accuracy))
train_step.run(feed_dict={_X: batch[0], _y: batch[1], keep_prob: 0.5})
print("test accuracy %g" % accuracy.eval(feed_dict={
_X: mnist.test.images, _y: mnist.test.labels, keep_prob: 1.0
}))
step 0, train_accuracy 0.08
step 100, train_accuracy 0.8
step 200, train_accuracy 0.88
step 300, train_accuracy 0.86
step 400, train_accuracy 0.94
省略很多很多行
step 19500, train_accuracy 1
step 19600, train_accuracy 1
step 19700, train_accuracy 1
step 19800, train_accuracy 1
step 19900, train_accuracy 1
test accuracy 0.9924
图片分类数据集:CIFAR-10 dataset 链接:http://www.cs.toronto.edu/~kriz/cifar.html)
http://blog.csdn.net/joshuaxx316/article/details/52062291
标准差
梯度下降背后的数学
http://www.cnblogs.com/happylion/p/4172632.html
tf.nn.conv2d是怎样实现卷积
https://www.zhihu.com/question/49376084
https://zh.wikipedia.org/wiki/%E5%8D%B7%E7%A7%AF
Dropout
防止过拟合
momentum