aboutsummaryrefslogtreecommitdiffstats
path: root/.agents/skills/architecture-design/examples
diff options
context:
space:
mode:
authorsillylaird <sillyfanboy@gmail.com>2026-09-03 00:33:59 +0000
committersillylaird <sillyfanboy@gmail.com>2026-09-03 00:33:59 +0000
commit898b52edcb47bcb3e9d6106e74ca73e74ea01e70 (patch)
tree85c6ee5ad58b860144551184d4cf86b560c62b91 /.agents/skills/architecture-design/examples
downloadwww-898b52edcb47bcb3e9d6106e74ca73e74ea01e70.tar.gz
www-898b52edcb47bcb3e9d6106e74ca73e74ea01e70.zip
import live www.sillylaird.ca webrootHEADmain
Diffstat (limited to '')
-rw-r--r--.agents/skills/architecture-design/examples/augmentation_example.py117
-rw-r--r--.agents/skills/architecture-design/examples/config_example.yaml131
-rw-r--r--.agents/skills/architecture-design/examples/custom_dataset.py50
-rw-r--r--.agents/skills/architecture-design/examples/custom_model.py217
-rwxr-xr-x.agents/skills/architecture-design/examples/pipeline_example.sh189
5 files changed, 704 insertions, 0 deletions
diff --git a/.agents/skills/architecture-design/examples/augmentation_example.py b/.agents/skills/architecture-design/examples/augmentation_example.py
new file mode 100644
index 0000000..949b033
--- /dev/null
+++ b/.agents/skills/architecture-design/examples/augmentation_example.py
@@ -0,0 +1,117 @@
+"""
+Data Augmentation Example
+
+Demonstrates how to create a custom data augmentation function
+following the architecture design pattern.
+"""
+
+import torch
+from typing import Dict
+from src.data_module.augmentation import register_augmentation
+
+
+@register_augmentation("time_shift")
+def time_shift(signal: torch.Tensor, max_shift: int = 10) -> torch.Tensor:
+ """Randomly shift signal in time.
+
+ Args:
+ signal: Input signal tensor of shape (channels, time_steps)
+ max_shift: Maximum number of steps to shift
+
+ Returns:
+ Shifted signal tensor
+ """
+ shift = torch.randint(-max_shift, max_shift + 1, (1,)).item()
+ return torch.roll(signal, shifts=shift, dims=-1)
+
+
+@register_augmentation("amplitude_scale")
+def amplitude_scale(
+ signal: torch.Tensor,
+ min_scale: float = 0.8,
+ max_scale: float = 1.2
+) -> torch.Tensor:
+ """Randomly scale signal amplitude.
+
+ Args:
+ signal: Input signal tensor of shape (channels, time_steps)
+ min_scale: Minimum scaling factor
+ max_scale: Maximum scaling factor
+
+ Returns:
+ Scaled signal tensor
+ """
+ scale = torch.empty(1).uniform_(min_scale, max_scale).item()
+ return signal * scale
+
+
+@register_augmentation("gaussian_noise")
+def add_gaussian_noise(
+ signal: torch.Tensor,
+ mean: float = 0.0,
+ std: float = 0.1
+) -> torch.Tensor:
+ """Add Gaussian noise to signal.
+
+ Args:
+ signal: Input signal tensor of shape (channels, time_steps)
+ mean: Mean of Gaussian noise
+ std: Standard deviation of Gaussian noise
+
+ Returns:
+ Signal with added noise
+ """
+ noise = torch.randn_like(signal) * std + mean
+ return signal + noise
+
+
+# Example: Composed augmentation
+@register_augmentation("composed")
+def composed_augmentation(signal: torch.Tensor, cfg) -> torch.Tensor:
+ """Apply multiple augmentations in sequence.
+
+ Args:
+ signal: Input signal tensor
+ cfg: Configuration object with augmentation parameters
+
+ Returns:
+ Augmented signal tensor
+ """
+ # Apply each augmentation based on config
+ if cfg.augmentation.time_shift:
+ signal = time_shift(signal, cfg.augmentation.max_shift)
+
+ if cfg.augmentation.amplitude_scale:
+ signal = amplitude_scale(
+ signal,
+ cfg.augmentation.min_scale,
+ cfg.augmentation.max_scale
+ )
+
+ if cfg.augmentation.gaussian_noise:
+ signal = add_gaussian_noise(
+ signal,
+ cfg.augmentation.noise_mean,
+ cfg.augmentation.noise_std
+ )
+
+ return signal
+
+
+# Usage in dataset class
+class AugmentedDataset:
+ """Example dataset with augmentation support."""
+
+ def __init__(self, cfg):
+ self.cfg = cfg
+ self.augmentation_fn = AugmentationFactory(cfg.augmentation.name)
+
+ def __getitem__(self, idx: int) -> Dict[str, torch.Tensor]:
+ # Load signal
+ signal = self.load_signal(idx)
+
+ # Apply augmentation (training mode only)
+ if self.training and self.augmentation_fn:
+ signal = self.augmentation_fn(signal, self.cfg)
+
+ return {"signal": signal, "label": self.labels[idx]}
diff --git a/.agents/skills/architecture-design/examples/config_example.yaml b/.agents/skills/architecture-design/examples/config_example.yaml
new file mode 100644
index 0000000..8b61442
--- /dev/null
+++ b/.agents/skills/architecture-design/examples/config_example.yaml
@@ -0,0 +1,131 @@
+# Hydra Configuration Example
+# This demonstrates the config structure for training pipeline
+
+# Run with: python train.py --config-name=config_example
+
+defaults:
+ - training: default
+ - dataset: brain_decoder
+ - model: transformer
+ - override hydra/launcher: submitit_local
+
+# Project settings
+project_name: brain_decoder
+experiment_name: transformer_baseline
+
+# Random seed
+seed: 42
+
+# Device settings
+device: cuda
+num_workers: 4
+pin_memory: true
+
+# Training configuration
+training:
+ epochs: 100
+ batch_size: 32
+ learning_rate: 0.001
+ weight_decay: 0.0001
+ gradient_clip: 1.0
+ early_stopping:
+ patience: 10
+ min_delta: 0.001
+
+ # Optimizer settings
+ optimizer: adamw
+ optimizer_params:
+ betas: [0.9, 0.999]
+ eps: 1.0e-08
+
+ # Scheduler settings
+ scheduler: cosine
+ scheduler_params:
+ warmup_epochs: 10
+ min_lr: 1.0e-06
+
+ # Checkpoint settings
+ checkpoint:
+ save_every: 5
+ save_best: true
+ monitor: val_loss
+ mode: min
+
+# Dataset configuration
+dataset:
+ name: brain_decoder
+ task: movement_classification
+ target_size:
+ movement_classification: 5
+ reconstruction: [64, 64]
+
+ # Data paths
+ data_dir: ${dir.data_dir}/processed
+ train_split: train
+ val_split: val
+ test_split: test
+
+ # Data loading
+ num_channels: 64
+ sampling_rate: 1000
+ sequence_length: 1000
+
+ # Augmentation
+ augmentation:
+ name: composed
+ time_shift: true
+ amplitude_scale: true
+ gaussian_noise: true
+ max_shift: 10
+ min_scale: 0.8
+ max_scale: 1.2
+ noise_mean: 0.0
+ noise_std: 0.1
+
+# Model configuration
+model:
+ name: Transformer
+ hidden_dim: 256
+ num_heads: 8
+ num_layers: 6
+ dropout: 0.1
+ activation: gelu
+
+ # Architecture specific
+ encoder:
+ input_dim: 64
+ embedding_dim: 256
+ positional_encoding: true
+
+ decoder:
+ output_dim: ${dataset.target_size.${dataset.task}}
+ pooling: avg
+
+# Logging configuration
+logging:
+ logger: wandb
+ log_every: 10
+ log_grads: false
+
+ # TensorBoard
+ tensorboard: true
+ histogram: true
+
+ # W&B
+ wandb:
+ project: ${project_name}
+ entity: null
+ tags: ["baseline", "transformer"]
+
+# Output directories
+dir:
+ data_dir: ./data
+ output_dir: ./outputs
+ log_dir: ${dir.output_dir}/logs
+ checkpoint_dir: ${dir.output_dir}/checkpoints
+ figure_dir: ${dir.output_dir}/figures
+ table_dir: ${dir.output_dir}/tables
+
+# Debug settings
+debug: false
+fast_dev_run: false
diff --git a/.agents/skills/architecture-design/examples/custom_dataset.py b/.agents/skills/architecture-design/examples/custom_dataset.py
new file mode 100644
index 0000000..c9fe002
--- /dev/null
+++ b/.agents/skills/architecture-design/examples/custom_dataset.py
@@ -0,0 +1,50 @@
+"""
+Example: Creating a Custom Dataset
+
+This example shows how to add a new dataset following the project architecture.
+"""
+
+from torch.utils.data import Dataset
+from typing import Dict
+import torch
+from src.data_module.dataset import register_dataset
+
+
+@register_dataset("time_series")
+class TimeSeriesDataset(Dataset):
+ """
+ Time series dataset for sequence modeling.
+
+ Args:
+ sequences: List of time series sequences
+ seq_length: Fixed sequence length (pad or truncate if needed)
+ """
+
+ def __init__(self, sequences: list, seq_length: int = 100):
+ self.sequences = sequences
+ self.seq_length = seq_length
+
+ def __len__(self) -> int:
+ return len(self.sequences)
+
+ def __getitem__(self, i: int) -> Dict[str, torch.Tensor]:
+ sequence = self.sequences[i]
+
+ # Pad or truncate to fixed length
+ if len(sequence) < self.seq_length:
+ padding = torch.zeros(self.seq_length - len(sequence))
+ sequence = torch.cat([sequence, padding])
+ else:
+ sequence = sequence[:self.seq_length]
+
+ return {
+ "input": sequence,
+ "label": sequence, # For autoencoder, etc.
+ "length": torch.tensor(min(len(self.sequences[i]), self.seq_length))
+ }
+
+
+# Usage in training:
+# from src.data_module.dataset import DatasetFactory
+# dataset = DatasetFactory("time_series")(sequences=training_data, seq_length=128)
+# dataloader = DataLoader(dataset, batch_size=32, shuffle=True)
diff --git a/.agents/skills/architecture-design/examples/custom_model.py b/.agents/skills/architecture-design/examples/custom_model.py
new file mode 100644
index 0000000..0e00752
--- /dev/null
+++ b/.agents/skills/architecture-design/examples/custom_model.py
@@ -0,0 +1,217 @@
+"""
+Example: Creating a Custom Model
+
+This example shows how to add a new model following the project architecture.
+IMPORTANT: Models use a config-driven pattern where __init__ only accepts cfg.
+
+Key Requirements:
+- Use @register_model('ModelName') decorator
+- __init__ accepts ONLY cfg parameter
+- All hyperparameters come from cfg (cfg.model.*, cfg.dataset.*, etc.)
+- forward() returns dict: {"loss": loss, "labels": labels, "logits": logits}
+"""
+
+import torch
+import torch.nn as nn
+import torch.nn.functional as F
+from typing import Dict, Optional
+
+# Import the register_model decorator
+# Location may vary: src.model_module.brain_decoder or src.model_module.model
+from src.model_module.brain_decoder import register_model
+
+
+@register_model('SimpleMLP')
+class SimpleMLP(nn.Module):
+ """
+ Simple Multi-Layer Perceptron for classification tasks.
+
+ Config structure ( Hydra YAML ):
+ model:
+ input_dim: 100
+ hidden_dim: 256
+ output_dim: 10
+ num_layers: 3
+ dropout: 0.1
+ dataset:
+ task: classification # Used to get target_size
+ target_size:
+ classification: 10
+ """
+
+ def __init__(self, cfg):
+ super().__init__()
+
+ # Store config
+ self.cfg = cfg
+
+ # Get task info from config
+ self.task = cfg.dataset.task
+
+ # Build model - ALL parameters from cfg
+ self.input_dim = cfg.model.input_dim
+ self.hidden_dim = cfg.model.get('hidden_dim', 256)
+ self.output_dim = cfg.dataset.target_size[cfg.dataset.task]
+ self.num_layers = cfg.model.get('num_layers', 3)
+ self.dropout = cfg.model.get('dropout', 0.1)
+
+ # Build layers
+ layers = []
+ in_dim = self.input_dim
+
+ for i in range(self.num_layers):
+ layers.extend([
+ nn.Linear(in_dim, self.hidden_dim),
+ nn.ReLU(),
+ nn.Dropout(self.dropout)
+ ])
+ in_dim = self.hidden_dim
+
+ # Output layer
+ layers.append(nn.Linear(self.hidden_dim, self.output_dim))
+ self.network = nn.Sequential(*layers)
+
+ # Loss function
+ self.loss_fn = nn.CrossEntropyLoss()
+
+ def forward(
+ self,
+ x: torch.Tensor,
+ labels: Optional[torch.Tensor] = None,
+ **kwargs
+ ) -> Dict[str, Optional[torch.Tensor]]:
+ """
+ Forward pass.
+
+ Args:
+ x: Input tensor of shape (batch_size, input_dim)
+ labels: Ground truth labels (optional, for training)
+
+ Returns:
+ Dictionary with:
+ - loss: Computed loss (None if labels not provided)
+ - labels: Ground truth labels
+ - logits: Model predictions
+ """
+ logits = self.network(x)
+
+ loss = None
+ if labels is not None:
+ # Convert labels to long type if needed
+ if labels.dtype != torch.long:
+ labels = labels.long()
+ loss = self.loss_fn(logits, labels)
+
+ return {
+ "loss": loss,
+ "labels": labels,
+ "logits": logits
+ }
+
+
+# ============================================
+# Example with Training/Inference Modes
+# ============================================
+
+@register_model('SimpleMLPWithModes')
+class SimpleMLPWithModes(nn.Module):
+ """
+ MLP with separate training and inference logic.
+ Shows how to handle different modes using self.training.
+ """
+
+ def __init__(self, cfg):
+ super().__init__()
+ self.cfg = cfg
+ self.task = cfg.dataset.task
+
+ self.input_dim = cfg.model.input_dim
+ self.hidden_dim = cfg.model.get('hidden_dim', 256)
+ self.output_dim = cfg.dataset.target_size[cfg.dataset.task]
+
+ self.fc_in = nn.Linear(self.input_dim, self.hidden_dim)
+ self.ln = nn.LayerNorm(self.hidden_dim)
+ self.fc_out = nn.Linear(self.hidden_dim, self.output_dim)
+ self.loss_fn = nn.CrossEntropyLoss()
+
+ # Test-time augmentation config
+ self.tta_times = cfg.model.get('tta_times', 1)
+
+ def forward(
+ self,
+ x: torch.Tensor,
+ labels: Optional[torch.Tensor] = None,
+ **kwargs
+ ) -> Dict[str, Optional[torch.Tensor]]:
+ """
+ Forward pass with training/inference modes.
+ """
+ if self.training:
+ # Training mode
+ x = x.float()
+ x = self.fc_in(x)
+ x = self.ln(x)
+ x = F.relu(x)
+ logits = self.fc_out(x)
+
+ loss = None
+ if labels is not None:
+ if labels.dtype != torch.long:
+ labels = labels.long()
+ loss = self.loss_fn(logits, labels)
+
+ return {
+ "loss": loss,
+ "labels": labels,
+ "logits": logits
+ }
+ else:
+ # Inference mode with TTA
+ all_logits = []
+ with torch.no_grad():
+ x = x.float()
+ for _ in range(self.tta_times):
+ x_aug = x.clone()
+ # Apply TTA transformations here if needed
+
+ x_aug = self.fc_in(x_aug)
+ x_aug = self.ln(x_aug)
+ x_aug = F.relu(x_aug)
+ logits = self.fc_out(x_aug)
+ all_logits.append(logits)
+
+ # Average predictions
+ avg_logits = torch.mean(torch.stack(all_logits), dim=0)
+
+ loss = None
+ if labels is not None:
+ if labels.dtype != torch.long:
+ labels = labels.long()
+ loss = self.loss_fn(avg_logits, labels)
+
+ return {
+ "loss": loss,
+ "labels": labels,
+ "logits": avg_logits
+ }
+
+
+# ============================================
+# Config Example (Hydra YAML)
+# ============================================
+"""
+# run/conf/model/simple_mlp.yaml
+
+model:
+ name: SimpleMLP
+ input_dim: 100
+ hidden_dim: 256
+ output_dim: 10
+ num_layers: 3
+ dropout: 0.1
+ tta_times: 1
+
+# Then in training pipeline:
+# from src.model_module.brain_decoder import ModelFactory
+# model = ModelFactory(cfg.model.name)(cfg)
+"""
diff --git a/.agents/skills/architecture-design/examples/pipeline_example.sh b/.agents/skills/architecture-design/examples/pipeline_example.sh
new file mode 100755
index 0000000..abd7b46
--- /dev/null
+++ b/.agents/skills/architecture-design/examples/pipeline_example.sh
@@ -0,0 +1,189 @@
+#!/bin/bash
+###############################################################################
+# Training Pipeline Script
+#
+# This script demonstrates the standard training pipeline execution pattern.
+# It handles environment setup, configuration, and execution with proper
+# error handling and logging.
+#
+# Usage:
+# ./run/pipeline/training/train.sh --config-name=config_example
+###############################################################################
+
+set -e # Exit on error
+set -o pipefail # Exit on pipe failure
+
+# Script configuration
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
+EXPERIMENT_NAME="baseline_experiment"
+TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
+
+# Colors for output
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+NC='\033[0m' # No Color
+
+###############################################################################
+# Helper Functions
+###############################################################################
+
+log_info() {
+ echo -e "${GREEN}[INFO]${NC} $1"
+}
+
+log_warn() {
+ echo -e "${YELLOW}[WARN]${NC} $1"
+}
+
+log_error() {
+ echo -e "${RED}[ERROR]${NC} $1"
+}
+
+cleanup() {
+ log_info "Cleaning up..."
+ # Add cleanup logic here (e.g., kill background processes)
+}
+
+trap cleanup EXIT
+
+###############################################################################
+# Environment Setup
+###############################################################################
+
+setup_environment() {
+ log_info "Setting up environment..."
+
+ # Activate virtual environment if it exists
+ if [ -f "${PROJECT_ROOT}/.venv/bin/activate" ]; then
+ source "${PROJECT_ROOT}/.venv/bin/activate"
+ log_info "Activated virtual environment"
+ fi
+
+ # Check required commands
+ command -v python >/dev/null 2>&1 || { log_error "Python not found"; exit 1; }
+
+ # Set Python path
+ export PYTHONPATH="${PROJECT_ROOT}/src:${PYTHONPATH}"
+ log_info "PYTHONPATH set to: ${PYTHONPATH}"
+}
+
+###############################################################################
+# Configuration
+###############################################################################
+
+parse_arguments() {
+ # Default values
+ CONFIG="default"
+ GPUS=0
+ SEED=42
+
+ # Parse arguments
+ while [[ $# -gt 0 ]]; do
+ case $1 in
+ --config-name|-c)
+ CONFIG="$2"
+ shift 2
+ ;;
+ --gpus|-g)
+ GPUS="$2"
+ shift 2
+ ;;
+ --seed|-s)
+ SEED="$2"
+ shift 2
+ ;;
+ *)
+ log_warn "Unknown argument: $1"
+ shift
+ ;;
+ esac
+ done
+
+ log_info "Configuration: ${CONFIG}"
+ log_info "GPUs: ${GPUS}"
+ log_info "Seed: ${SEED}"
+}
+
+###############################################################################
+# Main Training Function
+###############################################################################
+
+run_training() {
+ log_info "Starting training..."
+
+ # Output directory for this run
+ OUTPUT_DIR="${PROJECT_ROOT}/outputs/${EXPERIMENT_NAME}/${TIMESTAMP}"
+ mkdir -p "${OUTPUT_DIR}"
+
+ log_info "Output directory: ${OUTPUT_DIR}"
+
+ # Training command with Hydra
+ python "${PROJECT_ROOT}/train.py" \
+ --config-name="${CONFIG}" \
+ seed=${SEED} \
+ dir.output_dir="${OUTPUT_DIR}" \
+ training.device=cuda \
+ hydra.output_dir="${OUTPUT_DIR}/hydra" \
+ hydra.run.dir="${OUTPUT_DIR}/hydra" || {
+ log_error "Training failed!"
+ exit 1
+ }
+
+ log_info "Training completed successfully!"
+}
+
+###############################################################################
+# Post-Processing
+###############################################################################
+
+post_process() {
+ log_info "Post-processing results..."
+
+ # Copy logs to output directory
+ if [ -f "${OUTPUT_DIR}/hydra/*.log" ]; then
+ cp "${OUTPUT_DIR}/hydra/"*.log "${OUTPUT_DIR}/"
+ fi
+
+ # Generate summary
+ log_info "Run summary:"
+ log_info " Config: ${CONFIG}"
+ log_info " Seed: ${SEED}"
+ log_info " Output: ${OUTPUT_DIR}"
+
+ # Print path to best checkpoint
+ BEST_CHECKPOINT=$(find "${OUTPUT_DIR}" -name "best*.pt" | head -n 1)
+ if [ -n "${BEST_CHECKPOINT}" ]; then
+ log_info " Best checkpoint: ${BEST_CHECKPOINT}"
+ fi
+}
+
+###############################################################################
+# Main Execution
+###############################################################################
+
+main() {
+ log_info "=========================================="
+ log_info "Training Pipeline"
+ log_info "=========================================="
+
+ # Setup
+ setup_environment
+
+ # Parse arguments
+ parse_arguments "$@"
+
+ # Run training
+ run_training
+
+ # Post-process
+ post_process
+
+ log_info "=========================================="
+ log_info "Pipeline completed successfully!"
+ log_info "=========================================="
+}
+
+# Run main function
+main "$@"