TensorFlow 网络优化步骤与一般方法

2022-05-09 14:27:36 浏览数 (1)

深度学习中,网络的优化是训练过程中很重要的一部分,现在有很多的优化策略,而他们的核心的内容都是梯度下降。

理论的部分大家可以参考:

理解梯度下降在机器学习模型优化中的应用,其中介绍了批量梯度下降,随机梯度下降与小批量梯度下降的基本概念。

An overview of gradient descent optimization algorithms,其中介绍了各种改进的优化方法,包括动量法,adagrad等等

而在本文的内容中重要讨论的是TensorFlow中实现这些优化方法的一般步骤,先贴上代码(该代码是整个可以运行dome的优化部分,也就是说单独无法运行)

代码语言:javascript复制
 # 构建训练操作
  def _build_train_op(self):
    # 学习率/步长
    self.lrn_rate = tf.constant(self.hps.lrn_rate, tf.float32)
    tf.summary.scalar('learning_rate', self.lrn_rate)

    # 计算训练参数的梯度
    trainable_variables = tf.trainable_variables()
    grads = tf.gradients(self.cost, trainable_variables)

    # 设置优化方法 其实是在实例化对象 optimizer
    if self.hps.optimizer == 'sgd':
      optimizer = tf.train.GradientDescentOptimizer(self.lrn_rate)
    elif self.hps.optimizer == 'mom':
      optimizer = tf.train.MomentumOptimizer(self.lrn_rate, 0.9)

    # 梯度优化操作  optimizer对象中的方法 
    apply_op = optimizer.apply_gradients(
                        zip(grads, trainable_variables),
                        global_step=self.global_step, 
                        name='train_step')
    
    # 合并BN更新操作
    train_ops = [apply_op]   self._extra_train_ops
    # 建立优化操作组
    self.train_op = tf.group(*train_ops)

1.确定学习率

代码语言:javascript复制
self.lrn_rate = tf.constant(self.hps.lrn_rate, tf.float32)

学习率时由self.hps.lrn_rate这个东西来的,下面会说具体是什么。

2.计算梯度

使用梯度下降的优化算法当然要计算梯度,TensorFlow中提供了tf.gradients函数:

代码语言:javascript复制
grads = tf.gradients(self.cost, trainable_variables)

self.cost为定义好的loss function,trainable_variables为所有需要训练的变量,其中trainable_variables是直接用tf.trainable_variables()参数得到的(在这里可以看下计算梯度的公式,不就是loss对w求偏导么,也就不难理解为啥是这两个参数)

3.设置优化策略

这个过程其实是实例化一个对象出来,叫做optimizer,上面的代码选择使用随机梯度下降还是动量。比如:

代码语言:javascript复制
optimizer = tf.train.MomentumOptimizer(self.lrn_rate, 0.9)

tf.train.MomentumOptimizer是一个类,提供了动量优化方法,对象的初始化参数有学习率和一个超参数(这个参数要看公式才知道是什么意思)

4.执行优化(定义优化的op)

在上面的三步中确定了优化需要的所有东西:步长,梯度,方法,那么就能确定最后的优化操作了,直接使用实例化出来的那么对象—optimizer,下面提供的方法apply_gradients,最后返回值是一个op。其中需要的注意的地方是第一个参数:zip(grads, trainable_variables),这个东西的目的是为了把梯度和参数关联起来,因为我们知道,在梯度下降过程中,要训练的变量个数决定了loss surcafe的维度,那么当然每一个维度上都会有一个自己的梯度。

5.加入BN

按道理讲,这一部分和梯度下降没有关系,他只是把批归一化的操作加入到了梯度优化上,组合成新的操作 — train_ops。最后就是利用tf.group函数把多个操作合并为一个。并让一个Session去run这个op就好了。

从上面的代码中,我们知道了梯度优化过程中的一般步骤以及需要的变量是如何得到的,但是学习速率没有涉及,下面的代码说明了学习率如何确定:

代码语言:javascript复制
  class _LearningRateSetterHook(tf.train.SessionRunHook):

    def begin(self):
      #初始学习率
      self._lrn_rate = 0.1

    def before_run(self, run_context):
      return tf.train.SessionRunArgs(
                      # 获取全局Step
                      model.global_step,
                      # 设置学习率
                      feed_dict={model.lrn_rate: self._lrn_rate})  

    def after_run(self, run_context, run_values):
      # 动态更新学习率
      train_step = run_values.results
      if train_step < 40000:
        self._lrn_rate = 0.1
      elif train_step < 60000:
        self._lrn_rate = 0.01
      elif train_step < 80000:
        self._lrn_rate = 0.001
      else:
        self._lrn_rate = 0.0001
  # 建立可监控的Session
  with tf.train.MonitoredTrainingSession(
      checkpoint_dir='temp',
      hooks=[logging_hook, _LearningRateSetterHook()],
      chief_only_hooks=[summary_hook],
      # 禁用默认的SummarySaverHook,save_summaries_steps设置为0
      save_summaries_steps=0,
      #设备的软分配
      config=tf.ConfigProto(allow_soft_placement=True)) as mon_sess:
    while not mon_sess.should_stop():
      # 执行优化训练操作
      mon_sess.run(model.train_op)

可以看到,专门建立了_LearningRateSetterHook类来做学习率的更新,该代码没有涉及到自适应学习率的方法,而是根据执行步数逐步降低学习率。

在后面定义了可监控的Session — tf.train.MonitoredTrainingSession,并将学习率更新的类加入到Session中。

最后如果没有达到终止条件,则一直执行mon_sess.run(model.train_op),而model.train_op就是第一个代码中最后定的:self.train_op = tf.group(*train_ops),即优化方法和批归一化(这里不说批归一化)。

hook这个词在代码中常常看到,首先它是个变量的名,用什么词都可以,之所以大家都选择hook是因为变量涉及的部分就像钩子一样挂在graph上。

以上就是一个模型训练中的优化部分的代码,使用了动量的方法,手动设置学习率。除此之外,TensorFlow还提供了很多其他的优化方法,比如:

adagrad:在TensorFlow中的 tf.train.AdagradOptimizer类下封装。

Adam:在TensorFlow中的 tf.train.AdamOptimizer类下封装。

具体可以参考TensorFlow Training

0 人点赞