Upload 5 files
Browse files
__main__.py
CHANGED
|
@@ -57,6 +57,29 @@ def _config_from_args(args) -> HPCConfig:
|
|
| 57 |
cfg.quant_format = args.format
|
| 58 |
if args.format == "q2_k":
|
| 59 |
cfg.enable_q4_fallback = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
return cfg.validate()
|
| 61 |
|
| 62 |
|
|
@@ -101,6 +124,7 @@ def _run_gguf(args, cfg: HPCConfig) -> None:
|
|
| 101 |
promote_attn_q4=cfg.promote_attn_q4,
|
| 102 |
quant_format=cfg.quant_format,
|
| 103 |
source_type=ti.type,
|
|
|
|
| 104 |
)
|
| 105 |
tag = tgt if tgt else "COPY"
|
| 106 |
print(
|
|
@@ -254,9 +278,17 @@ def main() -> None:
|
|
| 254 |
parser.add_argument("--no-spectral", action="store_true", help="disable spectral/DC objective")
|
| 255 |
parser.add_argument(
|
| 256 |
"--format",
|
| 257 |
-
choices=("mixed", "mixed_q1", "q1_0", "q4_0", "q2_k"),
|
| 258 |
default=None,
|
| 259 |
-
help="quantization format mode: mixed (Q2_K+Q4_0), mixed_q1 (Q1_0+Q4_0), q1_0 (pure Q1_0), q4_0 (pure Q4_0), or q2_k (pure Q2_K)",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 260 |
)
|
| 261 |
parser.add_argument("--no-q4-fallback", action="store_true", help="disable Q4_0 fallback for non-256-aligned tensors")
|
| 262 |
parser.add_argument("--keep-embd", action="store_true", help="keep token_embd/output.weight at source precision")
|
|
|
|
| 57 |
cfg.quant_format = args.format
|
| 58 |
if args.format == "q2_k":
|
| 59 |
cfg.enable_q4_fallback = False
|
| 60 |
+
if getattr(args, "tensor_type", None):
|
| 61 |
+
custom: dict[str, str] = {}
|
| 62 |
+
for item in args.tensor_type:
|
| 63 |
+
if "=" not in item:
|
| 64 |
+
raise SystemExit(f"--tensor-type expects TYPE=QUANT, got {item!r}")
|
| 65 |
+
key, value = (part.strip() for part in item.split("=", 1))
|
| 66 |
+
if not key:
|
| 67 |
+
raise SystemExit(f"--tensor-type has an empty tensor type: {item!r}")
|
| 68 |
+
canonical = {
|
| 69 |
+
"q1_0": "Q1_0",
|
| 70 |
+
"q2_k": "Q2_K",
|
| 71 |
+
"q4_0": "Q4_0",
|
| 72 |
+
"copy": "Copy",
|
| 73 |
+
"auto": "Auto",
|
| 74 |
+
}
|
| 75 |
+
norm = canonical.get(value.lower())
|
| 76 |
+
if norm is None:
|
| 77 |
+
raise SystemExit(
|
| 78 |
+
f"--tensor-type {key!r} has invalid quant {value!r}; "
|
| 79 |
+
"use Q1_0, Q2_K, Q4_0, Copy, or Auto"
|
| 80 |
+
)
|
| 81 |
+
custom[key] = norm
|
| 82 |
+
cfg.custom_quant_map = custom
|
| 83 |
return cfg.validate()
|
| 84 |
|
| 85 |
|
|
|
|
| 124 |
promote_attn_q4=cfg.promote_attn_q4,
|
| 125 |
quant_format=cfg.quant_format,
|
| 126 |
source_type=ti.type,
|
| 127 |
+
custom_quant_map=cfg.custom_quant_map or None,
|
| 128 |
)
|
| 129 |
tag = tgt if tgt else "COPY"
|
| 130 |
print(
|
|
|
|
| 278 |
parser.add_argument("--no-spectral", action="store_true", help="disable spectral/DC objective")
|
| 279 |
parser.add_argument(
|
| 280 |
"--format",
|
| 281 |
+
choices=("mixed", "mixed_q1", "custom", "q1_0", "q4_0", "q2_k"),
|
| 282 |
default=None,
|
| 283 |
+
help="quantization format mode: mixed (Q2_K+Q4_0), mixed_q1 (Q1_0+Q4_0), custom (per-tensor-type overrides), q1_0 (pure Q1_0), q4_0 (pure Q4_0), or q2_k (pure Q2_K)",
|
| 284 |
+
)
|
| 285 |
+
parser.add_argument(
|
| 286 |
+
"--tensor-type",
|
| 287 |
+
action="append",
|
| 288 |
+
default=[],
|
| 289 |
+
metavar="TYPE=QUANT",
|
| 290 |
+
help="per-tensor-type override, e.g. ffn_up=Q1_0 or attn_output=Q1_0 "
|
| 291 |
+
"(QUANT: Q1_0, Q2_K, Q4_0, Copy, Auto; repeatable)",
|
| 292 |
)
|
| 293 |
parser.add_argument("--no-q4-fallback", action="store_true", help="disable Q4_0 fallback for non-256-aligned tensors")
|
| 294 |
parser.add_argument("--keep-embd", action="store_true", help="keep token_embd/output.weight at source precision")
|
config.py
CHANGED
|
@@ -8,7 +8,7 @@ The defaults are the C engine's production defaults unless marked
|
|
| 8 |
|
| 9 |
from __future__ import annotations
|
| 10 |
|
| 11 |
-
from dataclasses import dataclass
|
| 12 |
|
| 13 |
|
| 14 |
@dataclass(slots=True)
|
|
@@ -69,6 +69,7 @@ class HPCConfig:
|
|
| 69 |
use_candidate_floor: bool = True
|
| 70 |
apply_dc_carry: bool = True
|
| 71 |
quant_format: str = "mixed"
|
|
|
|
| 72 |
enable_q4_fallback: bool = True
|
| 73 |
quantize_embd_q4: bool = True
|
| 74 |
promote_attn_q4: bool = False
|
|
@@ -120,6 +121,15 @@ class HPCConfig:
|
|
| 120 |
raise ValueError("workers must be >= 0 (0 = auto CPU count)")
|
| 121 |
if self.device not in ("auto", "cuda", "cpu") and not self.device.startswith("cuda:"):
|
| 122 |
raise ValueError("device must be 'auto', 'cuda', 'cuda:N', or 'cpu'")
|
| 123 |
-
if self.quant_format not in ("mixed", "mixed_q1", "q1_0", "q4_0", "q2_k"):
|
| 124 |
-
raise ValueError(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
return self
|
|
|
|
| 8 |
|
| 9 |
from __future__ import annotations
|
| 10 |
|
| 11 |
+
from dataclasses import dataclass, field
|
| 12 |
|
| 13 |
|
| 14 |
@dataclass(slots=True)
|
|
|
|
| 69 |
use_candidate_floor: bool = True
|
| 70 |
apply_dc_carry: bool = True
|
| 71 |
quant_format: str = "mixed"
|
| 72 |
+
custom_quant_map: dict[str, str] = field(default_factory=dict)
|
| 73 |
enable_q4_fallback: bool = True
|
| 74 |
quantize_embd_q4: bool = True
|
| 75 |
promote_attn_q4: bool = False
|
|
|
|
| 121 |
raise ValueError("workers must be >= 0 (0 = auto CPU count)")
|
| 122 |
if self.device not in ("auto", "cuda", "cpu") and not self.device.startswith("cuda:"):
|
| 123 |
raise ValueError("device must be 'auto', 'cuda', 'cuda:N', or 'cpu'")
|
| 124 |
+
if self.quant_format not in ("mixed", "mixed_q1", "custom", "q1_0", "q4_0", "q2_k"):
|
| 125 |
+
raise ValueError(
|
| 126 |
+
"quant_format must be 'mixed', 'mixed_q1', 'custom', 'q1_0', 'q4_0', or 'q2_k'"
|
| 127 |
+
)
|
| 128 |
+
allowed_custom = {"q1_0", "q2_k", "q4_0", "copy", "auto"}
|
| 129 |
+
for tensor_type, quant in self.custom_quant_map.items():
|
| 130 |
+
if str(quant).strip().lower() not in allowed_custom:
|
| 131 |
+
raise ValueError(
|
| 132 |
+
f"custom_quant_map[{tensor_type!r}] must be one of "
|
| 133 |
+
f"Q1_0, Q2_K, Q4_0, Copy, Auto, got {quant!r}"
|
| 134 |
+
)
|
| 135 |
return self
|
gguf.py
CHANGED
|
@@ -14,6 +14,7 @@ packed by an external GGUF writer.
|
|
| 14 |
|
| 15 |
from __future__ import annotations
|
| 16 |
|
|
|
|
| 17 |
import struct
|
| 18 |
import time
|
| 19 |
from dataclasses import dataclass
|
|
@@ -412,6 +413,18 @@ def expand_importance(
|
|
| 412 |
)
|
| 413 |
|
| 414 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 415 |
def _is_skipped_metadata_tensor(name: str, dims: tuple[int, ...]) -> bool:
|
| 416 |
if len(dims) < 2:
|
| 417 |
return True
|
|
@@ -539,6 +552,7 @@ def target_quant_type(
|
|
| 539 |
promote_attn_q4: bool = False,
|
| 540 |
quant_format: str = "mixed",
|
| 541 |
source_type: int | None = None,
|
|
|
|
| 542 |
) -> str | None:
|
| 543 |
"""Determine target quantization format ('Q1_0', 'Q2_K', 'Q4_0', or None for Copy).
|
| 544 |
|
|
@@ -547,15 +561,39 @@ def target_quant_type(
|
|
| 547 |
tensors (and embeddings/head if quantize_embd_q4) -> Q4_0.
|
| 548 |
* 'mixed_q1': FFN/expert 128-aligned tensors -> Q1_0; attention/SSM projections,
|
| 549 |
embeddings/head, and non-128-aligned 32-aligned tensors -> Q4_0.
|
|
|
|
|
|
|
| 550 |
* 'q4_0': all 32-aligned eligible weight tensors -> Q4_0.
|
| 551 |
* 'q2_k': only Q2_K-eligible tensors -> Q2_K; everything else copied.
|
| 552 |
* 'q1_0': all 128-aligned eligible weight tensors -> Q1_0.
|
| 553 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 554 |
Never promotes a lower-bit source tensor to a higher-bit target format
|
| 555 |
(e.g., a Q2_K source tensor is never re-quantized to Q4_0).
|
| 556 |
"""
|
|
|
|
| 557 |
tgt: str | None = None
|
| 558 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 559 |
if promote_attn_q4 and _is_attention_projection(name) and is_q4_0_eligible(
|
| 560 |
name, dims, include_embd=quantize_embd_q4, promote_attn=True, allow_q2k_tensors=True
|
| 561 |
):
|
|
@@ -570,7 +608,7 @@ def target_quant_type(
|
|
| 570 |
name, dims, include_embd=quantize_embd_q4, promote_attn=True, allow_q2k_tensors=True
|
| 571 |
):
|
| 572 |
tgt = "Q4_0"
|
| 573 |
-
elif
|
| 574 |
if (_is_attention_projection(name) or _is_tiny_ssm_gate(name, dims)) and is_q4_0_eligible(
|
| 575 |
name, dims, include_embd=quantize_embd_q4, promote_attn=True, allow_q2k_tensors=True
|
| 576 |
):
|
|
@@ -584,12 +622,12 @@ def target_quant_type(
|
|
| 584 |
name, dims, include_embd=quantize_embd_q4, promote_attn=True, allow_q2k_tensors=True
|
| 585 |
):
|
| 586 |
tgt = "Q4_0"
|
| 587 |
-
elif
|
| 588 |
if is_q4_0_eligible(
|
| 589 |
name, dims, include_embd=quantize_embd_q4, allow_q2k_tensors=True
|
| 590 |
):
|
| 591 |
tgt = "Q4_0"
|
| 592 |
-
elif
|
| 593 |
if is_q2k_eligible(name, dims):
|
| 594 |
tgt = "Q2_K"
|
| 595 |
else:
|
|
@@ -649,6 +687,7 @@ def write_quantized_gguf(
|
|
| 649 |
embd_q4 = bool(getattr(cfg, "quantize_embd_q4", True))
|
| 650 |
attn_q4 = bool(getattr(cfg, "promote_attn_q4", False))
|
| 651 |
q_format = str(getattr(cfg, "quant_format", "mixed"))
|
|
|
|
| 652 |
|
| 653 |
plan: list[str | None] = []
|
| 654 |
quantized_count = 0
|
|
@@ -664,8 +703,16 @@ def write_quantized_gguf(
|
|
| 664 |
promote_attn_q4=attn_q4,
|
| 665 |
quant_format=q_format,
|
| 666 |
source_type=ti.type,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 667 |
)
|
| 668 |
-
if target_fmt is None:
|
| 669 |
src_bpw = source_type_bpw(ti.type)
|
| 670 |
if q_format == "q1_0" and len(ti.dims) >= 2 and ti.dims[0] % QK1_0 == 0 and src_bpw > 1.125:
|
| 671 |
target_fmt = "Q1_0"
|
|
@@ -686,6 +733,7 @@ def write_quantized_gguf(
|
|
| 686 |
promote_attn_q4=attn_q4,
|
| 687 |
quant_format=q_format,
|
| 688 |
source_type=ti.type,
|
|
|
|
| 689 |
)
|
| 690 |
if target_fmt and ti.type not in SUPPORTED_READ_TYPES:
|
| 691 |
if verbose:
|
|
|
|
| 14 |
|
| 15 |
from __future__ import annotations
|
| 16 |
|
| 17 |
+
import re
|
| 18 |
import struct
|
| 19 |
import time
|
| 20 |
from dataclasses import dataclass
|
|
|
|
| 413 |
)
|
| 414 |
|
| 415 |
|
| 416 |
+
_BLOCK_PREFIX_RE = re.compile(r"^blk\.\d+\.")
|
| 417 |
+
|
| 418 |
+
|
| 419 |
+
def tensor_type_key(name: str) -> str:
|
| 420 |
+
"""Return the layer-independent tensor type (e.g. ``blk.4.ffn_up.weight`` -> ``ffn_up``)."""
|
| 421 |
+
key = _BLOCK_PREFIX_RE.sub("", name)
|
| 422 |
+
for suffix in (".weight", ".bias"):
|
| 423 |
+
if key.endswith(suffix):
|
| 424 |
+
return key[: -len(suffix)]
|
| 425 |
+
return key
|
| 426 |
+
|
| 427 |
+
|
| 428 |
def _is_skipped_metadata_tensor(name: str, dims: tuple[int, ...]) -> bool:
|
| 429 |
if len(dims) < 2:
|
| 430 |
return True
|
|
|
|
| 552 |
promote_attn_q4: bool = False,
|
| 553 |
quant_format: str = "mixed",
|
| 554 |
source_type: int | None = None,
|
| 555 |
+
custom_quant_map: dict[str, str] | None = None,
|
| 556 |
) -> str | None:
|
| 557 |
"""Determine target quantization format ('Q1_0', 'Q2_K', 'Q4_0', or None for Copy).
|
| 558 |
|
|
|
|
| 561 |
tensors (and embeddings/head if quantize_embd_q4) -> Q4_0.
|
| 562 |
* 'mixed_q1': FFN/expert 128-aligned tensors -> Q1_0; attention/SSM projections,
|
| 563 |
embeddings/head, and non-128-aligned 32-aligned tensors -> Q4_0.
|
| 564 |
+
* 'custom': per-tensor-type overrides from ``custom_quant_map``; unlisted
|
| 565 |
+
tensor types fall back to the 'mixed' plan.
|
| 566 |
* 'q4_0': all 32-aligned eligible weight tensors -> Q4_0.
|
| 567 |
* 'q2_k': only Q2_K-eligible tensors -> Q2_K; everything else copied.
|
| 568 |
* 'q1_0': all 128-aligned eligible weight tensors -> Q1_0.
|
| 569 |
|
| 570 |
+
``custom_quant_map`` maps a layer-independent tensor type (see
|
| 571 |
+
:func:`tensor_type_key`) to 'Q1_0', 'Q2_K', 'Q4_0', 'Copy', or 'Auto'.
|
| 572 |
+
Non-Auto entries override the selected mode for that tensor type. A target
|
| 573 |
+
is applied only when the tensor is actually eligible for it; explicit
|
| 574 |
+
'Copy' (and ineligible targets) leave the tensor at its source precision.
|
| 575 |
+
|
| 576 |
Never promotes a lower-bit source tensor to a higher-bit target format
|
| 577 |
(e.g., a Q2_K source tensor is never re-quantized to Q4_0).
|
| 578 |
"""
|
| 579 |
+
fmt = "mixed" if quant_format == "custom" else quant_format
|
| 580 |
tgt: str | None = None
|
| 581 |
+
custom_choice = None
|
| 582 |
+
if custom_quant_map:
|
| 583 |
+
custom_choice = custom_quant_map.get(tensor_type_key(name))
|
| 584 |
+
if custom_choice is not None and str(custom_choice).strip().lower() not in ("", "auto"):
|
| 585 |
+
choice = str(custom_choice).strip().upper()
|
| 586 |
+
if choice == "Q1_0":
|
| 587 |
+
if is_q1_0_eligible(name, dims, include_embd=True):
|
| 588 |
+
tgt = "Q1_0"
|
| 589 |
+
elif choice == "Q2_K":
|
| 590 |
+
if is_q2k_eligible(name, dims):
|
| 591 |
+
tgt = "Q2_K"
|
| 592 |
+
elif choice == "Q4_0":
|
| 593 |
+
if is_q4_0_eligible(name, dims, include_embd=True, allow_q2k_tensors=True):
|
| 594 |
+
tgt = "Q4_0"
|
| 595 |
+
# 'COPY', unknown values, and ineligible targets leave tgt as None.
|
| 596 |
+
elif fmt == "q1_0":
|
| 597 |
if promote_attn_q4 and _is_attention_projection(name) and is_q4_0_eligible(
|
| 598 |
name, dims, include_embd=quantize_embd_q4, promote_attn=True, allow_q2k_tensors=True
|
| 599 |
):
|
|
|
|
| 608 |
name, dims, include_embd=quantize_embd_q4, promote_attn=True, allow_q2k_tensors=True
|
| 609 |
):
|
| 610 |
tgt = "Q4_0"
|
| 611 |
+
elif fmt == "mixed_q1":
|
| 612 |
if (_is_attention_projection(name) or _is_tiny_ssm_gate(name, dims)) and is_q4_0_eligible(
|
| 613 |
name, dims, include_embd=quantize_embd_q4, promote_attn=True, allow_q2k_tensors=True
|
| 614 |
):
|
|
|
|
| 622 |
name, dims, include_embd=quantize_embd_q4, promote_attn=True, allow_q2k_tensors=True
|
| 623 |
):
|
| 624 |
tgt = "Q4_0"
|
| 625 |
+
elif fmt == "q4_0":
|
| 626 |
if is_q4_0_eligible(
|
| 627 |
name, dims, include_embd=quantize_embd_q4, allow_q2k_tensors=True
|
| 628 |
):
|
| 629 |
tgt = "Q4_0"
|
| 630 |
+
elif fmt == "q2_k":
|
| 631 |
if is_q2k_eligible(name, dims):
|
| 632 |
tgt = "Q2_K"
|
| 633 |
else:
|
|
|
|
| 687 |
embd_q4 = bool(getattr(cfg, "quantize_embd_q4", True))
|
| 688 |
attn_q4 = bool(getattr(cfg, "promote_attn_q4", False))
|
| 689 |
q_format = str(getattr(cfg, "quant_format", "mixed"))
|
| 690 |
+
custom_map = getattr(cfg, "custom_quant_map", None) or None
|
| 691 |
|
| 692 |
plan: list[str | None] = []
|
| 693 |
quantized_count = 0
|
|
|
|
| 703 |
promote_attn_q4=attn_q4,
|
| 704 |
quant_format=q_format,
|
| 705 |
source_type=ti.type,
|
| 706 |
+
custom_quant_map=custom_map,
|
| 707 |
+
)
|
| 708 |
+
custom_choice = None
|
| 709 |
+
if custom_map:
|
| 710 |
+
custom_choice = custom_map.get(tensor_type_key(ti.name))
|
| 711 |
+
explicit_custom = (
|
| 712 |
+
custom_choice is not None
|
| 713 |
+
and str(custom_choice).strip().lower() not in ("", "auto")
|
| 714 |
)
|
| 715 |
+
if target_fmt is None and not explicit_custom:
|
| 716 |
src_bpw = source_type_bpw(ti.type)
|
| 717 |
if q_format == "q1_0" and len(ti.dims) >= 2 and ti.dims[0] % QK1_0 == 0 and src_bpw > 1.125:
|
| 718 |
target_fmt = "Q1_0"
|
|
|
|
| 733 |
promote_attn_q4=attn_q4,
|
| 734 |
quant_format=q_format,
|
| 735 |
source_type=ti.type,
|
| 736 |
+
custom_quant_map=custom_map,
|
| 737 |
)
|
| 738 |
if target_fmt and ti.type not in SUPPORTED_READ_TYPES:
|
| 739 |
if verbose:
|
gui.py
CHANGED
|
@@ -61,10 +61,39 @@ from hpc_quantize_v2 import (
|
|
| 61 |
quantize_tensor_q4_0,
|
| 62 |
read_imatrix,
|
| 63 |
)
|
| 64 |
-
from hpc_quantize_v2.gguf import
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
from hpc_quantize_v2.reference import reference_solve, synthetic_cases
|
| 66 |
|
| 67 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
def _ts() -> str:
|
| 69 |
return dt.datetime.now().strftime("%H:%M:%S.%f")[:-3]
|
| 70 |
|
|
@@ -100,6 +129,8 @@ class HPCQuantizeGUI:
|
|
| 100 |
self._init_vars()
|
| 101 |
self._apply_theme()
|
| 102 |
self._build_layout()
|
|
|
|
|
|
|
| 103 |
self._auto_populate_defaults()
|
| 104 |
self._update_cli_preview()
|
| 105 |
self.root.after(80, self._poll_queue)
|
|
@@ -133,8 +164,9 @@ class HPCQuantizeGUI:
|
|
| 133 |
self.q4_fallback_var = tk.BooleanVar(value=True)
|
| 134 |
self.embd_q4_var = tk.BooleanVar(value=True)
|
| 135 |
self.promote_attn_q4_var = tk.BooleanVar(value=False)
|
| 136 |
-
self.quant_format_var = tk.StringVar(value="
|
| 137 |
self.plan_filter_var = tk.StringVar(value="ALL")
|
|
|
|
| 138 |
|
| 139 |
self.dc_lambda_var = tk.DoubleVar(value=1.0)
|
| 140 |
self.vw_lambda_var = tk.DoubleVar(value=1.0)
|
|
@@ -310,8 +342,31 @@ class HPCQuantizeGUI:
|
|
| 310 |
command=self._on_format_changed,
|
| 311 |
).pack(side=tk.LEFT, padx=3)
|
| 312 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 313 |
opt_row = ttk.Frame(opt_frame)
|
| 314 |
opt_row.pack(fill=tk.X, pady=(0, 4))
|
|
|
|
| 315 |
ttk.Label(opt_row, text="Global Optimizer:").pack(side=tk.LEFT, padx=(0, 8))
|
| 316 |
ttk.Radiobutton(
|
| 317 |
opt_row, text="HPC (Sieve + 36-State Viterbi)", variable=self.optimizer_var, value="hpc"
|
|
@@ -566,6 +621,8 @@ class HPCQuantizeGUI:
|
|
| 566 |
return "-Q1_0.gguf"
|
| 567 |
if fmt == "mixed_q1":
|
| 568 |
return "-Q1_0-Q4_0.gguf"
|
|
|
|
|
|
|
| 569 |
if fmt == "q4_0":
|
| 570 |
return "-Q4_0.gguf"
|
| 571 |
return "-Q2K.gguf"
|
|
@@ -575,11 +632,96 @@ class HPCQuantizeGUI:
|
|
| 575 |
gguf_p = self.gguf_var.get().strip()
|
| 576 |
if gguf_p and (
|
| 577 |
not cur_out
|
| 578 |
-
or any(
|
|
|
|
|
|
|
|
|
|
| 579 |
):
|
| 580 |
p = Path(gguf_p)
|
| 581 |
self.output_var.set(str(p.with_name(f"{p.stem}{self._default_out_suffix()}")))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 582 |
self._populate_tensor_tree()
|
|
|
|
| 583 |
|
| 584 |
def _browse_gguf(self) -> None:
|
| 585 |
path = filedialog.askopenfilename(
|
|
@@ -636,6 +778,7 @@ class HPCQuantizeGUI:
|
|
| 636 |
dc_decay=float(self.dc_decay_var.get()),
|
| 637 |
sse_budget=float(self.sse_budget_var.get()),
|
| 638 |
quant_format=self.quant_format_var.get(),
|
|
|
|
| 639 |
enable_q4_fallback=bool(self.q4_fallback_var.get()) and self.quant_format_var.get() != "q2_k",
|
| 640 |
quantize_embd_q4=bool(self.embd_q4_var.get()),
|
| 641 |
promote_attn_q4=bool(self.promote_attn_q4_var.get()),
|
|
@@ -664,6 +807,10 @@ class HPCQuantizeGUI:
|
|
| 664 |
if self.limit_var.get() > 0:
|
| 665 |
cmd += ["--limit", str(self.limit_var.get())]
|
| 666 |
cmd += ["--format", self.quant_format_var.get()]
|
|
|
|
|
|
|
|
|
|
|
|
|
| 667 |
cmd += ["--device", self.device_var.get()]
|
| 668 |
cmd += ["--workers", str(self.workers_var.get())]
|
| 669 |
cmd += ["--cand-d", str(self.cand_d_var.get()), "--cand-m", str(self.cand_m_var.get())]
|
|
@@ -709,6 +856,8 @@ class HPCQuantizeGUI:
|
|
| 709 |
return
|
| 710 |
try:
|
| 711 |
self._reader = GGUFReader(gguf_path)
|
|
|
|
|
|
|
| 712 |
self._populate_tensor_tree()
|
| 713 |
except Exception as exc:
|
| 714 |
self._reader = None
|
|
@@ -731,6 +880,7 @@ class HPCQuantizeGUI:
|
|
| 731 |
promote_attn_q4=bool(self.promote_attn_q4_var.get()),
|
| 732 |
quant_format=self.quant_format_var.get(),
|
| 733 |
source_type=ti.type,
|
|
|
|
| 734 |
)
|
| 735 |
if tgt == "Q2_K":
|
| 736 |
n_q2 += 1
|
|
@@ -793,6 +943,7 @@ class HPCQuantizeGUI:
|
|
| 793 |
promote_attn_q4=bool(self.promote_attn_q4_var.get()),
|
| 794 |
quant_format=self.quant_format_var.get(),
|
| 795 |
source_type=ti.type,
|
|
|
|
| 796 |
)
|
| 797 |
is not None
|
| 798 |
and self.tree.exists(ti.name)
|
|
@@ -884,7 +1035,17 @@ class HPCQuantizeGUI:
|
|
| 884 |
raw = lookup_imatrix(imat, tname)
|
| 885 |
if raw is not None:
|
| 886 |
imp = expand_importance(raw, x.size, row)
|
| 887 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 888 |
raw_q1, nb_q1, rmse_q1 = quantize_tensor_q1_0(x, importance=imp, cfg=cfg)
|
| 889 |
np.savez_compressed(
|
| 890 |
out_path,
|
|
@@ -896,7 +1057,7 @@ class HPCQuantizeGUI:
|
|
| 896 |
("done", f"Saved Q1_0 {out_path} (RMSE={rmse_q1:.6e}, {time.time() - t0:.1f}s)")
|
| 897 |
)
|
| 898 |
return
|
| 899 |
-
if
|
| 900 |
raw_q4, nb_q4, rmse_q4 = quantize_tensor_q4_0(x, importance=imp, cfg=cfg)
|
| 901 |
np.savez_compressed(
|
| 902 |
out_path,
|
|
@@ -908,6 +1069,8 @@ class HPCQuantizeGUI:
|
|
| 908 |
("done", f"Saved Q4_0 {out_path} (RMSE={rmse_q4:.6e}, {time.time() - t0:.1f}s)")
|
| 909 |
)
|
| 910 |
return
|
|
|
|
|
|
|
| 911 |
res = quantizer.quantize(
|
| 912 |
x,
|
| 913 |
importance=imp,
|
|
|
|
| 61 |
quantize_tensor_q4_0,
|
| 62 |
read_imatrix,
|
| 63 |
)
|
| 64 |
+
from hpc_quantize_v2.gguf import (
|
| 65 |
+
is_q1_0_eligible,
|
| 66 |
+
is_q2k_eligible,
|
| 67 |
+
is_q4_0_eligible,
|
| 68 |
+
target_quant_type,
|
| 69 |
+
tensor_type_key,
|
| 70 |
+
write_quantized_gguf,
|
| 71 |
+
)
|
| 72 |
from hpc_quantize_v2.reference import reference_solve, synthetic_cases
|
| 73 |
|
| 74 |
|
| 75 |
+
DEFAULT_CUSTOM_RECIPE: dict[str, str] = {
|
| 76 |
+
# Q4_0: quality-critical embeddings, attention, and SSM projections
|
| 77 |
+
"token_embd": "Q4_0",
|
| 78 |
+
"output": "Q4_0",
|
| 79 |
+
"attn_gate": "Q4_0",
|
| 80 |
+
"ssm_out": "Q4_0",
|
| 81 |
+
"attn_q": "Q4_0",
|
| 82 |
+
"attn_k": "Q4_0",
|
| 83 |
+
"attn_v": "Q4_0",
|
| 84 |
+
# Q1_0: the one tensor type that tolerates it well
|
| 85 |
+
"attn_output": "Q1_0",
|
| 86 |
+
# Q2_K: the remaining compute-heavy tensors
|
| 87 |
+
"attn_qkv": "Q2_K",
|
| 88 |
+
"ffn_up": "Q2_K",
|
| 89 |
+
"ffn_gate": "Q2_K",
|
| 90 |
+
"ffn_down": "Q2_K",
|
| 91 |
+
"ssm_alpha": "Q2_K",
|
| 92 |
+
"ssm_beta": "Q2_K",
|
| 93 |
+
"nextn.eh_proj": "Q2_K",
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
|
| 97 |
def _ts() -> str:
|
| 98 |
return dt.datetime.now().strftime("%H:%M:%S.%f")[:-3]
|
| 99 |
|
|
|
|
| 129 |
self._init_vars()
|
| 130 |
self._apply_theme()
|
| 131 |
self._build_layout()
|
| 132 |
+
if self.quant_format_var.get() == "custom":
|
| 133 |
+
self._on_format_changed()
|
| 134 |
self._auto_populate_defaults()
|
| 135 |
self._update_cli_preview()
|
| 136 |
self.root.after(80, self._poll_queue)
|
|
|
|
| 164 |
self.q4_fallback_var = tk.BooleanVar(value=True)
|
| 165 |
self.embd_q4_var = tk.BooleanVar(value=True)
|
| 166 |
self.promote_attn_q4_var = tk.BooleanVar(value=False)
|
| 167 |
+
self.quant_format_var = tk.StringVar(value="custom")
|
| 168 |
self.plan_filter_var = tk.StringVar(value="ALL")
|
| 169 |
+
self.custom_type_vars: dict[str, tk.StringVar] = {}
|
| 170 |
|
| 171 |
self.dc_lambda_var = tk.DoubleVar(value=1.0)
|
| 172 |
self.vw_lambda_var = tk.DoubleVar(value=1.0)
|
|
|
|
| 342 |
command=self._on_format_changed,
|
| 343 |
).pack(side=tk.LEFT, padx=3)
|
| 344 |
|
| 345 |
+
custom_row = ttk.Frame(opt_frame)
|
| 346 |
+
custom_row.pack(fill=tk.X, pady=(0, 4))
|
| 347 |
+
ttk.Radiobutton(
|
| 348 |
+
custom_row,
|
| 349 |
+
text="Custom (per tensor type)",
|
| 350 |
+
variable=self.quant_format_var,
|
| 351 |
+
value="custom",
|
| 352 |
+
command=self._on_format_changed,
|
| 353 |
+
).pack(side=tk.LEFT, padx=4)
|
| 354 |
+
ttk.Label(
|
| 355 |
+
custom_row,
|
| 356 |
+
text="Override the quant type per tensor category (Auto = mixed plan).",
|
| 357 |
+
).pack(side=tk.LEFT, padx=6)
|
| 358 |
+
|
| 359 |
+
self.custom_frame = ttk.LabelFrame(opt_frame, text="Custom Tensor-Type Targets", padding=6)
|
| 360 |
+
self.custom_body = ttk.Frame(self.custom_frame)
|
| 361 |
+
self.custom_body.pack(fill=tk.X)
|
| 362 |
+
self.custom_hint_var = tk.StringVar(value="Load a source GGUF to list tensor types.")
|
| 363 |
+
ttk.Label(
|
| 364 |
+
self.custom_frame, textvariable=self.custom_hint_var, style="TeleKey.TLabel"
|
| 365 |
+
).pack(anchor=tk.W, pady=(4, 0))
|
| 366 |
+
|
| 367 |
opt_row = ttk.Frame(opt_frame)
|
| 368 |
opt_row.pack(fill=tk.X, pady=(0, 4))
|
| 369 |
+
self._custom_anchor = opt_row
|
| 370 |
ttk.Label(opt_row, text="Global Optimizer:").pack(side=tk.LEFT, padx=(0, 8))
|
| 371 |
ttk.Radiobutton(
|
| 372 |
opt_row, text="HPC (Sieve + 36-State Viterbi)", variable=self.optimizer_var, value="hpc"
|
|
|
|
| 621 |
return "-Q1_0.gguf"
|
| 622 |
if fmt == "mixed_q1":
|
| 623 |
return "-Q1_0-Q4_0.gguf"
|
| 624 |
+
if fmt == "custom":
|
| 625 |
+
return "-Custom.gguf"
|
| 626 |
if fmt == "q4_0":
|
| 627 |
return "-Q4_0.gguf"
|
| 628 |
return "-Q2K.gguf"
|
|
|
|
| 632 |
gguf_p = self.gguf_var.get().strip()
|
| 633 |
if gguf_p and (
|
| 634 |
not cur_out
|
| 635 |
+
or any(
|
| 636 |
+
cur_out.endswith(sfx)
|
| 637 |
+
for sfx in ("-Q2K.gguf", "-Q1_0.gguf", "-Q1_0-Q4_0.gguf", "-Custom.gguf", "-Q4_0.gguf")
|
| 638 |
+
)
|
| 639 |
):
|
| 640 |
p = Path(gguf_p)
|
| 641 |
self.output_var.set(str(p.with_name(f"{p.stem}{self._default_out_suffix()}")))
|
| 642 |
+
if self.quant_format_var.get() == "custom":
|
| 643 |
+
self.custom_frame.pack(fill=tk.X, pady=(0, 4), before=self._custom_anchor)
|
| 644 |
+
self._populate_custom_tensor_rows()
|
| 645 |
+
else:
|
| 646 |
+
self.custom_frame.pack_forget()
|
| 647 |
+
self._populate_tensor_tree()
|
| 648 |
+
|
| 649 |
+
def _custom_quant_map(self) -> dict[str, str]:
|
| 650 |
+
return {
|
| 651 |
+
key: var.get()
|
| 652 |
+
for key, var in self.custom_type_vars.items()
|
| 653 |
+
if var.get() not in ("", "Auto")
|
| 654 |
+
}
|
| 655 |
+
|
| 656 |
+
def _custom_map_for_plan(self) -> dict[str, str] | None:
|
| 657 |
+
if self.quant_format_var.get() != "custom":
|
| 658 |
+
return None
|
| 659 |
+
return self._custom_quant_map() or None
|
| 660 |
+
|
| 661 |
+
def _populate_custom_tensor_rows(self) -> None:
|
| 662 |
+
if not hasattr(self, "custom_body"):
|
| 663 |
+
return
|
| 664 |
+
for child in self.custom_body.winfo_children():
|
| 665 |
+
child.destroy()
|
| 666 |
+
|
| 667 |
+
if self._reader is None:
|
| 668 |
+
self.custom_hint_var.set("Load a source GGUF to list tensor types.")
|
| 669 |
+
self.custom_type_vars = {}
|
| 670 |
+
return
|
| 671 |
+
|
| 672 |
+
groups: dict[str, list] = {}
|
| 673 |
+
for ti in self._reader.tensors:
|
| 674 |
+
groups.setdefault(tensor_type_key(ti.name), []).append(ti)
|
| 675 |
+
|
| 676 |
+
previous = {key: var.get() for key, var in self.custom_type_vars.items()}
|
| 677 |
+
self.custom_type_vars = {}
|
| 678 |
+
rows = 0
|
| 679 |
+
for key in sorted(groups):
|
| 680 |
+
tensors = groups[key]
|
| 681 |
+
options = ["Auto", "Copy"]
|
| 682 |
+
if any(is_q1_0_eligible(t.name, t.dims) for t in tensors):
|
| 683 |
+
options.append("Q1_0")
|
| 684 |
+
if any(is_q2k_eligible(t.name, t.dims) for t in tensors):
|
| 685 |
+
options.append("Q2_K")
|
| 686 |
+
if any(
|
| 687 |
+
is_q4_0_eligible(t.name, t.dims, include_embd=True, allow_q2k_tensors=True)
|
| 688 |
+
for t in tensors
|
| 689 |
+
):
|
| 690 |
+
options.append("Q4_0")
|
| 691 |
+
if len(options) == 2:
|
| 692 |
+
continue
|
| 693 |
+
var = tk.StringVar(
|
| 694 |
+
value=previous.get(key, DEFAULT_CUSTOM_RECIPE.get(key, "Auto"))
|
| 695 |
+
)
|
| 696 |
+
if var.get() not in options:
|
| 697 |
+
var.set("Auto")
|
| 698 |
+
var.trace_add(
|
| 699 |
+
"write",
|
| 700 |
+
lambda *_a: self._on_custom_type_changed(),
|
| 701 |
+
)
|
| 702 |
+
self.custom_type_vars[key] = var
|
| 703 |
+
r, c = divmod(rows, 2)
|
| 704 |
+
ttk.Label(self.custom_body, text=key, width=18, anchor=tk.W).grid(
|
| 705 |
+
row=r, column=c * 2, sticky=tk.W, padx=(0, 4), pady=1
|
| 706 |
+
)
|
| 707 |
+
ttk.Combobox(
|
| 708 |
+
self.custom_body,
|
| 709 |
+
textvariable=var,
|
| 710 |
+
values=options,
|
| 711 |
+
state="readonly",
|
| 712 |
+
width=8,
|
| 713 |
+
).grid(row=r, column=c * 2 + 1, sticky=tk.W, padx=(0, 12), pady=1)
|
| 714 |
+
rows += 1
|
| 715 |
+
|
| 716 |
+
self.custom_hint_var.set(
|
| 717 |
+
"Quantizable tensor types only; norms/1D tensors and other non-eligible "
|
| 718 |
+
"weights are copied unchanged."
|
| 719 |
+
)
|
| 720 |
+
self._update_cli_preview()
|
| 721 |
+
|
| 722 |
+
def _on_custom_type_changed(self) -> None:
|
| 723 |
self._populate_tensor_tree()
|
| 724 |
+
self._update_cli_preview()
|
| 725 |
|
| 726 |
def _browse_gguf(self) -> None:
|
| 727 |
path = filedialog.askopenfilename(
|
|
|
|
| 778 |
dc_decay=float(self.dc_decay_var.get()),
|
| 779 |
sse_budget=float(self.sse_budget_var.get()),
|
| 780 |
quant_format=self.quant_format_var.get(),
|
| 781 |
+
custom_quant_map=self._custom_map_for_plan() or {},
|
| 782 |
enable_q4_fallback=bool(self.q4_fallback_var.get()) and self.quant_format_var.get() != "q2_k",
|
| 783 |
quantize_embd_q4=bool(self.embd_q4_var.get()),
|
| 784 |
promote_attn_q4=bool(self.promote_attn_q4_var.get()),
|
|
|
|
| 807 |
if self.limit_var.get() > 0:
|
| 808 |
cmd += ["--limit", str(self.limit_var.get())]
|
| 809 |
cmd += ["--format", self.quant_format_var.get()]
|
| 810 |
+
if self.quant_format_var.get() == "custom":
|
| 811 |
+
custom = self._custom_quant_map()
|
| 812 |
+
for key in sorted(custom):
|
| 813 |
+
cmd += ["--tensor-type", f"{key}={custom[key]}"]
|
| 814 |
cmd += ["--device", self.device_var.get()]
|
| 815 |
cmd += ["--workers", str(self.workers_var.get())]
|
| 816 |
cmd += ["--cand-d", str(self.cand_d_var.get()), "--cand-m", str(self.cand_m_var.get())]
|
|
|
|
| 856 |
return
|
| 857 |
try:
|
| 858 |
self._reader = GGUFReader(gguf_path)
|
| 859 |
+
if self.quant_format_var.get() == "custom":
|
| 860 |
+
self._populate_custom_tensor_rows()
|
| 861 |
self._populate_tensor_tree()
|
| 862 |
except Exception as exc:
|
| 863 |
self._reader = None
|
|
|
|
| 880 |
promote_attn_q4=bool(self.promote_attn_q4_var.get()),
|
| 881 |
quant_format=self.quant_format_var.get(),
|
| 882 |
source_type=ti.type,
|
| 883 |
+
custom_quant_map=self._custom_map_for_plan(),
|
| 884 |
)
|
| 885 |
if tgt == "Q2_K":
|
| 886 |
n_q2 += 1
|
|
|
|
| 943 |
promote_attn_q4=bool(self.promote_attn_q4_var.get()),
|
| 944 |
quant_format=self.quant_format_var.get(),
|
| 945 |
source_type=ti.type,
|
| 946 |
+
custom_quant_map=self._custom_map_for_plan(),
|
| 947 |
)
|
| 948 |
is not None
|
| 949 |
and self.tree.exists(ti.name)
|
|
|
|
| 1035 |
raw = lookup_imatrix(imat, tname)
|
| 1036 |
if raw is not None:
|
| 1037 |
imp = expand_importance(raw, x.size, row)
|
| 1038 |
+
tgt = target_quant_type(
|
| 1039 |
+
tname,
|
| 1040 |
+
ti.dims,
|
| 1041 |
+
enable_q4_fallback=bool(getattr(cfg, "enable_q4_fallback", True)),
|
| 1042 |
+
quantize_embd_q4=bool(getattr(cfg, "quantize_embd_q4", True)),
|
| 1043 |
+
promote_attn_q4=bool(getattr(cfg, "promote_attn_q4", False)),
|
| 1044 |
+
quant_format=str(getattr(cfg, "quant_format", "mixed")),
|
| 1045 |
+
source_type=ti.type,
|
| 1046 |
+
custom_quant_map=getattr(cfg, "custom_quant_map", None) or None,
|
| 1047 |
+
)
|
| 1048 |
+
if tgt == "Q1_0":
|
| 1049 |
raw_q1, nb_q1, rmse_q1 = quantize_tensor_q1_0(x, importance=imp, cfg=cfg)
|
| 1050 |
np.savez_compressed(
|
| 1051 |
out_path,
|
|
|
|
| 1057 |
("done", f"Saved Q1_0 {out_path} (RMSE={rmse_q1:.6e}, {time.time() - t0:.1f}s)")
|
| 1058 |
)
|
| 1059 |
return
|
| 1060 |
+
if tgt == "Q4_0":
|
| 1061 |
raw_q4, nb_q4, rmse_q4 = quantize_tensor_q4_0(x, importance=imp, cfg=cfg)
|
| 1062 |
np.savez_compressed(
|
| 1063 |
out_path,
|
|
|
|
| 1069 |
("done", f"Saved Q4_0 {out_path} (RMSE={rmse_q4:.6e}, {time.time() - t0:.1f}s)")
|
| 1070 |
)
|
| 1071 |
return
|
| 1072 |
+
if tgt != "Q2_K":
|
| 1073 |
+
raise ValueError(f"{tname}: target {tgt!r} is not quantizable to .npz")
|
| 1074 |
res = quantizer.quantize(
|
| 1075 |
x,
|
| 1076 |
importance=imp,
|