246 lines
6.4 KiB
Plaintext
246 lines
6.4 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "76db28c7",
|
|
"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",
|
|
"> **Solution version:** This notebook contains the complete solution. Run the cells in order and compare the results with your Lab notebook.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "778202bf",
|
|
"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": "91e09f7c",
|
|
"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": "29e0b207",
|
|
"metadata": {},
|
|
"source": [
|
|
"## 1. Train the baseline pipeline\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "f53d7241",
|
|
"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",
|
|
"pipeline.fit(X_train, y_train)\n",
|
|
"y_pred = pipeline.predict(X_test)\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "0781cb26",
|
|
"metadata": {},
|
|
"source": [
|
|
"## 2. Calculate the confusion matrix\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "21075b2f",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"cm = confusion_matrix(y_test, y_pred)\n",
|
|
"tn, fp, fn, tp = cm.ravel()\n",
|
|
"\n",
|
|
"print(\"Confusion matrix:\\n\", cm)\n",
|
|
"print(f\"TN={tn}, FP={fp}, FN={fn}, TP={tp}\")\n",
|
|
"\n",
|
|
"ConfusionMatrixDisplay(\n",
|
|
" confusion_matrix=cm,\n",
|
|
" display_labels=[\"BENIGN\", \"ATTACK\"],\n",
|
|
").plot(cmap=\"Blues\")\n",
|
|
"plt.title(\"Baseline intrusion classifier\")\n",
|
|
"plt.show()\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "223681c3",
|
|
"metadata": {},
|
|
"source": [
|
|
"## 3. Calculate classification metrics\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "a5a1597a",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"accuracy = accuracy_score(y_test, y_pred)\n",
|
|
"attack_precision = precision_score(y_test, y_pred, zero_division=0)\n",
|
|
"attack_recall = recall_score(y_test, y_pred, zero_division=0)\n",
|
|
"attack_f1 = f1_score(y_test, y_pred, zero_division=0)\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",
|
|
"print(classification_report(\n",
|
|
" y_test,\n",
|
|
" y_pred,\n",
|
|
" target_names=[\"BENIGN\", \"ATTACK\"],\n",
|
|
" zero_division=0,\n",
|
|
"))\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "ebbfff71",
|
|
"metadata": {},
|
|
"source": [
|
|
"## 4. Inspect false negatives\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "4adf798b",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"results = X_test.copy()\n",
|
|
"results[\"actual\"] = y_test\n",
|
|
"results[\"predicted\"] = y_pred\n",
|
|
"\n",
|
|
"false_negatives = results[\n",
|
|
" (results[\"actual\"] == 1) & (results[\"predicted\"] == 0)\n",
|
|
"]\n",
|
|
"\n",
|
|
"print(\"False negatives:\", len(false_negatives))\n",
|
|
"false_negatives.head()\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "4cba4276",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Self-check\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "0a64652b",
|
|
"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": "8f5b93d9",
|
|
"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
|
|
}
|