# bf16 memory test and GPU choice (2026-10-06, estimates, nothing was run) **No GPU job is started without Kral's go. Decision 2026-10-06: wait. Run the memory test when the training data is near the size of the first SFT run** (the real sample lengths and the count then decide the flavor; today there are 36 train samples). Script: `train/hf_train_bf16.py` (`--memory-test --sweep 16000,32000,48000,64000`: no data needed, 3 optimizer steps per length, stops at the first OOM, uploads the result as `memtest_*.json` to the output repo). **The length limit (48k or 64k) is decided by this test** (decision 3): the builder keeps `--max-tokens 48000` until then; 64k would bring back the long CDS trajectories (today 5 of 19 are over 48k). ## Model (from config.json of Qwen3.8-27B) 64 layers (48 linear attention, 16 full attention), hidden 5120, MLP 17408, vocab 248320, 27.8 B parameters. LoRA rank 16 on q/k/v/o, gate/up/down, in_proj_qkv, in_proj_z, out_proj: **107 M trainable parameters**. ## Memory by component (GB; estimate, not a measurement) | component | 48k | 64k | how | |---|---|---|---| | weights bf16 | 51.8 | 51.8 | 27.8 B x 2 bytes | | LoRA weights + grads + 8-bit Adam | 1.0 | 1.0 | 107 M x 10 bytes | | layer inputs for the backward pass | 29.3 on the GPU, about 0 with the Unsloth CPU offload (then 29 GB host RAM) | 39.1 / about 0 (host RAM 39 GB) | seq x 5120 x 2 bytes x 64 layers | | recompute peak of one layer | 9.2 | 11.3 | MLP tensors seq x 17408 x 2 bytes x 4 plus 3 GB for attention (assumption) | | logits and loss | 3 chunked, 67 full logits | 3 chunked, 89 full logits | full logits: seq x 248320 x (bf16 + fp32 upcast + grad) | ## Fit by GPU (92 % of the card counted as usable) | seq | checkpoints | loss | estimated peak GB | 1x A100 80 GB | 1x RTX PRO 6000 96 GB | 1x H200 141 GB | 2x H200 282 GB (model parallel) | |---|---|---|---|---|---|---|---| | 16k | CPU offload (Unsloth) | chunked | 61 | fits | fits | fits | fits | | 16k | CPU offload (Unsloth) | full logits | 80 | no | fits | fits | fits | | 16k | on the GPU | chunked | 71 | fits | fits | fits | fits | | 16k | on the GPU | full logits | 90 | no | tight | fits | fits | | 32k | CPU offload (Unsloth) | chunked | 63 | fits | fits | fits | fits | | 32k | CPU offload (Unsloth) | full logits | 104 | no | no | fits | fits | | 32k | on the GPU | chunked | 82 | no | fits | fits | fits | | 32k | on the GPU | full logits | 124 | no | no | fits | fits | | 48k | CPU offload (Unsloth) | chunked | 65 | fits | fits | fits | fits | | 48k | CPU offload (Unsloth) | full logits | 129 | no | no | fits | fits | | 48k | on the GPU | chunked | 94 | no | tight | fits | fits | | 48k | on the GPU | full logits | 158 | no | no | no | fits | | 64k | CPU offload (Unsloth) | chunked | 67 | fits | fits | fits | fits | | 64k | CPU offload (Unsloth) | full logits | 153 | no | no | no | fits | | 64k | on the GPU | chunked | 106 | no | no | fits | fits | | 64k | on the GPU | full logits | 192 | no | no | no | fits | Reading: - **The loss is the main risk, not the weights.** With full logits only the H200 (141 GB, CPU offload) fits 32k and 48k, and not 64k. The run needs a fused or chunked cross entropy (Unsloth has one for the architectures it patches; whether it covers `qwen3_5` is unknown). The memory test shows it at once: if 16000 already fails on an H200, the loss is the cause. Fallback (not built): hidden states, then the loss over the 32 % labeled positions only, in chunks of 4k. - **A100 80 GB (2.50 USD/hour)** only with the CPU offload and a chunked loss (about 65 GB at 48k, 67 GB at 64k: no margin). **H200 141 GB (5 USD/hour)** fits with margin; **RTX PRO 6000 96 GB (2.75 USD/hour)** fits with the offload and the chunked loss. - Multi-GPU (`a100x4`, `h200x2`): only with model parallelism, one card works at a time; not before the single card test. ## Order when the test is run 1. Memory test on **h200** (about 40 minutes with 64k, about 3.5 USD): sweep 16000,32000,48000,64000, offload on. The limit is the largest length that fits with margin. 2. If the loss is the problem: build the chunked loss (about 2 hours), repeat. 3. Real run with `--s2-epochs 3 --s2-loss-share 0.6` (stage 1 epochs are computed: stage 2 carries 60 % of the loss tokens; weights of the own-test class are applied). ## Time and cost (estimate from the nf4 run: 221 tokens/s on an A100 at 6.75k tokens per document; bf16 faster, an H200 about 2 to 2.5 times an A100) Today's data: stage 2 is 36 samples (0.94 M tokens per epoch, 3 epochs = 2.8 M tokens) and, by the 60 % rule, about 0.2 epochs of stage 1 (0.5 M tokens): 3.3 M tokens in total. A100 about 3.1 h (about 8 USD), H200 about 1.4 h (about 7 USD). With three times the stage 2 data (the size that the restart plan aims at): 2.8 M tokens per epoch, 8.5 M in 3 epochs, plus about 0.6 epochs of stage 1 (1.5 M): 10 M tokens, H200 about 4.3 h (about 21 USD), A100 about 9 h (about 23 USD). The real number comes from the memory test (`step_seconds`).