"""Audit cached teacher logits under plausible pitch/yaw coordinate conventions.""" from pathlib import Path import sys ROOT = Path(r"E:\Gaze_estimation") sys.path.insert(0, str(ROOT / ".codex_deps")) import h5py import numpy as np import pandas as pd OUT = ROOT / "artifacts" / "kd-teacher-trap-diagnostic" def expectation(logits): x = logits - logits.max(axis=1, keepdims=True) p = np.exp(x); p /= p.sum(axis=1, keepdims=True) return (p * np.arange(90)).sum(axis=1) * 2.0 - 90.0 def vectors(pitch_deg, yaw_deg): pitch = np.deg2rad(pitch_deg); yaw = np.deg2rad(yaw_deg) return np.column_stack([-np.cos(pitch) * np.sin(yaw), -np.sin(pitch), -np.cos(pitch) * np.cos(yaw)]) rows = [] for subject in ("p01", "p08", "p11"): path = ROOT / "data" / "processed" / f"{subject}_v16.h5" with h5py.File(path, "r") as f: a = expectation(f["teacher_pitch_logits"][:]) b = expectation(f["teacher_yaw_logits"][:]) gt = np.rad2deg(f["left_gaze"][:]) gt_vec = vectors(gt[:, 0], gt[:, 1]) for swap in (False, True): base_p, base_y = (b, a) if swap else (a, b) for pitch_sign in (-1, 1): for yaw_sign in (-1, 1): p = pitch_sign * base_p; y = yaw_sign * base_y axis = np.abs(np.column_stack([p, y]) - gt).mean(axis=1) pred_vec = vectors(p, y) angular = np.rad2deg(np.arccos(np.clip((pred_vec * gt_vec).sum(axis=1), -1, 1))) rows.append({ "subject": subject, "swap_pitch_yaw": swap, "pitch_sign": pitch_sign, "yaw_sign": yaw_sign, "samples": len(gt), "axis_mae_deg": axis.mean(), "angular_3d_deg": angular.mean(), }) df = pd.DataFrame(rows) summary = df.groupby(["swap_pitch_yaw", "pitch_sign", "yaw_sign"], as_index=False)[["axis_mae_deg", "angular_3d_deg"]].mean() summary["rank_axis"] = summary.axis_mae_deg.rank(method="min").astype(int) summary["rank_3d"] = summary.angular_3d_deg.rank(method="min").astype(int) df.to_csv(OUT / "teacher_coordinate_by_subject.csv", index=False) summary.sort_values("axis_mae_deg").to_csv(OUT / "teacher_coordinate_summary.csv", index=False) print(summary.sort_values("axis_mae_deg").to_string(index=False))