1. 学习率的设置
我们知道在参数的学习主要是通过反向传播和梯度下降,而其中梯度下降的学习率设置方法是指数衰减。
通过指数衰减的学习率既可以让模型在训练的前期快速接近较优解,又可以保证模型在训练后期不会有太大的波动,从而更加接近局部的最优解。
在tensorflow中提供了一个灵活的学习率设置方法,tf.train.exponential_decay
函数实现了指数衰减,其实现的原理如下:
decayed_learning_rate=learning_rate*decay_rate^(global_step/decay_steps)
其中:
decayed_learning_rate:每一轮优化时使用的学习率
learning_rate:事先设定的初始学习率
decay_rate:衰减系数
decay_steps:衰减速度
tf.train.exponential_decay函数还可以通过设置参数staircase 选择不同的衰减方式。
- staircase默认值是false,这时的学习率会随迭代的轮数成平滑的衰减下降,这里的不同训练数据有不同的学习率。
- staircase默认值是true,(global_step/decay_steps)会被转化为整数,这时的学习率会随着轮数成阶梯状的下降,在这种设置下,decay_steps指完整的使用一遍训练数据所需要的迭代轮数(总的训练样本数处以每一个batch中的训练样本数),这里的意思就是每完整的过完一遍训练数据,学习率就减少一次,这可以使得训练集中的所有数据对模型训练有相等的作用。如下图:
tensorflow使用如下:
LEARNING_RATE = tf.train.exponential_decay(0.1, global_step, 1, 0.96, staircase=True)
train_op = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(y, global_step=global_step)
示例:
import tensorflow as tf
from numpy.random import RandomState
# 假设我们要最小化函数 y=x^2 , 选择初始点 x0=5
TRAINING_STEPS = 100
LEARNING_RATE = 1
x = tf.Variable(tf.constant(5, dtype=tf.float32), name="x")
y = tf.square(x)
train_op = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(y)
global_step = tf.Variable(0)
LEARNING_RATE = tf.train.exponential_decay(0.1, global_step, 1, 0.96, staircase=True)
x = tf.Variable(tf.constant(5, dtype=tf.float32), name="x")
y = tf.square(x)
train_op = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(y, global_step=global_step)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for i in range(TRAINING_STEPS):
sess.run(train_op)
if i % 10 == 0:
LEARNING_RATE_value = sess.run(LEARNING_RATE)
x_value = sess.run(x)
print ("After %s iteration(s): x%s is %f, learning rate is %f." \
% (i+1, i+1, x_value, LEARNING_RATE_value))
运行结果:
After 1 iteration(s): x1 is 4.000000, learning rate is 0.096000.
After 11 iteration(s): x11 is 0.690561, learning rate is 0.063824.
After 21 iteration(s): x21 is 0.222583, learning rate is 0.042432.
After 31 iteration(s): x31 is 0.106405, learning rate is 0.028210.
After 41 iteration(s): x41 is 0.065548, learning rate is 0.018755.
After 51 iteration(s): x51 is 0.047625, learning rate is 0.012469.
After 61 iteration(s): x61 is 0.038558, learning rate is 0.008290.
After 71 iteration(s): x71 is 0.033523, learning rate is 0.005511.
After 81 iteration(s): x81 is 0.030553, learning rate is 0.003664.
After 91 iteration(s): x91 is 0.028727, learning rate is 0.002436.
2. 过拟合
通常解决过拟合的方法是正则化,正则化就是在损失函数中加入刻画模型复杂程度的指标。
具体就是:如果损失函数是
J(θ)
J
(
θ
)
,那么在优化时不直接优化
J(θ)
J
(
θ
)
,而是优化
J(θ)+λR(w)
J
(
θ
)
+
λ
R
(
w
)
,其中
R(w)
R
(
w
)
就是刻画模型的复杂度,而
λ
λ
就是表示模型复杂损失在总损失中的比例。常用来刻画模型复杂度的函数
R(w)
R
(
w
)
有2种:
L1正则化:
R(w)=||w||1=∑i|wi|
R
(
w
)
=
|
|
w
|
|
1
=
∑
i
|
w
i
|
L2正则化:
R(w)=||wi||22=∑i|w2i|
R
(
w
)
=
|
|
w
i
|
|
2
2
=
∑
i
|
w
i
2
|
这两种正则化的区别在于L1正则化会让参数变得稀疏(指更多的参数变为0,有点特征提取的意思),而L2正则化则不会,因为它会让系数变得非常小,但不至于变为0。但是在优化时需要对损失函数求偏导,所以常用的是L2正则化。
这里还可以同时用两种正则化:
R(w)=∑ia|wi|+(1−a)w2i
R
(
w
)
=
∑
i
a
|
w
i
|
+
(
1
−
a
)
w
i
2
tensorflow中提供了tf.contrib.layers.l2_regularizer(lambda1)(var)
函数,它可以返回一个函数,这个函数可以计算一个给定参数的L2正则化项的值。
例如:
weights=tf.constant([[1.0,2.0],[-3.0,4.0]])
with tf.Session() as sess:
# 输出:(|1|+|-2|+|-3|+|4|)x0.5=5
print sess.run(tf.contrib.layers.l1_regularizer(0.5)(wights))
当网络复杂时,这种方法就不行了,这时可以通过使用tensorflow中提供的集合(在一个计算图中保存一组实体)来解决。
示例:
import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
# 生成模拟数据集
data = []
label = []
np.random.seed(0)
# 以原点为圆心,半径为1的圆把散点划分成红蓝两部分,并加入随机噪音。
for i in range(150):
x1 = np.random.uniform(-1,1)
x2 = np.random.uniform(0,2)
if x1**2 + x2**2 <= 1:
data.append([np.random.normal(x1, 0.1),np.random.normal(x2,0.1)])
label.append(0)
else:
data.append([np.random.normal(x1, 0.1), np.random.normal(x2, 0.1)])
label.append(1)
data = np.hstack(data).reshape(-1,2)
label = np.hstack(label).reshape(-1, 1)
plt.scatter(data[:,0], data[:,1], c=np.squeeze(label),
cmap="RdBu", vmin=-.2, vmax=1.2, edgecolor="white")
plt.show()
# 定义一个获取权重,并自动加入正则项到损失的函数
def get_weight(shape, lambda1):
var = tf.Variable(tf.random_normal(shape), dtype=tf.float32)
# 下面的函数把新生成变量的L2正则化损失加入集合:第一个参数是集合的名字,第二个参数是要加入的集合
tf.add_to_collection('losses', tf.contrib.layers.l2_regularizer(lambda1)(var))
print('var:',var)
return var
####### 定义神经网络#############
# 输入数据是二维特征
x = tf.placeholder(tf.float32, shape=(None, 2))
y_ = tf.placeholder(tf.float32, shape=(None, 1))
sample_size = len(data)
# 每层节点的个数,这里是5层的神经网络
layer_dimension = [2,10,5,3,1]
n_layers = len(layer_dimension)
# 该变量维护前向传播时最深层的节点,开始的时候就是输入层
cur_layer = x
in_dimension = layer_dimension[0]
# 循环生成5层的网络结构,这里主要是为了构造前向传播的过程
for i in range(1, n_layers):
out_dimension = layer_dimension[i]
# 生成当前层的权重,并同时把权重的L2正则化损失加入计算图的集合
weight = get_weight([in_dimension, out_dimension], 0.003)
bias = tf.Variable(tf.constant(0.1, shape=[out_dimension]))
# 这里使用relu激活函数
cur_layer = tf.nn.relu(tf.matmul(cur_layer, weight) + bias)
# 更新需要运算的层
in_dimension = layer_dimension[i]
# 最后输出的样本预测值
y= cur_layer
# 损失函数的定义,这里只是模型在训练数据上变现的损失函数,因为L2的正则化已经加入了损失集合
mse_loss = tf.reduce_sum(tf.pow(y_ - y, 2)) / sample_size
# 将均方误差损失函数加入损失集合
tf.add_to_collection('losses', mse_loss)
# tf.get_collection('losses')返回集合losses中的元素,
# 得到的是一个列表,在这里就是损失函数的不同部分,加起来就是总的损失
loss = tf.add_n(tf.get_collection('losses'))
# 定义训练的目标函数loss,训练次数及训练模型
train_op = tf.train.AdamOptimizer(0.001).minimize(loss)
TRAINING_STEPS = 40000
# 开启会话,进行计算
with tf.Session() as sess:
tf.global_variables_initializer().run()
for i in range(TRAINING_STEPS):
sess.run(train_op, feed_dict={x: data, y_: label})
if i % 2000 == 0:
print("After %d steps, loss: %f" % (i, sess.run(loss, feed_dict={x: data, y_: label})))
# 画出训练后的分割曲线
xx, yy = np.mgrid[-1:1:.01, 0:2:.01]
grid = np.c_[xx.ravel(), yy.ravel()]
probs = sess.run(y, feed_dict={x:grid})
probs = probs.reshape(xx.shape)
plt.scatter(data[:,0], data[:,1], c=np.squeeze(label),
cmap="RdBu", vmin=-.2, vmax=1.2, edgecolor="white")
plt.contour(xx, yy, probs, levels=[.5], cmap="Greys", vmin=0, vmax=.1)
plt.show()
运行结果:
After 0 steps, loss: 1.503084
After 2000 steps, loss: 0.136486
After 4000 steps, loss: 0.077162
After 6000 steps, loss: 0.060446
After 8000 steps, loss: 0.057898
After 10000 steps, loss: 0.057211
After 12000 steps, loss: 0.057091
After 14000 steps, loss: 0.057002
After 16000 steps, loss: 0.056941
After 18000 steps, loss: 0.056830
After 20000 steps, loss: 0.056788
After 22000 steps, loss: 0.056766
After 24000 steps, loss: 0.056751
After 26000 steps, loss: 0.056741
After 28000 steps, loss: 0.056727
After 30000 steps, loss: 0.056714
After 32000 steps, loss: 0.056713
After 34000 steps, loss: 0.056713
After 36000 steps, loss: 0.056713
After 38000 steps, loss: 0.056714
这里会有一个报错:
ValueError: c of shape (150, 1) not acceptable as a color sequence for x with size 150, y with size 150
这里需要把:plt.scatter(data[:,0], data[:,1], c=label,
中的
cmap="RdBu", vmin=-.2, vmax=1.2, edgecolor="white")c=label
改成c=np.squeeze(label)
。
这里还有一个没太搞得太明白,就是神经网络是怎么画出分界线的,以后有时间再搞吧。。。
3. 滑动平均m模型
滑动平均模型可以使模型在测试集上更加健壮。
tensorflow中提供了 tf.train.ExponentialMovingAverage()
来实现滑动平均模型,初始化时需要提供一个衰减率,以用于模型更新的速度。ExponentialMovingAverage对每个变量会维护一个影子变量,这个影子变量的初始值就是相应变量的初始值,而每次运行时变量的值会更新为:
shadow_variable=decay×shadow_variable+(1−decay)×variable
s
h
a
d
o
w
_
v
a
r
i
a
b
l
e
=
d
e
c
a
y
×
s
h
a
d
o
w
_
v
a
r
i
a
b
l
e
+
(
1
−
d
e
c
a
y
)
×
v
a
r
i
a
b
l
e
其中:
shadow_variable:影子变量
variable:待更新变量
decay:衰减率(决定模型更新的速度)
ExponentialMovingAverage还提供了num_updates参数来动态设置decay:
下面简单一个例子说明更新过程:
import tensorflow as tf
# 定义一个变量用于计算滑动平均,初始值设为0,并且类型必须为实数型
v1 = tf.Variable(0, dtype=tf.float32)
# 这里的step变量模拟神经网络中迭代的轮数,用于动态控制衰减率
step = tf.Variable(0, trainable=False)
# 定义一个滑动平均类,初始衰减率为0.99
ema = tf.train.ExponentialMovingAverage(0.99, step)
# 定义一个更新变量滑动平均的操作,这里给定一个列表
maintain_averages_op = ema.apply([v1])
with tf.Session() as sess:
# 初始化所有的变量
init_op = tf.global_variables_initializer()
sess.run(init_op)
# ema.average(v1)获得滑动平均之后变量的取值。初始化之后变量v1的值和v1的滑动平均都为0
print(sess.run([v1, ema.average(v1)]))
# 更新变量v1的取值
sess.run(tf.assign(v1, 5))
# 更新v1的滑动平均值,这时的衰减率为min{0.99,(1+step)/(10+step)=0.1}=0.1
# 所以此时v1的滑动平均值更新为0.1x0+0.9x5=4.5
sess.run(maintain_averages_op)
print(sess.run([v1, ema.average(v1)]))
# 更新step和v1的取值
sess.run(tf.assign(step, 10000))
sess.run(tf.assign(v1, 10))
# 更新v1的滑动平均值,这时的衰减率为min{0.99,(1+step)/(10+step)=0.999}=0.99
# 所以此时v1的滑动平均值更新为0.99x4.5+0.01x10=4.555
sess.run(maintain_averages_op)
print(sess.run([v1, ema.average(v1)]))
# 更新一次v1的滑动平均值
sess.run(maintain_averages_op)
print (sess.run([v1, ema.average(v1)]))
结果:
[0.0, 0.0]
[5.0, 4.5]
[10.0, 4.555]
[10.0, 4.60945]
参考:《Tensorflow实战Google深度学习框架》