Download scripts/sensitivity_sweep.py from wepiqx/ASHQ1: direct link, hf CLI and curl.
- Browser
- Download file 5.78 kB
-
https://huggingface.co/wepiqx/ASHQ1/resolve/main/scripts/sensitivity_sweep.py
- Command line
-
hf download hf://wepiqx/ASHQ1/scripts/sensitivity_sweep.py
-
curl -L -o sensitivity_sweep.py https://huggingface.co/wepiqx/ASHQ1/resolve/main/scripts/sensitivity_sweep.py
5.78 kB
| #!/usr/bin/env python3 | |
| """ | |
| Sensitivity sweep: vary one tensor class tier, measure PPL. | |
| Usage: python sensitivity_sweep.py --model M.gguf --imatrix I.gguf --class ffn_down --tiers Q3_K,IQ4_XS,Q4_K,Q5_K,Q6_K,Q8_0 | |
| """ | |
| import argparse | |
| import json | |
| import os | |
| import subprocess | |
| import sys | |
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
| from model_reader import read_model | |
| from imatrix_reader import read_imatrix, detect_tied_groups, build_importance_table | |
| from config_generator import generate_flags, format_flags | |
| from quantizer import run_dry_run, run_quantization | |
| from constants import ( | |
| TIER_ORDER, get_tensor_class, strip_weight | |
| ) | |
| REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
| LLAMA_PPL = os.environ.get( | |
| "LLAMA_PPL", "/home/wepiqx/llama.cpp/build/bin/llama-perplexity" | |
| ) | |
| WIKITEXT_DATA = os.environ.get( | |
| "WIKITEXT_DATA", "/mnt/Vsio/wikitext-2-raw/wiki.test.raw" | |
| ) | |
| def parse_tiers(tier_str): | |
| return [t.strip() for t in tier_str.split(",")] | |
| def _tier_index(tier: str) -> int: | |
| return TIER_ORDER.index(tier) if tier in TIER_ORDER else -1 | |
| def build_assignments(model, target_class, tier, base_tier="Q5_K"): | |
| """Manually build assignments: all at base_tier, target_class at tier.""" | |
| model_tensors = model.get("tensors", {}) | |
| all_names = set() | |
| for tname in model_tensors: | |
| norm = strip_weight(tname) | |
| all_names.add(norm) | |
| assignments = {} | |
| for name in all_names: | |
| ttype = name.split(".")[-1] if "." in name else name | |
| cls = get_tensor_class(ttype) | |
| if cls == target_class: | |
| assignments[name] = tier | |
| else: | |
| assignments[name] = base_tier | |
| # Special: norms/ssm_params always F16 | |
| for name in list(assignments.keys()): | |
| ttype = name.split(".")[-1] if "." in name else name | |
| cls = get_tensor_class(ttype) | |
| if cls in ("norms", "ssm_params"): | |
| assignments[name] = "F16" | |
| # MTP special handling | |
| if cls == "mtp": | |
| assignments[name] = base_tier | |
| # Embedding - keep at base_tier | |
| if cls == "embd": | |
| assignments[name] = base_tier | |
| return assignments | |
| def main(): | |
| parser = argparse.ArgumentParser(description="Sensitivity sweep for one tensor class") | |
| parser.add_argument("--model", required=True) | |
| parser.add_argument("--imatrix", required=True) | |
| parser.add_argument("--class", dest="target_class", required=True, | |
| choices=["gate", "attn_proj", "ffn_gate_up", "ffn_down", "mtp", "embd"]) | |
| parser.add_argument("--tiers", default="Q3_K,IQ4_XS,Q4_K,Q5_K,Q6_K,Q8_0") | |
| parser.add_argument("--base-tier", default="Q5_K") | |
| parser.add_argument("--output-dir", default=None, | |
| help="Defaults to <repo>/output/sensitivity_sweep") | |
| parser.add_argument("--skip-ppl", action="store_true", help="Only dry-run, skip PPL") | |
| args = parser.parse_args() | |
| tiers = parse_tiers(args.tiers) | |
| if args.output_dir is None: | |
| args.output_dir = os.path.join(REPO_ROOT, "output", "sensitivity_sweep") | |
| os.makedirs(args.output_dir, exist_ok=True) | |
| print(f"=== Sensitivity sweep: {args.target_class} ===") | |
| print(f"Tiers: {tiers}") | |
| print(f"Base tier for others: {args.base_tier}") | |
| model = read_model(args.model) | |
| imatrix = read_imatrix(args.imatrix) | |
| tied_groups = detect_tied_groups(imatrix) | |
| imp_table = build_importance_table(imatrix, model) | |
| results = [] | |
| for tier in tiers: | |
| print(f"\n--- Testing {args.target_class} = {tier} ---") | |
| assignments = build_assignments(model, args.target_class, tier, args.base_tier) | |
| base_type = "Q5_K_M" | |
| flags = generate_flags(assignments, model, args.base_tier, 100000) | |
| flags["imatrix"] = args.imatrix | |
| # Dry run | |
| dry_size = run_dry_run(flags, args.model) | |
| print(f" Dry-run size: {dry_size:.0f} MiB") | |
| if args.skip_ppl: | |
| results.append({"tier": tier, "size": dry_size, "ppl": None}) | |
| continue | |
| # Quantize (CPU only to avoid OOM) | |
| out_path = os.path.join(args.output_dir, f"{args.target_class}_{tier}.gguf") | |
| print(f" Quantizing to {out_path}...") | |
| # Use CPU for quantization to avoid GPU OOM | |
| success = run_quantization(flags, args.model, out_path) | |
| if not success: | |
| print(f" FAILED") | |
| results.append({"tier": tier, "size": dry_size, "ppl": None, "error": "quant failed"}) | |
| continue | |
| # PPL test (CPU) | |
| print(f" Running PPL test (CPU)...") | |
| cmd = [ | |
| LLAMA_PPL, | |
| "-m", out_path, | |
| "-f", WIKITEXT_DATA, | |
| "-c", "1024", "-ngl", "0" # CPU only | |
| ] | |
| result = subprocess.run(cmd, capture_output=True, text=True, timeout=1800) | |
| ppl = None | |
| for line in result.stdout.split("\n"): | |
| if "Final estimate: PPL" in line: | |
| try: | |
| ppl = float(line.split("PPL = ")[1].split(" ")[0]) | |
| except: | |
| pass | |
| print(f" PPL: {ppl}") | |
| results.append({"tier": tier, "size": dry_size, "ppl": ppl}) | |
| # Cleanup | |
| if os.path.exists(out_path): | |
| os.remove(out_path) | |
| # Summary | |
| print("\n=== RESULTS ===") | |
| print(f"{'Tier':<10} {'Size (MiB)':<12} {'PPL':<10}") | |
| for r in results: | |
| print(f"{r['tier']:<10} {r['size']:<12.0f} {r['ppl'] if r['ppl'] else 'FAILED':<10}") | |
| out_json = os.path.join(args.output_dir, f"sensitivity_{args.target_class}.json") | |
| with open(out_json, "w") as f: | |
| json.dump(results, f, indent=2) | |
| print(f"\nSaved: {out_json}") | |
| if __name__ == "__main__": | |
| main() |