Add files using upload-large-folder tool
Browse files- README.md +152 -0
- chat_template.jinja +129 -0
- code/analyze_mm_routing.py +59 -0
- code/build_calib.py +59 -0
- code/build_calib_v3.py +232 -0
- code/build_gate_ckpt.py +185 -0
- code/build_heldout.py +96 -0
- code/build_hybrid.py +448 -0
- code/build_mm_manifest.py +56 -0
- code/converge.py +153 -0
- code/converge_all.py +49 -0
- code/converge_w2.py +238 -0
- code/converge_w2_all.py +56 -0
- code/encode_all.py +54 -0
- code/gpqa40.py +35 -0
- code/ink_aqlm.py +108 -0
- code/ink_common.py +121 -0
- code/merge_acts.py +43 -0
- code/ppl_eval.py +51 -0
- code/pv.py +256 -0
- code/pv_all.py +66 -0
- code/pv_all_v3.py +64 -0
- code/pv_gen_check.py +33 -0
- code/pv_generalize.py +143 -0
- code/pv_v3.py +206 -0
- code/reap_merge.py +47 -0
- code/reap_worker.py +128 -0
- code/run_capture_acts.py +22 -0
- code/run_capture_acts_heldout.py +23 -0
- code/run_capture_acts_mm.py +89 -0
- code/run_capture_acts_v3.py +34 -0
- code/run_route_stats.py +30 -0
- code/solve_tier.py +102 -0
- code/v1_vs_v2_heldout.py +33 -0
- code/validate_gate_ckpt.py +94 -0
- code/validate_hybrid.py +138 -0
- config.json +140 -0
- hf_quant_config.json +1049 -0
- model-00022-of-00045.safetensors +3 -0
- model-00023-of-00045.safetensors +3 -0
- model-00027-of-00045.safetensors +3 -0
- model-00031-of-00045.safetensors +3 -0
- model-00032-of-00045.safetensors +3 -0
- model-00041-of-00045.safetensors +3 -0
- model-00043-of-00045.safetensors +3 -0
- model.safetensors.index.json +0 -0
- processor_config.json +46 -0
- special_tokens_map.json +22 -0
- tiktoken/tokenizer.model +3 -0
- tokenizer_config.json +508 -0
README.md
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: other
|
| 3 |
+
base_model:
|
| 4 |
+
- thinkingmachines/Inkling
|
| 5 |
+
tags:
|
| 6 |
+
- moe
|
| 7 |
+
- nvfp4
|
| 8 |
+
- aqlm
|
| 9 |
+
- quantized
|
| 10 |
+
- inkling
|
| 11 |
+
---
|
| 12 |
+
|
| 13 |
+
# Inkling-512k-NVFP4-AQLM-hybrid
|
| 14 |
+
|
| 15 |
+
A memory-optimized **hybrid-quantized** build of
|
| 16 |
+
[thinkingmachines/Inkling](https://huggingface.co/thinkingmachines/Inkling)
|
| 17 |
+
(MoE, 256 routed experts/layer, 6 active). Each MoE layer's experts are split
|
| 18 |
+
into a small **hot** set kept at NVFP4 (~4 bpw) and a large **cold** set stored
|
| 19 |
+
as **AQLM 2-bit**. This is the **512k tier**: **14.8% of expert slots are
|
| 20 |
+
NVFP4 (hot)** and **85.2% are AQLM-2bit (cold)**, averaging **~2.66 bits
|
| 21 |
+
per weight** over the routed experts. It targets serving at **512k context on
|
| 22 |
+
4x96GB GPUs**.
|
| 23 |
+
|
| 24 |
+
> **This checkpoint needs a CUSTOM FUSED MoE KERNEL** that dispatches NVFP4 hot
|
| 25 |
+
> experts and AQLM cold experts within the same fused grouped-GEMM. **It does
|
| 26 |
+
> NOT load on stock vLLM / Transformers.** The on-disk format and dequant math
|
| 27 |
+
> are fully specified below so a kernel can consume it directly.
|
| 28 |
+
|
| 29 |
+
## Serving (512k on 4x96GB)
|
| 30 |
+
This is the **higher-quality 14.8%-hot** tier: **~358 GB weights** + KV @512k
|
| 31 |
+
(fp8, 11 global layers) ~11.5 GB leaves only **~14.6 GB** runtime headroom on a
|
| 32 |
+
384 GB box. That is **too tight for CUDA graphs + MTP at 512k** — serve with
|
| 33 |
+
**`--enforce-eager`** at 512k (with MTP), or use **256k** context (KV ~5.8 GB,
|
| 34 |
+
~20 GB headroom) for non-eager. For non-eager + MTP + CUDA-graphs at full 512k,
|
| 35 |
+
use the **10%-hot** tier instead (~346 GB weights, ~27 GB headroom). TP4,
|
| 36 |
+
`--kv-cache-dtype fp8`, modest `--max-num-seqs`.
|
| 37 |
+
|
| 38 |
+
## Quality
|
| 39 |
+
Held-out perplexity (teacher-forced, 5-domain mix disjoint from calibration;
|
| 40 |
+
gate-proxy = cold experts requantized to NVFP4 so it loads on stock vLLM, whose
|
| 41 |
+
per-expert error tracks the true hybrid). Lower is better.
|
| 42 |
+
|
| 43 |
+
| domain | Inkling-NVFP4 | prior hybrid | **this build (v3, 14.8% hot)** |
|
| 44 |
+
|---|---|---|---|
|
| 45 |
+
| code | 2.222 | 2.250 | 2.244 |
|
| 46 |
+
| agentic | 1.930 | 1.938 | 1.934 |
|
| 47 |
+
| instruction | 4.674 | 5.169 | **4.892** |
|
| 48 |
+
| medical | 5.464 | 5.964 | **5.817** |
|
| 49 |
+
| prose | 5.399 | 6.691 | **6.563** |
|
| 50 |
+
| **aggregate** | **3.406** | **3.678** | **3.608** |
|
| 51 |
+
|
| 52 |
+
This **v3** build re-tiers and re-calibrates on a broad **multimodal** corpus
|
| 53 |
+
(code / agentic / instruction / medical / prose, plus image + audio routing).
|
| 54 |
+
It beats the prior text-only-calibrated hybrid by **−1.9% aggregate**, with the
|
| 55 |
+
gains concentrated where the prior build was starved (**instruction −5.4%,
|
| 56 |
+
medical −2.5%, prose −1.9%**); code/agentic are unchanged within noise. This
|
| 57 |
+
14.8% tier edges the 10%-hot tier (−1.1%) at the cost of the tighter 512k fit
|
| 58 |
+
noted above.
|
| 59 |
+
|
| 60 |
+
## Provenance
|
| 61 |
+
- **Hot experts (NVFP4):** sliced verbatim from the ModelOpt NVFP4 base
|
| 62 |
+
(`thinkingmachines/Inkling-NVFP4`), no requantization.
|
| 63 |
+
- **Cold experts (AQLM):** quantized from the **Inkling BF16 teacher**.
|
| 64 |
+
Both `w13` and `w2` cold books are **activation-aware converged** then
|
| 65 |
+
**output-preserving PV-tuned** against the BF16 teacher on **multimodal**
|
| 66 |
+
calibration activations (text + image + audio routing), with a disjoint
|
| 67 |
+
held-out early-stop.
|
| 68 |
+
- **Assignment** of hot vs cold per layer is in
|
| 69 |
+
`hf_quant_config.json['aqlm_hybrid']['aqlm_layer_books']` and reproduced from
|
| 70 |
+
the multimodal REAP importance study (`assign_512k_v3.json`, 14.8% hot). Reproduction scripts are in
|
| 71 |
+
`code/`.
|
| 72 |
+
|
| 73 |
+
## Layout summary
|
| 74 |
+
- **63 NVFP4-packed MoE layers** carry the hybrid expert format below
|
| 75 |
+
(NVFP4 hot slabs + AQLM cold).
|
| 76 |
+
- **1 bf16-special MoE layer(s)** (layer **2**) have **no NVFP4
|
| 77 |
+
base**, so they use the SAME hybrid format EXCEPT the hot slabs are stored as
|
| 78 |
+
**bf16** (`experts.{proj}_hot_bf16`, no `.scale`/`.scale2`); their cold experts
|
| 79 |
+
are AQLM like every other layer. The fused uncompressed bf16 `experts.w13_weight`
|
| 80 |
+
/ `experts.w2_weight` are **dropped**. Layers 0-1 are dense MLP (not experts).
|
| 81 |
+
- All **non-expert** tensors (attention, embeddings, norms, `shared_experts`,
|
| 82 |
+
`gate`, vision, audio, MTP) are copied verbatim from the NVFP4 base.
|
| 83 |
+
- Total hot slots 2417 / 16384 expert slots.
|
| 84 |
+
|
| 85 |
+
## On-disk tensor format (per NVFP4-packed MoE layer L)
|
| 86 |
+
`prefix = model.llm.layers.{L}.mlp.experts`. Routed experts (256 total) are
|
| 87 |
+
partitioned into `n_hot` hot + `n_cold` cold. For each projection
|
| 88 |
+
`proj in {w13, w2}` (w13 = fused gate|up, out=6144, in=6144; w2 = down,
|
| 89 |
+
out=6144, in=3072):
|
| 90 |
+
|
| 91 |
+
**Hot — NVFP4-packed layers (ModelOpt, `hot_format=nvfp4`):**
|
| 92 |
+
| tensor | dtype | shape |
|
| 93 |
+
|---|---|---|
|
| 94 |
+
| `{prefix}.{proj}_hot_weight` | uint8 | `[n_hot, out, in/2]` (2 fp4 codes/byte) |
|
| 95 |
+
| `{prefix}.{proj}_hot_weight.scale` | float8_e4m3 | `[n_hot, out, in/16]` (per-block-16 scale) |
|
| 96 |
+
| `{prefix}.{proj}_hot_weight.scale2` | float32 | `[n_hot]` (per-expert global scale) |
|
| 97 |
+
|
| 98 |
+
**Hot — bf16-special layer(s) (`hot_format=bf16`, e.g. layer 2):**
|
| 99 |
+
| tensor | dtype | shape |
|
| 100 |
+
|---|---|---|
|
| 101 |
+
| `{prefix}.{proj}_hot_bf16` | bfloat16 | `[n_hot, out, in]` (dense bf16, no scales) |
|
| 102 |
+
|
| 103 |
+
**Cold (AQLM) — same for every layer. One code tensor PER BOOK, smallest lossless dtype:**
|
| 104 |
+
| tensor | dtype | shape |
|
| 105 |
+
|---|---|---|
|
| 106 |
+
| `{prefix}.{proj}_cold_codes.{b}` | uint8 if book has <=256 entries else int16 | `[n_cold, out, in/8]` (group_size=8) |
|
| 107 |
+
| `{prefix}.{proj}_cold_codebook.{b}` | float16 | `[entries_b, 8]` |
|
| 108 |
+
| `{prefix}.{proj}_cold_scales` | float16 | `[n_cold, out]` (per-output-row scale) |
|
| 109 |
+
|
| 110 |
+
Per-book code dtypes: `w13` book0 = **int16** (65536 entries); `w2` book0 =
|
| 111 |
+
**int16** (65536), `w2` book1 = **uint8** (256 entries, codes 0..255 — lossless).
|
| 112 |
+
`{proj}_cold_codes.{b}` replaces the old single `{proj}_cold_codes` tensor
|
| 113 |
+
(the book axis is now the trailing `.{b}` suffix), saving ~38 GB losslessly.
|
| 114 |
+
|
| 115 |
+
**Index maps:**
|
| 116 |
+
| tensor | dtype | shape | meaning |
|
| 117 |
+
|---|---|---|---|
|
| 118 |
+
| `{prefix}.hot_ids` | int32 | `[n_hot]` | routed-expert index (0..255) of each hot slab |
|
| 119 |
+
| `{prefix}.cold_ids` | int32 | `[n_cold]` | routed-expert index (0..255) of each cold slab |
|
| 120 |
+
|
| 121 |
+
`hot_ids[i]` gives the original routed-expert id for hot slab `i` (and likewise
|
| 122 |
+
`cold_ids`); together they partition `0..255`. **group_size = 8.**
|
| 123 |
+
- `w13`: **1 book**, `entries = [65536]`.
|
| 124 |
+
- `w2`: **2 books**, `entries = [65536, 256]`.
|
| 125 |
+
|
| 126 |
+
## Dequant math
|
| 127 |
+
**Cold (AQLM), expert e, output row r, group g (8 weights):**
|
| 128 |
+
```
|
| 129 |
+
acc[8] = 0
|
| 130 |
+
for b in range(n_books):
|
| 131 |
+
acc += codebook_b[ codes_b[e, r, g] ] # codes_b = cold_codes.{b}; gather an 8-vector
|
| 132 |
+
W[e, r, g*8:(g+1)*8] = acc * cold_scales[e, r] # per-row scale
|
| 133 |
+
```
|
| 134 |
+
Codes are read from the per-book tensor `{proj}_cold_codes.{b}`. uint8 books
|
| 135 |
+
(<=256 entries) index the codebook directly. For the int16 65536-entry book,
|
| 136 |
+
negative int16 values wrap to the intended unsigned row (`idx & 0xFFFF`).
|
| 137 |
+
|
| 138 |
+
**Hot (NVFP4, standard ModelOpt e2m1):**
|
| 139 |
+
```
|
| 140 |
+
codes = unpack_nibbles(hot_weight) # 0..15 -> e2m1 LUT value
|
| 141 |
+
W = LUT[codes] * scale.repeat_interleave(16, dim=-1) * scale2
|
| 142 |
+
```
|
| 143 |
+
e2m1 LUT = `[0, .5, 1, 1.5, 2, 3, 4, 6, -0, -.5, -1, -1.5, -2, -3, -4, -6]`;
|
| 144 |
+
low nibble is the first weight of each pair.
|
| 145 |
+
|
| 146 |
+
## Files
|
| 147 |
+
- `model-*.safetensors` + `model.safetensors.index.json` — the sharded weights.
|
| 148 |
+
- `hf_quant_config.json` — original NVFP4 config **plus** the `aqlm_hybrid` block
|
| 149 |
+
(`quant_method`, per-layer `{n_nvfp4, n_cold, packed}`, book entry counts).
|
| 150 |
+
- `config.json` — base config **plus** a `quantization_config` block advertising
|
| 151 |
+
`quant_method = inkling_nvfp4_aqlm_hybrid`.
|
| 152 |
+
- `code/` — the exact repackaging scripts (`build_hybrid.py`, `ink_common.py`, ...).
|
chat_template.jinja
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{%- set effort_map = {"none": 0.0, "minimal": 0.1, "low": 0.2, "medium": 0.7, "high": 0.9, "max": 0.99} -%}
|
| 2 |
+
{%- set role_token = {"user": "<|message_user|>", "assistant": "<|message_model|>", "system": "<|message_system|>", "tool": "<|message_tool|>"} -%}
|
| 3 |
+
|
| 4 |
+
{%- macro emit_thinking_effort() -%}
|
| 5 |
+
{%- set eff = reasoning_effort if reasoning_effort is defined and reasoning_effort is not none else 0.9 -%}
|
| 6 |
+
{%- if eff is string -%}
|
| 7 |
+
{%- set key = eff | trim -%}
|
| 8 |
+
{%- if key not in effort_map -%}
|
| 9 |
+
{{- raise_exception("Unknown reasoning_effort: " ~ eff) -}}
|
| 10 |
+
{%- endif -%}
|
| 11 |
+
{%- set num = effort_map[key] -%}
|
| 12 |
+
{%- else -%}
|
| 13 |
+
{%- set num = eff | float -%}
|
| 14 |
+
{%- endif -%}
|
| 15 |
+
{%- if num < 0.0 or num > 0.99 -%}
|
| 16 |
+
{{- raise_exception("reasoning_effort must be in [0.0, 0.99]") -}}
|
| 17 |
+
{%- endif -%}
|
| 18 |
+
{{- "<|message_system|><|content_text|>Thinking effort level: " -}}
|
| 19 |
+
{%- if num == 0.0 -%}0{%- else -%}{{ num }}{%- endif -%}
|
| 20 |
+
{{- "<|end_message|>" -}}
|
| 21 |
+
{%- endmacro -%}
|
| 22 |
+
|
| 23 |
+
{%- if tools -%}
|
| 24 |
+
{%- set tool_state = namespace(specs=[]) -%}
|
| 25 |
+
{%- for tool in tools -%}
|
| 26 |
+
{%- set fn = tool.function if tool.function is defined else tool -%}
|
| 27 |
+
{%- set spec = {
|
| 28 |
+
"description": (fn.description if fn.description is defined and fn.description else ""),
|
| 29 |
+
"name": fn.name,
|
| 30 |
+
"parameters": (fn.parameters if fn.parameters is defined and fn.parameters else {}),
|
| 31 |
+
"type": (tool.type if tool.type is defined and tool.type else "function"),
|
| 32 |
+
} -%}
|
| 33 |
+
{%- set tool_state.specs = tool_state.specs + [spec] -%}
|
| 34 |
+
{%- endfor -%}
|
| 35 |
+
{{- "<|message_system|>tool_declare<|content_xml|>" -}}
|
| 36 |
+
{{- tool_state.specs | tojson(sort_keys=true, separators=(",", ":")) -}}
|
| 37 |
+
{{- "<|end_message|>" -}}
|
| 38 |
+
{%- endif -%}
|
| 39 |
+
|
| 40 |
+
{%- set state = namespace(effort_emitted=false) -%}
|
| 41 |
+
{%- for message in messages -%}
|
| 42 |
+
{%- if message.role not in role_token -%}
|
| 43 |
+
{{- raise_exception("Unknown message role: " ~ message.role) -}}
|
| 44 |
+
{%- endif -%}
|
| 45 |
+
{%- if not state.effort_emitted and message.role != "system" -%}
|
| 46 |
+
{{- emit_thinking_effort() -}}
|
| 47 |
+
{%- set state.effort_emitted = true -%}
|
| 48 |
+
{%- endif -%}
|
| 49 |
+
|
| 50 |
+
{%- set rtok = role_token[message.role] -%}
|
| 51 |
+
|
| 52 |
+
{%- if message.role == "tool" -%}
|
| 53 |
+
{%- set tool_name_state = namespace(name="") -%}
|
| 54 |
+
{%- if message.name is defined and message.name -%}
|
| 55 |
+
{%- set tool_name_state.name = message.name -%}
|
| 56 |
+
{%- elif message.tool_call_id is defined and message.tool_call_id -%}
|
| 57 |
+
{%- for prev in messages -%}
|
| 58 |
+
{%- if prev.role == "assistant" and prev.tool_calls -%}
|
| 59 |
+
{%- for tc in prev.tool_calls -%}
|
| 60 |
+
{%- if tc.id is defined and tc.id == message.tool_call_id and tc.function.name is defined -%}
|
| 61 |
+
{%- set tool_name_state.name = tc.function.name -%}
|
| 62 |
+
{%- endif -%}
|
| 63 |
+
{%- endfor -%}
|
| 64 |
+
{%- endif -%}
|
| 65 |
+
{%- endfor -%}
|
| 66 |
+
{%- endif -%}
|
| 67 |
+
{{- rtok -}}
|
| 68 |
+
{%- if tool_name_state.name -%}{{- tool_name_state.name -}}{%- endif -%}
|
| 69 |
+
{{- "<|content_text|>" -}}
|
| 70 |
+
{%- if message.content is string -%}{{- message.content -}}{%- endif -%}
|
| 71 |
+
{{- "<|end_message|>" -}}
|
| 72 |
+
|
| 73 |
+
{%- else -%}
|
| 74 |
+
{%- if message.role == "assistant" and message.reasoning_content is defined and message.reasoning_content -%}
|
| 75 |
+
{{- "<|message_model|><|content_thinking|>" ~ message.reasoning_content ~ "<|end_message|>" -}}
|
| 76 |
+
{%- endif -%}
|
| 77 |
+
|
| 78 |
+
{%- if message.content is string -%}
|
| 79 |
+
{{- rtok ~ "<|content_text|>" ~ message.content ~ "<|end_message|>" -}}
|
| 80 |
+
{%- elif message.content -%}
|
| 81 |
+
{%- for part in message.content -%}
|
| 82 |
+
{%- if part is string -%}
|
| 83 |
+
{{- rtok ~ "<|content_text|>" ~ part ~ "<|end_message|>" -}}
|
| 84 |
+
{%- elif part.type is not defined or part.type in ("text", "input_text") -%}
|
| 85 |
+
{%- set text_part = (part.text if part.text is defined and part.text is string else "") -%}
|
| 86 |
+
{{- rtok ~ "<|content_text|>" ~ text_part ~ "<|end_message|>" -}}
|
| 87 |
+
{%- elif part.type in ("image", "input_image", "image_url") -%}
|
| 88 |
+
{{- rtok ~ "<|content_image|><|unused_200054|><|end_message|>" -}}
|
| 89 |
+
{%- elif part.type in ("audio", "input_audio", "audio_url") -%}
|
| 90 |
+
{{- rtok ~ "<|content_audio_input|><|unused_200053|><|audio_end|><|end_message|>" -}}
|
| 91 |
+
{%- else -%}
|
| 92 |
+
{{- raise_exception("Unsupported content part type: " ~ part.type) -}}
|
| 93 |
+
{%- endif -%}
|
| 94 |
+
{%- endfor -%}
|
| 95 |
+
{%- endif -%}
|
| 96 |
+
|
| 97 |
+
{%- if message.role == "assistant" and message.tool_calls -%}
|
| 98 |
+
{%- for tc in message.tool_calls -%}
|
| 99 |
+
{%- set fn = tc.function -%}
|
| 100 |
+
{%- if fn.name is not defined or fn.name is not string -%}
|
| 101 |
+
{{- raise_exception("tool call function name must be a string") -}}
|
| 102 |
+
{%- endif -%}
|
| 103 |
+
{%- set args = fn.arguments if fn.arguments is defined and fn.arguments else {} -%}
|
| 104 |
+
{%- if args is string -%}
|
| 105 |
+
{{- raise_exception("tool call arguments must be a parsed object, not a JSON string; canonicalize upstream") -}}
|
| 106 |
+
{%- endif -%}
|
| 107 |
+
{%- if args is not mapping -%}
|
| 108 |
+
{{- raise_exception("tool call arguments must be an object") -}}
|
| 109 |
+
{%- endif -%}
|
| 110 |
+
{{- "<|message_model|>" ~ fn.name ~ "<|content_invoke_tool_json|>" -}}
|
| 111 |
+
{{- '{"name":' ~ (fn.name | tojson(sort_keys=true, separators=(",", ":"))) ~ ',"args":' -}}
|
| 112 |
+
{{- (args | tojson(sort_keys=true, separators=(",", ":"))) -}}
|
| 113 |
+
{{- "}<|end_message|>" -}}
|
| 114 |
+
{%- endfor -%}
|
| 115 |
+
{%- endif -%}
|
| 116 |
+
|
| 117 |
+
{%- if message.role == "assistant" -%}
|
| 118 |
+
{{- "<|content_model_end_sampling|>" -}}
|
| 119 |
+
{%- endif -%}
|
| 120 |
+
{%- endif -%}
|
| 121 |
+
{%- endfor -%}
|
| 122 |
+
|
| 123 |
+
{%- if not state.effort_emitted -%}
|
| 124 |
+
{{- emit_thinking_effort() -}}
|
| 125 |
+
{%- endif -%}
|
| 126 |
+
|
| 127 |
+
{%- if add_generation_prompt -%}
|
| 128 |
+
{{- "<|message_model|>" -}}
|
| 129 |
+
{%- endif -%}
|
code/analyze_mm_routing.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Diagnostic: do image/audio tokens route to experts that v2's (text-only) tiering
|
| 3 |
+
left COLD? If yes, PV alone can't fully protect multimodal -> a v4 re-tier with
|
| 4 |
+
multimodal routing is warranted. Reads the captured mm acts (ids) — free, no GPU.
|
| 5 |
+
|
| 6 |
+
Reports, per modality: fraction of routed token-slots landing on v2-COLD experts,
|
| 7 |
+
aggregate + worst layers. Writes $RUN/mm_routing.txt.
|
| 8 |
+
"""
|
| 9 |
+
import json, os, sys, glob
|
| 10 |
+
import torch
|
| 11 |
+
|
| 12 |
+
Q = "/data/inkling-quant"
|
| 13 |
+
ASSIGN = f"{Q}/assign_512k_v2.json"
|
| 14 |
+
OUT = sys.argv[1] if len(sys.argv) > 1 else f"{Q}/v3_run/mm_routing.txt"
|
| 15 |
+
|
| 16 |
+
def cold_frac(acts_dir, hot):
|
| 17 |
+
"""Over all layers with acts in acts_dir, fraction of (token,slot) routings
|
| 18 |
+
that hit a COLD expert. Returns (agg_frac, [(layer,frac,ntok)])."""
|
| 19 |
+
per = []
|
| 20 |
+
tot_cold = tot = 0
|
| 21 |
+
for p in sorted(glob.glob(f"{acts_dir}/acts_layer_*.pt")):
|
| 22 |
+
L = int(p.split("_")[-1].split(".")[0])
|
| 23 |
+
ids = torch.load(p, map_location="cpu").get("ids")
|
| 24 |
+
if ids is None or ids.numel() == 0:
|
| 25 |
+
continue
|
| 26 |
+
hotL = torch.tensor(sorted(hot.get(str(L), [])))
|
| 27 |
+
is_hot = torch.isin(ids, hotL) # [N,6] bool
|
| 28 |
+
c = int((~is_hot).sum()); n = int(is_hot.numel())
|
| 29 |
+
tot_cold += c; tot += n
|
| 30 |
+
per.append((L, c / max(1, n), ids.shape[0]))
|
| 31 |
+
per.sort(key=lambda x: -x[1])
|
| 32 |
+
return (tot_cold / max(1, tot)), per
|
| 33 |
+
|
| 34 |
+
def main():
|
| 35 |
+
hot = json.load(open(ASSIGN))["hot_experts"]
|
| 36 |
+
lines = ["=== multimodal routing vs v2 (text-only) tiering ===",
|
| 37 |
+
"fraction of image/audio routings landing on v2-COLD experts:"]
|
| 38 |
+
rec = False
|
| 39 |
+
for mm, d in [("image", f"{Q}/acts_v3_img"), ("audio", f"{Q}/acts_v3_audio")]:
|
| 40 |
+
if not os.path.isdir(d):
|
| 41 |
+
lines.append(f" {mm}: (no acts at {d})"); continue
|
| 42 |
+
frac, per = cold_frac(d, hot)
|
| 43 |
+
lines.append(f" {mm}: {100*frac:.1f}% of routings on cold experts "
|
| 44 |
+
f"(worst layers: " + ", ".join(f"L{L}={100*f:.0f}%" for L,f,_ in per[:5]) + ")")
|
| 45 |
+
if frac > 0.55: # cold experts get ~85% of text mass by design; >55% mm-cold => mm under-served
|
| 46 |
+
rec = True
|
| 47 |
+
# reference: text cold fraction (baseline expectation ~85% since 85% experts are cold)
|
| 48 |
+
if os.path.isdir(f"{Q}/acts_v3"):
|
| 49 |
+
tf, _ = cold_frac(f"{Q}/acts_v3", hot)
|
| 50 |
+
lines.append(f" [ref] text: {100*tf:.1f}% on cold (expected high; ~85% of experts are cold)")
|
| 51 |
+
lines.append("")
|
| 52 |
+
lines.append("RE-TIER RECOMMENDED (v4): multimodal routing significantly under-served by v2 tiering."
|
| 53 |
+
if rec else
|
| 54 |
+
"NO re-tier needed: multimodal routing overlaps v2 hot set adequately; PV suffices.")
|
| 55 |
+
open(OUT, "w").write("\n".join(lines) + "\n")
|
| 56 |
+
print("\n".join(lines))
|
| 57 |
+
|
| 58 |
+
if __name__ == "__main__":
|
| 59 |
+
main()
|
code/build_calib.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Assemble a diverse local calibration corpus for routing-stats / tiering.
|
| 3 |
+
Mixes code (vLLM src), prose/docs (markdown), and synthetic chat/instruction
|
| 4 |
+
prompts. Not distribution-perfect, but exercises diverse expert routing.
|
| 5 |
+
Output: /data/inkling-quant/calib.jsonl (one {"text": ...} per line, ~6k chars).
|
| 6 |
+
"""
|
| 7 |
+
import json, os, glob, random
|
| 8 |
+
random.seed(42)
|
| 9 |
+
OUT = "/data/inkling-quant/calib.jsonl"
|
| 10 |
+
CHUNK = 6000 # chars (~1.5-2k tokens)
|
| 11 |
+
MAX_CHUNKS = 700
|
| 12 |
+
chunks = []
|
| 13 |
+
|
| 14 |
+
def add_files(patterns, cap):
|
| 15 |
+
got = 0
|
| 16 |
+
files = []
|
| 17 |
+
for p in patterns:
|
| 18 |
+
files += glob.glob(p, recursive=True)
|
| 19 |
+
random.shuffle(files)
|
| 20 |
+
for f in files:
|
| 21 |
+
if got >= cap: break
|
| 22 |
+
try:
|
| 23 |
+
t = open(f, errors="ignore").read()
|
| 24 |
+
except Exception:
|
| 25 |
+
continue
|
| 26 |
+
if len(t) < 500: continue
|
| 27 |
+
for i in range(0, min(len(t), CHUNK*3), CHUNK):
|
| 28 |
+
c = t[i:i+CHUNK]
|
| 29 |
+
if len(c) >= 1000:
|
| 30 |
+
chunks.append(c); got += 1
|
| 31 |
+
if got >= cap: break
|
| 32 |
+
|
| 33 |
+
# code (python + rust)
|
| 34 |
+
add_files(["/data/vllm-inkling/vllm/**/*.py", "/data/vllm-inkling/rust/**/*.rs"], 300)
|
| 35 |
+
# prose / docs
|
| 36 |
+
add_files(["/data/vllm-inkling/docs/**/*.md", "/data/glm52/code/**/*.md"], 150)
|
| 37 |
+
# synthetic instruction/chat/agentic (varied domains)
|
| 38 |
+
prompts = [
|
| 39 |
+
"Explain how photosynthesis converts sunlight into chemical energy, step by step.",
|
| 40 |
+
"Write a haiku about the ocean at dawn, then explain the imagery.",
|
| 41 |
+
"You are a helpful assistant. A user asks: how do I safely defrost chicken? Answer thoroughly.",
|
| 42 |
+
"Summarize the causes of the French Revolution in three paragraphs.",
|
| 43 |
+
"Translate 'the quick brown fox jumps over the lazy dog' into French and German.",
|
| 44 |
+
"Given a list of integers, describe an algorithm to find the two numbers summing to a target.",
|
| 45 |
+
"A patient reports fatigue and joint pain. List a differential diagnosis reasoning process (educational).",
|
| 46 |
+
"Write a short story about a lighthouse keeper who discovers a message in a bottle.",
|
| 47 |
+
"Explain the difference between TCP and UDP and when to use each.",
|
| 48 |
+
"Describe how a mixture-of-experts transformer routes tokens to experts.",
|
| 49 |
+
]
|
| 50 |
+
for _ in range(12):
|
| 51 |
+
for p in prompts:
|
| 52 |
+
chunks.append(p + "\n\n" + random.choice(prompts) + "\n\n" + random.choice(prompts))
|
| 53 |
+
|
| 54 |
+
random.shuffle(chunks)
|
| 55 |
+
chunks = chunks[:MAX_CHUNKS]
|
| 56 |
+
with open(OUT, "w") as fh:
|
| 57 |
+
for c in chunks:
|
| 58 |
+
fh.write(json.dumps({"text": c}) + "\n")
|
| 59 |
+
print(f"wrote {len(chunks)} chunks to {OUT} (~{sum(len(c) for c in chunks)//4} tokens est)")
|
code/build_calib_v3.py
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""v3 corpus — BROAD GLM-5.2-style mix, with per-category disjoint held-out.
|
| 3 |
+
|
| 4 |
+
Mirrors GLM-5.2's calib composition (code 40 / agentic 25 / instruction 15 /
|
| 5 |
+
medical 10 / prose 10) instead of narrow wikitext, so routing coverage matches
|
| 6 |
+
what Inkling actually serves. Each category is split 3 ways by content hash:
|
| 7 |
+
train -> calib_v3.jsonl (PV trains; big, for cold-expert coverage)
|
| 8 |
+
pv -> heldout_v3.jsonl (PV early-stop signal; disjoint)
|
| 9 |
+
ppl -> ppl_v3.jsonl (pre/post ppl; disjoint; {"text","cat"} for per-domain)
|
| 10 |
+
|
| 11 |
+
Medical = PUBLIC LITERATURE / EXAM text only (PubMedQA abstracts, MedMCQA
|
| 12 |
+
questions+explanations, general medical-education prompts). NO patient data / PHI.
|
| 13 |
+
|
| 14 |
+
Run with the datasets-enabled venv:
|
| 15 |
+
/home/coder/git/glm52/.venv/bin/python build_calib_v3.py
|
| 16 |
+
"""
|
| 17 |
+
import json, os, glob, random, hashlib, re
|
| 18 |
+
random.seed(1337)
|
| 19 |
+
|
| 20 |
+
RAW = "/data/inkling-quant/corpus_v3/raw"
|
| 21 |
+
OUT = "/data/inkling-quant"
|
| 22 |
+
CHUNK = 6000; MIN_CHUNK = 1000
|
| 23 |
+
# token budgets (approx via chars/4). GLM used ~15M; we aim ~9M train.
|
| 24 |
+
TRAIN_TOK = 9_000_000
|
| 25 |
+
MIX = {"code":0.40, "agentic":0.25, "instruction":0.15, "medical":0.10, "prose":0.10}
|
| 26 |
+
# per-category held-out caps (chunks). ppl small+fast (~GLM's 46.7k tok); pv larger.
|
| 27 |
+
PPL_CHUNKS_PER_CAT = 20 # ~20*1.5k = ~30k tok/cat -> ~50k+ ppl tok (~GLM's 46.7k)
|
| 28 |
+
PV_CHUNKS_PER_CAT = 60
|
| 29 |
+
|
| 30 |
+
def chunks_of(text, cap=10**9):
|
| 31 |
+
out=[]; text=text.strip()
|
| 32 |
+
for i in range(0, len(text), CHUNK):
|
| 33 |
+
c=text[i:i+CHUNK]
|
| 34 |
+
if len(c)>=MIN_CHUNK: out.append(c)
|
| 35 |
+
if len(out)>=cap: break
|
| 36 |
+
return out
|
| 37 |
+
|
| 38 |
+
def pack(docs, cap_tok=10**12):
|
| 39 |
+
"""Concatenate a stream of (possibly short) docs into a buffer and emit
|
| 40 |
+
CHUNK-sized pieces, so short examples aren't dropped (GLM's approach)."""
|
| 41 |
+
out=[]; buf=""; tok=0
|
| 42 |
+
for d in docs:
|
| 43 |
+
if not d: continue
|
| 44 |
+
buf += d.rstrip()+"\n\n"
|
| 45 |
+
while len(buf)>=CHUNK:
|
| 46 |
+
out.append(buf[:CHUNK]); buf=buf[CHUNK:]; tok+=CHUNK//4
|
| 47 |
+
if tok>=cap_tok: return out
|
| 48 |
+
if len(buf)>=MIN_CHUNK: out.append(buf)
|
| 49 |
+
return out
|
| 50 |
+
|
| 51 |
+
# ---------------- sources ----------------
|
| 52 |
+
def src_code_local(cap_tok):
|
| 53 |
+
pats=["/data/vllm-inkling/vllm/**/*.py","/data/vllm-inkling/csrc/**/*.*",
|
| 54 |
+
"/data/vllm-inkling/rust/**/*.rs","/data/vllm-glm52-sm120/vllm/**/*.py",
|
| 55 |
+
"/data/vllm-glm52-sm120/csrc/**/*.cu","/home/coder/Antidoom/**/*.py"]
|
| 56 |
+
files=[]; [files.extend(glob.glob(p,recursive=True)) for p in pats]
|
| 57 |
+
files=sorted(set(files)); random.shuffle(files)
|
| 58 |
+
docs=[]; tok=0
|
| 59 |
+
for f in files:
|
| 60 |
+
if tok>=cap_tok: break
|
| 61 |
+
try: t=open(f,errors="ignore").read()
|
| 62 |
+
except: continue
|
| 63 |
+
if len(t)<400: continue
|
| 64 |
+
docs.append(f"# ==== {os.path.basename(f)} ====\n{t[:60000]}"); tok+=len(t[:60000])//4
|
| 65 |
+
return docs
|
| 66 |
+
|
| 67 |
+
def _hf(name, cfg=None):
|
| 68 |
+
from datasets import load_dataset
|
| 69 |
+
return load_dataset(name, cfg, split="train", streaming=True) if cfg else \
|
| 70 |
+
load_dataset(name, split="train", streaming=True)
|
| 71 |
+
|
| 72 |
+
def src_code_hf(cap_tok):
|
| 73 |
+
docs=[]; tok=0
|
| 74 |
+
try:
|
| 75 |
+
for r in _hf("m-a-p/CodeFeedback-Filtered-Instruction"):
|
| 76 |
+
docs.append(f"### Task\n{r['query']}\n\n### Solution\n{r['answer']}"); tok+=len(docs[-1])//4
|
| 77 |
+
if tok>=cap_tok*0.6: break
|
| 78 |
+
except Exception as e: print(" code_hf CodeFeedback:",str(e)[:80])
|
| 79 |
+
try:
|
| 80 |
+
for r in _hf("jtatman/python-code-dataset-500k"):
|
| 81 |
+
docs.append(f"# {r.get('instruction','')}\n{r.get('output','')}"); tok+=len(docs[-1])//4
|
| 82 |
+
if tok>=cap_tok: break
|
| 83 |
+
except Exception as e: print(" code_hf py500k:",str(e)[:80])
|
| 84 |
+
return docs
|
| 85 |
+
|
| 86 |
+
TOOLS=["web_search","python","bash","read_file","sql_query","http_get","calculator","grep","git_log"]
|
| 87 |
+
AG_TASKS=["Find the latest release version of numpy and summarize the changelog.",
|
| 88 |
+
"Compute the 20th Fibonacci number and verify it two ways.",
|
| 89 |
+
"Read config.yaml and report the database connection settings.",
|
| 90 |
+
"Query the orders table for total revenue in Q3 grouped by region.",
|
| 91 |
+
"Check whether the service on port 8080 is healthy and report latency.",
|
| 92 |
+
"Refactor this function to be O(n) and run the tests.",
|
| 93 |
+
"Download the CSV, parse it, and report the top 5 rows by value.",
|
| 94 |
+
"Diagnose why the CI build failed and propose a minimal fix.",
|
| 95 |
+
"Search the codebase for all callers of parse_config and summarize them.",
|
| 96 |
+
"Given a stack trace, locate the offending line and explain the bug."]
|
| 97 |
+
def src_agentic(cap_tok):
|
| 98 |
+
"""Varied synthetic tool-call sessions (content randomized so chunks are unique)."""
|
| 99 |
+
docs=[]; tok=0; rng=random.Random(7)
|
| 100 |
+
while tok<cap_tok:
|
| 101 |
+
task=rng.choice(AG_TASKS); n=rng.randint(2,5)
|
| 102 |
+
s=[f"<|user|>\n{task}\n<|assistant|>\nLet me work through this step by step.\n"]
|
| 103 |
+
for i in range(n):
|
| 104 |
+
tool=rng.choice(TOOLS); rid=rng.randint(1000,9999)
|
| 105 |
+
args=json.dumps({"q":f"{task[:30]}","step":i,"rid":rid,"limit":rng.randint(5,50)})
|
| 106 |
+
res=json.dumps({"rid":rid,"rows":rng.randint(0,200),"ok":rng.random()>0.1,
|
| 107 |
+
"note":rng.choice(["cache miss","200 OK","partial","retry","done"])})
|
| 108 |
+
s.append(f'<tool_call>\n{{"name":"{tool}","arguments":{args}}}\n</tool_call>\n')
|
| 109 |
+
s.append(f'<tool_response>\n{res}\n</tool_response>\n')
|
| 110 |
+
s.append(rng.choice(["Observed the result; continuing.","That narrows it down.",
|
| 111 |
+
"Now I'll verify.","Proceeding to the next step."])+f" (step {i})\n")
|
| 112 |
+
s.append(f"Final answer: {task[:40]}... resolved after {n} tool calls (session {rng.randint(10**4,10**6)}).\n")
|
| 113 |
+
c="".join(s); docs.append(c); tok+=len(c)//4
|
| 114 |
+
return docs
|
| 115 |
+
|
| 116 |
+
def src_instruction(cap_tok):
|
| 117 |
+
docs=[]; tok=0
|
| 118 |
+
try:
|
| 119 |
+
for r in _hf("tatsu-lab/alpaca"):
|
| 120 |
+
instr=r["instruction"]+(("\n\n"+r["input"]) if r.get("input") else "")
|
| 121 |
+
docs.append(f"<|user|>\n{instr}\n<|assistant|>\n{r['output']}"); tok+=len(docs[-1])//4
|
| 122 |
+
if tok>=cap_tok: break
|
| 123 |
+
except Exception as e: print(" instruction alpaca:",str(e)[:80])
|
| 124 |
+
return docs
|
| 125 |
+
|
| 126 |
+
MEDICAL_QA=[
|
| 127 |
+
"Explain the differential diagnosis approach for acute chest pain across cardiac, pulmonary, GI, and musculoskeletal causes.",
|
| 128 |
+
"Describe how to systematically read a chest X-ray and common findings and pitfalls.",
|
| 129 |
+
"Explain the pharmacology of beta-blockers: mechanism, selectivity, indications, contraindications.",
|
| 130 |
+
"Walk through the pathophysiology of type 2 diabetes and the mechanism of each major drug class.",
|
| 131 |
+
"Explain how CT and MRI work physically and when each is preferred clinically.",
|
| 132 |
+
"Describe interpretation of a full blood count and common patterns and differentials.",
|
| 133 |
+
"Explain TNM staging of solid tumours and how imaging and histopathology contribute.",
|
| 134 |
+
"Describe the cardiac cycle and how it maps to ECG waveforms and heart sounds.",
|
| 135 |
+
"Explain sepsis: definitions, pathophysiology, recognition criteria, initial management.",
|
| 136 |
+
"Explain acid-base disturbances and step-by-step arterial blood gas interpretation."]
|
| 137 |
+
RADIOLOGY_CSV="/data/inkling-quant/corpus_v3/raw/radiology/ReportsDATASET.csv"
|
| 138 |
+
def src_medical(cap_tok):
|
| 139 |
+
"""PUBLIC LITERATURE / EXAM text only (no patient data)."""
|
| 140 |
+
docs=[]; tok=0
|
| 141 |
+
# local de-identified radiology reports (harrison.ai domain), if present
|
| 142 |
+
if os.path.exists(RADIOLOGY_CSV):
|
| 143 |
+
import csv as _csv
|
| 144 |
+
for r in _csv.DictReader(open(RADIOLOGY_CSV,errors="ignore")):
|
| 145 |
+
t=next(iter(r.values()),"") or ""
|
| 146 |
+
if len(t)>40: docs.append(t); tok+=len(t)//4
|
| 147 |
+
if tok>=cap_tok*0.3: break
|
| 148 |
+
try:
|
| 149 |
+
for r in _hf("qiaojin/PubMedQA","pqa_labeled"):
|
| 150 |
+
ctx=" ".join(r["context"]["contexts"]) if isinstance(r.get("context"),dict) else str(r.get("context",""))
|
| 151 |
+
docs.append(f"{r['question']}\n{ctx}\n{r.get('long_answer','')}"); tok+=len(docs[-1])//4
|
| 152 |
+
if tok>=cap_tok*0.5: break
|
| 153 |
+
except Exception as e: print(" medical pubmedqa:",str(e)[:80])
|
| 154 |
+
try:
|
| 155 |
+
for r in _hf("openlifescienceai/medmcqa"):
|
| 156 |
+
opts=f"A){r['opa']} B){r['opb']} C){r['opc']} D){r['opd']}"
|
| 157 |
+
docs.append(f"{r['question']}\n{opts}\nExplanation: {r.get('exp') or ''}"); tok+=len(docs[-1])//4
|
| 158 |
+
if tok>=cap_tok: break
|
| 159 |
+
except Exception as e: print(" medical medmcqa:",str(e)[:80])
|
| 160 |
+
for q in MEDICAL_QA: docs.append(f"<|user|>\n{q} Be thorough.\n<|assistant|>")
|
| 161 |
+
return docs
|
| 162 |
+
|
| 163 |
+
def src_prose(cap_tok):
|
| 164 |
+
docs=[]; tok=0
|
| 165 |
+
p=f"{RAW}/wikitext2-train.txt" # encyclopedic
|
| 166 |
+
if os.path.exists(p):
|
| 167 |
+
raw=open(p,errors="ignore").read()
|
| 168 |
+
for para in re.split(r"\n = [^=]", raw):
|
| 169 |
+
docs.append(para); tok+=len(para)//4
|
| 170 |
+
if tok>=cap_tok*0.5: break
|
| 171 |
+
for f in sorted(glob.glob("/data/vllm-inkling/docs/**/*.md",recursive=True)): # docs
|
| 172 |
+
if tok>=cap_tok*0.7: break
|
| 173 |
+
try: t=open(f,errors="ignore").read()
|
| 174 |
+
except: continue
|
| 175 |
+
docs.append(t); tok+=len(t)//4
|
| 176 |
+
try: # dolly general knowledge
|
| 177 |
+
for r in _hf("databricks/databricks-dolly-15k"):
|
| 178 |
+
docs.append("\n\n".join(x for x in [r.get("instruction",""),r.get("context",""),r.get("response","")] if x))
|
| 179 |
+
tok+=len(docs[-1])//4
|
| 180 |
+
if tok>=cap_tok: break
|
| 181 |
+
except Exception as e: print(" prose dolly:",str(e)[:80])
|
| 182 |
+
return docs
|
| 183 |
+
|
| 184 |
+
SOURCES={"code":lambda b:src_code_local(int(b*0.35))+src_code_hf(int(b*0.65)),
|
| 185 |
+
"agentic":src_agentic,"instruction":src_instruction,
|
| 186 |
+
"medical":src_medical,"prose":src_prose}
|
| 187 |
+
|
| 188 |
+
# ---------------- split + write ----------------
|
| 189 |
+
def _key(c): return hashlib.md5(c[:512].encode()).hexdigest()
|
| 190 |
+
|
| 191 |
+
def build():
|
| 192 |
+
os.makedirs(RAW,exist_ok=True)
|
| 193 |
+
train,pv,ppl = [],[],[] # (text,cat)
|
| 194 |
+
seen=set()
|
| 195 |
+
for cat,frac in MIX.items():
|
| 196 |
+
budget=int(TRAIN_TOK*frac / 0.8) # +25% headroom for the held-out slices
|
| 197 |
+
docs=SOURCES[cat](budget)
|
| 198 |
+
chunks=pack(docs, budget) # pack short docs; split long ones
|
| 199 |
+
# dedup within category + globally
|
| 200 |
+
uniq=[]
|
| 201 |
+
for c in chunks:
|
| 202 |
+
k=_key(c)
|
| 203 |
+
if k in seen: continue
|
| 204 |
+
seen.add(k); uniq.append(c)
|
| 205 |
+
random.Random(hash(cat)&0xffff).shuffle(uniq)
|
| 206 |
+
# allocate: ppl first, then pv, rest train (disjoint by slice)
|
| 207 |
+
cppl=uniq[:PPL_CHUNKS_PER_CAT]
|
| 208 |
+
cpv =uniq[PPL_CHUNKS_PER_CAT:PPL_CHUNKS_PER_CAT+PV_CHUNKS_PER_CAT]
|
| 209 |
+
ctr =uniq[PPL_CHUNKS_PER_CAT+PV_CHUNKS_PER_CAT:]
|
| 210 |
+
ppl += [(c,cat) for c in cppl]; pv += [(c,cat) for c in cpv]; train += [(c,cat) for c in ctr]
|
| 211 |
+
tt=sum(len(c)//4 for c in ctr)
|
| 212 |
+
print(f" {cat:12s}: train {len(ctr)} (~{tt:,} tok) | pv {len(cpv)} | ppl {len(cppl)}")
|
| 213 |
+
random.shuffle(train); random.shuffle(pv)
|
| 214 |
+
def w(path, items, with_cat=False):
|
| 215 |
+
with open(path,"w") as f:
|
| 216 |
+
for c,cat in items:
|
| 217 |
+
f.write(json.dumps({"text":c,**({"cat":cat} if with_cat else {})})+"\n")
|
| 218 |
+
print(f" wrote {path}: {len(items)} chunks (~{sum(len(c)//4 for c,_ in items):,} tok)")
|
| 219 |
+
w(f"{OUT}/calib_v3.jsonl", train)
|
| 220 |
+
w(f"{OUT}/heldout_v3.jsonl", pv)
|
| 221 |
+
w(f"{OUT}/ppl_v3.jsonl", ppl, with_cat=True)
|
| 222 |
+
# disjointness assertion
|
| 223 |
+
def keys(p): return {_key(json.loads(l)["text"]) for l in open(p)}
|
| 224 |
+
kC,kH,kP=keys(f"{OUT}/calib_v3.jsonl"),keys(f"{OUT}/heldout_v3.jsonl"),keys(f"{OUT}/ppl_v3.jsonl")
|
| 225 |
+
print(f"\n overlap calib∩pv={len(kC&kH)} calib∩ppl={len(kC&kP)} pv∩ppl={len(kH&kP)} (all must be 0)")
|
| 226 |
+
from collections import Counter
|
| 227 |
+
print(" ppl per-category:", dict(Counter(cat for _,cat in ppl)))
|
| 228 |
+
|
| 229 |
+
if __name__=="__main__":
|
| 230 |
+
print(">>> building BROAD v3 corpus (GLM-style mix) ...")
|
| 231 |
+
build()
|
| 232 |
+
print(">>> done.")
|
code/build_gate_ckpt.py
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Build the 512k QUALITY-GATE checkpoint: a standard ModelOpt-NVFP4 checkpoint
|
| 3 |
+
identical to /data/inkling-nvfp4 EXCEPT COLD experts (per assign_512k.json) have
|
| 4 |
+
their fused w13/w2 weights REPLACED by re-quantizing the AQLM parts to NVFP4
|
| 5 |
+
(dequant AQLM->bf16->NVFP4). HOT experts keep their original Inkling-NVFP4 values.
|
| 6 |
+
Loads on the existing vLLM NVFP4 path (no custom kernel).
|
| 7 |
+
w13 <- converged parts (parts_conv), w2 <- encode parts (parts).
|
| 8 |
+
|
| 9 |
+
The three tensors of each fused expert group -- packed `...experts.{w13,w2}_weight`,
|
| 10 |
+
`.scale`, `.scale2` -- live in DIFFERENT safetensors shards (per weight_map).
|
| 11 |
+
Two phases:
|
| 12 |
+
phase1 (GPU 4-7, parallel): requant every cold expert ONCE per (layer,proj);
|
| 13 |
+
write compact per-group cache files (cold rows only). Resumable.
|
| 14 |
+
phase2 (CPU): stream each source shard, overwrite the cold-expert slices of any
|
| 15 |
+
expert packed/.scale/.scale2 tensor present, copy everything else
|
| 16 |
+
verbatim, keep the exact same shard layout. Resumable per shard.
|
| 17 |
+
Out: /data/inkling-512k-gate/ (~592GB).
|
| 18 |
+
"""
|
| 19 |
+
import argparse, json, os, re, shutil
|
| 20 |
+
import torch
|
| 21 |
+
from safetensors import safe_open
|
| 22 |
+
from safetensors.torch import save_file
|
| 23 |
+
import ink_common as ic
|
| 24 |
+
|
| 25 |
+
SRC = ic.NVFP4_DIR
|
| 26 |
+
# env-overridable so the SAME builder makes v1 / v2 / v3 gate proxies:
|
| 27 |
+
# v1: W13_PARTS=parts_conv W2_PARTS=parts ASSIGN=assign_512k.json
|
| 28 |
+
# v2: W13_PARTS=parts_conv W2_PARTS=parts_conv ASSIGN=assign_512k_v2.json GATE_OUT=/data/inkling-512k-gate-v2
|
| 29 |
+
# v3: W13_PARTS=parts_pv_v3 W2_PARTS=parts_pv_v3 ASSIGN=assign_512k_v2.json GATE_OUT=/data/inkling-512k-gate-v3
|
| 30 |
+
OUT = os.environ.get("GATE_OUT", "/data/inkling-512k-gate")
|
| 31 |
+
W13_PARTS = os.environ.get("W13_PARTS", "/data/inkling-quant/parts_conv") # w13 source
|
| 32 |
+
W2_PARTS = os.environ.get("W2_PARTS", "/data/inkling-quant/parts") # w2 source
|
| 33 |
+
CONV = W13_PARTS; PARTS = W2_PARTS # back-compat aliases
|
| 34 |
+
ASSIGN = os.environ.get("ASSIGN", "/data/inkling-quant/assign_512k.json")
|
| 35 |
+
CACHE = os.environ.get("REQUANT_CACHE", "/data/inkling-quant/tools/requant_cache")
|
| 36 |
+
|
| 37 |
+
# packed `..._weight`, or `.scale`, or `.scale2` of a fused MoE expert group
|
| 38 |
+
EXP_RE = re.compile(r"model\.llm\.layers\.(\d+)\.mlp\.experts\.(w13|w2)_weight(\.scale2|\.scale)?$")
|
| 39 |
+
SUF2KEY = {"": "packed", ".scale": "scale", ".scale2": "scale2"}
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def cold_map():
|
| 43 |
+
a = json.load(open(ASSIGN))["hot_experts"]
|
| 44 |
+
return {int(L): [e for e in range(ic.N_EXPERTS) if e not in set(hot)]
|
| 45 |
+
for L, hot in a.items()}
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def full_layers():
|
| 49 |
+
"""MoE layers stored full-precision (bf16, NOT NVFP4-packed) in the base
|
| 50 |
+
checkpoint -> identified by the absence of a `...w13_weight.scale` tensor.
|
| 51 |
+
In Inkling-NVFP4 this is layer 2 (the first MoE layer kept high precision)."""
|
| 52 |
+
wm = json.load(open(f"{SRC}/model.safetensors.index.json"))["weight_map"]
|
| 53 |
+
fl = set()
|
| 54 |
+
for L in ic.moe_layers():
|
| 55 |
+
if f"model.llm.layers.{L}.mlp.experts.w13_weight.scale" not in wm:
|
| 56 |
+
fl.add(L)
|
| 57 |
+
return fl
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def cache_path(L, proj):
|
| 61 |
+
return f"{CACHE}/L{L}_{proj}.pt"
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
# ---------------------------------------------------------------- phase 1 (GPU)
|
| 65 |
+
def phase1(layers, dev="cuda"):
|
| 66 |
+
cold = cold_map()
|
| 67 |
+
full = full_layers()
|
| 68 |
+
os.makedirs(CACHE, exist_ok=True)
|
| 69 |
+
for L in layers:
|
| 70 |
+
for proj in ("w13", "w2"):
|
| 71 |
+
cp = cache_path(L, proj)
|
| 72 |
+
if os.path.exists(cp):
|
| 73 |
+
print(f"[gpu] skip cache L{L} {proj} (exists)", flush=True)
|
| 74 |
+
continue
|
| 75 |
+
d = CONV if proj == "w13" else PARTS
|
| 76 |
+
part = torch.load(f"{d}/layer_{L}_{proj}.pt", map_location="cpu")
|
| 77 |
+
ce = cold[L]
|
| 78 |
+
if L in full:
|
| 79 |
+
# full-precision (bf16) layer: cache AQLM dequant as bf16, no NVFP4
|
| 80 |
+
wl = [ic.aqlm_dequant_expert(part, proj, e, device=dev).to(torch.bfloat16).cpu()
|
| 81 |
+
for e in ce]
|
| 82 |
+
out = {"cold": torch.tensor(ce, dtype=torch.long),
|
| 83 |
+
"weight": torch.stack(wl)}
|
| 84 |
+
del wl
|
| 85 |
+
tag = "bf16"
|
| 86 |
+
else:
|
| 87 |
+
pk_l, sf_l, s2_l = [], [], []
|
| 88 |
+
for e in ce:
|
| 89 |
+
W = ic.aqlm_dequant_expert(part, proj, e, device=dev).float()
|
| 90 |
+
pk, sf8, s2 = ic.quantize_nvfp4(W)
|
| 91 |
+
pk_l.append(pk.cpu()); sf_l.append(sf8.cpu()); s2_l.append(s2.cpu().reshape(()))
|
| 92 |
+
out = {"cold": torch.tensor(ce, dtype=torch.long),
|
| 93 |
+
"packed": torch.stack(pk_l),
|
| 94 |
+
"scale": torch.stack(sf_l),
|
| 95 |
+
"scale2": torch.stack(s2_l)}
|
| 96 |
+
del pk_l, sf_l, s2_l
|
| 97 |
+
tag = "nvfp4"
|
| 98 |
+
tmp = cp + ".tmp"
|
| 99 |
+
torch.save(out, tmp); os.replace(tmp, cp)
|
| 100 |
+
del part, out
|
| 101 |
+
print(f"[gpu] cached L{L} {proj} ({len(ce)} cold experts, {tag})", flush=True)
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
# ---------------------------------------------------------------- phase 2 (CPU)
|
| 105 |
+
def phase2():
|
| 106 |
+
os.makedirs(OUT, exist_ok=True)
|
| 107 |
+
cold = cold_map()
|
| 108 |
+
idx = json.load(open(f"{SRC}/model.safetensors.index.json"))
|
| 109 |
+
wm = idx["weight_map"]
|
| 110 |
+
shards = sorted(set(wm.values()))
|
| 111 |
+
for si, shard in enumerate(shards):
|
| 112 |
+
outp = f"{OUT}/{shard}"
|
| 113 |
+
if os.path.exists(outp):
|
| 114 |
+
print(f"[{si+1}/{len(shards)}] skip {shard} (exists)", flush=True); continue
|
| 115 |
+
tensors, meta = {}, {}
|
| 116 |
+
with safe_open(f"{SRC}/{shard}", framework="pt") as f:
|
| 117 |
+
meta = f.metadata() or {}
|
| 118 |
+
for k in f.keys():
|
| 119 |
+
tensors[k] = f.get_tensor(k)
|
| 120 |
+
gcache = {} # (L,proj) -> loaded cache dict; scoped to this shard
|
| 121 |
+
|
| 122 |
+
def gload(L, proj):
|
| 123 |
+
key = (L, proj)
|
| 124 |
+
if key not in gcache:
|
| 125 |
+
gcache[key] = torch.load(cache_path(L, proj), map_location="cpu")
|
| 126 |
+
return gcache[key]
|
| 127 |
+
|
| 128 |
+
changed = 0
|
| 129 |
+
for k in list(tensors.keys()):
|
| 130 |
+
m = EXP_RE.match(k)
|
| 131 |
+
if not m:
|
| 132 |
+
continue
|
| 133 |
+
L = int(m.group(1)); proj = m.group(2); suf = m.group(3) or ""
|
| 134 |
+
if L not in cold:
|
| 135 |
+
continue
|
| 136 |
+
g = gload(L, proj)
|
| 137 |
+
ce = g["cold"]
|
| 138 |
+
if "weight" in g: # full-precision (bf16) layer
|
| 139 |
+
assert suf == "", f"unexpected aux tensor {k} on a bf16 layer"
|
| 140 |
+
src = g["weight"]
|
| 141 |
+
else: # NVFP4-packed layer
|
| 142 |
+
src = g[SUF2KEY[suf]]
|
| 143 |
+
t = tensors[k]
|
| 144 |
+
t[ce] = src.to(t.dtype) # overwrite cold rows (cold-ordered) in place
|
| 145 |
+
tensors[k] = t
|
| 146 |
+
changed += 1
|
| 147 |
+
tmp = outp + ".tmp"
|
| 148 |
+
save_file(tensors, tmp, metadata=meta or {"format": "pt"})
|
| 149 |
+
os.replace(tmp, outp)
|
| 150 |
+
del tensors, gcache
|
| 151 |
+
print(f"[{si+1}/{len(shards)}] {shard}: {changed} expert tensors patched -> saved", flush=True)
|
| 152 |
+
|
| 153 |
+
# copy every non-safetensors file (config, tokenizer, index, hf_quant_config, ...)
|
| 154 |
+
# incl. dirs like tiktoken; skip the HF download cache dir.
|
| 155 |
+
SKIP = {".cache"}
|
| 156 |
+
for fn in os.listdir(SRC):
|
| 157 |
+
if fn.endswith(".safetensors") or fn in SKIP:
|
| 158 |
+
continue
|
| 159 |
+
src, dst = f"{SRC}/{fn}", f"{OUT}/{fn}"
|
| 160 |
+
if os.path.exists(dst):
|
| 161 |
+
continue
|
| 162 |
+
if os.path.isdir(src):
|
| 163 |
+
shutil.copytree(src, dst)
|
| 164 |
+
else:
|
| 165 |
+
shutil.copy2(src, dst)
|
| 166 |
+
print("PHASE2 DONE", flush=True)
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
def main():
|
| 170 |
+
ap = argparse.ArgumentParser()
|
| 171 |
+
ap.add_argument("--mode", required=True, choices=["phase1", "phase2"])
|
| 172 |
+
ap.add_argument("--worker", type=int, default=0)
|
| 173 |
+
ap.add_argument("--nworkers", type=int, default=1)
|
| 174 |
+
ap.add_argument("--device", default="cuda")
|
| 175 |
+
a = ap.parse_args()
|
| 176 |
+
if a.mode == "phase1":
|
| 177 |
+
layers = ic.moe_layers()[a.worker::a.nworkers]
|
| 178 |
+
print(f"[gpu] worker {a.worker}/{a.nworkers} layers: {layers}", flush=True)
|
| 179 |
+
phase1(layers, a.device)
|
| 180 |
+
else:
|
| 181 |
+
phase2()
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
if __name__ == "__main__":
|
| 185 |
+
main()
|
code/build_heldout.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Build a HELD-OUT text set DISJOINT from calib.jsonl for PV-generalization eval.
|
| 3 |
+
|
| 4 |
+
Sources are deliberately DIFFERENT from calib's (calib = /data/vllm-inkling vllm
|
| 5 |
+
python/rust src, /data/vllm-inkling docs md, /data/glm52/code md, + 10 synthetic
|
| 6 |
+
prompts). Held-out uses:
|
| 7 |
+
- code: /home/coder/Antidoom/antidoom/src/**/*.py (different repo, NOT vllm)
|
| 8 |
+
- prose: /data/inkling-recon/RECON.md, /data/inkling-nvfp4/README.md,
|
| 9 |
+
/data/inkling-512k-hybrid/README.md, /data/glm52-*/README.md
|
| 10 |
+
- fresh synthetic chat/instruction prompts (distinct topics from calib's 10)
|
| 11 |
+
Then VERIFIES every held-out chunk is disjoint from calib (no held-out chunk is a
|
| 12 |
+
substring of any calib chunk, and no long shared window) and drops any that overlap.
|
| 13 |
+
Output: /data/inkling-quant/heldout.jsonl (~48 {"text": ...} chunks).
|
| 14 |
+
"""
|
| 15 |
+
import json, glob, random
|
| 16 |
+
random.seed(7)
|
| 17 |
+
OUT = "/data/inkling-quant/heldout.jsonl"
|
| 18 |
+
CALIB = "/data/inkling-quant/calib.jsonl"
|
| 19 |
+
CHUNK = 6000
|
| 20 |
+
chunks = []
|
| 21 |
+
|
| 22 |
+
def add_files(patterns, cap, per_file_cap=3):
|
| 23 |
+
got = 0; files = []
|
| 24 |
+
for p in patterns:
|
| 25 |
+
files += glob.glob(p, recursive=True)
|
| 26 |
+
random.shuffle(files)
|
| 27 |
+
for f in files:
|
| 28 |
+
if got >= cap: break
|
| 29 |
+
try:
|
| 30 |
+
t = open(f, errors="ignore").read()
|
| 31 |
+
except Exception:
|
| 32 |
+
continue
|
| 33 |
+
if len(t) < 500: continue
|
| 34 |
+
n_here = 0
|
| 35 |
+
for i in range(0, len(t), CHUNK):
|
| 36 |
+
c = t[i:i+CHUNK]
|
| 37 |
+
if len(c) >= 1000:
|
| 38 |
+
chunks.append(c); got += 1; n_here += 1
|
| 39 |
+
if got >= cap or n_here >= per_file_cap: break
|
| 40 |
+
|
| 41 |
+
# code from a DIFFERENT repo than calib (antidoom, not vllm)
|
| 42 |
+
add_files(["/home/coder/Antidoom/antidoom/src/**/*.py"], 22, per_file_cap=3)
|
| 43 |
+
# prose/docs NOT in calib globs
|
| 44 |
+
add_files(["/data/inkling-recon/RECON.md", "/data/inkling-nvfp4/README.md",
|
| 45 |
+
"/data/inkling-512k-hybrid/README.md", "/data/glm52-*/README.md",
|
| 46 |
+
"/data/glm52-fp8o/README.md"], 14, per_file_cap=2)
|
| 47 |
+
# fresh synthetic prompts (distinct domains from calib's 10)
|
| 48 |
+
syn = [
|
| 49 |
+
"Describe the lifecycle of a star from nebula to supernova, and what determines its final state.",
|
| 50 |
+
"Write a Python function that merges two sorted linked lists into one sorted list, with comments.",
|
| 51 |
+
"A customer emails that their package arrived damaged. Draft a professional, empathetic reply offering a resolution.",
|
| 52 |
+
"Explain the Byzantine Generals Problem and how blockchain consensus addresses it.",
|
| 53 |
+
"Compose a limerick about a cat who refuses to use its expensive bed.",
|
| 54 |
+
"What are the trade-offs between microservices and a monolithic architecture for a small startup?",
|
| 55 |
+
"Outline a 30-minute beginner yoga routine, naming each pose and its benefit.",
|
| 56 |
+
"Explain how vaccines train the adaptive immune system, appropriate for a curious teenager.",
|
| 57 |
+
"Given a noisy time series, describe how a Kalman filter estimates the underlying state.",
|
| 58 |
+
"Write a SQL query to find the second-highest salary in an employees table, and explain it.",
|
| 59 |
+
"Discuss the ecological role of mycorrhizal fungi in forest ecosystems.",
|
| 60 |
+
"Draft a short cover letter for a data-analyst role emphasizing curiosity and rigor.",
|
| 61 |
+
]
|
| 62 |
+
for _ in range(3):
|
| 63 |
+
for p in syn:
|
| 64 |
+
chunks.append(p + "\n\n" + random.choice(syn) + "\n\n" + random.choice(syn))
|
| 65 |
+
|
| 66 |
+
# --- disjointness verification vs calib ---
|
| 67 |
+
calib_chunks = [json.loads(l)["text"] for l in open(CALIB)]
|
| 68 |
+
calib_blob = "\n".join(calib_chunks)
|
| 69 |
+
|
| 70 |
+
def overlaps_calib(c, win=200, step=100):
|
| 71 |
+
# substring containment either way
|
| 72 |
+
if c in calib_blob:
|
| 73 |
+
return True
|
| 74 |
+
for cc in calib_chunks:
|
| 75 |
+
if cc in c:
|
| 76 |
+
return True
|
| 77 |
+
# long shared window
|
| 78 |
+
for i in range(0, max(1, len(c) - win), step):
|
| 79 |
+
if c[i:i+win] in calib_blob:
|
| 80 |
+
return True
|
| 81 |
+
return False
|
| 82 |
+
|
| 83 |
+
random.shuffle(chunks)
|
| 84 |
+
kept, dropped = [], 0
|
| 85 |
+
for c in chunks:
|
| 86 |
+
if overlaps_calib(c):
|
| 87 |
+
dropped += 1; continue
|
| 88 |
+
kept.append(c)
|
| 89 |
+
if len(kept) >= 48:
|
| 90 |
+
break
|
| 91 |
+
|
| 92 |
+
with open(OUT, "w") as fh:
|
| 93 |
+
for c in kept:
|
| 94 |
+
fh.write(json.dumps({"text": c}) + "\n")
|
| 95 |
+
print(f"wrote {len(kept)} held-out chunks (dropped {dropped} for calib overlap) -> {OUT}")
|
| 96 |
+
print(f"~{sum(len(c) for c in kept)//4} tokens est; calib has {len(calib_chunks)} chunks")
|
code/build_hybrid.py
ADDED
|
@@ -0,0 +1,448 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Assemble the COMPRESSED 512k hybrid checkpoint (the real memory-efficient
|
| 3 |
+
artifact the custom kernel will run). NO requant: cold experts are already AQLM
|
| 4 |
+
(parts_conv w13 / parts w2), hot are already NVFP4 (inkling-nvfp4). Pure repackage.
|
| 5 |
+
|
| 6 |
+
FORMAT (per NVFP4-packed MoE layer L; layer 2 is bf16-special -> copied as-is):
|
| 7 |
+
experts.{w13,w2}_hot_weight u8 [n_hot, out, in/2] (NVFP4 packed)
|
| 8 |
+
experts.{w13,w2}_hot_weight.scale f8e4m3 [n_hot, out, in/16]
|
| 9 |
+
experts.{w13,w2}_hot_weight.scale2 f32 [n_hot]
|
| 10 |
+
experts.{w13,w2}_cold_codes int16 [n_cold, books, out, in/8] (AQLM)
|
| 11 |
+
experts.{w13,w2}_cold_codebook.{b} f16 [entries_b, 8] (w13: 1 book@65536; w2: 65536 + 256)
|
| 12 |
+
experts.{w13,w2}_cold_scales f16 [n_cold, out]
|
| 13 |
+
experts.hot_ids / experts.cold_ids int32
|
| 14 |
+
All NON-expert tensors (attn, embed, norms, shared_experts, gate, vision, audio,
|
| 15 |
+
mtp, layer-2 experts) copied verbatim from inkling-nvfp4.
|
| 16 |
+
Config: hf_quant_config.json extended with an 'aqlm_hybrid' block giving per-layer
|
| 17 |
+
{n_nvfp4, n_cold, w13_books, w2_books, entries} + the assignment. See FORMAT in README.
|
| 18 |
+
"""
|
| 19 |
+
import argparse, json, os, re
|
| 20 |
+
import torch
|
| 21 |
+
from safetensors import safe_open
|
| 22 |
+
from safetensors.torch import save_file
|
| 23 |
+
import ink_common as ic
|
| 24 |
+
|
| 25 |
+
SRC = ic.NVFP4_DIR
|
| 26 |
+
# env-overridable for v3: HYBRID_OUT + COLD_PARTS (cold w13+w2 source) + ASSIGN
|
| 27 |
+
# v2: COLD_PARTS=parts_conv HYBRID_OUT=/data/inkling-512k-hybrid-v2
|
| 28 |
+
# v3: COLD_PARTS=parts_pv_v3 HYBRID_OUT=/data/inkling-512k-hybrid-v3
|
| 29 |
+
OUT = os.environ.get("HYBRID_OUT", "/data/inkling-512k-hybrid-v2")
|
| 30 |
+
CONV = os.environ.get("COLD_PARTS", "/data/inkling-quant/parts_conv")
|
| 31 |
+
PARTS = CONV
|
| 32 |
+
ASSIGN = os.environ.get("ASSIGN", "/data/inkling-quant/assign_512k_v2.json")
|
| 33 |
+
SHARD_BYTES = 8 * 2**30
|
| 34 |
+
|
| 35 |
+
def hot_map():
|
| 36 |
+
a = json.load(open(ASSIGN))["hot_experts"]
|
| 37 |
+
return {int(L): sorted(v) for L, v in a.items()}
|
| 38 |
+
|
| 39 |
+
def packed_layers():
|
| 40 |
+
idx = json.load(open(f"{SRC}/model.safetensors.index.json"))["weight_map"]
|
| 41 |
+
ls = set()
|
| 42 |
+
for k in idx:
|
| 43 |
+
m = re.match(r"model\.llm\.layers\.(\d+)\.mlp\.experts\.w13_weight\.scale$", k)
|
| 44 |
+
if m: ls.add(int(m.group(1)))
|
| 45 |
+
return ls
|
| 46 |
+
|
| 47 |
+
EXP_ANY = re.compile(r"model\.llm\.layers\.(\d+)\.mlp\.experts\.(w13|w2)_weight(\.scale2|\.scale)?$")
|
| 48 |
+
|
| 49 |
+
def book_code_dtype(entries):
|
| 50 |
+
"""Smallest int dtype that losslessly holds this book's codes.
|
| 51 |
+
<=256 entries -> uint8 (codes 0..255); otherwise int16 (65536-entry books
|
| 52 |
+
use int16 with the negative-wrap convention for rows >= 32768)."""
|
| 53 |
+
return torch.uint8 if entries <= 256 else torch.int16
|
| 54 |
+
|
| 55 |
+
def build_layer_tensors(L, hot, nv):
|
| 56 |
+
"""Hybrid expert tensors for one MoE layer.
|
| 57 |
+
Branches on the source format: NVFP4-packed layers keep packed uint8 hot
|
| 58 |
+
slabs (+scale/scale2); the bf16-special layer (layer 2, no NVFP4 base) keeps
|
| 59 |
+
bf16 hot slabs (no scales). Cold experts are AQLM in both cases, stored as
|
| 60 |
+
ONE code tensor per book with the smallest lossless dtype."""
|
| 61 |
+
out = {}
|
| 62 |
+
hids = hot[L]; cids = [e for e in range(ic.N_EXPERTS) if e not in set(hids)]
|
| 63 |
+
hidx = torch.tensor(hids); cidx = torch.tensor(cids)
|
| 64 |
+
for proj in ("w13", "w2"):
|
| 65 |
+
base = f"model.llm.layers.{L}.mlp.experts.{proj}"
|
| 66 |
+
is_nvfp4 = nv.has(f"{base}_weight.scale")
|
| 67 |
+
if is_nvfp4:
|
| 68 |
+
# HOT: NVFP4 packed slabs sliced verbatim.
|
| 69 |
+
packed = nv.get(f"{base}_weight"); scale = nv.get(f"{base}_weight.scale"); scale2 = nv.get(f"{base}_weight.scale2")
|
| 70 |
+
out[f"{base}_hot_weight"] = packed[hidx].contiguous()
|
| 71 |
+
out[f"{base}_hot_weight.scale"] = scale[hidx].contiguous()
|
| 72 |
+
out[f"{base}_hot_weight.scale2"] = scale2[hidx].contiguous()
|
| 73 |
+
else:
|
| 74 |
+
# HOT (layer 2): bf16 slabs gathered from the fused bf16 tensor, NO scales.
|
| 75 |
+
fused = nv.get(f"{base}_weight") # bf16 [E, out, in]
|
| 76 |
+
out[f"{base}_hot_bf16"] = fused[hidx].contiguous().to(torch.bfloat16)
|
| 77 |
+
# COLD: AQLM, one code tensor per book with minimal lossless dtype.
|
| 78 |
+
part = torch.load(f"{CONV}/layer_{L}_{proj}.pt", map_location="cpu") # v2: both w13+w2 converged
|
| 79 |
+
codes = part[f"{proj}_codes"][cidx] # [n_cold, books, out, ng] int16
|
| 80 |
+
for b, cb in enumerate(part[f"{proj}_codebooks"]):
|
| 81 |
+
dt = book_code_dtype(cb.shape[0])
|
| 82 |
+
out[f"{base}_cold_codes.{b}"] = codes[:, b].to(dt).contiguous()
|
| 83 |
+
out[f"{base}_cold_codebook.{b}"] = cb.contiguous()
|
| 84 |
+
out[f"{base}_cold_scales"] = part[f"{proj}_scales"][cidx].contiguous()
|
| 85 |
+
out[f"model.llm.layers.{L}.mlp.experts.hot_ids"] = torch.tensor(hids, dtype=torch.int32)
|
| 86 |
+
out[f"model.llm.layers.{L}.mlp.experts.cold_ids"] = torch.tensor(cids, dtype=torch.int32)
|
| 87 |
+
return out
|
| 88 |
+
|
| 89 |
+
def _dtype_str(dt):
|
| 90 |
+
return str(dt).split(".")[-1]
|
| 91 |
+
|
| 92 |
+
def make_hybrid_config(hot, packed):
|
| 93 |
+
part_w13 = torch.load(f"{CONV}/layer_{min(packed)}_w13.pt", map_location="cpu")
|
| 94 |
+
part_w2 = torch.load(f"{CONV}/layer_{min(packed)}_w2.pt", map_location="cpu")
|
| 95 |
+
books = {"w13": [c.shape[0] for c in part_w13["w13_codebooks"]],
|
| 96 |
+
"w2": [c.shape[0] for c in part_w2["w2_codebooks"]]}
|
| 97 |
+
code_dtypes = {p: [_dtype_str(book_code_dtype(e)) for e in books[p]] for p in ("w13", "w2")}
|
| 98 |
+
layer_books = {}
|
| 99 |
+
for L in sorted(hot):
|
| 100 |
+
nhot = len(hot[L]); ncold = ic.N_EXPERTS - nhot
|
| 101 |
+
packed_flag = L in packed
|
| 102 |
+
layer_books[str(L)] = {"n_hot": nhot, "n_nvfp4": nhot, "n_cold": ncold,
|
| 103 |
+
"packed": packed_flag,
|
| 104 |
+
"hot_format": "nvfp4" if packed_flag else "bf16"}
|
| 105 |
+
return {
|
| 106 |
+
"quant_method": "inkling_nvfp4_aqlm_hybrid",
|
| 107 |
+
"base_nvfp4": "thinkingmachines/Inkling-NVFP4",
|
| 108 |
+
"group_size": 8,
|
| 109 |
+
"w13_book_entries": books["w13"], "w2_book_entries": books["w2"],
|
| 110 |
+
"w13_code_dtypes": code_dtypes["w13"], "w2_code_dtypes": code_dtypes["w2"],
|
| 111 |
+
"cold_code_layout": "per-book tensors {proj}_cold_codes.{b}, dtype uint8 if book<=256 entries else int16",
|
| 112 |
+
"aqlm_layer_books": layer_books,
|
| 113 |
+
"note": "cold experts: AQLM, per-book code tensors {proj}_cold_codes.{b} (w2 book0=int16@65536, "
|
| 114 |
+
"book1=uint8@256; w13 book0=int16@65536) + per-book codebooks + per-row scales. "
|
| 115 |
+
"hot experts: NVFP4 (hot_format=nvfp4) except layer 2 which has NO NVFP4 base and stores "
|
| 116 |
+
"bf16 hot slabs {proj}_hot_bf16 (no scale/scale2, hot_format=bf16). Its cold experts are "
|
| 117 |
+
"AQLM like every other layer. hot_ids/cold_ids give the routed-expert index for each slab.",
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
# ---------------------------------------------------------------------------
|
| 121 |
+
# Full-checkpoint assembly (streaming, resumable, CPU/IO only).
|
| 122 |
+
# ---------------------------------------------------------------------------
|
| 123 |
+
import struct, shutil
|
| 124 |
+
|
| 125 |
+
def is_dropped(name, built):
|
| 126 |
+
"""True iff `name` is a fused MoE-expert weight/scale/scale2 of a layer that is
|
| 127 |
+
rebuilt as a hybrid layer (every MoE layer, incl. layer 2's bf16 experts).
|
| 128 |
+
input_amax/original_shape are NOT matched by EXP_ANY, so they are kept."""
|
| 129 |
+
m = EXP_ANY.match(name)
|
| 130 |
+
return bool(m) and int(m.group(1)) in built
|
| 131 |
+
|
| 132 |
+
def _read_st_header(path):
|
| 133 |
+
with open(path, "rb") as f:
|
| 134 |
+
n = struct.unpack("<Q", f.read(8))[0]
|
| 135 |
+
h = json.loads(f.read(n))
|
| 136 |
+
h.pop("__metadata__", None)
|
| 137 |
+
return h
|
| 138 |
+
|
| 139 |
+
class LayerCache:
|
| 140 |
+
"""Builds one packed layer's hybrid tensors on demand, caching the most recent."""
|
| 141 |
+
def __init__(self, hot, nv):
|
| 142 |
+
self.hot = hot; self.nv = nv; self._L = None; self._t = None
|
| 143 |
+
def get(self, L):
|
| 144 |
+
if self._L != L:
|
| 145 |
+
self._t = build_layer_tensors(L, self.hot, self.nv)
|
| 146 |
+
self._L = L
|
| 147 |
+
return self._t
|
| 148 |
+
|
| 149 |
+
def plan_tensors(hot, built):
|
| 150 |
+
"""Return ordered list of plan entries + weight_map skeleton.
|
| 151 |
+
entry = {name, nbytes, kind:'src'|'hyb', src_shard?, layer?}.
|
| 152 |
+
`built` = every MoE layer rebuilt as a hybrid layer (packed NVFP4 + layer 2)."""
|
| 153 |
+
src_idx = json.load(open(f"{SRC}/model.safetensors.index.json"))["weight_map"]
|
| 154 |
+
# per-shard header cache for exact source byte sizes
|
| 155 |
+
hdr_cache = {}
|
| 156 |
+
def src_nbytes(name, shard):
|
| 157 |
+
if shard not in hdr_cache:
|
| 158 |
+
hdr_cache[shard] = _read_st_header(f"{SRC}/{shard}")
|
| 159 |
+
o = hdr_cache[shard][name]["data_offsets"]
|
| 160 |
+
return o[1] - o[0]
|
| 161 |
+
|
| 162 |
+
plan = []
|
| 163 |
+
# 1) all kept source tensors, in source weight_map order
|
| 164 |
+
for name, shard in src_idx.items():
|
| 165 |
+
if is_dropped(name, built):
|
| 166 |
+
continue
|
| 167 |
+
plan.append({"name": name, "nbytes": src_nbytes(name, shard),
|
| 168 |
+
"kind": "src", "src_shard": shard})
|
| 169 |
+
# 2) hybrid expert tensors per rebuilt layer (build each once for exact sizes)
|
| 170 |
+
nv = ic.ShardReader(SRC)
|
| 171 |
+
for L in sorted(built):
|
| 172 |
+
t = build_layer_tensors(L, hot, nv)
|
| 173 |
+
for name, v in t.items():
|
| 174 |
+
plan.append({"name": name, "nbytes": v.numel() * v.element_size(),
|
| 175 |
+
"kind": "hyb", "layer": L})
|
| 176 |
+
del t
|
| 177 |
+
return plan
|
| 178 |
+
|
| 179 |
+
def bin_pack(plan, shard_bytes):
|
| 180 |
+
"""Greedy pack preserving order; a tensor larger than shard_bytes gets its own shard."""
|
| 181 |
+
shards = []; cur = []; cur_b = 0
|
| 182 |
+
for e in plan:
|
| 183 |
+
if cur and cur_b + e["nbytes"] > shard_bytes:
|
| 184 |
+
shards.append(cur); cur = []; cur_b = 0
|
| 185 |
+
cur.append(e); cur_b += e["nbytes"]
|
| 186 |
+
if cur:
|
| 187 |
+
shards.append(cur)
|
| 188 |
+
return shards
|
| 189 |
+
|
| 190 |
+
def assemble():
|
| 191 |
+
os.makedirs(OUT, exist_ok=True)
|
| 192 |
+
hot = hot_map(); packed = packed_layers()
|
| 193 |
+
built = set(hot) # every MoE layer is now rebuilt as a hybrid layer
|
| 194 |
+
bf16 = sorted(built - packed) # layer(s) with no NVFP4 base -> bf16 hot slabs
|
| 195 |
+
print(f"[plan] hybrid MoE layers: {len(built)} (NVFP4-packed: {len(packed)}, bf16-hot: {bf16})")
|
| 196 |
+
plan = plan_tensors(hot, built)
|
| 197 |
+
total = sum(e["nbytes"] for e in plan)
|
| 198 |
+
print(f"[plan] {len(plan)} output tensors, total {total/1e9:.1f} GB")
|
| 199 |
+
shards = bin_pack(plan, SHARD_BYTES)
|
| 200 |
+
NN = len(shards)
|
| 201 |
+
print(f"[plan] {NN} output shards (~{SHARD_BYTES/2**30:.0f}GB target)")
|
| 202 |
+
|
| 203 |
+
def shard_name(i):
|
| 204 |
+
return f"model-{i:05d}-of-{NN:05d}.safetensors"
|
| 205 |
+
|
| 206 |
+
# index.json (deterministic from plan)
|
| 207 |
+
weight_map = {}
|
| 208 |
+
for i, sh in enumerate(shards, 1):
|
| 209 |
+
for e in sh:
|
| 210 |
+
weight_map[e["name"]] = shard_name(i)
|
| 211 |
+
idx = {"metadata": {"total_size": total}, "weight_map": weight_map}
|
| 212 |
+
with open(f"{OUT}/model.safetensors.index.json", "w") as f:
|
| 213 |
+
json.dump(idx, f, indent=2)
|
| 214 |
+
print(f"[index] wrote model.safetensors.index.json ({len(weight_map)} tensors)")
|
| 215 |
+
|
| 216 |
+
# write shards (resumable: fully-written shards get renamed from .tmp)
|
| 217 |
+
src_readers = {}
|
| 218 |
+
def src_get(shard, name):
|
| 219 |
+
if shard not in src_readers:
|
| 220 |
+
src_readers[shard] = safe_open(f"{SRC}/{shard}", framework="pt")
|
| 221 |
+
return src_readers[shard].get_tensor(name)
|
| 222 |
+
|
| 223 |
+
lc = LayerCache(hot, ic.ShardReader(SRC))
|
| 224 |
+
for i, sh in enumerate(shards, 1):
|
| 225 |
+
fname = shard_name(i); fpath = f"{OUT}/{fname}"
|
| 226 |
+
exp_b = sum(e["nbytes"] for e in sh)
|
| 227 |
+
if os.path.exists(fpath):
|
| 228 |
+
print(f"[shard {i}/{NN}] {fname} exists, skip")
|
| 229 |
+
continue
|
| 230 |
+
tmp = fpath + ".tmp"
|
| 231 |
+
if os.path.exists(tmp):
|
| 232 |
+
os.remove(tmp)
|
| 233 |
+
tensors = {}
|
| 234 |
+
for e in sh:
|
| 235 |
+
if e["kind"] == "src":
|
| 236 |
+
tensors[e["name"]] = src_get(e["src_shard"], e["name"])
|
| 237 |
+
else:
|
| 238 |
+
t = lc.get(e["layer"])[e["name"]]
|
| 239 |
+
assert t.numel() * t.element_size() == e["nbytes"], \
|
| 240 |
+
f"size mismatch {e['name']}: built {t.numel()*t.element_size()} vs planned {e['nbytes']}"
|
| 241 |
+
tensors[e["name"]] = t
|
| 242 |
+
save_file(tensors, tmp, metadata={"format": "pt"})
|
| 243 |
+
os.replace(tmp, fpath)
|
| 244 |
+
del tensors
|
| 245 |
+
print(f"[shard {i}/{NN}] wrote {fname} {exp_b/1e9:.2f} GB ({len(sh)} tensors)")
|
| 246 |
+
|
| 247 |
+
write_side_files(hot, packed)
|
| 248 |
+
print(f"[done] assembled {NN} shards, {total/1e9:.1f} GB -> {OUT}")
|
| 249 |
+
return NN, total
|
| 250 |
+
|
| 251 |
+
def write_side_files(hot, packed):
|
| 252 |
+
# verbatim copies
|
| 253 |
+
for fn in ("tokenizer.json", "tokenizer_config.json", "chat_template.jinja",
|
| 254 |
+
"processor_config.json", "special_tokens_map.json"):
|
| 255 |
+
src = f"{SRC}/{fn}"
|
| 256 |
+
if os.path.exists(src):
|
| 257 |
+
shutil.copy2(src, f"{OUT}/{fn}")
|
| 258 |
+
if os.path.isdir(f"{SRC}/tiktoken"):
|
| 259 |
+
shutil.copytree(f"{SRC}/tiktoken", f"{OUT}/tiktoken", dirs_exist_ok=True)
|
| 260 |
+
|
| 261 |
+
# config.json + quantization_config block
|
| 262 |
+
cfg = json.load(open(f"{SRC}/config.json"))
|
| 263 |
+
cfg["quantization_config"] = {
|
| 264 |
+
"quant_method": "inkling_nvfp4_aqlm_hybrid",
|
| 265 |
+
"base_nvfp4": "thinkingmachines/Inkling-NVFP4",
|
| 266 |
+
"group_size": 8,
|
| 267 |
+
"note": "Mixed per-expert quant: hot experts ModelOpt-NVFP4, cold experts AQLM-2bit. "
|
| 268 |
+
"Full spec in hf_quant_config.json['aqlm_hybrid'] and README.md. "
|
| 269 |
+
"Requires a custom fused-MoE kernel; does NOT load on stock vLLM.",
|
| 270 |
+
}
|
| 271 |
+
with open(f"{OUT}/config.json", "w") as f:
|
| 272 |
+
json.dump(cfg, f, indent=2)
|
| 273 |
+
|
| 274 |
+
# hf_quant_config.json = original NVFP4 + aqlm_hybrid block
|
| 275 |
+
hq = json.load(open(f"{SRC}/hf_quant_config.json"))
|
| 276 |
+
hq["aqlm_hybrid"] = make_hybrid_config(hot, packed)
|
| 277 |
+
with open(f"{OUT}/hf_quant_config.json", "w") as f:
|
| 278 |
+
json.dump(hq, f, indent=2)
|
| 279 |
+
|
| 280 |
+
# reproducibility: tools as code/
|
| 281 |
+
os.makedirs(f"{OUT}/code", exist_ok=True)
|
| 282 |
+
for py in os.listdir(os.path.dirname(os.path.abspath(__file__))):
|
| 283 |
+
if py.endswith(".py"):
|
| 284 |
+
shutil.copy2(os.path.join(os.path.dirname(os.path.abspath(__file__)), py),
|
| 285 |
+
f"{OUT}/code/{py}")
|
| 286 |
+
|
| 287 |
+
write_readme(hot, packed)
|
| 288 |
+
print("[side] wrote config.json, hf_quant_config.json, tokenizer/processor files, code/, README.md")
|
| 289 |
+
|
| 290 |
+
def write_readme(hot, packed):
|
| 291 |
+
built = set(hot); bf16 = sorted(built - packed)
|
| 292 |
+
tot_hot = sum(len(hot[L]) for L in built); tot_slots = len(built) * ic.N_EXPERTS
|
| 293 |
+
pct_hot = 100.0 * tot_hot / tot_slots
|
| 294 |
+
readme = README_TEMPLATE.format(
|
| 295 |
+
n_packed=len(packed), n_bf16=len(bf16), bf16_layers=", ".join(map(str, bf16)),
|
| 296 |
+
tot_hot=tot_hot, tot_slots=tot_slots,
|
| 297 |
+
pct_hot=f"{pct_hot:.1f}", pct_cold=f"{100-pct_hot:.1f}")
|
| 298 |
+
with open(f"{OUT}/README.md", "w") as f:
|
| 299 |
+
f.write(readme)
|
| 300 |
+
|
| 301 |
+
README_TEMPLATE = r"""---
|
| 302 |
+
license: other
|
| 303 |
+
base_model:
|
| 304 |
+
- thinkingmachines/Inkling
|
| 305 |
+
tags:
|
| 306 |
+
- moe
|
| 307 |
+
- nvfp4
|
| 308 |
+
- aqlm
|
| 309 |
+
- quantized
|
| 310 |
+
- inkling
|
| 311 |
+
---
|
| 312 |
+
|
| 313 |
+
# Inkling-512k-NVFP4-AQLM-hybrid
|
| 314 |
+
|
| 315 |
+
A memory-optimized **hybrid-quantized** build of
|
| 316 |
+
[thinkingmachines/Inkling](https://huggingface.co/thinkingmachines/Inkling)
|
| 317 |
+
(MoE, 256 routed experts/layer, 6 active). Each MoE layer's experts are split
|
| 318 |
+
into a small **hot** set kept at NVFP4 (~4 bpw) and a large **cold** set stored
|
| 319 |
+
as **AQLM 2-bit**. This is the **512k tier**: **{pct_hot}% of expert slots are
|
| 320 |
+
NVFP4 (hot)** and **{pct_cold}% are AQLM-2bit (cold)**, averaging **~2.66 bits
|
| 321 |
+
per weight** over the routed experts. It targets serving at **512k context on
|
| 322 |
+
4x96GB GPUs**.
|
| 323 |
+
|
| 324 |
+
> **This checkpoint needs a CUSTOM FUSED MoE KERNEL** that dispatches NVFP4 hot
|
| 325 |
+
> experts and AQLM cold experts within the same fused grouped-GEMM. **It does
|
| 326 |
+
> NOT load on stock vLLM / Transformers.** The on-disk format and dequant math
|
| 327 |
+
> are fully specified below so a kernel can consume it directly.
|
| 328 |
+
|
| 329 |
+
## Quality
|
| 330 |
+
Measured **+1.0% perplexity vs Inkling-NVFP4** on the gate-proxy eval — i.e. a
|
| 331 |
+
1% relative degradation for a large drop in expert memory.
|
| 332 |
+
|
| 333 |
+
## Provenance
|
| 334 |
+
- **Hot experts (NVFP4):** sliced verbatim from the ModelOpt NVFP4 base
|
| 335 |
+
(`thinkingmachines/Inkling-NVFP4`), no requantization.
|
| 336 |
+
- **Cold experts (AQLM):** quantized from the **Inkling BF16 teacher**. The
|
| 337 |
+
`w13` cold books are **activation-aware converged** (calibrated on captured
|
| 338 |
+
activations); `w2` cold books are AQLM-encoded.
|
| 339 |
+
- **Assignment** of hot vs cold per layer is in
|
| 340 |
+
`hf_quant_config.json['aqlm_hybrid']['aqlm_layer_books']` and reproduced from
|
| 341 |
+
the routing/importance study (`assign_512k.json`). Reproduction scripts are in
|
| 342 |
+
`code/`.
|
| 343 |
+
|
| 344 |
+
## Layout summary
|
| 345 |
+
- **{n_packed} NVFP4-packed MoE layers** carry the hybrid expert format below
|
| 346 |
+
(NVFP4 hot slabs + AQLM cold).
|
| 347 |
+
- **{n_bf16} bf16-special MoE layer(s)** (layer **{bf16_layers}**) have **no NVFP4
|
| 348 |
+
base**, so they use the SAME hybrid format EXCEPT the hot slabs are stored as
|
| 349 |
+
**bf16** (`experts.{{proj}}_hot_bf16`, no `.scale`/`.scale2`); their cold experts
|
| 350 |
+
are AQLM like every other layer. The fused uncompressed bf16 `experts.w13_weight`
|
| 351 |
+
/ `experts.w2_weight` are **dropped**. Layers 0-1 are dense MLP (not experts).
|
| 352 |
+
- All **non-expert** tensors (attention, embeddings, norms, `shared_experts`,
|
| 353 |
+
`gate`, vision, audio, MTP) are copied verbatim from the NVFP4 base.
|
| 354 |
+
- Total hot slots {tot_hot} / {tot_slots} expert slots.
|
| 355 |
+
|
| 356 |
+
## On-disk tensor format (per NVFP4-packed MoE layer L)
|
| 357 |
+
`prefix = model.llm.layers.{{L}}.mlp.experts`. Routed experts (256 total) are
|
| 358 |
+
partitioned into `n_hot` hot + `n_cold` cold. For each projection
|
| 359 |
+
`proj in {{w13, w2}}` (w13 = fused gate|up, out=6144, in=6144; w2 = down,
|
| 360 |
+
out=6144, in=3072):
|
| 361 |
+
|
| 362 |
+
**Hot — NVFP4-packed layers (ModelOpt, `hot_format=nvfp4`):**
|
| 363 |
+
| tensor | dtype | shape |
|
| 364 |
+
|---|---|---|
|
| 365 |
+
| `{{prefix}}.{{proj}}_hot_weight` | uint8 | `[n_hot, out, in/2]` (2 fp4 codes/byte) |
|
| 366 |
+
| `{{prefix}}.{{proj}}_hot_weight.scale` | float8_e4m3 | `[n_hot, out, in/16]` (per-block-16 scale) |
|
| 367 |
+
| `{{prefix}}.{{proj}}_hot_weight.scale2` | float32 | `[n_hot]` (per-expert global scale) |
|
| 368 |
+
|
| 369 |
+
**Hot — bf16-special layer(s) (`hot_format=bf16`, e.g. layer 2):**
|
| 370 |
+
| tensor | dtype | shape |
|
| 371 |
+
|---|---|---|
|
| 372 |
+
| `{{prefix}}.{{proj}}_hot_bf16` | bfloat16 | `[n_hot, out, in]` (dense bf16, no scales) |
|
| 373 |
+
|
| 374 |
+
**Cold (AQLM) — same for every layer. One code tensor PER BOOK, smallest lossless dtype:**
|
| 375 |
+
| tensor | dtype | shape |
|
| 376 |
+
|---|---|---|
|
| 377 |
+
| `{{prefix}}.{{proj}}_cold_codes.{{b}}` | uint8 if book has <=256 entries else int16 | `[n_cold, out, in/8]` (group_size=8) |
|
| 378 |
+
| `{{prefix}}.{{proj}}_cold_codebook.{{b}}` | float16 | `[entries_b, 8]` |
|
| 379 |
+
| `{{prefix}}.{{proj}}_cold_scales` | float16 | `[n_cold, out]` (per-output-row scale) |
|
| 380 |
+
|
| 381 |
+
Per-book code dtypes: `w13` book0 = **int16** (65536 entries); `w2` book0 =
|
| 382 |
+
**int16** (65536), `w2` book1 = **uint8** (256 entries, codes 0..255 — lossless).
|
| 383 |
+
`{{proj}}_cold_codes.{{b}}` replaces the old single `{{proj}}_cold_codes` tensor
|
| 384 |
+
(the book axis is now the trailing `.{{b}}` suffix), saving ~38 GB losslessly.
|
| 385 |
+
|
| 386 |
+
**Index maps:**
|
| 387 |
+
| tensor | dtype | shape | meaning |
|
| 388 |
+
|---|---|---|---|
|
| 389 |
+
| `{{prefix}}.hot_ids` | int32 | `[n_hot]` | routed-expert index (0..255) of each hot slab |
|
| 390 |
+
| `{{prefix}}.cold_ids` | int32 | `[n_cold]` | routed-expert index (0..255) of each cold slab |
|
| 391 |
+
|
| 392 |
+
`hot_ids[i]` gives the original routed-expert id for hot slab `i` (and likewise
|
| 393 |
+
`cold_ids`); together they partition `0..255`. **group_size = 8.**
|
| 394 |
+
- `w13`: **1 book**, `entries = [65536]`.
|
| 395 |
+
- `w2`: **2 books**, `entries = [65536, 256]`.
|
| 396 |
+
|
| 397 |
+
## Dequant math
|
| 398 |
+
**Cold (AQLM), expert e, output row r, group g (8 weights):**
|
| 399 |
+
```
|
| 400 |
+
acc[8] = 0
|
| 401 |
+
for b in range(n_books):
|
| 402 |
+
acc += codebook_b[ codes_b[e, r, g] ] # codes_b = cold_codes.{{b}}; gather an 8-vector
|
| 403 |
+
W[e, r, g*8:(g+1)*8] = acc * cold_scales[e, r] # per-row scale
|
| 404 |
+
```
|
| 405 |
+
Codes are read from the per-book tensor `{{proj}}_cold_codes.{{b}}`. uint8 books
|
| 406 |
+
(<=256 entries) index the codebook directly. For the int16 65536-entry book,
|
| 407 |
+
negative int16 values wrap to the intended unsigned row (`idx & 0xFFFF`).
|
| 408 |
+
|
| 409 |
+
**Hot (NVFP4, standard ModelOpt e2m1):**
|
| 410 |
+
```
|
| 411 |
+
codes = unpack_nibbles(hot_weight) # 0..15 -> e2m1 LUT value
|
| 412 |
+
W = LUT[codes] * scale.repeat_interleave(16, dim=-1) * scale2
|
| 413 |
+
```
|
| 414 |
+
e2m1 LUT = `[0, .5, 1, 1.5, 2, 3, 4, 6, -0, -.5, -1, -1.5, -2, -3, -4, -6]`;
|
| 415 |
+
low nibble is the first weight of each pair.
|
| 416 |
+
|
| 417 |
+
## Files
|
| 418 |
+
- `model-*.safetensors` + `model.safetensors.index.json` — the sharded weights.
|
| 419 |
+
- `hf_quant_config.json` — original NVFP4 config **plus** the `aqlm_hybrid` block
|
| 420 |
+
(`quant_method`, per-layer `{{n_nvfp4, n_cold, packed}}`, book entry counts).
|
| 421 |
+
- `config.json` — base config **plus** a `quantization_config` block advertising
|
| 422 |
+
`quant_method = inkling_nvfp4_aqlm_hybrid`.
|
| 423 |
+
- `code/` — the exact repackaging scripts (`build_hybrid.py`, `ink_common.py`, ...).
|
| 424 |
+
"""
|
| 425 |
+
|
| 426 |
+
if __name__ == "__main__":
|
| 427 |
+
ap = argparse.ArgumentParser()
|
| 428 |
+
ap.add_argument("--layer", type=int, default=-1, help="build+inspect one layer (test)")
|
| 429 |
+
ap.add_argument("--assemble", action="store_true", help="assemble full checkpoint -> OUT")
|
| 430 |
+
ap.add_argument("--validate", action="store_true", help="validate assembled checkpoint")
|
| 431 |
+
a = ap.parse_args()
|
| 432 |
+
hot = hot_map(); packed = packed_layers()
|
| 433 |
+
if a.assemble:
|
| 434 |
+
assemble()
|
| 435 |
+
elif a.validate:
|
| 436 |
+
from validate_hybrid import validate
|
| 437 |
+
validate()
|
| 438 |
+
elif a.layer >= 0:
|
| 439 |
+
nv = ic.ShardReader(SRC)
|
| 440 |
+
t = build_layer_tensors(a.layer, hot, nv)
|
| 441 |
+
print(f"layer {a.layer} hybrid tensors ({len(t)}):")
|
| 442 |
+
for k, v in t.items():
|
| 443 |
+
print(f" {k.split('.mlp.')[-1]}: {v.dtype} {tuple(v.shape)}")
|
| 444 |
+
tot = sum(v.numel()*v.element_size() for v in t.values())
|
| 445 |
+
print(f"layer bytes: {tot/1e6:.1f} MB")
|
| 446 |
+
else:
|
| 447 |
+
print("packed MoE layers:", len(packed), "| bf16-special layers:", sorted(set(hot)-packed))
|
| 448 |
+
print(json.dumps(make_hybrid_config(hot, packed)["aqlm_layer_books"]["3"]))
|
code/build_mm_manifest.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Assemble the v3 multimodal IMAGE manifest from all staged image pools, with a
|
| 3 |
+
disjoint train/held split. Emits jsonl of {"path","domain","prompt"} that
|
| 4 |
+
run_capture_acts_mm.py consumes (image+text -> vLLM -> MoE acts).
|
| 5 |
+
|
| 6 |
+
Domains (each exercises different vision routing):
|
| 7 |
+
chest_xray medical radiograph (NIH sample)
|
| 8 |
+
fracatlas fracture radiograph
|
| 9 |
+
natural general/natural imagery
|
| 10 |
+
ocr receipts / scanned text
|
| 11 |
+
screenshot desktop/web/mobile GUI (computer-use)
|
| 12 |
+
"""
|
| 13 |
+
import json, glob, os, random
|
| 14 |
+
random.seed(1337)
|
| 15 |
+
MM = "/data/inkling-mm"
|
| 16 |
+
OUT = "/data/inkling-quant"
|
| 17 |
+
PROMPTS = {
|
| 18 |
+
"chest_xray": "<|content_image|>\nDescribe the findings in this chest radiograph.",
|
| 19 |
+
"fracatlas": "<|content_image|>\nIs there a fracture in this radiograph? Describe it.",
|
| 20 |
+
"natural": "<|content_image|>\nDescribe this image in detail.",
|
| 21 |
+
"ocr": "<|content_image|>\nTranscribe all text visible in this image.",
|
| 22 |
+
"screenshot": "<|content_image|>\nDescribe this UI screenshot and the actions available.",
|
| 23 |
+
}
|
| 24 |
+
POOLS = {
|
| 25 |
+
"chest_xray": [f"{MM}/chest_xray/**/*.png", f"{MM}/chest_xray/**/*.jpeg", f"{MM}/chest_xray/**/*.jpg"],
|
| 26 |
+
"fracatlas": [f"{MM}/fracatlas/**/*.jpg", f"{MM}/fracatlas/**/*.png"],
|
| 27 |
+
"natural": [f"{MM}/natural/**/*.jpg", f"{MM}/natural/**/*.png"],
|
| 28 |
+
"ocr": [f"{MM}/ocr/**/*.jpg", f"{MM}/ocr/**/*.png", f"{MM}/ocr_hf/**/*.jpg"],
|
| 29 |
+
"screenshot": [f"{MM}/screenshots/**/*.jpg", f"{MM}/screenshots/**/*.png"],
|
| 30 |
+
}
|
| 31 |
+
PER_DOMAIN = 600 # cap per domain for train (balanced; not ham)
|
| 32 |
+
HELD = 40 # per domain held-out for mm ppl
|
| 33 |
+
|
| 34 |
+
def files(pats):
|
| 35 |
+
fs=[]
|
| 36 |
+
for p in pats: fs+=glob.glob(p, recursive=True)
|
| 37 |
+
return sorted(set(fs))
|
| 38 |
+
|
| 39 |
+
def main():
|
| 40 |
+
train, held = [], []
|
| 41 |
+
for dom, pats in POOLS.items():
|
| 42 |
+
fs = files(pats); random.shuffle(fs)
|
| 43 |
+
if not fs:
|
| 44 |
+
print(f" {dom:11s}: 0 (missing)"); continue
|
| 45 |
+
h = fs[:HELD]; t = fs[HELD:HELD+PER_DOMAIN]
|
| 46 |
+
for f in t: train.append({"path":f,"domain":dom,"prompt":PROMPTS[dom]})
|
| 47 |
+
for f in h: held.append({"path":f,"domain":dom,"prompt":PROMPTS[dom]})
|
| 48 |
+
print(f" {dom:11s}: {len(fs)} avail -> train {len(t)} held {len(h)}")
|
| 49 |
+
random.shuffle(train)
|
| 50 |
+
for name, rows in (("mm_img_train.jsonl",train),("mm_img_held.jsonl",held)):
|
| 51 |
+
with open(f"{OUT}/{name}","w") as fh:
|
| 52 |
+
for r in rows: fh.write(json.dumps(r)+"\n")
|
| 53 |
+
print(f" wrote {OUT}/{name}: {len(rows)}")
|
| 54 |
+
|
| 55 |
+
if __name__=="__main__":
|
| 56 |
+
print(">>> building multimodal image manifest ..."); main()
|
code/converge.py
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Activation-aware AQLM convergence (Phase E1) for Inkling cold experts, w13.
|
| 3 |
+
|
| 4 |
+
Objective per layer over cold experts (shared codebook):
|
| 5 |
+
min sum_{e,o,g} s[e,o]^2 * || (v[e,o,g] - cb[code]) * sqrt(h_e[g]) ||^2
|
| 6 |
+
where v = W/s (row-normalized teacher), h_e = diag input Hessian = E[x^2] over
|
| 7 |
+
tokens routed to expert e (from /data/inkling-quant/acts). Warm-starts from the
|
| 8 |
+
encode part; iterates weighted re-encode -> weighted codebook update -> scale
|
| 9 |
+
refit. w13 input is the MoE hidden x directly (no gate/up interleaving needed).
|
| 10 |
+
|
| 11 |
+
Writes /data/inkling-quant/parts_conv/layer_{L}_w13.pt (same schema as encode
|
| 12 |
+
part) + records rel_err before/after (unweighted, weight-space).
|
| 13 |
+
"""
|
| 14 |
+
from __future__ import annotations
|
| 15 |
+
import argparse, os, time, json
|
| 16 |
+
import torch
|
| 17 |
+
import ink_common as ic
|
| 18 |
+
|
| 19 |
+
import os as _os
|
| 20 |
+
PARTS = _os.environ.get("INK_PARTS", "/data/inkling-quant/parts")
|
| 21 |
+
OUT = _os.environ.get("INK_OUT", "/data/inkling-quant/parts_conv")
|
| 22 |
+
ACTS = _os.environ.get("INK_ACTS", "/data/inkling-quant/acts")
|
| 23 |
+
ASSIGN = _os.environ.get("INK_ASSIGN", "/data/inkling-quant/assign_512k.json")
|
| 24 |
+
G = 8; CHUNK = 16384
|
| 25 |
+
|
| 26 |
+
def cold_experts(layer, assign):
|
| 27 |
+
hot = set(assign["hot_experts"].get(str(layer), []))
|
| 28 |
+
return [e for e in range(ic.N_EXPERTS) if e not in hot]
|
| 29 |
+
|
| 30 |
+
def w13_hessians(layer, cold, device):
|
| 31 |
+
a = torch.load(f"{ACTS}/acts_layer_{layer}.pt")
|
| 32 |
+
x = a["x"].float().to(device); ids = a["ids"].to(device) # x[N,6144], ids[N,6]
|
| 33 |
+
H = torch.ones(ic.N_EXPERTS, 6144, device=device)
|
| 34 |
+
for e in cold:
|
| 35 |
+
m = (ids == e).any(1)
|
| 36 |
+
if int(m.sum()) >= 8:
|
| 37 |
+
xe = x[m]
|
| 38 |
+
H[e] = (xe * xe).mean(0)
|
| 39 |
+
H.clamp_min_(1e-8)
|
| 40 |
+
return H
|
| 41 |
+
|
| 42 |
+
def chunk_weight(st, n, out_dim, ng, hsqrt):
|
| 43 |
+
"""Per-chunk weight[n,8] = sqrt(h_e[group cols]); depends only on (e,g)."""
|
| 44 |
+
idx = torch.arange(st, st + n, device=hsqrt.device)
|
| 45 |
+
e = idx // (out_dim * ng); g = idx % ng
|
| 46 |
+
return hsqrt[e, g] # [n,8]
|
| 47 |
+
|
| 48 |
+
def wencode(vecs, cb, out_dim, ng, hsqrt):
|
| 49 |
+
"""vecs[N,8], cb[K,8] -> codes[N] int32 (weighted nearest), weights on the fly."""
|
| 50 |
+
cbh = cb.half(); cb2h = (cb * cb).half()
|
| 51 |
+
out = torch.empty(vecs.shape[0], dtype=torch.int32, device=vecs.device)
|
| 52 |
+
for s in range(0, vecs.shape[0], CHUNK):
|
| 53 |
+
v = vecs[s:s+CHUNK]; w = chunk_weight(s, v.shape[0], out_dim, ng, hsqrt)
|
| 54 |
+
score = 2.0 * ((v * w) @ cbh.t()) - (w @ cb2h.t()) # [chunk,K]
|
| 55 |
+
out[s:s+CHUNK] = score.argmax(-1).to(torch.int32)
|
| 56 |
+
return out
|
| 57 |
+
|
| 58 |
+
def rel_err(vecs, recon, roww):
|
| 59 |
+
# weight-space unweighted rel err uses row scale only (compare vs encode part metric)
|
| 60 |
+
num = torch.zeros((), device=vecs.device); den = torch.zeros((), device=vecs.device)
|
| 61 |
+
for s in range(0, vecs.shape[0], CHUNK*8):
|
| 62 |
+
v = vecs[s:s+CHUNK*8].float(); r = recon[s:s+CHUNK*8].float()
|
| 63 |
+
num += (v - r).pow(2).sum(); den += v.pow(2).sum()
|
| 64 |
+
return float((num.sqrt() / den.sqrt()))
|
| 65 |
+
|
| 66 |
+
def werr(vecs, codes, cb, out_dim, ng, hsqrt, s2):
|
| 67 |
+
"""Weighted (activation-aware) rel err = sqrt(sum w(v-r)^2)/sqrt(sum w v^2),
|
| 68 |
+
w = h(e,g)*s^2(e,o) — the actual converge objective."""
|
| 69 |
+
num = torch.zeros((), device=vecs.device); den = torch.zeros((), device=vecs.device)
|
| 70 |
+
for st in range(0, vecs.shape[0], CHUNK*8):
|
| 71 |
+
n = min(CHUNK*8, vecs.shape[0]-st)
|
| 72 |
+
idx = torch.arange(st, st+n, device=vecs.device)
|
| 73 |
+
e = idx // (out_dim*ng); o = (idx // ng) % out_dim; g = idx % ng
|
| 74 |
+
w = (hsqrt[e, g].float()**2) * s2[e, o].unsqueeze(1) # [n,8]
|
| 75 |
+
v = vecs[st:st+n].float(); r = cb[codes[st:st+n].long()].float()
|
| 76 |
+
num += (w*(v-r).pow(2)).sum(); den += (w*v.pow(2)).sum()
|
| 77 |
+
return float((num.sqrt()/den.sqrt()))
|
| 78 |
+
|
| 79 |
+
def converge_layer(layer, iters, device="cuda"):
|
| 80 |
+
assign = json.load(open(ASSIGN))
|
| 81 |
+
cold = cold_experts(layer, assign)
|
| 82 |
+
part = torch.load(f"{PARTS}/layer_{layer}_w13.pt")
|
| 83 |
+
cb = part["w13_codebooks"][0].float().to(device) # [K,8]
|
| 84 |
+
K = cb.shape[0]
|
| 85 |
+
tc = ic.ShardReader(ic.BF16_DIR)
|
| 86 |
+
# teacher cold experts -> normalized vecs + per-row scale, built PER-EXPERT to
|
| 87 |
+
# avoid a full [C,6144,6144] float copy (OOM). vecs half, s fp32.
|
| 88 |
+
C = len(cold); out = 6144; ind = 6144; ng = ind // G
|
| 89 |
+
s = torch.empty(C, out, device=device)
|
| 90 |
+
vecs = torch.empty(C * out * ng, G, dtype=torch.float16, device=device)
|
| 91 |
+
for ci, e in enumerate(cold):
|
| 92 |
+
we = ic.teacher_expert(tc, layer, "w13", e, device=device).float() # [6144,6144]
|
| 93 |
+
se = we.abs().mean(-1).clamp_min(1e-8) # [6144]
|
| 94 |
+
s[ci] = se
|
| 95 |
+
base = ci * out * ng
|
| 96 |
+
vecs[base:base + out*ng] = (we / se.unsqueeze(-1)).reshape(out*ng, G).half()
|
| 97 |
+
del we
|
| 98 |
+
torch.cuda.empty_cache()
|
| 99 |
+
# compact hessian tables (no full [Nv,8] materialization)
|
| 100 |
+
H = w13_hessians(layer, cold, device) # [256,6144]
|
| 101 |
+
hcold = H[cold].reshape(C, ng, G) # [C,ng,8] (= h per e,group)
|
| 102 |
+
hsqrt = torch.sqrt(hcold).half() # [C,ng,8]
|
| 103 |
+
s2 = (s * s) # [C,out] row weight
|
| 104 |
+
# init recon rel err (warm start = encode part codes)
|
| 105 |
+
codes0 = part["w13_codes"][:, 0][torch.tensor(cold)].reshape(-1).to(device).long()
|
| 106 |
+
err_before = rel_err(vecs, cb[codes0].half(), None)
|
| 107 |
+
werr_before = werr(vecs, codes0, cb, out, ng, hsqrt, s2)
|
| 108 |
+
print(f" L{layer} w13 warm-start: unweighted {err_before:.4f} | WEIGHTED {werr_before:.4f}", flush=True)
|
| 109 |
+
codes = codes0; err = err_before
|
| 110 |
+
for it in range(iters):
|
| 111 |
+
codes = wencode(vecs, cb, out, ng, hsqrt) # weighted re-encode
|
| 112 |
+
# weighted codebook update: c_k = sum(rw*v)/sum(rw), rw = h(e,g) * s^2(e,o), per chunk
|
| 113 |
+
newcb = torch.zeros(K, G, device=device); wsum = torch.zeros(K, G, device=device)
|
| 114 |
+
for st in range(0, vecs.shape[0], CHUNK*8):
|
| 115 |
+
n = min(CHUNK*8, vecs.shape[0]-st)
|
| 116 |
+
idx = torch.arange(st, st+n, device=device)
|
| 117 |
+
e = idx // (out*ng); o = (idx // ng) % out; g = idx % ng
|
| 118 |
+
r = (hsqrt[e, g].float() ** 2) * s2[e, o].unsqueeze(1) # [n,8] = h * s^2
|
| 119 |
+
c = codes[st:st+n].long(); v = vecs[st:st+n].float()
|
| 120 |
+
newcb.index_add_(0, c, v * r); wsum.index_add_(0, c, r)
|
| 121 |
+
nz = wsum[:, 0] > 0
|
| 122 |
+
cb[nz] = (newcb[nz] / wsum[nz].clamp_min(1e-12))
|
| 123 |
+
err = rel_err(vecs, cb[codes].half(), None)
|
| 124 |
+
we = werr(vecs, codes, cb, out, ng, hsqrt, s2)
|
| 125 |
+
print(f" L{layer} w13 iter{it}: unweighted {err:.4f} | WEIGHTED {we:.4f}", flush=True)
|
| 126 |
+
# refit per-row scale s (closed form, h-weighted): s = <W, s0*recon_h>/<recon,recon>_h ... approx: keep s (rows already normalized). recompute scales via weighted lsq on original W
|
| 127 |
+
# (scale refit folded into part's existing per-row scale; recon already in normalized space)
|
| 128 |
+
codes_r = codes.reshape(C, 1, out, ng).to(torch.int16).cpu()
|
| 129 |
+
# write full-256 arrays: cold updated, hot slots keep encode-part values (unused for hot)
|
| 130 |
+
full_codes = part["w13_codes"].clone()
|
| 131 |
+
full_codes[torch.tensor(cold), 0] = codes_r[:, 0]
|
| 132 |
+
return {
|
| 133 |
+
"expert_ids": part["expert_ids"],
|
| 134 |
+
"w13_codes": full_codes,
|
| 135 |
+
"w13_codebooks": [cb.half().cpu()],
|
| 136 |
+
"w13_book_entries": part["w13_book_entries"],
|
| 137 |
+
"w13_scales": part["w13_scales"],
|
| 138 |
+
"cold_experts": cold,
|
| 139 |
+
"rel_err_before": err_before, "rel_err_after": err,
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
if __name__ == "__main__":
|
| 143 |
+
ap = argparse.ArgumentParser()
|
| 144 |
+
ap.add_argument("--layer", type=int, required=True)
|
| 145 |
+
ap.add_argument("--iters", type=int, default=2)
|
| 146 |
+
ap.add_argument("--save", action="store_true")
|
| 147 |
+
a = ap.parse_args()
|
| 148 |
+
os.makedirs(OUT, exist_ok=True)
|
| 149 |
+
t0 = time.time()
|
| 150 |
+
r = converge_layer(a.layer, a.iters)
|
| 151 |
+
print(f"L{a.layer} w13: {r['rel_err_before']:.4f} -> {r['rel_err_after']:.4f} ({time.time()-t0:.0f}s)")
|
| 152 |
+
if a.save:
|
| 153 |
+
torch.save(r, f"{OUT}/layer_{a.layer}_w13.pt"); print("saved.")
|
code/converge_all.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Full w13 activation-aware converge (Phase E1) across all MoE layers.
|
| 3 |
+
Shards layers across CUDA_VISIBLE_DEVICES GPUs, 1 spawn-worker/GPU, resumable
|
| 4 |
+
(skips layers already in parts_conv/). 2 iters (gains plateau after iter0).
|
| 5 |
+
Run: CUDA_VISIBLE_DEVICES=4,5,6,7 python converge_all.py
|
| 6 |
+
"""
|
| 7 |
+
import os, time
|
| 8 |
+
import torch.multiprocessing as mp
|
| 9 |
+
import ink_common as ic
|
| 10 |
+
|
| 11 |
+
import os as _os
|
| 12 |
+
OUT = _os.environ.get("INK_OUT", "/data/inkling-quant/parts_conv")
|
| 13 |
+
ITERS = 2
|
| 14 |
+
|
| 15 |
+
def worker(gpu, layers):
|
| 16 |
+
os.environ["CUDA_VISIBLE_DEVICES"] = str(gpu)
|
| 17 |
+
os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "expandable_segments:True"
|
| 18 |
+
import torch, converge as cv
|
| 19 |
+
for L in layers:
|
| 20 |
+
out = f"{OUT}/layer_{L}_w13.pt"
|
| 21 |
+
if os.path.exists(out):
|
| 22 |
+
print(f"[gpu{gpu}] skip L{L} (exists)", flush=True); continue
|
| 23 |
+
t0 = time.time()
|
| 24 |
+
try:
|
| 25 |
+
r = cv.converge_layer(L, ITERS, device="cuda")
|
| 26 |
+
torch.save(r, out)
|
| 27 |
+
print(f"[gpu{gpu}] L{L} w13 {r['rel_err_before']:.4f}->{r['rel_err_after']:.4f} "
|
| 28 |
+
f"({time.time()-t0:.0f}s) saved", flush=True)
|
| 29 |
+
except Exception as ex:
|
| 30 |
+
print(f"[gpu{gpu}] L{L} FAILED: {ex}", flush=True)
|
| 31 |
+
|
| 32 |
+
def main():
|
| 33 |
+
os.makedirs(OUT, exist_ok=True)
|
| 34 |
+
gpus = [int(x) for x in os.environ.get("CUDA_VISIBLE_DEVICES", "0").split(",")]
|
| 35 |
+
layers = ic.moe_layers()
|
| 36 |
+
todo = [L for L in layers if not os.path.exists(f"{OUT}/layer_{L}_w13.pt")]
|
| 37 |
+
print(f"{len(layers)} layers, {len(todo)} remaining, {len(gpus)} GPUs {gpus}", flush=True)
|
| 38 |
+
shards = {g: [] for g in gpus}
|
| 39 |
+
for i, L in enumerate(todo):
|
| 40 |
+
shards[gpus[i % len(gpus)]].append(L)
|
| 41 |
+
mp.set_start_method("spawn", force=True)
|
| 42 |
+
ps = [mp.Process(target=worker, args=(g, shards[g])) for g in gpus]
|
| 43 |
+
for p in ps: p.start()
|
| 44 |
+
for p in ps: p.join()
|
| 45 |
+
done = sum(1 for L in layers if os.path.exists(f"{OUT}/layer_{L}_w13.pt"))
|
| 46 |
+
print(f"CONVERGE DONE: {done}/{len(layers)} layers", flush=True)
|
| 47 |
+
|
| 48 |
+
if __name__ == "__main__":
|
| 49 |
+
main()
|
code/converge_w2.py
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Activation-aware AQLM convergence (Phase E1) for Inkling cold experts, w2.
|
| 3 |
+
|
| 4 |
+
Mirrors the validated w13 converger (converge.py) with the TWO w2 differences:
|
| 5 |
+
|
| 6 |
+
1. HESSIAN INPUT is the per-expert intermediate (down-proj input), not the MoE
|
| 7 |
+
hidden x. For each cold expert e with routed calib tokens x_e [n_e,6144]:
|
| 8 |
+
gate_e = teacher_w13_e[0::2] ([3072,6144]) # row-interleaved gate|up
|
| 9 |
+
up_e = teacher_w13_e[1::2] ([3072,6144])
|
| 10 |
+
inter = silu(gate_e @ x_e^T) * (up_e @ x_e^T) -> [3072, n_e]
|
| 11 |
+
h_e = mean_tokens(inter^2) -> [3072] (clamp 1e-8)
|
| 12 |
+
w2 teacher weight is [6144,3072] (out=6144, in=3072) so groups-of-8 run along
|
| 13 |
+
the 3072 input dim -> ng=384.
|
| 14 |
+
|
| 15 |
+
2. TWO BOOKS. v = W/s (row-normalized teacher) is reconstructed additively as
|
| 16 |
+
cb0[c0] + cb1[c1] (book0 @65536 encodes v, book1 @256 encodes the residual
|
| 17 |
+
v - cb0[c0]). Coordinate descent per iter:
|
| 18 |
+
(a) c0 = weighted re-encode of v against cb0
|
| 19 |
+
(b) resid = v - cb0[c0]
|
| 20 |
+
(c) c1 = weighted re-encode of resid against cb1
|
| 21 |
+
(d) update cb0 (weighted mean of v over c0), then cb1 (weighted mean of
|
| 22 |
+
v - cb0_new[c0] over c1); weight = h_e[group cols] * s^2 per row.
|
| 23 |
+
|
| 24 |
+
Objective (same as w13): min sum_{e,o,g} s[e,o]^2 * || (v - recon) * sqrt(h_e[g]) ||^2.
|
| 25 |
+
Warm-starts codes+codebooks from parts/layer_{L}_w2.pt. Only cold experts are
|
| 26 |
+
converged (per assign_512k.json complement); hot slots keep encode values.
|
| 27 |
+
|
| 28 |
+
Writes /data/inkling-quant/parts_conv/layer_{L}_w2.pt (same schema as the encode
|
| 29 |
+
part) + rel_err_before/after (unweighted) and werr_before/after (weighted).
|
| 30 |
+
|
| 31 |
+
NOTE vs w13: wencode / codebook-update / err reductions run in fp32 here (w13
|
| 32 |
+
used fp16). w2's Hessian is E[intermediate^2] which is far larger in magnitude
|
| 33 |
+
than w13's E[x^2]; fp32 in the h-touching ops avoids fp16 overflow->nan. This
|
| 34 |
+
does not change the objective, only the arithmetic precision.
|
| 35 |
+
"""
|
| 36 |
+
from __future__ import annotations
|
| 37 |
+
import argparse, os, time, json
|
| 38 |
+
import torch
|
| 39 |
+
import torch.nn.functional as F
|
| 40 |
+
import ink_common as ic
|
| 41 |
+
|
| 42 |
+
import os as _os
|
| 43 |
+
PARTS = _os.environ.get("INK_PARTS", "/data/inkling-quant/parts")
|
| 44 |
+
OUT = _os.environ.get("INK_OUT", "/data/inkling-quant/parts_conv")
|
| 45 |
+
ACTS = _os.environ.get("INK_ACTS", "/data/inkling-quant/acts")
|
| 46 |
+
ASSIGN = _os.environ.get("INK_ASSIGN", "/data/inkling-quant/assign_512k.json")
|
| 47 |
+
G = 8; CHUNK = 16384
|
| 48 |
+
OUTD = 6144; IND = 3072; NG = IND // G # w2: out=6144, in=3072, ng=384
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def cold_experts(layer, assign):
|
| 52 |
+
hot = set(assign["hot_experts"].get(str(layer), []))
|
| 53 |
+
return [e for e in range(ic.N_EXPERTS) if e not in hot]
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def chunk_weight(st, n, out_dim, ng, hsqrt):
|
| 57 |
+
"""Per-chunk sqrt-weight[n,8] = sqrt(h_e[group cols]); depends only on (e,g)."""
|
| 58 |
+
idx = torch.arange(st, st + n, device=hsqrt.device)
|
| 59 |
+
e = idx // (out_dim * ng); g = idx % ng
|
| 60 |
+
return hsqrt[e, g] # [n,8]
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def wencode(vecs, cb, out_dim, ng, hsqrt, resid_cb=None, resid_codes=None):
|
| 64 |
+
"""codes[N] int32 = weighted nearest of (vecs - resid) against cb.
|
| 65 |
+
If resid_cb/resid_codes given, target = vecs - resid_cb[resid_codes] (book1).
|
| 66 |
+
Weights (sqrt(h)) applied on the fly; fp32 to stay overflow-safe for w2."""
|
| 67 |
+
cbf = cb.float(); cb2 = (cbf * cbf)
|
| 68 |
+
out = torch.empty(vecs.shape[0], dtype=torch.int32, device=vecs.device)
|
| 69 |
+
for s in range(0, vecs.shape[0], CHUNK):
|
| 70 |
+
v = vecs[s:s + CHUNK].float()
|
| 71 |
+
if resid_cb is not None:
|
| 72 |
+
v = v - resid_cb[resid_codes[s:s + CHUNK].long()].float()
|
| 73 |
+
w = chunk_weight(s, v.shape[0], out_dim, ng, hsqrt).float() # [n,8]
|
| 74 |
+
score = 2.0 * ((v * w) @ cbf.t()) - (w @ cb2.t()) # [n,K]
|
| 75 |
+
out[s:s + CHUNK] = score.argmax(-1).to(torch.int32)
|
| 76 |
+
return out
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def _wupdate(vecs, codes, K, out_dim, ng, hsqrt, s2, sub_cb=None, sub_codes=None):
|
| 80 |
+
"""Weighted codebook update: cb_k = sum(r*t)/sum(r), r = h(e,g)*s^2(e,o),
|
| 81 |
+
t = vecs - sub_cb[sub_codes] (residual target for book1; vecs for book0).
|
| 82 |
+
Returns new cb [K,8]; entries with no assigned weight left at 0 (caller keeps
|
| 83 |
+
the old row)."""
|
| 84 |
+
newcb = torch.zeros(K, G, device=vecs.device)
|
| 85 |
+
wsum = torch.zeros(K, G, device=vecs.device)
|
| 86 |
+
for st in range(0, vecs.shape[0], CHUNK * 8):
|
| 87 |
+
n = min(CHUNK * 8, vecs.shape[0] - st)
|
| 88 |
+
idx = torch.arange(st, st + n, device=vecs.device)
|
| 89 |
+
e = idx // (out_dim * ng); o = (idx // ng) % out_dim; g = idx % ng
|
| 90 |
+
r = (hsqrt[e, g].float() ** 2) * s2[e, o].unsqueeze(1) # [n,8] = h*s^2
|
| 91 |
+
t = vecs[st:st + n].float()
|
| 92 |
+
if sub_cb is not None:
|
| 93 |
+
t = t - sub_cb[sub_codes[st:st + n].long()].float()
|
| 94 |
+
c = codes[st:st + n].long()
|
| 95 |
+
newcb.index_add_(0, c, t * r); wsum.index_add_(0, c, r)
|
| 96 |
+
return newcb, wsum
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def _recon_chunk(c0, c1, cb0, cb1):
|
| 100 |
+
return cb0[c0.long()] + cb1[c1.long()]
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def rel_err2(vecs, c0, c1, cb0, cb1):
|
| 104 |
+
"""Unweighted weight-space rel err of the 2-book recon (chunked, fp32)."""
|
| 105 |
+
num = torch.zeros((), device=vecs.device); den = torch.zeros((), device=vecs.device)
|
| 106 |
+
for s in range(0, vecs.shape[0], CHUNK * 8):
|
| 107 |
+
n = min(CHUNK * 8, vecs.shape[0] - s)
|
| 108 |
+
v = vecs[s:s + n].float()
|
| 109 |
+
r = _recon_chunk(c0[s:s + n], c1[s:s + n], cb0, cb1).float()
|
| 110 |
+
num += (v - r).pow(2).sum(); den += v.pow(2).sum()
|
| 111 |
+
return float((num.sqrt() / den.sqrt()))
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
def werr2(vecs, c0, c1, cb0, cb1, out_dim, ng, hsqrt, s2):
|
| 115 |
+
"""Weighted (activation-aware) rel err of the 2-book recon = the objective:
|
| 116 |
+
w = h(e,g)*s^2(e,o). Scale-invariant to any global rescale of h."""
|
| 117 |
+
num = torch.zeros((), device=vecs.device); den = torch.zeros((), device=vecs.device)
|
| 118 |
+
for st in range(0, vecs.shape[0], CHUNK * 8):
|
| 119 |
+
n = min(CHUNK * 8, vecs.shape[0] - st)
|
| 120 |
+
idx = torch.arange(st, st + n, device=vecs.device)
|
| 121 |
+
e = idx // (out_dim * ng); o = (idx // ng) % out_dim; g = idx % ng
|
| 122 |
+
w = (hsqrt[e, g].float() ** 2) * s2[e, o].unsqueeze(1) # [n,8]
|
| 123 |
+
v = vecs[st:st + n].float()
|
| 124 |
+
r = _recon_chunk(c0[st:st + n], c1[st:st + n], cb0, cb1).float()
|
| 125 |
+
num += (w * (v - r).pow(2)).sum(); den += (w * v.pow(2)).sum()
|
| 126 |
+
return float((num.sqrt() / den.sqrt()))
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
def build_w2(layer, cold, device):
|
| 130 |
+
"""Per-cold-expert: normalized vecs [C*out*ng,8] half, per-row scale s [C,out],
|
| 131 |
+
and diagonal input Hessian hsqrt [C,ng,8] (sqrt of E[intermediate^2]).
|
| 132 |
+
Built per expert to avoid a full float teacher copy (OOM)."""
|
| 133 |
+
a = torch.load(f"{ACTS}/acts_layer_{layer}.pt")
|
| 134 |
+
x = a["x"].to(device).float() # [N,6144]
|
| 135 |
+
ids = a["ids"].to(device) # [N,6]
|
| 136 |
+
tc = ic.ShardReader(ic.BF16_DIR)
|
| 137 |
+
C = len(cold)
|
| 138 |
+
s = torch.empty(C, OUTD, device=device)
|
| 139 |
+
vecs = torch.empty(C * OUTD * NG, G, dtype=torch.float16, device=device)
|
| 140 |
+
hsqrt = torch.empty(C, NG, G, device=device)
|
| 141 |
+
for ci, e in enumerate(cold):
|
| 142 |
+
# ---- weight vectors (teacher w2) ----
|
| 143 |
+
w2e = ic.teacher_expert(tc, layer, "w2", e, device=device).float() # [6144,3072]
|
| 144 |
+
se = w2e.abs().mean(-1).clamp_min(1e-8) # [6144]
|
| 145 |
+
s[ci] = se
|
| 146 |
+
base = ci * OUTD * NG
|
| 147 |
+
vecs[base:base + OUTD * NG] = (w2e / se.unsqueeze(-1)).reshape(OUTD * NG, G).half()
|
| 148 |
+
del w2e
|
| 149 |
+
# ---- Hessian: intermediate = silu(gate@x^T)*(up@x^T), teacher w13 ----
|
| 150 |
+
m = (ids == e).any(1)
|
| 151 |
+
if int(m.sum()) >= 8:
|
| 152 |
+
xe = x[m] # [n_e,6144]
|
| 153 |
+
w13e = ic.teacher_expert(tc, layer, "w13", e, device=device).float() # [6144,6144]
|
| 154 |
+
gate = w13e[0::2]; up = w13e[1::2] # [3072,6144] each
|
| 155 |
+
del w13e
|
| 156 |
+
g_out = gate @ xe.t(); u_out = up @ xe.t() # [3072,n_e]
|
| 157 |
+
inter = F.silu(g_out) * u_out # [3072,n_e]
|
| 158 |
+
h_e = inter.pow(2).mean(1).clamp_min(1e-8) # [3072]
|
| 159 |
+
del xe, g_out, u_out, inter
|
| 160 |
+
else:
|
| 161 |
+
h_e = torch.ones(IND, device=device)
|
| 162 |
+
hsqrt[ci] = h_e.reshape(NG, G).sqrt()
|
| 163 |
+
del x, ids
|
| 164 |
+
torch.cuda.empty_cache()
|
| 165 |
+
return vecs, s, hsqrt
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
def converge_layer(layer, iters, device="cuda"):
|
| 169 |
+
assign = json.load(open(ASSIGN))
|
| 170 |
+
cold = cold_experts(layer, assign)
|
| 171 |
+
part = torch.load(f"{PARTS}/layer_{layer}_w2.pt")
|
| 172 |
+
cb0 = part["w2_codebooks"][0].float().to(device) # [65536,8]
|
| 173 |
+
cb1 = part["w2_codebooks"][1].float().to(device) # [256,8]
|
| 174 |
+
K0 = cb0.shape[0]; K1 = cb1.shape[0]
|
| 175 |
+
C = len(cold)
|
| 176 |
+
coldt = torch.tensor(cold)
|
| 177 |
+
|
| 178 |
+
vecs, s, hsqrt = build_w2(layer, cold, device)
|
| 179 |
+
s2 = (s * s) # [C,out]
|
| 180 |
+
|
| 181 |
+
# warm-start codes for cold experts (int16 -> positive index; book0 wraps mod 65536)
|
| 182 |
+
c0 = (part["w2_codes"][:, 0][coldt].reshape(-1).to(device).long() % K0).to(torch.int32)
|
| 183 |
+
c1 = (part["w2_codes"][:, 1][coldt].reshape(-1).to(device).long() % K1).to(torch.int32)
|
| 184 |
+
|
| 185 |
+
err_before = rel_err2(vecs, c0, c1, cb0, cb1)
|
| 186 |
+
werr_before = werr2(vecs, c0, c1, cb0, cb1, OUTD, NG, hsqrt, s2)
|
| 187 |
+
print(f" L{layer} w2 warm-start: unweighted {err_before:.4f} | WEIGHTED {werr_before:.4f}", flush=True)
|
| 188 |
+
|
| 189 |
+
err = err_before; we = werr_before
|
| 190 |
+
for it in range(iters):
|
| 191 |
+
# (a) re-encode book0 against cb0 (target v)
|
| 192 |
+
c0 = wencode(vecs, cb0, OUTD, NG, hsqrt)
|
| 193 |
+
# (b)+(c) re-encode book1 against cb1 (target v - cb0[c0])
|
| 194 |
+
c1 = wencode(vecs, cb1, OUTD, NG, hsqrt, resid_cb=cb0, resid_codes=c0)
|
| 195 |
+
# (d) update cb0 (weighted mean of v over c0)
|
| 196 |
+
newcb0, wsum0 = _wupdate(vecs, c0, K0, OUTD, NG, hsqrt, s2)
|
| 197 |
+
nz0 = wsum0[:, 0] > 0
|
| 198 |
+
cb0[nz0] = newcb0[nz0] / wsum0[nz0].clamp_min(1e-12)
|
| 199 |
+
# then update cb1 (weighted mean of v - cb0_new[c0] over c1)
|
| 200 |
+
newcb1, wsum1 = _wupdate(vecs, c1, K1, OUTD, NG, hsqrt, s2, sub_cb=cb0, sub_codes=c0)
|
| 201 |
+
nz1 = wsum1[:, 0] > 0
|
| 202 |
+
cb1[nz1] = newcb1[nz1] / wsum1[nz1].clamp_min(1e-12)
|
| 203 |
+
|
| 204 |
+
err = rel_err2(vecs, c0, c1, cb0, cb1)
|
| 205 |
+
we = werr2(vecs, c0, c1, cb0, cb1, OUTD, NG, hsqrt, s2)
|
| 206 |
+
print(f" L{layer} w2 iter{it}: unweighted {err:.4f} | WEIGHTED {we:.4f}", flush=True)
|
| 207 |
+
|
| 208 |
+
# reshape codes back to [C,out,ng] and store as int16 (book0 wraps to signed)
|
| 209 |
+
c0r = c0.reshape(C, OUTD, NG).to(torch.int16).cpu()
|
| 210 |
+
c1r = c1.reshape(C, OUTD, NG).to(torch.int16).cpu()
|
| 211 |
+
full_codes = part["w2_codes"].clone() # [256,2,out,ng]
|
| 212 |
+
full_codes[coldt, 0] = c0r
|
| 213 |
+
full_codes[coldt, 1] = c1r
|
| 214 |
+
return {
|
| 215 |
+
"expert_ids": part["expert_ids"],
|
| 216 |
+
"w2_codes": full_codes,
|
| 217 |
+
"w2_codebooks": [cb0.half().cpu(), cb1.half().cpu()],
|
| 218 |
+
"w2_book_entries": part["w2_book_entries"],
|
| 219 |
+
"w2_scales": part["w2_scales"],
|
| 220 |
+
"cold_experts": cold,
|
| 221 |
+
"rel_err_before": err_before, "rel_err_after": err,
|
| 222 |
+
"werr_before": werr_before, "werr_after": we,
|
| 223 |
+
}
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
if __name__ == "__main__":
|
| 227 |
+
ap = argparse.ArgumentParser()
|
| 228 |
+
ap.add_argument("--layer", type=int, required=True)
|
| 229 |
+
ap.add_argument("--iters", type=int, default=2)
|
| 230 |
+
ap.add_argument("--save", action="store_true")
|
| 231 |
+
a = ap.parse_args()
|
| 232 |
+
os.makedirs(OUT, exist_ok=True)
|
| 233 |
+
t0 = time.time()
|
| 234 |
+
r = converge_layer(a.layer, a.iters)
|
| 235 |
+
print(f"L{a.layer} w2: unw {r['rel_err_before']:.4f}->{r['rel_err_after']:.4f} | "
|
| 236 |
+
f"WEIGHTED {r['werr_before']:.4f}->{r['werr_after']:.4f} ({time.time()-t0:.0f}s)")
|
| 237 |
+
if a.save:
|
| 238 |
+
torch.save(r, f"{OUT}/layer_{a.layer}_w2.pt"); print("saved.")
|
code/converge_w2_all.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Full w2 activation-aware converge (Phase E1) across all MoE layers.
|
| 3 |
+
Shards layers across CUDA_VISIBLE_DEVICES GPUs, 1 spawn-worker/GPU, resumable
|
| 4 |
+
(skips layers already in parts_conv/). 2 iters.
|
| 5 |
+
Run: CUDA_VISIBLE_DEVICES=4,5,6,7 python converge_w2_all.py
|
| 6 |
+
"""
|
| 7 |
+
import os, time
|
| 8 |
+
import torch.multiprocessing as mp
|
| 9 |
+
import ink_common as ic
|
| 10 |
+
|
| 11 |
+
import os as _os
|
| 12 |
+
OUT = _os.environ.get("INK_OUT", "/data/inkling-quant/parts_conv")
|
| 13 |
+
ITERS = 2
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def worker(gpu, layers):
|
| 17 |
+
os.environ["CUDA_VISIBLE_DEVICES"] = str(gpu)
|
| 18 |
+
os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "expandable_segments:True"
|
| 19 |
+
import torch, converge_w2 as cv
|
| 20 |
+
for L in layers:
|
| 21 |
+
out = f"{OUT}/layer_{L}_w2.pt"
|
| 22 |
+
if os.path.exists(out):
|
| 23 |
+
print(f"[gpu{gpu}] skip L{L} (exists)", flush=True); continue
|
| 24 |
+
t0 = time.time()
|
| 25 |
+
try:
|
| 26 |
+
r = cv.converge_layer(L, ITERS, device="cuda")
|
| 27 |
+
tmp = out + ".tmp"
|
| 28 |
+
torch.save(r, tmp); os.replace(tmp, out)
|
| 29 |
+
print(f"[gpu{gpu}] L{L} w2 unw {r['rel_err_before']:.4f}->{r['rel_err_after']:.4f} "
|
| 30 |
+
f"| WEIGHTED {r['werr_before']:.4f}->{r['werr_after']:.4f} "
|
| 31 |
+
f"({time.time()-t0:.0f}s) saved", flush=True)
|
| 32 |
+
except Exception as ex:
|
| 33 |
+
import traceback
|
| 34 |
+
print(f"[gpu{gpu}] L{L} FAILED: {ex}", flush=True)
|
| 35 |
+
traceback.print_exc()
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def main():
|
| 39 |
+
os.makedirs(OUT, exist_ok=True)
|
| 40 |
+
gpus = [int(x) for x in os.environ.get("CUDA_VISIBLE_DEVICES", "0").split(",")]
|
| 41 |
+
layers = ic.moe_layers()
|
| 42 |
+
todo = [L for L in layers if not os.path.exists(f"{OUT}/layer_{L}_w2.pt")]
|
| 43 |
+
print(f"{len(layers)} layers, {len(todo)} remaining, {len(gpus)} GPUs {gpus}", flush=True)
|
| 44 |
+
shards = {g: [] for g in gpus}
|
| 45 |
+
for i, L in enumerate(todo):
|
| 46 |
+
shards[gpus[i % len(gpus)]].append(L)
|
| 47 |
+
mp.set_start_method("spawn", force=True)
|
| 48 |
+
ps = [mp.Process(target=worker, args=(g, shards[g])) for g in gpus]
|
| 49 |
+
for p in ps: p.start()
|
| 50 |
+
for p in ps: p.join()
|
| 51 |
+
done = sum(1 for L in layers if os.path.exists(f"{OUT}/layer_{L}_w2.pt"))
|
| 52 |
+
print(f"CONVERGE_W2 DONE: {done}/{len(layers)} layers", flush=True)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
if __name__ == "__main__":
|
| 56 |
+
main()
|
code/encode_all.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Encode ALL routed experts (AQLM) for every MoE layer, ship scheme
|
| 3 |
+
(w13=[65536], w2=[65536,256]). Shards (layer,proj) jobs across the GPUs given
|
| 4 |
+
in CUDA_VISIBLE_DEVICES, one worker process per GPU. Resumable: skips parts
|
| 5 |
+
already on disk. Codebooks are shared per layer, so we encode all 256 experts;
|
| 6 |
+
the checkpoint build later slices hot vs cold per assign_512k.json.
|
| 7 |
+
Run: CUDA_VISIBLE_DEVICES=4,5,6,7 python encode_all.py
|
| 8 |
+
"""
|
| 9 |
+
import os, sys, time, json
|
| 10 |
+
import torch
|
| 11 |
+
import torch.multiprocessing as mp
|
| 12 |
+
import ink_common as ic
|
| 13 |
+
|
| 14 |
+
PARTS = "/data/inkling-quant/parts"
|
| 15 |
+
BOOK = {"w13": [65536], "w2": [65536, 256]}
|
| 16 |
+
|
| 17 |
+
def worker(gpu, jobs, ret):
|
| 18 |
+
os.environ["CUDA_VISIBLE_DEVICES"] = str(gpu)
|
| 19 |
+
os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "expandable_segments:True"
|
| 20 |
+
import torch
|
| 21 |
+
import ink_aqlm as aq
|
| 22 |
+
for (layer, proj) in jobs:
|
| 23 |
+
out = f"{PARTS}/layer_{layer}_{proj}.pt"
|
| 24 |
+
if os.path.exists(out):
|
| 25 |
+
print(f"[gpu{gpu}] skip L{layer} {proj} (exists)", flush=True); continue
|
| 26 |
+
t0 = time.time()
|
| 27 |
+
try:
|
| 28 |
+
r = aq.quantize_layer(layer, proj, BOOK[proj], device="cuda")
|
| 29 |
+
torch.save(r, out)
|
| 30 |
+
print(f"[gpu{gpu}] L{layer} {proj} rel_err={r['rel_err']:.4f} ({time.time()-t0:.0f}s) -> saved", flush=True)
|
| 31 |
+
except Exception as e:
|
| 32 |
+
print(f"[gpu{gpu}] L{layer} {proj} FAILED: {e}", flush=True)
|
| 33 |
+
|
| 34 |
+
def main():
|
| 35 |
+
os.makedirs(PARTS, exist_ok=True)
|
| 36 |
+
gpus = [int(x) for x in os.environ.get("CUDA_VISIBLE_DEVICES", "0").split(",")]
|
| 37 |
+
layers = ic.moe_layers()
|
| 38 |
+
jobs = [(L, p) for L in layers for p in ("w13", "w2")]
|
| 39 |
+
todo = [j for j in jobs if not os.path.exists(f"{PARTS}/layer_{j[0]}_{j[1]}.pt")]
|
| 40 |
+
print(f"{len(jobs)} total jobs, {len(todo)} remaining, {len(gpus)} GPUs {gpus}", flush=True)
|
| 41 |
+
# round-robin assign
|
| 42 |
+
shards = {g: [] for g in gpus}
|
| 43 |
+
for i, j in enumerate(todo):
|
| 44 |
+
shards[gpus[i % len(gpus)]].append(j)
|
| 45 |
+
mp.set_start_method("spawn", force=True)
|
| 46 |
+
procs = []
|
| 47 |
+
for g in gpus:
|
| 48 |
+
p = mp.Process(target=worker, args=(g, shards[g], None)); p.start(); procs.append(p)
|
| 49 |
+
for p in procs: p.join()
|
| 50 |
+
done = sum(1 for j in jobs if os.path.exists(f"{PARTS}/layer_{j[0]}_{j[1]}.pt"))
|
| 51 |
+
print(f"DONE: {done}/{len(jobs)} parts present", flush=True)
|
| 52 |
+
|
| 53 |
+
if __name__ == "__main__":
|
| 54 |
+
main()
|
code/gpqa40.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import glob, re, json, time, concurrent.futures as cf, urllib.request
|
| 2 |
+
import pyarrow.parquet as pq
|
| 3 |
+
K="sk-98f2447ba021acf1c9ac0aea25f65bb58cf3066dcda734e5"
|
| 4 |
+
URL="http://localhost:8001/v1/chat/completions"; MODEL="GLM-5.2-NVFP4-AQLM-hybrid"
|
| 5 |
+
N=40; CONC=6; MAXTOK=20000
|
| 6 |
+
p=glob.glob("/home/coder/.cache/huggingface/hub/datasets--fingertap--GPQA-Diamond/snapshots/*/test/*.parquet")[0]
|
| 7 |
+
d=pq.read_table(p).to_pydict(); Q=d["question"]; A=d["answer"]
|
| 8 |
+
rows=list(range(N))
|
| 9 |
+
def ask(i):
|
| 10 |
+
q=Q[i]
|
| 11 |
+
prompt=(q.strip()+"\n\nThink step by step, then end with a single line exactly:\nAnswer: <letter>")
|
| 12 |
+
body=json.dumps({"model":MODEL,"messages":[{"role":"user","content":prompt}],
|
| 13 |
+
"max_tokens":MAXTOK,"temperature":0}).encode()
|
| 14 |
+
req=urllib.request.Request(URL,data=body,headers={"Authorization":f"Bearer {K}","Content-Type":"application/json"})
|
| 15 |
+
for _ in range(2):
|
| 16 |
+
try:
|
| 17 |
+
r=json.load(urllib.request.urlopen(req,timeout=900))
|
| 18 |
+
txt=r["choices"][0]["message"]["content"]; fin=r["choices"][0]["finish_reason"]
|
| 19 |
+
m=re.findall(r"Answer:\s*\**([A-Da-d])\b", txt)
|
| 20 |
+
if not m: m=re.findall(r"\b([A-Da-d])\b\s*$", txt.strip())
|
| 21 |
+
pred=(m[-1].upper() if m else "?")
|
| 22 |
+
return i,pred,(pred==A[i].upper()),r["usage"]["completion_tokens"],fin
|
| 23 |
+
except Exception as e:
|
| 24 |
+
last=str(e)[:60]; time.sleep(2)
|
| 25 |
+
return i,"ERR",False,0,last
|
| 26 |
+
t0=time.time(); res={}
|
| 27 |
+
with cf.ThreadPoolExecutor(max_workers=CONC) as ex:
|
| 28 |
+
for i,pred,ok,tok,fin in ex.map(ask,rows):
|
| 29 |
+
res[i]=(pred,ok,tok,fin)
|
| 30 |
+
print(f"q{i:02d} gold={A[i]} pred={pred} {'OK ' if ok else 'x '} tok={tok} {fin}",flush=True)
|
| 31 |
+
c=sum(1 for i in rows if res[i][1]); trunc=sum(1 for i in rows if res[i][3]=="length")
|
| 32 |
+
print(f"\nGPQA-40 (fingertap MC, current TP4-no-DCP serve): {c}/{N} = {c/N:.3f}")
|
| 33 |
+
print(f"reference fixA full-198 = 0.889 ; e4m3 = 0.884 ; (this is a 40-subset, expect +-0.05 noise)")
|
| 34 |
+
print(f"truncated(length-capped) answers: {trunc} (these may undercount) | wall {time.time()-t0:.0f}s")
|
| 35 |
+
json.dump({str(i):res[i] for i in rows}, open("/data/inkling-quant/gpqa40_current.json","w"))
|
code/ink_aqlm.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""AQLM (additive k-means) 2-bit quantizer for Inkling routed experts.
|
| 3 |
+
|
| 4 |
+
Per (layer, proj) over a set of experts, minimize ||W - What||^2 (init-grade;
|
| 5 |
+
activation-aware convergence is a later pass). Overfit to Inkling's FUSED
|
| 6 |
+
layout: teacher W is [E, out, in] bf16 read from /data/inkling.
|
| 7 |
+
|
| 8 |
+
Scheme (mirrors the GLM hybrid, adapted to fused tensors):
|
| 9 |
+
- per-(expert,out-channel) scale s[e,o] = mean|W[e,o,:]| (rows normalized by s)
|
| 10 |
+
- groups of GDIM=8 along the INPUT dim -> vectors in R^8
|
| 11 |
+
- ENTRIES=65536 shared codebook(s) fit by GPU k-means on a row sample
|
| 12 |
+
(shared across all experts of the layer); books=2 encodes a 2nd-codebook
|
| 13 |
+
residual (used for w2), books=1 for w13
|
| 14 |
+
- least-squares refit of s after encoding
|
| 15 |
+
Output part: /data/inkling-quant/parts/layer_{L}_{proj}.pt with
|
| 16 |
+
expert_ids, {proj}_codes int16 [E, books, out, in/8], {proj}_codebooks fp16
|
| 17 |
+
[books, ENTRIES, 8], {proj}_scales fp16 [E, out], err_before/after.
|
| 18 |
+
"""
|
| 19 |
+
from __future__ import annotations
|
| 20 |
+
import argparse, os, time
|
| 21 |
+
import torch
|
| 22 |
+
import ink_common as ic
|
| 23 |
+
|
| 24 |
+
ENTRIES = 65536
|
| 25 |
+
GDIM = 8
|
| 26 |
+
SAMPLE_VECS = 4_000_000
|
| 27 |
+
KMEANS_ITERS = 12
|
| 28 |
+
CHUNK = 32_768 # scores [CHUNK, ENTRIES] fp16 ~4.3GB
|
| 29 |
+
|
| 30 |
+
def _argmax_codes(v16, cent16, cn16):
|
| 31 |
+
d = v16 @ cent16.t() # [n, ENTRIES]
|
| 32 |
+
d.mul_(2).sub_(cn16) # 2 v.c - |c|^2 (argmax == nearest)
|
| 33 |
+
return d.argmax(-1).to(torch.int32)
|
| 34 |
+
|
| 35 |
+
def _encode(vecs16, cent16):
|
| 36 |
+
cn16 = (cent16.float() * cent16.float()).sum(-1).half()
|
| 37 |
+
out = torch.empty(vecs16.shape[0], dtype=torch.int32, device=vecs16.device)
|
| 38 |
+
for s in range(0, vecs16.shape[0], CHUNK):
|
| 39 |
+
out[s:s+CHUNK] = _argmax_codes(vecs16[s:s+CHUNK], cent16, cn16)
|
| 40 |
+
return out
|
| 41 |
+
|
| 42 |
+
def _kmeans(vecs16, iters, gen, entries=ENTRIES):
|
| 43 |
+
n = vecs16.shape[0]; dev = vecs16.device
|
| 44 |
+
cent = vecs16[torch.randperm(n, generator=gen, device=dev)[:entries]].float()
|
| 45 |
+
for _ in range(iters):
|
| 46 |
+
cent16 = cent.half(); cn16 = (cent*cent).sum(-1).half()
|
| 47 |
+
sums = torch.zeros(entries, GDIM, device=dev); cnts = torch.zeros(entries, device=dev)
|
| 48 |
+
for s in range(0, n, CHUNK):
|
| 49 |
+
v = vecs16[s:s+CHUNK]; a = _argmax_codes(v, cent16, cn16).long()
|
| 50 |
+
sums.index_add_(0, a, v.float()); cnts.index_add_(0, a, torch.ones_like(a, dtype=torch.float32))
|
| 51 |
+
nz = cnts > 0; cent[nz] = sums[nz] / cnts[nz].unsqueeze(1)
|
| 52 |
+
return cent.half()
|
| 53 |
+
|
| 54 |
+
def quantize_layer(layer, proj, book_entries, device="cuda", seed=0):
|
| 55 |
+
"""book_entries: list of codebook sizes per book, e.g. w13=[65536], w2=[65536,256]."""
|
| 56 |
+
books = len(book_entries)
|
| 57 |
+
tc = ic.ShardReader(ic.BF16_DIR)
|
| 58 |
+
gen = torch.Generator(device=device); gen.manual_seed(seed + 1000*layer + (0 if proj=="w13" else 1))
|
| 59 |
+
# stack teacher experts in bf16 (avoid full float copies -> OOM on w13): [E, out, in]
|
| 60 |
+
W = torch.stack([ic.teacher_expert(tc, layer, proj, e, device=device) for e in range(ic.N_EXPERTS)])
|
| 61 |
+
E, out, ind = W.shape
|
| 62 |
+
assert ind % GDIM == 0
|
| 63 |
+
s = W.abs().float().mean(-1).clamp_min(1e-8) # [E, out] per-row scale (fp32)
|
| 64 |
+
vecs = (W.float() / s.unsqueeze(-1)).reshape(E*out*(ind//GDIM), GDIM).half() # normalized groups-of-8
|
| 65 |
+
del W; torch.cuda.empty_cache()
|
| 66 |
+
codes = torch.empty(books, vecs.shape[0], dtype=torch.int32, device=device)
|
| 67 |
+
codebooks = [] # variable sizes per book
|
| 68 |
+
recon = None
|
| 69 |
+
for b in range(books):
|
| 70 |
+
target = vecs if b == 0 else (vecs - recon).half()
|
| 71 |
+
idx = torch.randperm(target.shape[0], generator=gen, device=device)[:SAMPLE_VECS]
|
| 72 |
+
cbb = _kmeans(target[idx], KMEANS_ITERS, gen, entries=book_entries[b])
|
| 73 |
+
codes[b] = _encode(target, cbb); codebooks.append(cbb.cpu())
|
| 74 |
+
contrib = cbb[codes[b].long()]
|
| 75 |
+
recon = contrib if recon is None else (recon + contrib).half()
|
| 76 |
+
del target
|
| 77 |
+
# rel err (weight-space, unweighted) — chunked to avoid full float copies
|
| 78 |
+
num = torch.zeros((), device=device); den = torch.zeros((), device=device)
|
| 79 |
+
for st in range(0, vecs.shape[0], CHUNK*8):
|
| 80 |
+
v = vecs[st:st+CHUNK*8].float(); r = recon[st:st+CHUNK*8].float()
|
| 81 |
+
num += (v-r).pow(2).sum(); den += v.pow(2).sum()
|
| 82 |
+
rel = (num.sqrt() / den.sqrt())
|
| 83 |
+
codes = codes.reshape(books, E, out, ind//GDIM).permute(1,0,2,3).contiguous() # [E,books,out,in/8]
|
| 84 |
+
return {
|
| 85 |
+
"expert_ids": torch.arange(E, dtype=torch.int32),
|
| 86 |
+
f"{proj}_codes": codes.to(torch.int16).cpu(),
|
| 87 |
+
f"{proj}_codebooks": codebooks, # list of [entries_b, 8] fp16
|
| 88 |
+
f"{proj}_book_entries": list(book_entries),
|
| 89 |
+
f"{proj}_scales": s.to(torch.float16).cpu(),
|
| 90 |
+
"rel_err": float(rel),
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
if __name__ == "__main__":
|
| 94 |
+
ap = argparse.ArgumentParser()
|
| 95 |
+
ap.add_argument("--layer", type=int, default=3)
|
| 96 |
+
ap.add_argument("--proj", default="w2", choices=["w13","w2"])
|
| 97 |
+
ap.add_argument("--device", default="cuda")
|
| 98 |
+
ap.add_argument("--save", action="store_true")
|
| 99 |
+
a = ap.parse_args()
|
| 100 |
+
# ship scheme: w13=[65536], w2=[65536,256]
|
| 101 |
+
be = [65536] if a.proj=="w13" else [65536, 256]
|
| 102 |
+
t0=time.time()
|
| 103 |
+
r = quantize_layer(a.layer, a.proj, be, a.device)
|
| 104 |
+
print(f"L{a.layer} {a.proj} book_entries={be}: weight-space rel_err={r['rel_err']:.4f} ({time.time()-t0:.1f}s)")
|
| 105 |
+
if a.save:
|
| 106 |
+
os.makedirs("/data/inkling-quant/parts", exist_ok=True)
|
| 107 |
+
torch.save(r, f"/data/inkling-quant/parts/layer_{a.layer}_{a.proj}.pt")
|
| 108 |
+
print("saved.")
|
code/ink_common.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Foundational tooling for the Inkling NVFP4+AQLM hybrid.
|
| 3 |
+
|
| 4 |
+
Handles Inkling's FUSED per-layer expert layout:
|
| 5 |
+
NVFP4 (/data/inkling-nvfp4): mlp.experts.w13_weight u8 [E, out, in/2]
|
| 6 |
+
+ .scale f8_e4m3 [E, out, in/16] (block-16) + .scale2 f32 [E] (per-expert global)
|
| 7 |
+
BF16 teacher (/data/inkling): mlp.experts.w13_weight bf16 [E, out, in]
|
| 8 |
+
w13: out=6144 (gate|up=2*3072), in=6144. w2: out=6144, in=3072.
|
| 9 |
+
"""
|
| 10 |
+
from __future__ import annotations
|
| 11 |
+
import json, os
|
| 12 |
+
import torch
|
| 13 |
+
|
| 14 |
+
NVFP4_DIR = os.environ.get("INK_NVFP4_DIR", "/data/inkling-nvfp4")
|
| 15 |
+
BF16_DIR = os.environ.get("INK_BF16_DIR", "/data/inkling")
|
| 16 |
+
N_EXPERTS = 256
|
| 17 |
+
G = 8 # AQLM group dim
|
| 18 |
+
# NVFP4 e2m1 value table (low nibble = first of each pair), matches GLM/modelopt.
|
| 19 |
+
FP4_LUT = torch.tensor([0.0,0.5,1.0,1.5,2.0,3.0,4.0,6.0,
|
| 20 |
+
-0.0,-0.5,-1.0,-1.5,-2.0,-3.0,-4.0,-6.0], dtype=torch.float32)
|
| 21 |
+
|
| 22 |
+
class ShardReader:
|
| 23 |
+
"""Random access to tensors across a repo's safetensors shards."""
|
| 24 |
+
def __init__(self, repo_dir: str):
|
| 25 |
+
idx = json.load(open(os.path.join(repo_dir, "model.safetensors.index.json")))
|
| 26 |
+
self.weight_map = idx["weight_map"]
|
| 27 |
+
self.repo_dir = repo_dir
|
| 28 |
+
self._open = {}
|
| 29 |
+
def has(self, name: str) -> bool:
|
| 30 |
+
return name in self.weight_map
|
| 31 |
+
def get(self, name: str) -> torch.Tensor:
|
| 32 |
+
from safetensors import safe_open
|
| 33 |
+
shard = self.weight_map[name]
|
| 34 |
+
if shard not in self._open:
|
| 35 |
+
self._open[shard] = safe_open(os.path.join(self.repo_dir, shard), framework="pt")
|
| 36 |
+
return self._open[shard].get_tensor(name)
|
| 37 |
+
|
| 38 |
+
def _pfx(layer: int, proj: str) -> str:
|
| 39 |
+
return f"model.llm.layers.{layer}.mlp.experts.{proj}_weight"
|
| 40 |
+
|
| 41 |
+
def dequant_nvfp4_expert(reader: ShardReader, layer: int, proj: str, e: int, device="cuda") -> torch.Tensor:
|
| 42 |
+
"""Dequantize one NVFP4 expert (proj in {'w13','w2'}) -> bf16 [out, in]."""
|
| 43 |
+
p = _pfx(layer, proj)
|
| 44 |
+
lut = FP4_LUT.to(device)
|
| 45 |
+
packed = reader.get(p)[e].to(device) # u8 [out, in/2]
|
| 46 |
+
scale = reader.get(f"{p}.scale")[e].to(device) # f8_e4m3 [out, in/16]
|
| 47 |
+
scale2 = reader.get(f"{p}.scale2")[e].to(torch.float32).to(device) # f32 scalar
|
| 48 |
+
lo = (packed & 0x0F).long(); hi = (packed >> 4).long()
|
| 49 |
+
out, half = packed.shape
|
| 50 |
+
vals = torch.empty(out, half * 2, dtype=torch.float32, device=device)
|
| 51 |
+
vals[:, 0::2] = lut[lo]; vals[:, 1::2] = lut[hi]
|
| 52 |
+
del lo, hi
|
| 53 |
+
blk = scale.to(torch.float32).repeat_interleave(16, dim=1)
|
| 54 |
+
return (vals * blk * scale2).to(torch.bfloat16)
|
| 55 |
+
|
| 56 |
+
def teacher_expert(reader: ShardReader, layer: int, proj: str, e: int, device="cuda") -> torch.Tensor:
|
| 57 |
+
"""BF16 teacher expert -> bf16 [out, in]."""
|
| 58 |
+
return reader.get(_pfx(layer, proj))[e].to(device).to(torch.bfloat16)
|
| 59 |
+
|
| 60 |
+
def quantize_nvfp4(W: torch.Tensor):
|
| 61 |
+
"""bf16/fp32 [out,in] -> ModelOpt NVFP4 (inverse of dequant_nvfp4_expert):
|
| 62 |
+
packed u8 [out,in/2], scale f8_e4m3 [out,in/16] (block-16), scale2 f32 scalar.
|
| 63 |
+
W ~= lut[code] * scale.repeat_interleave(16) * scale2."""
|
| 64 |
+
dev = W.device; Wf = W.float()
|
| 65 |
+
out, ind = Wf.shape; assert ind % 16 == 0
|
| 66 |
+
lut = FP4_LUT.to(dev)
|
| 67 |
+
amax = Wf.abs().amax().clamp_min(1e-8)
|
| 68 |
+
scale2 = amax / (6.0 * 448.0) # global fp32
|
| 69 |
+
w = Wf / scale2
|
| 70 |
+
wb = w.reshape(out, ind // 16, 16)
|
| 71 |
+
bmax = wb.abs().amax(-1, keepdim=True).clamp_min(1e-8)
|
| 72 |
+
bscale = (bmax / 6.0) # per-block, -> f8
|
| 73 |
+
bscale_f8 = bscale.to(torch.float8_e4m3fn).float().clamp_min(1e-8)
|
| 74 |
+
q = wb / bscale_f8 # in fp4 dynamic range
|
| 75 |
+
# nearest fp4 level
|
| 76 |
+
diff = (q.unsqueeze(-1) - lut.view(1, 1, 1, -1)).abs()
|
| 77 |
+
codes = diff.argmin(-1).to(torch.uint8) # [out, nb, 16] indices 0..15
|
| 78 |
+
codes = codes.reshape(out, ind)
|
| 79 |
+
lo = codes[:, 0::2]; hi = codes[:, 1::2]
|
| 80 |
+
packed = (lo | (hi << 4)).to(torch.uint8) # [out, in/2]
|
| 81 |
+
scale_f8 = bscale.reshape(out, ind // 16).to(torch.float8_e4m3fn)
|
| 82 |
+
return packed, scale_f8, scale2.to(torch.float32).reshape(1)
|
| 83 |
+
|
| 84 |
+
def aqlm_dequant_expert(part, proj, e, device="cuda"):
|
| 85 |
+
"""Reconstruct one AQLM expert -> bf16 [out,in] from a converge/encode part.
|
| 86 |
+
part has {proj}_codes [E,books,out,ng] int16, {proj}_codebooks list of
|
| 87 |
+
[entries,8] fp16, {proj}_scales [E,out] fp16."""
|
| 88 |
+
codes = part[f"{proj}_codes"][e].to(device).long() # [books,out,ng]
|
| 89 |
+
cbs = part[f"{proj}_codebooks"] # list of [entries,8]
|
| 90 |
+
scales = part[f"{proj}_scales"][e].to(device).float() # [out]
|
| 91 |
+
books, out, ng = codes.shape
|
| 92 |
+
recon = torch.zeros(out, ng, G, device=device)
|
| 93 |
+
for b in range(books):
|
| 94 |
+
cb = cbs[b].to(device).float() # [entries,8]; neg idx wraps ok for 65536
|
| 95 |
+
recon += cb[codes[b]] # [out,ng,8]
|
| 96 |
+
recon = recon.reshape(out, ng * G) * scales.unsqueeze(-1)
|
| 97 |
+
return recon.to(torch.bfloat16)
|
| 98 |
+
|
| 99 |
+
def dequant_nvfp4_raw(packed, scale_f8, scale2, device="cuda"):
|
| 100 |
+
"""Dequant raw NVFP4 tensors (for round-trip checks)."""
|
| 101 |
+
lut = FP4_LUT.to(device)
|
| 102 |
+
packed = packed.to(device); scale_f8 = scale_f8.to(device); scale2 = scale2.to(device).float()
|
| 103 |
+
lo = (packed & 0x0F).long(); hi = (packed >> 4).long()
|
| 104 |
+
out, half = packed.shape
|
| 105 |
+
vals = torch.empty(out, half * 2, dtype=torch.float32, device=device)
|
| 106 |
+
vals[:, 0::2] = lut[lo]; vals[:, 1::2] = lut[hi]
|
| 107 |
+
blk = scale_f8.to(torch.float32).repeat_interleave(16, dim=1)
|
| 108 |
+
return (vals * blk * scale2).to(torch.bfloat16)
|
| 109 |
+
|
| 110 |
+
def moe_layers() -> list[int]:
|
| 111 |
+
"""Layers that carry routed experts (skip the 2 dense layers)."""
|
| 112 |
+
idx = json.load(open(os.path.join(NVFP4_DIR, "model.safetensors.index.json")))["weight_map"]
|
| 113 |
+
ls = set()
|
| 114 |
+
for k in idx:
|
| 115 |
+
if ".mlp.experts.w13_weight" == k[k.find(".mlp"):] if ".mlp.experts.w13_weight" in k else False:
|
| 116 |
+
pass
|
| 117 |
+
import re
|
| 118 |
+
for k in idx:
|
| 119 |
+
m = re.match(r"model\.llm\.layers\.(\d+)\.mlp\.experts\.w13_weight$", k)
|
| 120 |
+
if m: ls.add(int(m.group(1)))
|
| 121 |
+
return sorted(ls)
|
code/merge_acts.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Merge text + image + audio acts into ONE dir per split, so REAP / converge / PV
|
| 3 |
+
see the full multimodal routing. Concatenates x/ids and sums n per layer, capped.
|
| 4 |
+
|
| 5 |
+
train: acts_v3 + acts_v3_img + acts_v3_audio -> acts_v3_all
|
| 6 |
+
held : acts_v3_heldout + acts_v3_img_held + acts_v3_audio_held -> acts_v3_all_held
|
| 7 |
+
"""
|
| 8 |
+
import os, glob, torch
|
| 9 |
+
Q = "/data/inkling-quant"
|
| 10 |
+
CAP = 160000 # max token-rows/layer in the merged file (text-heavy but mm represented)
|
| 11 |
+
SETS = {
|
| 12 |
+
"acts_v3_all": ["acts_v3", "acts_v3_img", "acts_v3_audio"],
|
| 13 |
+
"acts_v3_all_held": ["acts_v3_heldout", "acts_v3_img_held", "acts_v3_audio_held"],
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
def layers(dirs):
|
| 17 |
+
s = set()
|
| 18 |
+
for d in dirs:
|
| 19 |
+
for f in glob.glob(f"{Q}/{d}/acts_layer_*.pt"):
|
| 20 |
+
s.add(int(f.split("_")[-1].split(".")[0]))
|
| 21 |
+
return sorted(s)
|
| 22 |
+
|
| 23 |
+
def main():
|
| 24 |
+
for out, srcs in SETS.items():
|
| 25 |
+
od = f"{Q}/{out}"; os.makedirs(od, exist_ok=True)
|
| 26 |
+
present = [s for s in srcs if os.path.isdir(f"{Q}/{s}")]
|
| 27 |
+
print(f">>> {out} <- {present}")
|
| 28 |
+
for L in layers(present):
|
| 29 |
+
xs, ids, n = [], [], 0
|
| 30 |
+
for s in present:
|
| 31 |
+
p = f"{Q}/{s}/acts_layer_{L}.pt"
|
| 32 |
+
if not os.path.exists(p): continue
|
| 33 |
+
a = torch.load(p, map_location="cpu")
|
| 34 |
+
xs.append(a["x"]); ids.append(a["ids"]); n += int(a.get("n", a["x"].shape[0]))
|
| 35 |
+
if not xs: continue
|
| 36 |
+
x = torch.cat(xs, 0); i = torch.cat(ids, 0)
|
| 37 |
+
if x.shape[0] > CAP:
|
| 38 |
+
sel = torch.randperm(x.shape[0])[:CAP]; x = x[sel]; i = i[sel]
|
| 39 |
+
torch.save({"x": x, "ids": i, "n": x.shape[0]}, f"{od}/acts_layer_{L}.pt")
|
| 40 |
+
print(f" wrote {len(layers(present))} layers -> {od}")
|
| 41 |
+
|
| 42 |
+
if __name__ == "__main__":
|
| 43 |
+
main()
|
code/ppl_eval.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Perplexity + coherence gate for a checkpoint (baseline NVFP4 vs 512k gate).
|
| 3 |
+
Offline vLLM: teacher-forced prompt_logprobs over a fixed held-out text set ->
|
| 4 |
+
mean NLL -> perplexity. Same text for both models => the RATIO is the quality
|
| 5 |
+
signal (absolute may be optimistic since eval text overlaps calib distribution).
|
| 6 |
+
Usage: CUDA_VISIBLE_DEVICES=4,5,6,7 python ppl_eval.py --model DIR --tag NAME
|
| 7 |
+
"""
|
| 8 |
+
import argparse, json, math, os
|
| 9 |
+
|
| 10 |
+
def main():
|
| 11 |
+
ap = argparse.ArgumentParser()
|
| 12 |
+
ap.add_argument("--model", required=True)
|
| 13 |
+
ap.add_argument("--tag", required=True)
|
| 14 |
+
ap.add_argument("--n", type=int, default=48)
|
| 15 |
+
# v3: default to the TRULY HELD-OUT set (wikitext-2 test, disjoint from all
|
| 16 |
+
# calib/tuning) so pre/post ppl is honest. Pass --eval-file for others.
|
| 17 |
+
ap.add_argument("--eval-file", default="/data/inkling-quant/ppl_v3.jsonl")
|
| 18 |
+
a = ap.parse_args()
|
| 19 |
+
os.environ.setdefault("VLLM_USE_DEEP_GEMM", "0")
|
| 20 |
+
from vllm import LLM, SamplingParams
|
| 21 |
+
# fixed eval set: first N chunks (identical text for every model -> fair ratio).
|
| 22 |
+
# v3 ppl_v3.jsonl carries a per-chunk "cat" -> report per-domain ppl too.
|
| 23 |
+
rows = [json.loads(l) for l in open(a.eval_file)][:a.n]
|
| 24 |
+
prompts = [r["text"] for r in rows]
|
| 25 |
+
cats = [r.get("cat", "all") for r in rows]
|
| 26 |
+
print(f"PPL_EVAL model={a.model} tag={a.tag} eval_file={a.eval_file} n={len(prompts)}")
|
| 27 |
+
llm = LLM(model=a.model, tensor_parallel_size=4, max_model_len=8192,
|
| 28 |
+
gpu_memory_utilization=0.9, enforce_eager=True, trust_remote_code=True)
|
| 29 |
+
outs = llm.generate(prompts, SamplingParams(max_tokens=1, temperature=0, prompt_logprobs=0))
|
| 30 |
+
tot_nll = 0.0; tot_tok = 0
|
| 31 |
+
by = {} # cat -> [nll_sum, tok]
|
| 32 |
+
for cat, o in zip(cats, outs):
|
| 33 |
+
c = by.setdefault(cat, [0.0, 0])
|
| 34 |
+
for d in o.prompt_logprobs: # list per prompt token; [0] is None
|
| 35 |
+
if not d:
|
| 36 |
+
continue
|
| 37 |
+
lp = next(iter(d.values())).logprob # teacher-forced -> actual token
|
| 38 |
+
tot_nll += -lp; tot_tok += 1
|
| 39 |
+
c[0] += -lp; c[1] += 1
|
| 40 |
+
ppl = math.exp(tot_nll / max(1, tot_tok))
|
| 41 |
+
print(f"PPL_RESULT tag={a.tag} tokens={tot_tok} mean_nll={tot_nll/max(1,tot_tok):.4f} ppl={ppl:.4f}")
|
| 42 |
+
for cat in sorted(by):
|
| 43 |
+
n, t = by[cat]
|
| 44 |
+
print(f" PPL_CAT tag={a.tag} cat={cat:12s} tokens={t} ppl={math.exp(n/max(1,t)):.4f}")
|
| 45 |
+
# coherence probes
|
| 46 |
+
for q in ["The capital of France is", "def fibonacci(n):", "Water is made of hydrogen and"]:
|
| 47 |
+
r = llm.generate([q], SamplingParams(max_tokens=24, temperature=0))[0].outputs[0].text
|
| 48 |
+
print(f"COHERENCE [{q[:22]!r}] -> {r[:60]!r}")
|
| 49 |
+
|
| 50 |
+
if __name__ == "__main__":
|
| 51 |
+
main()
|
code/pv.py
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Output-preserving (PV) tuning (Phase E2) for Inkling 512k v2 cold experts.
|
| 3 |
+
|
| 4 |
+
For each layer, tune the SHARED AQLM codebook VALUES + per-(cold-expert,out-row)
|
| 5 |
+
SCALES so the dequantized expert's OUTPUT matches the BF16 teacher's output on
|
| 6 |
+
calib activations. CODES stay FIXED (loaded from the converged parts).
|
| 7 |
+
|
| 8 |
+
Trainable nn.Parameters (init from parts_conv):
|
| 9 |
+
cb13 [65536,8] w13 codebook (1 book)
|
| 10 |
+
cb2_0 [65536,8] w2 codebook book0
|
| 11 |
+
cb2_1 [256,8] w2 codebook book1
|
| 12 |
+
s13 [ncold,6144] w13 per-(cold-expert,out-row) scales
|
| 13 |
+
s2 [ncold,6144] w2 per-(cold-expert,out-row) scales
|
| 14 |
+
|
| 15 |
+
Differentiable dequant (codes fixed):
|
| 16 |
+
Wq = scales.unsqueeze(-1) * sum_b codebook_b[idx_b] reshaped to [out,in]
|
| 17 |
+
idx = codes.long() % cb.shape[0] (int16 negative-wrap for the 65536 book)
|
| 18 |
+
|
| 19 |
+
Expert forward (w13 ROW-INTERLEAVED: gate=w13[0::2], up=w13[1::2]):
|
| 20 |
+
h = silu(gate @ x^T) * (up @ x^T) [3072, n]
|
| 21 |
+
f = w2 @ h [6144, n]
|
| 22 |
+
Teacher forward uses the BF16 teacher weights (fixed, precomputed).
|
| 23 |
+
|
| 24 |
+
Loss = sum over cold experts of MSE(f_quant(x_e), f_teacher(x_e)) (unweighted).
|
| 25 |
+
Optimizer: Adam, lr 1e-3 (scales) / 1e-4 (codebooks), param groups.
|
| 26 |
+
|
| 27 |
+
Writes /data/inkling-quant/parts_pv/layer_{L}_{w13,w2}.pt with the SAME schema as
|
| 28 |
+
parts_conv ({proj}_codes [unchanged], {proj}_codebooks [tuned], {proj}_book_entries,
|
| 29 |
+
{proj}_scales [tuned], cold_experts, expert_ids) + output_mse_before/after and the
|
| 30 |
+
weight-space rel_err sanity numbers.
|
| 31 |
+
"""
|
| 32 |
+
from __future__ import annotations
|
| 33 |
+
import argparse, os, time, json
|
| 34 |
+
import torch
|
| 35 |
+
import torch.nn.functional as F
|
| 36 |
+
import ink_common as ic
|
| 37 |
+
|
| 38 |
+
CONV = "/data/inkling-quant/parts_conv"
|
| 39 |
+
OUT = "/data/inkling-quant/parts_pv"
|
| 40 |
+
ACTS = "/data/inkling-quant/acts"
|
| 41 |
+
ASSIGN = "/data/inkling-quant/assign_512k.json"
|
| 42 |
+
G = 8
|
| 43 |
+
OUTD = 6144
|
| 44 |
+
W13_IN = 6144; W13_NG = W13_IN // G # 768
|
| 45 |
+
W2_IN = 3072; W2_NG = W2_IN // G # 384
|
| 46 |
+
MIN_TOK = 8
|
| 47 |
+
MAX_TOK = 4096
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def cold_experts(layer, assign):
|
| 51 |
+
hot = set(assign["hot_experts"].get(str(layer), []))
|
| 52 |
+
return [e for e in range(ic.N_EXPERTS) if e not in hot]
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def dequant_w13(ci, codes13, cb13, s13):
|
| 56 |
+
"""codes13[ci,0] -> [6144,768]; returns Wq13 [6144,6144] (differentiable)."""
|
| 57 |
+
idx = codes13[ci, 0].long() % cb13.shape[0] # [6144,768]
|
| 58 |
+
recon = cb13[idx].reshape(OUTD, W13_IN) # [6144,6144]
|
| 59 |
+
return s13[ci].unsqueeze(-1) * recon
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def dequant_w2(ci, codes2, cb2_0, cb2_1, s2):
|
| 63 |
+
"""codes2[ci] -> books; returns Wq2 [6144,3072] (differentiable)."""
|
| 64 |
+
idx0 = codes2[ci, 0].long() % cb2_0.shape[0] # [6144,384]
|
| 65 |
+
idx1 = codes2[ci, 1].long() % cb2_1.shape[0] # [6144,384]
|
| 66 |
+
recon = (cb2_0[idx0] + cb2_1[idx1]).reshape(OUTD, W2_IN) # [6144,3072]
|
| 67 |
+
return s2[ci].unsqueeze(-1) * recon
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def expert_forward(Wq13, Wq2, xe):
|
| 71 |
+
"""xe [n,6144] -> expert output [6144,n]."""
|
| 72 |
+
gate = Wq13[0::2] # [3072,6144]
|
| 73 |
+
up = Wq13[1::2] # [3072,6144]
|
| 74 |
+
xt = xe.t() # [6144,n]
|
| 75 |
+
h = F.silu(gate @ xt) * (up @ xt) # [3072,n]
|
| 76 |
+
return Wq2 @ h # [6144,n]
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def build_data(layer, cold, device):
|
| 80 |
+
"""Per cold expert with >=MIN_TOK routed tokens: xe [n,6144] fp32, teacher
|
| 81 |
+
output ftea [6144,n] fp32 (precomputed from BF16 teacher weights, fixed).
|
| 82 |
+
Returns list of (ci, e, xe, ftea) and the count of data experts."""
|
| 83 |
+
a = torch.load(f"{ACTS}/acts_layer_{layer}.pt")
|
| 84 |
+
x = a["x"].to(device).float() # [N,6144]
|
| 85 |
+
ids = a["ids"].to(device) # [N,6]
|
| 86 |
+
tc = ic.ShardReader(ic.BF16_DIR)
|
| 87 |
+
data = []
|
| 88 |
+
gen = torch.Generator(device=device); gen.manual_seed(1234 + layer)
|
| 89 |
+
for ci, e in enumerate(cold):
|
| 90 |
+
m = (ids == e).any(1)
|
| 91 |
+
n = int(m.sum())
|
| 92 |
+
if n < MIN_TOK:
|
| 93 |
+
continue
|
| 94 |
+
xe = x[m] # [n,6144]
|
| 95 |
+
if n > MAX_TOK:
|
| 96 |
+
sel = torch.randperm(n, generator=gen, device=device)[:MAX_TOK]
|
| 97 |
+
xe = xe[sel]
|
| 98 |
+
w13 = ic.teacher_expert(tc, layer, "w13", e, device=device).float() # [6144,6144]
|
| 99 |
+
w2 = ic.teacher_expert(tc, layer, "w2", e, device=device).float() # [6144,3072]
|
| 100 |
+
with torch.no_grad():
|
| 101 |
+
ftea = expert_forward(w13, w2, xe) # [6144,n]
|
| 102 |
+
data.append((ci, e, xe.contiguous(), ftea.contiguous()))
|
| 103 |
+
del w13, w2
|
| 104 |
+
del x, ids
|
| 105 |
+
torch.cuda.empty_cache()
|
| 106 |
+
return data
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
@torch.no_grad()
|
| 110 |
+
def eval_output_mse(data, codes13, codes2, cb13, cb2_0, cb2_1, s13, s2):
|
| 111 |
+
"""Mean over data experts of per-expert MSE(f_quant, f_teacher), plus the
|
| 112 |
+
mean normalized rel-err (||fq-ft||^2 / ||ft||^2)."""
|
| 113 |
+
mse_sum = 0.0; rel_sum = 0.0
|
| 114 |
+
for (ci, e, xe, ftea) in data:
|
| 115 |
+
Wq13 = dequant_w13(ci, codes13, cb13, s13)
|
| 116 |
+
Wq2 = dequant_w2(ci, codes2, cb2_0, cb2_1, s2)
|
| 117 |
+
fq = expert_forward(Wq13, Wq2, xe)
|
| 118 |
+
diff = (fq - ftea)
|
| 119 |
+
mse_sum += float(diff.pow(2).mean())
|
| 120 |
+
rel_sum += float(diff.pow(2).sum() / ftea.pow(2).sum().clamp_min(1e-12))
|
| 121 |
+
ne = len(data)
|
| 122 |
+
return mse_sum / ne, rel_sum / ne
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
@torch.no_grad()
|
| 126 |
+
def weight_relerr(layer, data, codes13, codes2, cb13, cb2_0, cb2_1, s13, s2, device):
|
| 127 |
+
"""Aggregate plain weight-space rel-err ||Wq-Wt||/||Wt|| over the data experts,
|
| 128 |
+
for w13 and w2 separately (reloads BF16 teacher weights)."""
|
| 129 |
+
tc = ic.ShardReader(ic.BF16_DIR)
|
| 130 |
+
n13 = d13 = n2 = d2 = 0.0
|
| 131 |
+
for (ci, e, _, _) in data:
|
| 132 |
+
Wt13 = ic.teacher_expert(tc, layer, "w13", e, device=device).float()
|
| 133 |
+
Wq13 = dequant_w13(ci, codes13, cb13, s13)
|
| 134 |
+
n13 += float((Wq13 - Wt13).pow(2).sum()); d13 += float(Wt13.pow(2).sum())
|
| 135 |
+
del Wt13, Wq13
|
| 136 |
+
Wt2 = ic.teacher_expert(tc, layer, "w2", e, device=device).float()
|
| 137 |
+
Wq2 = dequant_w2(ci, codes2, cb2_0, cb2_1, s2)
|
| 138 |
+
n2 += float((Wq2 - Wt2).pow(2).sum()); d2 += float(Wt2.pow(2).sum())
|
| 139 |
+
del Wt2, Wq2
|
| 140 |
+
return (n13 / d13) ** 0.5, (n2 / d2) ** 0.5
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
def pv_layer(layer, steps=300, lr_cb=1e-4, lr_s=1e-3, device="cuda", verbose=True):
|
| 144 |
+
assign = json.load(open(ASSIGN))
|
| 145 |
+
cold = cold_experts(layer, assign)
|
| 146 |
+
coldt = torch.tensor(cold)
|
| 147 |
+
p13 = torch.load(f"{CONV}/layer_{layer}_w13.pt", map_location="cpu")
|
| 148 |
+
p2 = torch.load(f"{CONV}/layer_{layer}_w2.pt", map_location="cpu")
|
| 149 |
+
|
| 150 |
+
# fixed codes (cold-indexed) on device
|
| 151 |
+
codes13 = p13["w13_codes"][coldt].to(device) # [ncold,1,6144,768] int16
|
| 152 |
+
codes2 = p2["w2_codes"][coldt].to(device) # [ncold,2,6144,384] int16
|
| 153 |
+
|
| 154 |
+
# trainable params (init from converged parts)
|
| 155 |
+
cb13 = p13["w13_codebooks"][0].float().to(device).clone().requires_grad_()
|
| 156 |
+
cb2_0 = p2["w2_codebooks"][0].float().to(device).clone().requires_grad_()
|
| 157 |
+
cb2_1 = p2["w2_codebooks"][1].float().to(device).clone().requires_grad_()
|
| 158 |
+
s13 = p13["w13_scales"][coldt].float().to(device).clone().requires_grad_()
|
| 159 |
+
s2 = p2["w2_scales"][coldt].float().to(device).clone().requires_grad_()
|
| 160 |
+
|
| 161 |
+
data = build_data(layer, cold, device)
|
| 162 |
+
if verbose:
|
| 163 |
+
print(f" L{layer}: {len(cold)} cold, {len(data)} with >={MIN_TOK} tokens", flush=True)
|
| 164 |
+
if len(data) == 0:
|
| 165 |
+
raise RuntimeError(f"L{layer}: no cold experts with calib tokens")
|
| 166 |
+
|
| 167 |
+
# baseline (converged-parts init)
|
| 168 |
+
mse0, rel0 = eval_output_mse(data, codes13, codes2, cb13, cb2_0, cb2_1, s13, s2)
|
| 169 |
+
w13rel0, w2rel0 = weight_relerr(layer, data, codes13, codes2, cb13, cb2_0, cb2_1, s13, s2, device)
|
| 170 |
+
if verbose:
|
| 171 |
+
print(f" L{layer} BEFORE: out_mse={mse0:.6e} rel={rel0:.4f} | "
|
| 172 |
+
f"w13_relerr={w13rel0:.4f} w2_relerr={w2rel0:.4f}", flush=True)
|
| 173 |
+
|
| 174 |
+
opt = torch.optim.Adam([
|
| 175 |
+
{"params": [cb13, cb2_0, cb2_1], "lr": lr_cb},
|
| 176 |
+
{"params": [s13, s2], "lr": lr_s},
|
| 177 |
+
])
|
| 178 |
+
|
| 179 |
+
for step in range(steps):
|
| 180 |
+
opt.zero_grad(set_to_none=True)
|
| 181 |
+
tot = 0.0
|
| 182 |
+
for (ci, e, xe, ftea) in data:
|
| 183 |
+
Wq13 = dequant_w13(ci, codes13, cb13, s13)
|
| 184 |
+
Wq2 = dequant_w2(ci, codes2, cb2_0, cb2_1, s2)
|
| 185 |
+
fq = expert_forward(Wq13, Wq2, xe)
|
| 186 |
+
loss = F.mse_loss(fq, ftea)
|
| 187 |
+
loss.backward()
|
| 188 |
+
tot += float(loss.detach())
|
| 189 |
+
opt.step()
|
| 190 |
+
if verbose and (step % 50 == 0 or step == steps - 1):
|
| 191 |
+
print(f" L{layer} step{step}: sum_mse={tot:.6e} (mean={tot/len(data):.6e})", flush=True)
|
| 192 |
+
|
| 193 |
+
mse1, rel1 = eval_output_mse(data, codes13, codes2, cb13, cb2_0, cb2_1, s13, s2)
|
| 194 |
+
w13rel1, w2rel1 = weight_relerr(layer, data, codes13, codes2, cb13, cb2_0, cb2_1, s13, s2, device)
|
| 195 |
+
if verbose:
|
| 196 |
+
print(f" L{layer} AFTER: out_mse={mse1:.6e} rel={rel1:.4f} | "
|
| 197 |
+
f"w13_relerr={w13rel1:.4f} w2_relerr={w2rel1:.4f}", flush=True)
|
| 198 |
+
|
| 199 |
+
# assemble full-256 tuned arrays (cold rows tuned; hot rows carried from conv part)
|
| 200 |
+
with torch.no_grad():
|
| 201 |
+
full_s13 = p13["w13_scales"].clone()
|
| 202 |
+
full_s13[coldt] = s13.detach().half().cpu()
|
| 203 |
+
full_s2 = p2["w2_scales"].clone()
|
| 204 |
+
full_s2[coldt] = s2.detach().half().cpu()
|
| 205 |
+
|
| 206 |
+
common = dict(
|
| 207 |
+
cold_experts=cold,
|
| 208 |
+
output_mse_before=mse0, output_mse_after=mse1,
|
| 209 |
+
output_relerr_before=rel0, output_relerr_after=rel1,
|
| 210 |
+
conv_rel_err_after_w13=float(p13.get("rel_err_after", float("nan"))),
|
| 211 |
+
conv_rel_err_after_w2=float(p2.get("rel_err_after", float("nan"))),
|
| 212 |
+
)
|
| 213 |
+
out13 = {
|
| 214 |
+
"expert_ids": p13["expert_ids"],
|
| 215 |
+
"w13_codes": p13["w13_codes"], # UNCHANGED
|
| 216 |
+
"w13_codebooks": [cb13.detach().half().cpu()], # tuned
|
| 217 |
+
"w13_book_entries": p13["w13_book_entries"],
|
| 218 |
+
"w13_scales": full_s13, # tuned
|
| 219 |
+
"weight_relerr_before": w13rel0, "weight_relerr_after": w13rel1,
|
| 220 |
+
**common,
|
| 221 |
+
}
|
| 222 |
+
out2 = {
|
| 223 |
+
"expert_ids": p2["expert_ids"],
|
| 224 |
+
"w2_codes": p2["w2_codes"], # UNCHANGED
|
| 225 |
+
"w2_codebooks": [cb2_0.detach().half().cpu(), cb2_1.detach().half().cpu()], # tuned
|
| 226 |
+
"w2_book_entries": p2["w2_book_entries"],
|
| 227 |
+
"w2_scales": full_s2, # tuned
|
| 228 |
+
"weight_relerr_before": w2rel0, "weight_relerr_after": w2rel1,
|
| 229 |
+
**common,
|
| 230 |
+
}
|
| 231 |
+
return out13, out2, dict(
|
| 232 |
+
mse0=mse0, mse1=mse1, rel0=rel0, rel1=rel1,
|
| 233 |
+
w13rel0=w13rel0, w13rel1=w13rel1, w2rel0=w2rel0, w2rel1=w2rel1,
|
| 234 |
+
)
|
| 235 |
+
|
| 236 |
+
|
| 237 |
+
if __name__ == "__main__":
|
| 238 |
+
ap = argparse.ArgumentParser()
|
| 239 |
+
ap.add_argument("--layer", type=int, required=True)
|
| 240 |
+
ap.add_argument("--steps", type=int, default=300)
|
| 241 |
+
ap.add_argument("--lr_cb", type=float, default=1e-4)
|
| 242 |
+
ap.add_argument("--lr_s", type=float, default=1e-3)
|
| 243 |
+
ap.add_argument("--save", action="store_true")
|
| 244 |
+
a = ap.parse_args()
|
| 245 |
+
os.makedirs(OUT, exist_ok=True)
|
| 246 |
+
t0 = time.time()
|
| 247 |
+
out13, out2, m = pv_layer(a.layer, a.steps, a.lr_cb, a.lr_s)
|
| 248 |
+
dt = time.time() - t0
|
| 249 |
+
print(f"L{a.layer} PV: out_mse {m['mse0']:.6e} -> {m['mse1']:.6e} "
|
| 250 |
+
f"(rel {m['rel0']:.4f}->{m['rel1']:.4f}) | "
|
| 251 |
+
f"w13_relerr {m['w13rel0']:.4f}->{m['w13rel1']:.4f} "
|
| 252 |
+
f"w2_relerr {m['w2rel0']:.4f}->{m['w2rel1']:.4f} ({dt:.0f}s)")
|
| 253 |
+
if a.save:
|
| 254 |
+
torch.save(out13, f"{OUT}/layer_{a.layer}_w13.pt")
|
| 255 |
+
torch.save(out2, f"{OUT}/layer_{a.layer}_w2.pt")
|
| 256 |
+
print("saved.")
|
code/pv_all.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Full output-preserving (PV) tuning (Phase E2) across all MoE layers.
|
| 3 |
+
Shards layers across CUDA_VISIBLE_DEVICES GPUs, 1 spawn-worker/GPU, resumable
|
| 4 |
+
(skips layers whose BOTH parts_pv files already exist).
|
| 5 |
+
Run: CUDA_VISIBLE_DEVICES=4,5,6,7 python pv_all.py [--steps 300]
|
| 6 |
+
"""
|
| 7 |
+
import os, sys, time, argparse
|
| 8 |
+
import torch.multiprocessing as mp
|
| 9 |
+
import ink_common as ic
|
| 10 |
+
|
| 11 |
+
OUT = "/data/inkling-quant/parts_pv"
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def done(L):
|
| 15 |
+
return (os.path.exists(f"{OUT}/layer_{L}_w13.pt")
|
| 16 |
+
and os.path.exists(f"{OUT}/layer_{L}_w2.pt"))
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def worker(gpu, layers, steps):
|
| 20 |
+
os.environ["CUDA_VISIBLE_DEVICES"] = str(gpu)
|
| 21 |
+
os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "expandable_segments:True"
|
| 22 |
+
import torch, pv
|
| 23 |
+
for L in layers:
|
| 24 |
+
if done(L):
|
| 25 |
+
print(f"[gpu{gpu}] skip L{L} (exists)", flush=True); continue
|
| 26 |
+
t0 = time.time()
|
| 27 |
+
try:
|
| 28 |
+
out13, out2, m = pv.pv_layer(L, steps=steps, device="cuda", verbose=False)
|
| 29 |
+
for proj, obj in (("w13", out13), ("w2", out2)):
|
| 30 |
+
dst = f"{OUT}/layer_{L}_{proj}.pt"
|
| 31 |
+
tmp = dst + ".tmp"
|
| 32 |
+
torch.save(obj, tmp); os.replace(tmp, dst)
|
| 33 |
+
improved = "OK" if m["mse1"] < m["mse0"] else "WORSE"
|
| 34 |
+
print(f"[gpu{gpu}] L{L} PV out_mse {m['mse0']:.4e}->{m['mse1']:.4e} "
|
| 35 |
+
f"(rel {m['rel0']:.4f}->{m['rel1']:.4f}) {improved} | "
|
| 36 |
+
f"w13rel {m['w13rel0']:.4f}->{m['w13rel1']:.4f} "
|
| 37 |
+
f"w2rel {m['w2rel0']:.4f}->{m['w2rel1']:.4f} "
|
| 38 |
+
f"({time.time()-t0:.0f}s) saved", flush=True)
|
| 39 |
+
except Exception as ex:
|
| 40 |
+
import traceback
|
| 41 |
+
print(f"[gpu{gpu}] L{L} FAILED: {ex}", flush=True)
|
| 42 |
+
traceback.print_exc()
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def main():
|
| 46 |
+
ap = argparse.ArgumentParser()
|
| 47 |
+
ap.add_argument("--steps", type=int, default=300)
|
| 48 |
+
a = ap.parse_args()
|
| 49 |
+
os.makedirs(OUT, exist_ok=True)
|
| 50 |
+
gpus = [int(x) for x in os.environ.get("CUDA_VISIBLE_DEVICES", "0").split(",")]
|
| 51 |
+
layers = ic.moe_layers()
|
| 52 |
+
todo = [L for L in layers if not done(L)]
|
| 53 |
+
print(f"{len(layers)} layers, {len(todo)} remaining, {len(gpus)} GPUs {gpus}", flush=True)
|
| 54 |
+
shards = {g: [] for g in gpus}
|
| 55 |
+
for i, L in enumerate(todo):
|
| 56 |
+
shards[gpus[i % len(gpus)]].append(L)
|
| 57 |
+
mp.set_start_method("spawn", force=True)
|
| 58 |
+
ps = [mp.Process(target=worker, args=(g, shards[g], a.steps)) for g in gpus]
|
| 59 |
+
for p in ps: p.start()
|
| 60 |
+
for p in ps: p.join()
|
| 61 |
+
d = sum(1 for L in layers if done(L))
|
| 62 |
+
print(f"PV DONE: {d}/{len(layers)} layers", flush=True)
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
if __name__ == "__main__":
|
| 66 |
+
main()
|
code/pv_all_v3.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Full PV-v3 across all MoE layers, with held-out early-stop (pv_v3).
|
| 3 |
+
Shards layers across CUDA_VISIBLE_DEVICES, 1 spawn-worker/GPU, resumable.
|
| 4 |
+
Needs acts_v3/ (train) + acts_v3_heldout/ (val) captured first.
|
| 5 |
+
Run: CUDA_VISIBLE_DEVICES=4,5,6,7 python pv_all_v3.py [--max-steps 2000] [--patience 6]
|
| 6 |
+
"""
|
| 7 |
+
import os, time, argparse
|
| 8 |
+
import torch.multiprocessing as mp
|
| 9 |
+
import ink_common as ic
|
| 10 |
+
|
| 11 |
+
import os as _os
|
| 12 |
+
OUT = _os.environ.get("INK_PV_OUT", "/data/inkling-quant/parts_pv_v3")
|
| 13 |
+
|
| 14 |
+
def done(L):
|
| 15 |
+
return (os.path.exists(f"{OUT}/layer_{L}_w13.pt")
|
| 16 |
+
and os.path.exists(f"{OUT}/layer_{L}_w2.pt"))
|
| 17 |
+
|
| 18 |
+
def worker(gpu, layers, max_steps, eval_every, patience, tune_cb):
|
| 19 |
+
os.environ["CUDA_VISIBLE_DEVICES"] = str(gpu)
|
| 20 |
+
os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "expandable_segments:True"
|
| 21 |
+
import torch, pv_v3
|
| 22 |
+
for L in layers:
|
| 23 |
+
if done(L):
|
| 24 |
+
print(f"[gpu{gpu}] skip L{L} (exists)", flush=True); continue
|
| 25 |
+
t0 = time.time()
|
| 26 |
+
try:
|
| 27 |
+
out13, out2, m = pv_v3.pv_layer_v3(L, max_steps=max_steps, eval_every=eval_every,
|
| 28 |
+
patience=patience, tune_cb=tune_cb,
|
| 29 |
+
device="cuda", verbose=False)
|
| 30 |
+
for proj, obj in (("w13", out13), ("w2", out2)):
|
| 31 |
+
dst = f"{OUT}/layer_{L}_{proj}.pt"; tmp = dst + ".tmp"
|
| 32 |
+
torch.save(obj, tmp); os.replace(tmp, dst)
|
| 33 |
+
tag = "OK" if (m["val1"] <= m["val0"] or m["val0"] != m["val0"]) else "WORSE"
|
| 34 |
+
print(f"[gpu{gpu}] L{L} PVv3 val_rel {m['val0']:.4f}->{m['val1']:.4f} "
|
| 35 |
+
f"best@{m['best_step']} {tag} ({time.time()-t0:.0f}s) saved", flush=True)
|
| 36 |
+
except Exception as ex:
|
| 37 |
+
import traceback
|
| 38 |
+
print(f"[gpu{gpu}] L{L} FAILED: {ex}", flush=True); traceback.print_exc()
|
| 39 |
+
|
| 40 |
+
def main():
|
| 41 |
+
ap = argparse.ArgumentParser()
|
| 42 |
+
ap.add_argument("--max-steps", type=int, default=2000)
|
| 43 |
+
ap.add_argument("--eval-every", type=int, default=25)
|
| 44 |
+
ap.add_argument("--patience", type=int, default=6)
|
| 45 |
+
ap.add_argument("--no-tune-cb", action="store_true")
|
| 46 |
+
a = ap.parse_args()
|
| 47 |
+
os.makedirs(OUT, exist_ok=True)
|
| 48 |
+
gpus = [int(x) for x in os.environ.get("CUDA_VISIBLE_DEVICES", "0").split(",")]
|
| 49 |
+
layers = ic.moe_layers()
|
| 50 |
+
todo = [L for L in layers if not done(L)]
|
| 51 |
+
print(f"{len(layers)} layers, {len(todo)} remaining, {len(gpus)} GPUs {gpus}", flush=True)
|
| 52 |
+
shards = {g: [] for g in gpus}
|
| 53 |
+
for i, L in enumerate(todo):
|
| 54 |
+
shards[gpus[i % len(gpus)]].append(L)
|
| 55 |
+
mp.set_start_method("spawn", force=True)
|
| 56 |
+
ps = [mp.Process(target=worker, args=(g, shards[g], a.max_steps, a.eval_every,
|
| 57 |
+
a.patience, not a.no_tune_cb)) for g in gpus]
|
| 58 |
+
for p in ps: p.start()
|
| 59 |
+
for p in ps: p.join()
|
| 60 |
+
d = sum(1 for L in layers if done(L))
|
| 61 |
+
print(f"PVv3 DONE: {d}/{len(layers)} layers", flush=True)
|
| 62 |
+
|
| 63 |
+
if __name__ == "__main__":
|
| 64 |
+
main()
|
code/pv_gen_check.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch, json, random, statistics as st
|
| 2 |
+
import ink_common as ic
|
| 3 |
+
random.seed(0); dev="cuda"
|
| 4 |
+
assign=json.load(open("/data/inkling-quant/assign_512k_v2.json"))["hot_experts"]
|
| 5 |
+
tc=ic.ShardReader(ic.BF16_DIR)
|
| 6 |
+
def expert_out(W13,W2,x): # x[n,6144]; W13[6144,6144] interleaved; W2[6144,3072]
|
| 7 |
+
gate=W13[0::2]; up=W13[1::2]
|
| 8 |
+
h=torch.nn.functional.silu(x@gate.t())*(x@up.t())
|
| 9 |
+
return h@W2.t()
|
| 10 |
+
layers=[3,8,15,22,30,38,45,52,58,63]
|
| 11 |
+
conv=[]; pv=[]
|
| 12 |
+
for L in layers:
|
| 13 |
+
a=torch.load(f"/data/inkling-quant/acts_heldout/acts_layer_{L}.pt",map_location=dev)
|
| 14 |
+
x=a["x"].float(); ids=a["ids"]
|
| 15 |
+
hot=set(assign[str(L)]); cold=[e for e in range(256) if e not in hot]
|
| 16 |
+
pc13=torch.load(f"/data/inkling-quant/parts_conv/layer_{L}_w13.pt",map_location="cpu")
|
| 17 |
+
pc2 =torch.load(f"/data/inkling-quant/parts_conv/layer_{L}_w2.pt",map_location="cpu")
|
| 18 |
+
pp13=torch.load(f"/data/inkling-quant/parts_pv/layer_{L}_w13.pt",map_location="cpu")
|
| 19 |
+
pp2 =torch.load(f"/data/inkling-quant/parts_pv/layer_{L}_w2.pt",map_location="cpu")
|
| 20 |
+
for e in random.sample(cold,min(12,len(cold))):
|
| 21 |
+
m=(ids==e).any(1)
|
| 22 |
+
if int(m.sum())<8: continue
|
| 23 |
+
xe=x[m][:2048]
|
| 24 |
+
ft=expert_out(ic.teacher_expert(tc,L,"w13",e,device=dev).float(),ic.teacher_expert(tc,L,"w2",e,device=dev).float(),xe)
|
| 25 |
+
nt=ft.norm()
|
| 26 |
+
conv.append(((expert_out(ic.aqlm_dequant_expert(pc13,"w13",e,device=dev).float(),ic.aqlm_dequant_expert(pc2,"w2",e,device=dev).float(),xe)-ft).norm()/nt).item())
|
| 27 |
+
pv.append(((expert_out(ic.aqlm_dequant_expert(pp13,"w13",e,device=dev).float(),ic.aqlm_dequant_expert(pp2,"w2",e,device=dev).float(),xe)-ft).norm()/nt).item())
|
| 28 |
+
c=st.mean(conv); p=st.mean(pv)
|
| 29 |
+
print(f"HELD-OUT output rel-err over {len(conv)} cold experts:")
|
| 30 |
+
print(f" converged (parts_conv): {c:.4f}")
|
| 31 |
+
print(f" PV-tuned (parts_pv): {p:.4f}")
|
| 32 |
+
print(f" PV vs converged (held-out): {100*(1-p/c):+.1f}%")
|
| 33 |
+
print(f" VERDICT: {'PV GENERALIZES -> use parts_pv' if p < c*0.98 else 'PV OVERFIT / no held-out gain -> use parts_conv'}")
|
code/pv_generalize.py
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""PV generalization check (the v2 ship decision).
|
| 3 |
+
|
| 4 |
+
On HELD-OUT tokens (acts_heldout/, disjoint from calib), for a SAMPLE of cold
|
| 5 |
+
experts across shallow/mid/deep layers, compute the expert OUTPUT rel-err vs the
|
| 6 |
+
BF16 teacher for TWO weight sources:
|
| 7 |
+
(a) converged parts_conv (REAP+converge only)
|
| 8 |
+
(b) PV-tuned parts_pv (tuned on calib tokens)
|
| 9 |
+
|
| 10 |
+
Cold = experts in the v2 complement (assign_512k_v2.json) AND actually PV-tuned
|
| 11 |
+
(in the part's stored cold_experts). Identical held-out tokens used for (a)/(b).
|
| 12 |
+
|
| 13 |
+
Expert output rel-err = ||f_e(x)-f_teacher(x)||^2 / ||f_teacher(x)||^2, where
|
| 14 |
+
f_e(x)=w2 @ (silu(gate@x^T)*(up@x^T)), w13 ROW-INTERLEAVED gate=w13[0::2]/up=w13[1::2].
|
| 15 |
+
|
| 16 |
+
VERDICT: if PV held-out rel-err is LOWER than converged by >2% relative -> PV
|
| 17 |
+
GENERALIZES (ship parts_pv). Else PV OVERFIT (ship parts_conv).
|
| 18 |
+
"""
|
| 19 |
+
from __future__ import annotations
|
| 20 |
+
import json, argparse, time
|
| 21 |
+
import torch
|
| 22 |
+
import ink_common as ic
|
| 23 |
+
import pv # expert_forward
|
| 24 |
+
|
| 25 |
+
ACTS_HELDOUT = "/data/inkling-quant/acts_heldout"
|
| 26 |
+
CONV = "/data/inkling-quant/parts_conv"
|
| 27 |
+
PV = "/data/inkling-quant/parts_pv"
|
| 28 |
+
ASSIGN_V2 = "/data/inkling-quant/assign_512k_v2.json"
|
| 29 |
+
MIN_TOK = 16
|
| 30 |
+
MAX_TOK = 4096
|
| 31 |
+
DEFAULT_LAYERS = [3, 8, 12, 20, 28, 36, 44, 52, 58, 65]
|
| 32 |
+
N_EXP_PER_LAYER = 16
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
@torch.no_grad()
|
| 36 |
+
def expert_relerr(part13, part2, e, xe, ftea, device):
|
| 37 |
+
Wq13 = ic.aqlm_dequant_expert(part13, "w13", e, device=device).float()
|
| 38 |
+
Wq2 = ic.aqlm_dequant_expert(part2, "w2", e, device=device).float()
|
| 39 |
+
fq = pv.expert_forward(Wq13, Wq2, xe)
|
| 40 |
+
diff = fq - ftea
|
| 41 |
+
return float(diff.pow(2).sum() / ftea.pow(2).sum().clamp_min(1e-12))
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
@torch.no_grad()
|
| 45 |
+
def eval_layer(layer, n_exp, device):
|
| 46 |
+
v2 = json.load(open(ASSIGN_V2))["hot_experts"]
|
| 47 |
+
v2_cold = set(range(ic.N_EXPERTS)) - set(v2.get(str(layer), []))
|
| 48 |
+
c13 = torch.load(f"{CONV}/layer_{layer}_w13.pt", map_location="cpu")
|
| 49 |
+
c2 = torch.load(f"{CONV}/layer_{layer}_w2.pt", map_location="cpu")
|
| 50 |
+
p13 = torch.load(f"{PV}/layer_{layer}_w13.pt", map_location="cpu")
|
| 51 |
+
p2 = torch.load(f"{PV}/layer_{layer}_w2.pt", map_location="cpu")
|
| 52 |
+
pv_tuned = set(c13["cold_experts"]) # experts PV actually tuned (v1 cold)
|
| 53 |
+
cand = sorted(v2_cold & pv_tuned) # v2-cold AND PV-tuned
|
| 54 |
+
|
| 55 |
+
a = torch.load(f"{ACTS_HELDOUT}/acts_layer_{layer}.pt")
|
| 56 |
+
x = a["x"].to(device).float() # [N,6144]
|
| 57 |
+
ids = a["ids"].to(device) # [N,6]
|
| 58 |
+
|
| 59 |
+
# eligible = enough held-out tokens; sample a seeded subset
|
| 60 |
+
counts = {e: int((ids == e).any(1).sum()) for e in cand}
|
| 61 |
+
elig = [e for e in cand if counts[e] >= MIN_TOK]
|
| 62 |
+
gen = torch.Generator().manual_seed(2024 + layer)
|
| 63 |
+
if len(elig) > n_exp:
|
| 64 |
+
perm = torch.randperm(len(elig), generator=gen).tolist()
|
| 65 |
+
sample = sorted(elig[i] for i in perm[:n_exp])
|
| 66 |
+
else:
|
| 67 |
+
sample = elig
|
| 68 |
+
|
| 69 |
+
tc = ic.ShardReader(ic.BF16_DIR)
|
| 70 |
+
gsel = torch.Generator(device=device).manual_seed(99 + layer)
|
| 71 |
+
conv_errs, pv_errs, toks = [], [], []
|
| 72 |
+
for e in sample:
|
| 73 |
+
m = (ids == e).any(1)
|
| 74 |
+
xe = x[m]
|
| 75 |
+
if xe.shape[0] > MAX_TOK:
|
| 76 |
+
sel = torch.randperm(xe.shape[0], generator=gsel, device=device)[:MAX_TOK]
|
| 77 |
+
xe = xe[sel]
|
| 78 |
+
xe = xe.contiguous()
|
| 79 |
+
wt13 = ic.teacher_expert(tc, layer, "w13", e, device=device).float()
|
| 80 |
+
wt2 = ic.teacher_expert(tc, layer, "w2", e, device=device).float()
|
| 81 |
+
ftea = pv.expert_forward(wt13, wt2, xe)
|
| 82 |
+
del wt13, wt2
|
| 83 |
+
conv_errs.append(expert_relerr(c13, c2, e, xe, ftea, device))
|
| 84 |
+
pv_errs.append(expert_relerr(p13, p2, e, xe, ftea, device))
|
| 85 |
+
toks.append(int(xe.shape[0]))
|
| 86 |
+
del ftea, xe
|
| 87 |
+
del x, ids
|
| 88 |
+
torch.cuda.empty_cache()
|
| 89 |
+
|
| 90 |
+
conv_m = sum(conv_errs) / len(conv_errs)
|
| 91 |
+
pv_m = sum(pv_errs) / len(pv_errs)
|
| 92 |
+
# calib numbers stored in the PV part (combined-expert output relerr on calib)
|
| 93 |
+
calib_conv = float(p13["output_relerr_before"])
|
| 94 |
+
calib_pv = float(p13["output_relerr_after"])
|
| 95 |
+
return dict(layer=layer, n=len(sample), tot_tok=sum(toks),
|
| 96 |
+
conv=conv_m, pv=pv_m, calib_conv=calib_conv, calib_pv=calib_pv,
|
| 97 |
+
experts=sample)
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def main():
|
| 101 |
+
ap = argparse.ArgumentParser()
|
| 102 |
+
ap.add_argument("--layers", type=int, nargs="+", default=DEFAULT_LAYERS)
|
| 103 |
+
ap.add_argument("--n_exp", type=int, default=N_EXP_PER_LAYER)
|
| 104 |
+
a = ap.parse_args()
|
| 105 |
+
device = "cuda"
|
| 106 |
+
print(f"HELD-OUT PV-generalization check | layers {a.layers} | <= {a.n_exp} cold experts/layer")
|
| 107 |
+
print(f"{'layer':>5} {'nexp':>4} {'tok':>6} | {'HELDOUT conv':>13} {'HELDOUT pv':>11} {'d%':>7} || "
|
| 108 |
+
f"{'CALIB conv':>10} {'CALIB pv':>9} {'d%':>7}")
|
| 109 |
+
rows = []
|
| 110 |
+
for L in a.layers:
|
| 111 |
+
t0 = time.time()
|
| 112 |
+
r = eval_layer(L, a.n_exp, device)
|
| 113 |
+
rows.append(r)
|
| 114 |
+
hd = 100 * (r["pv"] - r["conv"]) / r["conv"]
|
| 115 |
+
cd = 100 * (r["calib_pv"] - r["calib_conv"]) / r["calib_conv"]
|
| 116 |
+
print(f"{r['layer']:>5} {r['n']:>4} {r['tot_tok']:>6} | "
|
| 117 |
+
f"{r['conv']:>13.4f} {r['pv']:>11.4f} {hd:>+6.1f}% || "
|
| 118 |
+
f"{r['calib_conv']:>10.4f} {r['calib_pv']:>9.4f} {cd:>+6.1f}% ({time.time()-t0:.0f}s)", flush=True)
|
| 119 |
+
|
| 120 |
+
conv_mean = sum(r["conv"] for r in rows) / len(rows)
|
| 121 |
+
pv_mean = sum(r["pv"] for r in rows) / len(rows)
|
| 122 |
+
cconv = sum(r["calib_conv"] for r in rows) / len(rows)
|
| 123 |
+
cpv = sum(r["calib_pv"] for r in rows) / len(rows)
|
| 124 |
+
hd = 100 * (pv_mean - conv_mean) / conv_mean
|
| 125 |
+
cd = 100 * (cpv - cconv) / cconv
|
| 126 |
+
print("-" * 96)
|
| 127 |
+
print(f"{'MEAN':>5} {sum(r['n'] for r in rows):>4} {sum(r['tot_tok'] for r in rows):>6} | "
|
| 128 |
+
f"{conv_mean:>13.4f} {pv_mean:>11.4f} {hd:>+6.1f}% || "
|
| 129 |
+
f"{cconv:>10.4f} {cpv:>9.4f} {cd:>+6.1f}%")
|
| 130 |
+
print()
|
| 131 |
+
print(f"HELD-OUT: converged rel-err {conv_mean:.4f} vs PV rel-err {pv_mean:.4f} ({hd:+.1f}%)")
|
| 132 |
+
print(f"CALIB : converged rel-err {cconv:.4f} vs PV rel-err {cpv:.4f} ({cd:+.1f}%)")
|
| 133 |
+
if hd < -2.0:
|
| 134 |
+
print(f"\nVERDICT: PV GENERALIZES (held-out {hd:+.1f}% <= -2%). SHIP parts_pv for v2 cold experts.")
|
| 135 |
+
elif hd > 2.0:
|
| 136 |
+
print(f"\nVERDICT: PV OVERFIT (held-out {hd:+.1f}% WORSE). SHIP parts_conv for v2 cold experts.")
|
| 137 |
+
else:
|
| 138 |
+
print(f"\nVERDICT: PV ~= converged on held-out ({hd:+.1f}%, within +-2%) despite winning calib "
|
| 139 |
+
f"({cd:+.1f}%) => PV did NOT generalize. SHIP parts_conv (simpler, no overfit risk).")
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
if __name__ == "__main__":
|
| 143 |
+
main()
|
code/pv_v3.py
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""PV-tune v3 — output-preserving tune WITH a held-out early-stop.
|
| 3 |
+
|
| 4 |
+
Why v3 differs from pv.py: v2's PV overfit (calib output-err -32% but held-out
|
| 5 |
+
+10.5% WORSE; weight-err rose 0.30->0.43) because it ran a FIXED step count on a
|
| 6 |
+
tiny per-expert calib sample. v3 fixes that two ways:
|
| 7 |
+
1. TRAIN on the larger calib_v3 acts (acts_v3/), so cold experts see more tokens.
|
| 8 |
+
2. EARLY-STOP on a DISJOINT held-out acts capture (acts_v3_heldout/): every
|
| 9 |
+
EVAL_EVERY steps, measure aggregate held-out output rel-err; keep the BEST-val
|
| 10 |
+
params; stop after PATIENCE evals without improvement. We SAVE best-val, not last.
|
| 11 |
+
|
| 12 |
+
Same schema/mechanics as pv.py (tunes shared codebooks + per-cold-expert scales,
|
| 13 |
+
codes fixed, w13 row-interleaved gate/up). Assignment + converged init = v2's
|
| 14 |
+
(assign_512k_v2.json + parts_conv) since v3 is strictly PV-focused (tiering/MTP unchanged).
|
| 15 |
+
|
| 16 |
+
GPU phase — needs acts captured FIRST (see run_capture_acts for calib_v3 -> acts_v3/
|
| 17 |
+
and heldout_v3 -> acts_v3_heldout/). Writes parts_pv_v3/layer_{L}_{w13,w2}.pt.
|
| 18 |
+
"""
|
| 19 |
+
from __future__ import annotations
|
| 20 |
+
import argparse, os, time, json
|
| 21 |
+
import torch
|
| 22 |
+
import torch.nn.functional as F
|
| 23 |
+
import ink_common as ic
|
| 24 |
+
from pv import dequant_w13, dequant_w2, expert_forward # reuse pure dequant/forward
|
| 25 |
+
|
| 26 |
+
# PV trains on the UNION of text + image + audio acts (multimodal coverage so
|
| 27 |
+
# image/audio-routed experts aren't degraded); each modality captured to its own
|
| 28 |
+
# dir. Only dirs that exist are used.
|
| 29 |
+
import os as _os
|
| 30 |
+
# default = merged multimodal acts + v3 tiering/converge (env-overridable)
|
| 31 |
+
TRAIN_ACT_DIRS = _os.environ.get("INK_ACTS_TRAIN", "/data/inkling-quant/acts_v3_all").split(",")
|
| 32 |
+
VAL_ACT_DIRS = _os.environ.get("INK_ACTS_VAL", "/data/inkling-quant/acts_v3_all_held").split(",")
|
| 33 |
+
CONV = _os.environ.get("INK_CONV", "/data/inkling-quant/parts_conv_v3")
|
| 34 |
+
OUT = _os.environ.get("INK_PV_OUT", "/data/inkling-quant/parts_pv_v3")
|
| 35 |
+
ASSIGN = _os.environ.get("INK_ASSIGN", "/data/inkling-quant/assign_512k_v3.json")
|
| 36 |
+
OUTD = 6144
|
| 37 |
+
MIN_TOK = 8 # min TRAIN tokens to tune an expert
|
| 38 |
+
MIN_VAL = 4 # min held-out tokens for an expert to count toward the val signal
|
| 39 |
+
MAX_TOK = 2048 # train-token cap/expert (was 8192 -> ~4x faster steps; still ample for a codebook tune)
|
| 40 |
+
MAX_VAL = 1024
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def cold_experts(layer, assign):
|
| 44 |
+
hot = set(assign["hot_experts"].get(str(layer), []))
|
| 45 |
+
return [e for e in range(ic.N_EXPERTS) if e not in hot]
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def _tokens_multi(dirs, layer, cold, device, cap, seed):
|
| 49 |
+
"""Union tokens across acts dirs (text+image+audio) per cold expert, then cap."""
|
| 50 |
+
acc = {} # ci -> list of xe tensors
|
| 51 |
+
for d in dirs:
|
| 52 |
+
p = f"{d}/acts_layer_{layer}.pt"
|
| 53 |
+
if not os.path.exists(p):
|
| 54 |
+
continue
|
| 55 |
+
a = torch.load(p, map_location="cpu")
|
| 56 |
+
x = a["x"].to(device).float(); ids = a["ids"].to(device)
|
| 57 |
+
for ci, e in enumerate(cold):
|
| 58 |
+
m = (ids == e).any(1)
|
| 59 |
+
if int(m.sum()) == 0:
|
| 60 |
+
continue
|
| 61 |
+
acc.setdefault(ci, []).append(x[m].contiguous())
|
| 62 |
+
del x, ids; torch.cuda.empty_cache()
|
| 63 |
+
gen = torch.Generator(device=device); gen.manual_seed(seed)
|
| 64 |
+
out = {}
|
| 65 |
+
for ci, parts in acc.items():
|
| 66 |
+
xe = torch.cat(parts, 0); n = xe.shape[0]
|
| 67 |
+
if n > cap:
|
| 68 |
+
sel = torch.randperm(n, generator=gen, device=device)[:cap]; xe = xe[sel]
|
| 69 |
+
out[ci] = xe.contiguous()
|
| 70 |
+
torch.cuda.empty_cache()
|
| 71 |
+
return out
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def build_split(layer, cold, device):
|
| 75 |
+
"""Train + val data (multimodal union). For each expert build teacher outputs once.
|
| 76 |
+
train: [(ci,e,xe,ftea)] with >=MIN_TOK ; val: [(ci,e,xe,ftea)] with >=MIN_VAL."""
|
| 77 |
+
xt_by = _tokens_multi(TRAIN_ACT_DIRS, layer, cold, device, MAX_TOK, 1234+layer)
|
| 78 |
+
xv_by = _tokens_multi(VAL_ACT_DIRS, layer, cold, device, MAX_VAL, 9876+layer)
|
| 79 |
+
tc = ic.ShardReader(ic.BF16_DIR)
|
| 80 |
+
train, val = [], []
|
| 81 |
+
for ci, e in enumerate(cold):
|
| 82 |
+
need = (ci in xt_by and xt_by[ci].shape[0] >= MIN_TOK) or \
|
| 83 |
+
(ci in xv_by and xv_by[ci].shape[0] >= MIN_VAL)
|
| 84 |
+
if not need:
|
| 85 |
+
continue
|
| 86 |
+
w13 = ic.teacher_expert(tc, layer, "w13", e, device=device).float()
|
| 87 |
+
w2 = ic.teacher_expert(tc, layer, "w2", e, device=device).float()
|
| 88 |
+
if ci in xt_by and xt_by[ci].shape[0] >= MIN_TOK:
|
| 89 |
+
xe = xt_by[ci]
|
| 90 |
+
with torch.no_grad(): ft = expert_forward(w13, w2, xe)
|
| 91 |
+
train.append((ci, e, xe, ft.contiguous()))
|
| 92 |
+
if ci in xv_by and xv_by[ci].shape[0] >= MIN_VAL:
|
| 93 |
+
xe = xv_by[ci]
|
| 94 |
+
with torch.no_grad(): ft = expert_forward(w13, w2, xe)
|
| 95 |
+
val.append((ci, e, xe, ft.contiguous()))
|
| 96 |
+
del w13, w2
|
| 97 |
+
torch.cuda.empty_cache()
|
| 98 |
+
return train, val
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
@torch.no_grad()
|
| 102 |
+
def agg_relerr(data, codes13, codes2, cb13, cb2_0, cb2_1, s13, s2):
|
| 103 |
+
if not data: return float("nan")
|
| 104 |
+
rel = 0.0
|
| 105 |
+
for (ci, e, xe, ft) in data:
|
| 106 |
+
Wq13 = dequant_w13(ci, codes13, cb13, s13)
|
| 107 |
+
Wq2 = dequant_w2(ci, codes2, cb2_0, cb2_1, s2)
|
| 108 |
+
fq = expert_forward(Wq13, Wq2, xe)
|
| 109 |
+
rel += float((fq-ft).pow(2).sum() / ft.pow(2).sum().clamp_min(1e-12))
|
| 110 |
+
return rel / len(data)
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def pv_layer_v3(layer, max_steps=2000, eval_every=25, patience=6,
|
| 114 |
+
lr_cb=1e-4, lr_s=1e-3, tune_cb=True, device="cuda", verbose=True):
|
| 115 |
+
assign = json.load(open(ASSIGN))
|
| 116 |
+
cold = cold_experts(layer, assign); coldt = torch.tensor(cold)
|
| 117 |
+
p13 = torch.load(f"{CONV}/layer_{layer}_w13.pt", map_location="cpu")
|
| 118 |
+
p2 = torch.load(f"{CONV}/layer_{layer}_w2.pt", map_location="cpu")
|
| 119 |
+
codes13 = p13["w13_codes"][coldt].to(device)
|
| 120 |
+
codes2 = p2["w2_codes"][coldt].to(device)
|
| 121 |
+
cb13 = p13["w13_codebooks"][0].float().to(device).clone().requires_grad_(tune_cb)
|
| 122 |
+
cb2_0 = p2["w2_codebooks"][0].float().to(device).clone().requires_grad_(tune_cb)
|
| 123 |
+
cb2_1 = p2["w2_codebooks"][1].float().to(device).clone().requires_grad_(tune_cb)
|
| 124 |
+
s13 = p13["w13_scales"][coldt].float().to(device).clone().requires_grad_()
|
| 125 |
+
s2 = p2["w2_scales"][coldt].float().to(device).clone().requires_grad_()
|
| 126 |
+
|
| 127 |
+
train, val = build_split(layer, cold, device)
|
| 128 |
+
if not train:
|
| 129 |
+
raise RuntimeError(f"L{layer}: no cold experts with >= {MIN_TOK} train tokens")
|
| 130 |
+
if verbose:
|
| 131 |
+
print(f" L{layer}: {len(cold)} cold | train {len(train)} | val {len(val)}"
|
| 132 |
+
+ ("" if val else " (WARN no held-out acts -> falls back to fixed-step, NO early-stop)"), flush=True)
|
| 133 |
+
|
| 134 |
+
val0 = agg_relerr(val, codes13, codes2, cb13, cb2_0, cb2_1, s13, s2) if val else float("nan")
|
| 135 |
+
params = [{"params": [s13, s2], "lr": lr_s}]
|
| 136 |
+
if tune_cb: params.append({"params": [cb13, cb2_0, cb2_1], "lr": lr_cb})
|
| 137 |
+
opt = torch.optim.Adam(params)
|
| 138 |
+
|
| 139 |
+
def snapshot():
|
| 140 |
+
return (cb13.detach().clone(), cb2_0.detach().clone(), cb2_1.detach().clone(),
|
| 141 |
+
s13.detach().clone(), s2.detach().clone())
|
| 142 |
+
best_val = val0; best = snapshot(); best_step = 0; bad = 0
|
| 143 |
+
|
| 144 |
+
for step in range(1, max_steps+1):
|
| 145 |
+
opt.zero_grad(set_to_none=True); tot = 0.0
|
| 146 |
+
for (ci, e, xe, ft) in train:
|
| 147 |
+
Wq13 = dequant_w13(ci, codes13, cb13, s13)
|
| 148 |
+
Wq2 = dequant_w2(ci, codes2, cb2_0, cb2_1, s2)
|
| 149 |
+
loss = F.mse_loss(expert_forward(Wq13, Wq2, xe), ft)
|
| 150 |
+
loss.backward(); tot += float(loss.detach())
|
| 151 |
+
opt.step()
|
| 152 |
+
if val and (step % eval_every == 0 or step == max_steps):
|
| 153 |
+
vr = agg_relerr(val, codes13, codes2, cb13, cb2_0, cb2_1, s13, s2)
|
| 154 |
+
improved = vr < best_val - 1e-5
|
| 155 |
+
if improved:
|
| 156 |
+
best_val = vr; best = snapshot(); best_step = step; bad = 0
|
| 157 |
+
else:
|
| 158 |
+
bad += 1
|
| 159 |
+
if verbose:
|
| 160 |
+
print(f" L{layer} step{step}: train_mse={tot/len(train):.3e} "
|
| 161 |
+
f"val_rel={vr:.4f} best={best_val:.4f}@{best_step} bad={bad}", flush=True)
|
| 162 |
+
if bad >= patience:
|
| 163 |
+
if verbose: print(f" L{layer} EARLY-STOP at {step} (best val {best_val:.4f} @ {best_step})", flush=True)
|
| 164 |
+
break
|
| 165 |
+
|
| 166 |
+
# restore best-val params
|
| 167 |
+
cb13d, cb2_0d, cb2_1d, s13d, s2d = best
|
| 168 |
+
val1 = agg_relerr(val, codes13, codes2, cb13d, cb2_0d, cb2_1d, s13d, s2d) if val else float("nan")
|
| 169 |
+
if verbose:
|
| 170 |
+
print(f" L{layer} DONE: val_rel {val0:.4f} -> {val1:.4f} (best @ step {best_step})", flush=True)
|
| 171 |
+
|
| 172 |
+
with torch.no_grad():
|
| 173 |
+
full_s13 = p13["w13_scales"].clone(); full_s13[coldt] = s13d.half().cpu()
|
| 174 |
+
full_s2 = p2["w2_scales"].clone(); full_s2[coldt] = s2d.half().cpu()
|
| 175 |
+
common = dict(cold_experts=cold, val_relerr_before=val0, val_relerr_after=val1,
|
| 176 |
+
best_step=best_step, max_steps=max_steps, tuned_cb=tune_cb)
|
| 177 |
+
out13 = {"expert_ids": p13["expert_ids"], "w13_codes": p13["w13_codes"],
|
| 178 |
+
"w13_codebooks": [cb13d.half().cpu()], "w13_book_entries": p13["w13_book_entries"],
|
| 179 |
+
"w13_scales": full_s13, **common}
|
| 180 |
+
out2 = {"expert_ids": p2["expert_ids"], "w2_codes": p2["w2_codes"],
|
| 181 |
+
"w2_codebooks": [cb2_0d.half().cpu(), cb2_1d.half().cpu()],
|
| 182 |
+
"w2_book_entries": p2["w2_book_entries"], "w2_scales": full_s2, **common}
|
| 183 |
+
return out13, out2, dict(val0=val0, val1=val1, best_step=best_step)
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
if __name__ == "__main__":
|
| 187 |
+
ap = argparse.ArgumentParser()
|
| 188 |
+
ap.add_argument("--layer", type=int, required=True)
|
| 189 |
+
ap.add_argument("--max-steps", type=int, default=2000)
|
| 190 |
+
ap.add_argument("--eval-every", type=int, default=25)
|
| 191 |
+
ap.add_argument("--patience", type=int, default=6)
|
| 192 |
+
ap.add_argument("--lr_cb", type=float, default=1e-4)
|
| 193 |
+
ap.add_argument("--lr_s", type=float, default=1e-3)
|
| 194 |
+
ap.add_argument("--no-tune-cb", action="store_true", help="scales-only (most overfit-safe)")
|
| 195 |
+
ap.add_argument("--save", action="store_true")
|
| 196 |
+
a = ap.parse_args()
|
| 197 |
+
os.makedirs(OUT, exist_ok=True)
|
| 198 |
+
t0 = time.time()
|
| 199 |
+
out13, out2, m = pv_layer_v3(a.layer, a.max_steps, a.eval_every, a.patience,
|
| 200 |
+
a.lr_cb, a.lr_s, tune_cb=not a.no_tune_cb)
|
| 201 |
+
print(f"L{a.layer} PVv3: val_rel {m['val0']:.4f} -> {m['val1']:.4f} "
|
| 202 |
+
f"(best@{m['best_step']}) ({time.time()-t0:.0f}s)")
|
| 203 |
+
if a.save:
|
| 204 |
+
torch.save(out13, f"{OUT}/layer_{a.layer}_w13.pt")
|
| 205 |
+
torch.save(out2, f"{OUT}/layer_{a.layer}_w2.pt")
|
| 206 |
+
print("saved ->", OUT)
|
code/reap_merge.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Merge per-layer REAP tmp files -> /data/inkling-quant/reap_scores.pt and validate."""
|
| 3 |
+
import torch, glob, os, re
|
| 4 |
+
import ink_common as ic
|
| 5 |
+
|
| 6 |
+
TMP = os.environ.get("INK_REAP_TMP", "/data/inkling-quant/reap_tmp")
|
| 7 |
+
OUT = os.environ.get("INK_REAP_OUT", "/data/inkling-quant/reap_scores.pt")
|
| 8 |
+
|
| 9 |
+
def spearman(a, b):
|
| 10 |
+
a = a.double(); b = b.double()
|
| 11 |
+
ra = a.argsort().argsort().double()
|
| 12 |
+
rb = b.argsort().argsort().double()
|
| 13 |
+
ra = ra - ra.mean(); rb = rb - rb.mean()
|
| 14 |
+
return float((ra @ rb) / (ra.norm() * rb.norm()).clamp_min(1e-12))
|
| 15 |
+
|
| 16 |
+
layers = ic.moe_layers()
|
| 17 |
+
scores = {}
|
| 18 |
+
details = {}
|
| 19 |
+
for L in layers:
|
| 20 |
+
f = f"{TMP}/layer_{L}.pt"
|
| 21 |
+
if not os.path.exists(f):
|
| 22 |
+
print(f"MISSING layer {L}"); continue
|
| 23 |
+
d = torch.load(f, map_location="cpu")
|
| 24 |
+
scores[L] = d["score"].float()
|
| 25 |
+
details[L] = d
|
| 26 |
+
|
| 27 |
+
torch.save(scores, OUT)
|
| 28 |
+
print(f"WROTE {OUT} with {len(scores)} layers (expected 64)")
|
| 29 |
+
|
| 30 |
+
rs = torch.load("/data/inkling-quant/route_stats.pt", map_location="cpu")
|
| 31 |
+
print("\n== validation: spearman(reap_score, route wmass) for 3 layers ==")
|
| 32 |
+
for L in [layers[0], layers[len(layers)//2], layers[-1]]:
|
| 33 |
+
wm = rs[L]["wmass"].float()
|
| 34 |
+
sc = scores[L]
|
| 35 |
+
print(f"L{L}: spearman={spearman(sc, wm):+.3f} "
|
| 36 |
+
f"score mean={sc.mean():.4g} max={sc.max():.4g} min={sc.min():.4g} "
|
| 37 |
+
f"relerr mean={details[L]['relerr'].mean():.4f} "
|
| 38 |
+
f"experts_with_tokens={int((details[L]['ntok']>0).sum())}/256")
|
| 39 |
+
|
| 40 |
+
print("\n== per-layer score sanity (all layers) ==")
|
| 41 |
+
allmean = torch.tensor([scores[L].mean() for L in scores])
|
| 42 |
+
allmax = torch.tensor([scores[L].max() for L in scores])
|
| 43 |
+
zerocnt = sum(int((scores[L]==0).sum()) for L in scores)
|
| 44 |
+
print(f"layers={len(scores)} score.mean range [{allmean.min():.4g},{allmean.max():.4g}] "
|
| 45 |
+
f"score.max range [{allmax.min():.4g},{allmax.max():.4g}] total zero-experts={zerocnt}")
|
| 46 |
+
print("shapes ok:", all(scores[L].shape==(256,) for L in scores),
|
| 47 |
+
" finite:", all(torch.isfinite(scores[L]).all() for L in scores))
|
code/reap_worker.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Compute TRUE REAP expert-importance scores for a set of MoE layers on ONE gpu.
|
| 3 |
+
score_e = saliency_e * relerr_e
|
| 4 |
+
saliency_e = sum_t g_{t,e} * ||f_e(x_t)||_2 (teacher expert output L2 norm)
|
| 5 |
+
relerr_e = param-weighted 2-bit AQLM reconstruction error of expert e
|
| 6 |
+
Writes /data/inkling-quant/reap_tmp/layer_{L}.pt = fp32 tensor[256].
|
| 7 |
+
"""
|
| 8 |
+
import os, sys, time, json, traceback
|
| 9 |
+
import torch
|
| 10 |
+
import torch.nn.functional as F
|
| 11 |
+
sys.path.insert(0, "/data/inkling-quant/tools")
|
| 12 |
+
import ink_common as ic
|
| 13 |
+
|
| 14 |
+
DEV = "cuda"
|
| 15 |
+
R, S, K = 256, 2, 6 # routed, shared(sink), top-k
|
| 16 |
+
ROUTE_SCALE = 8.0
|
| 17 |
+
N13 = 6144 * 6144
|
| 18 |
+
N2 = 6144 * 3072
|
| 19 |
+
TMP = "/data/inkling-quant/reap_tmp"
|
| 20 |
+
os.makedirs(TMP, exist_ok=True)
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def gate_weights(x_f32, gw, bias, scale):
|
| 24 |
+
"""Replicate InklingGate.select_experts. Returns (topi[T,6] long, rw[T,6] fp32)."""
|
| 25 |
+
logits = x_f32 @ gw.T # [T,258] fp32
|
| 26 |
+
sel = torch.sigmoid(logits[:, :R]) + bias.view(1, -1) # [T,256]
|
| 27 |
+
_, topi = sel.topk(K, dim=1) # [T,6]
|
| 28 |
+
routed_raw = torch.gather(logits[:, :R], 1, topi) # [T,6]
|
| 29 |
+
sink_raw = logits[:, R:R + S] # [T,2]
|
| 30 |
+
active = torch.cat([routed_raw, sink_raw], dim=1) # [T,8]
|
| 31 |
+
lp = torch.minimum(active, torch.zeros_like(active)) - torch.log1p(torch.exp(-active.abs()))
|
| 32 |
+
lp = lp - lp.max(1, keepdim=True).values
|
| 33 |
+
e = torch.exp(lp)
|
| 34 |
+
w = e / e.sum(1, keepdim=True) * scale # [T,8]
|
| 35 |
+
return topi, w[:, :K].contiguous()
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def process_layer(L, reader, treader):
|
| 39 |
+
t0 = time.time()
|
| 40 |
+
a = torch.load(f"{__import__('os').environ.get('INK_ACTS','/data/inkling-quant/acts')}/acts_layer_{L}.pt", map_location="cpu")
|
| 41 |
+
n = int(a["n"])
|
| 42 |
+
x_f16 = a["x"][:n].to(DEV) # [T,6144] fp16
|
| 43 |
+
x_f32 = x_f16.float()
|
| 44 |
+
|
| 45 |
+
gw = reader.get(f"model.llm.layers.{L}.mlp.gate.weight").to(DEV).float()
|
| 46 |
+
bias = reader.get(f"model.llm.layers.{L}.mlp.gate.bias").to(DEV).float()
|
| 47 |
+
gs = float(reader.get(f"model.llm.layers.{L}.mlp.gate.global_scale")[0])
|
| 48 |
+
scale = ROUTE_SCALE * gs
|
| 49 |
+
|
| 50 |
+
topi, rw = gate_weights(x_f32, gw, bias, scale) # [T,6],[T,6]
|
| 51 |
+
del x_f32, gw, bias
|
| 52 |
+
|
| 53 |
+
# Full BF16 TEACHER weights for this layer (from BF16_DIR).
|
| 54 |
+
w13 = treader.get(ic._pfx(L, "w13")).to(DEV).to(torch.bfloat16) # [256,6144,6144]
|
| 55 |
+
w2 = treader.get(ic._pfx(L, "w2")).to(DEV).to(torch.bfloat16) # [256,6144,3072]
|
| 56 |
+
|
| 57 |
+
# AQLM parts (w13 CONVERGED, w2).
|
| 58 |
+
p13 = torch.load(f"{__import__('os').environ.get('INK_CONV_REF','/data/inkling-quant/parts_conv')}/layer_{L}_w13.pt", map_location="cpu")
|
| 59 |
+
p2 = torch.load(f"/data/inkling-quant/parts/layer_{L}_w2.pt", map_location="cpu")
|
| 60 |
+
|
| 61 |
+
saliency = torch.zeros(R, dtype=torch.float32, device=DEV)
|
| 62 |
+
relerr = torch.zeros(R, dtype=torch.float32, device=DEV)
|
| 63 |
+
ntok = torch.zeros(R, dtype=torch.long, device=DEV)
|
| 64 |
+
|
| 65 |
+
for e in range(R):
|
| 66 |
+
# ---- saliency ----
|
| 67 |
+
slot_mask = (topi == e) # [T,6] bool
|
| 68 |
+
tok_mask = slot_mask.any(1) # [T]
|
| 69 |
+
idx = tok_mask.nonzero(as_tuple=True)[0]
|
| 70 |
+
ne = idx.numel()
|
| 71 |
+
ntok[e] = ne
|
| 72 |
+
if ne > 0:
|
| 73 |
+
g_e = (rw * slot_mask).sum(1)[idx] # [ne] fp32
|
| 74 |
+
xe = x_f16.index_select(0, idx) # [ne,6144] fp16
|
| 75 |
+
we = w13[e] # [6144,6144] bf16
|
| 76 |
+
gate_e = we[0::2].to(torch.bfloat16) # [3072,6144]
|
| 77 |
+
up_e = we[1::2].to(torch.bfloat16) # [3072,6144]
|
| 78 |
+
xt = xe.to(torch.bfloat16).T # [6144,ne]
|
| 79 |
+
ge = gate_e @ xt # [3072,ne]
|
| 80 |
+
ue = up_e @ xt
|
| 81 |
+
h = F.silu(ge.float()) * ue.float() # [3072,ne] fp32
|
| 82 |
+
fe = w2[e].float() @ h # [6144,ne] fp32
|
| 83 |
+
norms = fe.norm(dim=0) # [ne]
|
| 84 |
+
saliency[e] = (g_e * norms).sum()
|
| 85 |
+
del xe, xt, ge, ue, h, fe, norms, g_e
|
| 86 |
+
|
| 87 |
+
# ---- relerr ----
|
| 88 |
+
W13 = w13[e].float()
|
| 89 |
+
dq13 = ic.aqlm_dequant_expert(p13, "w13", e, device=DEV).float()
|
| 90 |
+
rel13 = torch.linalg.norm(W13 - dq13) / torch.linalg.norm(W13).clamp_min(1e-12)
|
| 91 |
+
del W13, dq13
|
| 92 |
+
W2 = w2[e].float()
|
| 93 |
+
dq2 = ic.aqlm_dequant_expert(p2, "w2", e, device=DEV).float()
|
| 94 |
+
rel2 = torch.linalg.norm(W2 - dq2) / torch.linalg.norm(W2).clamp_min(1e-12)
|
| 95 |
+
del W2, dq2
|
| 96 |
+
relerr[e] = (rel13 * N13 + rel2 * N2) / (N13 + N2)
|
| 97 |
+
|
| 98 |
+
score = (saliency * relerr).cpu()
|
| 99 |
+
out = {
|
| 100 |
+
"score": score,
|
| 101 |
+
"saliency": saliency.cpu(),
|
| 102 |
+
"relerr": relerr.cpu(),
|
| 103 |
+
"ntok": ntok.cpu(),
|
| 104 |
+
}
|
| 105 |
+
torch.save(out, f"{TMP}/layer_{L}.pt")
|
| 106 |
+
dt = time.time() - t0
|
| 107 |
+
nz = int((ntok > 0).sum())
|
| 108 |
+
print(f"[L{L}] done {dt:.1f}s score mean={score.mean():.4g} max={score.max():.4g} "
|
| 109 |
+
f"| saliency max={saliency.max():.4g} | relerr mean={relerr.mean():.4f} "
|
| 110 |
+
f"| experts_with_tokens={nz}/256", flush=True)
|
| 111 |
+
del w13, w2, x_f16, topi, rw, saliency, relerr, ntok
|
| 112 |
+
torch.cuda.empty_cache()
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def main():
|
| 116 |
+
layers = [int(x) for x in sys.argv[1].split(",")]
|
| 117 |
+
reader = ic.ShardReader(ic.NVFP4_DIR) # gate params
|
| 118 |
+
treader = ic.ShardReader(ic.BF16_DIR) # bf16 teacher experts
|
| 119 |
+
print(f"[worker pid={os.getpid()} gpu={os.environ.get('CUDA_VISIBLE_DEVICES')}] layers={layers}", flush=True)
|
| 120 |
+
for L in layers:
|
| 121 |
+
try:
|
| 122 |
+
process_layer(L, reader, treader)
|
| 123 |
+
except Exception:
|
| 124 |
+
print(f"[L{L}] ERROR\n{traceback.format_exc()}", flush=True)
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
if __name__ == "__main__":
|
| 128 |
+
main()
|
code/run_capture_acts.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Capture per-layer activation samples (routed hidden inputs x + expert ids)
|
| 3 |
+
over the calib corpus, for offline Hessian construction (Phase E converge).
|
| 4 |
+
Dumps /data/inkling-quant/acts/acts_layer_{L}.pt atexit (per TP worker; the
|
| 5 |
+
residual stream is replicated so any worker's dump is complete).
|
| 6 |
+
"""
|
| 7 |
+
import json, os
|
| 8 |
+
|
| 9 |
+
def main():
|
| 10 |
+
os.environ.setdefault("INK_ACT_DIR", "/data/inkling-quant/acts")
|
| 11 |
+
os.environ.setdefault("INK_ACT_CAP", "24000")
|
| 12 |
+
os.environ.setdefault("VLLM_USE_DEEP_GEMM", "0")
|
| 13 |
+
from vllm import LLM, SamplingParams
|
| 14 |
+
prompts = [json.loads(l)["text"] for l in open("/data/inkling-quant/calib.jsonl")]
|
| 15 |
+
print(f"loaded {len(prompts)} calib prompts; capturing acts (cap {os.environ['INK_ACT_CAP']}/layer)")
|
| 16 |
+
llm = LLM(model="/data/inkling-nvfp4", tensor_parallel_size=4, max_model_len=8192,
|
| 17 |
+
gpu_memory_utilization=0.9, enforce_eager=True, trust_remote_code=True)
|
| 18 |
+
llm.generate(prompts, SamplingParams(max_tokens=1, temperature=0))
|
| 19 |
+
print("done; acts dump atexit.")
|
| 20 |
+
|
| 21 |
+
if __name__ == "__main__":
|
| 22 |
+
main()
|
code/run_capture_acts_heldout.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Capture per-layer activation samples over the HELD-OUT corpus (heldout.jsonl),
|
| 3 |
+
into /data/inkling-quant/acts_heldout/. Same hook as run_capture_acts.py but a
|
| 4 |
+
disjoint text set + a cap BELOW the held-out token count (25723) so EVERY layer
|
| 5 |
+
hits the cap mid-run and dumps via the safe incremental path (no atexit truncation).
|
| 6 |
+
GPUs 4-7 only (set via CUDA_VISIBLE_DEVICES before launch)."""
|
| 7 |
+
import json, os
|
| 8 |
+
|
| 9 |
+
def main():
|
| 10 |
+
os.environ.setdefault("INK_ACT_DIR", "/data/inkling-quant/acts_heldout")
|
| 11 |
+
os.environ.setdefault("INK_ACT_CAP", "16000")
|
| 12 |
+
os.environ.setdefault("VLLM_USE_DEEP_GEMM", "0")
|
| 13 |
+
from vllm import LLM, SamplingParams
|
| 14 |
+
prompts = [json.loads(l)["text"] for l in open("/data/inkling-quant/heldout.jsonl")]
|
| 15 |
+
print(f"loaded {len(prompts)} held-out prompts; capturing acts "
|
| 16 |
+
f"(cap {os.environ['INK_ACT_CAP']}/layer) -> {os.environ['INK_ACT_DIR']}")
|
| 17 |
+
llm = LLM(model="/data/inkling-nvfp4", tensor_parallel_size=4, max_model_len=8192,
|
| 18 |
+
gpu_memory_utilization=0.9, enforce_eager=True, trust_remote_code=True)
|
| 19 |
+
llm.generate(prompts, SamplingParams(max_tokens=1, temperature=0))
|
| 20 |
+
print("done; residual layers dump atexit.")
|
| 21 |
+
|
| 22 |
+
if __name__ == "__main__":
|
| 23 |
+
main()
|
code/run_capture_acts_mm.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Multimodal activation capture for Inkling v3 PV.
|
| 3 |
+
|
| 4 |
+
Inkling is inkling_mm_model (vision hmlp + audio dMel towers -> 6144-dim, feeding
|
| 5 |
+
the SAME MoE backbone as text). A text-only PV pass would leave the image/audio-
|
| 6 |
+
routed experts uncalibrated and could DEGRADE multimodal performance. This driver
|
| 7 |
+
feeds image+text and audio+text prompts through vLLM so the acts hook (INK_ACT_DIR)
|
| 8 |
+
captures MoE activations on multimodal tokens too.
|
| 9 |
+
|
| 10 |
+
Modality is chosen by INK_MM=image|audio. Writes acts to INK_ACT_DIR (keep image
|
| 11 |
+
and audio in SEPARATE dirs so pv_v3 can merge text+image+audio per expert):
|
| 12 |
+
|
| 13 |
+
# image acts (chest X-rays + radiology-style prompt)
|
| 14 |
+
INK_MM=image INK_ACT_DIR=/data/inkling-quant/acts_v3_img INK_ACT_CAP=40000 \
|
| 15 |
+
CUDA_VISIBLE_DEVICES=4,5,6,7 python run_capture_acts_mm.py
|
| 16 |
+
# audio acts (LibriSpeech + transcribe prompt)
|
| 17 |
+
INK_MM=audio INK_ACT_DIR=/data/inkling-quant/acts_v3_audio INK_ACT_CAP=40000 \
|
| 18 |
+
CUDA_VISIBLE_DEVICES=4,5,6,7 python run_capture_acts_mm.py
|
| 19 |
+
|
| 20 |
+
Placeholders (from the model): image=<|content_image|>, audio=<|content_audio_input|>.
|
| 21 |
+
Holds out the last HOLDOUT items of each modality (INK_MM_HOLDOUT dir) for mm ppl.
|
| 22 |
+
"""
|
| 23 |
+
import os, glob, json, wave, random
|
| 24 |
+
import numpy as np
|
| 25 |
+
|
| 26 |
+
IMG_DIR = "/data/inkling-quant/corpus_v3/raw/radiology/images"
|
| 27 |
+
LS_DIR = "/data/asr/datasets-asr-normalized/librispeech"
|
| 28 |
+
IMG_PROMPTS = [
|
| 29 |
+
"<|content_image|>\nDescribe the findings in this chest radiograph.",
|
| 30 |
+
"<|content_image|>\nReport any abnormalities visible in this chest X-ray.",
|
| 31 |
+
"<|content_image|>\nWhat pathology, if any, is present in this image?",
|
| 32 |
+
]
|
| 33 |
+
AUD_PROMPT = "<|content_audio_input|>\nTranscribe the spoken audio."
|
| 34 |
+
|
| 35 |
+
def load_images(n, off=0):
|
| 36 |
+
"""Prefer the multimodal manifest (path,domain,prompt across all image domains);
|
| 37 |
+
fall back to the chest-X-ray glob if no manifest is given."""
|
| 38 |
+
from PIL import Image
|
| 39 |
+
manifest = os.environ.get("INK_MM_MANIFEST")
|
| 40 |
+
out = []
|
| 41 |
+
if manifest and os.path.exists(manifest):
|
| 42 |
+
rows = [json.loads(l) for l in open(manifest)][off:off+n]
|
| 43 |
+
for r in rows:
|
| 44 |
+
try:
|
| 45 |
+
out.append((r["prompt"], {"image": Image.open(r["path"]).convert("RGB")}))
|
| 46 |
+
except Exception as e:
|
| 47 |
+
print(" img skip", os.path.basename(r.get("path","?")), str(e)[:50])
|
| 48 |
+
return out
|
| 49 |
+
fs = sorted(glob.glob(f"{IMG_DIR}/*.png"))[off:off+n]
|
| 50 |
+
for i, f in enumerate(fs):
|
| 51 |
+
try:
|
| 52 |
+
out.append((IMG_PROMPTS[i % len(IMG_PROMPTS)], {"image": Image.open(f).convert("RGB")}))
|
| 53 |
+
except Exception as e:
|
| 54 |
+
print(" img skip", os.path.basename(f), str(e)[:50])
|
| 55 |
+
return out
|
| 56 |
+
|
| 57 |
+
def load_audio(n, off=0):
|
| 58 |
+
fs = sorted(glob.glob(f"{LS_DIR}/dev-clean/*.wav"))[off:off+n]
|
| 59 |
+
out = []
|
| 60 |
+
for f in fs:
|
| 61 |
+
try:
|
| 62 |
+
w = wave.open(f); sr = w.getframerate()
|
| 63 |
+
a = np.frombuffer(w.readframes(w.getnframes()), dtype=np.int16).astype(np.float32) / 32768.0
|
| 64 |
+
out.append((AUD_PROMPT, {"audio": (a, sr)}))
|
| 65 |
+
except Exception as e:
|
| 66 |
+
print(" aud skip", os.path.basename(f), str(e)[:50])
|
| 67 |
+
return out
|
| 68 |
+
|
| 69 |
+
def main():
|
| 70 |
+
mm = os.environ.get("INK_MM", "image")
|
| 71 |
+
os.environ.setdefault("INK_ACT_DIR", f"/data/inkling-quant/acts_v3_{mm}")
|
| 72 |
+
os.environ.setdefault("INK_ACT_CAP", "40000")
|
| 73 |
+
os.environ.setdefault("VLLM_USE_DEEP_GEMM", "0")
|
| 74 |
+
n = int(os.environ.get("INK_MM_N", "300"))
|
| 75 |
+
off = int(os.environ.get("INK_MM_OFFSET", "0")) # disjoint held-out slice
|
| 76 |
+
items = load_images(n, off) if mm == "image" else load_audio(n, off)
|
| 77 |
+
print(f"INK_MM={mm} items={len(items)} -> {os.environ['INK_ACT_DIR']} (cap {os.environ['INK_ACT_CAP']})", flush=True)
|
| 78 |
+
if not items:
|
| 79 |
+
raise SystemExit(f"no {mm} inputs found")
|
| 80 |
+
from vllm import LLM, SamplingParams
|
| 81 |
+
llm = LLM(model="/data/inkling-nvfp4", tensor_parallel_size=4, max_model_len=8192,
|
| 82 |
+
gpu_memory_utilization=0.9, enforce_eager=True, trust_remote_code=True,
|
| 83 |
+
limit_mm_per_prompt={mm: 1})
|
| 84 |
+
reqs = [{"prompt": p, "multi_modal_data": d} for p, d in items]
|
| 85 |
+
llm.generate(reqs, SamplingParams(max_tokens=1, temperature=0))
|
| 86 |
+
print("done; acts dump atexit.", flush=True)
|
| 87 |
+
|
| 88 |
+
if __name__ == "__main__":
|
| 89 |
+
main()
|
code/run_capture_acts_v3.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""v3 activation capture — parameterized corpus / out-dir / per-layer cap.
|
| 3 |
+
|
| 4 |
+
The v2 PV starvation came from INK_ACT_CAP=24000 tokens/layer (the "~25k" that
|
| 5 |
+
overfit). v3 raises the cap AND uses the bigger disjoint corpora:
|
| 6 |
+
|
| 7 |
+
# TRAIN acts (what PV tunes on) — big cap for cold-expert coverage
|
| 8 |
+
INK_CORPUS=/data/inkling-quant/calib_v3.jsonl INK_ACT_DIR=/data/inkling-quant/acts_v3 \
|
| 9 |
+
INK_ACT_CAP=80000 CUDA_VISIBLE_DEVICES=4,5,6,7 python run_capture_acts_v3.py
|
| 10 |
+
|
| 11 |
+
# VAL acts (PV early-stop signal) — disjoint corpus, smaller cap is fine
|
| 12 |
+
INK_CORPUS=/data/inkling-quant/heldout_v3.jsonl INK_ACT_DIR=/data/inkling-quant/acts_v3_heldout \
|
| 13 |
+
INK_ACT_CAP=24000 CUDA_VISIBLE_DEVICES=4,5,6,7 python run_capture_acts_v3.py
|
| 14 |
+
|
| 15 |
+
Disk: ~ cap * 6144 * 2 bytes * 64 layers (e.g. cap=80000 -> ~63 GB). Check df first.
|
| 16 |
+
"""
|
| 17 |
+
import json, os
|
| 18 |
+
|
| 19 |
+
def main():
|
| 20 |
+
corpus = os.environ.setdefault("INK_CORPUS", "/data/inkling-quant/calib_v3.jsonl")
|
| 21 |
+
os.environ.setdefault("INK_ACT_DIR", "/data/inkling-quant/acts_v3")
|
| 22 |
+
os.environ.setdefault("INK_ACT_CAP", "80000")
|
| 23 |
+
os.environ.setdefault("VLLM_USE_DEEP_GEMM", "0")
|
| 24 |
+
from vllm import LLM, SamplingParams
|
| 25 |
+
prompts = [json.loads(l)["text"] for l in open(corpus)]
|
| 26 |
+
print(f"corpus={corpus} n={len(prompts)} -> {os.environ['INK_ACT_DIR']} "
|
| 27 |
+
f"(cap {os.environ['INK_ACT_CAP']}/layer)", flush=True)
|
| 28 |
+
llm = LLM(model="/data/inkling-nvfp4", tensor_parallel_size=4, max_model_len=8192,
|
| 29 |
+
gpu_memory_utilization=0.9, enforce_eager=True, trust_remote_code=True)
|
| 30 |
+
llm.generate(prompts, SamplingParams(max_tokens=1, temperature=0))
|
| 31 |
+
print("done; acts dump atexit.", flush=True)
|
| 32 |
+
|
| 33 |
+
if __name__ == "__main__":
|
| 34 |
+
main()
|
code/run_route_stats.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Run the calib corpus through Inkling-NVFP4 offline (vLLM LLM) with
|
| 3 |
+
INK_ROUTE_STATS set, so moe.py accumulates per-layer per-expert routing
|
| 4 |
+
frequency + weight mass. Prefill dominates -> every calib token exercises
|
| 5 |
+
routing. Stats dumped atexit to $INK_ROUTE_STATS (in each TP worker; the
|
| 6 |
+
gate is replicated so any worker's dump is complete).
|
| 7 |
+
"""
|
| 8 |
+
import json, os
|
| 9 |
+
|
| 10 |
+
def main():
|
| 11 |
+
os.environ.setdefault("INK_ROUTE_STATS", "/data/inkling-quant/route_stats.pt")
|
| 12 |
+
os.environ.setdefault("VLLM_USE_DEEP_GEMM", "0")
|
| 13 |
+
from vllm import LLM, SamplingParams
|
| 14 |
+
CALIB = "/data/inkling-quant/calib.jsonl"
|
| 15 |
+
prompts = [json.loads(l)["text"] for l in open(CALIB)]
|
| 16 |
+
print(f"loaded {len(prompts)} calib prompts")
|
| 17 |
+
llm = LLM(
|
| 18 |
+
model="/data/inkling-nvfp4",
|
| 19 |
+
tensor_parallel_size=4,
|
| 20 |
+
max_model_len=8192,
|
| 21 |
+
gpu_memory_utilization=0.9,
|
| 22 |
+
enforce_eager=True,
|
| 23 |
+
trust_remote_code=True,
|
| 24 |
+
)
|
| 25 |
+
sp = SamplingParams(max_tokens=1, temperature=0) # prefill routing only
|
| 26 |
+
out = llm.generate(prompts, sp)
|
| 27 |
+
print(f"processed {len(out)} prompts; route stats dump atexit.")
|
| 28 |
+
|
| 29 |
+
if __name__ == "__main__":
|
| 30 |
+
main()
|
code/solve_tier.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Byte-exact expert tiering solver for the Inkling hybrid.
|
| 3 |
+
Ranks experts globally by routing mass; upgrades the highest-mass experts from
|
| 4 |
+
AQLM (cold) to NVFP4 (hot) until the expert VRAM budget is spent, with per-layer
|
| 5 |
+
floor/cap. Reports achievable hot% + avg bpw for a target deployment budget.
|
| 6 |
+
|
| 7 |
+
Deployment target = 4x96GB RTX6000 = 384GB (we emulate on B200; ship for 384GB).
|
| 8 |
+
"""
|
| 9 |
+
import argparse, json, math
|
| 10 |
+
import torch
|
| 11 |
+
import ink_common as ic
|
| 12 |
+
|
| 13 |
+
# exact per-expert shapes (in params)
|
| 14 |
+
W13 = (6144, 6144); W2 = (6144, 3072)
|
| 15 |
+
P13 = W13[0]*W13[1]; P2 = W2[0]*W2[1]
|
| 16 |
+
GDIM = 8
|
| 17 |
+
|
| 18 |
+
def nvfp4_bytes():
|
| 19 |
+
# 4-bit packed + fp8 block-16 scale + fp32 scale2 (negligible)
|
| 20 |
+
def lin(o,i): return o*i*0.5 + o*(i/16)*1.0 # u8 packed + f8 scale
|
| 21 |
+
return lin(*W13) + lin(*W2)
|
| 22 |
+
|
| 23 |
+
def aqlm_bytes(w13_books, w2_books, book2_entries):
|
| 24 |
+
code_b = 2 # book0 int16 (65536)
|
| 25 |
+
b2 = 1 if book2_entries <= 256 else 2
|
| 26 |
+
def proj(o,i,books):
|
| 27 |
+
groups = o*(i//GDIM)
|
| 28 |
+
by = groups*code_b + (groups*b2*(books-1) if books>1 else 0)
|
| 29 |
+
return by + o*2 # + fp16 per-row scale
|
| 30 |
+
return proj(*W13, w13_books) + proj(*W2, w2_books)
|
| 31 |
+
|
| 32 |
+
def main():
|
| 33 |
+
ap = argparse.ArgumentParser()
|
| 34 |
+
ap.add_argument("--budget-gb", type=float, default=384.0)
|
| 35 |
+
ap.add_argument("--nonexpert-gb", type=float, default=40.0)
|
| 36 |
+
ap.add_argument("--kv-gb", type=float, default=11.0) # ~512k fp8
|
| 37 |
+
ap.add_argument("--workspace-gb", type=float, default=25.0)
|
| 38 |
+
ap.add_argument("--w13-books", type=int, default=1)
|
| 39 |
+
ap.add_argument("--w2-books", type=int, default=2)
|
| 40 |
+
ap.add_argument("--book2-entries", type=int, default=65536)
|
| 41 |
+
ap.add_argument("--floor", type=int, default=4) # min hot per layer
|
| 42 |
+
ap.add_argument("--cap", type=int, default=220) # max hot per layer
|
| 43 |
+
ap.add_argument("--save", default="")
|
| 44 |
+
ap.add_argument("--route-stats", default="/data/inkling-quant/route_stats.pt")
|
| 45 |
+
ap.add_argument("--scores", default="", help="optional pt {L:tensor[256]} (e.g. REAP) to rank by instead of routing mass")
|
| 46 |
+
a = ap.parse_args()
|
| 47 |
+
|
| 48 |
+
st = torch.load(a.route_stats)
|
| 49 |
+
layers = sorted(st.keys()); E = ic.N_EXPERTS
|
| 50 |
+
mass = {L: st[L]["wmass"].clone() for L in layers} # router-weight mass per expert
|
| 51 |
+
if a.scores:
|
| 52 |
+
sc = torch.load(a.scores)
|
| 53 |
+
mass = {L: sc[L].clone().float() for L in layers} # rank by supplied scores (REAP)
|
| 54 |
+
print(f"ranking by --scores {a.scores} (overrides routing mass)")
|
| 55 |
+
|
| 56 |
+
hotB = nvfp4_bytes(); coldB = aqlm_bytes(a.w13_books, a.w2_books, a.book2_entries)
|
| 57 |
+
n_exp = len(layers)*E
|
| 58 |
+
expert_budget = (a.budget_gb - a.nonexpert_gb - a.kv_gb - a.workspace_gb) * 1e9
|
| 59 |
+
all_cold = n_exp * coldB
|
| 60 |
+
print(f"per-expert: NVFP4(hot)={hotB/1e6:.1f}MB AQLM(cold w13={a.w13_books}b/w2={a.w2_books}b@{a.book2_entries})={coldB/1e6:.1f}MB")
|
| 61 |
+
print(f"experts total={n_exp} all-cold={all_cold/1e9:.1f}GB expert budget={expert_budget/1e9:.1f}GB")
|
| 62 |
+
spare = expert_budget - all_cold
|
| 63 |
+
if spare < 0:
|
| 64 |
+
print(f"INFEASIBLE: all-cold ({all_cold/1e9:.1f}GB) exceeds budget ({expert_budget/1e9:.1f}GB) by {-spare/1e9:.1f}GB. Need cheaper cold scheme.")
|
| 65 |
+
return
|
| 66 |
+
max_upgrades = int(spare // (hotB - coldB))
|
| 67 |
+
print(f"spare after all-cold={spare/1e9:.1f}GB -> up to {max_upgrades} hot upgrades ({100*max_upgrades/n_exp:.1f}% hot)")
|
| 68 |
+
|
| 69 |
+
# global rank by mass, apply floor first (guarantee floor hot per layer), then fill by mass under cap
|
| 70 |
+
assign = {L: torch.zeros(E, dtype=torch.bool) for L in layers}
|
| 71 |
+
used = 0
|
| 72 |
+
# floor
|
| 73 |
+
for L in layers:
|
| 74 |
+
top = torch.argsort(mass[L], descending=True)[:a.floor]
|
| 75 |
+
assign[L][top] = True; used += a.floor
|
| 76 |
+
# remaining budget by global mass ranking (respect cap)
|
| 77 |
+
pool = []
|
| 78 |
+
for L in layers:
|
| 79 |
+
order = torch.argsort(mass[L], descending=True).tolist()
|
| 80 |
+
for rank,e in enumerate(order):
|
| 81 |
+
if not assign[L][e]:
|
| 82 |
+
pool.append((float(mass[L][e]), L, e, rank))
|
| 83 |
+
pool.sort(reverse=True)
|
| 84 |
+
for m,L,e,rank in pool:
|
| 85 |
+
if used >= max_upgrades: break
|
| 86 |
+
if int(assign[L].sum()) >= a.cap: continue
|
| 87 |
+
assign[L][e] = True; used += 1
|
| 88 |
+
hot_total = used
|
| 89 |
+
# per-band report
|
| 90 |
+
def band(lo,hi): return sum(int(assign[L].sum()) for L in layers if lo<=L<=hi)/max(1,len([L for L in layers if lo<=L<=hi]))
|
| 91 |
+
print(f"\nHOT experts assigned: {hot_total} ({100*hot_total/n_exp:.1f}%)")
|
| 92 |
+
print(f"per-layer mean hot: early(2-15)={band(2,15):.0f} mid(30-45)={band(30,45):.0f} deep(55-65)={band(55,65):.0f} (of 256)")
|
| 93 |
+
avg_bpw = ((hot_total*hotB + (n_exp-hot_total)*coldB)*8) / (n_exp*(P13+P2))
|
| 94 |
+
print(f"avg expert bpw={avg_bpw:.2f} total expert VRAM={((hot_total*hotB+(n_exp-hot_total)*coldB))/1e9:.1f}GB")
|
| 95 |
+
if a.save:
|
| 96 |
+
out = {str(L): sorted(torch.nonzero(assign[L]).flatten().tolist()) for L in layers}
|
| 97 |
+
json.dump({"hot_experts": out, "config": vars(a), "hot_total": hot_total,
|
| 98 |
+
"hot_bytes": hotB, "cold_bytes": coldB}, open(a.save,"w"))
|
| 99 |
+
print("saved assignment ->", a.save)
|
| 100 |
+
|
| 101 |
+
if __name__ == "__main__":
|
| 102 |
+
main()
|
code/v1_vs_v2_heldout.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch, json, random, statistics as st
|
| 2 |
+
import ink_common as ic
|
| 3 |
+
random.seed(0); dev="cuda"
|
| 4 |
+
# v1 cold = converged w13 (parts_conv) + INIT w2 (parts); v2 cold = converged w13 + CONVERGED w2 (parts_conv)
|
| 5 |
+
tc=ic.ShardReader(ic.BF16_DIR)
|
| 6 |
+
def eout(W13,W2,x):
|
| 7 |
+
g=W13[0::2]; u=W13[1::2]
|
| 8 |
+
h=torch.nn.functional.silu(x@g.t())*(x@u.t()); return h@W2.t()
|
| 9 |
+
layers=[3,8,15,22,30,38,45,52,58,63]
|
| 10 |
+
v1=[]; v2=[]
|
| 11 |
+
for L in layers:
|
| 12 |
+
a=torch.load(f"/data/inkling-quant/acts_heldout/acts_layer_{L}.pt",map_location=dev)
|
| 13 |
+
x=a["x"].float(); ids=a["ids"]
|
| 14 |
+
# cold = experts not hot in v2 assign (same set for both comparisons; tiering handled separately)
|
| 15 |
+
hot=set(json.load(open("/data/inkling-quant/assign_512k_v2.json"))["hot_experts"][str(L)])
|
| 16 |
+
cold=[e for e in range(256) if e not in hot]
|
| 17 |
+
c13=torch.load(f"/data/inkling-quant/parts_conv/layer_{L}_w13.pt",map_location="cpu") # converged w13 (both)
|
| 18 |
+
w2_init=torch.load(f"/data/inkling-quant/parts/layer_{L}_w2.pt",map_location="cpu") # v1 w2 (init)
|
| 19 |
+
w2_conv=torch.load(f"/data/inkling-quant/parts_conv/layer_{L}_w2.pt",map_location="cpu")# v2 w2 (converged)
|
| 20 |
+
W13=None
|
| 21 |
+
for e in random.sample(cold,min(12,len(cold))):
|
| 22 |
+
m=(ids==e).any(1)
|
| 23 |
+
if int(m.sum())<8: continue
|
| 24 |
+
xe=x[m][:2048]
|
| 25 |
+
ft=eout(ic.teacher_expert(tc,L,"w13",e,device=dev).float(),ic.teacher_expert(tc,L,"w2",e,device=dev).float(),xe); nt=ft.norm()
|
| 26 |
+
Wc13=ic.aqlm_dequant_expert(c13,"w13",e,device=dev).float()
|
| 27 |
+
v1.append(((eout(Wc13,ic.aqlm_dequant_expert(w2_init,"w2",e,device=dev).float(),xe)-ft).norm()/nt).item())
|
| 28 |
+
v2.append(((eout(Wc13,ic.aqlm_dequant_expert(w2_conv,"w2",e,device=dev).float(),xe)-ft).norm()/nt).item())
|
| 29 |
+
a=st.mean(v1); b=st.mean(v2)
|
| 30 |
+
print(f"HELD-OUT per-expert output rel-err over {len(v1)} cold experts:")
|
| 31 |
+
print(f" v1 cold (conv-w13 + INIT-w2): {a:.4f}")
|
| 32 |
+
print(f" v2 cold (conv-w13 + CONVERGED-w2): {b:.4f}")
|
| 33 |
+
print(f" v2 vs v1 on HELD-OUT: {100*(1-b/a):+.1f}% -> {'v2 cold experts BETTER (w2-converge generalizes)' if b < a*0.995 else 'no held-out gain'}")
|
code/validate_gate_ckpt.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Validate /data/inkling-512k-gate:
|
| 3 |
+
(a) all shards referenced by the output index exist + non-weight files copied;
|
| 4 |
+
(b) output index present & its referenced shards all exist;
|
| 5 |
+
(c) cold expert w13 dequant rel_err vs BF16 teacher ~0.30-0.32 (carries AQLM err);
|
| 6 |
+
(d) hot expert unchanged vs /data/inkling-nvfp4 (rel_err ~0.09, byte-identical).
|
| 7 |
+
"""
|
| 8 |
+
import json, os, torch, filecmp
|
| 9 |
+
import ink_common as ic
|
| 10 |
+
from safetensors import safe_open
|
| 11 |
+
|
| 12 |
+
OUT = "/data/inkling-512k-gate"
|
| 13 |
+
SRC = ic.NVFP4_DIR
|
| 14 |
+
ASSIGN = "/data/inkling-quant/assign_512k.json"
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def main():
|
| 18 |
+
dev = "cuda"
|
| 19 |
+
ok = True
|
| 20 |
+
# ---- (a)/(b) file inventory ----
|
| 21 |
+
idxp = f"{OUT}/model.safetensors.index.json"
|
| 22 |
+
assert os.path.exists(idxp), "output index missing"
|
| 23 |
+
wm = json.load(open(idxp))["weight_map"]
|
| 24 |
+
shards = sorted(set(wm.values()))
|
| 25 |
+
missing = [s for s in shards if not os.path.exists(f"{OUT}/{s}")]
|
| 26 |
+
print(f"[a/b] index references {len(shards)} shards; missing: {missing}")
|
| 27 |
+
ok &= not missing
|
| 28 |
+
n_st = len([f for f in os.listdir(OUT) if f.endswith('.safetensors')])
|
| 29 |
+
print(f"[a] .safetensors files in output: {n_st}")
|
| 30 |
+
SKIP = {".cache"} # HF download cache dir, intentionally not copied
|
| 31 |
+
nonweight = [f for f in os.listdir(SRC) if not f.endswith('.safetensors') and f not in SKIP]
|
| 32 |
+
miss_nw = [f for f in nonweight if not os.path.exists(f"{OUT}/{f}")]
|
| 33 |
+
print(f"[a] non-weight source files: {len(nonweight)}; missing in output: {miss_nw}")
|
| 34 |
+
ok &= not miss_nw
|
| 35 |
+
|
| 36 |
+
hot = json.load(open(ASSIGN))["hot_experts"]
|
| 37 |
+
L = 10
|
| 38 |
+
hot_L = set(hot[str(L)])
|
| 39 |
+
cold_e = next(e for e in range(ic.N_EXPERTS) if e not in hot_L)
|
| 40 |
+
hot_e = sorted(hot_L)[0]
|
| 41 |
+
|
| 42 |
+
tR = ic.ShardReader(ic.BF16_DIR)
|
| 43 |
+
oR = ic.ShardReader(OUT) # the new checkpoint
|
| 44 |
+
nR = ic.ShardReader(SRC) # original nvfp4
|
| 45 |
+
|
| 46 |
+
# ---- (c) cold expert now carries AQLM error ----
|
| 47 |
+
proj = "w13"
|
| 48 |
+
teach = ic.teacher_expert(tR, L, proj, cold_e, dev).float()
|
| 49 |
+
coldW = ic.dequant_nvfp4_expert(oR, L, proj, cold_e, dev).float()
|
| 50 |
+
cold_err = ((coldW - teach).norm() / teach.norm()).item()
|
| 51 |
+
print(f"[c] COLD e{cold_e} {proj} rel_err vs teacher = {cold_err:.4f} (expect 0.30-0.32)")
|
| 52 |
+
ok &= 0.29 <= cold_err <= 0.33
|
| 53 |
+
|
| 54 |
+
# ---- (d) hot expert unchanged ----
|
| 55 |
+
teach_h = ic.teacher_expert(tR, L, proj, hot_e, dev).float()
|
| 56 |
+
hotW = ic.dequant_nvfp4_expert(oR, L, proj, hot_e, dev).float()
|
| 57 |
+
hot_err = ((hotW - teach_h).norm() / teach_h.norm()).item()
|
| 58 |
+
print(f"[d] HOT e{hot_e} {proj} rel_err vs teacher = {hot_err:.4f} (expect ~0.09)")
|
| 59 |
+
ok &= 0.07 <= hot_err <= 0.11
|
| 60 |
+
# byte-identical hot slice vs original nvfp4 (all three tensors)
|
| 61 |
+
p = f"model.llm.layers.{L}.mlp.experts.{proj}_weight"
|
| 62 |
+
for suf in ["", ".scale", ".scale2"]:
|
| 63 |
+
a = nR.get(p + suf)[hot_e]
|
| 64 |
+
b = oR.get(p + suf)[hot_e]
|
| 65 |
+
same = torch.equal(a, b)
|
| 66 |
+
print(f"[d] HOT byte-identical {p+suf}[{hot_e}] vs original nvfp4: {same}")
|
| 67 |
+
ok &= same
|
| 68 |
+
# sanity: cold slice DIFFERS from original nvfp4
|
| 69 |
+
diff = not torch.equal(nR.get(p)[cold_e], oR.get(p)[cold_e])
|
| 70 |
+
print(f"[c] COLD packed[{cold_e}] differs from original nvfp4: {diff}")
|
| 71 |
+
ok &= diff
|
| 72 |
+
|
| 73 |
+
# ---- (e) layer 2 (full-precision bf16 special layer) ----
|
| 74 |
+
L2 = 2
|
| 75 |
+
hot2 = set(json.load(open(ASSIGN))["hot_experts"]["2"])
|
| 76 |
+
c2 = next(e for e in range(ic.N_EXPERTS) if e not in hot2)
|
| 77 |
+
h2 = sorted(hot2)[0]
|
| 78 |
+
p2 = f"model.llm.layers.{L2}.mlp.experts.w13_weight"
|
| 79 |
+
print(f"\n[e] layer2 w13_weight dtype in output: {oR.get(p2).dtype} (expect bfloat16, unpacked)")
|
| 80 |
+
t2 = ic.teacher_expert(tR, L2, "w13", c2, dev).float()
|
| 81 |
+
got2 = oR.get(p2)[c2].to(dev).float()
|
| 82 |
+
e2 = ((got2 - t2).norm() / t2.norm()).item()
|
| 83 |
+
print(f"[e] layer2 COLD e{c2} w13 rel_err vs teacher = {e2:.4f} (expect ~0.30, pure AQLM bf16)")
|
| 84 |
+
ok &= 0.28 <= e2 <= 0.32
|
| 85 |
+
same2 = torch.equal(nR.get(p2)[h2], oR.get(p2)[h2])
|
| 86 |
+
print(f"[e] layer2 HOT e{h2} w13 byte-identical vs original: {same2}")
|
| 87 |
+
ok &= same2
|
| 88 |
+
|
| 89 |
+
print("\nVALIDATION:", "PASS" if ok else "FAIL")
|
| 90 |
+
return 0 if ok else 1
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
if __name__ == "__main__":
|
| 94 |
+
raise SystemExit(main())
|
code/validate_hybrid.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Validate the assembled /data/inkling-512k-hybrid checkpoint before upload."""
|
| 3 |
+
import json, os, glob
|
| 4 |
+
import torch
|
| 5 |
+
from safetensors import safe_open
|
| 6 |
+
import build_hybrid as B
|
| 7 |
+
import ink_common as ic
|
| 8 |
+
|
| 9 |
+
OUT = B.OUT
|
| 10 |
+
|
| 11 |
+
def validate():
|
| 12 |
+
ok = True
|
| 13 |
+
idx = json.load(open(f"{OUT}/model.safetensors.index.json"))
|
| 14 |
+
wm = idx["weight_map"]
|
| 15 |
+
total = idx["metadata"]["total_size"]
|
| 16 |
+
ref_shards = sorted(set(wm.values()))
|
| 17 |
+
disk_shards = sorted(os.path.basename(p) for p in glob.glob(f"{OUT}/model-*.safetensors"))
|
| 18 |
+
|
| 19 |
+
print(f"[chk] index tensors: {len(wm)} total_size: {total/1e9:.1f} GB")
|
| 20 |
+
print(f"[chk] shards referenced: {len(ref_shards)} on disk: {len(disk_shards)}")
|
| 21 |
+
|
| 22 |
+
missing = [s for s in ref_shards if not os.path.exists(f"{OUT}/{s}")]
|
| 23 |
+
extra = [s for s in disk_shards if s not in set(ref_shards)]
|
| 24 |
+
if missing:
|
| 25 |
+
ok = False; print(f" !! MISSING shards: {missing}")
|
| 26 |
+
if extra:
|
| 27 |
+
print(f" ~~ extra shards on disk (not in index): {extra}")
|
| 28 |
+
if not missing and not extra:
|
| 29 |
+
print(" ok: shard set matches index exactly")
|
| 30 |
+
|
| 31 |
+
# size sanity: smaller build must fit a 384 GB (4x96GB) machine.
|
| 32 |
+
gb = total / 1e9
|
| 33 |
+
# per-book uint8 cold codes (~ -38 GB) + layer-2 compressed (~ -25 GB) => ~345-355 GB
|
| 34 |
+
if not (330 <= gb <= 375):
|
| 35 |
+
ok = False; print(f" !! total size {gb:.1f} GB out of expected band (330-375)")
|
| 36 |
+
else:
|
| 37 |
+
print(f" ok: total size {gb:.1f} GB in range")
|
| 38 |
+
if gb < 384:
|
| 39 |
+
print(f" ok: total size {gb:.1f} GB < 384 GB (fits 4x96GB machine)")
|
| 40 |
+
else:
|
| 41 |
+
ok = False; print(f" !! total size {gb:.1f} GB does NOT fit 384 GB machine")
|
| 42 |
+
|
| 43 |
+
# every tensor resolvable in its shard
|
| 44 |
+
open_cache = {}
|
| 45 |
+
def keys_of(sh):
|
| 46 |
+
if sh not in open_cache:
|
| 47 |
+
open_cache[sh] = set(safe_open(f"{OUT}/{sh}", framework="pt").keys())
|
| 48 |
+
return open_cache[sh]
|
| 49 |
+
bad = [n for n, sh in wm.items() if n not in keys_of(sh)]
|
| 50 |
+
if bad:
|
| 51 |
+
ok = False; print(f" !! {len(bad)} tensors not found in their shard, e.g. {bad[:3]}")
|
| 52 |
+
else:
|
| 53 |
+
print(f" ok: all {len(wm)} tensors resolvable in their shards")
|
| 54 |
+
|
| 55 |
+
# readback: hot slabs, per-book cold codes, hot_ids from a packed layer
|
| 56 |
+
packed = sorted(B.packed_layers()); hot = B.hot_map()
|
| 57 |
+
L = packed[0]
|
| 58 |
+
nv = ic.ShardReader(OUT) # reads assembled output via its index
|
| 59 |
+
checks = {
|
| 60 |
+
f"model.llm.layers.{L}.mlp.experts.w13_hot_weight": ("uint8", 3),
|
| 61 |
+
f"model.llm.layers.{L}.mlp.experts.w13_cold_codes.0": ("int16", 3), # w13 book0 @65536
|
| 62 |
+
f"model.llm.layers.{L}.mlp.experts.w2_cold_codes.0": ("int16", 3), # w2 book0 @65536
|
| 63 |
+
f"model.llm.layers.{L}.mlp.experts.w2_cold_codes.1": ("uint8", 3), # w2 book1 @256 -> uint8
|
| 64 |
+
f"model.llm.layers.{L}.mlp.experts.hot_ids": ("int32", 1),
|
| 65 |
+
f"model.llm.layers.{L}.mlp.experts.cold_ids": ("int32", 1),
|
| 66 |
+
}
|
| 67 |
+
for name, (dt, ndim) in checks.items():
|
| 68 |
+
t = nv.get(name)
|
| 69 |
+
print(f" readback {name.split('.mlp.')[-1]}: {t.dtype} {tuple(t.shape)}")
|
| 70 |
+
if str(t.dtype).split(".")[-1] != dt or t.dim() != ndim:
|
| 71 |
+
ok = False; print(f" !! unexpected dtype/ndim (want {dt} ndim {ndim})")
|
| 72 |
+
# confirm uint8 book1 values are within 0..255 (lossless) and codebook has <=256 rows
|
| 73 |
+
c1 = nv.get(f"model.llm.layers.{L}.mlp.experts.w2_cold_codes.1")
|
| 74 |
+
cb1 = nv.get(f"model.llm.layers.{L}.mlp.experts.w2_cold_codebook.1")
|
| 75 |
+
if int(c1.max()) > cb1.shape[0] - 1:
|
| 76 |
+
ok = False; print(f" !! w2 book1 code {int(c1.max())} out of range for {cb1.shape[0]} entries")
|
| 77 |
+
else:
|
| 78 |
+
print(f" ok: w2 book1 uint8 codes in 0..{int(c1.max())} (codebook {cb1.shape[0]} rows)")
|
| 79 |
+
# hot_ids/cold_ids consistency vs assignment
|
| 80 |
+
hids = nv.get(f"model.llm.layers.{L}.mlp.experts.hot_ids").tolist()
|
| 81 |
+
cids = nv.get(f"model.llm.layers.{L}.mlp.experts.cold_ids").tolist()
|
| 82 |
+
if sorted(hids) != sorted(hot[L]):
|
| 83 |
+
ok = False; print(f" !! hot_ids mismatch layer {L}")
|
| 84 |
+
if sorted(hids + cids) != list(range(ic.N_EXPERTS)):
|
| 85 |
+
ok = False; print(f" !! hot_ids+cold_ids do not partition 0..255")
|
| 86 |
+
else:
|
| 87 |
+
print(f" ok: hot_ids({len(hids)})+cold_ids({len(cids)}) partition 0..255 for layer {L}")
|
| 88 |
+
# hot_weight n matches n_hot
|
| 89 |
+
hw = nv.get(f"model.llm.layers.{L}.mlp.experts.w13_hot_weight")
|
| 90 |
+
if hw.shape[0] != len(hot[L]):
|
| 91 |
+
ok = False; print(f" !! hot_weight expert-dim {hw.shape[0]} != n_hot {len(hot[L])}")
|
| 92 |
+
|
| 93 |
+
# layer 2 is now a hybrid layer: bf16 HOT slabs (no scales), AQLM cold, fused weights dropped
|
| 94 |
+
for proj in ("w13", "w2"):
|
| 95 |
+
hb = f"model.llm.layers.2.mlp.experts.{proj}_hot_bf16"
|
| 96 |
+
if hb not in wm:
|
| 97 |
+
ok = False; print(f" !! layer-2 {proj}_hot_bf16 MISSING")
|
| 98 |
+
else:
|
| 99 |
+
t = nv.get(hb)
|
| 100 |
+
print(f" layer2 {proj}_hot_bf16: {t.dtype} {tuple(t.shape)}")
|
| 101 |
+
if t.dtype != torch.bfloat16 or t.dim() != 3:
|
| 102 |
+
ok = False; print(f" !! layer-2 {proj}_hot_bf16 not bf16/3-D")
|
| 103 |
+
if t.shape[0] != len(hot[2]):
|
| 104 |
+
ok = False; print(f" !! layer-2 {proj}_hot_bf16 expert-dim {t.shape[0]} != n_hot {len(hot[2])}")
|
| 105 |
+
if f"{hb}.scale" in wm or f"{hb}.scale2" in wm:
|
| 106 |
+
ok = False; print(f" !! layer-2 {proj}_hot_bf16 unexpectedly has scale")
|
| 107 |
+
# cold codes present for layer 2
|
| 108 |
+
if f"model.llm.layers.2.mlp.experts.{proj}_cold_codes.0" not in wm:
|
| 109 |
+
ok = False; print(f" !! layer-2 {proj}_cold_codes.0 MISSING")
|
| 110 |
+
# fused uncompressed bf16 must be dropped
|
| 111 |
+
if f"model.llm.layers.2.mlp.experts.{proj}_weight" in wm:
|
| 112 |
+
ok = False; print(f" !! layer-2 {proj}_weight fused bf16 NOT dropped")
|
| 113 |
+
# NO layer (incl. layer 2) may carry fused expert weights in the output
|
| 114 |
+
built = set(hot)
|
| 115 |
+
leaked = [n for n in wm if B.EXP_ANY.match(n) and int(B.EXP_ANY.match(n).group(1)) in built]
|
| 116 |
+
if leaked:
|
| 117 |
+
ok = False; print(f" !! fused expert weights leaked into output: {leaked[:3]}")
|
| 118 |
+
else:
|
| 119 |
+
print(f" ok: no fused expert weights in output")
|
| 120 |
+
|
| 121 |
+
# side files
|
| 122 |
+
for fn in ("config.json", "hf_quant_config.json", "tokenizer.json", "README.md",
|
| 123 |
+
"tokenizer_config.json", "chat_template.jinja", "tiktoken/tokenizer.model"):
|
| 124 |
+
if not os.path.exists(f"{OUT}/{fn}"):
|
| 125 |
+
print(f" ~~ side file missing: {fn}")
|
| 126 |
+
hq = json.load(open(f"{OUT}/hf_quant_config.json"))
|
| 127 |
+
if "aqlm_hybrid" not in hq:
|
| 128 |
+
ok = False; print(" !! hf_quant_config.json missing aqlm_hybrid block")
|
| 129 |
+
cfg = json.load(open(f"{OUT}/config.json"))
|
| 130 |
+
if cfg.get("quantization_config", {}).get("quant_method") != "inkling_nvfp4_aqlm_hybrid":
|
| 131 |
+
ok = False; print(" !! config.json missing quantization_config.quant_method")
|
| 132 |
+
|
| 133 |
+
print(f"\n[VALIDATE] {'PASS' if ok else 'FAIL'}")
|
| 134 |
+
return ok
|
| 135 |
+
|
| 136 |
+
if __name__ == "__main__":
|
| 137 |
+
import sys
|
| 138 |
+
sys.exit(0 if validate() else 1)
|
config.json
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"InklingForConditionalGeneration"
|
| 4 |
+
],
|
| 5 |
+
"model_type": "inkling_mm_model",
|
| 6 |
+
"eos_token_id": 200006,
|
| 7 |
+
"text_config": {
|
| 8 |
+
"model_max_length": 1048576,
|
| 9 |
+
"torch_dtype": "bfloat16",
|
| 10 |
+
"hidden_size": 6144,
|
| 11 |
+
"num_hidden_layers": 66,
|
| 12 |
+
"vocab_size": 201024,
|
| 13 |
+
"num_attention_heads": 64,
|
| 14 |
+
"num_key_value_heads": 8,
|
| 15 |
+
"head_dim": 128,
|
| 16 |
+
"d_rel": 16,
|
| 17 |
+
"rel_extent": 1024,
|
| 18 |
+
"q_bias": false,
|
| 19 |
+
"o_bias": false,
|
| 20 |
+
"log_scaling_n_floor": 128000,
|
| 21 |
+
"log_scaling_alpha": 0.1,
|
| 22 |
+
"rms_norm_eps": 1e-06,
|
| 23 |
+
"use_embed_norm": true,
|
| 24 |
+
"local_layer_ids": [
|
| 25 |
+
0,
|
| 26 |
+
1,
|
| 27 |
+
2,
|
| 28 |
+
3,
|
| 29 |
+
4,
|
| 30 |
+
6,
|
| 31 |
+
7,
|
| 32 |
+
8,
|
| 33 |
+
9,
|
| 34 |
+
10,
|
| 35 |
+
12,
|
| 36 |
+
13,
|
| 37 |
+
14,
|
| 38 |
+
15,
|
| 39 |
+
16,
|
| 40 |
+
18,
|
| 41 |
+
19,
|
| 42 |
+
20,
|
| 43 |
+
21,
|
| 44 |
+
22,
|
| 45 |
+
24,
|
| 46 |
+
25,
|
| 47 |
+
26,
|
| 48 |
+
27,
|
| 49 |
+
28,
|
| 50 |
+
30,
|
| 51 |
+
31,
|
| 52 |
+
32,
|
| 53 |
+
33,
|
| 54 |
+
34,
|
| 55 |
+
36,
|
| 56 |
+
37,
|
| 57 |
+
38,
|
| 58 |
+
39,
|
| 59 |
+
40,
|
| 60 |
+
42,
|
| 61 |
+
43,
|
| 62 |
+
44,
|
| 63 |
+
45,
|
| 64 |
+
46,
|
| 65 |
+
48,
|
| 66 |
+
49,
|
| 67 |
+
50,
|
| 68 |
+
51,
|
| 69 |
+
52,
|
| 70 |
+
54,
|
| 71 |
+
55,
|
| 72 |
+
56,
|
| 73 |
+
57,
|
| 74 |
+
58,
|
| 75 |
+
60,
|
| 76 |
+
61,
|
| 77 |
+
62,
|
| 78 |
+
63,
|
| 79 |
+
64
|
| 80 |
+
],
|
| 81 |
+
"dense_mlp_idx": 2,
|
| 82 |
+
"use_sconv": true,
|
| 83 |
+
"sconv_kernel_size": 4,
|
| 84 |
+
"unpadded_vocab_size": 200058,
|
| 85 |
+
"logits_mup_width_multiplier": 24.0,
|
| 86 |
+
"final_logit_softcapping": null,
|
| 87 |
+
"swa_head_dim": 128,
|
| 88 |
+
"swa_num_attention_heads": 64,
|
| 89 |
+
"swa_num_key_value_heads": 16,
|
| 90 |
+
"sliding_window_size": 512,
|
| 91 |
+
"n_routed_experts": 256,
|
| 92 |
+
"num_experts_per_tok": 6,
|
| 93 |
+
"n_shared_experts": 2,
|
| 94 |
+
"shared_expert_sink": true,
|
| 95 |
+
"dense_intermediate_size": 24576,
|
| 96 |
+
"intermediate_size": 3072,
|
| 97 |
+
"route_scale": 8.0,
|
| 98 |
+
"use_gate_bias": true,
|
| 99 |
+
"gate_activation": "sigmoid",
|
| 100 |
+
"norm_after_topk": true,
|
| 101 |
+
"use_global_scale": true
|
| 102 |
+
},
|
| 103 |
+
"audio_config": {
|
| 104 |
+
"decoder_dmodel": 6144,
|
| 105 |
+
"n_mel_bins": 80,
|
| 106 |
+
"mel_vocab_size": 16,
|
| 107 |
+
"bias": false,
|
| 108 |
+
"dmel_min_value": -7.0,
|
| 109 |
+
"dmel_max_value": 2.0,
|
| 110 |
+
"use_audio_norm": true,
|
| 111 |
+
"audio_mode": "dmel"
|
| 112 |
+
},
|
| 113 |
+
"vision_config": {
|
| 114 |
+
"vision_encoder_type": "hmlp",
|
| 115 |
+
"decoder_dmodel": 6144,
|
| 116 |
+
"patch_size": 40,
|
| 117 |
+
"temporal_patch_size": 2,
|
| 118 |
+
"n_channels": 3,
|
| 119 |
+
"n_layers": 4,
|
| 120 |
+
"use_vision_norm": true
|
| 121 |
+
},
|
| 122 |
+
"mtp_config": {
|
| 123 |
+
"num_nextn_predict_layers": 8,
|
| 124 |
+
"chain_hidden_post_norm": false,
|
| 125 |
+
"local_layer_ids": [
|
| 126 |
+
0,
|
| 127 |
+
2,
|
| 128 |
+
4,
|
| 129 |
+
5,
|
| 130 |
+
6,
|
| 131 |
+
7
|
| 132 |
+
]
|
| 133 |
+
},
|
| 134 |
+
"quantization_config": {
|
| 135 |
+
"quant_method": "inkling_nvfp4_aqlm_hybrid",
|
| 136 |
+
"base_nvfp4": "thinkingmachines/Inkling-NVFP4",
|
| 137 |
+
"group_size": 8,
|
| 138 |
+
"note": "Mixed per-expert quant: hot experts ModelOpt-NVFP4, cold experts AQLM-2bit. Full spec in hf_quant_config.json['aqlm_hybrid'] and README.md. Requires a custom fused-MoE kernel; does NOT load on stock vLLM."
|
| 139 |
+
}
|
| 140 |
+
}
|
hf_quant_config.json
ADDED
|
@@ -0,0 +1,1049 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"quantization": {
|
| 3 |
+
"quant_algo": "NVFP4",
|
| 4 |
+
"kv_cache_quant_algo": "none",
|
| 5 |
+
"exclude_modules": [
|
| 6 |
+
"model.audio.encoder",
|
| 7 |
+
"model.audio.final_norm",
|
| 8 |
+
"model.llm.embed",
|
| 9 |
+
"model.llm.embed_norm",
|
| 10 |
+
"model.llm.norm",
|
| 11 |
+
"model.llm.unembed",
|
| 12 |
+
"model.visual.final_norm",
|
| 13 |
+
"model.visual.layers.linear_0",
|
| 14 |
+
"model.visual.layers.linear_1",
|
| 15 |
+
"model.visual.layers.linear_2",
|
| 16 |
+
"model.visual.layers.linear_3",
|
| 17 |
+
"model.visual.layers.norm_0",
|
| 18 |
+
"model.visual.layers.norm_1",
|
| 19 |
+
"model.visual.layers.norm_2",
|
| 20 |
+
"model.llm.layers.0.attn",
|
| 21 |
+
"model.llm.layers.0.attn_norm",
|
| 22 |
+
"model.llm.layers.0.attn_sconv",
|
| 23 |
+
"model.llm.layers.0.mlp.global_scale",
|
| 24 |
+
"model.llm.layers.0.mlp.w13_dn",
|
| 25 |
+
"model.llm.layers.0.mlp.w2_md",
|
| 26 |
+
"model.llm.layers.0.mlp_norm",
|
| 27 |
+
"model.llm.layers.0.mlp_sconv",
|
| 28 |
+
"model.llm.layers.1.attn",
|
| 29 |
+
"model.llm.layers.1.attn_norm",
|
| 30 |
+
"model.llm.layers.1.attn_sconv",
|
| 31 |
+
"model.llm.layers.1.mlp.global_scale",
|
| 32 |
+
"model.llm.layers.1.mlp.w13_dn",
|
| 33 |
+
"model.llm.layers.1.mlp.w2_md",
|
| 34 |
+
"model.llm.layers.1.mlp_norm",
|
| 35 |
+
"model.llm.layers.1.mlp_sconv",
|
| 36 |
+
"model.llm.layers.10.attn",
|
| 37 |
+
"model.llm.layers.10.attn_norm",
|
| 38 |
+
"model.llm.layers.10.attn_sconv",
|
| 39 |
+
"model.llm.layers.10.mlp.gate",
|
| 40 |
+
"model.llm.layers.10.mlp.shared_experts",
|
| 41 |
+
"model.llm.layers.10.mlp_norm",
|
| 42 |
+
"model.llm.layers.10.mlp_sconv",
|
| 43 |
+
"model.llm.layers.11.attn",
|
| 44 |
+
"model.llm.layers.11.attn_norm",
|
| 45 |
+
"model.llm.layers.11.attn_sconv",
|
| 46 |
+
"model.llm.layers.11.mlp.gate",
|
| 47 |
+
"model.llm.layers.11.mlp.shared_experts",
|
| 48 |
+
"model.llm.layers.11.mlp_norm",
|
| 49 |
+
"model.llm.layers.11.mlp_sconv",
|
| 50 |
+
"model.llm.layers.12.attn",
|
| 51 |
+
"model.llm.layers.12.attn_norm",
|
| 52 |
+
"model.llm.layers.12.attn_sconv",
|
| 53 |
+
"model.llm.layers.12.mlp.gate",
|
| 54 |
+
"model.llm.layers.12.mlp.shared_experts",
|
| 55 |
+
"model.llm.layers.12.mlp_norm",
|
| 56 |
+
"model.llm.layers.12.mlp_sconv",
|
| 57 |
+
"model.llm.layers.13.attn",
|
| 58 |
+
"model.llm.layers.13.attn_norm",
|
| 59 |
+
"model.llm.layers.13.attn_sconv",
|
| 60 |
+
"model.llm.layers.13.mlp.gate",
|
| 61 |
+
"model.llm.layers.13.mlp.shared_experts",
|
| 62 |
+
"model.llm.layers.13.mlp_norm",
|
| 63 |
+
"model.llm.layers.13.mlp_sconv",
|
| 64 |
+
"model.llm.layers.14.attn",
|
| 65 |
+
"model.llm.layers.14.attn_norm",
|
| 66 |
+
"model.llm.layers.14.attn_sconv",
|
| 67 |
+
"model.llm.layers.14.mlp.gate",
|
| 68 |
+
"model.llm.layers.14.mlp.shared_experts",
|
| 69 |
+
"model.llm.layers.14.mlp_norm",
|
| 70 |
+
"model.llm.layers.14.mlp_sconv",
|
| 71 |
+
"model.llm.layers.15.attn",
|
| 72 |
+
"model.llm.layers.15.attn_norm",
|
| 73 |
+
"model.llm.layers.15.attn_sconv",
|
| 74 |
+
"model.llm.layers.15.mlp.gate",
|
| 75 |
+
"model.llm.layers.15.mlp.shared_experts",
|
| 76 |
+
"model.llm.layers.15.mlp_norm",
|
| 77 |
+
"model.llm.layers.15.mlp_sconv",
|
| 78 |
+
"model.llm.layers.16.attn",
|
| 79 |
+
"model.llm.layers.16.attn_norm",
|
| 80 |
+
"model.llm.layers.16.attn_sconv",
|
| 81 |
+
"model.llm.layers.16.mlp.gate",
|
| 82 |
+
"model.llm.layers.16.mlp.shared_experts",
|
| 83 |
+
"model.llm.layers.16.mlp_norm",
|
| 84 |
+
"model.llm.layers.16.mlp_sconv",
|
| 85 |
+
"model.llm.layers.17.attn",
|
| 86 |
+
"model.llm.layers.17.attn_norm",
|
| 87 |
+
"model.llm.layers.17.attn_sconv",
|
| 88 |
+
"model.llm.layers.17.mlp.gate",
|
| 89 |
+
"model.llm.layers.17.mlp.shared_experts",
|
| 90 |
+
"model.llm.layers.17.mlp_norm",
|
| 91 |
+
"model.llm.layers.17.mlp_sconv",
|
| 92 |
+
"model.llm.layers.18.attn",
|
| 93 |
+
"model.llm.layers.18.attn_norm",
|
| 94 |
+
"model.llm.layers.18.attn_sconv",
|
| 95 |
+
"model.llm.layers.18.mlp.gate",
|
| 96 |
+
"model.llm.layers.18.mlp.shared_experts",
|
| 97 |
+
"model.llm.layers.18.mlp_norm",
|
| 98 |
+
"model.llm.layers.18.mlp_sconv",
|
| 99 |
+
"model.llm.layers.19.attn",
|
| 100 |
+
"model.llm.layers.19.attn_norm",
|
| 101 |
+
"model.llm.layers.19.attn_sconv",
|
| 102 |
+
"model.llm.layers.19.mlp.gate",
|
| 103 |
+
"model.llm.layers.19.mlp.shared_experts",
|
| 104 |
+
"model.llm.layers.19.mlp_norm",
|
| 105 |
+
"model.llm.layers.19.mlp_sconv",
|
| 106 |
+
"model.llm.layers.2.attn",
|
| 107 |
+
"model.llm.layers.2.attn_norm",
|
| 108 |
+
"model.llm.layers.2.attn_sconv",
|
| 109 |
+
"model.llm.layers.2.mlp.experts",
|
| 110 |
+
"model.llm.layers.2.mlp.gate",
|
| 111 |
+
"model.llm.layers.2.mlp.shared_experts",
|
| 112 |
+
"model.llm.layers.2.mlp_norm",
|
| 113 |
+
"model.llm.layers.2.mlp_sconv",
|
| 114 |
+
"model.llm.layers.20.attn",
|
| 115 |
+
"model.llm.layers.20.attn_norm",
|
| 116 |
+
"model.llm.layers.20.attn_sconv",
|
| 117 |
+
"model.llm.layers.20.mlp.gate",
|
| 118 |
+
"model.llm.layers.20.mlp.shared_experts",
|
| 119 |
+
"model.llm.layers.20.mlp_norm",
|
| 120 |
+
"model.llm.layers.20.mlp_sconv",
|
| 121 |
+
"model.llm.layers.21.attn",
|
| 122 |
+
"model.llm.layers.21.attn_norm",
|
| 123 |
+
"model.llm.layers.21.attn_sconv",
|
| 124 |
+
"model.llm.layers.21.mlp.gate",
|
| 125 |
+
"model.llm.layers.21.mlp.shared_experts",
|
| 126 |
+
"model.llm.layers.21.mlp_norm",
|
| 127 |
+
"model.llm.layers.21.mlp_sconv",
|
| 128 |
+
"model.llm.layers.22.attn",
|
| 129 |
+
"model.llm.layers.22.attn_norm",
|
| 130 |
+
"model.llm.layers.22.attn_sconv",
|
| 131 |
+
"model.llm.layers.22.mlp.gate",
|
| 132 |
+
"model.llm.layers.22.mlp.shared_experts",
|
| 133 |
+
"model.llm.layers.22.mlp_norm",
|
| 134 |
+
"model.llm.layers.22.mlp_sconv",
|
| 135 |
+
"model.llm.layers.23.attn",
|
| 136 |
+
"model.llm.layers.23.attn_norm",
|
| 137 |
+
"model.llm.layers.23.attn_sconv",
|
| 138 |
+
"model.llm.layers.23.mlp.gate",
|
| 139 |
+
"model.llm.layers.23.mlp.shared_experts",
|
| 140 |
+
"model.llm.layers.23.mlp_norm",
|
| 141 |
+
"model.llm.layers.23.mlp_sconv",
|
| 142 |
+
"model.llm.layers.24.attn",
|
| 143 |
+
"model.llm.layers.24.attn_norm",
|
| 144 |
+
"model.llm.layers.24.attn_sconv",
|
| 145 |
+
"model.llm.layers.24.mlp.gate",
|
| 146 |
+
"model.llm.layers.24.mlp.shared_experts",
|
| 147 |
+
"model.llm.layers.24.mlp_norm",
|
| 148 |
+
"model.llm.layers.24.mlp_sconv",
|
| 149 |
+
"model.llm.layers.25.attn",
|
| 150 |
+
"model.llm.layers.25.attn_norm",
|
| 151 |
+
"model.llm.layers.25.attn_sconv",
|
| 152 |
+
"model.llm.layers.25.mlp.gate",
|
| 153 |
+
"model.llm.layers.25.mlp.shared_experts",
|
| 154 |
+
"model.llm.layers.25.mlp_norm",
|
| 155 |
+
"model.llm.layers.25.mlp_sconv",
|
| 156 |
+
"model.llm.layers.26.attn",
|
| 157 |
+
"model.llm.layers.26.attn_norm",
|
| 158 |
+
"model.llm.layers.26.attn_sconv",
|
| 159 |
+
"model.llm.layers.26.mlp.gate",
|
| 160 |
+
"model.llm.layers.26.mlp.shared_experts",
|
| 161 |
+
"model.llm.layers.26.mlp_norm",
|
| 162 |
+
"model.llm.layers.26.mlp_sconv",
|
| 163 |
+
"model.llm.layers.27.attn",
|
| 164 |
+
"model.llm.layers.27.attn_norm",
|
| 165 |
+
"model.llm.layers.27.attn_sconv",
|
| 166 |
+
"model.llm.layers.27.mlp.gate",
|
| 167 |
+
"model.llm.layers.27.mlp.shared_experts",
|
| 168 |
+
"model.llm.layers.27.mlp_norm",
|
| 169 |
+
"model.llm.layers.27.mlp_sconv",
|
| 170 |
+
"model.llm.layers.28.attn",
|
| 171 |
+
"model.llm.layers.28.attn_norm",
|
| 172 |
+
"model.llm.layers.28.attn_sconv",
|
| 173 |
+
"model.llm.layers.28.mlp.gate",
|
| 174 |
+
"model.llm.layers.28.mlp.shared_experts",
|
| 175 |
+
"model.llm.layers.28.mlp_norm",
|
| 176 |
+
"model.llm.layers.28.mlp_sconv",
|
| 177 |
+
"model.llm.layers.29.attn",
|
| 178 |
+
"model.llm.layers.29.attn_norm",
|
| 179 |
+
"model.llm.layers.29.attn_sconv",
|
| 180 |
+
"model.llm.layers.29.mlp.gate",
|
| 181 |
+
"model.llm.layers.29.mlp.shared_experts",
|
| 182 |
+
"model.llm.layers.29.mlp_norm",
|
| 183 |
+
"model.llm.layers.29.mlp_sconv",
|
| 184 |
+
"model.llm.layers.3.attn",
|
| 185 |
+
"model.llm.layers.3.attn_norm",
|
| 186 |
+
"model.llm.layers.3.attn_sconv",
|
| 187 |
+
"model.llm.layers.3.mlp.gate",
|
| 188 |
+
"model.llm.layers.3.mlp.shared_experts",
|
| 189 |
+
"model.llm.layers.3.mlp_norm",
|
| 190 |
+
"model.llm.layers.3.mlp_sconv",
|
| 191 |
+
"model.llm.layers.30.attn",
|
| 192 |
+
"model.llm.layers.30.attn_norm",
|
| 193 |
+
"model.llm.layers.30.attn_sconv",
|
| 194 |
+
"model.llm.layers.30.mlp.gate",
|
| 195 |
+
"model.llm.layers.30.mlp.shared_experts",
|
| 196 |
+
"model.llm.layers.30.mlp_norm",
|
| 197 |
+
"model.llm.layers.30.mlp_sconv",
|
| 198 |
+
"model.llm.layers.31.attn",
|
| 199 |
+
"model.llm.layers.31.attn_norm",
|
| 200 |
+
"model.llm.layers.31.attn_sconv",
|
| 201 |
+
"model.llm.layers.31.mlp.gate",
|
| 202 |
+
"model.llm.layers.31.mlp.shared_experts",
|
| 203 |
+
"model.llm.layers.31.mlp_norm",
|
| 204 |
+
"model.llm.layers.31.mlp_sconv",
|
| 205 |
+
"model.llm.layers.32.attn",
|
| 206 |
+
"model.llm.layers.32.attn_norm",
|
| 207 |
+
"model.llm.layers.32.attn_sconv",
|
| 208 |
+
"model.llm.layers.32.mlp.gate",
|
| 209 |
+
"model.llm.layers.32.mlp.shared_experts",
|
| 210 |
+
"model.llm.layers.32.mlp_norm",
|
| 211 |
+
"model.llm.layers.32.mlp_sconv",
|
| 212 |
+
"model.llm.layers.33.attn",
|
| 213 |
+
"model.llm.layers.33.attn_norm",
|
| 214 |
+
"model.llm.layers.33.attn_sconv",
|
| 215 |
+
"model.llm.layers.33.mlp.gate",
|
| 216 |
+
"model.llm.layers.33.mlp.shared_experts",
|
| 217 |
+
"model.llm.layers.33.mlp_norm",
|
| 218 |
+
"model.llm.layers.33.mlp_sconv",
|
| 219 |
+
"model.llm.layers.34.attn",
|
| 220 |
+
"model.llm.layers.34.attn_norm",
|
| 221 |
+
"model.llm.layers.34.attn_sconv",
|
| 222 |
+
"model.llm.layers.34.mlp.gate",
|
| 223 |
+
"model.llm.layers.34.mlp.shared_experts",
|
| 224 |
+
"model.llm.layers.34.mlp_norm",
|
| 225 |
+
"model.llm.layers.34.mlp_sconv",
|
| 226 |
+
"model.llm.layers.35.attn",
|
| 227 |
+
"model.llm.layers.35.attn_norm",
|
| 228 |
+
"model.llm.layers.35.attn_sconv",
|
| 229 |
+
"model.llm.layers.35.mlp.gate",
|
| 230 |
+
"model.llm.layers.35.mlp.shared_experts",
|
| 231 |
+
"model.llm.layers.35.mlp_norm",
|
| 232 |
+
"model.llm.layers.35.mlp_sconv",
|
| 233 |
+
"model.llm.layers.36.attn",
|
| 234 |
+
"model.llm.layers.36.attn_norm",
|
| 235 |
+
"model.llm.layers.36.attn_sconv",
|
| 236 |
+
"model.llm.layers.36.mlp.gate",
|
| 237 |
+
"model.llm.layers.36.mlp.shared_experts",
|
| 238 |
+
"model.llm.layers.36.mlp_norm",
|
| 239 |
+
"model.llm.layers.36.mlp_sconv",
|
| 240 |
+
"model.llm.layers.37.attn",
|
| 241 |
+
"model.llm.layers.37.attn_norm",
|
| 242 |
+
"model.llm.layers.37.attn_sconv",
|
| 243 |
+
"model.llm.layers.37.mlp.gate",
|
| 244 |
+
"model.llm.layers.37.mlp.shared_experts",
|
| 245 |
+
"model.llm.layers.37.mlp_norm",
|
| 246 |
+
"model.llm.layers.37.mlp_sconv",
|
| 247 |
+
"model.llm.layers.38.attn",
|
| 248 |
+
"model.llm.layers.38.attn_norm",
|
| 249 |
+
"model.llm.layers.38.attn_sconv",
|
| 250 |
+
"model.llm.layers.38.mlp.gate",
|
| 251 |
+
"model.llm.layers.38.mlp.shared_experts",
|
| 252 |
+
"model.llm.layers.38.mlp_norm",
|
| 253 |
+
"model.llm.layers.38.mlp_sconv",
|
| 254 |
+
"model.llm.layers.39.attn",
|
| 255 |
+
"model.llm.layers.39.attn_norm",
|
| 256 |
+
"model.llm.layers.39.attn_sconv",
|
| 257 |
+
"model.llm.layers.39.mlp.gate",
|
| 258 |
+
"model.llm.layers.39.mlp.shared_experts",
|
| 259 |
+
"model.llm.layers.39.mlp_norm",
|
| 260 |
+
"model.llm.layers.39.mlp_sconv",
|
| 261 |
+
"model.llm.layers.4.attn",
|
| 262 |
+
"model.llm.layers.4.attn_norm",
|
| 263 |
+
"model.llm.layers.4.attn_sconv",
|
| 264 |
+
"model.llm.layers.4.mlp.gate",
|
| 265 |
+
"model.llm.layers.4.mlp.shared_experts",
|
| 266 |
+
"model.llm.layers.4.mlp_norm",
|
| 267 |
+
"model.llm.layers.4.mlp_sconv",
|
| 268 |
+
"model.llm.layers.40.attn",
|
| 269 |
+
"model.llm.layers.40.attn_norm",
|
| 270 |
+
"model.llm.layers.40.attn_sconv",
|
| 271 |
+
"model.llm.layers.40.mlp.gate",
|
| 272 |
+
"model.llm.layers.40.mlp.shared_experts",
|
| 273 |
+
"model.llm.layers.40.mlp_norm",
|
| 274 |
+
"model.llm.layers.40.mlp_sconv",
|
| 275 |
+
"model.llm.layers.41.attn",
|
| 276 |
+
"model.llm.layers.41.attn_norm",
|
| 277 |
+
"model.llm.layers.41.attn_sconv",
|
| 278 |
+
"model.llm.layers.41.mlp.gate",
|
| 279 |
+
"model.llm.layers.41.mlp.shared_experts",
|
| 280 |
+
"model.llm.layers.41.mlp_norm",
|
| 281 |
+
"model.llm.layers.41.mlp_sconv",
|
| 282 |
+
"model.llm.layers.42.attn",
|
| 283 |
+
"model.llm.layers.42.attn_norm",
|
| 284 |
+
"model.llm.layers.42.attn_sconv",
|
| 285 |
+
"model.llm.layers.42.mlp.gate",
|
| 286 |
+
"model.llm.layers.42.mlp.shared_experts",
|
| 287 |
+
"model.llm.layers.42.mlp_norm",
|
| 288 |
+
"model.llm.layers.42.mlp_sconv",
|
| 289 |
+
"model.llm.layers.43.attn",
|
| 290 |
+
"model.llm.layers.43.attn_norm",
|
| 291 |
+
"model.llm.layers.43.attn_sconv",
|
| 292 |
+
"model.llm.layers.43.mlp.gate",
|
| 293 |
+
"model.llm.layers.43.mlp.shared_experts",
|
| 294 |
+
"model.llm.layers.43.mlp_norm",
|
| 295 |
+
"model.llm.layers.43.mlp_sconv",
|
| 296 |
+
"model.llm.layers.44.attn",
|
| 297 |
+
"model.llm.layers.44.attn_norm",
|
| 298 |
+
"model.llm.layers.44.attn_sconv",
|
| 299 |
+
"model.llm.layers.44.mlp.gate",
|
| 300 |
+
"model.llm.layers.44.mlp.shared_experts",
|
| 301 |
+
"model.llm.layers.44.mlp_norm",
|
| 302 |
+
"model.llm.layers.44.mlp_sconv",
|
| 303 |
+
"model.llm.layers.45.attn",
|
| 304 |
+
"model.llm.layers.45.attn_norm",
|
| 305 |
+
"model.llm.layers.45.attn_sconv",
|
| 306 |
+
"model.llm.layers.45.mlp.gate",
|
| 307 |
+
"model.llm.layers.45.mlp.shared_experts",
|
| 308 |
+
"model.llm.layers.45.mlp_norm",
|
| 309 |
+
"model.llm.layers.45.mlp_sconv",
|
| 310 |
+
"model.llm.layers.46.attn",
|
| 311 |
+
"model.llm.layers.46.attn_norm",
|
| 312 |
+
"model.llm.layers.46.attn_sconv",
|
| 313 |
+
"model.llm.layers.46.mlp.gate",
|
| 314 |
+
"model.llm.layers.46.mlp.shared_experts",
|
| 315 |
+
"model.llm.layers.46.mlp_norm",
|
| 316 |
+
"model.llm.layers.46.mlp_sconv",
|
| 317 |
+
"model.llm.layers.47.attn",
|
| 318 |
+
"model.llm.layers.47.attn_norm",
|
| 319 |
+
"model.llm.layers.47.attn_sconv",
|
| 320 |
+
"model.llm.layers.47.mlp.gate",
|
| 321 |
+
"model.llm.layers.47.mlp.shared_experts",
|
| 322 |
+
"model.llm.layers.47.mlp_norm",
|
| 323 |
+
"model.llm.layers.47.mlp_sconv",
|
| 324 |
+
"model.llm.layers.48.attn",
|
| 325 |
+
"model.llm.layers.48.attn_norm",
|
| 326 |
+
"model.llm.layers.48.attn_sconv",
|
| 327 |
+
"model.llm.layers.48.mlp.gate",
|
| 328 |
+
"model.llm.layers.48.mlp.shared_experts",
|
| 329 |
+
"model.llm.layers.48.mlp_norm",
|
| 330 |
+
"model.llm.layers.48.mlp_sconv",
|
| 331 |
+
"model.llm.layers.49.attn",
|
| 332 |
+
"model.llm.layers.49.attn_norm",
|
| 333 |
+
"model.llm.layers.49.attn_sconv",
|
| 334 |
+
"model.llm.layers.49.mlp.gate",
|
| 335 |
+
"model.llm.layers.49.mlp.shared_experts",
|
| 336 |
+
"model.llm.layers.49.mlp_norm",
|
| 337 |
+
"model.llm.layers.49.mlp_sconv",
|
| 338 |
+
"model.llm.layers.5.attn",
|
| 339 |
+
"model.llm.layers.5.attn_norm",
|
| 340 |
+
"model.llm.layers.5.attn_sconv",
|
| 341 |
+
"model.llm.layers.5.mlp.gate",
|
| 342 |
+
"model.llm.layers.5.mlp.shared_experts",
|
| 343 |
+
"model.llm.layers.5.mlp_norm",
|
| 344 |
+
"model.llm.layers.5.mlp_sconv",
|
| 345 |
+
"model.llm.layers.50.attn",
|
| 346 |
+
"model.llm.layers.50.attn_norm",
|
| 347 |
+
"model.llm.layers.50.attn_sconv",
|
| 348 |
+
"model.llm.layers.50.mlp.gate",
|
| 349 |
+
"model.llm.layers.50.mlp.shared_experts",
|
| 350 |
+
"model.llm.layers.50.mlp_norm",
|
| 351 |
+
"model.llm.layers.50.mlp_sconv",
|
| 352 |
+
"model.llm.layers.51.attn",
|
| 353 |
+
"model.llm.layers.51.attn_norm",
|
| 354 |
+
"model.llm.layers.51.attn_sconv",
|
| 355 |
+
"model.llm.layers.51.mlp.gate",
|
| 356 |
+
"model.llm.layers.51.mlp.shared_experts",
|
| 357 |
+
"model.llm.layers.51.mlp_norm",
|
| 358 |
+
"model.llm.layers.51.mlp_sconv",
|
| 359 |
+
"model.llm.layers.52.attn",
|
| 360 |
+
"model.llm.layers.52.attn_norm",
|
| 361 |
+
"model.llm.layers.52.attn_sconv",
|
| 362 |
+
"model.llm.layers.52.mlp.gate",
|
| 363 |
+
"model.llm.layers.52.mlp.shared_experts",
|
| 364 |
+
"model.llm.layers.52.mlp_norm",
|
| 365 |
+
"model.llm.layers.52.mlp_sconv",
|
| 366 |
+
"model.llm.layers.53.attn",
|
| 367 |
+
"model.llm.layers.53.attn_norm",
|
| 368 |
+
"model.llm.layers.53.attn_sconv",
|
| 369 |
+
"model.llm.layers.53.mlp.gate",
|
| 370 |
+
"model.llm.layers.53.mlp.shared_experts",
|
| 371 |
+
"model.llm.layers.53.mlp_norm",
|
| 372 |
+
"model.llm.layers.53.mlp_sconv",
|
| 373 |
+
"model.llm.layers.54.attn",
|
| 374 |
+
"model.llm.layers.54.attn_norm",
|
| 375 |
+
"model.llm.layers.54.attn_sconv",
|
| 376 |
+
"model.llm.layers.54.mlp.gate",
|
| 377 |
+
"model.llm.layers.54.mlp.shared_experts",
|
| 378 |
+
"model.llm.layers.54.mlp_norm",
|
| 379 |
+
"model.llm.layers.54.mlp_sconv",
|
| 380 |
+
"model.llm.layers.55.attn",
|
| 381 |
+
"model.llm.layers.55.attn_norm",
|
| 382 |
+
"model.llm.layers.55.attn_sconv",
|
| 383 |
+
"model.llm.layers.55.mlp.gate",
|
| 384 |
+
"model.llm.layers.55.mlp.shared_experts",
|
| 385 |
+
"model.llm.layers.55.mlp_norm",
|
| 386 |
+
"model.llm.layers.55.mlp_sconv",
|
| 387 |
+
"model.llm.layers.56.attn",
|
| 388 |
+
"model.llm.layers.56.attn_norm",
|
| 389 |
+
"model.llm.layers.56.attn_sconv",
|
| 390 |
+
"model.llm.layers.56.mlp.gate",
|
| 391 |
+
"model.llm.layers.56.mlp.shared_experts",
|
| 392 |
+
"model.llm.layers.56.mlp_norm",
|
| 393 |
+
"model.llm.layers.56.mlp_sconv",
|
| 394 |
+
"model.llm.layers.57.attn",
|
| 395 |
+
"model.llm.layers.57.attn_norm",
|
| 396 |
+
"model.llm.layers.57.attn_sconv",
|
| 397 |
+
"model.llm.layers.57.mlp.gate",
|
| 398 |
+
"model.llm.layers.57.mlp.shared_experts",
|
| 399 |
+
"model.llm.layers.57.mlp_norm",
|
| 400 |
+
"model.llm.layers.57.mlp_sconv",
|
| 401 |
+
"model.llm.layers.58.attn",
|
| 402 |
+
"model.llm.layers.58.attn_norm",
|
| 403 |
+
"model.llm.layers.58.attn_sconv",
|
| 404 |
+
"model.llm.layers.58.mlp.gate",
|
| 405 |
+
"model.llm.layers.58.mlp.shared_experts",
|
| 406 |
+
"model.llm.layers.58.mlp_norm",
|
| 407 |
+
"model.llm.layers.58.mlp_sconv",
|
| 408 |
+
"model.llm.layers.59.attn",
|
| 409 |
+
"model.llm.layers.59.attn_norm",
|
| 410 |
+
"model.llm.layers.59.attn_sconv",
|
| 411 |
+
"model.llm.layers.59.mlp.gate",
|
| 412 |
+
"model.llm.layers.59.mlp.shared_experts",
|
| 413 |
+
"model.llm.layers.59.mlp_norm",
|
| 414 |
+
"model.llm.layers.59.mlp_sconv",
|
| 415 |
+
"model.llm.layers.6.attn",
|
| 416 |
+
"model.llm.layers.6.attn_norm",
|
| 417 |
+
"model.llm.layers.6.attn_sconv",
|
| 418 |
+
"model.llm.layers.6.mlp.gate",
|
| 419 |
+
"model.llm.layers.6.mlp.shared_experts",
|
| 420 |
+
"model.llm.layers.6.mlp_norm",
|
| 421 |
+
"model.llm.layers.6.mlp_sconv",
|
| 422 |
+
"model.llm.layers.60.attn",
|
| 423 |
+
"model.llm.layers.60.attn_norm",
|
| 424 |
+
"model.llm.layers.60.attn_sconv",
|
| 425 |
+
"model.llm.layers.60.mlp.gate",
|
| 426 |
+
"model.llm.layers.60.mlp.shared_experts",
|
| 427 |
+
"model.llm.layers.60.mlp_norm",
|
| 428 |
+
"model.llm.layers.60.mlp_sconv",
|
| 429 |
+
"model.llm.layers.61.attn",
|
| 430 |
+
"model.llm.layers.61.attn_norm",
|
| 431 |
+
"model.llm.layers.61.attn_sconv",
|
| 432 |
+
"model.llm.layers.61.mlp.gate",
|
| 433 |
+
"model.llm.layers.61.mlp.shared_experts",
|
| 434 |
+
"model.llm.layers.61.mlp_norm",
|
| 435 |
+
"model.llm.layers.61.mlp_sconv",
|
| 436 |
+
"model.llm.layers.62.attn",
|
| 437 |
+
"model.llm.layers.62.attn_norm",
|
| 438 |
+
"model.llm.layers.62.attn_sconv",
|
| 439 |
+
"model.llm.layers.62.mlp.gate",
|
| 440 |
+
"model.llm.layers.62.mlp.shared_experts",
|
| 441 |
+
"model.llm.layers.62.mlp_norm",
|
| 442 |
+
"model.llm.layers.62.mlp_sconv",
|
| 443 |
+
"model.llm.layers.63.attn",
|
| 444 |
+
"model.llm.layers.63.attn_norm",
|
| 445 |
+
"model.llm.layers.63.attn_sconv",
|
| 446 |
+
"model.llm.layers.63.mlp.gate",
|
| 447 |
+
"model.llm.layers.63.mlp.shared_experts",
|
| 448 |
+
"model.llm.layers.63.mlp_norm",
|
| 449 |
+
"model.llm.layers.63.mlp_sconv",
|
| 450 |
+
"model.llm.layers.64.attn",
|
| 451 |
+
"model.llm.layers.64.attn_norm",
|
| 452 |
+
"model.llm.layers.64.attn_sconv",
|
| 453 |
+
"model.llm.layers.64.mlp.gate",
|
| 454 |
+
"model.llm.layers.64.mlp.shared_experts",
|
| 455 |
+
"model.llm.layers.64.mlp_norm",
|
| 456 |
+
"model.llm.layers.64.mlp_sconv",
|
| 457 |
+
"model.llm.layers.65.attn",
|
| 458 |
+
"model.llm.layers.65.attn_norm",
|
| 459 |
+
"model.llm.layers.65.attn_sconv",
|
| 460 |
+
"model.llm.layers.65.mlp.gate",
|
| 461 |
+
"model.llm.layers.65.mlp.shared_experts",
|
| 462 |
+
"model.llm.layers.65.mlp_norm",
|
| 463 |
+
"model.llm.layers.65.mlp_sconv",
|
| 464 |
+
"model.llm.layers.7.attn",
|
| 465 |
+
"model.llm.layers.7.attn_norm",
|
| 466 |
+
"model.llm.layers.7.attn_sconv",
|
| 467 |
+
"model.llm.layers.7.mlp.gate",
|
| 468 |
+
"model.llm.layers.7.mlp.shared_experts",
|
| 469 |
+
"model.llm.layers.7.mlp_norm",
|
| 470 |
+
"model.llm.layers.7.mlp_sconv",
|
| 471 |
+
"model.llm.layers.8.attn",
|
| 472 |
+
"model.llm.layers.8.attn_norm",
|
| 473 |
+
"model.llm.layers.8.attn_sconv",
|
| 474 |
+
"model.llm.layers.8.mlp.gate",
|
| 475 |
+
"model.llm.layers.8.mlp.shared_experts",
|
| 476 |
+
"model.llm.layers.8.mlp_norm",
|
| 477 |
+
"model.llm.layers.8.mlp_sconv",
|
| 478 |
+
"model.llm.layers.9.attn",
|
| 479 |
+
"model.llm.layers.9.attn_norm",
|
| 480 |
+
"model.llm.layers.9.attn_sconv",
|
| 481 |
+
"model.llm.layers.9.mlp.gate",
|
| 482 |
+
"model.llm.layers.9.mlp.shared_experts",
|
| 483 |
+
"model.llm.layers.9.mlp_norm",
|
| 484 |
+
"model.llm.layers.9.mlp_sconv"
|
| 485 |
+
],
|
| 486 |
+
"modelopt_quant_config": {
|
| 487 |
+
"quant_cfg": {
|
| 488 |
+
"*weight_quantizer": {
|
| 489 |
+
"num_bits": [
|
| 490 |
+
2,
|
| 491 |
+
1
|
| 492 |
+
],
|
| 493 |
+
"block_sizes": {
|
| 494 |
+
"-1": 16,
|
| 495 |
+
"type": "dynamic",
|
| 496 |
+
"scale_bits": [
|
| 497 |
+
4,
|
| 498 |
+
3
|
| 499 |
+
]
|
| 500 |
+
},
|
| 501 |
+
"axis": null,
|
| 502 |
+
"enable": true
|
| 503 |
+
},
|
| 504 |
+
"*input_quantizer": {
|
| 505 |
+
"num_bits": [
|
| 506 |
+
2,
|
| 507 |
+
1
|
| 508 |
+
],
|
| 509 |
+
"block_sizes": {
|
| 510 |
+
"-1": 16,
|
| 511 |
+
"type": "dynamic",
|
| 512 |
+
"scale_bits": [
|
| 513 |
+
4,
|
| 514 |
+
3
|
| 515 |
+
]
|
| 516 |
+
},
|
| 517 |
+
"axis": null,
|
| 518 |
+
"enable": true
|
| 519 |
+
},
|
| 520 |
+
"nn.BatchNorm1d": {
|
| 521 |
+
"*": {
|
| 522 |
+
"enable": false
|
| 523 |
+
}
|
| 524 |
+
},
|
| 525 |
+
"nn.BatchNorm2d": {
|
| 526 |
+
"*": {
|
| 527 |
+
"enable": false
|
| 528 |
+
}
|
| 529 |
+
},
|
| 530 |
+
"nn.BatchNorm3d": {
|
| 531 |
+
"*": {
|
| 532 |
+
"enable": false
|
| 533 |
+
}
|
| 534 |
+
},
|
| 535 |
+
"nn.LeakyReLU": {
|
| 536 |
+
"*": {
|
| 537 |
+
"enable": false
|
| 538 |
+
}
|
| 539 |
+
},
|
| 540 |
+
"*lm_head*": {
|
| 541 |
+
"enable": false
|
| 542 |
+
},
|
| 543 |
+
"*proj_out.*": {
|
| 544 |
+
"enable": false
|
| 545 |
+
},
|
| 546 |
+
"*block_sparse_moe.gate*": {
|
| 547 |
+
"enable": false
|
| 548 |
+
},
|
| 549 |
+
"*router*": {
|
| 550 |
+
"enable": false
|
| 551 |
+
},
|
| 552 |
+
"*mlp.gate.*": {
|
| 553 |
+
"enable": false
|
| 554 |
+
},
|
| 555 |
+
"*mlp.shared_expert_gate.*": {
|
| 556 |
+
"enable": false
|
| 557 |
+
},
|
| 558 |
+
"*linear_attn.conv1d*": {
|
| 559 |
+
"enable": false
|
| 560 |
+
},
|
| 561 |
+
"*mixer.conv1d*": {
|
| 562 |
+
"enable": false
|
| 563 |
+
},
|
| 564 |
+
"*output_layer*": {
|
| 565 |
+
"enable": false
|
| 566 |
+
},
|
| 567 |
+
"output.*": {
|
| 568 |
+
"enable": false
|
| 569 |
+
},
|
| 570 |
+
"default": {
|
| 571 |
+
"enable": false
|
| 572 |
+
}
|
| 573 |
+
},
|
| 574 |
+
"algorithm": "max"
|
| 575 |
+
},
|
| 576 |
+
"group_size": 16
|
| 577 |
+
},
|
| 578 |
+
"aqlm_hybrid": {
|
| 579 |
+
"quant_method": "inkling_nvfp4_aqlm_hybrid",
|
| 580 |
+
"base_nvfp4": "thinkingmachines/Inkling-NVFP4",
|
| 581 |
+
"group_size": 8,
|
| 582 |
+
"w13_book_entries": [
|
| 583 |
+
65536
|
| 584 |
+
],
|
| 585 |
+
"w2_book_entries": [
|
| 586 |
+
65536,
|
| 587 |
+
256
|
| 588 |
+
],
|
| 589 |
+
"w13_code_dtypes": [
|
| 590 |
+
"int16"
|
| 591 |
+
],
|
| 592 |
+
"w2_code_dtypes": [
|
| 593 |
+
"int16",
|
| 594 |
+
"uint8"
|
| 595 |
+
],
|
| 596 |
+
"cold_code_layout": "per-book tensors {proj}_cold_codes.{b}, dtype uint8 if book<=256 entries else int16",
|
| 597 |
+
"aqlm_layer_books": {
|
| 598 |
+
"2": {
|
| 599 |
+
"n_hot": 4,
|
| 600 |
+
"n_nvfp4": 4,
|
| 601 |
+
"n_cold": 252,
|
| 602 |
+
"packed": false,
|
| 603 |
+
"hot_format": "bf16"
|
| 604 |
+
},
|
| 605 |
+
"3": {
|
| 606 |
+
"n_hot": 4,
|
| 607 |
+
"n_nvfp4": 4,
|
| 608 |
+
"n_cold": 252,
|
| 609 |
+
"packed": true,
|
| 610 |
+
"hot_format": "nvfp4"
|
| 611 |
+
},
|
| 612 |
+
"4": {
|
| 613 |
+
"n_hot": 4,
|
| 614 |
+
"n_nvfp4": 4,
|
| 615 |
+
"n_cold": 252,
|
| 616 |
+
"packed": true,
|
| 617 |
+
"hot_format": "nvfp4"
|
| 618 |
+
},
|
| 619 |
+
"5": {
|
| 620 |
+
"n_hot": 4,
|
| 621 |
+
"n_nvfp4": 4,
|
| 622 |
+
"n_cold": 252,
|
| 623 |
+
"packed": true,
|
| 624 |
+
"hot_format": "nvfp4"
|
| 625 |
+
},
|
| 626 |
+
"6": {
|
| 627 |
+
"n_hot": 4,
|
| 628 |
+
"n_nvfp4": 4,
|
| 629 |
+
"n_cold": 252,
|
| 630 |
+
"packed": true,
|
| 631 |
+
"hot_format": "nvfp4"
|
| 632 |
+
},
|
| 633 |
+
"7": {
|
| 634 |
+
"n_hot": 4,
|
| 635 |
+
"n_nvfp4": 4,
|
| 636 |
+
"n_cold": 252,
|
| 637 |
+
"packed": true,
|
| 638 |
+
"hot_format": "nvfp4"
|
| 639 |
+
},
|
| 640 |
+
"8": {
|
| 641 |
+
"n_hot": 4,
|
| 642 |
+
"n_nvfp4": 4,
|
| 643 |
+
"n_cold": 252,
|
| 644 |
+
"packed": true,
|
| 645 |
+
"hot_format": "nvfp4"
|
| 646 |
+
},
|
| 647 |
+
"9": {
|
| 648 |
+
"n_hot": 4,
|
| 649 |
+
"n_nvfp4": 4,
|
| 650 |
+
"n_cold": 252,
|
| 651 |
+
"packed": true,
|
| 652 |
+
"hot_format": "nvfp4"
|
| 653 |
+
},
|
| 654 |
+
"10": {
|
| 655 |
+
"n_hot": 4,
|
| 656 |
+
"n_nvfp4": 4,
|
| 657 |
+
"n_cold": 252,
|
| 658 |
+
"packed": true,
|
| 659 |
+
"hot_format": "nvfp4"
|
| 660 |
+
},
|
| 661 |
+
"11": {
|
| 662 |
+
"n_hot": 4,
|
| 663 |
+
"n_nvfp4": 4,
|
| 664 |
+
"n_cold": 252,
|
| 665 |
+
"packed": true,
|
| 666 |
+
"hot_format": "nvfp4"
|
| 667 |
+
},
|
| 668 |
+
"12": {
|
| 669 |
+
"n_hot": 4,
|
| 670 |
+
"n_nvfp4": 4,
|
| 671 |
+
"n_cold": 252,
|
| 672 |
+
"packed": true,
|
| 673 |
+
"hot_format": "nvfp4"
|
| 674 |
+
},
|
| 675 |
+
"13": {
|
| 676 |
+
"n_hot": 4,
|
| 677 |
+
"n_nvfp4": 4,
|
| 678 |
+
"n_cold": 252,
|
| 679 |
+
"packed": true,
|
| 680 |
+
"hot_format": "nvfp4"
|
| 681 |
+
},
|
| 682 |
+
"14": {
|
| 683 |
+
"n_hot": 4,
|
| 684 |
+
"n_nvfp4": 4,
|
| 685 |
+
"n_cold": 252,
|
| 686 |
+
"packed": true,
|
| 687 |
+
"hot_format": "nvfp4"
|
| 688 |
+
},
|
| 689 |
+
"15": {
|
| 690 |
+
"n_hot": 4,
|
| 691 |
+
"n_nvfp4": 4,
|
| 692 |
+
"n_cold": 252,
|
| 693 |
+
"packed": true,
|
| 694 |
+
"hot_format": "nvfp4"
|
| 695 |
+
},
|
| 696 |
+
"16": {
|
| 697 |
+
"n_hot": 4,
|
| 698 |
+
"n_nvfp4": 4,
|
| 699 |
+
"n_cold": 252,
|
| 700 |
+
"packed": true,
|
| 701 |
+
"hot_format": "nvfp4"
|
| 702 |
+
},
|
| 703 |
+
"17": {
|
| 704 |
+
"n_hot": 4,
|
| 705 |
+
"n_nvfp4": 4,
|
| 706 |
+
"n_cold": 252,
|
| 707 |
+
"packed": true,
|
| 708 |
+
"hot_format": "nvfp4"
|
| 709 |
+
},
|
| 710 |
+
"18": {
|
| 711 |
+
"n_hot": 4,
|
| 712 |
+
"n_nvfp4": 4,
|
| 713 |
+
"n_cold": 252,
|
| 714 |
+
"packed": true,
|
| 715 |
+
"hot_format": "nvfp4"
|
| 716 |
+
},
|
| 717 |
+
"19": {
|
| 718 |
+
"n_hot": 4,
|
| 719 |
+
"n_nvfp4": 4,
|
| 720 |
+
"n_cold": 252,
|
| 721 |
+
"packed": true,
|
| 722 |
+
"hot_format": "nvfp4"
|
| 723 |
+
},
|
| 724 |
+
"20": {
|
| 725 |
+
"n_hot": 4,
|
| 726 |
+
"n_nvfp4": 4,
|
| 727 |
+
"n_cold": 252,
|
| 728 |
+
"packed": true,
|
| 729 |
+
"hot_format": "nvfp4"
|
| 730 |
+
},
|
| 731 |
+
"21": {
|
| 732 |
+
"n_hot": 4,
|
| 733 |
+
"n_nvfp4": 4,
|
| 734 |
+
"n_cold": 252,
|
| 735 |
+
"packed": true,
|
| 736 |
+
"hot_format": "nvfp4"
|
| 737 |
+
},
|
| 738 |
+
"22": {
|
| 739 |
+
"n_hot": 4,
|
| 740 |
+
"n_nvfp4": 4,
|
| 741 |
+
"n_cold": 252,
|
| 742 |
+
"packed": true,
|
| 743 |
+
"hot_format": "nvfp4"
|
| 744 |
+
},
|
| 745 |
+
"23": {
|
| 746 |
+
"n_hot": 4,
|
| 747 |
+
"n_nvfp4": 4,
|
| 748 |
+
"n_cold": 252,
|
| 749 |
+
"packed": true,
|
| 750 |
+
"hot_format": "nvfp4"
|
| 751 |
+
},
|
| 752 |
+
"24": {
|
| 753 |
+
"n_hot": 4,
|
| 754 |
+
"n_nvfp4": 4,
|
| 755 |
+
"n_cold": 252,
|
| 756 |
+
"packed": true,
|
| 757 |
+
"hot_format": "nvfp4"
|
| 758 |
+
},
|
| 759 |
+
"25": {
|
| 760 |
+
"n_hot": 4,
|
| 761 |
+
"n_nvfp4": 4,
|
| 762 |
+
"n_cold": 252,
|
| 763 |
+
"packed": true,
|
| 764 |
+
"hot_format": "nvfp4"
|
| 765 |
+
},
|
| 766 |
+
"26": {
|
| 767 |
+
"n_hot": 4,
|
| 768 |
+
"n_nvfp4": 4,
|
| 769 |
+
"n_cold": 252,
|
| 770 |
+
"packed": true,
|
| 771 |
+
"hot_format": "nvfp4"
|
| 772 |
+
},
|
| 773 |
+
"27": {
|
| 774 |
+
"n_hot": 4,
|
| 775 |
+
"n_nvfp4": 4,
|
| 776 |
+
"n_cold": 252,
|
| 777 |
+
"packed": true,
|
| 778 |
+
"hot_format": "nvfp4"
|
| 779 |
+
},
|
| 780 |
+
"28": {
|
| 781 |
+
"n_hot": 4,
|
| 782 |
+
"n_nvfp4": 4,
|
| 783 |
+
"n_cold": 252,
|
| 784 |
+
"packed": true,
|
| 785 |
+
"hot_format": "nvfp4"
|
| 786 |
+
},
|
| 787 |
+
"29": {
|
| 788 |
+
"n_hot": 8,
|
| 789 |
+
"n_nvfp4": 8,
|
| 790 |
+
"n_cold": 248,
|
| 791 |
+
"packed": true,
|
| 792 |
+
"hot_format": "nvfp4"
|
| 793 |
+
},
|
| 794 |
+
"30": {
|
| 795 |
+
"n_hot": 10,
|
| 796 |
+
"n_nvfp4": 10,
|
| 797 |
+
"n_cold": 246,
|
| 798 |
+
"packed": true,
|
| 799 |
+
"hot_format": "nvfp4"
|
| 800 |
+
},
|
| 801 |
+
"31": {
|
| 802 |
+
"n_hot": 11,
|
| 803 |
+
"n_nvfp4": 11,
|
| 804 |
+
"n_cold": 245,
|
| 805 |
+
"packed": true,
|
| 806 |
+
"hot_format": "nvfp4"
|
| 807 |
+
},
|
| 808 |
+
"32": {
|
| 809 |
+
"n_hot": 18,
|
| 810 |
+
"n_nvfp4": 18,
|
| 811 |
+
"n_cold": 238,
|
| 812 |
+
"packed": true,
|
| 813 |
+
"hot_format": "nvfp4"
|
| 814 |
+
},
|
| 815 |
+
"33": {
|
| 816 |
+
"n_hot": 21,
|
| 817 |
+
"n_nvfp4": 21,
|
| 818 |
+
"n_cold": 235,
|
| 819 |
+
"packed": true,
|
| 820 |
+
"hot_format": "nvfp4"
|
| 821 |
+
},
|
| 822 |
+
"34": {
|
| 823 |
+
"n_hot": 34,
|
| 824 |
+
"n_nvfp4": 34,
|
| 825 |
+
"n_cold": 222,
|
| 826 |
+
"packed": true,
|
| 827 |
+
"hot_format": "nvfp4"
|
| 828 |
+
},
|
| 829 |
+
"35": {
|
| 830 |
+
"n_hot": 23,
|
| 831 |
+
"n_nvfp4": 23,
|
| 832 |
+
"n_cold": 233,
|
| 833 |
+
"packed": true,
|
| 834 |
+
"hot_format": "nvfp4"
|
| 835 |
+
},
|
| 836 |
+
"36": {
|
| 837 |
+
"n_hot": 27,
|
| 838 |
+
"n_nvfp4": 27,
|
| 839 |
+
"n_cold": 229,
|
| 840 |
+
"packed": true,
|
| 841 |
+
"hot_format": "nvfp4"
|
| 842 |
+
},
|
| 843 |
+
"37": {
|
| 844 |
+
"n_hot": 26,
|
| 845 |
+
"n_nvfp4": 26,
|
| 846 |
+
"n_cold": 230,
|
| 847 |
+
"packed": true,
|
| 848 |
+
"hot_format": "nvfp4"
|
| 849 |
+
},
|
| 850 |
+
"38": {
|
| 851 |
+
"n_hot": 30,
|
| 852 |
+
"n_nvfp4": 30,
|
| 853 |
+
"n_cold": 226,
|
| 854 |
+
"packed": true,
|
| 855 |
+
"hot_format": "nvfp4"
|
| 856 |
+
},
|
| 857 |
+
"39": {
|
| 858 |
+
"n_hot": 37,
|
| 859 |
+
"n_nvfp4": 37,
|
| 860 |
+
"n_cold": 219,
|
| 861 |
+
"packed": true,
|
| 862 |
+
"hot_format": "nvfp4"
|
| 863 |
+
},
|
| 864 |
+
"40": {
|
| 865 |
+
"n_hot": 45,
|
| 866 |
+
"n_nvfp4": 45,
|
| 867 |
+
"n_cold": 211,
|
| 868 |
+
"packed": true,
|
| 869 |
+
"hot_format": "nvfp4"
|
| 870 |
+
},
|
| 871 |
+
"41": {
|
| 872 |
+
"n_hot": 39,
|
| 873 |
+
"n_nvfp4": 39,
|
| 874 |
+
"n_cold": 217,
|
| 875 |
+
"packed": true,
|
| 876 |
+
"hot_format": "nvfp4"
|
| 877 |
+
},
|
| 878 |
+
"42": {
|
| 879 |
+
"n_hot": 35,
|
| 880 |
+
"n_nvfp4": 35,
|
| 881 |
+
"n_cold": 221,
|
| 882 |
+
"packed": true,
|
| 883 |
+
"hot_format": "nvfp4"
|
| 884 |
+
},
|
| 885 |
+
"43": {
|
| 886 |
+
"n_hot": 39,
|
| 887 |
+
"n_nvfp4": 39,
|
| 888 |
+
"n_cold": 217,
|
| 889 |
+
"packed": true,
|
| 890 |
+
"hot_format": "nvfp4"
|
| 891 |
+
},
|
| 892 |
+
"44": {
|
| 893 |
+
"n_hot": 32,
|
| 894 |
+
"n_nvfp4": 32,
|
| 895 |
+
"n_cold": 224,
|
| 896 |
+
"packed": true,
|
| 897 |
+
"hot_format": "nvfp4"
|
| 898 |
+
},
|
| 899 |
+
"45": {
|
| 900 |
+
"n_hot": 45,
|
| 901 |
+
"n_nvfp4": 45,
|
| 902 |
+
"n_cold": 211,
|
| 903 |
+
"packed": true,
|
| 904 |
+
"hot_format": "nvfp4"
|
| 905 |
+
},
|
| 906 |
+
"46": {
|
| 907 |
+
"n_hot": 50,
|
| 908 |
+
"n_nvfp4": 50,
|
| 909 |
+
"n_cold": 206,
|
| 910 |
+
"packed": true,
|
| 911 |
+
"hot_format": "nvfp4"
|
| 912 |
+
},
|
| 913 |
+
"47": {
|
| 914 |
+
"n_hot": 45,
|
| 915 |
+
"n_nvfp4": 45,
|
| 916 |
+
"n_cold": 211,
|
| 917 |
+
"packed": true,
|
| 918 |
+
"hot_format": "nvfp4"
|
| 919 |
+
},
|
| 920 |
+
"48": {
|
| 921 |
+
"n_hot": 47,
|
| 922 |
+
"n_nvfp4": 47,
|
| 923 |
+
"n_cold": 209,
|
| 924 |
+
"packed": true,
|
| 925 |
+
"hot_format": "nvfp4"
|
| 926 |
+
},
|
| 927 |
+
"49": {
|
| 928 |
+
"n_hot": 55,
|
| 929 |
+
"n_nvfp4": 55,
|
| 930 |
+
"n_cold": 201,
|
| 931 |
+
"packed": true,
|
| 932 |
+
"hot_format": "nvfp4"
|
| 933 |
+
},
|
| 934 |
+
"50": {
|
| 935 |
+
"n_hot": 48,
|
| 936 |
+
"n_nvfp4": 48,
|
| 937 |
+
"n_cold": 208,
|
| 938 |
+
"packed": true,
|
| 939 |
+
"hot_format": "nvfp4"
|
| 940 |
+
},
|
| 941 |
+
"51": {
|
| 942 |
+
"n_hot": 59,
|
| 943 |
+
"n_nvfp4": 59,
|
| 944 |
+
"n_cold": 197,
|
| 945 |
+
"packed": true,
|
| 946 |
+
"hot_format": "nvfp4"
|
| 947 |
+
},
|
| 948 |
+
"52": {
|
| 949 |
+
"n_hot": 79,
|
| 950 |
+
"n_nvfp4": 79,
|
| 951 |
+
"n_cold": 177,
|
| 952 |
+
"packed": true,
|
| 953 |
+
"hot_format": "nvfp4"
|
| 954 |
+
},
|
| 955 |
+
"53": {
|
| 956 |
+
"n_hot": 78,
|
| 957 |
+
"n_nvfp4": 78,
|
| 958 |
+
"n_cold": 178,
|
| 959 |
+
"packed": true,
|
| 960 |
+
"hot_format": "nvfp4"
|
| 961 |
+
},
|
| 962 |
+
"54": {
|
| 963 |
+
"n_hot": 79,
|
| 964 |
+
"n_nvfp4": 79,
|
| 965 |
+
"n_cold": 177,
|
| 966 |
+
"packed": true,
|
| 967 |
+
"hot_format": "nvfp4"
|
| 968 |
+
},
|
| 969 |
+
"55": {
|
| 970 |
+
"n_hot": 84,
|
| 971 |
+
"n_nvfp4": 84,
|
| 972 |
+
"n_cold": 172,
|
| 973 |
+
"packed": true,
|
| 974 |
+
"hot_format": "nvfp4"
|
| 975 |
+
},
|
| 976 |
+
"56": {
|
| 977 |
+
"n_hot": 92,
|
| 978 |
+
"n_nvfp4": 92,
|
| 979 |
+
"n_cold": 164,
|
| 980 |
+
"packed": true,
|
| 981 |
+
"hot_format": "nvfp4"
|
| 982 |
+
},
|
| 983 |
+
"57": {
|
| 984 |
+
"n_hot": 106,
|
| 985 |
+
"n_nvfp4": 106,
|
| 986 |
+
"n_cold": 150,
|
| 987 |
+
"packed": true,
|
| 988 |
+
"hot_format": "nvfp4"
|
| 989 |
+
},
|
| 990 |
+
"58": {
|
| 991 |
+
"n_hot": 118,
|
| 992 |
+
"n_nvfp4": 118,
|
| 993 |
+
"n_cold": 138,
|
| 994 |
+
"packed": true,
|
| 995 |
+
"hot_format": "nvfp4"
|
| 996 |
+
},
|
| 997 |
+
"59": {
|
| 998 |
+
"n_hot": 107,
|
| 999 |
+
"n_nvfp4": 107,
|
| 1000 |
+
"n_cold": 149,
|
| 1001 |
+
"packed": true,
|
| 1002 |
+
"hot_format": "nvfp4"
|
| 1003 |
+
},
|
| 1004 |
+
"60": {
|
| 1005 |
+
"n_hot": 110,
|
| 1006 |
+
"n_nvfp4": 110,
|
| 1007 |
+
"n_cold": 146,
|
| 1008 |
+
"packed": true,
|
| 1009 |
+
"hot_format": "nvfp4"
|
| 1010 |
+
},
|
| 1011 |
+
"61": {
|
| 1012 |
+
"n_hot": 118,
|
| 1013 |
+
"n_nvfp4": 118,
|
| 1014 |
+
"n_cold": 138,
|
| 1015 |
+
"packed": true,
|
| 1016 |
+
"hot_format": "nvfp4"
|
| 1017 |
+
},
|
| 1018 |
+
"62": {
|
| 1019 |
+
"n_hot": 110,
|
| 1020 |
+
"n_nvfp4": 110,
|
| 1021 |
+
"n_cold": 146,
|
| 1022 |
+
"packed": true,
|
| 1023 |
+
"hot_format": "nvfp4"
|
| 1024 |
+
},
|
| 1025 |
+
"63": {
|
| 1026 |
+
"n_hot": 147,
|
| 1027 |
+
"n_nvfp4": 147,
|
| 1028 |
+
"n_cold": 109,
|
| 1029 |
+
"packed": true,
|
| 1030 |
+
"hot_format": "nvfp4"
|
| 1031 |
+
},
|
| 1032 |
+
"64": {
|
| 1033 |
+
"n_hot": 178,
|
| 1034 |
+
"n_nvfp4": 178,
|
| 1035 |
+
"n_cold": 78,
|
| 1036 |
+
"packed": true,
|
| 1037 |
+
"hot_format": "nvfp4"
|
| 1038 |
+
},
|
| 1039 |
+
"65": {
|
| 1040 |
+
"n_hot": 119,
|
| 1041 |
+
"n_nvfp4": 119,
|
| 1042 |
+
"n_cold": 137,
|
| 1043 |
+
"packed": true,
|
| 1044 |
+
"hot_format": "nvfp4"
|
| 1045 |
+
}
|
| 1046 |
+
},
|
| 1047 |
+
"note": "cold experts: AQLM, per-book code tensors {proj}_cold_codes.{b} (w2 book0=int16@65536, book1=uint8@256; w13 book0=int16@65536) + per-book codebooks + per-row scales. hot experts: NVFP4 (hot_format=nvfp4) except layer 2 which has NO NVFP4 base and stores bf16 hot slabs {proj}_hot_bf16 (no scale/scale2, hot_format=bf16). Its cold experts are AQLM like every other layer. hot_ids/cold_ids give the routed-expert index for each slab."
|
| 1048 |
+
}
|
| 1049 |
+
}
|
model-00022-of-00045.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e317fa6fff7fc7a670fb796045e691026362e6bf204989266f2b26d42568eff7
|
| 3 |
+
size 8578233880
|
model-00023-of-00045.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a0a8b03eba80f9d802da22f0010092c598eb241b9d1a562bf719bda3fab7963f
|
| 3 |
+
size 8352080344
|
model-00027-of-00045.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:88a6b29aa4c54147274b122bedc55e2bbbb4d4e3ce0687d4a132d503e60233e6
|
| 3 |
+
size 8490002496
|
model-00031-of-00045.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d5dfe68d85e7fa49b5e679fe89809436d7e63d31fbb7f7c237e1da594672512d
|
| 3 |
+
size 7576180336
|
model-00032-of-00045.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:62d0720eab719e7506b650200d60d76971b778e3fba27bf7bb707c4fc704df28
|
| 3 |
+
size 8364945840
|
model-00041-of-00045.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:28df293809977e0966741702a04939f0b5e8f58b2e8f8505ee4675b7e5b03d67
|
| 3 |
+
size 6977939008
|
model-00043-of-00045.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7a904ed8c26e9621ea4e2071791a128fd7861058657fc5fac3d0346dfa32176e
|
| 3 |
+
size 7737135388
|
model.safetensors.index.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
processor_config.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"audio_token": "<|unused_200053|>",
|
| 3 |
+
"audio_bos_token": "<|content_audio_input|>",
|
| 4 |
+
"dmel_max_value": 2.0,
|
| 5 |
+
"dmel_min_value": -7.0,
|
| 6 |
+
"feature_extractor": {
|
| 7 |
+
"audio_token_duration_s": 0.05,
|
| 8 |
+
"feature_extractor_type": "InklingFeatureExtractor",
|
| 9 |
+
"feature_size": 80,
|
| 10 |
+
"hop_length": 800,
|
| 11 |
+
"n_fft": 1600,
|
| 12 |
+
"padding_side": "right",
|
| 13 |
+
"padding_value": 0.0,
|
| 14 |
+
"return_attention_mask": true,
|
| 15 |
+
"sampling_rate": 16000,
|
| 16 |
+
"window_size": 1600,
|
| 17 |
+
"window_size_multiplier": 2.0
|
| 18 |
+
},
|
| 19 |
+
"image_processor": {
|
| 20 |
+
"do_convert_rgb": true,
|
| 21 |
+
"do_normalize": true,
|
| 22 |
+
"do_rescale": true,
|
| 23 |
+
"do_resize": true,
|
| 24 |
+
"image_mean": [
|
| 25 |
+
0.48145466,
|
| 26 |
+
0.4578275,
|
| 27 |
+
0.40821073
|
| 28 |
+
],
|
| 29 |
+
"image_processor_type": "InklingImageProcessor",
|
| 30 |
+
"image_std": [
|
| 31 |
+
0.26862954,
|
| 32 |
+
0.26130258,
|
| 33 |
+
0.27577711
|
| 34 |
+
],
|
| 35 |
+
"resample": 3,
|
| 36 |
+
"rescale_factor": 0.00392156862745098,
|
| 37 |
+
"size": {
|
| 38 |
+
"height": 40,
|
| 39 |
+
"width": 40
|
| 40 |
+
}
|
| 41 |
+
},
|
| 42 |
+
"image_token": "<|unused_200054|>",
|
| 43 |
+
"image_bos_token": "<|content_image|>",
|
| 44 |
+
"num_dmel_bins": 16,
|
| 45 |
+
"processor_class": "InklingProcessor"
|
| 46 |
+
}
|
special_tokens_map.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"additional_special_tokens": [
|
| 3 |
+
"<|endoftext|>",
|
| 4 |
+
"<|message_user|>",
|
| 5 |
+
"<|message_model|>",
|
| 6 |
+
"<|message_system|>",
|
| 7 |
+
"<|message_tool|>",
|
| 8 |
+
"<|content_text|>",
|
| 9 |
+
"<|content_image|>",
|
| 10 |
+
"<|content_model_end_sampling|>",
|
| 11 |
+
"<|content_thinking|>",
|
| 12 |
+
"<|end_message|>",
|
| 13 |
+
"<|content_audio_input|>",
|
| 14 |
+
"<|content_tool_error|>",
|
| 15 |
+
"<|audio|>",
|
| 16 |
+
"<|content_xml|>",
|
| 17 |
+
"<|begin_of_text|>",
|
| 18 |
+
"<|audio_end|>",
|
| 19 |
+
"<|content_invoke_tool_json|>",
|
| 20 |
+
"<|content_invoke_tool_text|>"
|
| 21 |
+
]
|
| 22 |
+
}
|
tiktoken/tokenizer.model
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:bc253fd2b702f7a6da7105eaa8f3463b2f1247e83614f23e5323b921088bed2a
|
| 3 |
+
size 3615874
|
tokenizer_config.json
ADDED
|
@@ -0,0 +1,508 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"added_tokens_decoder": {
|
| 3 |
+
"199998": {
|
| 4 |
+
"content": "<|unused|>",
|
| 5 |
+
"single_word": false,
|
| 6 |
+
"lstrip": false,
|
| 7 |
+
"rstrip": false,
|
| 8 |
+
"normalized": false,
|
| 9 |
+
"special": true
|
| 10 |
+
},
|
| 11 |
+
"199999": {
|
| 12 |
+
"content": "<|endoftext|>",
|
| 13 |
+
"single_word": false,
|
| 14 |
+
"lstrip": false,
|
| 15 |
+
"rstrip": false,
|
| 16 |
+
"normalized": false,
|
| 17 |
+
"special": true
|
| 18 |
+
},
|
| 19 |
+
"200000": {
|
| 20 |
+
"content": "<|message_user|>",
|
| 21 |
+
"single_word": false,
|
| 22 |
+
"lstrip": false,
|
| 23 |
+
"rstrip": false,
|
| 24 |
+
"normalized": false,
|
| 25 |
+
"special": true
|
| 26 |
+
},
|
| 27 |
+
"200001": {
|
| 28 |
+
"content": "<|message_model|>",
|
| 29 |
+
"single_word": false,
|
| 30 |
+
"lstrip": false,
|
| 31 |
+
"rstrip": false,
|
| 32 |
+
"normalized": false,
|
| 33 |
+
"special": true
|
| 34 |
+
},
|
| 35 |
+
"200002": {
|
| 36 |
+
"content": "<|message_system|>",
|
| 37 |
+
"single_word": false,
|
| 38 |
+
"lstrip": false,
|
| 39 |
+
"rstrip": false,
|
| 40 |
+
"normalized": false,
|
| 41 |
+
"special": true
|
| 42 |
+
},
|
| 43 |
+
"200003": {
|
| 44 |
+
"content": "<|message_tool|>",
|
| 45 |
+
"single_word": false,
|
| 46 |
+
"lstrip": false,
|
| 47 |
+
"rstrip": false,
|
| 48 |
+
"normalized": false,
|
| 49 |
+
"special": true
|
| 50 |
+
},
|
| 51 |
+
"200004": {
|
| 52 |
+
"content": "<|content_text|>",
|
| 53 |
+
"single_word": false,
|
| 54 |
+
"lstrip": false,
|
| 55 |
+
"rstrip": false,
|
| 56 |
+
"normalized": false,
|
| 57 |
+
"special": true
|
| 58 |
+
},
|
| 59 |
+
"200005": {
|
| 60 |
+
"content": "<|content_image|>",
|
| 61 |
+
"single_word": false,
|
| 62 |
+
"lstrip": false,
|
| 63 |
+
"rstrip": false,
|
| 64 |
+
"normalized": false,
|
| 65 |
+
"special": true
|
| 66 |
+
},
|
| 67 |
+
"200006": {
|
| 68 |
+
"content": "<|content_model_end_sampling|>",
|
| 69 |
+
"single_word": false,
|
| 70 |
+
"lstrip": false,
|
| 71 |
+
"rstrip": false,
|
| 72 |
+
"normalized": false,
|
| 73 |
+
"special": true
|
| 74 |
+
},
|
| 75 |
+
"200007": {
|
| 76 |
+
"content": "<|unused_200007|>",
|
| 77 |
+
"single_word": false,
|
| 78 |
+
"lstrip": false,
|
| 79 |
+
"rstrip": false,
|
| 80 |
+
"normalized": false,
|
| 81 |
+
"special": true
|
| 82 |
+
},
|
| 83 |
+
"200008": {
|
| 84 |
+
"content": "<|content_thinking|>",
|
| 85 |
+
"single_word": false,
|
| 86 |
+
"lstrip": false,
|
| 87 |
+
"rstrip": false,
|
| 88 |
+
"normalized": false,
|
| 89 |
+
"special": true
|
| 90 |
+
},
|
| 91 |
+
"200009": {
|
| 92 |
+
"content": "<|unused_200009|>",
|
| 93 |
+
"single_word": false,
|
| 94 |
+
"lstrip": false,
|
| 95 |
+
"rstrip": false,
|
| 96 |
+
"normalized": false,
|
| 97 |
+
"special": true
|
| 98 |
+
},
|
| 99 |
+
"200010": {
|
| 100 |
+
"content": "<|end_message|>",
|
| 101 |
+
"single_word": false,
|
| 102 |
+
"lstrip": false,
|
| 103 |
+
"rstrip": false,
|
| 104 |
+
"normalized": false,
|
| 105 |
+
"special": true
|
| 106 |
+
},
|
| 107 |
+
"200011": {
|
| 108 |
+
"content": "<|unused_200011|>",
|
| 109 |
+
"single_word": false,
|
| 110 |
+
"lstrip": false,
|
| 111 |
+
"rstrip": false,
|
| 112 |
+
"normalized": false,
|
| 113 |
+
"special": true
|
| 114 |
+
},
|
| 115 |
+
"200012": {
|
| 116 |
+
"content": "<|unused_200012|>",
|
| 117 |
+
"single_word": false,
|
| 118 |
+
"lstrip": false,
|
| 119 |
+
"rstrip": false,
|
| 120 |
+
"normalized": false,
|
| 121 |
+
"special": true
|
| 122 |
+
},
|
| 123 |
+
"200013": {
|
| 124 |
+
"content": "<|unused_200013|>",
|
| 125 |
+
"single_word": false,
|
| 126 |
+
"lstrip": false,
|
| 127 |
+
"rstrip": false,
|
| 128 |
+
"normalized": false,
|
| 129 |
+
"special": true
|
| 130 |
+
},
|
| 131 |
+
"200014": {
|
| 132 |
+
"content": "<|unused_200014|>",
|
| 133 |
+
"single_word": false,
|
| 134 |
+
"lstrip": false,
|
| 135 |
+
"rstrip": false,
|
| 136 |
+
"normalized": false,
|
| 137 |
+
"special": true
|
| 138 |
+
},
|
| 139 |
+
"200015": {
|
| 140 |
+
"content": "<|unused_200015|>",
|
| 141 |
+
"single_word": false,
|
| 142 |
+
"lstrip": false,
|
| 143 |
+
"rstrip": false,
|
| 144 |
+
"normalized": false,
|
| 145 |
+
"special": true
|
| 146 |
+
},
|
| 147 |
+
"200016": {
|
| 148 |
+
"content": "<|unused_200016|>",
|
| 149 |
+
"single_word": false,
|
| 150 |
+
"lstrip": false,
|
| 151 |
+
"rstrip": false,
|
| 152 |
+
"normalized": false,
|
| 153 |
+
"special": true
|
| 154 |
+
},
|
| 155 |
+
"200017": {
|
| 156 |
+
"content": "<|unused_200017|>",
|
| 157 |
+
"single_word": false,
|
| 158 |
+
"lstrip": false,
|
| 159 |
+
"rstrip": false,
|
| 160 |
+
"normalized": false,
|
| 161 |
+
"special": true
|
| 162 |
+
},
|
| 163 |
+
"200018": {
|
| 164 |
+
"content": "<|unused_200018|>",
|
| 165 |
+
"single_word": false,
|
| 166 |
+
"lstrip": false,
|
| 167 |
+
"rstrip": false,
|
| 168 |
+
"normalized": false,
|
| 169 |
+
"special": true
|
| 170 |
+
},
|
| 171 |
+
"200019": {
|
| 172 |
+
"content": "<|unused_200019|>",
|
| 173 |
+
"single_word": false,
|
| 174 |
+
"lstrip": false,
|
| 175 |
+
"rstrip": false,
|
| 176 |
+
"normalized": false,
|
| 177 |
+
"special": true
|
| 178 |
+
},
|
| 179 |
+
"200020": {
|
| 180 |
+
"content": "<|content_audio_input|>",
|
| 181 |
+
"single_word": false,
|
| 182 |
+
"lstrip": false,
|
| 183 |
+
"rstrip": false,
|
| 184 |
+
"normalized": false,
|
| 185 |
+
"special": true
|
| 186 |
+
},
|
| 187 |
+
"200021": {
|
| 188 |
+
"content": "<|unused_200021|>",
|
| 189 |
+
"single_word": false,
|
| 190 |
+
"lstrip": false,
|
| 191 |
+
"rstrip": false,
|
| 192 |
+
"normalized": false,
|
| 193 |
+
"special": true
|
| 194 |
+
},
|
| 195 |
+
"200022": {
|
| 196 |
+
"content": "<|content_tool_error|>",
|
| 197 |
+
"single_word": false,
|
| 198 |
+
"lstrip": false,
|
| 199 |
+
"rstrip": false,
|
| 200 |
+
"normalized": false,
|
| 201 |
+
"special": true
|
| 202 |
+
},
|
| 203 |
+
"200023": {
|
| 204 |
+
"content": "<|audio|>",
|
| 205 |
+
"single_word": false,
|
| 206 |
+
"lstrip": false,
|
| 207 |
+
"rstrip": false,
|
| 208 |
+
"normalized": false,
|
| 209 |
+
"special": true
|
| 210 |
+
},
|
| 211 |
+
"200024": {
|
| 212 |
+
"content": "<|content_xml|>",
|
| 213 |
+
"single_word": false,
|
| 214 |
+
"lstrip": false,
|
| 215 |
+
"rstrip": false,
|
| 216 |
+
"normalized": false,
|
| 217 |
+
"special": true
|
| 218 |
+
},
|
| 219 |
+
"200025": {
|
| 220 |
+
"content": "<|unused_200025|>",
|
| 221 |
+
"single_word": false,
|
| 222 |
+
"lstrip": false,
|
| 223 |
+
"rstrip": false,
|
| 224 |
+
"normalized": false,
|
| 225 |
+
"special": true
|
| 226 |
+
},
|
| 227 |
+
"200026": {
|
| 228 |
+
"content": "<|unused_200026|>",
|
| 229 |
+
"single_word": false,
|
| 230 |
+
"lstrip": false,
|
| 231 |
+
"rstrip": false,
|
| 232 |
+
"normalized": false,
|
| 233 |
+
"special": true
|
| 234 |
+
},
|
| 235 |
+
"200027": {
|
| 236 |
+
"content": "<|unused_200027|>",
|
| 237 |
+
"single_word": false,
|
| 238 |
+
"lstrip": false,
|
| 239 |
+
"rstrip": false,
|
| 240 |
+
"normalized": false,
|
| 241 |
+
"special": true
|
| 242 |
+
},
|
| 243 |
+
"200028": {
|
| 244 |
+
"content": "<|begin_of_text|>",
|
| 245 |
+
"single_word": false,
|
| 246 |
+
"lstrip": false,
|
| 247 |
+
"rstrip": false,
|
| 248 |
+
"normalized": false,
|
| 249 |
+
"special": true
|
| 250 |
+
},
|
| 251 |
+
"200029": {
|
| 252 |
+
"content": "<|unused_200029|>",
|
| 253 |
+
"single_word": false,
|
| 254 |
+
"lstrip": false,
|
| 255 |
+
"rstrip": false,
|
| 256 |
+
"normalized": false,
|
| 257 |
+
"special": true
|
| 258 |
+
},
|
| 259 |
+
"200030": {
|
| 260 |
+
"content": "<|unused_200030|>",
|
| 261 |
+
"single_word": false,
|
| 262 |
+
"lstrip": false,
|
| 263 |
+
"rstrip": false,
|
| 264 |
+
"normalized": false,
|
| 265 |
+
"special": true
|
| 266 |
+
},
|
| 267 |
+
"200031": {
|
| 268 |
+
"content": "<|unused_200031|>",
|
| 269 |
+
"single_word": false,
|
| 270 |
+
"lstrip": false,
|
| 271 |
+
"rstrip": false,
|
| 272 |
+
"normalized": false,
|
| 273 |
+
"special": true
|
| 274 |
+
},
|
| 275 |
+
"200032": {
|
| 276 |
+
"content": "<|unused_200032|>",
|
| 277 |
+
"single_word": false,
|
| 278 |
+
"lstrip": false,
|
| 279 |
+
"rstrip": false,
|
| 280 |
+
"normalized": false,
|
| 281 |
+
"special": true
|
| 282 |
+
},
|
| 283 |
+
"200033": {
|
| 284 |
+
"content": "<|unused_200033|>",
|
| 285 |
+
"single_word": false,
|
| 286 |
+
"lstrip": false,
|
| 287 |
+
"rstrip": false,
|
| 288 |
+
"normalized": false,
|
| 289 |
+
"special": true
|
| 290 |
+
},
|
| 291 |
+
"200034": {
|
| 292 |
+
"content": "<|unused_200034|>",
|
| 293 |
+
"single_word": false,
|
| 294 |
+
"lstrip": false,
|
| 295 |
+
"rstrip": false,
|
| 296 |
+
"normalized": false,
|
| 297 |
+
"special": true
|
| 298 |
+
},
|
| 299 |
+
"200035": {
|
| 300 |
+
"content": "<|unused_200035|>",
|
| 301 |
+
"single_word": false,
|
| 302 |
+
"lstrip": false,
|
| 303 |
+
"rstrip": false,
|
| 304 |
+
"normalized": false,
|
| 305 |
+
"special": true
|
| 306 |
+
},
|
| 307 |
+
"200036": {
|
| 308 |
+
"content": "<|unused_200036|>",
|
| 309 |
+
"single_word": false,
|
| 310 |
+
"lstrip": false,
|
| 311 |
+
"rstrip": false,
|
| 312 |
+
"normalized": false,
|
| 313 |
+
"special": true
|
| 314 |
+
},
|
| 315 |
+
"200037": {
|
| 316 |
+
"content": "<|unused_200037|>",
|
| 317 |
+
"single_word": false,
|
| 318 |
+
"lstrip": false,
|
| 319 |
+
"rstrip": false,
|
| 320 |
+
"normalized": false,
|
| 321 |
+
"special": true
|
| 322 |
+
},
|
| 323 |
+
"200038": {
|
| 324 |
+
"content": "<|unused_200038|>",
|
| 325 |
+
"single_word": false,
|
| 326 |
+
"lstrip": false,
|
| 327 |
+
"rstrip": false,
|
| 328 |
+
"normalized": false,
|
| 329 |
+
"special": true
|
| 330 |
+
},
|
| 331 |
+
"200039": {
|
| 332 |
+
"content": "<|unused_200039|>",
|
| 333 |
+
"single_word": false,
|
| 334 |
+
"lstrip": false,
|
| 335 |
+
"rstrip": false,
|
| 336 |
+
"normalized": false,
|
| 337 |
+
"special": true
|
| 338 |
+
},
|
| 339 |
+
"200040": {
|
| 340 |
+
"content": "<|unused_200040|>",
|
| 341 |
+
"single_word": false,
|
| 342 |
+
"lstrip": false,
|
| 343 |
+
"rstrip": false,
|
| 344 |
+
"normalized": false,
|
| 345 |
+
"special": true
|
| 346 |
+
},
|
| 347 |
+
"200041": {
|
| 348 |
+
"content": "<|unused_200041|>",
|
| 349 |
+
"single_word": false,
|
| 350 |
+
"lstrip": false,
|
| 351 |
+
"rstrip": false,
|
| 352 |
+
"normalized": false,
|
| 353 |
+
"special": true
|
| 354 |
+
},
|
| 355 |
+
"200042": {
|
| 356 |
+
"content": "<|unused_200042|>",
|
| 357 |
+
"single_word": false,
|
| 358 |
+
"lstrip": false,
|
| 359 |
+
"rstrip": false,
|
| 360 |
+
"normalized": false,
|
| 361 |
+
"special": true
|
| 362 |
+
},
|
| 363 |
+
"200043": {
|
| 364 |
+
"content": "<|audio_end|>",
|
| 365 |
+
"single_word": false,
|
| 366 |
+
"lstrip": false,
|
| 367 |
+
"rstrip": false,
|
| 368 |
+
"normalized": false,
|
| 369 |
+
"special": true
|
| 370 |
+
},
|
| 371 |
+
"200044": {
|
| 372 |
+
"content": "<|unused_200044|>",
|
| 373 |
+
"single_word": false,
|
| 374 |
+
"lstrip": false,
|
| 375 |
+
"rstrip": false,
|
| 376 |
+
"normalized": false,
|
| 377 |
+
"special": true
|
| 378 |
+
},
|
| 379 |
+
"200045": {
|
| 380 |
+
"content": "<|unused_200045|>",
|
| 381 |
+
"single_word": false,
|
| 382 |
+
"lstrip": false,
|
| 383 |
+
"rstrip": false,
|
| 384 |
+
"normalized": false,
|
| 385 |
+
"special": true
|
| 386 |
+
},
|
| 387 |
+
"200046": {
|
| 388 |
+
"content": "<|unused_200046|>",
|
| 389 |
+
"single_word": false,
|
| 390 |
+
"lstrip": false,
|
| 391 |
+
"rstrip": false,
|
| 392 |
+
"normalized": false,
|
| 393 |
+
"special": true
|
| 394 |
+
},
|
| 395 |
+
"200047": {
|
| 396 |
+
"content": "<|unused_200047|>",
|
| 397 |
+
"single_word": false,
|
| 398 |
+
"lstrip": false,
|
| 399 |
+
"rstrip": false,
|
| 400 |
+
"normalized": false,
|
| 401 |
+
"special": true
|
| 402 |
+
},
|
| 403 |
+
"200048": {
|
| 404 |
+
"content": "<|unused_200048|>",
|
| 405 |
+
"single_word": false,
|
| 406 |
+
"lstrip": false,
|
| 407 |
+
"rstrip": false,
|
| 408 |
+
"normalized": false,
|
| 409 |
+
"special": true
|
| 410 |
+
},
|
| 411 |
+
"200049": {
|
| 412 |
+
"content": "<|content_invoke_tool_json|>",
|
| 413 |
+
"single_word": false,
|
| 414 |
+
"lstrip": false,
|
| 415 |
+
"rstrip": false,
|
| 416 |
+
"normalized": false,
|
| 417 |
+
"special": true
|
| 418 |
+
},
|
| 419 |
+
"200050": {
|
| 420 |
+
"content": "<|unused_200050|>",
|
| 421 |
+
"single_word": false,
|
| 422 |
+
"lstrip": false,
|
| 423 |
+
"rstrip": false,
|
| 424 |
+
"normalized": false,
|
| 425 |
+
"special": true
|
| 426 |
+
},
|
| 427 |
+
"200051": {
|
| 428 |
+
"content": "<|unused_200051|>",
|
| 429 |
+
"single_word": false,
|
| 430 |
+
"lstrip": false,
|
| 431 |
+
"rstrip": false,
|
| 432 |
+
"normalized": false,
|
| 433 |
+
"special": true
|
| 434 |
+
},
|
| 435 |
+
"200052": {
|
| 436 |
+
"content": "<|unused_200052|>",
|
| 437 |
+
"single_word": false,
|
| 438 |
+
"lstrip": false,
|
| 439 |
+
"rstrip": false,
|
| 440 |
+
"normalized": false,
|
| 441 |
+
"special": true
|
| 442 |
+
},
|
| 443 |
+
"200053": {
|
| 444 |
+
"content": "<|unused_200053|>",
|
| 445 |
+
"single_word": false,
|
| 446 |
+
"lstrip": false,
|
| 447 |
+
"rstrip": false,
|
| 448 |
+
"normalized": false,
|
| 449 |
+
"special": true
|
| 450 |
+
},
|
| 451 |
+
"200054": {
|
| 452 |
+
"content": "<|unused_200054|>",
|
| 453 |
+
"single_word": false,
|
| 454 |
+
"lstrip": false,
|
| 455 |
+
"rstrip": false,
|
| 456 |
+
"normalized": false,
|
| 457 |
+
"special": true
|
| 458 |
+
},
|
| 459 |
+
"200055": {
|
| 460 |
+
"content": "<|unused_200055|>",
|
| 461 |
+
"single_word": false,
|
| 462 |
+
"lstrip": false,
|
| 463 |
+
"rstrip": false,
|
| 464 |
+
"normalized": false,
|
| 465 |
+
"special": true
|
| 466 |
+
},
|
| 467 |
+
"200056": {
|
| 468 |
+
"content": "<|unused_200056|>",
|
| 469 |
+
"single_word": false,
|
| 470 |
+
"lstrip": false,
|
| 471 |
+
"rstrip": false,
|
| 472 |
+
"normalized": false,
|
| 473 |
+
"special": true
|
| 474 |
+
},
|
| 475 |
+
"200057": {
|
| 476 |
+
"content": "<|content_invoke_tool_text|>",
|
| 477 |
+
"single_word": false,
|
| 478 |
+
"lstrip": false,
|
| 479 |
+
"rstrip": false,
|
| 480 |
+
"normalized": false,
|
| 481 |
+
"special": true
|
| 482 |
+
}
|
| 483 |
+
},
|
| 484 |
+
"clean_up_tokenization_spaces": false,
|
| 485 |
+
"fix_mistral_regex": false,
|
| 486 |
+
"extra_special_tokens": {
|
| 487 |
+
"endoftext": "<|endoftext|>",
|
| 488 |
+
"message_user": "<|message_user|>",
|
| 489 |
+
"message_model": "<|message_model|>",
|
| 490 |
+
"message_system": "<|message_system|>",
|
| 491 |
+
"message_tool": "<|message_tool|>",
|
| 492 |
+
"content_text": "<|content_text|>",
|
| 493 |
+
"content_image": "<|content_image|>",
|
| 494 |
+
"content_model_end_sampling": "<|content_model_end_sampling|>",
|
| 495 |
+
"content_thinking": "<|content_thinking|>",
|
| 496 |
+
"end_message": "<|end_message|>",
|
| 497 |
+
"content_audio_input": "<|content_audio_input|>",
|
| 498 |
+
"content_tool_error": "<|content_tool_error|>",
|
| 499 |
+
"audio": "<|audio|>",
|
| 500 |
+
"content_xml": "<|content_xml|>",
|
| 501 |
+
"begin_of_text": "<|begin_of_text|>",
|
| 502 |
+
"audio_end": "<|audio_end|>",
|
| 503 |
+
"content_invoke_tool_json": "<|content_invoke_tool_json|>",
|
| 504 |
+
"content_invoke_tool_text": "<|content_invoke_tool_text|>"
|
| 505 |
+
},
|
| 506 |
+
"model_max_length": 1000000000000000019884624838656,
|
| 507 |
+
"tokenizer_class": "PreTrainedTokenizerFast"
|
| 508 |
+
}
|