Files
labs/Lab-3/Lab-3-Practical/04_Classification_Metrics_Lab.ipynb
T

237 lines
6.2 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"id": "1ca2a12e",
"metadata": {},
"source": [
"# Module 3 — Lab 4: Evaluate an Intrusion Classifier\n",
"\n",
"\n",
"**Topics:** confusion matrix, accuracy, precision, recall, F1-score, error analysis\n",
"\n",
"> **Instructions:** Run the cells in order. Complete every `TODO`, then run the **Self-check** cell. Do not open the Solution notebook before attempting the tasks yourself.\n"
]
},
{
"cell_type": "markdown",
"id": "04669dcb",
"metadata": {},
"source": [
"## Learning objectives\n",
"\n",
"After completing this lab, you will be able to:\n",
"\n",
"- calculate a confusion matrix and classification metrics;\n",
"- distinguish false positives from false negatives;\n",
"- explain why attack recall matters in intrusion detection;\n",
"- inspect misclassified network flows.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f78f254b",
"metadata": {},
"outputs": [],
"source": [
"from pathlib import Path\n",
"import numpy as np\n",
"import pandas as pd\n",
"\n",
"DATA_PATH = Path(\"data/cicids2017_binary_sample.csv\")\n",
"RANDOM_STATE = 42\n",
"\n",
"import matplotlib.pyplot as plt\n",
"from sklearn.model_selection import train_test_split\n",
"from sklearn.pipeline import Pipeline\n",
"from sklearn.impute import SimpleImputer\n",
"from sklearn.ensemble import RandomForestClassifier\n",
"from sklearn.metrics import (\n",
" ConfusionMatrixDisplay, accuracy_score, classification_report,\n",
" confusion_matrix, precision_score, recall_score, f1_score\n",
")\n"
]
},
{
"cell_type": "markdown",
"id": "b9399f00",
"metadata": {},
"source": [
"## 1. Train the baseline pipeline\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4e0967ef",
"metadata": {},
"outputs": [],
"source": [
"df = pd.read_csv(DATA_PATH).drop_duplicates()\n",
"df.columns = df.columns.str.strip()\n",
"df = df.replace([np.inf, -np.inf], np.nan)\n",
"\n",
"X = df.drop(columns=\"Label\").select_dtypes(include=\"number\")\n",
"y = df[\"Label\"].astype(str).str.strip().str.upper().ne(\"BENIGN\").astype(int)\n",
"\n",
"X_train, X_test, y_train, y_test = train_test_split(\n",
" X, y, test_size=0.2, random_state=RANDOM_STATE, stratify=y\n",
")\n",
"\n",
"pipeline = Pipeline([\n",
" (\"imputer\", SimpleImputer(strategy=\"median\")),\n",
" (\"model\", RandomForestClassifier(\n",
" n_estimators=100, max_depth=12,\n",
" random_state=RANDOM_STATE, n_jobs=-1\n",
" )),\n",
"])\n",
"\n",
"# TODO 1: fit the pipeline and create y_pred\n",
"y_pred = None\n"
]
},
{
"cell_type": "markdown",
"id": "b83691ce",
"metadata": {},
"source": [
"## 2. Calculate the confusion matrix\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2e6d0759",
"metadata": {},
"outputs": [],
"source": [
"# TODO 2: calculate the confusion matrix and unpack tn, fp, fn, tp\n",
"cm = None\n",
"tn, fp, fn, tp = None, None, None, None\n",
"\n",
"print(\"Confusion matrix:\\n\", cm)\n",
"print(f\"TN={tn}, FP={fp}, FN={fn}, TP={tp}\")\n",
"\n",
"# TODO 3: display the confusion matrix with class names BENIGN and ATTACK\n"
]
},
{
"cell_type": "markdown",
"id": "e410267b",
"metadata": {},
"source": [
"## 3. Calculate classification metrics\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2bb935f4",
"metadata": {},
"outputs": [],
"source": [
"# TODO 4: calculate accuracy, attack precision, attack recall, and attack F1\n",
"accuracy = None\n",
"attack_precision = None\n",
"attack_recall = None\n",
"attack_f1 = None\n",
"\n",
"metrics = pd.Series({\n",
" \"accuracy\": accuracy,\n",
" \"attack_precision\": attack_precision,\n",
" \"attack_recall\": attack_recall,\n",
" \"attack_f1\": attack_f1,\n",
"})\n",
"print(metrics.round(3))\n",
"\n",
"# TODO 5: print classification_report with BENIGN and ATTACK target names\n"
]
},
{
"cell_type": "markdown",
"id": "c2719c46",
"metadata": {},
"source": [
"## 4. Inspect false negatives\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9b14d27c",
"metadata": {},
"outputs": [],
"source": [
"results = X_test.copy()\n",
"results[\"actual\"] = y_test\n",
"results[\"predicted\"] = y_pred\n",
"\n",
"# TODO 6: retain rows where actual is ATTACK and predicted is BENIGN\n",
"false_negatives = None\n",
"\n",
"print(\"False negatives:\", None)\n",
"false_negatives.head()\n"
]
},
{
"cell_type": "markdown",
"id": "9383f9fa",
"metadata": {},
"source": [
"## Self-check\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "21628fcf",
"metadata": {},
"outputs": [],
"source": [
"assert cm.shape == (2, 2)\n",
"assert tn + fp + fn + tp == len(y_test)\n",
"assert 0 <= accuracy <= 1\n",
"assert 0 <= attack_precision <= 1\n",
"assert 0 <= attack_recall <= 1\n",
"assert 0 <= attack_f1 <= 1\n",
"assert len(false_negatives) == fn\n",
"print(\"✓ Lab 4 completed\")\n"
]
},
{
"cell_type": "markdown",
"id": "e2f95d64",
"metadata": {},
"source": [
"## Analyst questions\n",
"\n",
"1. Which confusion-matrix cell represents a missed attack?\n",
"2. Which metric should receive more attention if missed attacks are very costly?\n",
"3. Why can improving attack recall increase the number of false alerts?\n"
]
}
],
"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
}