- code-style/SKILL.md — skill spec for the LISAD code-style enforcement - code-style/reference/gin_examples.md — gin-config canonical examples Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7.1 KiB
7.1 KiB
Gin-Config Strict Pattern: Reference Examples
Example 1: Config class + loader + .gin file
src/conf/pipeline_conf.py
from __future__ import annotations
import gin
@gin.configurable
class PipelineConfig:
"""Configuration for the augmentation pipeline stages and output."""
def __init__(
self,
input_root: str = "/data/UAV-GeoLoc",
output_root: str = "/data/UAV-GeoLoc-aug",
stages: list[str] | None = None,
save_npy: bool = True,
save_vis: bool = True,
save_concat: bool = False,
resume: bool = True,
subset: str | None = None,
source: str | None = None,
log_level: str = "INFO",
) -> None:
self.input_root = input_root
self.output_root = output_root
self.stages = stages or ["depth", "edges", "segmentation"]
self.save_npy = save_npy
self.save_vis = save_vis
self.save_concat = save_concat
self.resume = resume
self.subset = subset
self.source = source
self.log_level = log_level
def get_pipeline_cfg(path2cfg: str) -> PipelineConfig:
"""Load pipeline config from gin file.
Args:
path2cfg: Path to config directory (with trailing slash).
Returns:
Instantiated PipelineConfig with values from gin file.
"""
gin.parse_config_file(f"{path2cfg}pipeline.gin")
return PipelineConfig()
in/config_files/pipeline.gin
# Pipeline configuration
PipelineConfig.input_root = '/data/UAV-GeoLoc'
PipelineConfig.output_root = '/data/UAV-GeoLoc-aug'
PipelineConfig.stages = ['depth', 'edges', 'segmentation']
PipelineConfig.save_npy = True
PipelineConfig.save_vis = True
PipelineConfig.save_concat = False
PipelineConfig.resume = True
PipelineConfig.subset = 'Rot'
PipelineConfig.source = None
PipelineConfig.log_level = 'INFO'
Example 2: Model config with fallback IDs
src/conf/models_conf.py
from __future__ import annotations
import gin
@gin.configurable
class ModelsConfig:
"""Model identifiers and fallback strategy."""
def __init__(
self,
depth_model_id: str = "depth-anything/DA3-BASE",
depth_fallback_id: str = "depth-anything/Depth-Anything-V2-Large-hf",
seg_model_type: str = "segearth-ov3",
seg_fallback_type: str = "segformer-b5",
seg_fallback_id: str = "nvidia/segformer-b5-finetuned-ade-640-640",
) -> None:
self.depth_model_id = depth_model_id
self.depth_fallback_id = depth_fallback_id
self.seg_model_type = seg_model_type
self.seg_fallback_type = seg_fallback_type
self.seg_fallback_id = seg_fallback_id
def get_models_cfg(path2cfg: str) -> ModelsConfig:
"""Load models config from gin file."""
gin.parse_config_file(f"{path2cfg}models.gin")
return ModelsConfig()
Example 3: Central config loader (src/conf/config_loader.py)
from __future__ import annotations
import logging
from pathlib import Path
from typing import Any
import gin
from conf.pipeline_conf import PipelineConfig
from conf.hardware_conf import HardwareConfig
from conf.models_conf import ModelsConfig
from conf.input_conf import InputConfig
from conf.seg_conf import SegConfig
logger = logging.getLogger(__name__)
def load_all_configs(path2cfg: str) -> dict[str, Any]:
"""Parse ALL .gin files at once and return all config objects.
CRITICAL: calls gin.clear_config() to reset global state.
Args:
path2cfg: Path to config directory (WITH trailing slash).
Returns:
Dict with config objects keyed by name.
"""
cfg_dir = Path(path2cfg)
gin_files = sorted(cfg_dir.glob("*.gin"))
gin.clear_config() # MUST reset before loading
gin.parse_config_files_and_bindings(
config_files=[str(f) for f in gin_files],
bindings=[],
)
return {
"pipeline": PipelineConfig(),
"hardware": HardwareConfig(),
"models": ModelsConfig(),
"input": InputConfig(),
"seg": SegConfig(),
}
# Individual loaders — TESTING ONLY (always clear_config first):
def get_hardware_cfg(path2cfg: str) -> HardwareConfig:
gin.clear_config()
gin.parse_config_file(f"{path2cfg}hardware.gin")
return HardwareConfig()
Example 4: Main entry point (uses load_all_configs)
src/main.py
from __future__ import annotations
import logging
import numpy as np
import torch
from conf.config_loader import load_all_configs
from utils.utils_file_dir import get_proj_dir
logger = logging.getLogger(__name__)
def main() -> None:
"""Entry point: load ALL configs at once, then run pipeline."""
proj_dir = get_proj_dir()
path2cfg = f"{proj_dir}in/config_files/"
# ONE call loads everything:
configs = load_all_configs(path2cfg)
# Set seeds:
torch.manual_seed(42)
np.random.seed(42)
# Pass configs explicitly:
run_pipeline(
configs["pipeline"],
configs["hardware"],
configs["models"],
configs["input"],
configs["seg"],
)
if __name__ == "__main__":
main()
Example 4: Model loading with config object
from __future__ import annotations
import gc
import logging
from typing import Any
import torch
import torch.nn as nn
logger = logging.getLogger(__name__)
def load_depth_model(
models_conf: ModelsConfig,
hardware_conf: HardwareConfig,
device: torch.device,
) -> nn.Module:
"""Load depth estimation model based on config.
Args:
models_conf: Model IDs from gin config.
hardware_conf: FP16 and device settings.
device: Target CUDA device.
Returns:
Loaded depth model on device.
"""
model_id = models_conf.depth_model_id
logger.info("Loading depth: %s", model_id)
try:
from depth_anything_3 import DepthAnything3
model = DepthAnything3.from_pretrained(model_id)
if hardware_conf.use_fp16:
model = model.half()
return model.to(device).eval()
except ImportError:
logger.warning("DA3 not found, falling back to %s", models_conf.depth_fallback_id)
from transformers import AutoModelForDepthEstimation
dtype = torch.float16 if hardware_conf.use_fp16 else torch.float32
model = AutoModelForDepthEstimation.from_pretrained(
models_conf.depth_fallback_id, torch_dtype=dtype,
)
return model.to(device).eval()
def unload_model(model: Any) -> None:
"""Free GPU memory after model use."""
del model
gc.collect()
if torch.cuda.is_available():
torch.cuda.empty_cache()
Example 5: Model loading with config objects (not hardcoded IDs)
# BAD: dataclass + gin
@gin.configurable
@dataclass # ← FORBIDDEN
class Config:
param: int = 1
# BAD: argparse
parser = argparse.ArgumentParser() # ← FORBIDDEN, use gin
# BAD: global gin state inside function
def process():
val = gin.query_parameter("Config.param") # ← FORBIDDEN
# BAD: gin.constant / macros
LEARNING_RATE = gin.constant("lr", 0.001) # ← FORBIDDEN
# BAD: hardcoded model ID
model = AutoModel.from_pretrained("depth-anything/DA3-BASE") # ← move to gin