#二次代价函数(quadratic cost)
- C是代价函数;
- x表示样本,y表示实际值,a表示输出值;
- n表示样本总数。
- 以一个样本为例进行说明,此时:
- a=δ(z),z=∑Wj*Xj+b;
- δ()是激活函数
假如我们使用梯度下降法(Gradient descent)来调整权值参数的大小,权值w和偏置b的梯度推导:
- z表示神经元的输入,δ表示激活函数;
- w和b的梯度跟激活函数的梯度成正比,激活函数的梯度越大,w和b的大小调整得越快,训练收敛得就越快。
假设激活函数为sigmoid函数:
- 假设目标是收敛到1,A点位0.82离目标较远,梯度较大,权值调整比较大。B点位0.98离目标比较近,梯度比较小,权值调整比较小。调整方案合理;
- 假设目标是收敛到0,A点位0.82离目标较近,梯度较大,权值调整比较大。B点位0.98离目标比较远,梯度比较小,权值调整比较小。调整方案不合理;
#交叉熵代价函数(cross-entropy)
换个思路,我们不改变激活函数,而是改变代价函数,改用交叉熵代价函数:
- C是代价函数,x表示样本,y表示实际值,a表示输出值,n表示样本的总数;
- a=δ(z),z=∑Wj*Xj+b;
- δ'(z)=δ(z)(1-δ(z))
最终得到:
- 权值和偏置值的调整与δ'(z)无关。此外,梯度公式中的 δ(z)-y 表示输出值与实际值的误差,所以当误差越大,梯度就越大,参数w和b的调整就越快,训练的速度也就越快;
- 如果输出神经元是线性的,则二次代价函数就是一种合适的选择。如果输出神经元是S型函数,那么比较适合用交叉熵代价函数。
#对数似然代价函数(log-likelihood cost)
对数似然函数常用来作为softmax回归的代价函数。
- 如果输出层神经元是sigmoid函数,则可以采用交叉熵代价函数。而深度学习中更普遍的做法是将softmax作为最后一层,此时常用的代价函数是对数似然代价函数。
- 对数似然代价函数与softmax的组合和交叉熵与sigmoid函数的组合非常相似。对数似然代价函数在二分类时可以化简为交叉熵代价函数的形式。
- 在tensorflow中:
- tg.nn.sigmoid_cross_entropy_with_logits() 来表示跟sigmoid搭配使用的交叉熵;
- tg.nn.softmax_cross_entropy_with_logits() 来表示跟softmax搭配使用的交叉熵。
修改【深度学习之tensorflow(二)】中的代码:
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y,logits=prediction))
执行结果:
*收敛变快,精确度变高。
#拟合
回归问题:
分类问题:
解决方法:
- 增加数据集
- 正则化方法:
- Dropout:
- 即训练时每次迭代只使用部分神经元
#Dropout
#创建一个复杂的神经网络
#W = tf.Variable(tf.zeros([784,10]))
#b = tf.Variable(tf.zeros([10]))
W1 = tf.Variable(tf.truncated_normal([784,2000],stddev=0.1))
b1 = tf.Variable(tf.zeros([2000])+0.1)
L1 = tf.nn.tanh(tf.matmul(x,W1)+b1)
L1_drop = tf.nn.dropout(L1,keep_drop)
W2 = tf.Variable(tf.truncated_normal([2000,2000],stddev=0.1))
b2 = tf.Variable(tf.zeros([2000])+0.1)
L2 = tf.nn.tanh(tf.matmul(L1_drop,W2)+b2)
L2_drop = tf.nn.dropout(L2,keep_drop)
W3 = tf.Variable(tf.truncated_normal([2000,1000],stddev=0.1))
b3 = tf.Variable(tf.zeros([1000])+0.1)
L3 = tf.nn.tanh(tf.matmul(L2_drop,W3)+b3)
L3_drop = tf.nn.dropout(L3,keep_drop)
W4 = tf.Variable(tf.truncated_normal([1000,10],stddev=0.1))
b4 = tf.Variable(tf.zeros([10])+0.1)
prediction = tf.nn.softmax(tf.matmul(L3_drop,W4)+b4)
#...
with tf.Session() as sess:
sess.run(init)
for epoch in range(31):
for batch in range(n_batch):
batch_xs,batch_ys = mnist.train.next_batch(batch_size)
sess.run(train_step,feed_dict={x:batch_xs,y:batch_ys,keep_drop:1.0})
test_acc = sess.run(accuracy,feed_dict={x:mnist.test.images,y:mnist.test.labels,keep_drop:1.0})
train_acc = sess.run(accuracy,feed_dict={x:mnist.train.images,y:mnist.train.labels,keep_drop:1.0})
print("Iter " + str(epoch) + ",Testing Accuracy " + str(test_acc) + ",Training Accuray " + str(train_acc))
没使用dropout之前:
使用dropout之后:
with tf.Session() as sess:
sess.run(init)
for epoch in range(31):
for batch in range(n_batch):
batch_xs,batch_ys = mnist.train.next_batch(batch_size)
sess.run(train_step,feed_dict={x:batch_xs,y:batch_ys,keep_drop:0.7})
test_acc = sess.run(accuracy,feed_dict={x:mnist.test.images,y:mnist.test.labels,keep_drop:1.0})
train_acc = sess.run(accuracy,feed_dict={x:mnist.train.images,y:mnist.train.labels,keep_drop:1.0})
print("Iter " + str(epoch) + ",Testing Accuracy " + str(test_acc) + ",Training Accuray " + str(train_acc))
*sess.run(train_step,feed_dict={x:batch_xs,y:batch_ys,keep_drop:0.7}) 中的0.7是指每一层都只有70%的神经元工作。
执行结果:
*使用dropout,速度会变慢。
*测试集精确度和训练集精确度的差异减小了,说明过拟合现象得到改善。
*dropout可以改善过拟合的问题!
#Optimizer(优化器)
- tf.train.GradientDescentOptimizer
- tf.train.AdadeltaOptimizer
- tf.train.AdagradOptimizer
- tf.train.AdagradDAOptimizer
- tf.train.MomentumOptimizer
- tf.train.AdamOptimizer
- tf.train.FtrlOptimizer
- tf.train.ProximalGradientDescentOptimizer
- tf.train.ProximalAdagradOptimizer
- tf.train.RMSPropOptimizer
各自优化器比较:
- 标准梯度下降法:标准梯度下降法先计算所有样本汇总误差,然后根据总误差来更新权值。
- 随机梯度下降法SGD:随机梯度下降随机抽取一个样本来计算误差,然后更新权值。
- 批量梯度下降法:批量梯度下降算是一种折中方案,从总样本中选取一个批次,然后计算这个batch的总误差,根据它来更新权值。
W:要训练的参数
J(W):代价函数
▽wJ(W):代价函数的梯度
η:学习率
SGD:
Momentum:
- 当前权值的改变会受到上一次权值改变的影响,类似于小球向下滚动的时候带上了惯性,这样可以加快小球向下的速度。
NAG(Nesterov Accelerated gradient):
- NAG在TF中跟Momentum合并在同一个函数tf.train.MonmentumOptimizer中,可以通过参数配置启用。
- 在Momentum中小球会盲目地跟从下坡的梯度,容易发生错误,所以我们需要一个更聪明的小球,这个小球提前知道它要去哪里,它还要知道走到坡底的时候速度慢下来而不是又冲上另一个坡。Yvt-1会用来修改W的值,计算W-Yvt-1可以表示小球下一个位置大概在哪里,从而我们可以提前计算下一个位置的梯度,然后使用到当前位置。
Adagrad:
i:代表第i个分类
t:代表出现次数
ε:作用是避免分母为0,取值一般为1e-8
η:取值一般为0.01
- 是基于SGD的一种算法,核心思想是对比较常见的数据给予较小的学习率去调整参数,对于比较罕见的数据给予较大的学习率去调整参数。
- 适合应用于数据稀疏的数据集(如一个图片数据集,有10000张狗的照片,10000张猫的照片,只有100张大象的照片)
- Adagrad主要优势在于不需要人为的调整学习率,它可以自动调节。
- 缺点在于,随着迭代次数的增多,学习率也会越来越低,最终会趋向于0。
RMSprop(Root Mean Square,均方根):
- RMSprop借鉴了一些Adagrad的思想,不过这里RMSprop只用到了前t-1次梯度平方的均值加上当前梯度的平方的和的开方作为学习率的分母。这样RMSprop不会出现学习率越来越低的问题,而且也能自己调节学习率,并且有一个比较好的效果。
Adadelta:
- 不需要设置一个默认学习率,在Adadelta不需要使用学习率也可以达到一个非常好的效果。
Adam:
- 就像Adadelta和RMSprop一样,Adam会存储之前衰减的平方梯度,同时它也会保存之前衰减的梯度,经过一些处理之后再使用类似Adadelta和RMSprop的方式更新参数。
综上:
#优化器
train_step = tf.train.AdamOptimizer(1e-2).minimize(loss)
- 一般使用AdamOptimizer时,学习率都会调得比较小,但是收敛速度明显比梯度下降快。
执行结果:
#作业
继续优化,把识别准确率提升到98%以上。
Tip:
1、交叉熵可以更快地收敛;
2、权值可以使用迭代的正态分布来初始化,偏置值的初始化一般为0.1;
3、调节神经元的层数和个数;
4、dropout;
5、其他的激活函数;
6、不同的优化器,调节学习率。
#自我实验
1、条件:
- 批次:batch_size = 100;
- 一个隐藏层,个数1000;
-
使用交叉熵;
-
使用AdadeltaOptimizer,学习率0.01;
-
for loop:21次
执行结果:
2、条件:
- 批次:batch_size = 50;
- 一个隐藏层,个数1000;
-
使用交叉熵;
-
使用AdadeltaOptimizer,学习率0.01;
-
for loop:51次
执行结果:
3、条件:
- 批次:batch_size = 50;
- 两个隐藏层,个数1000;
-
使用交叉熵;
-
使用AdadeltaOptimizer,学习率0.01;
-
for loop:31次
执行结果:
4、条件:
- 批次:batch_size = 50;
- 两个隐藏层,个数2000;
-
使用交叉熵;
-
使用AdadeltaOptimizer,学习率0.01;
-
for loop:31次
执行结果:
*非常慢!
5、条件:
- 批次:batch_size = 50;
- 两个隐藏层,个数1000;
-
使用交叉熵;
-
使用AdadeltaOptimizer,学习率0.1;
-
for loop:31次
执行结果:
*速度很快!
6、条件:
- 批次:batch_size = 50;
- 两个隐藏层,个数1000;
-
使用交叉熵;
-
使用AdadeltaOptimizer,学习率0.3;
-
for loop:31次
执行结果:
#参考答案
- 批次:batch_size = 100;
- 定义了一个学习率变量,每迭代一次就给学习率赋一次值,使学习率刚开始时是一个较大的值,随训练过程逐渐减小;
- 两个隐藏层,第一个个数500,第二个个数300;
- 没使用dropout;
-
使用交叉熵;
-
使用AdamOptimizer;
-
for loop:51次。
执行结果:
PS.此为学习《深度学习框架Tensorflow学习与应用》课程的笔记。【http://www.bilibili.com/video/av20542427/?share_source=copy_link&p=4&ts=1551709559&share_medium=iphone&bbid=7db773463cc4248e755f030556bc67d1】