fz3002's picture
Upload project files
e4ac40d verified
Raw
History Blame Contribute Delete
5.98 kB
import json
import os
from pathlib import Path
import tqdm
from torch import nn, optim
from torch.optim.lr_scheduler import CosineAnnealingLR
from src.process.metrics import *
from src.data_preparation.data_handler.datahandler import DataHandler
from src.model.model import SqueezeNet
from src.process.validate import validate_one_epoch
import tqdm
def train_one_epoch(model, loader, optimizer, criterion, device, num_classes) -> dict[str, float]:
model.train()
losses = []
accuracies = []
precisions = []
recalls = []
f1s = []
f1s_weighted = []
for images, labels in tqdm.tqdm(loader, leave=False):
images = images.to(device).float()
labels = labels.to(device).long()
optimizer.zero_grad(set_to_none=True)
logits = model(images)
loss = criterion(logits, labels)
loss.backward()
optimizer.step()
m = metrics(logits.detach(), labels, num_classes)
losses.append(loss.detach())
accuracies.append(m["accuracy"])
precisions.append(m["precision"])
recalls.append(m["recall"])
f1s.append(m["f1"])
f1s_weighted.append(m["f1_w"])
losses = torch.stack(losses)
accuracies = torch.stack(accuracies)
precisions = torch.stack(precisions)
recalls = torch.stack(recalls)
f1s = torch.stack(f1s)
f1s_weighted = torch.stack(f1s_weighted)
return {
"loss": losses.mean().item(),
"accuracy": accuracies.mean().item(),
"precision": precisions.mean().item(),
"recall": recalls.mean().item(),
"f1": f1s.mean().item(),
"f1_w": f1s_weighted.mean().item()
}
def save_checkpoint(path: Path, model, optimizer, scheduler, epoch: int, best_metric: float) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
payload = {
"epoch": epoch,
"best_metric": float(best_metric),
"model_state_dict": model.state_dict(),
"optimizer_state_dict": optimizer.state_dict() if optimizer is not None else None,
"scheduler_state_dict": scheduler.state_dict() if scheduler is not None else None,
}
torch.save(payload, path)
def save_metrics(path: Path, train_metrics: dict, val_metrics: dict, epoch: int) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
payload = {
"epoch": epoch,
"train": train_metrics,
"val": val_metrics,
}
path.write_text(json.dumps(payload, indent=2, ensure_ascii=False), encoding="utf-8")
def save_weights(path: Path, model) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
torch.save(model.state_dict(), path)
def fit(model_section: dict, train_section: dict, output_dir: str) -> None:
os.makedirs(output_dir, exist_ok=True)
os.makedirs(Path(output_dir) / "checkpoints", exist_ok=True)
os.makedirs(Path(output_dir) / "metrics", exist_ok=True)
os.makedirs(Path(output_dir) / "weights", exist_ok=True)
num_classes = model_section["num_classes"]
handler = DataHandler()
train_loader = handler.get_dataloader('train')
val_loader = handler.get_dataloader('val')
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = SqueezeNet(num_classes=model_section["num_classes"]).to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(
model.parameters(),
lr=train_section["learning_rate"],
weight_decay=train_section["weight_decay"]
)
scheduler = CosineAnnealingLR(
optimizer=optimizer,
T_max=train_section["epochs"],
eta_min=5e-7
)
print("=== Training config ===")
print(f"device: {device}")
print(f"epochs: {train_section['epochs']}")
print(f"lr: {train_section['learning_rate']}")
print(f"weight_decay: {train_section['weight_decay']}")
print(f"scheduler: CosineAnnealingLR (eta_min={5e-7})")
print("========================\n")
val_loss: float = 10.0
for epoch in range(1, train_section["epochs"] + 1):
print(f"\nEpoch {epoch:02d}/{train_section['epochs']} | lr={optimizer.param_groups[0]['lr']:.2e}")
train_metrics = train_one_epoch(
model=model,
loader=train_loader,
optimizer=optimizer,
criterion=criterion,
device=device,
num_classes=num_classes
)
val_metrics = validate_one_epoch(
model=model,
loader=val_loader,
criterion=criterion,
device=device,
num_classes=num_classes
)
scheduler.step()
print(f"train | loss={train_metrics['loss']:.4f} acc={train_metrics['accuracy']:.4f} f1={train_metrics['f1']:.4f}")
print(f"val | loss={val_metrics['loss']:.4f} acc={val_metrics['accuracy']:.4f} f1={val_metrics['f1']:.4f}")
save_checkpoint(path=Path(output_dir) / "checkpoints" / f"epoch_{epoch}.pt",
model=model,
optimizer=optimizer,
scheduler=scheduler,
epoch=epoch,
best_metric=val_metrics["loss"])
save_metrics(path=Path(output_dir) / "metrics" / f"epoch_{epoch}.json",
train_metrics=train_metrics,
val_metrics=val_metrics,
epoch=epoch)
save_weights(path=Path(output_dir) / "weights" / f"epoch_{epoch}.pt",
model=model)
if val_loss > val_metrics["loss"]:
val_loss = val_metrics["loss"]
save_weights(path=Path(output_dir) / "weights" / f"best.pt",
model=model)
save_checkpoint(path=Path(output_dir) / "checkpoints" / f"best.pt",
model=model,
optimizer=optimizer,
scheduler=scheduler,
epoch=epoch,
best_metric=val_metrics["loss"])