美文网首页
深层神经网络(三)神经网络的优化

深层神经网络(三)神经网络的优化

作者: 又双叒叕苟了一天 | 来源:发表于2018-07-18 10:46 被阅读0次

    梯度下降、随机梯度下降、mini-batch

    众所周知,梯度下降算法进行一轮参数的更新需要遍历一遍训练集。显然,这是很费时的。于是,后来产生了随机梯度下降的方法(SGD),我们用一条数据去代替整个训练集进行一轮参数的更新。但是,一条数据损失更小不代表整个数据集的损失就小了。所以我们通常采用两种方法的折中:mini-batch。即每次从训练集中选取batch条数据来进行参数的更新。

    实现代码:

    batch_size = n
    
    x = tf.placeholder(tf.float32, shape=(batch_size, 2), name="x-input")
    y = tf.placeholder(tf.float32, shape=(batch_size, 1), name="y-input")
    
    loss=...
    train_step = tf.train.AdamOptimizer(0.001).minimize(loss)
    
    with tf.Session() as sess:
        for i in range(STEPS):
            current_X, current_Y = ...
            sess.run(train_step, feed_dict={x: current_X, y: current_Y})
    

    学习率的设置

    学习率过高,可能会导致参数左右横跳,而学习率过低又会造成参数收敛较慢的问题。所以学习率的选取非常的重要。

    TensorFlow提供了一种更加灵活的学习率设置方法——指数衰减法

    以下代码实现了该功能:

    decayed_learning_rate = learning_rate *decay_rate ^ (global_step / decay_steps)
    

    换成数学公式就是:
    decayedlearningrate=learningrate*decayrate^{\frac{globalstep}{decaystep}}
    decayed_learning_rate:当前学习速率

    learning_rate:初始学习速率

    decay_rate:衰减系数

    global_step:当前参数更新次数

    decay_steps:衰减速度(类似参数每更新多少次进行衰减)

    其中learning_rate,decay_rate,decay_steps是可以自己设置的。global_step初始设置为0,每更新一次参数+1。由此计算出当前的学习速率。

    从公式可以看出:

    • decay_rate越大,衰减越快。
    • decay_steps越大,指数部分增加的就越慢,衰减越慢

    TensorFlow提供的api及代码实现:

    global_step = tf.Variable(0)
    
    #初始学习率为0.1,global_step随参数更新次数+1而+1,decay_steps=100,decay_rate=0.96
    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=global_step)
    

    值得注意的是global_step要单独声明一个变量,初始为0。

    参数staircase=True时,指的是采用阶梯下降方式。意思是指数部分(global_step / decay_steps)取整数,每当进行了decay_steps轮的参数更新之后,学习率才会进行改变。

    正则化

    在实际过程中,我们可能会出现过拟合的情况,解决这种问题的方法就是正则化。

    我们之前优化的对象为J(\theta),现加入正则项\lambda R(\mathrm{w}),去优化J(\theta)+\lambda R(\mathrm{w}),其中\mathrm{w}为权值矩阵。

    R(\mathrm{w})刻画的模型复杂度,\lambda刻画的是模型复杂度的损失程度。

    R(\mathrm{w})通常情况有两种计算方式:

    L1正则化:
    R(\mathrm{w})=\begin{Vmatrix} \mathrm{w} \end{Vmatrix}_1=\sum_i\begin{vmatrix}\mathrm{w}_i\end{vmatrix}
    L2正则化:
    R(\mathrm{w})=\begin{Vmatrix} \mathrm{w} \end{Vmatrix}_1^2=\sum_i\begin{vmatrix}\mathrm{w}_i^2\end{vmatrix}
    L1正则化和L2正则化有以下区别:

    • L2正则化会让参数变的更加稀疏
    • L2正则化可导,而L1正则化则不可导,所以L2正则化更容易优化

    在实践中,我们也可以一起使用:
    R(\mathrm{w})=\sum_i\alpha\begin{vmatrix}\mathrm{w}_i\end{vmatrix}+(1-\alpha)\mathrm{w_i^2}
    采用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)
    

    当神经网络有很多层时,我们可以通过集合来管理正则化项,提高代码可读性:

    import tensorflow as tf
    
    #得到权值矩阵,并将其l2正则化添加到‘losses’的集合当中
    def get_weight(shape, lambda)
        var = tf.Variable(tf.random_normal(shape), dtype = tf.float32)
        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, 2))
    
    batch_size = 8
    #每层结点数
    layer_dimension = [2, 10, 10, 10, 1]
    #层数
    n_layers = len(layer_dimension)
    #当前层
    cur_layer = x
    #输入层的结点数
    in_dimension = layer_dimension[0]
    
    #网络全连接
    for i in range(1, n_layers):
        out_dimension = layer_dimension[i]
        weight = get_weight([in_dimension, out_dimension], 0.001)
        bias = tf.Variable(tf.constant(0.1, shape=[out_dimension]))
        cur_layer = tf.nn.relu(tf.matmul(cur_layer, weight) + bias)
        in_dimension = layer_dimension[i]
    
    #定义mse损失
    mse_loss = tf.reduce_mean(tf.square(y_ - cur_layer))
    #将mse添加到‘losses’集合
    tf.add_to_collection('losses', mse_loss)
    #把‘losses’集合的所有项加起来作为最终的loss函数
    loss = tf.add_n(tf.get_collection('losses'))
    

    滑动平均模型

    在更新参数上,我们也可以通过滑动平均模型来提高最终模型在测试数据上的表现。TensorFlow提供了tf.train.ExponentialMovingAverage来实现了滑动平均模型。

    滑动模型会保存一个变量的影子变量,影子变量的初始值为该参数的初始值,影子变量更新方式如下:

    shadow_variable = decay * shadow_variable + (1-decay) * variable
    

    shadow_variable:影子变量

    decay:衰减率

    variable:待更新的变量

    decay决定了参数更新的速度,通常我们设置为一个比较大的值,例如:0.99。但是这在刚开始时就取这么的值会使得参数收敛的很,于是ExponentialMovingAverage还提供了num_updates参数来提高更新的速度:
    decay=\min\{decay,\frac{1+num_{updates}}{10+num_{updates}}\}
    num_updates:为更新参数的轮数,我们一般初始化为0,然后在运行优化函数时作为global_step的参数传入,它会随着更新轮数+1而+1,在上文指数衰减时也用到过。

    我们可以看到,随着num_updates增加,后一项逐渐增大趋近于1。

    下面代码模拟了训练时滑动平均模型的使用:

    import tensorflow as tf
    
    #v1为采用滑动平均模型更新的参数,参数必须是实数才能用这个模型
    v1 = tf.Variable(0, dtype=tf.float)
    #用来模拟参数更新的轮数
    step = tf.Variable(0, trainable=False)
    
    #声明一个ExponentialMovingAverage类,decay=0.99,num_updates=step
    ema = tf.train.ExponentialMovingAverage(0.99, step)
    #将要用这个模型的参数加入,要一次性传入而不能分次添加,因为每次调用apply()都会更新这个参数。通常采用
    #tf.trainable_variables()将要训练的参数全部传入
    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活动平均之后的取值
        print(sess.run([v1, ema.average(v1)]))
        #[0.0, 0.0]
        
        #v1从0更新为5
        sess.run(tf.assign(v1, 5))
        sess.run(maintain_averages_op)
        print(sess.run([v1, ema.average(v1)]))
        #[5.0, 4.5]
        
        #v1更新为10,注意此时的v1的shadow为4.5,step模拟为10000
        sess.run(tf.assign(step, 10000))
        sess.run(tf.assign(v1, 10))
        sess.run(maintain_averages_op)
        print(sess.run([v1, ema.average(v1)]))
        #[10.0, 4.5549998]
        
        #继续滑动
        sess.run(maintain_averages_op)
        print(sess.run([v1, ema.average(v1)]))
        #[10.0, 4.6094499]
    

    由此可见,滑动平均更新参数时,参数不会马上变成要更新的参数,而是通过decay控制更新速度来逐渐更新,向要更新的参数靠近。

    相关文章

      网友评论

          本文标题:深层神经网络(三)神经网络的优化

          本文链接:https://www.haomeiwen.com/subject/hmfopftx.html