|
@@ -1,4 +1,6 @@
|
|
|
import pathlib as pl
|
|
import pathlib as pl
|
|
|
|
|
+import time
|
|
|
|
|
+from threading import Event
|
|
|
from typing import Callable, Tuple, cast
|
|
from typing import Callable, Tuple, cast
|
|
|
|
|
|
|
|
import numpy as np
|
|
import numpy as np
|
|
@@ -8,6 +10,8 @@ import torch.nn as nn
|
|
|
from torch.utils.data import DataLoader
|
|
from torch.utils.data import DataLoader
|
|
|
|
|
|
|
|
from model.dataset import ADNIDataset
|
|
from model.dataset import ADNIDataset
|
|
|
|
|
+from util.progress import ProgressTracker
|
|
|
|
|
+from util.ui_logger import PipelineLogger
|
|
|
|
|
|
|
|
type TrainMetrics = Tuple[
|
|
type TrainMetrics = Tuple[
|
|
|
float, float, float, float
|
|
float, float, float, float
|
|
@@ -17,12 +21,47 @@ type TestMetrics = Tuple[float, float] # (test_loss, test_acc)
|
|
|
type KLLossFn = Callable[[nn.Module], torch.Tensor | None]
|
|
type KLLossFn = Callable[[nn.Module], torch.Tensor | None]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+def _batch_correct_and_total(
|
|
|
|
|
+ outputs: torch.Tensor,
|
|
|
|
|
+ targets: torch.Tensor,
|
|
|
|
|
+) -> Tuple[int, int]:
|
|
|
|
|
+ if outputs.ndim > 1 and outputs.size(-1) > 1:
|
|
|
|
|
+ predicted_classes = outputs.argmax(dim=1)
|
|
|
|
|
+ if targets.ndim > 1 and targets.size(-1) > 1:
|
|
|
|
|
+ target_classes = targets.argmax(dim=1)
|
|
|
|
|
+ else:
|
|
|
|
|
+ target_classes = targets.long().view(-1)
|
|
|
|
|
+
|
|
|
|
|
+ correct = int((predicted_classes == target_classes).sum().item())
|
|
|
|
|
+ total = int(target_classes.numel())
|
|
|
|
|
+ return correct, total
|
|
|
|
|
+
|
|
|
|
|
+ predicted = (outputs > 0.5).to(dtype=targets.dtype)
|
|
|
|
|
+ correct = int((predicted == targets).sum().item())
|
|
|
|
|
+ total = int(targets.numel())
|
|
|
|
|
+ return correct, total
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _check_control_events(
|
|
|
|
|
+ stop_event: Event | None,
|
|
|
|
|
+ pause_event: Event | None,
|
|
|
|
|
+) -> None:
|
|
|
|
|
+ if stop_event is not None and stop_event.is_set():
|
|
|
|
|
+ raise InterruptedError("Pipeline execution stopped by user.")
|
|
|
|
|
+ while pause_event is not None and pause_event.is_set():
|
|
|
|
|
+ time.sleep(0.5)
|
|
|
|
|
+ if stop_event is not None and stop_event.is_set():
|
|
|
|
|
+ raise InterruptedError("Pipeline execution stopped by user while paused.")
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
def test_model(
|
|
def test_model(
|
|
|
model: nn.Module,
|
|
model: nn.Module,
|
|
|
test_loader: DataLoader[ADNIDataset],
|
|
test_loader: DataLoader[ADNIDataset],
|
|
|
criterion: nn.Module,
|
|
criterion: nn.Module,
|
|
|
- progress: Callable[[int | None, int], None],
|
|
|
|
|
- write_log: Callable[[str, bool], None],
|
|
|
|
|
|
|
+ progress: ProgressTracker,
|
|
|
|
|
+ log: PipelineLogger,
|
|
|
|
|
+ stop_event: Event | None = None,
|
|
|
|
|
+ pause_event: Event | None = None,
|
|
|
) -> TestMetrics:
|
|
) -> TestMetrics:
|
|
|
"""
|
|
"""
|
|
|
Tests the model on the test dataset.
|
|
Tests the model on the test dataset.
|
|
@@ -39,18 +78,25 @@ def test_model(
|
|
|
test_loss = 0.0
|
|
test_loss = 0.0
|
|
|
correct = 0
|
|
correct = 0
|
|
|
total = 0
|
|
total = 0
|
|
|
|
|
+ total_samples = 0
|
|
|
|
|
|
|
|
|
|
+ test_progress = progress.get_sub_tracker("Testing Batches")
|
|
|
|
|
+ test_progress.update(total=len(test_loader), advance=0)
|
|
|
for _, (mri, xls, targets, _) in enumerate(test_loader):
|
|
for _, (mri, xls, targets, _) in enumerate(test_loader):
|
|
|
|
|
+ _check_control_events(stop_event=stop_event, pause_event=pause_event)
|
|
|
outputs = model((mri, xls))
|
|
outputs = model((mri, xls))
|
|
|
loss = criterion(outputs, targets)
|
|
loss = criterion(outputs, targets)
|
|
|
- test_loss += loss.item() * (mri.size(0) + xls.size(0))
|
|
|
|
|
|
|
+ batch_size = mri.size(0)
|
|
|
|
|
+ test_loss += loss.item() * batch_size
|
|
|
|
|
+ total_samples += batch_size
|
|
|
|
|
|
|
|
# Calculate accuracy
|
|
# Calculate accuracy
|
|
|
- predicted = (outputs > 0.5).float()
|
|
|
|
|
- correct += (predicted == targets).sum().item()
|
|
|
|
|
- total += targets.numel()
|
|
|
|
|
|
|
+ batch_correct, batch_total = _batch_correct_and_total(outputs, targets)
|
|
|
|
|
+ correct += batch_correct
|
|
|
|
|
+ total += batch_total
|
|
|
|
|
+ test_progress.update(total=len(test_loader), advance=1)
|
|
|
|
|
|
|
|
- test_loss /= len(test_loader)
|
|
|
|
|
|
|
+ test_loss = test_loss / total_samples if total_samples > 0 else 0.0
|
|
|
test_acc = correct / total if total > 0 else 0.0
|
|
test_acc = correct / total if total > 0 else 0.0
|
|
|
return test_loss, test_acc
|
|
return test_loss, test_acc
|
|
|
|
|
|
|
@@ -61,6 +107,10 @@ def train_epoch(
|
|
|
val_loader: DataLoader[ADNIDataset],
|
|
val_loader: DataLoader[ADNIDataset],
|
|
|
optimizer: torch.optim.Optimizer,
|
|
optimizer: torch.optim.Optimizer,
|
|
|
criterion: nn.Module,
|
|
criterion: nn.Module,
|
|
|
|
|
+ log: PipelineLogger,
|
|
|
|
|
+ progress: ProgressTracker,
|
|
|
|
|
+ stop_event: Event | None = None,
|
|
|
|
|
+ pause_event: Event | None = None,
|
|
|
) -> Tuple[float, float, float, float]:
|
|
) -> Tuple[float, float, float, float]:
|
|
|
"""
|
|
"""
|
|
|
Trains the model for one epoch and evaluates it on the validation set.
|
|
Trains the model for one epoch and evaluates it on the validation set.
|
|
@@ -77,36 +127,59 @@ def train_epoch(
|
|
|
"""
|
|
"""
|
|
|
model.train()
|
|
model.train()
|
|
|
train_loss = 0.0
|
|
train_loss = 0.0
|
|
|
-
|
|
|
|
|
- # Training loop
|
|
|
|
|
|
|
+ train_correct = 0
|
|
|
|
|
+ train_total = 0
|
|
|
|
|
+ train_total_samples = 0
|
|
|
|
|
+
|
|
|
|
|
+ # Training loop - with progress bar for the training batches
|
|
|
|
|
+ train_progress = progress.get_sub_tracker("Training Batches")
|
|
|
|
|
+ train_progress.reset()
|
|
|
|
|
+ train_progress.set_title("Training Batches")
|
|
|
|
|
+ train_progress.update(total=len(train_loader), advance=0)
|
|
|
for _, (mri, xls, targets, _) in enumerate(train_loader):
|
|
for _, (mri, xls, targets, _) in enumerate(train_loader):
|
|
|
|
|
+ _check_control_events(stop_event=stop_event, pause_event=pause_event)
|
|
|
optimizer.zero_grad()
|
|
optimizer.zero_grad()
|
|
|
outputs = model((mri, xls))
|
|
outputs = model((mri, xls))
|
|
|
loss = criterion(outputs, targets)
|
|
loss = criterion(outputs, targets)
|
|
|
loss.backward() # type: ignore[reportUnknownMemberType]
|
|
loss.backward() # type: ignore[reportUnknownMemberType]
|
|
|
optimizer.step()
|
|
optimizer.step()
|
|
|
- train_loss += loss.item() * (mri.size(0) + xls.size(0))
|
|
|
|
|
- train_loss /= len(train_loader)
|
|
|
|
|
|
|
+ batch_size = mri.size(0)
|
|
|
|
|
+ train_loss += loss.item() * batch_size
|
|
|
|
|
+ train_total_samples += batch_size
|
|
|
|
|
+ batch_correct, batch_total = _batch_correct_and_total(outputs, targets)
|
|
|
|
|
+ train_correct += batch_correct
|
|
|
|
|
+ train_total += batch_total
|
|
|
|
|
+ train_progress.update(total=len(train_loader), advance=1)
|
|
|
|
|
+ train_loss = train_loss / train_total_samples if train_total_samples > 0 else 0.0
|
|
|
|
|
|
|
|
model.eval()
|
|
model.eval()
|
|
|
val_loss = 0.0
|
|
val_loss = 0.0
|
|
|
correct = 0
|
|
correct = 0
|
|
|
total = 0
|
|
total = 0
|
|
|
|
|
+ val_total_samples = 0
|
|
|
|
|
|
|
|
|
|
+ test_progress = progress.get_sub_tracker("Validation Batches")
|
|
|
|
|
+ test_progress.reset()
|
|
|
|
|
+ test_progress.set_title("Validation Batches")
|
|
|
|
|
+ test_progress.update(total=len(val_loader), advance=0)
|
|
|
with torch.no_grad():
|
|
with torch.no_grad():
|
|
|
for _, (mri, xls, targets, _) in enumerate(val_loader):
|
|
for _, (mri, xls, targets, _) in enumerate(val_loader):
|
|
|
|
|
+ _check_control_events(stop_event=stop_event, pause_event=pause_event)
|
|
|
outputs = model((mri, xls))
|
|
outputs = model((mri, xls))
|
|
|
loss = criterion(outputs, targets)
|
|
loss = criterion(outputs, targets)
|
|
|
- val_loss += loss.item() * (mri.size(0) + xls.size(0))
|
|
|
|
|
|
|
+ batch_size = mri.size(0)
|
|
|
|
|
+ val_loss += loss.item() * batch_size
|
|
|
|
|
+ val_total_samples += batch_size
|
|
|
|
|
|
|
|
# Calculate accuracy
|
|
# Calculate accuracy
|
|
|
- predicted = (outputs > 0.5).float()
|
|
|
|
|
- correct += (predicted == targets).sum().item()
|
|
|
|
|
- total += targets.numel()
|
|
|
|
|
|
|
+ batch_correct, batch_total = _batch_correct_and_total(outputs, targets)
|
|
|
|
|
+ correct += batch_correct
|
|
|
|
|
+ total += batch_total
|
|
|
|
|
+ test_progress.update(total=len(val_loader), advance=1)
|
|
|
|
|
|
|
|
- val_loss /= len(val_loader)
|
|
|
|
|
|
|
+ val_loss = val_loss / val_total_samples if val_total_samples > 0 else 0.0
|
|
|
val_acc = correct / total if total > 0 else 0.0
|
|
val_acc = correct / total if total > 0 else 0.0
|
|
|
- train_acc = correct / total if total > 0 else 0.0
|
|
|
|
|
|
|
+ train_acc = train_correct / train_total if train_total > 0 else 0.0
|
|
|
return train_loss, val_loss, train_acc, val_acc
|
|
return train_loss, val_loss, train_acc, val_acc
|
|
|
|
|
|
|
|
|
|
|
|
@@ -118,6 +191,10 @@ def train_model(
|
|
|
criterion: nn.Module,
|
|
criterion: nn.Module,
|
|
|
num_epochs: int,
|
|
num_epochs: int,
|
|
|
output_path: pl.Path,
|
|
output_path: pl.Path,
|
|
|
|
|
+ progress: ProgressTracker,
|
|
|
|
|
+ log: PipelineLogger,
|
|
|
|
|
+ stop_event: Event | None = None,
|
|
|
|
|
+ pause_event: Event | None = None,
|
|
|
) -> Tuple[nn.Module, pd.DataFrame]:
|
|
) -> Tuple[nn.Module, pd.DataFrame]:
|
|
|
"""
|
|
"""
|
|
|
Trains the model using the provided training and validation data loaders.
|
|
Trains the model using the provided training and validation data loaders.
|
|
@@ -139,13 +216,22 @@ def train_model(
|
|
|
|
|
|
|
|
nhist = np.zeros((num_epochs, 4), dtype=np.float32)
|
|
nhist = np.zeros((num_epochs, 4), dtype=np.float32)
|
|
|
|
|
|
|
|
|
|
+ # Get new progress bar for epochs
|
|
|
|
|
+
|
|
|
|
|
+ epoch_progress = progress.get_sub_tracker("Epochs")
|
|
|
|
|
+ epoch_progress.update(total=num_epochs, advance=0)
|
|
|
for epoch in range(num_epochs):
|
|
for epoch in range(num_epochs):
|
|
|
|
|
+ _check_control_events(stop_event=stop_event, pause_event=pause_event)
|
|
|
train_loss, val_loss, train_acc, val_acc = train_epoch(
|
|
train_loss, val_loss, train_acc, val_acc = train_epoch(
|
|
|
model,
|
|
model,
|
|
|
train_loader,
|
|
train_loader,
|
|
|
val_loader,
|
|
val_loader,
|
|
|
optimizer,
|
|
optimizer,
|
|
|
criterion,
|
|
criterion,
|
|
|
|
|
+ log=log,
|
|
|
|
|
+ progress=epoch_progress,
|
|
|
|
|
+ stop_event=stop_event,
|
|
|
|
|
+ pause_event=pause_event,
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
# Update the history
|
|
# Update the history
|
|
@@ -154,7 +240,7 @@ def train_model(
|
|
|
nhist[epoch, 2] = train_acc
|
|
nhist[epoch, 2] = train_acc
|
|
|
nhist[epoch, 3] = val_acc
|
|
nhist[epoch, 3] = val_acc
|
|
|
|
|
|
|
|
- print(
|
|
|
|
|
|
|
+ log.info(
|
|
|
f"Epoch [{epoch + 1}/{num_epochs}], "
|
|
f"Epoch [{epoch + 1}/{num_epochs}], "
|
|
|
f"Train Loss: {train_loss:.4f}, Val Loss: {val_loss:.4f}, "
|
|
f"Train Loss: {train_loss:.4f}, Val Loss: {val_loss:.4f}, "
|
|
|
f"Train Acc: {train_acc:.4f}, Val Acc: {val_acc:.4f}"
|
|
f"Train Acc: {train_acc:.4f}, Val Acc: {val_acc:.4f}"
|
|
@@ -167,7 +253,9 @@ def train_model(
|
|
|
output_path / "intermediate_models" / f"model_epoch_{epoch + 1}.pt"
|
|
output_path / "intermediate_models" / f"model_epoch_{epoch + 1}.pt"
|
|
|
)
|
|
)
|
|
|
torch.save(model.state_dict(), model_save_path)
|
|
torch.save(model.state_dict(), model_save_path)
|
|
|
- print(f"Model saved at epoch {epoch + 1}")
|
|
|
|
|
|
|
+ log.info(f"Model saved at epoch {epoch + 1}")
|
|
|
|
|
+
|
|
|
|
|
+ epoch_progress.update(total=num_epochs, advance=1)
|
|
|
|
|
|
|
|
# return the trained model and the traning history
|
|
# return the trained model and the traning history
|
|
|
|
|
|
|
@@ -185,6 +273,10 @@ def test_model_bayesian(
|
|
|
test_loader: DataLoader[ADNIDataset],
|
|
test_loader: DataLoader[ADNIDataset],
|
|
|
criterion: nn.Module,
|
|
criterion: nn.Module,
|
|
|
get_kl_loss: KLLossFn,
|
|
get_kl_loss: KLLossFn,
|
|
|
|
|
+ progress: ProgressTracker,
|
|
|
|
|
+ log: PipelineLogger,
|
|
|
|
|
+ stop_event: Event | None = None,
|
|
|
|
|
+ pause_event: Event | None = None,
|
|
|
) -> TestMetrics:
|
|
) -> TestMetrics:
|
|
|
"""
|
|
"""
|
|
|
Tests a Bayesian model on the test dataset with KL-augmented loss.
|
|
Tests a Bayesian model on the test dataset with KL-augmented loss.
|
|
@@ -193,9 +285,13 @@ def test_model_bayesian(
|
|
|
test_loss = 0.0
|
|
test_loss = 0.0
|
|
|
correct = 0
|
|
correct = 0
|
|
|
total = 0
|
|
total = 0
|
|
|
|
|
+ total_samples = 0
|
|
|
|
|
|
|
|
|
|
+ test_progress = progress.get_sub_tracker("Testing Batches")
|
|
|
|
|
+ test_progress.update(total=len(test_loader), advance=0)
|
|
|
with torch.no_grad():
|
|
with torch.no_grad():
|
|
|
for _, (mri, xls, targets, _) in enumerate(test_loader):
|
|
for _, (mri, xls, targets, _) in enumerate(test_loader):
|
|
|
|
|
+ _check_control_events(stop_event=stop_event, pause_event=pause_event)
|
|
|
outputs = model((mri, xls))
|
|
outputs = model((mri, xls))
|
|
|
data_loss = cast(torch.Tensor, criterion(outputs, targets))
|
|
data_loss = cast(torch.Tensor, criterion(outputs, targets))
|
|
|
batch_size = mri.size(0)
|
|
batch_size = mri.size(0)
|
|
@@ -207,13 +303,15 @@ def test_model_bayesian(
|
|
|
)
|
|
)
|
|
|
loss: torch.Tensor = data_loss + kl_loss
|
|
loss: torch.Tensor = data_loss + kl_loss
|
|
|
|
|
|
|
|
- test_loss += loss.item() * (mri.size(0) + xls.size(0))
|
|
|
|
|
|
|
+ test_loss += loss.item() * batch_size
|
|
|
|
|
+ total_samples += batch_size
|
|
|
|
|
|
|
|
- predicted = (outputs > 0.5).float()
|
|
|
|
|
- correct += (predicted == targets).sum().item()
|
|
|
|
|
- total += targets.numel()
|
|
|
|
|
|
|
+ batch_correct, batch_total = _batch_correct_and_total(outputs, targets)
|
|
|
|
|
+ correct += batch_correct
|
|
|
|
|
+ total += batch_total
|
|
|
|
|
+ test_progress.update(total=len(test_loader), advance=1)
|
|
|
|
|
|
|
|
- test_loss /= len(test_loader)
|
|
|
|
|
|
|
+ test_loss = test_loss / total_samples if total_samples > 0 else 0.0
|
|
|
test_acc = correct / total if total > 0 else 0.0
|
|
test_acc = correct / total if total > 0 else 0.0
|
|
|
return test_loss, test_acc
|
|
return test_loss, test_acc
|
|
|
|
|
|
|
@@ -225,14 +323,26 @@ def train_epoch_bayesian(
|
|
|
optimizer: torch.optim.Optimizer,
|
|
optimizer: torch.optim.Optimizer,
|
|
|
criterion: nn.Module,
|
|
criterion: nn.Module,
|
|
|
get_kl_loss: KLLossFn,
|
|
get_kl_loss: KLLossFn,
|
|
|
|
|
+ log: PipelineLogger,
|
|
|
|
|
+ progress: ProgressTracker,
|
|
|
|
|
+ stop_event: Event | None = None,
|
|
|
|
|
+ pause_event: Event | None = None,
|
|
|
) -> TrainMetrics:
|
|
) -> TrainMetrics:
|
|
|
"""
|
|
"""
|
|
|
Trains a Bayesian model for one epoch and evaluates on validation data.
|
|
Trains a Bayesian model for one epoch and evaluates on validation data.
|
|
|
"""
|
|
"""
|
|
|
model.train()
|
|
model.train()
|
|
|
train_loss = 0.0
|
|
train_loss = 0.0
|
|
|
-
|
|
|
|
|
|
|
+ train_correct = 0
|
|
|
|
|
+ train_total = 0
|
|
|
|
|
+ train_total_samples = 0
|
|
|
|
|
+
|
|
|
|
|
+ train_progress = progress.get_sub_tracker("Training Batches")
|
|
|
|
|
+ train_progress.reset()
|
|
|
|
|
+ train_progress.set_title("Training Batches")
|
|
|
|
|
+ train_progress.update(total=len(train_loader), advance=0)
|
|
|
for _, (mri, xls, targets, _) in enumerate(train_loader):
|
|
for _, (mri, xls, targets, _) in enumerate(train_loader):
|
|
|
|
|
+ _check_control_events(stop_event=stop_event, pause_event=pause_event)
|
|
|
optimizer.zero_grad()
|
|
optimizer.zero_grad()
|
|
|
outputs = model((mri, xls))
|
|
outputs = model((mri, xls))
|
|
|
data_loss = cast(torch.Tensor, criterion(outputs, targets))
|
|
data_loss = cast(torch.Tensor, criterion(outputs, targets))
|
|
@@ -246,16 +356,27 @@ def train_epoch_bayesian(
|
|
|
loss: torch.Tensor = data_loss + kl_loss
|
|
loss: torch.Tensor = data_loss + kl_loss
|
|
|
loss.backward() # type: ignore[reportUnknownMemberType]
|
|
loss.backward() # type: ignore[reportUnknownMemberType]
|
|
|
optimizer.step()
|
|
optimizer.step()
|
|
|
- train_loss += loss.item() * (mri.size(0) + xls.size(0))
|
|
|
|
|
- train_loss /= len(train_loader)
|
|
|
|
|
|
|
+ train_loss += loss.item() * batch_size
|
|
|
|
|
+ train_total_samples += batch_size
|
|
|
|
|
+ batch_correct, batch_total = _batch_correct_and_total(outputs, targets)
|
|
|
|
|
+ train_correct += batch_correct
|
|
|
|
|
+ train_total += batch_total
|
|
|
|
|
+ train_progress.update(total=len(train_loader), advance=1)
|
|
|
|
|
+ train_loss = train_loss / train_total_samples if train_total_samples > 0 else 0.0
|
|
|
|
|
|
|
|
model.eval()
|
|
model.eval()
|
|
|
val_loss = 0.0
|
|
val_loss = 0.0
|
|
|
correct = 0
|
|
correct = 0
|
|
|
total = 0
|
|
total = 0
|
|
|
|
|
+ val_total_samples = 0
|
|
|
|
|
|
|
|
|
|
+ test_progress = progress.get_sub_tracker("Validation Batches")
|
|
|
|
|
+ test_progress.reset()
|
|
|
|
|
+ test_progress.set_title("Validation Batches")
|
|
|
|
|
+ test_progress.update(total=len(val_loader), advance=0)
|
|
|
with torch.no_grad():
|
|
with torch.no_grad():
|
|
|
for _, (mri, xls, targets, _) in enumerate(val_loader):
|
|
for _, (mri, xls, targets, _) in enumerate(val_loader):
|
|
|
|
|
+ _check_control_events(stop_event=stop_event, pause_event=pause_event)
|
|
|
outputs = model((mri, xls))
|
|
outputs = model((mri, xls))
|
|
|
data_loss = cast(torch.Tensor, criterion(outputs, targets))
|
|
data_loss = cast(torch.Tensor, criterion(outputs, targets))
|
|
|
batch_size = mri.size(0)
|
|
batch_size = mri.size(0)
|
|
@@ -266,15 +387,17 @@ def train_epoch_bayesian(
|
|
|
else torch.tensor(0.0, device=outputs.device)
|
|
else torch.tensor(0.0, device=outputs.device)
|
|
|
)
|
|
)
|
|
|
vloss: torch.Tensor = data_loss + kl_loss
|
|
vloss: torch.Tensor = data_loss + kl_loss
|
|
|
- val_loss += vloss.item() * (mri.size(0) + xls.size(0))
|
|
|
|
|
|
|
+ val_loss += vloss.item() * batch_size
|
|
|
|
|
+ val_total_samples += batch_size
|
|
|
|
|
|
|
|
- predicted = (outputs > 0.5).float()
|
|
|
|
|
- correct += (predicted == targets).sum().item()
|
|
|
|
|
- total += targets.numel()
|
|
|
|
|
|
|
+ batch_correct, batch_total = _batch_correct_and_total(outputs, targets)
|
|
|
|
|
+ correct += batch_correct
|
|
|
|
|
+ total += batch_total
|
|
|
|
|
+ test_progress.update(total=len(val_loader), advance=1)
|
|
|
|
|
|
|
|
- val_loss /= len(val_loader)
|
|
|
|
|
|
|
+ val_loss = val_loss / val_total_samples if val_total_samples > 0 else 0.0
|
|
|
val_acc = correct / total if total > 0 else 0.0
|
|
val_acc = correct / total if total > 0 else 0.0
|
|
|
- train_acc = correct / total if total > 0 else 0.0
|
|
|
|
|
|
|
+ train_acc = train_correct / train_total if train_total > 0 else 0.0
|
|
|
|
|
|
|
|
return train_loss, val_loss, train_acc, val_acc
|
|
return train_loss, val_loss, train_acc, val_acc
|
|
|
|
|
|
|
@@ -288,13 +411,20 @@ def train_model_bayesian(
|
|
|
num_epochs: int,
|
|
num_epochs: int,
|
|
|
output_path: pl.Path,
|
|
output_path: pl.Path,
|
|
|
get_kl_loss: KLLossFn,
|
|
get_kl_loss: KLLossFn,
|
|
|
|
|
+ progress: ProgressTracker,
|
|
|
|
|
+ log: PipelineLogger,
|
|
|
|
|
+ stop_event: Event | None = None,
|
|
|
|
|
+ pause_event: Event | None = None,
|
|
|
) -> Tuple[nn.Module, pd.DataFrame]:
|
|
) -> Tuple[nn.Module, pd.DataFrame]:
|
|
|
"""
|
|
"""
|
|
|
Trains a Bayesian model with KL-augmented objective.
|
|
Trains a Bayesian model with KL-augmented objective.
|
|
|
"""
|
|
"""
|
|
|
nhist = np.zeros((num_epochs, 4), dtype=np.float32)
|
|
nhist = np.zeros((num_epochs, 4), dtype=np.float32)
|
|
|
|
|
|
|
|
|
|
+ epoch_progress = progress.get_sub_tracker("Epochs")
|
|
|
|
|
+ epoch_progress.update(total=num_epochs, advance=0)
|
|
|
for epoch in range(num_epochs):
|
|
for epoch in range(num_epochs):
|
|
|
|
|
+ _check_control_events(stop_event=stop_event, pause_event=pause_event)
|
|
|
train_loss, val_loss, train_acc, val_acc = train_epoch_bayesian(
|
|
train_loss, val_loss, train_acc, val_acc = train_epoch_bayesian(
|
|
|
model,
|
|
model,
|
|
|
train_loader,
|
|
train_loader,
|
|
@@ -302,6 +432,10 @@ def train_model_bayesian(
|
|
|
optimizer,
|
|
optimizer,
|
|
|
criterion,
|
|
criterion,
|
|
|
get_kl_loss,
|
|
get_kl_loss,
|
|
|
|
|
+ log=log,
|
|
|
|
|
+ progress=epoch_progress,
|
|
|
|
|
+ stop_event=stop_event,
|
|
|
|
|
+ pause_event=pause_event,
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
nhist[epoch, 0] = train_loss
|
|
nhist[epoch, 0] = train_loss
|
|
@@ -309,7 +443,7 @@ def train_model_bayesian(
|
|
|
nhist[epoch, 2] = train_acc
|
|
nhist[epoch, 2] = train_acc
|
|
|
nhist[epoch, 3] = val_acc
|
|
nhist[epoch, 3] = val_acc
|
|
|
|
|
|
|
|
- print(
|
|
|
|
|
|
|
+ log.info(
|
|
|
f"Epoch [{epoch + 1}/{num_epochs}], "
|
|
f"Epoch [{epoch + 1}/{num_epochs}], "
|
|
|
f"Train Loss: {train_loss:.4f}, Val Loss: {val_loss:.4f}, "
|
|
f"Train Loss: {train_loss:.4f}, Val Loss: {val_loss:.4f}, "
|
|
|
f"Train Acc: {train_acc:.4f}, Val Acc: {val_acc:.4f}"
|
|
f"Train Acc: {train_acc:.4f}, Val Acc: {val_acc:.4f}"
|
|
@@ -321,7 +455,9 @@ def train_model_bayesian(
|
|
|
output_path / "intermediate_models" / f"model_epoch_{epoch + 1}.pt"
|
|
output_path / "intermediate_models" / f"model_epoch_{epoch + 1}.pt"
|
|
|
)
|
|
)
|
|
|
torch.save(model.state_dict(), model_save_path)
|
|
torch.save(model.state_dict(), model_save_path)
|
|
|
- print(f"Model saved at epoch {epoch + 1}")
|
|
|
|
|
|
|
+ log.info(f"Model saved at epoch {epoch + 1}")
|
|
|
|
|
+
|
|
|
|
|
+ epoch_progress.update(total=num_epochs, advance=1)
|
|
|
|
|
|
|
|
history = pd.DataFrame(
|
|
history = pd.DataFrame(
|
|
|
data=nhist.astype(np.float32),
|
|
data=nhist.astype(np.float32),
|