最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

python?配置管理框架Hydra使用指南

 更新時(shí)間:2026年01月06日 08:45:18   作者:落痕的寒假  
Hydra是Facebook?Research開發(fā)的開源Python配置管理框架,用于簡(jiǎn)化復(fù)雜項(xiàng)目中的配置管理,它采用分層配置和動(dòng)態(tài)組合設(shè)計(jì),支持以YAML文件實(shí)現(xiàn)結(jié)構(gòu)化配置,本文給大家介紹python配置管理框架Hydra使用,感興趣的朋友跟隨小編一起看看吧

Hydra是Facebook Research開發(fā)的開源Python配置管理框架,旨在解決復(fù)雜項(xiàng)目中配置混亂、多環(huán)境與多參數(shù)組合管理的難題。該框架采用分層配置與動(dòng)態(tài)組合設(shè)計(jì),支持以YAML文件實(shí)現(xiàn)結(jié)構(gòu)化配置。Hydra尤其適用于簡(jiǎn)化機(jī)器學(xué)習(xí)實(shí)驗(yàn)、軟件開發(fā)及其他復(fù)雜應(yīng)用的配置管理。它的名字來源于希臘神話中的九頭蛇,寓意其能夠靈活管理多種配置組合。Hydra的核心特性包括支持多源分層配置組合、可通過命令行直接覆蓋配置、提供動(dòng)態(tài)命令補(bǔ)全功能,同時(shí)支持本地與遠(yuǎn)程運(yùn)行,并能通過單命令執(zhí)行批量參數(shù)作業(yè)。

Hydra的官方倉庫地址為:hydra,詳細(xì)文檔可參閱:hydra-doc。Hydra功能全面,本文主要介紹其基本使用方法,更多高級(jí)功能請(qǐng)參考官方文檔。截至本文撰寫時(shí),Hydra的穩(wěn)定版本為1.3,該版本兼容Python 3.6至3.11,并全面支持Linux、macOS和Windows操作系統(tǒng)。安裝命令如下:

pip install hydra-core --upgrade

1 基礎(chǔ)教程

1.1 快速入門

簡(jiǎn)單示例

以下代碼是一個(gè)簡(jiǎn)單的Hydra應(yīng)用示例,它會(huì)打印出配置信息,其中my_app函數(shù)是編寫業(yè)務(wù)邏輯的入口。

from omegaconf import DictConfig, OmegaConf
import hydra
@hydra.main(version_base=None)
def my_app(cfg: DictConfig) -> None:
    print(OmegaConf.to_yaml(cfg))
if __name__ == "__main__":
    my_app()

如果你直接執(zhí)行這段代碼(沒有任何命令行參數(shù)),程序會(huì)輸出一個(gè)空的配置對(duì)象:

{}

這是因?yàn)?,?dāng)運(yùn)行my_app.py時(shí),@hydra.main裝飾器會(huì)自動(dòng)攔截對(duì) my_app()的調(diào)用。此時(shí)Hydra會(huì)初始化一個(gè)空的DictConfig對(duì)象(類似于Python字典),并將其作為參數(shù)cfg 傳遞給函數(shù)。由于當(dāng)前配置為空,OmegaConf.to_yaml(cfg)將其轉(zhuǎn)換為YAML格式后,僅輸出一個(gè)空對(duì)象。OmegaConf是Hydra的底層配置引擎,Hydra基于OmegaConf實(shí)現(xiàn)上層的復(fù)雜應(yīng)用配置與運(yùn)行管理,且OmegaConf可獨(dú)立使用。

此外默認(rèn)情況下,Hydra會(huì)創(chuàng)建以下目錄結(jié)構(gòu)以追蹤和管理程序的運(yùn)行結(jié)果:

outputs/
├── yyyy-mm-dd/          # 按日期分組
│   └── hh-mm-ss/        # 按時(shí)間精確到秒
│       └── .hydra/      # 保存本次運(yùn)行的配置
│           ├── config.yaml    # 完整的配置
│           ├── hydra.yaml     # Hydra 自身的配置
│           └── overrides.yaml # 命令行覆蓋的參數(shù)
│       └── my_app.log   # 日志文件(如果配置了日志)
│       └── 其他輸出文件     # 你的程序生成的文件

可以通過以下方式為配置添加內(nèi)容:

通過命令行添加:

# 不支持直接在 +key=value 語法中傳入非 ASCII 字符
python my_app.py +name="zhangsan" +age=25

輸出:

name: zhangsan
age: 25

創(chuàng)建配置文件:
創(chuàng)建一個(gè)config.yaml文件,然后運(yùn)行:

python my_app.py --config-path=. --config-name=config

在代碼中設(shè)置默認(rèn)配置:
可以修改代碼,為@hydra.main裝飾器添加配置參數(shù):

from omegaconf import DictConfig, OmegaConf
import hydra
@hydra.main(version_base=None, config_path=".", config_name="config")
def my_app(cfg):
    print(OmegaConf.to_yaml(cfg))
if __name__ == "__main__":
    my_app()

可以通過命令行覆蓋已加載配置中的值,但是注意無需添加+前綴:

python my_app.py name="lisi"

使用++前綴可實(shí)現(xiàn)若配置中已存在該參數(shù)則覆蓋,若不存在則新增:

python my_app.py ++name="wangwu" ++password=1234

要注意??:Hydra通過命令行修改配置時(shí),僅會(huì)覆蓋或新增程序運(yùn)行時(shí)內(nèi)存中的配置數(shù)據(jù),不會(huì)改動(dòng)磁盤上的原始配置文件,重啟程序后仍會(huì)配置加載文件的原始配置。

配置對(duì)象使用

通過Hydra加載配置后,可通過屬性或字典式訪問或修改已有的配置項(xiàng),訪問不存在的配置項(xiàng)時(shí)會(huì)拋出異常:

from omegaconf import DictConfig, OmegaConf
import hydra
@hydra.main(version_base=None, config_path=".", config_name="config")
def my_app(cfg: DictConfig):
    # 屬性式訪問配置值
    assert cfg.name == "張三"
    # 字典式訪問配置值
    assert cfg["age"] == 25
    # 修改已有配置值
    cfg.name = "李四"          
    cfg["age"] = 30            
    assert cfg.name == "李四"
    assert cfg["age"] == 30
    # 訪問缺失值會(huì)拋出異常
    try:
        cfg.birth_year
    except Exception as e:
        print("error !!")
        print(e)
if __name__ == "__main__":
    my_app()

之所以不允許訪問不存在的配置鍵,僅能操作已有配置鍵,是因?yàn)镠ydra默認(rèn)啟用了struct模式以嚴(yán)格結(jié)構(gòu)化配置。如需新增或修改配置,可先關(guān)閉嚴(yán)格模式,允許動(dòng)態(tài)新增鍵。但如果嵌套層級(jí)也未提前聲明,則需要先創(chuàng)建空嵌套,再為其添加子項(xiàng):

from omegaconf import DictConfig, OmegaConf
import hydra
import os
@hydra.main(version_base=None, config_path=".", config_name="config")
def my_app(cfg: DictConfig):
    # 關(guān)閉struct模式,允許新增配置鍵
    OmegaConf.set_struct(cfg, False)
    # 同一級(jí)新增配置
    cfg.birth_year = 1995      
    cfg["hobby"] = ["籃球", "編程"]
    # 無法直接給不存在的嵌套層級(jí)鏈?zhǔn)劫x值
    # cfg.address.city = "北京"          
    # 需要先創(chuàng)建嵌套層級(jí),再賦值子鍵
    cfg.address = OmegaConf.create({})  # 顯式創(chuàng)建空的嵌套
    cfg.address.city = "北京"         
    cfg.address["district"] = "朝陽區(qū)"
    # 驗(yàn)證寫入結(jié)果
    assert cfg.birth_year == 1995  
    assert cfg.address.district == "朝陽區(qū)"
    print("配置寫入驗(yàn)證通過!")
if __name__ == "__main__":
    my_app()

對(duì)配置文件進(jìn)行分組

若希望分別使用CNN和Transformer模型對(duì)數(shù)據(jù)集進(jìn)行訓(xùn)練基準(zhǔn)測(cè)試,可通過配置組(Config Group)實(shí)現(xiàn)這一需求。配置組是一個(gè)帶有名稱的分組,包含一組有效的配置項(xiàng)。若選擇不存在的配置項(xiàng),系統(tǒng)會(huì)生成錯(cuò)誤提示,并列出所有有效的配置項(xiàng)。

創(chuàng)建配置組時(shí),需先新建一個(gè)目錄(例如model),用于存放各模型配置項(xiàng)對(duì)應(yīng)的文件。由于預(yù)計(jì)會(huì)創(chuàng)建多個(gè)配置組,建議提前將所有配置文件統(tǒng)一移至conf目錄下管理。

目錄結(jié)構(gòu)如下:

├─ conf
│  └─ model
│      ├─ cnn.yaml
│      └─ transformer.yaml
└── my_app.py

model/cnn.yaml:

backbone: resnet50
learning_rate: 0.001
batch_size: 32
epochs: 20
dropout: 0.2

model/transformer.yaml:

backbone: vit_base
learning_rate: 0.0001
batch_size: 16
epochs: 30
attention_heads: 12

所有配置文件已統(tǒng)一存放至conf目錄,需通過config_path參數(shù)告知Hydra該目錄位置,并在代碼中指定待加載的配置文件名config_name。若未明確指定具體配置文件名,Hydra無法自動(dòng)推斷加載目標(biāo),最終會(huì)輸出空配置:

from omegaconf import DictConfig, OmegaConf
import hydra
@hydra.main(version_base=None, config_path="conf", config_name="model/cnn")
def my_app(cfg: DictConfig) -> None:
    print(OmegaConf.to_yaml(cfg))
if __name__ == "__main__":
    my_app()

也可以通過命令行從配置組中選擇特定配置項(xiàng),命令行使用+分組名=配置項(xiàng)的格式,例如:

python my_app.py +model=transformer

與常規(guī)用法一致,仍可覆蓋最終配置中的單個(gè)參數(shù)值:

python my_app.py +model=transformer model.epochs=40

多文件處理

可以生成一個(gè)配置文件,在配置文件中用defaults參數(shù)添加默認(rèn)配置列表。該列表用于指定Hydra組合最終配置對(duì)象的規(guī)則,按照約定,它需作為配置文件的首個(gè)配置項(xiàng)。如下所示:

defaults:
  - model: cnn

然后這個(gè)配置文件可以命名為任意名字,如conf文件夾下的config.yaml,這樣運(yùn)行會(huì)默認(rèn)加載model對(duì)應(yīng)的文件配置:

from omegaconf import DictConfig, OmegaConf
import hydra
@hydra.main(version_base=None, config_path="conf", config_name="config")
def my_app(cfg: DictConfig) -> None:
    print(OmegaConf.to_yaml(cfg))
if __name__ == "__main__":
    my_app()

默認(rèn)配置列表支持疊加多個(gè)深度學(xué)習(xí)相關(guān)配置項(xiàng)。若同一配置組存在兩個(gè)配置文件,系統(tǒng)會(huì)將這兩個(gè)配置文件合并為一個(gè)新字典;當(dāng)配置中出現(xiàn)相同鍵名時(shí),后加載的配置項(xiàng)會(huì)覆蓋先加載的配置項(xiàng)。示例默認(rèn)配置如下:

defaults:
  - model: 
    - cnn
    - transformer

若在配置文件夾conf下的dataset目錄中,存在如下配置文件model/cifar10.yaml:

name: CIFAR-10
path: ./data/cifar10
num_classes: 10
learning_rate: 0.0001
augmentation: true  # 是否開啟數(shù)據(jù)增強(qiáng)

當(dāng)默認(rèn)配置文件conf/config.yaml中默認(rèn)配置列表的內(nèi)容如下:

defaults:
  - model: cnn
  - dataset: cifar10 

由于model和dataset分屬不同的配置組,Hydra會(huì)將這兩個(gè)配置組的默認(rèn)配置進(jìn)行獨(dú)立合并。最終生成的完整配置結(jié)構(gòu)中,會(huì)包含model和dataset兩個(gè)一級(jí)配置項(xiàng),各自保留對(duì)應(yīng)配置組的完整參數(shù):

model:
  # 此處為conf/model/cnn.yaml中的配置內(nèi)容
dataset:
  # 此處為conf/dataset/cifar10.yaml中的配置內(nèi)容

即使設(shè)置了默認(rèn)配置,仍可手動(dòng)確定參數(shù)并覆蓋部分配置參數(shù):

python my_app.py model=cnn model.epochs=30

在配置項(xiàng)前添加~前綴,可從默認(rèn)配置列表中移除該默認(rèn)項(xiàng):

python my_app.py ~model

主配置的組合順序

主配置文件中可同時(shí)包含配置參數(shù)和默認(rèn)配置列表。在此情況下,若需調(diào)整默認(rèn)配置列表與主配置之間的覆蓋關(guān)系,可通過添加_self_關(guān)鍵字實(shí)現(xiàn):將_self_置于默認(rèn)配置列表末尾,則主配置參數(shù)將覆蓋默認(rèn)配置列表中的對(duì)應(yīng)項(xiàng);若將其置于列表開頭,則默認(rèn)配置列表中的參數(shù)將覆蓋主配置中的內(nèi)容。

需注意的是,從Hydra 1.1版本開始,默認(rèn)行為為主配置覆蓋默認(rèn)配置列表中的配置;而在此之前的版本中,默認(rèn)配置列表會(huì)覆蓋主配置的參數(shù)。

例如默認(rèn)配置文件config.yaml內(nèi)容如下,會(huì)進(jìn)行數(shù)據(jù)覆蓋,也就是說配置文件里dataset部分會(huì)覆蓋默認(rèn)配置中的同名部分:

defaults:
  - model: cnn
  - dataset: cifar10 
  - _self_
dataset: 
  name: my_dataset
version: 1.0

1.2 整合應(yīng)用

隨著軟件復(fù)雜度的不斷提升,我們會(huì)采用模塊化與組合化的設(shè)計(jì)思路來保證其可維護(hù)性。這種思路同樣適用于配置文件的管理。假設(shè)我們需要為示例程序配置多類深度學(xué)習(xí)模型支持,且每個(gè)模型對(duì)應(yīng)多種訓(xùn)練策略、搭配不同的數(shù)據(jù)預(yù)處理流程。使用Hydra時(shí),既不必為模型、策略、預(yù)處理流程的各類組合編寫?yīng)毩㈩悾矡o需為其單獨(dú)編寫配置文件。我們可以借鑒底層軟件開發(fā)的核心思路:通過組合化配置來解決這一問題。

多輪運(yùn)行(Multi-run)

對(duì)于使用多套配置運(yùn)行同一應(yīng)用程序的場(chǎng)景,可以通過命令行或配置文件兩種方式為Hydra應(yīng)用啟用多輪運(yùn)行功能。該功能自Hydra 1.2版本起引入,通過設(shè)置hydra.mode配置項(xiàng)實(shí)現(xiàn)。hydra.mode的合法取值包括RUN(單次運(yùn)行)和MULTIRUN(多輪運(yùn)行)。若在輸入配置中將hydra.mode設(shè)為MULTIRUN,應(yīng)用程序?qū)⒛J(rèn)以多輪運(yùn)行模式啟動(dòng)。

例如默認(rèn)配置文件為:

defaults:
  - model: cnn
  - dataset: cifar10 

多輪運(yùn)行命令如下:

python my_app.py hydra.mode=MULTIRUN model=cnn,transformer dataset=cifar10

只要參數(shù)值用逗號(hào)分隔,就會(huì)被Hydra識(shí)別為多取值參數(shù),Hydra會(huì)把所有帶多個(gè)取值的參數(shù)做笛卡爾積(全組合),Hydra會(huì)把每個(gè)參數(shù)的取值兩兩配對(duì),生成以下多個(gè)任務(wù),依次運(yùn)行:

python my_app.py hydra.mode=MULTIRUN model=cnn,transformer dataset=cifar10
# 本地啟動(dòng)2個(gè)任務(wù)
#0 : model=cnn dataset=cifar10
#1 : model=transformer dataset=cifar10

該命令可以用命令行參數(shù)簡(jiǎn)化:

python my_app.py --multirun model=cnn,transformer dataset=cifar10
# 或
python my_app.py -m model=cnn,transformer dataset=cifar10

注意Hydra會(huì)在任務(wù)啟動(dòng)時(shí)延遲組合配置。若在啟動(dòng)任務(wù)參數(shù)遍歷后修改代碼或配置文件,最終組合生成的配置可能會(huì)受影響。

也可以在輸入配置中通過覆蓋hydra.sweeper.params來定義參數(shù)遍歷規(guī)則并通過mode設(shè)置運(yùn)行模式。沿用上述示例,以下配置可實(shí)現(xiàn)完全相同的多輪運(yùn)行效果:

defaults:
  - model: cnn
  - dataset: cifar10 
hydra:
  mode: MULTIRUN # 設(shè)置運(yùn)行模式
  sweeper:
    params:
      dataset: cifar10
      model: transformer, cnn

直接運(yùn)行程序不使用任何附加參數(shù),結(jié)果如下:

$ python my_app.py
# 本地啟動(dòng)2個(gè)任務(wù)
#0 : model=transformer dataset=cifar10
#1 : model=cnn dataset=cifar10

1.3 信息管理

輸出目錄

Hydra能夠解決每次運(yùn)行程序時(shí)需要手動(dòng)指定新輸出目錄的問題,它會(huì)為每次運(yùn)行自動(dòng)創(chuàng)建一個(gè)專屬目錄,并在該輸出目錄中執(zhí)行代碼。默認(rèn)情況下,每次運(yùn)行應(yīng)用程序時(shí),都會(huì)生成一個(gè)全新的輸出目錄??梢酝ㄟ^讀取Hydra配置來獲取本次運(yùn)行該輸出目錄的路徑,示例如下:

from omegaconf import DictConfig, OmegaConf
import hydra
import os
@hydra.main(version_base=None, config_path="conf", config_name="config")
def my_app(_cfg: DictConfig) -> None:
    print(f"工作目錄:{os.getcwd()}")
    print(f"輸出目錄:{hydra.core.hydra_config.HydraConfig.get().runtime.output_dir}")
if __name__ == "__main__":
    my_app()

通過設(shè)置hydra.job.chdir=True,可以讓Hydra的@hydra.main裝飾器在執(zhí)行用戶的主函數(shù)前,調(diào)用os.chdir將Python工作目錄切換到輸出目錄:

python my_app.py hydra.job.chdir=True

可以通過覆蓋配置項(xiàng)hydra.output_subdir將設(shè)為null,則會(huì)完全禁用該子目錄的創(chuàng)建。

日志

由于標(biāo)準(zhǔn)logging模塊配置較為復(fù)雜,為實(shí)現(xiàn)常規(guī)的日志功能通常需要編寫較多代碼,且配置過程不夠簡(jiǎn)便。Hydra能夠自動(dòng)完成Python logging的配置,從而有效解決這一問題。默認(rèn)情況下,Hydra會(huì)以INFO級(jí)別向控制臺(tái)輸出日志,同時(shí)在當(dāng)前工作目錄自動(dòng)生成日志文件留存記錄。以下為使用Hydra進(jìn)行日志記錄的示例:

# hydra_log_demo.py
import logging
from omegaconf import DictConfig
import hydra
# 為本文件創(chuàng)建日志器
log = logging.getLogger(__name__)
@hydra.main(version_base=None)
def my_app(_cfg: DictConfig) -> None:
    log.info("Info 級(jí)別日志消息")
    log.debug("Debug 級(jí)別日志消息")
if __name__ == "__main__":
    my_app()

可通過在命令行中指定hydra.verbose配置項(xiàng)來啟用DEBUG級(jí)別的日志輸出。該配置項(xiàng)支持布爾值、字符串或列表類型的取值,開啟全部或指定日志器的DEBUG級(jí)別輸出如下:

python hydra_log_demo.py hydra.verbose=true

若要將特定函數(shù)對(duì)應(yīng)日志器的級(jí)別設(shè)為DEBUG,可使用如下命令:

python hydra_log_demo.py hydra.verbose="[__main__,my_custom_logger]"

其效果等同于代碼:

import logging
logging.getLogger(NAME).setLevel(logging.DEBUG)

如果不希望Hydra自動(dòng)配置日志系統(tǒng),可以將hydra/job_logging(對(duì)應(yīng)程序的日志)和hydra/hydra_logging(對(duì)應(yīng)Hydra框架自身的日志)均設(shè)為none:

python my_app.py hydra/job_logging=None hydra/hydra_logging=None

調(diào)試功能

Hydra提供多種配置選項(xiàng),可有效提升程序的可調(diào)試性。在命令行中使用--cfg-c參數(shù),即可在不運(yùn)行目標(biāo)函數(shù)的情況下打印應(yīng)用程序的配置信息。該參數(shù)需配合一個(gè)選項(xiàng)來指定打印的配置范圍:

  • job:打印業(yè)務(wù)代碼的配置
  • hydra:打印hydra框架自身的配置
  • all:打印完整配置內(nèi)容,即業(yè)務(wù)配置與hydra配置的合集

僅打印業(yè)務(wù)配置指令如下:

$ python my_app.py --cfg job

若只展示配置中的某一子集,可搭配參數(shù)--package或簡(jiǎn)寫-p使用:

python my_app.py --cfg hydra --package hydra.job

默認(rèn)情況下,配置中的插值表達(dá)式不會(huì)被解析。若需打印解析后的最終配置,可在--cfg參數(shù)基礎(chǔ)上,額外添加--resolve參數(shù)。

信息查詢功能

使用--info參數(shù)可查詢Hydra框架及應(yīng)用程序的各類相關(guān)信息:

  • --info all:默認(rèn)模式,打印所有可用信息
  • --info config:打印配置組合相關(guān)的輔助信息,包括:配置搜索路徑、默認(rèn)配置樹、默認(rèn)配置列表及最終生效的配置內(nèi)容
  • --info defaults:打印最終的默認(rèn)配置列表
  • --info defaults-tree:打印默認(rèn)配置樹結(jié)構(gòu)
  • --info plugins:打印已安裝的插件信息

2 結(jié)構(gòu)化配置

在復(fù)雜項(xiàng)目中,配置文件常面臨類型模糊、配置錯(cuò)誤難排查、缺少靜態(tài)校驗(yàn)等問題。例如:字段類型不明確易引發(fā)運(yùn)行時(shí)異常、多層級(jí)配置的結(jié)構(gòu)一致性難以保障、協(xié)作時(shí)難以通過工具提前發(fā)現(xiàn)配置沖突。

為此,Hydra基于Python數(shù)據(jù)類(dataclasses)定義了配置結(jié)構(gòu)與類型,其核心價(jià)值在于提供運(yùn)行時(shí)類型檢查與靜態(tài)類型檢查雙重保障。它支持基礎(chǔ)類型(int、str、bool、float、Enum 等)、嵌套結(jié)構(gòu)、容器類型(List、Dict)以及可選字段,但也存在部分限制,例如僅部分支持聯(lián)合類型,且不支持自定義方法。

Hydra中結(jié)構(gòu)化配置主要有兩種使用模式,均完整保留其核心功能:

  1. 直接作為配置使用(替代配置文件),適合快速入門;
  2. 作為配置模式(schema)使用,用于校驗(yàn)現(xiàn)有配置文件,適合大型或協(xié)作項(xiàng)目。

本教程將按此順序依次詳解兩種模式。

2.1 Hydra代碼配置

在后續(xù)的教程中,我們將使用ConfigStore類把數(shù)據(jù)類(dataclasses)注冊(cè)為Hydra中的輸入配置。ConfigStore是一個(gè)在內(nèi)存中存儲(chǔ)配置的單例(singleton)對(duì)象,與它交互的核心API是下文將要介紹的store方法。

class ConfigStore(metaclass=Singleton):
    def store(
        self,
        name: str,
        node: Any,
        group: Optional[str] = None,
        package: Optional[str] = "_group_",
        provider: Optional[str] = None,
    ) -> None:
        """
        將配置節(jié)點(diǎn)存儲(chǔ)至配置倉庫中
        :param name: 配置名稱
        :param node: 配置節(jié)點(diǎn),支持 DictConfig、ListConfig、
            結(jié)構(gòu)化配置(Structured configs),甚至普通的 dict 和 list 類型
        :param group: 配置分組,子分組分隔符為 '/',
            例如 hydra/launcher
        :param package: 配置節(jié)點(diǎn)的父級(jí)層級(jí)結(jié)構(gòu)。
            子節(jié)點(diǎn)分隔符為 '.',例如 foo.bar.baz
        :param provider: 提供該配置的模塊/應(yīng)用名稱,
            有助于調(diào)試排查問題。
        """
    ...

ConfigStore具備與YAML輸入配置完全一致的功能,除此之外還提供類型校驗(yàn)?zāi)芰?。它既可單?dú)使用,也可與YAML配合使用。

基礎(chǔ)用例

假設(shè)我們有一個(gè)簡(jiǎn)單的應(yīng)用程序,且存在一個(gè)包含cnn選項(xiàng)的model配置分組:

from omegaconf import DictConfig, OmegaConf
import hydra
@hydra.main(version_base=None, config_path="conf", config_name="model/cnn")
def my_app(cfg: DictConfig) -> None:
    print(OmegaConf.to_yaml(cfg))
if __name__ == "__main__":
    my_app()

目錄結(jié)構(gòu):

├─ conf
│  └─ model
│      └─ cnn.yaml
└── my_app.py

model/cnn.yaml:

backbone: resnet50
learning_rate: 0.001
batch_size: 32
epochs: 20
dropout: 0.2

如果現(xiàn)在想要新增一個(gè)transformer選項(xiàng)該怎么做?我們可以直接新增model/transformer.yaml配置分組文件,但這并非唯一方式!也可以通過ConfigStore為Hydra新增model配置分組的transformer選項(xiàng)。

要實(shí)現(xiàn)這個(gè)需求,只需在上述代碼文件中添加幾行代碼:

from dataclasses import dataclass
import hydra
from omegaconf import DictConfig, OmegaConf
from hydra.core.config_store import ConfigStore
@dataclass
class TransformerConfig:
    optimizer: str = "sgd"
    lr: float = 0.0005
    hidden_dim: int = 256
cs = ConfigStore.instance()
# 將名為transformer的配置類注冊(cè)至model配置分組
# 注意出現(xiàn)實(shí)體文件會(huì)報(bào)錯(cuò)
cs.store(name="transformer", group="model", node=TransformerConfig)
@hydra.main(version_base=None, config_path="conf")
def my_app(cfg: DictConfig) -> None:
    print(OmegaConf.to_yaml(cfg))
if __name__ == "__main__":
    my_app()

上述代碼不會(huì)生成實(shí)際的物理配置文件,它僅用于在內(nèi)存中注冊(cè)配置類。現(xiàn)在應(yīng)用程序已經(jīng)能夠識(shí)別model配置組中的兩個(gè)選項(xiàng),您可以通過以下命令運(yùn)行程序來驗(yàn)證效果:

python my_app.py +model=cnn

python my_app.py +model=transformer

在深度學(xué)習(xí)實(shí)驗(yàn)中管理多個(gè)模型配置時(shí),我們還可以借助ConfigStore支持的三種注冊(cè)方式靈活控制配置節(jié)點(diǎn),實(shí)現(xiàn)不同方案間的快速切換:

from dataclasses import dataclass
import hydra
from omegaconf import DictConfig, OmegaConf
from hydra.core.config_store import ConfigStore
@dataclass
class TransformerConfig:
    optimizer: str = "sgd"
    lr: float = 0.0005
    hidden_dim: int = 256
cs = ConfigStore.instance()
# 直接使用類類型
cs.store(name="config1", node=TransformerConfig)
# 使用類實(shí)例(覆蓋部分默認(rèn)值)
cs.store(name="config2", node=TransformerConfig(optimizer="rmsprop", lr=0.002))
# 使用字典(會(huì)失去運(yùn)行時(shí)類型安全保障)
cs.store(name="config3", node={"optimizer": "adam", "lr": 0.003, "hidden_dim": 256})
# 3. Hydra主函數(shù):加載并打印配置
@hydra.main(version_base=None, config_name="config1")  # 默認(rèn)加載config1
def main(cfg: DictConfig) -> None:
    print("當(dāng)前加載的配置內(nèi)容:")
    print(cfg)  
if __name__ == "__main__":
    main()

配置組

在Hydra框架中,配置組是一種用于組織互斥但相關(guān)配置項(xiàng)的機(jī)制。以深度學(xué)習(xí)場(chǎng)景為例,訓(xùn)練CNN與Transformer屬于不同的模型配置,它們都屬于模型配置這一大類,但一次訓(xùn)練只能選擇其中一種,這就是配置組的典型應(yīng)用。

# 導(dǎo)入必要的庫
from dataclasses import dataclass 
from typing import Any           
import hydra                   
from hydra.core.config_store import ConfigStore 
from omegaconf import OmegaConf  
# 1. 定義CNN模型的配置
@dataclass  # 裝飾器:將普通類轉(zhuǎn)為結(jié)構(gòu)化配置類(自動(dòng)生成初始化、比較等方法)
class CNNConfig:
    """CNN模型的訓(xùn)練配置(包含該模型特有的所有參數(shù))"""
    model_type: str = "cnn"      
    batch_size: int = 32          
    learning_rate: float = 0.001  
# 2. 定義Transformer模型的配置
@dataclass
class TransformerConfig:
    """Transformer模型的訓(xùn)練配置(包含該模型特有的所有參數(shù))"""
    model_type: str = "transformer" 
    batch_size: int = 16            
    learning_rate: float = 0.0001  
    num_heads: int = 8         
@dataclass
class Config:
    """整個(gè)訓(xùn)練程序的主配置類"""
    # model字段:用于接收配置組中選擇的模型配置(暫時(shí)標(biāo)注為Any類型)
    model: Any
# 1. 獲取配置存儲(chǔ)庫的單例實(shí)例(整個(gè)程序只有一個(gè)ConfigStore)
cs = ConfigStore.instance()
# 2. 注冊(cè)主配置(名稱為"config",對(duì)應(yīng)后續(xù)hydra.main的config_name)
cs.store(name="config", node=Config)
# 3. 注冊(cè)配置組:組名是"model",包含兩個(gè)選項(xiàng):
cs.store(group="model", name="cnn", node=CNNConfig)
cs.store(group="model", name="transformer", node=TransformerConfig)
# hydra.main裝飾器:標(biāo)記程序入口,指定配置名稱為"config"
@hydra.main(version_base=None, config_name="config")
def train_model(cfg: Config) -> None:
    """深度學(xué)習(xí)模型訓(xùn)練的主函數(shù)"""
    print("===== 當(dāng)前使用的訓(xùn)練配置 =====")
    print(OmegaConf.to_yaml(cfg))
# 程序啟動(dòng)入口
if __name__ == "__main__":
    train_model()

代碼運(yùn)行直接輸出為:

model: ???

??? 表示:該字段本應(yīng)有值,但目前處于缺失狀態(tài)。由于我們未給模型配置組設(shè)置默認(rèn)值,因此必須通過命令行顯式指定要使用的模型配置。注意命令中的+是必需的,因?yàn)槟P团渲媒M沒有默認(rèn)值,+在這里表示添加并覆蓋該配置字段:

python my_app.py +model=cnn

在上面實(shí)現(xiàn)中,model字段被標(biāo)注為Any類型,這雖然不會(huì)阻礙程序運(yùn)行,但卻把配置對(duì)象當(dāng)作一個(gè)缺乏類型信息的黑箱字典,使得IDE無法提供智能提示,靜態(tài)類型檢查也完全失效,從而降低了代碼的可維護(hù)性和長(zhǎng)期可靠性。要解決這一問題,解決方法是將不同模型配置之間的公共字段進(jìn)行抽象,創(chuàng)建一個(gè)BaseModelConfig基礎(chǔ)配置類:

from dataclasses import dataclass
from typing import Any
import hydra
from omegaconf import MISSING  # 標(biāo)記字段“無默認(rèn)值”
from hydra.core.config_store import ConfigStore 
from omegaconf import OmegaConf  
@dataclass
class BaseModelConfig:
    """所有深度學(xué)習(xí)模型的基礎(chǔ)配置(抽離公共字段)"""
    model_type: str = MISSING       # 模型類型:無默認(rèn)值(必須由子類指定)
    batch_size: int = 32            # 公共字段:默認(rèn)批次大?。ㄗ宇惪芍貙懀?
    learning_rate: float = 0.001    # 公共字段:默認(rèn)學(xué)習(xí)率(子類可重寫)
# 1. 定義CNN模型的配置
@dataclass  # 裝飾器:將普通類轉(zhuǎn)為結(jié)構(gòu)化配置類(自動(dòng)生成初始化、比較等方法)
class CNNConfig(BaseModelConfig):
    """CNN模型的訓(xùn)練配置(包含該模型特有的所有參數(shù))"""
    model_type: str = "cnn"      
    batch_size: int = 32          
    learning_rate: float = 0.001  
# 2. 定義Transformer模型的配置
@dataclass
class TransformerConfig(BaseModelConfig):
    """Transformer模型的訓(xùn)練配置(包含該模型特有的所有參數(shù))"""
    model_type: str = "transformer" 
    batch_size: int = 16            
    learning_rate: float = 0.0001   
    num_heads: int = 8         
@dataclass
class Config:
    """整個(gè)訓(xùn)練程序的主配置類"""
    # 不再是Any,而是BaseModelConfig
    model: BaseModelConfig
# 1. 獲取配置存儲(chǔ)庫的單例實(shí)例(整個(gè)程序只有一個(gè)ConfigStore)
cs = ConfigStore.instance()
# 2. 注冊(cè)主配置(名稱為"config",對(duì)應(yīng)后續(xù)hydra.main的config_name)
cs.store(name="config", node=Config)
# 3. 注冊(cè)配置組:組名是"model",包含兩個(gè)選項(xiàng):
cs.store(group="model", name="cnn", node=CNNConfig)
cs.store(group="model", name="transformer", node=TransformerConfig)
# hydra.main裝飾器:標(biāo)記程序入口,指定配置名稱為"config"
@hydra.main(version_base=None, config_name="config")
def train_model(cfg: Config) -> None:
    """深度學(xué)習(xí)模型訓(xùn)練的主函數(shù)"""
    print("===== 當(dāng)前使用的訓(xùn)練配置 =====")
    print(OmegaConf.to_yaml(cfg))
# 程序啟動(dòng)入口
if __name__ == "__main__":
    train_model()

可以在主結(jié)構(gòu)化配置中設(shè)置默認(rèn)值,方法與在config.yaml配置文件中定義相似。以下是一個(gè)深度學(xué)習(xí)模型配置的示例,新增了默認(rèn)配置列表,使其默認(rèn)加載model=cnn。只需在代碼中添加默認(rèn)列表,并相應(yīng)修改配置類即可:

from dataclasses import dataclass, field
from typing import Any, List       
# 定義默認(rèn)配置列表:從配置組"model"中加載名為"cnn"的配置
defaults = [
    {"model": "cnn"}
    # 設(shè)為 MISSING,則可強(qiáng)制用戶在命令行中指定該參數(shù)的值。
    # {"model": MISSING}
]
@dataclass
class Config:
    """整個(gè)訓(xùn)練程序的主配置類"""
    # 受@dataclass限制,此處需通過field定義默認(rèn)配置列表(默認(rèn)加載cnn配置)
    defaults: List[Any] = field(default_factory=lambda: defaults)
    # Hydra會(huì)根據(jù)默認(rèn)配置列表自動(dòng)填充該字段,類型為BaseModelConfig
    model: BaseModelConfig = MISSING

你也可以通過命令行覆蓋默認(rèn)配置,指定使用Transformer模型,注意不要+號(hào),因?yàn)檫@是覆蓋操作:

python my_app.py model=transformer

2.2 配置模式

Hydra的結(jié)構(gòu)化配置本質(zhì)是用代碼定義的結(jié)構(gòu)化規(guī)則來管理配置。它除了可以用代碼定義的結(jié)構(gòu)化配置替代傳統(tǒng)的YAML配置文件外,還能將結(jié)構(gòu)化配置作為配置規(guī)則模板(Schema),用于校驗(yàn)已有YAML配置文件是否符合規(guī)范。Schema能夠在程序啟動(dòng)時(shí)校驗(yàn)配置的合法性,提前攔截所有配置錯(cuò)誤。這對(duì)于保障大型項(xiàng)目的穩(wěn)定性至關(guān)重要,尤其適合多人協(xié)作的場(chǎng)景,可以有效避免配置錯(cuò)寫、漏寫字段等問題。

同一配置組內(nèi)的Schema校驗(yàn)

以深度學(xué)習(xí)訓(xùn)練場(chǎng)景的模型配置為例,下文將拆解如何通過預(yù)設(shè)的Schema模板,校驗(yàn)同一配置分組下各配置文件的字段、類型等是否符合規(guī)范要求。

給定如下配置目錄結(jié)構(gòu):

conf/
├── config.yaml          # 主配置(包含訓(xùn)練、模型、數(shù)據(jù)等)
└── model                # 模型配置組
    ├── resnet.yaml      # ResNet模型配置
    └── transformer.yaml # Transformer模型配置

需為上述每個(gè)配置文件添加結(jié)構(gòu)化配置Schema,核心方式是在YAML文件的defaults列表中聲明要繼承的Schema模板,并將這些Schema以base_config、model/resnet.yaml、model/transformer.yaml為名注冊(cè)至Hydra配置倉庫。各配置文件的defaults列表配置如下:

config.yaml:

defaults:
  - base_config          # 繼承基礎(chǔ)配置Schema
  - model: resnet        # 默認(rèn)使用ResNet模型配置
  - _self_               # 自身配置覆蓋默認(rèn)值
# 自定義訓(xùn)練參數(shù)
train:
  batch_size: 32
  lr: 0.001
debug: true

model/resnet.yaml:

defaults:
  - base_resnet  # 繼承ResNet基礎(chǔ)Schema
# ResNet專屬參數(shù)
layers: 50
pretrained: true
num_classes: 1000

model/transformer.yaml:

defaults:
  - base_transformer  # 繼承Transformer基礎(chǔ)Schema
# Transformer專屬參數(shù)
num_heads: 8
num_layers: 6
hidden_dim: 512
max_seq_len: 512

通過Python dataclass定義各種Schema規(guī)則,并注冊(cè)到Hydra配置倉庫,使YAML配置文件可關(guān)聯(lián)到對(duì)應(yīng)的校驗(yàn)規(guī)則:

from dataclasses import dataclass
from omegaconf import OmegaConf, MISSING
import hydra
from hydra.core.config_store import ConfigStore
# -------------------------- 基礎(chǔ)Schema定義 --------------------------
@dataclass
class BaseModelConfig:
    """所有模型的基礎(chǔ)配置Schema"""
    model_type: str = MISSING  # 必選字段,無默認(rèn)值
    device: str = "cuda"       # 可選字段,默認(rèn)值cuda
    dropout: float = MISSING   # 必選字段,無默認(rèn)值
@dataclass
class ResNetConfig(BaseModelConfig):
    """ResNet模型專屬Schema(繼承基礎(chǔ)模型配置)"""
    model_type: str = "resnet"  # 固定值,標(biāo)識(shí)模型類型
    dropout: float = 0.1        # 覆蓋默認(rèn)值
    layers: int = MISSING       # ResNet專屬必選字段
    pretrained: bool = MISSING
    num_classes: int = MISSING
@dataclass
class TransformerConfig(BaseModelConfig):
    """Transformer模型專屬Schema(繼承基礎(chǔ)模型配置)"""
    model_type: str = "transformer"  # 固定值
    dropout: float = 0.1             # 覆蓋默認(rèn)值
    num_heads: int = MISSING         # Transformer專屬必選字段
    num_layers: int = MISSING
    hidden_dim: int = MISSING
    max_seq_len: int = MISSING
@dataclass
class TrainConfig:
    """訓(xùn)練配置Schema"""
    batch_size: int = MISSING
    lr: float = MISSING
    epochs: int = 10  # 默認(rèn)訓(xùn)練10輪
@dataclass
class Config:
    """整體配置Schema"""
    model: BaseModelConfig = MISSING  # 模型配置(必選)
    train: TrainConfig = MISSING     # 訓(xùn)練配置(必選)
    debug: bool = False              # 調(diào)試模式(可選)
# -------------------------- 注冊(cè)Schema到配置倉庫 --------------------------
cs = ConfigStore.instance()
cs.store(name="base_config", node=Config)  # 注冊(cè)主配置Schema
cs.store(group="model", name="base_resnet", node=ResNetConfig)  # 注冊(cè)ResNet Schema
cs.store(group="model", name="base_transformer", node=TransformerConfig)  # 注冊(cè)Transformer Schema
# -------------------------- 主函數(shù) --------------------------
@hydra.main(version_base=None, config_path="conf", config_name="config")
def train_app(cfg: Config) -> None:
    """深度學(xué)習(xí)訓(xùn)練入口,打印最終配置"""
    print("最終訓(xùn)練配置:")
    print(OmegaConf.to_yaml(cfg))
if __name__ == "__main__":
    train_app()

運(yùn)行代碼時(shí),Hydra會(huì)先加載YAML配置文件,再通過關(guān)聯(lián)的Schema完成合法性校驗(yàn),若配置存在錯(cuò)誤會(huì)立即拋出異常:

# 正常運(yùn)行(使用默認(rèn)ResNet配置)
python my_app.py
# 模擬配置錯(cuò)誤(layers字段應(yīng)為int類型,傳入字符串觸發(fā)校驗(yàn)失敗)
python my_app.py model.layers='attention' 

跨配置組的Schema校驗(yàn)

在前文的模型訓(xùn)練場(chǎng)景中,Schema都定義在主程序里。但實(shí)際開發(fā)中,常會(huì)遇到第三方庫提供標(biāo)準(zhǔn)化的Schema,我們需要跨配置組引用這些Schema來校驗(yàn)自己的配置文件,而非把所有Schema都寫在主程序中。

假設(shè)存在一個(gè)公共的optimizer_lib庫,該庫預(yù)先定義了所有模型的標(biāo)準(zhǔn)Schema并注冊(cè)在獨(dú)立配置組中;本地conf/optimizer配置組下的YAML文件(如sgd.yaml、adam.yaml)僅需編寫YAML配置文件,并關(guān)聯(lián)這個(gè)外部庫的Schema完成校驗(yàn),無需重復(fù)定義模型規(guī)則,配置目錄結(jié)構(gòu)如下:

# 項(xiàng)目整體目錄
├── my_app.py               # 主程序
├── optimizer_lib.py        # 獨(dú)立的優(yōu)化器Schema庫
└── conf/
    ├── config.yaml         # 主配置
    └── optimizer/          # 本地優(yōu)化器配置組
        ├── sgd.yaml        
        └── adam.yaml       

optimizer_lib.py代碼如下:

from dataclasses import dataclass
from omegaconf import MISSING
from hydra.core.config_store import ConfigStore
# -------------------------- 優(yōu)化器基礎(chǔ)Schema --------------------------
@dataclass
class BaseOptimizerConfig:
    """所有優(yōu)化器的基礎(chǔ)Schema(獨(dú)立庫定義)"""
    opt_type: str = MISSING    # 必選字段:優(yōu)化器類型
    lr: float = MISSING        # 必選字段:學(xué)習(xí)率
    weight_decay: float = 0.0  # 可選字段:權(quán)重衰減,默認(rèn)0
# -------------------------- 具體優(yōu)化器Schema --------------------------
@dataclass
class SGDConfig(BaseOptimizerConfig):
    """SGD優(yōu)化器專屬Schema"""
    opt_type: str = "sgd"      # 固定標(biāo)識(shí)
    momentum: float = MISSING  # SGD專屬必選字段
    nesterov: bool = False     # 可選字段:是否使用Nesterov動(dòng)量
@dataclass
class AdamConfig(BaseOptimizerConfig):
    """Adam優(yōu)化器專屬Schema"""
    opt_type: str = "adam"     # 固定標(biāo)識(shí)
    betas: tuple[float, float] = (0.9, 0.999)  # 可選字段:beta參數(shù)
    eps: float = MISSING                       # Adam專屬必選字段
# -------------------------- 注冊(cè)跨配置組的Schema --------------------------
def register_optimizer_configs() -> None:
    cs = ConfigStore.instance()
    # 注冊(cè)到獨(dú)立分組:optimizer_lib/optimizer
    cs.store(
        group="optimizer_lib/optimizer",
        name="sgd",
        node=SGDConfig
    )
    cs.store(
        group="optimizer_lib/optimizer",
        name="adam",
        node=AdamConfig
    )

主程序my_app.py中定義整體配置Schema,并調(diào)用optimizer_lib的注冊(cè)函數(shù),將跨配置組的Schema納入Hydra配置倉庫:

from dataclasses import dataclass
from omegaconf import MISSING, OmegaConf
import hydra
from hydra.core.config_store import ConfigStore
import optimizer_lib  # 導(dǎo)入獨(dú)立的優(yōu)化器庫
# -------------------------- 整體配置Schema --------------------------
@dataclass
class TrainConfig:
    """訓(xùn)練配置Schema"""
    batch_size: int = 32
    epochs: int = 10
@dataclass
class Config:
    """主配置Schema"""
    optimizer: optimizer_lib.BaseOptimizerConfig = MISSING  # 引用獨(dú)立庫的Schema
    train: TrainConfig = MISSING
    debug: bool = False
# -------------------------- 注冊(cè)本地Schema并加載跨組Schema --------------------------
cs = ConfigStore.instance()
cs.store(name="base_config", node=Config)  # 注冊(cè)主配置Schema
optimizer_lib.register_optimizer_configs()  # 注冊(cè)跨配置組的優(yōu)化器Schema
# -------------------------- 主函數(shù) --------------------------
@hydra.main(version_base=None, config_path="conf", config_name="config")
def train_app(cfg: Config) -> None:
    """訓(xùn)練入口,打印最終配置并觸發(fā)Schema校驗(yàn)"""
    print("最終訓(xùn)練配置(含跨組優(yōu)化器配置):")
    print(OmegaConf.to_yaml(cfg))
if __name__ == "__main__":
    train_app()

本地conf/optimizer下的YAML文件需要通過絕對(duì)路徑引用optimizer_lib中的Schema,并通過@_here_指定包路徑,確保Schema的校驗(yàn)作用域與當(dāng)前配置一致。

conf/optimizer/sgd.yaml:

defaults:
  - /optimizer_lib/optimizer/sgd@_here_  # 絕對(duì)路徑引用跨組Schema,@_here_統(tǒng)一包作用域
# SGD專屬配置(需符合SGDConfig的Schema規(guī)則)
lr: 0.01
momentum: 0.9
weight_decay: 0.0001

conf/optimizer/adam.yaml:

defaults:
  - /optimizer_lib/optimizer/adam@_here_  # 絕對(duì)路徑引用跨組Schema
  - _self_  # 自身配置覆蓋Schema默認(rèn)值(組合順序:Schema先加載,自身配置后覆蓋)
# Adam專屬配置(需符合AdamConfig的Schema規(guī)則)
lr: 0.001
betas: (0.9, 0.999)
eps: 1e-08
weight_decay: 0.0005

主配置conf/config.yaml:

defaults:
  - base_config          # 主配置Schema
  - optimizer: sgd      # 默認(rèn)使用SGD優(yōu)化器配置
  - _self_
# 自定義訓(xùn)練參數(shù)
train:
  batch_size: 64
  epochs: 20
debug: true

運(yùn)行代碼時(shí),Hydra會(huì)先加載第三方庫的Schema,再校驗(yàn)本地YAML配置:

# 正常運(yùn)行(SGD配置符合Schema規(guī)則)
python my_app.py
# 配置錯(cuò)誤(momentum應(yīng)為float,傳入字符串觸發(fā)校驗(yàn)失?。?
python my_app.py optimizer.momentum='high'
# 配置錯(cuò)誤(Adam必選字段eps缺失,啟動(dòng)時(shí)直接報(bào)錯(cuò))
python my_app.py optimizer=adam optimizer.eps=none

3 參考

到此這篇關(guān)于python 配置管理框架Hydra使用指北的文章就介紹到這了,更多相關(guān)python 配置管理框架Hydra使用指北內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:

相關(guān)文章

最新評(píng)論

刚察县| 镶黄旗| 新蔡县| 白河县| 金湖县| 仙桃市| 女性| 普陀区| 和平区| 霸州市| 碌曲县| 墨脱县| 安乡县| 会昌县| 顺义区| 孟村| 江川县| 新蔡县| 中江县| 临西县| 景泰县| 绿春县| 成武县| 静海县| 湖南省| 彰武县| 张北县| 招远市| 长顺县| 额济纳旗| 西乌| 宁夏| 伊吾县| 新野县| 桐梓县| 周口市| 芦溪县| 宣威市| 永定县| 富锦市| 综艺|