Three torch-neuronx 2.14 device behaviours, each worked around in a bit-identical way: - torch.cat of float8 tensors is wrong on the device: merge_linear_weights (fused q_a/kv, q_b, ... projections) now concatenates the raw bytes, like the HF loader's read_concat. - int64 constants are narrowed to int32 by the compiler (--implicit-integer-downcast): deep_select.topk used int64.max as the sort sentinel, which became -1 and pushed the selected indices out of rows with end < topk -- the first compressed layer then attended to no compressed KV for short prompts. The sentinel is now vocab_size. - advanced indexing of non-contiguous float8 views is wrong: the remaining site (deep_gemm sparse MQA logits) goes through uint8 views; deep_ep's byte path also covers e8m0. DEEPINFER_NEURON_FP8_GUARD=1|raise (neuron_ops/fp8_guard.py) reports every cat/stack/advanced-index/gather/scatter/where on a float8 tensor with its call site; the tiny end-to-end run and all CPU unit tests report zero sites. Tiny checkpoint on one NeuronCore, teacher-forced vs the official reference: short 9/9 (min cos 0.9985), medium 8/9 (0.9991), long 8/9 (0.9978); logits rel <= 0.066, within the reference's own noise floor -- same as CPU mode. Also: probe_layer0 PROBE_LAYERS=N and attention-input capture, device_diff copies tensor attributes (replays deep_gemm sparse MQA metadata), device primitive checks for the narrowing and strided fp8 casts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| golden | ||
| check_tiny_checkpoint.py | ||
| compare_golden.py | ||
| device_diff.py | ||
| make_tiny_checkpoint.py | ||
| probe_layer0.py | ||
| README.md | ||
| ref_kernels_torch.py | ||
| reference_golden.py | ||
| run_deepinfer_tiny.py | ||
| test_ref_kernels_torch.py | ||
DeepInfer on AWS Trainium — end-to-end check with a tiny checkpoint
Design and conventions of the port: deepinfer/neuron_ops/NEURON_PORT.md.
The ground truth is DeepSeek's official reference implementation (inference/ in the
deepseek-ai/DeepSeek-V4.1-Flash-1017 HF repo) running on CPU, with its TileLang kernels replaced by
the pure-torch versions in ref_kernels_torch.py.
| Script | Purpose |
|---|---|
make_tiny_checkpoint.py |
Random tiny V4.1 checkpoint in the real HF layout (FP8 e4m3 + ue8m0 32x32 scales, FP4 e2m1 experts with 1x32 scales, engram tables) |
check_tiny_checkpoint.py |
DeepInfer loader coverage / shape / dtype checks on the meta device |
ref_kernels_torch.py, test_ref_kernels_torch.py |
Torch replacements for the reference kernel.py |
reference_golden.py |
Reference prefill + greedy decode; saves logits, tokens and per-layer hidden states |
run_deepinfer_tiny.py |
Same prompts through the DeepInfer engine (LLM offline API), teacher-forced |
compare_golden.py |
Candidate vs golden: argmax match, logits rel / cos / KL, per-layer hidden error |
Tiny config: 10 layers + 3 nextn, hidden 1024, 8 heads x 512 (MLA, rope 64), 16 experts / top-4, indexer 4 x 128 with top-64, engram on layers 1 and 4, real tokenizer (vocab 129280).
export DEEPINFER_ACCELERATOR=neuron DEEPINFER_NEURON_DEVICE=cpu # or =neuron for the NeuronCore
python examples/neuron/run_deepinfer_tiny.py --threads 32 --hidden --compare
Results so far (CPU mode, basic mode: eager, no DSpark/MTP, EP=DP=CP=1)
Teacher-forced, prefill + 8 decode steps per prompt; "floor" is the reference compared with itself under a different CPU thread count (FP8 activation quantization amplifies sub-ulp differences).
| prompt (tokens) | argmax match | max logits rel (floor) | min cos (floor) |
|---|---|---|---|
| short (11) | 9/9 | 0.054 (0.032) | 0.9985 (0.9995) |
| medium (172, wraps the 128 SWA window) | 8/9 * | 0.041 (0.039) | 0.9992 (0.9993) |
| long (715, candidate-block filter active) | 9/9 | 0.059 (0.060) | 0.9983 (0.9982) |
* medium step 6 has a top-1/top-2 margin of 0.0096; the reference flips it against itself too.
probe_layer0.py compares ~35 named ops per layer between the reference and DeepInfer. Layer 0 is
bit-exact once three rounding points are aligned:
- attention P rounded to bf16 before P@V (online softmax, 64-key blocks) — the CUDA FlashMLA kernels and the reference do this; the Neuron flash_mla shim now does too (was fp32, a port bug);
- SwiGLU output quantized to FP8 straight from fp32 (the reference rounds to bf16 first) and
- the MoE combine rounding the routed sum to bf16 before adding the shared expert (the reference adds in fp32) — both are DeepInfer's own GPU/Ascend behaviour, kept as is.
The remaining logits difference comes from those two, amplified by FP8 activation quantization layer by
layer. Per-step / per-layer numbers before the attention fix: golden/deepinfer_cpu_vs_golden.json.