NN build system: characterization tests + component registry (steps 1-2)#390
NN build system: characterization tests + component registry (steps 1-2)#390max-dax wants to merge 9 commits into
Conversation
|
Added the concrete metadata-contract proposal (the "(1)" item from the PR description): commit 4456a03 adds Full proposal for the sampler group (incl. the step-3 dict-batch call-signature migration plan and the exact |
Pin current behavior ahead of the NN build-system refactor (hackathon/ NN_Build_System_Design.md, step 1): build_model_from_kwargs dispatch, autocomplete_model_kwargs dimensional completion (plain + GNPE), end-to-end forward passes for all three posterior model types, GNPE added_context embedding, unconditional flow, SVD initial-weight seeding, old nsf+embedding schema mapping, and save/rebuild round trip. Also documents (without fixing) a latent bug: get_theta_embedding_net reads 'frequencies' from the wrong settings level, so any nonzero value crashes the FMPE/score build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Step 2 of the NN build-system refactor (hackathon/NN_Build_System_Design.md):
- dingo/core/registry.py: generic Registry with four-step name resolution
(registered name -> entry points ('dingo.architectures') -> dotted import
path -> 'file.py:Class'), plus the NEURAL_DISTRIBUTIONS / EMBEDDING_NETS /
CONTEXT_MERGERS instances. Third-party architectures no longer require
editing dingo source.
- The three model types register themselves; build_model_from_kwargs
dispatches via the registry instead of the hardcoded models_dict, so
posterior_model_type can also name a plugin (tested end-to-end with a
file-path plugin).
- Name lookup is now case-sensitive; the historical case-insensitivity for
built-in type names moved into update_model_config (the back-compat
boundary), so old checkpoints still load.
- BasePosteriorModel -> NeuralDistribution ('posterior' was too narrow: the
same class models priors/proposals, e.g. the unconditional flow). Lasting
alias BasePosteriorModel kept for existing imports and branches.
Behavior pinned by tests/core/test_build_model.py is unchanged (all pass).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
inference_parameters / context_parameters / standardization as properties: the interface FlowFactor.from_model (sampler-revamp) consumes, so samplers stop dict-spelunking metadata. Reads the model's own train_settings['data'] (matching core/samplers.py semantics, also for unconditional models); works on old checkpoints unchanged. Proposal doc: hackathon/NN_Sampler_Interface.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Batches are now dicts of named tensors end to end (NN_Build_System_Design §4.4): SelectKeys replaces UnpackDict at the end of the training transform chain, train/test epochs move a dict to device, and the NeuralDistribution methods sample / sample_and_log_prob / log_prob / loss take a single context dict (None for unconditional models) instead of positional *context tensors. FlowWrapper and ContinuousFlow route context[k] for k in context_keys into the embedding network, so tensor ordering is declared once at build time instead of being implied by the loader's key order; a missing key raises a ValueError naming it. Tensors stay positional inside the network modules. autocomplete_model_kwargs reads the named sample, which removes the GNPE-proxy detection via IndexError and the overloaded third data slot. Sampler call sites and SampleDataset build the context dict directly. The sampler-side dict signature is part 2 of the NN <-> sampler interface (hackathon/NN_Sampler_Interface.md §2); the sampler-revamp call-site updates follow once both branches are on hackathon-1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New model-settings schema (NN_Build_System_Design §4.3/4.5/4.7): a model declares distribution / embedding_net / context_merger, each a registry type plus a flat kwargs dict. Embedding networks follow a small contract (input_keys, output_dim, complete_settings): dense_svd (the classic LinearProjectionRB + DenseResidualNet stack) and the concat context merger are the first registered components. NeuralDistribution.build_embedding_net assembles them generically, so initialize_network loses its embedding_kwargs-presence branches, and any embedding composes with any distribution type. complete_model_settings replaces autocomplete_model_kwargs: each architecture infers its own input dims from a named sample batch, the generic theta/context dims are computed once, and the completed settings are saved in the checkpoint so loading never needs a data sample. Dimensions in user settings and unused context mergers are errors. The V_rb_list deepcopy hack is gone: initial weights are extra constructor kwargs and never touch the saved settings. save_model now enforces that the standardization covers inference and context parameters (NN_Sampler_Interface §1). update_model_config remains the single back-compat boundary: it maps the old posterior_model_type / posterior_kwargs / embedding_kwargs schema (and the older nsf+embedding form) onto the new one, including added_context -> concat merger; old checkpoints build state-dict-identical networks (pinned by test). Deleted along the way: the never-wired embedding_net_builder parameter, create_nsf_wrapped, create_nsf_with_rb_projection_embedding_net. Example configs, pipe defaults, and the architecture docs notebook use the new schema. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Folds the training side of the chained-NPE branch into the conditioning design (NN_Build_System_Design §4.5): user-declared data.conditioning_parameters feed the same context_parameters list as GNPE proxies (two sources, one declared contract), the ContextMergerMLP lands as the registered "mlp" context merger (mixes context through a learned MLP so context_dim does not grow; dimensions inferred at completion instead of hand-written input_dim), and data.time_alignment=True trains on time-aligned data by skipping the per-detector time shift in ProjectOntoDetectors, with a validation guard on the required conditioning. Tests ported from the chained branch. Its inference side is a factorized-sampler chain (Group A) and is not built here. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Step 3 landed: dict batches, registry-built embeddings,
|
Data-driven weight initialization moves behind two hooks on the embedding contract (NN_Build_System_Design §4.6): init_data_spec() declares the data variation a network wants (noise on/off, network formatting, pinned prior parameters, sample count), and initialize_weights(batches, out_dir) consumes a matching batch iterator. DenseSVDEmbedding implements them, absorbing the SVD math from build_svd_for_embedding_network: it collects per-block strains, builds one SVD basis per block, saves validation diagnostics, drops the zero rows outside its input range, and seeds the projection layer. The trainer no longer knows about the SVD: prepare_training_new walks pm.network.modules(), answers any module's spec via the new initialization_dataloader (a transform-stack variation using the existing omit_transforms mechanism), and lets the module initialize itself — plugin architectures get data-driven initialization without touching dingo. Seeding triggers on svd.num_training_samples, so loading a saved model never re-runs it. Deleted: build_svd_for_embedding_network, the initial_weights threading through NeuralDistribution/build_model_from_kwargs, and the "embedding network is assumed to have an SVD projection layer" special case. SVDBasis moves from dingo/gw/SVD.py to dingo/core/SVD.py — it was always domain-agnostic (numpy/scipy only) and core may not import from gw; the old module remains as a re-export shim. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ports the transformer branch (origin/transformer, stage 1 of the T1 merge) onto the registry build system. TransformerEmbedding registers as "transformer": tokenized strain -> Tokenizer (shared DenseResidualNet with GLU position-conditioning) -> TransformerEncoder -> CLS/average pooling -> optional final net. complete_settings infers the tokenizer input dims and num_blocks from a named sample batch (position is its own batch entry now, instead of overloading the GNPE-proxy slot). Because the seam is generic, transformer x flow-matching works with zero extra wiring (pinned by test), and context parameters compose via the concat merger - both impossible on the original branch. StrainTokenization lands as a data transform, wired into set_train_transforms and GWSampler via the tokenization data settings; core samplers accept dict outputs from transform_pre. DenseResidualNet is unified rather than duplicated: the branch's implementation (per-block GLU context, optional layer_norm, arbitrary leading batch dims) moves to core/nn/resnet.py and replaces the glasflow-block-based version in enets.py - MyResidualBlock is a strict superset of glasflow's ResidualBlock with identical parameter names and forward pass (pinned by test), so old checkpoints load unchanged. Only the rq-coupling conditioner is genuinely a different architecture; per the team decision it is selected by an explicit conditioner_type setting (glasflow_residual default | dense_residual with optional layer_norm), not a layer_norm flag. layer_norm threads through the cfnets builders, and ContinuousFlow no longer shares a mutable-default Identity module. Not ported (deliberate): the advertised-but-dead augmentation config keys (drop_detectors, drop_frequency_range - stage 2, from dingo-t1), the allow_tf32 pop-and-discard, the embedding_type branches (registry handles dispatch), and create_nsf_with_transformer_embedding_net. The ConcatContextMerger now routes multi-input embeddings. Registration of built-in architectures moves to dingo/core/nn/__init__.py. Adds examples/transformer_model and the branch's transformer / resnet / tokenization / sampler-transform tests, adapted to the registry API. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Stage 2 of the transformer merge, from origin/dingo-t1 (build-system step 6). Training-side augmentation for tokenized models, wired via data.tokenization: DropDetectors, DropFrequenciesToUpdateRange (f_cut), DropFrequencyInterval (mask_interval), DropRandomTokens, and NormalizePosition, all marking tokens in drop_token_mask that the transformer then does not attend to. Ported near-verbatim with two deviations: DropDetectors gains unbatched-sample support (our pipeline feeds single samples; the other drop transforms already handled this - covered by the ported no-batch test), and DropRandomTokens loses the increase_p_until_epoch ramp, which is dead on dingo-t1 as well (the epoch injection into samples is commented out there). Inference side: GWSampler gains a suppress property ([f_lo, f_hi] or per-detector dict) with real validation (dingo-t1 left it as an unimplemented warning stub): it requires a tokenized model trained with drop augmentation and an in-domain interval. Frequency-range updates and suppression for tokenized models go through the ported UpdateFrequencyRange transform (masks tokens instead of zeroing strain data), with NormalizePosition applied after mask updates, matching training. Unchanged frequency bounds are passed as None so the domain default does not needlessly mask the zero-padded final token. UpdateFrequencyRange gets the test dingo-t1 left as a TODO. PoolingTransformer (sinusoidal MultiPositionalEncoding + average pooling) registers as the "pooling_transformer" embedding, composing with any distribution through the generic build path. The transformer example config now demonstrates the augmentation settings. Not ported (out of step-6 scope, separate research features on dingo-t1): pretraining (enets_pretraining/pretraining_model), custom schedulers/AMP trainer changes, mixed ASD datasets, TimeShiftStrainGrid, BlockEncoding, skymaps. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
First two steps of the NN build-system refactor, tracking hackathon/NN_Build_System_Design.md. Draft so the interfaces are visible early; steps 3+ (dict batches, per-architecture dim inference replacing
autocomplete_model_kwargs, new config schema) will land here as they are ready.Step 1 — characterization tests (
tests/core/test_build_model.py)Pins current behavior of the build path before it moves:
build_model_from_kwargsdispatch,autocomplete_model_kwargs(plain + GNPE), end-to-end forward passes for all three model types, GNPEadded_contextembedding, the unconditional flow (models-as-priors path), SVD initial-weight seeding, oldnsf+embeddingschema mapping, save/rebuild round trip.Documents (without fixing) a latent bug on main:
get_theta_embedding_netreadsfrequenciesfrom the wrong level oftheta_embedding_kwargs, so any nonzero value crashes the FMPE/score build — the fmpe example only works because it usesfrequencies: 0.Step 2 — registry +
NeuralDistributionrenamedingo/core/registry.py: genericRegistrywith four-step name resolution — registered short name → installed entry points (groupdingo.architectures) → dotted import path →/path/to/file.py:Class. Third-party architectures no longer require editing dingo source (pip plugin or plain file).build_model_from_kwargsdispatches via the registry instead of the hardcodedmodels_dict. A file-path plugin distribution works end-to-end (tested).update_model_config(the back-compat boundary), so old checkpoints still load.BasePosteriorModel→NeuralDistribution: "posterior" was too narrow — the same class models priors/proposals (e.g. the unconditional flow). A lasting aliasBasePosteriorModelkeeps existing imports and other branches working.Compatibility
Old checkpoints and settings load unchanged (pinned by the step-1 tests, which pass unmodified against step 2).
tests/core: 67 passed.Next (step 3) — for reviewers to weigh in on
The metadata contract this build system will write —
inference_parameters/context_parameters/standardization— is exactly whatFlowFactor.from_modelonsampler-revampconsumes; the concrete schema will be proposed here and needs sign-off from the sampler group.🤖 Generated with Claude Code