psiddh commited on
Commit
86276a1
·
verified ·
1 Parent(s): 2764d1a

Add ExecuTorch WebGPU program for smolvlm-500m-instruct

Browse files
.gitattributes CHANGED
@@ -36,3 +36,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
36
  vlm_decoder.pte filter=lfs diff=lfs merge=lfs -text
37
  vlm_prefill.pte filter=lfs diff=lfs merge=lfs -text
38
  vlm_vision_q8.pte filter=lfs diff=lfs merge=lfs -text
 
 
 
36
  vlm_decoder.pte filter=lfs diff=lfs merge=lfs -text
37
  vlm_prefill.pte filter=lfs diff=lfs merge=lfs -text
38
  vlm_vision_q8.pte filter=lfs diff=lfs merge=lfs -text
39
+ smolvlm_decoder_q8.pte filter=lfs diff=lfs merge=lfs -text
40
+ smolvlm_vision_q8.pte filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -16,12 +16,12 @@ tags:
16
 
17
  This repository contains an [ExecuTorch](https://github.com/pytorch/executorch) conversion of
18
  [HuggingFaceTB/SmolVLM-500M-Instruct](https://huggingface.co/HuggingFaceTB/SmolVLM-500M-Instruct) for the ExecuTorch **WebGPU** backend, so the model runs entirely in a web browser on
19
- the visitor's GPU. It powers the corresponding demo in the ExecuTorch WebGPU Playground (launching soon on [executorch.ai](https://executorch.ai)).
20
 
21
  - **Base model:** [HuggingFaceTB/SmolVLM-500M-Instruct](https://huggingface.co/HuggingFaceTB/SmolVLM-500M-Instruct)
22
  - **Format:** ExecuTorch `.pte` programs delegated to the WebGPU backend
23
- - **Quantization:** int8 vision encoder and token embeddings; decoder as exported
24
- - **Total size:** 893.9 MB
25
 
26
  ## About the original model
27
 
@@ -43,15 +43,16 @@ Not for high-stakes decisions about people (for example identity, employment or
43
 
44
  ## What changed in this conversion
45
 
46
- - Vision encoder and token embeddings quantized to int8
47
- - Vision encoder, prefill and decoder exported as separate programs
48
- - Context capped at 512 tokens
 
49
 
50
  ## Inputs and outputs
51
 
52
- - `vlm_vision_q8.pte`: one image to visual tokens
53
- - `vlm_prefill.pte` and `vlm_decoder.pte`: prompt prefill, then token-by-token decoding; embeddings come from `embed_tokens.i8.bin` with `embed_tokens.scale.f32.bin`
54
- - Tokenize with the files in `tokenizer/`; `vlm_meta.json` lists the special tokens and sizes
55
 
56
  ## Files
57
 
@@ -59,30 +60,30 @@ Not for high-stakes decisions about people (for example identity, employment or
59
  |---|---|---|
60
  | `embed_tokens.i8.bin` | 45.1 MB | `d3ea47247be0b070…` |
61
  | `embed_tokens.scale.f32.bin` | 192.5 KB | `adefdadaae81d572…` |
 
 
62
  | `tokenizer/special_tokens_map.json` | 1.0 KB | `aa0ff906077086df…` |
63
  | `tokenizer/tokenizer.json` | 3.4 MB | `5ece781dc8d2b2f3…` |
64
  | `tokenizer/tokenizer_config.json` | 27.6 KB | `36c6fd44d07d10fd…` |
65
- | `vlm_decoder.pte` | 370.7 MB | `d92697a8c32eeaa1…` |
66
  | `vlm_meta.json` | 201 B | `b36fcf7ef7da8c2a…` |
67
- | `vlm_prefill.pte` | 370.7 MB | `1e8671eb77ae2f60…` |
68
- | `vlm_vision_q8.pte` | 103.8 MB | `5f09d6028d9bb945…` |
69
 
70
  Full checksums are in `manifest.json`. The playground verifies them after download.
71
 
72
  ## Running it
73
 
74
  These programs were exported with the ExecuTorch WebGPU backend
75
- ([backends/webgpu](https://github.com/pytorch/executorch/tree/main/backends/webgpu)) and validated with the
76
- WebAssembly runtime build used by the ExecuTorch WebGPU Playground (launching soon on [executorch.ai](https://executorch.ai)). That runtime build is not published yet; until it is,
77
- the playground is the supported way to run them. It downloads these files once, caches them in the browser,
78
  and runs everything locally.
79
 
80
  Requirements: a browser with WebGPU enabled.
81
 
82
  ## Provenance
83
 
84
- - Converted from the upstream checkpoint by the ExecuTorch team with the WebGPU export path.
85
- - A reproducible export recipe will be published alongside the open-source ExecuTorch WebGPU runtime.
 
 
86
 
87
  ## Citation
88
 
 
16
 
17
  This repository contains an [ExecuTorch](https://github.com/pytorch/executorch) conversion of
18
  [HuggingFaceTB/SmolVLM-500M-Instruct](https://huggingface.co/HuggingFaceTB/SmolVLM-500M-Instruct) for the ExecuTorch **WebGPU** backend, so the model runs entirely in a web browser on
19
+ the visitor's GPU. It powers the corresponding demo in the [ExecuTorch WebGPU Playground](https://executorch.ai/playground/).
20
 
21
  - **Base model:** [HuggingFaceTB/SmolVLM-500M-Instruct](https://huggingface.co/HuggingFaceTB/SmolVLM-500M-Instruct)
22
  - **Format:** ExecuTorch `.pte` programs delegated to the WebGPU backend
23
+ - **Quantization:** 8-bit linears in the vision encoder and decoder (int8 static activations in two passes, int8 per-channel GPTQ weights); int8 token embeddings
24
+ - **Total size:** 497.7 MB
25
 
26
  ## About the original model
27
 
 
43
 
44
  ## What changed in this conversion
45
 
46
+ - All linear layers of the vision encoder and decoder use 8-bit weights (per-channel, GPTQ) and 8-bit activations with one static scale per layer, applied in two passes (the second pass quantizes the first pass's rounding error)
47
+ - Token embeddings quantized to int8
48
+ - The key/value of the first prompt token (`<|im_start|>`) are built into the decoder, so prompts must start with it
49
+ - Context capped at 512 tokens; quantization changes outputs slightly compared with the original model
50
 
51
  ## Inputs and outputs
52
 
53
+ - `smolvlm_vision_q8.pte`: `pixels float32 [1, 3, 512, 512]` (scaled as `(x/255 - 0.5) / 0.5`) -> `image_embeds float32 [1, 64, 960]`
54
+ - `smolvlm_decoder_q8.pte`: `forward(embeds float32 [1, 4, 960], input_pos int64 [1]) -> logits float32 [1, 49280]` for the last of the 4 rows; feed windows of 4 consecutive positions, the KV cache lives inside the program
55
+ - Text embeddings come from `embed_tokens.i8.bin` with `embed_tokens.scale.f32.bin`; tokenize with the files in `tokenizer/`; `vlm_meta.json` lists the special tokens and sizes
56
 
57
  ## Files
58
 
 
60
  |---|---|---|
61
  | `embed_tokens.i8.bin` | 45.1 MB | `d3ea47247be0b070…` |
62
  | `embed_tokens.scale.f32.bin` | 192.5 KB | `adefdadaae81d572…` |
63
+ | `smolvlm_decoder_q8.pte` | 349.9 MB | `a54d81fbafd289c3…` |
64
+ | `smolvlm_vision_q8.pte` | 99.0 MB | `a48d6919aa2067be…` |
65
  | `tokenizer/special_tokens_map.json` | 1.0 KB | `aa0ff906077086df…` |
66
  | `tokenizer/tokenizer.json` | 3.4 MB | `5ece781dc8d2b2f3…` |
67
  | `tokenizer/tokenizer_config.json` | 27.6 KB | `36c6fd44d07d10fd…` |
 
68
  | `vlm_meta.json` | 201 B | `b36fcf7ef7da8c2a…` |
 
 
69
 
70
  Full checksums are in `manifest.json`. The playground verifies them after download.
71
 
72
  ## Running it
73
 
74
  These programs were exported with the ExecuTorch WebGPU backend
75
+ ([backends/webgpu](https://github.com/pytorch/executorch/tree/main/backends/webgpu)) and run in a browser (headless Chromium, WebGPU on SwiftShader) with the WebAssembly runtime build used by the [ExecuTorch WebGPU Playground](https://executorch.ai/playground/), where every generated token (78 of 78 over 4 turns) matched the same programs run on CPU. That runtime build is published in
76
+ [executorch-community/ExecuTorch-WebGPU-Runtime](https://huggingface.co/executorch-community/ExecuTorch-WebGPU-Runtime); the playground is the easiest way to run them. It downloads these files once, caches them in the browser,
 
77
  and runs everything locally.
78
 
79
  Requirements: a browser with WebGPU enabled.
80
 
81
  ## Provenance
82
 
83
+ - **Upstream:** `HuggingFaceTB/SmolVLM-500M-Instruct` at revision `a7da5b986cb59b408707209984f360a5f4ad7e47` (official)
84
+ - **Exported with:** executorch 1.5.1+cpu, torch 2.14.1+cpu, torchao 0.18.0+cpu, transformers 5.19.0 (executorch git `3b6068392324`)
85
+ - **Reproduce:** `python recipes/smolvlm-500m-instruct-q8.py --out out/smolvlm-500m-instruct-q8` (recipe in `export/`)
86
+ - Re-running the recipe produces byte-identical files.
87
 
88
  ## Citation
89
 
export/_common.py ADDED
@@ -0,0 +1,330 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Shared helpers for the ExecuTorch WebGPU export recipes in this directory.
2
+
3
+ Each recipe builds an eager model from a pinned upstream revision and calls
4
+ `export_vision` (or its own LLM path), which writes:
5
+
6
+ <out>/<name>_webgpu.pte program lowered with WebGPUPartitioner (delegate id "VulkanBackend")
7
+ <out>/<name>_portable.pte same exported graph without a delegate, for CPU validation only
8
+ <out>/recipe.json upstream id + revision, versions, shapes, dtype, command line
9
+ <out>/validation.json eager vs exported-program vs portable .pte (and goldens, if given)
10
+ """
11
+
12
+ import hashlib
13
+ import io
14
+ import json
15
+ import platform
16
+ import shlex
17
+ import sys
18
+ import urllib.request
19
+ import zipfile
20
+ from pathlib import Path
21
+
22
+ import numpy as np
23
+ import torch
24
+
25
+ PROXY_HINT = (
26
+ "set https_proxy if the machine needs a proxy for huggingface.co / pypi.org"
27
+ )
28
+
29
+
30
+ def versions():
31
+ import executorch
32
+ import torchao
33
+ from executorch.version import git_version
34
+
35
+ out = {
36
+ "python": platform.python_version(),
37
+ "torch": torch.__version__,
38
+ "torchao": torchao.__version__,
39
+ "executorch": getattr(executorch, "__version__", None)
40
+ or __import__("executorch.version").version.__version__,
41
+ "executorch_git": git_version,
42
+ }
43
+ try:
44
+ import transformers
45
+
46
+ out["transformers"] = transformers.__version__
47
+ except ImportError:
48
+ pass
49
+ try:
50
+ import huggingface_hub
51
+
52
+ out["huggingface_hub"] = huggingface_hub.__version__
53
+ except ImportError:
54
+ pass
55
+ return out
56
+
57
+
58
+ def sha256_file(path):
59
+ h = hashlib.sha256()
60
+ with open(path, "rb") as fh:
61
+ for chunk in iter(lambda: fh.read(1 << 20), b""):
62
+ h.update(chunk)
63
+ return h.hexdigest()
64
+
65
+
66
+ def hf_file(repo_id, filename, revision, expected_sha256=None, cache_dir=None):
67
+ from huggingface_hub import hf_hub_download
68
+
69
+ path = hf_hub_download(
70
+ repo_id=repo_id, filename=filename, revision=revision, cache_dir=cache_dir
71
+ )
72
+ digest = sha256_file(path)
73
+ if expected_sha256 and digest != expected_sha256:
74
+ sys.exit(
75
+ f"{repo_id}@{revision}:{filename} sha256 {digest} != {expected_sha256}"
76
+ )
77
+ return Path(path), digest
78
+
79
+
80
+ def pypi_wheel_member(url, expected_sha256, member):
81
+ """Read one file out of a PyPI wheel, verifying the wheel's sha256."""
82
+ with urllib.request.urlopen(url, timeout=120) as resp:
83
+ blob = resp.read()
84
+ digest = hashlib.sha256(blob).hexdigest()
85
+ if digest != expected_sha256:
86
+ sys.exit(f"{url}: sha256 {digest} != {expected_sha256} ({PROXY_HINT})")
87
+ with zipfile.ZipFile(io.BytesIO(blob)) as zf:
88
+ return zf.read(member).decode()
89
+
90
+
91
+ def scrub(text, out_dir):
92
+ """Replace machine-specific paths so recipe.json can ship with the model."""
93
+ text = text.replace(str(Path(out_dir).resolve()), "<out>")
94
+ text = text.replace(str(Path(__file__).resolve().parent), "recipes")
95
+ return text.replace(str(Path.home()), "~")
96
+
97
+
98
+ def command_line(out_dir):
99
+ argv = [f"recipes/{Path(sys.argv[0]).name}", *sys.argv[1:]]
100
+ args = []
101
+ for a in argv:
102
+ if a.startswith("/") or a.startswith("."):
103
+ a = scrub(str(Path(a).resolve()), out_dir)
104
+ args.append(a)
105
+ return scrub(" ".join(["python", *(shlex.quote(a) for a in args)]), out_dir)
106
+
107
+
108
+ def lower_webgpu(ep):
109
+ from executorch.backends.webgpu.partitioner import WebGPUPartitioner
110
+ from executorch.exir import to_edge_transform_and_lower
111
+
112
+ return to_edge_transform_and_lower(
113
+ ep, partitioner=[WebGPUPartitioner()]
114
+ ).to_executorch()
115
+
116
+
117
+ def lower_portable(ep):
118
+ from executorch.exir import to_edge_transform_and_lower
119
+
120
+ return to_edge_transform_and_lower(ep).to_executorch()
121
+
122
+
123
+ def delegate_summary(pte_bytes):
124
+ """Non-delegated kernels and per-delegate op histograms of a serialized program."""
125
+ import collections
126
+
127
+ from executorch.backends.vulkan.serialization.vulkan_graph_serialize import (
128
+ extract_vk_flatbuffer,
129
+ flatbuffer_to_vk_graph,
130
+ )
131
+ from executorch.exir._serialize._program import deserialize_pte_binary
132
+
133
+ pte = deserialize_pte_binary(pte_bytes)
134
+ prog = pte.program if hasattr(pte, "program") else pte
135
+ seg_base = int.from_bytes(pte_bytes[24:32], "little")
136
+ plan = prog.execution_plan[0]
137
+ ops = [f"{o.name}.{o.overload}" for o in plan.operators]
138
+ kernels = collections.Counter()
139
+ for chain in plan.chains:
140
+ for ins in chain.instructions:
141
+ if type(ins.instr_args).__name__ == "KernelCall":
142
+ kernels[ops[ins.instr_args.op_index]] += 1
143
+ delegates = []
144
+ for d in plan.delegates:
145
+ proc = d.processed
146
+ if proc.location.name == "INLINE":
147
+ blob = prog.backend_delegate_data[proc.index].data
148
+ else:
149
+ seg = prog.segments[proc.index]
150
+ blob = pte_bytes[seg_base + seg.offset : seg_base + seg.offset + seg.size]
151
+ g = flatbuffer_to_vk_graph(extract_vk_flatbuffer(blob))
152
+ delegates.append(
153
+ {
154
+ "id": d.id,
155
+ "ops": dict(collections.Counter(c.name for c in g.chain)),
156
+ "inputs": len(g.input_ids),
157
+ "outputs": len(g.output_ids),
158
+ }
159
+ )
160
+ return {"non_delegated_kernels": dict(kernels), "delegates": delegates}
161
+
162
+
163
+ def compare(a, b):
164
+ a = np.asarray(a, dtype=np.float64).ravel()
165
+ b = np.asarray(b, dtype=np.float64).ravel()
166
+ n = min(a.size, b.size)
167
+ a, b = a[:n], b[:n]
168
+ cos = float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b) + 1e-30))
169
+ return {
170
+ "max_abs": float(np.max(np.abs(a - b))),
171
+ "mean_abs": float(np.mean(np.abs(a - b))),
172
+ "cosine": cos,
173
+ }
174
+
175
+
176
+ def run_pte(pte_bytes, inputs):
177
+ from executorch.extension.pybindings.portable_lib import (
178
+ _load_for_executorch_from_buffer,
179
+ )
180
+
181
+ m = _load_for_executorch_from_buffer(pte_bytes)
182
+ return [
183
+ o.detach().numpy() if isinstance(o, torch.Tensor) else o
184
+ for o in m.forward(inputs)
185
+ ]
186
+
187
+
188
+ def try_run_webgpu_pte_on_cpu(pte_bytes, inputs):
189
+ try:
190
+ return run_pte(pte_bytes, inputs), None
191
+ except Exception as e: # noqa: BLE001 - the error text is the result
192
+ return None, f"{type(e).__name__}: {str(e).splitlines()[0][:300]}"
193
+
194
+
195
+ def export_vision(
196
+ *,
197
+ name,
198
+ model,
199
+ example_input,
200
+ out_dir,
201
+ upstream,
202
+ extra_recipe,
203
+ golden=None,
204
+ output_index=None,
205
+ ):
206
+ """Export, lower, write both .pte files, recipe.json, and validation.json.
207
+
208
+ `golden` is an optional (input ndarray, output ndarray) pair; when its input
209
+ shape matches `example_input`, all paths are also compared against it.
210
+ `output_index` picks one tensor when the eager model returns a tuple.
211
+ """
212
+ out_dir = Path(out_dir)
213
+ out_dir.mkdir(parents=True, exist_ok=True)
214
+ torch.manual_seed(0)
215
+ model = model.eval()
216
+
217
+ class Wrapped(torch.nn.Module):
218
+ def __init__(self, m):
219
+ super().__init__()
220
+ self.m = m
221
+
222
+ def forward(self, x):
223
+ y = self.m(x)
224
+ return y[output_index] if output_index is not None else y
225
+
226
+ wrapped = Wrapped(model).eval()
227
+ with torch.no_grad():
228
+ ep = torch.export.export(wrapped, (example_input,), strict=False)
229
+ webgpu = lower_webgpu(ep)
230
+ portable = lower_portable(ep)
231
+
232
+ webgpu_path = out_dir / f"{name}_webgpu.pte"
233
+ portable_path = out_dir / f"{name}_portable.pte"
234
+ webgpu_path.write_bytes(webgpu.buffer)
235
+ portable_path.write_bytes(portable.buffer)
236
+
237
+ summary = delegate_summary(webgpu.buffer)
238
+ out_shape = list(ep.module()(example_input).shape)
239
+ recipe = {
240
+ "model": name,
241
+ "upstream": upstream,
242
+ "versions": versions(),
243
+ "export": {
244
+ "api": "torch.export.export(strict=False) -> to_edge_transform_and_lower(partitioner=[WebGPUPartitioner()]) -> to_executorch()",
245
+ "partitioner": "executorch.backends.webgpu.partitioner.WebGPUPartitioner() (default compile options; wraps VulkanPartitioner)",
246
+ "inputs": [
247
+ {
248
+ "shape": list(example_input.shape),
249
+ "dtype": "float32",
250
+ "layout": "NCHW",
251
+ }
252
+ ],
253
+ "outputs": [{"shape": out_shape, "dtype": "float32"}],
254
+ "dtype": "float32",
255
+ "quantization": "none (fp32)",
256
+ **extra_recipe,
257
+ },
258
+ "files": {
259
+ webgpu_path.name: {
260
+ "bytes": webgpu_path.stat().st_size,
261
+ "sha256": sha256_file(webgpu_path),
262
+ },
263
+ portable_path.name: {
264
+ "bytes": portable_path.stat().st_size,
265
+ "sha256": sha256_file(portable_path),
266
+ "note": "CPU-only validation artifact (portable kernels, no delegate); not for release",
267
+ },
268
+ },
269
+ "graph": summary,
270
+ "command": command_line(out_dir),
271
+ }
272
+ (out_dir / "recipe.json").write_text(json.dumps(recipe, indent=2) + "\n")
273
+
274
+ # Validation on CPU.
275
+ gen = torch.Generator().manual_seed(1234)
276
+ rand_input = torch.rand(example_input.shape, generator=gen)
277
+ cases = {"random": rand_input}
278
+ if golden is not None and tuple(golden[0].shape) == tuple(example_input.shape):
279
+ cases["golden"] = torch.from_numpy(golden[0].copy())
280
+ report = {
281
+ "cpu_runtime": "executorch pybindings (portable_lib) from the pip wheel",
282
+ "cases": {},
283
+ }
284
+ webgpu_cpu_error = None
285
+ for label, x in cases.items():
286
+ with torch.no_grad():
287
+ eager = wrapped(x).numpy()
288
+ exported = ep.module()(x).numpy()
289
+ port = run_pte(portable.buffer, (x,))[0]
290
+ res = {
291
+ "exported_vs_eager": compare(exported, eager),
292
+ "portable_pte_vs_eager": compare(port, eager),
293
+ }
294
+ wg, err = try_run_webgpu_pte_on_cpu(webgpu.buffer, (x,))
295
+ if wg is not None:
296
+ res["webgpu_pte_vs_eager"] = compare(wg[0], eager)
297
+ else:
298
+ webgpu_cpu_error = err
299
+ if label == "golden":
300
+ ref = golden[1]
301
+ res["internal_golden_vs_eager"] = compare(eager, ref)
302
+ res["internal_golden_vs_portable_pte"] = compare(port, ref)
303
+ res["golden_note"] = (
304
+ "reference output from the internal goldens.manifest.json"
305
+ )
306
+ report["cases"][label] = res
307
+ report["webgpu_pte_on_cpu"] = (
308
+ "ran"
309
+ if webgpu_cpu_error is None
310
+ else f"not runnable on CPU pybindings: {webgpu_cpu_error}"
311
+ )
312
+ (out_dir / "validation.json").write_text(json.dumps(report, indent=2) + "\n")
313
+ print(
314
+ json.dumps(
315
+ {"recipe": recipe["files"], "graph": summary, "validation": report},
316
+ indent=2,
317
+ )
318
+ )
319
+ return ep, webgpu, portable
320
+
321
+
322
+ def load_golden(manifest_path, key):
323
+ man = json.loads(Path(manifest_path).read_text())[key]["tensors"]
324
+ base = Path(manifest_path).parent
325
+
326
+ def ld(t):
327
+ return np.fromfile(base / t["file"], dtype=np.float32).reshape(t["shape"])
328
+
329
+ inp = man.get("input")
330
+ return ld(inp), ld(man["output"])
export/_smolvlm_q8.py ADDED
@@ -0,0 +1,545 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Building blocks for the 8-bit SmolVLM WebGPU recipe (smolvlm-500m-instruct-q8.py).
2
+
3
+ The one 8-bit linear that public ExecuTorch lowers for WebGPU and the public WebGPU runtime runs is
4
+ et_vk.linear_q8ta_q8csw: int8 activations with one static per-tensor scale, times int8 weights with
5
+ one scale per output channel. It is emitted for linear(dq(q(x, s, z)), dq_per_channel(W8, ws, 0)),
6
+ which Q8Linear below writes out directly with quantized_decomposed ops (no PT2E prepare/convert:
7
+ the scales come from torchao MinMaxObservers run over calibration data on the fp32 model).
8
+
9
+ Two things make it accurate enough for this model (see VLM_FOLLOWUP.md for the measurements):
10
+
11
+ - Two passes. y = W8.dq(q(x, s1)) + W8.dq(q(r, s2)), r = x - dq(q(x, s1)): the second pass quantizes
12
+ the first pass's rounding error with its own static scale (s2 ~ s1/255) and reuses the same int8
13
+ weight, so the activation error drops from s1/2 to about s1/510. The residual is written with
14
+ plain ops (round/clamp/mul): the lowering would fold an explicit dq(q(x)) back to x for a
15
+ non-quantized consumer, which makes the residual zero.
16
+ - A built-in attention sink. The text decoder's position 0 is always <|im_start|>, whose hidden
17
+ state carries massive activations (|x| ~ 3000 at the input of layer 3's down projection, vs ~4 at
18
+ every other position). Its K/V are the same in every conversation, so they are computed once in
19
+ fp32 and written into the cache by the program itself whenever a call starts at position 0
20
+ (SinkKVCache); the calibration excludes that row, so it never sets a scale.
21
+ """
22
+
23
+ import math
24
+
25
+ import torch
26
+ import torch.nn.functional as F
27
+
28
+ qd = torch.ops.quantized_decomposed
29
+ QMIN, QMAX = -128, 127
30
+
31
+
32
+ # ----------------------------------------------------------------------------- quantization
33
+
34
+
35
+ def weight_int8(w):
36
+ """VulkanQuantizer's weight spec: int8 [-128, 127], per-output-channel symmetric, scale from
37
+ torchao PerChannelMinMaxObserver (amax / 127.5). Returns (int8 [N, K], fp32 scales [N])."""
38
+ from torchao.quantization.pt2e import PerChannelMinMaxObserver
39
+
40
+ obs = PerChannelMinMaxObserver(
41
+ ch_axis=0,
42
+ dtype=torch.int8,
43
+ quant_min=QMIN,
44
+ quant_max=QMAX,
45
+ qscheme=torch.per_channel_symmetric,
46
+ )
47
+ obs(w)
48
+ scale, zp = obs.calculate_qparams()
49
+ assert int(zp.abs().max()) == 0
50
+ scale = scale.float()
51
+ q = torch.clamp(torch.round(w / scale[:, None]), QMIN, QMAX).to(torch.int8)
52
+ return q, scale
53
+
54
+
55
+ def act_observer():
56
+ """Static per-tensor affine int8 activation observer (min/max of everything it saw)."""
57
+ from torchao.quantization.pt2e import MinMaxObserver
58
+
59
+ return MinMaxObserver(
60
+ dtype=torch.int8,
61
+ quant_min=QMIN,
62
+ quant_max=QMAX,
63
+ qscheme=torch.per_tensor_affine,
64
+ )
65
+
66
+
67
+ def qparams(obs):
68
+ s, z = obs.calculate_qparams()
69
+ return float(s), int(z)
70
+
71
+
72
+ def fake_quant(x, s, z):
73
+ """dq(q(x)) exactly as quantized_decomposed computes it (round(x * (1/s)) + z, clamp)."""
74
+ return (torch.clamp(torch.round(x * (1.0 / s)) + z, QMIN, QMAX) - z) * s
75
+
76
+
77
+ class Q8Linear(torch.nn.Module):
78
+ """et_vk.linear_q8ta_q8csw, one or two passes (see the module docstring)."""
79
+
80
+ def __init__(self, lin, act, second=None):
81
+ super().__init__()
82
+ q, scale = weight_int8(lin.weight.detach().float())
83
+ self.register_buffer("weight_int8", q)
84
+ self.register_buffer("weight_scale", scale)
85
+ self.register_buffer("weight_zero", torch.zeros(q.shape[0], dtype=torch.int64))
86
+ self.bias = (
87
+ None
88
+ if lin.bias is None
89
+ else torch.nn.Parameter(lin.bias.detach().float(), requires_grad=False)
90
+ )
91
+ self.act = (float(act[0]), int(act[1]))
92
+ self.second = None if second is None else (float(second[0]), int(second[1]))
93
+
94
+ def forward(self, x):
95
+ s1, z1 = self.act
96
+ w = qd.dequantize_per_channel(
97
+ self.weight_int8,
98
+ self.weight_scale,
99
+ self.weight_zero,
100
+ 0,
101
+ QMIN,
102
+ QMAX,
103
+ torch.int8,
104
+ )
105
+ x1 = qd.dequantize_per_tensor(
106
+ qd.quantize_per_tensor(x, s1, z1, QMIN, QMAX, torch.int8),
107
+ s1,
108
+ z1,
109
+ QMIN,
110
+ QMAX,
111
+ torch.int8,
112
+ )
113
+ y = F.linear(x1, w, self.bias)
114
+ if self.second is not None:
115
+ s2, z2 = self.second
116
+ r = x - fake_quant(x, s1, z1)
117
+ r1 = qd.dequantize_per_tensor(
118
+ qd.quantize_per_tensor(r, s2, z2, QMIN, QMAX, torch.int8),
119
+ s2,
120
+ z2,
121
+ QMIN,
122
+ QMAX,
123
+ torch.int8,
124
+ )
125
+ y = y + F.linear(r1, w)
126
+ return y
127
+
128
+
129
+ def linear_names(module):
130
+ return [n for n, m in module.named_modules() if isinstance(m, torch.nn.Linear)]
131
+
132
+
133
+ def set_module(root, name, new):
134
+ parent, _, leaf = name.rpartition(".")
135
+ setattr(root.get_submodule(parent) if parent else root, leaf, new)
136
+
137
+
138
+ class Recorder:
139
+ """Forward pre-hooks feeding one observer per linear. `skip_first_row` (set by the feeder while
140
+ a call starts at position 0) drops row 0, the attention-sink position, from what is observed."""
141
+
142
+ def __init__(self, module, names, transform=None):
143
+ self.obs = {n: act_observer() for n in names}
144
+ self.skip_first_row = False
145
+ self.hooks = []
146
+ for n in names:
147
+
148
+ def hook(m, args, n=n):
149
+ x = args[0].detach().float()
150
+ if self.skip_first_row and x.dim() == 3:
151
+ x = x[:, 1:]
152
+ x = x.reshape(-1, x.shape[-1])
153
+ if x.numel():
154
+ self.obs[n](transform(n, x) if transform else x)
155
+
156
+ self.hooks.append(module.get_submodule(n).register_forward_pre_hook(hook))
157
+
158
+ def close(self):
159
+ for h in self.hooks:
160
+ h.remove()
161
+ return {n: qparams(o) for n, o in self.obs.items()}
162
+
163
+
164
+ def calibrate(module, names, feed, two_pass):
165
+ """Static activation qparams for every linear in `names`: {name: (first, second or None)}.
166
+ feed(recorder) runs the fp32 module over the calibration data (twice for two passes: the
167
+ second run observes each linear's first-pass rounding error)."""
168
+ rec = Recorder(module, names)
169
+ with torch.no_grad():
170
+ feed(rec)
171
+ first = rec.close()
172
+ second = {}
173
+ if two_pass:
174
+ rec = Recorder(
175
+ module, names, transform=lambda n, x: x - fake_quant(x, *first[n])
176
+ )
177
+ with torch.no_grad():
178
+ feed(rec)
179
+ second = rec.close()
180
+ return {n: (first[n], second.get(n)) for n in names}
181
+
182
+
183
+ def quantize(module, act):
184
+ """Replace every linear named in `act` by Q8Linear (in place)."""
185
+ for n, (first, second) in act.items():
186
+ set_module(module, n, Q8Linear(module.get_submodule(n), first, second))
187
+ return module
188
+
189
+
190
+ _FAKE_FIXED = []
191
+
192
+
193
+ def fix_q8ta_fake():
194
+ """executorch 1.5.1's reference kernel for et_vk.linear_q8ta_q8csw computes linear(x_int8, w_fp32),
195
+ so under FakeTensorMode (the Vulkan FusePatternsPass re-trace) its output comes out int8. Every
196
+ consumer then inherits int8 metadata: llama::custom_sdpa's meta check fails on the query, and
197
+ the serialized delegate would declare fp32 activations as int8 (the WebGPU kernel rejects a
198
+ non-fp32 output). A fake kernel with the real output dtype (fp32, [..., N]) fixes the metadata;
199
+ the serialized ops and constants are otherwise unchanged."""
200
+ if _FAKE_FIXED:
201
+ return
202
+ import executorch.backends.vulkan.custom_ops_lib # noqa: F401 (defines et_vk ops)
203
+
204
+ @torch.library.register_fake("et_vk::linear_q8ta_q8csw")
205
+ def _fake(x, input_scale, input_zero_point, weights, weight_sums, weight_scales, bias=None):
206
+ return x.new_empty((*x.shape[:-1], weights.shape[0]), dtype=torch.float32)
207
+
208
+ _FAKE_FIXED.append(True)
209
+
210
+
211
+ # ----------------------------------------------------------------------------- attention sink
212
+
213
+
214
+ class SinkKVCache(torch.nn.Module):
215
+ """executorch's CustomKVCache (llama::update_cache into a [1, C, H, D] cache) with the K/V of
216
+ position 0 built in: when a call starts at position 0, its row 0 is replaced by the constants
217
+ before the cache update. sink = relu(1 - input_pos) is 1 at position 0 and 0 everywhere else,
218
+ so later calls are untouched (x + 0 * (c - x) = x)."""
219
+
220
+ def __init__(self, base, k0, v0, rows):
221
+ super().__init__()
222
+ self.max_context_length = base.max_context_length
223
+ self.max_batch_size, self.n_heads, self.head_dim = (
224
+ base.max_batch_size,
225
+ base.n_heads,
226
+ base.head_dim,
227
+ )
228
+ self.register_buffer("k_cache", base.k_cache)
229
+ self.register_buffer("v_cache", base.v_cache)
230
+ # [B, S, H, D], the cache layout (update() receives [B, H, S, D] and transposes back first,
231
+ # so the arithmetic below sees one dim order: the portable kernels require that).
232
+ self.register_buffer("k0", k0.detach().float().contiguous())
233
+ self.register_buffer("v0", v0.detach().float().contiguous())
234
+ first_row = torch.zeros(1, rows, 1, 1)
235
+ first_row[0, 0, 0, 0] = 1.0
236
+ self.register_buffer("first_row", first_row)
237
+
238
+ def update(self, input_pos, k_val, v_val, indices=None):
239
+ assert indices is None
240
+ k_val = k_val.transpose(1, 2)
241
+ v_val = v_val.transpose(1, 2)
242
+ sink = torch.relu(1.0 - input_pos[:1].to(torch.float32)).reshape(1, 1, 1, 1) * self.first_row
243
+ k_val = k_val + sink * (self.k0 - k_val)
244
+ v_val = v_val + sink * (self.v0 - v_val)
245
+ start_pos = input_pos[0].item()
246
+ _ = torch.ops.llama.update_cache(k_val, self.k_cache, start_pos)
247
+ _ = torch.ops.llama.update_cache(v_val, self.v_cache, start_pos)
248
+ return self.k_cache.transpose(1, 2), self.v_cache.transpose(1, 2)
249
+
250
+
251
+ def sink_kv(transformer, row0):
252
+ """Per-layer K/V of position 0 for the fp32 transformer, given its input row [hidden]."""
253
+ for name, buf in transformer.named_buffers():
254
+ if name.endswith("k_cache") or name.endswith("v_cache"):
255
+ buf.zero_()
256
+ with torch.no_grad():
257
+ transformer(None, {"input_pos": torch.tensor([0])}, row0[None, None])
258
+ out = []
259
+ for layer in transformer.layers:
260
+ kv = layer.attention.kv_cache
261
+ out.append((kv.k_cache[:, :1].clone(), kv.v_cache[:, :1].clone()))
262
+ kv.k_cache.zero_()
263
+ kv.v_cache.zero_()
264
+ return out
265
+
266
+
267
+ def install_sink(transformer, kvs, rows):
268
+ for layer, (k0, v0) in zip(transformer.layers, kvs):
269
+ layer.attention.kv_cache = SinkKVCache(layer.attention.kv_cache, k0, v0, rows)
270
+ return transformer
271
+
272
+
273
+ class RefSDPA(torch.nn.Module):
274
+ """Causal attention over the KV cache in plain PyTorch, used only while collecting calibration
275
+ statistics. llama::custom_sdpa's CPU kernel divides its work by the threads it gets, so its float
276
+ sums (and with them a few activation ranges, in the last bits) vary from run to run; this keeps
277
+ the calibration, and the exported files, byte-reproducible. Same inputs/outputs as SDPACustom."""
278
+
279
+ def __init__(self, dim, n_rep):
280
+ super().__init__()
281
+ self.dim, self.n_rep = dim, n_rep
282
+
283
+ def forward(self, input_pos, q, k, v, bsz, seqlen, mask):
284
+ # q [B, Hq, S, D]; k, v [B, Hkv, L, D] (the whole cache)
285
+ end = int(input_pos[0]) + seqlen
286
+ k = k[:, :, :end].repeat_interleave(self.n_rep, dim=1)
287
+ v = v[:, :, :end].repeat_interleave(self.n_rep, dim=1)
288
+ scores = (q @ k.transpose(-1, -2)) / math.sqrt(q.shape[-1])
289
+ rows = torch.arange(end - seqlen, end)[:, None]
290
+ scores = scores.masked_fill(torch.arange(end)[None, :] > rows, float("-inf"))
291
+ out = torch.softmax(scores, dim=-1) @ v
292
+ return out.transpose(1, 2).reshape(bsz, seqlen, self.dim)
293
+
294
+
295
+ class reference_attention:
296
+ """Context manager: every layer's SDPA module replaced by RefSDPA, restored on exit."""
297
+
298
+ def __init__(self, transformer):
299
+ self.t = transformer
300
+
301
+ def __enter__(self):
302
+ self.saved = []
303
+ for layer in self.t.layers:
304
+ a = layer.attention
305
+ self.saved.append(a.SDPA)
306
+ a.SDPA = RefSDPA(a.SDPA.dim, a.n_heads // a.n_kv_heads)
307
+ return self.t
308
+
309
+ def __exit__(self, *exc):
310
+ for layer, sdpa in zip(self.t.layers, self.saved):
311
+ layer.attention.SDPA = sdpa
312
+
313
+
314
+ # ----------------------------------------------------------------------------- windows
315
+
316
+
317
+ def window_starts(start, end, rows):
318
+ """First positions of the `rows`-row windows that feed positions [start, end): each window ends
319
+ at most at `end`, the last exactly there (the same plan as windowStarts() in vlm.js)."""
320
+ if start >= end:
321
+ return []
322
+ assert end >= rows, (start, end, rows)
323
+ out, stop = [], min(start + rows, end)
324
+ while True:
325
+ out.append(stop - rows)
326
+ if stop == end:
327
+ return out
328
+ stop = min(stop + rows, end)
329
+
330
+
331
+ # ----------------------------------------------------------------------------- inspection
332
+
333
+
334
+ def q8_ops_vs_eager(pte_bytes, module):
335
+ """Every et_vk.linear_q8ta_q8csw in the delegates: its int8 weight and weight scales must be
336
+ bit-identical to one Q8Linear's buffers, and its activation scale / zero point one of that
337
+ linear's static qparams (scale to 1e-7 relative: the flatc JSON dump prints about 8 significant
338
+ digits). Returns counts."""
339
+ import hashlib
340
+
341
+ import _smolvlm as S
342
+ import numpy as np
343
+ from executorch.backends.vulkan.serialization.vulkan_graph_serialize import (
344
+ VulkanDelegateHeader,
345
+ )
346
+ from executorch.exir._serialize._program import deserialize_pte_binary
347
+
348
+ eager = {}
349
+ for m in module.modules():
350
+ if isinstance(m, Q8Linear):
351
+ key = hashlib.sha256(m.weight_int8.numpy().tobytes()).hexdigest()
352
+ eager[key] = (
353
+ m.weight_scale.numpy(),
354
+ {m.act} | ({m.second} if m.second else set()),
355
+ )
356
+ pte = deserialize_pte_binary(pte_bytes)
357
+ prog = pte.program if hasattr(pte, "program") else pte
358
+ nd = getattr(pte, "named_data", None)
359
+ seg_base = int.from_bytes(pte_bytes[24:32], "little")
360
+ total = weights_ok = scales_ok = act_ok = 0
361
+ for plan in prog.execution_plan:
362
+ for d in plan.delegates:
363
+ proc = d.processed
364
+ if proc.location.name == "INLINE":
365
+ blob = bytes(prog.backend_delegate_data[proc.index].data)
366
+ else:
367
+ seg = prog.segments[proc.index]
368
+ blob = pte_bytes[
369
+ seg_base + seg.offset : seg_base + seg.offset + seg.size
370
+ ]
371
+ h = VulkanDelegateHeader.from_bytes(
372
+ blob[: VulkanDelegateHeader.EXPECTED_LENGTH]
373
+ )
374
+ raw = blob[h.bytes_offset : h.bytes_offset + h.bytes_size]
375
+ g = S._vk_graph(blob)
376
+
377
+ def const(vid):
378
+ t = g.values[vid].value
379
+ c = g.constants[t.constant_id]
380
+ if c.named_key:
381
+ return bytes(
382
+ nd.buffers[nd.pte_data[c.named_key].buffer_index]
383
+ ), list(t.dims)
384
+ return raw[c.offset : c.offset + c.length], list(t.dims)
385
+
386
+ for op in g.chain:
387
+ if op.name != "et_vk.linear_q8ta_q8csw.default":
388
+ continue
389
+ total += 1
390
+ wb, (n, k) = const(op.args[3])
391
+ hit = eager.get(hashlib.sha256(wb[: n * k]).hexdigest())
392
+ if hit is None:
393
+ continue
394
+ weights_ok += 1
395
+ sb, _ = const(op.args[5])
396
+ scales = np.frombuffer(sb, dtype=np.float32)[:n]
397
+ scales_ok += int(np.array_equal(scales, hit[0]))
398
+ s = g.values[op.args[1]].value.double_val
399
+ z = g.values[op.args[2]].value.int_val
400
+ act_ok += int(
401
+ any(abs(s - a) <= 1e-7 * a and z == b for a, b in hit[1])
402
+ )
403
+ return {
404
+ "linear_q8ta_q8csw_ops": total,
405
+ "weight_int8_bit_identical": weights_ok,
406
+ "weight_scales_bit_identical": scales_ok,
407
+ "activation_qparams_match": act_ok,
408
+ "eager_q8_linears": len(eager),
409
+ }
410
+
411
+
412
+ # ----------------------------------------------------------------------------- GPTQ (optional)
413
+
414
+
415
+ def gptq_int8(w, H, damp=0.01, block=128):
416
+ """GPTQ (Frantar et al. 2022) for the same int8 grid as weight_int8 (per-output-channel symmetric,
417
+ scale = amax / 127.5 of the original weight): columns are rounded in order and each rounding
418
+ error is pushed onto the not-yet-rounded columns through the inverse Hessian H = sum x x^T of the
419
+ layer's calibration inputs. Returns (int8 [N, K], fp32 scales [N]); float64 throughout."""
420
+ _, scale = weight_int8(w)
421
+ s = scale.double()[:, None]
422
+ w = w.detach().double().clone()
423
+ H = H.detach().double().clone()
424
+ K = w.shape[1]
425
+ dead = torch.diag(H) == 0
426
+ H[dead, dead] = 1
427
+ w[:, dead] = 0
428
+ H += damp * torch.mean(torch.diag(H)) * torch.eye(K, dtype=H.dtype)
429
+ hinv = torch.linalg.cholesky(torch.cholesky_inverse(torch.linalg.cholesky(H)), upper=True)
430
+ q = torch.zeros_like(w)
431
+ for i1 in range(0, K, block):
432
+ i2 = min(i1 + block, K)
433
+ w1 = w[:, i1:i2].clone()
434
+ err1 = torch.zeros_like(w1)
435
+ h1 = hinv[i1:i2, i1:i2]
436
+ for i in range(i2 - i1):
437
+ col = w1[:, i : i + 1]
438
+ qc = torch.clamp(torch.round(col / s), QMIN, QMAX)
439
+ q[:, i1 + i : i1 + i + 1] = qc
440
+ e = (col - qc * s) / h1[i, i]
441
+ w1[:, i:] -= e * h1[i : i + 1, i:]
442
+ err1[:, i : i + 1] = e
443
+ w[:, i2:] -= err1 @ hinv[i1:i2, i2:]
444
+ return q.to(torch.int8), scale
445
+
446
+
447
+ def hessians(module, names, feed):
448
+ """sum x x^T (float64) of every named linear's input over feed(recorder-like ctx)."""
449
+ hs, hooks = {}, []
450
+
451
+ class Ctx:
452
+ skip_first_row = False
453
+
454
+ ctx = Ctx()
455
+ for n in names:
456
+
457
+ def hook(m, args, n=n):
458
+ x = args[0].detach().double()
459
+ if ctx.skip_first_row and x.dim() == 3:
460
+ x = x[:, 1:]
461
+ x = x.reshape(-1, x.shape[-1])
462
+ hs[n] = hs[n] + x.T @ x if n in hs else x.T @ x
463
+ rows[n] = rows.get(n, 0) + x.shape[0]
464
+
465
+ hooks.append(module.get_submodule(n).register_forward_pre_hook(hook))
466
+ rows = {}
467
+ with torch.no_grad():
468
+ feed(ctx)
469
+ for h in hooks:
470
+ h.remove()
471
+ hessians.rows = rows
472
+ return hs
473
+
474
+
475
+ # ----------------------------------------------------------------------------- model builders
476
+
477
+
478
+ def quantize_with(module, act, weights=None):
479
+ """Like quantize(), with optional precomputed (int8, scale) weights per linear (GPTQ)."""
480
+ for n, (first, second) in act.items():
481
+ lin = module.get_submodule(n)
482
+ q = Q8Linear(lin, first, second)
483
+ if weights and n in weights:
484
+ q.weight_int8.copy_(weights[n][0])
485
+ q.weight_scale.copy_(weights[n][1])
486
+ set_module(module, n, q)
487
+ return module
488
+
489
+
490
+ def build_vision(vision_fp32, images, *, two_pass, gptq):
491
+ """Quantize a (deep-copied) fp32 VisionEncoder: every linear becomes Q8Linear, scales from the
492
+ calibration images (pixel tensors [1,3,S,S])."""
493
+ import copy
494
+
495
+ v = copy.deepcopy(vision_fp32).eval()
496
+ names = linear_names(v)
497
+
498
+ def feed(rec):
499
+ for px in images:
500
+ v(px)
501
+
502
+ act = calibrate(v, names, feed, two_pass)
503
+ weights = None
504
+ if gptq:
505
+ # GPTQ only where the calibration rows outnumber the input features: the connector
506
+ # (12288 inputs, 64 rows per image) keeps round-to-nearest.
507
+ hs = hessians(v, names, feed)
508
+ weights = {n: gptq_int8(v.get_submodule(n).weight, hs[n]) for n in names if hessians.rows[n] >= hs[n].shape[0]}
509
+ del hs
510
+ return quantize_with(v, act, weights), act
511
+
512
+
513
+ def build_text(transformer, calib_rows, *, rows, two_pass, gptq, sink_row=None):
514
+ """Quantize the fp32 executorch llama Transformer in place.
515
+
516
+ calib_rows: list of [n, hidden] embedding sequences (prompt + continuation), fed in the same
517
+ `rows`-row windows the browser uses; row 0 (the sink) is excluded from every statistic.
518
+ sink_row: the input row of position 0 (the <|im_start|> embedding as the host feeds it), or None
519
+ for no built-in sink. Returns (transformer, activation qparams, sink K/V)."""
520
+ import _smolvlm as S
521
+
522
+ names = linear_names(transformer)
523
+ sink = sink_row is not None
524
+ with reference_attention(transformer):
525
+ kvs = sink_kv(transformer, sink_row) if sink else None
526
+
527
+ def feed(rec):
528
+ for seq in calib_rows:
529
+ S.reset_kv(transformer)
530
+ for start in window_starts(0, seq.shape[0], rows):
531
+ rec.skip_first_row = sink and start == 0
532
+ transformer(None, {"input_pos": torch.tensor([start])}, seq[None, start : start + rows])
533
+ rec.skip_first_row = False
534
+ S.reset_kv(transformer)
535
+
536
+ with reference_attention(transformer):
537
+ act = calibrate(transformer, names, feed, two_pass)
538
+ hs = hessians(transformer, names, feed) if gptq else None
539
+ weights = None
540
+ if gptq:
541
+ weights = {n: gptq_int8(transformer.get_submodule(n).weight, hs.pop(n)) for n in names}
542
+ quantize_with(transformer, act, weights)
543
+ if sink:
544
+ install_sink(transformer, kvs, rows)
545
+ return transformer, act, kvs
export/recipe.json ADDED
@@ -0,0 +1,3510 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model": "smolvlm-500m-instruct-q8",
3
+ "variant": "q8",
4
+ "upstream": {
5
+ "model": "HuggingFaceTB/SmolVLM-500M-Instruct",
6
+ "revision": "a7da5b986cb59b408707209984f360a5f4ad7e47",
7
+ "official": true,
8
+ "sha256": {
9
+ "model.safetensors": "d05b567eeaf534e83d375551f068ed57b5f52d37c657197f644af5ef9db091a2",
10
+ "config.json": "daacbbca6af3c34e50466c72aed2df4553a08084f6a0551d4ae420a241cb66c6",
11
+ "generation_config.json": "067a2a54e5f87162ecac6e0e911cc4665fc8f7f3324794ecbac0f76badb56636",
12
+ "preprocessor_config.json": "6cb6e36d6fcb88ca1502c4a26750715dc3e7dedddc9a8f17b27d8d167d1457e7",
13
+ "tokenizer.json": "5ece781dc8d2b2f3e2f289ca0ae50b17cfc27dd27bfe7971bb8241e0b964331a",
14
+ "tokenizer_config.json": "36c6fd44d07d10fd8180ee6b46dcccf69fb7c06753968ff0d7e17b8bfe17b777",
15
+ "special_tokens_map.json": "aa0ff906077086dfa9734a7f97f68c825877a48f9468807be65504495cdeef09"
16
+ }
17
+ },
18
+ "versions": {
19
+ "python": "3.12.15+meta",
20
+ "torch": "2.14.1+cpu",
21
+ "torchao": "0.18.0+cpu",
22
+ "executorch": "1.5.1+cpu",
23
+ "executorch_git": "3b60683923245cf472b7323426920e15623ba361",
24
+ "transformers": "5.19.0",
25
+ "huggingface_hub": "2.2.0"
26
+ },
27
+ "export": {
28
+ "partitioner": "executorch.backends.webgpu.partitioner.WebGPUPartitioner, default options (static shapes)",
29
+ "linear": "et_vk.linear_q8ta_q8csw: int8 activations, one static per-tensor affine scale per pass (torchao MinMaxObserver over the calibration data, [-128, 127]); int8 weights, per-output-channel symmetric (VulkanQuantizer weight spec, scale amax/127.5)",
30
+ "export_workaround": "_smolvlm_q8.fix_q8ta_fake(): a fake kernel giving et_vk.linear_q8ta_q8csw its real fp32 output dtype during lowering (executorch 1.5.1's reference kernel reports int8, which breaks llama::custom_sdpa's meta check and would mis-declare tensors). Metadata only",
31
+ "programs": {
32
+ "smolvlm_vision_q8.pte": {
33
+ "api": "torch.export.export(strict=False) -> to_edge_transform_and_lower(partitioner=[WebGPUPartitioner()]) -> to_executorch()",
34
+ "module": "transformers Idefics3 vision tower (eager attention) + post_layernorm + connector; identity position ids for a full 512x512 image",
35
+ "inputs": [
36
+ {
37
+ "name": "pixel_values",
38
+ "shape": [
39
+ 1,
40
+ 3,
41
+ 512,
42
+ 512
43
+ ],
44
+ "dtype": "float32",
45
+ "range": "(x/255 - 0.5)/0.5"
46
+ }
47
+ ],
48
+ "outputs": [
49
+ {
50
+ "name": "image_embeds",
51
+ "shape": [
52
+ 1,
53
+ 64,
54
+ 960
55
+ ],
56
+ "dtype": "float32"
57
+ }
58
+ ],
59
+ "quantization": "all 73 linears 8-bit, two passes (second pass on the first pass's rounding error, same int8 weight), weights GPTQ (damp 0.01, block 128, float64) on the calibration inputs, same grid (the connector, with fewer calibration rows than inputs, round to nearest); patch embedding conv, layer norms, position table fp32"
60
+ },
61
+ "smolvlm_decoder_q8.pte": {
62
+ "api": "LLMEdgeManager: export(strict) -> run_canonical_optimizations -> export_to_edge -> to_backend([WebGPUPartitioner()]) -> to_executorch; static shapes",
63
+ "module": "executorch examples/models/llama Transformer (apply_embedding=False, use_hf_rope, enable_dynamic_shape for the start position) with weights from executorch.examples.models.smolvlm.convert_weights; source transforms from export_llama_lib._get_source_transforms(use_sdpa_with_kv_cache, use_kv_cache)",
64
+ "params": {
65
+ "dim": 960,
66
+ "hidden_dim": 2560,
67
+ "n_heads": 15,
68
+ "n_kv_heads": 5,
69
+ "head_dim": 64,
70
+ "n_layers": 32,
71
+ "norm_eps": 1e-05,
72
+ "rope_theta": 100000.0,
73
+ "use_scaled_rope": false,
74
+ "use_hf_rope": true,
75
+ "attention_qkv_bias": false,
76
+ "vocab_size": 49280,
77
+ "apply_embedding": false
78
+ },
79
+ "inputs": [
80
+ {
81
+ "name": "embeds",
82
+ "shape": [
83
+ 1,
84
+ 4,
85
+ 960
86
+ ],
87
+ "dtype": "float32",
88
+ "note": "rows of positions input_pos .. input_pos + rows - 1"
89
+ },
90
+ {
91
+ "name": "input_pos",
92
+ "shape": [
93
+ 1
94
+ ],
95
+ "dtype": "int64",
96
+ "note": "position of the first row"
97
+ }
98
+ ],
99
+ "outputs": [
100
+ {
101
+ "name": "logits",
102
+ "shape": [
103
+ 1,
104
+ 49280
105
+ ],
106
+ "dtype": "float32",
107
+ "note": "last row only"
108
+ }
109
+ ],
110
+ "kv_cache": "in the program: 32 layers x k/v [1,512,5,64] fp32; every call writes its rows' positions",
111
+ "attention_sink": "position 0 must be <|im_start|> (id 1), as the chat template always starts: its per-layer K/V (fp32 model, int8-table embedding row) are constants the program writes over row 0 when input_pos is 0; that row is excluded from calibration",
112
+ "host_contract": "feed positions in windows of `rows` rows ending at the newest position (window_starts in _smolvlm_q8.py / windowStarts in vlm.js); input_pos + rows <= max_context",
113
+ "max_context_length": 512,
114
+ "quantization": "all 225 linears incl. lm_head 8-bit, two passes (second pass on the first pass's rounding error, same int8 weight), weights GPTQ (damp 0.01, block 128, float64) on the calibration inputs, same grid; RMSNorm weights fp32"
115
+ }
116
+ },
117
+ "calibration": {
118
+ "pairs": [
119
+ {
120
+ "image": "yolo_cat.jpg",
121
+ "image_sha256": "88b9eb3e6ec026c5ab96aab516f4a4a2773522a474abc52e934a52d86bba21e5",
122
+ "question": "What animal is this?",
123
+ "rows": 85,
124
+ "continuation": " This is a cat."
125
+ },
126
+ {
127
+ "image": "yolo_people.jpg",
128
+ "image_sha256": "16d73869e3267a7d4ed00de8e860833bd1657c1b252e94c0c348277adc7b6edb",
129
+ "question": "Describe this image",
130
+ "rows": 110,
131
+ "continuation": " The image depicts two men standing on a field, likely during a sports event or a public gathering. The men are dressed in formal attire, suggesting that they might"
132
+ },
133
+ {
134
+ "image": "yolo_street.jpg",
135
+ "image_sha256": "c02019c4979c191eb739ddd944445ef408dad5679acab6fd520ef9d434bfbc63",
136
+ "question": "What vehicles can you see?",
137
+ "rows": 88,
138
+ "continuation": " There are buses in the image."
139
+ },
140
+ {
141
+ "image": "yolo_table.jpg",
142
+ "image_sha256": "0ef0c3f7caa3ad4ec92dc8968f9ac0e81ede5b4805aae89648a4c7c4236768df",
143
+ "question": "What objects are on the table?",
144
+ "rows": 110,
145
+ "continuation": " There are cups, saucers, a cup with coffee, a bowl with pastries, and a plate with a napkin on the table."
146
+ },
147
+ {
148
+ "image": "sample_shapes.jpg",
149
+ "image_sha256": "f7911a973ef5a6dc962b92349f65a34f33b15e4c078052f8166018fe8338e074",
150
+ "question": "What shapes are in this picture?",
151
+ "rows": 114,
152
+ "continuation": " The image contains four distinct geometric shapes. Here is a detailed description of each shape:\n\n1. **Red Triangle**: Located on the left side of the image"
153
+ },
154
+ {
155
+ "image": "chihuahuas.jpg",
156
+ "image_sha256": "6f18287a5728ce32e3714b28a971f5c62d57e2cf0f689786611d438d5d7b7522",
157
+ "question": "How many dogs are there?",
158
+ "rows": 89,
159
+ "continuation": " There are two dogs in the image."
160
+ },
161
+ {
162
+ "image": "kitchen.jpg",
163
+ "image_sha256": "dbecca629f4ebe4c6d311c07f55de921bd77d32d6c85ab32e7a8033175556580",
164
+ "question": "Describe this image",
165
+ "rows": 110,
166
+ "continuation": " A kitchen with a large island in the center, which has a granite countertop and a sink with a double faucet. There are two large bowls on the island"
167
+ },
168
+ {
169
+ "image": "giraffes.jpg",
170
+ "image_sha256": "e10d0f49a1e3b0d01bb6186bf59ca25a2c310a8d7d8ca7781eec76313ccc4ea1",
171
+ "question": "What is happening here?",
172
+ "rows": 88,
173
+ "continuation": " There are giraffes walking in the field."
174
+ }
175
+ ],
176
+ "continuation_tokens": 32,
177
+ "decoder_windows": "4 rows, the browser's window plan",
178
+ "vision": "the 8 calibration images"
179
+ },
180
+ "activation_qparams": {
181
+ "vision": {
182
+ "layers.0.self_attn.k_proj": [
183
+ [
184
+ 0.11057502031326294,
185
+ 0
186
+ ],
187
+ [
188
+ 0.00043362739961594343,
189
+ -1
190
+ ]
191
+ ],
192
+ "layers.0.self_attn.v_proj": [
193
+ [
194
+ 0.11057502031326294,
195
+ 0
196
+ ],
197
+ [
198
+ 0.00043362739961594343,
199
+ -1
200
+ ]
201
+ ],
202
+ "layers.0.self_attn.q_proj": [
203
+ [
204
+ 0.11057502031326294,
205
+ 0
206
+ ],
207
+ [
208
+ 0.00043362739961594343,
209
+ -1
210
+ ]
211
+ ],
212
+ "layers.0.self_attn.out_proj": [
213
+ [
214
+ 0.03311925381422043,
215
+ 9
216
+ ],
217
+ [
218
+ 0.00012987936497665942,
219
+ -1
220
+ ]
221
+ ],
222
+ "layers.0.mlp.fc1": [
223
+ [
224
+ 0.29603707790374756,
225
+ 3
226
+ ],
227
+ [
228
+ 0.0011609295615926385,
229
+ -1
230
+ ]
231
+ ],
232
+ "layers.0.mlp.fc2": [
233
+ [
234
+ 0.04607229679822922,
235
+ -124
236
+ ],
237
+ [
238
+ 0.00018067570636048913,
239
+ 0
240
+ ]
241
+ ],
242
+ "layers.1.self_attn.k_proj": [
243
+ [
244
+ 0.11797387152910233,
245
+ -7
246
+ ],
247
+ [
248
+ 0.00046264249249361455,
249
+ 0
250
+ ]
251
+ ],
252
+ "layers.1.self_attn.v_proj": [
253
+ [
254
+ 0.11797387152910233,
255
+ -7
256
+ ],
257
+ [
258
+ 0.00046264249249361455,
259
+ 0
260
+ ]
261
+ ],
262
+ "layers.1.self_attn.q_proj": [
263
+ [
264
+ 0.11797387152910233,
265
+ -7
266
+ ],
267
+ [
268
+ 0.00046264249249361455,
269
+ 0
270
+ ]
271
+ ],
272
+ "layers.1.self_attn.out_proj": [
273
+ [
274
+ 0.025301799178123474,
275
+ 5
276
+ ],
277
+ [
278
+ 9.922267781803384e-05,
279
+ 0
280
+ ]
281
+ ],
282
+ "layers.1.mlp.fc1": [
283
+ [
284
+ 0.15761414170265198,
285
+ -11
286
+ ],
287
+ [
288
+ 0.0006180948112159967,
289
+ -1
290
+ ]
291
+ ],
292
+ "layers.1.mlp.fc2": [
293
+ [
294
+ 0.021325966343283653,
295
+ -120
296
+ ],
297
+ [
298
+ 8.363127562915906e-05,
299
+ 0
300
+ ]
301
+ ],
302
+ "layers.2.self_attn.k_proj": [
303
+ [
304
+ 0.18797777593135834,
305
+ -25
306
+ ],
307
+ [
308
+ 0.0007371677202172577,
309
+ -1
310
+ ]
311
+ ],
312
+ "layers.2.self_attn.v_proj": [
313
+ [
314
+ 0.18797777593135834,
315
+ -25
316
+ ],
317
+ [
318
+ 0.0007371677202172577,
319
+ -1
320
+ ]
321
+ ],
322
+ "layers.2.self_attn.q_proj": [
323
+ [
324
+ 0.18797777593135834,
325
+ -25
326
+ ],
327
+ [
328
+ 0.0007371677202172577,
329
+ -1
330
+ ]
331
+ ],
332
+ "layers.2.self_attn.out_proj": [
333
+ [
334
+ 0.02796168625354767,
335
+ -9
336
+ ],
337
+ [
338
+ 0.0001096536434488371,
339
+ -1
340
+ ]
341
+ ],
342
+ "layers.2.mlp.fc1": [
343
+ [
344
+ 0.15474054217338562,
345
+ -36
346
+ ],
347
+ [
348
+ 0.0006068257498554885,
349
+ 0
350
+ ]
351
+ ],
352
+ "layers.2.mlp.fc2": [
353
+ [
354
+ 0.019050095230340958,
355
+ -119
356
+ ],
357
+ [
358
+ 7.470636046491563e-05,
359
+ -1
360
+ ]
361
+ ],
362
+ "layers.3.self_attn.k_proj": [
363
+ [
364
+ 0.18854397535324097,
365
+ -16
366
+ ],
367
+ [
368
+ 0.0007393882842734456,
369
+ -1
370
+ ]
371
+ ],
372
+ "layers.3.self_attn.v_proj": [
373
+ [
374
+ 0.18854397535324097,
375
+ -16
376
+ ],
377
+ [
378
+ 0.0007393882842734456,
379
+ -1
380
+ ]
381
+ ],
382
+ "layers.3.self_attn.q_proj": [
383
+ [
384
+ 0.18854397535324097,
385
+ -16
386
+ ],
387
+ [
388
+ 0.0007393882842734456,
389
+ -1
390
+ ]
391
+ ],
392
+ "layers.3.self_attn.out_proj": [
393
+ [
394
+ 0.028024116531014442,
395
+ 26
396
+ ],
397
+ [
398
+ 0.00010989848669851199,
399
+ -1
400
+ ]
401
+ ],
402
+ "layers.3.mlp.fc1": [
403
+ [
404
+ 0.1748019903898239,
405
+ -28
406
+ ],
407
+ [
408
+ 0.0006854981184005737,
409
+ -1
410
+ ]
411
+ ],
412
+ "layers.3.mlp.fc2": [
413
+ [
414
+ 0.021401548758149147,
415
+ -120
416
+ ],
417
+ [
418
+ 8.392766176257282e-05,
419
+ 0
420
+ ]
421
+ ],
422
+ "layers.4.self_attn.k_proj": [
423
+ [
424
+ 0.2204122543334961,
425
+ -24
426
+ ],
427
+ [
428
+ 0.0008643616456538439,
429
+ 0
430
+ ]
431
+ ],
432
+ "layers.4.self_attn.v_proj": [
433
+ [
434
+ 0.2204122543334961,
435
+ -24
436
+ ],
437
+ [
438
+ 0.0008643616456538439,
439
+ 0
440
+ ]
441
+ ],
442
+ "layers.4.self_attn.q_proj": [
443
+ [
444
+ 0.2204122543334961,
445
+ -24
446
+ ],
447
+ [
448
+ 0.0008643616456538439,
449
+ 0
450
+ ]
451
+ ],
452
+ "layers.4.self_attn.out_proj": [
453
+ [
454
+ 0.026799356564879417,
455
+ 5
456
+ ],
457
+ [
458
+ 0.00010509543790249154,
459
+ 0
460
+ ]
461
+ ],
462
+ "layers.4.mlp.fc1": [
463
+ [
464
+ 0.21318072080612183,
465
+ -15
466
+ ],
467
+ [
468
+ 0.000836003280710429,
469
+ 0
470
+ ]
471
+ ],
472
+ "layers.4.mlp.fc2": [
473
+ [
474
+ 0.03725965693593025,
475
+ -123
476
+ ],
477
+ [
478
+ 0.00014611640654038638,
479
+ 0
480
+ ]
481
+ ],
482
+ "layers.5.self_attn.k_proj": [
483
+ [
484
+ 0.1860852837562561,
485
+ -7
486
+ ],
487
+ [
488
+ 0.0007297461852431297,
489
+ -1
490
+ ]
491
+ ],
492
+ "layers.5.self_attn.v_proj": [
493
+ [
494
+ 0.1860852837562561,
495
+ -7
496
+ ],
497
+ [
498
+ 0.0007297461852431297,
499
+ -1
500
+ ]
501
+ ],
502
+ "layers.5.self_attn.q_proj": [
503
+ [
504
+ 0.1860852837562561,
505
+ -7
506
+ ],
507
+ [
508
+ 0.0007297461852431297,
509
+ -1
510
+ ]
511
+ ],
512
+ "layers.5.self_attn.out_proj": [
513
+ [
514
+ 0.03250356763601303,
515
+ 21
516
+ ],
517
+ [
518
+ 0.00012746502761729062,
519
+ 0
520
+ ]
521
+ ],
522
+ "layers.5.mlp.fc1": [
523
+ [
524
+ 0.22218218445777893,
525
+ -6
526
+ ],
527
+ [
528
+ 0.0008713023853488266,
529
+ -1
530
+ ]
531
+ ],
532
+ "layers.5.mlp.fc2": [
533
+ [
534
+ 0.12603624165058136,
535
+ -127
536
+ ],
537
+ [
538
+ 0.0004942595842294395,
539
+ 0
540
+ ]
541
+ ],
542
+ "layers.6.self_attn.k_proj": [
543
+ [
544
+ 0.20868107676506042,
545
+ -1
546
+ ],
547
+ [
548
+ 0.000818357162643224,
549
+ 0
550
+ ]
551
+ ],
552
+ "layers.6.self_attn.v_proj": [
553
+ [
554
+ 0.20868107676506042,
555
+ -1
556
+ ],
557
+ [
558
+ 0.000818357162643224,
559
+ 0
560
+ ]
561
+ ],
562
+ "layers.6.self_attn.q_proj": [
563
+ [
564
+ 0.20868107676506042,
565
+ -1
566
+ ],
567
+ [
568
+ 0.000818357162643224,
569
+ 0
570
+ ]
571
+ ],
572
+ "layers.6.self_attn.out_proj": [
573
+ [
574
+ 0.028652627021074295,
575
+ 6
576
+ ],
577
+ [
578
+ 0.00011236334830755368,
579
+ -1
580
+ ]
581
+ ],
582
+ "layers.6.mlp.fc1": [
583
+ [
584
+ 0.18634845316410065,
585
+ 8
586
+ ],
587
+ [
588
+ 0.0007307780906558037,
589
+ 0
590
+ ]
591
+ ],
592
+ "layers.6.mlp.fc2": [
593
+ [
594
+ 0.04141681641340256,
595
+ -124
596
+ ],
597
+ [
598
+ 0.00016241887351498008,
599
+ 0
600
+ ]
601
+ ],
602
+ "layers.7.self_attn.k_proj": [
603
+ [
604
+ 0.19424130022525787,
605
+ 7
606
+ ],
607
+ [
608
+ 0.00076173065463081,
609
+ -1
610
+ ]
611
+ ],
612
+ "layers.7.self_attn.v_proj": [
613
+ [
614
+ 0.19424130022525787,
615
+ 7
616
+ ],
617
+ [
618
+ 0.00076173065463081,
619
+ -1
620
+ ]
621
+ ],
622
+ "layers.7.self_attn.q_proj": [
623
+ [
624
+ 0.19424130022525787,
625
+ 7
626
+ ],
627
+ [
628
+ 0.00076173065463081,
629
+ -1
630
+ ]
631
+ ],
632
+ "layers.7.self_attn.out_proj": [
633
+ [
634
+ 0.025726741179823875,
635
+ -21
636
+ ],
637
+ [
638
+ 0.00010088913404615596,
639
+ 0
640
+ ]
641
+ ],
642
+ "layers.7.mlp.fc1": [
643
+ [
644
+ 0.18025848269462585,
645
+ 27
646
+ ],
647
+ [
648
+ 0.0007068958948366344,
649
+ 0
650
+ ]
651
+ ],
652
+ "layers.7.mlp.fc2": [
653
+ [
654
+ 0.0317450687289238,
655
+ -123
656
+ ],
657
+ [
658
+ 0.0001244904997292906,
659
+ -1
660
+ ]
661
+ ],
662
+ "layers.8.self_attn.k_proj": [
663
+ [
664
+ 0.2215735763311386,
665
+ 19
666
+ ],
667
+ [
668
+ 0.0008689158130437136,
669
+ -1
670
+ ]
671
+ ],
672
+ "layers.8.self_attn.v_proj": [
673
+ [
674
+ 0.2215735763311386,
675
+ 19
676
+ ],
677
+ [
678
+ 0.0008689158130437136,
679
+ -1
680
+ ]
681
+ ],
682
+ "layers.8.self_attn.q_proj": [
683
+ [
684
+ 0.2215735763311386,
685
+ 19
686
+ ],
687
+ [
688
+ 0.0008689158130437136,
689
+ -1
690
+ ]
691
+ ],
692
+ "layers.8.self_attn.out_proj": [
693
+ [
694
+ 0.03458048775792122,
695
+ 20
696
+ ],
697
+ [
698
+ 0.00013560972001869231,
699
+ 0
700
+ ]
701
+ ],
702
+ "layers.8.mlp.fc1": [
703
+ [
704
+ 0.18420721590518951,
705
+ 17
706
+ ],
707
+ [
708
+ 0.000722382334060967,
709
+ -1
710
+ ]
711
+ ],
712
+ "layers.8.mlp.fc2": [
713
+ [
714
+ 0.028086207807064056,
715
+ -122
716
+ ],
717
+ [
718
+ 0.0001101423695217818,
719
+ -1
720
+ ]
721
+ ],
722
+ "layers.9.self_attn.k_proj": [
723
+ [
724
+ 0.24290066957473755,
725
+ 31
726
+ ],
727
+ [
728
+ 0.000952551665250212,
729
+ 0
730
+ ]
731
+ ],
732
+ "layers.9.self_attn.v_proj": [
733
+ [
734
+ 0.24290066957473755,
735
+ 31
736
+ ],
737
+ [
738
+ 0.000952551665250212,
739
+ 0
740
+ ]
741
+ ],
742
+ "layers.9.self_attn.q_proj": [
743
+ [
744
+ 0.24290066957473755,
745
+ 31
746
+ ],
747
+ [
748
+ 0.000952551665250212,
749
+ 0
750
+ ]
751
+ ],
752
+ "layers.9.self_attn.out_proj": [
753
+ [
754
+ 0.03084946796298027,
755
+ 10
756
+ ],
757
+ [
758
+ 0.00012097826402168721,
759
+ 0
760
+ ]
761
+ ],
762
+ "layers.9.mlp.fc1": [
763
+ [
764
+ 0.3155311346054077,
765
+ -4
766
+ ],
767
+ [
768
+ 0.0012373793870210648,
769
+ -1
770
+ ]
771
+ ],
772
+ "layers.9.mlp.fc2": [
773
+ [
774
+ 0.06736534833908081,
775
+ -125
776
+ ],
777
+ [
778
+ 0.0002641777682583779,
779
+ 0
780
+ ]
781
+ ],
782
+ "layers.10.self_attn.k_proj": [
783
+ [
784
+ 0.2827392816543579,
785
+ 33
786
+ ],
787
+ [
788
+ 0.0011087822495028377,
789
+ 0
790
+ ]
791
+ ],
792
+ "layers.10.self_attn.v_proj": [
793
+ [
794
+ 0.2827392816543579,
795
+ 33
796
+ ],
797
+ [
798
+ 0.0011087822495028377,
799
+ 0
800
+ ]
801
+ ],
802
+ "layers.10.self_attn.q_proj": [
803
+ [
804
+ 0.2827392816543579,
805
+ 33
806
+ ],
807
+ [
808
+ 0.0011087822495028377,
809
+ 0
810
+ ]
811
+ ],
812
+ "layers.10.self_attn.out_proj": [
813
+ [
814
+ 0.02676890231668949,
815
+ 10
816
+ ],
817
+ [
818
+ 0.00010497611219761893,
819
+ -1
820
+ ]
821
+ ],
822
+ "layers.10.mlp.fc1": [
823
+ [
824
+ 0.6608939170837402,
825
+ -3
826
+ ],
827
+ [
828
+ 0.002591740805655718,
829
+ 0
830
+ ]
831
+ ],
832
+ "layers.10.mlp.fc2": [
833
+ [
834
+ 0.11313328146934509,
835
+ -126
836
+ ],
837
+ [
838
+ 0.0004436599847394973,
839
+ 0
840
+ ]
841
+ ],
842
+ "layers.11.self_attn.k_proj": [
843
+ [
844
+ 0.29342105984687805,
845
+ 23
846
+ ],
847
+ [
848
+ 0.0011506739538162947,
849
+ -1
850
+ ]
851
+ ],
852
+ "layers.11.self_attn.v_proj": [
853
+ [
854
+ 0.29342105984687805,
855
+ 23
856
+ ],
857
+ [
858
+ 0.0011506739538162947,
859
+ -1
860
+ ]
861
+ ],
862
+ "layers.11.self_attn.q_proj": [
863
+ [
864
+ 0.29342105984687805,
865
+ 23
866
+ ],
867
+ [
868
+ 0.0011506739538162947,
869
+ -1
870
+ ]
871
+ ],
872
+ "layers.11.self_attn.out_proj": [
873
+ [
874
+ 0.03917478024959564,
875
+ -11
876
+ ],
877
+ [
878
+ 0.00015362711565103382,
879
+ 0
880
+ ]
881
+ ],
882
+ "layers.11.mlp.fc1": [
883
+ [
884
+ 0.5828185081481934,
885
+ -9
886
+ ],
887
+ [
888
+ 0.002285564551129937,
889
+ 0
890
+ ]
891
+ ],
892
+ "layers.11.mlp.fc2": [
893
+ [
894
+ 0.42501285672187805,
895
+ -128
896
+ ],
897
+ [
898
+ 0.0016667165327817202,
899
+ 0
900
+ ]
901
+ ],
902
+ "connector.modality_projection.proj": [
903
+ [
904
+ 0.18216344714164734,
905
+ 19
906
+ ],
907
+ [
908
+ 0.000714366149622947,
909
+ -1
910
+ ]
911
+ ]
912
+ },
913
+ "decoder": {
914
+ "layers.0.attention.wq": [
915
+ [
916
+ 0.017823828384280205,
917
+ 40
918
+ ],
919
+ [
920
+ 6.989705434534699e-05,
921
+ 0
922
+ ]
923
+ ],
924
+ "layers.0.attention.wk": [
925
+ [
926
+ 0.017823828384280205,
927
+ 40
928
+ ],
929
+ [
930
+ 6.989705434534699e-05,
931
+ 0
932
+ ]
933
+ ],
934
+ "layers.0.attention.wv": [
935
+ [
936
+ 0.017823828384280205,
937
+ 40
938
+ ],
939
+ [
940
+ 6.989705434534699e-05,
941
+ 0
942
+ ]
943
+ ],
944
+ "layers.0.attention.wo": [
945
+ [
946
+ 0.007807944901287556,
947
+ -40
948
+ ],
949
+ [
950
+ 3.0619306926382706e-05,
951
+ 0
952
+ ]
953
+ ],
954
+ "layers.0.feed_forward.w1": [
955
+ [
956
+ 0.08885412663221359,
957
+ -112
958
+ ],
959
+ [
960
+ 0.000348444766132161,
961
+ 0
962
+ ]
963
+ ],
964
+ "layers.0.feed_forward.w2": [
965
+ [
966
+ 0.04989893361926079,
967
+ 8
968
+ ],
969
+ [
970
+ 0.0001956819323822856,
971
+ -1
972
+ ]
973
+ ],
974
+ "layers.0.feed_forward.w3": [
975
+ [
976
+ 0.08885412663221359,
977
+ -112
978
+ ],
979
+ [
980
+ 0.000348444766132161,
981
+ 0
982
+ ]
983
+ ],
984
+ "layers.1.attention.wq": [
985
+ [
986
+ 0.035501036792993546,
987
+ 53
988
+ ],
989
+ [
990
+ 0.000139219468110241,
991
+ -1
992
+ ]
993
+ ],
994
+ "layers.1.attention.wk": [
995
+ [
996
+ 0.035501036792993546,
997
+ 53
998
+ ],
999
+ [
1000
+ 0.000139219468110241,
1001
+ -1
1002
+ ]
1003
+ ],
1004
+ "layers.1.attention.wv": [
1005
+ [
1006
+ 0.035501036792993546,
1007
+ 53
1008
+ ],
1009
+ [
1010
+ 0.000139219468110241,
1011
+ -1
1012
+ ]
1013
+ ],
1014
+ "layers.1.attention.wo": [
1015
+ [
1016
+ 0.019016792997717857,
1017
+ -27
1018
+ ],
1019
+ [
1020
+ 7.457551691913977e-05,
1021
+ -1
1022
+ ]
1023
+ ],
1024
+ "layers.1.feed_forward.w1": [
1025
+ [
1026
+ 0.008405232802033424,
1027
+ 6
1028
+ ],
1029
+ [
1030
+ 3.296163777122274e-05,
1031
+ -1
1032
+ ]
1033
+ ],
1034
+ "layers.1.feed_forward.w2": [
1035
+ [
1036
+ 0.040298622101545334,
1037
+ 29
1038
+ ],
1039
+ [
1040
+ 0.00015803368296474218,
1041
+ -1
1042
+ ]
1043
+ ],
1044
+ "layers.1.feed_forward.w3": [
1045
+ [
1046
+ 0.008405232802033424,
1047
+ 6
1048
+ ],
1049
+ [
1050
+ 3.296163777122274e-05,
1051
+ -1
1052
+ ]
1053
+ ],
1054
+ "layers.2.attention.wq": [
1055
+ [
1056
+ 0.03276512399315834,
1057
+ 8
1058
+ ],
1059
+ [
1060
+ 0.00012849035556428134,
1061
+ -1
1062
+ ]
1063
+ ],
1064
+ "layers.2.attention.wk": [
1065
+ [
1066
+ 0.03276512399315834,
1067
+ 8
1068
+ ],
1069
+ [
1070
+ 0.00012849035556428134,
1071
+ -1
1072
+ ]
1073
+ ],
1074
+ "layers.2.attention.wv": [
1075
+ [
1076
+ 0.03276512399315834,
1077
+ 8
1078
+ ],
1079
+ [
1080
+ 0.00012849035556428134,
1081
+ -1
1082
+ ]
1083
+ ],
1084
+ "layers.2.attention.wo": [
1085
+ [
1086
+ 0.011804603971540928,
1087
+ -4
1088
+ ],
1089
+ [
1090
+ 4.6292545448523015e-05,
1091
+ -1
1092
+ ]
1093
+ ],
1094
+ "layers.2.feed_forward.w1": [
1095
+ [
1096
+ 0.010259619913995266,
1097
+ -1
1098
+ ],
1099
+ [
1100
+ 4.0233793697552755e-05,
1101
+ 0
1102
+ ]
1103
+ ],
1104
+ "layers.2.feed_forward.w2": [
1105
+ [
1106
+ 0.0317905955016613,
1107
+ -17
1108
+ ],
1109
+ [
1110
+ 0.00012466894986573607,
1111
+ 0
1112
+ ]
1113
+ ],
1114
+ "layers.2.feed_forward.w3": [
1115
+ [
1116
+ 0.010259619913995266,
1117
+ -1
1118
+ ],
1119
+ [
1120
+ 4.0233793697552755e-05,
1121
+ 0
1122
+ ]
1123
+ ],
1124
+ "layers.3.attention.wq": [
1125
+ [
1126
+ 0.03699791058897972,
1127
+ -2
1128
+ ],
1129
+ [
1130
+ 0.00014508973981719464,
1131
+ 0
1132
+ ]
1133
+ ],
1134
+ "layers.3.attention.wk": [
1135
+ [
1136
+ 0.03699791058897972,
1137
+ -2
1138
+ ],
1139
+ [
1140
+ 0.00014508973981719464,
1141
+ 0
1142
+ ]
1143
+ ],
1144
+ "layers.3.attention.wv": [
1145
+ [
1146
+ 0.03699791058897972,
1147
+ -2
1148
+ ],
1149
+ [
1150
+ 0.00014508973981719464,
1151
+ 0
1152
+ ]
1153
+ ],
1154
+ "layers.3.attention.wo": [
1155
+ [
1156
+ 0.021720269694924355,
1157
+ 32
1158
+ ],
1159
+ [
1160
+ 8.517743117408827e-05,
1161
+ 0
1162
+ ]
1163
+ ],
1164
+ "layers.3.feed_forward.w1": [
1165
+ [
1166
+ 0.014318770729005337,
1167
+ -17
1168
+ ],
1169
+ [
1170
+ 5.615192276309244e-05,
1171
+ 0
1172
+ ]
1173
+ ],
1174
+ "layers.3.feed_forward.w2": [
1175
+ [
1176
+ 0.33642786741256714,
1177
+ -104
1178
+ ],
1179
+ [
1180
+ 0.001319324248470366,
1181
+ 0
1182
+ ]
1183
+ ],
1184
+ "layers.3.feed_forward.w3": [
1185
+ [
1186
+ 0.014318770729005337,
1187
+ -17
1188
+ ],
1189
+ [
1190
+ 5.615192276309244e-05,
1191
+ 0
1192
+ ]
1193
+ ],
1194
+ "layers.4.attention.wq": [
1195
+ [
1196
+ 0.0462617389857769,
1197
+ 25
1198
+ ],
1199
+ [
1200
+ 0.00018141795590054244,
1201
+ 0
1202
+ ]
1203
+ ],
1204
+ "layers.4.attention.wk": [
1205
+ [
1206
+ 0.0462617389857769,
1207
+ 25
1208
+ ],
1209
+ [
1210
+ 0.00018141795590054244,
1211
+ 0
1212
+ ]
1213
+ ],
1214
+ "layers.4.attention.wv": [
1215
+ [
1216
+ 0.0462617389857769,
1217
+ 25
1218
+ ],
1219
+ [
1220
+ 0.00018141795590054244,
1221
+ 0
1222
+ ]
1223
+ ],
1224
+ "layers.4.attention.wo": [
1225
+ [
1226
+ 0.01729913242161274,
1227
+ 2
1228
+ ],
1229
+ [
1230
+ 6.783962453482673e-05,
1231
+ 0
1232
+ ]
1233
+ ],
1234
+ "layers.4.feed_forward.w1": [
1235
+ [
1236
+ 0.015150951221585274,
1237
+ -27
1238
+ ],
1239
+ [
1240
+ 5.9415444411570206e-05,
1241
+ 0
1242
+ ]
1243
+ ],
1244
+ "layers.4.feed_forward.w2": [
1245
+ [
1246
+ 0.04258466139435768,
1247
+ 15
1248
+ ],
1249
+ [
1250
+ 0.00016699809930287302,
1251
+ -1
1252
+ ]
1253
+ ],
1254
+ "layers.4.feed_forward.w3": [
1255
+ [
1256
+ 0.015150951221585274,
1257
+ -27
1258
+ ],
1259
+ [
1260
+ 5.9415444411570206e-05,
1261
+ 0
1262
+ ]
1263
+ ],
1264
+ "layers.5.attention.wq": [
1265
+ [
1266
+ 0.04702253267168999,
1267
+ 17
1268
+ ],
1269
+ [
1270
+ 0.00018440181156620383,
1271
+ 0
1272
+ ]
1273
+ ],
1274
+ "layers.5.attention.wk": [
1275
+ [
1276
+ 0.04702253267168999,
1277
+ 17
1278
+ ],
1279
+ [
1280
+ 0.00018440181156620383,
1281
+ 0
1282
+ ]
1283
+ ],
1284
+ "layers.5.attention.wv": [
1285
+ [
1286
+ 0.04702253267168999,
1287
+ 17
1288
+ ],
1289
+ [
1290
+ 0.00018440181156620383,
1291
+ 0
1292
+ ]
1293
+ ],
1294
+ "layers.5.attention.wo": [
1295
+ [
1296
+ 0.015384010970592499,
1297
+ -16
1298
+ ],
1299
+ [
1300
+ 6.032934470567852e-05,
1301
+ 0
1302
+ ]
1303
+ ],
1304
+ "layers.5.feed_forward.w1": [
1305
+ [
1306
+ 0.012542051263153553,
1307
+ -31
1308
+ ],
1309
+ [
1310
+ 4.9184363888343796e-05,
1311
+ 0
1312
+ ]
1313
+ ],
1314
+ "layers.5.feed_forward.w2": [
1315
+ [
1316
+ 0.0316687673330307,
1317
+ -13
1318
+ ],
1319
+ [
1320
+ 0.00012419100676197559,
1321
+ -1
1322
+ ]
1323
+ ],
1324
+ "layers.5.feed_forward.w3": [
1325
+ [
1326
+ 0.012542051263153553,
1327
+ -31
1328
+ ],
1329
+ [
1330
+ 4.9184363888343796e-05,
1331
+ 0
1332
+ ]
1333
+ ],
1334
+ "layers.6.attention.wq": [
1335
+ [
1336
+ 0.053652409464120865,
1337
+ 20
1338
+ ],
1339
+ [
1340
+ 0.00021040139836259186,
1341
+ 0
1342
+ ]
1343
+ ],
1344
+ "layers.6.attention.wk": [
1345
+ [
1346
+ 0.053652409464120865,
1347
+ 20
1348
+ ],
1349
+ [
1350
+ 0.00021040139836259186,
1351
+ 0
1352
+ ]
1353
+ ],
1354
+ "layers.6.attention.wv": [
1355
+ [
1356
+ 0.053652409464120865,
1357
+ 20
1358
+ ],
1359
+ [
1360
+ 0.00021040139836259186,
1361
+ 0
1362
+ ]
1363
+ ],
1364
+ "layers.6.attention.wo": [
1365
+ [
1366
+ 0.016384968534111977,
1367
+ -14
1368
+ ],
1369
+ [
1370
+ 6.425465835491195e-05,
1371
+ -1
1372
+ ]
1373
+ ],
1374
+ "layers.6.feed_forward.w1": [
1375
+ [
1376
+ 0.013905930332839489,
1377
+ -22
1378
+ ],
1379
+ [
1380
+ 5.453297490021214e-05,
1381
+ -1
1382
+ ]
1383
+ ],
1384
+ "layers.6.feed_forward.w2": [
1385
+ [
1386
+ 0.06569673120975494,
1387
+ -7
1388
+ ],
1389
+ [
1390
+ 0.000257634004810825,
1391
+ 0
1392
+ ]
1393
+ ],
1394
+ "layers.6.feed_forward.w3": [
1395
+ [
1396
+ 0.013905930332839489,
1397
+ -22
1398
+ ],
1399
+ [
1400
+ 5.453297490021214e-05,
1401
+ -1
1402
+ ]
1403
+ ],
1404
+ "layers.7.attention.wq": [
1405
+ [
1406
+ 0.048247963190078735,
1407
+ 20
1408
+ ],
1409
+ [
1410
+ 0.00018920755246654153,
1411
+ 0
1412
+ ]
1413
+ ],
1414
+ "layers.7.attention.wk": [
1415
+ [
1416
+ 0.048247963190078735,
1417
+ 20
1418
+ ],
1419
+ [
1420
+ 0.00018920755246654153,
1421
+ 0
1422
+ ]
1423
+ ],
1424
+ "layers.7.attention.wv": [
1425
+ [
1426
+ 0.048247963190078735,
1427
+ 20
1428
+ ],
1429
+ [
1430
+ 0.00018920755246654153,
1431
+ 0
1432
+ ]
1433
+ ],
1434
+ "layers.7.attention.wo": [
1435
+ [
1436
+ 0.01625002734363079,
1437
+ -19
1438
+ ],
1439
+ [
1440
+ 6.372541247401386e-05,
1441
+ 0
1442
+ ]
1443
+ ],
1444
+ "layers.7.feed_forward.w1": [
1445
+ [
1446
+ 0.013832789845764637,
1447
+ -6
1448
+ ],
1449
+ [
1450
+ 5.424611299531534e-05,
1451
+ -1
1452
+ ]
1453
+ ],
1454
+ "layers.7.feed_forward.w2": [
1455
+ [
1456
+ 0.055258866399526596,
1457
+ -3
1458
+ ],
1459
+ [
1460
+ 0.00021670118439942598,
1461
+ -1
1462
+ ]
1463
+ ],
1464
+ "layers.7.feed_forward.w3": [
1465
+ [
1466
+ 0.013832789845764637,
1467
+ -6
1468
+ ],
1469
+ [
1470
+ 5.424611299531534e-05,
1471
+ -1
1472
+ ]
1473
+ ],
1474
+ "layers.8.attention.wq": [
1475
+ [
1476
+ 0.046826478093862534,
1477
+ 25
1478
+ ],
1479
+ [
1480
+ 0.0001836329756770283,
1481
+ -1
1482
+ ]
1483
+ ],
1484
+ "layers.8.attention.wk": [
1485
+ [
1486
+ 0.046826478093862534,
1487
+ 25
1488
+ ],
1489
+ [
1490
+ 0.0001836329756770283,
1491
+ -1
1492
+ ]
1493
+ ],
1494
+ "layers.8.attention.wv": [
1495
+ [
1496
+ 0.046826478093862534,
1497
+ 25
1498
+ ],
1499
+ [
1500
+ 0.0001836329756770283,
1501
+ -1
1502
+ ]
1503
+ ],
1504
+ "layers.8.attention.wo": [
1505
+ [
1506
+ 0.020178530365228653,
1507
+ 6
1508
+ ],
1509
+ [
1510
+ 7.91311904322356e-05,
1511
+ 0
1512
+ ]
1513
+ ],
1514
+ "layers.8.feed_forward.w1": [
1515
+ [
1516
+ 0.015370944514870644,
1517
+ -26
1518
+ ],
1519
+ [
1520
+ 6.0277943703113124e-05,
1521
+ 0
1522
+ ]
1523
+ ],
1524
+ "layers.8.feed_forward.w2": [
1525
+ [
1526
+ 0.06391426175832748,
1527
+ 34
1528
+ ],
1529
+ [
1530
+ 0.0002506438468117267,
1531
+ 0
1532
+ ]
1533
+ ],
1534
+ "layers.8.feed_forward.w3": [
1535
+ [
1536
+ 0.015370944514870644,
1537
+ -26
1538
+ ],
1539
+ [
1540
+ 6.0277943703113124e-05,
1541
+ 0
1542
+ ]
1543
+ ],
1544
+ "layers.9.attention.wq": [
1545
+ [
1546
+ 0.05844179913401604,
1547
+ 14
1548
+ ],
1549
+ [
1550
+ 0.00022918317699804902,
1551
+ -1
1552
+ ]
1553
+ ],
1554
+ "layers.9.attention.wk": [
1555
+ [
1556
+ 0.05844179913401604,
1557
+ 14
1558
+ ],
1559
+ [
1560
+ 0.00022918317699804902,
1561
+ -1
1562
+ ]
1563
+ ],
1564
+ "layers.9.attention.wv": [
1565
+ [
1566
+ 0.05844179913401604,
1567
+ 14
1568
+ ],
1569
+ [
1570
+ 0.00022918317699804902,
1571
+ -1
1572
+ ]
1573
+ ],
1574
+ "layers.9.attention.wo": [
1575
+ [
1576
+ 0.020972328260540962,
1577
+ -10
1578
+ ],
1579
+ [
1580
+ 8.224398334277794e-05,
1581
+ -1
1582
+ ]
1583
+ ],
1584
+ "layers.9.feed_forward.w1": [
1585
+ [
1586
+ 0.01582447625696659,
1587
+ -30
1588
+ ],
1589
+ [
1590
+ 6.205654062796384e-05,
1591
+ -1
1592
+ ]
1593
+ ],
1594
+ "layers.9.feed_forward.w2": [
1595
+ [
1596
+ 0.061518240720033646,
1597
+ -41
1598
+ ],
1599
+ [
1600
+ 0.00024124795163515955,
1601
+ -1
1602
+ ]
1603
+ ],
1604
+ "layers.9.feed_forward.w3": [
1605
+ [
1606
+ 0.01582447625696659,
1607
+ -30
1608
+ ],
1609
+ [
1610
+ 6.205654062796384e-05,
1611
+ -1
1612
+ ]
1613
+ ],
1614
+ "layers.10.attention.wq": [
1615
+ [
1616
+ 0.057099029421806335,
1617
+ 13
1618
+ ],
1619
+ [
1620
+ 0.00022391745005734265,
1621
+ 0
1622
+ ]
1623
+ ],
1624
+ "layers.10.attention.wk": [
1625
+ [
1626
+ 0.057099029421806335,
1627
+ 13
1628
+ ],
1629
+ [
1630
+ 0.00022391745005734265,
1631
+ 0
1632
+ ]
1633
+ ],
1634
+ "layers.10.attention.wv": [
1635
+ [
1636
+ 0.057099029421806335,
1637
+ 13
1638
+ ],
1639
+ [
1640
+ 0.00022391745005734265,
1641
+ 0
1642
+ ]
1643
+ ],
1644
+ "layers.10.attention.wo": [
1645
+ [
1646
+ 0.02361183613538742,
1647
+ 25
1648
+ ],
1649
+ [
1650
+ 9.259540820494294e-05,
1651
+ -1
1652
+ ]
1653
+ ],
1654
+ "layers.10.feed_forward.w1": [
1655
+ [
1656
+ 0.015977581962943077,
1657
+ -31
1658
+ ],
1659
+ [
1660
+ 6.265722186071798e-05,
1661
+ 0
1662
+ ]
1663
+ ],
1664
+ "layers.10.feed_forward.w2": [
1665
+ [
1666
+ 0.05527188628911972,
1667
+ -12
1668
+ ],
1669
+ [
1670
+ 0.000216752290725708,
1671
+ 0
1672
+ ]
1673
+ ],
1674
+ "layers.10.feed_forward.w3": [
1675
+ [
1676
+ 0.015977581962943077,
1677
+ -31
1678
+ ],
1679
+ [
1680
+ 6.265722186071798e-05,
1681
+ 0
1682
+ ]
1683
+ ],
1684
+ "layers.11.attention.wq": [
1685
+ [
1686
+ 0.05523655563592911,
1687
+ 18
1688
+ ],
1689
+ [
1690
+ 0.0002166134217986837,
1691
+ 0
1692
+ ]
1693
+ ],
1694
+ "layers.11.attention.wk": [
1695
+ [
1696
+ 0.05523655563592911,
1697
+ 18
1698
+ ],
1699
+ [
1700
+ 0.0002166134217986837,
1701
+ 0
1702
+ ]
1703
+ ],
1704
+ "layers.11.attention.wv": [
1705
+ [
1706
+ 0.05523655563592911,
1707
+ 18
1708
+ ],
1709
+ [
1710
+ 0.0002166134217986837,
1711
+ 0
1712
+ ]
1713
+ ],
1714
+ "layers.11.attention.wo": [
1715
+ [
1716
+ 0.02736421301960945,
1717
+ -27
1718
+ ],
1719
+ [
1720
+ 0.000107310559542384,
1721
+ 0
1722
+ ]
1723
+ ],
1724
+ "layers.11.feed_forward.w1": [
1725
+ [
1726
+ 0.022182175889611244,
1727
+ -12
1728
+ ],
1729
+ [
1730
+ 8.698877354618162e-05,
1731
+ -1
1732
+ ]
1733
+ ],
1734
+ "layers.11.feed_forward.w2": [
1735
+ [
1736
+ 0.048375293612480164,
1737
+ -32
1738
+ ],
1739
+ [
1740
+ 0.00018970677047036588,
1741
+ -1
1742
+ ]
1743
+ ],
1744
+ "layers.11.feed_forward.w3": [
1745
+ [
1746
+ 0.022182175889611244,
1747
+ -12
1748
+ ],
1749
+ [
1750
+ 8.698877354618162e-05,
1751
+ -1
1752
+ ]
1753
+ ],
1754
+ "layers.12.attention.wq": [
1755
+ [
1756
+ 0.060529887676239014,
1757
+ 17
1758
+ ],
1759
+ [
1760
+ 0.00023737164156045765,
1761
+ -1
1762
+ ]
1763
+ ],
1764
+ "layers.12.attention.wk": [
1765
+ [
1766
+ 0.060529887676239014,
1767
+ 17
1768
+ ],
1769
+ [
1770
+ 0.00023737164156045765,
1771
+ -1
1772
+ ]
1773
+ ],
1774
+ "layers.12.attention.wv": [
1775
+ [
1776
+ 0.060529887676239014,
1777
+ 17
1778
+ ],
1779
+ [
1780
+ 0.00023737164156045765,
1781
+ -1
1782
+ ]
1783
+ ],
1784
+ "layers.12.attention.wo": [
1785
+ [
1786
+ 0.024945423007011414,
1787
+ -17
1788
+ ],
1789
+ [
1790
+ 9.782514098333195e-05,
1791
+ -1
1792
+ ]
1793
+ ],
1794
+ "layers.12.feed_forward.w1": [
1795
+ [
1796
+ 0.02477002516388893,
1797
+ -32
1798
+ ],
1799
+ [
1800
+ 9.713695908430964e-05,
1801
+ 0
1802
+ ]
1803
+ ],
1804
+ "layers.12.feed_forward.w2": [
1805
+ [
1806
+ 0.03289053589105606,
1807
+ -7
1808
+ ],
1809
+ [
1810
+ 0.00012898237037006766,
1811
+ -1
1812
+ ]
1813
+ ],
1814
+ "layers.12.feed_forward.w3": [
1815
+ [
1816
+ 0.02477002516388893,
1817
+ -32
1818
+ ],
1819
+ [
1820
+ 9.713695908430964e-05,
1821
+ 0
1822
+ ]
1823
+ ],
1824
+ "layers.13.attention.wq": [
1825
+ [
1826
+ 0.06709799915552139,
1827
+ 18
1828
+ ],
1829
+ [
1830
+ 0.00026312837144359946,
1831
+ 0
1832
+ ]
1833
+ ],
1834
+ "layers.13.attention.wk": [
1835
+ [
1836
+ 0.06709799915552139,
1837
+ 18
1838
+ ],
1839
+ [
1840
+ 0.00026312837144359946,
1841
+ 0
1842
+ ]
1843
+ ],
1844
+ "layers.13.attention.wv": [
1845
+ [
1846
+ 0.06709799915552139,
1847
+ 18
1848
+ ],
1849
+ [
1850
+ 0.00026312837144359946,
1851
+ 0
1852
+ ]
1853
+ ],
1854
+ "layers.13.attention.wo": [
1855
+ [
1856
+ 0.02714546211063862,
1857
+ -8
1858
+ ],
1859
+ [
1860
+ 0.00010645274596754462,
1861
+ -1
1862
+ ]
1863
+ ],
1864
+ "layers.13.feed_forward.w1": [
1865
+ [
1866
+ 0.0362023301422596,
1867
+ -55
1868
+ ],
1869
+ [
1870
+ 0.00014196964912116528,
1871
+ -1
1872
+ ]
1873
+ ],
1874
+ "layers.13.feed_forward.w2": [
1875
+ [
1876
+ 0.29110389947891235,
1877
+ -97
1878
+ ],
1879
+ [
1880
+ 0.0011415830813348293,
1881
+ -1
1882
+ ]
1883
+ ],
1884
+ "layers.13.feed_forward.w3": [
1885
+ [
1886
+ 0.0362023301422596,
1887
+ -55
1888
+ ],
1889
+ [
1890
+ 0.00014196964912116528,
1891
+ -1
1892
+ ]
1893
+ ],
1894
+ "layers.14.attention.wq": [
1895
+ [
1896
+ 0.06070156395435333,
1897
+ 20
1898
+ ],
1899
+ [
1900
+ 0.0002380445512244478,
1901
+ -1
1902
+ ]
1903
+ ],
1904
+ "layers.14.attention.wk": [
1905
+ [
1906
+ 0.06070156395435333,
1907
+ 20
1908
+ ],
1909
+ [
1910
+ 0.0002380445512244478,
1911
+ -1
1912
+ ]
1913
+ ],
1914
+ "layers.14.attention.wv": [
1915
+ [
1916
+ 0.06070156395435333,
1917
+ 20
1918
+ ],
1919
+ [
1920
+ 0.0002380445512244478,
1921
+ -1
1922
+ ]
1923
+ ],
1924
+ "layers.14.attention.wo": [
1925
+ [
1926
+ 0.02148558758199215,
1927
+ -7
1928
+ ],
1929
+ [
1930
+ 8.425699343206361e-05,
1931
+ 0
1932
+ ]
1933
+ ],
1934
+ "layers.14.feed_forward.w1": [
1935
+ [
1936
+ 0.016782715916633606,
1937
+ 15
1938
+ ],
1939
+ [
1940
+ 6.581440538866445e-05,
1941
+ -1
1942
+ ]
1943
+ ],
1944
+ "layers.14.feed_forward.w2": [
1945
+ [
1946
+ 0.036045271903276443,
1947
+ -1
1948
+ ],
1949
+ [
1950
+ 0.00014135372475720942,
1951
+ 0
1952
+ ]
1953
+ ],
1954
+ "layers.14.feed_forward.w3": [
1955
+ [
1956
+ 0.016782715916633606,
1957
+ 15
1958
+ ],
1959
+ [
1960
+ 6.581440538866445e-05,
1961
+ -1
1962
+ ]
1963
+ ],
1964
+ "layers.15.attention.wq": [
1965
+ [
1966
+ 0.052386704832315445,
1967
+ 26
1968
+ ],
1969
+ [
1970
+ 0.00020543771097436547,
1971
+ -1
1972
+ ]
1973
+ ],
1974
+ "layers.15.attention.wk": [
1975
+ [
1976
+ 0.052386704832315445,
1977
+ 26
1978
+ ],
1979
+ [
1980
+ 0.00020543771097436547,
1981
+ -1
1982
+ ]
1983
+ ],
1984
+ "layers.15.attention.wv": [
1985
+ [
1986
+ 0.052386704832315445,
1987
+ 26
1988
+ ],
1989
+ [
1990
+ 0.00020543771097436547,
1991
+ -1
1992
+ ]
1993
+ ],
1994
+ "layers.15.attention.wo": [
1995
+ [
1996
+ 0.02741842530667782,
1997
+ 10
1998
+ ],
1999
+ [
2000
+ 0.00010752306843642145,
2001
+ 0
2002
+ ]
2003
+ ],
2004
+ "layers.15.feed_forward.w1": [
2005
+ [
2006
+ 0.016052227467298508,
2007
+ 13
2008
+ ],
2009
+ [
2010
+ 6.294966442510486e-05,
2011
+ -1
2012
+ ]
2013
+ ],
2014
+ "layers.15.feed_forward.w2": [
2015
+ [
2016
+ 0.036300696432590485,
2017
+ -10
2018
+ ],
2019
+ [
2020
+ 0.0001423554349457845,
2021
+ 0
2022
+ ]
2023
+ ],
2024
+ "layers.15.feed_forward.w3": [
2025
+ [
2026
+ 0.016052227467298508,
2027
+ 13
2028
+ ],
2029
+ [
2030
+ 6.294966442510486e-05,
2031
+ -1
2032
+ ]
2033
+ ],
2034
+ "layers.16.attention.wq": [
2035
+ [
2036
+ 0.057359110563993454,
2037
+ 24
2038
+ ],
2039
+ [
2040
+ 0.00022493749565910548,
2041
+ -1
2042
+ ]
2043
+ ],
2044
+ "layers.16.attention.wk": [
2045
+ [
2046
+ 0.057359110563993454,
2047
+ 24
2048
+ ],
2049
+ [
2050
+ 0.00022493749565910548,
2051
+ -1
2052
+ ]
2053
+ ],
2054
+ "layers.16.attention.wv": [
2055
+ [
2056
+ 0.057359110563993454,
2057
+ 24
2058
+ ],
2059
+ [
2060
+ 0.00022493749565910548,
2061
+ -1
2062
+ ]
2063
+ ],
2064
+ "layers.16.attention.wo": [
2065
+ [
2066
+ 0.022664036601781845,
2067
+ -4
2068
+ ],
2069
+ [
2070
+ 8.887828153092414e-05,
2071
+ -1
2072
+ ]
2073
+ ],
2074
+ "layers.16.feed_forward.w1": [
2075
+ [
2076
+ 0.015661122277379036,
2077
+ 21
2078
+ ],
2079
+ [
2080
+ 6.141592893982306e-05,
2081
+ 0
2082
+ ]
2083
+ ],
2084
+ "layers.16.feed_forward.w2": [
2085
+ [
2086
+ 0.029099423438310623,
2087
+ 9
2088
+ ],
2089
+ [
2090
+ 0.0001141151660704054,
2091
+ -1
2092
+ ]
2093
+ ],
2094
+ "layers.16.feed_forward.w3": [
2095
+ [
2096
+ 0.015661122277379036,
2097
+ 21
2098
+ ],
2099
+ [
2100
+ 6.141592893982306e-05,
2101
+ 0
2102
+ ]
2103
+ ],
2104
+ "layers.17.attention.wq": [
2105
+ [
2106
+ 0.06801969558000565,
2107
+ 22
2108
+ ],
2109
+ [
2110
+ 0.00026674376567825675,
2111
+ -1
2112
+ ]
2113
+ ],
2114
+ "layers.17.attention.wk": [
2115
+ [
2116
+ 0.06801969558000565,
2117
+ 22
2118
+ ],
2119
+ [
2120
+ 0.00026674376567825675,
2121
+ -1
2122
+ ]
2123
+ ],
2124
+ "layers.17.attention.wv": [
2125
+ [
2126
+ 0.06801969558000565,
2127
+ 22
2128
+ ],
2129
+ [
2130
+ 0.00026674376567825675,
2131
+ -1
2132
+ ]
2133
+ ],
2134
+ "layers.17.attention.wo": [
2135
+ [
2136
+ 0.03552840277552605,
2137
+ -11
2138
+ ],
2139
+ [
2140
+ 0.00013932664296589792,
2141
+ 0
2142
+ ]
2143
+ ],
2144
+ "layers.17.feed_forward.w1": [
2145
+ [
2146
+ 0.016431303694844246,
2147
+ 16
2148
+ ],
2149
+ [
2150
+ 6.443633901653811e-05,
2151
+ -1
2152
+ ]
2153
+ ],
2154
+ "layers.17.feed_forward.w2": [
2155
+ [
2156
+ 0.03217285871505737,
2157
+ 6
2158
+ ],
2159
+ [
2160
+ 0.00012616798630915582,
2161
+ 0
2162
+ ]
2163
+ ],
2164
+ "layers.17.feed_forward.w3": [
2165
+ [
2166
+ 0.016431303694844246,
2167
+ 16
2168
+ ],
2169
+ [
2170
+ 6.443633901653811e-05,
2171
+ -1
2172
+ ]
2173
+ ],
2174
+ "layers.18.attention.wq": [
2175
+ [
2176
+ 0.059997912496328354,
2177
+ 22
2178
+ ],
2179
+ [
2180
+ 0.00023528531892225146,
2181
+ 0
2182
+ ]
2183
+ ],
2184
+ "layers.18.attention.wk": [
2185
+ [
2186
+ 0.059997912496328354,
2187
+ 22
2188
+ ],
2189
+ [
2190
+ 0.00023528531892225146,
2191
+ 0
2192
+ ]
2193
+ ],
2194
+ "layers.18.attention.wv": [
2195
+ [
2196
+ 0.059997912496328354,
2197
+ 22
2198
+ ],
2199
+ [
2200
+ 0.00023528531892225146,
2201
+ 0
2202
+ ]
2203
+ ],
2204
+ "layers.18.attention.wo": [
2205
+ [
2206
+ 0.03692711889743805,
2207
+ 22
2208
+ ],
2209
+ [
2210
+ 0.00014481216203421354,
2211
+ -1
2212
+ ]
2213
+ ],
2214
+ "layers.18.feed_forward.w1": [
2215
+ [
2216
+ 0.018235012888908386,
2217
+ 16
2218
+ ],
2219
+ [
2220
+ 7.15096975909546e-05,
2221
+ -1
2222
+ ]
2223
+ ],
2224
+ "layers.18.feed_forward.w2": [
2225
+ [
2226
+ 0.03604672849178314,
2227
+ 4
2228
+ ],
2229
+ [
2230
+ 0.0001413596619386226,
2231
+ -1
2232
+ ]
2233
+ ],
2234
+ "layers.18.feed_forward.w3": [
2235
+ [
2236
+ 0.018235012888908386,
2237
+ 16
2238
+ ],
2239
+ [
2240
+ 7.15096975909546e-05,
2241
+ -1
2242
+ ]
2243
+ ],
2244
+ "layers.19.attention.wq": [
2245
+ [
2246
+ 0.05455257371068001,
2247
+ 32
2248
+ ],
2249
+ [
2250
+ 0.00021393051429186016,
2251
+ 0
2252
+ ]
2253
+ ],
2254
+ "layers.19.attention.wk": [
2255
+ [
2256
+ 0.05455257371068001,
2257
+ 32
2258
+ ],
2259
+ [
2260
+ 0.00021393051429186016,
2261
+ 0
2262
+ ]
2263
+ ],
2264
+ "layers.19.attention.wv": [
2265
+ [
2266
+ 0.05455257371068001,
2267
+ 32
2268
+ ],
2269
+ [
2270
+ 0.00021393051429186016,
2271
+ 0
2272
+ ]
2273
+ ],
2274
+ "layers.19.attention.wo": [
2275
+ [
2276
+ 0.024298356845974922,
2277
+ -11
2278
+ ],
2279
+ [
2280
+ 9.528754162602127e-05,
2281
+ -1
2282
+ ]
2283
+ ],
2284
+ "layers.19.feed_forward.w1": [
2285
+ [
2286
+ 0.016636382788419724,
2287
+ 14
2288
+ ],
2289
+ [
2290
+ 6.52406524750404e-05,
2291
+ -1
2292
+ ]
2293
+ ],
2294
+ "layers.19.feed_forward.w2": [
2295
+ [
2296
+ 0.037912607192993164,
2297
+ 16
2298
+ ],
2299
+ [
2300
+ 0.00014867674326524138,
2301
+ 0
2302
+ ]
2303
+ ],
2304
+ "layers.19.feed_forward.w3": [
2305
+ [
2306
+ 0.016636382788419724,
2307
+ 14
2308
+ ],
2309
+ [
2310
+ 6.52406524750404e-05,
2311
+ -1
2312
+ ]
2313
+ ],
2314
+ "layers.20.attention.wq": [
2315
+ [
2316
+ 0.05900881439447403,
2317
+ 26
2318
+ ],
2319
+ [
2320
+ 0.00023140695702750236,
2321
+ -1
2322
+ ]
2323
+ ],
2324
+ "layers.20.attention.wk": [
2325
+ [
2326
+ 0.05900881439447403,
2327
+ 26
2328
+ ],
2329
+ [
2330
+ 0.00023140695702750236,
2331
+ -1
2332
+ ]
2333
+ ],
2334
+ "layers.20.attention.wv": [
2335
+ [
2336
+ 0.05900881439447403,
2337
+ 26
2338
+ ],
2339
+ [
2340
+ 0.00023140695702750236,
2341
+ -1
2342
+ ]
2343
+ ],
2344
+ "layers.20.attention.wo": [
2345
+ [
2346
+ 0.029481958597898483,
2347
+ -2
2348
+ ],
2349
+ [
2350
+ 0.00011561506835278124,
2351
+ 0
2352
+ ]
2353
+ ],
2354
+ "layers.20.feed_forward.w1": [
2355
+ [
2356
+ 0.01973850466310978,
2357
+ 13
2358
+ ],
2359
+ [
2360
+ 7.740584260318428e-05,
2361
+ 0
2362
+ ]
2363
+ ],
2364
+ "layers.20.feed_forward.w2": [
2365
+ [
2366
+ 0.05077813193202019,
2367
+ 15
2368
+ ],
2369
+ [
2370
+ 0.00019912967400159687,
2371
+ -1
2372
+ ]
2373
+ ],
2374
+ "layers.20.feed_forward.w3": [
2375
+ [
2376
+ 0.01973850466310978,
2377
+ 13
2378
+ ],
2379
+ [
2380
+ 7.740584260318428e-05,
2381
+ 0
2382
+ ]
2383
+ ],
2384
+ "layers.21.attention.wq": [
2385
+ [
2386
+ 0.0625663548707962,
2387
+ 29
2388
+ ],
2389
+ [
2390
+ 0.00024535812553949654,
2391
+ 0
2392
+ ]
2393
+ ],
2394
+ "layers.21.attention.wk": [
2395
+ [
2396
+ 0.0625663548707962,
2397
+ 29
2398
+ ],
2399
+ [
2400
+ 0.00024535812553949654,
2401
+ 0
2402
+ ]
2403
+ ],
2404
+ "layers.21.attention.wv": [
2405
+ [
2406
+ 0.0625663548707962,
2407
+ 29
2408
+ ],
2409
+ [
2410
+ 0.00024535812553949654,
2411
+ 0
2412
+ ]
2413
+ ],
2414
+ "layers.21.attention.wo": [
2415
+ [
2416
+ 0.02409578487277031,
2417
+ -14
2418
+ ],
2419
+ [
2420
+ 9.449264325667173e-05,
2421
+ -1
2422
+ ]
2423
+ ],
2424
+ "layers.21.feed_forward.w1": [
2425
+ [
2426
+ 0.02278539538383484,
2427
+ 17
2428
+ ],
2429
+ [
2430
+ 8.935417281463742e-05,
2431
+ 0
2432
+ ]
2433
+ ],
2434
+ "layers.21.feed_forward.w2": [
2435
+ [
2436
+ 0.07497410476207733,
2437
+ 1
2438
+ ],
2439
+ [
2440
+ 0.0002940157719422132,
2441
+ 0
2442
+ ]
2443
+ ],
2444
+ "layers.21.feed_forward.w3": [
2445
+ [
2446
+ 0.02278539538383484,
2447
+ 17
2448
+ ],
2449
+ [
2450
+ 8.935417281463742e-05,
2451
+ 0
2452
+ ]
2453
+ ],
2454
+ "layers.22.attention.wq": [
2455
+ [
2456
+ 0.05468488484621048,
2457
+ 23
2458
+ ],
2459
+ [
2460
+ 0.0002144504978787154,
2461
+ -1
2462
+ ]
2463
+ ],
2464
+ "layers.22.attention.wk": [
2465
+ [
2466
+ 0.05468488484621048,
2467
+ 23
2468
+ ],
2469
+ [
2470
+ 0.0002144504978787154,
2471
+ -1
2472
+ ]
2473
+ ],
2474
+ "layers.22.attention.wv": [
2475
+ [
2476
+ 0.05468488484621048,
2477
+ 23
2478
+ ],
2479
+ [
2480
+ 0.0002144504978787154,
2481
+ -1
2482
+ ]
2483
+ ],
2484
+ "layers.22.attention.wo": [
2485
+ [
2486
+ 0.025124160572886467,
2487
+ 6
2488
+ ],
2489
+ [
2490
+ 9.852605580817908e-05,
2491
+ 0
2492
+ ]
2493
+ ],
2494
+ "layers.22.feed_forward.w1": [
2495
+ [
2496
+ 0.0270016361027956,
2497
+ 10
2498
+ ],
2499
+ [
2500
+ 0.00010588859004201367,
2501
+ -1
2502
+ ]
2503
+ ],
2504
+ "layers.22.feed_forward.w2": [
2505
+ [
2506
+ 0.10119497030973434,
2507
+ 6
2508
+ ],
2509
+ [
2510
+ 0.00039684283547103405,
2511
+ -1
2512
+ ]
2513
+ ],
2514
+ "layers.22.feed_forward.w3": [
2515
+ [
2516
+ 0.0270016361027956,
2517
+ 10
2518
+ ],
2519
+ [
2520
+ 0.00010588859004201367,
2521
+ -1
2522
+ ]
2523
+ ],
2524
+ "layers.23.attention.wq": [
2525
+ [
2526
+ 0.050478190183639526,
2527
+ 27
2528
+ ],
2529
+ [
2530
+ 0.00019795302068814635,
2531
+ 0
2532
+ ]
2533
+ ],
2534
+ "layers.23.attention.wk": [
2535
+ [
2536
+ 0.050478190183639526,
2537
+ 27
2538
+ ],
2539
+ [
2540
+ 0.00019795302068814635,
2541
+ 0
2542
+ ]
2543
+ ],
2544
+ "layers.23.attention.wv": [
2545
+ [
2546
+ 0.050478190183639526,
2547
+ 27
2548
+ ],
2549
+ [
2550
+ 0.00019795302068814635,
2551
+ 0
2552
+ ]
2553
+ ],
2554
+ "layers.23.attention.wo": [
2555
+ [
2556
+ 0.0362025685608387,
2557
+ -4
2558
+ ],
2559
+ [
2560
+ 0.00014197066775523126,
2561
+ -1
2562
+ ]
2563
+ ],
2564
+ "layers.23.feed_forward.w1": [
2565
+ [
2566
+ 0.0261356383562088,
2567
+ 11
2568
+ ],
2569
+ [
2570
+ 0.00010249243496218696,
2571
+ 0
2572
+ ]
2573
+ ],
2574
+ "layers.23.feed_forward.w2": [
2575
+ [
2576
+ 0.16038192808628082,
2577
+ -29
2578
+ ],
2579
+ [
2580
+ 0.0006289482116699219,
2581
+ -1
2582
+ ]
2583
+ ],
2584
+ "layers.23.feed_forward.w3": [
2585
+ [
2586
+ 0.0261356383562088,
2587
+ 11
2588
+ ],
2589
+ [
2590
+ 0.00010249243496218696,
2591
+ 0
2592
+ ]
2593
+ ],
2594
+ "layers.24.attention.wq": [
2595
+ [
2596
+ 0.05497172102332115,
2597
+ 5
2598
+ ],
2599
+ [
2600
+ 0.0002155762631446123,
2601
+ 0
2602
+ ]
2603
+ ],
2604
+ "layers.24.attention.wk": [
2605
+ [
2606
+ 0.05497172102332115,
2607
+ 5
2608
+ ],
2609
+ [
2610
+ 0.0002155762631446123,
2611
+ 0
2612
+ ]
2613
+ ],
2614
+ "layers.24.attention.wv": [
2615
+ [
2616
+ 0.05497172102332115,
2617
+ 5
2618
+ ],
2619
+ [
2620
+ 0.0002155762631446123,
2621
+ 0
2622
+ ]
2623
+ ],
2624
+ "layers.24.attention.wo": [
2625
+ [
2626
+ 0.03732874244451523,
2627
+ -30
2628
+ ],
2629
+ [
2630
+ 0.000146387072163634,
2631
+ -1
2632
+ ]
2633
+ ],
2634
+ "layers.24.feed_forward.w1": [
2635
+ [
2636
+ 0.025813216343522072,
2637
+ 8
2638
+ ],
2639
+ [
2640
+ 0.00010122814273927361,
2641
+ 0
2642
+ ]
2643
+ ],
2644
+ "layers.24.feed_forward.w2": [
2645
+ [
2646
+ 0.12522609531879425,
2647
+ -27
2648
+ ],
2649
+ [
2650
+ 0.000491082202643156,
2651
+ -1
2652
+ ]
2653
+ ],
2654
+ "layers.24.feed_forward.w3": [
2655
+ [
2656
+ 0.025813216343522072,
2657
+ 8
2658
+ ],
2659
+ [
2660
+ 0.00010122814273927361,
2661
+ 0
2662
+ ]
2663
+ ],
2664
+ "layers.25.attention.wq": [
2665
+ [
2666
+ 0.047167424112558365,
2667
+ 10
2668
+ ],
2669
+ [
2670
+ 0.0001849701948231086,
2671
+ -1
2672
+ ]
2673
+ ],
2674
+ "layers.25.attention.wk": [
2675
+ [
2676
+ 0.047167424112558365,
2677
+ 10
2678
+ ],
2679
+ [
2680
+ 0.0001849701948231086,
2681
+ -1
2682
+ ]
2683
+ ],
2684
+ "layers.25.attention.wv": [
2685
+ [
2686
+ 0.047167424112558365,
2687
+ 10
2688
+ ],
2689
+ [
2690
+ 0.0001849701948231086,
2691
+ -1
2692
+ ]
2693
+ ],
2694
+ "layers.25.attention.wo": [
2695
+ [
2696
+ 0.03641931712627411,
2697
+ 21
2698
+ ],
2699
+ [
2700
+ 0.00014282084885053337,
2701
+ 0
2702
+ ]
2703
+ ],
2704
+ "layers.25.feed_forward.w1": [
2705
+ [
2706
+ 0.024308975785970688,
2707
+ 7
2708
+ ],
2709
+ [
2710
+ 9.532903641229495e-05,
2711
+ 0
2712
+ ]
2713
+ ],
2714
+ "layers.25.feed_forward.w2": [
2715
+ [
2716
+ 0.22050899267196655,
2717
+ -18
2718
+ ],
2719
+ [
2720
+ 0.0008647405775263906,
2721
+ 0
2722
+ ]
2723
+ ],
2724
+ "layers.25.feed_forward.w3": [
2725
+ [
2726
+ 0.024308975785970688,
2727
+ 7
2728
+ ],
2729
+ [
2730
+ 9.532903641229495e-05,
2731
+ 0
2732
+ ]
2733
+ ],
2734
+ "layers.26.attention.wq": [
2735
+ [
2736
+ 0.05102696269750595,
2737
+ 16
2738
+ ],
2739
+ [
2740
+ 0.00020010532170999795,
2741
+ -1
2742
+ ]
2743
+ ],
2744
+ "layers.26.attention.wk": [
2745
+ [
2746
+ 0.05102696269750595,
2747
+ 16
2748
+ ],
2749
+ [
2750
+ 0.00020010532170999795,
2751
+ -1
2752
+ ]
2753
+ ],
2754
+ "layers.26.attention.wv": [
2755
+ [
2756
+ 0.05102696269750595,
2757
+ 16
2758
+ ],
2759
+ [
2760
+ 0.00020010532170999795,
2761
+ -1
2762
+ ]
2763
+ ],
2764
+ "layers.26.attention.wo": [
2765
+ [
2766
+ 0.04784587025642395,
2767
+ -11
2768
+ ],
2769
+ [
2770
+ 0.0001876299938885495,
2771
+ -1
2772
+ ]
2773
+ ],
2774
+ "layers.26.feed_forward.w1": [
2775
+ [
2776
+ 0.024701740592718124,
2777
+ 9
2778
+ ],
2779
+ [
2780
+ 9.686937846709043e-05,
2781
+ -1
2782
+ ]
2783
+ ],
2784
+ "layers.26.feed_forward.w2": [
2785
+ [
2786
+ 0.20375220477581024,
2787
+ 24
2788
+ ],
2789
+ [
2790
+ 0.0007990262238308787,
2791
+ -1
2792
+ ]
2793
+ ],
2794
+ "layers.26.feed_forward.w3": [
2795
+ [
2796
+ 0.024701740592718124,
2797
+ 9
2798
+ ],
2799
+ [
2800
+ 9.686937846709043e-05,
2801
+ -1
2802
+ ]
2803
+ ],
2804
+ "layers.27.attention.wq": [
2805
+ [
2806
+ 0.05648134648799896,
2807
+ 19
2808
+ ],
2809
+ [
2810
+ 0.00022149378492031246,
2811
+ -1
2812
+ ]
2813
+ ],
2814
+ "layers.27.attention.wk": [
2815
+ [
2816
+ 0.05648134648799896,
2817
+ 19
2818
+ ],
2819
+ [
2820
+ 0.00022149378492031246,
2821
+ -1
2822
+ ]
2823
+ ],
2824
+ "layers.27.attention.wv": [
2825
+ [
2826
+ 0.05648134648799896,
2827
+ 19
2828
+ ],
2829
+ [
2830
+ 0.00022149378492031246,
2831
+ -1
2832
+ ]
2833
+ ],
2834
+ "layers.27.attention.wo": [
2835
+ [
2836
+ 0.05714663490653038,
2837
+ 16
2838
+ ],
2839
+ [
2840
+ 0.00022410399105865508,
2841
+ 0
2842
+ ]
2843
+ ],
2844
+ "layers.27.feed_forward.w1": [
2845
+ [
2846
+ 0.024300716817378998,
2847
+ 16
2848
+ ],
2849
+ [
2850
+ 9.529681119602174e-05,
2851
+ -1
2852
+ ]
2853
+ ],
2854
+ "layers.27.feed_forward.w2": [
2855
+ [
2856
+ 0.23974163830280304,
2857
+ -23
2858
+ ],
2859
+ [
2860
+ 0.0009401627467013896,
2861
+ -1
2862
+ ]
2863
+ ],
2864
+ "layers.27.feed_forward.w3": [
2865
+ [
2866
+ 0.024300716817378998,
2867
+ 16
2868
+ ],
2869
+ [
2870
+ 9.529681119602174e-05,
2871
+ -1
2872
+ ]
2873
+ ],
2874
+ "layers.28.attention.wq": [
2875
+ [
2876
+ 0.05538808926939964,
2877
+ 18
2878
+ ],
2879
+ [
2880
+ 0.00021720785298384726,
2881
+ -1
2882
+ ]
2883
+ ],
2884
+ "layers.28.attention.wk": [
2885
+ [
2886
+ 0.05538808926939964,
2887
+ 18
2888
+ ],
2889
+ [
2890
+ 0.00021720785298384726,
2891
+ -1
2892
+ ]
2893
+ ],
2894
+ "layers.28.attention.wv": [
2895
+ [
2896
+ 0.05538808926939964,
2897
+ 18
2898
+ ],
2899
+ [
2900
+ 0.00021720785298384726,
2901
+ -1
2902
+ ]
2903
+ ],
2904
+ "layers.28.attention.wo": [
2905
+ [
2906
+ 0.04831133037805557,
2907
+ 1
2908
+ ],
2909
+ [
2910
+ 0.00018945617193821818,
2911
+ 0
2912
+ ]
2913
+ ],
2914
+ "layers.28.feed_forward.w1": [
2915
+ [
2916
+ 0.02387886494398117,
2917
+ 10
2918
+ ],
2919
+ [
2920
+ 9.364249854115769e-05,
2921
+ -1
2922
+ ]
2923
+ ],
2924
+ "layers.28.feed_forward.w2": [
2925
+ [
2926
+ 0.21306206285953522,
2927
+ 16
2928
+ ],
2929
+ [
2930
+ 0.0008355369791388512,
2931
+ -1
2932
+ ]
2933
+ ],
2934
+ "layers.28.feed_forward.w3": [
2935
+ [
2936
+ 0.02387886494398117,
2937
+ 10
2938
+ ],
2939
+ [
2940
+ 9.364249854115769e-05,
2941
+ -1
2942
+ ]
2943
+ ],
2944
+ "layers.29.attention.wq": [
2945
+ [
2946
+ 0.05176341533660889,
2947
+ 23
2948
+ ],
2949
+ [
2950
+ 0.00020299319294281304,
2951
+ -1
2952
+ ]
2953
+ ],
2954
+ "layers.29.attention.wk": [
2955
+ [
2956
+ 0.05176341533660889,
2957
+ 23
2958
+ ],
2959
+ [
2960
+ 0.00020299319294281304,
2961
+ -1
2962
+ ]
2963
+ ],
2964
+ "layers.29.attention.wv": [
2965
+ [
2966
+ 0.05176341533660889,
2967
+ 23
2968
+ ],
2969
+ [
2970
+ 0.00020299319294281304,
2971
+ -1
2972
+ ]
2973
+ ],
2974
+ "layers.29.attention.wo": [
2975
+ [
2976
+ 0.06051347032189369,
2977
+ -5
2978
+ ],
2979
+ [
2980
+ 0.0002373075985815376,
2981
+ 0
2982
+ ]
2983
+ ],
2984
+ "layers.29.feed_forward.w1": [
2985
+ [
2986
+ 0.02441590465605259,
2987
+ 13
2988
+ ],
2989
+ [
2990
+ 9.574845898896456e-05,
2991
+ 0
2992
+ ]
2993
+ ],
2994
+ "layers.29.feed_forward.w2": [
2995
+ [
2996
+ 0.2622620761394501,
2997
+ -9
2998
+ ],
2999
+ [
3000
+ 0.001028476282954216,
3001
+ -1
3002
+ ]
3003
+ ],
3004
+ "layers.29.feed_forward.w3": [
3005
+ [
3006
+ 0.02441590465605259,
3007
+ 13
3008
+ ],
3009
+ [
3010
+ 9.574845898896456e-05,
3011
+ 0
3012
+ ]
3013
+ ],
3014
+ "layers.30.attention.wq": [
3015
+ [
3016
+ 0.03940032795071602,
3017
+ 21
3018
+ ],
3019
+ [
3020
+ 0.00015451051876880229,
3021
+ -1
3022
+ ]
3023
+ ],
3024
+ "layers.30.attention.wk": [
3025
+ [
3026
+ 0.03940032795071602,
3027
+ 21
3028
+ ],
3029
+ [
3030
+ 0.00015451051876880229,
3031
+ -1
3032
+ ]
3033
+ ],
3034
+ "layers.30.attention.wv": [
3035
+ [
3036
+ 0.03940032795071602,
3037
+ 21
3038
+ ],
3039
+ [
3040
+ 0.00015451051876880229,
3041
+ -1
3042
+ ]
3043
+ ],
3044
+ "layers.30.attention.wo": [
3045
+ [
3046
+ 0.06193621829152107,
3047
+ 5
3048
+ ],
3049
+ [
3050
+ 0.0002428869338473305,
3051
+ -1
3052
+ ]
3053
+ ],
3054
+ "layers.30.feed_forward.w1": [
3055
+ [
3056
+ 0.029521800577640533,
3057
+ -9
3058
+ ],
3059
+ [
3060
+ 0.000115771617856808,
3061
+ 0
3062
+ ]
3063
+ ],
3064
+ "layers.30.feed_forward.w2": [
3065
+ [
3066
+ 0.2706244885921478,
3067
+ -7
3068
+ ],
3069
+ [
3070
+ 0.0010612712940201163,
3071
+ -1
3072
+ ]
3073
+ ],
3074
+ "layers.30.feed_forward.w3": [
3075
+ [
3076
+ 0.029521800577640533,
3077
+ -9
3078
+ ],
3079
+ [
3080
+ 0.000115771617856808,
3081
+ 0
3082
+ ]
3083
+ ],
3084
+ "layers.31.attention.wq": [
3085
+ [
3086
+ 0.03465617075562477,
3087
+ 23
3088
+ ],
3089
+ [
3090
+ 0.00013590618618763983,
3091
+ 0
3092
+ ]
3093
+ ],
3094
+ "layers.31.attention.wk": [
3095
+ [
3096
+ 0.03465617075562477,
3097
+ 23
3098
+ ],
3099
+ [
3100
+ 0.00013590618618763983,
3101
+ 0
3102
+ ]
3103
+ ],
3104
+ "layers.31.attention.wv": [
3105
+ [
3106
+ 0.03465617075562477,
3107
+ 23
3108
+ ],
3109
+ [
3110
+ 0.00013590618618763983,
3111
+ 0
3112
+ ]
3113
+ ],
3114
+ "layers.31.attention.wo": [
3115
+ [
3116
+ 0.04215332493185997,
3117
+ 18
3118
+ ],
3119
+ [
3120
+ 0.00016530673019587994,
3121
+ 0
3122
+ ]
3123
+ ],
3124
+ "layers.31.feed_forward.w1": [
3125
+ [
3126
+ 0.04697251319885254,
3127
+ -11
3128
+ ],
3129
+ [
3130
+ 0.00018420587002765387,
3131
+ 0
3132
+ ]
3133
+ ],
3134
+ "layers.31.feed_forward.w2": [
3135
+ [
3136
+ 0.5093206763267517,
3137
+ 19
3138
+ ],
3139
+ [
3140
+ 0.001997330691665411,
3141
+ 0
3142
+ ]
3143
+ ],
3144
+ "layers.31.feed_forward.w3": [
3145
+ [
3146
+ 0.04697251319885254,
3147
+ -11
3148
+ ],
3149
+ [
3150
+ 0.00018420587002765387,
3151
+ 0
3152
+ ]
3153
+ ],
3154
+ "output": [
3155
+ [
3156
+ 0.23516124486923218,
3157
+ -15
3158
+ ],
3159
+ [
3160
+ 0.0009221913642250001,
3161
+ 0
3162
+ ]
3163
+ ]
3164
+ }
3165
+ },
3166
+ "host_data": "embed_tokens / vlm_meta.json / tokenizer exactly as in smolvlm-500m-instruct.py"
3167
+ },
3168
+ "files": {
3169
+ "embed_tokens.i8.bin": {
3170
+ "bytes": 47308800,
3171
+ "sha256": "d3ea47247be0b0703ca1246335cb3bca5f68a4068e177475388e287d9eb5f385"
3172
+ },
3173
+ "embed_tokens.scale.f32.bin": {
3174
+ "bytes": 197120,
3175
+ "sha256": "adefdadaae81d572c6094b6826e850f263939d716b74da03484dc20c0769562a"
3176
+ },
3177
+ "vlm_meta.json": {
3178
+ "bytes": 201,
3179
+ "sha256": "b36fcf7ef7da8c2ace5271aa5da5ffaa5f10a779db2095cb69f5eadfc9972ae4"
3180
+ },
3181
+ "tokenizer/tokenizer.json": {
3182
+ "sha256": "5ece781dc8d2b2f3e2f289ca0ae50b17cfc27dd27bfe7971bb8241e0b964331a"
3183
+ },
3184
+ "tokenizer/tokenizer_config.json": {
3185
+ "sha256": "36c6fd44d07d10fd8180ee6b46dcccf69fb7c06753968ff0d7e17b8bfe17b777"
3186
+ },
3187
+ "tokenizer/special_tokens_map.json": {
3188
+ "sha256": "aa0ff906077086dfa9734a7f97f68c825877a48f9468807be65504495cdeef09"
3189
+ },
3190
+ "smolvlm_vision_q8.pte": {
3191
+ "bytes": 103839108,
3192
+ "sha256": "a48d6919aa2067be9a8d615e46f1cac5c8e0ac94a6cea7af9e4798290f5a9747"
3193
+ },
3194
+ "smolvlm_decoder_q8.pte": {
3195
+ "bytes": 366912132,
3196
+ "sha256": "a54d81fbafd289c3b68891b150aa7ba9b069ffc5ecabe4114cc437fa726bc08a"
3197
+ }
3198
+ },
3199
+ "graph": {
3200
+ "smolvlm_vision_q8.pte": {
3201
+ "forward": {
3202
+ "inputs": [
3203
+ {
3204
+ "shape": [
3205
+ 1,
3206
+ 3,
3207
+ 512,
3208
+ 512
3209
+ ],
3210
+ "dtype": "FLOAT"
3211
+ }
3212
+ ],
3213
+ "outputs": [
3214
+ {
3215
+ "shape": [
3216
+ 1,
3217
+ 64,
3218
+ 960
3219
+ ],
3220
+ "dtype": "FLOAT"
3221
+ }
3222
+ ],
3223
+ "non_delegated_kernels": {},
3224
+ "delegates": [
3225
+ {
3226
+ "id": "VulkanBackend",
3227
+ "ops": {
3228
+ "aten._softmax.default": 12,
3229
+ "aten.add.Tensor": 171,
3230
+ "aten.bmm.default": 24,
3231
+ "aten.clamp.default": 73,
3232
+ "aten.clone.default": 268,
3233
+ "aten.convolution.default": 1,
3234
+ "aten.gelu.default": 12,
3235
+ "aten.mul.Tensor": 158,
3236
+ "aten.native_layer_norm.default": 25,
3237
+ "aten.permute_copy.default": 63,
3238
+ "aten.round.default": 73,
3239
+ "aten.squeeze_copy.dims": 48,
3240
+ "aten.sub.Tensor": 146,
3241
+ "aten.view_copy.default": 76,
3242
+ "dim_order_ops._to_dim_order_copy.default": 146,
3243
+ "et_vk.linear_q8ta_q8csw.default": 146,
3244
+ "et_vk.prepack.default": 305,
3245
+ "quantized_decomposed.quantize_per_tensor.default": 146
3246
+ },
3247
+ "op_arity": {
3248
+ "aten._softmax.default": [
3249
+ 4
3250
+ ],
3251
+ "aten.add.Tensor": [
3252
+ 4
3253
+ ],
3254
+ "aten.bmm.default": [
3255
+ 3
3256
+ ],
3257
+ "aten.clamp.default": [
3258
+ 4
3259
+ ],
3260
+ "aten.clone.default": [
3261
+ 3
3262
+ ],
3263
+ "aten.convolution.default": [
3264
+ 10
3265
+ ],
3266
+ "aten.gelu.default": [
3267
+ 3
3268
+ ],
3269
+ "aten.mul.Tensor": [
3270
+ 3
3271
+ ],
3272
+ "aten.native_layer_norm.default": [
3273
+ 6
3274
+ ],
3275
+ "aten.permute_copy.default": [
3276
+ 3
3277
+ ],
3278
+ "aten.round.default": [
3279
+ 2
3280
+ ],
3281
+ "aten.squeeze_copy.dims": [
3282
+ 3
3283
+ ],
3284
+ "aten.sub.Tensor": [
3285
+ 4
3286
+ ],
3287
+ "aten.view_copy.default": [
3288
+ 3
3289
+ ],
3290
+ "dim_order_ops._to_dim_order_copy.default": [
3291
+ 8
3292
+ ],
3293
+ "et_vk.linear_q8ta_q8csw.default": [
3294
+ 8
3295
+ ],
3296
+ "et_vk.prepack.default": [
3297
+ 2
3298
+ ],
3299
+ "quantized_decomposed.quantize_per_tensor.default": [
3300
+ 7
3301
+ ]
3302
+ },
3303
+ "inputs": 1,
3304
+ "outputs": 1
3305
+ }
3306
+ ]
3307
+ }
3308
+ },
3309
+ "smolvlm_decoder_q8.pte": {
3310
+ "forward": {
3311
+ "inputs": [
3312
+ {
3313
+ "shape": [
3314
+ 1,
3315
+ 4,
3316
+ 960
3317
+ ],
3318
+ "dtype": "FLOAT"
3319
+ },
3320
+ {
3321
+ "shape": [
3322
+ 1
3323
+ ],
3324
+ "dtype": "LONG"
3325
+ }
3326
+ ],
3327
+ "outputs": [
3328
+ {
3329
+ "shape": [
3330
+ 1,
3331
+ 49280
3332
+ ],
3333
+ "dtype": "FLOAT"
3334
+ }
3335
+ ],
3336
+ "non_delegated_kernels": {},
3337
+ "delegates": [
3338
+ {
3339
+ "id": "VulkanBackend",
3340
+ "ops": {
3341
+ "aten._to_copy.default": 482,
3342
+ "aten.add.Tensor": 578,
3343
+ "aten.clamp.default": 225,
3344
+ "aten.clone.default": 450,
3345
+ "aten.mul.Tensor": 610,
3346
+ "aten.relu.default": 32,
3347
+ "aten.round.default": 225,
3348
+ "aten.select_copy.int": 1,
3349
+ "aten.sigmoid.default": 32,
3350
+ "aten.sub.Tensor": 546,
3351
+ "aten.view_copy.default": 160,
3352
+ "et_vk.apply_rotary_emb_hf.default": 32,
3353
+ "et_vk.linear_q8ta_q8csw.default": 450,
3354
+ "et_vk.prepack.default": 1030,
3355
+ "et_vk.rms_norm.default": 65,
3356
+ "et_vk.select_as_symint.default": 2,
3357
+ "quantized_decomposed.quantize_per_tensor.default": 450,
3358
+ "sdpa_with_kv_cache.default": 32
3359
+ },
3360
+ "op_arity": {
3361
+ "aten._to_copy.default": [
3362
+ 8
3363
+ ],
3364
+ "aten.add.Tensor": [
3365
+ 4
3366
+ ],
3367
+ "aten.clamp.default": [
3368
+ 4
3369
+ ],
3370
+ "aten.clone.default": [
3371
+ 3
3372
+ ],
3373
+ "aten.mul.Tensor": [
3374
+ 3
3375
+ ],
3376
+ "aten.relu.default": [
3377
+ 2
3378
+ ],
3379
+ "aten.round.default": [
3380
+ 2
3381
+ ],
3382
+ "aten.select_copy.int": [
3383
+ 4
3384
+ ],
3385
+ "aten.sigmoid.default": [
3386
+ 2
3387
+ ],
3388
+ "aten.sub.Tensor": [
3389
+ 4
3390
+ ],
3391
+ "aten.view_copy.default": [
3392
+ 3
3393
+ ],
3394
+ "et_vk.apply_rotary_emb_hf.default": [
3395
+ 6
3396
+ ],
3397
+ "et_vk.linear_q8ta_q8csw.default": [
3398
+ 8
3399
+ ],
3400
+ "et_vk.prepack.default": [
3401
+ 2
3402
+ ],
3403
+ "et_vk.rms_norm.default": [
3404
+ 4
3405
+ ],
3406
+ "et_vk.select_as_symint.default": [
3407
+ 4
3408
+ ],
3409
+ "quantized_decomposed.quantize_per_tensor.default": [
3410
+ 7
3411
+ ],
3412
+ "sdpa_with_kv_cache.default": [
3413
+ 12
3414
+ ]
3415
+ },
3416
+ "inputs": 2,
3417
+ "outputs": 1
3418
+ }
3419
+ ]
3420
+ },
3421
+ "get_max_context_len": {
3422
+ "inputs": [],
3423
+ "outputs": [
3424
+ {
3425
+ "value": 512
3426
+ }
3427
+ ],
3428
+ "non_delegated_kernels": {},
3429
+ "delegates": []
3430
+ },
3431
+ "get_n_layers": {
3432
+ "inputs": [],
3433
+ "outputs": [
3434
+ {
3435
+ "value": 32
3436
+ }
3437
+ ],
3438
+ "non_delegated_kernels": {},
3439
+ "delegates": []
3440
+ },
3441
+ "get_vocab_size": {
3442
+ "inputs": [],
3443
+ "outputs": [
3444
+ {
3445
+ "value": 49280
3446
+ }
3447
+ ],
3448
+ "non_delegated_kernels": {},
3449
+ "delegates": []
3450
+ },
3451
+ "use_kv_cache": {
3452
+ "inputs": [],
3453
+ "outputs": [
3454
+ {
3455
+ "value": true
3456
+ }
3457
+ ],
3458
+ "non_delegated_kernels": {},
3459
+ "delegates": []
3460
+ },
3461
+ "use_sdpa_with_kv_cache": {
3462
+ "inputs": [],
3463
+ "outputs": [
3464
+ {
3465
+ "value": true
3466
+ }
3467
+ ],
3468
+ "non_delegated_kernels": {},
3469
+ "delegates": []
3470
+ },
3471
+ "enable_dynamic_shape": {
3472
+ "inputs": [],
3473
+ "outputs": [
3474
+ {
3475
+ "value": false
3476
+ }
3477
+ ],
3478
+ "non_delegated_kernels": {},
3479
+ "delegates": []
3480
+ },
3481
+ "get_max_seq_len": {
3482
+ "inputs": [],
3483
+ "outputs": [
3484
+ {
3485
+ "value": 5
3486
+ }
3487
+ ],
3488
+ "non_delegated_kernels": {},
3489
+ "delegates": []
3490
+ }
3491
+ }
3492
+ },
3493
+ "delegate_weight_check": {
3494
+ "smolvlm_decoder_q8.pte": {
3495
+ "linear_q8ta_q8csw_ops": 450,
3496
+ "weight_int8_bit_identical": 450,
3497
+ "weight_scales_bit_identical": 450,
3498
+ "activation_qparams_match": 450,
3499
+ "eager_q8_linears": 225
3500
+ },
3501
+ "smolvlm_vision_q8.pte": {
3502
+ "linear_q8ta_q8csw_ops": 146,
3503
+ "weight_int8_bit_identical": 146,
3504
+ "weight_scales_bit_identical": 146,
3505
+ "activation_qparams_match": 146,
3506
+ "eager_q8_linears": 73
3507
+ }
3508
+ },
3509
+ "command": "python recipes/smolvlm-500m-instruct-q8.py --out out/smolvlm-500m-instruct-q8"
3510
+ }
export/requirements.txt ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Environment the re-exports in REEXPORT.md were produced with (Python 3.12, Linux x86_64, CPU only).
2
+ # python -m venv venv
3
+ # venv/bin/pip install -r recipes/requirements.txt --extra-index-url https://download.pytorch.org/whl/cpu
4
+ # executorch 1.5.1 = pytorch/executorch git 3b60683923245cf472b7323426920e15623ba361; its wheel ships
5
+ # backends/webgpu (WebGPUPartitioner) and examples/models/llama (export_llm).
6
+ executorch==1.5.1
7
+ torch==2.14.1+cpu
8
+ torchvision==0.29.1+cpu
9
+ torchao==0.18.0+cpu
10
+ transformers==5.19.0
11
+ huggingface_hub==2.2.0
12
+ safetensors==0.8.0
13
+ tokenizers==0.23.3
14
+ numpy==2.5.4
15
+ pillow==12.3.0
export/smolvlm-500m-instruct-q8.py ADDED
@@ -0,0 +1,754 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """SmolVLM 500M Instruct -> ExecuTorch WebGPU with 8-bit linears (public ExecuTorch only).
3
+
4
+ The 4-bit public export (smolvlm-500m-instruct.py) loses too much answer quality. This variant uses
5
+ the one 8-bit linear that public ExecuTorch lowers for WebGPU and the public WebGPU runtime runs,
6
+ et_vk.linear_q8ta_q8csw (static per-tensor int8 activations x per-channel int8 weights), with the
7
+ additions described in _smolvlm_q8.py (two-pass activations, built-in attention sink, optional GPTQ).
8
+
9
+ Outputs in --out:
10
+ smolvlm_vision_q8.pte vision tower + connector: pixels[1,3,512,512] -> image embeds[1,64,960]
11
+ smolvlm_decoder_q8.pte text decoder, one static window of R rows per call:
12
+ embeds[1,R,960], input_pos[1] -> logits[1,49280] of the last row;
13
+ KV cache [1,512,5,64] x 32 layers in the program
14
+ embed_tokens.i8.bin, embed_tokens.scale.f32.bin, vlm_meta.json, tokenizer/
15
+ identical to the 4-bit export (and to the internal files)
16
+ recipe.json, validation.json
17
+
18
+ The decoder has static shapes because the Vulkan partitioner does not delegate
19
+ quantized_decomposed.quantize_per_tensor with a dynamic dimension ("no dynamic shape support"), and
20
+ the public WebGPU quantize / q8ta kernels have no resize hooks. One program means one KV cache:
21
+ prompts are fed in windows of R rows, the last aligned to end on the prompt's last position, and
22
+ each decode step feeds the new token with the R-1 rows before it (vlm.js, window_starts()).
23
+ """
24
+
25
+ import argparse
26
+ import copy
27
+ import hashlib
28
+ import json
29
+ import shutil
30
+ import sys
31
+ import time
32
+ import types
33
+ from pathlib import Path
34
+
35
+ import numpy as np
36
+ import torch
37
+
38
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
39
+ import _common # noqa: E402
40
+ import _smolvlm as S # noqa: E402
41
+ import _smolvlm_q8 as Q # noqa: E402
42
+
43
+ SPEC = {
44
+ "name": "smolvlm-500m-instruct-q8",
45
+ "upstream_repo": "HuggingFaceTB/SmolVLM-500M-Instruct",
46
+ "revision": "a7da5b986cb59b408707209984f360a5f4ad7e47",
47
+ "sha256": {
48
+ "model.safetensors": "d05b567eeaf534e83d375551f068ed57b5f52d37c657197f644af5ef9db091a2",
49
+ "config.json": "daacbbca6af3c34e50466c72aed2df4553a08084f6a0551d4ae420a241cb66c6",
50
+ "generation_config.json": "067a2a54e5f87162ecac6e0e911cc4665fc8f7f3324794ecbac0f76badb56636",
51
+ "preprocessor_config.json": "6cb6e36d6fcb88ca1502c4a26750715dc3e7dedddc9a8f17b27d8d167d1457e7",
52
+ "tokenizer.json": "5ece781dc8d2b2f3e2f289ca0ae50b17cfc27dd27bfe7971bb8241e0b964331a",
53
+ "tokenizer_config.json": "36c6fd44d07d10fd8180ee6b46dcccf69fb7c06753968ff0d7e17b8bfe17b777",
54
+ "special_tokens_map.json": "aa0ff906077086dfa9734a7f97f68c825877a48f9468807be65504495cdeef09",
55
+ },
56
+ }
57
+ TOKENIZER_FILES = ["tokenizer.json", "tokenizer_config.json", "special_tokens_map.json"]
58
+
59
+ # End-to-end check: the 3 fixed pairs of the 4-bit export (images from the playground samples).
60
+ PAIRS = [
61
+ ("astronaut.jpg", "Describe this image"),
62
+ ("receipt.jpg", "What text can you read?"),
63
+ ("city_ny.jpg", "How many people are there?"),
64
+ ]
65
+ # Calibration: held-out image + question pairs. None of these images is in the 3-pair check or in
66
+ # the 12-pair quality evaluation (VLM_FOLLOWUP.md); the continuation is the fp32 model's own
67
+ # greedy answer (CALIB_NEW tokens).
68
+ CALIB = [
69
+ ("yolo_cat.jpg", "What animal is this?"),
70
+ ("yolo_people.jpg", "Describe this image"),
71
+ ("yolo_street.jpg", "What vehicles can you see?"),
72
+ ("yolo_table.jpg", "What objects are on the table?"),
73
+ ("sample_shapes.jpg", "What shapes are in this picture?"),
74
+ ("chihuahuas.jpg", "How many dogs are there?"),
75
+ ("kitchen.jpg", "Describe this image"),
76
+ ("giraffes.jpg", "What is happening here?"),
77
+ ]
78
+ CALIB_NEW = 32
79
+
80
+ VARIANTS = {
81
+ # vision: q8x2 = two-pass 8-bit, q8 = one pass; two_pass: decoder; gptq: weights of both programs
82
+ "q8": {"vision": "q8x2", "two_pass": True, "gptq": True},
83
+ "q8-rtn": {"vision": "q8x2", "two_pass": True, "gptq": False},
84
+ "q8-1pass": {"vision": "q8", "two_pass": False, "gptq": True},
85
+ }
86
+
87
+
88
+ # ----------------------------------------------------------------------------- inputs
89
+
90
+
91
+ def find_image(name, dirs):
92
+ for d in dirs:
93
+ if (Path(d) / name).exists():
94
+ return Path(d) / name
95
+ sys.exit(f"image {name} not found in {[str(d) for d in dirs]}")
96
+
97
+
98
+ class Prompts:
99
+ """Playground prompt construction (vlm.js firstTurnIds) and preprocessing (normalizeRGBA)."""
100
+
101
+ def __init__(self, tok, meta):
102
+ self.tok, self.meta = tok, meta
103
+ self.eos = set(meta["eos_token_ids"])
104
+
105
+ def ids(self, question):
106
+ conv = self.tok.convert_tokens_to_ids
107
+ user = self.tok.encode("<|im_start|>User:", add_special_tokens=False)
108
+ asst = self.tok.encode(
109
+ "<end_of_utterance>\nAssistant:", add_special_tokens=False
110
+ )
111
+ image = (
112
+ [conv(S.FAKE_IMAGE), conv(S.GLOBAL_IMAGE)]
113
+ + [self.meta["image_token_id"]] * self.meta["n_image_tokens"]
114
+ + [conv(S.FAKE_IMAGE)]
115
+ )
116
+ return user + image + self.tok.encode(
117
+ question, add_special_tokens=False
118
+ ) + asst, len(user) + 2
119
+
120
+ def pixels(self, path):
121
+ """Stretch to 512x512 (PIL bilinear stands in for canvas drawImage), rescale, normalize, CHW."""
122
+ from PIL import Image
123
+
124
+ size = self.meta["image_size"]
125
+ im = Image.open(path).convert("RGB").resize((size, size), Image.BILINEAR)
126
+ a = np.asarray(im, dtype=np.float32).transpose(2, 0, 1) * np.float32(
127
+ self.meta["rescale"]
128
+ )
129
+ a = (a - np.float32(self.meta["mean"])) / np.float32(self.meta["std"])
130
+ return torch.from_numpy(np.ascontiguousarray(a))[None]
131
+
132
+
133
+ # ----------------------------------------------------------------------------- export
134
+
135
+
136
+ def lower_static(module, example, name):
137
+ from executorch.backends.webgpu.partitioner import WebGPUPartitioner
138
+ from executorch.exir import to_edge_transform_and_lower
139
+
140
+ with torch.no_grad():
141
+ ep = torch.export.export(module, example, strict=False)
142
+ webgpu = (
143
+ to_edge_transform_and_lower(ep, partitioner=[WebGPUPartitioner()])
144
+ .to_executorch()
145
+ .buffer
146
+ )
147
+ portable = (
148
+ to_edge_transform_and_lower(
149
+ torch.export.export(module, example, strict=False)
150
+ )
151
+ .to_executorch()
152
+ .buffer
153
+ )
154
+ return ep, webgpu, portable
155
+
156
+
157
+ def export_decoder(dec, params, args):
158
+ from executorch.backends.webgpu.partitioner import WebGPUPartitioner
159
+ from executorch.extension.llm.export.builder import DType, LLMEdgeManager
160
+
161
+ def manager():
162
+ return LLMEdgeManager(
163
+ model=dec,
164
+ modelname="smolvlm_decoder_q8",
165
+ max_seq_len=args.rows + 1,
166
+ use_kv_cache=True,
167
+ example_inputs=(
168
+ torch.zeros(1, args.rows, params["dim"]),
169
+ torch.tensor([0], dtype=torch.long),
170
+ ),
171
+ dtype=DType.fp32,
172
+ enable_dynamic_shape=False,
173
+ metadata={
174
+ "get_max_context_len": args.max_context,
175
+ "get_n_layers": params["n_layers"],
176
+ "get_vocab_size": params["vocab_size"],
177
+ "use_kv_cache": True,
178
+ "use_sdpa_with_kv_cache": True,
179
+ "enable_dynamic_shape": False,
180
+ },
181
+ dynamic_shapes=None,
182
+ )
183
+
184
+ mgr = manager()
185
+ mgr.export()
186
+ mgr.run_canonical_optimizations()
187
+ mgr.export_to_edge()
188
+ graph = copy.deepcopy(mgr.pre_autograd_graph_module)
189
+ mgr.to_backend([WebGPUPartitioner()])
190
+ mgr.to_executorch(passes=[])
191
+ webgpu = mgr.export_program.buffer
192
+ del mgr
193
+ pmgr = manager()
194
+ pmgr.export()
195
+ pmgr.run_canonical_optimizations()
196
+ pmgr.export_to_edge()
197
+ pmgr.to_executorch(passes=[])
198
+ portable = pmgr.export_program.buffer
199
+ del pmgr
200
+ return graph, webgpu, portable
201
+
202
+
203
+ # ----------------------------------------------------------------------------- validation
204
+
205
+
206
+ class Pipeline:
207
+ """Host logic of vlm.js around a vision callable and a windowed decoder callable
208
+ decoder(rows [1, R, hidden], start) -> logits [vocab] of the last row."""
209
+
210
+ def __init__(self, prompts, vision, decoder, embed, rows):
211
+ self.p, self.vision, self.decoder, self.embed, self.rows = (
212
+ prompts,
213
+ vision,
214
+ decoder,
215
+ embed,
216
+ rows,
217
+ )
218
+
219
+ def feed(self, seq, start, end):
220
+ logits = None
221
+ for s in Q.window_starts(start, end, self.rows):
222
+ logits = self.decoder(seq[None, s : s + self.rows].contiguous(), s)
223
+ return logits
224
+
225
+ def prompt_rows(self, image, question):
226
+ ids, start = self.p.ids(question)
227
+ rows = self.embed[torch.tensor(ids)].clone()
228
+ rows[start : start + image.shape[0]] = image
229
+ return ids, rows
230
+
231
+ def greedy(self, image, question, steps):
232
+ ids, rows = self.prompt_rows(image, question)
233
+ logits = self.feed(rows, 0, len(ids))
234
+ out = []
235
+ for _ in range(steps):
236
+ t = int(logits.argmax())
237
+ if t in self.p.eos:
238
+ break
239
+ out.append(t)
240
+ rows = torch.cat([rows, self.embed[t][None]])
241
+ logits = self.feed(rows, rows.shape[0] - 1, rows.shape[0])
242
+ return out
243
+
244
+ def teacher_forced(self, image, question, ref_tokens, ref_logits):
245
+ ids, rows = self.prompt_rows(image, question)
246
+ logits = self.feed(rows, 0, len(ids))
247
+ hits, cos = [], []
248
+ for i in range(len(ref_tokens) + 1):
249
+ hits.append(int(logits.argmax()) == int(ref_logits[i].argmax()))
250
+ cos.append(_common.compare(logits.numpy(), ref_logits[i].numpy())["cosine"])
251
+ if i == len(ref_tokens):
252
+ break
253
+ rows = torch.cat([rows, self.embed[ref_tokens[i]][None]])
254
+ logits = self.feed(rows, rows.shape[0] - 1, rows.shape[0])
255
+ return {
256
+ "prefill_top1_match": hits[0],
257
+ "prefill_logits_cosine": cos[0],
258
+ "teacher_forced_top1_agreement": sum(hits) / len(hits),
259
+ "teacher_forced_mean_logits_cosine": float(np.mean(cos)),
260
+ }
261
+
262
+
263
+ def agreement(a, b):
264
+ prefix = 0
265
+ for x, y in zip(a, b):
266
+ if x != y:
267
+ break
268
+ prefix += 1
269
+ n = max(len(a), len(b))
270
+ return {
271
+ "tokens": len(a),
272
+ "reference_tokens": len(b),
273
+ "prefix_match": prefix,
274
+ "positional_agreement": (sum(x == y for x, y in zip(a, b)) / n) if n else 1.0,
275
+ }
276
+
277
+
278
+ def validate(hf, prompts, args, vis_q, vis_ep, dec, dec_graph, programs, emb_i8):
279
+ import executorch.extension.llm.custom_ops.custom_ops # noqa: F401 (llama:: CPU kernels)
280
+ import executorch.kernels.quantized # noqa: F401 (quantized_decomposed CPU kernels)
281
+ from executorch.extension.pybindings.portable_lib import (
282
+ _load_for_executorch_from_buffer,
283
+ )
284
+
285
+ report = {
286
+ "cpu_runtime": "executorch pybindings (portable_lib) from the pip wheel, with executorch.extension.llm.custom_ops (llama::update_cache, custom_sdpa) and executorch.kernels.quantized (quantized_decomposed::quantize/dequantize_per_tensor, dequantize_per_channel)",
287
+ "portable_programs": "the same exported graphs without a delegate: int8 weights, static activation scales and the quantize/dequantize ops run as stored",
288
+ "note": "the WebGPU programs cannot run on CPU; see VLM_FOLLOWUP.md for the browser run",
289
+ }
290
+ vprog = _load_for_executorch_from_buffer(programs["vision_portable"])
291
+ dprog = _load_for_executorch_from_buffer(programs["decoder_portable"])
292
+ for name in ("vision", "decoder"):
293
+ try:
294
+ probe = (
295
+ (torch.zeros(1, 3, 512, 512),)
296
+ if name == "vision"
297
+ else (torch.zeros(1, args.rows, 960), torch.tensor([0]))
298
+ )
299
+ _load_for_executorch_from_buffer(programs[name]).forward(probe)
300
+ report[f"{name}_webgpu_pte_on_cpu"] = "ran"
301
+ except Exception as e: # noqa: BLE001 - the error text is the result
302
+ report[f"{name}_webgpu_pte_on_cpu"] = (
303
+ f"not runnable on CPU pybindings (expected: VulkanBackend is not linked): {type(e).__name__}: {str(e).splitlines()[0][:200]}"
304
+ )
305
+
306
+ gen = torch.Generator().manual_seed(1234)
307
+ x = torch.rand(1, 3, 512, 512, generator=gen) * 2 - 1
308
+ with torch.no_grad():
309
+ ve = vis_q(x)
310
+ report["vision_random_input"] = {
311
+ "exported_vs_eager": _common.compare(
312
+ vis_ep.module()(x).numpy(), ve.numpy()
313
+ ),
314
+ "portable_pte_vs_eager": _common.compare(
315
+ np.asarray(vprog.forward((x.contiguous(),))[0]), ve.numpy()
316
+ ),
317
+ }
318
+ # Decoder: a real prompt's rows through a sequence of windows (overlapping re-feeds included).
319
+ img = find_image(PAIRS[0][0], args.images)
320
+ with torch.no_grad():
321
+ image = vis_q(prompts.pixels(img))[0]
322
+ ids, start = prompts.ids(PAIRS[0][1])
323
+ rows = emb_i8[torch.tensor(ids)].clone()
324
+ rows[start : start + 64] = image
325
+ R = args.rows
326
+ starts = [0, R, R // 2, R + 1, R + 2]
327
+ out = {}
328
+ for label, fn in (
329
+ ("eager", lambda e, p: dec(e, torch.tensor([p]))),
330
+ ("exported", lambda e, p: dec_graph(e, torch.tensor([p]))),
331
+ (
332
+ "portable_pte",
333
+ lambda e, p: torch.from_numpy(
334
+ np.asarray(dprog.forward((e.contiguous(), torch.tensor([p])))[0])
335
+ ),
336
+ ),
337
+ ):
338
+ S.reset_kv(dec)
339
+ S.reset_kv(dec_graph)
340
+ with torch.no_grad():
341
+ out[label] = [
342
+ fn(rows[None, s : s + R].contiguous(), s).clone() for s in starts
343
+ ]
344
+ report["decoder_windows"] = {
345
+ "starts": starts,
346
+ **{
347
+ f"{k}_vs_eager": [
348
+ _common.compare(a.numpy(), b.numpy())
349
+ for a, b in zip(out[k], out["eager"])
350
+ ]
351
+ for k in ("exported", "portable_pte")
352
+ },
353
+ }
354
+ del dprog
355
+
356
+ # End to end on the 3 fixed pairs.
357
+ dprog = _load_for_executorch_from_buffer(programs["decoder_portable"])
358
+ pipelines = {
359
+ "eager_quantized": Pipeline(
360
+ prompts,
361
+ lambda px: vis_q(px)[0].detach(),
362
+ lambda e, p: dec(e, torch.tensor([p]))[0].detach(),
363
+ emb_i8,
364
+ R,
365
+ ),
366
+ "export_portable_pte": Pipeline(
367
+ prompts,
368
+ lambda px: torch.from_numpy(
369
+ np.asarray(vprog.forward((px.contiguous(),))[0])
370
+ )[0],
371
+ lambda e, p: torch.from_numpy(
372
+ np.asarray(dprog.forward((e.contiguous(), torch.tensor([p])))[0])
373
+ )[0],
374
+ emb_i8,
375
+ R,
376
+ ),
377
+ }
378
+ vis_fp32 = S.VisionEncoder(hf).eval()
379
+ pairs = {}
380
+ for name, question in PAIRS:
381
+ px = prompts.pixels(find_image(name, args.images))
382
+ ids, _ = prompts.ids(question)
383
+ with torch.no_grad():
384
+ g = hf.generate(
385
+ input_ids=torch.tensor([ids]),
386
+ pixel_values=px[None],
387
+ max_new_tokens=args.new_tokens,
388
+ do_sample=False,
389
+ num_beams=1,
390
+ )
391
+ ref = [t for t in g[0, len(ids) :].tolist() if t not in prompts.eos]
392
+ ref_logits = hf(
393
+ input_ids=torch.tensor([ids + ref]), pixel_values=px[None]
394
+ ).logits[0][len(ids) - 1 :]
395
+ ref_image = vis_fp32(px)[0]
396
+ entry = {
397
+ "question": question,
398
+ "image": name,
399
+ "prompt_tokens": len(ids),
400
+ "transformers_fp32": {
401
+ "text": prompts.tok.decode(ref, skip_special_tokens=True),
402
+ "token_ids": ref,
403
+ },
404
+ }
405
+ for label, pipe in pipelines.items():
406
+ S.reset_kv(dec)
407
+ t0 = time.time()
408
+ with torch.no_grad():
409
+ image = pipe.vision(px)
410
+ toks = pipe.greedy(image, question, args.new_tokens)
411
+ S.reset_kv(dec)
412
+ tf = pipe.teacher_forced(image, question, ref, ref_logits)
413
+ entry[label] = {
414
+ "text": prompts.tok.decode(toks, skip_special_tokens=True),
415
+ "token_ids": toks,
416
+ **agreement(toks, ref),
417
+ **tf,
418
+ "image_embeds_vs_fp32": _common.compare(
419
+ image.numpy(), ref_image.numpy()
420
+ ),
421
+ "seconds": round(time.time() - t0, 1),
422
+ }
423
+ pairs[name] = entry
424
+ print(json.dumps(entry, default=str)[:1500], flush=True)
425
+ summary = {}
426
+ for label in pipelines:
427
+ rows_ = [pairs[p][label] for p in pairs]
428
+ summary[label] = {
429
+ "exact_answers": sum(
430
+ r["token_ids"] == pairs[p]["transformers_fp32"]["token_ids"]
431
+ for p, r in zip(pairs, rows_)
432
+ ),
433
+ "positional_agreement_mean": float(
434
+ np.mean([r["positional_agreement"] for r in rows_])
435
+ ),
436
+ "teacher_forced_top1_agreement_mean": float(
437
+ np.mean([r["teacher_forced_top1_agreement"] for r in rows_])
438
+ ),
439
+ "prefill_top1_matches": sum(r["prefill_top1_match"] for r in rows_),
440
+ }
441
+ report["end_to_end"] = {
442
+ "decoding": f"greedy, at most {args.new_tokens} new tokens, stop at eos {sorted(prompts.eos)}; prompt fed in {R}-row windows, then one window per token (vlm.js)",
443
+ "preprocessing": "PIL bilinear resize to 512x512 (stand-in for canvas drawImage), (x/255 - 0.5)/0.5",
444
+ "reference": "transformers Idefics3ForConditionalGeneration fp32, eager attention, same input_ids and pixel_values",
445
+ "pipelines": {
446
+ "eager_quantized": "the quantized eager modules (the numerics of the exported graphs) + int8 host embeddings",
447
+ "export_portable_pte": "the exported programs as portable .pte via pybindings + int8 host embeddings: the shipped pipeline minus the WebGPU kernels",
448
+ },
449
+ "pairs": pairs,
450
+ "summary": summary,
451
+ }
452
+ return report
453
+
454
+
455
+ # ----------------------------------------------------------------------------- main
456
+
457
+
458
+ def build(args, var, out):
459
+ """Host data (written to `out`), calibration data and the quantized eager vision tower and
460
+ decoder. Shared with the quality evaluation, so it measures exactly what is exported."""
461
+ snap = S.snapshot(SPEC["upstream_repo"], SPEC["revision"], SPEC["sha256"])
462
+ hf = S.load_hf(snap)
463
+ from transformers import AutoTokenizer
464
+
465
+ tok = AutoTokenizer.from_pretrained(snap)
466
+ files = {}
467
+ q, s = S.embedding_table(hf)
468
+ files["embed_tokens.i8.bin"] = S.write_bytes(
469
+ out / "embed_tokens.i8.bin", q.tobytes()
470
+ )
471
+ files["embed_tokens.scale.f32.bin"] = S.write_bytes(
472
+ out / "embed_tokens.scale.f32.bin", s.tobytes()
473
+ )
474
+ meta = S.vlm_meta(snap, hf.config)
475
+ files["vlm_meta.json"] = S.write_bytes(
476
+ out / "vlm_meta.json", json.dumps(meta, indent=1).encode()
477
+ )
478
+ (out / "tokenizer").mkdir(exist_ok=True)
479
+ for f in TOKENIZER_FILES:
480
+ shutil.copyfile(snap / f, out / "tokenizer" / f)
481
+ files[f"tokenizer/{f}"] = {"sha256": _common.sha256_file(out / "tokenizer" / f)}
482
+ emb_i8 = torch.from_numpy(q.astype(np.float32) * s[:, None])
483
+ prompts = Prompts(tok, meta)
484
+
485
+ # Calibration data: images for the vision tower; prompt + fp32 greedy continuation rows for the
486
+ # decoder (the image rows come from the quantized vision tower, as at run time).
487
+ calib_imgs = [find_image(n, args.images) for n, _ in CALIB]
488
+ calib_px = [prompts.pixels(p) for p in calib_imgs]
489
+ calib_info = []
490
+ t0 = time.time()
491
+ vis_fp32 = S.VisionEncoder(hf).eval()
492
+ if var["vision"] == "q4gsw":
493
+ vis_q = S.quantize_4w(copy.deepcopy(vis_fp32), 32)
494
+ vis_act = None
495
+ else:
496
+ vis_q, vis_act = Q.build_vision(
497
+ vis_fp32, calib_px, two_pass=var["vision"] == "q8x2", gptq=var["gptq"]
498
+ )
499
+ calib_rows = []
500
+ for (name, question), path, px in zip(CALIB, calib_imgs, calib_px):
501
+ ids, start = prompts.ids(question)
502
+ with torch.no_grad():
503
+ g = hf.generate(
504
+ input_ids=torch.tensor([ids]),
505
+ pixel_values=px[None],
506
+ max_new_tokens=CALIB_NEW,
507
+ do_sample=False,
508
+ num_beams=1,
509
+ )
510
+ cont = [t for t in g[0, len(ids) :].tolist() if t not in prompts.eos]
511
+ image = vis_q(px)[0]
512
+ rows = emb_i8[torch.tensor(ids + cont)].clone()
513
+ rows[start : start + 64] = image
514
+ calib_rows.append(rows)
515
+ calib_info.append(
516
+ {
517
+ "image": name,
518
+ "image_sha256": _common.sha256_file(path),
519
+ "question": question,
520
+ "rows": int(rows.shape[0]),
521
+ "continuation": tok.decode(cont, skip_special_tokens=True),
522
+ }
523
+ )
524
+ params = S.text_params(hf.config)
525
+ transformer = S.build_text(
526
+ snap,
527
+ params,
528
+ max_context=args.max_context,
529
+ prefill_max=args.rows,
530
+ group_size=None,
531
+ )
532
+ sink_row = emb_i8[tok.encode("<|im_start|>", add_special_tokens=False)[0]]
533
+ transformer, dec_act, _ = Q.build_text(
534
+ transformer,
535
+ calib_rows,
536
+ rows=args.rows,
537
+ two_pass=var["two_pass"],
538
+ gptq=var["gptq"],
539
+ sink_row=sink_row,
540
+ )
541
+ dec = S.TextDecoder(transformer).eval()
542
+ print(f"calibration + quantization {time.time() - t0:.0f}s", flush=True)
543
+ return types.SimpleNamespace(**locals())
544
+
545
+
546
+ def main():
547
+ ap = argparse.ArgumentParser(
548
+ description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
549
+ )
550
+ ap.add_argument("--out", required=True)
551
+ ap.add_argument("--variant", choices=sorted(VARIANTS), default="q8")
552
+ ap.add_argument(
553
+ "--rows", type=int, default=4, help="decoder rows per call (static)"
554
+ )
555
+ ap.add_argument("--max-context", type=int, default=512)
556
+ ap.add_argument("--new-tokens", type=int, default=48)
557
+ ap.add_argument("--no-validate", action="store_true")
558
+ ap.add_argument("--threads", type=int, default=16, help="torch CPU threads (fixed for reproducible calibration)")
559
+ ap.add_argument("--save-portable", type=Path, help="also write the portable (CPU) programs here (validation aid, not a release file)")
560
+ ap.add_argument(
561
+ "--images",
562
+ type=Path,
563
+ nargs="+",
564
+ default=[
565
+ Path.home() / "etai-playground/_internal/assets/dashboard/web",
566
+ Path.home() / "etai-playground/site/playground/webgpu/assets/samples",
567
+ ],
568
+ help="directories searched for the evaluation and calibration images",
569
+ )
570
+ args = ap.parse_args()
571
+ var = VARIANTS[args.variant]
572
+ out = Path(args.out).resolve()
573
+ out.mkdir(parents=True, exist_ok=True)
574
+ torch.manual_seed(0)
575
+ # The activation scales come from fp32 forward passes, whose summation order depends on the
576
+ # thread count; a fixed count keeps re-runs byte-identical.
577
+ torch.set_num_threads(args.threads)
578
+ import executorch.kernels.quantized # noqa: F401 (out variants of quantized_decomposed for to_executorch)
579
+
580
+ Q.fix_q8ta_fake()
581
+
582
+ m = build(args, var, out)
583
+ hf, tok, meta, prompts, emb_i8, files, calib_info = m.hf, m.tok, m.meta, m.prompts, m.emb_i8, m.files, m.calib_info
584
+ vis_q, vis_act, params, transformer, dec_act, dec = m.vis_q, m.vis_act, m.params, m.transformer, m.dec_act, m.dec
585
+
586
+ vision_ep, vision_pte, vision_portable = lower_static(
587
+ vis_q, (torch.zeros(1, 3, 512, 512),), "vision"
588
+ )
589
+ vname = (
590
+ "smolvlm_vision_q8.pte"
591
+ if var["vision"] != "q4gsw"
592
+ else "smolvlm_vision_q4gsw.pte"
593
+ )
594
+ files[vname] = S.write_bytes(out / vname, vision_pte)
595
+ dec_graph, decoder_pte, decoder_portable = export_decoder(dec, params, args)
596
+ files["smolvlm_decoder_q8.pte"] = S.write_bytes(
597
+ out / "smolvlm_decoder_q8.pte", decoder_pte
598
+ )
599
+
600
+ graph = {
601
+ vname: S.program_summary(vision_pte),
602
+ "smolvlm_decoder_q8.pte": S.program_summary(decoder_pte),
603
+ }
604
+ for name, g in graph.items():
605
+ fwd = g["forward"]
606
+ assert not fwd["non_delegated_kernels"], (name, fwd["non_delegated_kernels"])
607
+ assert len(fwd["delegates"]) == 1, (name, len(fwd["delegates"]))
608
+ weight_check = {
609
+ "smolvlm_decoder_q8.pte": Q.q8_ops_vs_eager(decoder_pte, transformer)
610
+ }
611
+ if var["vision"] != "q4gsw":
612
+ weight_check[vname] = Q.q8_ops_vs_eager(vision_pte, vis_q)
613
+ for name, c in weight_check.items():
614
+ n = c["linear_q8ta_q8csw_ops"]
615
+ assert (
616
+ n
617
+ == c["weight_int8_bit_identical"]
618
+ == c["weight_scales_bit_identical"]
619
+ == c["activation_qparams_match"]
620
+ ), (name, c)
621
+
622
+ two = {
623
+ True: "two passes (second pass on the first pass's rounding error, same int8 weight)",
624
+ False: "one pass",
625
+ }
626
+ wq = (
627
+ "GPTQ (damp 0.01, block 128, float64) on the calibration inputs, same grid"
628
+ if var["gptq"]
629
+ else "round to nearest"
630
+ )
631
+ recipe = {
632
+ "model": SPEC["name"],
633
+ "variant": args.variant,
634
+ "upstream": {
635
+ "model": SPEC["upstream_repo"],
636
+ "revision": SPEC["revision"],
637
+ "official": True,
638
+ "sha256": SPEC["sha256"],
639
+ },
640
+ "versions": _common.versions(),
641
+ "export": {
642
+ "partitioner": "executorch.backends.webgpu.partitioner.WebGPUPartitioner, default options (static shapes)",
643
+ "linear": "et_vk.linear_q8ta_q8csw: int8 activations, one static per-tensor affine scale per pass (torchao MinMaxObserver over the calibration data, [-128, 127]); int8 weights, per-output-channel symmetric (VulkanQuantizer weight spec, scale amax/127.5)",
644
+ "export_workaround": "_smolvlm_q8.fix_q8ta_fake(): a fake kernel giving et_vk.linear_q8ta_q8csw its real fp32 output dtype during lowering (executorch 1.5.1's reference kernel reports int8, which breaks llama::custom_sdpa's meta check and would mis-declare tensors). Metadata only",
645
+ "programs": {
646
+ vname: {
647
+ "api": "torch.export.export(strict=False) -> to_edge_transform_and_lower(partitioner=[WebGPUPartitioner()]) -> to_executorch()",
648
+ "module": "transformers Idefics3 vision tower (eager attention) + post_layernorm + connector; identity position ids for a full 512x512 image",
649
+ "inputs": [
650
+ {
651
+ "name": "pixel_values",
652
+ "shape": [1, 3, 512, 512],
653
+ "dtype": "float32",
654
+ "range": "(x/255 - 0.5)/0.5",
655
+ }
656
+ ],
657
+ "outputs": [
658
+ {
659
+ "name": "image_embeds",
660
+ "shape": [1, meta["n_image_tokens"], meta["hidden"]],
661
+ "dtype": "float32",
662
+ }
663
+ ],
664
+ "quantization": "4-bit groupwise (as the 4-bit export)"
665
+ if var["vision"] == "q4gsw"
666
+ else f"all 73 linears 8-bit, {two[var['vision'] == 'q8x2']}, weights {wq}{' (the connector, with fewer calibration rows than inputs, round to nearest)' if var['gptq'] else ''}; patch embedding conv, layer norms, position table fp32",
667
+ },
668
+ "smolvlm_decoder_q8.pte": {
669
+ "api": "LLMEdgeManager: export(strict) -> run_canonical_optimizations -> export_to_edge -> to_backend([WebGPUPartitioner()]) -> to_executorch; static shapes",
670
+ "module": "executorch examples/models/llama Transformer (apply_embedding=False, use_hf_rope, enable_dynamic_shape for the start position) with weights from executorch.examples.models.smolvlm.convert_weights; source transforms from export_llama_lib._get_source_transforms(use_sdpa_with_kv_cache, use_kv_cache)",
671
+ "params": params,
672
+ "inputs": [
673
+ {
674
+ "name": "embeds",
675
+ "shape": [1, args.rows, meta["hidden"]],
676
+ "dtype": "float32",
677
+ "note": "rows of positions input_pos .. input_pos + rows - 1",
678
+ },
679
+ {
680
+ "name": "input_pos",
681
+ "shape": [1],
682
+ "dtype": "int64",
683
+ "note": "position of the first row",
684
+ },
685
+ ],
686
+ "outputs": [
687
+ {
688
+ "name": "logits",
689
+ "shape": [1, meta["vocab"]],
690
+ "dtype": "float32",
691
+ "note": "last row only",
692
+ }
693
+ ],
694
+ "kv_cache": f"in the program: 32 layers x k/v [1,{args.max_context},5,64] fp32; every call writes its rows' positions",
695
+ "attention_sink": "position 0 must be <|im_start|> (id 1), as the chat template always starts: its per-layer K/V (fp32 model, int8-table embedding row) are constants the program writes over row 0 when input_pos is 0; that row is excluded from calibration",
696
+ "host_contract": "feed positions in windows of `rows` rows ending at the newest position (window_starts in _smolvlm_q8.py / windowStarts in vlm.js); input_pos + rows <= max_context",
697
+ "max_context_length": args.max_context,
698
+ "quantization": f"all 225 linears incl. lm_head 8-bit, {two[var['two_pass']]}, weights {wq}; RMSNorm weights fp32",
699
+ },
700
+ },
701
+ "calibration": {
702
+ "pairs": calib_info,
703
+ "continuation_tokens": CALIB_NEW,
704
+ "decoder_windows": f"{args.rows} rows, the browser's window plan",
705
+ "vision": "the 8 calibration images",
706
+ },
707
+ "activation_qparams": {
708
+ "vision": vis_act and {k: list(v) for k, v in vis_act.items()},
709
+ "decoder": {k: list(v) for k, v in dec_act.items()},
710
+ },
711
+ "host_data": "embed_tokens / vlm_meta.json / tokenizer exactly as in smolvlm-500m-instruct.py",
712
+ },
713
+ "files": files,
714
+ "graph": graph,
715
+ "delegate_weight_check": weight_check,
716
+ "command": _common.command_line(out),
717
+ }
718
+ (out / "recipe.json").write_text(
719
+ json.dumps(
720
+ recipe,
721
+ indent=2,
722
+ default=lambda o: list(o) if isinstance(o, tuple) else str(o),
723
+ )
724
+ + "\n"
725
+ )
726
+ print(json.dumps({"files": files}, indent=2), flush=True)
727
+
728
+ if args.save_portable:
729
+ args.save_portable.mkdir(parents=True, exist_ok=True)
730
+ (args.save_portable / "vision_portable.pte").write_bytes(vision_portable)
731
+ (args.save_portable / "decoder_portable.pte").write_bytes(decoder_portable)
732
+ if not args.no_validate:
733
+ programs = {
734
+ "vision": vision_pte,
735
+ "decoder": decoder_pte,
736
+ "vision_portable": vision_portable,
737
+ "decoder_portable": decoder_portable,
738
+ }
739
+ report = validate(
740
+ hf, prompts, args, vis_q, vision_ep, dec, dec_graph, programs, emb_i8
741
+ )
742
+ report["portable_program_files"] = {
743
+ k: {
744
+ "bytes": len(programs[k]),
745
+ "sha256": hashlib.sha256(programs[k]).hexdigest(),
746
+ }
747
+ for k in ("vision_portable", "decoder_portable")
748
+ }
749
+ (out / "validation.json").write_text(json.dumps(report, indent=2) + "\n")
750
+ print(json.dumps(report["end_to_end"]["summary"], indent=2))
751
+
752
+
753
+ if __name__ == "__main__":
754
+ main()
manifest.json CHANGED
@@ -12,6 +12,16 @@
12
  "bytes": 197120,
13
  "sha256": "adefdadaae81d572c6094b6826e850f263939d716b74da03484dc20c0769562a"
14
  },
 
 
 
 
 
 
 
 
 
 
15
  {
16
  "path": "tokenizer/special_tokens_map.json",
17
  "bytes": 1069,
@@ -27,25 +37,10 @@
27
  "bytes": 28249,
28
  "sha256": "36c6fd44d07d10fd8180ee6b46dcccf69fb7c06753968ff0d7e17b8bfe17b777"
29
  },
30
- {
31
- "path": "vlm_decoder.pte",
32
- "bytes": 388664832,
33
- "sha256": "d92697a8c32eeaa1d73603cf608d658cf297fdf41ed81384ec29b7025e78e996"
34
- },
35
  {
36
  "path": "vlm_meta.json",
37
  "bytes": 201,
38
  "sha256": "b36fcf7ef7da8c2ace5271aa5da5ffaa5f10a779db2095cb69f5eadfc9972ae4"
39
- },
40
- {
41
- "path": "vlm_prefill.pte",
42
- "bytes": 388664320,
43
- "sha256": "1e8671eb77ae2f60c1eb80073454230bfaeb1eb22cee36dd9b028d8e291a3e3f"
44
- },
45
- {
46
- "path": "vlm_vision_q8.pte",
47
- "bytes": 108885888,
48
- "sha256": "5f09d6028d9bb945ce135d4be9ad499c1081f13d46655d2d1288c207f536ee4e"
49
  }
50
  ]
51
  }
 
12
  "bytes": 197120,
13
  "sha256": "adefdadaae81d572c6094b6826e850f263939d716b74da03484dc20c0769562a"
14
  },
15
+ {
16
+ "path": "smolvlm_decoder_q8.pte",
17
+ "bytes": 366912132,
18
+ "sha256": "a54d81fbafd289c3b68891b150aa7ba9b069ffc5ecabe4114cc437fa726bc08a"
19
+ },
20
+ {
21
+ "path": "smolvlm_vision_q8.pte",
22
+ "bytes": 103839108,
23
+ "sha256": "a48d6919aa2067be9a8d615e46f1cac5c8e0ac94a6cea7af9e4798290f5a9747"
24
+ },
25
  {
26
  "path": "tokenizer/special_tokens_map.json",
27
  "bytes": 1069,
 
37
  "bytes": 28249,
38
  "sha256": "36c6fd44d07d10fd8180ee6b46dcccf69fb7c06753968ff0d7e17b8bfe17b777"
39
  },
 
 
 
 
 
40
  {
41
  "path": "vlm_meta.json",
42
  "bytes": 201,
43
  "sha256": "b36fcf7ef7da8c2ace5271aa5da5ffaa5f10a779db2095cb69f5eadfc9972ae4"
 
 
 
 
 
 
 
 
 
 
44
  }
45
  ]
46
  }
vlm_decoder.pte → smolvlm_decoder_q8.pte RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:d92697a8c32eeaa1d73603cf608d658cf297fdf41ed81384ec29b7025e78e996
3
- size 388664832
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a54d81fbafd289c3b68891b150aa7ba9b069ffc5ecabe4114cc437fa726bc08a
3
+ size 366912132
vlm_prefill.pte → smolvlm_vision_q8.pte RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:1e8671eb77ae2f60c1eb80073454230bfaeb1eb22cee36dd9b028d8e291a3e3f
3
- size 388664320
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a48d6919aa2067be9a8d615e46f1cac5c8e0ac94a6cea7af9e4798290f5a9747
3
+ size 103839108
vlm_vision_q8.pte DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:5f09d6028d9bb945ce135d4be9ad499c1081f13d46655d2d1288c207f536ee4e
3
- size 108885888