AQIT 0.1.0
Loading...
Searching...
No Matches
calibration.py
Go to the documentation of this file.
1# Copyright (c) 2025-present Aquin Labs Private Limited. All Rights Reserved.
2# This file is part of the Aquin Engine. Unauthorized copying, modification,
3# distribution, or use of this file, via any medium, is strictly prohibited.
4# Proprietary and confidential. See LICENSE for terms.
5
6"""Confidence calibration — base vs fine-tuned mean token confidence / ECE proxy."""
7from __future__ import annotations
8
9from typing import Any
10
11
12def ece_proxy(confs: list[float]) -> float:
13 """Scalar ECE proxy: mean |confidence - 0.5| in [0, 0.5]. Lower = less peaked."""
14 if not confs:
15 return 0.5
16 return round(float(sum(abs(c - 0.5) for c in confs) / len(confs)), 4)
17
18
19def mean_token_confidence(model: Any, tokenizer: Any, device: str, prompts: list[str]) -> list[float]:
20 import torch
21
22 confs: list[float] = []
23 for prompt in prompts:
24 enc = tokenizer(prompt, return_tensors="pt").to(device)
25 with torch.no_grad():
26 logits = model(**enc).logits
27 probs = torch.softmax(logits[0], dim=-1)
28 greedy = logits[0].argmax(dim=-1)
29 token_confs = probs[torch.arange(len(greedy), device=greedy.device), greedy]
30 confs.append(float(token_confs.mean().item()))
31 return confs
32
33
35 *,
36 base_model: Any,
37 ft_model: Any,
38 tokenizer: Any,
39 device: str,
40 eval_prompts: list[str],
41 eval_categories: list[str] | None = None,
42 ft_outputs: list[str] | None = None,
43 threshold: float = 0.40,
44) -> dict:
45 """Return calibration payload matching web CalibrationPayload / simulate event."""
46 cats = eval_categories or ["dataset"] * len(eval_prompts)
47 if len(cats) < len(eval_prompts):
48 cats = cats + ["dataset"] * (len(eval_prompts) - len(cats))
49
50 base_confs = mean_token_confidence(base_model, tokenizer, device, eval_prompts)
51 ft_confs = mean_token_confidence(ft_model, tokenizer, device, eval_prompts)
52
53 base_ece = ece_proxy(base_confs)
54 ft_ece = ece_proxy(ft_confs)
55
56 all_categories = sorted(set(cats))
57 cat_base: dict[str, list[float]] = {}
58 cat_ft: dict[str, list[float]] = {}
59 for i, cat in enumerate(cats[: len(eval_prompts)]):
60 cat_base.setdefault(cat, []).append(base_confs[i])
61 cat_ft.setdefault(cat, []).append(ft_confs[i])
62
63 topics = []
64 for cat in all_categories:
65 b_ece = ece_proxy(cat_base.get(cat, [0.5]))
66 f_ece = ece_proxy(cat_ft.get(cat, [0.5]))
67 topics.append({
68 "topic": cat,
69 "base_ece": b_ece,
70 "ft_ece": f_ece,
71 "delta": round(f_ece - b_ece, 4),
72 })
73
74 outputs = ft_outputs or []
75 low_conf_rows: list[dict] = []
76 for i, (prompt, fc, bc) in enumerate(zip(eval_prompts, ft_confs, base_confs)):
77 if fc < threshold:
78 row: dict = {
79 "idx": i,
80 "instruction": prompt,
81 "confidence": round(fc, 4),
82 "topic": cats[i] if i < len(cats) else "dataset",
83 }
84 if i < len(outputs) and outputs[i]:
85 row["response"] = outputs[i]
86 low_conf_rows.append(row)
87
88 return {
89 "base_ece": base_ece,
90 "ft_ece": ft_ece,
91 "ece_delta": round(ft_ece - base_ece, 4),
92 "topics": topics,
93 "low_confidence_rows": low_conf_rows,
94 "threshold": threshold,
95 }
float ece_proxy(list[float] confs)
list[float] mean_token_confidence(Any model, Any tokenizer, str device, list[str] prompts)
dict run_calibration_for_models(*, Any base_model, Any ft_model, Any tokenizer, str device, list[str] eval_prompts, list[str]|None eval_categories=None, list[str]|None ft_outputs=None, float threshold=0.40)