vesuvius.train: generate configured auxiliary targets instead of training them against nothing - #1799
Draft
CVasilopoulos wants to merge 1 commit into
Draft
CVasilopoulos wants to merge 1 commit into
CVasilopoulos wants to merge 1 commit into
Conversation
…ning them against nothing ZarrDataset and CrossFrameZarrDataset only load primary targets, and BaseTrainer does not derive auxiliary ones. With auxiliary_tasks in the config, vesuvius.train built the extra heads, never had a target for them and printed "Avg Loss = 0.0000" for them every epoch. ScrollPrize#1490 lost two ablation runs to it. The generators exist. ScrollPrize#422 moved per-patch generation out of the dataset into trainers/auxiliary/, where each trainer adds its targets in _prepare_batch. But the CLI never selected any of them, and each accepts only one task type, so a distance_transform + surface_normals config could not use them. - AuxiliaryTrainer sends each configured auxiliary target to the existing trainer for its task_type, and raises for an unknown type. - vesuvius.train --trainer base (the default) uses AuxiliaryTrainer when auxiliary targets are configured. - BaseTrainer._initialize_training raises before building datasets when auxiliary targets are configured that the trainer does not generate. BaseAuxTrainer reports the targets it generates. The check is in the trainer because the aux trainers build ZarrDataset themselves. - training_flow.md and data_formatting.md no longer say the dataset generates these tensors. Real data: Scroll 1 instance-label cube 01744_02256_04048 (256^3, label = instance id > 0), surface_normals_3d.yaml plus the distance_transform entry from ScrollPrize#1490, patch 64^3, batch 1, 5 train and 2 val steps, CPU. - main: the batch has no aux keys after _prepare_batch, and distance_transform and surface_normals report 0.0000 train and val loss. - this change: _prepare_batch adds distance_transform (1,1,64,64,64) and surface_normals (1,3,64,64,64). Train loss 0.2130 and 0.2927, val loss 1.9482 and 1.0957. - --trainer mean_teacher with the same config stops at startup with "TrainMeanTeacher cannot generate auxiliary targets ['distance_transform', 'surface_normals']". tests/models: 182 passed on main, 186 with this change. The new tests cover the dispatch (matches the single-type trainers, batch shapes, unknown type) and the startup error. Reported by @flummoxjr in ScrollPrize#1490. The fail-fast idea comes from ScrollPrize#1491 (@robertlangdonn) and @ge-al's fix/aux-tasks-fail-fast branch, which put the check in the datasets. Neither's code is used here. Fixes ScrollPrize#1490
|
@CVasilopoulos is attempting to deploy a commit to the scroll Team on Vercel. A member of the Team first needs to authorize it. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
In one sentence: Auxiliary tasks in a training config (
distance_transform,surface_normals, ...) now get real targets and train, and trainers that cannot make those targets stop at startup instead of reportingAvg Loss = 0.0000.One real example: Starting with the Scroll 1 instance-label cube
01744_02256_04048(256^3, label = instance id > 0) andconfiguration/aux_tasks/surface_normals_3d.yamlplus thedistance_transformentry from #1490, I ranvesuvius.trainfor 5 train and 2 validation steps, anddistance_transformandsurface_normalsreported train loss0.2130and0.2927instead of0.0000.Before:
ZarrDatasetonly loads primary targets andBaseTrainerdoes not derive auxiliary ones. The auxiliary heads were built but never had a target, and every epoch printedAvg Loss = 0.0000for them with no warning. That is what #1490 reports: two runs of a 4-run ablation were plain baselines with dead decoders.The code that derives these targets already exists. #422 moved it out of the dataset into
trainers/auxiliary/, where each trainer adds its target in_prepare_batch. Butvesuvius.trainnever selects those trainers, and each accepts only one task type, so a config with bothdistance_transformandsurface_normalscould not use them.After this PR:
AuxiliaryTrainersends each configured auxiliary target to the existing trainer for itstask_type.vesuvius.trainwith the default--trainer baseuses it when auxiliary targets are configured, so the config from auxiliary_tasks silently train nothing with ZarrDataset: aux targets filtered out, no derived-tensor generation, loss reports 0.0000 #1490 trains all three heads without new flags._initialize_training, before datasets are built, with an error naming them.Proof: Upstream main
4b3c72882against this branch on the same base, same data, config and flags, CPU only. The attached images show the evidence script's terminal output. Its output is filtered to the trainer, batch and loss lines.show_batch.pyis a small wrapper that prints the tensor keys of the first batch before and after_prepare_batch, then callsvesuvius.train'smainunchanged. Thevesuvius code:,EXIT=and--lines are printed by the scripts. Look at theAvg Losslines.--trainer base--trainer base--trainer mean_teacher, which does not generate auxiliary targetsWhy / where this is useful:
I picked this up because configs with auxiliary tasks were silently training those heads against nothing, and the only earlier fixes just made the run stop. I ran the before and after on a real Scroll 1 labelled cube and checked that the auxiliary losses go from 0.0000 to real values, and that a trainer that can't make the targets stops at startup.
Auxiliary heads such as signed distance and surface normals are meant to give the surface model extra geometric supervision for tracing papyrus sheets. Anyone running ablations or training with them, as in #1490, now gets heads that learn from targets derived per batch, and the config shipped in
configuration/aux_tasks/works with the default command. If they pick a trainer that cannot derive the targets, the run stops before it spends GPU time.Details
What changed
trainers/auxiliary/auxiliary_trainer.py(new):AuxiliaryTrainer(BaseAuxTrainer)mapstask_typeto the five existing single-type trainers and calls their_compute_aux_tensor. It raises at construction for an unknown type. No generation code is duplicated.trainers/auxiliary/base_aux_trainer.py:_generated_auxiliary_targets()returns the auxiliary targets the trainer will fill.train.py:BaseTrainer._generated_auxiliary_targets()returns an empty set, and_check_auxiliary_targets()raises when a configured auxiliary target is not in it._initialize_trainingcalls it first. All trainers that override_initialize_trainingcallsuper()first, so the check covers them too.cli.py: only theelif trainer_name == "base":branch changes. It picksAuxiliaryTrainerwhen any target hasauxiliary_task: true.docs/training_flow.md,docs/data_formatting.md: the lines that said the dataset derives these tensors now say which trainer does, and that other trainers stop at startup.AuxiliaryTraineroutput equals the single-type trainers' output for a mixed config,_prepare_batchstacks both tasks with the right channel counts, an unknown type raises, and_initialize_trainingraises before_configure_datasetis called.Why the trainer and not the dataset, and why automatic selection
The generators already live in trainers, and they build
ZarrDatasetthrough_build_dataset_for_mgr. A check insideZarrDataset.__init__would also stop those trainers. Putting it in the trainer lets each trainer declare what it generates.auxiliary_tasksis set in the YAML and the docs never mention a trainer flag, so the default command should train them. A new--trainervalue would mean the #1490 config still fails until users find it, and it would add another option to the list. If you prefer an explicit--trainer auxiliary, it is a two-line change.Tested
4b3c72882. Linux x86_64 only,python:3.14-slimcontainer, torch 2.14.0+cpu, zarr 3.3.0, CPU only, 8 cores. Not tested on GPU, macOS or Windows.https://dl.ash2txt.org/full-scrolls/Scroll1/PHercParis4.volpkg/volumetric-instance-labels/instance-labels/01744_02256_04048/(_volume.nrrd15 MB,_mask.nrrd0.8 MB, 9 instance ids). Written unchanged todata/images/01744_02256_04048.zarr, withdata/labels/01744_02256_04048_surface.zarr= mask > 0 (23.7% of voxels).configuration/aux_tasks/surface_normals_3d.yamlplus thedistance_transformentry from auxiliary_tasks silently train nothing with ZarrDataset: aux targets filtered out, no derived-tensor generation, loss reports 0.0000 #1490 (SignedDistanceLoss,distance_type: signed),batch_size: 1,patch_size: [64, 64, 64], deep supervision on.tr_config(vesuvius.train: argparse schedule defaults (--max-epoch 1000 etc.) silently override YAML tr_config values #1489, open PR vesuvius.train: stop argparse schedule defaults from overriding tr_config (fixes #1489) #1702).pytest tests/modelsin the same container.Limitations
--seedpins the train/val split but not patch sampling, so the two runs trained on different patches (seepatchin evidence.log). The comparison is about whether the auxiliary heads get a target at all, not about the loss values.augment_on_device: true,_prepare_batchruns before the on-device transforms, so vector targets such as normals would be transformed like images. I did not change that.vesuvius.train --verbosefails at the first-batch printout on main already, withAttributeError: 'list' object has no attribute 'dtype'onpatch_info. It fails the same way with this change. I left it out of this PR.CrossFrameZarrDatasetwith auxiliary tasks goes through the same trainer path, but I did not run it.Prior work and credit
fix/aux-tasks-fail-fast(eb53bef) raise inZarrDataset(and, in the branch,CrossFrameZarrDataset) when auxiliary tasks are configured. The startup error here follows that idea but lives in the trainer, because the aux trainers buildZarrDatasetthemselves. Fail fast when ZarrDataset cannot generate auxiliary targets #1491 also changed the docs to say generation does not exist. The generators do exist intrainers/auxiliary/, so the docs here point to them. No code from either is used.trainers/auxiliary/and its tests. This PR only wires them up.cli.py, in the argument parser. The change here is in the trainer dispatch further down.AI-assisted (Claude Code), human-directed. All runs above were executed on real data, not inferred.
Fixes #1490