File size: 5,190 Bytes
0810902
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# Quantization

## Summary

| Format | Status | Weights | Practical VRAM | Notes |
|---|---|---:|---:|---|
| bfloat16 (native) | Shipped | 19.8 GB | ~22 GB | The published checkpoint |
| 4-bit NF4 (bitsandbytes) | **Validated** | 5.6 GB | **8.1 GB measured** | Every measured result here used this |
| 8-bit (bitsandbytes) | Untested here | 10.1 GB | ~12 GB | Should work; not validated |
| GGUF | **Not produced** | — | — | See below |
| AWQ | **Not produced** | — | — | Script provided, not run |
| GPTQ | **Not produced** | — | — | Script provided, not run |

No quantized artefact has been published for Piko-9b. Nothing in this section claims a
quantization works unless it was run.

## 4-bit NF4 — the validated path

This is what every number in this repository was measured with.

```python
import torch
from transformers import AutoModelForMultimodalLM, AutoProcessor, BitsAndBytesConfig

quantization = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16,
    bnb_4bit_use_double_quant=True,
)

model = AutoModelForMultimodalLM.from_pretrained(
    "Dexy2/Piko-9b",
    quantization_config=quantization,
    device_map={"": 0},          # never "auto"
    dtype=torch.bfloat16,
)
processor = AutoProcessor.from_pretrained("Dexy2/Piko-9b")
```

Measured on an RTX 5070 Ti (17.1 GB), weights on NVMe:

| | |
|---|---|
| Cold load | 100–102 s |
| Resident VRAM | 8.1 GB |
| Text generation | 0.9–2.9 s per short answer |
| Image + text | ~4 s per answer |
| 14,429-token prompt | 3.5 s, needle retrieved correctly |
| Custom suite | 65/70 |

**The vision path survives 4-bit.** OCR scored 10/10 and document understanding 10/10 under NF4,
so the projector and vision tower are not visibly damaged by weight quantization. That is a
measurement on this suite, not a general guarantee.

## The rule that matters more than the format

Whatever quantization you choose, **the whole model must stay on one device.**

Piko-9b keeps a recurrent `float32` state across its 24 linear-attention layers. `device_map="auto"`
on an undersized GPU offloads layers to CPU, corrupts that state, and the model emits a single
repeated character with no error raised. Choosing a stronger quantization is the correct response
to insufficient VRAM; enabling offload is not.

```python
device_map={"": 0}     # correct
device_map="auto"      # silently breaks unless the model fits entirely
```

## 8-bit

```python
quantization = BitsAndBytesConfig(load_in_8bit=True)
```

Expected ~10.1 GB of weights. **Not validated here** — no result in this repository was produced
in 8-bit. If you use it, run `evaluation/run_smoke_eval.py` first; its first check exists to catch
a degenerate model.

## GGUF — why there isn't one

`llama.cpp` support depends on the architecture being implemented there. Piko-9b is `qwen3_5`: a
hybrid stack of gated linear attention plus periodic full attention, with mRoPE and a Qwen3.5
vision tower. Converting it means the converter must handle:

* linear-attention layers (`in_proj_qkv`, `in_proj_a/b/z`, `A_log`, `dt_bias`, `conv1d`)
* the `full_attention_interval` layer pattern
* mRoPE with `mrope_section [11, 11, 10]` and `partial_rotary_factor` 0.25
* a separate multimodal projector for the vision tower

A GGUF of an *earlier, text-only* checkpoint in this project's history exists locally
(`Ornith-1.0-9B.BF16.gguf` plus an mmproj file), which shows the family can be converted. That is
**not** this checkpoint and says nothing about whether the published composition converts cleanly.

No GGUF is published, and none should be assumed to work until someone runs
`scripts/validate_quantized_model.py` against it.

## AWQ and GPTQ

Scripts are provided but **were not run**:

* [`scripts/quantize_awq.py`](../scripts/quantize_awq.py)
* [`scripts/quantize_gptq.py`](../scripts/quantize_gptq.py)

Both are activation-aware methods that need a calibration pass. Two specific risks for this
architecture:

1. **Linear-attention projections.** `A_log` and `dt_bias` are not ordinary linear weights.
   Quantizing them like `q_proj` can destabilise the recurrent state. Both scripts exclude them by
   default.
2. **The vision tower.** Most AWQ/GPTQ tooling calibrates on text only. A text-calibrated
   quantizer applied to the vision tower can degrade image handling in a way no text benchmark
   will show. Both scripts leave the vision tower in bf16 by default.

## Validating any quantized build

Never publish a quantized artefact without this:

```bash
python scripts/validate_quantized_model.py \
  --quantized ./piko-9b-awq \
  --reference Dexy2/Piko-9b \
  --output reports/quantization_validation.json
```

It checks loading, degenerate output, text quality against the reference, **image input**, memory,
and throughput. A quantization that passes text checks but breaks the vision path is the most
likely failure mode here, and the easiest to miss.

## Choosing

| You have | Use |
|---|---|
| ≥ 22 GB VRAM | bfloat16, `device_map={"": 0}` |
| 12–22 GB | 4-bit NF4 (validated) or 8-bit (untested) |
| 8–12 GB | 4-bit NF4 |
| < 8 GB | Not supported. Do not use offload |