Repository navigation
Document intentional zero-init of RMSNorm scale - #481
meemoprasad wants to merge 4 commits into
Conversation
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
self.scale was initialized with jnp.zeros instead of jnp.ones, causing RMSNorm to output all zeros on a fresh forward pass (before checkpoint loading overwrites it). LayerNorm in the same file correctly uses jnp.ones for its scale param — this looks like a copy-paste bug.
1fd9281 to
b6f1be2
Compare
CLA is now signed and verified — ready for review whenever a maintainer gets a chance. |
sylvesterkaczmarek
left a comment
There was a problem hiding this comment.
This fixes the zero-initialized scale, but there is no regression covering the failure mode. With the old initializer, a fresh RMSNorm maps every input to zero because the normalized activations are multiplied by an all-zero scale, and the current suite would not catch that coming back. Could we add a small test that a newly constructed layer has unit scale and preserves nonzero normalized output?
|
Thanks for the review. Before adding a test, I want to flag something I missed: tests/test_torch_layers.py:160-171 (test_zero_scale_produces_zeros) documents zero-initialised scale as an intentional property (“relying on the residual connection to carry signal”), and the torch RMSNorm (src/timesfm/torch/normalization.py:31) also initialises with zeros. So changing only the flax backend to ones would make the two backends diverge. Maintainers: is zero-init intended for both backends? If yes, I’ll close this PR or turn it into a docstring note on RMSNorm explaining the zero-init. If you’d prefer unit init, I can update both backends and the existing test in this PR. |
|
Good catch. |
|
Thanks @sylvesterkaczmarek, agreed. I’ll repurpose this PR: revert the init change and instead add a short docstring to RMSNorm in both the flax and torch backends explaining that zero init is intentional, so the next reader doesn’t hit the same question. Happy to close it instead if maintainers prefer. |
Change scale initialization from ones to zeros.
Added detailed docstring to RMSNorm class explaining scale initialization and behavior.
Added detailed docstring explaining the initialization of the scale in RMSNorm class.
|
Reverted the init change, kept it as zeros. Added a docstring on RMSNorm in both backends explaining the intentional zero-init, pointing to test_zero_scale_produces_zeros. Ready for review as a docs-only PR. |
sylvesterkaczmarek
left a comment
There was a problem hiding this comment.
Rechecked head 81a1b77. The initialization is unchanged in both backends, and the new RMSNorm docstrings accurately document the intended zero scale and point to the existing Torch regression. This resolves my earlier concern. Looks good as a documentation-only change.
RMSNorm.__init__insrc/timesfm/flax/normalization.pyinitializesself.scalewithjnp.zerosinstead ofjnp.ones:Since
__call__doesnormed_inputs *= self.scale, this makesRMSNorm output all zeros on a fresh forward pass, before any
checkpoint load overwrites the parameter.
LayerNormin the same file correctly initializes its scale withjnp.ones— this looks like a copy-paste bug between the two classes.Currently masked in inference when checkpoints are always loaded
before a forward pass, but affects:
Fix:
jnp.zeros→jnp.ones, matchingLayerNorm's pattern.