robomind-vla / calibration.py
mitvho09's picture
RoboMind VLA: vision-language reward model for robot locomotion (built with Codex)
321ba64 verified
Raw
History Blame Contribute Delete
5.01 kB
"""
RoboMind VLA — Task 8: calibration.py
Uncertainty calibration for the reward judge.
Computes temperature scaling and reliability metrics from validation predictions.
Runs on Modal CPU.
"""
from __future__ import annotations
import modal
image = modal.Image.debian_slim(python_version="3.11").pip_install(
"numpy<2",
"scipy",
"scikit-learn",
"matplotlib",
)
app = modal.App("robomind-calibration")
volume = modal.Volume.from_name("robomind-data", create_if_missing=True)
import json
import os
import numpy as np
def temperature_scale(logits: np.ndarray, temperature: float) -> np.ndarray:
"""Apply temperature scaling to logits."""
scaled = logits / temperature
exp_shifted = scaled - np.max(scaled, axis=-1, keepdims=True)
return np.exp(exp_shifted) / np.sum(np.exp(exp_shifted), axis=-1, keepdims=True)
def compute_calibration_metrics(
confidences: np.ndarray,
accuracies: np.ndarray,
n_bins: int = 10,
) -> dict:
"""Compute Expected Calibration Error (ECE) and related metrics."""
bin_boundaries = np.linspace(0, 1, n_bins + 1)
ece = 0.0
bin_data = []
for i in range(n_bins):
mask = (confidences > bin_boundaries[i]) & (confidences <= bin_boundaries[i + 1])
count = np.sum(mask)
if count == 0:
continue
avg_conf = np.mean(confidences[mask])
avg_acc = np.mean(accuracies[mask])
bin_data.append({
"bin": f"({bin_boundaries[i]:.1f}, {bin_boundaries[i+1]:.1f}]",
"count": int(count),
"avg_confidence": float(avg_conf),
"avg_accuracy": float(avg_acc),
"gap": float(abs(avg_conf - avg_acc)),
})
ece += (count / len(confidences)) * abs(avg_conf - avg_acc)
return {
"ece": float(ece),
"num_bins": n_bins,
"total_samples": int(len(confidences)),
"mean_confidence": float(np.mean(confidences)),
"mean_accuracy": float(np.mean(accuracies)),
"bins": bin_data,
}
def find_optimal_temperature(
confidences: np.ndarray,
accuracies: np.ndarray,
) -> float:
"""Find optimal temperature using NLL minimization."""
from scipy.optimize import minimize_scalar
def nll(temperature):
if temperature <= 0:
return float("inf")
probs = temperature_scale(confidences, temperature)
nll_val = -np.mean(
accuracies * np.log(probs[:, 1] + 1e-8)
+ (1 - accuracies) * np.log(probs[:, 0] + 1e-8)
)
return nll_val
result = minimize_scalar(nll, bounds=(0.1, 10.0), method="bounded")
return float(result.x)
@app.function(
image=image,
volumes={"/data": volume},
timeout=600,
)
def calibrate():
"""Run calibration on stored predictions."""
pred_path = "/data/calibration/predictions.jsonl"
if not os.path.exists(pred_path):
print("[cal] no predictions found, generating synthetic calibration data")
os.makedirs("/data/calibration", exist_ok=True)
generate_synthetic_data(pred_path)
confidences = []
accuracies = []
with open(pred_path, "r") as f:
for line in f:
row = json.loads(line)
confidences.append(row.get("confidence", 0.5))
accuracies.append(row.get("correct", 0))
confidences = np.array(confidences)
accuracies = np.array(accuracies)
print(f"[cal] loaded {len(confidences)} predictions")
metrics = compute_calibration_metrics(confidences, accuracies)
print(f"[cal] ECE: {metrics['ece']:.4f}")
print(f"[cal] Mean confidence: {metrics['mean_confidence']:.4f}")
print(f"[cal] Mean accuracy: {metrics['mean_accuracy']:.4f}")
for b in metrics.get("bins", []):
print(f" {b['bin']}: count={b['count']}, conf={b['avg_confidence']:.3f}, acc={b['avg_accuracy']:.3f}")
cal_result = {
"metrics": metrics,
"n_samples": len(confidences),
}
out_path = "/data/calibration/calibration_result.json"
with open(out_path, "w") as f:
json.dump(cal_result, f, indent=2)
volume.commit()
print(f"[cal] calibration result saved to {out_path}")
return cal_result
def generate_synthetic_data(path: str):
"""Generate synthetic calibration data for initial setup."""
import random
random.seed(42)
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w") as f:
for _ in range(100):
confidence = random.uniform(0.3, 0.95)
correct = 1 if random.random() < confidence else 0
f.write(json.dumps({
"confidence": confidence,
"correct": correct,
"predicted_reward": random.uniform(0, 1),
"ground_truth_reward": random.uniform(0, 1),
}) + "\n")
print(f"[cal] generated 100 synthetic samples at {path}")
@app.local_entrypoint()
def main():
result = calibrate.remote()
print("RESULT:", json.dumps(result, indent=2))