CSAI2026 labs: Lab-2..Lab-6 (Practical + Solution, data), Module-2..Module-6 notebooks and datasets
This commit is contained in:
@@ -0,0 +1,288 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "cell-001",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Module 6 — Lab 6: Capstone — Hybrid Network Intrusion Triage\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"**Topics:** classifier, anomaly score, hybrid policy, alert budget, SOC report\n",
|
||||
"\n",
|
||||
"> **Lab version:** Complete every `TODO`, run the cells in order, and use the self-check before opening the Solution."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "cell-002",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Mission\n",
|
||||
"\n",
|
||||
"Compare three alert policies on the fixed NSL-KDD test set:\n",
|
||||
"\n",
|
||||
"1. a supervised Decision Tree for known attack patterns;\n",
|
||||
"2. a normal-only Isolation Forest for anomaly hunting;\n",
|
||||
"3. a hybrid OR policy that alerts when either detector fires.\n",
|
||||
"\n",
|
||||
"**Required outputs**\n",
|
||||
"\n",
|
||||
"- `output/hybrid_ids_metrics.csv`\n",
|
||||
"- `output/hybrid_ids_group_recall.csv`\n",
|
||||
"- `output/hybrid_ids_top_alerts.csv`\n",
|
||||
"- `output/hybrid_ids_confusion_matrix.png`\n",
|
||||
"- `output/hybrid_ids_score_distribution.png`\n",
|
||||
"- `output/hybrid_ids_analyst_summary.md`"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "cell-003",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from pathlib import Path\n",
|
||||
"import numpy as np\n",
|
||||
"import pandas as pd\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"\n",
|
||||
"TRAIN_PATH = Path(\"data/nsl_kdd_train.csv\")\n",
|
||||
"TEST_PATH = Path(\"data/nsl_kdd_test.csv\")\n",
|
||||
"\n",
|
||||
"missing_files = [str(path) for path in (TRAIN_PATH, TEST_PATH) if not path.exists()]\n",
|
||||
"if missing_files:\n",
|
||||
" raise FileNotFoundError(\n",
|
||||
" \"Missing prepared NSL-KDD files: \" + \", \".join(missing_files) +\n",
|
||||
" \". Follow data/README.md and run: python data/prepare_dataset.py\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"TARGET_COLUMNS = [\"attack_label\", \"attack_group\", \"binary_label\", \"difficulty\"]\n",
|
||||
"CATEGORICAL_FEATURES = [\"protocol_type\", \"service\", \"flag\"]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"from sklearn.compose import ColumnTransformer\n",
|
||||
"from sklearn.ensemble import IsolationForest\n",
|
||||
"from sklearn.impute import SimpleImputer\n",
|
||||
"from sklearn.metrics import ConfusionMatrixDisplay\n",
|
||||
"from sklearn.model_selection import train_test_split\n",
|
||||
"from sklearn.pipeline import Pipeline\n",
|
||||
"from sklearn.preprocessing import OneHotEncoder, StandardScaler\n",
|
||||
"from sklearn.tree import DecisionTreeClassifier\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"from sklearn.metrics import (\n",
|
||||
" accuracy_score, average_precision_score, confusion_matrix,\n",
|
||||
" f1_score, precision_score, recall_score,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def security_metrics(y_true, y_pred):\n",
|
||||
" tn, fp, fn, tp = confusion_matrix(y_true, y_pred, labels=[0, 1]).ravel()\n",
|
||||
" return {\n",
|
||||
" \"accuracy\": accuracy_score(y_true, y_pred),\n",
|
||||
" \"attack_precision\": precision_score(y_true, y_pred, zero_division=0),\n",
|
||||
" \"attack_recall\": recall_score(y_true, y_pred, zero_division=0),\n",
|
||||
" \"attack_f1\": f1_score(y_true, y_pred, zero_division=0),\n",
|
||||
" \"false_positives\": int(fp),\n",
|
||||
" \"false_negatives\": int(fn),\n",
|
||||
" \"false_positive_rate\": fp / (fp + tn) if fp + tn else 0.0,\n",
|
||||
" \"alerts_per_10000\": 10000 * (fp + tp) / len(y_true),\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"OUTPUT_DIR = Path(\"output\")\n",
|
||||
"OUTPUT_DIR.mkdir(exist_ok=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "cell-004",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# TODO 1: load the fixed split and build the supervised feature matrices.\n",
|
||||
"train_df = None\n",
|
||||
"test_df = None\n",
|
||||
"feature_cols = None\n",
|
||||
"numeric_features = None\n",
|
||||
"X_train, y_train, X_test, y_test = None, None, None, None\n",
|
||||
"\n",
|
||||
"# TODO 2: fit the bounded, balanced Decision Tree pipeline from Lab 5.\n",
|
||||
"tree_model = None\n",
|
||||
"tree_predictions = None\n",
|
||||
"tree_probabilities = None"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "cell-005",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 1. Fit a normal-only Isolation Forest and set its alert budget"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "cell-006",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# TODO 3: define anomaly_features, split normal training data, fit an\n",
|
||||
"# Isolation Forest pipeline, and select the 99th validation percentile.\n",
|
||||
"anomaly_features = None\n",
|
||||
"normal_fit = None\n",
|
||||
"normal_valid = None\n",
|
||||
"anomaly_model = None\n",
|
||||
"validation_scores = None\n",
|
||||
"anomaly_scores = None\n",
|
||||
"anomaly_threshold = None\n",
|
||||
"anomaly_predictions = None"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "cell-007",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 2. Compare classifier, anomaly, and hybrid policies"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "cell-008",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# TODO 4: create Classifier, Anomaly, and Hybrid_OR predictions/scores.\n",
|
||||
"policy_predictions = None\n",
|
||||
"policy_scores = None\n",
|
||||
"\n",
|
||||
"# TODO 5: build metrics_table and select the highest-recall policy with\n",
|
||||
"# FPR <= 0.10, breaking ties by F1 then lower FPR.\n",
|
||||
"metrics_table = None\n",
|
||||
"selected_policy = None\n",
|
||||
"selected_predictions = None\n",
|
||||
"display(metrics_table)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "cell-009",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 3. Audit attack groups and create the alert evidence table"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "cell-010",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# TODO 6: calculate per-group recall for every policy.\n",
|
||||
"group_recall = None\n",
|
||||
"\n",
|
||||
"# TODO 7: build alert_table, derive alert_reason, and keep the top 200\n",
|
||||
"# selected alerts ordered by anomaly score and tree probability.\n",
|
||||
"alert_table = None\n",
|
||||
"top_alerts = None\n",
|
||||
"display(top_alerts)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "cell-011",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 4. Export evidence and write the SOC recommendation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "cell-012",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# TODO 8: save a confusion matrix and anomaly-score distribution chart.\n",
|
||||
"\n",
|
||||
"# TODO 9: write an English SOC summary covering the selected policy,\n",
|
||||
"# metrics, alert volume, detector roles, weak groups, and limitations.\n",
|
||||
"analyst_summary = None\n",
|
||||
"\n",
|
||||
"# TODO 10: export the metrics, group recall, top alerts, and summary.\n",
|
||||
"print(analyst_summary)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "cell-013",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Self-check"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "cell-014",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"expected_files = {\n",
|
||||
" \"hybrid_ids_metrics.csv\", \"hybrid_ids_group_recall.csv\",\n",
|
||||
" \"hybrid_ids_top_alerts.csv\", \"hybrid_ids_confusion_matrix.png\",\n",
|
||||
" \"hybrid_ids_score_distribution.png\", \"hybrid_ids_analyst_summary.md\",\n",
|
||||
"}\n",
|
||||
"assert set(policy_predictions) == {\"Classifier\", \"Anomaly\", \"Hybrid_OR\"}\n",
|
||||
"assert selected_policy in policy_predictions\n",
|
||||
"assert len(alert_table) == len(test_df)\n",
|
||||
"assert set(group_recall[\"attack_group\"]) == {\"DoS\", \"Probe\", \"R2L\", \"U2R\"}\n",
|
||||
"assert expected_files.issubset({path.name for path in OUTPUT_DIR.iterdir()})\n",
|
||||
"assert \"NSL-KDD\" in analyst_summary\n",
|
||||
"print(\"✓ Lab 6 completed\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "cell-015",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Submission checklist\n",
|
||||
"\n",
|
||||
"1. Run the notebook from top to bottom.\n",
|
||||
"2. Confirm that all six output files exist.\n",
|
||||
"3. Verify that the selected policy follows the documented rule.\n",
|
||||
"4. Explain how anomaly-only alerts differ from classifier-only alerts.\n",
|
||||
"5. Identify the weakest attack group and one dataset limitation."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.6"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
Reference in New Issue
Block a user