python的gin的介绍和使用

本文参考官方文档

1.简介

由于现在很多机器学习的实验需要设置繁琐的参数,在多次实验中,有些参数是一样的,为了方便设置参数,Gin库出现了。它允许函数或类被注释为@gin.configurable,这使得能够使用清晰而强大的语法通过简单的配置文件来设置它们的参数。这种方法减少了配置维护,同时使实验配置透明且易于重复。

简单理解,gin像一个封装了参数配置的类,使用这个类将使得大量的参数配置变得简单清晰

安装

pip install gin-config

2.@gin.configurable

任何函数和类都可以使用@gin.configurable装饰器

@gin.configurable
def my_network(images, num_outputs, num_layers=3, weight_decay=1e-4):
  ...

@gin.configurable装饰器做了如下三件事:

  1. 把类或函数声明成了可配置的东西
  2. 它决定了函数或类构造函数的哪些参数是可配置的(默认情况下是其所有的参数)
  3. 封装类或函数,拦截调用,并向函数的可配置参数提供来自参数设置全局注册表的值(这些值是类或函数声明时没有指定的值)

为了确定哪些是可以配置的参数,@gin.configurable会使用到allowlist和denylist参数,分别声明哪些是可配的哪些是不可配的,我们通常用一个即可,默认没有用allowlist指定的都为不可配,反之亦然。

@gin.configurable('supernet', denylist=['images'])
def my_network(images, num_outputs, num_layers=3, weight_decay=1e-4):
  ...

其中supernet是我们指定的配置名。

3.赋值

我们使用如下两种格式给参数赋值:

  1. gin.bind_parameter('configurable_name.parameter_name', value)
  2. configurable_name.parameter_name = value

具体例子分别如下:

gin.bind_parameter('supernet.num_layers', 5)
gin.bind_parameter('supernet.weight_decay', 1e-3)
supernet.num_layers = 5
supernet.weight_decay = 1e-3

4.取值

我们可以用gin.query_parameter来取值,具体例子如下

num_layers = gin.query_parameter('supernet.num_layers')
weight_decay = gin.query_parameter('supernet.weight_decay')

5.配置参考文件

假如我们有以下代码:

@gin.configurable
class DNN(object):
  def __init__(self, num_units=(1024, 1024)):
    ...
  def __call__(inputs, num_outputs):
    ...

@gin.configurable(denylist=['data'])
def train_model(network_fn, data, learning_rate, optimizer):
  ...

我们可以在gin文件里配置参数:

train_model.network_fn = @DNN()  # An instance of DNN is passed.
train_model.optimizer = @MomentumOptimizer  # The class itself is passed.
train_model.learning_rate = 0.001

DNN.num_units = (2048, 2048, 2048)
MomentumOptimizer.momentum = 0.9

上面显示了两种配置参数风格。@DNN()@MomentumOptimizer。对于前者将会调用DNN类的实例参数,且每次参数配置都会随着每个DNN类的实例变动。对于后者将会调用类MomentumOptimizer的默认参数。

6.使用gin文件

我们经常会和absl下flags一起使用gin,比如下面这样

from absl import flags

flags.DEFINE_multi_string(
  'gin_file', None, 'List of paths to the config files.')
flags.DEFINE_multi_string(
  'gin_param', None, 'Newline separated list of Gin parameter bindings.')

FLAGS = flags.FLAGS

然后主程序main.py里最先解析参数:

gin.parse_config_files_and_bindings(FLAGS.gin_file, FLAGS.gin_param)

假设我们参数文件example.gin在当前目录下,则运行时,我们在终端输入python main.py --gin_file=example.gin

也可以在代码里改成这样:

flags.DEFINE_multi_string(
  'gin_file', ["example.gin"], 'List of paths to the config files.')

然后直接运行

6.调用其他类或函数

我们可以用下面代码调用其他类或函数的参数,甚至这个类或函数可以在其他项目里。

gin.external_configurable(tf.train.MomentumOptimizer)

7.范围限定

当一个可配置函数在程序执行过程中被多次调用时,可能需要为每次调用提供不同的参数绑定。Gin提供了一个范围限定机制来促进这一点。
例如,假设我们想要实现一个GAN,我们必须交替训练一个生成器和一个鉴别器。在Tensoflow中,这最容易通过两个优化器来实现,因此我们可能有这样一个函数:

gin.external_configurable(tf.train.GradientDescentOptimizer)

@gin.configurable(allowlist=['generator_optimizer', 'discriminator_optimizer'])
def gan_trainer(
    generator_loss, generator_vars, generator_optimizer,
    discriminator_loss, discriminator_vars, discriminator_optimizer):
  # Construct the optimizers and minimize w.r.t. the correct variables.
  generator_train_op = generator_optimizer().minimize(
      generator_loss, generator_vars)
  discriminator_train_op = discriminator_optimizer().minimize(
      discriminator_loss, discriminator_vars)
  ...

我们如何将generator_optimizerdiscriminator_optimizer都配置为@GradientDescentOptimizer,但具有不同的学习速率?
下面是个错误示范:

# Won't work!
gan_trainer.generator_optimizer = @GradientDescentOptimizer
GradientDescentOptimizer.learning_rate = 0.01

gan_trainer.discriminator_optimizer = @GradientDescentOptimizer
# This binding will overwrite the previous one:
GradientDescentOptimizer.learning_rate = 0.001

Gin提供了一个范围界定机制来处理这种情况。任何可配置引用的前面都可以有一个作用域名称,用/字符与可配置名称分开。同样,也可以通过在可配置名称前面加上一个范围名称来应用特定于某个范围的绑定。
下面是对的示范:

# This will work! Use scoping to apply different parameter bindings.
gan_trainer.generator_optimizer = @generator/GradientDescentOptimizer
gan_trainer.discriminator_optimizer = @discriminator/GradientDescentOptimizer

generator/GradientDescentOptimizer.learning_rate = 0.01
discriminator/GradientDescentOptimizer.learning_rate = 0.001

8.标记gin参数

Gin允许您指示在Gin配置中必须提供某些参数。这可以通过两种方式实现:
1.在函数的调用位置
2.在函数的签名中

当调用一个可配置时,您可以通过gin.REQUIRED标记任何arg或kwarg。所需对象:

my_network(images, gin.REQUIRED, num_layers=5, weight_decay=gin.REQUIRED)

将在调用时检查所需参数。如果没有为这些参数提供Gin绑定,将会引发一个错误,列出缺少的参数绑定以及需要它们的可配置名称。
定义可配置时,可以使用gin.REQUIRED将参数标记为必需的:

@gin.configurable
def run_training(model_dir=gin.REQUIRED, network=gin.REQUIRED, ...):
  ...

9.从Gin文件中导入模块

import some.module.spec

10.在Gin文件中调用另一个Gin文件参数

一个Gin文件可以包含其他Gin文件,这样可以更容易地将一个配置拆分成单独的组件(例如,一个“基础”配置,它被其他派生配置包含和修改)。包含另一个Gin文件可以使用以下语法完成:

include 'path/to/another/file.gin'

11.Gin “macros”

有时一个值应该在多个绑定之间共享。为了实现这一点并避免多次重复该值(导致维护负担),Gin提供了以下预定义的可配置功能:

@gin.configurable
def macro(value):
  return value

可以引用“宏”函数(通过“()”来取值)。例如:

num_layers/macro.value = 10
network.num_layers = @num_layers/macro()

也可以这样写

num_layers = 10
network.num_layers = %num_layers

12.常量

gin.constant函数可用于定义常量,这些常量可通过上述宏语法访问。例如,在Python中:

gin.constant('THE_ANSWER', 42)

然后在配置文件gin中

meaning.of_life = %THE_ANSWER

请注意,任何Python对象都可以用作常量的值(包括不能表示为Gin文字的对象)。值将被存储到Gin内部字典中,直到程序终止,因此避免创建具有有限生命周期的值的常数。
一个消除歧义的模块可以放在常量名称的前面。例如:

gin.constant('some.modules.PI', 3.14159)

13.实验使用多个Gin文件和额外的命令行绑定

在许多情况下,可以定义多个包含实验整体配置不同部分的Gin文件。对整体配置的额外“调整”可以通过命令行标志作为单独的绑定来传递。

一种推荐的方法是创建一个包含多个Gin配置的文件夹,然后创建一个包含以下内容的BUILD文件:

filegroup(
    name = "gin_files",
    srcs = glob(["*.gin"]),
    visibility = [":internal"],
)

filegroup可用作二进制文件中的数据依赖项:

data = ["//path/to/configs:gin_files",]

在二进制文件中,可以定义以下标志:

from absl import flags

flags.DEFINE_multi_string(
  'gin_file', None, 'List of paths to the config files.')
flags.DEFINE_multi_string(
  'gin_param', None, 'Newline separated list of Gin parameter bindings.')

FLAGS = flags.FLAGS

然后用Gin解析它们:

gin.parse_config_files_and_bindings(FLAGS.gin_file, FLAGS.gin_param)

最后,二进制文件可以运行为:

.../run_gin_eval \
  --gin_file=$CONFIGS_PATH/cartpole_balance.gin \
  --gin_file=$CONFIGS_PATH/base_dqn.gin \
  --gin_file=$CONFIGS_PATH/eval.gin \
  --gin_param='evaluate.num_episodes_eval = 10' \
  --gin_param='evaluate.generate_videos = False' \
  --gin_param='evaluate.eval_interval_secs = 60'
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐