Code-style guide and reference patterns for DL/CV research at the ЛИСАД laboratory (NADEZHDA / SOFIA CVGL projects). Files: - Стандарт написания кода для DL CV исследований (CVGL).md - Правила написания Python-кода (Gin-Config Strict Pattern).md - REQUIREMENTS_GIN_STYLE.md - Gin-Config Strict Pattern Reference Examples.md - Переход от argparse и dataclass к gin-config.md - gin-parse.md - Рекомендуемые gin-config категории.md - config_loader_reference.py - README.md (this commit) - .gitignore (Python artifacts)
4.0 KiB
4.0 KiB
Хороший вопрос. Вижу проблему в текущем паттерне — каждый get_*_cfg() вызывает gin.parse_config_file() отдельно, а gin имеет глобальное состояние. Если вызвать два загрузчика подряд — второй gin.parse_config_file() добавляет параметры к первому, не очищая.
Проблемы текущего подхода
# Текущий паттерн — каждый загрузчик парсит свой .gin:
fdir_conf = get_fdir_cfg(path2cfg) # gin.parse_config_file("file_dir.gin")
input_conf = get_inputval_cfg(path2cfg) # gin.parse_config_file("input.gin")
train_conf = get_train_cfg(path2cfg) # gin.parse_config_file("train.gin")
| Проблема | Почему |
|---|---|
| Gin state accumulates | Второй parse_config_file не очищает первый — параметры накапливаются |
| Порядок зависимость | Если input.gin случайно переопределит параметр из file_dir.gin — тихая ошибка |
| Нет единой точки загрузки | Разбросанные gin.parse_config_file() в каждом файле conf/ |
| Дублирование кода | Каждый get_*_cfg() — одинаковые 3 строки |
Рекомендуемое решение
Один раз загрузить все .gin файлы, один раз создать все конфиги:
from __future__ import annotations
import gin
from pathlib import Path
def load_all_configs(path2cfg: str) -> dict[str, object]:
"""Load all gin configs at once and return all config objects.
Args:
path2cfg: Path to config directory (with trailing slash).
Returns:
Dict with config objects keyed by name.
"""
gin.clear_config() # Reset global state — critical!
# Parse ALL .gin files in one call:
gin_files = sorted(Path(path2cfg).glob("*.gin"))
gin.parse_config_files_and_bindings(
[str(f) for f in gin_files],
bindings=[], # No CLI overrides
)
# Instantiate all configs (gin state is now fully populated):
return {
"pipeline": PipelineConfig(),
"hardware": HardwareConfig(),
"models": ModelsConfig(),
"input": InputConfig(),
"seg": SegConfig(),
}
Использование в main:
def main() -> None:
proj_dir = get_proj_dir()
path2cfg = f"{proj_dir}in/config_files/"
configs = load_all_configs(path2cfg)
run_pipeline(
configs["pipeline"],
configs["hardware"],
configs["models"],
configs["input"],
configs["seg"],
)
Сохранить ли отдельные get_*_cfg() ?
Да, но как обёртки для тестирования (когда нужен один конфиг в изоляции):
def get_hardware_cfg(path2cfg: str) -> HardwareConfig:
"""Load ONLY hardware config (for testing/debugging)."""
gin.clear_config()
gin.parse_config_file(f"{path2cfg}hardware.gin")
return HardwareConfig()
Ключевое: добавить gin.clear_config() перед каждым parse_config_file().
Итого: что изменить
| Было | Стало |
|---|---|
Каждый get_*_cfg() парсит свой .gin |
load_all_configs() парсит все .gin за один вызов |
Нет gin.clear_config() |
Обязательный gin.clear_config() перед загрузкой |
| 5 отдельных вызовов в main | 1 вызов load_all_configs() |
Отдельные get_*_cfg() для продакшена |
Оставить только для тестирования |
Хотите, чтобы я создал файл src/conf/config_loader.py с этим паттерном и обновил REQUIREMENTS_GIN_STYLE.md?