fix(magic): score per-query MAGIC by document, not by row - #414
Closed
luciaquirke wants to merge 1 commit into
Closed
fix(magic): score per-query MAGIC by document, not by row#414luciaquirke wants to merge 1 commit into
luciaquirke wants to merge 1 commit into
Conversation
luciaquirke
force-pushed
the
fix/per-query-chunked-queries
branch
3 times, most recently
from
August 6, 2026 11:33
bbbc185 to
6c0edd3
Compare
A chunked query set (`query.chunk_length > 0`) carries a per-token
`doc_ids` column: a row can pack several documents and a document can
span several rows. `compute_per_query_magic_scores` selected row `qi`
for query `qi` and sized the stream's weights by row, so `DataStream`
— which indexes the weights by document id whenever the batch has a
`doc_ids` column — walked off the end:
IndexError: index 2 is out of bounds for dimension 0 with size 2
bergson/magic/data_stream.py:132 self.weights[indices]
Repro: `bergson magic <run> --query.chunk_length 32 ...`.
Pick the rows holding each document's tokens instead, mask every other
document's tokens out of the labels, and drop `doc_ids` from the
per-query stream so the weights (which `compute_query_gradients`
discards anyway) are indexed by row. Each column is then that
document's own mean cross-entropy, which is the unit `validate_scores`
and `per_doc_query_losses` already score against.
Padding cycles through the document's own rows rather than appending
dead ones — a pad row is a real term in the query loss, and an all-pad
batch on some rank would silently scale the query gradient down. With
one row per document this repeats that row, exactly as
`pad_dataset_to_batch_size` did, so unchunked runs are unchanged.
Chunking drops the tail that doesn't fill a chunk, so a short document
can leave no tokens behind; those score zeros, matching the zero
baseline loss `per_doc_query_losses` gives them. Their correlation is
undefined, so the reported mean Spearman now averages the queries that
have one instead of coming out nan for the whole run.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
luciaquirke
force-pushed
the
fix/per-query-chunked-queries
branch
from
August 6, 2026 11:46
6c0edd3 to
65cb5cb
Compare
This was referenced Aug 6, 2026
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.
Opus slop
The bug
Per-query MAGIC crashes on a chunked query set (
--query.chunk_length 32, i.e. any raw dataset you let bergson chunk):A chunked query set carries a per-token
doc_idscolumn: a row can pack several documents and a document can span several rows, so queryiis not rowi.compute_per_query_magic_scoresselected rowqiand sized the per-query stream's weights by row, whileDataStreamindexes weights by document id whenever the batch hasdoc_ids— hence the overrun. Unchunked query sets (every shipped example useschunk_length: 0) have nodoc_idscolumn and were unaffected.Fix
Score per document, which is the unit everything downstream already uses (
validate_scoresrequires one column per query document;per_doc_query_lossesaggregates losses bydoc_ids):qi's tokens,doc_idsfrom the per-query stream, so its row-sized weights are indexed by row.compute_query_gradientsdeletesexample_weightanyway — the document restriction now lives in the labels.Padding cycles through the document's own rows instead of appending dead ones: a pad row is a real term in the query loss (the weights that would silence it are discarded), and an all-pad batch on some rank would silently scale the query gradient by
1/world_size. With one row per document this repeats that row — exactly whatpad_dataset_to_batch_sizedid — so unchunked runs are bit-for-bit unchanged.Chunking drops the tail that doesn't fill a chunk, so a short document (e.g. a blank line in wikitext) can leave no tokens behind. Those score zeros, matching the zero baseline loss
per_doc_query_lossesgives them; their correlation is undefined, so the reported mean Spearman now averages the queries that have one rather than coming outnanfor the whole run: