yujiepan commited on
Commit
88c2000
·
verified ·
1 Parent(s): 544d74f

Upload folder using huggingface_hub

Browse files
Files changed (6) hide show
  1. .meta.json +11 -0
  2. README.md +590 -0
  3. config.json +128 -0
  4. model.safetensors +3 -0
  5. tokenizer.json +0 -0
  6. tokenizer_config.json +14 -0
.meta.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Pillow": "12.3.0",
3
+ "flashinfer-python": "0.6.15.post1",
4
+ "huggingface_hub": "1.27.0",
5
+ "numpy": "2.5.2",
6
+ "safetensors": "0.8.0",
7
+ "sympy": "1.14.0",
8
+ "tilelang": "0.1.12",
9
+ "torch": "2.13.0+cu126",
10
+ "transformers": "5.15.0.dev0"
11
+ }
README.md ADDED
@@ -0,0 +1,590 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ library_name: transformers
3
+ base_model:
4
+ - deepseek-ai/DeepSeek-V4.1-Flash-1017
5
+ ---
6
+
7
+ This tiny model is intended for debugging. It is randomly initialized using the configuration adapted from [deepseek-ai/DeepSeek-V4.1-Flash-1017](https://huggingface.co/deepseek-ai/DeepSeek-V4.1-Flash-1017).
8
+
9
+ Note:
10
+ - Structural comparison with the original `deepseek-ai/DeepSeek-V4.1-Flash-1017`:
11
+
12
+ | Structural feature | Original V4.1 Flash 1017 | This tiny model |
13
+ |---|---|---|
14
+ | Modalities | Vision + text | Same |
15
+ | Vocabulary / special-token IDs | 129,280; original BOS/EOS/PAD/image IDs | Same |
16
+ | Text backbone layers | 40 | 6 |
17
+ | Hidden size | 5,120 | 128 |
18
+ | Attention heads / head dim | 64 / 512 | 4 / 512 (SGLang DSV4 ABI; `head_dim >= 32`) |
19
+ | RoPE head dim | 64 | Same |
20
+ | Q/O low-rank projections | q=1,280; o=1,024; 8 output groups | q=128; o=128; 2 groups |
21
+ | Routed/shared experts | 384 / 1, top-6 | 32 / 1, top-6 |
22
+ | MoE intermediate size | 2,304 | 256 (128 per TP rank) |
23
+ | Sparse attention ratios | Backbone: 2 sliding-only, 18 ratio-2, 20 ratio-1; MTP: 3 sliding-only | Backbone: 2 sliding-only, 2 ratio-2, 2 ratio-1; MTP: 3 sliding-only |
24
+ | KV / index source layers | KV: 2,8,14,20; index: 2,8,14,20,24,28,32,36 | KV/index: 1,3; index head dim 128 |
25
+ | Candidate source layer | 20 | 3 |
26
+ | Hyper-Connections | 4 residual streams | Same |
27
+ | Engram | layers 1,14; 8 heads x 256; ~384M rows/table | layers 1,2; 4 heads x 64; 6,748 / 7,592 rows |
28
+ | DSpark/MTP | 3 stages; targets 37,38,39; Markov rank 256; 128 experts, top-3 | 3 stages; targets 3,4,5; rank 16; 8 experts, top-3 |
29
+ | Vision tower | 32 layers; width 1,024; 16 heads | 2 layers; width 64; 4 heads |
30
+ | Vision patch/downsample | patch 14; downsample 3 | Same |
31
+ | Quantization | Routed experts FP4; most other linears FP8; UE8M0 scales; 32x32 blocks | Same |
32
+ | Public checkpoint | 96,085 tensors; ~510.3 GB | 1,629 tensors; 96.7 MB |
33
+
34
+ - Preserved topology includes compressed sparse attention and its indexer, four-way
35
+ Hyper-Connections, n-gram Engram lookup, three-stage DSpark/MTP, the vision aligner,
36
+ FP4 expert packing, and FP8 block scales. Widths, layer cadence, expert counts,
37
+ Engram tables, and vision depth are reduced for a practical debugging checkpoint.
38
+
39
+ - The structure is derived by instantiating the source repository's pinned inference
40
+ implementation on the meta device, then converting its runtime state back to the
41
+ public Hugging Face checkpoint representation.
42
+
43
+ - The SGLang example is intentionally a conservative TP=2 launch. The
44
+ attention latent stays 512-wide and the indexer stays 128-wide because those are
45
+ fixed by SGLang's DSV4 cache/kernel ABI; all TP-sharded widths, head/group counts,
46
+ expert counts, vocabulary rows, and 32-wide quantization blocks divide cleanly by 2.
47
+ The online example enables the embedded three-stage DSpark/MTP draft explicitly.
48
+
49
+ - Validated on two NVIDIA H20 GPUs with Python 3.12.13, CUDA Toolkit 13.0,
50
+ torch 2.14.1, SGLang `0.5.23.dev384+g9ab74b851`, FlashInfer 0.7.2,
51
+ Transformers 5.19.0, TileLang 0.1.12, and safetensors 0.8.0. Separate offline
52
+ `sglang.Engine` runs using the same TP=2/backend settings reached engine-ready
53
+ with both base decoding and `speculative_algorithm="DSPARK"`; CUDA Graph remained
54
+ enabled, and the latter reported DSpark enabled, generated all three cases
55
+ (including `Hello` with exactly 10 new tokens), and shut down with exit code 0.
56
+ The tested checkpoint contains 1,629
57
+ tensors, is 96,712,592 bytes, and has SHA-256
58
+ `53aae999fdcacd310c400a2fc3112bb68ce35588441784c75dca5820abf0f35b`. The README
59
+ keeps the equivalent online `sglang serve` form for normal deployment. The
60
+ official source `inference/convert.py` also produced two shards which loaded
61
+ strictly into the source runtime for both ranks.
62
+
63
+ | File path | Size |
64
+ |------|------|
65
+ | model.safetensors | 96.7MB |
66
+
67
+
68
+ ### Example usage:
69
+
70
+ - SGLang
71
+
72
+ ```bash
73
+ # Requires an SGLang preview/main build with DeepSeek-V4.1 support.
74
+ # Runs the target model and its embedded three-stage DSpark/MTP draft with TP=2.
75
+ model_id=tiny-random/deepseek-v4.1-flash-1017
76
+ sglang serve \
77
+ --model-path $model_id \
78
+ --trust-remote-code \
79
+ --tp-size 2 \
80
+ --ep-size 1 \
81
+ --attention-backend dsv4 \
82
+ --moe-runner-backend flashinfer_mxfp4 \
83
+ --speculative-algorithm DSPARK \
84
+ --speculative-dspark-block-size 5 \
85
+ --context-length 4096 \
86
+ --max-total-tokens 4096 \
87
+ --chunked-prefill-size 512 \
88
+ --mem-fraction-static 0.5 \
89
+ --disable-custom-all-reduce
90
+ ```
91
+
92
+ ### Codes to create this repo:
93
+
94
+ <details><summary>Click to expand</summary>
95
+
96
+ ```python
97
+ import json
98
+ import hashlib
99
+ import sys
100
+ from pathlib import Path
101
+ from typing import Any, TypedDict
102
+
103
+ import torch
104
+ from huggingface_hub import hf_hub_download
105
+ from safetensors.torch import save_file
106
+ from transformers import AutoTokenizer
107
+
108
+ source_model_id = "deepseek-ai/DeepSeek-V4.1-Flash-1017"
109
+ save_folder = "/tmp/tiny-random/deepseek-v41-flash-1017"
110
+ source_revision = "1bae0e1088993b9b82de08f59de5be5a34e42170"
111
+ # DeepSeek-V4.1-Flash-1017 is a multimodal (vision + text) MoE model. The released checkpoint uses the
112
+ # inference-stack tensor naming (see the repo's inference/model.py), not HF-transformers naming, so
113
+ # this generator mirrors that layout. The config is nested: text_config + vision_config, with a
114
+ # shared quantization_config (fp8 weights, fp4 routed experts, ue8m0 scales, 32x32 blocks).
115
+ config = {
116
+ "architectures": [
117
+ "DeepseekV41ForCausalLM"
118
+ ],
119
+ "model_type": "deepseek_v41",
120
+ "dtype": "bfloat16",
121
+ "transformers_version": "5.6.0",
122
+ "bos_token_id": 0,
123
+ "eos_token_id": 1,
124
+ "pad_token_id": 2,
125
+ "image_token_id": 129264,
126
+ "quantization_config": {
127
+ "quant_method": "fp8",
128
+ "activation_scheme": "dynamic",
129
+ "weight_block_size": [
130
+ 32,
131
+ 32
132
+ ],
133
+ "scale_fmt": "ue8m0",
134
+ "expert_dtype": "fp4"
135
+ },
136
+ "text_config": {
137
+ "model_type": "deepseek_v41_text",
138
+ "vocab_size": 129280,
139
+ "hidden_size": 128,
140
+ # TP=2 leaves 128 columns per rank, the minimum accepted by the
141
+ # FlashInfer MXFP4 MoE kernel.
142
+ "moe_intermediate_size": 256,
143
+ "num_hidden_layers": 6,
144
+ "num_attention_heads": 4,
145
+ "num_key_value_heads": 1,
146
+ # SGLang's DSV4 cache/backend ABI fixes this at 448 non-RoPE + 64 RoPE.
147
+ "head_dim": 512,
148
+ "qk_rope_head_dim": 64,
149
+ "q_lora_rank": 128,
150
+ "o_lora_rank": 128,
151
+ "o_groups": 2,
152
+ "hidden_act": "silu",
153
+ "swiglu_limit": 10.0,
154
+ "rms_norm_eps": 1e-20,
155
+ "attention_bias": False,
156
+ "attention_dropout": 0.0,
157
+ "initializer_range": 0.02,
158
+ "use_cache": True,
159
+ "tie_word_embeddings": False,
160
+ "max_position_embeddings": 1048576,
161
+ "rope_theta": 10000,
162
+ "rope_scaling": {
163
+ "rope_type": "yarn",
164
+ "factor": 16,
165
+ "beta_fast": 32,
166
+ "beta_slow": 1,
167
+ "original_max_position_embeddings": 65536
168
+ },
169
+ "n_routed_experts": 32,
170
+ "n_shared_experts": 1,
171
+ "num_experts_per_tok": 6,
172
+ "scoring_func": "sqrtsoftplus",
173
+ "topk_method": "noaux_tc",
174
+ "norm_topk_prob": True,
175
+ "routed_scaling_factor": 1.5,
176
+ "sliding_window": 128,
177
+ # one entry per layer (backbone + MTP): 0 = sliding-window only, r = KV compressed r-to-1
178
+ "compress_ratios": [0, 2, 2, 1, 1, 0, 0, 0, 0],
179
+ "compress_rope_theta": 160000,
180
+ # layers that produce (own) the shared compressed KV / index keys
181
+ "kv_source_layer_ids": [1, 3],
182
+ "index_source_layer_ids": [1, 3],
183
+ "index_n_heads": 4,
184
+ # The SGLang FP4 indexer kernels use a fixed 128-wide key/query layout.
185
+ "index_head_dim": 128,
186
+ "index_topk": 512,
187
+ "candidate_source_layer_id": 3,
188
+ "candidate_topk_blocks": 2048,
189
+ "candidate_block_size": 8,
190
+ "hc_mult": 4,
191
+ "hc_sinkhorn_iters": 20,
192
+ "hc_eps": 1e-06,
193
+ # n-gram engram hash tables added into the residual at a few layers
194
+ # The final KV source layer must remain Engram-free in SGLang; the
195
+ # source model likewise carries Engram on an earlier KV source only.
196
+ "engram_layer_ids": [1, 2],
197
+ # row counts of the two hash tables; derived from engram_vocab_size via the prime layout
198
+ # in the repo's engram.py (find_next_prime chain), computed for the tiny vocab below
199
+ "engram_num_embeddings": [6748, 7592],
200
+ "engram_max_ngram_size": 4,
201
+ "engram_vocab_size": 512,
202
+ "engram_n_heads": 4,
203
+ "engram_head_dim": 64,
204
+ "engram_pad_token_id": 2,
205
+ "engram_compressed_vocab_size": 99092,
206
+ "num_nextn_predict_layers": 3,
207
+ "dspark_block_size": 5,
208
+ "dspark_noise_token_id": 128799,
209
+ "dspark_target_layer_ids": [3, 4, 5],
210
+ # Keep the source low-rank Markov head topology while trimming the two
211
+ # vocabulary-sized BF16 matrices. SGLang only requires a positive rank.
212
+ "dspark_markov_rank": 16,
213
+ "dspark_n_routed_experts": 8,
214
+ "dspark_num_experts_per_tok": 3
215
+ },
216
+ "vision_config": {
217
+ "model_type": "deepseek_v41_vision",
218
+ "num_hidden_layers": 2,
219
+ "hidden_size": 64,
220
+ "num_attention_heads": 4,
221
+ "intermediate_size": 128,
222
+ "patch_size": 14,
223
+ "rope_theta": 10000,
224
+ "downsample_ratio": 3,
225
+ "max_image_tokens": 1024,
226
+ "min_pixels": 295936,
227
+ "max_wh_ratio": None
228
+ }
229
+ }
230
+
231
+ def main():
232
+ torch.manual_seed(42)
233
+ Path(save_folder).mkdir(parents=True, exist_ok=True)
234
+ tokenizer = AutoTokenizer.from_pretrained(
235
+ source_model_id,
236
+ revision=source_revision,
237
+ trust_remote_code=True,
238
+ )
239
+ state_dict = generate(config, tokenizer)
240
+ save_file(state_dict, Path(save_folder) / "model.safetensors")
241
+ with open(Path(save_folder) / "model.safetensors", "rb") as f:
242
+ state_dict = f.read()
243
+ print("Hash: ", hashlib.sha256(state_dict).hexdigest())
244
+
245
+ with open(Path(save_folder) / "config.json", "w", encoding="utf-8") as f:
246
+ json.dump(config, f, indent=2, ensure_ascii=False)
247
+
248
+ tokenizer.save_pretrained(save_folder)
249
+
250
+ try:
251
+ from transformers import GenerationConfig
252
+ generation_config = GenerationConfig.from_pretrained(
253
+ source_model_id,
254
+ revision=source_revision,
255
+ trust_remote_code=True,
256
+ )
257
+ generation_config.save_pretrained(save_folder)
258
+ except Exception as exc: # noqa: BLE001
259
+ print(f"Skipping generation config: {exc}")
260
+
261
+ BF16 = "torch.bfloat16"
262
+ F32 = "torch.float32"
263
+ FP8 = "torch.float8_e4m3fn"
264
+ SCALE = "torch.float8_e8m0fnu"
265
+ I8 = "torch.int8"
266
+
267
+ class TensorSpec(TypedDict):
268
+ shape: list[int]
269
+ dtype: str
270
+
271
+ Config = dict[str, Any]
272
+ State = dict[str, TensorSpec]
273
+ TensorDict = dict[str, torch.Tensor]
274
+
275
+ def initialize(state: State, config: Config, init_bound: float) -> TensorDict:
276
+ tensors: TensorDict = {}
277
+ scale_dtype = torch.float8_e8m0fnu
278
+ fp4_boundaries = torch.tensor([0.25, 0.75, 1.25, 1.75, 2.5, 3.5, 5.0])
279
+
280
+ def scale_for(
281
+ amax: torch.Tensor, qmax: float, dtype: torch.dtype
282
+ ) -> torch.Tensor:
283
+ scale = amax.float().clamp_min(torch.finfo(torch.float32).tiny) / qmax
284
+ if dtype == scale_dtype:
285
+ scale = torch.pow(2.0, torch.round(torch.log2(scale)))
286
+ return scale.to(dtype)
287
+
288
+ def fp8_tensor(
289
+ shape: list[int], scale_shape: list[int], dtype: torch.dtype
290
+ ) -> tuple[torch.Tensor, torch.Tensor]:
291
+ # block sizes are inferred from the scale shape, so this also handles the engram embedding
292
+ # table, which is quantized per-row over 32-column blocks (block_out == 1)
293
+ assert shape[0] % scale_shape[0] == 0 and shape[1] % scale_shape[1] == 0
294
+ block_out = shape[0] // scale_shape[0]
295
+ block_in = shape[1] // scale_shape[1]
296
+ value = torch.empty(shape, dtype=torch.bfloat16).uniform_(
297
+ -init_bound, init_bound
298
+ )
299
+ blocks = value.view(
300
+ scale_shape[0], block_out, scale_shape[1], block_in
301
+ ).transpose(1, 2)
302
+ scales = scale_for(blocks.abs().amax(dim=(-1, -2)), 448, dtype)
303
+ weight = (
304
+ (blocks.float() / scales.float()[..., None, None])
305
+ .clamp(-448, 448)
306
+ .to(torch.float8_e4m3fn)
307
+ .transpose(1, 2)
308
+ .reshape(shape)
309
+ .contiguous()
310
+ )
311
+ return weight, scales
312
+
313
+ def fp4_tensors(
314
+ count: int,
315
+ shape: list[int],
316
+ scale_shape: list[int],
317
+ dtype: torch.dtype,
318
+ ) -> tuple[torch.Tensor, torch.Tensor]:
319
+ out_dim, packed_in_dim = shape
320
+ block_out, block_in = config["quantization_config"]["weight_block_size"]
321
+ assert out_dim % block_out == 0
322
+ assert packed_in_dim * 2 % block_in == 0
323
+ assert packed_in_dim == scale_shape[1] * 16
324
+ value = torch.empty(
325
+ count, out_dim, packed_in_dim * 2, dtype=torch.bfloat16
326
+ ).uniform_(-init_bound, init_bound)
327
+ blocks = value.view(count, out_dim, scale_shape[1], 32)
328
+ scales = scale_for(blocks.abs().amax(dim=-1), 6, dtype)
329
+ normalized = (blocks.float() / scales.float()[..., None]).clamp(-6, 6)
330
+ code = torch.bucketize(normalized.abs(), fp4_boundaries)
331
+ code += normalized.signbit() * 8
332
+ code = code.view(count, out_dim, packed_in_dim * 2)
333
+ packed = (code[..., ::2] | (code[..., 1::2] << 4)).to(torch.uint8)
334
+ weight = packed.view(torch.int8).contiguous()
335
+ return weight, scales
336
+
337
+ dtype_map: dict[str, torch.dtype] = {
338
+ BF16: torch.bfloat16,
339
+ F32: torch.float32,
340
+ FP8: torch.float8_e4m3fn,
341
+ SCALE: scale_dtype,
342
+ I8: torch.int8,
343
+ }
344
+ fp4_groups: dict[
345
+ tuple[tuple[int, ...], tuple[int, ...], torch.dtype], list[str]
346
+ ] = {}
347
+ for name, spec in state.items():
348
+ if spec["dtype"] != I8:
349
+ continue
350
+ scale_spec = state[name.replace(".weight", ".scale")]
351
+ key = (
352
+ tuple(spec["shape"]),
353
+ tuple(scale_spec["shape"]),
354
+ dtype_map[scale_spec["dtype"]],
355
+ )
356
+ fp4_groups.setdefault(key, []).append(name)
357
+
358
+ fp4_cache: dict[str, tuple[torch.Tensor, torch.Tensor]] = {}
359
+ max_batch_elements = 4 * 1024 * 1024
360
+ for (shape_tuple, scale_shape_tuple, dtype), names in fp4_groups.items():
361
+ shape = list(shape_tuple)
362
+ scale_shape = list(scale_shape_tuple)
363
+ logical_elements = shape[0] * shape[1] * 2
364
+ batch_size = max(1, max_batch_elements // logical_elements)
365
+ for start in range(0, len(names), batch_size):
366
+ batch_names = names[start: start + batch_size]
367
+ weights, scales = fp4_tensors(
368
+ len(batch_names), shape, scale_shape, dtype
369
+ )
370
+ for index, name in enumerate(batch_names):
371
+ fp4_cache[name] = (
372
+ weights[index].clone(),
373
+ scales[index].clone(),
374
+ )
375
+
376
+ for name, spec in state.items():
377
+ if name in tensors:
378
+ continue
379
+ shape, dtype = spec["shape"], dtype_map[spec["dtype"]]
380
+ scale_name = name.replace(".weight", ".scale")
381
+ if spec["dtype"] == FP8:
382
+ scale_spec = state[scale_name]
383
+ scale_type = dtype_map[scale_spec["dtype"]]
384
+ tensors[name], tensors[scale_name] = fp8_tensor(
385
+ shape, scale_spec["shape"], scale_type
386
+ )
387
+ elif spec["dtype"] == I8:
388
+ tensors[name], tensors[scale_name] = fp4_cache.pop(name)
389
+ elif not name.endswith(".scale"):
390
+ tensors[name] = torch.empty(shape, dtype=dtype).uniform_(
391
+ -init_bound, init_bound
392
+ )
393
+ print(f"{name}: {shape} {dtype}", flush=True)
394
+ file_size_by_name = {name: tensor.numel() * tensor.element_size() for name, tensor in tensors.items()}
395
+ total_file_size = sum(file_size_by_name.values())
396
+ k = 20
397
+ topk = sorted(file_size_by_name.items(), key=lambda x: x[1], reverse=True)[:k]
398
+ print(f"File size: {total_file_size / 1024 / 1024:.2f} MB")
399
+ print(f"Top {k} largest tensors:")
400
+ for name, size in topk:
401
+ print(f" {name}: {size / 1024 / 1024:.2f} MB")
402
+ return tensors
403
+
404
+ def generate(config: Config, tokenizer: Any, init_bound: float = 0.2) -> TensorDict:
405
+ assert init_bound > 0
406
+ text = config["text_config"]
407
+ vision = config["vision_config"]
408
+ quant = config["quantization_config"]
409
+ rope = text["rope_scaling"]
410
+
411
+ # Transformers has no deepseek_v41 registration or auto_map. The source repository instead
412
+ # ships a complete readable runtime under inference/. Instantiate that implementation on the
413
+ # meta device so the official modules, rather than a handwritten tensor list, define the tiny
414
+ # checkpoint structure. Pin the inspected source revision for reproducibility.
415
+ runtime_files = (
416
+ "model.py",
417
+ "engram.py",
418
+ "vision.py",
419
+ "kernel.py",
420
+ "image_processor.py",
421
+ )
422
+ runtime_dir = None
423
+ for filename in runtime_files:
424
+ file_path = hf_hub_download(
425
+ source_model_id,
426
+ filename=f"inference/{filename}",
427
+ revision=source_revision,
428
+ )
429
+ runtime_dir = str(Path(file_path).parent)
430
+ assert runtime_dir is not None
431
+ sys.path.insert(0, runtime_dir)
432
+ try:
433
+ from model import ModelArgs, Transformer
434
+ finally:
435
+ sys.path.remove(runtime_dir)
436
+
437
+ args = ModelArgs(
438
+ max_batch_size=1,
439
+ max_seq_len=min(text["max_position_embeddings"], 128),
440
+ dtype="fp8" if quant["quant_method"] == "fp8" else "bf16",
441
+ expert_dtype=quant.get("expert_dtype"),
442
+ vocab_size=text["vocab_size"],
443
+ dim=text["hidden_size"],
444
+ moe_inter_dim=text["moe_intermediate_size"],
445
+ n_layers=text["num_hidden_layers"],
446
+ n_mtp_layers=text["num_nextn_predict_layers"],
447
+ n_heads=text["num_attention_heads"],
448
+ n_routed_experts=text["n_routed_experts"],
449
+ n_shared_experts=text["n_shared_experts"],
450
+ n_activated_experts=text["num_experts_per_tok"],
451
+ score_func=text["scoring_func"],
452
+ norm_topk_prob=text["norm_topk_prob"],
453
+ route_scale=text["routed_scaling_factor"],
454
+ swiglu_limit=text["swiglu_limit"],
455
+ q_lora_rank=text["q_lora_rank"],
456
+ head_dim=text["head_dim"],
457
+ rope_head_dim=text["qk_rope_head_dim"],
458
+ norm_eps=text["rms_norm_eps"],
459
+ o_groups=text["o_groups"],
460
+ o_lora_rank=text["o_lora_rank"],
461
+ window_size=text["sliding_window"],
462
+ compress_ratios=tuple(text["compress_ratios"]),
463
+ kv_source_layers=tuple(text["kv_source_layer_ids"]),
464
+ index_source_layers=tuple(text["index_source_layer_ids"]),
465
+ compress_rope_theta=text["compress_rope_theta"],
466
+ original_seq_len=rope["original_max_position_embeddings"],
467
+ rope_theta=text["rope_theta"],
468
+ rope_factor=rope["factor"],
469
+ beta_fast=rope["beta_fast"],
470
+ beta_slow=rope["beta_slow"],
471
+ index_n_heads=text["index_n_heads"],
472
+ index_head_dim=text["index_head_dim"],
473
+ index_topk=text["index_topk"],
474
+ candidate_source_layer=text["candidate_source_layer_id"],
475
+ candidate_topk_blocks=text["candidate_topk_blocks"],
476
+ candidate_block_size=text["candidate_block_size"],
477
+ hc_mult=text["hc_mult"],
478
+ hc_sinkhorn_iters=text["hc_sinkhorn_iters"],
479
+ hc_eps=text["hc_eps"],
480
+ engram_layer_ids=tuple(text["engram_layer_ids"]),
481
+ engram_num_embeddings=tuple(text["engram_num_embeddings"]),
482
+ engram_max_ngram_size=text["engram_max_ngram_size"],
483
+ engram_vocab_size=text["engram_vocab_size"],
484
+ engram_n_heads=text["engram_n_heads"],
485
+ engram_head_dim=text["engram_head_dim"],
486
+ engram_pad_id=text["engram_pad_token_id"],
487
+ engram_compressed_vocab_size=text["engram_compressed_vocab_size"],
488
+ vision_n_layers=vision["num_hidden_layers"],
489
+ vision_dim=vision["hidden_size"],
490
+ vision_n_heads=vision["num_attention_heads"],
491
+ vision_inter_dim=vision["intermediate_size"],
492
+ vision_patch_size=vision["patch_size"],
493
+ vision_rope_theta=vision["rope_theta"],
494
+ vision_downsample_ratio=vision["downsample_ratio"],
495
+ vision_max_n_token=vision["max_image_tokens"],
496
+ vision_min_pixels=vision["min_pixels"],
497
+ vision_max_wh_ratio=vision["max_wh_ratio"],
498
+ image_token_id=config["image_token_id"],
499
+ dspark_block_size=text["dspark_block_size"],
500
+ dspark_noise_token_id=text["dspark_noise_token_id"],
501
+ dspark_target_layer_ids=tuple(text["dspark_target_layer_ids"]),
502
+ dspark_markov_rank=text["dspark_markov_rank"],
503
+ dspark_n_routed_experts=text["dspark_n_routed_experts"],
504
+ dspark_n_activated_experts=text["dspark_num_experts_per_tok"],
505
+ )
506
+
507
+ previous_dtype = torch.get_default_dtype()
508
+ try:
509
+ torch.set_default_dtype(torch.bfloat16)
510
+ with torch.device("meta"):
511
+ runtime_model = Transformer(args, tokenizer)
512
+ finally:
513
+ torch.set_default_dtype(previous_dtype)
514
+
515
+ dtype_map = {
516
+ torch.bfloat16: BF16,
517
+ torch.float32: F32,
518
+ torch.float8_e4m3fn: FP8,
519
+ torch.float8_e8m0fnu: SCALE,
520
+ torch.float4_e2m1fn_x2: I8,
521
+ }
522
+ state: State = {}
523
+ block_out, block_in = quant["weight_block_size"]
524
+ for name, tensor in runtime_model.state_dict().items():
525
+ # Runtime modules tie these to the backbone. The public HF checkpoint stores only the
526
+ # backbone copies; safetensors cannot duplicate shared storage either.
527
+ if name.startswith("mtp.") and name.split(".", 2)[-1] in (
528
+ "embed.weight",
529
+ "head.weight",
530
+ ):
531
+ continue
532
+
533
+ shape = list(tensor.shape)
534
+ dtype = dtype_map[tensor.dtype]
535
+
536
+ # inference/convert.py dequantizes wo_a to bf16 for its grouped einsum. Undo that runtime
537
+ # representation here: the public HF checkpoint stores wo_a as block-quantized fp8.
538
+ if name.endswith(".attn.wo_a.weight"):
539
+ assert shape[0] % block_out == 0 and shape[1] % block_in == 0
540
+ dtype = FP8
541
+ state[name] = {"shape": shape, "dtype": dtype}
542
+ state[name.replace(".weight", ".scale")] = {
543
+ "shape": [shape[0] // block_out, shape[1] // block_in],
544
+ "dtype": SCALE,
545
+ }
546
+ continue
547
+
548
+ # The readable runtime promotes these parameters for computation. The published HF
549
+ # checkpoint keeps them in bf16, as confirmed from all 48 source shard headers.
550
+ if (
551
+ name == "head.weight"
552
+ or name.endswith(".markov_head.head.weight")
553
+ or name.endswith(".confidence_head.proj.weight")
554
+ or ".attn.compressor.w" in name
555
+ or (
556
+ name.startswith("vision.")
557
+ and (
558
+ name == "vision.norm.weight"
559
+ or name.endswith(".norm1.weight")
560
+ or name.endswith(".norm2.weight")
561
+ )
562
+ )
563
+ ):
564
+ dtype = BF16
565
+
566
+ state[name] = {"shape": shape, "dtype": dtype}
567
+
568
+ del runtime_model
569
+ return initialize(state, config, init_bound)
570
+
571
+ main()
572
+ ```
573
+
574
+ </details>
575
+
576
+ ### Test environment:
577
+
578
+ - Pillow: 12.3.0
579
+ - flashinfer-python: 0.6.15.post1
580
+ - huggingface_hub: 1.27.0
581
+ - numpy: 2.5.2
582
+ - safetensors: 0.8.0
583
+ - sympy: 1.14.0
584
+ - tilelang: 0.1.12
585
+ - torch: 2.13.0+cu126
586
+ - transformers: 5.15.0.dev0
587
+
588
+ ### Change log:
589
+
590
+ - 2026-10-20: Initial DeepSeek-V4.1-Flash-1017 tiny model. Instantiate the pinned source inference implementation, export its public HF checkpoint schema, document the source/tiny structural comparison, and add an online SGLang TP=2 launch profile with the DSV4-required 512-wide attention latent and 128-wide FP4 indexer; validate the official converter/runtime and a real two-H20 offline SGLang generation using equivalent launch settings.
config.json ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "DeepseekV41ForCausalLM"
4
+ ],
5
+ "model_type": "deepseek_v41",
6
+ "dtype": "bfloat16",
7
+ "transformers_version": "5.6.0",
8
+ "bos_token_id": 0,
9
+ "eos_token_id": 1,
10
+ "pad_token_id": 2,
11
+ "image_token_id": 129264,
12
+ "quantization_config": {
13
+ "quant_method": "fp8",
14
+ "activation_scheme": "dynamic",
15
+ "weight_block_size": [
16
+ 32,
17
+ 32
18
+ ],
19
+ "scale_fmt": "ue8m0",
20
+ "expert_dtype": "fp4"
21
+ },
22
+ "text_config": {
23
+ "model_type": "deepseek_v41_text",
24
+ "vocab_size": 129280,
25
+ "hidden_size": 128,
26
+ "moe_intermediate_size": 256,
27
+ "num_hidden_layers": 6,
28
+ "num_attention_heads": 4,
29
+ "num_key_value_heads": 1,
30
+ "head_dim": 512,
31
+ "qk_rope_head_dim": 64,
32
+ "q_lora_rank": 128,
33
+ "o_lora_rank": 128,
34
+ "o_groups": 2,
35
+ "hidden_act": "silu",
36
+ "swiglu_limit": 10.0,
37
+ "rms_norm_eps": 1e-20,
38
+ "attention_bias": false,
39
+ "attention_dropout": 0.0,
40
+ "initializer_range": 0.02,
41
+ "use_cache": true,
42
+ "tie_word_embeddings": false,
43
+ "max_position_embeddings": 1048576,
44
+ "rope_theta": 10000,
45
+ "rope_scaling": {
46
+ "rope_type": "yarn",
47
+ "factor": 16,
48
+ "beta_fast": 32,
49
+ "beta_slow": 1,
50
+ "original_max_position_embeddings": 65536
51
+ },
52
+ "n_routed_experts": 32,
53
+ "n_shared_experts": 1,
54
+ "num_experts_per_tok": 6,
55
+ "scoring_func": "sqrtsoftplus",
56
+ "topk_method": "noaux_tc",
57
+ "norm_topk_prob": true,
58
+ "routed_scaling_factor": 1.5,
59
+ "sliding_window": 128,
60
+ "compress_ratios": [
61
+ 0,
62
+ 2,
63
+ 2,
64
+ 1,
65
+ 1,
66
+ 0,
67
+ 0,
68
+ 0,
69
+ 0
70
+ ],
71
+ "compress_rope_theta": 160000,
72
+ "kv_source_layer_ids": [
73
+ 1,
74
+ 3
75
+ ],
76
+ "index_source_layer_ids": [
77
+ 1,
78
+ 3
79
+ ],
80
+ "index_n_heads": 4,
81
+ "index_head_dim": 128,
82
+ "index_topk": 512,
83
+ "candidate_source_layer_id": 3,
84
+ "candidate_topk_blocks": 2048,
85
+ "candidate_block_size": 8,
86
+ "hc_mult": 4,
87
+ "hc_sinkhorn_iters": 20,
88
+ "hc_eps": 1e-06,
89
+ "engram_layer_ids": [
90
+ 1,
91
+ 2
92
+ ],
93
+ "engram_num_embeddings": [
94
+ 6748,
95
+ 7592
96
+ ],
97
+ "engram_max_ngram_size": 4,
98
+ "engram_vocab_size": 512,
99
+ "engram_n_heads": 4,
100
+ "engram_head_dim": 64,
101
+ "engram_pad_token_id": 2,
102
+ "engram_compressed_vocab_size": 99092,
103
+ "num_nextn_predict_layers": 3,
104
+ "dspark_block_size": 5,
105
+ "dspark_noise_token_id": 128799,
106
+ "dspark_target_layer_ids": [
107
+ 3,
108
+ 4,
109
+ 5
110
+ ],
111
+ "dspark_markov_rank": 16,
112
+ "dspark_n_routed_experts": 8,
113
+ "dspark_num_experts_per_tok": 3
114
+ },
115
+ "vision_config": {
116
+ "model_type": "deepseek_v41_vision",
117
+ "num_hidden_layers": 2,
118
+ "hidden_size": 64,
119
+ "num_attention_heads": 4,
120
+ "intermediate_size": 128,
121
+ "patch_size": 14,
122
+ "rope_theta": 10000,
123
+ "downsample_ratio": 3,
124
+ "max_image_tokens": 1024,
125
+ "min_pixels": 295936,
126
+ "max_wh_ratio": null
127
+ }
128
+ }
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:53aae999fdcacd310c400a2fc3112bb68ce35588441784c75dca5820abf0f35b
3
+ size 96712592
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "backend": "tokenizers",
3
+ "bos_token": "<|begin▁of▁sentence|>",
4
+ "clean_up_tokenization_spaces": false,
5
+ "eos_token": "<|end▁of▁sentence|>",
6
+ "is_local": false,
7
+ "legacy": true,
8
+ "local_files_only": false,
9
+ "model_max_length": 1048576,
10
+ "pad_token": "<|end▁of▁sentence|>",
11
+ "sp_model_kwargs": {},
12
+ "tokenizer_class": "TokenizersBackend",
13
+ "unk_token": null
14
+ }