Skip to content

Migration to structured and validated configs - #3198

Merged
MMathisLab merged 98 commits into
mainfrom
feat/structured_configs
Jun 25, 2026
Merged

Migration to structured and validated configs#3198
MMathisLab merged 98 commits into
mainfrom
feat/structured_configs

Conversation

@deruyter92

@deruyter92 deruyter92 commented Feb 4, 2026

Copy link
Copy Markdown
Collaborator

This PR implements the features proposed in #3193, and will be the final PR for merging all changes from the feature branch, implemented in the smaller PRs #3190, #3191, #3194, etc..

Summary

Large refactor (~117 files, +7k/−1.5k lines) that introduces typed Pydantic configs for DLC, starting with project config.yaml and PyTorch pytorch_config.yaml. Config I/O is centralized in deeplabcut.core.config, and the PyTorch pipeline is wired to use the new typed configurations.

Motivation
Config handling had grown organically: YAML I/O was spread across several modules, configs were plain dicts without a shared schema, and there was no single place to validate shape or types at load time. That made it easy for typos, type mismatches, and inconsistent defaults to slip through undetected until much later in the pipeline. It was also often unclear what each field was supposed to be, and configs were frequently patched in multiple places rather than built once up front — so it was hard to tell which component owned which settings, or when a config was truly complete versus still missing downstream additions.

This PR consolidates that into a clearer model: centralized I/O, validation at load time, typed configs with IDE support, and a versioning framework for future schema changes. Pose configs are built through canonical entry points (e.g. PoseConfig.build), after which the result is treated as fully initialized.

Scope
The current typed schema version 0 deliberately captures the existing config shape as closely as possible. This enables for round-trip compatibility. A future v1 migration could introduce a refined structure and naming (e.g. replacing different aliases to a single canonical name); but this is conisidered a separate topic and is out of scope for this PR. The migration infrastructure (config_version, registered migrations, aliases) is included so v1 can be added later with a smooth-transition refactor. Note that currently deeper nested dicts, such as the model configuration passed to the registry builder, are kept as dictionaries because the large variety of models do not fit in a singe schema. In the future we may want to add separate hierarchical schemas for the models with clear discriminating fields if better validation per model is required.


What's new

Typed config models

  • DLCBaseConfig — base for nested configs (data, inference, runner, etc.)
  • DLCVersionedConfig — top-level configs with versioning and change tracking
  • ProjectConfig — typed project config.yaml
  • PoseConfig (+ nested configs) — typed pytorch_config.yaml
  • TestConfig, WeightInitialization, and related helpers

Core capabilities

  • Strict validation on load and assignment (extra="forbid")
  • Unified I/O: from_yaml, from_dict, from_any, to_yaml, to_dict (with optional normalization for serialization)
  • Legacy dict-style access (cfg["key"], .get(), .update())
  • Field aliases for renamed keys, with deprecation warnings
  • Nested access via select() / set_nested()
  • Change tracking: dirty fields, change notes, optional logging on save
  • YAML comments preserved on write

Not everything is fully typed: some nested sections (e.g. model-specific parameters inside ModelConfig) remain plain dicts so different architectures can keep their own flexible parameter sets.

API changes

  • read_config() returns ProjectConfig (still re-exported from auxiliaryfunctions)
  • make_pytorch_pose_config and related factories are deprecated wrappers around PoseConfig.build / TestConfig.build
  • Loader accepts model_config (path, dict, or PoseConfig); model_config_path still works with a deprecation warning
  • PyTorch APIs accept typed configs or legacy inputs via from_any

Important behavioral changes

  • Stricter loading — unknown keys in config.yaml raise validation errors
  • Missing engine — defaults to pytorch (was tensorflow)
  • project_path auto-repair — still corrected on load; read_config writes back when needed
  • Legacy YAML!!python/tuple tags are no longer auto-repaired
  • Optional nested fields — many optional sections are None rather than {}; call sites updated accordingly
  • Snapshots/exports — configs normalized to plain dicts before torch.save

Scope

  • In scope: deeplabcut/core/config/, PyTorch config package, and call sites across training, inference, export, modelzoo, metadata, and utilities
  • Mostly unchanged: TensorFlow path
  • Out of scope: v1 schema redesign and migrations

Test changes

New tests (~3,400 lines)

  • tests/core/config/ — base config, project config, YAML I/O, versioning, change tracking, edge cases
  • test_pose_config_creation.py — parametrized PoseConfig.build with v0-style YAML fixtures
  • test_apis_training.pytrain() respects resume_training_from
  • test_generalized_data_converter_config.py — modelzoo ProjectConfig templates

Modified existing tests (11 files) — mostly to match typed configs:

  • Export, modelzoo, webapp: ProjectConfig / PoseConfig fixtures and assertions
  • Inference helpers: mocks updated for new config loading path
  • test_dataset.py: removed config mock on Loader — tests now use real config loading
  • test_pose_multianimal_imgaug.py: fixture patched to satisfy new multi-animal validation
  • Trainset metadata: shared YAML helpers instead of direct ruamel calls

test_make_pose_config.py and test_config_utils.py are unchanged; new behavior is covered by the added tests.


Test plan

  • Run tests/core/config/
  • Run tests/pose_estimation_pytorch/config/test_pose_config_creation.py (runs on main as well)
  • Run modified PyTorch API/modelzoo tests

@deruyter92 deruyter92 added this to the Structured configs milestone Feb 4, 2026
@deruyter92
deruyter92 force-pushed the feat/structured_configs branch 5 times, most recently from b5f2fdc to fdfec16 Compare February 4, 2026 11:04
@deruyter92
deruyter92 force-pushed the feat/structured_configs branch 2 times, most recently from 786653b to 46a0e56 Compare February 18, 2026 07:31
deruyter92 and others added 21 commits February 18, 2026 09:19
…onverter_config.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- reading breaks for corrupted yaml
- added tests for prefered behavior that is currently not implemented (yaml safeloading and validating config keys)
This mixin provides methods for:
    - Loading configurations from dictionaries or YAML files
    - Validating configuration data against pydantic models
    - Converting configurations to dictionaries
    - Pretty printing configuration data

add imports from utils
- Introduced new configuration classes for inference, logging, model, pose, project, runner, and training settings.
- Refactored data loading mechanisms to utilize new configuration structures.
- Moved the multithreading and compilation options in inference configuration to the config module.
- Typed configuration for logging.
- Updated dataset loaders to accept model configurations directly or via file paths.
(The fields are kept identical to old multianimal project configs for now)

move ProjectConfig to deeplabcut/core/config
The return value should be the dictionary, not the instantiated transforms
@C-Achard C-Achard added the lint required Please run pre-commit hooks to ensure your formatting is up-to-date label Mar 30, 2026
@deruyter92
deruyter92 marked this pull request as ready for review June 18, 2026 09:32
@deruyter92
deruyter92 marked this pull request as draft June 18, 2026 09:33
@deruyter92 deruyter92 changed the title [WIP] Final migration to configuration version 1: structured and validated configs [WIP] Final migration to structured and validated configs Jun 22, 2026
deruyter92 and others added 7 commits June 24, 2026 13:06
…, field validation

This commit introduces several refactors and simplifactions:

1. Settle with Pydantic BaseModel (no OmegaConf or Pydantic dataclasses, or Mixins)
2. Introduce a separate PoseMetadataConfig, rather than using a ProjectConfig as metadata field on the PoseConfig
3. Add a validation suite for field validators e.g. BodypartPair, UniqueStrList, etc
4. Clean up / update the schemas where necessary (e.g. default values.)
5. Better normalization / serializion for tuples, lists, ndArrays (WeightInitialization)
6. Simplify change tracking on the DLCBaseConfig
7. Simplify versioning migration: only `from_dict`, not on the full class
8. remove duplicate fields and add linting rule
9. add BaseConfig `set_nested` method
10. ProjectConfig: add convenience repair project path
Apply a set of small fixes and cleanup changes across multiple modules:
- fix Path/string handling in weight_init
- remove many redundant `pass` stubs in abstract classes and placeholders
- adjust loop/range usages (camera calibration, tracklets, DataFrame index)
- improve legacy-argument handling in Loader (DeprecationWarning with stacklevel, error messages) and infer model config path
- reorder and add/remove imports where appropriate
- avoid silently swallowing exceptions after printing (materialize, make_labeled_video)
- use tuple form for startswith check in auxiliary functions
- tweak test random.sample range; and other minor formatting/consistency tweaks. These changes are intended to improve correctness, clarity, and maintainability without altering core behavior.
- Prevent notes on missing fields.
- Fix yaml comments for nested config fields.
- fix validation for None-type read_config_as_dict.
- VersionedConfigs use default factories for PrivateAttrs.
- add missing snapshot_prefix to runner (SA inference requires it).
- fix some faulty default values for modelzoo config templates
1. Refactor PoseConfig: separate modules and add canonical build method.

- Split enums into separate file
- Split PoseMetadata into separate file
- Split PafParamters into separate file
- Add canonical build methods for structured configs: e.g. build from project config

2. Refactor make_pose_config:

- build entire default dict for net_type from the yaml file, before validation.
- deprecate `make_pytorch_test_config`, `make_pytorch_pose_config` and `make_basic_project_config`.

3. Refactor modelzoo - superanimal configs construction via build method
1. Add tests for centralized config edge cases

Introduce tests/core/config/test_config_breakage.py covering pathological cases for the centralized config model. Tests exercise in-place nested mutation validation (xfail), dirty-state isolation between instances, change-note handling and alias-to-canonical mapping, nested YAML comments (xfail), and normalization/serialization of nested models containing Path and Enum values. Uses ProjectConfig, DLCBaseConfig and DLCVersionedConfig to assert expected dirty-tracking, logging, validation, and YAML output behaviors. add test for cross-field validation with bulk updates

2. fix invalid test imaug (faulty fixture) see DeepLabCut/UnitTestData#4

3. Update testscript_pytorch_multi_animal: ctd_conditions required for ctd shuffle.

The documented primary workflow is: set conditions at shuffle / dataset creation, not defer until inference. This was not reflected in the test, and surfaced with the new structured configs.

This commit makes the test in line with recommended workflow in API docstring; Config docs; BUCTD COLAB notebook; and GUI. -> ctd_conditions are passed at dataset creation time. making the shuffle a fully complete artefact, rather than somthing that requires patching in later API calls.

4. fix testscript multianimal transfer learning pass net_type when creating training dataset

5. add tests:  resume training from existing snapshot

add tests for pose config creation

refactor test_pose_config_creation (test cases for migration to typed)

update xfail for cross fields overrides

remove comment tests

fix test_apis_training

make migrations fixtures to fix registry pollution

use tmp instead of tempfile

tests/core/config: catch expected DeprecationWarnings for aliases

move TEST_DATA_DIR to fixture

use centralized yaml loader instead of pyyaml

update test_modify_train_test_cfg_sets_expected_values

update documentation and tests for write_project_config

update test_pose_config_creation copy dict instead of mutate

update test_pose_config_creation, use centralized yaml loader

add test for ProjectConfig bodypartslist

update test_pose_config_creation separate testing of saving
expose get_yaml_loader get_yaml_dumper in core.config
…old API

- export.py
- deprecate update_config_by_dotpath and update_config
- simplified schema DetectorDataConfig instead of DataConfig

use centralized read_config in trainingsetmanipulation

hook up trainsetmanipulation.py to typed ProjectConfig

update callers of edit_config to use typed updates instead
Notable:
revert  default `box_score_thresh` in `DetectorModelConfig`
currently a different box_score_thresh is used in inference (0.01) and evaluation (0.6), putting a default value in the config would change behavior.

move deprecation.py from utils to core
@deruyter92 deruyter92 changed the title [WIP] Final migration to structured and validated configs Final migration to structured and validated configs Jun 24, 2026
@deruyter92 deruyter92 changed the title Final migration to structured and validated configs Migration to structured and validated configs Jun 24, 2026
@deruyter92
deruyter92 marked this pull request as ready for review June 24, 2026 14:58
@deruyter92
deruyter92 requested review from AlexEMG and MMathisLab June 24, 2026 14:58
@C-Achard
C-Achard self-requested a review June 24, 2026 15:50
@MMathisLab
MMathisLab merged commit 061f8dc into main Jun 25, 2026
31 checks passed
@MMathisLab
MMathisLab deleted the feat/structured_configs branch June 25, 2026 19:38
@deruyter92 deruyter92 mentioned this pull request Jul 20, 2026
9 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lint required Please run pre-commit hooks to ensure your formatting is up-to-date WORK IN PROGRESS! developers are currently working on this feature... stay tuned.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants