Files
labs/Lab-6/Lab-6-Practical/01_NSL_KDD_Flow_Audit_Lab.ipynb
T

5.9 KiB

Module 6 — Lab 1: Auditing NSL-KDD Flow Records

Topics: network flows, fixed split, labels, attack groups, leakage

Lab version: Complete every TODO, run the cells in order, and use the self-check before opening the Solution.

Learning objectives

After completing this lab, you will be able to:

  • distinguish network-flow features from targets and audit fields;
  • preserve the predefined NSL-KDD train/test split;
  • describe binary labels and the DoS, Probe, R2L, and U2R groups;
  • identify attack labels that appear only in the test set;
  • document imbalance, duplicates, and benchmark limitations.
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"]

1. Load and validate the fixed split

In [ ]:
# TODO 1: load the prepared train and test tables.
train_df = None
test_df = None

# TODO 2: create feature_cols and numeric_features without target/audit columns.
feature_cols = None
numeric_features = None

print("Train shape:", None)
print("Test shape:", None)
print("Model features:", None)

2. Audit classes, groups, and unseen attack labels

In [ ]:
# TODO 3: calculate binary-label and attack-group counts for both splits.
train_binary = None
test_binary = None
train_groups = None
test_groups = None

# TODO 4: find attack labels present in test but absent from training.
train_attack_labels = None
test_attack_labels = None
unseen_test_attacks = None

display(pd.DataFrame({"train": train_binary, "test": test_binary}))
print("Attack labels seen only in test:", unseen_test_attacks)

3. Audit quality and leakage risks

In [ ]:
# TODO 5: build a per-feature table of missing counts and unique counts.
quality_summary = None

# TODO 6: count exact duplicate rows separately in train and test.
duplicate_counts = None

# TODO 7: list target/audit columns that must never enter the model.
forbidden_model_columns = None
print(duplicate_counts)
print("Columns excluded from modeling:", forbidden_model_columns)

Benchmark note

NSL-KDD is useful for learning a reproducible IDS workflow. Its fixed test set includes attacks that are absent from training, but it remains a legacy benchmark and does not represent modern production traffic.

Self-check

In [ ]:
assert len(feature_cols) == 41
assert set(CATEGORICAL_FEATURES).issubset(feature_cols)
assert set(train_df["binary_label"].unique()) == {0, 1}
assert set(test_df["attack_group"].unique()).issubset({"Normal", "DoS", "Probe", "R2L", "U2R"})
assert len(unseen_test_attacks) > 0
assert set(feature_cols).isdisjoint(TARGET_COLUMNS)
assert quality_summary.shape[0] == 41
print("✓ Lab 1 completed")

Reflection questions

  1. Why should the predefined train/test split be preserved?
  2. Why must difficulty and derived label columns be excluded from X?
  3. Why can binary accuracy hide failure on rare attack groups?