File size: 4,826 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
139
140
141
142
143
#!/usr/bin/env python3
"""Produce an AWQ build of Piko-9b.

NOT RUN for this release. No AWQ artefact has been produced or validated, and
nothing in the documentation claims one works.

Two architecture-specific hazards this script guards against:

1. **Linear-attention parameters are not ordinary linear weights.** `A_log`,
   `dt_bias` and `conv1d` govern a recurrent state; quantizing them like a
   `q_proj` can destabilise generation in ways a perplexity check will not catch.
   They are excluded by default.
2. **Text-only calibration silently degrades the vision tower.** AWQ calibrates
   on a text corpus. Applied to the vision tower and merger, that can break image
   handling while every text metric stays healthy. The tower is left in bf16 by
   default.

    python scripts/quantize_awq.py --model Dexy2/Piko-9b --output ./piko-9b-awq

Afterwards you MUST run:

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

from __future__ import annotations

import argparse
import json
import sys
import time
from pathlib import Path

# Modules whose quantization risks the recurrent state or the vision path.
EXCLUDED_PATTERNS = [
    "visual",  # entire vision tower and merger
    "linear_attn.A_log",
    "linear_attn.dt_bias",
    "linear_attn.conv1d",
    "linear_attn.norm",
    "lm_head",
]


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--model", required=True)
    parser.add_argument("--output", type=Path, required=True)
    parser.add_argument("--bits", type=int, default=4, choices=[4])
    parser.add_argument("--group-size", type=int, default=128)
    parser.add_argument("--zero-point", action="store_true", default=True)
    parser.add_argument(
        "--calibration-samples",
        type=int,
        default=128,
        help="More samples give a better scale estimate and a slower run.",
    )
    parser.add_argument(
        "--quantize-vision",
        action="store_true",
        help="Quantize the vision tower too. Unvalidated; expect image regressions.",
    )
    parser.add_argument("--dry-run", action="store_true")
    args = parser.parse_args()

    excluded = list(EXCLUDED_PATTERNS)
    if args.quantize_vision:
        excluded.remove("visual")
        print(
            "WARNING: quantizing the vision tower with text calibration. "
            "Validate the image path before publishing.",
            file=sys.stderr,
        )

    config = {
        "zero_point": args.zero_point,
        "q_group_size": args.group_size,
        "w_bit": args.bits,
        "version": "GEMM",
        "modules_to_not_convert": excluded,
    }

    print("AWQ configuration:")
    print(json.dumps(config, indent=2))
    print(f"\nmodel  : {args.model}")
    print(f"output : {args.output}")
    print(f"samples: {args.calibration_samples}")
    print("\nEstimated cost: 30-90 minutes and >= 24 GB VRAM for a 9.65B model.")

    if args.dry_run:
        print("\n--dry-run: nothing executed.")
        return

    try:
        from awq import AutoAWQForCausalLM
    except ImportError:
        sys.exit(
            "autoawq is not installed:\n"
            "    pip install autoawq\n"
            "Note: autoawq support for the qwen3_5 hybrid architecture has NOT been "
            "verified. If it does not recognise the model type, this path is a dead end "
            "until upstream adds support."
        )

    from transformers import AutoTokenizer

    print("\nLoading model for quantization...", flush=True)
    began = time.time()
    model = AutoAWQForCausalLM.from_pretrained(args.model, device_map="cpu")
    tokenizer = AutoTokenizer.from_pretrained(args.model)

    print("Calibrating...", flush=True)
    model.quantize(tokenizer, quant_config=config, max_calib_samples=args.calibration_samples)

    args.output.mkdir(parents=True, exist_ok=True)
    model.save_quantized(str(args.output))
    tokenizer.save_pretrained(str(args.output))

    (args.output / "quantization_provenance.json").write_text(
        json.dumps(
            {
                "method": "awq",
                "source_model": args.model,
                "config": config,
                "calibration_samples": args.calibration_samples,
                "vision_quantized": args.quantize_vision,
                "elapsed_seconds": round(time.time() - began, 1),
                "timestamp": time.strftime("%Y-%m-%dT%H:%M:%S%z"),
                "validated": False,
            },
            indent=2,
        )
        + "\n",
        encoding="utf-8",
    )

    print(f"\nWrote {args.output}")
    print("NOT YET VALIDATED. Run scripts/validate_quantized_model.py before publishing.")


if __name__ == "__main__":
    main()