当前位置: 首页 > news >正文

Hydra基础教程

基础使用

跑起来

安装包:pip install hydra-core
初始代码:

from omegaconf import DictConfig, OmegaConf  
import hydra  # 使用hydra.main装饰器,指定配置文件路径和名称  
# config_path: 配置文件所在目录  
# config_name: 配置文件名称
@hydra.main(version_base=None, config_path=".", config_name="config")  
def my_app(cfg: DictConfig) -> None:  print(OmegaConf.to_yaml(cfg))  if __name__ == "__main__":  my_app()

missing_value

对于???占位的,可以赋值后输出

@hydra.main(version_base=None, config_path=".", config_name="config")  
def my_app(cfg: DictConfig) -> None:  OmegaConf.resolve(cfg)  cfg.node.waldo = 11 # 给???node.waldo赋值,还可以在运行的时候通过命令行赋值  print(cfg.node.waldo)  print(OmegaConf.to_yaml(cfg))  if __name__ == "__main__":  my_app()

cli命令行输入

# 没有配置文件,只使用命令行输入参数,命令行输入参数的格式为:python my_app.py ++key=value  
# 必须加上++,要不然参数传不进去  
# python my_app.py ++db=10  
@hydra.main(version_base=None)  
def my_app(cfg: DictConfig) -> None:  print(OmegaConf.to_yaml(cfg))  if __name__ == "__main__":  my_app()

配置文件组

文件是

- conf- dbmysql.yamlpostgresql.yaml
# python my_app.py +db=mysql  
# 通过命令行输入参数,指定配置文件路径,格式为:python my_app.py +db=mysql  
# +db=mysql 表示在配置文件中添加一个新的配置项 db,并将其值设置为 mysql  
# 因为只指定了config_path的值
# 外面这个db文件夹就是一个配置文件组,里面有mysql.yaml和postgresql.yaml两个配置文件,分别对应不同的数据库配置  
@hydra.main(config_path="conf")  
def my_app(cfg: DictConfig) -> None:  print(OmegaConf.to_yaml(cfg))  if __name__ == "__main__":  my_app()
操作符 行为 如果键 不存在 如果键 已存在
+ (加号) 添加, 即创建新键 ✅ 成功 (创建新键) ❌ 失败 (报错)
++ (双加号) 强制覆盖或添加 ✅ 成功 (创建新键) ✅ 成功 (覆盖旧值)
一个问题:Hydra 的代码怎么知道 +db=mysql 里的 db 是一个配置组(要去文件夹里找文件),而不是一个普通字段(直接赋个字符串值)?

Hydra会自动去找,知道配置目录是conf,那就会找db文件夹,找到了就说明是配置组,没找到就是普通文件。

如果 Hydra 在 conf/ 目录下找不到 db 这个文件夹(也就是不存在名为 db 的配置组),那么 +db=mysql 就会被当作一个普通的字段赋值,最终 cfg.db 的值就是字符串 "mysql"

默认配置

目录结构:

- conf- dbmysql.yamlsql.yaml- hosta.yamla2.yaml
config.yaml

config.yaml

# 有多个组时,在这里配置默认值,用户可以在运行时覆盖这些值  # 所以此时的config.yaml就是新的配置文件  
defaults:  - db: mysql- host: a

代码配置

@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()

配置的继承

- confconfig.yaml- dbbase_mysql.yaml继承本组.yaml继承其他组.yaml- db_schemabase_mysql.yaml

同组可以直接继承某个yaml文件的配置
base_mysql.yaml

host: localhost  
port: 3306  
user: ???  
password: ???

同组继承:(直接覆盖参数或者添加)

# 继承同组配置,直接引用  
defaults:  - base_mysql  user: omry  
password: secret  
port: 3307  
encoding: utf8

继承其他组的配置:

# 继承另一个组的配置  
# 格式:组路径/组名@_here_  
# @here_表示: 当前组继承自另一个组,不加就是插入,而不是覆盖继承  
defaults:  - /db_schema/base_mysql@_here_  user: omry  
password: secret  
port: 3307  
encoding: utf8

实验组

实验组的意思是:config中设置了默认的配置,但是我们有其他常用的东西,我们可以创建一个实验组,然后在命令行中指定这个实验组,这样,我们就不需要每次都去修改配置文件了

比如db组合server组,不同的数据库配置不同的server服务器。
我们可以在默认config中写最常用的db+server,但是其他的配置组合可以单独写到experient组中,需要的时候导入。

package指令

很多时候,你需要在一个程序里使用两份相同类型但不同角色的配置。比如:从数据库 A 读数据,写到数据库 B。两个数据库的配置结构是一样的(都有 hostportuser 等),但值不同。

如果用普通的 - db: mysql,你只能生成一个 cfg.db,没法同时得到两个独立的配置块。

而 db@source: mysql 和 db@destination: mysql 这种语法就能完美解决:它把同一个 db 配置组(mysql.yaml加载两次,并分别放到 source 和 destination 这两个不同的键下面。

@hydra.main(version_base=None, config_path="conf", config_name="two_packages")  
def my_app(cfg: DictConfig) -> None:  print(OmegaConf.to_yaml(cfg))  # 配置文件内容如下:  
# defaults:  
#   - db@source: mysql  
#   - db@destination: mysql  
# 意思是:source和destination都使用mysql,之后文件中就有两个配置项source和destination  
if __name__ == "__main__":  my_app()

db@source: mysql

  • db:配置组的名字(对应 conf/db/ 文件夹)。
  • mysql:选项的名字(对应 conf/db/mysql.yaml 文件)。
  • @source安装路径。意思是:“不要把加载的内容放在默认的 db 键下,而是放到 source 键下。”

如果不写 @source,默认 - db: mysql 会把内容放到 cfg.db
加上 @source 后,内容会被放到 cfg.source;同理,db@destination: mysql 会把内容放到 cfg.destination

所以最后算是一个文件被导入两次,成了两个不同的对象,便于分开操作。

instantiate实例化

通过配置来动态创建对象。
核心就是 hydra.utils.instantiate 这个函数。它可以根据配置yaml中的 _target_ 字段,自动找到对应的类,并用其余字段作为参数来创建该类的实例。

yaml文件:

# _target_的作用:告诉Hydra如何实例化这个类  
_target_: my_app.MySQLConnection  
host: localhost  
user: root  
password: 1234
# 抽象基类
class DBConnection:  def connect(self) -> None:  ...  class MySQLConnection(DBConnection):  def __init__(self, host: str, user: str, password: str) -> None:  self.host = host  self.user = user  self.password = password  def connect(self) -> None:  print(f"MySQL connecting to {self.host}")  class PostgreSQLConnection(DBConnection):  def __init__(self, host: str, user: str, password: str, database: str) -> None:  self.host = host  self.user = user  self.password = password  self.database = database  def connect(self) -> None:  print(f"PostgreSQL connecting to {self.host}")  @hydra.main(version_base=None, config_path="conf", config_name="config")  
def my_app(cfg: DictConfig) -> None:  # instantiate函数的作用是:根据配置文件中的内容,创建一个对象  # 配置文件必须有_target_字段,才能被instantiate函数识别,_target_字段的值是一个字符串,表示要创建的对象的类名  connection = instantiate(cfg.db)  connection.connect()  if __name__ == "__main__":  my_app()

instantiate(cfg.db) 大致做了这些事情:

  1. 读取 cfg.db 中的 _target_ 字段,它的值是一个字符串,比如 "__main__.MySQLConnection"
  2. 通过 Python 的导入机制找到这个类。
  3. 将 cfg.db 中 除了 _target_ 以外的所有字段 作为关键字参数,传递给该类的构造函数。
  4. 返回构造好的实例。
    上面代码相当于执行:MySQLConnection(host="localhost", user="root", password="secret")

partial instantiate 部分实例化

model:  _target_: my_app.Model  optim_partial:  _partial_: true  _target_: my_app.Optimizer  algo: SGD

Optimizer类,algo和lr参数,配置文件中想先只固定algo,lr先不固定,等创建的时候再说。

# 优化器类Optimizer的参数algo和lr,在yaml中定义的时候,加上_partial_: true,表示只实例化部分参数,其他参数在Model类中实例化的时候再传入,这样就可以实现部分实例化了  
# 这里是优化器的类Optimizer,包含了algo和lr两个参数,在yaml中定义的时候,只实例化了algo参数,lr参数在Model类中实例化的时候传入,这样就可以实现部分实例化了  
class Optimizer:  algo: str  lr: float  def __init__(self, algo: str, lr: float) -> None:  self.algo = algo  self.lr = lr  def __repr__(self) -> str:  return f"Optimizer(algo={self.algo},lr={self.lr})"  class Model:  def __init__(self, optim_partial: Any):  super().__init__()  self.optim = optim_partial(lr=0.1)  def __repr__(self) -> str:  return f"Model(Optimizer={self.optim})"  # 部分实例化,不想全实例化,只实例化部分参数  
# yaml中加上_partial_: true,表示只实例化部分参数  
@hydra.main(version_base=None, config_path=".", config_name="config")  
def my_app(cfg: DictConfig) -> None:  model = instantiate(cfg.model)  print(model)  if __name__ == "__main__":  my_app()
  1. instantiate(cfg.model)
    • 创建 Model 实例。
    • 在创建 Model 时,需要传入 optim_partial 参数。
    • 这个 optim_partial 参数来自配置中的子字典,它包含 _target__partial_: truealgo: SGD
  2. instantiate 处理 _partial_: true
    • 因为它看到了 _partial_: true,所以 不会立即创建 Optimizer 对象
    • 相反,它返回一个 可调用对象(一个 partial 函数),这个可调用对象已经绑定了 algo='SGD',但还没有绑定 lr
    • 返回值类似于:partial(Optimizer, algo='SGD')
  3. 将这个可调用对象赋值给 optim_partial
    在 Model 的构造函数中:
    • optim_partial 就是上面那个 partial 函数。
    • 调用 optim_partial(lr=0.1) 时,它会将 lr=0.1 补充进去,然后完整调用 Optimizer(algo='SGD', lr=0.1),最终得到真正的 Optimizer 实例。
    • 这个实例被赋值给 self.optim
  4. 最终打印 model

递归实例化(Recursive Instantiation)

car:  _target_: my_app.Car  driver:  _target_: my_app.Driver  name: James Bond  age: 7  wheels:  - _target_: my_app.Wheel  radius: 20  width: 1  - _target_: my_app.Wheel  radius: 20  width: 1  - _target_: my_app.Wheel  radius: 20  width: 1  - _target_: my_app.Wheel  radius: 20  width: 1

递归创建,代码只写了一次,但是wheels四个配置,直接创建了四个

class Driver:  def __init__(self, name: str, age: int) -> None:  self.name = name  self.age = age  class Wheel:  def __init__(self, radius: int, width: int) -> None:  self.radius = radius  self.width = width  class Car:  def __init__(self, driver: Driver, wheels: List[Wheel]):  self.driver = driver  self.wheels = wheels  def drive(self) -> None:  print(f"Driver : {self.driver.name}, {len(self.wheels)} wheels")  @hydra.main(version_base=None, config_path=".", config_name="config")  
def my_app(cfg: DictConfig) -> None:  car: Car = instantiate(cfg.car)  car.drive()  if __name__ == "__main__":  my_app()

结构化配置StructuredConfig和dataclass结合

通过 ConfigStore 将 Python 的 dataclass 注册为配置,从而完全替代 YAML 配置文件

@dataclass  
class MySQLConfig:  host: str = "localhost"  port: int = 3306  # 作用:创建一个ConfigStore对象,并将MySQLConfig类注册到ConfigStore中,以便在Hydra应用程序中使用。  
cs = ConfigStore.instance()  
# 注册MySQLConfig类到ConfigStore中,指定名称为"config",以便在Hydra应用程序中通过名称引用该配置类。  
cs.store(name="config", node=MySQLConfig)  # 使用@hydra.main装饰器,指定配置文件名称为"config",并将MySQLConfig类作为参数传递给my_app函数,以便在函数中使用配置对象。  
@hydra.main(version_base=None, config_name="config")  
def my_app(cfg: MySQLConfig) -> None:  print(f"Host: {cfg.host}, port: {cfg.port}")  if __name__ == "__main__":  my_app()

Hydra 结构化配置的嵌套用法:用多个 dataclass 组合成一个大的配置类,并通过 ConfigStore 注册,从而完全替代 YAML 文件。
相当于一个类中有俩子类

@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 = MySQLConfig()  ui: UserInterface = UserInterface()  cs = ConfigStore.instance()  
cs.store(name="config", node=MyConfig)  # 这里直接调用的是和dataclass结合的配置文件类MyConfig,cfg就是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()

配置组

  • db: AnyConfig 中的 db 字段类型是 Any,可以接受任意类型的对象。
  • 命令行必须指定python my_app.py +db=mysql
    因为 Config.db 没有默认值,Hydra 不知道要加载哪个数据库配置,所以必须通过命令行 +db=mysql 告诉它选择 "db" 组下的 mysql 选项。
@dataclass  
class MySQLConfig:  driver: str = "mysql"  host: str = "localhost"  port: int = 3306  @dataclass  
class PostGreSQLConfig:  driver: str = "postgresql"  host: str = "localhost"  port: int = 5432  timeout: int = 10  @dataclass  
class Config:  # 这里的db字段被注解为Any类型,这意味着它可以接受任何类型的值。  # 我们是在命令行中输入参数:python my_app.py +db=mysql,这样Hydra会根据输入的参数来决定db字段的具体类型。  db: Any  cs = ConfigStore.instance()  
cs.store(name="config", node=Config)  
# 注册MySQLConfig类到ConfigStore中,指定名称为"mysql",以便在Hydra应用程序中通过名称引用该配置类。  
# 指定group="db"表示这个配置类属于"db"组,这样在使用时可以通过"group=db, name=mysql"来引用它。  
cs.store(group="db", name="mysql", node=MySQLConfig)  
cs.store(group="db", name="postgresql", node=PostGreSQLConfig)  @hydra.main(version_base=None, config_name="config")  
def my_app(cfg: Config) -> None:  print(OmegaConf.to_yaml(cfg))  if __name__ == "__main__":  my_app()

配置继承
这里很简单,继承类就是继承配置了

@dataclass  
class DBConfig:  host: str = "localhost"  port: int = MISSING  driver: str = MISSING  @dataclass  
class MySQLConfig(DBConfig): # 这是继承父类DBConfig  driver: str = "mysql"  port: int = 3306  @dataclass  
class PostGreSQLConfig(DBConfig):  driver: str = "postgresql"  port: int = 5432  timeout: int = 10  @dataclass  
class Config:  # We can now annotate db as DBConfig which  # improves both static and dynamic type safety.    db: DBConfig  cs = ConfigStore.instance()  
cs.store(name="config", node=Config)  
cs.store(group="db", name="mysql", node=MySQLConfig)  
cs.store(group="db", name="postgresql", node=PostGreSQLConfig)  # python my_app_with_inheritance.py +db=mysql  
@hydra.main(version_base=None, config_name="config")  
def my_app(cfg: Config) -> None:  print(OmegaConf.to_yaml(cfg))  if __name__ == "__main__":  my_app()

defaults为配置组提供默认值

@dataclass  
class MySQLConfig:  driver: str = "mysql"  host: str = "localhost"  port: int = 3306  user: str = "omry"  password: str = "secret"  @dataclass  
class PostGreSQLConfig:  driver: str = "postgresql"  host: str = "localhost"  port: int = 5432  timeout: int = 10  user: str = "postgres_user"  password: str = "drowssap"  defaults = [  # config group name db will load config named mysql  {"db": "mysql"}  
]  @dataclass  
class Config:  # this is unfortunately verbose due to @dataclass limitations  # 这里有显式的defaults字段,Hydra会根据defaults列表来填充db字段  # 所以命令行调用的时候,不用+,python my_app.py db=mysql  defaults: List[Any] = field(default_factory=lambda: defaults)  # Hydra will populate this field based on the defaults list  db: Any = MISSING  cs = ConfigStore.instance()  
cs.store(group="db", name="mysql", node=MySQLConfig)  
cs.store(group="db", name="postgresql", node=PostGreSQLConfig)  
cs.store(name="config", node=Config)  @hydra.main(version_base=None, config_name="config")  
def my_app(cfg: Config) -> None:  print(OmegaConf.to_yaml(cfg))  if __name__ == "__main__":  my_app()
http://www.jsqmd.com/news/749795/

相关文章:

  • DownKyi:3步解决B站视频下载难题,打造个人专属高清内容库
  • 大语言模型在数学推理与翻译任务中的表现与优化
  • BepInEx:Unity游戏插件框架终极指南 - 5分钟快速上手
  • YAML基础教程
  • Gazebo模型贴图变形?手把手教你搞定UV映射和纹理比例问题(以长方体为例)
  • 别再问板厂要什么文件了!AD21导出Gerber保姆级教程,附每个文件用途详解
  • 成都无缝钢管|流体管|结构管|锅炉管|碳素无缝管|合金无缝管|四川批发无缝管-四川盛世钢联国际贸易有限公司 - 四川盛世钢联营销中心
  • 个人整理的超全C++ 八股文(全是干货)
  • 崩坏星穹铁道自动化助手终极指南:三月七小助手的完整使用教程
  • Cursor额度实时监控:VS Code扩展开发实战与本地数据读取方案
  • CHAOS故障注入系统:提升计算系统可靠性的关键技术
  • 如何让Windows文件管理更智能:FileMeta完整指南
  • 别再死记硬背状态转移方程了!用‘数字三角形’带你彻底搞懂动态规划的自底向上思想
  • 怎样高效管理音乐元数据:163MusicLyrics智能整理工具实战解析
  • 术语俗话 --- POSIX 就是 Unix 世界的普通话,大家说同一种语言就能互相理解。
  • AI-Radar-Pulse:构建自动化AI信息追踪系统,高效获取前沿技术动态
  • WarcraftHelper终极配置指南:魔兽争霸3全版本兼容性修复与优化方案
  • Agent 的核心秘密 —— 智能来自模型,能力来自 Harness
  • 基于MCP与RAG技术,一键为网站部署本地化AI聊天机器人
  • NCMconverter终极指南:免费解锁加密音乐文件的完整教程
  • 3步解锁QQ音乐加密文件:macOS音频格式转换终极指南
  • PromptBridge技术:实现大语言模型提示词跨平台迁移
  • 用GPT-4生成数据破解视觉指令冷启动
  • DS4Windows终极指南:3分钟让PS4手柄在Windows上完美运行
  • RTX 4060笔记本跑PyTorch报错?手把手教你搞定CUDA算力不兼容(附详细诊断脚本)
  • Android开发中的Wi-Fi技术详解
  • Lightning Pose:基于深度学习的动物姿态追踪工具
  • 【企业级低代码安全红线】:Python自动生成代码中的5类隐蔽漏洞(含AST静态扫描脚本)
  • 论文查重和ai检测都超标!什么工具能同时降重复率和AI率?
  • BepInEx终极指南:Unity游戏插件框架完整教程