Python——Hydra库的使用


整体说明

  • Hydra 是一个开源的 Python 框架 ,旨在简化复杂应用程序的配置管理
  • Hydra 的核心功能是能够通过组合动态创建分层配置 ,并且可以通过配置文件命令行轻松覆盖这些配置
  • Hydra 的名字来源于神话中的九头蛇(Hydra) ,象征着它能够轻松地使用不同配置运行多个相似的作业(即 Multirun 功能),这在机器学习和科学实验中尤其有用
  • Hydra 的主要特点总结如下
    • 分层配置 (Hierarchical Configuration): 配置可以从多个独立的配置文件组合而成
    • 命令行覆盖 (Command-Line Overrides): 能够通过命令行参数轻松修改配置的任何部分
    • 多任务运行 (Multirun): 使用一个命令就能运行多次实验,每次实验使用不同的配置组合
    • 配置快照 (Configuration Snapshots): 自动保存每次运行的完整配置,确保结果的可复现性
    • 工作目录管理 (Working Directory Management): 每次运行都会在 outputs/multirun/ 目录下创建一个以日期和时间命名的新目录,将运行结果和日志隔离
  • Hydra 常常和 omegaconf 包一起使用

Hydra 安装

  • 通过 pip 安装 hydra-core

    1
    pip install hydra-core --upgrade
    • 依赖的 omegaconf 包会自动安装

常用示例(必会)

  • 文件结构

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    tree
    .
    ├── config
    │   ├── color
    │   │   ├── blue.yaml
    │   │   └── green.yaml
    │   ├── config.yaml
    │   ├── config2.yaml
    │   └── person
    │   ├── alice.yaml
    │   └── bob.yaml
    └── hydra_demo.py
  • ./config/color/blue.yaml文件内容

    1
    2
    favorite_color: blue
    time: 10
  • ./config/color/green.yaml文件内容

    1
    favorite_color: green
  • ./config/person/alice.yaml文件内容

    1
    2
    name: Alice
    age: 30
  • ./config/person/bob.yaml文件内容

    1
    2
    name: Bob
    age: 25
  • config/config2.yaml 文件内容:

    1
    name_aux: 100
  • config/config.yaml 文件内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    # 定义到 defaults 的一定是配置文件,没有配置文件会出错,索引方式见下图
    defaults:
    # - _self_ # 放到最前面则用下面的默认参数覆盖当前文件定义(比如 person.name: "lilian" 会被其他文件覆盖)
    - person: alice # 索引 ./person/alice.yaml,也可以被参数覆盖,注意:这里 person 是文件夹,可以理解为 Hydra 的包的概念
    - color: blue # 索引 ./blue/blue.yaml,直接效果与 - color/blue 等价,但 - color/blue 覆盖参数需要使用 `+`,不建议使用
    - person@aux_person: bob # 索引 ./person/bob.yaml,同时重命名为 aux_person,后续通过 "aux_person" 替换 ”person" 作为引用
    # 引用示例:
    # cfg.person.name 访问来自 alice.yaml 的数据;
    # cfg.aux_person.name 访问来自 bob.yaml 的数据;
    # 理解替换逻辑:先看有没有完整匹配 defaults 指定的列表参数的,如 person@aux_person 必须是完整参数匹配,然后再按照正常的参数逻辑匹配具体参数(第一轮替换是全量匹配 defaults 中的列表参数:
    # 若要覆盖 aux_person 导入的对象,需要在命令行使用 person@aux_person=alice 来替换(不能是 person=alice 也不能是 aux_person=alice)
    # 若要覆盖内部的具体参数,则只能使用类似 aux_person.name=Tom 来替换(不能是 person.name 也不能是 person@aux_person.name)
    - person@aux.person: bob # 同上索引,但命名为类似 cfg.aux.person.name ,注意这里的 aux.person 会解析为 ["aux"]["person"]
    - config2 # 直接引用同步目录下的其他文件,相关参数字段会被 config2.yaml 文件内容更新,注:这里的 config2.yaml 不在任何包下
    - _self_ # 放到最后则用当前文件定义覆盖前面的默认参数(比如 person.name: "lilian" 会覆盖其他文件,本文件优先!)
    # 可以在这里添加其他全局配置
    full_name: "${person.name} Li" # 全局参数,要等到所有解析完成才解析这里,所以不用担心先后顺序,这个总是最后执行的
    modes: ??? # ??? 的变量比较特殊,在通过命令行传入该参数值前,无法直接使用,否则会报错:omegaconf.errors.MissingMandatoryValue: Missing mandatory value: modes
    person:
    name: "lilian" # 当前文件定义参数,是否覆盖引入的默认值与 `_self_` 的位置有关
    ENV_PATH: ${oc.env:PATH} # 读取环境变量 $PATH,环境变量不存在会出错
    work: # 这里
    _target_: demo.test.Work # 这是一个类,可以将下面的参数传入 demo.test.Work 构造函数构造对象(注意:类定义和 yaml 参数配置要对齐,yaml 参数可以少(但不能多),因为可以有默认参数,而且若在本配置文件中定义 _partial_: true ,则可以进一步少传非默认参数);
    # 这个定义下,后续初始化对象的使用方式是 `work = hydra.utils.instantiate(cfg.work)`
    # 注:也可以整个配置文件上定义 _target_,此时整个文件就是一个对象 `obj = instantiate(cfg)`
    # 注:可以嵌套,即 定义了 _target_ 时的参数可以继续时 target,从而实现对象的嵌套引用
    # 若在本配置文件中定义 _partial_: true,则 `work = hydra.utils.instantiate(cfg.work)` 得到的是 'functools.partial' 对象,需要调用 `work(time=8)` 等补充必要参数来得到最终对象
    # _target_ 本身只是一个申明,如果 hydra 中定义了 _target_ 但是没有调用,相当于没定义
    salary: 100
    time: 8
    • 容易出错的地方:

      • 想要定义一个 .person.name 的变量时必须使用下面的写法:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        person:
        name: "lilian"
        ## 解析结果:
        # {
        # "person": {
        # "name": "lilian" # person 是字典,name 是其子字段
        # }
        # }
        # 引用方式:
        # * 正确:`cfg.person.name` 或 `cfg["person"]["name"]`
        # * 错误:`cfg["person.name"]`
      • 错误写法是:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        "person.name": "lilian"
        # or 等价定义:
        person.name: "lilian"
        ## 解析结果: 一个扁平的字符串 key
        # {
        # "person.name": "lilian" # key 是字面字符串 "person.name",没有层级
        # }
        # 引用方式是:
        # * 正确:`cfg["person.name"]`
        # * 错误:`cfg.person.name` 或 `cfg["person"]["name"]`
      • 特别说明:

        • 虽然在参数直接定义时使用 person.name 的语法会被解析为 "person.name",但是在 hydra 的 default 别名(@ 后的名称)中可以使用 x.y 这样的重命名方式,这时候可以解析为 ["x"]["y"]
        • 示例:
          1
          2
          defaults:
          - person@private.person: text # 解析为 ["private"]["person"]
  • hydra_demo.py 文件内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    import hydra
    from omegaconf import OmegaConf
    import json

    # 注意:这里的 config_path 和 config_name 也可以被覆盖
    # 覆盖方式:
    # * `config_path`:只能用 `--config-path` 覆盖,建议使用绝对路径
    # * `config_name`:直接作为位置参数传入(如 `python app.py prod`),也可用 `--config-name`(或 `-cn`)显式指定(建议显示制定)
    @hydra.main(config_path="config", config_name="config", version_base=None)
    def main(cfg):
    print("===== to yaml =====:")
    print(OmegaConf.to_yaml(cfg))

    print("===== parse to json =====:")
    dict_obj = OmegaConf.to_container(cfg, resolve=True)
    json_str = json.dumps(dict_obj, indent=4, ensure_ascii=False)
    print(json_str)

    if __name__ == '__main__':
    main()
  • 执行命令1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    python hydra_demo.py
    # 等价于 python hydra_demo.py --config-path=config --config-name=config

    # ===== to yaml =====:
    # person:
    # name: lilian
    # age: 30
    # color:
    # favorite_color: blue
    # time: 10
    # aux_person:
    # name: Bob
    # age: 25
    # name_aux: 100
    # full_name: ${person.name} Li
    # modes: ???
    # ENV_PATH: ${oc.env:PATH}
    #
    # ===== parse to json =====:
    # {
    # "person": {
    # "name": "lilian",
    # "age": 30
    # },
    # "color": {
    # "favorite_color": "blue",
    # "time": 10
    # },
    # "aux_person": {
    # "name": "Bob",
    # "age": 25
    # },
    # "name_aux": 100,
    # "full_name": "lilian Li",
    # "modes": "???",
    # "ENV_PATH": "/Users/sanye/.nvm/versions/node/v12.14.0/bin:/usr/local/opt/node@16/bin:/Users/sanye/anaconda3/envs/torch_py310/bin:/Users/sanye/anaconda3/condabin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin"
    # }
  • 执行命令2

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    python hydra_demo.py +new_name=Joey person=bob color.time=15

    # ===== to yaml =====:
    # person:
    # name: lilian
    # age: 25
    # color:
    # favorite_color: blue
    # time: 15
    # aux_person:
    # name: Bob
    # age: 25
    # name_aux: 100
    # full_name: ${person.name} Li
    # modes: ???
    # ENV_PATH: ${oc.env:PATH}
    # new_name: Joey
    #
    # ===== parse to json =====:
    # {
    # "person": {
    # "name": "lilian",
    # "age": 25
    # },
    # "color": {
    # "favorite_color": "blue",
    # "time": 15
    # },
    # "aux_person": {
    # "name": "Bob",
    # "age": 25
    # },
    # "name_aux": 100,
    # "full_name": "lilian Li",
    # "modes": "???",
    # "ENV_PATH": "/Users/sanye/.nvm/versions/node/v12.14.0/bin:/usr/local/opt/node@16/bin:/Users/sanye/anaconda3/envs/torch_py310/bin:/Users/sanye/anaconda3/condabin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin",
    # "new_name": "Joey"
    # }

Multi-run:启动多个配置运行

  • 启动方式:

    1
    2
    3
    # 两种启动方式等价
    python my_app.py --multirun db=mysql,postgresql schema=warehouse,support,school
    python my_app.py -m db=mysql,postgresql schema=warehouse,support,school
    • 以上启动会生成6份任务,且串行执行
  • 使用 --multirun 启动的任务配置记录在 multirun/ 文件夹下(单任务启动方式的记录在 outputs/ 下)

Multi-run 的高阶用法

  • 通过覆盖 hydra.sweeper.param 实现启动多个任务

    1
    2
    3
    4
    5
    hydra:
    sweeper:
    params:
    db: mysql,postgresql
    schema: warehouse,support,school
  • 启动命令:

    1
    2
    3
    4
    5
    python my_app.py -m db=mysql
    # [2021-01-20 17:25:03,317][HYDRA] Launching 3 jobs locally
    # [2021-01-20 17:25:03,318][HYDRA] #0 : db=mysql schema=warehouse
    # [2021-01-20 17:25:03,458][HYDRA] #1 : db=mysql schema=support
    # [2021-01-20 17:25:03,602][HYDRA] #2 : db=mysql schema=school

日志文件说明

  • 每次执行命令后都会按照时间生成日志文件

    1
    2
    3
    4
    5
    6
    7
    $ tree outputs/2024-09-25/15-16-17
    outputs/2024-09-25/15-16-17
    ├── .hydra
    │ ├── config.yaml
    │ ├── hydra.yaml
    │ └── overrides.yaml
    └── my_app.log
  • config.yaml: A dump of the user specified configuration

  • hydra.yaml: A dump of the Hydra configuration

  • overrides.yaml: The command line overrides used

  • my_app.log: A log file created for this run

    • 用 Python 文件命令的日志文件,记录被 @hydra.main 注解过的函数中的 log 对象输出
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      import logging

      log = logging.getLogger(__name__)

      @hydra.main(config_path="config", config_name="config", version_base=None)
      def main(config):
      log.info("Info level message")
      log.debug("Debug level message") # 若输出日志的等级包含 debug,则这句话也会输出到日志文件
      pass

      if __name__ == '__main__':
      log.info("out info") # 不会输出到日志文件中(因为不在 `@hydra.main` 注解过的函数中)
      main()

特别需要注意的点

  • 参数覆盖规则:
    • 传入的参数 > 后定义的参数 > 先定义的参数
  • 传入参数的规则:
    • 被覆盖的参数必须是存在的,如 name=Joe 要求 name 已经存在,若不存在则会报错
    • 不存在的参数就需要添加:
      • 使用 + 增加参数,如 +name=Joe
    • 如果存在的参数上使用 +name=Joe 会出现错误(不可以同时出现两个相同的 key)
    • 如果要覆盖已有的参数,可以使用 ++name=Joe 这样的句子,++ 的含义是 不管这个配置原来有没有,都设成这个值
      • 作为对比,+ 号只能添加不存在的参数(存在就会报错);++ 则可以新加参数,也可以覆盖参数
    • 注:由于传入的参数会影响生效的子配置文件,自配置文件的参数配置命名上可能不同,所以参数的判定有一定的复杂性
  • 对于子配置可以使用动态方式添加(+),但建议使用 defaults 关键字定义,方便管理,定义后可以被正常覆盖(不再需要 +

附录:使用 Structured Config

  • 在新增加文件的情况下,也可以使用 Python 类定义对象实现类似 yaml 文件的效果(不常用)

  • 示例(无需任何 yaml 文件配置):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    from dataclasses import dataclass
    import hydra
    from hydra.core.config_store import ConfigStore

    @dataclass
    class MySQLConfig:
    host: str = "localhost"
    port: int = 3306

    cs = ConfigStore.instance()
    # Registering the Config class with the name 'config'.
    cs.store(name="config", node=MySQLConfig)

    @hydra.main(version_base=None, config_name="config")
    def my_app(cfg: MySQLConfig) -> None:
    if cfg.port == 80:
    print("Is this a webserver?!")

    if __name__ == "__main__":
    my_app()
    • 等价于有了 config.yaml 配置文件,写入了下面的信息
      1
      2
      3
      # config.yaml
      'host': 'localhost'
      'port': 3306
  • 更高阶的层级示例(参考自:https://hydra.cc/docs/tutorials/structured_config/hierarchical_static_config/):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    from dataclasses import dataclass

    import hydra
    from hydra.core.config_store import ConfigStore

    @dataclass
    class MySQLConfig:
    host: str = "localhost"
    port: int = 3306

    @dataclass
    class UserInterface:
    title: str = "My app"
    width: int = 1024
    height: int = 768

    @dataclass
    class MyConfig:
    db: MySQLConfig = field(default_factory=MySQLConfig)
    ui: UserInterface = field(default_factory=UserInterface)

    cs = ConfigStore.instance()
    cs.store(name="config", node=MyConfig)

    @hydra.main(version_base=None, config_name="config")
    def my_app(cfg: MyConfig) -> None:
    print(f"Title={cfg.ui.title}, size={cfg.ui.width}x{cfg.ui.height} pixels")

    if __name__ == "__main__":
    my_app()

附录:Hydra/OmegaConf 的动态配置引用语法

  • ${oc.select:path,default} 是 Hydra/OmegaConf 提供的安全配置引用机制:

    • 作用是从配置的其他位置读取值,避免重复定义
    • 路径不存在时使用默认值,不会导致配置加载失败
    • 大型配置中复用参数(如 Actor 和 Critic 共享模型配置)
    • 推荐总是使用 oc.select 而不是直接插值,以提高配置的鲁棒性
  • 示例语法:

    1
    diy_flag: ${oc.select:demo_config.diy_flag,false}
    • 用于从配置的其他位置读取值,并支持默认值
      • **${...}**:Hydra/OmegaConf 的变量插值语法
      • **oc.select:**:OmegaConf 的 select 函数,用于安全地访问嵌套配置
      • **demo_config.diy_flag**:要读取的配置路径(点分隔)
      • **,false**:如果路径不存在时的默认值
    • 等价逻辑
      1
      2
      3
      4
      5
      # 这行配置相当于:
      try:
      diy_flag = config.demo_config.diy_flag
      except (AttributeError, KeyError, OmegaConf.errors.ConfigAttributeError):
      diy_flag = False

基础用法

  • 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
      # config.yaml
    demo_config:
    diy_flag: true
    hidden_size: 768

    # 其他地方引用
    trainer:
    diy_flag: ${oc.select:demo_config.diy_flag,false}
    use_attention_mask: ${oc.select:demo_config.use_attention_mask,true}
  • 加载后:

    1
    2
    3
    cfg = OmegaConf.load("config.yaml")
    print(cfg.trainer.diy_flag) # True (从 demo_config 读取)
    print(cfg.trainer.use_attention_mask) # True (使用默认值,因为路径不存在)
  • oc.select vs 普通插值

    • 普通插值(不推荐,路径不存在会报错)

      1
      2
      # 如果路径不存在,会抛出异常
      diy_flag: ${demo_config.diy_flag}
    • oc.select(推荐,安全)

      1
      2
      # 路径不存在时使用默认值,不会报错
      diy_flag: ${oc.select:demo_config.diy_flag,false}

高级用法

  • 默认值本身也可以是变量

    1
    2
    default_value: ${oc.select:global.default_padding,true}
    diy_flag: ${oc.select:demo_config.diy_flag,${default_value}}
  • 可以配合类型转换

    1
    2
    diy_flag: ${oc.select:demo_config.diy_flag,false}
    # 即使配置中是字符串 "true",也会正确转换为布尔值
  • 多层回退

    1
    2
    # 尝试多个路径,逐个回退
    diy_flag: ${oc.select:${nested.select.path},${oc.select:global.fallback,false}}
  • 注:命令行可以直接覆盖这个值

    1
    2
    3
    4
    python main.py trainer.diy_flag=true

    # 或者覆盖源值
    python main.py demo_config.diy_flag=true

注意事项

  • 避免循环引用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 错误:循环引用
    model:
    param1: ${oc.select:model.param2,1}
    param2: ${oc.select:model.param1,2} # 循环引用!

    # 最好是单向依赖
    model:
    base_param: 10
    derived_param: ${oc.select:model.base_param,5}
  • 如果键名本身包含点号,需要加引号转义

    1
    value: ${oc.select:'level1.key.with.dots',default}
  • 默认值类型

    1
    2
    3
    4
    5
    6
    7
    8
    # 默认值可以是各种类型
    string: ${oc.select:path,hello_world} # string: ${oc.select:path,"hello world!"}
    int: ${oc.select:path,100}
    float: ${oc.select:path,3.14}
    bool: ${oc.select:path,false}
    list: ${oc.select:path,[1,2,3]}
    dict: ${oc.select:path,{a:1,b:2}}
    null: ${oc.select:path,null}
  • 调试技巧

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    from omegaconf import OmegaConf

    # 查看解析后的配置
    cfg = OmegaConf.load("config.yaml")
    print(OmegaConf.to_yaml(cfg, resolve=True)) # 显示解析后的值

    # 检查特定路径是否存在
    path_exists = OmegaConf.select(cfg, "demo_config.diy_flag") is not None
    print(f"Path exists: {path_exists}")

    # 安全获取值(Python 代码中)
    value = OmegaConf.select(cfg, "trainer.diy_flag", default=False)

附录:运行时文件工作路径获取


附录:调试参数配置情况

  • 在命令中添加 --cfg job 等来输出自己的配置
    • job: 个人配置参数生效情况,包括命令行传入的参数,这里是最终生效参数情况
    • hydra: Hydra’s config
    • all: The full config, which is a union of job and hydra. 二者融合
  • 参考链接:https://hydra.cc/docs/tutorials/basic/running_your_app/debugging/

附录:_target_ 的继承用法

  • _target_ 只是一个字符串路径 ,继承机制完全由 Python 的类继承处理,Hydra 只负责根据路径去实例化

  • 基本继承示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # models.py
    class BaseModel:
    def __init__(self, lr: float = 0.001):
    self.lr = lr

    class ResNet50(BaseModel):
    def __init__(self, lr: float = 0.001, layers: int = 50):
    super().__init__(lr)
    self.layers = layers

    class ResNet101(ResNet50):
    def __init__(self, lr: float = 0.001, layers: int = 101):
    super().__init__(lr, layers)
    • 对应的 yaml
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      # config.yaml
      base_model:
      _target_: models.BaseModel
      lr: 0.001

      resnet50:
      _target_: models.ResNet50
      lr: 0.001
      layers: 50

      resnet101:
      _target_: models.ResNet101
      lr: 0.001
      layers: 101
  • 配置级别的“继承”(推荐)

    • Hydra 中更常见的是配置文件之间的继承,而不是在 _target_ 路径上体现:

    • 文件一:

      1
      2
      3
      4
      # config/model/base.yaml
      _target_: models.BaseModel
      lr: 0.001
      name: base
    • 文件二:

      1
      2
      3
      4
      5
      6
      # config/model/resnet50.yaml
      defaults:
      - base # 继承 base.yaml

      _target_: models.ResNet50 # 覆盖 _target_
      layers: 50 # 添加新字段
    • 文件三:

      1
      2
      3
      4
      5
      6
      # config/model/resnet101.yaml
      defaults:
      - resnet50 # 继承 resnet50.yaml

      _target_: models.ResNet101 # 覆盖 _target_
      layers: 101 # 覆盖 layers
    • 最终效果:

      1
      2
      3
      cfg = OmegaConf.load("config/model/resnet101.yaml")
      model = instantiate(cfg)
      # 等价于:ResNet101(lr=0.001, name="base", layers=101)
  • 运行时切换对象

    • 这是 Hydr 最强大的用法:通过配置切换实现类

    • Python 文件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      # models.py
      class Backbone:
      def forward(self, x): pass

      class ResNet(Backbone):
      def __init__(self, depth: int): ...

      class ViT(Backbone):
      def __init__(self, patch_size: int): ...
    • yaml 文件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
          # config.yaml
      defaults:
      - backbone: resnet50 # 默认使用 ResNet50

      backbone:
      resnet50:
      _target_: models.ResNet
      depth: 50
      vit_base:
      _target_: models.ViT
      patch_size: 16
    • 运行时切换

      1
      2
      3
      # 运行时切换
      cfg.backbone = "vit_base" # 或通过命令行 backbone=vit_base
      backbone = instantiate(cfg.backbone) # 自动创建对应类型