diff --git a/.gitignore b/.gitignore index fe5b607..e75bef3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,7 @@ ml/data/* !ml/data ml/.idea -ml/__pycache__ \ No newline at end of file +ml/__pycache__ + +ml/mlflow_server/* +!ml/mlflow_server \ No newline at end of file diff --git a/ml/ML_IMPROVEMENT_TASKS.md b/ml/ML_IMPROVEMENT_TASKS.md new file mode 100644 index 0000000..ea07476 --- /dev/null +++ b/ml/ML_IMPROVEMENT_TASKS.md @@ -0,0 +1,1160 @@ +# ML Model Improvement - Task Backlog + +**Project Goal:** Improve model metrics from 40% to 70%+ accuracy +**Current Metrics:** Accuracy ~40%, F1/Precision/Recall ~0.4 +**Target Metrics:** Accuracy ≥70%, F1/Precision/Recall ≥0.7 + +--- + +## Epic 1: Quick Wins & Data Augmentation + +### TASK-001: Implement SpecAugment for Audio Data +**Priority:** 🔴 CRITICAL +**Story Points:** 3 +**Estimated Time:** 4-6 hours +**Expected Impact:** +5-8% accuracy + +**Description:** +Implement SpecAugment (Time and Frequency masking) to augment mel-spectrograms during training. This is a proven technique for audio classification that prevents overfitting and improves generalization. + +**Why This Matters:** +- Current dataset is small (11K samples) +- Model is likely overfitting to training data +- SpecAugment is the industry standard for audio augmentation +- Low implementation cost, high impact + +**Acceptance Criteria:** +- [ ] Create `audio_augmentations.py` module +- [ ] Implement `TimeMasking` with configurable mask_param (start with 40) +- [ ] Implement `FrequencyMasking` with configurable freq_mask_param (start with 15) +- [ ] Add probability controls (e.g., apply with 50% chance) +- [ ] Integrate with `PreprocessedAudioSetDataset` via transform parameter +- [ ] Add augmentation only to training split (not val/test) +- [ ] Train model for 10 epochs and compare metrics with baseline +- [ ] Document usage in code comments +- [ ] Verify augmented spectrograms visually (save sample images) + +**Technical Notes:** +```python +import torchaudio.transforms as T +# Use torchaudio.transforms.TimeMasking and FrequencyMasking +# Apply after mel-spectrogram, before normalization +``` + +**Definition of Done:** +- Code merged and tested +- Training script updated to use augmentation +- Metrics improved by at least +3% +- Documentation updated + +--- + +### TASK-002: Add Audio Signal Augmentations +**Priority:** 🟠 HIGH +**Story Points:** 5 +**Estimated Time:** 6-8 hours +**Expected Impact:** +3-5% accuracy + +**Description:** +Implement time-domain and frequency-domain audio augmentations: Time Stretching, Pitch Shifting, Gaussian Noise, and Volume Scaling. These augmentations simulate real-world variations. + +**Why This Matters:** +- Real-world audio has variations in speed, pitch, and volume +- Augmentations make model robust to these variations +- Combines well with SpecAugment for maximum effect +- Helps model generalize to deployment scenarios + +**Acceptance Criteria:** +- [ ] Implement Time Stretching (±10-20% speed change) +- [ ] Implement Pitch Shifting (±2-3 semitones) +- [ ] Implement Gaussian Noise addition (SNR 10-30 dB) +- [ ] Implement Volume Scaling (±10-20% amplitude) +- [ ] Create configurable augmentation pipeline with probabilities +- [ ] Add option to apply random subset of augmentations +- [ ] Integrate with existing preprocessing pipeline +- [ ] Test with sample audio files to verify quality +- [ ] Ensure augmentations preserve label information +- [ ] Add unit tests for each augmentation +- [ ] Train model and measure impact on metrics + +**Technical Notes:** +```python +# Use torchaudio.transforms for audio augmentations +# Or librosa for more advanced options +# Apply augmentations BEFORE mel-spectrogram conversion +``` + +**Definition of Done:** +- All augmentations implemented and tested +- Pipeline integrated with training code +- Ablation study shows positive impact +- Code reviewed and documented + +--- + +### TASK-003: Hyperparameter Optimization +**Priority:** 🟠 HIGH +**Story Points:** 5 +**Estimated Time:** 8-12 hours (mostly training time) +**Expected Impact:** +4-7% accuracy + +**Description:** +Systematically search for optimal hyperparameters using grid search or random search. Focus on learning rate, batch size, dropout rate, weight decay, and label smoothing. + +**Why This Matters:** +- Current hyperparameters are not tuned for this specific task +- Small changes in hyperparameters can have large impact +- This is a proven way to squeeze extra performance +- Establishes a strong baseline for further improvements + +**Acceptance Criteria:** +- [ ] Define hyperparameter search space: + - Learning Rate: [0.0001, 0.0003, 0.0005, 0.001, 0.003] + - Batch Size: [128, 192, 256] + - Dropout: [0.3, 0.4, 0.5] + - Weight Decay: [0, 1e-5, 1e-4, 1e-3] + - Label Smoothing: [0, 0.05, 0.1] +- [ ] Implement search script (random search with 30-50 trials) +- [ ] Log all experiments to MLflow +- [ ] Train each configuration for 20-30 epochs +- [ ] Use validation F1 as optimization metric +- [ ] Analyze results and identify best configuration +- [ ] Create visualization of hyperparameter importance +- [ ] Update default config with best parameters +- [ ] Retrain final model with best config for full epochs + +**Technical Notes:** +- Use MLflow for experiment tracking +- Consider using Optuna or Ray Tune for efficient search +- Use early stopping to save time on bad configs + +**Definition of Done:** +- At least 30 configurations tested +- Best hyperparameters identified and documented +- Final model retrained with optimal settings +- Report with results and analysis created + +--- + +### TASK-004: Implement Class Weighting +**Priority:** 🟠 HIGH +**Story Points:** 2 +**Estimated Time:** 2-3 hours +**Expected Impact:** +3-5% accuracy + +**Description:** +Implement weighted CrossEntropyLoss to handle class imbalance. Calculate weights based on inverse class frequencies to give more importance to underrepresented classes. + +**Why This Matters:** +- Dataset has class imbalance (6.48% to 7.90% per class) +- Model is likely biased toward majority classes +- Weighted loss forces model to learn all classes equally +- Simple implementation with proven effectiveness + +**Acceptance Criteria:** +- [ ] Calculate class frequencies from training set +- [ ] Compute balanced class weights (inverse frequency) +- [ ] Modify loss function to use weights +- [ ] Add weight calculation to preprocessing script +- [ ] Save weights to HDF5 metadata for reproducibility +- [ ] Test on validation set to verify improvement +- [ ] Compare per-class accuracy before/after weighting +- [ ] Document weight calculation method +- [ ] Add option to enable/disable weighting in config + +**Technical Notes:** +```python +from sklearn.utils.class_weight import compute_class_weight +# weights = compute_class_weight('balanced', classes=..., y=...) +# criterion = nn.CrossEntropyLoss(weight=torch.FloatTensor(weights)) +``` + +**Definition of Done:** +- Class weighting implemented and tested +- Per-class accuracy more balanced +- Overall metrics improved +- Config option added + +--- + +### TASK-005: Implement Label Smoothing +**Priority:** 🟡 MEDIUM +**Story Points:** 2 +**Estimated Time:** 2-3 hours +**Expected Impact:** +2-3% accuracy + +**Description:** +Implement label smoothing regularization to prevent model overconfidence and improve generalization. Replace hard targets (0, 1) with soft targets (ε, 1-ε). + +**Why This Matters:** +- Models often become overconfident on training data +- Label smoothing acts as regularization +- Improves calibration of predictions +- Works well with cross-entropy loss + +**Acceptance Criteria:** +- [ ] Implement label smoothing in loss function +- [ ] Add smoothing_epsilon parameter to config (start with 0.1) +- [ ] Test with values [0, 0.05, 0.1, 0.15] +- [ ] Measure impact on validation metrics +- [ ] Check calibration using reliability diagrams +- [ ] Compare confidence scores before/after +- [ ] Document optimal smoothing value +- [ ] Add to trainer configuration + +**Technical Notes:** +```python +class LabelSmoothingCrossEntropy(nn.Module): + def __init__(self, epsilon=0.1): + # Implement label smoothing + # target = (1 - epsilon) * target + epsilon / num_classes +``` + +**Definition of Done:** +- Label smoothing implemented +- Optimal epsilon found +- Metrics improved on validation set +- Code documented + +--- + +## Epic 2: Architecture Improvements + +### TASK-101: Implement Residual Connections +**Priority:** 🔴 CRITICAL +**Story Points:** 5 +**Estimated Time:** 6-8 hours +**Expected Impact:** +5-8% accuracy + +**Description:** +Add residual (skip) connections to the AudioCNN architecture, similar to ResNet. This allows training deeper networks and improves gradient flow. + +**Why This Matters:** +- Current architecture is relatively shallow +- Residual connections enable deeper networks +- Proven to improve performance on image and audio tasks +- Helps with vanishing gradient problem + +**Acceptance Criteria:** +- [ ] Create new architecture: `ResidualAudioCNN` +- [ ] Add skip connections every 2 conv blocks +- [ ] Implement proper dimension matching (1x1 conv if needed) +- [ ] Add BatchNorm after addition (pre-activation ResNet style) +- [ ] Test forward pass with dummy data +- [ ] Compare parameter count with original +- [ ] Train for 50 epochs and compare with baseline +- [ ] Visualize training curves (loss, accuracy) +- [ ] Save architecture diagram +- [ ] Update model registry in MLflow + +**Technical Notes:** +```python +# Residual block pattern: +# out = F.relu(bn(conv(x))) +# out = bn(conv(out)) +# out += x # Skip connection +# out = F.relu(out) +``` + +**Definition of Done:** +- New architecture implemented and tested +- Training converges better than baseline +- Metrics improved by at least +4% +- Code reviewed and merged + +--- + +### TASK-102: Add Squeeze-and-Excitation (SE) Blocks +**Priority:** 🟠 HIGH +**Story Points:** 4 +**Estimated Time:** 4-6 hours +**Expected Impact:** +3-5% accuracy + +**Description:** +Implement SE blocks to add channel-wise attention mechanism. SE blocks learn to emphasize important frequency channels in spectrograms. + +**Why This Matters:** +- SE blocks improve feature representation quality +- Minimal parameter overhead (~5% increase) +- Proven effective for audio classification +- Can be added to existing or residual architecture + +**Acceptance Criteria:** +- [ ] Implement `SEBlock` module with squeeze and excitation +- [ ] Add SE block after each conv block in architecture +- [ ] Use reduction ratio of 16 (configurable) +- [ ] Test that SE blocks produce valid attention weights +- [ ] Visualize attention weights for sample inputs +- [ ] Integrate with ResidualAudioCNN or create SEAudioCNN +- [ ] Compare with baseline on validation set +- [ ] Measure parameter increase (should be <10%) +- [ ] Train for 50 epochs and evaluate + +**Technical Notes:** +```python +class SEBlock(nn.Module): + # Global avg pooling -> FC -> ReLU -> FC -> Sigmoid + # Scale input channels by attention weights +``` + +**Definition of Done:** +- SE blocks implemented correctly +- Attention weights visualized and make sense +- Metrics improved on validation set +- Documentation updated + +--- + +### TASK-103: Increase Network Depth (5th Conv Block) +**Priority:** 🟠 HIGH +**Story Points:** 3 +**Estimated Time:** 3-4 hours +**Expected Impact:** +2-4% accuracy + +**Description:** +Add a 5th convolutional block to increase model capacity. Current architecture has 4 blocks (32→64→128→256), expand to 5 blocks (32→64→128→256→512). + +**Why This Matters:** +- More depth = more capacity to learn complex patterns +- Audio classification benefits from deep hierarchical features +- 4 blocks might be insufficient for 14-class problem +- With residual connections, can go deeper safely + +**Acceptance Criteria:** +- [ ] Add 5th conv block with 512 filters +- [ ] Keep same block structure (2 conv layers + pooling) +- [ ] Update GlobalAvgPooling input size +- [ ] Update FC layer input dimension (512 → 128) +- [ ] Test forward pass with sample data +- [ ] Verify output shape is correct (batch_size, 14) +- [ ] Compare parameter count and memory usage +- [ ] Train and compare with 4-block version +- [ ] Check for overfitting (train vs val curves) + +**Technical Notes:** +- Ensure GPU memory is sufficient for larger model +- May need to reduce batch size slightly +- Use gradient checkpointing if memory is tight + +**Definition of Done:** +- 5-block architecture implemented +- Model trains without memory issues +- Metrics improved or at least not degraded +- Ablation study comparing 4 vs 5 blocks + +--- + +### TASK-104: Implement ResNet-18 Transfer Learning +**Priority:** 🔴 CRITICAL +**Story Points:** 5 +**Estimated Time:** 6-8 hours +**Expected Impact:** +8-12% accuracy + +**Description:** +Use pre-trained ResNet-18 (ImageNet weights) as backbone and fine-tune for audio classification. Treat mel-spectrograms as grayscale images. + +**Why This Matters:** +- Transfer learning often outperforms training from scratch +- ImageNet features generalize well to spectrograms +- ResNet-18 is proven, battle-tested architecture +- Can achieve high accuracy with less training data + +**Acceptance Criteria:** +- [ ] Load torchvision ResNet-18 with pretrained=True +- [ ] Modify first conv layer for 1-channel input (or replicate to 3 channels) +- [ ] Replace final FC layer for 14 classes +- [ ] Implement two-stage training: + - Stage 1: Freeze backbone, train only FC (5-10 epochs) + - Stage 2: Unfreeze, fine-tune end-to-end (30-50 epochs) +- [ ] Use lower learning rate for backbone (1e-5) vs head (1e-3) +- [ ] Compare with from-scratch ResNet-18 +- [ ] Evaluate on validation set +- [ ] Save best model to MLflow +- [ ] Document fine-tuning strategy + +**Technical Notes:** +```python +import torchvision.models as models +model = models.resnet18(pretrained=True) +# Modify conv1 and fc layers +``` + +**Definition of Done:** +- Transfer learning pipeline working +- Model achieves better results than custom CNN +- Two-stage training documented +- Saved to model registry + +--- + +### TASK-105: Try EfficientNet Architecture +**Priority:** 🟡 MEDIUM +**Story Points:** 4 +**Estimated Time:** 5-7 hours +**Expected Impact:** +4-8% accuracy + +**Description:** +Implement EfficientNet-B0 or B1 as an alternative architecture. EfficientNets achieve better accuracy with fewer parameters through compound scaling. + +**Why This Matters:** +- EfficientNet is SOTA for image classification +- More parameter-efficient than ResNet +- Good balance of accuracy and speed +- Transfer learning available + +**Acceptance Criteria:** +- [ ] Install efficientnet-pytorch library +- [ ] Load EfficientNet-B0 with pretrained weights +- [ ] Adapt for 1-channel spectrograms +- [ ] Replace classification head for 14 classes +- [ ] Implement two-stage fine-tuning +- [ ] Compare with ResNet-18 on same data +- [ ] Measure inference speed (FPS) +- [ ] Evaluate parameter count and memory usage +- [ ] Choose best variant (B0 vs B1) +- [ ] Document pros/cons vs ResNet + +**Definition of Done:** +- EfficientNet implemented and trained +- Comparison with ResNet documented +- Best variant selected +- Added to model zoo + +--- + +## Epic 3: Dataset Expansion + +### TASK-201: Expand AudioSet Dataset (Critical!) +**Priority:** 🔴 CRITICAL +**Story Points:** 8 +**Estimated Time:** 12-20 hours (mostly download time) +**Expected Impact:** +10-15% accuracy + +**Description:** +Dramatically expand the AudioSet dataset from 11K to 50-100K audio samples. This is THE most impactful task for improving model performance. + +**Why This Matters:** +- **MOST IMPORTANT TASK** for achieving 70%+ accuracy +- Current dataset is too small (11K samples for 14 classes) +- Deep learning needs data (rule of thumb: 1000+ per class) +- More data > better architecture in most cases +- Will reduce overfitting significantly + +**Acceptance Criteria:** +- [ ] Audit current dataset: count samples per AudioSet class +- [ ] Create download plan: target 300-500 samples per AudioSet class +- [ ] Update `data/process.py` download script if needed +- [ ] Download additional samples for all 103 AudioSet classes: + ```bash + for class in "Gunshot" "Siren" "Explosion" ...; do + python3 data/process.py download -c "$class" -d './data/audioset' -n 500 + done + ``` +- [ ] Verify audio file integrity (check for corrupted files) +- [ ] Remove duplicates and invalid files +- [ ] Rerun preprocessing script to regenerate HDF5 +- [ ] Verify new dataset size: target 50,000-100,000 samples +- [ ] Check class distribution balance +- [ ] Update dataset documentation +- [ ] Retrain baseline model on expanded dataset +- [ ] Measure accuracy improvement + +**Technical Notes:** +- Use parallel downloads to speed up process +- Monitor disk space (may need 10-20 GB) +- Consider using youtube-dl rate limiting to avoid blocks +- Keep track of failed downloads for retry + +**Definition of Done:** +- Dataset expanded to 50K+ samples minimum +- HDF5 file regenerated and validated +- All classes have adequate representation (300+ each) +- Model retrained shows significant improvement +- Dataset statistics documented + +--- + +### TASK-202: Integrate FSD50K Dataset +**Priority:** 🟠 HIGH +**Story Points:** 8 +**Estimated Time:** 10-15 hours +**Expected Impact:** +5-8% accuracy + +**Description:** +Download and integrate Freesound Dataset 50K (FSD50K) to complement AudioSet. Map FSD50K classes to the 14 target classes. + +**Why This Matters:** +- FSD50K has 50K+ high-quality audio samples +- More diverse data sources improve generalization +- Some classes have better coverage in FSD50K +- Cross-dataset training improves robustness + +**Acceptance Criteria:** +- [ ] Download FSD50K dataset from official source +- [ ] Explore FSD50K class ontology (200+ classes) +- [ ] Create mapping from FSD50K classes to 14 target classes +- [ ] Write dataset loader for FSD50K format +- [ ] Combine with AudioSet in preprocessing pipeline +- [ ] Handle different audio formats/sample rates +- [ ] Generate combined HDF5 file or separate HDF5 +- [ ] Verify no data leakage between train/val/test +- [ ] Train model on combined dataset +- [ ] Compare with AudioSet-only baseline +- [ ] Document class mapping decisions + +**Technical Notes:** +- FSD50K ontology: https://annotator.freesound.org/fsd/ontology/ +- Download size: ~30 GB +- Some FSD50K classes may map to multiple target classes + +**Definition of Done:** +- FSD50K downloaded and integrated +- Class mapping documented and validated +- Combined dataset preprocessed +- Model trained shows improvement +- Integration documented + +--- + +### TASK-203: Add UrbanSound8K Dataset +**Priority:** 🟡 MEDIUM +**Story Points:** 5 +**Estimated Time:** 6-8 hours +**Expected Impact:** +2-4% accuracy + +**Description:** +Integrate UrbanSound8K dataset, which contains 8,732 urban sound samples across 10 classes. Focus on classes relevant to anomaly detection (sirens, gunshots, etc.). + +**Why This Matters:** +- UrbanSound8K is high-quality and well-curated +- Strong coverage of urban/emergency sounds +- Complements AudioSet's weaknesses +- Widely used benchmark dataset + +**Acceptance Criteria:** +- [ ] Download UrbanSound8K from official source +- [ ] Map 10 UrbanSound classes to 14 target classes: + - air_conditioner → work_sounds or household_sounds + - car_horn → traffic_emergency + - children_playing → public_spaces + - dog_bark → nature_sounds + - drilling → work_sounds + - engine_idling → normal_transport + - gun_shot → weapon_violence (!) + - jackhammer → work_sounds + - siren → emergency_services (!) + - street_music → music_entertainment +- [ ] Respect 10-fold split structure or create new split +- [ ] Add to preprocessing pipeline +- [ ] Generate HDF5 or add to existing +- [ ] Train and evaluate contribution +- [ ] Document which classes benefited most + +**Definition of Done:** +- UrbanSound8K integrated successfully +- Class mapping validated +- Improvement measured +- Documentation updated + +--- + +### TASK-204: Implement Mixup Data Augmentation +**Priority:** 🟡 MEDIUM +**Story Points:** 4 +**Estimated Time:** 4-6 hours +**Expected Impact:** +2-4% accuracy + +**Description:** +Implement Mixup augmentation that creates virtual training examples by mixing pairs of samples and their labels. This improves generalization and prevents overfitting. + +**Why This Matters:** +- Mixup is proven to improve generalization +- Works by interpolating between training examples +- Reduces overfitting on small datasets +- Simple to implement, good results + +**Acceptance Criteria:** +- [ ] Implement mixup function for spectrograms +- [ ] Mix both features and labels with same lambda +- [ ] Use beta distribution for lambda (alpha=0.2-0.4) +- [ ] Apply mixup in training loop (not preprocessing) +- [ ] Handle batch mixing efficiently +- [ ] Test with different alpha values +- [ ] Ensure mixed samples make sense (visualize) +- [ ] Train model with and without mixup +- [ ] Compare validation metrics +- [ ] Add mixup option to config + +**Technical Notes:** +```python +def mixup_data(x, y, alpha=0.4): + lam = np.random.beta(alpha, alpha) + index = torch.randperm(x.size(0)) + mixed_x = lam * x + (1 - lam) * x[index] + y_a, y_b = y, y[index] + return mixed_x, y_a, y_b, lam +# Loss: lam * loss(pred, y_a) + (1-lam) * loss(pred, y_b) +``` + +**Definition of Done:** +- Mixup implemented correctly +- Training uses mixup augmentation +- Metrics improved on validation +- Optimal alpha documented + +--- + +## Epic 4: Loss Functions & Optimization + +### TASK-301: Implement Focal Loss +**Priority:** 🟠 HIGH +**Story Points:** 3 +**Estimated Time:** 3-4 hours +**Expected Impact:** +3-5% accuracy + +**Description:** +Replace CrossEntropyLoss with Focal Loss to focus training on hard examples. Focal Loss down-weights easy examples and focuses on misclassified samples. + +**Why This Matters:** +- Handles class imbalance better than weighted CE +- Focuses on hard-to-classify examples +- Proven effective for imbalanced datasets +- Can combine with class weighting + +**Acceptance Criteria:** +- [ ] Implement `FocalLoss` class +- [ ] Add alpha (class weights) and gamma (focusing) parameters +- [ ] Start with gamma=2.0, alpha=None (balanced) +- [ ] Test focal loss in training loop +- [ ] Compare with weighted CrossEntropyLoss +- [ ] Tune gamma values [1.0, 2.0, 3.0, 5.0] +- [ ] Visualize loss distribution (easy vs hard examples) +- [ ] Measure per-class accuracy improvement +- [ ] Add to trainer config as loss option + +**Technical Notes:** +```python +class FocalLoss(nn.Module): + def __init__(self, alpha=None, gamma=2.0): + # FL(p_t) = -alpha_t * (1 - p_t)^gamma * log(p_t) +``` + +**Definition of Done:** +- Focal loss implemented and tested +- Optimal gamma found +- Improvement over CE loss demonstrated +- Config option added + +--- + +### TASK-302: Switch to AdamW Optimizer +**Priority:** 🟡 MEDIUM +**Story Points:** 2 +**Estimated Time:** 2-3 hours +**Expected Impact:** +2-3% accuracy + +**Description:** +Replace Adam optimizer with AdamW, which implements weight decay correctly. AdamW decouples weight decay from gradient updates. + +**Why This Matters:** +- AdamW fixes weight decay implementation in Adam +- Better generalization than Adam +- Widely adopted in modern deep learning +- Simple drop-in replacement + +**Acceptance Criteria:** +- [ ] Replace `torch.optim.Adam` with `torch.optim.AdamW` +- [ ] Add weight_decay parameter (start with 1e-4) +- [ ] Test with values [0, 1e-5, 1e-4, 1e-3] +- [ ] Compare training curves with Adam +- [ ] Measure validation metrics improvement +- [ ] Update config with optimal weight decay +- [ ] Document differences observed +- [ ] Ensure backward compatibility (option in config) + +**Technical Notes:** +```python +optimizer = torch.optim.AdamW( + model.parameters(), + lr=learning_rate, + weight_decay=1e-4, + betas=(0.9, 0.999) +) +``` + +**Definition of Done:** +- AdamW implemented as default optimizer +- Optimal weight decay found +- Comparison with Adam documented +- Config updated + +--- + +### TASK-303: Implement OneCycleLR Scheduler +**Priority:** 🟠 HIGH +**Story Points:** 3 +**Estimated Time:** 3-5 hours +**Expected Impact:** +3-5% accuracy + +**Description:** +Replace ReduceLROnPlateau with OneCycleLR scheduler for faster convergence. OneCycleLR uses triangular learning rate schedule with momentum annealing. + +**Why This Matters:** +- OneCycleLR achieves super-convergence +- Trains faster with better final accuracy +- Automatically handles LR warmup and decay +- Less tuning required than multi-step schedulers + +**Acceptance Criteria:** +- [ ] Implement `torch.optim.lr_scheduler.OneCycleLR` +- [ ] Set max_lr to 10x base learning rate +- [ ] Calculate total_steps from epochs and batch count +- [ ] Set pct_start=0.3 (30% warmup) +- [ ] Add anneal_strategy='cos' for smooth decay +- [ ] Update trainer to call scheduler.step() every batch +- [ ] Visualize LR schedule across training +- [ ] Compare with ReduceLROnPlateau baseline +- [ ] Measure training time reduction +- [ ] Verify final accuracy improvement + +**Technical Notes:** +```python +scheduler = OneCycleLR( + optimizer, + max_lr=learning_rate * 10, + total_steps=epochs * len(train_loader), + pct_start=0.3, + anneal_strategy='cos' +) +# Call scheduler.step() after every batch! +``` + +**Definition of Done:** +- OneCycleLR implemented correctly +- Training converges faster +- Final metrics improved +- LR schedule visualized + +--- + +### TASK-304: Implement Cosine Annealing with Warm Restarts +**Priority:** 🟡 MEDIUM +**Story Points:** 3 +**Estimated Time:** 3-4 hours +**Expected Impact:** +2-4% accuracy + +**Description:** +Implement CosineAnnealingWarmRestarts as alternative to OneCycleLR. This scheduler performs periodic LR restarts to escape local minima. + +**Why This Matters:** +- Periodic restarts help escape poor local minima +- Can find better solutions than monotonic decay +- Works well with long training runs +- Good alternative if OneCycleLR doesn't work well + +**Acceptance Criteria:** +- [ ] Implement `torch.optim.lr_scheduler.CosineAnnealingWarmRestarts` +- [ ] Set T_0 (restart period) to 10-20 epochs +- [ ] Set T_mult=2 (double period each restart) +- [ ] Use eta_min=1e-6 (minimum LR) +- [ ] Visualize LR schedule with restarts +- [ ] Train model for 80-100 epochs (multiple restarts) +- [ ] Compare with OneCycleLR +- [ ] Measure accuracy at each restart peak +- [ ] Save best model across all restarts +- [ ] Document when to use vs OneCycleLR + +**Technical Notes:** +```python +scheduler = CosineAnnealingWarmRestarts( + optimizer, + T_0=10, # First restart after 10 epochs + T_mult=2, # Double period after each restart + eta_min=1e-6 +) +``` + +**Definition of Done:** +- Warm restarts implemented +- Comparison with other schedulers done +- Best use cases documented +- Config option added + +--- + +## Epic 5: Advanced Techniques + +### TASK-401: Add MFCC Features +**Priority:** 🟡 MEDIUM +**Story Points:** 5 +**Estimated Time:** 6-8 hours +**Expected Impact:** +2-4% accuracy + +**Description:** +Extract Mel-Frequency Cepstral Coefficients (MFCC) in addition to mel-spectrograms and use multi-input architecture to process both feature types. + +**Why This Matters:** +- MFCC captures different aspects of audio than mel-spec +- Multi-feature models often outperform single-feature +- MFCC is standard in speech recognition +- Provides complementary information + +**Acceptance Criteria:** +- [ ] Implement MFCC extraction in preprocessing +- [ ] Store MFCCs in HDF5 alongside mel-spectrograms +- [ ] Use 13-20 MFCC coefficients +- [ ] Create multi-input model architecture: + - Branch 1: CNN for mel-spectrogram + - Branch 2: CNN for MFCC + - Concatenate features before FC layers +- [ ] Test both features independently and combined +- [ ] Measure accuracy gain from multi-feature approach +- [ ] Optimize feature fusion strategy +- [ ] Document which feature is more important +- [ ] Add option to enable/disable MFCC + +**Technical Notes:** +```python +mfcc_transform = torchaudio.transforms.MFCC( + sample_rate=16000, + n_mfcc=20, + melkwargs={'n_fft': 2048, 'hop_length': 512} +) +``` + +**Definition of Done:** +- MFCC extraction implemented +- Multi-input model working +- Accuracy improved with both features +- Feature importance analyzed + +--- + +### TASK-402: Implement K-Fold Cross-Validation +**Priority:** 🟡 MEDIUM +**Story Points:** 5 +**Estimated Time:** 8-12 hours (mostly training time) +**Expected Impact:** +1-3% accuracy (better generalization) + +**Description:** +Implement 5-fold cross-validation to better utilize data and get more robust performance estimates. Average predictions across folds for inference. + +**Why This Matters:** +- Current single split may not be representative +- K-fold gives more reliable performance estimate +- Better data utilization (especially with small dataset) +- Ensemble of folds improves predictions + +**Acceptance Criteria:** +- [ ] Implement stratified K-fold split (K=5) +- [ ] Ensure each fold is balanced by class +- [ ] Create training script for K-fold CV +- [ ] Train separate model for each fold +- [ ] Log each fold's metrics to MLflow +- [ ] Compute mean and std of metrics across folds +- [ ] Implement ensemble prediction (average or voting) +- [ ] Compare single split vs K-fold results +- [ ] Save all fold models for ensemble +- [ ] Document fold split strategy + +**Technical Notes:** +```python +from sklearn.model_selection import StratifiedKFold +skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) +for fold, (train_idx, val_idx) in enumerate(skf.split(X, y)): + # Train fold model +``` + +**Definition of Done:** +- 5-fold CV implemented and working +- All folds trained successfully +- Mean metrics and confidence intervals computed +- Ensemble inference implemented + +--- + +### TASK-403: Add Gradient Accumulation +**Priority:** 🟢 LOW +**Story Points:** 2 +**Estimated Time:** 2-3 hours +**Expected Impact:** +1-2% accuracy + +**Description:** +Implement gradient accumulation to simulate larger batch sizes without increasing GPU memory. This improves gradient estimates. + +**Why This Matters:** +- Larger effective batch size stabilizes training +- Can't increase actual batch size due to GPU memory +- Better gradient estimates with accumulated gradients +- Simple implementation, minimal overhead + +**Acceptance Criteria:** +- [ ] Add accumulation_steps parameter to config (start with 4) +- [ ] Modify training loop to accumulate gradients +- [ ] Scale loss by accumulation_steps +- [ ] Call optimizer.step() every N steps +- [ ] Verify effective batch size calculation is correct +- [ ] Test with steps = [2, 4, 8] +- [ ] Monitor memory usage (should not increase) +- [ ] Compare training stability with/without accumulation +- [ ] Measure impact on final metrics +- [ ] Document tradeoff (training time vs accuracy) + +**Technical Notes:** +```python +for i, (inputs, targets) in enumerate(dataloader): + loss = criterion(outputs, targets) / accumulation_steps + loss.backward() + if (i + 1) % accumulation_steps == 0: + optimizer.step() + optimizer.zero_grad() +``` + +**Definition of Done:** +- Gradient accumulation working correctly +- Effective batch size verified +- Impact on metrics measured +- Added to trainer config + +--- + +### TASK-404: Implement Test-Time Augmentation (TTA) +**Priority:** 🟢 LOW +**Story Points:** 3 +**Estimated Time:** 3-4 hours +**Expected Impact:** +1-2% accuracy + +**Description:** +Apply augmentations at test time and average predictions across augmented versions. This improves inference robustness. + +**Why This Matters:** +- Ensemble of augmented inputs reduces prediction variance +- Simple way to squeeze extra accuracy at inference +- No retraining required +- Commonly used in competitions + +**Acceptance Criteria:** +- [ ] Implement TTA inference function +- [ ] Apply 5-10 different augmentations per sample +- [ ] Average logits or probabilities across augmentations +- [ ] Test augmentations: horizontal flip, time shift, pitch shift, noise +- [ ] Measure accuracy improvement on test set +- [ ] Balance accuracy gain vs inference time +- [ ] Add TTA as optional flag in inference API +- [ ] Document recommended TTA settings +- [ ] Compare TTA vs single prediction + +**Technical Notes:** +```python +def predict_with_tta(model, sample, n_augmentations=5): + predictions = [] + for _ in range(n_augmentations): + aug_sample = apply_random_augmentation(sample) + pred = model(aug_sample) + predictions.append(pred) + return torch.mean(torch.stack(predictions), dim=0) +``` + +**Definition of Done:** +- TTA implemented for inference +- Accuracy improvement measured +- Inference time impact documented +- Added to API as option + +--- + +### TASK-405: Implement Model Ensemble +**Priority:** 🟢 LOW +**Story Points:** 4 +**Estimated Time:** 6-8 hours (mostly training) +**Expected Impact:** +2-4% accuracy + +**Description:** +Train 3-5 diverse models (different architectures, seeds, hyperparameters) and ensemble their predictions for final inference. + +**Why This Matters:** +- Ensembles almost always improve accuracy +- Different models capture different patterns +- Reduces variance and improves robustness +- Industry standard for maximizing performance + +**Acceptance Criteria:** +- [ ] Select 3-5 diverse models to ensemble: + - ResidualAudioCNN with seed 42 + - ResidualAudioCNN with seed 123 + - ResNet-18 transfer learning + - EfficientNet-B0 + - AudioCNN with different augmentations +- [ ] Train all models to convergence +- [ ] Implement ensemble prediction: + - Soft voting (average probabilities) + - Hard voting (majority vote) + - Weighted voting (weights by validation accuracy) +- [ ] Test all ensemble strategies +- [ ] Measure ensemble accuracy on test set +- [ ] Analyze diversity of ensemble (prediction correlation) +- [ ] Create inference script for ensemble +- [ ] Document ensemble composition and weights + +**Definition of Done:** +- 3-5 models trained successfully +- Ensemble prediction working +- Significant accuracy boost demonstrated +- Inference pipeline supports ensemble + +--- + +## Epic 6: Monitoring & Analysis + +### TASK-501: Create Detailed Error Analysis +**Priority:** 🟡 MEDIUM +**Story Points:** 3 +**Estimated Time:** 4-6 hours +**Expected Impact:** Indirect (identifies improvement areas) + +**Description:** +Perform comprehensive error analysis to understand which classes and samples the model struggles with. Generate confusion matrices, per-class metrics, and failure case visualizations. + +**Why This Matters:** +- Identifies which classes need more attention +- Reveals systematic errors (e.g., confusing similar sounds) +- Guides data collection and augmentation strategy +- Essential for targeted improvements + +**Acceptance Criteria:** +- [ ] Generate confusion matrix for best model +- [ ] Compute per-class precision, recall, F1 +- [ ] Identify most confused class pairs +- [ ] Extract and analyze worst-performing samples +- [ ] Visualize spectrograms of misclassified examples +- [ ] Compare predictions vs ground truth +- [ ] Identify patterns in failures (e.g., length, quality) +- [ ] Create error analysis report with visualizations +- [ ] Recommend specific improvements based on findings +- [ ] Share report with team + +**Deliverables:** +- Confusion matrix heatmap +- Per-class metrics table +- Top-10 failure cases with spectrograms +- Error analysis document (markdown/PDF) +- Recommendations for next steps + +**Definition of Done:** +- Complete error analysis performed +- Report created and reviewed +- Action items identified +- Shared with team + +--- + +### TASK-502: Set Up Continuous Monitoring +**Priority:** 🟢 LOW +**Story Points:** 5 +**Estimated Time:** 6-8 hours +**Expected Impact:** Indirect (prevents degradation) + +**Description:** +Set up monitoring infrastructure to track model performance over time, including drift detection and alerting. + +**Why This Matters:** +- Detects performance degradation early +- Tracks data distribution shifts +- Monitors prediction confidence +- Essential for production deployment + +**Acceptance Criteria:** +- [ ] Log predictions and confidences for all inferences +- [ ] Track prediction distribution over time +- [ ] Implement data drift detection +- [ ] Monitor average confidence scores +- [ ] Set up alerts for: + - Accuracy drop below threshold + - Unusual prediction distribution + - Low confidence predictions +- [ ] Create monitoring dashboard (Grafana or similar) +- [ ] Log to MLflow or custom DB +- [ ] Document monitoring setup +- [ ] Test alert system + +**Definition of Done:** +- Monitoring infrastructure deployed +- Metrics tracked in dashboard +- Alerts configured and tested +- Documentation complete + +--- + +## Summary Statistics + +### By Priority +- 🔴 **CRITICAL**: 5 tasks (TASK-001, TASK-101, TASK-104, TASK-201) +- 🟠 **HIGH**: 10 tasks +- 🟡 **MEDIUM**: 10 tasks +- 🟢 **LOW**: 5 tasks + +### By Epic +- **Epic 1 (Quick Wins)**: 5 tasks, 17 story points, ~26 hours +- **Epic 2 (Architecture)**: 5 tasks, 21 story points, ~29 hours +- **Epic 3 (Dataset)**: 4 tasks, 25 story points, ~38 hours +- **Epic 4 (Optimization)**: 4 tasks, 11 story points, ~16 hours +- **Epic 5 (Advanced)**: 5 tasks, 19 story points, ~30 hours +- **Epic 6 (Monitoring)**: 2 tasks, 8 story points, ~12 hours + +**Total**: 25 tasks, 101 story points, ~151 hours + +### Expected Cumulative Impact +- **Critical tasks only**: +28-43% → **68-83% accuracy** +- **Critical + High**: +45-70% → **85-110% accuracy** (target achieved!) +- **All tasks**: +50-85% → **90-125% accuracy** (well above target) + +--- + +## Recommended Sprint Plan + +### Sprint 1 (Week 1): Foundation - Target 55-60% accuracy +- TASK-001: SpecAugment ⭐ +- TASK-004: Class Weighting ⭐ +- TASK-201: Expand Dataset ⭐⭐⭐ (START IMMEDIATELY!) +- TASK-003: Hyperparameter Tuning + +### Sprint 2 (Week 2): Architecture - Target 65-70% accuracy +- TASK-101: Residual Connections ⭐⭐ +- TASK-104: ResNet-18 Transfer Learning ⭐⭐ +- TASK-301: Focal Loss ⭐ +- TASK-002: Audio Augmentations + +### Sprint 3 (Week 3): Polish - Target 70-75% accuracy +- TASK-202: FSD50K Integration ⭐ +- TASK-303: OneCycleLR Scheduler ⭐ +- TASK-102: SE Blocks +- TASK-005: Label Smoothing + +### Sprint 4 (Week 4): Optimization - Target 72-78% accuracy +- TASK-302: AdamW Optimizer +- TASK-204: Mixup Augmentation +- TASK-405: Model Ensemble +- TASK-501: Error Analysis + +--- + +## Notes + +1. **TASK-201 (Dataset Expansion) is the single most important task.** Start it immediately and run it in parallel with other tasks. + +2. Tasks marked with ⭐ are highest ROI (return on investment). + +3. Story points are estimated using Fibonacci scale (1, 2, 3, 5, 8) based on complexity. + +4. Time estimates include implementation, testing, and training time. Actual wall-clock time may be longer due to long training runs. + +5. Expected impact is cumulative - later tasks build on earlier improvements. + +6. Some tasks have dependencies: + - TASK-102 (SE Blocks) depends on TASK-101 (Residual Connections) + - TASK-402 (K-Fold CV) should wait until architecture is finalized + - TASK-405 (Ensemble) requires multiple trained models + +7. MLflow tracking should be used for ALL experiments to track progress. + +8. After Sprint 2, you should hit the 70% target. Remaining sprints are for exceeding targets and production readiness. diff --git a/ml/PREPROCESSING_README.md b/ml/PREPROCESSING_README.md new file mode 100644 index 0000000..9e5a0f7 --- /dev/null +++ b/ml/PREPROCESSING_README.md @@ -0,0 +1,320 @@ +# AudioSet Preprocessing Pipeline + +This document describes how to use the HDF5 preprocessing pipeline to significantly speed up model training. + +## Overview + +The preprocessing pipeline processes all audio files once, applies transformations (resampling, mel-spectrogram computation, normalization), and saves the results in HDF5 format. This eliminates the need to repeatedly load and process audio files during training. + +**Expected speedup: 5-10x faster training** + +## Files + +- `preprocess_audioset.py` - Script to preprocess AudioSet and save to HDF5 +- `preprocessed_dataset.py` - PyTorch Dataset class for loading preprocessed data +- `training_preprocessed.py` - Example training script using preprocessed data + +## Installation + +Install the required dependency: + +```bash +pip install h5py +``` + +Or add to requirements.txt: +``` +h5py>=3.0.0 +``` + +## Step 1: Preprocess the Dataset + +Run the preprocessing script to convert all audio files to HDF5 format: + +```bash +python preprocess_audioset.py \ + --audioset-root ./data \ + --output ./data/audioset_preprocessed.h5 \ + --train-ratio 0.6 \ + --val-ratio 0.2 \ + --test-ratio 0.2 \ + --seed 42 +``` + +**Parameters:** +- `--audioset-root`: Path to directory containing `audioset` folder (default: `./data`) +- `--output`: Where to save HDF5 file (default: `./data/audioset_preprocessed.h5`) +- `--train-ratio`: Training set ratio (default: 0.6) +- `--val-ratio`: Validation set ratio (default: 0.2) +- `--test-ratio`: Test set ratio (default: 0.2) +- `--seed`: Random seed for reproducible splits (default: 42) + +**Output:** +- Single HDF5 file containing all preprocessed data +- Typical size: ~100-500 MB (depends on dataset size) +- Processing time: ~5-15 minutes (depends on dataset size) + +**HDF5 Structure:** +``` +audioset_preprocessed.h5 +├── /train +│ ├── features (N, 1, 128, 157) - preprocessed spectrograms +│ └── labels (N,) - class indices +├── /val +│ ├── features +│ └── labels +├── /test +│ ├── features +│ └── labels +└── metadata (attributes) +``` + +## Step 2: Test the Preprocessed Dataset + +Verify that preprocessing worked correctly: + +```bash +python preprocessed_dataset.py ./data/audioset_preprocessed.h5 +``` + +This will: +- Load the dataset +- Print metadata and class distribution +- Test loading samples +- Test DataLoader integration + +## Step 3: Train with Preprocessed Data + +### Option A: Use the Python script + +```bash +python training_preprocessed.py +``` + +### Option B: Use in Jupyter Notebook + +In your `training.ipynb`, replace the dataset loading code: + +**OLD CODE (slow):** +```python +from custom_transformations import get_audio_transforms + +audioset = AudioSetDataset( + path_to_root_dir='./data', + transform=get_audio_transforms() +) + +train_data, val_data, test_data = random_split( + audioset, [0.6, 0.2, 0.2] +) +``` + +**NEW CODE (fast):** +```python +from preprocessed_dataset import PreprocessedAudioSetDataset + +train_data = PreprocessedAudioSetDataset( + hdf5_path='./data/audioset_preprocessed.h5', + split='train', + cache_in_memory=False # Set to True if dataset fits in RAM +) + +val_data = PreprocessedAudioSetDataset( + hdf5_path='./data/audioset_preprocessed.h5', + split='val', + cache_in_memory=False +) + +test_data = PreprocessedAudioSetDataset( + hdf5_path='./data/audioset_preprocessed.h5', + split='test', + cache_in_memory=False +) +``` + +**Create DataLoaders with optimized settings:** +```python +train_loader = DataLoader( + train_data, + batch_size=128, # Can increase since I/O is faster + shuffle=True, + num_workers=4, # Parallel data loading + pin_memory=True, # Faster GPU transfer + persistent_workers=True # Keep workers alive between epochs +) + +val_loader = DataLoader( + val_data, + batch_size=128, + shuffle=False, + num_workers=4, + pin_memory=True, + persistent_workers=True +) + +test_loader = DataLoader( + test_data, + batch_size=128, + shuffle=False, + num_workers=2, + pin_memory=True +) +``` + +## Performance Optimization Tips + +### 1. Cache in Memory (if dataset is small) + +If your preprocessed dataset is < 2GB and you have enough RAM: + +```python +train_data = PreprocessedAudioSetDataset( + hdf5_path='./data/audioset_preprocessed.h5', + split='train', + cache_in_memory=True # Load entire dataset into RAM +) +``` + +This provides the fastest possible data loading. + +### 2. Increase Batch Size + +Since I/O is much faster, you can increase batch size: + +```python +BATCH_SIZE = 256 # or even 512 if GPU memory allows +``` + +### 3. Use Multiple Workers + +Increase `num_workers` in DataLoader (but not too much): + +```python +num_workers=4 # Good for most systems +# or +num_workers=8 # If you have many CPU cores +``` + +**Note:** Too many workers can slow things down due to overhead. + +### 4. Enable Pin Memory (for GPU training) + +```python +pin_memory=True # Faster data transfer to GPU +``` + +### 5. Use Persistent Workers + +```python +persistent_workers=True # Avoid recreating workers each epoch +``` + +## Expected Performance + +**Before preprocessing:** +- Loading speed: ~2-3 iterations/second +- Epoch time: ~3-5 minutes + +**After preprocessing:** +- Loading speed: ~15-30 iterations/second +- Epoch time: ~30-60 seconds + +**Total speedup: 5-10x faster training** + +## Troubleshooting + +### "FileNotFoundError: HDF5 file not found" + +Make sure you ran the preprocessing script first: +```bash +python preprocess_audioset.py +``` + +### "Out of Memory" error + +Reduce batch size or disable `cache_in_memory`: +```python +cache_in_memory=False +``` + +### Slow loading with `num_workers > 0` + +On Windows, multiprocessing can be slow. Try: +```python +num_workers=0 # Single-threaded loading +``` + +### "Split 'train' not found in HDF5 file" + +The HDF5 file might be corrupted. Delete it and rerun preprocessing: +```bash +rm ./data/audioset_preprocessed.h5 +python preprocess_audioset.py +``` + +## HDF5 File Inspection + +To inspect the HDF5 file structure: + +```python +import h5py + +with h5py.File('./data/audioset_preprocessed.h5', 'r') as f: + print("Keys:", list(f.keys())) + print("Train features shape:", f['train']['features'].shape) + print("Train labels shape:", f['train']['labels'].shape) + print("\nMetadata:") + for key, value in f.attrs.items(): + print(f" {key}: {value}") +``` + +## Re-preprocessing + +If you need to re-preprocess (e.g., after changing audio parameters): + +1. Delete old HDF5 file: + ```bash + rm ./data/audioset_preprocessed.h5 + ``` + +2. Update parameters in `cfg.py` if needed + +3. Run preprocessing again: + ```bash + python preprocess_audioset.py + ``` + +## Adding Data Augmentation + +Even with preprocessed data, you can add augmentation: + +```python +import torch +import torchaudio + +class SpectrogramAugmentation: + def __init__(self): + self.time_mask = torchaudio.transforms.TimeMasking(time_mask_param=20) + self.freq_mask = torchaudio.transforms.FrequencyMasking(freq_mask_param=20) + + def __call__(self, spectrogram): + if torch.rand(1) < 0.5: # 50% chance + spectrogram = self.time_mask(spectrogram) + if torch.rand(1) < 0.5: + spectrogram = self.freq_mask(spectrogram) + return spectrogram + +# Use with dataset +train_data = PreprocessedAudioSetDataset( + hdf5_path='./data/audioset_preprocessed.h5', + split='train', + transform=SpectrogramAugmentation() # Apply augmentation +) +``` + +## Notes + +- The HDF5 file uses gzip compression (level 4) to save disk space +- Preprocessing is done only once - subsequent training runs use the cached data +- The train/val/test split is fixed after preprocessing (determined by `--seed`) +- Invalid audio files (that failed preprocessing) are marked with label `-1` and automatically filtered out diff --git a/ml/QUICKSTART_PREPROCESSING.md b/ml/QUICKSTART_PREPROCESSING.md new file mode 100644 index 0000000..2a063df --- /dev/null +++ b/ml/QUICKSTART_PREPROCESSING.md @@ -0,0 +1,192 @@ +# Quick Start: Preprocessed Training Pipeline + +## Быстрый старт в 3 шага + +### Шаг 1: Установите h5py + +```bash +pip install h5py +``` + +### Шаг 2: Запустите preprocessing (один раз) + +```bash +python preprocess_audioset.py +``` + +Это займет 5-15 минут. Результат будет сохранен в `./data/audioset_preprocessed.h5` + +### Шаг 3: Запустите обучение + +```bash +python training_preprocessed.py +``` + +**Готово!** Обучение теперь в 5-10 раз быстрее. + +--- + +## Что изменилось? + +**До (медленно):** +- Каждая эпоха: загрузка WAV → FFT → спектрограмма → обучение +- Скорость: ~2-3 it/s +- Время эпохи: ~3-5 минут + +**После (быстро):** +- Preprocessing один раз: загрузка WAV → FFT → спектрограмма → сохранение в HDF5 +- Каждая эпоха: загрузка готовых спектрограмм из HDF5 → обучение +- Скорость: ~15-30 it/s +- Время эпохи: ~30-60 секунд + +--- + +## Использование в Jupyter Notebook + +В `training.ipynb` замените: + +```python +# СТАРЫЙ КОД (удалить) +from custom_transformations import get_audio_transforms + +audioset = AudioSetDataset( + path_to_root_dir='./data', + transform=get_audio_transforms() +) +train_data, val_data, test_data = random_split(audioset, [0.6, 0.2, 0.2]) +``` + +На: + +```python +# НОВЫЙ КОД (использовать) +from preprocessed_dataset import PreprocessedAudioSetDataset + +train_data = PreprocessedAudioSetDataset( + hdf5_path='./data/audioset_preprocessed.h5', + split='train', + cache_in_memory=False +) + +val_data = PreprocessedAudioSetDataset( + hdf5_path='./data/audioset_preprocessed.h5', + split='val', + cache_in_memory=False +) + +test_data = PreprocessedAudioSetDataset( + hdf5_path='./data/audioset_preprocessed.h5', + split='test', + cache_in_memory=False +) +``` + +И обновите DataLoader: + +```python +train_loader = DataLoader( + train_data, + batch_size=128, # Можно увеличить до 256 + shuffle=True, + num_workers=4, # Для ускорения загрузки + pin_memory=True, + persistent_workers=True +) +``` + +--- + +## Параметры preprocessing + +Изменить split ratio: + +```bash +python preprocess_audioset.py \ + --train-ratio 0.7 \ + --val-ratio 0.15 \ + --test-ratio 0.15 +``` + +Указать другой путь: + +```bash +python preprocess_audioset.py \ + --audioset-root /path/to/your/data \ + --output /path/to/output.h5 +``` + +Изменить random seed: + +```bash +python preprocess_audioset.py --seed 123 +``` + +--- + +## Проверка работоспособности + +Тест загрузки данных: + +```bash +python preprocessed_dataset.py ./data/audioset_preprocessed.h5 +``` + +Вывод должен показать: +- Количество сэмплов +- Размерности features +- Распределение классов +- Тест загрузки батча + +--- + +## Оптимизация производительности + +### Если датасет маленький (<2GB) и есть много RAM: + +```python +train_data = PreprocessedAudioSetDataset( + hdf5_path='./data/audioset_preprocessed.h5', + split='train', + cache_in_memory=True # Загрузить всё в RAM +) +``` + +### Если много CPU ядер: + +```python +train_loader = DataLoader( + train_data, + batch_size=256, + num_workers=8, # Больше workers + pin_memory=True, + persistent_workers=True +) +``` + +### Если GPU память позволяет: + +```python +BATCH_SIZE = 512 # Увеличить batch size +``` + +--- + +## Troubleshooting + +**"FileNotFoundError: HDF5 file not found"** +→ Запустите `python preprocess_audioset.py` + +**"Out of Memory"** +→ Уменьшите batch size или установите `cache_in_memory=False` + +**Медленная загрузка** +→ Уменьшите `num_workers` (на Windows попробуйте `num_workers=0`) + +**Нужно переделать preprocessing** +→ Удалите старый файл: `rm ./data/audioset_preprocessed.h5` и запустите preprocessing снова + +--- + +## Дополнительная информация + +Полная документация: [PREPROCESSING_README.md](PREPROCESSING_README.md) diff --git a/ml/anomaly_classifier_architecture.py b/ml/anomaly_classifier_architecture.py index dac9813..2377ce1 100644 --- a/ml/anomaly_classifier_architecture.py +++ b/ml/anomaly_classifier_architecture.py @@ -1,14 +1,94 @@ +import torch import torch.nn as nn +import torch.nn.functional as F -class AnomalyClassifier(nn.Module): - def __init__(self, input_shape: int, output_shape: int): - super().__init__() - self.layer1 = nn.Linear(input_shape, 128) - self.act1 = nn.ReLU() - self.layer2 = nn.Linear(128, output_shape) +from cfg import TargetClass + + +class AudioCNN(nn.Module): + def __init__(self, num_classes=14 , dropout_rate=0.3): + super(AudioCNN, self).__init__() + + self.num_classes = num_classes + self.dropout_rate = dropout_rate + + # Входные размеры: (batch_size, 1, 128, 157) - мел-спектрограмма + + # Блок 1: Выделение локальных признаков + self.conv1 = nn.Conv2d(1, 32, kernel_size=(3, 3), padding=1) + self.bn1 = nn.BatchNorm2d(32) + self.conv2 = nn.Conv2d(32, 32, kernel_size=(3, 3), padding=1) + self.bn2 = nn.BatchNorm2d(32) + self.pool1 = nn.MaxPool2d(kernel_size=(2, 2)) # -> (32, 64, 78) + + # Блок 2: Выделение средних признаков + self.conv3 = nn.Conv2d(32, 64, kernel_size=(3, 3), padding=1) + self.bn3 = nn.BatchNorm2d(64) + self.conv4 = nn.Conv2d(64, 64, kernel_size=(3, 3), padding=1) + self.bn4 = nn.BatchNorm2d(64) + self.pool2 = nn.MaxPool2d(kernel_size=(2, 2)) # -> (64, 32, 39) + + # Блок 3: Выделение высокоуровневых признаков + self.conv5 = nn.Conv2d(64, 128, kernel_size=(3, 3), padding=1) + self.bn5 = nn.BatchNorm2d(128) + self.conv6 = nn.Conv2d(128, 128, kernel_size=(3, 3), padding=1) + self.bn6 = nn.BatchNorm2d(128) + self.pool3 = nn.MaxPool2d(kernel_size=(2, 2)) # -> (128, 16, 19) + + # Блок 4: Специфичные для звука признаки + self.conv7 = nn.Conv2d(128, 256, kernel_size=(3, 3), padding=1) + self.bn7 = nn.BatchNorm2d(256) + self.conv8 = nn.Conv2d(256, 256, kernel_size=(3, 3), padding=1) + self.bn8 = nn.BatchNorm2d(256) + self.pool4 = nn.MaxPool2d(kernel_size=(2, 2)) # -> (256, 8, 9) + + # Глобальное усреднение для уменьшения размерности + self.global_avg_pool = nn.AdaptiveAvgPool2d((1, 1)) # -> (256, 1, 1) + + # Классификационная голова + self.dropout = nn.Dropout(self.dropout_rate) + self.fc1 = nn.Linear(256, 128) + self.fc2 = nn.Linear(128, 64) + # self.fc3 = nn.Linear(64, num_classes) + self.fc3 = nn.Linear(64, self.num_classes) def forward(self, x): - x = self.layer1(x) - x = self.act1(x) - out = self.layer2(x) - return out \ No newline at end of file + # Входные данные должны быть (batch_size, 1, 128, 157) + # Если входные данные плоские, преобразуем их + if len(x.shape) == 2: # (batch_size, 20096) + x = x.view(x.size(0), 1, 128, 157) + elif len(x.shape) == 3: # (batch_size, 128, 157) + x = x.unsqueeze(1) + + # Блок 1 + x = F.relu(self.bn1(self.conv1(x))) + x = F.relu(self.bn2(self.conv2(x))) + x = self.pool1(x) + + # Блок 2 + x = F.relu(self.bn3(self.conv3(x))) + x = F.relu(self.bn4(self.conv4(x))) + x = self.pool2(x) + + # Блок 3 + x = F.relu(self.bn5(self.conv5(x))) + x = F.relu(self.bn6(self.conv6(x))) + x = self.pool3(x) + + # Блок 4 + x = F.relu(self.bn7(self.conv7(x))) + x = F.relu(self.bn8(self.conv8(x))) + x = self.pool4(x) + + # Глобальное усреднение + x = self.global_avg_pool(x) + x = x.view(x.size(0), -1) # Flatten: (batch_size, 256) + + # Классификация + x = self.dropout(x) + x = F.relu(self.fc1(x)) + x = self.dropout(x) + x = F.relu(self.fc2(x)) + x = self.fc3(x) + + return x \ No newline at end of file diff --git a/ml/app.py b/ml/app.py index b41ec95..4057b9a 100644 --- a/ml/app.py +++ b/ml/app.py @@ -6,9 +6,10 @@ import torch import torchaudio -from sympy.stats.rv import probability +import mlflow +import mlflow.pytorch -from anomaly_classifier_architecture import AnomalyClassifier +from anomaly_classifier_architecture import AudioCNN from custom_transformations import get_audio_transforms @@ -19,8 +20,6 @@ from cfg import TARGET_SAMPLE_RATE, TARGET_DURATION, MAX_FILE_SIZE, ALLOWED_FILE_FORMATS, TargetClass -# from main import best_model_path - app = FastAPI( title="Audio Classification API", @@ -37,38 +36,71 @@ class AudioPredictionOutput(BaseModel): anomaly_classifier_model = None device = None +model_info = {} @app.on_event('startup') async def load_model(): - """Загружаем модель при запуске сервера""" + """Загружаем модель из MLflow Model Registry при запуске сервера""" global anomaly_classifier_model global device + global model_info + try: # Check if cuda is available device = 'cuda' if torch.cuda.is_available() else 'cpu' + print(f'Using device: {device}') if device == 'cuda': - print('Cuda is available and will be used as default for computing') + print('CUDA is available and will be used for inference') + + # Настройка MLflow + mlflow.set_tracking_uri("http://localhost:5000") + + # Загрузка модели из MLflow Model Registry + model_name = "AnomalyClassifier" + model_version = "latest" - # Установить backend принудительно + print(f"Loading model '{model_name}' version '{model_version}' from MLflow Model Registry...") - # TODO: Обов'язково потім треба помінять вхідні і вихідні розміри моделі. Вони будуть мінятися при зміні архітектури. - # В Ідеалі вираховувати їх в залежності від параметрів - архітектури, sample rate, ітп. - anomaly_classifier_model = AnomalyClassifier(20096, 14).to(device) + # Формируем URI модели + model_uri = f"models:/{model_name}/{model_version}" - # TODO: Тут теж треба переробити так, щоб шлях до моделі не був захардкожений, а залежав від того, яка на разі найкраща модель. - # Є ідея зробити змінну BEST_MODEL в сfg і при навчанні моделі її актуалізувати. - # А щоб хранить найкращі моделі з різних навчань можна в cfg додать ще одну змінну типу TRAINING_RUN_ID або TRAINING_RUN_COUNTER. - best_model_path = Path('./model_states/best_anomaly_classifier3.pt') - model_parameters = torch.load(best_model_path, map_location=torch.device(device)) - anomaly_classifier_model.load_state_dict(model_parameters) + # Загружаем модель через MLflow + anomaly_classifier_model = mlflow.pytorch.load_model(model_uri, map_location=device) - # Перевод моделі в стан inference + # Переводим модель в режим inference + anomaly_classifier_model.to(device) anomaly_classifier_model.eval() - print("Model was loaded successfully") - # model = your_audio_model_loading_function() + # Сохраняем информацию о модели + from mlflow.tracking import MlflowClient + client = MlflowClient() + + # Получаем информацию о версии модели + model_version_details = client.get_latest_versions(model_name, stages=["None", "Production", "Staging", "Archived"]) + if model_version_details: + latest_version = model_version_details[0] + model_info = { + "name": model_name, + "version": latest_version.version, + "run_id": latest_version.run_id, + "status": latest_version.status, + "stage": latest_version.current_stage + } + print(f"Model loaded successfully!") + print(f" Name: {model_info['name']}") + print(f" Version: {model_info['version']}") + print(f" Run ID: {model_info['run_id']}") + print(f" Stage: {model_info['stage']}") + else: + print("Model loaded successfully (details unavailable)") + except Exception as e: - print(f"Error while loading model: {e}") + print(f"Error while loading model from MLflow: {e}") + print(f"Make sure:") + print(f" 1. MLflow server is running at http://localhost:5000") + print(f" 2. Model '{model_name}' is registered in MLflow Model Registry") + print(f" 3. At least one version of the model exists") + raise def validate_audio_file(file: UploadFile) -> None: @@ -100,6 +132,7 @@ async def health_check(): return { "status": "healthy", "model_loaded": anomaly_classifier_model is not None, + "model_info": model_info if model_info else None, "supported_formats": ALLOWED_FILE_FORMATS, "max_file_size_mb": MAX_FILE_SIZE / 1024 / 1024, "classes": TargetClass.get_class_names() @@ -128,19 +161,25 @@ async def predict_audio(file: UploadFile = File(...)): transform_pipeline = get_audio_transforms() - # Передаем tuple (waveform, sample_rate) в трансформации !!! + # Передаем tuple (waveform, sample_rate) в трансформации processed_tensor = transform_pipeline((waveform, original_sr)) - # TODO: При заміні архітектури можливо треба буде змінити розмірність - processed_tensor = processed_tensor.reshape(128 * 157).to(device) + # Добавляем batch dimension если нужно + if len(processed_tensor.shape) == 2: + processed_tensor = processed_tensor.unsqueeze(0) # (1, 128, 157) + + processed_tensor = processed_tensor.to(device) # Prediction with torch.no_grad(): logits = anomaly_classifier_model(processed_tensor) + # Берем первый элемент из batch (batch_size=1) + logits = logits[0] # (1, 14) -> (14,) + probabilities_tensor = torch.nn.functional.softmax(logits, dim=-1) - predicted_class_index = torch.argmax(logits, dim=-1).item() + predicted_class_index = torch.argmax(logits).item() predicted_class = TargetClass.from_index(predicted_class_index) probability_dict = {cls.class_name: probabilities_tensor[cls.index].item() for cls in TargetClass} diff --git a/ml/cfg.py b/ml/cfg.py index d643673..2d7025e 100644 --- a/ml/cfg.py +++ b/ml/cfg.py @@ -1,5 +1,5 @@ TARGET_SAMPLE_RATE = 16000 # Стандартная частота для обработки речи -TARGET_DURATION = 5.0 # Длительность в секундах +TARGET_DURATION = 10.0 # Длительность в секундах N_MELS = 128 # Количество мел-фильтров N_FFT = 2048 # Размер FFT HOP_LENGTH = 512 # Шаг для STFT diff --git a/ml/main.ipynb b/ml/main.ipynb deleted file mode 100644 index 1438deb..0000000 --- a/ml/main.ipynb +++ /dev/null @@ -1,871 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "id": "initial_id", - "metadata": { - "collapsed": true, - "ExecuteTime": { - "end_time": "2025-08-28T15:50:10.789779Z", - "start_time": "2025-08-28T15:50:10.744555Z" - } - }, - "source": [ - "import numpy as np\n", - "import pandas as pd\n", - "import matplotlib.pyplot as plt\n", - "\n", - "\n", - "import torch\n", - "import torch.nn as nn\n", - "import torch.nn.functional as F\n", - "from sympy.physics.units import length\n", - "from torch.utils.data import Dataset, DataLoader, random_split\n", - "import torchaudio\n", - "import torchvision\n", - "\n", - "\n", - "import os\n", - "from pathlib import Path\n", - "from typing import Optional, Callable\n", - "from tqdm import tqdm\n", - "\n", - "\n", - "import cfg" - ], - "outputs": [], - "execution_count": 83 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-08-28T15:50:10.928944Z", - "start_time": "2025-08-28T15:50:10.914552Z" - } - }, - "cell_type": "code", - "source": [ - "# Defining device to work with tensors\n", - "device = 'cuda' if torch.cuda.is_available() else 'cpu'\n", - "device" - ], - "id": "612f9cd2dedda865", - "outputs": [ - { - "data": { - "text/plain": [ - "'cuda'" - ] - }, - "execution_count": 84, - "metadata": {}, - "output_type": "execute_result" - } - ], - "execution_count": 84 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-08-28T15:50:11.039390Z", - "start_time": "2025-08-28T15:50:11.024915Z" - } - }, - "cell_type": "code", - "source": [ - "# UrbanSound8K маппинг\n", - "URBANSOUND8K_MAPPING = {\n", - " 'air_conditioner': 'household_sounds',\n", - " 'car_horn': 'normal_transport',\n", - " 'children_playing': 'public_spaces',\n", - " 'dog_bark': 'nature_sounds',\n", - " 'drilling': 'work_sounds',\n", - " 'engine_idling': 'normal_transport',\n", - " 'gun_shot': 'weapon_violence', # ОПАСНО!\n", - " 'jackhammer': 'work_sounds',\n", - " 'siren': 'emergency_services', # ОПАСНО!\n", - " 'street_music': 'music_entertainment'\n", - "}\n", - "\n", - "# Ваши собственные данные (добавляйте сюда)\n", - "CUSTOM_MAPPING = {\n", - " # Добавляйте ваши классы тут\n", - " 'police_siren': 'emergency_services',\n", - " 'baby_crying': 'human_distress',\n", - " 'breaking_glass': 'structural_damage',\n", - " # ...\n", - "}\n", - "\n" - ], - "id": "ddc33507199a0dba", - "outputs": [], - "execution_count": 85 - }, - { - "metadata": {}, - "cell_type": "markdown", - "source": [ - "# Мапер класів для класифікації звуків\n", - "\n", - "Цей модуль призначений для уніфікації різних датасетів звуків під єдину систему класифікації, що розділяє звуки на **небезпечні** та **безпечні**.\n", - "\n", - "## Цільові класи\n", - "\n", - "Система використовує 14 цільових класів, розбитих на дві категорії:\n", - "\n", - "### 🚨 Небезпечні звуки (0-5)\n", - "- `emergency_services` - сирени, служби екстреного реагування\n", - "- `human_distress` - плач, крики про допомогу\n", - "- `structural_damage` - розбиття скла, обвалення\n", - "- `fire_explosion` - пожежі, вибуховолнення\n", - "- `traffic_emergency` - аварії, екстрені ситуації на дорозі\n", - "- `weapon_violence` - постріли, насильство\n", - "\n", - "### ✅ Безпечні звуки (6-13)\n", - "- `speech_communication` - мовлення, розмови\n", - "- `music_entertainment` - музика, розваги\n", - "- `household_sounds` - побутові звуки\n", - "- `work_sounds` - робочі інструменти\n", - "- `normal_transport` - звичайний транспорт\n", - "- `nature_sounds` - природні звуки\n", - "- `public_spaces` - звуки громадських місць\n", - "- `special_events` - свята, події\n", - "\n", - "## Підтримувані датасети\n", - "\n", - "- **ESC-50** - 50 класів навколишніх звуків\n", - "- **UrbanSound8K** - 10 класів міських звуків\n", - "- **Custom** - ваші власні дані\n", - "\n", - "## Використання\n", - "\n", - "```python\n", - "# Отримати індекс класу для моделі\n", - "index = map_class_to_index('esc50', 'siren') # -> 0 (emergency_services)\n", - "index = map_class_to_index('urbansound8k', 'gun_shot') # -> 5 (weapon_violence)\n", - "\n", - "# Отримати назву класу за індексом\n", - "class_name = IDX_TO_CLASS[0] # -> 'emergency_services'\n", - "```\n", - "\n", - "Функція повертає `None` якщо клас не знайдено в мепінгу." - ], - "id": "7a87e7497f10f245" - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-08-28T15:50:11.147546Z", - "start_time": "2025-08-28T15:50:11.117866Z" - } - }, - "cell_type": "code", - "source": [ - "from cfg import TargetClass\n", - "\n", - "class ESC50Dataset(Dataset):\n", - " \"\"\"PyTorch Dataset for ESC-50\"\"\"\n", - " def __init__(self, path_to_root_dir: str, target_sample_rate: int = TARGET_SAMPLE_RATE,\n", - " duration: float = TARGET_DURATION, transform: Optional[Callable] = None):\n", - " self.root_dir_path = Path(path_to_root_dir)\n", - " self.audio_dir_path = self.root_dir_path / '44100'\n", - " self.target_sample_rate = target_sample_rate\n", - " self.duration = duration\n", - " self.transform = transform\n", - "\n", - " self.data_list = []\n", - " self.length = 0\n", - "\n", - " # Get metadata\n", - " meta_file_path = self.root_dir_path / 'esc50.csv'\n", - " self.metadata = pd.read_csv(meta_file_path)\n", - " self.length = len(self.metadata)\n", - "\n", - " # Attributes to work with ESC-50 dataset markings\n", - " classes_target_df = self.metadata[['category', 'target']].drop_duplicates().sort_values('target')\n", - " self.class_to_idx = dict(zip(classes_target_df['category'], classes_target_df['target']))\n", - " self.idx_to_class = dict(zip(classes_target_df['target'], classes_target_df['category']))\n", - " self.classes = classes_target_df['category'].to_list()\n", - "\n", - " self.ESC50_TO_TARGET_MAPPING = {\n", - " # Nature sounds\n", - " 'dog': TargetClass.NATURE_SOUNDS,\n", - " 'rooster': TargetClass.NATURE_SOUNDS,\n", - " 'pig': TargetClass.NATURE_SOUNDS,\n", - " 'cow': TargetClass.NATURE_SOUNDS,\n", - " 'frog': TargetClass.NATURE_SOUNDS,\n", - " 'cat': TargetClass.NATURE_SOUNDS,\n", - " 'hen': TargetClass.NATURE_SOUNDS,\n", - " 'insects': TargetClass.NATURE_SOUNDS,\n", - " 'sheep': TargetClass.NATURE_SOUNDS,\n", - " 'crow': TargetClass.NATURE_SOUNDS,\n", - " 'rain': TargetClass.NATURE_SOUNDS,\n", - " 'sea_waves': TargetClass.NATURE_SOUNDS,\n", - " 'crackling_fire': TargetClass.NATURE_SOUNDS,\n", - " 'crickets': TargetClass.NATURE_SOUNDS,\n", - " 'chirping_birds': TargetClass.NATURE_SOUNDS,\n", - " 'water_drops': TargetClass.NATURE_SOUNDS,\n", - " 'wind': TargetClass.NATURE_SOUNDS,\n", - " 'pouring_water': TargetClass.NATURE_SOUNDS,\n", - " 'toilet_flush': TargetClass.HOUSEHOLD_SOUNDS,\n", - " 'thunderstorm': TargetClass.NATURE_SOUNDS,\n", - "\n", - " # Human sounds\n", - " 'crying_baby': TargetClass.HUMAN_DISTRESS, # ОПАСНО\n", - " 'sneezing': TargetClass.SPEECH_COMMUNICATION,\n", - " 'clapping': TargetClass.PUBLIC_SPACES,\n", - " 'breathing': TargetClass.SPEECH_COMMUNICATION,\n", - " 'coughing': TargetClass.SPEECH_COMMUNICATION,\n", - " 'footsteps': TargetClass.SPEECH_COMMUNICATION,\n", - " 'laughing': TargetClass.SPEECH_COMMUNICATION,\n", - " 'brushing_teeth': TargetClass.HOUSEHOLD_SOUNDS,\n", - " 'snoring': TargetClass.SPEECH_COMMUNICATION,\n", - " 'drinking_sipping': TargetClass.HOUSEHOLD_SOUNDS,\n", - "\n", - " # Every day/work sounds\n", - " 'door_wood_knock': TargetClass.HOUSEHOLD_SOUNDS,\n", - " 'mouse_click': TargetClass.HOUSEHOLD_SOUNDS,\n", - " 'keyboard_typing': TargetClass.HOUSEHOLD_SOUNDS,\n", - " 'door_wood_creaks': TargetClass.HOUSEHOLD_SOUNDS,\n", - " 'can_opening': TargetClass.HOUSEHOLD_SOUNDS,\n", - " 'washing_machine': TargetClass.HOUSEHOLD_SOUNDS,\n", - " 'vacuum_cleaner': TargetClass.HOUSEHOLD_SOUNDS,\n", - " 'clock_alarm': TargetClass.HOUSEHOLD_SOUNDS,\n", - " 'clock_tick': TargetClass.HOUSEHOLD_SOUNDS,\n", - " 'chainsaw': TargetClass.WORK_SOUNDS,\n", - " 'hand_saw': TargetClass.WORK_SOUNDS,\n", - "\n", - " # Transport\n", - " 'helicopter': TargetClass.NORMAL_TRANSPORT,\n", - " 'car_horn': TargetClass.NORMAL_TRANSPORT,\n", - " 'engine': TargetClass.NORMAL_TRANSPORT,\n", - " 'train': TargetClass.NORMAL_TRANSPORT,\n", - " 'airplane': TargetClass.NORMAL_TRANSPORT,\n", - "\n", - " # Dangerous sounds\n", - " 'glass_breaking': TargetClass.STRUCTURAL_DAMAGE, # ОПАСНО!\n", - " 'siren': TargetClass.EMERGENCY_SERVICES, # ОПАСНО!\n", - "\n", - " # Events\n", - " 'church_bells': TargetClass.SPECIAL_EVENTS,\n", - " 'fireworks': TargetClass.SPECIAL_EVENTS\n", - " }\n", - "\n", - "\n", - " # Make list with filepath and target for dataset\n", - " mapped_file_names = self.metadata['filename'].map(lambda x: self.audio_dir_path / x)\n", - " mapped_target_categories = self.metadata['category'].map(lambda x: self.ESC50_TO_TARGET_MAPPING[x].index) # Map categories from ESC-50 to indexes\n", - " self.data_list = list(zip(mapped_file_names, mapped_target_categories))\n", - "\n", - " def __len__(self) -> int:\n", - " return self.length\n", - "\n", - " def __getitem__(self, idx: int) -> tuple[torch.Tensor, int]:\n", - " file_path, target = self.data_list[idx]\n", - " sample, _ = torchaudio.load(file_path)\n", - "\n", - " if self.transform is not None:\n", - " sample = self.transform(sample)\n", - "\n", - " return sample, target\n", - "\n", - "# '''\n", - "# !!!DEPRECATED!!!\n", - "# Але бляді прибрали лінк для torchcodec для вінди.\n", - "# Треба чекати, поки він з'явиться і тоді переписати, або працювать на лінуксі.\n", - "# Є ідея підняти це в докери, але він може не дозваоляти відеокарті працювати на повну\n", - "# !!! ffmpeg через conda обов'язково треба качать з каналу conda-forge !!!\n", - "# '''\n", - "# path = Path('./data/esc50/44100/1-137-A-32.wav')\n", - "# waveform, sample_rate = torchaudio.load(path)\n", - "# type(waveform)" - ], - "id": "31ddd0d74a7bf381", - "outputs": [], - "execution_count": 86 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-08-28T15:50:11.238983Z", - "start_time": "2025-08-28T15:50:11.225101Z" - } - }, - "cell_type": "code", - "source": [ - "from custom_transformations import (ResampleTransform, FixedLengthTransform, ToDecibelTransform,\n", - " MelSpectrogramTransform, NormalizeTransform, get_audio_transforms)" - ], - "id": "6b596154e905878e", - "outputs": [], - "execution_count": 87 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-08-28T15:50:11.410935Z", - "start_time": "2025-08-28T15:50:11.335420Z" - } - }, - "cell_type": "code", - "source": [ - "esc_50_dataset = ESC50Dataset(\n", - " path_to_root_dir='./data/esc50',\n", - " transform=get_audio_transforms()\n", - ")\n", - "\n", - "print(esc_50_dataset[0][0].shape)\n", - "\n", - "esc_50_train_data, esc_50_val_data, esc_50_test_data = random_split(esc_50_dataset, [0.5, 0.3, 0.2])" - ], - "id": "9019ab1114560dd", - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "torch.Size([1, 128, 157])\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Users\\Dalvy07\\miniconda3\\envs\\ml\\lib\\site-packages\\torchaudio\\_backend\\utils.py:213: UserWarning: In 2.9, this function's implementation will be changed to use torchaudio.load_with_torchcodec` under the hood. Some parameters like ``normalize``, ``format``, ``buffer_size``, and ``backend`` will be ignored. We recommend that you port your code to rely directly on TorchCodec's decoder instead: https://docs.pytorch.org/torchcodec/stable/generated/torchcodec.decoders.AudioDecoder.html#torchcodec.decoders.AudioDecoder.\n", - " warnings.warn(\n", - "C:\\Users\\Dalvy07\\miniconda3\\envs\\ml\\lib\\site-packages\\torchaudio\\_backend\\ffmpeg.py:88: UserWarning: torio.io._streaming_media_decoder.StreamingMediaDecoder has been deprecated. This deprecation is part of a large refactoring effort to transition TorchAudio into a maintenance phase. The decoding and encoding capabilities of PyTorch for both audio and video are being consolidated into TorchCodec. Please see https://github.com/pytorch/audio/issues/3902 for more information. It will be removed from the 2.9 release. \n", - " s = torchaudio.io.StreamReader(src, format, None, buffer_size)\n" - ] - } - ], - "execution_count": 88 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-08-28T15:50:11.534709Z", - "start_time": "2025-08-28T15:50:11.520743Z" - } - }, - "cell_type": "code", - "source": [ - "# Dataloaders defining\n", - "BATCHSIZE = 2\n", - "LEARNING_RATE = 0.001\n", - "\n", - "esc_50_train_loader = DataLoader(esc_50_train_data, batch_size=BATCHSIZE, shuffle=True)\n", - "esc_50_val_loader = DataLoader(esc_50_val_data, batch_size=BATCHSIZE, shuffle=False)\n", - "esc_50_test_loader = DataLoader(esc_50_test_data, batch_size=BATCHSIZE, shuffle=False)" - ], - "id": "b55510b1c511745e", - "outputs": [], - "execution_count": 89 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-08-28T15:50:11.644873Z", - "start_time": "2025-08-28T15:50:11.630591Z" - } - }, - "cell_type": "code", - "source": "from anomaly_classifier_architecture import AnomalyClassifier", - "id": "da7a3c80c10ad792", - "outputs": [], - "execution_count": 90 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-08-28T15:50:11.769504Z", - "start_time": "2025-08-28T15:50:11.740538Z" - } - }, - "cell_type": "code", - "source": [ - "anomaly_classifier = AnomalyClassifier(20096, 14).to(device)\n", - "\n", - "loss_func = nn.CrossEntropyLoss()\n", - "optimizer = torch.optim.Adam(anomaly_classifier.parameters(), lr=0.001)\n", - "\n", - "lr_scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(\n", - " optimizer,\n", - " mode='min',\n", - " factor=0.1,\n", - " patience=5\n", - ")" - ], - "id": "f04c415dc3ac0285", - "outputs": [], - "execution_count": 91 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-08-28T15:50:11.878886Z", - "start_time": "2025-08-28T15:50:11.865422Z" - } - }, - "cell_type": "code", - "source": [ - "test_input = torch.rand([16, 20096], dtype=torch.float32).to(device)\n", - "\n", - "result = anomaly_classifier(test_input)\n", - "result.shape" - ], - "id": "c03418fd3a131ff7", - "outputs": [ - { - "data": { - "text/plain": [ - "torch.Size([16, 14])" - ] - }, - "execution_count": 92, - "metadata": {}, - "output_type": "execute_result" - } - ], - "execution_count": 92 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-08-28T15:53:18.980177Z", - "start_time": "2025-08-28T15:50:11.972872Z" - } - }, - "cell_type": "code", - "source": [ - "EPOCHS = 10\n", - "\n", - "train_loss = []\n", - "train_acc = []\n", - "val_loss = []\n", - "val_acc = []\n", - "lr_list = []\n", - "best_loss = None\n", - "best_model_path = None\n", - "checkpoint_path = None\n", - "threshold = 0.0001\n", - "\n", - "\n", - "for epoch in range(EPOCHS):\n", - " anomaly_classifier.train()\n", - "\n", - " running_train_loss = []\n", - " true_answer_counter = 0\n", - " train_loop = tqdm(esc_50_train_loader)\n", - " for x, targets in train_loop:\n", - " x = x.reshape(-1, 128*157).to(device)\n", - "\n", - " targets = targets.reshape(-1).to(torch.int32)\n", - " targets = torch.eye(14)[targets].to(device)\n", - "\n", - " pred = anomaly_classifier(x)\n", - " loss = loss_func(pred, targets)\n", - "\n", - " # Backward dataflow\n", - " optimizer.zero_grad() # Zeroing previously calculated gradients\n", - " loss.backward() # Backward passage (проход). In result we calculating new gradients for model parameters. De facto we calculate how much every parameter value on loss\n", - " optimizer.step() # Correct learning parameters (веса связей между нейронами) based on calculated gradients using choosed optimizer of gradient descending. Its named optimization step\n", - "\n", - " # Calculating mean loss\n", - " running_train_loss.append(loss.item())\n", - " mean_train_loss = sum(running_train_loss)/len(running_train_loss)\n", - "\n", - " # Calculating number of true answers for metrics (accuracy)\n", - " true_answer_counter += (pred.argmax(dim=1) == targets.argmax(dim=1)).sum().item()\n", - "\n", - " train_loop.set_description(f'Epoch [{epoch+1}/{EPOCHS}], training_loss={mean_train_loss:.4f}')\n", - "\n", - " # Calculating accuracy\n", - " running_train_accuracy = true_answer_counter / len(esc_50_train_data)\n", - "\n", - " # Save metrics\n", - " train_loss.append(mean_train_loss)\n", - " train_acc.append(running_train_accuracy)\n", - " # BASIC LEARNING PROCESS\n", - " # |---------------------------------------------------------------------|\n", - "\n", - "\n", - " anomaly_classifier.eval()\n", - " with torch.no_grad(): # With this context manager FORBID to calculate gradients inside manager\n", - " running_val_loss = []\n", - " true_val_answer_counter = 0\n", - "\n", - " for x, targets in esc_50_val_loader:\n", - " x = x.reshape(-1, 128*157).to(device)\n", - "\n", - " targets = targets.reshape(-1).to(torch.int32)\n", - " targets = torch.eye(14)[targets].to(device)\n", - "\n", - " pred = anomaly_classifier(x)\n", - " loss = loss_func(pred, targets)\n", - "\n", - " running_val_loss.append(loss.item())\n", - " mean_val_loss = sum(running_val_loss)/len(running_val_loss)\n", - "\n", - " true_val_answer_counter += (pred.argmax(dim=1) == targets.argmax(dim=1)).sum().item()\n", - "\n", - " # Calculating accuracy\n", - " running_val_accuracy = true_val_answer_counter / len(esc_50_val_data)\n", - "\n", - " # Save metrics\n", - " val_loss.append(mean_val_loss)\n", - " val_acc.append(running_val_accuracy)\n", - "\n", - " lr_scheduler.step(mean_val_loss)\n", - " lr = lr_scheduler.get_last_lr()[0]\n", - " lr_list.append(lr)\n", - "\n", - " print(f'Epoch [{epoch+1}/{EPOCHS}], training_loss={mean_train_loss:.4f}, training_acc={running_train_accuracy:.4f}, val_loss={mean_val_loss:.4f}, val_acc={running_val_accuracy:.4f}, lr={lr:.4f}')\n", - "\n", - " # Saving a model if it has better metrics\n", - " if best_loss is None:\n", - " best_loss = mean_val_loss\n", - "\n", - " if mean_val_loss < best_loss - best_loss * threshold:\n", - " # Delete the previous best model\n", - " if best_model_path is not None and best_model_path.exists():\n", - " best_model_path.unlink()\n", - " print(f'Last checkpoint was deleted')\n", - "\n", - " best_loss = mean_val_loss\n", - "\n", - " # Create a new path for the model state\n", - " best_model_path = Path(f'./model_states/best_anomaly_classifier{epoch+1}.pt')\n", - "\n", - " # Create a directory if it does not exist\n", - " # best_model_path.parent.mkdir(parents=True, exist_ok=True)\n", - "\n", - " torch.save(anomaly_classifier.state_dict(), best_model_path)\n", - " print(f'On epoch - {epoch+1} was saved model with mean_val_loss: {mean_val_loss:.4f}')\n", - "\n", - " if checkpoint_path is not None and checkpoint_path.exists():\n", - " checkpoint_path.unlink()\n", - " print(f'The previous best model was deleted')\n", - "\n", - " checkpoint_path = Path(f'./model_states/checkpoint_states{epoch+1}.pt')\n", - "\n", - " checkpoint = {\n", - " 'info': 'Checkpoint for anomaly classification model',\n", - " 'state_model': anomaly_classifier.state_dict(),\n", - " 'state_optimizer': optimizer.state_dict(),\n", - " 'state_lr_scheduler': lr_scheduler.state_dict(),\n", - " 'loss': {\n", - " 'train_loss': train_loss,\n", - " 'val_loss': val_loss,\n", - " 'best_loss': best_loss\n", - " },\n", - " 'metrics': {\n", - " 'train_acc': train_acc,\n", - " 'val_acc': val_acc\n", - " },\n", - " 'lr': lr_list,\n", - " 'epoch': {\n", - " 'EPOCHS': EPOCHS,\n", - " 'save_epoch': epoch\n", - " }\n", - " }\n", - "\n", - " torch.save(checkpoint, checkpoint_path)\n", - "\n", - " print(f'Checkpoint for model training was saved on epoch - {epoch+1}', end='\\n\\n')\n", - "\n" - ], - "id": "304f9f1dd7e374b3", - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch [1/10], training_loss=2.0162: 100%|██████████| 500/500 [00:13<00:00, 35.86it/s]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Epoch [1/10], training_loss=2.0162, training_acc=0.3510, val_loss=1.7233, val_acc=0.4050, lr=0.0010\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch [2/10], training_loss=1.5658: 100%|██████████| 500/500 [00:12<00:00, 39.96it/s]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Epoch [2/10], training_loss=1.5658, training_acc=0.4550, val_loss=1.6032, val_acc=0.4333, lr=0.0010\n", - "On epoch - 2 was saved model with mean_val_loss: 1.6032\n", - "Checkpoint for model training was saved on epoch - 2\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch [3/10], training_loss=1.3515: 100%|██████████| 500/500 [00:12<00:00, 39.55it/s]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Epoch [3/10], training_loss=1.3515, training_acc=0.4970, val_loss=1.5241, val_acc=0.4100, lr=0.0010\n", - "Last checkpoint was deleted\n", - "On epoch - 3 was saved model with mean_val_loss: 1.5241\n", - "The previous best model was deleted\n", - "Checkpoint for model training was saved on epoch - 3\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch [4/10], training_loss=1.1936: 100%|██████████| 500/500 [00:12<00:00, 39.66it/s]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Epoch [4/10], training_loss=1.1936, training_acc=0.5490, val_loss=1.6468, val_acc=0.3650, lr=0.0010\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch [5/10], training_loss=1.0661: 100%|██████████| 500/500 [00:12<00:00, 40.43it/s]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Epoch [5/10], training_loss=1.0661, training_acc=0.6170, val_loss=1.6539, val_acc=0.3817, lr=0.0010\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch [6/10], training_loss=0.8776: 100%|██████████| 500/500 [00:12<00:00, 38.48it/s]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Epoch [6/10], training_loss=0.8776, training_acc=0.6750, val_loss=1.6719, val_acc=0.4367, lr=0.0010\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch [7/10], training_loss=0.7845: 100%|██████████| 500/500 [00:12<00:00, 38.87it/s]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Epoch [7/10], training_loss=0.7845, training_acc=0.6920, val_loss=1.6697, val_acc=0.4333, lr=0.0010\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch [8/10], training_loss=0.7551: 100%|██████████| 500/500 [00:13<00:00, 38.18it/s]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Epoch [8/10], training_loss=0.7551, training_acc=0.7270, val_loss=1.8404, val_acc=0.4000, lr=0.0010\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch [9/10], training_loss=0.7122: 100%|██████████| 500/500 [00:12<00:00, 40.30it/s]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Epoch [9/10], training_loss=0.7122, training_acc=0.7380, val_loss=1.7727, val_acc=0.4217, lr=0.0001\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Epoch [10/10], training_loss=0.4950: 100%|██████████| 500/500 [00:12<00:00, 39.32it/s]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Epoch [10/10], training_loss=0.4950, training_acc=0.8250, val_loss=1.7061, val_acc=0.4550, lr=0.0001\n" - ] - } - ], - "execution_count": 93 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-08-28T15:53:19.443791Z", - "start_time": "2025-08-28T15:53:19.057975Z" - } - }, - "cell_type": "code", - "source": [ - "# Убираем лишнее измерение и отображаем спектрограмму\n", - "spectrogram = esc_50_dataset[1000][0].squeeze() # Форма: (128, 157)\n", - "\n", - "plt.figure(figsize=(12, 6))\n", - "plt.imshow(spectrogram.numpy(), aspect='auto', origin='lower', cmap='viridis')\n", - "plt.colorbar(label='Amplitude (dB)')\n", - "plt.title('Мел-спектрограмма')\n", - "plt.xlabel('Время (кадры)')\n", - "plt.ylabel('Мел-фильтры')\n", - "plt.show()\n" - ], - "id": "26b3dcd68b3c84ac", - "outputs": [ - { - "data": { - "text/plain": [ - "
" - ], - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA6wAAAIjCAYAAAAHqpb9AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjUsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvWftoOwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAkjNJREFUeJzs3Xl8VNX9//H3zCSZ7AtbQlgExQUEQUER0dYqLS5FaW2tlgouVatSFVq3VsUdtS64INQFrK1Wq1Vr1WoRFfUnIoJUKQio7JCwZt9n7u8Pv069+VwkmdyQCXk9H495PLyfOXPuuXcm6rnnfM4JOI7jCAAAAACABBNs6wYAAAAAAOCFDisAAAAAICHRYQUAAAAAJCQ6rAAAAACAhESHFQAAAACQkOiwAgAAAAASEh1WAAAAAEBCosMKAAAAAEhIdFgBAAAAAAmJDisAAAAAICHRYQWANvD4448rEAgoEAjovffeM+87jqNevXopEAjohz/8YRu0EAAAoO3RYQWANpSamqqnnnrKxOfNm6cNGzYoHA63QasAAAASAx1WAGhDJ510kp599lk1NDS44k899ZSGDh2qgoKCNmoZAABA26PDCgBt6Mwzz9T27ds1Z86cWKyurk7PPfecfv7zn5vy0WhU06ZN08EHH6zU1FTl5+frwgsv1M6dO03Zt99+Ozbt+JuvPn36NKltJSUlmjRpkvr06aNwOKyePXtq/Pjx2rZt27fW//Xr7LPPNvVdfvnl6tWrl8LhsPr166c77rhD0Wg0VmbNmjUKBAJ6/PHHY7Hy8nINHTpUffv21ebNm2NlmnLur6dev/POO7rwwgvVuXNnZWdna/z48Z737KGHHtLBBx+scDiswsJCXXLJJSopKXGVOfbYYz3POWrUqFiZQCCgiRMn6sknn9SBBx6o1NRUDR06VO+8846rrrVr1+riiy/WgQceqLS0NHXu3Fk//elPtWbNGle5r68jJSVFW7dudb03f/78WBs++ugj086xY8ea67zwwgsVCAQ0cOBAV/yuu+7SUUcdpc6dOystLU1Dhw7Vc889Zz4PAMCektTWDQCAjqxPnz4aMWKE/vrXv+rEE0+UJP3rX/9SaWmpzjjjDN1///2u8hdeeKEef/xxnXPOObr00ku1evVqPfjgg/r444/1//7f/1NycrI5x+9+9zv1799fkvTwww9r3bp1u21XRUWFjjnmGC1fvlznnnuuDjvsMG3btk0vvfSSNmzYoC5dusTKXnrppTr88MNdn//lL3/pOq6qqtJ3v/tdbdy4URdeeKF69+6t999/X9dcc402b96sadOmebajvr5ep512mtatW6f/9//+n7p3767Kykr9+c9/jpV5/vnn9cILL7hi++23n6ueiRMnKjc3VzfccINWrFihGTNmaO3atbFOtyTdcMMNuvHGGzVq1ChddNFFsXILFy4097Znz56aOnWq6xzdu3d3Hc+bN0/PPPOMLr30UoXDYT300EM64YQT9OGHH8Y6igsXLtT777+vM844Qz179tSaNWs0Y8YMHXvssVq2bJnS09NddYZCIf3lL3/RpEmTYrHZs2crNTVVNTU15v6lpqbqlVde0ZYtW9StWzdJUnV1tZ555hmlpqaa8vfdd59OOeUUjRs3TnV1dXr66af105/+VC+//LJOPvlkj28IAIBW5gAA9rjZs2c7kpyFCxc6Dz74oJOVleVUVVU5juM4P/3pT53vfe97juM4zj777OOcfPLJjuM4zrvvvutIcp588klXXa+99ppnfM6cOY4kZ968ebHYhAkTnH322We37bv++usdSc7zzz9v3otGo47jOM5bb73lSHKeffZZUyYjI8OZMGFC7Pjmm292MjIynJUrV7rKXX311U4oFHLWrVvnOI7jrF692pHkzJ4924lGo864ceOc9PR0Z8GCBbts65QpU5xd/efs6/s8dOhQp66uLha/8847HUnOP/7xD8dxHGfLli1OSkqK84Mf/MCJRCKxcg8++KAjyZk1a1Ys9t3vftc5+OCDd9kex3EcSY4k56OPPorF1q5d66Smpjo/+tGPYrGvv/Nvmj9/viPJeeKJJ8x1nHnmmc6gQYNi8crKSic7O9v5+c9/Hvs9NW7nIYcc4tx1112x+J///GenZ8+ezjHHHGOuo3F76urqnIEDBzrHHXfct14vAACthSnBANDGTj/9dFVXV+vll19WeXm5Xn75Zc/pwM8++6xycnL0/e9/X9u2bYu9hg4dqszMTL311luu8nV1dZIU18JNf//73zV48GD96Ec/Mu99PSLZHM8++6yOOeYY5eXludo+atQoRSIRM1VWkq644go9+eST+tvf/qYjjjii2ef8pgsuuMA1QnrRRRcpKSlJr776qiTpjTfeUF1dnS6//HIFg//7T+P555+v7OxsvfLKK80+54gRIzR06NDYce/evXXqqafq9ddfVyQSkSSlpaXF3q+vr9f27dvVr18/5ebmavHixabOs846S5999lls6u/f//535eTk6Pjjj99lO8455xzNnj07djx79mxNmDDBdZ1f+2Z7du7cqdLSUh1zzDGebQEAYE9gSjAAtLGuXbtq1KhReuqpp1RVVaVIJKKf/OQnptyqVatUWloam9rZ2JYtW1zHX+deZmZm7vLcRUVFruOcnBylpaXpiy++0GmnndbMK9m1VatW6ZNPPlHXrl0932/c9j/+8Y/64IMPJMkz17S59t9/f9dxZmamunfvHssVXbt2rSTpwAMPdJVLSUnRvvvuG3u/JeeUpAMOOEBVVVXaunWrCgoKVF1dralTp2r27NnauHGjHMeJlS0tLTWf79q1q04++WTNmjVLw4YN06xZs3bZ+fzauHHjdOWVV+rDDz9Ut27d9Pbbb+uPf/yj53ZKL7/8sm655RYtWbJEtbW1sXg8DykAAPADHVYASAA///nPdf7556uoqEgnnniicnNzTZloNKpu3brpySef9KyjcWfw687ot6003Djvcvbs2WaxJD9Eo1F9//vf15VXXun5/gEHHOA6/uCDD3Trrbdq4cKFmjRpkk444QRX3uze4te//rVmz56tyy+/XCNGjFBOTo4CgYDOOOMM12JU33Tuuedq/Pjx+vWvf6133nlHjz76qN59991dnqNr164aM2aMZs+erfz8fI0cOVL9+vUz5d59912dcsop+s53vqOHHnpI3bt3V3JysmbPnu259RIAAHsCHVYASAA/+tGPdOGFF+qDDz7QM88841lmv/320xtvvKGRI0e6pm7uyrJly9S1a1d17tx5l2W+uTqxJB188MGxcy1durQZV/Dt9ttvP1VUVLhW0v025557rn73u99p06ZNGjBggCZNmuRaVKm5Vq1ape9973ux44qKCm3evFknnXSSJGmfffaRJK1YsUL77rtvrFxdXZ1Wr17d5HY3PmdjK1euVHp6euzhwnPPPacJEybo7rvvjpWpqakxKxN/04knnqjU1FSdccYZOvroo7Xffvt9a4dV+up+jhs3Tjk5Obrhhhs8y/z9739XamqqXn/9ddc08m9OJwYAYE8jhxUAEkBmZqZmzJihG264QWPGjPEsc/rppysSiejmm2827zU0NLg6OeXl5Xr11Vd13HHHfet5R40a5Xp9PeJ62mmn6T//+Y9eeOEF85lvTlttqtNPP13z58/X66+/bt4rKSkx+9Aec8wxkqTCwkLdcccd+stf/qJ///vfzT7v1x5++GHV19fHjmfMmKGGhobYysyjRo1SSkqK7r//ftf1PfbYYyotLY1rhdz58+e7cj/Xr1+vf/zjH/rBD36gUCgk6atVfxvfzwceeCCW4+olKSlJ48eP1yeffKJzzz23SW054YQTlJGRoR07duj000/3LBMKhRQIBFznXrNmjV588cUmnQMAgNbACCsAJIgJEyZ86/vf/e53deGFF2rq1KlasmSJfvCDHyg5OVmrVq3Ss88+q/vuu08/+clP9Le//U033nijdu7cqauvvjqutlxxxRV67rnn9NOf/lTnnnuuhg4dqh07duill17SzJkzNXjw4GbX99JLL+mHP/yhzj77bA0dOlSVlZX69NNP9dxzz2nNmjW7nPJ7wQUX6KmnntKvfvUrLV261Gz10hR1dXU6/vjjdfrpp2vFihV66KGHdPTRR+uUU06R9NW02WuuuUY33nijTjjhBJ1yyimxcocffrh+8YtfNPucAwcO1OjRo13b2kjSjTfeGCvzwx/+UH/+85+Vk5OjAQMGaP78+XrjjTe+dVRckm6++WZdccUVysvLa1JbQqGQli9fLsdxlJGR4Vnm5JNP1j333KMTTjhBP//5z7VlyxZNnz5d/fr10yeffNLEqwYAwF90WAGgHZk5c6aGDh2qP/7xj/rd736npKQk9enTR7/4xS80cuRISdLTTz+tffbZR7Nnz9aQIUPiOk9mZqbeffddTZkyRS+88IL+9Kc/qVu3bjr++OPVs2fPZteXnp6uefPm6bbbbtOzzz6rJ554QtnZ2TrggAN04403KicnZ5efDQQCeuSRRzR48GBde+21uueee5p9/gcffFBPPvmkrr/+etXX1+vMM8/U/fff71pM6IYbblDXrl314IMPatKkSerUqZMuuOAC3XbbbZ772+7Od7/7XY0YMUI33nij1q1bpwEDBujxxx/XIYccEitz3333KRQK6cknn1RNTY1GjhypN954Q6NHj/7WulNSUpqd05udnf2t7x933HF67LHHdPvtt+vyyy9X3759dccdd2jNmjV0WAEAbSbgxDO3CwCAduDxxx/XOeeco4UL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l1/qzTff1Lhx4/Tqq6/q888/18UXX6z6+npNmTLF8zO766y2tHxcHdY+ffp4ThN1HEeBQECRiMcSuAAAAADQETgB/1f1/b/61q9fr+xvpA16ja7GKxqNqlu3bnr44YcVCoU0dOhQbdy4UX/4wx922WH9pu3bt8c6pOvXr9cjjzyi6upqnXLKKTrmmGPialNcHdaPP/44rpPtjZI8cjLreuSZWHV+iuvYCdofcNZqW38w2eMravSwIFhh81Wja/3LYQ3md7VNqLFb1jSsWe86Tgrb7X0a599+9Tk7Yh/cntqMFu4Z5KsCAACgKeJdJGl3dUpSdna2q8O6K126dFEoFFJxcbErXlxcrIKCAs/PdO/eXcnJya7pv/3791dRUZHq6uqUkpLi+blPP/1UY8aM0fr167X//vvr6aef1gknnKDKykoFg0Hde++9eu655zR27NimXew3xLVK8ODBg2OvgQMHau7cubr77rv173//WwcffHA8VQIAAAAAfJKSkqKhQ4dq7ty5sVg0GtXcuXM1YsQIz8+MHDlSn3/+uaLRaCy2cuVKde/efZedVUm68sorNWjQIL3zzjs69thj9cMf/lAnn3yySktLtXPnTl144YW6/fbb47qOFm9rc/XVV+vmm29WTU2N7r33Xk2aNKmlVQIAAABA++W00quZJk+erEceeUR/+tOftHz5cl100UWqrKyMrRo8fvx416JMF110kXbs2KHLLrtMK1eu1CuvvKLbbrtNl1xyybeeZ+HChbr11ls1cuRI3XXXXdq0aZMuvvhiBYNBBYNB/frXv95l3uzutHiV4H/84x964oknNGbMGC1atEinnHKKHnjggZZWCwAAAABogZ/97GfaunWrrr/+ehUVFWnIkCF67bXXYgsxrVu3TsHg/8Ywe/Xqpddff12TJk3SIYccoh49euiyyy7TVVdd9a3n2bFjR2yacWZmpjIyMpSX9780yby8PJWXl8d1DS3usBYXF8f22zn44IPNHOm9nVNmb3yo2uawpm92PxLxysEOlnrkouakm1ht50b5nY7d4zWtrMLEIjt32pM2RdAOxDsVlbZctNFiWx6fk0fuqxevfV4BAACA9qA1t7VprokTJ2rixIme77399tsmNmLECH3wwQfNPk/jRXm9FumNR4s7rI7jxHrlgUBAjt/ZxQAAAACAhHb22WfHViyuqanR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hgwZokAgoEAg4Dn1Ny0tTQ888EBcdTerw5qXlxfbf7W8vFyPP/64gsGvpkRGo1FVVlYqKytLt956a+wzO3bsiKthX9c5bNgw3XbbbZK+2v916dKlmjlzZtzLIkvSNddc41pyuaysTL169Yq7PgAAAADoqFavXi3HcWJrGnXt2jX2XkpKirp166ZQyKYUNkWzOqxfj6iuWLFCzz33nK666qrYqsD19fX6wx/+oNNPP139+vWLqzGNde/eXQMGDHDF+vfvr7///e+SpIKCAklScXGxunfvHitTXFysIUOG7LLecDiscNhuRxOPQNSOzUdTbLlgjfuJiJNsn5B47E6jYK2N9emxdbft+miD7YB3W2e3mXEabeDbUFRsyqRtLbTt2s9ukdOw3R1zPOYtpHhc48/2s9vMHJv+uev4iTq77UzaVo95EQH3fQ3ldzNFIsVbTKwhI9nE6grd15NUGbFtWOPxO2LBLwAAgI6tg21rs88++0j6asDRb83qsH49qjlo0CA9/fTTOuyww1zvDxkyRGeffbaWLl3qS+NGjhypFStWuGIrV66M3ZC+ffuqoKBAc+fOjXVQy8rKtGDBAl100UW+tAEAAAAA4O2ll17SiSeeqOTkZL300kvfWvaUU05pdv1xL7q0evVq02FdvXq11q9fH0+VniZNmqSjjjpKt912m04//XR9+OGHevjhh/Xwww9LkgKBgC6//HLdcsst2n///dW3b19dd911Kiws1NixY31rBwAAAAA0VUdadGns2LEqKipSt27dvrUPFggEFInYGYu7E1eHddKkSTrrrLP0j3/8Q/3795ckLV++XH//+9/1m9/8Jp4qPR1++OF64YUXdM011+imm25S3759NW3aNI0bNy5W5sorr1RlZaUuuOAClZSU6Oijj9Zrr72m1FS7BQkAAAAAwD/fnAbc5lOCvzZlyhQdccQReuyxx/TMM89Ikvbdd18988wz+uEPf+hrA3/4wx9+a52BQEA33XSTbrrpJl/P22QeTzqSqjyCjbbr9MrvrOptnzgk77D7fB7eaa3r+IA0uyLy605/E6vuZBOdM/v1dR1H19gR8soCm6fp2GYpbYO7fq+nQF6f21KXZWL/r7qP6/ifaweaMjkbbUJsdMs293FVlT2hh5Riu19rcqn7z8MJ2rxjp2d3EwvW2MTjKHmtAAAAHUcHy2FtTXF1WCXpxBNP1IknnuhnWwAAAAAA7cj999/f5LKXXnpps+uPu8P6tZNPPlmPPvqoa5VeAAAAAOiwWiGHNVFHWO+9994mlQsEAm3TYX3nnXdUXV3d0mrarcxl20ysonu+iTVkuqf7Burt3NhQjZ1y2pBhf5k5Se773St5uynTu8tO266adBNrvC1PMDPDlEnbZqfeOkG7Dcz2Q9ztb8i2c9i9pji/vcFug7Q8q8B1XPHfTqZMwYJlJhZp4hTgxoLldkqwttW5Dp3aOlumq21XpM5j7x4AAAB0HB1oSvDq1atbtX6PjEIAAAAAAOLnOI4cp+W97BZ3WPfZZx8lJ9vRNgAAAADokJxWerUDjz32mAYOHKjU1FSlpqZq4MCBevTRR+Our8VTgpcuXdrSKgAAAAAA7dz111+ve+65R7/+9a81YsQISdL8+fM1adIkrVu3Lq6dXeLqsL700kvf+v4pp5wST7XtUlW/ziYWCdtc1Nxl7ltdY1MfVZ9jcz6jaTb2xMojXMfre9vKdlSlmVjWTrttTsOXa1zHSX33MWWq8u0Iek2uHZxPKXEf1+fZx0B1hR5b0azINbE1jjuW86UpokhJqQ3GKVK0xcRCPdwLiUV3lpgygVSbrxwcdIAtt6HYfb7tO5rZQgAAALQXgVZYdMn3RZxawYwZM/TII4/ozDPPjMVOOeUUHXLIIfr1r3+95zqsY8eO3eV7gUBAkYjtGAEAAAAA9l719fUaNmyYiQ8dOlQNDQ1x1Rl3DuvmzZsVjUbNi84qAAAAAHQ8Z511lmbMmGHiDz/8sMaNGxdXnXHnsAaDLDAsSalFdhuV9NwsE6vo6b5fwcF2OutRhetN7IvSLiZWVp3qOv5kR6Eps2ObbUN6qv3Okvr0dh07JWWmTCDqsceux9cfbMpDE4/tfFJK7BTqulz3nIe6bFsmmGWvMVpe3oRGWKG8XBNzytx1OfX2AqMZYduuevvQJpjtbmuw0v5uojU1u2smAAAAkNAee+wx/fvf/9aRRx4pSVqwYIHWrVun8ePHa/LkybFy99xzT5Pqi7vD+sgjjygvL08ZGRkqLCzUkCFD1KWL7VwBAAAAQIfSgfZh/aalS5fqsMMOkyR98cUXkqQuXbqoS5cursV6AwE7GLUrcXVYe/furYcfflj19fUqKytTZWWlgsGgTj75ZP35z39WdnZ2PNUCAAAAQLvXURddeuutt3yvM655vWvWrNGaNWu0ceNGlZeXq7S0VK+//rpWrlypK6+80u82AgAAAAA6oBbvwypJWVlZOv744/XQQw/pvPPO86PKdqOuc6qJNc5XlaTKnu7taZIbQqZMOGhzHzun2VzHnRXpruPssM19TEm328co4PF119S6i2Sk2yJ5dsg++cStJtYp7K6rcrPd8idaZ6+7IdM+LkqucJ8zdbstE2++qpeqIb1NLBB1nzNt2WZTxinxyEXNtL+JaE6GO1BMDjgAAMBerR2MiPqtpqZGDzzwgN566y1t2bJF0ai7D7R48eJm1+lLh/Vr3/ve9/Tllx4bZgIAAAAA9mrnnXee/v3vf+snP/mJjjjiiGblqu5K3B3Wd999V3/84x/1xRdf6LnnnlOPHj305z//WX379tXRRx/d4oYBAAAAQLvUQRddevnll/Xqq69q5MiRvtUZ17zEv//97xo9erTS0tL08ccfq7b2q6mgpaWluu2223xrHAAAAACgfejRo4eyPLaebIm4RlhvueUWzZw5U+PHj9fTTz8di48cOVK33HKLb41rDyKpNifT6+lHwD19W3UVKabM/I19TOyALltMLCej2nVc3ZBsytTX2q82dWudiTUUFbuOQ/vva8rUdLJD+d0b5atK0oSe77uON+fnmTJ/WjHcxKojHlMFQu6bmF5kr9FPqZsrTKwh252L6mRnmDLRdPs9RtLtvU8qdecnB5JsmZDH6tqRMrsvLgAAABJbR10l+O6779ZVV12lmTNnap999vGlzrg6rCtWrNB3vvMdE8/JyVFJSUlL2wQAAAAAaGeGDRummpoa7bvvvkpPT1dysnvQaceOHc2uM64Oa0FBgT7//HP16dPHFX/vvfe07752hA4AAAAAOowOmsN65plnauPGjbrtttuUn5/fdosunX/++brssss0a9YsBQIBbdq0SfPnz9dvf/tbXXfddS1uVHuSuqXaxAIH2nnboRr3lxWI2ltfXWY/V5VbYmKZKe6pveu355oywS12qmrKBrstS0PjwNbt9nwb8k0s2WMLnoPDm1zHqUG7tU4kYtOm0zvbrWFSU9yfrc3rYsr4qSErbGKbR6a5jqOhNFMmf5G9xpQSO106UOsux1RfAACAvVdHnRL8/vvva/78+Ro8eLBvdcbVYb366qsVjUZ1/PHHq6qqSt/5zncUDof129/+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a3R1b9LuO6ySNHnyZE2YMEHDhg3TEUccoWnTpqmysjK2avDuBINB9ezZMzbSmp2dzb9U2gj3vu1w79sG973tcO/bDve+7XDv2wb3PT7taWT1m1JTU5WamtrWzYhrJmpBQUGzyu8J7X7RJUn62c9+prvuukvXX3+9hgwZoiVLlui1114zw9kAAAAA0BF8cybq176eiTpixAjPz4wYMcJVXpLmzJmzy/J7wl4xwipJEydObPIUYAAAAADY2+1uJur48ePVo0cPTZ06VZJ02WWX6bvf/a7uvvtunXzyyXr66af10Ucf6eGHH26za9hrOqx+CIfDmjJlyl4/DzwRce/bDve+bXDf2w73vu1w79sO975tcN/R1n72s59p69atuv7661VUVKQhQ4a4ZqKuW7dOweD/Jt0eddRReuqpp3Tttdfqd7/7nfbff3+9+OKLGjhwYFtdggJOe10rGgAAAACwV9srclgBAAAAAHsfOqwAAAAAgIREhxUAAAAAkJDosAIAAAAAEhId1m+YPn26+vTpo9TUVA0fPlwffvhhWzdprzJ16lQdfvjhysrKUrdu3TR27FitWLHCVaampkaXXHKJOnfurMzMTJ122mlm82K03O23365AIKDLL788FuPet56NGzfqF7/4hTp37qy0tDQNGjRIH330Uex9x3F0/fXXq3v37kpLS9OoUaO0atWqNmxx+xeJRHTdddepb9++SktL03777aebb75Z31xnkPvuj3feeUdjxoxRYWGhAoGAXnzxRdf7TbnPO3bs0Lhx45Sdna3c3Fydd955qqio2INX0T59272vr6/XVVddpUGDBikjI0OFhYUaP368Nm3a5KqDex+f3f3uv+lXv/qVAoGApk2b5opz74GmocP6f5555hlNnjxZU6ZM0eLFizV48GCNHj1aW7Zsaeum7TXmzZunSy65RB988IHmzJmj+vp6/eAHP1BlZWWszKRJk/TPf/5Tzz77rObNm6dNmzbpxz/+cRu2eu+zcOFC/fGPf9QhhxziinPvW8fOnTs1cuRIJScn61//+peWLVumu+++W3l5ebEyd955p+6//37NnDlTCxYsUEZGhkaPHq2ampo2bHn7dscdd2jGjBl68MEHtXz5ct1xxx2688479cADD8TKcN/9UVlZqcGDB2v69Ome7zflPo8bN07//e9/NWfOHL388st65513dMEFF+ypS2i3vu3eV1VVafHixbruuuu0ePFiPf/881qxYoVOOeUUVznufXx297v/2gsvvKAPPvhAhYWF5j3uPdBEDhzHcZwjjjjCueSSS2LHkUjEKSwsdKZOndqGrdq7bdmyxZHkzJs3z3EcxykpKXGSk5OdZ599NlZm+fLljiRn/vz5bdXMvUp5ebmz//77O3PmzHG++93vOpdddpnjONz71nTVVVc5Rx999C7fj0ajTkFBgfOHP/whFispKXHC4bDz17/+dU80ca908sknO+eee64r9uMf/9gZN26c4zjc99YiyXnhhRdix025z8uWLXMkOQsXLoyV+de//uUEAgFn48aNe6zt7V3je+/lww8/dCQ5a9eudRyHe++XXd37DRs2OD169HCWLl3q7LPPPs69994be497DzQdI6yS6urqtGjRIo0aNSoWCwaDGjVqlObPn9+GLdu7lZaWSpI6deokSVq0aJHq6+td38NBBx2k3r178z345JJLLtHJJ5/suscS9741vfTSSxo2bJh++tOfqlu3bjr00EP1yCOPxN5fvXq1ioqKXPc+JydHw4cP5963wFFHHaW5c+dq5cqVkqT//Oc/eu+993TiiSdK4r7vKU25z/Pnz1dubq6GDRsWKzNq1CgFg0EtWLBgj7d5b1ZaWqpAIKDc3FxJ3PvWFI1GddZZZ+mKK67QwQcfbN7n3gNNl9TWDUgE27ZtUyQSUX5+viuen5+vzz77rI1atXeLRqO6/PLLNXLkSA0cOFCSVFRUpJSUlNh/SL+Wn5+voqKiNmjl3uXpp5/W4sWLtXDhQvMe9771fPnll5oxY4YmT56s3/3ud1q4cKEuvfRSpaSkaMKECbH76/XvH+59/K6++mqVlZXpoIMOUigUUiQS0a233qpx48ZJEvd9D2nKfS4qKlK3bt1c7yclJalTp058Fz6qqanRVVddpTPPPFPZ2dmSuPet6Y477lBSUpIuvfRSz/e590DT0WFFm7jkkku0dOlSvffee23dlA5h/fr1uuyyyzRnzhylpqa2dXM6lGg0qmHDhum2226TJB166KFaunSpZs6cqQkTJrRx6/Zef/vb3/Tkk0/qqaee0sEHH6wlS5bo8ssvV2FhIfcdHU59fb1OP/10OY6jGTNmtHVz9nqLFi3Sfffdp8WLFysQCLR1c4B2jynBkrp06aJQKGRWRC0uLlZBQUEbtWrvNXHiRL388st666231LNnz1i8oKBAdXV1KikpcZXne2i5RYsWacuWLTrssMOUlJSkpKQkzZs3T/fff7+SkpKUn5/PvW8l3bt314ABA1yx/v37a926dZIUu7/8+8dfV1xxha6++mqdccYZGjRokM466yxNmjRJU6dOlcR931Oacp8LCgrMAocNDQ3asWMH34UPvu6srl27VnPmzImNrkrc+9by7rvvasuWLerdu3fsv7lr167Vb37zG/Xp00cS9x5oDjqsklJSUjR06FDNnTs3FotGo5o7d65GjBjRhi3buziOo4kTJ+qFF17Qm2++qb59+7reHzp0qJKTk13fw4oVK7Ru3Tq+hxY6/vjj9emnn2rJkiWx17BhwzRu3LjYP3PvW8fIkSPN9k0rV67UPvvsI0nq27evCgoKXPe+rKxMCxYs4N63QFVVlYJB93/iQqGQotGoJO77ntKU+zxixAiVlJRo0aJFsTJvvvmmotGohg8fvsfbvDf5urO6atUqvfHGG+rcubPrfe596zjrrLP0ySefuP6bW1hYqCuuuEKvv/66JO490CxtvepTonj66aedcDjsPP74486yZcucCy64wMnNzXWKiorauml7jYsuusjJyclx3n77bWfz5s2xV1VVVazMr371K6d3797Om2++6Xz00UfOiBEjnBEjRrRhq/de31wl2HG4963lww8/dJKSkpxbb73VWbVqlfPkk0866enpzl/+8pdYmdtvv93Jzc11/vGPfziffPKJc+qppzp9+/Z1qqur27Dl7duECROcHj16OC+//LKzevVq5/nnn3e6dOniXHnllbEy3Hd/lJeXOx9//LHz8ccfO5Kce+65x/n4449jK9E25T6fcMIJzqGHHuosWLDAee+995z999/fOfPMM9vqktqNb7v3dXV1zimnnOL07NnTWbJkieu/u7W1tbE6uPfx2d3vvrHGqwQ7DvceaCo6rN/wwAMPOL1793ZSUlKcI444wvnggw/aukl7FUmer9mzZ8fKVFdXOxdffLGTl5fnpKenOz/60Y+czZs3t12j92KNO6zc+9bzz3/+0xk4cKATDoedgw46yHn44Ydd70ejUee6665z8vPznXA47Bx//PHOihUr2qi1e4eysjLnsssuc3r37u2kpqY6++67r/P73//e9T/q3Hd/vPXWW57/bp8wYYLjOE27z9u3b3fOPPNMJzMz08nOznbOOeccp7y8vA2upn35tnu/evXqXf5396233orVwb2Pz+5+9415dVi590DTBBzHcfbESC4AAAAAAM1BDisAAAAAICHRYQUAAAAAJCQ6rAAAAACAhESHFQAAAACQkOiwAgAAAAASEh1WAAAAAEBCosMKAAAAAEhIdFgBAAAAAAmJDisAYK/xne98R0899VRbN6PFXnvtNQ0ZMkTRaLStmwIAQJuiwwoAHdjZZ5+tQCAQe3Xu3FknnHCCPvnkk7ZuWrO99NJLKi4u1hlnnNHWTWmxE044QcnJyXryySfbuikAALQpOqwA0MGdcMIJ2rx5szZv3qy5c+cqKSlJP/zhD9u6Wc12//3365xzzlEwuHf8p+3ss8/W/fff39bNAACgTe0d/1UHAMQtHA6roKBABQUFGjJkiK6++mqtX79eW7dulSStWbNGgUBATz/9tI466iilpqZq4MCBmjdvnquepUuX6sQTT1RmZqby8/N11llnadu2bbH3jz32WAUCAT3//POuzx166KEKBAJ6++23Y7GXX35ZgwcPVlpaWmz0d+zYsbu8hq1bt+rNN9/UmDFjXPFAIKAXX3xRkuQ4jsaPH69DDjlEO3fulCR98cUXOvXUU5Wfn6/MzEwdfvjheuONN0z9jz/+uGskOhAIaMiQIbH33377bQUCAb3yyis65JBDlJqaqiOPPFJLly511ZGbm+vZ/iVLligQCGjNmjWx2JgxY/TRRx/piy++2OV1AwCwt6PDCgCIqaio0F/+8hf169dPnTt3dr13xRVX6De/+Y0+/vhjjRgxQmPGjNH27dslSSUlJTruuON06KGH6qOPPtJrr72m4uJinX766a46evTooYcffjh2/OGHH8Y6xl8rKSnRz372Mx177LFatmyZNm/ebOpp7L333lN6err69++/yzKXXnqp3n//ff373/9WXl5e7HpPOukkzZ07Vx9//LFOOOEEjRkzRuvWrTOfz87Ojo1E/+Y3v/E8xxVXXKG7775bCxcuVNeuXTVmzBjV19d/a9t3pXfv3srPz9e7774b1+cBANgb0GEFgA7u5ZdfVmZmpjIzM5WVlaWXXnpJzzzzjJlaO3HiRJ122mnq37+/ZsyYoZycHD322GOSpAcffFCHHnqobrvtNh100EE69NBDNWvWLL311ltauXJlrI5T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" - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "execution_count": 94 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-08-28T15:53:19.552662Z", - "start_time": "2025-08-28T15:53:19.491389Z" - } - }, - "cell_type": "code", - "source": [ - "# Load checkpoint\n", - "\n", - "loaded_checkpoint = torch.load(checkpoint_path)" - ], - "id": "10f2ac57bf7f137c", - "outputs": [], - "execution_count": 95 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-08-28T15:53:19.646459Z", - "start_time": "2025-08-28T15:53:19.616553Z" - } - }, - "cell_type": "code", - "source": [ - "# Restoring a model after an interruption\n", - "\n", - "restored_model = AnomalyClassifier(20096, 14).to(device)\n", - "\n", - "loss_func = nn.CrossEntropyLoss()\n", - "optimizer = torch.optim.Adam(anomaly_classifier.parameters(), lr=0.001)\n", - "\n", - "lr_scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(\n", - " optimizer\n", - ")" - ], - "id": "3cbb6f385cbfb12b", - "outputs": [], - "execution_count": 96 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-08-28T15:53:19.756198Z", - "start_time": "2025-08-28T15:53:19.725420Z" - } - }, - "cell_type": "code", - "source": [ - "# Load best model state\n", - "\n", - "state_anomaly_classifier = torch.load(best_model_path)\n", - "\n", - "restored_model.load_state_dict(state_anomaly_classifier)" - ], - "id": "12a99903df484ea8", - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 97, - "metadata": {}, - "output_type": "execute_result" - } - ], - "execution_count": 97 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-08-28T15:53:19.849503Z", - "start_time": "2025-08-28T15:53:19.835437Z" - } - }, - "cell_type": "code", - "source": "# TODO: load states from checkpoint and make training loop from saved epoch to EPOCH\n", - "id": "b5a80293b9f9c0f2", - "outputs": [], - "execution_count": 98 - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/ml/mlflow_server/artefacts/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/artifacts/checkpoints/tmpgujpfiss.pt b/ml/mlflow_server/artefacts/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/artifacts/checkpoints/tmpgujpfiss.pt new file mode 100644 index 0000000..90084e8 Binary files /dev/null and b/ml/mlflow_server/artefacts/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/artifacts/checkpoints/tmpgujpfiss.pt differ diff --git a/ml/mlflow_server/artefacts/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/artifacts/checkpoints/tmpneadmend.pt b/ml/mlflow_server/artefacts/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/artifacts/checkpoints/tmpneadmend.pt new file mode 100644 index 0000000..d22c6eb Binary files /dev/null and b/ml/mlflow_server/artefacts/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/artifacts/checkpoints/tmpneadmend.pt differ diff --git a/ml/mlflow_server/artefacts/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/artifacts/MLmodel b/ml/mlflow_server/artefacts/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/artifacts/MLmodel new file mode 100644 index 0000000..b5e27dc --- /dev/null +++ b/ml/mlflow_server/artefacts/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/artifacts/MLmodel @@ -0,0 +1,23 @@ +artifact_path: file:///C:\Users\Dalvy07\POLLUB\anomaly-project-implementation\ml\mlflow_server\artefacts/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/artifacts +flavors: + python_function: + config: + device: null + data: data + env: + conda: conda.yaml + virtualenv: python_env.yaml + loader_module: mlflow.pytorch + pickle_module_name: mlflow.pytorch.pickle_module + python_version: 3.10.18 + pytorch: + code: null + model_data: data + pytorch_version: 2.8.0+cu126 +mlflow_version: 3.4.0 +model_id: m-2c42de9a5b5949e49be9ca72d48bf742 +model_size_bytes: 4893373 +model_uuid: m-2c42de9a5b5949e49be9ca72d48bf742 +prompts: null +run_id: 2c08b9fbc2f44dffb1fb76e7e619563c +utc_time_created: '2025-09-30 21:47:11.919794' diff --git a/ml/mlflow_server/artefacts/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/artifacts/conda.yaml b/ml/mlflow_server/artefacts/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/artifacts/conda.yaml new file mode 100644 index 0000000..416c7a6 --- /dev/null +++ b/ml/mlflow_server/artefacts/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/artifacts/conda.yaml @@ -0,0 +1,13 @@ +channels: +- conda-forge +dependencies: +- python=3.10.18 +- pip<=25.1 +- pip: + - mlflow==3.4.0 + - cloudpickle==3.1.1 + - numpy==2.1.2 + - pandas==2.3.2 + - torch==2.8.0 + - tqdm==4.67.1 +name: mlflow-env diff --git a/ml/mlflow_server/artefacts/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/artifacts/data/model.pth b/ml/mlflow_server/artefacts/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/artifacts/data/model.pth new file mode 100644 index 0000000..f559865 Binary files /dev/null and b/ml/mlflow_server/artefacts/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/artifacts/data/model.pth differ diff --git a/ml/mlflow_server/artefacts/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/artifacts/data/pickle_module_info.txt b/ml/mlflow_server/artefacts/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/artifacts/data/pickle_module_info.txt new file mode 100644 index 0000000..31b9e7e --- /dev/null +++ b/ml/mlflow_server/artefacts/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/artifacts/data/pickle_module_info.txt @@ -0,0 +1 @@ +mlflow.pytorch.pickle_module \ No newline at end of file diff --git a/ml/mlflow_server/artefacts/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/artifacts/python_env.yaml b/ml/mlflow_server/artefacts/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/artifacts/python_env.yaml new file mode 100644 index 0000000..3971f75 --- /dev/null +++ b/ml/mlflow_server/artefacts/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/artifacts/python_env.yaml @@ -0,0 +1,7 @@ +python: 3.10.18 +build_dependencies: +- pip==25.1 +- setuptools==78.1.1 +- wheel==0.45.1 +dependencies: +- -r requirements.txt diff --git a/ml/mlflow_server/artefacts/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/artifacts/requirements.txt b/ml/mlflow_server/artefacts/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/artifacts/requirements.txt new file mode 100644 index 0000000..ae9bc61 --- /dev/null +++ b/ml/mlflow_server/artefacts/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/artifacts/requirements.txt @@ -0,0 +1,6 @@ +mlflow==3.4.0 +cloudpickle==3.1.1 +numpy==2.1.2 +pandas==2.3.2 +torch==2.8.0 +tqdm==4.67.1 \ No newline at end of file diff --git a/ml/mlflow_server/artefacts/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/artifacts/MLmodel b/ml/mlflow_server/artefacts/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/artifacts/MLmodel new file mode 100644 index 0000000..4acf579 --- /dev/null +++ b/ml/mlflow_server/artefacts/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/artifacts/MLmodel @@ -0,0 +1,23 @@ +artifact_path: file:///C:\Users\Dalvy07\POLLUB\anomaly-project-implementation\ml\mlflow_server\artefacts/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/artifacts +flavors: + python_function: + config: + device: null + data: data + env: + conda: conda.yaml + virtualenv: python_env.yaml + loader_module: mlflow.pytorch + pickle_module_name: mlflow.pytorch.pickle_module + python_version: 3.10.18 + pytorch: + code: null + model_data: data + pytorch_version: 2.8.0+cu126 +mlflow_version: 3.4.0 +model_id: m-5035c0b502f8443b9a48f8b7ef4532a0 +model_size_bytes: 4893373 +model_uuid: m-5035c0b502f8443b9a48f8b7ef4532a0 +prompts: null +run_id: 2c08b9fbc2f44dffb1fb76e7e619563c +utc_time_created: '2025-09-30 21:46:33.198985' diff --git a/ml/mlflow_server/artefacts/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/artifacts/conda.yaml b/ml/mlflow_server/artefacts/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/artifacts/conda.yaml new file mode 100644 index 0000000..416c7a6 --- /dev/null +++ b/ml/mlflow_server/artefacts/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/artifacts/conda.yaml @@ -0,0 +1,13 @@ +channels: +- conda-forge +dependencies: +- python=3.10.18 +- pip<=25.1 +- pip: + - mlflow==3.4.0 + - cloudpickle==3.1.1 + - numpy==2.1.2 + - pandas==2.3.2 + - torch==2.8.0 + - tqdm==4.67.1 +name: mlflow-env diff --git a/ml/mlflow_server/artefacts/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/artifacts/data/model.pth b/ml/mlflow_server/artefacts/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/artifacts/data/model.pth new file mode 100644 index 0000000..ed325ec Binary files /dev/null and b/ml/mlflow_server/artefacts/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/artifacts/data/model.pth differ diff --git a/ml/mlflow_server/artefacts/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/artifacts/data/pickle_module_info.txt b/ml/mlflow_server/artefacts/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/artifacts/data/pickle_module_info.txt new file mode 100644 index 0000000..31b9e7e --- /dev/null +++ b/ml/mlflow_server/artefacts/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/artifacts/data/pickle_module_info.txt @@ -0,0 +1 @@ +mlflow.pytorch.pickle_module \ No newline at end of file diff --git a/ml/mlflow_server/artefacts/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/artifacts/python_env.yaml b/ml/mlflow_server/artefacts/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/artifacts/python_env.yaml new file mode 100644 index 0000000..3971f75 --- /dev/null +++ b/ml/mlflow_server/artefacts/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/artifacts/python_env.yaml @@ -0,0 +1,7 @@ +python: 3.10.18 +build_dependencies: +- pip==25.1 +- setuptools==78.1.1 +- wheel==0.45.1 +dependencies: +- -r requirements.txt diff --git a/ml/mlflow_server/artefacts/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/artifacts/requirements.txt b/ml/mlflow_server/artefacts/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/artifacts/requirements.txt new file mode 100644 index 0000000..ae9bc61 --- /dev/null +++ b/ml/mlflow_server/artefacts/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/artifacts/requirements.txt @@ -0,0 +1,6 @@ +mlflow==3.4.0 +cloudpickle==3.1.1 +numpy==2.1.2 +pandas==2.3.2 +torch==2.8.0 +tqdm==4.67.1 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/meta.yaml b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/meta.yaml new file mode 100644 index 0000000..52d07a7 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/meta.yaml @@ -0,0 +1,14 @@ +artifact_uri: file:///C:\Users\Dalvy07\POLLUB\anomaly-project-implementation\ml\mlflow_server\artefacts/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/artifacts +end_time: 1759269026301 +entry_point_name: '' +experiment_id: '588266442625671109' +lifecycle_stage: active +run_id: 1a936796138a44ee93d0a2e22fc0dd0c +run_name: legendary-lamb-99 +source_name: '' +source_type: 4 +source_version: '' +start_time: 1759269018560 +status: 4 +tags: [] +user_id: Dalvy07 diff --git a/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/batch_size b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/batch_size new file mode 100644 index 0000000..1758ddd --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/batch_size @@ -0,0 +1 @@ +32 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/device b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/device new file mode 100644 index 0000000..5b36a5d --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/device @@ -0,0 +1 @@ +cuda \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/early_stop_patience b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/early_stop_patience new file mode 100644 index 0000000..c793025 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/early_stop_patience @@ -0,0 +1 @@ +7 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/epochs b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/epochs new file mode 100644 index 0000000..3f10ffe --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/epochs @@ -0,0 +1 @@ +15 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/gradient_clip_max_norm b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/gradient_clip_max_norm new file mode 100644 index 0000000..9f8e9b6 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/gradient_clip_max_norm @@ -0,0 +1 @@ +1.0 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/learning_rate b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/learning_rate new file mode 100644 index 0000000..eb5a1db --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/learning_rate @@ -0,0 +1 @@ +0.001 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/loss_function b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/loss_function new file mode 100644 index 0000000..45eeeb8 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/loss_function @@ -0,0 +1 @@ +CrossEntropyLoss \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/metrics_average b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/metrics_average new file mode 100644 index 0000000..fc9944e --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/metrics_average @@ -0,0 +1 @@ +macro \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/model_class b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/model_class new file mode 100644 index 0000000..1f8c656 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/model_class @@ -0,0 +1 @@ +AudioCNN \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/num_classes b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/num_classes new file mode 100644 index 0000000..da2d398 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/num_classes @@ -0,0 +1 @@ +14 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/optimizer b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/optimizer new file mode 100644 index 0000000..a139610 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/optimizer @@ -0,0 +1 @@ +Adam \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/scheduler b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/scheduler new file mode 100644 index 0000000..23eeeb1 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/scheduler @@ -0,0 +1 @@ +ReduceLROnPlateau \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/total_parameters b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/total_parameters new file mode 100644 index 0000000..30b92b7 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/total_parameters @@ -0,0 +1 @@ +1215662 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/train_size b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/train_size new file mode 100644 index 0000000..130e16f --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/train_size @@ -0,0 +1 @@ +1200 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/trainable_parameters b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/trainable_parameters new file mode 100644 index 0000000..30b92b7 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/trainable_parameters @@ -0,0 +1 @@ +1215662 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/val_size b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/val_size new file mode 100644 index 0000000..6b3ed8d --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/params/val_size @@ -0,0 +1 @@ +400 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/tags/mlflow.runName b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/tags/mlflow.runName new file mode 100644 index 0000000..1f9d1b2 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/tags/mlflow.runName @@ -0,0 +1 @@ +legendary-lamb-99 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/tags/mlflow.source.name b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/tags/mlflow.source.name new file mode 100644 index 0000000..f8f55c9 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/tags/mlflow.source.name @@ -0,0 +1 @@ +C:\Users\Dalvy07\miniconda3\envs\ml\lib\site-packages\ipykernel_launcher.py \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/tags/mlflow.source.type b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/tags/mlflow.source.type new file mode 100644 index 0000000..0c2c1fe --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/tags/mlflow.source.type @@ -0,0 +1 @@ +LOCAL \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/tags/mlflow.user b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/tags/mlflow.user new file mode 100644 index 0000000..be1bd86 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/1a936796138a44ee93d0a2e22fc0dd0c/tags/mlflow.user @@ -0,0 +1 @@ +Dalvy07 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/meta.yaml b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/meta.yaml new file mode 100644 index 0000000..16fa9a6 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/meta.yaml @@ -0,0 +1,14 @@ +artifact_uri: file:///C:\Users\Dalvy07\POLLUB\anomaly-project-implementation\ml\mlflow_server\artefacts/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/artifacts +end_time: 1759268881627 +entry_point_name: '' +experiment_id: '588266442625671109' +lifecycle_stage: active +run_id: 2c08b9fbc2f44dffb1fb76e7e619563c +run_name: Test_run_1 +source_name: '' +source_type: 4 +source_version: '' +start_time: 1759268763292 +status: 4 +tags: [] +user_id: Dalvy07 diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/learning_rate b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/learning_rate new file mode 100644 index 0000000..0ee7040 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/learning_rate @@ -0,0 +1,6 @@ +1759268790801 0.001 0 +1759268790801 0.001 0 +1759268829413 0.001 1 +1759268790801 0.001 0 +1759268790801 0.001 0 +1759268860202 0.001 2 diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/train_accuracy b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/train_accuracy new file mode 100644 index 0000000..8e45b39 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/train_accuracy @@ -0,0 +1,6 @@ +1759268790801 0.3866666555404663 0 +1759268790801 0.3866666555404663 0 +1759268829413 0.3916666805744171 1 +1759268790801 0.3866666555404663 0 +1759268790801 0.3866666555404663 0 +1759268860202 0.4241666793823242 2 diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/train_f1 b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/train_f1 new file mode 100644 index 0000000..350767a --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/train_f1 @@ -0,0 +1,6 @@ +1759268790801 0.0651419535279274 0 +1759268790801 0.0651419535279274 0 +1759268829413 0.11219029873609543 1 +1759268790801 0.0651419535279274 0 +1759268790801 0.0651419535279274 0 +1759268860202 0.12259894609451294 2 diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/train_loss b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/train_loss new file mode 100644 index 0000000..7876be7 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/train_loss @@ -0,0 +1,6 @@ +1759268790801 1.974422630510832 0 +1759268790801 1.974422630510832 0 +1759268829413 1.6352215534762333 1 +1759268790801 1.974422630510832 0 +1759268790801 1.974422630510832 0 +1759268860202 1.5545525864550942 2 diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/train_precision b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/train_precision new file mode 100644 index 0000000..2b2f124 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/train_precision @@ -0,0 +1,6 @@ +1759268790801 0.08374731987714767 0 +1759268790801 0.08374731987714767 0 +1759268829413 0.17709678411483765 1 +1759268790801 0.08374731987714767 0 +1759268790801 0.08374731987714767 0 +1759268860202 0.1528811752796173 2 diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/train_recall b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/train_recall new file mode 100644 index 0000000..1d31bc4 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/train_recall @@ -0,0 +1,6 @@ +1759268790801 0.08165134489536285 0 +1759268790801 0.08165134489536285 0 +1759268829413 0.12881529331207275 1 +1759268790801 0.08165134489536285 0 +1759268790801 0.08165134489536285 0 +1759268860202 0.14088264107704163 2 diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/val_accuracy b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/val_accuracy new file mode 100644 index 0000000..cdd65cf --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/val_accuracy @@ -0,0 +1,6 @@ +1759268790801 0.14000000059604645 0 +1759268790801 0.14000000059604645 0 +1759268829413 0.4124999940395355 1 +1759268790801 0.14000000059604645 0 +1759268790801 0.14000000059604645 0 +1759268860202 0.42250001430511475 2 diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/val_f1 b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/val_f1 new file mode 100644 index 0000000..67f8c76 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/val_f1 @@ -0,0 +1,6 @@ +1759268790801 0.039743974804878235 0 +1759268790801 0.039743974804878235 0 +1759268829413 0.14017152786254883 1 +1759268790801 0.039743974804878235 0 +1759268790801 0.039743974804878235 0 +1759268860202 0.14456434547901154 2 diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/val_loss b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/val_loss new file mode 100644 index 0000000..45a1338 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/val_loss @@ -0,0 +1,6 @@ +1759268790801 3.87488447702848 0 +1759268790801 3.87488447702848 0 +1759268829413 1.5589325428009033 1 +1759268790801 3.87488447702848 0 +1759268790801 3.87488447702848 0 +1759268860202 1.5700963552181537 2 diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/val_precision b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/val_precision new file mode 100644 index 0000000..b00510d --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/val_precision @@ -0,0 +1,6 @@ +1759268790801 0.07447916269302368 0 +1759268790801 0.07447916269302368 0 +1759268829413 0.14970332384109497 1 +1759268790801 0.07447916269302368 0 +1759268790801 0.07447916269302368 0 +1759268860202 0.13096778094768524 2 diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/val_recall b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/val_recall new file mode 100644 index 0000000..1cb78dc --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/metrics/val_recall @@ -0,0 +1,6 @@ +1759268790801 0.11075268685817719 0 +1759268790801 0.11075268685817719 0 +1759268829413 0.15671925246715546 1 +1759268790801 0.11075268685817719 0 +1759268790801 0.11075268685817719 0 +1759268860202 0.16692975163459778 2 diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/outputs/m-2c42de9a5b5949e49be9ca72d48bf742/meta.yaml b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/outputs/m-2c42de9a5b5949e49be9ca72d48bf742/meta.yaml new file mode 100644 index 0000000..397dcb8 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/outputs/m-2c42de9a5b5949e49be9ca72d48bf742/meta.yaml @@ -0,0 +1,6 @@ +destination_id: m-2c42de9a5b5949e49be9ca72d48bf742 +destination_type: MODEL_OUTPUT +source_id: m-2c42de9a5b5949e49be9ca72d48bf742 +source_type: RUN_OUTPUT +step: 0 +tags: {} diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/outputs/m-5035c0b502f8443b9a48f8b7ef4532a0/meta.yaml b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/outputs/m-5035c0b502f8443b9a48f8b7ef4532a0/meta.yaml new file mode 100644 index 0000000..a5222ab --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/outputs/m-5035c0b502f8443b9a48f8b7ef4532a0/meta.yaml @@ -0,0 +1,6 @@ +destination_id: m-5035c0b502f8443b9a48f8b7ef4532a0 +destination_type: MODEL_OUTPUT +source_id: m-5035c0b502f8443b9a48f8b7ef4532a0 +source_type: RUN_OUTPUT +step: 0 +tags: {} diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/batch_size b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/batch_size new file mode 100644 index 0000000..1758ddd --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/batch_size @@ -0,0 +1 @@ +32 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/device b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/device new file mode 100644 index 0000000..5b36a5d --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/device @@ -0,0 +1 @@ +cuda \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/early_stop_patience b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/early_stop_patience new file mode 100644 index 0000000..c793025 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/early_stop_patience @@ -0,0 +1 @@ +7 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/epochs b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/epochs new file mode 100644 index 0000000..3f10ffe --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/epochs @@ -0,0 +1 @@ +15 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/gradient_clip_max_norm b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/gradient_clip_max_norm new file mode 100644 index 0000000..9f8e9b6 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/gradient_clip_max_norm @@ -0,0 +1 @@ +1.0 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/learning_rate b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/learning_rate new file mode 100644 index 0000000..eb5a1db --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/learning_rate @@ -0,0 +1 @@ +0.001 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/loss_function b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/loss_function new file mode 100644 index 0000000..45eeeb8 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/loss_function @@ -0,0 +1 @@ +CrossEntropyLoss \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/metrics_average b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/metrics_average new file mode 100644 index 0000000..fc9944e --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/metrics_average @@ -0,0 +1 @@ +macro \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/model_class b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/model_class new file mode 100644 index 0000000..1f8c656 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/model_class @@ -0,0 +1 @@ +AudioCNN \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/num_classes b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/num_classes new file mode 100644 index 0000000..da2d398 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/num_classes @@ -0,0 +1 @@ +14 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/optimizer b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/optimizer new file mode 100644 index 0000000..a139610 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/optimizer @@ -0,0 +1 @@ +Adam \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/scheduler b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/scheduler new file mode 100644 index 0000000..23eeeb1 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/scheduler @@ -0,0 +1 @@ +ReduceLROnPlateau \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/total_parameters b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/total_parameters new file mode 100644 index 0000000..30b92b7 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/total_parameters @@ -0,0 +1 @@ +1215662 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/train_size b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/train_size new file mode 100644 index 0000000..130e16f --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/train_size @@ -0,0 +1 @@ +1200 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/trainable_parameters b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/trainable_parameters new file mode 100644 index 0000000..30b92b7 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/trainable_parameters @@ -0,0 +1 @@ +1215662 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/val_size b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/val_size new file mode 100644 index 0000000..6b3ed8d --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/params/val_size @@ -0,0 +1 @@ +400 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/tags/mlflow.runName b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/tags/mlflow.runName new file mode 100644 index 0000000..1d1b391 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/tags/mlflow.runName @@ -0,0 +1 @@ +Test_run_1 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/tags/mlflow.source.name b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/tags/mlflow.source.name new file mode 100644 index 0000000..f8f55c9 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/tags/mlflow.source.name @@ -0,0 +1 @@ +C:\Users\Dalvy07\miniconda3\envs\ml\lib\site-packages\ipykernel_launcher.py \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/tags/mlflow.source.type b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/tags/mlflow.source.type new file mode 100644 index 0000000..0c2c1fe --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/tags/mlflow.source.type @@ -0,0 +1 @@ +LOCAL \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/tags/mlflow.user b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/tags/mlflow.user new file mode 100644 index 0000000..be1bd86 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/2c08b9fbc2f44dffb1fb76e7e619563c/tags/mlflow.user @@ -0,0 +1 @@ +Dalvy07 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/meta.yaml b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/meta.yaml new file mode 100644 index 0000000..889ef89 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/meta.yaml @@ -0,0 +1,14 @@ +artifact_uri: file:///C:\Users\Dalvy07\POLLUB\anomaly-project-implementation\ml\mlflow_server\artefacts/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/artifacts +end_time: 1759269074083 +entry_point_name: '' +experiment_id: '588266442625671109' +lifecycle_stage: active +run_id: 83051d2f1bc34d6eaba8f5ea3805cc0e +run_name: powerful-croc-671 +source_name: '' +source_type: 4 +source_version: '' +start_time: 1759269070115 +status: 4 +tags: [] +user_id: Dalvy07 diff --git a/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/batch_size b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/batch_size new file mode 100644 index 0000000..1758ddd --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/batch_size @@ -0,0 +1 @@ +32 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/device b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/device new file mode 100644 index 0000000..5b36a5d --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/device @@ -0,0 +1 @@ +cuda \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/early_stop_patience b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/early_stop_patience new file mode 100644 index 0000000..c793025 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/early_stop_patience @@ -0,0 +1 @@ +7 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/epochs b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/epochs new file mode 100644 index 0000000..3f10ffe --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/epochs @@ -0,0 +1 @@ +15 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/gradient_clip_max_norm b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/gradient_clip_max_norm new file mode 100644 index 0000000..9f8e9b6 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/gradient_clip_max_norm @@ -0,0 +1 @@ +1.0 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/learning_rate b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/learning_rate new file mode 100644 index 0000000..eb5a1db --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/learning_rate @@ -0,0 +1 @@ +0.001 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/loss_function b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/loss_function new file mode 100644 index 0000000..45eeeb8 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/loss_function @@ -0,0 +1 @@ +CrossEntropyLoss \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/metrics_average b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/metrics_average new file mode 100644 index 0000000..fc9944e --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/metrics_average @@ -0,0 +1 @@ +macro \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/model_class b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/model_class new file mode 100644 index 0000000..1f8c656 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/model_class @@ -0,0 +1 @@ +AudioCNN \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/num_classes b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/num_classes new file mode 100644 index 0000000..da2d398 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/num_classes @@ -0,0 +1 @@ +14 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/optimizer b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/optimizer new file mode 100644 index 0000000..a139610 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/optimizer @@ -0,0 +1 @@ +Adam \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/scheduler b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/scheduler new file mode 100644 index 0000000..23eeeb1 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/scheduler @@ -0,0 +1 @@ +ReduceLROnPlateau \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/total_parameters b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/total_parameters new file mode 100644 index 0000000..30b92b7 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/total_parameters @@ -0,0 +1 @@ +1215662 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/train_size b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/train_size new file mode 100644 index 0000000..130e16f --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/train_size @@ -0,0 +1 @@ +1200 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/trainable_parameters b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/trainable_parameters new file mode 100644 index 0000000..30b92b7 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/trainable_parameters @@ -0,0 +1 @@ +1215662 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/val_size b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/val_size new file mode 100644 index 0000000..6b3ed8d --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/params/val_size @@ -0,0 +1 @@ +400 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/tags/mlflow.runName b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/tags/mlflow.runName new file mode 100644 index 0000000..2c6acb5 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/tags/mlflow.runName @@ -0,0 +1 @@ +powerful-croc-671 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/tags/mlflow.source.name b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/tags/mlflow.source.name new file mode 100644 index 0000000..f8f55c9 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/tags/mlflow.source.name @@ -0,0 +1 @@ +C:\Users\Dalvy07\miniconda3\envs\ml\lib\site-packages\ipykernel_launcher.py \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/tags/mlflow.source.type b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/tags/mlflow.source.type new file mode 100644 index 0000000..0c2c1fe --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/tags/mlflow.source.type @@ -0,0 +1 @@ +LOCAL \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/tags/mlflow.user b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/tags/mlflow.user new file mode 100644 index 0000000..be1bd86 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/83051d2f1bc34d6eaba8f5ea3805cc0e/tags/mlflow.user @@ -0,0 +1 @@ +Dalvy07 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/meta.yaml b/ml/mlflow_server/data_local/588266442625671109/meta.yaml new file mode 100644 index 0000000..ae4ad23 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/meta.yaml @@ -0,0 +1,6 @@ +artifact_location: file:///C:\Users\Dalvy07\POLLUB\anomaly-project-implementation\ml\mlflow_server\artefacts/588266442625671109 +creation_time: 1759268701705 +experiment_id: '588266442625671109' +last_update_time: 1759268701705 +lifecycle_stage: active +name: Anomaly_Classifier_Exp diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/meta.yaml b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/meta.yaml new file mode 100644 index 0000000..3302902 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/meta.yaml @@ -0,0 +1,10 @@ +artifact_location: file:///C:\Users\Dalvy07\POLLUB\anomaly-project-implementation\ml\mlflow_server\artefacts/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/artifacts +creation_timestamp: 1759268831629 +experiment_id: '588266442625671109' +last_updated_timestamp: 1759268839194 +model_id: m-2c42de9a5b5949e49be9ca72d48bf742 +model_type: '' +name: best_model +source_run_id: 2c08b9fbc2f44dffb1fb76e7e619563c +status: 2 +status_message: null diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/learning_rate b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/learning_rate new file mode 100644 index 0000000..4f93eff --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/learning_rate @@ -0,0 +1,2 @@ +1759268790801 0.001 0 2c08b9fbc2f44dffb1fb76e7e619563c +1759268790801 0.001 0 2c08b9fbc2f44dffb1fb76e7e619563c diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/train_accuracy b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/train_accuracy new file mode 100644 index 0000000..07655a1 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/train_accuracy @@ -0,0 +1,2 @@ +1759268790801 0.3866666555404663 0 2c08b9fbc2f44dffb1fb76e7e619563c +1759268790801 0.3866666555404663 0 2c08b9fbc2f44dffb1fb76e7e619563c diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/train_f1 b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/train_f1 new file mode 100644 index 0000000..303b9cd --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/train_f1 @@ -0,0 +1,2 @@ +1759268790801 0.0651419535279274 0 2c08b9fbc2f44dffb1fb76e7e619563c +1759268790801 0.0651419535279274 0 2c08b9fbc2f44dffb1fb76e7e619563c diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/train_loss b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/train_loss new file mode 100644 index 0000000..df892ff --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/train_loss @@ -0,0 +1,2 @@ +1759268790801 1.974422630510832 0 2c08b9fbc2f44dffb1fb76e7e619563c +1759268790801 1.974422630510832 0 2c08b9fbc2f44dffb1fb76e7e619563c diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/train_precision b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/train_precision new file mode 100644 index 0000000..b0e6ae7 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/train_precision @@ -0,0 +1,2 @@ +1759268790801 0.08374731987714767 0 2c08b9fbc2f44dffb1fb76e7e619563c +1759268790801 0.08374731987714767 0 2c08b9fbc2f44dffb1fb76e7e619563c diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/train_recall b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/train_recall new file mode 100644 index 0000000..c2df2d0 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/train_recall @@ -0,0 +1,2 @@ +1759268790801 0.08165134489536285 0 2c08b9fbc2f44dffb1fb76e7e619563c +1759268790801 0.08165134489536285 0 2c08b9fbc2f44dffb1fb76e7e619563c diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/val_accuracy b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/val_accuracy new file mode 100644 index 0000000..4e992c7 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/val_accuracy @@ -0,0 +1,2 @@ +1759268790801 0.14000000059604645 0 2c08b9fbc2f44dffb1fb76e7e619563c +1759268790801 0.14000000059604645 0 2c08b9fbc2f44dffb1fb76e7e619563c diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/val_f1 b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/val_f1 new file mode 100644 index 0000000..0ae5ed1 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/val_f1 @@ -0,0 +1,2 @@ +1759268790801 0.039743974804878235 0 2c08b9fbc2f44dffb1fb76e7e619563c +1759268790801 0.039743974804878235 0 2c08b9fbc2f44dffb1fb76e7e619563c diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/val_loss b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/val_loss new file mode 100644 index 0000000..3abfabd --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/val_loss @@ -0,0 +1,2 @@ +1759268790801 3.87488447702848 0 2c08b9fbc2f44dffb1fb76e7e619563c +1759268790801 3.87488447702848 0 2c08b9fbc2f44dffb1fb76e7e619563c diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/val_precision b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/val_precision new file mode 100644 index 0000000..bebbe23 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/val_precision @@ -0,0 +1,2 @@ +1759268790801 0.07447916269302368 0 2c08b9fbc2f44dffb1fb76e7e619563c +1759268790801 0.07447916269302368 0 2c08b9fbc2f44dffb1fb76e7e619563c diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/val_recall b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/val_recall new file mode 100644 index 0000000..1756e7b --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/metrics/val_recall @@ -0,0 +1,2 @@ +1759268790801 0.11075268685817719 0 2c08b9fbc2f44dffb1fb76e7e619563c +1759268790801 0.11075268685817719 0 2c08b9fbc2f44dffb1fb76e7e619563c diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/batch_size b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/batch_size new file mode 100644 index 0000000..1758ddd --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/batch_size @@ -0,0 +1 @@ +32 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/device b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/device new file mode 100644 index 0000000..5b36a5d --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/device @@ -0,0 +1 @@ +cuda \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/early_stop_patience b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/early_stop_patience new file mode 100644 index 0000000..c793025 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/early_stop_patience @@ -0,0 +1 @@ +7 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/epochs b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/epochs new file mode 100644 index 0000000..3f10ffe --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/epochs @@ -0,0 +1 @@ +15 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/gradient_clip_max_norm b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/gradient_clip_max_norm new file mode 100644 index 0000000..9f8e9b6 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/gradient_clip_max_norm @@ -0,0 +1 @@ +1.0 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/learning_rate b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/learning_rate new file mode 100644 index 0000000..eb5a1db --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/learning_rate @@ -0,0 +1 @@ +0.001 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/loss_function b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/loss_function new file mode 100644 index 0000000..45eeeb8 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/loss_function @@ -0,0 +1 @@ +CrossEntropyLoss \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/metrics_average b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/metrics_average new file mode 100644 index 0000000..fc9944e --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/metrics_average @@ -0,0 +1 @@ +macro \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/model_class b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/model_class new file mode 100644 index 0000000..1f8c656 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/model_class @@ -0,0 +1 @@ +AudioCNN \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/num_classes b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/num_classes new file mode 100644 index 0000000..da2d398 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/num_classes @@ -0,0 +1 @@ +14 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/optimizer b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/optimizer new file mode 100644 index 0000000..a139610 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/optimizer @@ -0,0 +1 @@ +Adam \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/scheduler b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/scheduler new file mode 100644 index 0000000..23eeeb1 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/scheduler @@ -0,0 +1 @@ +ReduceLROnPlateau \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/total_parameters b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/total_parameters new file mode 100644 index 0000000..30b92b7 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/total_parameters @@ -0,0 +1 @@ +1215662 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/train_size b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/train_size new file mode 100644 index 0000000..130e16f --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/train_size @@ -0,0 +1 @@ +1200 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/trainable_parameters b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/trainable_parameters new file mode 100644 index 0000000..30b92b7 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/trainable_parameters @@ -0,0 +1 @@ +1215662 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/val_size b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/val_size new file mode 100644 index 0000000..6b3ed8d --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/params/val_size @@ -0,0 +1 @@ +400 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/tags/mlflow.source.name b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/tags/mlflow.source.name new file mode 100644 index 0000000..f8f55c9 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/tags/mlflow.source.name @@ -0,0 +1 @@ +C:\Users\Dalvy07\miniconda3\envs\ml\lib\site-packages\ipykernel_launcher.py \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/tags/mlflow.source.type b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/tags/mlflow.source.type new file mode 100644 index 0000000..0c2c1fe --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/tags/mlflow.source.type @@ -0,0 +1 @@ +LOCAL \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/tags/mlflow.user b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/tags/mlflow.user new file mode 100644 index 0000000..be1bd86 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-2c42de9a5b5949e49be9ca72d48bf742/tags/mlflow.user @@ -0,0 +1 @@ +Dalvy07 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/meta.yaml b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/meta.yaml new file mode 100644 index 0000000..227b6a0 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/meta.yaml @@ -0,0 +1,10 @@ +artifact_location: file:///C:\Users\Dalvy07\POLLUB\anomaly-project-implementation\ml\mlflow_server\artefacts/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/artifacts +creation_timestamp: 1759268792987 +experiment_id: '588266442625671109' +last_updated_timestamp: 1759268805202 +model_id: m-5035c0b502f8443b9a48f8b7ef4532a0 +model_type: '' +name: best_model +source_run_id: 2c08b9fbc2f44dffb1fb76e7e619563c +status: 2 +status_message: null diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/learning_rate b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/learning_rate new file mode 100644 index 0000000..0306fb5 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/learning_rate @@ -0,0 +1 @@ +1759268790801 0.001 0 2c08b9fbc2f44dffb1fb76e7e619563c diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/train_accuracy b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/train_accuracy new file mode 100644 index 0000000..dbb7867 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/train_accuracy @@ -0,0 +1 @@ +1759268790801 0.3866666555404663 0 2c08b9fbc2f44dffb1fb76e7e619563c diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/train_f1 b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/train_f1 new file mode 100644 index 0000000..e5368da --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/train_f1 @@ -0,0 +1 @@ +1759268790801 0.0651419535279274 0 2c08b9fbc2f44dffb1fb76e7e619563c diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/train_loss b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/train_loss new file mode 100644 index 0000000..b24b8bc --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/train_loss @@ -0,0 +1 @@ +1759268790801 1.974422630510832 0 2c08b9fbc2f44dffb1fb76e7e619563c diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/train_precision b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/train_precision new file mode 100644 index 0000000..18e16c0 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/train_precision @@ -0,0 +1 @@ +1759268790801 0.08374731987714767 0 2c08b9fbc2f44dffb1fb76e7e619563c diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/train_recall b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/train_recall new file mode 100644 index 0000000..00686bd --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/train_recall @@ -0,0 +1 @@ +1759268790801 0.08165134489536285 0 2c08b9fbc2f44dffb1fb76e7e619563c diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/val_accuracy b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/val_accuracy new file mode 100644 index 0000000..79ae381 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/val_accuracy @@ -0,0 +1 @@ +1759268790801 0.14000000059604645 0 2c08b9fbc2f44dffb1fb76e7e619563c diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/val_f1 b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/val_f1 new file mode 100644 index 0000000..a5daee4 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/val_f1 @@ -0,0 +1 @@ +1759268790801 0.039743974804878235 0 2c08b9fbc2f44dffb1fb76e7e619563c diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/val_loss b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/val_loss new file mode 100644 index 0000000..2d4ce14 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/val_loss @@ -0,0 +1 @@ +1759268790801 3.87488447702848 0 2c08b9fbc2f44dffb1fb76e7e619563c diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/val_precision b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/val_precision new file mode 100644 index 0000000..28bc7dc --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/val_precision @@ -0,0 +1 @@ +1759268790801 0.07447916269302368 0 2c08b9fbc2f44dffb1fb76e7e619563c diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/val_recall b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/val_recall new file mode 100644 index 0000000..b43ef1a --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/metrics/val_recall @@ -0,0 +1 @@ +1759268790801 0.11075268685817719 0 2c08b9fbc2f44dffb1fb76e7e619563c diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/batch_size b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/batch_size new file mode 100644 index 0000000..1758ddd --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/batch_size @@ -0,0 +1 @@ +32 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/device b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/device new file mode 100644 index 0000000..5b36a5d --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/device @@ -0,0 +1 @@ +cuda \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/early_stop_patience b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/early_stop_patience new file mode 100644 index 0000000..c793025 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/early_stop_patience @@ -0,0 +1 @@ +7 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/epochs b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/epochs new file mode 100644 index 0000000..3f10ffe --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/epochs @@ -0,0 +1 @@ +15 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/gradient_clip_max_norm b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/gradient_clip_max_norm new file mode 100644 index 0000000..9f8e9b6 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/gradient_clip_max_norm @@ -0,0 +1 @@ +1.0 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/learning_rate b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/learning_rate new file mode 100644 index 0000000..eb5a1db --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/learning_rate @@ -0,0 +1 @@ +0.001 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/loss_function b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/loss_function new file mode 100644 index 0000000..45eeeb8 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/loss_function @@ -0,0 +1 @@ +CrossEntropyLoss \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/metrics_average b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/metrics_average new file mode 100644 index 0000000..fc9944e --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/metrics_average @@ -0,0 +1 @@ +macro \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/model_class b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/model_class new file mode 100644 index 0000000..1f8c656 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/model_class @@ -0,0 +1 @@ +AudioCNN \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/num_classes b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/num_classes new file mode 100644 index 0000000..da2d398 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/num_classes @@ -0,0 +1 @@ +14 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/optimizer b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/optimizer new file mode 100644 index 0000000..a139610 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/optimizer @@ -0,0 +1 @@ +Adam \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/scheduler b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/scheduler new file mode 100644 index 0000000..23eeeb1 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/scheduler @@ -0,0 +1 @@ +ReduceLROnPlateau \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/total_parameters b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/total_parameters new file mode 100644 index 0000000..30b92b7 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/total_parameters @@ -0,0 +1 @@ +1215662 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/train_size b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/train_size new file mode 100644 index 0000000..130e16f --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/train_size @@ -0,0 +1 @@ +1200 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/trainable_parameters b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/trainable_parameters new file mode 100644 index 0000000..30b92b7 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/trainable_parameters @@ -0,0 +1 @@ +1215662 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/val_size b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/val_size new file mode 100644 index 0000000..6b3ed8d --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/params/val_size @@ -0,0 +1 @@ +400 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/tags/mlflow.source.name b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/tags/mlflow.source.name new file mode 100644 index 0000000..f8f55c9 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/tags/mlflow.source.name @@ -0,0 +1 @@ +C:\Users\Dalvy07\miniconda3\envs\ml\lib\site-packages\ipykernel_launcher.py \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/tags/mlflow.source.type b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/tags/mlflow.source.type new file mode 100644 index 0000000..0c2c1fe --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/tags/mlflow.source.type @@ -0,0 +1 @@ +LOCAL \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/tags/mlflow.user b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/tags/mlflow.user new file mode 100644 index 0000000..be1bd86 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/models/m-5035c0b502f8443b9a48f8b7ef4532a0/tags/mlflow.user @@ -0,0 +1 @@ +Dalvy07 \ No newline at end of file diff --git a/ml/mlflow_server/data_local/588266442625671109/tags/mlflow.experimentKind b/ml/mlflow_server/data_local/588266442625671109/tags/mlflow.experimentKind new file mode 100644 index 0000000..bee2484 --- /dev/null +++ b/ml/mlflow_server/data_local/588266442625671109/tags/mlflow.experimentKind @@ -0,0 +1 @@ +custom_model_development \ No newline at end of file diff --git a/ml/model_states/best_anomaly_classifier3.pt b/ml/model_states/best_anomaly_classifier3.pt deleted file mode 100644 index c745a74..0000000 Binary files a/ml/model_states/best_anomaly_classifier3.pt and /dev/null differ diff --git a/ml/model_states/best_model_epoch_20.pt b/ml/model_states/best_model_epoch_20.pt new file mode 100644 index 0000000..d2daad0 Binary files /dev/null and b/ml/model_states/best_model_epoch_20.pt differ diff --git a/ml/model_states/checkpoint_states3.pt b/ml/model_states/checkpoint_states3.pt deleted file mode 100644 index c540a48..0000000 Binary files a/ml/model_states/checkpoint_states3.pt and /dev/null differ diff --git a/ml/preprocess_audioset.py b/ml/preprocess_audioset.py new file mode 100644 index 0000000..74abf49 --- /dev/null +++ b/ml/preprocess_audioset.py @@ -0,0 +1,495 @@ +""" +Preprocessing script for AudioSet dataset. +Processes all audio files, applies transformations, and saves to HDF5 format. +""" + +import h5py +import numpy as np +import torch +import torchaudio +from pathlib import Path +from tqdm import tqdm +import json +from typing import Optional, Callable, Tuple, List +import argparse +from datetime import datetime + +from custom_transformations import get_audio_transforms +from cfg import TargetClass, TARGET_SAMPLE_RATE, TARGET_DURATION, N_MELS, N_FFT, HOP_LENGTH + + +class AudioSetPreprocessor: + """Preprocessor for AudioSet dataset to HDF5 format""" + + def __init__(self, + audioset_root: str, + output_path: str, + train_ratio: float = 0.6, + val_ratio: float = 0.2, + test_ratio: float = 0.2, + seed: int = 42): + """ + Initialize preprocessor + + Args: + audioset_root: Path to data directory containing audioset folder + output_path: Path where to save HDF5 file + train_ratio: Training set ratio + val_ratio: Validation set ratio + test_ratio: Test set ratio + seed: Random seed for reproducible splits + """ + self.audioset_root = Path(audioset_root) + self.audio_dir = self.audioset_root / 'audioset' + self.output_path = Path(output_path) + + self.train_ratio = train_ratio + self.val_ratio = val_ratio + self.test_ratio = test_ratio + self.seed = seed + + # Ensure ratios sum to 1 + assert abs(train_ratio + val_ratio + test_ratio - 1.0) < 1e-6, \ + "Train, val, and test ratios must sum to 1.0" + + # Get audio transforms + self.transform = get_audio_transforms() + + # AudioSet to Target class mapping + self.AUDIOSET_TO_TARGET_MAPPING = { + # EMERGENCY_SERVICES + 'Siren': TargetClass.EMERGENCY_SERVICES, + 'Police car (siren)': TargetClass.EMERGENCY_SERVICES, + 'Ambulance (siren)': TargetClass.EMERGENCY_SERVICES, + 'Fire engine, fire truck (siren)': TargetClass.EMERGENCY_SERVICES, + 'Smoke detector, smoke alarm': TargetClass.EMERGENCY_SERVICES, + 'Fire alarm': TargetClass.EMERGENCY_SERVICES, + 'Civil defense siren': TargetClass.EMERGENCY_SERVICES, + + # FIRE_EXPLOSION + 'Fire': TargetClass.FIRE_EXPLOSION, + 'Crackle': TargetClass.FIRE_EXPLOSION, + 'Explosion': TargetClass.FIRE_EXPLOSION, + 'Boom': TargetClass.FIRE_EXPLOSION, + 'Eruption': TargetClass.FIRE_EXPLOSION, + 'Fireworks': TargetClass.FIRE_EXPLOSION, + 'Burst, pop': TargetClass.FIRE_EXPLOSION, + + # HUMAN_DISTRESS + 'Screaming': TargetClass.HUMAN_DISTRESS, + 'Shout': TargetClass.HUMAN_DISTRESS, + 'Yell': TargetClass.HUMAN_DISTRESS, + 'Crying, sobbing': TargetClass.HUMAN_DISTRESS, + 'Baby cry, infant cry': TargetClass.HUMAN_DISTRESS, + 'Wail, moan': TargetClass.HUMAN_DISTRESS, + 'Children shouting': TargetClass.HUMAN_DISTRESS, + 'Whimper': TargetClass.HUMAN_DISTRESS, + + # STRUCTURAL_DAMAGE + 'Shatter': TargetClass.STRUCTURAL_DAMAGE, + 'Chink, clink': TargetClass.STRUCTURAL_DAMAGE, + 'Smash, crash': TargetClass.STRUCTURAL_DAMAGE, + 'Breaking': TargetClass.STRUCTURAL_DAMAGE, + 'Bang': TargetClass.STRUCTURAL_DAMAGE, + 'Thump, thud': TargetClass.STRUCTURAL_DAMAGE, + 'Slam': TargetClass.STRUCTURAL_DAMAGE, + 'Crack': TargetClass.STRUCTURAL_DAMAGE, + + # TRAFFIC_EMERGENCY + 'Car alarm': TargetClass.TRAFFIC_EMERGENCY, + 'Skidding': TargetClass.TRAFFIC_EMERGENCY, + 'Tire squeal': TargetClass.TRAFFIC_EMERGENCY, + 'Vehicle horn, car horn, honking': TargetClass.TRAFFIC_EMERGENCY, + 'Reversing beeps': TargetClass.TRAFFIC_EMERGENCY, + 'Air horn, truck horn': TargetClass.TRAFFIC_EMERGENCY, + 'Air brake': TargetClass.TRAFFIC_EMERGENCY, + + # WEAPON_VIOLENCE + 'Gunshot, gunfire': TargetClass.WEAPON_VIOLENCE, + 'Machine gun': TargetClass.WEAPON_VIOLENCE, + 'Fusillade': TargetClass.WEAPON_VIOLENCE, + 'Artillery fire': TargetClass.WEAPON_VIOLENCE, + 'Growling': TargetClass.WEAPON_VIOLENCE, + 'Roar': TargetClass.WEAPON_VIOLENCE, + 'Rattle': TargetClass.WEAPON_VIOLENCE, + + # HOUSEHOLD_SOUNDS + 'Dishes, pots, and pans': TargetClass.HOUSEHOLD_SOUNDS, + 'Cutlery, silverware': TargetClass.HOUSEHOLD_SOUNDS, + 'Water tap, faucet': TargetClass.HOUSEHOLD_SOUNDS, + 'Microwave oven': TargetClass.HOUSEHOLD_SOUNDS, + 'Blender': TargetClass.HOUSEHOLD_SOUNDS, + 'Vacuum cleaner': TargetClass.HOUSEHOLD_SOUNDS, + 'Toilet flush': TargetClass.HOUSEHOLD_SOUNDS, + 'Hair dryer': TargetClass.HOUSEHOLD_SOUNDS, + + # MUSIC_ENTERTAINMENT + 'Piano': TargetClass.MUSIC_ENTERTAINMENT, + 'Guitar': TargetClass.MUSIC_ENTERTAINMENT, + 'Drum': TargetClass.MUSIC_ENTERTAINMENT, + 'Music': TargetClass.MUSIC_ENTERTAINMENT, + 'Song': TargetClass.MUSIC_ENTERTAINMENT, + 'Pop music': TargetClass.MUSIC_ENTERTAINMENT, + 'Rock music': TargetClass.MUSIC_ENTERTAINMENT, + 'Background music': TargetClass.MUSIC_ENTERTAINMENT, + + # NATURE_SOUNDS + 'Rain': TargetClass.NATURE_SOUNDS, + 'Wind': TargetClass.NATURE_SOUNDS, + 'Rustling leaves': TargetClass.NATURE_SOUNDS, + 'Water': TargetClass.NATURE_SOUNDS, + 'Stream': TargetClass.NATURE_SOUNDS, + 'Waves, surf': TargetClass.NATURE_SOUNDS, + 'Bird vocalization, bird call, bird song': TargetClass.NATURE_SOUNDS, + 'Chirp, tweet': TargetClass.NATURE_SOUNDS, + + # NORMAL_TRANSPORT + 'Car': TargetClass.NORMAL_TRANSPORT, + 'Car passing by': TargetClass.NORMAL_TRANSPORT, + 'Bus': TargetClass.NORMAL_TRANSPORT, + 'Train': TargetClass.NORMAL_TRANSPORT, + 'Bicycle': TargetClass.NORMAL_TRANSPORT, + 'Fixed-wing aircraft, airplane': TargetClass.NORMAL_TRANSPORT, + 'Traffic noise, roadway noise': TargetClass.NORMAL_TRANSPORT, + + # PUBLIC_SPACES + 'Crowd': TargetClass.PUBLIC_SPACES, + 'Chatter': TargetClass.PUBLIC_SPACES, + 'Cheering': TargetClass.PUBLIC_SPACES, + 'Applause': TargetClass.PUBLIC_SPACES, + 'Conversation': TargetClass.PUBLIC_SPACES, + 'Hubbub, speech noise, speech babble': TargetClass.PUBLIC_SPACES, + 'Children playing': TargetClass.PUBLIC_SPACES, + + # SPECIAL_EVENTS + 'Christmas music': TargetClass.SPECIAL_EVENTS, + 'Wedding music': TargetClass.SPECIAL_EVENTS, + 'Laughter': TargetClass.SPECIAL_EVENTS, + 'Baby laughter': TargetClass.SPECIAL_EVENTS, + 'Bell': TargetClass.SPECIAL_EVENTS, + 'Doorbell': TargetClass.SPECIAL_EVENTS, + 'Wind chime': TargetClass.SPECIAL_EVENTS, + + # SPEECH_COMMUNICATION + 'Speech': TargetClass.SPEECH_COMMUNICATION, + 'Male speech, man speaking': TargetClass.SPEECH_COMMUNICATION, + 'Female speech, woman speaking': TargetClass.SPEECH_COMMUNICATION, + 'Child speech, kid speaking': TargetClass.SPEECH_COMMUNICATION, + 'Narration, monologue': TargetClass.SPEECH_COMMUNICATION, + 'Singing': TargetClass.SPEECH_COMMUNICATION, + 'Whispering': TargetClass.SPEECH_COMMUNICATION, + + # WORK_SOUNDS + 'Typing': TargetClass.WORK_SOUNDS, + 'Computer keyboard': TargetClass.WORK_SOUNDS, + 'Telephone': TargetClass.WORK_SOUNDS, + 'Printer': TargetClass.WORK_SOUNDS, + 'Drill': TargetClass.WORK_SOUNDS, + 'Sawing': TargetClass.WORK_SOUNDS, + 'Hammer': TargetClass.WORK_SOUNDS, + } + + def collect_audio_files(self) -> List[Tuple[Path, int]]: + """ + Collect all audio files with their labels + + Returns: + List of (file_path, label_index) tuples + """ + data_list = [] + audio_extensions = {'.wav'} + + print(f"Scanning directory: {self.audio_dir}") + + for directory in self.audio_dir.iterdir(): + if directory.is_dir(): + class_name = directory.name + + if class_name not in self.AUDIOSET_TO_TARGET_MAPPING: + print(f"Warning: Unknown class '{class_name}', skipping...") + continue + + target_class = self.AUDIOSET_TO_TARGET_MAPPING[class_name] + label = target_class.index + + # Find all audio files + files = [ + f for f in directory.glob('*') + if f.is_file() and f.suffix.lower() in audio_extensions + ] + + for file_path in files: + data_list.append((file_path, label)) + + print(f" {class_name}: {len(files)} files -> Label {label} ({target_class.class_name})") + + print(f"\nTotal files found: {len(data_list)}") + return data_list + + def create_splits(self, data_list: List[Tuple[Path, int]]) -> Tuple[List, List, List]: + """ + Create train/val/test splits + + Args: + data_list: List of (file_path, label) tuples + + Returns: + train_list, val_list, test_list + """ + # Set random seed for reproducibility + np.random.seed(self.seed) + + # Shuffle data + indices = np.random.permutation(len(data_list)) + + # Calculate split sizes + n_total = len(data_list) + n_train = int(n_total * self.train_ratio) + n_val = int(n_total * self.val_ratio) + + # Split indices + train_indices = indices[:n_train] + val_indices = indices[n_train:n_train + n_val] + test_indices = indices[n_train + n_val:] + + train_list = [data_list[i] for i in train_indices] + val_list = [data_list[i] for i in val_indices] + test_list = [data_list[i] for i in test_indices] + + print(f"\nData split:") + print(f" Train: {len(train_list)} samples ({len(train_list)/n_total*100:.1f}%)") + print(f" Val: {len(val_list)} samples ({len(val_list)/n_total*100:.1f}%)") + print(f" Test: {len(test_list)} samples ({len(test_list)/n_total*100:.1f}%)") + + return train_list, val_list, test_list + + def process_audio_file(self, file_path: Path) -> Optional[np.ndarray]: + """ + Load and process a single audio file + + Args: + file_path: Path to audio file + + Returns: + Processed spectrogram as numpy array or None if error + """ + try: + # Load audio + waveform, sample_rate = torchaudio.load(file_path) + + # Apply transforms + spectrogram = self.transform((waveform, sample_rate)) + + # Convert to numpy + spectrogram_np = spectrogram.numpy() + + return spectrogram_np + + except Exception as e: + print(f"\nError processing {file_path}: {e}") + return None + + def process_and_save(self): + """ + Main processing function: load all audio, process, and save to HDF5 + """ + print("=" * 60) + print("AudioSet Preprocessing to HDF5") + print("=" * 60) + + # Collect all files + data_list = self.collect_audio_files() + + if len(data_list) == 0: + print("Error: No audio files found!") + return + + # Create splits + train_list, val_list, test_list = self.create_splits(data_list) + + # Create output directory + self.output_path.parent.mkdir(parents=True, exist_ok=True) + + # Process each split + print(f"\nProcessing and saving to: {self.output_path}") + + with h5py.File(self.output_path, 'w') as hf: + # Process train set + print("\n" + "=" * 60) + print("Processing TRAIN set...") + print("=" * 60) + self._process_split(hf, train_list, 'train') + + # Process validation set + print("\n" + "=" * 60) + print("Processing VALIDATION set...") + print("=" * 60) + self._process_split(hf, val_list, 'val') + + # Process test set + print("\n" + "=" * 60) + print("Processing TEST set...") + print("=" * 60) + self._process_split(hf, test_list, 'test') + + # Save metadata + self._save_metadata(hf, len(train_list), len(val_list), len(test_list)) + + print("\n" + "=" * 60) + print(f"Preprocessing complete!") + print(f"Output saved to: {self.output_path}") + print(f"File size: {self.output_path.stat().st_size / 1024 / 1024:.2f} MB") + print("=" * 60) + + def _process_split(self, hf: h5py.File, data_list: List[Tuple[Path, int]], split_name: str): + """ + Process a single split and save to HDF5 + + Args: + hf: HDF5 file handle + data_list: List of (file_path, label) tuples + split_name: Name of split ('train', 'val', or 'test') + """ + n_samples = len(data_list) + + # First pass: get shape from first valid sample + print("Determining data shape from first sample...") + first_spec = None + for file_path, _ in data_list: + first_spec = self.process_audio_file(file_path) + if first_spec is not None: + break + + if first_spec is None: + print(f"Error: Could not process any files in {split_name} split!") + return + + spec_shape = first_spec.shape # (1, 128, 157) or similar + print(f"Spectrogram shape: {spec_shape}") + + # Create datasets with compression + features_shape = (n_samples,) + spec_shape + group = hf.create_group(split_name) + + features_ds = group.create_dataset( + 'features', + shape=features_shape, + dtype='float32', + compression='gzip', + compression_opts=4, + chunks=(1,) + spec_shape # One sample per chunk + ) + + labels_ds = group.create_dataset( + 'labels', + shape=(n_samples,), + dtype='int64', + compression='gzip', + compression_opts=4 + ) + + # Process all files + successful = 0 + failed = 0 + + for idx, (file_path, label) in enumerate(tqdm(data_list, desc=f"Processing {split_name}")): + spectrogram = self.process_audio_file(file_path) + + if spectrogram is not None: + features_ds[idx] = spectrogram + labels_ds[idx] = label + successful += 1 + else: + # Fill with zeros if processing failed + features_ds[idx] = np.zeros(spec_shape, dtype=np.float32) + labels_ds[idx] = -1 # Mark as invalid + failed += 1 + + print(f"{split_name.capitalize()} set: {successful} successful, {failed} failed") + + def _save_metadata(self, hf: h5py.File, n_train: int, n_val: int, n_test: int): + """Save metadata as attributes""" + metadata = { + 'preprocessing_date': datetime.now().isoformat(), + 'target_sample_rate': TARGET_SAMPLE_RATE, + 'target_duration': TARGET_DURATION, + 'n_mels': N_MELS, + 'n_fft': N_FFT, + 'hop_length': HOP_LENGTH, + 'num_classes': TargetClass.get_total_classes(), + 'class_names': json.dumps(TargetClass.get_class_names()), + 'train_samples': n_train, + 'val_samples': n_val, + 'test_samples': n_test, + 'total_samples': n_train + n_val + n_test, + 'train_ratio': self.train_ratio, + 'val_ratio': self.val_ratio, + 'test_ratio': self.test_ratio, + 'random_seed': self.seed, + } + + for key, value in metadata.items(): + hf.attrs[key] = value + + print("\nMetadata saved:") + for key, value in metadata.items(): + print(f" {key}: {value}") + + +def main(): + parser = argparse.ArgumentParser(description='Preprocess AudioSet dataset to HDF5 format') + parser.add_argument( + '--audioset-root', + type=str, + default='./data', + help='Path to data directory containing audioset folder' + ) + parser.add_argument( + '--output', + type=str, + default='./data/audioset_preprocessed.h5', + help='Output HDF5 file path' + ) + parser.add_argument( + '--train-ratio', + type=float, + default=0.6, + help='Training set ratio (default: 0.6)' + ) + parser.add_argument( + '--val-ratio', + type=float, + default=0.2, + help='Validation set ratio (default: 0.2)' + ) + parser.add_argument( + '--test-ratio', + type=float, + default=0.2, + help='Test set ratio (default: 0.2)' + ) + parser.add_argument( + '--seed', + type=int, + default=42, + help='Random seed for reproducible splits (default: 42)' + ) + + args = parser.parse_args() + + # Create preprocessor + preprocessor = AudioSetPreprocessor( + audioset_root=args.audioset_root, + output_path=args.output, + train_ratio=args.train_ratio, + val_ratio=args.val_ratio, + test_ratio=args.test_ratio, + seed=args.seed + ) + + # Run preprocessing + preprocessor.process_and_save() + + +if __name__ == '__main__': + main() diff --git a/ml/preprocessed_dataset.py b/ml/preprocessed_dataset.py new file mode 100644 index 0000000..c740f6d --- /dev/null +++ b/ml/preprocessed_dataset.py @@ -0,0 +1,222 @@ +""" +Dataset class for loading preprocessed AudioSet data from HDF5 format. +""" + +import h5py +import torch +from torch.utils.data import Dataset +from pathlib import Path +from typing import Tuple, Optional +import json +import numpy as np + +from cfg import TargetClass + + +class PreprocessedAudioSetDataset(Dataset): + """ + PyTorch Dataset for preprocessed AudioSet data stored in HDF5 format. + + The dataset loads preprocessed mel-spectrograms from HDF5 file, + which significantly speeds up training by avoiding repeated audio loading + and FFT computation. + """ + + def __init__(self, + hdf5_path: str, + split: str = 'train', + cache_in_memory: bool = False, + transform: Optional[callable] = None): + """ + Initialize preprocessed dataset + + Args: + hdf5_path: Path to HDF5 file with preprocessed data + split: Which split to use ('train', 'val', or 'test') + cache_in_memory: If True, load entire dataset into RAM (faster but memory-intensive) + transform: Optional transforms to apply (e.g., data augmentation) + """ + self.hdf5_path = Path(hdf5_path) + self.split = split + self.cache_in_memory = cache_in_memory + self.transform = transform + + if not self.hdf5_path.exists(): + raise FileNotFoundError(f"HDF5 file not found: {self.hdf5_path}") + + if split not in ['train', 'val', 'test']: + raise ValueError(f"Split must be 'train', 'val', or 'test', got: {split}") + + # Open HDF5 file to get metadata and check structure + with h5py.File(self.hdf5_path, 'r') as hf: + if split not in hf: + raise ValueError(f"Split '{split}' not found in HDF5 file. Available: {list(hf.keys())}") + + # Get dataset info + self.n_samples = hf[split]['features'].shape[0] + self.feature_shape = hf[split]['features'].shape[1:] # (1, 128, 157) + + # Load metadata + self.metadata = dict(hf.attrs) + + # Cache data in memory if requested + if self.cache_in_memory: + print(f"Loading {split} set into memory...") + self.features = torch.from_numpy(hf[split]['features'][:]) + self.labels = torch.from_numpy(hf[split]['labels'][:]) + print(f"Loaded {self.n_samples} samples into memory") + print(f" Features shape: {self.features.shape}") + print(f" Memory usage: {self.features.element_size() * self.features.nelement() / 1024 / 1024:.2f} MB") + else: + self.features = None + self.labels = None + # Keep file handle for lazy loading + self.hf = None + + # Validate labels (remove samples with label -1, which are failed preprocessings) + if self.cache_in_memory: + valid_mask = self.labels >= 0 + if not valid_mask.all(): + print(f"Warning: Found {(~valid_mask).sum()} invalid samples, filtering them out") + self.features = self.features[valid_mask] + self.labels = self.labels[valid_mask] + self.n_samples = len(self.labels) + + def __len__(self) -> int: + """Return number of samples""" + return self.n_samples + + def __getitem__(self, idx: int) -> Tuple[torch.Tensor, int]: + """ + Get a single sample + + Args: + idx: Sample index + + Returns: + Tuple of (feature_tensor, label) + """ + if self.cache_in_memory: + # Load from cached memory + feature = self.features[idx] + label = self.labels[idx].item() + else: + # Lazy load from HDF5 + if self.hf is None: + self.hf = h5py.File(self.hdf5_path, 'r') + + feature = torch.from_numpy(self.hf[self.split]['features'][idx]) + label = int(self.hf[self.split]['labels'][idx]) + + # Skip invalid samples + if label < 0: + # Return a valid sample instead (fallback) + return self.__getitem__((idx + 1) % self.n_samples) + + # Apply optional transforms (e.g., data augmentation) + if self.transform is not None: + feature = self.transform(feature) + + return feature, label + + def __del__(self): + """Close HDF5 file if it was opened""" + if hasattr(self, 'hf') and self.hf is not None: + self.hf.close() + + def get_metadata(self) -> dict: + """Return preprocessing metadata""" + return self.metadata + + def get_class_distribution(self) -> dict: + """ + Get distribution of classes in this split + + Returns: + Dictionary mapping class_name -> count + """ + if self.cache_in_memory: + labels = self.labels.numpy() + else: + with h5py.File(self.hdf5_path, 'r') as hf: + labels = hf[self.split]['labels'][:] + + # Count occurrences + unique, counts = np.unique(labels[labels >= 0], return_counts=True) + + distribution = {} + for label_idx, count in zip(unique, counts): + target_class = TargetClass.from_index(int(label_idx)) + distribution[target_class.class_name] = int(count) + + return distribution + + def print_info(self): + """Print dataset information""" + print("=" * 60) + print(f"Preprocessed AudioSet Dataset - {self.split.upper()} split") + print("=" * 60) + print(f"HDF5 file: {self.hdf5_path}") + print(f"Number of samples: {self.n_samples}") + print(f"Feature shape: {self.feature_shape}") + print(f"Cached in memory: {self.cache_in_memory}") + print(f"\nMetadata:") + print(f" Preprocessing date: {self.metadata.get('preprocessing_date', 'N/A')}") + print(f" Sample rate: {self.metadata.get('target_sample_rate', 'N/A')} Hz") + print(f" Duration: {self.metadata.get('target_duration', 'N/A')} seconds") + print(f" N_mels: {self.metadata.get('n_mels', 'N/A')}") + print(f" N_FFT: {self.metadata.get('n_fft', 'N/A')}") + print(f" Hop length: {self.metadata.get('hop_length', 'N/A')}") + print(f" Total classes: {self.metadata.get('num_classes', 'N/A')}") + + print(f"\nClass distribution:") + distribution = self.get_class_distribution() + for class_name, count in sorted(distribution.items()): + percentage = count / self.n_samples * 100 + print(f" {class_name:30s}: {count:4d} ({percentage:5.2f}%)") + print("=" * 60) + + +def test_dataset(): + """Test function to verify dataset loading""" + import sys + + if len(sys.argv) < 2: + print("Usage: python preprocessed_dataset.py ") + sys.exit(1) + + hdf5_path = sys.argv[1] + + print("\nTesting PreprocessedAudioSetDataset...") + + # Test train split + train_dataset = PreprocessedAudioSetDataset( + hdf5_path=hdf5_path, + split='train', + cache_in_memory=False + ) + train_dataset.print_info() + + # Test loading a sample + print("\nTesting sample loading...") + feature, label = train_dataset[0] + print(f"Feature shape: {feature.shape}") + print(f"Feature dtype: {feature.dtype}") + print(f"Label: {label}") + print(f"Label class: {TargetClass.from_index(label).class_name}") + + # Test with DataLoader + from torch.utils.data import DataLoader + print("\nTesting with DataLoader (batch_size=8)...") + loader = DataLoader(train_dataset, batch_size=8, shuffle=False, num_workers=0) + + for batch_idx, (features, labels) in enumerate(loader): + print(f"Batch {batch_idx}: features shape={features.shape}, labels shape={labels.shape}") + if batch_idx >= 2: # Test only 3 batches + break + + print("\n✓ All tests passed!") + + +if __name__ == '__main__': + test_dataset() diff --git a/ml/requirements.txt b/ml/requirements.txt index 6f9790d..c1706d2 100644 --- a/ml/requirements.txt +++ b/ml/requirements.txt @@ -12,6 +12,7 @@ python-multipart==0.0.20 numpy==2.1.2 pandas==2.3.2 pillow==11.0.0 +h5py>=3.0.0 # Audio processing (если нужно) soundfile==0.13.1 diff --git a/ml/training.ipynb b/ml/training.ipynb new file mode 100644 index 0000000..fc41ef5 --- /dev/null +++ b/ml/training.ipynb @@ -0,0 +1,1569 @@ +{ + "cells": [ + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-10-12T21:56:12.920213Z", + "start_time": "2025-10-12T21:56:12.888813Z" + } + }, + "cell_type": "code", + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import mlflow\n", + "import matplotlib.pyplot as plt\n", + "\n", + "\n", + "import torch\n", + "import torch.nn as nn\n", + "import torch.nn.functional as F\n", + "from sympy.physics.units import length\n", + "from torch.utils.data import Dataset, DataLoader, random_split\n", + "import torchaudio\n", + "import torchvision\n", + "\n", + "\n", + "\n", + "import os\n", + "from pathlib import Path\n", + "from typing import Optional, Callable\n", + "from tqdm import tqdm\n", + "from datetime import datetime\n", + "import logging\n", + "\n", + "# Импорт ваших модулей\n", + "from anomaly_classifier_architecture import AudioCNN\n", + "from custom_transformations import get_audio_transforms\n", + "import cfg\n", + "\n", + "# mlflow server --backend-store-uri \"file:///C:\\Users\\Dalvy07\\POLLUB\\anomaly-project-implementation\\ml\\mlflow_server\\data_local\" --default-artifact-root \"file:///C:\\Users\\Dalvy07\\POLLUB\\anomaly-project-implementation\\ml\\mlflow_server\\artefacts\" --host localhost --port 5000\n", + "\n", + "\n", + "# python3 process.py download -c \"\" -d './audioset' -n 150" + ], + "id": "692d5af1933b4007", + "outputs": [], + "execution_count": 21 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-10-12T21:56:13.004048Z", + "start_time": "2025-10-12T21:56:12.963552Z" + } + }, + "cell_type": "code", + "source": [ + "from cfg import TargetClass, TARGET_DURATION, TARGET_SAMPLE_RATE\n", + "\n", + "class AudioSetDataset(Dataset):\n", + " \"\"\"\"Pytorch Dataset for Google AudioSet\"\"\"\n", + "\n", + " def __init__(self, path_to_root_dir: str, target_sample_rate: int = TARGET_SAMPLE_RATE,\n", + " duration: float = TARGET_DURATION, transform: Optional[Callable] = None):\n", + " self.root_dir_path = Path(path_to_root_dir)\n", + " self.audio_dir_path = self.root_dir_path / 'audioset'\n", + " self.target_sample_rate = target_sample_rate\n", + " self.duration = duration\n", + " self.transform = transform\n", + "\n", + " self.length = 0\n", + "\n", + " self.AUDIOSET_TO_TARGET_MAPPING = {\n", + " # ============================================\n", + " # EMERGENCY_SERVICES (7 классов)\n", + " # ============================================\n", + " 'Siren': TargetClass.EMERGENCY_SERVICES,\n", + " 'Police car (siren)': TargetClass.EMERGENCY_SERVICES,\n", + " 'Ambulance (siren)': TargetClass.EMERGENCY_SERVICES,\n", + " 'Fire engine, fire truck (siren)': TargetClass.EMERGENCY_SERVICES,\n", + " 'Smoke detector, smoke alarm': TargetClass.EMERGENCY_SERVICES,\n", + " 'Fire alarm': TargetClass.EMERGENCY_SERVICES,\n", + " 'Civil defense siren': TargetClass.EMERGENCY_SERVICES,\n", + "\n", + " # ============================================\n", + " # FIRE_EXPLOSION (7 классов)\n", + " # ============================================\n", + " 'Fire': TargetClass.FIRE_EXPLOSION,\n", + " 'Crackle': TargetClass.FIRE_EXPLOSION,\n", + " 'Explosion': TargetClass.FIRE_EXPLOSION,\n", + " 'Boom': TargetClass.FIRE_EXPLOSION,\n", + " 'Eruption': TargetClass.FIRE_EXPLOSION,\n", + " 'Fireworks': TargetClass.FIRE_EXPLOSION,\n", + " 'Burst, pop': TargetClass.FIRE_EXPLOSION,\n", + "\n", + " # ============================================\n", + " # HUMAN_DISTRESS (8 классов)\n", + " # ============================================\n", + " 'Screaming': TargetClass.HUMAN_DISTRESS,\n", + " 'Shout': TargetClass.HUMAN_DISTRESS,\n", + " 'Yell': TargetClass.HUMAN_DISTRESS, #End\n", + " 'Crying, sobbing': TargetClass.HUMAN_DISTRESS, #End\n", + " 'Baby cry, infant cry': TargetClass.HUMAN_DISTRESS, #End\n", + " 'Wail, moan': TargetClass.HUMAN_DISTRESS, #End - Chjeta, 24 fajla\n", + " 'Children shouting': TargetClass.HUMAN_DISTRESS, #End\n", + " 'Whimper': TargetClass.HUMAN_DISTRESS, #End\n", + "\n", + " # ============================================\n", + " # STRUCTURAL_DAMAGE (8 классов) INSTALLED\n", + " # ============================================\n", + " 'Shatter': TargetClass.STRUCTURAL_DAMAGE,\n", + " 'Chink, clink': TargetClass.STRUCTURAL_DAMAGE,\n", + " 'Smash, crash': TargetClass.STRUCTURAL_DAMAGE,\n", + " 'Breaking': TargetClass.STRUCTURAL_DAMAGE,\n", + " 'Bang': TargetClass.STRUCTURAL_DAMAGE,\n", + " 'Thump, thud': TargetClass.STRUCTURAL_DAMAGE,\n", + " 'Slam': TargetClass.STRUCTURAL_DAMAGE,\n", + " 'Crack': TargetClass.STRUCTURAL_DAMAGE,\n", + "\n", + " # ============================================\n", + " # TRAFFIC_EMERGENCY (7 классов) INSTALLED\n", + " # ============================================\n", + " 'Car alarm': TargetClass.TRAFFIC_EMERGENCY,\n", + " 'Skidding': TargetClass.TRAFFIC_EMERGENCY,\n", + " 'Tire squeal': TargetClass.TRAFFIC_EMERGENCY,\n", + " 'Vehicle horn, car horn, honking': TargetClass.TRAFFIC_EMERGENCY,\n", + " 'Reversing beeps': TargetClass.TRAFFIC_EMERGENCY,\n", + " 'Air horn, truck horn': TargetClass.TRAFFIC_EMERGENCY,\n", + " 'Air brake': TargetClass.TRAFFIC_EMERGENCY,\n", + "\n", + " # ============================================\n", + " # WEAPON_VIOLENCE (7 классов)\n", + " # ============================================\n", + " 'Gunshot, gunfire': TargetClass.WEAPON_VIOLENCE, # ---\n", + " 'Machine gun': TargetClass.WEAPON_VIOLENCE,\n", + " 'Fusillade': TargetClass.WEAPON_VIOLENCE,\n", + " 'Artillery fire': TargetClass.WEAPON_VIOLENCE, # ---\n", + " 'Growling': TargetClass.WEAPON_VIOLENCE,\n", + " 'Roar': TargetClass.WEAPON_VIOLENCE,\n", + " 'Rattle': TargetClass.WEAPON_VIOLENCE,\n", + "\n", + " # ============================================\n", + " # HOUSEHOLD_SOUNDS (8 классов)\n", + " # ============================================\n", + " 'Dishes, pots, and pans': TargetClass.HOUSEHOLD_SOUNDS,\n", + " 'Cutlery, silverware': TargetClass.HOUSEHOLD_SOUNDS,\n", + " 'Water tap, faucet': TargetClass.HOUSEHOLD_SOUNDS,\n", + " 'Microwave oven': TargetClass.HOUSEHOLD_SOUNDS,\n", + " 'Blender': TargetClass.HOUSEHOLD_SOUNDS,\n", + " 'Vacuum cleaner': TargetClass.HOUSEHOLD_SOUNDS,\n", + " 'Toilet flush': TargetClass.HOUSEHOLD_SOUNDS,\n", + " 'Hair dryer': TargetClass.HOUSEHOLD_SOUNDS,\n", + "\n", + " # ============================================\n", + " # MUSIC_ENTERTAINMENT (8 классов)\n", + " # ============================================\n", + " 'Piano': TargetClass.MUSIC_ENTERTAINMENT,\n", + " 'Guitar': TargetClass.MUSIC_ENTERTAINMENT,\n", + " 'Drum': TargetClass.MUSIC_ENTERTAINMENT,\n", + " 'Music': TargetClass.MUSIC_ENTERTAINMENT,\n", + " 'Song': TargetClass.MUSIC_ENTERTAINMENT,\n", + " 'Pop music': TargetClass.MUSIC_ENTERTAINMENT,\n", + " 'Rock music': TargetClass.MUSIC_ENTERTAINMENT,\n", + " 'Background music': TargetClass.MUSIC_ENTERTAINMENT,\n", + "\n", + " # ============================================\n", + " # NATURE_SOUNDS (8 классов)\n", + " # ============================================\n", + " 'Rain': TargetClass.NATURE_SOUNDS,\n", + " 'Wind': TargetClass.NATURE_SOUNDS,\n", + " 'Rustling leaves': TargetClass.NATURE_SOUNDS,\n", + " 'Water': TargetClass.NATURE_SOUNDS,\n", + " 'Stream': TargetClass.NATURE_SOUNDS,\n", + " 'Waves, surf': TargetClass.NATURE_SOUNDS,\n", + " 'Bird vocalization, bird call, bird song': TargetClass.NATURE_SOUNDS,\n", + " 'Chirp, tweet': TargetClass.NATURE_SOUNDS,\n", + "\n", + " # ============================================\n", + " # NORMAL_TRANSPORT (7 классов)\n", + " # ============================================\n", + " 'Car': TargetClass.NORMAL_TRANSPORT,\n", + " 'Car passing by': TargetClass.NORMAL_TRANSPORT,\n", + " 'Bus': TargetClass.NORMAL_TRANSPORT,\n", + " 'Train': TargetClass.NORMAL_TRANSPORT,\n", + " 'Bicycle': TargetClass.NORMAL_TRANSPORT,\n", + " 'Fixed-wing aircraft, airplane': TargetClass.NORMAL_TRANSPORT,\n", + " 'Traffic noise, roadway noise': TargetClass.NORMAL_TRANSPORT,\n", + "\n", + " # ============================================\n", + " # PUBLIC_SPACES (7 классов)\n", + " # ============================================\n", + " 'Crowd': TargetClass.PUBLIC_SPACES,\n", + " 'Chatter': TargetClass.PUBLIC_SPACES,\n", + " 'Cheering': TargetClass.PUBLIC_SPACES,\n", + " 'Applause': TargetClass.PUBLIC_SPACES,\n", + " 'Conversation': TargetClass.PUBLIC_SPACES,\n", + " 'Hubbub, speech noise, speech babble': TargetClass.PUBLIC_SPACES,\n", + " 'Children playing': TargetClass.PUBLIC_SPACES,\n", + "\n", + " # ============================================\n", + " # SPECIAL_EVENTS (7 классов)\n", + " # ============================================\n", + " 'Christmas music': TargetClass.SPECIAL_EVENTS,\n", + " 'Wedding music': TargetClass.SPECIAL_EVENTS,\n", + " 'Laughter': TargetClass.SPECIAL_EVENTS,\n", + " 'Baby laughter': TargetClass.SPECIAL_EVENTS,\n", + " 'Bell': TargetClass.SPECIAL_EVENTS,\n", + " 'Doorbell': TargetClass.SPECIAL_EVENTS,\n", + " 'Wind chime': TargetClass.SPECIAL_EVENTS,\n", + "\n", + " # ============================================\n", + " # SPEECH_COMMUNICATION (7 классов)\n", + " # ============================================\n", + " 'Speech': TargetClass.SPEECH_COMMUNICATION,\n", + " 'Male speech, man speaking': TargetClass.SPEECH_COMMUNICATION,\n", + " 'Female speech, woman speaking': TargetClass.SPEECH_COMMUNICATION,\n", + " 'Child speech, kid speaking': TargetClass.SPEECH_COMMUNICATION,\n", + " 'Narration, monologue': TargetClass.SPEECH_COMMUNICATION,\n", + " 'Singing': TargetClass.SPEECH_COMMUNICATION,\n", + " 'Whispering': TargetClass.SPEECH_COMMUNICATION,\n", + "\n", + " # ============================================\n", + " # WORK_SOUNDS (7 классов)\n", + " # ============================================\n", + " 'Typing': TargetClass.WORK_SOUNDS,\n", + " 'Computer keyboard': TargetClass.WORK_SOUNDS,\n", + " 'Telephone': TargetClass.WORK_SOUNDS,\n", + " 'Printer': TargetClass.WORK_SOUNDS,\n", + " 'Drill': TargetClass.WORK_SOUNDS,\n", + " 'Sawing': TargetClass.WORK_SOUNDS,\n", + " 'Hammer': TargetClass.WORK_SOUNDS,\n", + " }\n", + "\n", + " # Create a dict {class_name: [list of all files for class]}, count those files and save and sort class names\n", + " self.data_list = []\n", + " class_names_list = []\n", + "\n", + " # Поддерживаемые аудио форматы\n", + " audio_extensions = {'.wav'}\n", + "\n", + " for directory in self.audio_dir_path.iterdir():\n", + " if directory.is_dir():\n", + " class_names_list.append(directory.name)\n", + " # Фильтруем только аудиофайлы\n", + " file_names_in_directory = [\n", + " (file_name, directory.name)\n", + " for file_name in directory.glob('*')\n", + " if file_name.is_file() and file_name.suffix.lower() in audio_extensions\n", + " ]\n", + " self.length += len(file_names_in_directory)\n", + " self.data_list.extend(file_names_in_directory)\n", + " self.class_names_list_sorted = sorted(class_names_list)\n", + "\n", + " def __len__(self) -> int:\n", + " return self.length\n", + "\n", + " def __getitem__(self, idx: int) -> tuple[torch.Tensor, int]:\n", + " file_path, target = self.data_list[idx]\n", + " target = self.AUDIOSET_TO_TARGET_MAPPING[target].index\n", + " sample, _ = torchaudio.load(file_path)\n", + "\n", + " if self.transform is not None:\n", + " sample = self.transform(sample)\n", + "\n", + " return sample, target" + ], + "id": "69e1b30634e9624a", + "outputs": [], + "execution_count": 22 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-10-12T21:56:13.063339Z", + "start_time": "2025-10-12T21:56:13.048557Z" + } + }, + "cell_type": "code", + "source": "", + "id": "3726bdc8789d96cb", + "outputs": [], + "execution_count": null + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-10-12T21:56:13.124670Z", + "start_time": "2025-10-12T21:56:13.094625Z" + } + }, + "cell_type": "code", + "source": [ + "from cfg import TargetClass, TARGET_DURATION, TARGET_SAMPLE_RATE\n", + "\n", + "class ESC50Dataset(Dataset):\n", + " \"\"\"PyTorch Dataset for ESC-50\"\"\"\n", + "\n", + " def __init__(self, path_to_root_dir: str, target_sample_rate: int = TARGET_SAMPLE_RATE,\n", + " duration: float = TARGET_DURATION, transform: Optional[Callable] = None):\n", + " self.root_dir_path = Path(path_to_root_dir)\n", + " self.audio_dir_path = self.root_dir_path / '44100'\n", + " self.target_sample_rate = target_sample_rate\n", + " self.duration = duration\n", + " self.transform = transform\n", + "\n", + " self.data_list = []\n", + " self.length = 0\n", + "\n", + " # Get metadata\n", + " meta_file_path = self.root_dir_path / 'esc50.csv'\n", + " self.metadata = pd.read_csv(meta_file_path)\n", + " self.length = len(self.metadata)\n", + "\n", + " # Attributes to work with ESC-50 dataset markings\n", + " classes_target_df = self.metadata[['category', 'target']].drop_duplicates().sort_values('target')\n", + " self.class_to_idx = dict(zip(classes_target_df['category'], classes_target_df['target']))\n", + " self.idx_to_class = dict(zip(classes_target_df['target'], classes_target_df['category']))\n", + " self.classes = classes_target_df['category'].to_list()\n", + "\n", + " self.ESC50_TO_TARGET_MAPPING = {\n", + " # Nature sounds\n", + " 'dog': TargetClass.NATURE_SOUNDS,\n", + " 'rooster': TargetClass.NATURE_SOUNDS,\n", + " 'pig': TargetClass.NATURE_SOUNDS,\n", + " 'cow': TargetClass.NATURE_SOUNDS,\n", + " 'frog': TargetClass.NATURE_SOUNDS,\n", + " 'cat': TargetClass.NATURE_SOUNDS,\n", + " 'hen': TargetClass.NATURE_SOUNDS,\n", + " 'insects': TargetClass.NATURE_SOUNDS,\n", + " 'sheep': TargetClass.NATURE_SOUNDS,\n", + " 'crow': TargetClass.NATURE_SOUNDS,\n", + " 'rain': TargetClass.NATURE_SOUNDS,\n", + " 'sea_waves': TargetClass.NATURE_SOUNDS,\n", + " 'crackling_fire': TargetClass.NATURE_SOUNDS,\n", + " 'crickets': TargetClass.NATURE_SOUNDS,\n", + " 'chirping_birds': TargetClass.NATURE_SOUNDS,\n", + " 'water_drops': TargetClass.NATURE_SOUNDS,\n", + " 'wind': TargetClass.NATURE_SOUNDS,\n", + " 'pouring_water': TargetClass.NATURE_SOUNDS,\n", + " 'toilet_flush': TargetClass.HOUSEHOLD_SOUNDS,\n", + " 'thunderstorm': TargetClass.NATURE_SOUNDS,\n", + "\n", + " # Human sounds\n", + " 'crying_baby': TargetClass.HUMAN_DISTRESS, # ОПАСНО\n", + " 'sneezing': TargetClass.SPEECH_COMMUNICATION,\n", + " 'clapping': TargetClass.PUBLIC_SPACES,\n", + " 'breathing': TargetClass.SPEECH_COMMUNICATION,\n", + " 'coughing': TargetClass.SPEECH_COMMUNICATION,\n", + " 'footsteps': TargetClass.SPEECH_COMMUNICATION,\n", + " 'laughing': TargetClass.SPEECH_COMMUNICATION,\n", + " 'brushing_teeth': TargetClass.HOUSEHOLD_SOUNDS,\n", + " 'snoring': TargetClass.SPEECH_COMMUNICATION,\n", + " 'drinking_sipping': TargetClass.HOUSEHOLD_SOUNDS,\n", + "\n", + " # Every day/work sounds\n", + " 'door_wood_knock': TargetClass.HOUSEHOLD_SOUNDS,\n", + " 'mouse_click': TargetClass.HOUSEHOLD_SOUNDS,\n", + " 'keyboard_typing': TargetClass.HOUSEHOLD_SOUNDS,\n", + " 'door_wood_creaks': TargetClass.HOUSEHOLD_SOUNDS,\n", + " 'can_opening': TargetClass.HOUSEHOLD_SOUNDS,\n", + " 'washing_machine': TargetClass.HOUSEHOLD_SOUNDS,\n", + " 'vacuum_cleaner': TargetClass.HOUSEHOLD_SOUNDS,\n", + " 'clock_alarm': TargetClass.HOUSEHOLD_SOUNDS,\n", + " 'clock_tick': TargetClass.HOUSEHOLD_SOUNDS,\n", + " 'chainsaw': TargetClass.WORK_SOUNDS,\n", + " 'hand_saw': TargetClass.WORK_SOUNDS,\n", + "\n", + " # Transport\n", + " 'helicopter': TargetClass.NORMAL_TRANSPORT,\n", + " 'car_horn': TargetClass.NORMAL_TRANSPORT,\n", + " 'engine': TargetClass.NORMAL_TRANSPORT,\n", + " 'train': TargetClass.NORMAL_TRANSPORT,\n", + " 'airplane': TargetClass.NORMAL_TRANSPORT,\n", + "\n", + " # Dangerous sounds\n", + " 'glass_breaking': TargetClass.STRUCTURAL_DAMAGE, # ОПАСНО!\n", + " 'siren': TargetClass.EMERGENCY_SERVICES, # ОПАСНО!\n", + "\n", + " # Events\n", + " 'church_bells': TargetClass.SPECIAL_EVENTS,\n", + " 'fireworks': TargetClass.SPECIAL_EVENTS\n", + " }\n", + "\n", + "\n", + " # Make list with filepath and target for dataset\n", + " mapped_file_names = self.metadata['filename'].map(lambda x: self.audio_dir_path / x)\n", + " mapped_target_categories = self.metadata['category'].map(lambda x: self.ESC50_TO_TARGET_MAPPING[x].index) # Map categories from ESC-50 to indexes\n", + " self.data_list = list(zip(mapped_file_names, mapped_target_categories))\n", + "\n", + " def __len__(self) -> int:\n", + " return self.length\n", + "\n", + " def __getitem__(self, idx: int) -> tuple[torch.Tensor, int]:\n", + " file_path, target = self.data_list[idx]\n", + " sample, _ = torchaudio.load(file_path)\n", + "\n", + " if self.transform is not None:\n", + " sample = self.transform(sample)\n", + "\n", + " return sample, target\n", + "\n", + "# '''\n", + "# !!!DEPRECATED!!!\n", + "# Але бляді прибрали лінк для torchcodec для вінди.\n", + "# Треба чекати, поки він з'явиться і тоді переписати, або працювать на лінуксі.\n", + "# Є ідея підняти це в докери, але він може не дозваоляти відеокарті працювати на повну\n", + "# !!! ffmpeg через conda обов'язково треба качать з каналу conda-forge !!!\n", + "# '''\n", + "# path = Path('./data/esc50/44100/1-137-A-32.wav')\n", + "# waveform, sample_rate = torchaudio.load(path)\n", + "# type(waveform)" + ], + "id": "455691f3b9d6f173", + "outputs": [], + "execution_count": 23 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-10-12T21:56:13.216019Z", + "start_time": "2025-10-12T21:56:13.158927Z" + } + }, + "cell_type": "code", + "source": "from typing import Dict, Any\nimport tempfile\nimport mlflow\nimport mlflow.pytorch\nfrom pathlib import Path\nfrom datetime import datetime\nimport torch\nimport torch.nn as nn\nimport torchmetrics\nfrom torchmetrics import MetricCollection\nfrom tqdm import tqdm\nimport matplotlib.pyplot as plt\nimport logging\n\nfrom cfg import TargetClass\n# from trainer_config import TrainerConfig\n\n\nclass Trainer:\n def __init__(self, model: nn.Module, optimizer, criterion, scheduler , config: Dict[str, Any]):\n \"\"\"\n Инициализация тренера через конфиг.\n\n Args:\n config: Словарь со всеми параметрами\n \"\"\"\n self.config = config\n self.device = config['device']\n\n # Основные параметры\n self.model = model.to(self.device)\n self.num_classes = self.model.num_classes\n self.criterion = criterion\n self.optimizer = optimizer\n self.scheduler = scheduler\n\n # Инициализация метрик TorchMetrics\n self._init_metrics()\n\n # История обучения\n self.history = {\n 'train_loss': [], 'train_acc': [], 'train_f1': [], 'train_precision': [], 'train_recall': [],\n 'val_loss': [], 'val_acc': [], 'val_f1': [], 'val_precision': [], 'val_recall': [],\n 'lr': []\n }\n\n # Для отслеживания стартовой эпохи при возобновлении обучения\n self.start_epoch = 0\n self.best_val_loss = float('inf')\n\n # MLflow настройки\n mlflow.set_tracking_uri(config['mlflow_tracking_uri'])\n mlflow.set_experiment(config['mlflow_experiment_name'])\n\n # Отключаем вывод ворнингов от MLflow\n mlflow_logger = logging.getLogger(\"mlflow\")\n mlflow_logger.setLevel(logging.ERROR)\n\n self.mlflow_run = None\n\n def _init_metrics(self):\n \"\"\"Инициализация коллекции метрик для обучения и валидации\"\"\"\n task = \"binary\" if self.num_classes == 2 else \"multiclass\"\n\n self.train_metrics = MetricCollection({\n 'accuracy': torchmetrics.Accuracy(task=task, num_classes=self.num_classes),\n 'f1': torchmetrics.F1Score(task=task, num_classes=self.num_classes, average=self.config['metrics_average']),\n 'precision': torchmetrics.Precision(task=task, num_classes=self.num_classes, average=self.config['metrics_average']),\n 'recall': torchmetrics.Recall(task=task, num_classes=self.num_classes, average=self.config['metrics_average']),\n }).to(self.device)\n\n self.val_metrics = MetricCollection({\n 'accuracy': torchmetrics.Accuracy(task=task, num_classes=self.num_classes),\n 'f1': torchmetrics.F1Score(task=task, num_classes=self.num_classes, average=self.config['metrics_average']),\n 'precision': torchmetrics.Precision(task=task, num_classes=self.num_classes, average=self.config['metrics_average']),\n 'recall': torchmetrics.Recall(task=task, num_classes=self.num_classes, average=self.config['metrics_average']),\n }).to(self.device)\n\n if self.config['compute_confusion_matrix']:\n self.confusion_matrix = torchmetrics.ConfusionMatrix(\n task=task, num_classes=self.num_classes\n ).to(self.device)\n\n if self.config['compute_per_class_accuracy']:\n self.per_class_accuracy = torchmetrics.Accuracy(\n task=task, num_classes=self.num_classes, average=None\n ).to(self.device)\n\n def load_checkpoint_from_mlflow(self, run_id: str, checkpoint_name: str = \"best_checkpoint.pt\"):\n \"\"\"\n Загрузка чекпоинта из MLflow артифактов\n \n Args:\n run_id: ID MLflow run\n checkpoint_name: Имя файла чекпоинта (по умолчанию \"best_checkpoint.pt\")\n \"\"\"\n from mlflow.tracking import MlflowClient\n \n client = MlflowClient()\n \n # Скачиваем артифакт во временную директорию\n artifact_path = f\"checkpoints/{checkpoint_name}\"\n \n try:\n # Используем временную директорию для скачивания\n with tempfile.TemporaryDirectory() as temp_dir:\n print(f\"Downloading checkpoint from MLflow run: {run_id}\")\n print(f\" Artifact path: {artifact_path}\")\n \n # Скачиваем артифакт\n local_path = client.download_artifacts(run_id, artifact_path, temp_dir)\n \n # Загружаем чекпоинт\n checkpoint = torch.load(local_path, map_location=self.device)\n self._load_checkpoint_data(checkpoint)\n \n print(f\"✓ Successfully loaded checkpoint from epoch {self.start_epoch}\")\n if 'val_loss' in checkpoint:\n print(f\" Val Loss: {checkpoint['val_loss']:.4f}\")\n if 'val_metrics' in checkpoint:\n val_acc = checkpoint['val_metrics'].get('accuracy', None)\n if val_acc:\n print(f\" Val Accuracy: {val_acc*100:.2f}%\")\n \n except Exception as e:\n raise RuntimeError(f\"Failed to load checkpoint from MLflow: {e}\")\n \n def _load_checkpoint_data(self, checkpoint: Dict[str, Any]):\n \"\"\"\n Загрузка данных из чекпоинта\n \n Args:\n checkpoint: Словарь с данными чекпоинта\n \"\"\"\n # Загружаем состояния\n self.model.load_state_dict(checkpoint['model_state_dict'])\n self.optimizer.load_state_dict(checkpoint['optimizer_state_dict'])\n self.scheduler.load_state_dict(checkpoint['scheduler_state_dict'])\n \n # Восстанавливаем историю обучения\n if 'history' in checkpoint:\n self.history = checkpoint['history']\n \n # Устанавливаем стартовую эпоху\n self.start_epoch = checkpoint.get('epoch', 0) + 1\n \n # Восстанавливаем лучший val_loss\n self.best_val_loss = checkpoint.get('val_loss', float('inf'))\n \n # Переносим оптимизатор на правильное устройство\n for state in self.optimizer.state.values():\n for k, v in state.items():\n if isinstance(v, torch.Tensor):\n state[k] = v.to(self.device)\n\n def train_epoch(self, dataloader):\n self.model.train()\n total_loss = 0\n\n self.train_metrics.reset()\n\n pbar = tqdm(dataloader, desc='Training')\n for batch_idx, (inputs, targets) in enumerate(pbar):\n inputs = inputs.to(self.device)\n targets = targets.to(self.device)\n\n outputs = self.model(inputs)\n loss = self.criterion(outputs, targets)\n\n self.optimizer.zero_grad()\n loss.backward()\n\n # Gradient clipping если задан в конфиге\n if self.config['gradient_clip_max_norm'] is not None:\n torch.nn.utils.clip_grad_norm_(\n self.model.parameters(),\n max_norm=self.config['gradient_clip_max_norm']\n )\n\n self.optimizer.step()\n\n total_loss += loss.item()\n self.train_metrics.update(outputs, targets)\n\n current_metrics = self.train_metrics.compute()\n\n pbar.set_postfix({\n 'loss': f'{loss.item():.4f}',\n 'acc': f'{current_metrics[\"accuracy\"]*100:.2f}%',\n 'f1': f'{current_metrics[\"f1\"]:.3f}',\n 'precision': f'{current_metrics[\"precision\"]:.3f}',\n 'recall': f'{current_metrics[\"recall\"]:.3f}'\n })\n\n epoch_metrics = self.train_metrics.compute()\n avg_loss = total_loss / len(dataloader)\n\n return avg_loss, epoch_metrics\n\n def validate(self, dataloader):\n self.model.eval()\n total_loss = 0\n\n self.val_metrics.reset()\n if self.config['compute_confusion_matrix']:\n self.confusion_matrix.reset()\n if self.config['compute_per_class_accuracy']:\n self.per_class_accuracy.reset()\n\n with torch.no_grad():\n for inputs, targets in tqdm(dataloader, desc='Validation'):\n inputs = inputs.to(self.device)\n targets = targets.to(self.device)\n\n outputs = self.model(inputs)\n loss = self.criterion(outputs, targets)\n\n total_loss += loss.item()\n\n self.val_metrics.update(outputs, targets)\n if self.config['compute_confusion_matrix']:\n self.confusion_matrix.update(outputs, targets)\n if self.config['compute_per_class_accuracy']:\n self.per_class_accuracy.update(outputs, targets)\n\n epoch_metrics = self.val_metrics.compute()\n avg_loss = total_loss / len(dataloader)\n\n return avg_loss, epoch_metrics\n\n def train(self, train_loader, val_loader, resume_from_mlflow_run: str = None):\n \"\"\"\n Основной метод обучения. Все параметры берутся из self.config\n \n Args:\n train_loader: DataLoader для обучения\n val_loader: DataLoader для валидации\n resume_from_mlflow_run: ID MLflow run для загрузки чекпоинта и возобновления обучения\n \"\"\"\n # Загружаем чекпоинт из MLflow если указан\n if resume_from_mlflow_run:\n self.load_checkpoint_from_mlflow(resume_from_mlflow_run)\n \n best_val_loss = self.best_val_loss\n patience_counter = 0\n\n # Запуск MLflow run (если включен)\n mlflow_context = mlflow.start_run(run_name=self.config['mlflow_run_name'])\n\n with mlflow_context as run:\n self.mlflow_run = run\n print(f\"\\nMLflow run started: {run.info.run_id}\")\n print(f\" Tracking URI: {mlflow.get_tracking_uri()}\")\n \n if self.start_epoch > 0:\n print(f\"Resuming training from epoch {self.start_epoch + 1}\")\n mlflow.log_param('resumed_from_epoch', self.start_epoch)\n mlflow.log_param('resumed_from_run_id', resume_from_mlflow_run)\n\n self._log_initial_params(train_loader, val_loader)\n\n for epoch in range(self.start_epoch, self.config['epochs']):\n print(f'\\n==== Epoch {epoch+1}/{self.config[\"epochs\"]} ====')\n\n train_loss, train_metrics = self.train_epoch(train_loader)\n val_loss, val_metrics = self.validate(val_loader)\n\n current_lr = self.optimizer.param_groups[0]['lr']\n\n # Сохранение в историю\n self._update_history(train_loss, train_metrics, val_loss, val_metrics, current_lr)\n\n # Логирование метрик в MLflow\n self._log_epoch_metrics(epoch, train_loss, train_metrics, val_loss, val_metrics, current_lr)\n\n self.scheduler.step(val_loss)\n\n # Вывод метрик\n self._print_epoch_results(train_loss, train_metrics, val_loss, val_metrics, current_lr)\n\n # Сохранение модели\n if val_loss < best_val_loss:\n best_val_loss = val_loss\n patience_counter = 0\n self._save_best_model(epoch, val_loss, val_metrics)\n else:\n patience_counter += 1\n\n # Сохранение чекпоинтов по частоте (если задано)\n if self.config['save_all_checkpoints']:\n if self.config['save_frequency'] and (epoch + 1) % self.config['save_frequency'] == 0:\n self._save_checkpoint(epoch, val_loss, val_metrics, prefix='checkpoint')\n\n # Early stopping\n if patience_counter >= self.config['early_stop_patience']:\n print(f'\\nEarly stopping triggered after {epoch+1} epochs')\n mlflow.log_metric('early_stopped_epoch', epoch+1)\n break\n\n # Финальное логирование\n self._log_final_results(best_val_loss)\n print(f\"\\nMLflow run completed successfully!\")\n print(f\"Run ID: {run.info.run_id}\")\n print(f\"View at: {mlflow.get_tracking_uri()}/#/experiments/{run.info.experiment_id}/runs/{run.info.run_id}\")\n\n return self.history\n\n def _log_initial_params(self, train_loader, val_loader):\n \"\"\"Логирование начальных параметров в MLflow\"\"\"\n mlflow.log_params({\n 'model_class': self.model.__class__.__name__,\n 'num_classes': self.num_classes,\n 'learning_rate': self.config['learning_rate'],\n 'optimizer': self.optimizer.__class__.__name__,\n 'scheduler': self.scheduler.__class__.__name__,\n 'loss_function': self.criterion.__class__.__name__,\n 'early_stop_patience': self.config['early_stop_patience'],\n 'epochs': self.config['epochs'],\n 'batch_size': train_loader.batch_size,\n 'train_size': len(train_loader.dataset),\n 'val_size': len(val_loader.dataset),\n 'device': str(self.device),\n 'gradient_clip_max_norm': self.config['gradient_clip_max_norm'],\n 'metrics_average': self.config['metrics_average'],\n })\n\n # Логирование тегов\n if self.config['mlflow_tags']:\n mlflow.set_tags(self.config['mlflow_tags'])\n\n # Логирование информации о модели\n try:\n total_params_to_log = sum(p.numel() for p in self.model.parameters())\n trainable_params_to_log = sum(p.numel() for p in self.model.parameters() if p.requires_grad)\n mlflow.log_params({\n 'total_parameters': total_params_to_log,\n 'trainable_parameters': trainable_params_to_log,\n })\n except Exception as e:\n print(f\"Warning: Could not log model parameters: {e}\")\n\n def _update_history(self, train_loss, train_metrics, val_loss, val_metrics, current_lr):\n \"\"\"Обновление истории обучения\"\"\"\n self.history['train_loss'].append(train_loss)\n self.history['train_acc'].append(train_metrics['accuracy'].item())\n self.history['train_f1'].append(train_metrics['f1'].item())\n self.history['train_precision'].append(train_metrics['precision'].item())\n self.history['train_recall'].append(train_metrics['recall'].item())\n\n self.history['val_loss'].append(val_loss)\n self.history['val_acc'].append(val_metrics['accuracy'].item())\n self.history['val_f1'].append(val_metrics['f1'].item())\n self.history['val_precision'].append(val_metrics['precision'].item())\n self.history['val_recall'].append(val_metrics['recall'].item())\n\n self.history['lr'].append(current_lr)\n\n def _log_epoch_metrics(self, epoch, train_loss, train_metrics, val_loss, val_metrics, current_lr):\n \"\"\"Логирование метрик эпохи в MLflow\"\"\"\n mlflow.log_metrics({\n 'train_loss': train_loss,\n 'train_accuracy': train_metrics['accuracy'].item(),\n 'train_f1': train_metrics['f1'].item(),\n 'train_precision': train_metrics['precision'].item(),\n 'train_recall': train_metrics['recall'].item(),\n 'val_loss': val_loss,\n 'val_accuracy': val_metrics['accuracy'].item(),\n 'val_f1': val_metrics['f1'].item(),\n 'val_precision': val_metrics['precision'].item(),\n 'val_recall': val_metrics['recall'].item(),\n 'learning_rate': current_lr,\n }, step=epoch)\n\n def _print_epoch_results(self, train_loss, train_metrics, val_loss, val_metrics, current_lr):\n \"\"\"Вывод результатов эпохи в консоль\"\"\"\n print(f'Train Loss: {train_loss:.4f} | Train Metrics:')\n print(f' Acc: {train_metrics[\"accuracy\"]*100:.2f}% | F1: {train_metrics[\"f1\"]:.3f} | '\n f'Prec: {train_metrics[\"precision\"]:.3f} | Rec: {train_metrics[\"recall\"]:.3f}')\n\n print(f'Val Loss: {val_loss:.4f} | Val Metrics:')\n print(f' Acc: {val_metrics[\"accuracy\"]*100:.2f}% | F1: {val_metrics[\"f1\"]:.3f} | '\n f'Prec: {val_metrics[\"precision\"]:.3f} | Rec: {val_metrics[\"recall\"]:.3f}')\n\n print(f'Learning Rate: {current_lr:.6f}')\n\n def _save_best_model(self, epoch, val_loss, val_metrics):\n \"\"\"Сохранение лучшей модели в MLflow\"\"\"\n # Создаем временный файл с фиксированным именем\n checkpoint_path = Path(tempfile.gettempdir()) / 'best_checkpoint.pt'\n \n torch.save({\n 'epoch': epoch,\n 'model_state_dict': self.model.state_dict(),\n 'optimizer_state_dict': self.optimizer.state_dict(),\n 'scheduler_state_dict': self.scheduler.state_dict(),\n 'val_loss': val_loss,\n 'val_metrics': {k: v.item() for k, v in val_metrics.items()},\n 'history': self.history,\n 'config': self.config\n }, checkpoint_path)\n\n # Логируем модель в MLflow (автоматически перезапишет предыдущую)\n mlflow.pytorch.log_model(self.model, \"best_model\")\n\n # Логируем полный чекпоинт (автоматически перезапишет предыдущий благодаря одинаковому имени)\n mlflow.log_artifact(str(checkpoint_path), artifact_path=\"checkpoints\")\n\n # Удаляем временный файл\n checkpoint_path.unlink()\n\n print(f'Saved best model to MLflow (val_loss: {val_loss:.4f})')\n\n def _save_checkpoint(self, epoch, val_loss, val_metrics, prefix='checkpoint'):\n \"\"\"Сохранение чекпоинта в MLflow\"\"\"\n with tempfile.NamedTemporaryFile(mode='wb', suffix='.pt', delete=False) as tmp_file:\n checkpoint_path = tmp_file.name\n torch.save({\n 'epoch': epoch,\n 'model_state_dict': self.model.state_dict(),\n 'optimizer_state_dict': self.optimizer.state_dict(),\n 'scheduler_state_dict': self.scheduler.state_dict(),\n 'val_loss': val_loss,\n 'val_metrics': {k: v.item() for k, v in val_metrics.items()},\n 'history': self.history,\n 'config': self.config\n }, tmp_file)\n\n mlflow.log_artifact(checkpoint_path, artifact_path=\"checkpoints\")\n\n # Удаляем временный файл\n Path(checkpoint_path).unlink()\n\n print(f'Checkpoint saved to MLflow: epoch {epoch+1}')\n\n def _log_final_results(self, best_val_loss):\n \"\"\"Логирование финальных результатов в MLflow\"\"\"\n mlflow.log_metrics({\n 'best_val_loss': best_val_loss,\n 'final_train_loss': self.history['train_loss'][-1],\n 'final_val_loss': self.history['val_loss'][-1],\n 'final_val_accuracy': self.history['val_acc'][-1],\n 'final_val_f1': self.history['val_f1'][-1],\n })\n\n # Логирование Per-class метрик (если включено)\n if self.config['compute_per_class_accuracy']:\n per_class_acc = self.per_class_accuracy.compute()\n for class_idx, acc in enumerate(per_class_acc):\n mlflow.log_metric(f'per_class_accuracy_class_{class_idx}', acc.item())\n\n # Логирование confusion matrix (если включено)\n if self.config['compute_confusion_matrix']:\n cm = self.confusion_matrix.compute().cpu().numpy()\n self._log_confusion_matrix(cm)\n\n def _log_confusion_matrix(self, cm):\n \"\"\"Логирование confusion matrix в MLflow\"\"\"\n fig, ax = plt.subplots(figsize=(10, 8))\n im = ax.imshow(cm, interpolation='nearest', cmap=plt.cm.Blues)\n ax.figure.colorbar(im, ax=ax)\n\n ax.set(xticks=range(self.num_classes),\n yticks=range(self.num_classes),\n xlabel='Predicted label',\n ylabel='True label',\n title='Confusion Matrix')\n\n thresh = cm.max() / 2.\n for i in range(self.num_classes):\n for j in range(self.num_classes):\n ax.text(j, i, format(cm[i, j], 'd'),\n ha=\"center\", va=\"center\",\n color=\"white\" if cm[i, j] > thresh else \"black\")\n\n plt.tight_layout()\n\n # Сохраняем с фиксированным именем\n confusion_matrix_path = Path(tempfile.gettempdir()) / 'confusion_matrix.png'\n plt.savefig(confusion_matrix_path, dpi=100, bbox_inches='tight')\n \n # Логируем в MLflow (автоматически перезапишет предыдущую)\n mlflow.log_artifact(str(confusion_matrix_path), artifact_path=\"visualizations\")\n \n # Удаляем временный файл\n confusion_matrix_path.unlink()\n \n plt.close()\n print('Confusion matrix logged to MLflow')\n\n def get_per_class_metrics(self):\n \"\"\"Получение метрик по классам\"\"\"\n if self.config['compute_per_class_accuracy']:\n per_class_acc = self.per_class_accuracy.compute()\n return per_class_acc\n else:\n print(\"Per-class accuracy computation is disabled in config\")\n return None", + "id": "eca48ee112e6d522", + "outputs": [], + "execution_count": 24 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-10-12T21:56:13.308307Z", + "start_time": "2025-10-12T21:56:13.244861Z" + } + }, + "cell_type": "code", + "source": [ + "TRAINER_CONFIG = {\n", + " # Обязательные параметры\n", + " \"mlflow_run_name\": None,\n", + "\n", + " # Базовые параметры обучения\n", + " \"device\": \"cuda\" if torch.cuda.is_available() else \"cpu\",\n", + " \"learning_rate\": 0.001,\n", + " \"batch_size\": 128, # Can be increased since I/O is faster\n", + " \"epochs\": 80,\n", + " \"early_stop_patience\": 7,\n", + " \"gradient_clip_max_norm\": 1.0,\n", + "\n", + " # Параметры метрик\n", + " \"metrics_average\": \"macro\",\n", + " \"compute_confusion_matrix\": True,\n", + " \"compute_per_class_accuracy\": False,\n", + "\n", + " # Пути для сохранения\n", + " \"save_all_checkpoints\": False,\n", + " \"save_frequency\": 10,\n", + "\n", + " # MLflow параметры\n", + " \"mlflow_experiment_name\": \"Anomaly_Classifier_Exp\",\n", + " \"mlflow_tracking_uri\": \"http://localhost:5000\",\n", + " \"mlflow_tags\": {\"data_source\": \"preprocessed_hdf5\"},\n", + "}\n", + "\n", + "\n", + "\n", + "# TRAINER_CONFIG = {\n", + "# # Обязательные параметры\n", + "# \"mlflow_run_name\": None,\n", + "#\n", + "# # Базовые параметры обучения\n", + "# \"device\": \"cuda\" if torch.cuda.is_available() else \"cpu\" , # или \"cpu\"\n", + "# \"learning_rate\": 0.001,\n", + "# \"batch_size\": 128,\n", + "# \"epochs\": 10,\n", + "# \"early_stop_patience\": 7,\n", + "# \"gradient_clip_max_norm\": 1.0,\n", + "#\n", + "# # Параметры метрик\n", + "# \"metrics_average\": \"macro\", # \"micro\", \"macro\", \"weighted\", \"samples\"\n", + "# \"compute_confusion_matrix\": True,\n", + "# \"compute_per_class_accuracy\": True,\n", + "#\n", + "# # Пути для сохранения\n", + "# \"save_all_checkpoints\": False,\n", + "# \"save_frequency\": 10,\n", + "#\n", + "# # MLflow параметры\n", + "# \"mlflow_experiment_name\": \"Anomaly_Classifier_Exp\",\n", + "# \"mlflow_tracking_uri\": \"http://localhost:5000\",\n", + "# \"mlflow_tags\": None, # или {\"key\": \"value\"}\n", + "# }\n", + "\n", + "BATCH_SIZE = TRAINER_CONFIG[\"batch_size\"]\n", + "LEARNING_RATE = TRAINER_CONFIG[\"learning_rate\"]\n", + "EPOCHS = TRAINER_CONFIG[\"epochs\"]\n", + "DEVICE = TRAINER_CONFIG[\"device\"]\n", + "HDF5_PATH = './data/audioset_preprocessed.h5'\n", + "\n", + "print(f'Using device: {DEVICE}')\n", + "\n", + "\n", + "# ============================================================================\n", + "# LOAD PREPROCESSED DATASETS\n", + "# ============================================================================\n", + "\n", + "print(\"\\n\" + \"=\" * 70)\n", + "print(\"Loading preprocessed datasets from HDF5\")\n", + "print(\"=\" * 70)\n", + "\n", + "from preprocessed_dataset import PreprocessedAudioSetDataset\n", + "\n", + "# Load datasets (no transforms needed - already preprocessed!)\n", + "train_dataset = PreprocessedAudioSetDataset(\n", + " hdf5_path=HDF5_PATH,\n", + " split='train',\n", + " cache_in_memory=False, # Set to True if you have enough RAM\n", + " transform=None # No transforms - data already preprocessed\n", + ")\n", + "\n", + "val_dataset = PreprocessedAudioSetDataset(\n", + " hdf5_path=HDF5_PATH,\n", + " split='val',\n", + " cache_in_memory=False,\n", + " transform=None\n", + ")\n", + "\n", + "test_dataset = PreprocessedAudioSetDataset(\n", + " hdf5_path=HDF5_PATH,\n", + " split='test',\n", + " cache_in_memory=False,\n", + " transform=None\n", + ")\n", + "\n", + "# Print dataset info\n", + "train_dataset.print_info()\n", + "\n", + "print(\"\\n\" + \"=\" * 70)\n", + "print(\"Creating DataLoaders\")\n", + "print(\"=\" * 70)\n", + "\n", + "# Create DataLoaders with increased num_workers for faster loading\n", + "train_loader = DataLoader(\n", + " train_dataset,\n", + " batch_size=BATCH_SIZE,\n", + " shuffle=True,\n", + " num_workers=0, # Increase if you have more CPU cores\n", + " pin_memory=True if DEVICE == 'cuda' else False, # Speeds up GPU transfer\n", + " # persistent_workers=True # Keep workers alive between epochs\n", + ")\n", + "\n", + "val_loader = DataLoader(\n", + " val_dataset,\n", + " batch_size=BATCH_SIZE,\n", + " shuffle=False,\n", + " num_workers=0,\n", + " pin_memory=True if DEVICE == 'cuda' else False,\n", + " # persistent_workers=True\n", + ")\n", + "\n", + "test_loader = DataLoader(\n", + " test_dataset,\n", + " batch_size=BATCH_SIZE,\n", + " shuffle=False,\n", + " num_workers=0,\n", + " pin_memory=True if DEVICE == 'cuda' else False\n", + ")\n", + "\n", + "print(f\"Train loader: {len(train_loader)} batches ({len(train_dataset)} samples)\")\n", + "print(f\"Val loader: {len(val_loader)} batches ({len(val_dataset)} samples)\")\n", + "print(f\"Test loader: {len(test_loader)} batches ({len(test_dataset)} samples)\")\n", + "\n", + "# # Создание датасета (используя ваш ESC50Dataset)\n", + "# from pathlib import Path\n", + "# dataset = ESC50Dataset(\n", + "# path_to_root_dir='./data/esc50',\n", + "# transform=get_audio_transforms()\n", + "# )\n", + "#\n", + "# audioset = AudioSetDataset(\n", + "# path_to_root_dir='./data',\n", + "# transform=get_audio_transforms()\n", + "# )\n", + "#\n", + "# train_data, val_data, test_data = random_split(\n", + "# audioset, [0.6, 0.2, 0.2]\n", + "# )\n", + "#\n", + "# # DataLoaders\n", + "# train_loader = DataLoader(train_data,\n", + "# batch_size=BATCH_SIZE,\n", + "# shuffle=True,\n", + "# )\n", + "# val_loader = DataLoader(val_data,\n", + "# batch_size=BATCH_SIZE,\n", + "# shuffle=False,\n", + "# )\n", + "# test_loader = DataLoader(test_data, batch_size=BATCH_SIZE, shuffle=False)" + ], + "id": "ef79f3300a9cdc3a", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using device: cuda\n", + "\n", + "======================================================================\n", + "Loading preprocessed datasets from HDF5\n", + "======================================================================\n", + "============================================================\n", + "Preprocessed AudioSet Dataset - TRAIN split\n", + "============================================================\n", + "HDF5 file: data\\audioset_preprocessed.h5\n", + "Number of samples: 6775\n", + "Feature shape: (1, 128, 313)\n", + "Cached in memory: False\n", + "\n", + "Metadata:\n", + " Preprocessing date: 2025-10-12T21:51:48.533679\n", + " Sample rate: 16000 Hz\n", + " Duration: 10.0 seconds\n", + " N_mels: 128\n", + " N_FFT: 2048\n", + " Hop length: 512\n", + " Total classes: 14\n", + "\n", + "Class distribution:\n", + " emergency_services : 468 ( 6.91%)\n", + " fire_explosion : 484 ( 7.14%)\n", + " household_sounds : 535 ( 7.90%)\n", + " human_distress : 486 ( 7.17%)\n", + " music_entertainment : 477 ( 7.04%)\n", + " nature_sounds : 524 ( 7.73%)\n", + " normal_transport : 494 ( 7.29%)\n", + " public_spaces : 480 ( 7.08%)\n", + " special_events : 439 ( 6.48%)\n", + " speech_communication : 454 ( 6.70%)\n", + " structural_damage : 531 ( 7.84%)\n", + " traffic_emergency : 498 ( 7.35%)\n", + " weapon_violence : 439 ( 6.48%)\n", + " work_sounds : 465 ( 6.86%)\n", + "============================================================\n", + "\n", + "======================================================================\n", + "Creating DataLoaders\n", + "======================================================================\n", + "Train loader: 53 batches (6775 samples)\n", + "Val loader: 18 batches (2258 samples)\n", + "Test loader: 18 batches (2260 samples)\n" + ] + } + ], + "execution_count": 25 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-10-12T23:59:55.557057Z", + "start_time": "2025-10-12T21:56:13.338447Z" + } + }, + "cell_type": "code", + "source": [ + "from anomaly_classifier_architecture import AudioCNN\n", + "\n", + "\n", + "# Модель\n", + "model = AudioCNN(dropout_rate=0.3).to(DEVICE)\n", + "optimizer = torch.optim.Adam(model.parameters(), lr=LEARNING_RATE)\n", + "criterion = nn.CrossEntropyLoss()\n", + "scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(\n", + " optimizer,\n", + " mode='min',\n", + " factor=0.5,\n", + " patience=3,\n", + " min_lr=1e-6,\n", + ")\n", + "\n", + "\n", + "# Создание тренера и обучение (обратите внимание на num_classes)\n", + "# trainer = Trainer(DEVICE, model, TargetClass.get_total_classes(), LEARNING_RATE)\n", + "trainer = Trainer(model, optimizer, criterion, scheduler, TRAINER_CONFIG)\n", + "history = trainer.train(train_loader, val_loader)\n", + "\n", + "\n", + "test_loss, test_metrics = trainer.validate(test_loader)\n", + "\n", + "print(f'\\n=== Final Test Results ===')\n", + "print(f'Test Loss: {test_loss:.4f}')\n", + "print(f'Test Accuracy: {test_metrics[\"accuracy\"]*100:.2f}%')\n", + "print(f'Test F1: {test_metrics[\"f1\"]:.3f}')\n", + "print(f'Test Precision: {test_metrics[\"precision\"]:.3f}')\n", + "print(f'Test Recall: {test_metrics[\"recall\"]:.3f}')" + ], + "id": "4f1f890a8003aa72", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "MLflow run started: 5af2b34673d24fa2a94226e93ec9ed53\n", + " Tracking URI: http://localhost:5000\n", + "\n", + "==== Epoch 1/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [02:04<00:00, 2.34s/it, loss=2.3761, acc=15.81%, f1=0.134, precision=0.152, recall=0.160]\n", + "Validation: 100%|██████████| 18/18 [00:11<00:00, 1.60it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 2.4669 | Train Metrics:\n", + " Acc: 15.81% | F1: 0.134 | Prec: 0.152 | Rec: 0.160\n", + "Val Loss: 2.8284 | Val Metrics:\n", + " Acc: 13.51% | F1: 0.067 | Prec: 0.135 | Rec: 0.130\n", + "Learning Rate: 0.001000\n", + "Saved best model to MLflow (val_loss: 2.8284)\n", + "\n", + "==== Epoch 2/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [01:57<00:00, 2.22s/it, loss=2.1407, acc=22.33%, f1=0.195, precision=0.196, recall=0.226]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.77it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 2.2651 | Train Metrics:\n", + " Acc: 22.33% | F1: 0.195 | Prec: 0.196 | Rec: 0.226\n", + "Val Loss: 2.2710 | Val Metrics:\n", + " Acc: 24.89% | F1: 0.214 | Prec: 0.244 | Rec: 0.256\n", + "Learning Rate: 0.001000\n", + "Saved best model to MLflow (val_loss: 2.2710)\n", + "\n", + "==== Epoch 3/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [01:56<00:00, 2.21s/it, loss=2.1299, acc=25.82%, f1=0.235, precision=0.241, recall=0.260]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.76it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 2.1805 | Train Metrics:\n", + " Acc: 25.82% | F1: 0.235 | Prec: 0.241 | Rec: 0.260\n", + "Val Loss: 2.3701 | Val Metrics:\n", + " Acc: 19.75% | F1: 0.142 | Prec: 0.175 | Rec: 0.202\n", + "Learning Rate: 0.001000\n", + "\n", + "==== Epoch 4/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [02:02<00:00, 2.31s/it, loss=2.2864, acc=28.07%, f1=0.261, precision=0.262, recall=0.283]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.77it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 2.1110 | Train Metrics:\n", + " Acc: 28.07% | F1: 0.261 | Prec: 0.262 | Rec: 0.283\n", + "Val Loss: 2.2152 | Val Metrics:\n", + " Acc: 26.13% | F1: 0.222 | Prec: 0.244 | Rec: 0.265\n", + "Learning Rate: 0.001000\n", + "Saved best model to MLflow (val_loss: 2.2152)\n", + "\n", + "==== Epoch 5/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [01:55<00:00, 2.18s/it, loss=2.0409, acc=31.13%, f1=0.295, precision=0.298, recall=0.312]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.77it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 2.0304 | Train Metrics:\n", + " Acc: 31.13% | F1: 0.295 | Prec: 0.298 | Rec: 0.312\n", + "Val Loss: 1.9732 | Val Metrics:\n", + " Acc: 32.29% | F1: 0.295 | Prec: 0.319 | Rec: 0.319\n", + "Learning Rate: 0.001000\n", + "Saved best model to MLflow (val_loss: 1.9732)\n", + "\n", + "==== Epoch 6/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [02:07<00:00, 2.40s/it, loss=1.9124, acc=32.47%, f1=0.312, precision=0.316, recall=0.325]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.77it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.9774 | Train Metrics:\n", + " Acc: 32.47% | F1: 0.312 | Prec: 0.316 | Rec: 0.325\n", + "Val Loss: 2.7284 | Val Metrics:\n", + " Acc: 20.19% | F1: 0.159 | Prec: 0.269 | Rec: 0.203\n", + "Learning Rate: 0.001000\n", + "\n", + "==== Epoch 7/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [02:03<00:00, 2.33s/it, loss=2.0054, acc=34.30%, f1=0.330, precision=0.333, recall=0.344]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.76it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.9457 | Train Metrics:\n", + " Acc: 34.30% | F1: 0.330 | Prec: 0.333 | Rec: 0.344\n", + "Val Loss: 1.9710 | Val Metrics:\n", + " Acc: 32.06% | F1: 0.305 | Prec: 0.345 | Rec: 0.320\n", + "Learning Rate: 0.001000\n", + "Saved best model to MLflow (val_loss: 1.9710)\n", + "\n", + "==== Epoch 8/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [02:00<00:00, 2.27s/it, loss=1.7789, acc=35.38%, f1=0.342, precision=0.343, recall=0.353]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.76it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.8984 | Train Metrics:\n", + " Acc: 35.38% | F1: 0.342 | Prec: 0.343 | Rec: 0.353\n", + "Val Loss: 2.0149 | Val Metrics:\n", + " Acc: 30.56% | F1: 0.290 | Prec: 0.357 | Rec: 0.305\n", + "Learning Rate: 0.001000\n", + "\n", + "==== Epoch 9/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [01:54<00:00, 2.17s/it, loss=1.7931, acc=36.63%, f1=0.357, precision=0.359, recall=0.367]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.76it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.8645 | Train Metrics:\n", + " Acc: 36.63% | F1: 0.357 | Prec: 0.359 | Rec: 0.367\n", + "Val Loss: 2.1281 | Val Metrics:\n", + " Acc: 31.31% | F1: 0.286 | Prec: 0.378 | Rec: 0.302\n", + "Learning Rate: 0.001000\n", + "\n", + "==== Epoch 10/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [01:58<00:00, 2.24s/it, loss=1.9480, acc=36.93%, f1=0.359, precision=0.362, recall=0.369]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.77it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.8435 | Train Metrics:\n", + " Acc: 36.93% | F1: 0.359 | Prec: 0.362 | Rec: 0.369\n", + "Val Loss: 1.9602 | Val Metrics:\n", + " Acc: 34.50% | F1: 0.324 | Prec: 0.377 | Rec: 0.339\n", + "Learning Rate: 0.001000\n", + "Saved best model to MLflow (val_loss: 1.9602)\n", + "\n", + "==== Epoch 11/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [02:07<00:00, 2.41s/it, loss=1.8752, acc=37.65%, f1=0.369, precision=0.370, recall=0.377]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.77it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.8155 | Train Metrics:\n", + " Acc: 37.65% | F1: 0.369 | Prec: 0.370 | Rec: 0.377\n", + "Val Loss: 2.1909 | Val Metrics:\n", + " Acc: 28.74% | F1: 0.250 | Prec: 0.325 | Rec: 0.292\n", + "Learning Rate: 0.001000\n", + "\n", + "==== Epoch 12/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [02:03<00:00, 2.32s/it, loss=1.6982, acc=39.25%, f1=0.383, precision=0.385, recall=0.392]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.76it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.7840 | Train Metrics:\n", + " Acc: 39.25% | F1: 0.383 | Prec: 0.385 | Rec: 0.392\n", + "Val Loss: 1.9997 | Val Metrics:\n", + " Acc: 34.68% | F1: 0.322 | Prec: 0.382 | Rec: 0.345\n", + "Learning Rate: 0.001000\n", + "\n", + "==== Epoch 13/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [03:50<00:00, 4.34s/it, loss=1.9135, acc=39.48%, f1=0.386, precision=0.388, recall=0.394]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.77it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.7651 | Train Metrics:\n", + " Acc: 39.48% | F1: 0.386 | Prec: 0.388 | Rec: 0.394\n", + "Val Loss: 1.8517 | Val Metrics:\n", + " Acc: 36.98% | F1: 0.368 | Prec: 0.405 | Rec: 0.372\n", + "Learning Rate: 0.001000\n", + "Saved best model to MLflow (val_loss: 1.8517)\n", + "\n", + "==== Epoch 14/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [01:55<00:00, 2.18s/it, loss=1.8402, acc=40.50%, f1=0.397, precision=0.399, recall=0.405]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.78it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.7566 | Train Metrics:\n", + " Acc: 40.50% | F1: 0.397 | Prec: 0.399 | Rec: 0.405\n", + "Val Loss: 2.2230 | Val Metrics:\n", + " Acc: 28.96% | F1: 0.267 | Prec: 0.379 | Rec: 0.297\n", + "Learning Rate: 0.001000\n", + "\n", + "==== Epoch 15/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [01:54<00:00, 2.16s/it, loss=1.9603, acc=39.87%, f1=0.391, precision=0.391, recall=0.398]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.77it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.7600 | Train Metrics:\n", + " Acc: 39.87% | F1: 0.391 | Prec: 0.391 | Rec: 0.398\n", + "Val Loss: 1.8404 | Val Metrics:\n", + " Acc: 38.26% | F1: 0.364 | Prec: 0.405 | Rec: 0.385\n", + "Learning Rate: 0.001000\n", + "Saved best model to MLflow (val_loss: 1.8404)\n", + "\n", + "==== Epoch 16/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [01:55<00:00, 2.17s/it, loss=1.7567, acc=41.93%, f1=0.409, precision=0.411, recall=0.418]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.77it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.7178 | Train Metrics:\n", + " Acc: 41.93% | F1: 0.409 | Prec: 0.411 | Rec: 0.418\n", + "Val Loss: 1.7885 | Val Metrics:\n", + " Acc: 40.66% | F1: 0.387 | Prec: 0.419 | Rec: 0.406\n", + "Learning Rate: 0.001000\n", + "Saved best model to MLflow (val_loss: 1.7885)\n", + "\n", + "==== Epoch 17/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [01:55<00:00, 2.19s/it, loss=1.5358, acc=43.42%, f1=0.427, precision=0.429, recall=0.434]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.77it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.6744 | Train Metrics:\n", + " Acc: 43.42% | F1: 0.427 | Prec: 0.429 | Rec: 0.434\n", + "Val Loss: 2.0062 | Val Metrics:\n", + " Acc: 33.75% | F1: 0.314 | Prec: 0.399 | Rec: 0.336\n", + "Learning Rate: 0.001000\n", + "\n", + "==== Epoch 18/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [01:54<00:00, 2.16s/it, loss=1.7415, acc=43.68%, f1=0.429, precision=0.431, recall=0.436]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.77it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.6731 | Train Metrics:\n", + " Acc: 43.68% | F1: 0.429 | Prec: 0.431 | Rec: 0.436\n", + "Val Loss: 2.0740 | Val Metrics:\n", + " Acc: 33.17% | F1: 0.318 | Prec: 0.380 | Rec: 0.328\n", + "Learning Rate: 0.001000\n", + "\n", + "==== Epoch 19/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [02:04<00:00, 2.35s/it, loss=1.7529, acc=43.91%, f1=0.432, precision=0.432, recall=0.439]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.78it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.6495 | Train Metrics:\n", + " Acc: 43.91% | F1: 0.432 | Prec: 0.432 | Rec: 0.439\n", + "Val Loss: 1.9631 | Val Metrics:\n", + " Acc: 34.10% | F1: 0.330 | Prec: 0.398 | Rec: 0.347\n", + "Learning Rate: 0.001000\n", + "\n", + "==== Epoch 20/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [02:08<00:00, 2.42s/it, loss=1.7327, acc=44.24%, f1=0.435, precision=0.436, recall=0.442]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.77it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.6350 | Train Metrics:\n", + " Acc: 44.24% | F1: 0.435 | Prec: 0.436 | Rec: 0.442\n", + "Val Loss: 1.9353 | Val Metrics:\n", + " Acc: 37.87% | F1: 0.368 | Prec: 0.401 | Rec: 0.374\n", + "Learning Rate: 0.001000\n", + "\n", + "==== Epoch 21/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [02:03<00:00, 2.33s/it, loss=1.6361, acc=47.28%, f1=0.466, precision=0.468, recall=0.472]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.77it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.5702 | Train Metrics:\n", + " Acc: 47.28% | F1: 0.466 | Prec: 0.468 | Rec: 0.472\n", + "Val Loss: 1.7552 | Val Metrics:\n", + " Acc: 39.77% | F1: 0.374 | Prec: 0.398 | Rec: 0.393\n", + "Learning Rate: 0.000500\n", + "Saved best model to MLflow (val_loss: 1.7552)\n", + "\n", + "==== Epoch 22/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [01:54<00:00, 2.16s/it, loss=1.5475, acc=47.29%, f1=0.466, precision=0.467, recall=0.472]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.77it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.5411 | Train Metrics:\n", + " Acc: 47.29% | F1: 0.466 | Prec: 0.467 | Rec: 0.472\n", + "Val Loss: 1.9842 | Val Metrics:\n", + " Acc: 37.47% | F1: 0.350 | Prec: 0.424 | Rec: 0.369\n", + "Learning Rate: 0.000500\n", + "\n", + "==== Epoch 23/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [02:02<00:00, 2.32s/it, loss=1.5031, acc=48.69%, f1=0.479, precision=0.482, recall=0.486]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.77it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.5197 | Train Metrics:\n", + " Acc: 48.69% | F1: 0.479 | Prec: 0.482 | Rec: 0.486\n", + "Val Loss: 1.8240 | Val Metrics:\n", + " Acc: 41.05% | F1: 0.394 | Prec: 0.441 | Rec: 0.410\n", + "Learning Rate: 0.000500\n", + "\n", + "==== Epoch 24/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [01:57<00:00, 2.22s/it, loss=1.6117, acc=48.40%, f1=0.478, precision=0.480, recall=0.483]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.77it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.5159 | Train Metrics:\n", + " Acc: 48.40% | F1: 0.478 | Prec: 0.480 | Rec: 0.483\n", + "Val Loss: 1.7525 | Val Metrics:\n", + " Acc: 42.25% | F1: 0.406 | Prec: 0.447 | Rec: 0.418\n", + "Learning Rate: 0.000500\n", + "Saved best model to MLflow (val_loss: 1.7525)\n", + "\n", + "==== Epoch 25/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [02:36<00:00, 2.95s/it, loss=1.3989, acc=48.86%, f1=0.482, precision=0.484, recall=0.488]\n", + "Validation: 100%|██████████| 18/18 [00:11<00:00, 1.60it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.4940 | Train Metrics:\n", + " Acc: 48.86% | F1: 0.482 | Prec: 0.484 | Rec: 0.488\n", + "Val Loss: 2.1656 | Val Metrics:\n", + " Acc: 35.34% | F1: 0.353 | Prec: 0.428 | Rec: 0.358\n", + "Learning Rate: 0.000500\n", + "\n", + "==== Epoch 26/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [06:35<00:00, 7.45s/it, loss=1.4435, acc=50.48%, f1=0.498, precision=0.500, recall=0.504]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.76it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.4688 | Train Metrics:\n", + " Acc: 50.48% | F1: 0.498 | Prec: 0.500 | Rec: 0.504\n", + "Val Loss: 1.7489 | Val Metrics:\n", + " Acc: 41.85% | F1: 0.409 | Prec: 0.449 | Rec: 0.417\n", + "Learning Rate: 0.000500\n", + "Saved best model to MLflow (val_loss: 1.7489)\n", + "\n", + "==== Epoch 27/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [06:33<00:00, 7.42s/it, loss=1.3896, acc=50.29%, f1=0.497, precision=0.497, recall=0.502]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.76it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.4627 | Train Metrics:\n", + " Acc: 50.29% | F1: 0.497 | Prec: 0.497 | Rec: 0.502\n", + "Val Loss: 1.7198 | Val Metrics:\n", + " Acc: 44.82% | F1: 0.439 | Prec: 0.462 | Rec: 0.446\n", + "Learning Rate: 0.000500\n", + "Saved best model to MLflow (val_loss: 1.7198)\n", + "\n", + "==== Epoch 28/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [06:37<00:00, 7.51s/it, loss=1.3919, acc=51.41%, f1=0.510, precision=0.510, recall=0.514]\n", + "Validation: 100%|██████████| 18/18 [00:11<00:00, 1.60it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.4351 | Train Metrics:\n", + " Acc: 51.41% | F1: 0.510 | Prec: 0.510 | Rec: 0.514\n", + "Val Loss: 1.9794 | Val Metrics:\n", + " Acc: 36.58% | F1: 0.348 | Prec: 0.427 | Rec: 0.369\n", + "Learning Rate: 0.000500\n", + "\n", + "==== Epoch 29/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [06:41<00:00, 7.57s/it, loss=1.4895, acc=51.60%, f1=0.509, precision=0.512, recall=0.515]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.76it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.4249 | Train Metrics:\n", + " Acc: 51.60% | F1: 0.509 | Prec: 0.512 | Rec: 0.515\n", + "Val Loss: 1.7544 | Val Metrics:\n", + " Acc: 42.21% | F1: 0.403 | Prec: 0.428 | Rec: 0.423\n", + "Learning Rate: 0.000500\n", + "\n", + "==== Epoch 30/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [06:36<00:00, 7.49s/it, loss=1.4731, acc=51.54%, f1=0.509, precision=0.511, recall=0.515]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.66it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.4056 | Train Metrics:\n", + " Acc: 51.54% | F1: 0.509 | Prec: 0.511 | Rec: 0.515\n", + "Val Loss: 1.9902 | Val Metrics:\n", + " Acc: 40.39% | F1: 0.381 | Prec: 0.450 | Rec: 0.401\n", + "Learning Rate: 0.000500\n", + "\n", + "==== Epoch 31/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [06:42<00:00, 7.60s/it, loss=1.4422, acc=52.43%, f1=0.519, precision=0.520, recall=0.523]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.76it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.3897 | Train Metrics:\n", + " Acc: 52.43% | F1: 0.519 | Prec: 0.520 | Rec: 0.523\n", + "Val Loss: 1.9291 | Val Metrics:\n", + " Acc: 40.04% | F1: 0.385 | Prec: 0.443 | Rec: 0.397\n", + "Learning Rate: 0.000500\n", + "\n", + "==== Epoch 32/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [06:42<00:00, 7.59s/it, loss=1.4009, acc=55.41%, f1=0.549, precision=0.550, recall=0.553]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.69it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.3223 | Train Metrics:\n", + " Acc: 55.41% | F1: 0.549 | Prec: 0.550 | Rec: 0.553\n", + "Val Loss: 1.7432 | Val Metrics:\n", + " Acc: 46.32% | F1: 0.459 | Prec: 0.479 | Rec: 0.462\n", + "Learning Rate: 0.000250\n", + "\n", + "==== Epoch 33/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [06:39<00:00, 7.54s/it, loss=1.2094, acc=55.69%, f1=0.553, precision=0.555, recall=0.555]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.75it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.2941 | Train Metrics:\n", + " Acc: 55.69% | F1: 0.553 | Prec: 0.555 | Rec: 0.555\n", + "Val Loss: 1.7428 | Val Metrics:\n", + " Acc: 43.89% | F1: 0.423 | Prec: 0.451 | Rec: 0.435\n", + "Learning Rate: 0.000250\n", + "\n", + "==== Epoch 34/80 ====\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training: 100%|██████████| 53/53 [06:39<00:00, 7.53s/it, loss=1.3645, acc=57.00%, f1=0.566, precision=0.567, recall=0.570]\n", + "Validation: 100%|██████████| 18/18 [00:10<00:00, 1.75it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train Loss: 1.2686 | Train Metrics:\n", + " Acc: 57.00% | F1: 0.566 | Prec: 0.567 | Rec: 0.570\n", + "Val Loss: 1.9265 | Val Metrics:\n", + " Acc: 40.48% | F1: 0.382 | Prec: 0.439 | Rec: 0.397\n", + "Learning Rate: 0.000250\n", + "\n", + "Early stopping triggered after 34 epochs\n", + "Confusion matrix logged to MLflow\n", + "\n", + "MLflow run completed successfully!\n", + "Run ID: 5af2b34673d24fa2a94226e93ec9ed53\n", + "View at: http://localhost:5000/#/experiments/588266442625671109/runs/5af2b34673d24fa2a94226e93ec9ed53\n", + "🏃 View run gregarious-wolf-944 at: http://localhost:5000/#/experiments/588266442625671109/runs/5af2b34673d24fa2a94226e93ec9ed53\n", + "🧪 View experiment at: http://localhost:5000/#/experiments/588266442625671109\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Validation: 100%|██████████| 18/18 [00:11<00:00, 1.52it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== Final Test Results ===\n", + "Test Loss: 2.0044\n", + "Test Accuracy: 38.05%\n", + "Test F1: 0.372\n", + "Test Precision: 0.430\n", + "Test Recall: 0.386\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "execution_count": 26 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-10-12T23:59:55.702264Z", + "start_time": "2025-10-12T23:59:55.689625Z" + } + }, + "cell_type": "code", + "source": "", + "id": "280b7eb321f7a0b3", + "outputs": [], + "execution_count": null + }, + { + "cell_type": "code", + "id": "yb5wwiwsijk", + "source": [ + "# # Пример 1: Возобновление обучения с чекпоинта из предыдущего run\n", + "# # Замените 'your_run_id' на реальный ID run из MLflow\n", + "#\n", + "# # На данный момент необходимо, чтобы все базовые параметры для запуска обучения были такие-же как и при первом запуске\n", + "#\n", + "# PREVIOUS_RUN_ID = '0799928d2b70456c820abac3e9237a0d' # ID прерванного обучения\n", + "#\n", + "# # Создаем новый trainer (модель, оптимизатор и т.д. должны быть те же)\n", + "# model = AudioCNN(dropout_rate=0.3).to(DEVICE)\n", + "# optimizer = torch.optim.Adam(model.parameters(), lr=LEARNING_RATE)\n", + "# criterion = nn.CrossEntropyLoss()\n", + "# scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(\n", + "# optimizer, mode='min', factor=0.5, patience=3, min_lr=1e-6\n", + "# )\n", + "#\n", + "# trainer = Trainer(model, optimizer, criterion, scheduler, TRAINER_CONFIG)\n", + "#\n", + "# # Возобновляем обучение из чекпоинта\n", + "# history = trainer.train(\n", + "# train_loader,\n", + "# val_loader,\n", + "# resume_from_mlflow_run=PREVIOUS_RUN_ID\n", + "# )\n", + "#\n", + "# print(\"Чтобы возобновить обучение, раскомментируйте код выше и укажите run_id\")" + ], + "metadata": { + "ExecuteTime": { + "end_time": "2025-10-12T23:59:55.745936Z", + "start_time": "2025-10-12T23:59:55.732256Z" + } + }, + "outputs": [], + "execution_count": 27 + }, + { + "cell_type": "code", + "id": "f3n5esj9cph", + "source": "# Пример 2: Как найти run_id для возобновления обучения\n\n# import mlflow\n# from mlflow.tracking import MlflowClient\n# \n# client = MlflowClient()\n# \n# # Получаем последние runs из эксперимента\n# experiment_name = TRAINER_CONFIG['mlflow_experiment_name']\n# experiment = client.get_experiment_by_name(experiment_name)\n# \n# if experiment:\n# runs = client.search_runs(\n# experiment_ids=[experiment.experiment_id],\n# order_by=[\"start_time DESC\"],\n# max_results=5\n# )\n# \n# print(f\"Последние 5 runs для эксперимента '{experiment_name}':\\n\")\n# for i, run in enumerate(runs, 1):\n# status = run.info.status\n# run_id = run.info.run_id\n# run_name = run.data.tags.get('mlflow.runName', 'N/A')\n# start_time = run.info.start_time\n# \n# # Получаем метрики если есть\n# metrics = run.data.metrics\n# val_loss = metrics.get('best_val_loss', 'N/A')\n# \n# print(f\"{i}. Run ID: {run_id}\")\n# print(f\" Name: {run_name}\")\n# print(f\" Status: {status}\")\n# print(f\" Best Val Loss: {val_loss}\")\n# print(f\" Started: {start_time}\")\n# \n# # Проверяем наличие чекпоинта\n# try:\n# artifacts = client.list_artifacts(run_id, \"checkpoints\")\n# checkpoint_files = [a.path for a in artifacts if a.path.endswith('.pt')]\n# if checkpoint_files:\n# print(f\" ✓ Checkpoints: {checkpoint_files}\")\n# else:\n# print(f\" ✗ No checkpoints found\")\n# except:\n# print(f\" ✗ No checkpoints\")\n# \n# print()\n\nprint(\"Раскомментируйте код выше, чтобы найти доступные runs с чекпоинтами\")", + "metadata": { + "ExecuteTime": { + "end_time": "2025-10-12T23:59:55.789862Z", + "start_time": "2025-10-12T23:59:55.774465Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Раскомментируйте код выше, чтобы найти доступные runs с чекпоинтами\n" + ] + } + ], + "execution_count": 28 + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/ml/training_preprocessed.py b/ml/training_preprocessed.py new file mode 100644 index 0000000..5934cb8 --- /dev/null +++ b/ml/training_preprocessed.py @@ -0,0 +1,254 @@ +""" +Training script using preprocessed AudioSet data from HDF5. +This is a Python script version that can be used standalone or converted to notebook. +""" + +import numpy as np +import pandas as pd +import mlflow +import matplotlib.pyplot as plt + +import torch +import torch.nn as nn +import torch.nn.functional as F +from torch.utils.data import DataLoader + +import os +from pathlib import Path +from tqdm import tqdm +from datetime import datetime + +# Import modules +from anomaly_classifier_architecture import AudioCNN +from preprocessed_dataset import PreprocessedAudioSetDataset +from training import Trainer # Assuming Trainer class is in training module +import cfg + + +# ============================================================================ +# CONFIGURATION +# ============================================================================ + +TRAINER_CONFIG = { + # Обязательные параметры + "mlflow_run_name": "preprocessed_run", + + # Базовые параметры обучения + "device": "cuda" if torch.cuda.is_available() else "cpu", + "learning_rate": 0.001, + "batch_size": 128, # Can be increased since I/O is faster + "epochs": 10, + "early_stop_patience": 7, + "gradient_clip_max_norm": 1.0, + + # Параметры метрик + "metrics_average": "macro", + "compute_confusion_matrix": True, + "compute_per_class_accuracy": True, + + # Пути для сохранения + "save_all_checkpoints": False, + "save_frequency": 10, + + # MLflow параметры + "mlflow_experiment_name": "Anomaly_Classifier_Exp", + "mlflow_tracking_uri": "http://localhost:5000", + "mlflow_tags": {"data_source": "preprocessed_hdf5"}, +} + +BATCH_SIZE = TRAINER_CONFIG["batch_size"] +LEARNING_RATE = TRAINER_CONFIG["learning_rate"] +EPOCHS = TRAINER_CONFIG["epochs"] +DEVICE = TRAINER_CONFIG["device"] + +# Path to preprocessed HDF5 file +HDF5_PATH = './data/audioset_preprocessed.h5' + +print(f'Using device: {DEVICE}') + + +# ============================================================================ +# LOAD PREPROCESSED DATASETS +# ============================================================================ + +print("\n" + "=" * 70) +print("Loading preprocessed datasets from HDF5") +print("=" * 70) + +# Load datasets (no transforms needed - already preprocessed!) +train_dataset = PreprocessedAudioSetDataset( + hdf5_path=HDF5_PATH, + split='train', + cache_in_memory=False, # Set to True if you have enough RAM + transform=None # No transforms - data already preprocessed +) + +val_dataset = PreprocessedAudioSetDataset( + hdf5_path=HDF5_PATH, + split='val', + cache_in_memory=False, + transform=None +) + +test_dataset = PreprocessedAudioSetDataset( + hdf5_path=HDF5_PATH, + split='test', + cache_in_memory=False, + transform=None +) + +# Print dataset info +train_dataset.print_info() + +print("\n" + "=" * 70) +print("Creating DataLoaders") +print("=" * 70) + +# Create DataLoaders with increased num_workers for faster loading +train_loader = DataLoader( + train_dataset, + batch_size=BATCH_SIZE, + shuffle=True, + num_workers=4, # Increase if you have more CPU cores + pin_memory=True if DEVICE == 'cuda' else False, # Speeds up GPU transfer + persistent_workers=True # Keep workers alive between epochs +) + +val_loader = DataLoader( + val_dataset, + batch_size=BATCH_SIZE, + shuffle=False, + num_workers=4, + pin_memory=True if DEVICE == 'cuda' else False, + persistent_workers=True +) + +test_loader = DataLoader( + test_dataset, + batch_size=BATCH_SIZE, + shuffle=False, + num_workers=2, + pin_memory=True if DEVICE == 'cuda' else False +) + +print(f"Train loader: {len(train_loader)} batches ({len(train_dataset)} samples)") +print(f"Val loader: {len(val_loader)} batches ({len(val_dataset)} samples)") +print(f"Test loader: {len(test_loader)} batches ({len(test_dataset)} samples)") + + +# ============================================================================ +# MODEL SETUP +# ============================================================================ + +print("\n" + "=" * 70) +print("Setting up model") +print("=" * 70) + +# Model +model = AudioCNN(dropout_rate=0.3).to(DEVICE) +optimizer = torch.optim.Adam(model.parameters(), lr=LEARNING_RATE) +criterion = nn.CrossEntropyLoss() +scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau( + optimizer, + mode='min', + factor=0.5, + patience=3, + min_lr=1e-6, +) + +print(f"Model: AudioCNN") +print(f"Optimizer: Adam (lr={LEARNING_RATE})") +print(f"Loss: CrossEntropyLoss") +print(f"Scheduler: ReduceLROnPlateau") + + +# ============================================================================ +# TRAINING +# ============================================================================ + +print("\n" + "=" * 70) +print("Starting training") +print("=" * 70) + +# Create trainer +trainer = Trainer(model, optimizer, criterion, scheduler, TRAINER_CONFIG) + +# Train model +history = trainer.train(train_loader, val_loader) + + +# ============================================================================ +# TESTING +# ============================================================================ + +print("\n" + "=" * 70) +print("Evaluating on test set") +print("=" * 70) + +test_loss, test_metrics = trainer.validate(test_loader) + +print(f'\n=== Final Test Results ===') +print(f'Test Loss: {test_loss:.4f}') +print(f'Test Accuracy: {test_metrics["accuracy"]*100:.2f}%') +print(f'Test F1: {test_metrics["f1"]:.3f}') +print(f'Test Precision: {test_metrics["precision"]:.3f}') +print(f'Test Recall: {test_metrics["recall"]:.3f}') + + +# ============================================================================ +# OPTIONAL: PLOT TRAINING HISTORY +# ============================================================================ + +def plot_training_history(history): + """Plot training history""" + fig, axes = plt.subplots(2, 2, figsize=(15, 10)) + + # Loss + axes[0, 0].plot(history['train_loss'], label='Train Loss') + axes[0, 0].plot(history['val_loss'], label='Val Loss') + axes[0, 0].set_xlabel('Epoch') + axes[0, 0].set_ylabel('Loss') + axes[0, 0].set_title('Loss over Epochs') + axes[0, 0].legend() + axes[0, 0].grid(True) + + # Accuracy + axes[0, 1].plot(history['train_acc'], label='Train Acc') + axes[0, 1].plot(history['val_acc'], label='Val Acc') + axes[0, 1].set_xlabel('Epoch') + axes[0, 1].set_ylabel('Accuracy') + axes[0, 1].set_title('Accuracy over Epochs') + axes[0, 1].legend() + axes[0, 1].grid(True) + + # F1 Score + axes[1, 0].plot(history['train_f1'], label='Train F1') + axes[1, 0].plot(history['val_f1'], label='Val F1') + axes[1, 0].set_xlabel('Epoch') + axes[1, 0].set_ylabel('F1 Score') + axes[1, 0].set_title('F1 Score over Epochs') + axes[1, 0].legend() + axes[1, 0].grid(True) + + # Learning Rate + axes[1, 1].plot(history['lr'], label='Learning Rate') + axes[1, 1].set_xlabel('Epoch') + axes[1, 1].set_ylabel('Learning Rate') + axes[1, 1].set_title('Learning Rate over Epochs') + axes[1, 1].set_yscale('log') + axes[1, 1].legend() + axes[1, 1].grid(True) + + plt.tight_layout() + plt.savefig('./training_results/training_history_preprocessed.png', dpi=150) + print("Training history plot saved to: ./training_results/training_history_preprocessed.png") + +# Plot history +try: + plot_training_history(history) +except Exception as e: + print(f"Could not plot history: {e}") + +print("\n" + "=" * 70) +print("Training complete!") +print("=" * 70) diff --git a/ml/training_results/confusion_matrix.png b/ml/training_results/confusion_matrix.png new file mode 100644 index 0000000..1fe87c3 Binary files /dev/null and b/ml/training_results/confusion_matrix.png differ diff --git a/ml/training_results/training_history_20250922_210451.png b/ml/training_results/training_history_20250922_210451.png new file mode 100644 index 0000000..32a696c Binary files /dev/null and b/ml/training_results/training_history_20250922_210451.png differ diff --git a/ml/training_results/training_history_20250922_210811.png b/ml/training_results/training_history_20250922_210811.png new file mode 100644 index 0000000..153355c Binary files /dev/null and b/ml/training_results/training_history_20250922_210811.png differ diff --git a/ml/training_results/training_history_20250922_210906.png b/ml/training_results/training_history_20250922_210906.png new file mode 100644 index 0000000..142a970 Binary files /dev/null and b/ml/training_results/training_history_20250922_210906.png differ diff --git a/ml/training_results/training_history_20250922_212811.png b/ml/training_results/training_history_20250922_212811.png new file mode 100644 index 0000000..acaf02c Binary files /dev/null and b/ml/training_results/training_history_20250922_212811.png differ diff --git a/ml/training_results/training_history_20250923_173334.png b/ml/training_results/training_history_20250923_173334.png new file mode 100644 index 0000000..b4e4f47 Binary files /dev/null and b/ml/training_results/training_history_20250923_173334.png differ diff --git a/ml/training_results/training_history_20250929_204002.png b/ml/training_results/training_history_20250929_204002.png new file mode 100644 index 0000000..3a325a7 Binary files /dev/null and b/ml/training_results/training_history_20250929_204002.png differ diff --git a/ml/training_results/training_history_20250929_205902.png b/ml/training_results/training_history_20250929_205902.png new file mode 100644 index 0000000..5ed5901 Binary files /dev/null and b/ml/training_results/training_history_20250929_205902.png differ