| """ |
| Deep Learning Models for Anomaly Detection |
| LSTM Autoencoder and Transformer-based models |
| """ |
|
|
| import numpy as np |
| import pandas as pd |
| import tensorflow as tf |
| from tensorflow import keras |
| from tensorflow.keras import layers, Model |
| from sklearn.preprocessing import StandardScaler |
| from sklearn.metrics import ( |
| confusion_matrix, precision_score, recall_score, f1_score, |
| roc_auc_score, roc_curve, auc |
| ) |
| import matplotlib.pyplot as plt |
| import os |
| from baseline_models import FeatureEngineer, evaluate_model |
|
|
|
|
| class LSTMAutoencoder(keras.Model): |
| """LSTM-based autoencoder for sequence anomaly detection.""" |
|
|
| def __init__(self, input_shape, latent_dim=16): |
| super(LSTMAutoencoder, self).__init__() |
| self.latent_dim = latent_dim |
| self.input_shape_model = input_shape |
|
|
| |
| self.encoder = keras.Sequential([ |
| layers.LSTM(64, activation='relu', input_shape=input_shape), |
| layers.Dense(latent_dim, activation='relu') |
| ]) |
|
|
| |
| self.decoder = keras.Sequential([ |
| layers.RepeatVector(input_shape[0]), |
| layers.LSTM(64, activation='relu', return_sequences=True), |
| layers.TimeDistributed(layers.Dense(input_shape[1])) |
| ]) |
|
|
| def call(self, x): |
| encoded = self.encoder(x) |
| decoded = self.decoder(encoded) |
| return decoded |
|
|
|
|
| class TransformerAnomalyDetector(keras.Model): |
| """Transformer-based model for sequence anomaly detection.""" |
|
|
| def __init__(self, input_shape, num_heads=4, ff_dim=32): |
| super(TransformerAnomalyDetector, self).__init__() |
| self.input_shape_model = input_shape |
|
|
| |
| self.embedding = layers.Dense(32) |
|
|
| |
| self.attention = layers.MultiHeadAttention( |
| num_heads=num_heads, |
| key_dim=32 // num_heads |
| ) |
|
|
| |
| self.ffn = keras.Sequential([ |
| layers.Dense(ff_dim, activation='relu'), |
| layers.Dense(32) |
| ]) |
|
|
| self.layernorm1 = layers.LayerNormalization(epsilon=1e-6) |
| self.layernorm2 = layers.LayerNormalization(epsilon=1e-6) |
|
|
| |
| self.global_pool = layers.GlobalAveragePooling1D() |
| self.dense1 = layers.Dense(16, activation='relu') |
| self.dense2 = layers.Dense(1, activation='sigmoid') |
|
|
| def call(self, x): |
| |
| x_embedded = self.embedding(x) |
|
|
| |
| attn_output = self.attention(x_embedded, x_embedded) |
| x_attn = self.layernorm1(x_embedded + attn_output) |
|
|
| |
| ffn_output = self.ffn(x_attn) |
| x_ffn = self.layernorm2(x_attn + ffn_output) |
|
|
| |
| pooled = self.global_pool(x_ffn) |
| dense1_out = self.dense1(pooled) |
| output = self.dense2(dense1_out) |
|
|
| return output |
|
|
|
|
| def create_sequences(data, labels, sequence_length=10): |
| """Create sliding window sequences from data.""" |
| X, y = [], [] |
| for i in range(len(data) - sequence_length + 1): |
| X.append(data[i:i + sequence_length]) |
| |
| y.append(1 if labels[i:i + sequence_length].sum() > 0 else 0) |
| return np.array(X), np.array(y) |
|
|
|
|
| def train_lstm_autoencoder(X_train, X_test, y_test, sequence_length=10, epochs=20): |
| """Train LSTM autoencoder.""" |
| print("\n" + "="*60) |
| print("Training LSTM Autoencoder") |
| print("="*60) |
|
|
| model = LSTMAutoencoder(input_shape=(sequence_length, X_train.shape[2]), latent_dim=16) |
| model.compile(optimizer='adam', loss='mse') |
|
|
| |
| normal_idx = (y_train == 0) |
| X_normal = X_train[normal_idx] |
|
|
| history = model.fit( |
| X_normal, X_normal, |
| epochs=epochs, |
| batch_size=32, |
| validation_split=0.1, |
| verbose=0 |
| ) |
|
|
| |
| X_test_recon = model.predict(X_test, verbose=0) |
| reconstruction_error = np.mean(np.abs(X_test - X_test_recon), axis=(1, 2)) |
|
|
| |
| threshold = np.percentile(reconstruction_error[y_test == 0], 95) |
| y_pred = (reconstruction_error > threshold).astype(int) |
|
|
| metrics = evaluate_model(y_test, y_pred, "LSTM Autoencoder") |
| return model, metrics, reconstruction_error, threshold |
|
|
|
|
| def train_transformer(X_train, X_test, y_test, sequence_length=10, epochs=20): |
| """Train Transformer-based model.""" |
| print("\n" + "="*60) |
| print("Training Transformer Model") |
| print("="*60) |
|
|
| model = TransformerAnomalyDetector( |
| input_shape=(sequence_length, X_train.shape[2]), |
| num_heads=4, |
| ff_dim=32 |
| ) |
|
|
| model.compile( |
| optimizer='adam', |
| loss='binary_crossentropy', |
| metrics=['binary_accuracy'] |
| ) |
|
|
| |
| history = model.fit( |
| X_train, y_train, |
| epochs=epochs, |
| batch_size=32, |
| validation_split=0.1, |
| verbose=0 |
| ) |
|
|
| |
| y_pred_prob = model.predict(X_test, verbose=0).flatten() |
| y_pred = (y_pred_prob > 0.5).astype(int) |
|
|
| metrics = evaluate_model(y_test, y_pred, "Transformer Model") |
| return model, metrics, y_pred_prob |
|
|
|
|
| def main(): |
| """Train and evaluate deep learning models.""" |
| print("Loading training data...") |
| train_df = pd.read_csv('data/train_logs.csv') |
| test_df = pd.read_csv('data/test_logs.csv') |
|
|
| |
| print("Feature engineering...") |
| fe = FeatureEngineer() |
| train_features = fe.fit_transform(train_df) |
| test_features = fe.transform(test_df) |
|
|
| |
| X_train_raw = train_features[fe.feature_cols].fillna(0).values |
| y_train_raw = train_features['is_anomaly'].values |
|
|
| X_test_raw = test_features[fe.feature_cols].fillna(0).values |
| y_test_raw = test_features['is_anomaly'].values |
|
|
| |
| scaler = StandardScaler() |
| X_train_scaled = scaler.fit_transform(X_train_raw) |
| X_test_scaled = scaler.transform(X_test_raw) |
|
|
| |
| sequence_length = 10 |
| print(f"\nCreating sequences (window size: {sequence_length})...") |
| X_train_seq, y_train_seq = create_sequences(X_train_scaled, y_train_raw, sequence_length) |
| X_test_seq, y_test_seq = create_sequences(X_test_scaled, y_test_raw, sequence_length) |
|
|
| print(f"Train sequences: {X_train_seq.shape}") |
| print(f"Test sequences: {X_test_seq.shape}") |
| print(f"Positive samples in train: {y_train_seq.sum()} ({y_train_seq.mean()*100:.1f}%)") |
| print(f"Positive samples in test: {y_test_seq.sum()} ({y_test_seq.mean()*100:.1f}%)") |
|
|
| |
| os.makedirs('models', exist_ok=True) |
| os.makedirs('results', exist_ok=True) |
|
|
| results = {} |
|
|
| |
| lstm_model, lstm_metrics, _, _ = train_lstm_autoencoder( |
| X_train_seq, X_test_seq, y_test_seq, |
| sequence_length=sequence_length, |
| epochs=20 |
| ) |
| results['lstm_autoencoder'] = lstm_metrics |
| lstm_model.save('models/lstm_autoencoder.h5') |
| print("✓ LSTM model saved") |
|
|
| |
| transformer_model, transformer_metrics, _ = train_transformer( |
| X_train_seq, X_test_seq, y_test_seq, |
| sequence_length=sequence_length, |
| epochs=20 |
| ) |
| results['transformer'] = transformer_metrics |
| transformer_model.save('models/transformer_model.h5') |
| print("✓ Transformer model saved") |
|
|
| |
| results_df = pd.DataFrame(results).T |
| results_df.to_csv('results/deep_learning_results.csv') |
| print("\n✓ Results saved to results/deep_learning_results.csv") |
|
|
| |
| print("\n" + "="*60) |
| print("Model Comparison Summary") |
| print("="*60) |
| print(results_df[['precision', 'recall', 'f1']]) |
|
|
| return lstm_model, transformer_model, results |
|
|
|
|
| if __name__ == '__main__': |
| main() |
|
|