Angshul's picture
Upload 11 files
cdfcfcf verified
Raw History Blame Contribute Delete
4 kB
"""Public, reusable pruning interface for Hugging Face model repositories."""
from __future__ import annotations
import json
from dataclasses import asdict, replace
from pathlib import Path
from typing import Any, Callable, Iterable, Sequence
from huggingface_hub import hf_hub_download
from torch import nn
from .core import PruneConfig, PruneReport, prune
from .hf import prune_hf_model
class HigherOrderPruner:
"""Configuration-backed API for higher-order pruning.
The object contains method defaults, not model weights. ``from_pretrained``
reads those defaults from a Hugging Face *model* repository. The same
instance can prune any compatible model; it is not tied to Qwen or Falcon.
"""
CONFIG_NAME = "pruner_config.json"
def __init__(self, config: PruneConfig | None = None):
self.config = config or PruneConfig(sparsity=0.5)
@classmethod
def from_pretrained(
cls,
repo_id: str,
*,
revision: str = "main",
token: str | bool | None = None,
cache_dir: str | Path | None = None,
local_files_only: bool = False,
) -> "HigherOrderPruner":
path = hf_hub_download(
repo_id=repo_id,
filename=cls.CONFIG_NAME,
revision=revision,
token=token,
cache_dir=cache_dir,
local_files_only=local_files_only,
)
payload = json.loads(Path(path).read_text())
if payload.get("format") != "higher-order-pruner-v1":
raise ValueError("Unsupported pruner configuration format")
values = dict(payload["defaults"])
if "backtracking" in values:
values["backtracking"] = tuple(values["backtracking"])
return cls(PruneConfig(**values))
def save_pretrained(self, directory: str | Path) -> Path:
destination = Path(directory)
destination.mkdir(parents=True, exist_ok=True)
path = destination / self.CONFIG_NAME
path.write_text(json.dumps({
"format": "higher-order-pruner-v1",
"method": "diagonal-higher-order-kfac",
"defaults": asdict(self.config),
}, indent=2) + "\n")
return path
def prune(
self,
model: nn.Module,
calibration_batches: Iterable[Any],
loss_fn: Callable[[nn.Module, Any], Any],
*,
sparsity: float | None = None,
validation_batches: Iterable[Any] | None = None,
module_names: Sequence[str] | None = None,
progress_dir: str | Path | None = None,
on_progress: Callable[[dict[str, Any]], None] | None = None,
) -> PruneReport:
config = self.config if sparsity is None else replace(self.config, sparsity=sparsity)
return prune(
model,
calibration_batches,
loss_fn,
config,
validation_batches=validation_batches,
module_names=module_names,
progress_dir=progress_dir,
on_progress=on_progress,
)
def prune_hf_model(
self,
model_id: str,
calibration_texts: Sequence[str],
*,
output_dir: str | Path,
sparsity: float | None = None,
validation_texts: Sequence[str] | None = None,
revision: str = "main",
sequence_length: int = 256,
module_names: Sequence[str] | None = None,
device: str = "cuda",
on_progress: Callable[[dict], None] | None = None,
) -> dict:
selected = self.config.sparsity if sparsity is None else sparsity
return prune_hf_model(
model_id,
calibration_texts,
validation_texts=validation_texts,
sparsity=selected,
output_dir=output_dir,
revision=revision,
sequence_length=sequence_length,
probes=self.config.probes,
module_names=module_names,
device=device,
on_progress=on_progress,
)