counterflow-nn / src /utils.py
DanielRegaladoCardoso's picture
Upload src/utils.py with huggingface_hub
6b3618b verified
Raw
History Blame Contribute Delete
5.1 kB
"""Training utilities: data loading, training loops, metric computation."""
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import make_moons, make_circles, load_iris, load_wine, load_breast_cancer
def load_synthetic_dataset(name="moons", n_samples=1000, noise=0.2, seed=42):
"""Load synthetic 2D dataset: 'moons', 'circles', or 'xor'."""
rng = np.random.RandomState(seed)
if name == "moons":
return make_moons(n_samples=n_samples, noise=noise, random_state=seed)
elif name == "circles":
return make_circles(n_samples=n_samples, noise=noise, factor=0.5, random_state=seed)
elif name == "xor":
X = rng.randn(n_samples, 2)
y = ((X[:, 0] > 0) ^ (X[:, 1] > 0)).astype(int)
X += noise * rng.randn(n_samples, 2)
return X, y
raise ValueError(f"Unknown dataset: {name}")
def load_uci_dataset(name="iris"):
"""Load UCI dataset. Returns (X, y, task_type)."""
loaders = {
"iris": (load_iris, "classification"),
"wine": (load_wine, "classification"),
"breast_cancer": (load_breast_cancer, "classification"),
}
if name == "california_housing":
from sklearn.datasets import fetch_california_housing
d = fetch_california_housing()
return d.data, d.target, "regression"
if name not in loaders:
raise ValueError(f"Unknown dataset: {name}")
d = loaders[name][0]()
return d.data, d.target, loaders[name][1]
def prepare_data(X, y, test_size=0.2, batch_size=64, seed=42, scale=True):
"""Split, scale, and create DataLoaders. Returns (train_loader, test_loader, d_in, n_classes)."""
stratify = y if len(np.unique(y)) < 20 else None
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=test_size, random_state=seed, stratify=stratify)
if scale:
sc = StandardScaler()
X_tr, X_te = sc.fit_transform(X_tr), sc.transform(X_te)
X_tr_t, X_te_t = torch.FloatTensor(X_tr), torch.FloatTensor(X_te)
n_classes = len(np.unique(y))
if n_classes > 20:
y_tr_t = torch.FloatTensor(y_tr).unsqueeze(-1)
y_te_t = torch.FloatTensor(y_te).unsqueeze(-1)
n_classes = 1
else:
y_tr_t, y_te_t = torch.LongTensor(y_tr), torch.LongTensor(y_te)
train_loader = DataLoader(TensorDataset(X_tr_t, y_tr_t), batch_size=batch_size, shuffle=True)
test_loader = DataLoader(TensorDataset(X_te_t, y_te_t), batch_size=batch_size)
return train_loader, test_loader, X_tr.shape[1], n_classes
def train_epoch(model, loader, criterion, optimizer, device):
"""Train one epoch. Returns average loss."""
model.train()
total, n = 0.0, 0
for xb, yb in loader:
xb, yb = xb.to(device), yb.to(device)
optimizer.zero_grad()
loss = criterion(model(xb), yb)
loss.backward()
optimizer.step()
total += loss.item()
n += 1
return total / max(n, 1)
@torch.no_grad()
def evaluate(model, loader, criterion, device, task="classification"):
"""Evaluate model. Returns dict with 'loss' and 'accuracy' or 'rmse'."""
model.eval()
total_loss, correct, total = 0.0, 0, 0
preds_all, targs_all = [], []
for xb, yb in loader:
xb, yb = xb.to(device), yb.to(device)
out = model(xb)
total_loss += criterion(out, yb).item()
if task == "classification":
correct += (out.argmax(-1) == yb).sum().item()
total += yb.shape[0]
else:
preds_all.append(out.cpu())
targs_all.append(yb.cpu())
res = {"loss": total_loss / max(len(loader), 1)}
if task == "classification":
res["accuracy"] = correct / max(total, 1)
else:
res["rmse"] = torch.sqrt(((torch.cat(preds_all) - torch.cat(targs_all)) ** 2).mean()).item()
return res
def train_model(model, train_loader, test_loader, n_epochs=100, lr=1e-3, device=None, task="classification", verbose=True, print_every=10):
"""Full training loop. Returns history dict."""
if device is None:
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)
criterion = nn.CrossEntropyLoss() if task == "classification" else nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=lr)
history = {"train_losses": [], "test_losses": [], "test_metrics": []}
for epoch in range(1, n_epochs + 1):
tl = train_epoch(model, train_loader, criterion, optimizer, device)
te = evaluate(model, test_loader, criterion, device, task)
history["train_losses"].append(tl)
history["test_losses"].append(te["loss"])
mk = "accuracy" if task == "classification" else "rmse"
history["test_metrics"].append(te[mk])
if verbose and epoch % print_every == 0:
print(f"Epoch {epoch:4d} | Train: {tl:.4f} | Test: {te['loss']:.4f} | {mk}: {te[mk]:.4f}")
return history