用tf.app.flags构建可配置的Python应用程序

发布时间:2023-05-16

当我们为一个Python应用程序编写命令行参数时,我们经常会使用argparse库。可是,如何处理那些在我们应用程序中有多个层次结构,或者有多个组件需要一些参数的情况呢?TensorFlow的tf.app.flags就是来处理这样的问题的,它提供了一种基于命令行参数的配置框架,可以方便、灵活地管理多个参数。

一、界面介绍

tf.app.flags模块有5个部分:部分1是定义所有的flag参数;部分2定义了各个参数的默认值;部分3是从命令行读入参数和使用默认值来初始化FLAGS;部分4用于处理多余的不属于FLAGS的命令行参数;部分5用于打印使用方法和flag当前值。 下面让我们来看看这些部分的详细介绍:

1. 定义flag参数

flags.DEFINE_XXXX()函数是创建flag参数的主要方法,其中XXXX是flag的数据类型。可以通过这些方法定义整数、字符串、布尔等类型的参数。例如,以下是定义一些字符串和布尔类型flag的示例:

import tensorflow as tf
flags = tf.app.flags
flags.DEFINE_string('input', './data/input.txt', 'input data path')
flags.DEFINE_string('output', './data/output.txt', 'output data path')
flags.DEFINE_bool('verbose', False, 'display log messages')

以上代码定义了三个flag:inputoutputverbose。输入文件路径默认值为"./data/input.txt",输出文件路径的默认值为"./data/output.txt"verbose参数默认为False

2. 定义默认flag参数

tf.app.flags中,所有flag都具有默认值。你可以通过调用flags.FLAGS.xxx来获取FLAG参数xxx的默认值,但是在没有完全初始化FLAGS之前,这是无法完成的。为了确保我们始终可以访问FLAGS中的所有flag的值,我们需要为所有flag制定默认值。以下是设置默认值的示例:

import tensorflow as tf
flags = tf.app.flags
flags.DEFINE_string('input', './data/input.txt', 'input data path')
flags.DEFINE_string('output', './data/output.txt', 'output data path')
flags.DEFINE_bool('verbose', False, 'display log messages')
flags.DEFINE_integer('max_steps', 10000, 'maximum number of steps for training')   # 追加一行代码
flags.DEFINE_float('learning_rate', 0.1, 'initial learning rate for training')    # 追加一行代码

以上代码增加了两个默认的参数:max_stepslearning_rate

3. 从命令行读入参数和使用默认值来初始化FLAGS

在程序的运行期间,我们可以从命令行读取参数。例如,以下命令行指定了3个参数的值:

python my_program.py --input='./new_input.txt' --output='./new_output.txt' --verbose=True

使用以下代码将flags(即FLAGS)初始化,这里假设用户没有提供这些flag,这意味着它们将使用默认值。

import tensorflow as tf
flags = tf.app.flags
flags.DEFINE_string('input', './data/input.txt', 'input data path')
flags.DEFINE_string('output', './data/output.txt', 'output data path')
flags.DEFINE_bool('verbose', False, 'display log messages')
flags.DEFINE_integer('max_steps', 10000, 'maximum number of steps for training')
flags.DEFINE_float('learning_rate', 0.1, 'initial learning rate for training')
FLAGS = flags.FLAGS
FLAGS.__dict__['__flags'].update(flags.FLAGS.__flags) # 将默认值添加到FLAGS.__dict__中

以上代码中使用flags.FLAGS命令从命令行获取它们的参数值,如果用户没有特别指定参数,程序会将参数的值设置为默认值。FLAGS.__dict__['__flags'].update(flags.FLAGS.__flags)的作用是将默认值添加到FLAGS.__dict__中,这意味着我们可以通过FLAGS.xxx访问flag。

4. 处理多余的不属于FLAGS的命令行参数

如果用户提供了多余的参数(不属于FLAGS),则需要在应用程序代码中处理这些额外的命令行参数。可以使用以下代码示例将它们存储到额外的变量中:

import sys
import tensorflow as tf
flags = tf.app.flags
# 如前所述,指定参数以及参数默认值
FLAGS = flags.FLAGS
FLAGS.__dict__['__flags'].update(flags.FLAGS.__flags)
# 处理多余的非FLAG命令行参数
if len(sys.argv) > 1:
    for i in range(1, len(sys.argv)):
        if not sys.argv[i].startswith('--'):
            sys.exit('Invalid argument: ' + sys.argv[i])

5. 打印使用方法和flag当前值

以下代码演示了如何打印使用方法和flag当前值:

import tensorflow as tf
flags = tf.app.flags
flags.DEFINE_string('input', './data/input.txt', 'input data path')
flags.DEFINE_string('output', './data/output.txt', 'output data path')
flags.DEFINE_bool('verbose', False, 'display log messages')
flags.DEFINE_integer('max_steps', 10000, 'maximum number of steps for training')
flags.DEFINE_float('learning_rate', 0.1, 'initial learning rate for training')
FLAGS = flags.FLAGS
FLAGS.__dict__['__flags'].update(flags.FLAGS.__flags)
# 打印使用方法和flag当前值
flags.mark_flag_as_required('input')    # 声明必须项
print("input=" + FLAGS.input)    # 输出:./data/input.txt
print("output=" + FLAGS.output)  # 输出:./data/output.txt
print("verbose=" + str(FLAGS.verbose))    # 输出:False
print("max_steps=" + str(FLAGS.max_steps))    # 输出:10000
print("learning_rate=" + str(FLAGS.learning_rate))    # 输出:0.1

二、优缺点分析

1. 优点

tf.app.flags模块提供了一种简单、快速的方式来管理应用程序的参数。其主要优点如下:

  • 可维护性更好:团队使用这种配置框架后,可维护性将会更好。因为不存在与"魔术值"或"硬编码常数"相关的问题。所有参数值全部通过标志传递。
  • 风格一致性:如果一个团队使用tf.app.flags模块来管理程序的参数,那么每个组件的所有参数都将遵循相同的命令行接口标准,也就是说,每个参数都将由--flag--flag=value--flag value这几个标准的命令行参数值指定。
  • 易于使用:作为Python库的一部分,它需要很少的代码来配置和使用标志参数。它对于Python开发人员来说,非常容易使用。

2. 缺点

然而,tf.app.flags模块的缺点是,这种方法需要在每个组件的启动代码中维护标志。当应用程序由大量组件和参数时,必须维护大量重复的代码,导致代码冗长,并增加了出错的风险。

三、总结

在本篇文章中,我们介绍了tf.app.flags模块,并看了看如何定义flag参数、如何设置默认flag参数、如何从命令行读入参数并初始化FLAGS、如何处理多余的不属于FLAGS的命令行参数,以及如何打印使用方法和flag当前值。还分析了tf.app.flags模块的优缺点,这将有助于了解如何更好地使用tf.app.flags模块。