Repository navigation
Generalise FourierEncoder with a column/frequency schema - #913
Conversation
|
Also I think we should move DeepIce out of the |
|
@pweigel I think your opinion on this is valuable! |
f6ba4e6 to
2f10ea8
Compare
|
@Aske-Rosted and I had the concern that renaming the Kaggle version of the FourierEncoder to a different name will break backwards compatibility with older pretrained state dicts. I thought about this a bit more, and I actually do not think this is an issue, since the name of the nn.Modules is determined by the attribute name and not the class name. Example: |
f2a211a to
3b9a2d1
Compare
Aske-Rosted
left a comment
There was a problem hiding this comment.
I think the description is a bit verbose and historical. I think it's better to provide just a description of what the class does but exclude information about how it came to exist. If this was a special scenario of the more general class then it might make sense to have that description but not the other way around.
Aske-Rosted
left a comment
There was a problem hiding this comment.
Apart from the doc string looks fine to me.
…-backbone Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
We've built Neptune (#923) on this |
`FourierEncoder` assumed its input was ordered (x, y, z, time, charge, auxiliary) and applied multipliers fixed to the Kaggle dataset's normalisation, so it index-errors or silently mis-encodes on data laid out differently (issue graphnet-team#753). The multipliers are the more subtle half: they set which separations the sinusoidal ladder can resolve, so carrying them to a detector with a different normalisation changes the resolved physical range without any error. Rename the existing class to `FourierEncoderEPJC`, documented as the EPJ-C Kaggle implementation with those assumptions, and add a `FourierEncoder` taking `schema: Dict[int, float]` -- column index to multiplier. Nothing is assumed about column order or meaning, and the boolean embedding and MLP projection are left to the model, both being problem-specific. Follows the design proposed in the review of graphnet-team#759. The refactor is representation-preserving: the sinusoidal encoding is elementwise, so one embedding per column lays channels out exactly as the strided xyz block did. `tests/models/test_embedding.py` asserts that, and that a schema of the EPJC columns reproduces its pre-projection embedding bit-for-bit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F75gXoxTcs4nVRxGp91ENf
`seq_length` is a per-sequence quantity, while `x` is per-step, and the name is also the constructor's embedding-width argument -- so state the `[B]` shape and that each step receives a copy of its sequence's embedded length. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F75gXoxTcs4nVRxGp91ENf
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F75gXoxTcs4nVRxGp91ENf
A column's resolvable band has two ends: the multiplier fixes the finest wavelength, `n_freq` fixes how far the ladder reaches beyond it. Only the first was per column, so features wanting different spans could not get them -- and they do differ. On a 1 km neutrino detector the sensor spacing and extent ask for a span of ~180x, event timing for ~3600x and the charge quantisation for ~25x, against the single hardcoded 10000 (a ~9085x ladder) every feature shares today. `schema` now takes either a multiplier or a `(multiplier, n_freq)` pair per column, with a bare multiplier falling back to the encoder's `n_freq`, so existing schemas are unchanged. One `SinusoidalPosEmb` is built per distinct span rather than per column, which keeps the common single-span case at one module and one learnable scale under `scaled`. `SinusoidalPosEmb` also precomputes its ladder into a non-persistent buffer instead of rebuilding it every forward pass, so the extra spans cost nothing and checkpoints are untouched. Its `n_freq` docstring said "Number of frequencies", which is `dim / 2`; it is the span of the ladder, and that mislabel is what hid the missing knob. Verified bit-identical against the previous implementation for the ladder itself, `FourierEncoderEPJC`, `SpacetimeEncoder`, and `FourierEncoder` with bare multipliers; a per-column span changes only its own block. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F75gXoxTcs4nVRxGp91ENf
Refactor FourierEncoderEPJC to remove dataset assumptions and allow flexible schema input.
Clarified usage instructions and limitations of the model.
Whitespace-only: rewrap the class summary to the line width docformatter v1.7.7 enforces, which the pre-commit hook otherwise reformats. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016pqUKMMW7ZPkhc9EChzCux
Add `phase_dtype` to `SinusoidalPosEmb` and thread it through `FourierEncoderEPJC` and `FourierEncoder`. At large multipliers the sinusoidal phase grows beyond what the bf16/fp16 mantissa can represent, so neighbouring positions collapse to the same embedding under low-precision training. Evaluating the phase and its sine and cosine in `phase_dtype` (float32 by default) and casting the result back to the input dtype removes that failure while leaving float32 runs unchanged. Pass `phase_dtype=None` to keep the input dtype. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016pqUKMMW7ZPkhc9EChzCux
e3daeab to
611f153
Compare
good point, checkout the way I handled it @felixyu7 |
Addresses #753, following the design @RasmusOrsoe proposed in his review of #759. Picks up where @mobra7's PR left off.
Problem
FourierEncoderbakes in two assumptions:(x, y, z, time, charge, auxiliary), with the slicing hardcoded (x[:, :, :3],x[:, :, 3], …) and the presence of charge/auxiliary inferred from the feature count. This is the index error reported in FourierEncoder not flexible enough for different detectors #753 forFEATURES.ICECUBE86.4096(xyz, time) and1024(charge) are fixed to the Kaggle normalisation.Change
As proposed in the review of #759:
FourierEncoderEPJC— the existing class, behaviour unchanged, with a docstring noting it is the graphnet implementation of the EPJ-C solution (arXiv:2310.15674) and naming the assumptions that make it hard to use elsewhere.IceMix/DeepIceandISeeCubenow reference it, so nothing currently onmainchanges behaviour.FourierEncoder— new, takingschema: Dict[int, float]mapping column index to multiplier, e.g.{1: 1024, 3: 4096}. Columns absent from the schema are not embedded. Nothing is assumed about column order or meaning, and the boolean embedding and MLP projection are left to the model, both being problem-specific. Exposesoutput_dimso the model can size what follows.The
schemadocstring states the wavelength relation, so the next person choosing a value has the arithmetic rather than a constant copied from another detector.Not included
The review also proposed renaming
DeepIcetoDeepIceEPJCwith a refactoredDeepIceowning the MLP, dropping the auxiliary embedding, and updatingexamples/04_training/06_train_icemix_model.py. Left for a follow-up: dropping the auxiliary embedding is a behaviour change worth deciding on its own, whereas this half is strictly additive. Happy to carry it through in this PR instead if you would rather have it in one piece.