naidusai's picture
Initial deploy: MicroPlastiNet Dash dashboard (synthetic data, honest disclosure)
3a5b233 verified
Raw
History Blame Contribute Delete
6.16 kB
"""
finalize.py — Finalize training (save metrics JSON), run evaluate.py,
run sample inference. Called after training completes or is interrupted
with a valid checkpoint.
"""
import os
import sys
import json
import csv
import torch
import numpy as np
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from dataset import get_dataloaders, POLYMER_CLASSES
from model import build_model
from infer import load_model
from evaluate import evaluate
_BASE = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
PROC_DIR = os.path.join(_BASE, "data", "processed", "m2b")
ASSETS_DIR = os.path.join(_BASE, "assets")
def finalize_training(arch="cnn", seed=42):
ckpt_path = os.path.join(PROC_DIR, f"m2b_{arch}_best.pt")
log_path = os.path.join(PROC_DIR, f"m2b_{arch}_train_log.csv")
if not os.path.exists(ckpt_path):
print(f"[ERROR] Checkpoint not found: {ckpt_path}")
return
# Read history from CSV
history = []
if os.path.exists(log_path):
with open(log_path, "r") as f:
reader = csv.DictReader(f)
for row in reader:
history.append({k: float(v) for k, v in row.items()})
# Load checkpoint for meta
device = torch.device("cpu")
ckpt = torch.load(ckpt_path, map_location=device, weights_only=True)
# Get data for test eval
_, _, test_loader, meta = get_dataloaders(seed=seed, augment_train=False)
model = build_model(arch, n_classes=ckpt.get("n_classes", 6),
input_len=ckpt.get("input_dim", 901))
model.load_state_dict(ckpt["model_state"])
model.eval()
# Compute test accuracy
import torch.nn as nn
criterion = nn.CrossEntropyLoss()
total_loss, correct, total = 0.0, 0, 0
all_true, all_pred, all_proba = [], [], []
with torch.no_grad():
for X_batch, y_batch in test_loader:
logits = model(X_batch)
loss = criterion(logits, y_batch)
probs = torch.softmax(logits, dim=-1).numpy()
preds = probs.argmax(axis=1)
total_loss += loss.item() * len(y_batch)
correct += (preds == y_batch.numpy()).sum()
total += len(y_batch)
all_true.extend(y_batch.numpy())
all_pred.extend(preds)
all_proba.extend(probs)
test_acc = correct / total
test_loss = total_loss / total
best_val_acc = ckpt.get("val_acc", max((h.get("val_acc", 0) for h in history), default=0))
best_epoch = ckpt.get("epoch", len(history))
print(f"[INFO] Test accuracy: {test_acc:.4%} (best val: {best_val_acc:.4%} @ epoch {best_epoch})")
# Save metrics JSON
metrics = {
"arch": arch,
"seed": seed,
"best_epoch": int(best_epoch),
"best_val_acc": float(best_val_acc),
"test_acc": float(test_acc),
"test_loss": float(test_loss),
"n_params": sum(p.numel() for p in model.parameters() if p.requires_grad),
"n_train": meta["n_train"],
"n_val": meta["n_val"],
"n_test": meta["n_test"],
"class_names": meta["class_names"],
"data_source": meta["source"],
"history": history,
}
metrics_path = os.path.join(PROC_DIR, f"m2b_{arch}_metrics.json")
with open(metrics_path, "w") as f:
json.dump(metrics, f, indent=2)
print(f"[INFO] Metrics saved → {metrics_path}")
return metrics, np.array(all_true), np.array(all_pred), np.array(all_proba)
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--arch", default="cnn")
parser.add_argument("--seed", type=int, default=42)
args = parser.parse_args()
result = finalize_training(args.arch, args.seed)
if result is None:
sys.exit(1)
metrics, y_true, y_pred, y_proba = result
print("\n[Step 2] Running full evaluation + generating plots...")
eval_report = evaluate(arch=args.arch, seed=args.seed, save_preds=True)
print(f"\n[Step 3] Sample inference demo...")
from synthetic_spectra import generate_spectrum, POLYMER_CLASSES
clf = load_model(arch=args.arch)
rng = np.random.default_rng(777)
sample_results = []
print(f"\n{'Polymer':>8} | {'Predicted':>9} | {'Confidence':>11} | Correct")
print("─" * 55)
for polymer in POLYMER_CLASSES:
spectrum = generate_spectrum(polymer, rng)
result = clf.predict(spectrum)
correct = "✓" if result["polymer"] == polymer else "✗"
sample_results.append({
"true": polymer,
"pred": result["polymer"],
"confidence": result["confidence"],
"probabilities": result["probabilities"],
"correct": result["polymer"] == polymer,
})
print(f"{polymer:>8} | {result['polymer']:>9} | {result['confidence']:>10.4f} | {correct}")
# Save sample inference output
samples_path = os.path.join(PROC_DIR, "m2b_sample_inference.json")
with open(samples_path, "w") as f:
json.dump(sample_results, f, indent=2)
print(f"\n[INFO] Sample inference → {samples_path}")
print(f"\n{'='*60}")
print(f" TRAINING SUMMARY")
print(f"{'='*60}")
print(f" Architecture: {args.arch.upper()}")
print(f" Best Val Accuracy: {metrics['best_val_acc']:.4%}")
print(f" Test Accuracy: {metrics['test_acc']:.4%}")
print(f" Macro AUC: {eval_report['macro_auc']:.4f}")
print(f" Epochs trained: {metrics['best_epoch']}")
print(f" Parameters: {metrics['n_params']:,}")
print(f"{'='*60}")
print(f"\n Artifacts:")
print(f" Checkpoint: {os.path.join(PROC_DIR, f'm2b_{args.arch}_best.pt')}")
print(f" Metrics JSON: {os.path.join(PROC_DIR, f'm2b_{args.arch}_metrics.json')}")
print(f" Confusion PNG: {os.path.join(PROC_DIR.replace('processed/m2b', 'assets'), 'm2b_confusion.png').replace('data/', '')}")
print(f" Eval Report: {os.path.join(PROC_DIR, 'm2b_eval_report.json')}")