Fix soft-prompt optimization diverging to NaN in half precision - #8
Merged
Conversation
`SoftPromptOptimizer` optimizes the trigger embeddings returned by the
model's embedding layer, so the parameter inherits the model's dtype. Adam
keeps its state in that same dtype, and in fp16 both `grad ** 2` and the
default `eps=1e-8` flush to zero -- so the first step evaluates
`m_hat / (sqrt(0) + 0)`, the prompt becomes +-inf, and every subsequent loss
is NaN.
Repro (gte-modernbert-base, which loads fp16):
losses=[0.0089569091796875, nan, nan, nan]
Keep a float32 master copy of the soft prompt, as mixed-precision training
does, and cast to the model dtype only for the forward/backward. The result's
embedding is cast back, so the returned dtype is unchanged.
fp32 models were never affected, which is why this went unnoticed -- the test
suite loads every model in float32 by design. The added test therefore builds
an fp16 encoder explicitly; it fails on the unpatched optimizer with
`[-0.4306640625, nan, nan]` and passes with the fix.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
The bug
SoftPromptOptimizeroptimizes the tensor returned by the model's embedding layer, so the parameter inherits the model's dtype. Adam keeps its state in that same dtype, and in fp16 bothgrad ** 2and the defaulteps=1e-8flush to zero:So the very first step evaluates
m_hat / (sqrt(0) + 0), the prompt becomes±inf, and every subsequent loss is NaN:This affects any model loaded in fp16/bf16. fp32 models are unaffected — on
all-MiniLM-L6-v2(fp32) the same code descends normally, which is why it went unnoticed.The fix
Keep a float32 master copy of the soft prompt, as mixed-precision training does, and cast to the model dtype only for the forward/backward. The result's embedding is cast back, so the returned dtype is unchanged.
Why it wasn't caught
tests/conftest.pyloads every model in float32 by design ("Both run on CPU in float32 so the suite is portable"), so no existing test exercises the half-precision path. The added test builds an fp16 encoder explicitly.Verified both directions:
AssertionError: [-0.4306640625, nan, nan]Full suite: 8 passed (excluding
test_api_integrations.py, which needs credentials).🤖 Generated with Claude Code