CERTA-BioMistral-7B-LLMINT8 / certa_handler.py
lazem's picture
CERTA wrapper (no weights) + license/attribution
27df5b2 verified
Raw History Blame Contribute Delete
4.15 kB
#!/usr/bin/env python3
"""CERTA certify-or-escalate inference handler (weights-aware).
Load behaviour:
* If the repository contains baked quantized weights (`*.safetensors`), load them **directly** from the
repo folder. A model saved after bitsandbytes NF4/INT8 quantization carries its `quantization_config`
in `config.json`, so `from_pretrained(path)` reconstructs the quantized model without re-downloading or
re-quantizing the base.
* Otherwise fall back to the wrapper form: download the base model named in `certa_config.json` and
quantize it at load time.
For each accept/reject decision it serves the quantized decision only when its margin clears the CERTA
threshold for its predicted class at the requested risk level alpha; otherwise it returns escalate=True
(refer the decision to the full-precision reference). Requires a CUDA GPU + bitsandbytes for NF4/INT8.
Scope: the thresholds certify the clinical accept/reject verification decision on the PMC-Patients
calibration distribution. They carry NO finite-sample guarantee on a different task or population;
re-calibrate for a new distribution. RTN variants (bfloat16 storage) are not runnable through this handler.
"""
import json, os, glob, torch
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
class EndpointHandler:
def __init__(self, path="."):
self.cfg = json.load(open(os.path.join(path, "certa_config.json")))
self.th = json.load(open(os.path.join(path, "certa_thresholds.json")))
self.alpha = str(self.cfg.get("default_alpha", 0.05))
base, q, bits = self.cfg["base_model"], self.cfg["quantizer"], self.cfg["bits"]
has_local_weights = bool(glob.glob(os.path.join(path, "*.safetensors")))
if has_local_weights:
# Baked quantized weights present: load them directly from the repo (no re-download/re-quantize).
self.model = AutoModelForCausalLM.from_pretrained(path, device_map="auto")
self.tok = AutoTokenizer.from_pretrained(path)
elif q == "bnb" and bits == 4:
qc = BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True, bnb_4bit_compute_dtype=torch.bfloat16)
self.model = AutoModelForCausalLM.from_pretrained(base, quantization_config=qc, device_map="auto")
self.tok = AutoTokenizer.from_pretrained(base)
elif q == "bnb" and bits == 8:
self.model = AutoModelForCausalLM.from_pretrained(
base, quantization_config=BitsAndBytesConfig(load_in_8bit=True), device_map="auto")
self.tok = AutoTokenizer.from_pretrained(base)
else:
raise NotImplementedError(
"This is an RTN evaluation variant (bfloat16 storage, not memory-reducing); "
"it is provided for reproducibility and is not runnable through this handler.")
self.model.eval()
yes, no = self.th["answer_tokens"]["YES"], self.th["answer_tokens"]["NO"]
self.yid = self.tok.encode(yes, add_special_tokens=False)[-1]
self.nid = self.tok.encode(no, add_special_tokens=False)[-1]
@torch.no_grad()
def __call__(self, data):
prompt = data["inputs"] if isinstance(data, dict) else data
alpha = str(data.get("alpha", self.alpha)) if isinstance(data, dict) else self.alpha
th = self.th["alpha"][alpha]["class_conditional"]
ids = self.tok(prompt, return_tensors="pt").to(self.model.device)
logits = self.model(**ids).logits[0, -1]
zy, zn = float(logits[self.yid]), float(logits[self.nid])
decision = "YES" if zy >= zn else "NO"
margin = abs(zy - zn)
tau = th["tau_yes"] if decision == "YES" else th["tau_no"]
escalate = (tau is None) or (margin < tau)
return {"decision": decision, "margin": round(margin, 4), "threshold": tau, "alpha": float(alpha),
"escalate": escalate, "served_by": "reference" if escalate else "quantized",
"note": "escalate=True: refer this decision to the full-precision reference model"}