1、学习率的设置-----指数衰减法
TensorFlow提供了tf.train.exponential_decay函数实现了指数衰减学习率。通过这个函数,可以先使用较大的学习率来快速得到一个比较优的解,然后随着迭代的继续逐步减小学习率,使得模型在训练后期更加稳定。exponential_decay函数会指数级的减小学习率,实现了以下代码功能:
decayed_learning_rate = learning_rate * decay_rate ^ (global_step / decay_steps)
decayed_learning_rate - 每一轮优化时使用的学习率
learning_rate - 事先设定的初始学习率
decay_rate - 衰减系数
decay_steps - 为衰减速度,通常代表了完整的使用以便训练数据所需要的迭代次数。
global_step - 用于衰减计算的全局步骤。 一定不为负数。喂入一次 BACTH_SIZE 计为一次 global_step
staircase - 若 ‘ True ’ ,则学习率衰减呈 ‘ 离散间隔 ’ (discrete intervals),具体地讲,`global_step / decay_steps`是整数除法,衰减学习率( the decayed learning rate )遵循阶梯函数;若为 ’ False ‘ ,则更新学习率的值是一个连续的过程,每步都会更新学习率。
global_step = tf.Variable(0) #通过expoential_decay函数生成学习率 learning_rate = tf.train.exponential_decay(0.1, global_step, 100, 0.96, staircase = True) #使用指数衰减的学习率,在minimize函数中传入global_step将自动更新 learning_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(...my loss..., global_step = global_step)
上述代码中设定了初始学习率为0.1,因为指定了staircase = True,所以每训练100轮或学习率乘以0.96.
2、过拟合问题
为了避免过拟合问题,一个非常有用的方法是正则化。正则化的思想就是在损失函数中加入刻画模型复杂程度的指标。假设用于刻画模型在训练数据上表现的损失函数为J(Ø),那么优化时并不是直接优化J(Ø),而是优化J(Ø) + λR(ω)。其中R(ω)刻画的是模型的复杂程度,λ表示模型复杂程度在总损失中的比例。注意这里Ø表示的是一个神经网络中的所有参数,它包括边上的权重w和偏置项b。一般来说模型复杂度只有权重w决定。常用的刻画模型复杂度的函数R(ω)有两种,一种是L1正则化,计算公式为:
R(ω) = ||w||1 = ∑i |wi|
另一种为L2正则化,计算公式为:
R(ω) = ||w||22 = ∑i |wi2|
二者的基本思想都是想通过限制权重的大小,使得模型不能任意拟合数据中的随机噪音。但二者有很大的区别。首先,L1正则化会让参数变得更稀疏,而L2正则化不会,所谓参数变得更稀疏是指会有更多的参数变为0,这样可以达到类似特征选取的功能。之所以L2正则化不会让参数变得稀疏的原因是当参数很小时,这个参数的平方基本上就可以忽略了,于是模型不会进一步将这个参数调整为0.其次,L1正则化的计算公式不可导(不平滑),而L2正则化公式可导。因为在优化时需要计算损失函数的偏导数,所以对含有L2正则化损失函数的优化更加简洁。
下属为TensorFlow定义的带L2正则化的损失函数:
w = tf.Variable(tf.random_normal([2, 1], stddev = 1, seed = 1)) y = tf.matmul(x, w) loss = tf.reduce_mean(tf.square(y_ - y)) + tf.contrib.layers.l2_regularizer(lambda)(w)
通过集合计算一个5层神经网络带L2正则化的损失函数的计算方法:
import tensorflow as tf #获取一层神经网络边上的权重,并将这个权重的L2正则化损失加入名称为“losses”的集合中 def get_weight(shape, lambda): #生成一个变量 var = tf.Variable(tf.random_normal(shape), dtype = tf.float32) #add_to_collection的函数将这个新生成的L2正则化损失项加入集合 #这个函数的第一个参数“losses”是集合的名字,第二个采纳数是要加入这个集合的内容。 tf.add_to_collection("losses", tf.contrib.layers.l2_regularizer(lambda)(var)) #返回生成的变量 return var x = tf.placeholder(tf.float32, shape = (None, 2)) y_ = tf.placeholder(tf.float32, shape = (None, 1)) batch_size= 8 #定义了每一层网络中节点的个数 layer_dimension = [2, 10, 10, 10, 1] #神经网络的层数 n_layers = len(layer_dimension) #这个变量维护前向传播时最深入层的节点,开始的时候就是输入层 cur_layer = x #当前层的节点个数 in_dimension = layer_dimension[0] #通过一个循环来生成5层全连接的神经网络结构 for i in range(1, n_layers): #layer_dimension[i]为下一层的节点个数 out_dimension = layer_dimension[i] #生成当前层中权重的变量,并将这个变量的L2正则化损失加入到计算图上的集合。 weight = get_weight([in_dimension, out_dimension], 0.001) 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] #在定义神经网络前向传播的同时已经将所有的L2正则化损失加入了图上的集合, #这里只需要计算刻画模型在训练数据上表现的损失函数 mse_loss = tf.reduce_mean(tf.square(y_ - cur_layer)) #将均方误差损失函数加入损失集合 tf.add_to_collection('losses', mse_loss) #get_collection返回一个列表,这个列表是所有这个集合中的元素。在这个样例中, #这些元素就是损失函数的不同部分,将它们加起来就可以得到最终的损失函数。 loss = tf.add_n(tf.get_collection('losses'))
3、 滑动平均模型
滑动平均模型可以使得模型在测试数据上更加健壮。在采用随机梯度下降算法训练神经网络时,使用滑动平均模型在很多应用上都可以一定程度提高最终模型在测试数据上的表现。
TensorFlow提供了tf.train.ExpoentialMovingAverage来实现滑动平均模型。在初始化ExponentialMovingAverage时,需要提供一个衰减率(decay)。这个衰减率用于控制模型更新的速度。ExponentialMovingAverage对每一个变量维护一个影子变量(shadow variable),这个影子变量的初始值就是相应变量的初始值,俄日每次变量更新时,影子变量的值会更新为:
shadow_variable = decay × shadow_variable + (1 - decay) × variable
其中shadow_variable为影子变量,variable为待更新的变量,decay为衰减率,其中decay决定了模型更新的速度,decay越大模型月趋近于稳定。实际应用中decay通常被设置为接近1的数。
import tensorflow as tf #定义一个变量用于计算滑动平均,这个变量的初始值为0.注意这里手动指定了变量的类型 #为tf.float32,因为所有需要计算滑动平均的变量必须是实数型。 v1 = tf.Variable(0, dtype = tf.float32) #这里的step变量模拟神经网络中迭代的轮数,可以用于东塔控制衰减率 step = tf.Variable(0, trainable = False) #定义一个滑动平均的类(class)。初始化给定了衰减率(0.99)和控制衰减率的变量step。 ema = tf.train.ExponentialMovingAverage(0.99, step) #定义了一个更新变量滑动平均的操作。这里需要给定一个列表,每次执行这个操作时 #这个列表中的变量会被更新 maintain_averages_op = ema.apply([v1]) with tf.Session() as sess: #初始化所有变量 init_op = tf.initialize_all_variables() sess.run(init_op) #通过ema.average(v1)获取滑动平均之后变量的取值,在初始化之后变量v1的值和 #v1的滑动平均都为0 print(sess.run([v1, ema.average(v1)])) #更新变量v1的值到5 sess.run(tf.assign(v1, 5)) #更新v1的滑动平均值。衰减率为min{0.99, (1 + step) / (10 + step) = 0.1} = 0.1 #所以滑动平均会被更新为0.1 * 0 + 0.9 * 5 = 4.5 sess.run(maintain_averages_op) print(sess.run([v1, ema.avarage(v1)])) #更新step的值为10000 sess.run(tf.assign(step, 10000)) #更新v1的值为10 sess.run(tf.assign(v1, 10)) #更新v1的滑动平均值。衰减率为min{0.99, (1 + step) / (10 + step) = 0.999} = 0.99 #所以v1的滑动平均会被更新为0.99 * 4.5 + 0.01 * 10 = 4.555 sess.run(maintain_averages_op) print(sess.run([v1, ema.average(v1)])) #输出[10.0, 4.5549998] #再次更新滑动平均值,得到新滑动平均值为0.99 * 4.555 + 0.01 * 10 = 4.60945 sess.run(maintain_averages_op) print(sess.run([v1, ema.average(v1)])) #输出[10.0, 4.6094499]