Skip to content

Commit 3eb097b

Browse files
committed
Merge branch 'export/issue-7-rotation-0-3-5' into 'export/v0-5-0'
Add rotation See merge request onecomp/onecomp-lab!36
2 parents f6c2675 + 81be5bd commit 3eb097b

25 files changed

Lines changed: 123973 additions & 10 deletions

CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,24 @@
1515
- Provides learning-based post-quantization fine-tuning for GPTQ-quantized models
1616
- Public API is exposed as `PostProcessLoraSFT`
1717

18+
### New Feature: Rotation Preprocessing Pipeline (`onecomp/pre_process/`)
19+
20+
SpinQuant/OstQuant-based rotation preprocessing that reduces quantization error by learning optimal rotation matrices before quantization. Supports Llama and Qwen3 architectures.
21+
22+
- Added `prepare_rotated_model()` (`onecomp/pre_process/prepare_rotated_model.py`): End-to-end pipeline — model loading → rotation/scaling training → rotation application → saving
23+
- Memory-optimized: moves model between CPU/GPU to reduce peak memory (e.g. Qwen3-32B: ~128GB → ~64GB)
24+
- Added `RotatedModelConfig` (`onecomp/rotated_model_config.py`): `ModelConfig` subclass that automatically registers Hadamard hooks on `down_proj` layers during `load_model()`
25+
- Added `onecomp/pre_process/` package:
26+
- `train_rotation.py`: Training pipeline with `PreprocessManager` (R1/R2/S_* tensor management), HF `Trainer` subclass, `apply_preprocess_train` / `apply_preprocess_eval`
27+
- `optimizer.py`: `SGDG` — SGD on the Stiefel manifold with Cayley-retraction orthogonal updates (ported from SpinQuant)
28+
- `quant_models.py`: `WeightQuantizer` (RTN proxy) with per-channel / per-tensor / group-wise quantization; quantized decoder layers for Llama and Qwen3
29+
- `rotation_utils.py`: `fuse_layer_norms`, `rotate_model`, `register_online_hadamard_hooks`
30+
- `hadamard_utils.py`: Hadamard transform utilities and pre-computed matrices (ported from QuIP#)
31+
- `modeling_llama.py` / `modeling_qwen3.py`: Custom `ForCausalLM` classes that propagate R1 through the forward pass during training
32+
- `preprocess_args.py`: `TrainingArguments` subclass with SGDG-specific LR/momentum fields
33+
- Fixed `_PreprocessTrainer` to override `create_optimizer()` instead of `create_optimizer_and_scheduler()` for transformers >= 5.x compatibility (SGDG optimizer was silently replaced by AdamW)
34+
- Updated `Runner.save_dequantized_model()` and `Runner.save_quantized_model()` to warn when saving models loaded with additional preprocessing (e.g., Hadamard hooks)
35+
1836
### Added JointQ Quantizer
1937

2038
- **Added new `JointQ` quantizer (`onecomp/quantizer/jointq/`)**
@@ -55,6 +73,8 @@
5573
- Added `example/post_process/example_lora_sft_knowledge.py`: Knowledge injection demo — teaches the quantized model about "OneCompression" via LoRA SFT and compares generation before/after
5674
- Added `example/post_process/onecomp_knowledge.jsonl`: Training data describing OneCompression for the knowledge injection example
5775
- Added `example/example_jointq.py`: JointQ 4-bit (groupsize=128) quantization example with dequantized model PPL evaluation
76+
- Added `example/pre_process/example_llama_preprocess_rtn.py`: Rotation preprocessing + RTN quantization (TinyLlama-1.1B)
77+
- Added `example/pre_process/example_preprocess_save_load.py`: Rotation preprocessing + GPTQ quantization → save → load → PPL verification
5878

5979
### Documentation
6080

@@ -64,6 +84,11 @@
6484
- Updated `docs/api/runner.md` to include `create_quantized_model` and `save_quantized_model_pt`
6585
- Updated `docs/api/quantized_model_loader.md` to include `load_quantized_model_pt`
6686
- Updated `mkdocs.yml` navigation with new post-process pages
87+
- Added `docs/user-guide/pre-process.md`: Rotation preprocessing user guide covering workflow, key parameters, save/load, and limitations
88+
- Added `docs/api/pre_process.md`: API reference for `prepare_rotated_model` and `RotatedModelConfig`
89+
- Updated `docs/user-guide/examples.md` with rotation preprocessing code examples (RTN, GPTQ with save/load)
90+
- Updated `docs/api/index.md` with `RotatedModelConfig`, `prepare_rotated_model`, and `pre_process/` module structure
91+
- Updated `docs/index.md` Key Features with rotation preprocessing
6792

6893
### Tests
6994

README.md

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ This package is currently under development (version 0) and may behave unstably.
1111
- **AutoBit**: Mixed-precision quantization with ILP-based bitwidth assignment. Automatically estimates the target bitwidth from available VRAM and assigns per-layer bitwidths to minimize quantization error under the memory budget.
1212
- **JointQ**: Joint quantization method that optimizes weight assignments and scale parameters simultaneously for improved quantization accuracy. Supports group-wise quantization (e.g., 4-bit, groupsize=128).
1313
- **LoRA SFT Post-Process**: Fine-tune quantized models with LoRA adapters for accuracy recovery or domain-specific knowledge injection. Supports SFT loss, teacher distillation, and intermediate block alignment.
14+
- **Rotation Preprocessing**: SpinQuant/OstQuant-based rotation preprocessing that reduces quantization error by learning optimal rotation matrices before quantization. Rotation/scaling matrices are absorbed into model weights, with online Hadamard hooks automatically registered at load time. Supports Llama and Qwen3 architectures.
1415
- (TBD)
1516

1617
## 🔧 Installation
@@ -142,11 +143,21 @@ uv run mkdocs serve
142143

143144
Then open [http://127.0.0.1:8000](http://127.0.0.1:8000) in your browser.
144145

145-
## 🚀 Example
146-
147-
See [example/example1.py](./example/example1.py) and [example/example2.py](./example/example2.py) for step-by-step GPTQ + QEP examples.
148-
149-
For AutoBit mixed-precision quantization, see [example/example3.py](./example/example3.py) and [example/example_auto_run.py](./example/example_auto_run.py).
146+
## 🚀 Examples
147+
148+
| Category | Script | Description |
149+
|----------|--------|-------------|
150+
| Quantization | [example_gptq.py](./example/example_gptq.py) | GPTQ quantization |
151+
| | [example_qep_gptq.py](./example/example_qep_gptq.py) | GPTQ + QEP (error propagation) |
152+
| | [example_jointq.py](./example/example_jointq.py) | JointQ quantization |
153+
| | [example_autobit.py](./example/example_autobit.py) | AutoBit mixed-precision quantization |
154+
| | [example_auto_run.py](./example/example_auto_run.py) | AutoBit with automatic VRAM estimation |
155+
| Save / Load | [example_save_load.py](./example/example_save_load.py) | Save and load quantized models |
156+
| Rotation Preprocessing | [example_llama_preprocess_rtn.py](./example/pre_process/example_llama_preprocess_rtn.py) | Rotation preprocessing + RTN (TinyLlama) |
157+
| | [example_preprocess_save_load.py](./example/pre_process/example_preprocess_save_load.py) | Save and load rotation-preprocessed quantized models |
158+
| Post-Process | [example_lora_sft.py](./example/post_process/example_lora_sft.py) | LoRA SFT post-quantization fine-tuning |
159+
| | [example_lora_sft_knowledge.py](./example/post_process/example_lora_sft_knowledge.py) | LoRA SFT knowledge injection |
160+
| vLLM | [example_vllm_inference.py](./example/example_vllm_inference.py) | Serve quantized models with vLLM |
150161

151162
## 🔌 vLLM Inference
152163

docs/api/index.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@ The following are available directly from `import onecomp`:
2121
| `CQ` | Class | CQ quantizer |
2222
| `QBB` | Class | QBB quantizer |
2323
| `Onebit` | Class | 1-bit quantizer |
24+
| `RotatedModelConfig` | Class | ModelConfig for rotation-preprocessed models |
2425
| `QuantizedModelLoader` | Class | Loader for saved quantized models |
2526
| `load_quantized_model` | Function | Shortcut for `QuantizedModelLoader.load_quantized_model` |
27+
| `load_quantized_model_pt` | Function | Shortcut for `QuantizedModelLoader.load_quantized_model_pt` |
28+
| `prepare_rotated_model`| Function | Rotation preprocessing pipeline |
2629
| `setup_logger` | Function | Configure logging output |
2730

2831
## Module Structure
@@ -33,6 +36,9 @@ onecomp/
3336
cli.py # CLI entry point (onecomp command)
3437
__main__.py # python -m onecomp support
3538
model_config.py # ModelConfig class
39+
rotated_model_config.py # RotatedModelConfig class
40+
pre_process/ # Rotation preprocessing pipeline
41+
prepare_rotated_model.py # prepare_rotated_model()
3642
qep/ # QEP module
3743
_qep_config.py # QEPConfig dataclass
3844
quantizer/ # Quantizer implementations

docs/api/pre_process.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Pre-Process (Rotation Preprocessing)
2+
3+
Rotation preprocessing reduces quantization error by learning optimal rotation matrices
4+
(SpinQuant/OstQuant) and absorbing them into model weights before quantization.
5+
6+
## `prepare_rotated_model`
7+
8+
::: onecomp.pre_process.prepare_rotated_model.prepare_rotated_model
9+
options:
10+
show_source: false
11+
12+
## `RotatedModelConfig`
13+
14+
`ModelConfig` subclass for loading rotation-preprocessed models.
15+
Automatically registers Hadamard `forward_pre_hook` on `down_proj` layers.
16+
17+
::: onecomp.rotated_model_config.RotatedModelConfig
18+
options:
19+
show_source: false
20+
21+
## Workflow
22+
23+
```
24+
┌─────────────────────────────────────────────────────────────┐
25+
│ Step 1: Rotation Preprocessing │
26+
│ │
27+
│ ModelConfig ──► prepare_rotated_model() ──► RotatedModelConfig
28+
│ (train rotation matrices, │
29+
│ absorb into weights, │
30+
│ save rotated model) │
31+
└──────────────────────────┬──────────────────────────────────┘
32+
33+
┌──────────────────────────▼──────────────────────────────────┐
34+
│ Step 2: Quantization │
35+
│ │
36+
│ RotatedModelConfig ──► Runner(quantizer=GPTQ/RTN/...) ──► run()
37+
│ (auto-registers ──► save_quantized_model() │
38+
│ Hadamard hooks) │
39+
└──────────────────────────┬──────────────────────────────────┘
40+
41+
┌──────────────────────────▼──────────────────────────────────┐
42+
│ Step 3: Load │
43+
│ │
44+
│ load_quantized_model() │
45+
│ (auto-detects "rotated: true" in config.json, │
46+
│ registers Hadamard hooks automatically) │
47+
└─────────────────────────────────────────────────────────────┘
48+
```
49+
50+
!!! note
51+
The `wbits`, `groupsize`, and `sym` parameters passed to `prepare_rotated_model()`
52+
control the RTN proxy used during rotation training. These values **must match**
53+
the quantizer parameters used in Step 2.

docs/index.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ It implements state-of-the-art quantization algorithms including GPTQ, DBF, RTN,
1010

1111
## Key Features
1212

13-
- **Multiple quantization algorithms** -- GPTQ, DBF (Double Binary Factorization), RTN (Round-To-Nearest), JointQ, QuIP, and more
14-
- **Quantization Error Propagation (QEP)** -- A novel method that adjusts weights before quantization to compensate for error propagation across layers
15-
- **Simple, unified API** -- Configure model, quantizer, and runner in a few lines of code
16-
- **Save/Load pipeline** -- Save quantized models in a format compatible with Hugging Face Transformers and vLLM
17-
- **Evaluation tools** -- Built-in perplexity and zero-shot accuracy benchmarks
13+
- **Quantization Error Propagation (QEP)** -- A post-training quantization method that corrects quantization errors by propagating them to subsequent layers, improving the accuracy of quantized LLMs. See [Arai & Ichikawa, NeurIPS 2025](https://openreview.net/forum?id=a3l3K9khbL) for details.
14+
- **vLLM Plugin Integration** -- Serve OneComp-quantized models with [vLLM](https://docs.vllm.ai/) via built-in plugins for DBF and Mixed-GPTQ quantization methods.
15+
- **AutoBit** -- Mixed-precision quantization with ILP-based bitwidth assignment. Automatically estimates the target bitwidth from available VRAM and assigns per-layer bitwidths to minimize quantization error under the memory budget.
16+
- **JointQ** -- Joint quantization method that optimizes weight assignments and scale parameters simultaneously for improved quantization accuracy. Supports group-wise quantization (e.g., 4-bit, groupsize=128).
17+
- **LoRA SFT Post-Process** -- Fine-tune quantized models with LoRA adapters for accuracy recovery or domain-specific knowledge injection. Supports SFT loss, teacher distillation, and intermediate block alignment.
18+
- **Rotation Preprocessing** -- SpinQuant/OstQuant-based rotation preprocessing that reduces quantization error by learning optimal rotation matrices before quantization. Rotation/scaling matrices are absorbed into model weights, with online Hadamard hooks automatically registered at load time. Supports Llama and Qwen3 architectures.
1819

1920
## Quick Example
2021

docs/user-guide/examples.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,81 @@ print(ppl_dict)
193193
# {'original': 5.47, 'GPTQ': 5.72, 'JointQ': 5.68}
194194
```
195195

196+
## Rotation Preprocessing + RTN
197+
198+
Apply SpinQuant-style rotation preprocessing before quantization to reduce quantization error:
199+
200+
```python
201+
from onecomp import ModelConfig, Runner, RTN, prepare_rotated_model, setup_logger
202+
203+
setup_logger()
204+
205+
model_config = ModelConfig(
206+
model_id="TinyLlama/TinyLlama-1.1B-intermediate-step-1431k-3T",
207+
device="cuda:0",
208+
)
209+
210+
# Step 1: Rotation preprocessing
211+
rotated_config = prepare_rotated_model(
212+
model_config=model_config,
213+
save_directory="./rotated_model",
214+
seed=0,
215+
wbits=3,
216+
groupsize=-1,
217+
sym=False,
218+
)
219+
220+
# Step 2: Quantize the rotated model (wbits/groupsize/sym must match Step 1)
221+
rtn = RTN(wbits=3, groupsize=-1, sym=False)
222+
runner = Runner(model_config=rotated_config, quantizer=rtn)
223+
runner.run()
224+
225+
original_ppl, dequantized_ppl, _ = runner.calculate_perplexity(
226+
original_model=True, dequantized_model=True, quantized_model=False
227+
)
228+
print(f"Original model perplexity: {original_ppl}")
229+
print(f"Dequantized model perplexity: {dequantized_ppl}")
230+
```
231+
232+
## Rotation Preprocessing + GPTQ with Save/Load
233+
234+
Full pipeline including save and load of rotation-preprocessed quantized models:
235+
236+
```python
237+
from onecomp import (
238+
ModelConfig, Runner, GPTQ,
239+
prepare_rotated_model, load_quantized_model, setup_logger,
240+
)
241+
242+
setup_logger()
243+
244+
# Step 1: Rotation preprocessing
245+
model_config = ModelConfig(
246+
model_id="TinyLlama/TinyLlama-1.1B-intermediate-step-1431k-3T",
247+
device="cuda:0",
248+
)
249+
rotated_config = prepare_rotated_model(
250+
model_config=model_config,
251+
save_directory="./rotated_model",
252+
seed=0,
253+
wbits=4,
254+
groupsize=128,
255+
)
256+
257+
# Step 2: Quantize and save
258+
gptq = GPTQ(wbits=4, groupsize=128)
259+
runner = Runner(model_config=rotated_config, quantizer=gptq)
260+
runner.run()
261+
runner.save_quantized_model("./quantized_model")
262+
263+
# Step 3: Load (Hadamard hooks are auto-registered via "rotated: true" in config.json)
264+
model, tokenizer = load_quantized_model("./quantized_model")
265+
```
266+
267+
See [Pre-Process API](../api/pre_process.md) for full parameter documentation.
268+
269+
---
270+
196271
## Saving and Loading Quantized Models
197272

198273
### Save the quantized model

docs/user-guide/pre-process.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Pre-Process (Rotation Preprocessing)
2+
3+
Rotation preprocessing applies SpinQuant/OstQuant-style rotation matrices to model weights
4+
before quantization, reducing quantization error. This is particularly effective for
5+
low-bit quantization (e.g., 3-bit).
6+
7+
## Overview
8+
9+
The rotation preprocessing pipeline:
10+
11+
1. **Trains** rotation/scaling matrices using calibration data with an RTN quantization proxy
12+
2. **Absorbs** the learned matrices into model weights (fuses LayerNorms, rotates projections)
13+
3. **Registers** online Hadamard hooks on `down_proj` layers for inference correctness
14+
4. **Saves** the rotated model as a standard Hugging Face model directory
15+
16+
The saved model can then be quantized with any quantizer (GPTQ, RTN, etc.) using the
17+
standard `Runner` pipeline.
18+
19+
## Quick Start
20+
21+
```python
22+
from onecomp import ModelConfig, Runner, GPTQ, prepare_rotated_model, setup_logger
23+
24+
setup_logger()
25+
26+
# Step 1: Rotation preprocessing
27+
model_config = ModelConfig(model_id="meta-llama/Llama-2-7b-hf", device="cuda:0")
28+
29+
rotated_config = prepare_rotated_model(
30+
model_config=model_config,
31+
save_directory="./rotated_model",
32+
wbits=4,
33+
groupsize=128,
34+
)
35+
36+
# Step 2: Quantize (wbits/groupsize/sym must match Step 1)
37+
gptq = GPTQ(wbits=4, groupsize=128)
38+
runner = Runner(model_config=rotated_config, quantizer=gptq)
39+
runner.run()
40+
```
41+
42+
## Supported Architectures
43+
44+
| Architecture | Status |
45+
|-------------|--------|
46+
| Llama | Supported |
47+
| Qwen3 | Supported |
48+
49+
## Key Parameters
50+
51+
| Parameter | Description | Default |
52+
|-----------|-------------|---------|
53+
| `rotation` | Apply rotation matrices (R1, R2) | `True` |
54+
| `scaling` | Apply scaling diagonals (S_*) | `False` |
55+
| `enable_training` | Train rotation matrices (vs. random init) | `True` |
56+
| `wbits` | RTN proxy bit-width (must match quantizer) | `4` |
57+
| `groupsize` | RTN proxy group size (must match quantizer) | `-1` |
58+
| `sym` | RTN proxy symmetric quantization | `False` |
59+
| `fp32_had` | Use FP32 for online Hadamard transform | `False` |
60+
| `seed` | Seed for rotation init and calibration data | `0` |
61+
62+
!!! warning "Parameter matching"
63+
The `wbits`, `groupsize`, and `sym` parameters **must match** the quantizer
64+
used in Step 2. Mismatched values will degrade quantization quality because
65+
the rotation matrices were optimized for different quantization settings.
66+
67+
## Save and Load
68+
69+
Rotation-preprocessed quantized models support the standard save/load API:
70+
71+
```python
72+
# Save
73+
runner.save_quantized_model("./quantized_model")
74+
75+
# Load (Hadamard hooks are auto-registered)
76+
from onecomp import load_quantized_model
77+
model, tokenizer = load_quantized_model("./quantized_model")
78+
```
79+
80+
The saved `config.json` includes `"rotated": true` and `"fp32_had": false`,
81+
which `load_quantized_model()` uses to automatically register the required
82+
Hadamard hooks on `down_proj` layers.
83+
84+
## Limitations
85+
86+
- **vLLM inference is not supported.** vLLM kernels do not apply the online Hadamard
87+
transform required by rotation-preprocessed models.
88+
- Only Llama and Qwen3 architectures are currently supported.
89+
90+
## API Reference
91+
92+
See [Pre-Process API](../api/pre_process.md) for full parameter documentation.

docs/user-guide/vllm-inference.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ The plugins are automatically registered via Python entry points when `onecomp`
1010
| DBF | `dbf` | 1-bit Double Binary Factorization. Uses GemLite kernels by default; set `ONECOMP_DBF_NAIVE_LINEAR=1` to use the naive fallback. |
1111
| Mixed-GPTQ | `mixed_gptq` | Per-layer mixed-bitwidth GPTQ. Automatically dispatches to Marlin or Exllama kernels based on bit-width and symmetry. |
1212

13+
!!! warning "Rotation-preprocessed models are not supported"
14+
Models quantized after rotation preprocessing (`prepare_rotated_model`) cannot be served with vLLM. vLLM kernels do not apply the online Hadamard transform on `down_proj` inputs that rotation-preprocessed models require for correct inference.
15+
1316
## Installation
1417

1518
vLLM is available as an optional dependency:

0 commit comments

Comments
 (0)