Appearance
Precision Is the Whole Job: From 13M-Parameter Pretraining to FP4 Inference Engines
Four things crossed my desk this week: a repo that trains an LLM from scratch in plain PyTorch, a Reddit post showing Qwen 3.8 Next running across six V100s, a community Space shipping ternary WebGPU kernels, and an angry thread about FP4 inference engines. Different scales, different hardware, same obsession: how many bits does each tensor deserve, and what happens when you get that wrong?
At one end you have a 13-million-parameter transformer you can train on a free Colab T4. At the other, quantized engines promising the fastest tokens per second by throwing precision out the window. The middle is where most real work happens, and it's where people either learn to manage precision or get cooked by it.
Training from scratch: the 13M parameter on-ramp
FareedKhan-dev/train-llm-from-scratch is exactly what the name says: a full transformer pipeline in pure PyTorch, no trl, no peft, no transformers. Pretraining, SFT, a Bradley-Terry reward model, PPO with GAE, DPO/ORPO/KTO, and GRPO are all handwritten and run on the same small transformer.
The smallest target is 13M parameters, and that number changes who can participate. It trains on a free Colab or Kaggle T4. You can also see what the small end produces. The README shows the 13M model generating a paragraph that opens "In 1978, The park was returned to the factory-plate that the public share to the lower of the electronic fence..." Grammatically plausible, semantically lost. That's the starting line, and the repo is honest about it.
The repo includes a rough GPU guide for training bigger models:
| GPU | VRAM | 2B training | 13M training | Max practical size |
|---|---|---|---|---|
| A100 | 40 GB | Yes | Yes | ~6B to 8B |
| RTX 5090 | 32 GB | Yes | Yes | TBD, only 13M verified |
| RTX 4090 | 24 GB | Yes | Yes | ~4B |
| V100 | 16 GB | No | Yes | ~2B |
| Tesla T4 | 16 GB | No | Yes | ~1.5B to 2B |
| RTX 4060 | 8 GB | No | Yes | ~1B |
Two conclusions fall out of that table. A 2B training run needs a 24GB card; 16GB cards stop at much smaller configs. And when a config OOMs, the pretraining script has --amp, --grad-checkpointing, and --grad-accum flags that cut memory substantially before you spend money on new hardware.
Four data streams, one loss mask
The model itself is a textbook pre-norm transformer: single-head attention with causal masking, multi-head attention that concatenates heads and projects, MLP with 4x expansion, residual connections everywhere. Reading it piece by piece pays off if you've only ever consumed models through transformers. The causal mask is the line that makes it a language model: position t attends only to 0..t, never the future it's trying to predict.
The data pipeline is where the hard-won lessons live. A model only sees integers, so everything begins with tokenization. The repo uses r50k_base from OpenAI's tiktoken, the same tokenizer GPT-3 used. Text becomes a flat array of int32 ids stored in HDF5, with an <|endoftext|> token appended so the model learns where documents end.
Then the data branches into four streams, each with its own format:
SFT needs the trick most people miss: a loss mask. The chat template marks role headers and user content with mask 0, assistant tokens with mask 1. In one packed row from the repo, only 48 of 512 tokens get trained. Drop that mask and the model learns to parrot prompts back instead of answering, and you'll spend weeks blaming the wrong thing.
The RL stages add a verifier reward for GSM8K: a correct answer in the right <think>/<answer> format scores 1.2, a wrong answer that still uses the format scores 0.2. Output shape is part of the contract. That's precision management of a different kind, and it matters just as much as bit width.
Quick Take: Every layer of this stack, from 13M-parameter pretraining to FP4 inference on a six-GPU rig, comes down to one job: deciding which numbers can afford to lose precision and which cannot.
Six V100s, TP2 PP3, and the OOM odyssey
Getting Qwen 3.8 Next running on six V100s with TP2 PP3 (tensor parallel 2, pipeline parallel 3), per the multi-GPU post, was an odyssey. V100s are old hardware by 2025 standards: 16GB HBM2 each, no native bf16, and a track record of breaking modern inference stacks.
The problems started immediately. One card silently dropped to PCIe Gen 1 x16, and diagnosing that ate the better part of two days. Then came engine roulette. The sglang V100 build OOM'd continuously. The pxa engine recommended in another thread threw errors too. The stable run finally came from 1cat-vllm after enough tweaking that I stopped counting attempts.
The memory stats frame the whole setup: 8.78 GiB available KV cache, 531,288 tokens of KV cache, max concurrency of 2.03x at 262,144 tokens per request. Speculative decoding is capped at speculative=1 because speculative=2 OOMs. On a rig with 96GB of VRAM, the KV cache still rules the budget.
Prompt processing numbers hold up well for V100-era hardware:
The pattern is clear. Prefill scales with input length up to a peak of 4,679 tok/s at 16K tokens, then decays as the growing KV cache eats bandwidth. At 131K tokens you're paying roughly 40% below that peak. For long-context workloads, that's a real throughput tax.
Generation is where MTP (multi-token prediction) earns its keep:
| Output length (tokens) | Base (tok/s) | MTP enabled (tok/s) |
|---|---|---|
| 128 | 22.23 | 41.34 |
| 256 | 22.32 | 42.48 |
| 512 | 22.70 | 43.04 |
| 1,024 | 22.86 | 43.28 |
| 2,048 | 22.83 | 43.38 |
| Average | 22.59 | 42.70 |
MTP nearly doubles token generation, from 22.59 to 42.70 tok/s. That takes a 2,048-token generation from roughly 91 seconds down to 48. Worth the KV cache cost if you have headroom, and the OOM at speculative=2 shows the cost is real.
Then the thermal test, because the recurring multi-GPU joke is that these rigs are space heaters and jet engines. A 20-minute gpu-burn run changed the story: peak 65°C, stable at 64°C, fans at 76%. The thermal results were uneventful. The hard parts of that build were the PCIe diagnostic and engine selection, not cooling.
The FP4 backlash: quantizing everything cooks small models
The thread that ties this cluster together is a reaction to a specific pattern. A new inference engine posts impressive numbers, people get excited, and then the footnote says NVFP4/MXFP4 only.
The complaint is technically correct. A standard Q4 quantization like Q4_K_M or Q4_K_XL doesn't quantize everything. Sensitive tensors stay in BF16, Q8, or Q6/Q5, and the KV cache is typically capped at FP8 or Q8. Those mixed-width schemes encode years of llama.cpp measurements about which tensors tolerate low bits.
The engines that prompted the thread do something different. They fork llama.cpp, SGLang, or vLLM, then quantize everything, weights, activations, and KV cache, down to FP4. Skipping online dequantization makes them fast. It also wrecks output quality. On 70B-class models, quantization noise gets diluted across billions of parameters, so you lose a recipe or two and it barely matters. On small dense models, 3B or 7B, the damage is catastrophic. Arithmetic breaks. I've watched a small model at FP4 confidently state that 1+1=3 within a few hundred tokens of generation. That model isn't degraded. It's unusable.
Reading the thread, I mostly agree with the core position. There's a difference between a quantization scheme that trades a little quality for a lot of speed and an engine that benchmarks well on hardware that hides the damage. What the community is saying is consistent: Q4_K_M/XL runs are fine, blanket FP4 on small models is not. The thread's objection is narrower than an anti-quantization stance: don't benchmark a broken config and present it as an advance.
Key numbers: 13M parameters is the smallest trainable model in the repo and fits a free T4. MTP lifts generation from 22.59 to 42.70 tok/s on six V100s, close to 2x. The six-GPU rig peaked at 65°C during a 20-minute gpu-burn stress test, fans at 76%. KV cache: 8.78 GiB, 531,288 tokens.
The other precision frontier: ternary kernels in WebGPU
At the far end of the precision spectrum sits the webml-community ternary-bonsai-2-webgpu-kernels Space, a community-maintained project with 84 likes at the time of writing. Ternary weights go a step past FP4: each weight is restricted to -1, 0, or +1, which lands around 1.58 bits per weight in the BitNet b1.58 line of research.
The WebGPU part is what makes this interesting engineering. Browsers are the least forgiving inference target. No vendor CUDA kernels, no tensor-core libraries, memory bandwidth is limited, and shader compilation is part of the runtime cost. A ternary kernel in WebGPU means someone hand-wrote the matmul paths, the dequantization, and the memory layout for a GPU API that's still maturing. It's a research demo with hand-written kernels behind it.
The honest take: if FP4 breaks arithmetic on small dense models, ternary is further out on the same risk curve. The research case for ternary is that activations stay in higher precision while weights go extreme, and you pay for it with wider models and more training compute. The quality equation isn't settled. Don't ship it to production yet. But the kernel work matters, because the browser is where inference distribution is heading.
Common pitfalls
- Quantize everything to FP4, including activations and KV cache, with no escape hatch. Small dense models lose arithmetic entirely. Copy the Q4_K_M approach instead: sensitive tensors in FP8 or BF16, KV cache capped at FP8.
- Max out speculative decoding or MTP without checking KV headroom. The V100 setup OOM'd at
speculative=2. Start atspeculative=1, watch the cache stats, then raise it. - Run SFT without a loss mask over prompt tokens. The model learns to parrot prompts. Mask everything except assistant tokens and the end-of-turn token. In the repo's packed rows that's often fewer than 50 of 512 tokens.
- Assume PCIe links are healthy on a multi-GPU rig. One card silently dropped to Gen 1 x16 and cost two days of debugging. Run
nvidia-smi -q -d PCIEwhen you build or change a rig. - Try to train a 2B model on a 16GB card without memory flags. Use
--amp,--grad-checkpointing, and--grad-accumbefore you reach for a bigger GPU.
One thing to remember
None of these projects stands on a single clever trick. The train-from-scratch repo works because of loss masks and reward contracts. The V100 rig works because someone checked PCIe links and picked an engine that fit the KV cache budget. The FP4 backlash exists because a class of engines discarded all of that measured knowledge for a benchmark number. Precision management is the job at every scale, and the scale just changes the price of getting it wrong.
Where the precision spectrum lands
- If you're training a small model or teaching someone the full pipeline, start from the train-from-scratch repo on a single T4 or 4090. It covers raw text to GRPO-aligned reasoning in pure PyTorch, and 13M parameters keeps iteration time in minutes.
- If you're serving on a multi-GPU rig, replicate the TP2 PP3 setup on an engine that fits your cards, enable MTP for roughly 2x generation throughput, and keep speculative decoding at 1 until your KV cache has clear headroom.
- If you're building an inference engine, skip blanket FP4 for small dense models and follow the mixed-width playbook. Watch the ternary WebGPU kernels as a research signal; expect browser-based inference tooling to mature within 6 to 12 months.