Spaces:
Sleeping
Sleeping
| import os | |
| import argparse | |
| from datetime import datetime | |
| import torch | |
| import torch.nn as nn | |
| from torch.utils.data import DataLoader | |
| from tqdm import tqdm | |
| from sklearn.metrics import roc_auc_score, f1_score | |
| from dataset import PTBXLDataset | |
| from models.hmt_ecgnet import HMT_ECGNet | |
| from utils_seed import set_seed | |
| from config import BATCH_SIZE, LR, WEIGHT_DECAY, N_EPOCHS, N_LEADS | |
| # ---------------- ARGUMENTS ---------------- | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("--task", choices=["mi_vs_norm", "norm_vs_abnormal"], required=True) | |
| parser.add_argument("--seed", type=int, default=42) | |
| parser.add_argument("--epochs", type=int, default=N_EPOCHS) | |
| args = parser.parse_args() | |
| def get_loader(ds, shuffle): | |
| use_cuda = torch.cuda.is_available() | |
| return DataLoader( | |
| ds, | |
| batch_size=BATCH_SIZE, | |
| shuffle=shuffle, | |
| num_workers=0, | |
| pin_memory=use_cuda | |
| ) | |
| def main(): | |
| set_seed(args.seed) | |
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") | |
| run_name = f"hmt_binary_{args.task}_seed{args.seed}_{datetime.now():%Y%m%d-%H%M%S}" | |
| log_dir = os.path.join("logs", run_name) | |
| os.makedirs(log_dir, exist_ok=True) | |
| # ---------------- DATA ---------------- | |
| train_ds = PTBXLDataset(split="train", task="binary", binary_task=args.task) | |
| val_ds = PTBXLDataset(split="val", task="binary", binary_task=args.task) | |
| train_loader = get_loader(train_ds, shuffle=True) | |
| val_loader = get_loader(val_ds, shuffle=False) | |
| # ---------------- MODEL ---------------- | |
| model = HMT_ECGNet(num_classes=1, num_leads=N_LEADS).to(device) | |
| print(f"Model parameters: {sum(p.numel() for p in model.parameters()):,}") | |
| criterion = nn.BCEWithLogitsLoss() | |
| optimizer = torch.optim.AdamW(model.parameters(), lr=LR, weight_decay=WEIGHT_DECAY) | |
| best_auc = 0.0 | |
| for epoch in range(1, args.epochs + 1): | |
| # -------- TRAIN -------- | |
| model.train() | |
| train_loss = 0.0 | |
| for x, y in tqdm(train_loader, desc=f"Epoch {epoch:03d} [Train]"): | |
| x, y = x.to(device), y.to(device).float() | |
| optimizer.zero_grad() | |
| logits = model(x).squeeze(1) | |
| loss = criterion(logits, y) | |
| loss.backward() | |
| optimizer.step() | |
| train_loss += loss.item() | |
| train_loss /= len(train_loader) | |
| # -------- VALIDATE -------- | |
| model.eval() | |
| all_logits, all_targets = [], [] | |
| with torch.no_grad(): | |
| for x, y in val_loader: | |
| x = x.to(device) | |
| logits = model(x).squeeze(1) | |
| all_logits.append(logits.cpu()) | |
| all_targets.append(y) | |
| logits = torch.cat(all_logits) | |
| targets = torch.cat(all_targets) | |
| probs = torch.sigmoid(logits).numpy() | |
| val_auc = roc_auc_score(targets.numpy(), probs) | |
| val_f1 = f1_score(targets.numpy(), probs >= 0.5) | |
| print( | |
| f"Epoch {epoch:03d} | loss={train_loss:.4f} | " | |
| f"AUROC={val_auc:.4f} | F1={val_f1:.4f}" | |
| ) | |
| if val_auc > best_auc: | |
| best_auc = val_auc | |
| torch.save( | |
| {"epoch": epoch, "model_state_dict": model.state_dict()}, | |
| os.path.join(log_dir, f"hmt_binary_{args.task}_best.pth"), | |
| ) | |
| print(" -> Best model saved") | |
| print("Training complete.") | |
| if __name__ == "__main__": | |
| main() |