allenai / allenai/open-instruct

MoE + Dolci-Think SFT baseline: seq 16384, reasoning-general math gains where instruct SFT gave 0.0000

Abierto
#1,849 3 comentarios 0 reacciones 1 asignado Reclamado por @abhishekraok Ver en GitHub
Lenguaje dominante
Python
Estrellas
3.9k
Forks
585
Merge medio
5 d 17 h
PR fusionados (30 d)
16

Descripción

> # ⚠️ THIS EXPERIMENT USED THE **MIDTRAIN** CHECKPOINT, NOT THE LONG-CONTEXT ONE
>
> Everything below is SFT from the **8192-native midtrain** base:
> `.../olmo-ddp/midtraining/mt-1p2b-kda-ev2-neg-nope-gated-latentmoe-l2-paper-cx8-samebatch-lr1p6e-4-r1/step63802`
>
> A **long-context sibling of the same lineage and step count exists**, native 65536:
> ```
> /weka/oe-training-default/ai2-llm/checkpoints/jacobm/olmoe3/olmo-ddp/long_context/legacy-cx8-v2/latent-kda-l2/1.2B/cx8-samebatch/step63802
> ```
> (verified: `train_module.max_sequence_length = 65536`, `rank_microbatch_size = 65536`)
>
> **Read this issue as an instruct-vs-think data-mixture experiment on the midtrain base**, which is
> what it is and what it is internally consistent for. Do not read it as the recommended baseline
> checkpoint. Midtrain was the consistent choice at the time — #1843 and every baseline number here
> use it, including the instruct-SFT arm that supplies the `+0.0000` control the whole result rests on
> — but a follow-up on the LC base is the better artifact to build on.
>
> **The LC HF export declares the wrong context — confirmed by its author.** The export at
> `eval-comparisons/legacy-cx8-v2/latent-kda-l2/1.2B/long-context/hf` declares
> `max_position_embeddings: 8192` for a model trained at **65536**. Jacob, asked directly: *"that was
> just a mistake if so, the conversion code should set the config to 65k at all times, the model was
> trained to 65k."*
>
> (This warning previously said the 8192 was a deliberate choice for stage-comparable eval instance
> sets. That was a plausible story fitted to the evidence, not something the artifacts could settle,
> and it was wrong. Only the author could resolve it.)
>
> **Scope:** all **9** long-context exports under `eval-comparisons/` are affected —
> `latent-kda-l2` and `kda-nonlatent`, every size 275m/480m/810m/1.2B, plus
> `480m/long-context-lr40pct`. The 17 pretrain and midtrain exports declare 8192 and are **correct**,
> having been trained at 8192. Uniform-8192 batch conversion, right for every stage except LC.
>
> **No re-conversion needed.** The value appears in exactly two fields —
> `config.json:max_position_embeddings` and `tokenizer_config.json:model_max_length`. `tokenizer.json`
> sets no truncation or padding, `generation_config.json` has no length field, and the model is NoPE
> (`use_rope: false`, so `modeling_olmo3moe.py:1126` leaves `rotary_embs = None`) — no weights or
> buffers depend on it. Two JSON edits, versus ~2 h per 35 GB re-export.
>
> **Why it matters:** the value governs vLLM's `max_model_len`. Evaluating an unfixed export caps
> context at 8192 and silently drops over-length prompts — the same mechanism that dropped 512
> ifeval_mt instances in this issue. Fix the config or pass `hf_overrides` before evaluating. For
> *training* it is irrelevant; the olmo-core DCP config carries 65536 correctly. These are Jacob's
> artifacts and patching is his call.
>
> **Note also:** at seq 16384 the LC base changes nothing about truncation (still 30.6% of think rows
> cut). Truncation only improves if you also train at 32k/64k — and 16384 already sits at 245.4 GiB of
> 267.7 on a B300, so 65536 is a memory-architecture problem (4x microbatch, needs
> `recompute_each_block`, likely multi-node), not a config flag. "LC base at seq 16384" and "train at
> 64k" are separable experiments; the former costs nothing extra.

---

Tracking issue for making **OLMoE3 latent-KDA MoE + `allenai/Dolci-Think-SFT`** the standing SFT baseline for future iterations. Follow-on from #1843 (which covered the same model on Dolci-**Instruct**).

Model: 18.5B total / 1.3B active, 20 layers (15 KimiDeltaAttention + 4 NoPE full-attention), 512 routed experts top-16 + 1 shared, d_model 1280. Base = Jacob's midtrain `step63802`.

Plan, full run log and failure ledger: `/weka/oe-adapt-default/abhishekr/handoff/moe-think-plan.md`

## Headline: it works, and the gain is reasoning-general

Anchor run at **1,806,696,448 tokens** — chosen to exactly match dense-think, dense-instruct, hybrid-instruct and MoE-instruct, so the numbers sit in one matrix. seq 16384, LR 2.5e-5, 1x8 B300.

| task | n | base | + think | Δ | paired McNemar |
|---|---|---|---|---|---|
| **gsm_symbolic** | 5000 | 0.5606 | 0.6476 | **+8.70** | z=12.29, p=1e-34 |
| **gsm8k** | 1319 | 0.6308 | 0.6907 | **+5.99** | z=4.92, p=8.5e-07 |
| **math500** | 500 | 0.3100 | 0.3520 | **+4.20** | z=2.01, p=0.044 |
| **ifeval_mt** (restricted) | 1262 | 0.2686 | 0.4128 | **+14.4** | ~7.6 unpaired |

**Instruct SFT moved gsm8k by exactly +0.0000 at the same token budget.** That flatness is what makes these deltas attributable to the think mixture rather than to SFT in general.

**Pre-registered test passes.** GSM-Symbolic exists to separate real arithmetic reasoning from template familiarity on GSM8K. Criterion fixed before the base number was measured: symbolic Δ ≥ gsm8k Δ ⇒ reasoning-general. Result: **+8.70 > +5.99**, the same ordering the dense report found (+7.2 > +5.8). Not contamination or memorisation.

**Cross-architecture transfer**, on gsm8k code verified byte-identical across `479f90e..moe-parity`:

| | dense 7B | MoE 18.5B/1.3B-active |
|---|---|---|
| gsm8k | +5.8 | **+5.99** |
| gsm_symbolic | +7.2 | **+8.70** |

## seq 16384 instead of the native 8192

At 8192, **49.1%** of Dolci-Think rows are cut mid-trace; at 16384, **30.6%**. Measured from the tokenised cache's doc offsets. Truncated rows are unterminated reasoning traces, i.e. supervision to never stop.

16384 turned out to be **~47% faster per token** than 8192 (31,556 vs 21,544 tok/s/device on B300) — KDA is linear attention and only 4 of 20 blocks are full attention at d_model 1280, so longer sequences amortise better. 245.4 GiB reserved of 267.7 at 8 ranks.

Caveat worth stating: this *narrows* the context gap to dense/hybrid (32768) rather than opening a new one, but the within-family instruct-vs-think contrast now varies context as well as data. Instruct rows are ~840 tokens and sequences are packed, so seq length is near-irrelevant for that arm.

## Saturation curve — the actionable finding

| step | tokens | gsm8k | Δ |
|---|---|---|---|
| base | — | 0.6308 | — |
| 345 | 0.36B | 0.6748 | +4.40 |
| 690 | 0.72B | 0.6710 | +4.02 |
| 1380 | 1.45B | 0.6937 | +6.29 |
| 1723 | 1.81B | 0.6907 | +5.99 |

**~3/4 of the gsm8k effect is present by step 345 — 1.9% of one think epoch.** 1380→1723 is flat within noise. Meanwhile ifeval_mt is still climbing (+11.3 at 1380 → +14.4 at 1723). So math transfer is cheap and early-saturating; instruction-following is the axis that keeps paying.

## LR is not a sensitive knob

Two arms at the **full anchor length** (not a short screen — `build_scheduler` derives warmup and annealing from `num_training_steps`, so a 300-step probe fully anneals inside its window and systematically favours the larger LR):

| arm | last-100-step mean CE | load imbalance |
|---|---|---|
| 2.5e-5 | 0.8681 | 3.476 |
| 5e-5 | 0.8577 | 3.376 |

Indistinguishable. No expert collapse in either — imbalance fell monotonically in both.

## Methodology notes that changed conclusions

1. **ifeval_mt denominators differ by context length.** A 16384 checkpoint scored **1582** instances vs the 8192 baselines' **1262**, because vLLM accepts prompts the shorter-context models rejected. Comparing raw numbers would have been invalid *and looked entirely normal*. Restricted metric via `handoff/rescore/restrict_to_baseline.py`, validated against a known baseline first. (192 prompts exceed even 16384, so "full set" n=1582 is still not the full 1774.)
2. **Run the paired test.** math500 unpaired reads z=1.72 / 1.39 — "no effect". Paired McNemar: z=2.50 / 2.01, significant. At n=500, 80% of items agree between arms and contribute variance without signal. Reported unpaired, this task would have been written up as a null and that would have been wrong.
3. **math500 is underpowered** (n=500, SE on a difference ≈ 3.0pt): a null there is uninformative, not evidence of no effect. Pre-registered before the base landed.
4. **Converter defaults to `--max-sequence-length 8192`.** Converting a 16384 checkpoint with the default exports `max_position_embeddings=8192` and silently caps vLLM — worst on exactly the long-generation tasks the larger context exists to serve. All five exports verified at 16384 before evaluation.
5. **The eval launcher's default `OLMO_EVAL_DIR` is pre-#296**, i.e. the buggy IFEval loose scorer. All numbers here are at `abhishekr/moe-parity` (post-fix). Baselines were re-scored offline to match; the fix costs ~1.5-1.7pt uniformly and leaves deltas unchanged.

## In progress

**Phase 2** — the actual baseline run: 0.5 epoch (9,124 steps / 9.57B tokens, 5x the anchor), 1x8, seq 16384, LR 5e-5, checkpoints every 1,825 steps. Currently ~85%. Will extend the curve to test whether the early plateau holds at 5x the budget.

## Known-unmeasured: 2x8 multi-node

Two smoke attempts were cancelled by cordoned nodes **before running any model code**, so nothing is known about 16-rank DDP or the 207 GB synchronous checkpoint write at that scale. Not a negative result — an absence of measurement. Worth retrying when holmes is healthy; it would halve wall-clock (5.3h vs 10.5h per half-epoch).

## Repo-level issues surfaced along the way

- **96 scripts (27 under `scripts/train/debug/`) pass `--preemptible`.** On holmes that makes a job unallocated ⇒ backfill-only ⇒ it never schedules when the cluster is busy. A job that failed to schedule for 70 minutes at priority `high` ran in **30 seconds** without the flag. This is the default failure mode for real work on holmes and reads like a capacity problem.
- `--max_retries` does not cover cordon cancellations (`canceledCode: 12`), and multi-node additionally requires all replicas up within 10 minutes — so one bad node kills the group with no retry.
- Cache-key non-reproducibility (#1818) blocked reusing an existing tokenisation; see comment there.

## Artefacts

- Anchor arms: W&B `nwq5613d` (2.5e-5), `ly87xpq5` (5e-5); Phase 2: `6ks90lcj`
- Checkpoints: `deletable_checkpoint_states/r708wdtx` (anchor A, 5 steps + HF), `givs31pn` (arm B), `s3zl5u0c` (Phase 2)
- Launch script: `scripts/train/debug/oc_sft_olmoe3_kda_think.sh` (branch `think/moe-baseline`)
- Tooling: `handoff/rescore/{restrict_to_baseline,mcnemar_generic}.py`

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.