Describe the bug
The VLM LengthGroupedSampler pre-filter says it uses 1.2x headroom but adds a
flat 512 tokens, and the log line reports the flat threshold labelled as
1.2 * max_length. The two diverge as max_length grows, so at long context the
sampler drops far more samples than the comment describes and the log misstates
the rule that was applied.
nemo_automodel/components/datasets/vlm/samplers.py L117-131:
# Use 1.2x headroom: the estimated length is a heuristic, so only
# drop samples that are clearly overlong. Borderline samples are
# left to PreTokenizedDatasetWrapper's precise tokenize-and-retry.
filter_threshold = max_length + 512
kept = [i for i in all_indices if self.lengths[i] <= filter_threshold]
n_dropped = len(dataset) - len(kept)
if n_dropped:
logger.info(
"LengthGroupedSampler: pre-filtered %d/%d samples with "
"estimated length > %.0f tokens (1.2 * max_length %d).",
n_dropped,
len(dataset),
filter_threshold, # <- max_length + 512
max_length,
)
%.0f receives filter_threshold, so the message asserts that
max_length + 512 is 1.2 * max_length. At max_length=32768 it prints:
LengthGroupedSampler: pre-filtered N/M samples with estimated length > 33280 tokens (1.2 * max_length 32768).
but 1.2 * 32768 is 39322, not 33280.
How far the two diverge
max_length |
code (+512) |
stated (1.2x) |
effective headroom |
delta |
| 2048 |
2560 |
2457 |
25.0% |
+103 |
| 4096 |
4608 |
4915 |
12.5% |
-307 |
| 8192 |
8704 |
9830 |
6.2% |
-1126 |
| 16384 |
16896 |
19660 |
3.1% |
-2764 |
| 32768 |
33280 |
39321 |
1.6% |
-6041 |
| 131072 |
131584 |
157286 |
0.4% |
-25702 |
A flat 512 is ~25% headroom at 2k and ~0.4% at 128k. Beyond max_length=2560
the filter is stricter than the documented rule, and the gap grows with context
length — 6k tokens' worth of samples at 32k, 25k at 128k.
Why it matters: the comment's stated design is that borderline samples are
not dropped here — they are left to PreTokenizedDatasetWrapper's precise
tokenize-and-retry, because self.lengths[i] is only an estimate. With
effectively no headroom at long context, samples the estimator merely
over-counts are silently removed from training instead of reaching that retry
path. The repo has long-context VLM recipes, so this is the regime where the
heuristic's error matters most and the headroom protecting against it has all
but vanished.
There is no user-visible signal that the rule differs from the one logged.
Steps/Code to reproduce bug
for m in (2048, 8192, 32768):
print(m, "code:", m + 512, " logged as 1.2 *", m, "=", int(m * 1.2))
2048 code: 2560 logged as 1.2 * 2048 = 2457
8192 code: 8704 logged as 1.2 * 8192 = 9830
32768 code: 33280 logged as 1.2 * 32768 = 39321
Expected behavior
The log states the threshold actually applied, and the comment describes the
rule the code implements — whichever of the two is intended.
Environment overview
main at 3ddef9b. Static/arithmetic; no GPU, dataset or processor needed.
Additional context
The fix depends on which was intended, and that is a maintainer call:
- The flat
+512 is correct — then the comment and the log string are
simply stale and should say max_length + 512. No behaviour change.
- The 1.2x was correct — then
filter_threshold should be
int(max_length * 1.2), which keeps more borderline samples at long context.
I will send a PR for (1), since it is the strictly non-behaviour-changing
reading and makes the log truthful immediately. Say the word and I will switch
it to (2) instead — happy to follow whichever matches the original intent.
Describe the bug
The VLM
LengthGroupedSamplerpre-filter says it uses 1.2x headroom but adds aflat 512 tokens, and the log line reports the flat threshold labelled as
1.2 * max_length. The two diverge asmax_lengthgrows, so at long context thesampler drops far more samples than the comment describes and the log misstates
the rule that was applied.
nemo_automodel/components/datasets/vlm/samplers.pyL117-131:%.0freceivesfilter_threshold, so the message asserts thatmax_length + 512is1.2 * max_length. Atmax_length=32768it prints:but
1.2 * 32768is 39322, not 33280.How far the two diverge
max_length+512)1.2x)A flat 512 is ~25% headroom at 2k and ~0.4% at 128k. Beyond
max_length=2560the filter is stricter than the documented rule, and the gap grows with context
length — 6k tokens' worth of samples at 32k, 25k at 128k.
Why it matters: the comment's stated design is that borderline samples are
not dropped here — they are left to
PreTokenizedDatasetWrapper's precisetokenize-and-retry, because
self.lengths[i]is only an estimate. Witheffectively no headroom at long context, samples the estimator merely
over-counts are silently removed from training instead of reaching that retry
path. The repo has long-context VLM recipes, so this is the regime where the
heuristic's error matters most and the headroom protecting against it has all
but vanished.
There is no user-visible signal that the rule differs from the one logged.
Steps/Code to reproduce bug
Expected behavior
The log states the threshold actually applied, and the comment describes the
rule the code implements — whichever of the two is intended.
Environment overview
mainat 3ddef9b. Static/arithmetic; no GPU, dataset or processor needed.Additional context
The fix depends on which was intended, and that is a maintainer call:
+512is correct — then the comment and the log string aresimply stale and should say
max_length + 512. No behaviour change.filter_thresholdshould beint(max_length * 1.2), which keeps more borderline samples at long context.I will send a PR for (1), since it is the strictly non-behaviour-changing
reading and makes the log truthful immediately. Say the word and I will switch
it to (2) instead — happy to follow whichever matches the original intent.