Download ml/tests/test_sequence_vae.py from PRiyanshu0-1/TrackChain: direct link, hf CLI and curl.
- Browser
- Download file 4.33 kB
-
https://huggingface.co/PRiyanshu0-1/TrackChain/resolve/main/ml/tests/test_sequence_vae.py
- Command line
-
hf download hf://PRiyanshu0-1/TrackChain/ml/tests/test_sequence_vae.py
-
curl -L -o test_sequence_vae.py https://huggingface.co/PRiyanshu0-1/TrackChain/resolve/main/ml/tests/test_sequence_vae.py
4.33 kB
| """ | |
| ml/tests/test_sequence_vae.py | |
| Tests for Phase 2.5: 1D-CNN Sequence VAE for Novel Track Geometry Anomaly Detection. | |
| """ | |
| import pytest | |
| import torch | |
| import numpy as np | |
| from ml.models.geometry.sequence_vae import SequenceVAE, SequenceVAEDetector | |
| from ml.core.schema import SignalType, DefectClass, CalibratedSignal | |
| from ml.data.synthetic_geometry import SyntheticGeometryDataset, GeometryFaultType | |
| def test_sequence_vae_1d_cnn_architecture(): | |
| """Verify 1D-CNN encoder, latent bottleneck (16), decoder transpose, and output shapes.""" | |
| batch_size = 8 | |
| seq_len = 80 | |
| n_features = 5 | |
| latent_dim = 16 | |
| model = SequenceVAE(seq_len=seq_len, n_features=n_features, latent_dim=latent_dim) | |
| x = torch.randn(batch_size, seq_len, n_features) | |
| recon_x, mu, logvar = model(x) | |
| assert recon_x.shape == (batch_size, seq_len, n_features) | |
| assert mu.shape == (batch_size, latent_dim) | |
| assert logvar.shape == (batch_size, latent_dim) | |
| def test_sequence_vae_loss_and_beta(): | |
| """Verify Beta-VAE loss function computation (beta=0.01).""" | |
| model = SequenceVAE(seq_len=80, n_features=5, latent_dim=16) | |
| x = torch.randn(4, 80, 5) | |
| recon_x, mu, logvar = model(x) | |
| total_loss, recon_loss, kld_loss = model.loss_function(recon_x, x, mu, logvar, beta=0.01) | |
| assert total_loss.item() > 0.0 | |
| assert recon_loss.item() > 0.0 | |
| assert kld_loss.item() >= 0.0 | |
| assert torch.isclose(total_loss, recon_loss + 0.01 * kld_loss, atol=1e-5) | |
| def test_sequence_vae_anomaly_reconstruction_sensitivity(): | |
| """ | |
| Train a tiny Sequence VAE on normal track and verify that it achieves | |
| much lower reconstruction error on normal test samples than on injected anomalies. | |
| """ | |
| torch.manual_seed(42) | |
| model = SequenceVAE(seq_len=80, n_features=5, latent_dim=16) | |
| optimizer = torch.optim.Adam(model.parameters(), lr=0.01) | |
| # 1. Synthesize normal sequences | |
| normal_data = torch.randn(60, 80, 5) * 0.3 # Low amplitude noise | |
| # Quick train on normal data | |
| model.train() | |
| for _ in range(30): | |
| optimizer.zero_grad() | |
| recon, mu, logvar = model(normal_data) | |
| loss, _, _ = model.loss_function(recon, normal_data, mu, logvar, beta=0.01) | |
| loss.backward() | |
| optimizer.step() | |
| # 2. Test error on normal sequence vs extreme harmonic anomaly | |
| model.eval() | |
| with torch.no_grad(): | |
| clean_test = torch.randn(1, 80, 5) * 0.3 | |
| err_clean = model.compute_anomaly_score(clean_test).item() | |
| # Injected anomaly: massive 10mm sine wave across features | |
| anomalous_test = clean_test.clone() | |
| x_axis = torch.linspace(0, 20, 80) | |
| anomalous_test[0, :, 3] += 8.0 * torch.sin(x_axis) | |
| err_anomaly = model.compute_anomaly_score(anomalous_test).item() | |
| assert err_anomaly > err_clean * 3.0, f"Expected anomaly error ({err_anomaly}) >> clean error ({err_clean})" | |
| def test_sequence_vae_detector_calibration_and_contract(): | |
| """Verify SequenceVAEDetector predict method and tc.v1 CalibratedSignal compliance.""" | |
| detector = SequenceVAEDetector(weights_path=None) | |
| # Generate synthetic validation normal data to fit calibration | |
| normal_windows = np.random.randn(50, 80, 5).astype(np.float32) * 0.5 | |
| thresh = detector.fit_calibration(normal_windows, percentile=95.0) | |
| assert thresh > 0.0 | |
| # 1. Predict on normal window | |
| signal_normal = detector.predict(normal_windows[0]) | |
| assert isinstance(signal_normal, CalibratedSignal) | |
| assert signal_normal.signal_type == SignalType.GEOMETRY_NOVEL | |
| assert signal_normal.stream_name == "geometry_vae" | |
| assert 0.0 <= signal_normal.calibrated_prob <= 1.0 | |
| assert signal_normal.raw_score >= 0.0 | |
| assert "reconstruction_error" in signal_normal.explanation | |
| # 2. Predict on dictionary feature input | |
| dict_in = { | |
| "twist_3m_mm": np.zeros(80), | |
| "versine_10m_mm": np.zeros(80), | |
| "versine_20m_mm": np.zeros(80), | |
| "unevenness_10m_mm": np.full(80, 10.0), # Extreme offset | |
| "cant_mm": np.zeros(80), | |
| } | |
| signal_anomaly = detector.predict(dict_in) | |
| assert isinstance(signal_anomaly, CalibratedSignal) | |
| assert signal_anomaly.signal_type == SignalType.GEOMETRY_NOVEL | |
| assert signal_anomaly.is_anomaly is True | |
| assert signal_anomaly.predicted_class == DefectClass.GEOMETRY_ANOMALY | |