MNIST手写数字识别(二)几种模型优化方式介绍
日期: 2018-11-08 分类: 个人收藏 327次阅读
本篇的主要内容有:
- 动态衰减法设置可变学习率
- 为损失函数添加正则项
- 滑动平均模型介绍
为了让MNIST数字识别模型更准确,学习几种常用的模型优化手段:
学习率的优化
学习率的设置一定程度上也会影响模型的训练,如果学习率过小,那么将会经过很长时间才会收敛到想要的结果,反之,学习率过大则可能会导致不收敛的结果,比如:优化 J(x) = x^{2}, 如果设置学习率为 1,那么整个过程:
可以看到整个过程不管迭代多少次,都不会改变参数值了。这里多提醒一句,我前几天刚刚接触到线性模型也遇到了这个问题,之前处理数据,我使用的学习率都是0.2, 当时遇到的数据集是这样的:
train_x = np.array([3.3, 4.4,5.5,6.71,6.93,4.168,9.779,6.182,7.59,2.167,
7.042,10.791,5.313,7.997,5.654,9.27,3.1])
train_y = np.array([1.7,2.76,2.09,3.19,1.694,1.573,3.366,2.596,2.53,1.221,
2.827,3.465,1.65,2.904,2.42,2.94,1.3])
结果使用0.2 的学习率的使用就出问题了,模型无法收敛到一个正确的范围,当时纠结了好一会才意识到是学习率设置错了,毕竟数据的范围就已经很小了,再使用0.2就相对大了,所以一定要根据具体的数据进行设置学习率!
言归正传,在TensorFlow中,为了解决固定学习率的这种弊端,提供了一种很灵活的学习率设置方法–指数衰减法,使用这个方法可以先用较大的学习率得到一个比较好的位置(也就是图像上由比较“陡峭”的地方快速来到比较“平缓”的位置),随着迭代的过程,学习率会逐渐减小,从而使模型变得更稳定。
TensorFlow中实现了指数衰减法的函数是 tf.train.exponential_decay
,这个函数可以指数级地减小学习率,实现功能如下:
decay_learning_rate = learning_rate * decay_rate ^(global_step / decay_steps)
参数:
decayed_learning_rate: 每一轮结束后更新地学习率
learning_rate: 最初设置地学习率
decay_rate: 衰减系数
decay_steps: 衰减速度
样例
global_step = tf.Variable(0)
learning_rate = tf.train.exponential_decay(
0.1, global_step, 100, 0.96, staircase=True)
learning_step = tf.train.GradientDescentOptimizer(learning_rate).
minimize(loss, global_step=globalstep)
我们应用指数衰减法解决一个简单地线性回归问题:
首先在没有动态设置学习率的时候:
# 线性回归模型
import tensorflow as tf
import numpy as np
x_data = np.random.rand(100)
noise = np.random.normal(0.0, 0.02, x_data.shape)
y_data = 1.5 * x_data + 2.3 + noise # 定义 w: 1.5 b: 2.3
# 定义线性模型
k = tf.Variable(np.random.rand(1))
b = tf.Variable(np.random.rand(1))
y = k * x_data + b
# 损失函数以及优化器定义
learning_rate = 0.2
loss = tf.reduce_mean(tf.square(y - y_data))
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
train = optimizer.minimize(loss)
init_op = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init_op)
for i in range(201):
sess.run(train)
if i % 10 == 0: # 10次就输出一组 方便比较两次的收敛速度
print(sess.run([k, b]), learning_rate)
这时候的输出
[array([0.83753064]), array([1.47330253])] 0.2
[array([1.39016995]), array([2.3620908])] 0.2
[array([1.41398799]), array([2.3490679])] 0.2
[array([1.4323059]), array([2.33850712])] 0.2
[array([1.44657744]), array([2.33027885])] 0.2
[array([1.45769655]), array([2.32386812])] 0.2
[array([1.46635957]), array([2.31887346])] 0.2
[array([1.47310902]), array([2.31498206])] 0.2
[array([1.47836758]), array([2.31195023])] 0.2
[array([1.48246459]), array([2.3095881])] 0.2
[array([1.48565661]), array([2.30774774])] 0.2
[array([1.48814355]), array([2.30631389])] 0.2
[array([1.49008115]), array([2.30519677])] 0.2 w 更新到1.49
[array([1.49159075]), array([2.3043264])] 0.2
[array([1.4927669]), array([2.30364829])] 0.2
[array([1.49368325]), array([2.30311997])] 0.2
[array([1.49439719]), array([2.30270835])] 0.2
[array([1.49495343]), array([2.30238765])] 0.2
[array([1.4953868]), array([2.30213779])] 0.2
[array([1.49572444]), array([2.30194312])] 0.2
[array([1.4959875]), array([2.30179145])] 0.2
使用指数衰减法之后,修改损失函数部分:
# 损失函数以及优化器定义
global_steps = tf.Variable(0)
learning_rate = tf.train.exponential_decay(
0.2, global_steps, 100, 0.96, staircase=True)
loss = tf.reduce_mean(tf.square(y - y_data))
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
train = optimizer.minimize(loss, global_step=global_steps)
使用指数衰减法之后的输出:
[array([1.24186946]), array([1.37994397]), 0.2]
[array([1.64187716]), array([2.22352439]), 0.2]
[array([1.60681132]), array([2.24353267]), 0.2]
[array([1.58028094]), array([2.25804498]), 0.2]
[array([1.56040553]), array([2.26891649]), 0.2]
[array([1.54551588]), array([2.27706087]), 0.2]
[array([1.53436132]), array([2.28316223]), 0.2]
[array([1.52600488]), array([2.28773306]), 0.2]
[array([1.51974467]), array([2.29115729]), 0.2] w 更新到1.51左右
[array([1.51505484]), array([2.29372254]), 0.2]
[array([1.51155375]), array([2.29563758]), 0.192]
[array([1.50901135]), array([2.29702823]), 0.192]
[array([1.50708427]), array([2.29808231]), 0.192]
[array([1.50562359]), array([2.29888128]), 0.192]
[array([1.50451642]), array([2.29948688]), 0.192]
[array([1.50367722]), array([2.29994591]), 0.192]
[array([1.50304112]), array([2.30029384]), 0.192]
[array([1.50255898]), array([2.30055757]), 0.192]
[array([1.50219352]), array([2.30075746]), 0.192]
[array([1.50191652]), array([2.30090898]), 0.192]
[array([1.50170729]), array([2.30102343]), 0.18432]
可以看到,设置了动态学习率之后,模型更快地收敛到了同样准确率的地方。
过拟合问题的优化
过拟合现象与欠拟合现象相对,欠拟合是模型太弱,不能很好地对数据进行处理,过拟合则是模型学习能力太强,以至于记住了模型中的噪音部分,这样模型不具有“泛化”的能力,为了避免过拟合,一种非常常用的方法是正则化(regularization),简单来说就是修改原来的损失函数,加入模型复杂度衡量的指标,这时候,进行优化就是
J
(
θ
)
+
λ
R
(
w
)
J(\theta) + \lambda R(w)
J(θ)+λR(w),通常使用的这个 R(w) 有两种,一种是 L1 正则化,计算方式是:
R
(
w
)
=
∥
w
∥
1
=
∑
∣
w
∣
R(w) = \left \| w \right \|_{1} = \sum\left | w \right |
R(w)=∥w∥1=∑∣w∣
另一种是L2 正则化,计算方式:
R
(
w
)
=
∥
w
∥
1
2
=
∑
∣
w
∣
2
R(w)=\left \| w \right \|_{1}^{2} = \sum\left | w \right |^{2}
R(w)=∥w∥12=∑∣w∣2
两种方式都是通过限制权重的大小,使得模型不能任意地拟合训练数据中地随机噪音,其中L1正则化方法会使得参数变得稀疏(也就是会有更多地参数变为0)并且不能求导,所以感觉大部分使用地都是L2正则化方式,在TensorFlow中使用方式如下:
loss = tf.reduce_mean(tf.square(y - y_data)) + tf.contrib.layers.l2_regularizer(lambda)(w)
# 这就是定义的 l2 正则化
# lambda 是正则化系数 w 是 需要计算正则化的参数
# 如果手工计算一下某个权重数组的 L2 范式 会与这个函数的
# 输出不一样 因为这个函数会自动除以2 方便求导
滑动平均模型
滑动平均模型稳定模型的原理很简单,变量变化的同时会有一个与之对应的 shadow_variable 逼近变化后的变量值,这样,通过记录过去一段时间的平均值,消除数值波动对参数变化的影响,从而使得模型的泛化能力更好(提高 robust)。
在TensorFlow中提供了使用滑动平均的函数:
tf.train.ExponentialMovingAverage
# 调用结构:
tensorflow.python.training.moving_averages.ExponentialMovingAverage def __init__(self,
decay: Any, # 衰减率 用来控制模型更新的速度
num_updates: Any = None, # 模型迭代的轮数
zero_debias: bool = False,
name: str = "ExponentialMovingAverage") -> None
滑动平均模型对每一个变量都会维护一个影子变量(shadow_variable),或者称它为滑动平均,这个shadow_variable的初始值就是对应的变量的初始值,在每次运行时,shadow_variable的更新公式是:
shadow_variable = decay * shadow_variable + (1 - decay) * variable
从这个更新公式中可以看出来,decay 决定了模型的更新速度,decay较大的模型趋于稳定,在实际使用的时候一般会将decay设置为比较大(0.99左右),这样可以使模型前期更新较快,而后期趋于稳定,这就需要一个动态的decay,所以,函数还提供了num_undates 参数,可以用来动态设置decay,动态衰减率的更新公式是:
d
e
c
a
y
=
m
i
n
{
d
e
c
a
y
,
1
+
n
u
m
u
p
d
a
t
e
s
10
+
n
u
m
u
p
d
a
t
e
s
}
decay =min \begin{Bmatrix} decay ,& \frac{1+num_updates}{10+num_updates} \end{Bmatrix}
decay=min{decay,10+numupdates1+numupdates}
很显然,在上面的公式中在 num_updates 比较小的时候, decay会选择后者,之后随着训练次数的增加,decay会变大,也就使得模型趋于稳定。
下面看一个例子,演示滑动平均模型如何使用:
import tensorflow as tf
# 定义计算滑动平均的变量 变量的类型必须是 实数
v1 = tf.Variable(0, dtype=tf.float32)
# 模拟神经网络中迭代的轮数的变量
step = tf.Variable(10, trainable=False)
# 定义一个滑动平均的类 初始化的衰减率为0.99, 衰减率的变量是step
ema = tf.train.ExponentialMovingAverage(0.99, step)
# 定义每次滑动平均所更新的列表 这里
maintrain_average_op = ema.apply([v1])
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
# 更新 v1 的滑动平均值
# 衰减率为 min(0.99, (1+step) / (10 + step)=0.1) = 0.1
sess.run(maintrain_average_op)
# 下面演示三种情况下的滑动平均输出
# 1
# 刚开始滑动平均 与 原变量 值一样
print("1: ", sess.run([v1, ema.average(v1)]))
# 2
# 只变量更新为5 step保持不变
sess.run(tf.assign(v1, 5))
# 此时运行 decay会更新为 min(0.99, (1+step)/ (10 + step)=0.1) = 0.1
sess.run(maintrain_average_op)
print("2: ", sess.run([v1, ema.average(v1)]))
# 3
# 这里模拟训练中更为一般的情况:
# step v1 都改变
sess.run(tf.assign(v1, 10))
sess.run(tf.assign(step, 10000))
# 这时候计算的 decay 应该是 0.99 了
print("3: ",sess.run([v1, ema.average(v1)]))
输出:
1: [0.0, 0.0]
2: [5.0, 2.25]
3: [10.0, 2.25]
以上~
除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog
上一篇: 给初学者的链接器指南
下一篇: Shell 编程快速入门
精华推荐