2"""Per-layer activation drift: catalog base vs fine-tuned checkpoint (LLM)."""
4from __future__
import annotations
6from pathlib
import Path
16 """Per-row cosine distance. a, b: (n, d)."""
17 a_n = a.float() / a.float().norm(dim=-1, keepdim=
True).clamp(min=1e-8)
18 b_n = b.float() / b.float().norm(dim=-1, keepdim=
True).clamp(min=1e-8)
19 return 1.0 - (a_n * b_n).sum(dim=-1)
23 base_acts: dict[int, torch.Tensor],
24 ft_acts: dict[int, torch.Tensor],
26) -> list[dict[str, Any]]:
27 rows: list[dict[str, Any]] = []
28 for layer
in range(n_layers):
29 base = base_acts.get(layer)
30 ft = ft_acts.get(layer)
31 if base
is None or ft
is None or base.shape != ft.shape:
36 "mean_cosine_distance": round(float(dists.mean().item()), 6),
37 "max_cosine_distance": round(float(dists.max().item()), 6),
38 "mean_cosine_sim": round(float((1.0 - dists).mean().item()), 6),
39 "n_probes": int(dists.shape[0]),
45 base_acts: dict[int, torch.Tensor],
46 ft_acts: dict[int, torch.Tensor],
47 layer_rows: list[dict[str, Any]],
49) -> list[dict[str, Any]]:
52 layers = [int(r[
"layer"])
for r
in layer_rows]
53 out: list[dict[str, Any]] = []
54 n_probes = int(layer_rows[0].get(
"n_probes")
or len(probes))
55 for i
in range(n_probes):
56 dists: list[float] = []
58 base = base_acts.get(layer)
59 ft = ft_acts.get(layer)
60 if base
is None or ft
is None:
65 peak_layer = layers[int(max(range(len(dists)), key=
lambda j: dists[j]))]
66 preview = probes[i][:80] + (
"…" if len(probes[i]) > 80
else "")
69 "probe_preview": preview,
70 "mean_drift": round(sum(dists) / len(dists), 6),
71 "max_drift": round(max(dists), 6),
72 "peak_layer": peak_layer,
74 return sorted(out, key=
lambda r: r[
"max_drift"], reverse=
True)
85 layer_profile: list[dict[str, Any]],
86 per_probe: list[dict[str, Any]],
87 step: int |
None =
None,
89 drifts = [float(r[
"mean_cosine_distance"])
for r
in layer_profile]
90 peak = max(layer_profile, key=
lambda r: r[
"mean_cosine_distance"])
if layer_profile
else None
93 "type":
"residualDrift",
94 "baseModelId": model_id,
95 "ftCheckpointName": checkpoint_name,
96 "checkpointPath": checkpoint_path,
98 "activationMode": activation_mode,
100 "nProbes": len(prompts),
101 "nLayers": len(layer_profile),
102 "meanDrift": round(sum(drifts) / len(drifts), 6)
if drifts
else 0.0,
103 "maxDrift": round(max(drifts), 6)
if drifts
else 0.0,
104 "peakLayer": peak[
"layer"]
if peak
else None,
105 "layerProfile": layer_profile,
106 "topLayers": sorted(layer_profile, key=
lambda r: r[
"mean_cosine_distance"], reverse=
True)[:10],
107 "perProbe": per_probe[:25],
108 "promptsUsed": prompts,
114 checkpoint_path: str | Path,
117 checkpoint_name: str =
"checkpoint",
124 short = resolve_model_id(model_id)
125 _, step = load_checkpoint_state(checkpoint_path)
127 unload_weights(clear_active=
False)
130 tl_base = load_base_tl(short)
131 base_acts = _collect_layer_activations(tl_base, prompts)
132 n_layers = int(tl_base.cfg.n_layers)
134 unload_weights(clear_active=
False)
137 tl_ft = load_tl_from_checkpoint(short, checkpoint_path=checkpoint_path)
138 ft_acts = _collect_layer_activations(tl_ft, prompts)
140 unload_weights(clear_active=
False)
144 per_probe =
_per_probe_rows(base_acts, ft_acts, layer_profile, prompts)
149 activation_mode=
"hook_resid_post_last_token",
150 checkpoint_name=checkpoint_name,
151 checkpoint_path=str(checkpoint_path),
153 layer_profile=layer_profile,
161 checkpoint_path: str | Path,
162 prompts: list[str] |
None =
None,
164 checkpoint_name: str |
None =
None,
168 ckpt = Path(checkpoint_path)
169 if not ckpt.exists():
170 raise FileNotFoundError(f
"Checkpoint not found: {ckpt}")
172 probe_list = list(prompts
or load_prompts(
None))
173 name = checkpoint_name
or ckpt.stem
175 short = resolve_model_id(model_id)
182 model_id = args.get(
"model_id")
or get_active_model_id()
or "llama-3.2-1b"
183 checkpoint = args.get(
"checkpoint")
185 return {
"error":
"Missing --checkpoint <path>. Run: aquin diff residue --help"}
188 model_id = resolve_model_id(str(model_id))
189 except ValueError
as exc:
190 return {
"error": str(exc)}
192 prompts_path = args.get(
"prompts")
194 prompts = load_prompts(prompts_path)
if prompts_path
else None
195 except (FileNotFoundError, ValueError)
as exc:
196 return {
"error": str(exc)}
203 checkpoint_name=args.get(
"name"),
205 except Exception
as exc:
206 return {
"error": str(exc)}
list[dict[str, Any]] _layer_drift_rows(dict[int, torch.Tensor] base_acts, dict[int, torch.Tensor] ft_acts, int n_layers)
dict[str, Any] run_residual_drift_from_args(dict[str, Any] args)
dict[str, Any] run_llm_residual_drift(str model_id, str|Path checkpoint_path, list[str] prompts, *, str checkpoint_name="checkpoint")
dict[str, Any] run_residual_drift(str model_id, str|Path checkpoint_path, list[str]|None prompts=None, *, str|None checkpoint_name=None)
torch.Tensor _cosine_distance_rows(torch.Tensor a, torch.Tensor b)
dict[str, Any] _finalize_payload(*, str model_id, str mode, str activation_mode, str checkpoint_name, str checkpoint_path, list[str] prompts, list[dict[str, Any]] layer_profile, list[dict[str, Any]] per_probe, int|None step=None)
list[dict[str, Any]] _per_probe_rows(dict[int, torch.Tensor] base_acts, dict[int, torch.Tensor] ft_acts, list[dict[str, Any]] layer_rows, list[str] probes)