| # HLC-compressed-models |
|
|
| Compressed LLM checkpoints from **Hierarchical Low-Rank Compression for LLMs** (NeurIPS 2026 submission). |
|
|
| ## Available Models |
|
|
| | Model | Ratio | Stage B (SVD) | Stage F (Fine-tuned) | |
| |-------|-------|---------------|----------------------| |
| | LLaMA-7B | 20%-80% | `llama7b/r{02,04,06,08}/B` | `llama7b/r{02,04,06,08}/F` | |
| | Qwen3-14B | 20% | `qwen3_14b/r02/B` | `qwen3_14b/r02/F` | |
|
|
| ## Quick Start — Merged Format |
|
|
| Standard HuggingFace `from_pretrained()`, same size as original model: |
|
|
| ```python |
| from transformers import AutoModelForCausalLM, AutoTokenizer |
| |
| repo = "zhc12/HLC-compressed-models" |
| |
| # Qwen3-14B at 80% compression (fine-tuned) |
| model = AutoModelForCausalLM.from_pretrained(repo, subfolder="qwen3_14b/r02/F") |
| tokenizer = AutoTokenizer.from_pretrained(repo, subfolder="qwen3_14b/r02/F") |
| ``` |
|
|
| ## Factored Format — Low-Rank A, B Matrices |
|
|
| Each `F/` directory also contains `factors.pt` with the low-rank factors |
| `A (d*r)` and `B (r*n)` for every compressed linear layer. This is ~20% |
| smaller than the merged weights and preserves the compression structure. |
|
|
| ### Loading factors (for analysis or continued training) |
|
|
| ```python |
| import torch |
| from transformers import AutoModelForCausalLM, AutoTokenizer |
| from huggingface_hub import hf_hub_download |
| |
| repo = "zhc12/HLC-compressed-models" |
| subfolder = "qwen3_14b/r02/F" |
| |
| # Load the base merged model |
| model = AutoModelForCausalLM.from_pretrained(repo, subfolder=subfolder) |
| tokenizer = AutoTokenizer.from_pretrained(repo, subfolder=subfolder) |
| |
| # Download and load factors |
| factors_path = hf_hub_download(repo, f"{subfolder}/factors.pt") |
| factors = torch.load(factors_path, map_location="cpu", weights_only=True) |
| |
| # factors is a dict: {(layer_idx, sublayer_name): {"A": tensor, "B": tensor}} |
| # Example: factors[(0, "self_attn.q_proj")]["A"].shape = (5120, 2048) |
| print(f"Loaded {len(factors)} factor pairs") |
| ``` |
|
|
| ### Restoring low-rank structure (no external dependencies) |
|
|
| ```python |
| import torch |
| import torch.nn as nn |
| |
| class CompressedLinear(nn.Module): |
| """Low-rank linear: y = A @ (B @ x) + bias, where A is (d, r) and B is (r, n).""" |
| def __init__(self, A, B, bias=None): |
| super().__init__() |
| d, r = A.shape |
| _, n = B.shape |
| self.first = nn.Linear(n, r, bias=False) |
| self.second = nn.Linear(r, d, bias=bias is not None) |
| self.first.weight = nn.Parameter(B) |
| self.second.weight = nn.Parameter(A) |
| if bias is not None: |
| self.second.bias = nn.Parameter(bias) |
| |
| def forward(self, x): |
| return self.second(self.first(x.to(self.first.weight.dtype))).to(x.dtype) |
| |
| # Replace merged layers with factored versions |
| for (layer_idx, sublayer_name), f in factors.items(): |
| layer = model.model.layers[layer_idx] |
| parts = sublayer_name.split(".") |
| parent = layer |
| for p in parts[:-1]: |
| parent = getattr(parent, p) |
| original = getattr(parent, parts[-1]) |
| bias = original.bias.data if original.bias is not None else None |
| compressed = CompressedLinear(f["A"], f["B"], bias=bias) |
| setattr(parent, parts[-1], compressed) |
| |
| # Now each compressed sublayer has .first.weight (B) and .second.weight (A) |
| # Total trainable params = sum of A and B sizes, ~20% fewer than original |
| ``` |
|
|
| ### Compression details |
|
|
| - **Method**: SVD-LLM whitening + mixed calibration (4096 samples, seqlen=2048) |
| - **Compressed sublayers** (7 per transformer block): |
| `q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj` |
| - **Rank formula**: `r = (1 - ratio) * d * n / (d + n)` |
| - **Stage B**: Per-matrix whitened SVD truncation |
| - **Stage F**: End-to-end LM-loss refinement of A, B factors |
|
|
| ## License |
|
|
| Apache 2.0 |
|
|