fix: make the spatial transforms sample the way they claim to - #57
Open
Hendrik-code wants to merge 2 commits into
Open
fix: make the spatial transforms sample the way they claim to#57Hendrik-code wants to merge 2 commits into
Hendrik-code wants to merge 2 commits into
Conversation
Three defects in gpu/spatial.py, all silent -- nothing crashes and no test fails,
the pipeline just does something other than what the config asked for.
* RandomFlipTransformGPU never read the flip flags its own generator sampled. The
loop recomputed the same `flip_axis`-derived list for every batch element, so it
flipped all configured axes, identically, on every call: three seeded calls gave
byte-identical output, and FlipGenerator3D -- including its "at least one axis"
guarantee -- was dead code. It now reads params["flip"], falling back to the old
all-configured-axes behaviour only for callers that reach into apply_transform
directly with no sampled params.
* The single-axis generators drew their "random" axis in make_samplers, which
kornia calls once and caches. The same axis was therefore degraded for a whole
training run. The draw moves to forward(), via _choose_axis/_keep_one_axis.
* CropGenerator3D additionally drew separate axes for the crop and for its
position, so the crop could be taken along one axis while the position placing
it was randomised along another. It also neutralised the position to 1.0 using
the crop's neutral value; the position is the crop centre as a fraction of the
axis, so its neutral value is 0.5 (centred), not the far edge.
Also `flags.get("resample")` instead of `"resample" in flags` plus a bare
assignment in apply_transform_mask: `resample_method` was annotated but only
assigned inside the `if`, so the restore below could read an unbound local. It is
unreachable today -- apply_transform indexes flags["resample"] and raises KeyError
first -- so this is removing a hazard, not fixing an observed failure, and its two
tests are guards that pass either way.
unit_tests/test_spatial_sampling.py covers all of it; the five regression tests
fail against the previous implementation.
Models trained before this change saw the old behaviour and will not reproduce
against it. No config key, parameter or default changed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The typecheck job started failing on an error in numpy's own stub:
numpy/__init__.pyi:737: error: Type statement is only supported in
Python 3.12 and greater [syntax]
Found 1 error in 1 file (errors prevented further checking)
mypy applies its target version when parsing *every* file, third-party stubs
included. [tool.mypy] deliberately sets python_version = "3.10" -- the floor of
the range `requires-python` promises, rather than whatever CI happens to run --
but the job itself ran on 3.12. pip therefore resolved numpy 2.5, which requires
Python >=3.12 and writes PEP 695 `type` statements in its stubs, and mypy
rejected them as too new for the declared target. Nothing in smauglab/ was ever
reached: the run aborted at the parse error.
Running the job on 3.10 makes the interpreter and the target agree, which is what
the config comment already said was intended. It also resolves numpy 2.2.x, the
newest release that supports 3.10, whose stubs parse under the target. The test
job has been installing this same dependency set on 3.10 all along, so the
install path is already exercised.
Unrelated to the augmentation fix in this branch -- a dependency released since
the last run on main, which was green on 2026-08-10. It is here because the
branch is red without it; happy to split it out if you would rather review it
on its own.
Not reproducible locally: numpy 2.5 cannot be installed below 3.12. Verified
instead that numpy 2.2.6 (the 3.10 resolution) type-checks clean against
python_version = "3.10", and that numpy 2.3.4 does not yet carry the offending
syntax.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 20, 2026
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.
Three defects in
gpu/spatial.py, all silent — nothing crashes and no test fails, the pipeline just does something other than what the config asked for.RandomFlipTransformGPUnever read the flip flags its own generator sampledThe loop recomputed the same
flip_axis-derived list for every batch element, so it flipped all configured axes, identically, on every call. Three seeded calls gave byte-identical output, andFlipGenerator3D— including its "at least one axis" guarantee — was dead code. It now readsparams["flip"], falling back to the old all-configured-axes behaviour only for callers that reach intoapply_transformdirectly with no sampled params.The single-axis generators drew their "random" axis in
make_samplerskornia calls
make_samplersonce and caches the samplers it builds, so the axis was fixed for the transform's lifetime — "degrade a random axis" degraded the same axis for a whole training run. The draw moves toforward(), via new_choose_axis/_keep_one_axishelpers shared byScaleGenerator3DandCropGenerator3D.CropGenerator3Dplaced the crop and its position on different axesIt drew a separate
dimfor each, so the crop could be taken along one axis while the position placing it was randomised along another. It also neutralised the position to1.0by copying the crop's neutral value; the position is the crop centre as a fraction of the axis, so its neutral value is0.5(centred), not the far edge.Also, one hazard rather than a fix
apply_transform_maskused"resample" in flagsplus a bare assignment:resample_methodwas annotated but only assigned inside theif, so the restore below it could read an unbound local. It is unreachable today —apply_transformindexesflags["resample"]and raisesKeyErrorfirst — soflags.get(...)removes a hazard rather than fixing an observed failure. Its two tests inTestMaskResampleRestoreare guards and pass either way; the file's docstring says so.Testing
unit_tests/test_spatial_sampling.py, 12 tests. Five of them fail against the parent commit, verified by stashing the source change and re-running:The rest pin behaviour that must survive the change (mask and image flip together, only configured axes are flipped,
one_dim=Falsestill leaves every axis independent).Full suite: 25 passed, 142 subtests.
ruff checkandruff format --checkclean,mypy smauglab/clean.Compatibility
Models trained before this change saw the old behaviour and will not reproduce against it. No config key, parameter or default changed, so no config needs touching.
Second commit: an unrelated CI fix
dcd133dfixes the redmypyjob. It is not caused by the change above — the job broke on a dependency released sincemain's last lint run (green on 2026-08-10):[tool.mypy]setspython_version = "3.10"deliberately — the floor of whatrequires-pythonpromises — but the job ran on 3.12. mypy applies its target version when parsing every file, third-party stubs included, so pip resolving numpy 2.5 (which requires Python >=3.12 and uses PEP 695typestatements) made mypy reject numpy's own stub. Nothing insmauglab/was reached; the run aborted at the parse error.Running the job on 3.10 makes interpreter and target agree — which the config comment already said was the intent — and resolves numpy 2.2.x, whose stubs parse under the target. The
test (python 3.10)job installs this same dependency set already, so the install path is exercised.Not reproducible locally (numpy 2.5 cannot be installed below 3.12). I verified instead that numpy 2.2.6 type-checks clean against
python_version = "3.10", and that numpy 2.3.4 does not yet carry the offending syntax.Happy to split this into its own PR if you'd rather review it separately — it's here because the branch is red without it.