File size: 2,883 Bytes
a10ba7f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | import torch
import numpy as np
import h5py
import os
import sys
from pathlib import Path
from tqdm import tqdm
# Add project root to path
sys.path.append(str(Path(__file__).parent.parent.parent))
from src.models.student import LIPEV2Student
def analyze_worst_errors(model_path, h5_path, top_n=10):
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f"Analyzing errors for model: {model_path}")
model = LIPEV2Student().to(device)
state_dict = torch.load(model_path, map_location=device)
model.load_state_dict(state_dict, strict=False)
model.eval()
errors = []
with h5py.File(h5_path, 'r') as f:
lp = torch.from_numpy(f['left_patches'][:]).float().to(device)
rp = torch.from_numpy(f['right_patches'][:]).float().to(device)
lm = torch.from_numpy(f['landmarks'][:]).float().view(lp.shape[0], -1).to(device)
# Average left and right gaze for ground truth
gt_l = f['left_gaze'][:]
gt_r = f['right_gaze'][:]
gt = torch.from_numpy((gt_l + gt_r) / 2).float().to(device)
with torch.no_grad():
p_logits_l, y_logits_l, _ = model(lp, lm, state='A')
p_logits_r, y_logits_r, _ = model(rp, lm, state='A')
# Logits to deg
def logits_to_deg(p, y):
idx = torch.arange(90).float().to(device)
p_deg = (torch.softmax(p, dim=1) @ idx) * 2 - 90
y_deg = (torch.softmax(y, dim=1) @ idx) * 2 - 90
return p_deg, y_deg
p_l, y_l = logits_to_deg(p_logits_l, y_logits_l)
p_r, y_r = logits_to_deg(p_logits_r, y_logits_r)
p_pred = (p_l + p_r) / 2
y_pred = (y_l + y_r) / 2
gt_deg = gt * (180.0 / np.pi)
# Error per sample
sample_errors = (torch.abs(p_pred - gt_deg[:, 0]) + torch.abs(y_pred - gt_deg[:, 1])).cpu().numpy()
for i in range(len(sample_errors)):
errors.append({
'idx': i,
'mae': sample_errors[i] / 2,
'gt_pitch': gt_deg[i, 0].item(),
'gt_yaw': gt_deg[i, 1].item(),
'pred_pitch': p_pred[i].item(),
'pred_yaw': y_pred[i].item()
})
# Sort by error descending
errors.sort(key=lambda x: x['mae'], reverse=True)
print(f"\nTop {top_n} Worst Errors:")
print(f"{'Idx':<8} | {'MAE':<10} | {'GT (P,Y)':<20} | {'Pred (P,Y)':<20}")
print("-" * 70)
for e in errors[:top_n]:
print(f"{e['idx']:<8} | {e['mae']:.4f} | ({e['gt_pitch']:.1f}, {e['gt_yaw']:.1f}) | ({e['pred_pitch']:.1f}, {e['pred_yaw']:.1f})")
if __name__ == "__main__":
analyze_worst_errors(
model_path='checkpoints/best_student_p08.pt',
h5_path='data/processed/p08_v16_new.h5'
)
|