Skip to content

fix: warn when max_length cuts a supervised span part-way - #49

Merged
Neonkraft merged 2 commits into
feat/qwen3-sft-templatefrom
fix/sft-filter-truncation-categories
Aug 25, 2026
Merged

Neonkraft merged 2 commits into
feat/qwen3-sft-templatefrom
fix/sft-filter-truncation-categories

Conversation

@KonstiNik

@KonstiNik KonstiNik commented Aug 24, 2026 •

Copy link
Copy Markdown
Member

Follow-up to #47 (and merge into it).

The filter keeps a row if any assistant token survives the render truncated to max_length — so it drops one whose assistant turn falls entirely past the cap, but not one whose span straddles it.

My suggestion would be to have an untruncated render that classifies three cases instead: no assistant tokens at all, all of them past the cap, or straddling it. The last kept, with a warning. The render must be untruncated for that case to be visible, so the window is applied by slicing.

The keep/drop decision is unchanged (any(mask[:max_length]) is what the truncated render already computed), so the existing 21 row-filter tests pass untouched. 5 new tests, 108 total; ruff and black clean.

`any(assistant_masks)` only catches rows whose assistant turn is truncated away
entirely. A row that straddles max_length keeps a non-zero mask, so it stays in
training and teaches an answer cut mid-sentence with no end-of-turn token.

One untruncated render classifies all three cases instead — no assistant tokens,
all of them past the cap, or straddling it — at the same cost. The keep/drop
decision is unchanged.
@KonstiNik
KonstiNik requested a review from Neonkraft August 24, 2026 13:36
@Neonkraft

Copy link
Copy Markdown
Collaborator

Hi @KonstiNik

Great catch! Assistant turns cut-off mid-sentence could indeed be a problem. I wonder if it would be better to drop these rows entirely, instead of simply warning the user? One challenge I can see with doing that is that perhaps one too many rows will be dropped. I'll take a look to see if this happens.

Warning is right when the cap is wrong, since raising max_seq_length keeps the
data. It is not enough when the sequence length is fixed by memory and dropping
is the only lever left, so make it a switch. Default stays warn.

Drop still warns. data.datasets[].weight multiplies the rows that survive
filtering, so an uneven drop changes a dataset's share of the mixture while the
config still claims the original weights — that has to be in the log rather than
inferred from a row count.
@KonstiNik

KonstiNik commented Aug 25, 2026 •

Copy link
Copy Markdown
Member Author

Good point, and I think this should be the user's choice:

I'd be against dropping straight away, because the user should first rethink the max_seq_length setting. If memory pins you down, then they should deliberately switch to a dropping mode where we discard the rows.
But I'd like to avoid people just going ahead and not catching that most of the good data is dropped — so dropping warns too, with the count and the share.

I've pushed this as sft.truncated_span_action (warn by default, drop opt-in).

@Neonkraft

Copy link
Copy Markdown
Collaborator

Nice, LGTM!

@Neonkraft
Neonkraft merged commit 604e731 into feat/qwen3-sft-template Aug 25, 2026
Neonkraft added a commit that referenced this pull request Aug 25, 2026
* feat: add original Qwen3-8B template from HuggingFace

* feat: add generation markers

* feat: add Qwen3-8B template to registry

* fix: exclude think-stripped history from Qwen3 loss mask

Qwen3 drops <think> from assistant turns at or before the last user query, so history rendered without a reasoning trace was being trained on.

* test: assert which spans the qwen3 template puts in the loss

* fix: exclude think-stripped history from Qwen3 loss mask

Qwen3 drops <think> from assistant turns at or before the
last user query, so history rendered without a reasoning
trace was being trained on.

* test: assert which spans the qwen3 template puts in the loss

* test: assert qwen3 template renders byte-identically to upstream

Vendors the pristine Qwen/Qwen3-8B chat_template as a fixture and diffs rendered output across the 20-combination matrix.

* refactor: rename qwen3 jinja file

* feat: filter out rows with all zero assistant loss mask

* tests: add tests for row filters

* fix: warn when max_length cuts a supervised span part-way (#49)

* fix: warn when max_length cuts a supervised span part-way

`any(assistant_masks)` only catches rows whose assistant turn is truncated away
entirely. A row that straddles max_length keeps a non-zero mask, so it stays in
training and teaches an answer cut mid-sentence with no end-of-turn token.

One untruncated render classifies all three cases instead — no assistant tokens,
all of them past the cap, or straddling it — at the same cost. The keep/drop
decision is unchanged.

* feat: add sft.truncated_span_action to drop rows a cut span damages

Warning is right when the cap is wrong, since raising max_seq_length keeps the
data. It is not enough when the sequence length is fixed by memory and dropping
is the only lever left, so make it a switch. Default stays warn.

Drop still warns. data.datasets[].weight multiplies the rows that survive
filtering, so an uneven drop changes a dataset's share of the mixture while the
config still claims the original weights — that has to be in the log rather than
inferred from a row count.

---------

Co-authored-by: Konstantin Nikolaou <knikolaou@icp.uni-stuttgart.de>

---------

Co-authored-by: Konstantin Nikolaou <87869540+KonstiNik@users.noreply.github.com>
Co-authored-by: Konstantin Nikolaou <knikolaou@icp.uni-stuttgart.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants