6.4 KiB
6.4 KiB
In [ ]:
from pathlib import Path
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
TRAIN_PATH = Path("data/nsl_kdd_train.csv")
TEST_PATH = Path("data/nsl_kdd_test.csv")
missing_files = [str(path) for path in (TRAIN_PATH, TEST_PATH) if not path.exists()]
if missing_files:
raise FileNotFoundError(
"Missing prepared NSL-KDD files: " + ", ".join(missing_files) +
". Follow data/README.md and run: python data/prepare_dataset.py"
)
TARGET_COLUMNS = ["attack_label", "attack_group", "binary_label", "difficulty"]
CATEGORICAL_FEATURES = ["protocol_type", "service", "flag"]
from sklearn.ensemble import IsolationForest
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import OneClassSVM
from sklearn.metrics import (
accuracy_score, average_precision_score, confusion_matrix,
f1_score, precision_score, recall_score,
)
def security_metrics(y_true, y_pred):
tn, fp, fn, tp = confusion_matrix(y_true, y_pred, labels=[0, 1]).ravel()
return {
"accuracy": accuracy_score(y_true, y_pred),
"attack_precision": precision_score(y_true, y_pred, zero_division=0),
"attack_recall": recall_score(y_true, y_pred, zero_division=0),
"attack_f1": f1_score(y_true, y_pred, zero_division=0),
"false_positives": int(fp),
"false_negatives": int(fn),
"false_positive_rate": fp / (fp + tn) if fp + tn else 0.0,
"alerts_per_10000": 10000 * (fp + tp) / len(y_true),
}In [ ]:
# TODO 1: load data, define anomaly_features, split normal training rows,
# and cap the normal fit sample at 5,000 records.
train_df = None
test_df = None
anomaly_features = None
normal_fit = None
normal_valid = None
X_test = None
y_test = NoneIn [ ]:
# TODO 2: build StandardScaler pipelines for OneClassSVM and IsolationForest.
detectors = None
# TODO 3: fit both detectors on normal_fit only.
print("Fitted detectors:", None)In [ ]:
# TODO 4: for each detector, negate decision_function so larger means
# more anomalous, choose the 99th normal-validation percentile, and evaluate.
detector_outputs = None
detector_comparison = None
display(detector_comparison)In [ ]:
# TODO 5: calculate recall separately for DoS, Probe, R2L, and U2R.
group_recall = None
display(group_recall)In [ ]:
assert set(detectors) == {"One-Class SVM", "Isolation Forest"}
assert len(normal_fit) <= 5000
assert all(len(output["test_scores"]) == len(test_df) for output in detector_outputs.values())
assert detector_comparison.shape[0] == 2
assert set(group_recall.index) == {"DoS", "Probe", "R2L", "U2R"}
assert detector_comparison["false_positive_rate"].between(0, 1).all()
print("✓ Lab 4 completed")