Initial commit — gin-config strict-pattern coding standard
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)
This commit is contained in:
513
Gin-Config Strict Pattern Reference Examples.md
Normal file
513
Gin-Config Strict Pattern Reference Examples.md
Normal file
@@ -0,0 +1,513 @@
|
||||
# Gin-Config Strict Pattern: Reference Examples
|
||||
|
||||
|
||||
|
||||
## Example 1: Config class + loader + .gin file
|
||||
|
||||
|
||||
|
||||
### `src/conf/pipeline_conf.py`
|
||||
|
||||
```python
|
||||
|
||||
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`
|
||||
|
||||
```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`
|
||||
|
||||
```python
|
||||
|
||||
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: Main entry point
|
||||
|
||||
|
||||
|
||||
### `src/main.py`
|
||||
|
||||
```python
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
|
||||
import gc
|
||||
|
||||
import logging
|
||||
|
||||
import time
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
|
||||
import numpy as np
|
||||
|
||||
import torch
|
||||
|
||||
|
||||
|
||||
from conf.pipeline_conf import get_pipeline_cfg, PipelineConfig
|
||||
|
||||
from conf.hardware_conf import get_hardware_cfg, HardwareConfig
|
||||
|
||||
from conf.models_conf import get_models_cfg, ModelsConfig
|
||||
|
||||
from conf.input_conf import get_input_cfg, InputConfig
|
||||
|
||||
from conf.seg_conf import get_seg_cfg, SegConfig
|
||||
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
||||
|
||||
def get_proj_dir() -> str:
|
||||
|
||||
"""Return project root directory with trailing slash."""
|
||||
|
||||
return str(Path(__file__).resolve().parent.parent) + "/"
|
||||
|
||||
|
||||
|
||||
|
||||
def run_pipeline(
|
||||
|
||||
pipeline_conf: PipelineConfig,
|
||||
|
||||
hardware_conf: HardwareConfig,
|
||||
|
||||
models_conf: ModelsConfig,
|
||||
|
||||
input_conf: InputConfig,
|
||||
|
||||
seg_conf: SegConfig,
|
||||
|
||||
) -> None:
|
||||
|
||||
"""Execute the full augmentation pipeline.
|
||||
|
||||
|
||||
|
||||
Args:
|
||||
|
||||
pipeline_conf: Pipeline stage configuration.
|
||||
|
||||
hardware_conf: GPU hardware profile.
|
||||
|
||||
models_conf: Model identifiers and fallbacks.
|
||||
|
||||
input_conf: Image preprocessing parameters.
|
||||
|
||||
seg_conf: Segmentation prompts and thresholds.
|
||||
|
||||
"""
|
||||
|
||||
torch.manual_seed(42)
|
||||
|
||||
np.random.seed(42)
|
||||
|
||||
|
||||
|
||||
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
||||
|
||||
|
||||
|
||||
for stage in pipeline_conf.stages:
|
||||
|
||||
logger.info("Running stage: %s", stage)
|
||||
|
||||
t0 = time.perf_counter()
|
||||
|
||||
|
||||
|
||||
if stage == "depth":
|
||||
|
||||
run_depth_stage(pipeline_conf, hardware_conf, models_conf, input_conf, device)
|
||||
|
||||
elif stage == "edges":
|
||||
|
||||
run_edges_stage(pipeline_conf, input_conf)
|
||||
|
||||
elif stage == "segmentation":
|
||||
|
||||
run_seg_stage(pipeline_conf, hardware_conf, models_conf, seg_conf, device)
|
||||
|
||||
|
||||
|
||||
logger.info("Stage %s done in %.1f s", stage, time.perf_counter() - t0)
|
||||
|
||||
|
||||
|
||||
|
||||
def main() -> None:
|
||||
|
||||
"""Entry point: load gin configs and run pipeline."""
|
||||
|
||||
proj_dir = get_proj_dir()
|
||||
|
||||
path2cfg = f"{proj_dir}in/config_files/"
|
||||
|
||||
|
||||
|
||||
pipeline_conf = get_pipeline_cfg(path2cfg)
|
||||
|
||||
hardware_conf = get_hardware_cfg(path2cfg)
|
||||
|
||||
models_conf = get_models_cfg(path2cfg)
|
||||
|
||||
input_conf = get_input_cfg(path2cfg)
|
||||
|
||||
seg_conf = get_seg_cfg(path2cfg)
|
||||
|
||||
|
||||
|
||||
run_pipeline(pipeline_conf, hardware_conf, models_conf, input_conf, seg_conf)
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
main()
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Example 4: Model loading with config object
|
||||
|
||||
|
||||
|
||||
```python
|
||||
|
||||
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()
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Anti-patterns (DO NOT)
|
||||
|
||||
|
||||
|
||||
```python
|
||||
|
||||
# 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
|
||||
|
||||
```
|
||||
Reference in New Issue
Block a user