quantbench-engine 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- quantbench_engine-0.1.0/.github/workflows/ci.yml +35 -0
- quantbench_engine-0.1.0/.github/workflows/release.yml +43 -0
- quantbench_engine-0.1.0/.gitignore +52 -0
- quantbench_engine-0.1.0/CONTRIBUTING.md +146 -0
- quantbench_engine-0.1.0/DEVLOG.md +72 -0
- quantbench_engine-0.1.0/GUIDE.md +193 -0
- quantbench_engine-0.1.0/LICENSE +190 -0
- quantbench_engine-0.1.0/PKG-INFO +276 -0
- quantbench_engine-0.1.0/README.md +248 -0
- quantbench_engine-0.1.0/examples/01_quant_linear_inference.py +124 -0
- quantbench_engine-0.1.0/examples/02_advanced_calibration.py +111 -0
- quantbench_engine-0.1.0/examples/03_servebench_roofline_eval.py +140 -0
- quantbench_engine-0.1.0/pyproject.toml +78 -0
- quantbench_engine-0.1.0/src/quantbench/__init__.py +76 -0
- quantbench_engine-0.1.0/src/quantbench/bench/__init__.py +22 -0
- quantbench_engine-0.1.0/src/quantbench/bench/pareto.py +182 -0
- quantbench_engine-0.1.0/src/quantbench/cli.py +301 -0
- quantbench_engine-0.1.0/src/quantbench/eval/__init__.py +27 -0
- quantbench_engine-0.1.0/src/quantbench/eval/perplexity.py +143 -0
- quantbench_engine-0.1.0/src/quantbench/eval/sqnr.py +66 -0
- quantbench_engine-0.1.0/src/quantbench/nn/__init__.py +9 -0
- quantbench_engine-0.1.0/src/quantbench/nn/model_convert.py +109 -0
- quantbench_engine-0.1.0/src/quantbench/nn/quant_linear.py +296 -0
- quantbench_engine-0.1.0/src/quantbench/quant/__init__.py +58 -0
- quantbench_engine-0.1.0/src/quantbench/quant/affine.py +188 -0
- quantbench_engine-0.1.0/src/quantbench/quant/algorithms/__init__.py +41 -0
- quantbench_engine-0.1.0/src/quantbench/quant/algorithms/awq.py +184 -0
- quantbench_engine-0.1.0/src/quantbench/quant/algorithms/gptq.py +181 -0
- quantbench_engine-0.1.0/src/quantbench/quant/algorithms/smoothquant.py +132 -0
- quantbench_engine-0.1.0/src/quantbench/quant/blockwise.py +115 -0
- quantbench_engine-0.1.0/src/quantbench/quant/fp8.py +166 -0
- quantbench_engine-0.1.0/src/quantbench/quant/packing/__init__.py +27 -0
- quantbench_engine-0.1.0/src/quantbench/quant/packing/int4_pack.py +169 -0
- quantbench_engine-0.1.0/src/quantbench/quant/packing/sparse_pack.py +170 -0
- quantbench_engine-0.1.0/src/quantbench/quant/packing/ternary_pack.py +193 -0
- quantbench_engine-0.1.0/tests/test_algorithms.py +209 -0
- quantbench_engine-0.1.0/tests/test_eval_and_pareto.py +182 -0
- quantbench_engine-0.1.0/tests/test_packing.py +213 -0
- quantbench_engine-0.1.0/tests/test_primitives.py +206 -0
- quantbench_engine-0.1.0/tests/test_quant_linear.py +193 -0
- quantbench_engine-0.1.0/uv.lock +1204 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ${{ matrix.os }}
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
15
|
+
python-version: ["3.11", "3.12"]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Install uv
|
|
21
|
+
uses: astral-sh/setup-uv@v5
|
|
22
|
+
with:
|
|
23
|
+
enable-cache: true
|
|
24
|
+
|
|
25
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
26
|
+
run: uv python install ${{ matrix.python-version }}
|
|
27
|
+
|
|
28
|
+
- name: Install dependencies
|
|
29
|
+
run: uv sync --all-groups
|
|
30
|
+
|
|
31
|
+
- name: Run linter
|
|
32
|
+
run: uv run ruff check .
|
|
33
|
+
|
|
34
|
+
- name: Run test suite with coverage
|
|
35
|
+
run: uv run pytest
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
name: Build package
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: astral-sh/setup-uv@v5
|
|
15
|
+
with:
|
|
16
|
+
enable-cache: true
|
|
17
|
+
- name: Build sdist and wheel
|
|
18
|
+
run: uv build
|
|
19
|
+
- name: Store distributions
|
|
20
|
+
uses: actions/upload-artifact@v4
|
|
21
|
+
with:
|
|
22
|
+
name: python-package-distributions
|
|
23
|
+
path: dist/
|
|
24
|
+
|
|
25
|
+
publish-pypi:
|
|
26
|
+
name: Publish to PyPI
|
|
27
|
+
needs: [build]
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
environment:
|
|
30
|
+
name: pypi
|
|
31
|
+
url: https://pypi.org/p/quantbench
|
|
32
|
+
permissions:
|
|
33
|
+
id-token: write
|
|
34
|
+
steps:
|
|
35
|
+
- name: Download distributions
|
|
36
|
+
uses: actions/download-artifact@v4
|
|
37
|
+
with:
|
|
38
|
+
name: python-package-distributions
|
|
39
|
+
path: dist/
|
|
40
|
+
- name: Publish to PyPI
|
|
41
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
42
|
+
with:
|
|
43
|
+
skip-existing: true
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*$py.class
|
|
4
|
+
*.so
|
|
5
|
+
.Python
|
|
6
|
+
build/
|
|
7
|
+
develop-eggs/
|
|
8
|
+
dist/
|
|
9
|
+
downloads/
|
|
10
|
+
eggs/
|
|
11
|
+
.eggs/
|
|
12
|
+
lib/
|
|
13
|
+
lib64/
|
|
14
|
+
parts/
|
|
15
|
+
sdist/
|
|
16
|
+
var/
|
|
17
|
+
wheels/
|
|
18
|
+
share/python-wheels/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.installed.cfg
|
|
21
|
+
*.egg
|
|
22
|
+
MANIFEST
|
|
23
|
+
|
|
24
|
+
# Virtual environments
|
|
25
|
+
.venv/
|
|
26
|
+
venv/
|
|
27
|
+
ENV/
|
|
28
|
+
env/
|
|
29
|
+
|
|
30
|
+
# IDE & Editor
|
|
31
|
+
.vscode/
|
|
32
|
+
.idea/
|
|
33
|
+
*.swp
|
|
34
|
+
*.swo
|
|
35
|
+
|
|
36
|
+
# Testing & Coverage
|
|
37
|
+
.pytest_cache/
|
|
38
|
+
.coverage
|
|
39
|
+
htmlcov/
|
|
40
|
+
.tox/
|
|
41
|
+
.ruff_cache/
|
|
42
|
+
|
|
43
|
+
# Artifacts & Local Weights
|
|
44
|
+
*.safetensors
|
|
45
|
+
*.bin
|
|
46
|
+
*.pt
|
|
47
|
+
*.onnx
|
|
48
|
+
models/
|
|
49
|
+
cache/
|
|
50
|
+
results/
|
|
51
|
+
decisions.md
|
|
52
|
+
flow.md
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Contributing to quantbench ⚖️
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing to `quantbench`!
|
|
4
|
+
|
|
5
|
+
`quantbench` is an open-source, first-principles quantization engine, bit-packing runtime, 2:4 sparsity toolkit, and Quality-vs-Speed Pareto evaluation benchmark written in pure Python and PyTorch. We welcome contributions that improve mathematical transparency, expand sub-byte compression layouts, add new post-training calibration algorithms, and bridge inference serving systems with precision engineering.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🛠️ Development Setup
|
|
10
|
+
|
|
11
|
+
We use [uv](https://docs.astral.sh/uv/) for Python environment and dependency management.
|
|
12
|
+
|
|
13
|
+
1. **Clone the repository**:
|
|
14
|
+
```bash
|
|
15
|
+
git clone https://github.com/Prajwal7-2002/quantbench.git
|
|
16
|
+
cd quantbench
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
2. **Sync dependencies and virtual environment**:
|
|
20
|
+
```bash
|
|
21
|
+
uv sync
|
|
22
|
+
```
|
|
23
|
+
This automatically installs all runtime, benchmark, and development dependencies into a isolated `.venv`.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 🧪 Testing Protocol
|
|
28
|
+
|
|
29
|
+
We maintain high test coverage across mathematical primitives, sub-byte bit manipulation, neural network layers, and calibration algorithms.
|
|
30
|
+
|
|
31
|
+
1. **Run the entire test suite**:
|
|
32
|
+
```bash
|
|
33
|
+
uv run pytest
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
2. **Run with coverage report**:
|
|
37
|
+
```bash
|
|
38
|
+
uv run pytest --cov=quantbench --cov-report=term-missing
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
3. **Run a specific test category**:
|
|
42
|
+
```bash
|
|
43
|
+
# Test bit-packing roundtrip invariants
|
|
44
|
+
uv run pytest tests/test_packing.py
|
|
45
|
+
|
|
46
|
+
# Test calibration algorithms (SmoothQuant, AWQ, GPTQ)
|
|
47
|
+
uv run pytest tests/test_algorithms.py
|
|
48
|
+
|
|
49
|
+
# Test drop-in QuantLinear layers
|
|
50
|
+
uv run pytest tests/test_quant_linear.py
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Invariant Testing Rules
|
|
54
|
+
When contributing new quantization schemes or packing runtimes, you **must** include tests verifying:
|
|
55
|
+
- **Roundtrip Fidelity**: $x = \text{unpack}(\text{pack}(x))$ for all valid integer representations.
|
|
56
|
+
- **Mathematical Invariance**: Continuous exactness proofs (e.g. SmoothQuant $(X s^{-1})(s W) = XW$ within $10^{-5}$ drift).
|
|
57
|
+
- **Error Bounds**: Maximum absolute error bounded by $\frac{\Delta}{2}$ for uniform quantizers.
|
|
58
|
+
- **Extreme Inputs**: Subnormal values, exact zeros, all-positive, and all-negative tensor ranges.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 🎨 Code Quality & Style
|
|
63
|
+
|
|
64
|
+
We enforce modern Python conventions, strict type hints, and clean code formatting via [ruff](https://docs.astral.sh/ruff/):
|
|
65
|
+
|
|
66
|
+
1. **Lint and format checks**:
|
|
67
|
+
```bash
|
|
68
|
+
uv run ruff check .
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
2. **Auto-fix formatting**:
|
|
72
|
+
```bash
|
|
73
|
+
uv run ruff format .
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Architectural Principles
|
|
77
|
+
|
|
78
|
+
1. **Zero Compiled C++/CUDA Dependencies**:
|
|
79
|
+
`quantbench` is designed for radical algorithmic transparency. Core quantization logic and bit-packing must run in pure Python and PyTorch without requiring NVCC or compiler toolchains.
|
|
80
|
+
2. **Vectorized PyTorch Operations**:
|
|
81
|
+
Avoid Python `for` loops across tensor dimensions. Sub-byte bit-packing must be implemented via vectorized tensor operations (`&`, `|`, `>>`, `<<`) on `torch.uint8` containers.
|
|
82
|
+
3. **Hardware Agnostic**:
|
|
83
|
+
All algorithms must run seamlessly on consumer CPUs, Apple Silicon, and CUDA GPUs without hardcoded device assumptions.
|
|
84
|
+
4. **Honest Pareto Benchmarks**:
|
|
85
|
+
Never cherry-pick perplexity numbers. Always evaluate against standard validation corpora (WikiText-2, C4) using standardized sliding-window stride lengths.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 🎯 High-Impact Areas for Contribution
|
|
90
|
+
|
|
91
|
+
We actively welcome contributions in the following domains:
|
|
92
|
+
|
|
93
|
+
### 1. New Sub-Byte Formats & Precision Schemes (`src/quantbench/quant/`)
|
|
94
|
+
- [ ] **NVIDIA Blackwell NVFP4 Emulation**: Bit-exact emulation of 4-bit floating point formats (E2M1) with micro-scaling factors.
|
|
95
|
+
- [ ] **2-bit Non-Linear Quantization**: Quantization via Lloyd-Max optimal non-uniform codebooks.
|
|
96
|
+
- [ ] **NF4 (NormalFloat4)**: Quantile-based optimal representation for zero-mean normally distributed weights.
|
|
97
|
+
|
|
98
|
+
### 2. Quantized KV-Cache Runtimes
|
|
99
|
+
- [ ] **INT4 / FP8 KV-Cache Page Allocator**: Interfacing with `mini-infer`'s paged memory manager to benchmark memory savings during multi-turn generation.
|
|
100
|
+
- [ ] **Dynamic Per-Token Key/Value Quantization**: Asymmetric scaling per token vector along the sequence dimension.
|
|
101
|
+
|
|
102
|
+
### 3. Advanced Calibration & Optimization (`src/quantbench/quant/algorithms/`)
|
|
103
|
+
- [ ] **SqueezeLLM Clustering**: Non-uniform sensitivity-weighted k-means clustering with sparse outlier matrices.
|
|
104
|
+
- [ ] **QuIP# Vector Quantization**: Randomized Hadamard transform pre-processing to eliminate activation outliers.
|
|
105
|
+
- [ ] **HQQ (Half-Quadratic Quantization)**: Closed-form optimization without calibration data.
|
|
106
|
+
|
|
107
|
+
### 4. Real-World Model Support (`src/quantbench/nn/`)
|
|
108
|
+
- [ ] **GGUF Dequantization Loader**: Direct parsing and loading of `.gguf` quantized tensors into `QuantLinear` modules.
|
|
109
|
+
- [ ] **SafeTensors Model Converter**: Auto-convert and shard Hugging Face SafeTensors checkpoints directly into disk-mapped quantized weights.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 📬 Pull Request Rules & Submission Process
|
|
114
|
+
|
|
115
|
+
To keep `quantbench` reliable and maintainable, all contributions must follow these conventions:
|
|
116
|
+
|
|
117
|
+
### 1. Branch Naming Conventions
|
|
118
|
+
Create a descriptive branch off `main`:
|
|
119
|
+
* `feat/short-feature-name` (e.g. `feat/nvfp4-packing`)
|
|
120
|
+
* `fix/short-bug-description` (e.g. `fix/ternary-base3-rounding`)
|
|
121
|
+
* `perf/short-perf-description` (e.g. `perf/vectorized-hessian-chol`)
|
|
122
|
+
* `docs/short-doc-description` (e.g. `docs/smoothquant-proof-derivation`)
|
|
123
|
+
* `test/short-test-description` (e.g. `test/gptq-corner-cases`)
|
|
124
|
+
|
|
125
|
+
### 2. Conventional Commit Standards
|
|
126
|
+
Commits must follow the Conventional Commits specification:
|
|
127
|
+
```
|
|
128
|
+
feat(quant): add NVIDIA NVFP4 analytical emulator and bit-packing runtime
|
|
129
|
+
fix(packing): correct uint8 mask overflow in sparse24 index condensation
|
|
130
|
+
docs(guide): add mathematical derivation of optimal brain surgeon Hessian update
|
|
131
|
+
test(eval): add sliding window perplexity regression test on small corpus
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 3. PR Verification Checklist
|
|
135
|
+
Before submitting a PR, ensure:
|
|
136
|
+
- [ ] `uv run pytest` passes 100% of tests.
|
|
137
|
+
- [ ] `uv run ruff check .` reports 0 errors.
|
|
138
|
+
- [ ] Test coverage does not decrease.
|
|
139
|
+
- [ ] Relevant documentation or examples are updated.
|
|
140
|
+
- [ ] Single author attribution is preserved.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## 📜 License
|
|
145
|
+
|
|
146
|
+
By contributing to `quantbench`, you agree that your contributions will be licensed under the [Apache 2.0 License](LICENSE).
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# DEVLOG: quantbench
|
|
2
|
+
|
|
3
|
+
## 2026-10-26: Project Inception & Phase 1 Mathematical Primitives
|
|
4
|
+
|
|
5
|
+
- Initialized Project #5 `quantbench` (Unified Quantization Engine & Quality-vs-Speed Pareto Benchmark).
|
|
6
|
+
- Architecture decision: Unified from-scratch quantization algorithms (`quantbench.quant`), drop-in PyTorch modules (`quantbench.nn`), and sliding-window Pareto evaluation (`quantbench.eval`, `quantbench.bench`) in a single zero-dependency-friction repository.
|
|
7
|
+
- Initialized Apache 2.0 license, `.gitignore`, and `pyproject.toml` with `hatchling` build backend and `uv` package manager.
|
|
8
|
+
- Implemented Phase 1 Mathematical Primitives:
|
|
9
|
+
- Affine asymmetric quantization ($s, z$) and symmetric AbsMax quantization ($s, z=0$) with dynamic per-tensor and per-channel scaling along arbitrary dimensions.
|
|
10
|
+
- Provable clamp invariants verifying $|x - \hat{x}| \le \frac{s}{2}$.
|
|
11
|
+
- Group-wise / block-wise quantization for group sizes $G \in \{32, 64, 128\}$ with arbitrary tensor padding and dequantization reconstruction.
|
|
12
|
+
- Bit-level simulation of OCP FP8 E4M3FN (range $\pm 448$, 3 mantissa bits) and E5M2 (range $\pm 57344$, 2 mantissa bits).
|
|
13
|
+
- 100% test coverage across mathematical invariants and error bounds.
|
|
14
|
+
|
|
15
|
+
## 2026-10-26: Phase 2 — Integer Bit-Packing Runtimes (Sub-Byte Storage)
|
|
16
|
+
|
|
17
|
+
- Implemented vectorized sub-byte bit-packing runtimes in `quantbench.quant.packing`:
|
|
18
|
+
- `int4_pack.py`: Vectorized bit-shift packing of two INT4 values (unsigned $[0, 15]$ or signed two's complement $[-8, 7]$) into a single `uint8` byte container: `byte = (v0 & 0x0F) | ((v1 & 0x0F) << 4)`. Full support for odd dimension length padding and exact shape restoration.
|
|
19
|
+
- `ternary_pack.py`: BitNet 1.58b packing supporting both hardware-friendly 2-bit SIMD packing (4 weights/byte using biased offset $u = v + 1 \in \{0, 1, 2\}$) and theoretical maximal density Radix-3 base-3 packing (5 weights/byte via $3^5 = 243 < 256$).
|
|
20
|
+
- `sparse_pack.py`: NVIDIA Ampere/Ada/Hopper 2:4 structured sparsity layout. Automated row pruning, value condensation to $50\%$ storage ($M, N/2$), and 2-bit metadata column index packing into 4-bit nibbles ($M, N/8$). Decompression scatter preserves exact zeros and non-zero positions.
|
|
21
|
+
- Added CLI command `pack-inspect` to visualize sub-byte bit layouts and theoretical memory compression directly from the terminal.
|
|
22
|
+
- 22/22 unit tests passing with 93% test coverage in 2.18s. All Ruff checks passing.
|
|
23
|
+
|
|
24
|
+
## 2026-10-26: Phase 3 — Drop-in Neural Layers (`QuantLinear`) & Model Conversion
|
|
25
|
+
|
|
26
|
+
- Created `QuantLinear` (`quantbench.nn.quant_linear`):
|
|
27
|
+
- A drop-in replacement for `torch.nn.Linear` storing compressed integer weights inside persistent buffers (`self.register_buffer("packed_weight", ...)`).
|
|
28
|
+
- Supports INT4 (groupwise and per-channel), INT8, BitNet Ternary, and 2:4 Structured Sparsity.
|
|
29
|
+
- Implements dynamic vector dequantization in `forward(x)`.
|
|
30
|
+
- Overrides `_load_from_state_dict` to dynamically bind serialized buffer tensors during checkpoint restoration.
|
|
31
|
+
- Computes exact layer parameter memory bytes and compression factor relative to FP16.
|
|
32
|
+
- Created `convert_model_to_quant` (`quantbench.nn.model_convert`):
|
|
33
|
+
- In-place recursive model converter traversing PyTorch module trees.
|
|
34
|
+
- Swaps targeted linear layers (`q_proj`, `k_proj`, `v_proj`, `o_proj`, `gate_proj`, `up_proj`, `down_proj`) while cleanly preserving sensitive layers (`lm_head`).
|
|
35
|
+
- Reports layer conversion counts, memory reduction statistics, and elapsed conversion duration.
|
|
36
|
+
## 2026-10-26: Phase 4 — Advanced Calibration Algorithms (SmoothQuant, AWQ, GPTQ)
|
|
37
|
+
|
|
38
|
+
- Created `quantbench.quant.algorithms`:
|
|
39
|
+
- `smoothquant.py`: Implemented activation outlier channel profiling and per-channel migration scale computation ($Y = (X \cdot \text{diag}(s)^{-1}) \cdot (\text{diag}(s) \cdot W)$). Mathematical verification confirms exact continuous output invariance before discrete rounding.
|
|
40
|
+
- `awq.py`: Implemented Activation-aware Weight Quantization with mean activation channel profiling and grid-search over scale candidate $\alpha \in [0, 1]$ to minimize Frobenius reconstruction error $\| W X - \hat{W} X \|_F^2$.
|
|
41
|
+
- `gptq.py`: Implemented Accurate Post-Training Quantization for Generative Pre-trained Transformers using empirical activation Hessian ($H = 2 X X^T$), diagonal damping regularization ($H_{\text{damp}} = H + \lambda \cdot \text{mean}(\text{diag}(H)) \cdot I$), Cholesky decomposition inversion ($H^{-1} = L^{-T} L^{-1}$), Optimal Brain Surgeon (OBS) coordinate updates, and vectorized block GEMM updates ($W_{:, j+B:} \mathrel{-}= E_{\text{block}} \cdot [H^{-1}]_{j:j+B, j+B:}$).
|
|
42
|
+
- Added comprehensive unit tests in `tests/test_algorithms.py`:
|
|
43
|
+
- Outlier profiling and continuous mathematical invariance testing.
|
|
44
|
+
- AWQ reconstruction error reduction testing on synthetic salient channels.
|
|
45
|
+
- Hessian symmetry, positive semi-definiteness, and GPTQ OBS error reduction testing.
|
|
46
|
+
- 39/39 unit tests passing with 93% test coverage across the repository. All Ruff checks passing.
|
|
47
|
+
|
|
48
|
+
## 2026-10-26: Phase 5 & 6 — Sliding-Window Perplexity, SQNR & Pareto Benchmarking
|
|
49
|
+
|
|
50
|
+
- Created `quantbench.eval`:
|
|
51
|
+
- `sqnr.py`: Implemented Signal-to-Quantization-Noise Ratio ($10 \log_{10}(P_{\text{signal}} / P_{\text{noise}})$) in decibels (dB) for layer-level numerical fidelity tracking without forward inference overhead.
|
|
52
|
+
- `perplexity.py`: Implemented statistically rigorous sliding-window causal language model evaluation engine. Strided contexts ($S < L$) eliminate context boundary collapse by evaluating loss strictly on trailing $S$ tokens.
|
|
53
|
+
- Created `quantbench.bench.pareto`:
|
|
54
|
+
- `ParetoBenchmark`: Automated sweep engine mapping (Compression Ratio, Footprint MB, Delta PPL, SQNR dB) across FP16 baseline, INT8, INT4 (G=128, G=64), BitNet Ternary, and 2:4 Structured Sparsity.
|
|
55
|
+
- Formats GitHub Markdown tables and ASCII terminal scatter plots.
|
|
56
|
+
- Added CLI commands `eval` and `pareto` in `quantbench.cli`.
|
|
57
|
+
- Added unit tests in `tests/test_eval_and_pareto.py`:
|
|
58
|
+
- 47/47 unit tests passing across entire repo with 95% test coverage in 3.33s. All Ruff checks passing.
|
|
59
|
+
|
|
60
|
+
## 2026-10-26: Phase 7 — Public Learning Guide & Packaging Complete
|
|
61
|
+
|
|
62
|
+
- Authoring of `GUIDE.md`:
|
|
63
|
+
- Comprehensive educational engineering guide detailing arithmetic intensity rooflines, zero-preserving affine invariant derivation, banker's rounding drift, OCP FP8 specifications, vectorized sub-byte packing runtimes, SmoothQuant, AWQ, GPTQ second-order Hessian compensation, persistent buffer architecture, and dual-metric Pareto evaluation.
|
|
64
|
+
- Validated PyPI distribution packaging with `uv build`:
|
|
65
|
+
- Successfully produced source distribution (`.tar.gz`) and platform-independent wheel (`.whl`).
|
|
66
|
+
- Repository verified across 47 automated test suites with 95% line coverage. All Ruff checks passing.
|
|
67
|
+
- All 7 staged roadmap milestones in `README.md` completed.
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# The Comprehensive Guide to LLM Quantization & Hardware Sparsity ⚡
|
|
2
|
+
**From First-Principles Mathematical Formulations to Vectorized Bit-Packing Runtimes and Pareto Evaluation**
|
|
3
|
+
|
|
4
|
+
*Author: Prajwal Pujari ([@Prajwal7-2002](https://github.com/Prajwal7-2002))*
|
|
5
|
+
*Project: [quantbench](https://github.com/Prajwal7-2002/quantbench)*
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Introduction: Why Quantization is Mandatory in LLM Serving
|
|
10
|
+
|
|
11
|
+
When serving modern Generative Large Language Models (LLMs), the primary inference bottleneck during autoregressive token generation (the decoding phase) is **memory bandwidth, not compute capacity**.
|
|
12
|
+
|
|
13
|
+
### The Arithmetic Intensity Bottleneck
|
|
14
|
+
In the autoregressive decode loop, the model processes tokens with batch size $B=1$ and query length 1:
|
|
15
|
+
* The GEMV (General Matrix-Vector multiplication) arithmetic intensity is:
|
|
16
|
+
$$\text{Arithmetic Intensity} \approx \frac{2 \cdot P \text{ FLOPs}}{2 \cdot P \text{ Bytes}} = 1.0 \text{ FLOP/Byte} \quad (\text{in FP16})$$
|
|
17
|
+
* On an NVIDIA A100 SXM (312 TFLOPS FP16 Tensor Core, 2,039 GB/s HBM2e bandwidth), the hardware ridge point is:
|
|
18
|
+
$$\text{Ridge Point} = \frac{312 \times 10^{12} \text{ FLOP/s}}{2,039 \times 10^9 \text{ B/s}} \approx 153 \text{ FLOPs/Byte}$$
|
|
19
|
+
* Since $1.0 \ll 153$, the GPU Tensor Cores sit **99.3% idle**, starved for weights streaming from high-bandwidth memory (HBM).
|
|
20
|
+
|
|
21
|
+
### Memory Bandwidth Reduction = Linear Decode Acceleration
|
|
22
|
+
By compressing weights from 16-bit to 4-bit (INT4):
|
|
23
|
+
* Weight memory footprint is cut by $\mathbf{4.0\times}$.
|
|
24
|
+
* The byte volume transferred over the memory bus per generated token drops by $\mathbf{4.0\times}$.
|
|
25
|
+
* Autoregressive decoding throughput increases nearly linearly with the compression factor!
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 2. Mathematical Foundations of Quantization
|
|
30
|
+
|
|
31
|
+
### 2.1. The Zero-Preserving Affine Invariant
|
|
32
|
+
Quantization maps continuous real numbers $x \in [\alpha, \beta]$ to a discrete integer grid $q \in [q_{\min}, q_{\max}]$.
|
|
33
|
+
|
|
34
|
+
$$\text{scale} = s = \frac{\beta - \alpha}{q_{\max} - q_{\min}}$$
|
|
35
|
+
$$\text{zero\_point} = z = \text{round}\left( - \frac{\alpha}{s} \right) + q_{\min}$$
|
|
36
|
+
$$q = \text{clamp}\left( \text{round}\left( \frac{x}{s} \right) + z, \; q_{\min}, \; q_{\max} \right)$$
|
|
37
|
+
$$\hat{x} = (q - z) \cdot s$$
|
|
38
|
+
|
|
39
|
+
#### Why Real $0.0$ Must Map Exactly to an Integer Zero-Point $z$:
|
|
40
|
+
In neural network operations:
|
|
41
|
+
1. **Padded Tokens**: Sequences of varying lengths use zero-padding ($x = 0.0$).
|
|
42
|
+
2. **ReLU / SwiGLU Zeros**: Non-linear activations introduce exact zero activations.
|
|
43
|
+
3. If $x = 0.0$ did not map to an integer $z$:
|
|
44
|
+
$$\hat{x} = (\text{round}(0 + z_{\text{float}}) - z_{\text{float}}) \cdot s \ne 0.0$$
|
|
45
|
+
Padding tokens would leak non-zero bias signals across self-attention projections, corrupting transformer output logits!
|
|
46
|
+
|
|
47
|
+
### 2.2. Symmetric AbsMax Quantization
|
|
48
|
+
When data distributions are symmetric around zero (e.g. weights centered at mean $0$):
|
|
49
|
+
* Restrict zero point to strictly $z = 0$.
|
|
50
|
+
* For signed $b$-bit integer ($q_{\max} = 2^{b-1} - 1$):
|
|
51
|
+
$$s = \frac{\max(|x|)}{2^{b-1} - 1}$$
|
|
52
|
+
$$q = \text{clamp}\left( \text{round}\left( \frac{x}{s} \right), \; -q_{\max}, \; q_{\max} \right)$$
|
|
53
|
+
$$\hat{x} = q \cdot s$$
|
|
54
|
+
* In symmetric AbsMax, dequantization simplifies from $(q - z) s$ to pure multiplication $q \cdot s$, eliminating subtraction instructions from GPU GEMM inner loops.
|
|
55
|
+
|
|
56
|
+
### 2.3. Group-wise / Block-wise Quantization
|
|
57
|
+
Per-tensor quantization uses a single scale $s$ for the entire weight matrix. A single outlier value pushes $s$ up, severely degrading the precision of normal values.
|
|
58
|
+
|
|
59
|
+
Block-wise quantization slices the weight matrix into independent groups of size $G \in \{32, 64, 128\}$:
|
|
60
|
+
* Each block $k$ computes its own independent scale $s_k$ and zero-point $z_k$.
|
|
61
|
+
* **The Granularity Trade-off**:
|
|
62
|
+
* Smaller $G$ (e.g. 32): Higher precision, but scales consume $16 / 32 = 0.5$ bits/weight ($12.5\%$ metadata overhead).
|
|
63
|
+
* Larger $G$ (e.g. 128): Lower metadata overhead ($16 / 128 = 0.125$ bits/weight), but slightly reduced ability to isolate local outliers.
|
|
64
|
+
* In practice, **$G = 128$ or $G = 64$** represents the Pareto-optimal operating point for LLMs.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 3. Modern FP8 Formats (OCP Standard)
|
|
69
|
+
|
|
70
|
+
The Open Compute Project (OCP) standardizes two distinct 8-bit floating point formats:
|
|
71
|
+
|
|
72
|
+
| Specification | OCP FP8 E4M3FN | OCP FP8 E5M2 |
|
|
73
|
+
|---|---|---|
|
|
74
|
+
| **Bit Layout** | 1 sign, 4 exponent, 3 mantissa | 1 sign, 5 exponent, 2 mantissa |
|
|
75
|
+
| **Exponent Bias** | 7 | 15 |
|
|
76
|
+
| **Max Absolute Value** | $\pm 448.0$ | $\pm 57344.0$ |
|
|
77
|
+
| **Mantissa Resolution** | High ($2^{-3} = 0.125$) | Low ($2^{-2} = 0.25$) |
|
|
78
|
+
| **Primary Use-Case** | **Forward GEMM** (Weights & Activations) | **KV Cache, Gradients, Optimizer States** |
|
|
79
|
+
|
|
80
|
+
### Analytical Emulation on Older GPUs / CPUs
|
|
81
|
+
Hardware lacking native FP8 instructions (e.g. RTX 30-series, Apple Silicon, CPUs) cannot natively execute `torch.float8_e4m3fn`. `quantbench` provides a bit-exact software emulator that analytically simulates exponent bias, mantissa truncation, and saturation bounds:
|
|
82
|
+
```python
|
|
83
|
+
# Analytical bit-level reconstruction
|
|
84
|
+
e = clamp(floor(log2(|x|)) + bias, 0, 2^exp_bits - 1)
|
|
85
|
+
m = round((|x| / 2^(e - bias) - 1.0) * 2^mantissa_bits)
|
|
86
|
+
x_hat = sign(x) * (1.0 + m / 2^mantissa_bits) * 2^(e - bias)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 4. Sub-Byte Vectorized Bit-Packing Runtimes
|
|
92
|
+
|
|
93
|
+
Modern CPU and GPU memory subsystems are byte-addressable (minimum addressable boundary is **8 bits**). Storing 4-bit, 2-bit, or ternary weights requires sub-byte bit packing into `uint8` containers.
|
|
94
|
+
|
|
95
|
+
### 4.1. Vectorized INT4 Packing
|
|
96
|
+
* Two 4-bit values $v_0$ and $v_1$ pack into 1 byte:
|
|
97
|
+
$$\text{byte} = (v_0 \& 0x0F) \mid ((v_1 \& 0x0F) \ll 4)$$
|
|
98
|
+
* Unpacking:
|
|
99
|
+
$$v_0 = \text{byte} \& 0x0F, \quad v_1 = (\text{byte} \gg 4) \& 0x0F$$
|
|
100
|
+
* **Signed Two's Complement Extension**:
|
|
101
|
+
If bit 3 is set ($v \ge 8$), sign-extend to negative: $v_{\text{signed}} = v - 16$.
|
|
102
|
+
|
|
103
|
+
### 4.2. BitNet 1.58b Ternary Packing
|
|
104
|
+
Constrains weights strictly to $\{-1, 0, +1\}$:
|
|
105
|
+
1. **2-bit Biased SIMD Packing (Fastest)**:
|
|
106
|
+
* Maps $u = v + 1 \in \{0, 1, 2\}$.
|
|
107
|
+
* 4 weights fit into 1 byte:
|
|
108
|
+
$$\text{byte} = u_0 \mid (u_1 \ll 2) \mid (u_2 \ll 4) \mid (u_3 \ll 6)$$
|
|
109
|
+
* Decodes via parallel SIMD bit shifts without integer division.
|
|
110
|
+
2. **Radix-3 Base-3 Packing (Maximum Density)**:
|
|
111
|
+
* 5 weights fit into 1 byte since $3^5 = 243 \le 255$:
|
|
112
|
+
$$\text{byte} = u_0 + 3 u_1 + 9 u_2 + 27 u_3 + 81 u_4$$
|
|
113
|
+
* Achieves theoretical $\log_2(3) \approx 1.585 \text{ bits/weight}$.
|
|
114
|
+
|
|
115
|
+
### 4.3. NVIDIA Ampere/Ada/Hopper 2:4 Structured Sparsity
|
|
116
|
+
NVIDIA Tensor Cores natively support 2:4 sparsity (in every 4 contiguous elements, at least 2 must be zero):
|
|
117
|
+
1. **Condensed Matrix**: Compresses non-zeros into dense matrix $(M, N/2)$ (50% reduction).
|
|
118
|
+
2. **Metadata Nibbles**: Records 2-bit column indices ($0..3$) for each non-zero element.
|
|
119
|
+
3. Two 4-element blocks (8 dense elements, 4 non-zeros) pack into **1 metadata byte**:
|
|
120
|
+
$$\text{byte} = (idx_{A,0} \& 3) \mid ((idx_{A,1} \& 3) \ll 2) \mid ((idx_{B,0} \& 3) \ll 4) \mid ((idx_{B,1} \& 3) \ll 6)$$
|
|
121
|
+
4. Metadata overhead is strictly **2 bits per non-zero value** (only 12.5% of FP16 weight size).
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## 5. Advanced Post-Training Calibration Algorithms
|
|
126
|
+
|
|
127
|
+
### 5.1. SmoothQuant: Activation Outlier Migration
|
|
128
|
+
* **Insight**: LLM activations have systematic outlier channels ($100\times$ normal values), while weights are smooth.
|
|
129
|
+
* **Invariant Transformation**:
|
|
130
|
+
$$Y = X W = (X \cdot \text{diag}(s)^{-1}) \cdot (\text{diag}(s) \cdot W)$$
|
|
131
|
+
* **Scale Derivation**:
|
|
132
|
+
$$s_j = \frac{\max(|X_j|)^\alpha}{\max(|W_j|)^{1 - \alpha}} \quad (\alpha=0.5 \text{ balances activation and weight})$$
|
|
133
|
+
* Scale vector $s$ is folded into preceding LayerNorm/RMSNorm weights, enabling standard INT8 Tensor Core GEMM.
|
|
134
|
+
|
|
135
|
+
### 5.2. AWQ: Activation-Aware Weight Quantization
|
|
136
|
+
* **Insight**: Only top 1% salient weight channels preserve perplexity.
|
|
137
|
+
* Salience is determined by mean activation magnitude: $s_X = \frac{1}{N} \sum |X|$.
|
|
138
|
+
* Instead of keeping salient weights in FP16 (which ruins uniform SIMD kernels), AWQ applies a per-channel protection scale:
|
|
139
|
+
$$s = s_X^\alpha$$
|
|
140
|
+
where $\alpha \in [0, 1]$ is optimized via grid search on calibration tokens to minimize Frobenius reconstruction error:
|
|
141
|
+
$$\min_\alpha \| W X - \text{dequantize}(\text{quantize}(W \text{diag}(s))) \text{diag}(s)^{-1} X \|_F^2$$
|
|
142
|
+
|
|
143
|
+
### 5.3. GPTQ: Second-Order Hessian Compensation
|
|
144
|
+
* Uses second-order Taylor expansion of quantization error:
|
|
145
|
+
$$E \approx \frac{1}{2} (w - \hat{w})^T H (w - \hat{w}), \quad H = 2 X X^T$$
|
|
146
|
+
* **Damping Regularization**:
|
|
147
|
+
$$H_{\text{damp}} = H + \lambda \cdot \text{mean}(\text{diag}(H)) \cdot I \quad (\lambda = 0.01)$$
|
|
148
|
+
* Inverts via Cholesky decomposition: $H = L L^T \implies H^{-1} = L^{-T} L^{-1}$.
|
|
149
|
+
* **Optimal Brain Surgeon (OBS) Update**:
|
|
150
|
+
When rounding column $q$, updates remaining columns $F$:
|
|
151
|
+
$$\Delta W_{:, F} = - \frac{W_{:, q} - \hat{W}_{:, q}}{[H^{-1}]_{qq}} \cdot [H^{-1}]_{q, F}$$
|
|
152
|
+
* **Block Updates**: Accumulates errors in blocks of $B$ columns (e.g. 128) and performs dense matrix multiplications (GEMM) to update the remaining matrix.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 6. Drop-in PyTorch Architecture & In-Place Conversion
|
|
157
|
+
|
|
158
|
+
### Why Persistent Buffers Beat `nn.Parameter` for Inference
|
|
159
|
+
1. `nn.Parameter` forces float dtypes and triggers Autograd gradient tracking (`requires_grad=True`).
|
|
160
|
+
2. If touched by an optimizer, it allocates momentum and gradient buffers ($2\times\text{--}3\times$ model size).
|
|
161
|
+
3. `QuantLinear` stores packed weights as persistent buffers (`self.register_buffer("packed_weight", ...)`):
|
|
162
|
+
* Zero gradient tracking.
|
|
163
|
+
* Zero optimizer memory overhead.
|
|
164
|
+
* Automatically serializes into `state_dict()` and `safetensors`.
|
|
165
|
+
4. **Dynamic Checkpoint Hook (`_load_from_state_dict`)**:
|
|
166
|
+
Overrides PyTorch's hook to register serialized buffer shapes dynamically before strict key validation runs.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## 7. Quality-vs-Speed Pareto Benchmarking
|
|
171
|
+
|
|
172
|
+
### Dual-Metric Evaluation
|
|
173
|
+
1. **Layer-Level SQNR (dB)**:
|
|
174
|
+
$$\text{SQNR (dB)} = 10 \cdot \log_{10} \left( \frac{\sum |W|^2}{\sum |W - \hat{W}|^2 + \epsilon} \right)$$
|
|
175
|
+
Diagnoses numerical reconstruction fidelity layer-by-layer without inference passes.
|
|
176
|
+
2. **Model-Level Sliding-Window Perplexity**:
|
|
177
|
+
Uses context window $L$ with stride $S < L$, evaluating loss strictly on trailing $S$ tokens to eliminate chunk boundary context collapse.
|
|
178
|
+
3. **Pareto Table Output**:
|
|
179
|
+
Maps configurations across (Compression Ratio, Footprint MB, Delta PPL, SQNR dB).
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 8. Summary Comparison of Quantization Schemes
|
|
184
|
+
|
|
185
|
+
| Scheme | Target Bits | Memory Reduction | Compute Speedup | Accuracy Impact | Recommended Use-Case |
|
|
186
|
+
|---|---|---|---|---|---|
|
|
187
|
+
| **FP16 Baseline** | 16-bit | $1.0\times$ | $1.0\times$ | Baseline | Reference quality |
|
|
188
|
+
| **FP8 (E4M3)** | 8-bit | $2.0\times$ | $1.8\times\text{--}2.0\times$ | Near zero ($\Delta \text{PPL} < 0.01$) | Standard serving on Ada/Hopper |
|
|
189
|
+
| **INT8 AbsMax** | 8-bit | $2.0\times$ | $1.7\times\text{--}1.9\times$ | Near zero ($\Delta \text{PPL} < 0.02$) | Universal CPU/GPU acceleration |
|
|
190
|
+
| **INT4 Blockwise** | 4-bit | $3.2\times\text{--}3.6\times$ | $2.5\times\text{--}3.5\times$ | Low ($\Delta \text{PPL} < 0.15$) | VRAM-constrained edge / consumer GPUs |
|
|
191
|
+
| **AWQ / GPTQ INT4** | 4-bit | $3.5\times$ | $3.0\times$ | Minimal ($\Delta \text{PPL} < 0.05$) | Production 4-bit LLM deployment |
|
|
192
|
+
| **Ampere 2:4 Sparse**| 8-bit eff. | $2.0\times$ | $2.0\times$ math | Negligible ($\Delta \text{PPL} < 0.08$) | Dense-throughput acceleration |
|
|
193
|
+
| **BitNet Ternary** | 1.58-bit | $7.5\times\text{--}8.0\times$ | $5.0\times\text{--}7.0\times$ | Requires fine-tuning | Ultra-low power / specialized silicon |
|