tensorflow中学习率、过拟合、滑动平均的学习

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|+(1a)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+(1decay)×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:

min{decay,1+num_updates10+num_updates} 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 }

下面简单一个例子说明更新过程:

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深度学习框架》

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值