Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "deepaudio-x"
version = "0.4.3"
version = "0.4.4"
description = "DeepAudio-X: Self-supervised audio toolkit for audio classification and beyond."
authors = [
{ name = "Christos Nikou", email = "chrisnick92@gmail.com" },
Expand Down
2 changes: 1 addition & 1 deletion src/deepaudiox/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
This page provides the core API reference for DeepAudioX.
"""

__version__ = "0.4.3"
__version__ = "0.4.4"

from deepaudiox.datasets.audio_classification_dataset import ( # noqa: F401
AudioClassificationDataset,
Expand Down
14 changes: 8 additions & 6 deletions src/deepaudiox/callbacks/checkpointer.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,12 @@ def on_epoch_end(self, trainer):
if trainer.state.lowest_loss > latest_validation_loss:
decrease_percentage = (trainer.state.lowest_loss - latest_validation_loss) / trainer.state.lowest_loss * 100

self.logger.info(
f"[CHECKPOINTER] Validation loss decreased: "
f"({trainer.state.lowest_loss:.6f} --> {latest_validation_loss:.6f}), "
f"{GREEN}(-{decrease_percentage:.2f}%){ENDC}."
)
if trainer.verbose:
self.logger.info(
f"[CHECKPOINTER] Validation loss decreased: "
f"({trainer.state.lowest_loss:.6f} --> {latest_validation_loss:.6f}), "
f"{GREEN}(-{decrease_percentage:.2f}%){ENDC}."
)

trainer.state.lowest_loss = latest_validation_loss

Expand All @@ -61,7 +62,8 @@ def on_epoch_end(self, trainer):
},
self.path_to_checkpoint,
)
self.logger.info(f"[CHECKPOINTER] Checkpoint saved successfully at: {self.path_to_checkpoint}")
if trainer.verbose:
self.logger.info(f"[CHECKPOINTER] Checkpoint saved successfully at: {self.path_to_checkpoint}")
except PermissionError:
self.logger.info(f"[CHECKPOINTER] Permission denied: cannot write to {self.path_to_checkpoint}")
except FileNotFoundError:
Expand Down
79 changes: 0 additions & 79 deletions src/deepaudiox/callbacks/console_logger.py

This file was deleted.

Empty file.
25 changes: 17 additions & 8 deletions src/deepaudiox/loops/evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from torch.utils.data import DataLoader
from tqdm import tqdm

from deepaudiox.callbacks.console_logger import ConsoleLogger
from deepaudiox.callbacks.reporter import Reporter
from deepaudiox.datasets.audio_classification_dataset import AudioClassificationDataset
from deepaudiox.modules.baseclasses import BaseAudioClassifier
Expand Down Expand Up @@ -36,11 +35,12 @@ class Evaluator:

Attributes:
state (State): Stores testing variables.
verbose (bool): Whether to log the evaluation report after testing.
device (str): The device used for testing.
class_mapping (dict): A mapping between class names and IDs.
logger (logging.Logger): A module used for logging messages.
test_dloader (torch.DataLoader): The DataLoader of the testing set.
model (BaseAudioClassifier): An AudioClassifier module inhereting from BaseAudioClassifier.
model (BaseAudioClassifier): An AudioClassifier module inheriting from BaseAudioClassifier.
callbacks (list): A list of callbacks used throughout the testing lifecycle.
"""

Expand All @@ -53,19 +53,22 @@ def __init__(
num_workers: int = 4,
device: DeviceName = "cuda",
device_index: int | None = None,
verbose: bool = True,
):
"""Initialize the Evaluator.

Args:
test_dset (AudioClassificationDataset): The testing dataset.
model (BaseAudioClassifier): An AudioClassifier module inhereting from BaseAudioClassifier.
model (BaseAudioClassifier): An AudioClassifier module inheriting from BaseAudioClassifier.
class_mapping (dict): A mapping between class names and IDs.
batch_size (int, optional): The batch size for Python Data Loaders. Defaults to 16.
num_workers (int, optional): The number of workers for Python Data Loaders. Defaults to 4.
device (DeviceName): The device to use for evaluation. One of ``"cuda"``, ``"mps"``, or ``"cpu"``.
Defaults to ``"cuda"``.
device_index (int | None): The GPU device index. Only applicable when ``device="cuda"``.
If ``None``, uses the default CUDA device.
verbose (bool): If True, prints the classification report, confusion matrix, and average
posteriors after evaluation. "Evaluation has finished." is always printed. Defaults to True.

Example:
>>> import torch
Expand All @@ -83,6 +86,7 @@ def __init__(
>>> evaluator.evaluate()
"""
self.state = State()
self.verbose = verbose
self.device = get_device(device=device, device_index=device_index)
self.class_mapping = class_mapping

Expand All @@ -106,7 +110,7 @@ def __init__(
self.model.eval()

# Configure callbacks
self.callbacks = [ConsoleLogger(logger=self.logger), Reporter(logger=self.logger)]
self.callbacks = [Reporter(logger=self.logger)]

@torch.inference_mode()
def evaluate(self) -> None:
Expand All @@ -116,6 +120,10 @@ def evaluate(self) -> None:
predicted labels, and posterior probabilities into ``self.state``, then
triggers the registered callbacks via ``on_testing_end``.

Always prints "Evaluation has finished." regardless of ``verbose``.
The ``Reporter`` callback (classification report, confusion matrix, average
posteriors) is only executed when ``verbose=True``.

After this method returns, ``self.state`` holds:
- ``y_true`` (np.ndarray): Ground-truth class indices, shape (N,).
- ``y_pred`` (np.ndarray): Predicted class indices, shape (N,).
Expand All @@ -124,7 +132,6 @@ def evaluate(self) -> None:
Note:
The model is expected to already be in eval mode (set in ``__init__``).
Runs under ``torch.inference_mode()`` — gradients are fully disabled.
Callbacks (``ConsoleLogger``, ``Reporter``) are executed once after the loop.
"""
# Lists to accumulate evaluation results, i.e., true_labels, prediction_labels, and posteriors
y_true_batches, y_pred_batches, posterior_batches = [], [], []
Expand All @@ -149,6 +156,8 @@ def evaluate(self) -> None:
self.state.y_pred = np.concatenate(y_pred_batches)
self.state.posteriors = np.concatenate(posterior_batches)

# Execute callbacks at the end of testing
for cb in self.callbacks:
cb.on_testing_end(self)
self.logger.info("Evaluation has finished.")

if self.verbose:
for cb in self.callbacks:
cb.on_testing_end(self)
58 changes: 49 additions & 9 deletions src/deepaudiox/loops/trainer.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import time
from dataclasses import dataclass, field

import numpy as np
Expand All @@ -9,7 +10,6 @@
from tqdm import tqdm

from deepaudiox.callbacks.checkpointer import Checkpointer
from deepaudiox.callbacks.console_logger import ConsoleLogger
from deepaudiox.callbacks.early_stopper import EarlyStopper
from deepaudiox.datasets.audio_classification_dataset import AudioClassificationDataset
from deepaudiox.modules.baseclasses import BaseAudioClassifier
Expand Down Expand Up @@ -46,15 +46,16 @@ class Trainer:
Attributes:
state (State): Stores training variables.
epochs (int): The maximum number of training epochs.
verbose (bool): Whether to log epoch-level artifacts.
device (str): The device used for training.
logger (logging.Logger): A module used for logging messages.
train_dloader (torch.DataLoader): The DataLoader of the training set.
validation_dloader (torch.DataLoader): The DataLoader of the validation set.
model (BaseAudioClassifier): The BaseAudioClassifier to be trained.
optimizer (torch.optim.Optimizer): The optimizer of the training process.
lr_scheduler (torch.optim.Optimizer): The scheduler of the training process.
scheduler (LRScheduler): The learning rate scheduler of the training process.
loss_function (nn.Module): The loss function used for optimization.
callbacks (list): A list of callbacks used throught the training lifecycle.
callbacks (list): A list of callbacks used throughout the training lifecycle.

"""

Expand All @@ -75,6 +76,7 @@ def __init__(
path_to_checkpoint: str = "checkpoint.pt",
device: DeviceName = "cuda",
device_index: int | None = None,
verbose: bool = True,
):
"""Initialize the Trainer.

Expand All @@ -97,6 +99,8 @@ def __init__(
Defaults to ``"cuda"``.
device_index (int | None): The GPU device index. Only applicable when ``device="cuda"``.
If ``None``, uses the default CUDA device.
verbose (bool): If True, logs epoch-level artifacts (loss, time). If False, only
start/end messages and the final training summary are printed. Defaults to True.

Example:
>>> from deepaudiox import AudioClassifier, Trainer
Expand All @@ -114,6 +118,8 @@ def __init__(
# Configure training state
self.state = State()
self.epochs = epochs
self.verbose = verbose
self.path_to_checkpoint = path_to_checkpoint
self.device = get_device(device=device, device_index=device_index)

# Configure logger
Expand All @@ -137,10 +143,10 @@ def __init__(

# Configure callbacks
self.callbacks = [
ConsoleLogger(logger=self.logger),
Checkpointer(path_to_checkpoint=path_to_checkpoint, logger=self.logger),
EarlyStopper(patience=patience, logger=self.logger),
]
self._epoch_start_time: float = 0.0

def train_step(self) -> float:
"""Run one pass over the training set.
Expand Down Expand Up @@ -188,10 +194,10 @@ def val_step(self) -> float:
def epoch_step(self) -> tuple[float, float]:
"""Run one complete training epoch.

Executes ``on_epoch_start`` callbacks, calls ``train_step()`` and
``val_step()``, updates the LR scheduler and ``self.state``, then
executes ``on_epoch_end`` callbacks (which may trigger early stopping
or checkpointing).
Logs the epoch header and metrics when ``verbose=True``, calls ``train_step()``
and ``val_step()``, updates the LR scheduler and
``self.state``, then executes ``on_epoch_end`` callbacks (which may trigger
early stopping or checkpointing).

Note:
``self.state.current_epoch`` must be set by the caller before
Expand Down Expand Up @@ -220,6 +226,10 @@ def epoch_step(self) -> tuple[float, float]:
... if trainer.state.early_stop:
... break
"""
self._epoch_start_time = time.time()
if self.verbose:
self.logger.info(f"[Epoch {self.state.current_epoch}/{self.epochs}]")

for cb in self.callbacks:
cb.on_epoch_start(self)

Expand All @@ -234,13 +244,28 @@ def epoch_step(self) -> tuple[float, float]:
self.state.train_loss.append(train_loss)
self.state.validation_loss.append(val_loss)

if self.verbose:
elapsed = time.time() - self._epoch_start_time
self.logger.info(
f"Epoch {self.state.current_epoch} | "
f"Train Loss: {train_loss:.4f} | "
f"Val. Loss: {val_loss:.4f} | "
f"Time: {elapsed:.2f}s"
)

for cb in self.callbacks:
cb.on_epoch_end(self)

return train_loss, val_loss

def train(self) -> None:
"""Perform the full training process."""
"""Perform the full training process.

Epoch-level output is controlled by ``verbose``. The training summary
(best epoch, losses, checkpoint path) is always printed on completion.
"""
self.logger.info("Training has started.")

for cb in self.callbacks:
cb.on_train_start(self)

Expand All @@ -253,6 +278,21 @@ def train(self) -> None:
for cb in self.callbacks:
cb.on_train_end(self)

best_idx = int(np.argmin(self.state.validation_loss))
best_val_loss = self.state.validation_loss[best_idx]
best_train_loss = self.state.train_loss[best_idx]
best_epoch = best_idx + 1
sep = "─" * 52
self.logger.info(
f"\n{sep}\n"
f" Training Complete\n"
f" Best Epoch : {best_epoch}\n"
f" Train Loss : {best_train_loss:.6f}\n"
f" Val. Loss : {best_val_loss:.6f}\n"
f" Checkpoint : {self.path_to_checkpoint}\n"
f"{sep}"
)

def _setup_dataloaders(
self,
train_dset: AudioClassificationDataset,
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading