Repository navigation
fix(export): keep checkpoint precision in SGLang exports and config dtype - #890
Conversation
…type The SGLang exporter serialized the state of a draft materialized in BF16, so a checkpoint trained in float32 or float16 was rounded on export. Both exporters also stamped `dtype: bfloat16` into the exported config.json regardless of the checkpoint, so a Transformers reload without an explicit dtype cast the HF exporter's preserved weights back to BF16. Materialize the draft in the checkpoint's floating-point dtype, falling back to BF16 for a checkpoint that mixes dtypes, and serialize the checkpoint tensors in the SGLang exporter as the HF exporter already does, keeping an explicit vocabulary mapping override. BF16 checkpoints export byte-for-byte as before. Signed-off-by: Chuyue Wang <stevenwang0805@outlook.com>
|
The That test imports nothing from |
|
@maocheng23, could you review this exporter follow-up to #872? It keeps the SGLang export at the checkpoint's precision and makes the exported On head |
|
@FrankLeeeee, ready for review; CODEOWNERS assigned you via Head The job's one error, |
|
@StevenWang-CY Hi, I have reviewed it and add some new tests. I think the loading logic will need to be updated to accommodate mixed-precision draft models later. For instance, the "dspark" variant of DeepSeek-V4-Flash-0731. |
Motivation
specforge export --to sglangserializes the state of a draft thatmaterialize_draftbuilds in BF16, so a checkpoint trained withmodel.torch_dtype: float32orfloat16is rounded on export.--to hfhas preserved the checkpoint tensors since #872, but both exporters still stampdtype: bfloat16into the exportedconfig.json, and Transformers 5.12.1 resolves the reload dtype from that config:AutoDraftModel.from_pretrained(export_dir)without an explicit dtype casts the preserved FP32 weights back to BF16. The export test module's docstring promises bit-identical round trips for both formats, and the existing CUDA gate only checks that with a BF16-trained draft, where the cast is a no-op.Modifications
materialize_draftbuilds the draft in the checkpoint's floating-point dtype (checkpoint_dtype, the single dtype shared by the draft weights), so the module's parameters and the dtypesave_pretrainedstamps into the config both carry the trained precision. A checkpoint that mixes dtypes keeps the BF16 fallback.export_to_sglangserializes the checkpoint tensors as stored, exactly asexport_to_hfdoes, while keeping thet2d/d2tbuffers loaded from an explicit--vocab-mappingoverride._serving_statevalidation and weight-map renames are unchanged.config.jsonandmodel.safetensorshashes on unmodified main and on this head for both formats.docs/sections/basic_usage/training.mdstates the precision contract.Serving is unaffected: SGLang casts draft weights to the server dtype at load time. FP32 checkpoints produce larger export files than before, at the precision they were trained in.
Related Issues
Follow-up to #872. No linked issue; the live audit of open PRs and issues found no competing precision fix. #811 (draft) and #882 touch neighbouring lines in these files for MoE and expert-parallel layouts, not precision.
Accuracy Test
Validation on head
6f8629588a1aaf8c27811ab9b14957a9b9cdf190against unmodified mained64d275bac8a48e126adb2827368603566a3029, CPU only (macOS, torch 2.13.0, transformers 5.12.1; no CUDA, sglang, or triton available here):TestExportCheckpointPrecision(three CPU tests, FP32/FP16/BF16 subtests) fails on unmodified main for the intended reasons:torch.bfloat16 != torch.float32/float16on every serialized SGLang weight, and config dtype'bfloat16' != 'float32'on the HF export; the BF16 subtest passes on main as the control. All pass on this head.python -m unittest tests.test_runtime.test_export: 18 tests, 4 CUDA-only skips, all passing.python -m unittest discover -s ./tests -p "test_*.py"in a fresh clean worktree of this exact commit: 1158 tests; the 17 failures and 20 errors are identical to the unmodified-main baseline in this environment and are all environmental (no CUDA, nosglang, notriton).pre-commit run --all-filespasses.export_to_sglangandexport_to_hfon main and on this head yields identical SHA-256 forconfig.jsonandmodel.safetensors.The hosted PR Test job on this head has since run the CUDA-gated export round-trips that CPU runs skip —
test_to_hf_reloads_bit_identical,test_to_sglang_produces_exact_serving_keys,test_weight_map_renames_are_applied— alongside the three new precision tests, and all of them passed; lint passed too.That job's only error is unrelated to this change:
test_dp_reduction_and_ragged_shardsfailed indist.init_process_groupwithEADDRINUSEon port 46497, the rendezvous port its own_free_port()helper binds, closes, and then hands tomp.spawn. It ran before any test in this PR's module, it passes locally with this patch, and the same runner passed it on later runs of other branches. A rerun clears it.Serving is unaffected by the file's precision: SGLang v0.5.18's
default_weight_loadercopies each tensor into the server-dtype parameter (param.data.copy_(loaded_weight)), and its EAGLE3 draft loader readsd2t/t2das integer buffers. As the export docs already state, loading a draft in a real speculative-decoding server and measuring acceptance remains a GPU serving validation step.Benchmark & Profiling
Not applicable; export-time code path only.
Checklist