Use fused attention, make the mask fill fp16-safe, validate the AMP dtype, expose DataLoader worker options - #47
Open
Rian354 wants to merge 1 commit into
Open
Conversation
…, expose DataLoader worker options The explicit attention path builds several (B, H, S, S) tensors for each layer and is needed only for interpretability. The mask fill value -1e9 is outside the fp16 range. Any amp_dtype other than bf16 previously selected fp16 with no message. Measurements are in the PR description.
Rian354
force-pushed
the
perf/fused-attention-and-amp-validation
branch
2 times, most recently
from
August 13, 2026 19:07
432107b to
b4db805
Compare
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.
Summary
Performance and correctness in the attention path and the data loader.
The explicit attention calculation builds several
(B, H, S, S)tensors for each layer. Only aninterpretability pass reads them. Ordinary training now uses fused SDPA, and the explicit path
stays available behind
register_hook=True.Two silent defects are also repaired. The attention mask fill value
-1e9is outside the fp16range, so the explicit path is not safe under fp16 mixed precision. The mixed-precision dtype was
not validated, so any value other than
"bf16"selected fp16 with no message.This PR does not add mixed precision.
mainalready hasuse_amp,amp_dtypeand correctGradScalergating for fp16.Implemented
1. Fused attention with
scaled_dot_product_attentionpyhealth/models/transformer.pyMultiHeadedAttention.forwardnow usesF.scaled_dot_product_attentionwhen the caller doesnot request the attention map.
register_hook=True, because an interpretability pass needs themap and its gradient.
4.768e-07 with padding.
attn_mapandattn_gradients. Before this change the map stayedin memory after every forward pass, and no caller read it.
2. Mask fill value that is safe in half precision
pyhealth/models/transformer.pyscores.masked_fill(pad_mask, -1e9). The value-1e9is outside therange of fp16, so the calculation is not correct under fp16 mixed precision.
torch.finfo(scores.dtype).min.masked_fillafter the softmax sets masked positions to exactly 0.3. Validation for the mixed-precision dtype
pyhealth/trainer.pytorch.bfloat16 if amp_dtype == "bf16" else torch.float16. Everyother value therefore selected fp16 with no message. A typo, or the spelling
"bfloat16",changed the precision of a run in silence.
GradScaler, so the silent path changed the gradient behaviour as well.resolve_amp_dtype(). It acceptsbf16,bfloat16,fp16andfloat16. It raises forany other value. It also raises when bf16 is requested on a CUDA device without bf16 support.
4. DataLoader worker options
pyhealth/datasets/utils.pynum_workers,pin_memory,persistent_workersandprefetch_factortoget_dataloader.persistent_workersneedsnum_workersabove 0.prefetch_factorneedsnum_workersabove 0 and a positive value.litdata.StreamingDataLoaderis selected only whennum_workersis above 0. It coordinatesshard reads between workers, and it gives no advantage in a single process.
Validation
The two attention paths agree
Measured effect of mixed precision
These numbers characterise the behaviour that
mainalready has. No measurement of it existstoday, and the projected cost of pretraining depends on it.
A10 GPU,
notes_labs, transformer 128/2/4, full scale, 2 epochs, batch 8:Speed for each epoch is 2.84x for epoch 1 and 2.41x as a mean of the two epochs. Peak
VRAM decreases by 24.5%. The final training losses agree to 0.63%.
An earlier measurement on a
--dev 2500subset gave 3.6x to 3.8x. On that subset the fixedstartup cost is a large part of each epoch. The full-scale numbers above are the correct values.
The DataLoader options give no advantage here
Measured with mixed precision already enabled: 39.0 and 41.8 seconds for each epoch by default,
against 40.0 and 43.0 seconds when tuned. There is no advantage. The options are available and
are not enabled. This result is reported so that a reader does not repeat the measurement.
Unit tests
File:
tests/test_attention_paths.py, 7 tests.test_fused_and_explicit_paths_agree_without_paddingtest_fused_and_explicit_paths_agree_with_paddingtest_fused_path_does_not_retain_the_attention_maptest_explicit_path_still_supplies_the_attention_maptest_mask_fill_value_is_representable_in_half_precisionmaintest_amp_dtype_is_validated_not_silently_coercedmaintest_gradients_flow_through_the_fused_pathTwo of the seven tests fail on
main. These are the two defect tests. The other five areregression guards:
mainhas one attention path, so agreement between two paths is true therewithout any change.
No regression: 12 existing transformer and dataset tests pass on this branch.
Not in this PR
The Optuna changes are held for a later PR.
scripts/optuna_e2e.pyimportsfit_lab_standardizerandlab_standardizer_fit_scope, andscripts/optuna_pretrain.pyimportspyhealth.models.pretrain.mainhas none of these, so an Optuna PR cannot import until thepretraining PR and the observation-window PR are merged.
Draft #43 has the same problem today.
scripts/optuna_pretrain.py:65importspyhealth.models.pretrain, and that package is not on the #43 branch.The cache for the frozen text encoder is also held for the pretraining PR. The cache is selected
by
field_name in self._frozen_text_fields, andmainhas no_frozen_text_fieldsand notext_finetune_mode.Merge order
This PR shares no file with the observation-window PR (#46), so the two can merge in either
order.
It does share files with the later PRs:
pyhealth/models/transformer.pyandpyhealth/datasets/utils.pywith #48, andpyhealth/trainer.pywith #49. Merge #46, then #48,then #49, and rebase this PR onto the result. The changes do not depend on each other, but a
clean merge at any time will conflict.
Questions for the reviewer
read
attn_maptoday?resolve_amp_dtypenow raises for a value that previously selected fp16. Is any configurationin use that depends on the previous behaviour?