TwilightTechie's picture
Publish experimental commitment-classifier pilot
f0e7192 verified
Raw History Blame Contribute Delete
3.89 kB
"""Inspect the effective task head and verify its adapter/merged storage."""
# ruff: noqa: PLC0415
import hashlib
def _modules(model):
base = model.get_base_model() if hasattr(model, "get_base_model") else model
if type(base).__name__ not in {
"ModernBertForSequenceClassification",
"ModernBertForTokenClassification",
}:
raise ValueError(
"Task-head auditing requires a supported ModernBERT task model"
)
for name in ("head", "classifier"):
module = getattr(base, name)
owned = hasattr(module, "modules_to_save")
if owned:
active = module.active_adapter
if isinstance(active, list):
if len(active) != 1:
raise ValueError("Task-head auditing requires one active adapter")
active = active[0]
module = module.modules_to_save[active]
yield name, module, owned
def tensor_digest(tensor):
import torch
value = tensor.detach().cpu().contiguous()
if not bool(torch.isfinite(value).all()):
raise ValueError("Task head contains non-finite tensors")
return hashlib.sha256(value.view(torch.uint8).numpy().tobytes()).hexdigest()
def task_head_scope(model):
"""Describe actual effective tensors, including deliberately inherited heads."""
tensors = {}
for name, module, owned in _modules(model):
trainable = dict(module.named_parameters())
for key, tensor in module.state_dict().items():
parameter = trainable.get(key)
tensors[f"{name}.{key}"] = {
"shape": list(tensor.shape),
"dtype": str(tensor.dtype),
"numel": tensor.numel(),
"trainable": parameter is not None and parameter.requires_grad,
"adapter_owned": owned,
"sha256": tensor_digest(tensor),
}
base = model.get_base_model() if hasattr(model, "get_base_model") else model
return {
"architecture": type(base).__name__,
"modules_to_save": (
model.peft_config["default"].modules_to_save
if hasattr(model, "get_base_model")
else None
),
"trainable_task_head_parameters": sum(
item["numel"] for item in tensors.values() if item["trainable"]
),
"tensors": tensors,
}
def verify_saved_task_head(model, adapter_path):
"""Reject an adapter that omits or changes an effective trainable task tensor."""
from safetensors.torch import load_file
saved = load_file(str(adapter_path), device="cpu")
scope = task_head_scope(model)
for name, item in scope["tensors"].items():
key = f"base_model.model.{name}"
value = saved.get(key)
item["saved_key"] = key if value is not None else None
if value is None:
if item["trainable"] or item["adapter_owned"]:
raise ValueError(f"Adapter omitted effective task tensor: {name}")
continue
if (
list(value.shape) != item["shape"]
or str(value.dtype) != item["dtype"]
or tensor_digest(value) != item["sha256"]
):
raise ValueError(f"Adapter changed effective task tensor: {name}")
return scope
def assert_task_head_preserved(expected, model):
"""Compare tensor identity across PEFT merge and serialized model reload."""
observed = task_head_scope(model)
identity = ("shape", "dtype", "sha256")
expected_tensors = expected["tensors"]
if expected_tensors.keys() != observed["tensors"].keys():
raise ValueError("Task-head tensor names changed")
for name, item in expected_tensors.items():
if any(item[key] != observed["tensors"][name][key] for key in identity):
raise ValueError(f"Task-head tensor changed: {name}")
return observed