{ "nbformat": 4, "nbformat_minor": 0, "metadata": { "colab": { "provenance": [] }, "kernelspec": { "name": "python3", "display_name": "Python 3" }, "language_info": { "name": "python" } }, "cells": [ { "cell_type": "code", "source": [ "import tensorflow as tf\n", "import numpy as np\n", "import pandas as pd\n", "from tensorflow import keras\n", "from tensorflow.keras import layers\n", "from keras import losses\n", "from keras import optimizers\n", "from keras import metrics\n", "import math\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns" ], "metadata": { "id": "sIDDU2PYPdH_" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "CSV_HEADER = [\n", " \"duration\",\n", " \"protocol_type\",\n", " \"service\",\n", " \"flag\",\n", " \"src_bytes\",\n", " \"dst_bytes\",\n", " \"land\",\n", " \"wrong_fragment\",\n", " \"urgent\",\n", " \"hot\",\n", " \"num_failed_logins\",\n", " \"logged_in\",\n", " \"num_compromised\",\n", " \"root_shell\",\n", " \"su_attempted\",\n", " \"num_root\",\n", " \"num_file_creations\",\n", " \"num_shells\",\n", " \"num_access_files\",\n", " \"num_outbound_cmds\",\n", " \"is_host_login\",\n", " \"is_guest_login\",\n", " \"count\",\n", " \"srv_count\",\n", " \"serror_rate\",\n", " \"srv_serror_rate\",\n", " \"rerror_rate\",\n", " \"srv_rerror_rate\",\n", " \"same_srv_rate\",\n", " \"diff_srv_rate\",\n", " \"srv_diff_host_rate\",\n", " \"dst_host_count\",\n", " \"dst_host_srv_count\",\n", " \"dst_host_same_srv_rate\",\n", " \"dst_host_diff_srv_rate\",\n", " \"dst_host_same_src_port_rate\",\n", " \"dst_host_srv_diff_host_rate\",\n", " \"dst_host_serror_rate\",\n", " \"dst_host_srv_serror_rate\",\n", " \"dst_host_rerror_rate\",\n", " \"dst_host_srv_rerror_rate\",\n", " \"class\"\n", "]\n", "\n", "\n", "train_data = pd.read_csv(\"train.csv\", header=None, names=CSV_HEADER)\n", "\n", "test_data = pd.read_csv(\"test.csv\", header=None, names=CSV_HEADER)\n", "\n", "print(f\"Train dataset shape: {train_data.shape}\")\n", "print(f\"Test dataset shape: {test_data.shape}\")\n", "train_data['class'] = train_data['class'].str.replace(r\"^(.(?:53: FutureWarning: The default value of regex will change from True to False in a future version.\n", " train_data['class'] = train_data['class'].str.replace(r\"^(.(?:54: FutureWarning: The default value of regex will change from True to False in a future version.\n", " test_data['class'] = test_data['class'].str.replace(r\"^(.(?" ], "image/png": "iVBORw0KGgoAAAANSUhEUgAAAk0AAAHHCAYAAACiOWx7AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA+D0lEQVR4nO3de1iUdf7/8deAcvAw4AmQFQ+ppShJoiJ20IqciroyrdAs8VgaWkoe4ruKZu26q+WhNM0O6n7LPLTZQQtlMXVX8YRaampWGrY6oCmMkYLC/fujL/fPCcpbQgf0+biu+9rm83nP537fc10jr73nnntshmEYAgAAwO/y8nQDAAAAVQGhCQAAwAJCEwAAgAWEJgAAAAsITQAAABYQmgAAACwgNAEAAFhAaAIAALCA0AQAAGABoQlAlbRw4ULZbDYdPnzY060AuEYQmgDgKvPpp59q0qRJnm4DuOoQmgDgKvPpp5/q+eef93QbwFWH0AQAAGABoQnAZff+++/LZrNp/fr1peZef/112Ww27dmzR5L05Zdfqn///rruuuvk5+enkJAQDRw4UD/++ONF92Oz2cr8WKpp06bq37+/21hubq5GjhypsLAw+fr6qkWLFvr73/+u4uJiS8f02WefqWvXrqpdu7bsdrs6duyoxYsXu9UsX75cUVFR8vf3V/369fXYY4/pv//9r1tNt27d1K1bt1Lr9+/fX02bNjUfHz58WDabTS+99JLmz5+v5s2by9fXVx07dtS2bdvcnjdnzhzz9SjZAPxx1TzdAICrX1xcnGrVqqVly5apa9eubnNLly5VmzZt1LZtW0lSWlqavvvuOw0YMEAhISHau3ev5s+fr71792rz5s0VEgB+/vlnde3aVf/973/15JNPqnHjxtq0aZOSk5N17NgxzZw583efv3DhQg0cOFBt2rRRcnKyAgMDtXPnTqWmpurRRx81awYMGKCOHTtqypQpys7O1qxZs7Rx40bt3LlTgYGB5ep98eLFOn36tJ588knZbDZNnTpVPXv21Hfffafq1avrySef1NGjR5WWlqb//d//Ldc+APwGAwCugD59+hhBQUHG+fPnzbFjx44ZXl5exuTJk82xn3/+udRz33vvPUOSsWHDBnNswYIFhiTj0KFD5pgkY+LEiaWe36RJEyMhIcF8/MILLxg1a9Y0vv76a7e65557zvD2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yxf9Zh0AAEBVVK7Q9Pnnn1d0HwAAAJVauUJTiePHj+vAgQOSpBtuuEENGjSokKYAAAAqm3JdCJ6fn6+BAweqYcOGuu2223TbbbcpNDRUgwYN0s8//1zRPQIAAHhcuUJTUlKS1q9fr08++US5ubnKzc3VRx99pPXr1+vZZ5+t6B4BAAA8rlwfz/3zn//U+++/r27duplj9957r/z9/fXII49o7ty5FdUfAABApVCuM00///yzgoODS40HBQXx8RwAALgqlSs0xcTEaOLEiTp79qw5dubMGT3//POKiYmpsOYAAAAqi3J9PDdz5kzdfffdatSokdq1aydJ+uKLL+Tr66s1a9ZUaIMAAACVQblCU0REhA4ePKh3331X+/fvlyT16dNHffv2lb+/f4U2CAAAUBmUKzRNmTJFwcHBGjJkiNv422+/rePHj2vcuHEV0hwAAEBlUa5rml5//XW1atWq1HibNm00b968P9wUAABAZVOu0OR0OtWwYcNS4w0aNNCxY8f+cFMAAACVTblCU1hYmDZu3FhqfOPGjQoNDf3DTQEAAFQ25bqmaciQIRo5cqTOnTunO+64Q5KUnp6usWPHckdwAABwVSpXaBozZox+/PFHPfXUUyosLJQk+fn5ady4cUpOTq7QBgEAACqDcoUmm82mv//975owYYL27dsnf39/tWzZUr6+vhXdHwAAQKVQrtBUolatWurYsWNF9QIAAFBpletCcAAAgGsNoQkAAMACj4amuXPn6sYbb5TdbpfdbldMTIw+++wzc/7s2bNKTExUvXr1VKtWLfXq1UvZ2dlua2RlZSkuLk41atRQUFCQxowZo/Pnz7vVrFu3Tu3bt5evr69atGihhQsXluplzpw5atq0qfz8/BQdHa2tW7delmMGAABVk0dDU6NGjfS3v/1NmZmZ2r59u+644w498MAD2rt3ryRp1KhR+uSTT7R8+XKtX79eR48eVc+ePc3nFxUVKS4uToWFhdq0aZMWLVqkhQsXKiUlxaw5dOiQ4uLidPvtt2vXrl0aOXKkBg8erNWrV5s1S5cuVVJSkiZOnKgdO3aoXbt2cjgcysnJuXIvBgAAqNRshmEYnm7iQnXr1tW0adP00EMPqUGDBlq8eLEeeughSdL+/fvVunVrZWRkqHPnzvrss89033336ejRowoODpYkzZs3T+PGjdPx48fl4+OjcePGadWqVdqzZ4+5j969eys3N1epqamSpOjoaHXs2FGzZ8+WJBUXFyssLEwjRozQc889Z6lvl8ulgIAA5eXlyW63V+RL4iZqzD8u29pAVZU5rZ+nW6gQWZMjPN0CUOk0Ttl9Wde/lL/fleaapqKiIi1ZskT5+fmKiYlRZmamzp07p9jYWLOmVatWaty4sTIyMiRJGRkZioiIMAOTJDkcDrlcLvNsVUZGhtsaJTUlaxQWFiozM9OtxsvLS7GxsWZNWQoKCuRyudw2AABw9fJ4aNq9e7dq1aolX19fDR06VCtWrFB4eLicTqd8fHwUGBjoVh8cHCyn0ynpl9/AuzAwlcyXzP1ejcvl0pkzZ3TixAkVFRWVWVOyRlmmTJmigIAAcwsLCyvX8QMAgKrB46Hphhtu0K5du7RlyxYNGzZMCQkJ+uqrrzzd1kUlJycrLy/P3I4cOeLplgAAwGX0h25uWRF8fHzUokULSVJUVJS2bdumWbNmKT4+XoWFhcrNzXU725Sdna2QkBBJUkhISKlvuZV8u+7Cml9/4y47O1t2u13+/v7y9vaWt7d3mTUla5TF19eXO6ADAHAN8fiZpl8rLi5WQUGBoqKiVL16daWnp5tzBw4cUFZWlmJiYiRJMTEx2r17t9u33NLS0mS32xUeHm7WXLhGSU3JGj4+PoqKinKrKS4uVnp6ulkDAADg0TNNycnJuueee9S4cWOdPn1aixcv1rp167R69WoFBARo0KBBSkpKUt26dWW32zVixAjFxMSoc+fOkqTu3bsrPDxcjz/+uKZOnSqn06nx48crMTHRPAs0dOhQzZ49W2PHjtXAgQO1du1aLVu2TKtWrTL7SEpKUkJCgjp06KBOnTpp5syZys/P14ABAzzyugAAgMrHo6EpJydH/fr107FjxxQQEKAbb7xRq1ev1l133SVJmjFjhry8vNSrVy8VFBTI4XDotddeM5/v7e2tlStXatiwYYqJiVHNmjWVkJCgyZMnmzXNmjXTqlWrNGrUKM2aNUuNGjXSm2++KYfDYdbEx8fr+PHjSklJkdPpVGRkpFJTU0tdHA4AAK5dle4+TVUV92kCPIf7NAFXL+7TBAAAUMUQmgAAACwgNAEAAFhAaAIAALCA0AQAAGABoQkAAMACQhMAAIAFhCYAAAALCE0AAAAWEJoAAAAsIDQBAABYQGgCAACwgNAEAABgAaEJAADAAkITAACABYQmAAAACwhNAAAAFhCaAAAALCA0AQAAWEBoAgAAsIDQBAAAYAGhCQAAwAJCEwAAgAWEJgAAAAsITQAAABYQmgAAACwgNAEAAFhAaAIAALCA0AQAAGABoQkAAMACQhMAAIAFhCYAAAALCE0AAAAWEJoAAAAsIDQBAABYQGgCAACwgNAEAABgAaEJAADAAkITAACABYQmAAAACwhNAAAAFng0NE2ZMkUdO3ZU7dq1FRQUpB49eujAgQNuNWfPnlViYqLq1aunWrVqqVevXsrOznarycrKUlxcnGrUqKGgoCCNGTNG58+fd6tZt26d2rdvL19fX7Vo0UILFy4s1c+cOXPUtGlT+fn5KTo6Wlu3bq3wYwYAAFWTR0PT+vXrlZiYqM2bNystLU3nzp1T9+7dlZ+fb9aMGjVKn3zyiZYvX67169fr6NGj6tmzpzlfVFSkuLg4FRYWatOmTVq0aJEWLlyolJQUs+bQoUOKi4vT7bffrl27dmnkyJEaPHiwVq9ebdYsXbpUSUlJmjhxonbs2KF27drJ4XAoJyfnyrwYAACgUrMZhmF4uokSx48fV1BQkNavX6/bbrtNeXl5atCggRYvXqyHHnpIkrR//361bt1aGRkZ6ty5sz777DPdd999Onr0qIKDgyVJ8+bN07hx43T8+HH5+Pho3LhxWrVqlfbs2WPuq3fv3srNzVVqaqokKTo6Wh07dtTs2bMlScXFxQoLC9OIESP03HPPXbR3l8ulgIAA5eXlyW63V/RLY4oa84/LtjZQVWVO6+fpFipE1uQIT7cAVDqNU3Zf1vUv5e93pbqmKS8vT5JUt25dSVJmZqbOnTun2NhYs6ZVq1Zq3LixMjIyJEkZGRmKiIgwA5MkORwOuVwu7d2716y5cI2SmpI1CgsLlZmZ6Vbj5eWl2NhYs+bXCgoK5HK53DYAAHD1qjShqbi4WCNHjtTNN9+stm3bSpKcTqd8fHwUGBjoVhscHCyn02nWXBiYSuZL5n6vxuVy6cyZMzpx4oSKiorKrClZ49emTJmigIAAcwsLCyvfgQMAgCqh0oSmxMRE7dmzR0uWLPF0K5YkJycrLy/P3I4cOeLplgAAwGVUzdMNSNLw4cO1cuVKbdiwQY0aNTLHQ0JCVFhYqNzcXLezTdnZ2QoJCTFrfv0tt5Jv111Y8+tv3GVnZ8tut8vf31/e3t7y9vYus6ZkjV/z9fWVr69v+Q4YAABUOR4902QYhoYPH64VK1Zo7dq1atasmdt8VFSUqlevrvT0dHPswIEDysrKUkxMjCQpJiZGu3fvdvuWW1pamux2u8LDw82aC9coqSlZw8fHR1FRUW41xcXFSk9PN2sAAMC1zaNnmhITE7V48WJ99NFHql27tnn9UEBAgPz9/RUQEKBBgwYpKSlJdevWld1u14gRIxQTE6POnTtLkrp3767w8HA9/vjjmjp1qpxOp8aPH6/ExETzTNDQoUM1e/ZsjR07VgMHDtTatWu1bNkyrVq1yuwlKSlJCQkJ6tChgzp16qSZM2cqPz9fAwYMuPIvDAAAqHQ8Gprmzp0rSerWrZvb+IIFC9S/f39J0owZM+Tl5aVevXqpoKBADodDr732mlnr7e2tlStXatiwYYqJiVHNmjWVkJCgyZMnmzXNmjXTqlWrNGrUKM2aNUuNGjXSm2++KYfDYdbEx8fr+PHjSklJkdPpVGRkpFJTU0tdHA4AAK5Nleo+TVUZ92kCPIf7NAFXL+7TBAAAUMUQmgAAACwgNAEAAFhAaAIAALCA0AQAAGABoQkAAMACQhMAAIAFhCYAAAALCE0AAAAWEJoAAAAsIDQBAABYQGgCAACwgNAEAABgAaEJAADAAkITAACABYQmAAAACwhNAAAAFhCaAAAALCA0AQAAWEBoAgAAsIDQBAAAYAGhCQAAwAJCEwAAgAWEJgAAAAsITQAAABYQmgAAACwgNAEAAFhAaAIAALCA0AQAAGABoQkAAMACQhMAAIAFhCYAAAALCE0AAAAWEJoAAAAsIDQBAABYQGgCAACwgNAEAABgAaEJAADAAkITAACABYQmAAAACwhNAAAAFng0NG3YsEH333+/QkNDZbPZ9OGHH7rNG4ahlJQUNWzYUP7+/oqNjdXBgwfdak6ePKm+ffvKbrcrMDBQgwYN0k8//eRW8+WXX+rWW2+Vn5+fwsLCNHXq1FK9LF++XK1atZKfn58iIiL06aefVvjxAgCAqsujoSk/P1/t2rXTnDlzypyfOnWqXnnlFc2bN09btmxRzZo15XA4dPbsWbOmb9++2rt3r9LS0rRy5Upt2LBBTzzxhDnvcrnUvXt3NWnSRJmZmZo2bZomTZqk+fPnmzWbNm1Snz59NGjQIO3cuVM9evRQjx49tGfPnst38AAAoEqxGYZheLoJSbLZbFqxYoV69Ogh6ZezTKGhoXr22Wc1evRoSVJeXp6Cg4O1cOFC9e7dW/v27VN4eLi2bdumDh06SJJSU1N177336ocfflBoaKjmzp2rP//5z3I6nfLx8ZEkPffcc/rwww+1f/9+SVJ8fLzy8/O1cuVKs5/OnTsrMjJS8+bNs9S/y+VSQECA8vLyZLfbK+plKSVqzD8u29pAVZU5rZ+nW6gQWZMjPN0CUOk0Ttl9Wde/lL/flfaapkOHDsnpdCo2NtYcCwgIUHR0tDIyMiRJGRkZCgwMNAOTJMXGxsrLy0tbtmwxa2677TYzMEmSw+HQgQMHdOrUKbPmwv2U1JTspywFBQVyuVxuGwAAuHpV2tDkdDolScHBwW7jwcHB5pzT6VRQUJDbfLVq1VS3bl23mrLWuHAfv1VTMl+WKVOmKCAgwNzCwsIu9RABAEAVUmlDU2WXnJysvLw8czty5IinWwIAAJdRpQ1NISEhkqTs7Gy38ezsbHMuJCREOTk5bvPnz5/XyZMn3WrKWuPCffxWTcl8WXx9fWW32902AABw9aq0oalZs2YKCQlRenq6OeZyubRlyxbFxMRIkmJiYpSbm6vMzEyzZu3atSouLlZ0dLRZs2HDBp07d86sSUtL0w033KA6deqYNRfup6SmZD8AAAAeDU0//fSTdu3apV27dkn65eLvXbt2KSsrSzabTSNHjtSLL76ojz/+WLt371a/fv0UGhpqfsOudevWuvvuuzVkyBBt3bpVGzdu1PDhw9W7d2+FhoZKkh599FH5+Pho0KBB2rt3r5YuXapZs2YpKSnJ7OOZZ55RamqqXn75Ze3fv1+TJk3S9u3bNXz48Cv9kgAAgEqqmid3vn37dt1+++3m45Igk5CQoIULF2rs2LHKz8/XE088odzcXN1yyy1KTU2Vn5+f+Zx3331Xw4cP15133ikvLy/16tVLr7zyijkfEBCgNWvWKDExUVFRUapfv75SUlLc7uXUpUsXLV68WOPHj9f//M//qGXLlvrwww/Vtm3bK/AqAACAqqDS3KepquM+TYDncJ8m4OrFfZoAAACqGEITAACABYQmAAAACwhNAAAAFhCaAAAALCA0AQAAWEBoAgAAsIDQBAAAYAGhCQAAwAJCEwAAgAWEJgAAAAsITQAAABYQmgAAACwgNAEAAFhAaAIAALCA0AQAAGABoQkAAMACQhMAAIAFhCYAAAALCE0AAAAWEJoAAAAsIDQBAABYQGgCAACwgNAEAABgAaEJAADAAkITAACABYQmAAAACwhNAAAAFhCaAAAALCA0AQAAWEBoAgAAsIDQBAAAYAGhCQAAwAJCEwAAgAWEJgAAAAsITQAAABYQmgAAACwgNAEAAFhAaAIAALCA0AQAAGABoQkAAMACQtOvzJkzR02bNpWfn5+io6O1detWT7cEAAAqAULTBZYuXaqkpCRNnDhRO3bsULt27eRwOJSTk+Pp1gAAgIcRmi4wffp0DRkyRAMGDFB4eLjmzZunGjVq6O233/Z0awAAwMMITf+nsLBQmZmZio2NNce8vLwUGxurjIwMD3YGAAAqg2qebqCyOHHihIqKihQcHOw2HhwcrP3795eqLygoUEFBgfk4Ly9PkuRyuS5rn0UFZy7r+kBVdLnfd1fK6bNFnm4BqHQu9/u7ZH3DMC5aS2gqpylTpuj5558vNR4WFuaBboBrW8CrQz3dAoDLZUrAFdnN6dOnFRDw+/siNP2f+vXry9vbW9nZ2W7j2dnZCgkJKVWfnJyspKQk83FxcbFOnjypevXqyWazXfZ+4Vkul0thYWE6cuSI7Ha7p9sBUIF4f19bDMPQ6dOnFRoaetFaQtP/8fHxUVRUlNLT09WjRw9JvwSh9PR0DR8+vFS9r6+vfH193cYCAwOvQKeoTOx2O/+oAlcp3t/XjoudYSpBaLpAUlKSEhIS1KFDB3Xq1EkzZ85Ufn6+BgwY4OnWAACAhxGaLhAfH6/jx48rJSVFTqdTkZGRSk1NLXVxOAAAuPYQmn5l+PDhZX4cB1zI19dXEydOLPURLYCqj/c3fovNsPIdOwAAgGscN7cEAACwgNAEAABgAaEJAADAAkITUIk0bdpUM2fO9HQbACpQt27dNHLkSE+3gQpAaAIAALCA0ARcgsLCQk+3AADwEEITrmrdunXT008/rbFjx6pu3boKCQnRpEmTzPmsrCw98MADqlWrlux2ux555BG33x+cNGmSIiMj9eabb6pZs2by8/OTJNlsNr3++uu67777VKNGDbVu3VoZGRn65ptv1K1bN9WsWVNdunTRt99+a6717bff6oEHHlBwcLBq1aqljh076l//+tcVey2Aqiw1NVW33HKLAgMDVa9ePd13333m++vw4cOy2Wz64IMPdPvtt6tGjRpq166dMjIy3Nb45z//qTZt2sjX11dNmzbVyy+/7DbftGlTvfjii+rXr59q1aqlJk2a6OOPP9bx48fNfyduvPFGbd++3XzOjz/+qD59+uhPf/qTatSooYiICL333nu/eRyTJ09W27ZtS41HRkZqwoQJf+QlwhVAaMJVb9GiRapZs6a2bNmiqVOnavLkyUpLS1NxcbEeeOABnTx5UuvXr1daWpq+++47xcfHuz3/m2++0T//+U998MEH2rVrlzn+wgsvqF+/ftq1a5datWqlRx99VE8++aSSk5O1fft2GYbhdqPUn376Sffee6/S09O1c+dO3X333br//vuVlZV1pV4KoMrKz89XUlKStm/frvT0dHl5eenBBx9UcXGxWfPnP/9Zo0eP1q5du3T99derT58+On/+vCQpMzNTjzzyiHr37q3du3dr0qRJmjBhghYuXOi2nxkzZujmm2/Wzp07FRcXp8cff1z9+vXTY489ph07dqh58+bq16+fSm5xePbsWUVFRWnVqlXas2ePnnjiCT3++OPaunVrmccxcOBA7du3T9u2bTPHdu7cqS+//JKf7KoKDOAq1rVrV+OWW25xG+vYsaMxbtw4Y82aNYa3t7eRlZVlzu3du9eQZGzdutUwDMOYOHGiUb16dSMnJ8dtDUnG+PHjzccZGRmGJOOtt94yx9577z3Dz8/vd/tr06aN8eqrr5qPmzRpYsyYMeOSjxO41hw/ftyQZOzevds4dOiQIcl48803zfmS9/K+ffsMwzCMRx991Ljrrrvc1hgzZowRHh5uPm7SpInx2GOPmY+PHTtmSDImTJhgjpW8148dO/abvcXFxRnPPvus+bhr167GM888Yz6+5557jGHDhpmPR4wYYXTr1u0Sjh6ewpkmXPVuvPFGt8cNGzZUTk6O9u3bp7CwMIWFhZlz4eHhCgwM1L59+8yxJk2aqEGDBr+7bsnvE0ZERLiNnT17Vi6XS9IvZ5pGjx6t1q1bKzAwULVq1dK+ffs40wRYcPDgQfXp00fXXXed7Ha7mjZtKklu758L35MNGzaUJOXk5EiS9u3bp5tvvtltzZtvvlkHDx5UUVFRmWv81vv6wnWLior0wgsvKCIiQnXr1lWtWrW0evXq331fDxkyRO+9957Onj2rwsJCLV68WAMHDrT+YsBj+O05XPWqV6/u9thms7md0r+YmjVrXnRdm832m2Ml+xo9erTS0tL00ksvqUWLFvL399dDDz3ExeWABffff7+aNGmiN954Q6GhoSouLlbbtm3d3j+/9/6z6lLf19OmTdOsWbM0c+ZMRUREqGbNmho5cuTvvq/vv/9++fr6asWKFfLx8dG5c+f00EMPXVKf8AxCE65ZrVu31pEjR3TkyBHzbNNXX32l3NxchYeHV/j+Nm7cqP79++vBBx+U9MuZp8OHD1f4foCrzY8//qgDBw7ojTfe0K233ipJ+s9//nNJa7Ru3VobN250G9u4caOuv/56eXt7l7u3jRs36oEHHtBjjz0m6Zcw9fXXX//uvyHVqlVTQkKCFixYIB8fH/Xu3Vv+/v7l7gFXDqEJ16zY2FhFRESob9++mjlzps6fP6+nnnpKXbt2VYcOHSp8fy1bttQHH3yg+++/XzabTRMmTLjk/xcMXIvq1KmjevXqaf78+WrYsKGysrL03HPPXdIazz77rDp27KgXXnhB8fHxysjI0OzZs/Xaa6/9od5atmyp999/X5s2bVKdOnU0ffp0ZWdnX/T/eA0ePFitW7eWpFJhDpUX1zThmmWz2fTRRx+pTp06uu222xQbG6vrrrtOS5cuvSz7mz59uurUqaMuXbro/vvvl8PhUPv27S/LvoCriZeXl5YsWaLMzEy1bdtWo0aN0rRp0y5pjfbt22vZsmVasmSJ2rZtq5SUFE2ePFn9+/f/Q72NHz9e7du3l8PhULdu3RQSEqIePXpc9HktW7ZUly5d1KpVK0VHR/+hHnDl2Azj/743CQAArgjDMNSyZUs99dRTSkpK8nQ7sIiP5wAAuIKOHz+uJUuWyOl0cm+mKobQBADAFRQUFKT69etr/vz5qlOnjqfbwSUgNAEAcAVxVUzVxYXgAAAAFhCaAAAALCA0AQAAWEBoAgAAsIDQBOCad/jwYdlsNu3atcvTrQCoxAhNAAAAFhCaAAAALCA0AbhmFBcXa+rUqWrRooV8fX3VuHFj/eUvfylVV1RUpEGDBqlZs2by9/fXDTfcoFmzZrnVrFu3Tp06dVLNmjUVGBiom2++Wd9//70k6YsvvtDtt9+u2rVry263KyoqStu3b78ixwjg8uHmlgCuGcnJyXrjjTc0Y8YM3XLLLTp27Jj2799fqq64uFiNGjXS8uXLVa9ePW3atElPPPGEGjZsqEceeUTnz59Xjx49NGTIEL333nsqLCzU1q1bZbPZJEl9+/bVTTfdpLlz58rb21u7du1S9erVr/ThAqhg/GAvgGvC6dOn1aBBA82ePVuDBw92mzt8+LCaNWumnTt3KjIyssznDx8+XE6nU++//75OnjypevXqad26deratWupWrvdrldffVUJCQmX41AAeAgfzwG4Juzbt08FBQW68847LdXPmTNHUVFRatCggWrVqqX58+crKytLklS3bl31799fDodD999/v2bNmqVjx46Zz01KStLgwYMVGxurv/3tb/r2228vyzEBuLIITQCuCf7+/pZrlyxZotGjR2vQoEFas2aNdu3apQEDBqiwsNCsWbBggTIyMtSlSxctXbpU119/vTZv3ixJmjRpkvbu3au4uDitXbtW4eHhWrFiRYUfE4Ari4/nAFwTzp49q7p16+qVV1656MdzI0aM0FdffaX09HSzJjY2VidOnPjNeznFxMSoY8eOeuWVV0rN9enTR/n5+fr4448r9JgAXFmcaQJwTfDz89O4ceM0duxY/eMf/9C3336rzZs366233ipV27JlS23fvl2rV6/W119/rQkTJmjbtm3m/KFDh5ScnKyMjAx9//33WrNmjQ4ePKjWrVvrzJkzGj58uNatW6fvv/9eGzdu1LZt29S6desrebgALgO+PQfgmjFhwgRVq1ZNKSkpOnr0qBo2bKihQ4eWqnvyySe1c+dOxcfHy2azqU+fPnrqqaf02WefSZJq1Kih/fv3a9GiRfrxxx/VsGFDJSYm6sknn9T58+f1448/ql+/fsrOzlb9+vXVs2dPPf/881f6cAFUMD6eAwAAsICP5wAAACwgNAEAAFhAaAIAALCA0AQAAGABoQkAAMACQhMAAIAFhCYAAAALCE0AAAAWEJoAAAAsIDQBAABYQGgCAACwgNAEAABgwf8D5hjVopT9TlUAAAAASUVORK5CYII=\n" }, "metadata": {} } ] }, { "cell_type": "markdown", "source": [ "## Removing outliers and duplicates" ], "metadata": { "id": "wHhIvDl9V5kl" } }, { "cell_type": "code", "source": [ "def Remove_Outlier_Indices(df):\n", " Q1 = df.quantile(0.02)\n", " Q3 = df.quantile(0.98)\n", " IQR = Q3 - Q1\n", " #trueList = ~((df < (Q1 - 1.5 * IQR)) |(df > (Q3 + 1.5 * IQR)))\n", " trueList = ~((df < (Q1 - 1.5 * IQR)) |(df > (Q3 + 1.5 * IQR))).any(axis=1)\n", " return trueList\n", "\n", "nonOutlierList = Remove_Outlier_Indices(train_data)\n", "new_train_data = train_data[nonOutlierList]\n", "\n", "nonOutlierList = Remove_Outlier_Indices(test_data)\n", "new_test_data = test_data[nonOutlierList]" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "8GZwWkuSV5HT", "outputId": "ab0a253e-f92e-46e7-9e93-b72402784899" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stderr", "text": [ ":2: FutureWarning: The default value of numeric_only in DataFrame.quantile is deprecated. In a future version, it will default to False. Select only valid columns or specify the value of numeric_only to silence this warning.\n", " Q1 = df.quantile(0.02)\n", ":3: FutureWarning: The default value of numeric_only in DataFrame.quantile is deprecated. In a future version, it will default to False. Select only valid columns or specify the value of numeric_only to silence this warning.\n", " Q3 = df.quantile(0.98)\n", ":6: FutureWarning: Automatic reindexing on DataFrame vs Series comparisons is deprecated and will raise ValueError in a future version. Do `left, right = left.align(right, axis=1, copy=False)` before e.g. `left == right`\n", " trueList = ~((df < (Q1 - 1.5 * IQR)) |(df > (Q3 + 1.5 * IQR))).any(axis=1)\n" ] } ] }, { "cell_type": "markdown", "source": [ "## Merging train and test datasets" ], "metadata": { "id": "uNzhq0uHKkUB" } }, { "cell_type": "code", "source": [ "frames = [new_train_data, new_test_data]\n", "df = pd.concat(frames)\n", "df = df.reset_index(drop=True)\n", "df" ], "metadata": { "id": "3kxP6kBRGX2y", "colab": { "base_uri": "https://localhost:8080/", "height": 444 }, "outputId": "8f225d41-1967-4ae7-a5d1-5d8b58d2b26e" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ " duration protocol_type service flag src_bytes dst_bytes land \\\n", "0 0 1 20 9 491 0 0 \n", "1 0 2 44 9 146 0 0 \n", "2 0 1 49 5 0 0 0 \n", "3 0 1 24 9 232 8153 0 \n", "4 0 1 24 9 199 420 0 \n", "... ... ... ... ... ... ... ... \n", "139899 0 0 14 9 1032 0 0 \n", "139900 0 1 49 9 794 333 0 \n", "139901 0 1 22 9 317 938 0 \n", "139902 0 2 11 9 42 42 0 \n", "139903 0 1 52 1 0 0 0 \n", "\n", " wrong_fragment urgent hot ... dst_host_srv_count \\\n", "0 0 0 0 ... 25 \n", "1 0 0 0 ... 1 \n", "2 0 0 0 ... 26 \n", "3 0 0 0 ... 255 \n", "4 0 0 0 ... 255 \n", "... ... ... ... ... ... \n", "139899 0 0 0 ... 255 \n", "139900 0 0 0 ... 141 \n", "139901 0 0 0 ... 255 \n", "139902 0 0 0 ... 252 \n", "139903 0 0 0 ... 21 \n", "\n", " dst_host_same_srv_rate dst_host_diff_srv_rate \\\n", "0 0.17 0.03 \n", "1 0.00 0.60 \n", "2 0.10 0.05 \n", "3 1.00 0.00 \n", "4 1.00 0.00 \n", "... ... ... \n", "139899 1.00 0.00 \n", "139900 0.72 0.06 \n", "139901 1.00 0.00 \n", "139902 0.99 0.01 \n", "139903 0.08 0.03 \n", "\n", " dst_host_same_src_port_rate dst_host_srv_diff_host_rate \\\n", "0 0.17 0.00 \n", "1 0.88 0.00 \n", "2 0.00 0.00 \n", "3 0.03 0.04 \n", "4 0.00 0.00 \n", "... ... ... \n", "139899 1.00 0.00 \n", "139900 0.01 0.01 \n", "139901 0.01 0.01 \n", "139902 0.00 0.00 \n", "139903 0.00 0.00 \n", "\n", " dst_host_serror_rate dst_host_srv_serror_rate dst_host_rerror_rate \\\n", "0 0.00 0.00 0.05 \n", "1 0.00 0.00 0.00 \n", "2 1.00 1.00 0.00 \n", "3 0.03 0.01 0.00 \n", "4 0.00 0.00 0.00 \n", "... ... ... ... \n", "139899 0.00 0.00 0.00 \n", "139900 0.01 0.00 0.00 \n", "139901 0.01 0.00 0.00 \n", "139902 0.00 0.00 0.00 \n", "139903 0.00 0.00 0.44 \n", "\n", " dst_host_srv_rerror_rate class \n", "0 0.00 normal \n", "1 0.00 normal \n", "2 0.00 anomaly \n", "3 0.01 normal \n", "4 0.00 normal \n", "... ... ... \n", "139899 0.00 anomaly \n", "139900 0.00 normal \n", "139901 0.00 normal \n", "139902 0.00 normal \n", "139903 1.00 anomaly \n", "\n", "[139904 rows x 42 columns]" ], "text/html": [ "\n", "
\n", "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
durationprotocol_typeserviceflagsrc_bytesdst_byteslandwrong_fragmenturgenthot...dst_host_srv_countdst_host_same_srv_ratedst_host_diff_srv_ratedst_host_same_src_port_ratedst_host_srv_diff_host_ratedst_host_serror_ratedst_host_srv_serror_ratedst_host_rerror_ratedst_host_srv_rerror_rateclass
00120949100000...250.170.030.170.000.000.000.050.00normal
10244914600000...10.000.600.880.000.000.000.000.00normal
201495000000...260.100.050.000.001.001.000.000.00anomaly
30124923281530000...2551.000.000.030.040.030.010.000.01normal
4012491994200000...2551.000.000.000.000.000.000.000.00normal
..................................................................
13989900149103200000...2551.000.001.000.000.000.000.000.00anomaly
139900014997943330000...1410.720.060.010.010.010.000.000.00normal
139901012293179380000...2551.000.000.010.010.010.000.000.00normal
1399020211942420000...2520.990.010.000.000.000.000.000.00normal
13990301521000000...210.080.030.000.000.000.000.441.00anomaly
\n", "

139904 rows × 42 columns

\n", "
\n", "
\n", "\n", "
\n", " \n", "\n", " \n", "\n", " \n", "
\n", "\n", "\n", "
\n", " \n", "\n", "\n", "\n", " \n", "
\n", "
\n", "
\n" ] }, "metadata": {}, "execution_count": 5 } ] }, { "cell_type": "markdown", "source": [ "## Feature Selection" ], "metadata": { "id": "FiXD9jj7JzAt" } }, { "cell_type": "code", "source": [ "from sklearn.ensemble import RandomForestClassifier\n", "X = df.drop(['class'], axis=1)\n", "Y = df[\"class\"].astype('category').cat.codes\n", "clf = RandomForestClassifier()\n", "clf.fit(X, Y)\n", "features = pd.Series(clf.feature_importances_, index=X.columns)\n", "features.sort_values(ascending=False, inplace=True)\n", "print(features.head(10))" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "vkQIwJLqxnb2", "outputId": "c71170f5-2ec3-4fa7-e5c3-0c2eef6e236c" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "src_bytes 0.170986\n", "dst_bytes 0.136790\n", "flag 0.109430\n", "diff_srv_rate 0.071831\n", "logged_in 0.052381\n", "same_srv_rate 0.050228\n", "dst_host_srv_count 0.044186\n", "dst_host_diff_srv_rate 0.041677\n", "dst_host_same_srv_rate 0.037905\n", "service 0.037454\n", "dtype: float64\n" ] } ] }, { "cell_type": "code", "source": [ "fs = features.head(10).to_dict()\n", "fs" ], "metadata": { "id": "CcnIqyU6u_lU", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "fc65c693-356c-4641-b5f9-7ae6cfa56958" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "{'src_bytes': 0.17098620255574568,\n", " 'dst_bytes': 0.13679024895951913,\n", " 'flag': 0.10942976466676348,\n", " 'diff_srv_rate': 0.07183130071546076,\n", " 'logged_in': 0.05238089001369002,\n", " 'same_srv_rate': 0.05022756247592041,\n", " 'dst_host_srv_count': 0.04418600834209949,\n", " 'dst_host_diff_srv_rate': 0.04167726706300874,\n", " 'dst_host_same_srv_rate': 0.037905206562235054,\n", " 'service': 0.037454366858942253}" ] }, "metadata": {}, "execution_count": 7 } ] }, { "cell_type": "code", "source": [ "columns = []\n", "for k in fs:\n", " columns.append(str(k))\n", "columns" ], "metadata": { "id": "tUrifaWBvZpx", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "385f28a5-ab97-4248-c34e-7d306c58d4b4" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "['src_bytes',\n", " 'dst_bytes',\n", " 'flag',\n", " 'diff_srv_rate',\n", " 'logged_in',\n", " 'same_srv_rate',\n", " 'dst_host_srv_count',\n", " 'dst_host_diff_srv_rate',\n", " 'dst_host_same_srv_rate',\n", " 'service']" ] }, "metadata": {}, "execution_count": 8 } ] }, { "cell_type": "code", "source": [ "X = df.drop(['class'], axis=1)\n", "Y = df[\"class\"].astype('category').cat.codes\n", "principalDf = pd.DataFrame(data = X\n", " , columns = columns)\n", "finalDf = pd.concat([principalDf, df[\"class\"]], axis = 1)\n", "finalDf" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 424 }, "id": "RzFbtWLlsmn0", "outputId": "5b44b483-bf9a-429e-b4bf-36e2f6b91afd" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ " src_bytes dst_bytes flag diff_srv_rate logged_in same_srv_rate \\\n", "0 491 0 9 0.00 0 1.00 \n", "1 146 0 9 0.15 0 0.08 \n", "2 0 0 5 0.07 0 0.05 \n", "3 232 8153 9 0.00 1 1.00 \n", "4 199 420 9 0.00 1 1.00 \n", "... ... ... ... ... ... ... \n", "139899 1032 0 9 0.00 0 1.00 \n", "139900 794 333 9 0.00 1 1.00 \n", "139901 317 938 9 0.00 1 1.00 \n", "139902 42 42 9 0.00 0 1.00 \n", "139903 0 0 1 1.00 0 0.25 \n", "\n", " dst_host_srv_count dst_host_diff_srv_rate dst_host_same_srv_rate \\\n", "0 25 0.03 0.17 \n", "1 1 0.60 0.00 \n", "2 26 0.05 0.10 \n", "3 255 0.00 1.00 \n", "4 255 0.00 1.00 \n", "... ... ... ... \n", "139899 255 0.00 1.00 \n", "139900 141 0.06 0.72 \n", "139901 255 0.00 1.00 \n", "139902 252 0.01 0.99 \n", "139903 21 0.03 0.08 \n", "\n", " service class \n", "0 20 normal \n", "1 44 normal \n", "2 49 anomaly \n", "3 24 normal \n", "4 24 normal \n", "... ... ... \n", "139899 14 anomaly \n", "139900 49 normal \n", "139901 22 normal \n", "139902 11 normal \n", "139903 52 anomaly \n", "\n", "[139904 rows x 11 columns]" ], "text/html": [ "\n", "
\n", "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
src_bytesdst_bytesflagdiff_srv_ratelogged_insame_srv_ratedst_host_srv_countdst_host_diff_srv_ratedst_host_same_srv_rateserviceclass
0491090.0001.00250.030.1720normal
1146090.1500.0810.600.0044normal
20050.0700.05260.050.1049anomaly
3232815390.0011.002550.001.0024normal
419942090.0011.002550.001.0024normal
....................................
1398991032090.0001.002550.001.0014anomaly
13990079433390.0011.001410.060.7249normal
13990131793890.0011.002550.001.0022normal
139902424290.0001.002520.010.9911normal
1399030011.0000.25210.030.0852anomaly
\n", "

139904 rows × 11 columns

\n", "
\n", "
\n", "\n", "
\n", " \n", "\n", " \n", "\n", " \n", "
\n", "\n", "\n", "
\n", " \n", "\n", "\n", "\n", " \n", "
\n", "
\n", "
\n" ] }, "metadata": {}, "execution_count": 9 } ] }, { "cell_type": "markdown", "source": [ "# Model" ], "metadata": { "id": "B6B5ZuR5J5WQ" } }, { "cell_type": "code", "source": [ "from sklearn.model_selection import train_test_split\n", "train_data, test_data = train_test_split(finalDf, test_size=0.25)\n", "train_data_file = \"train_data.csv\"\n", "test_data_file = \"test_data.csv\"\n", "\n", "train_data.to_csv(train_data_file, index=False, header=False)\n", "test_data.to_csv(test_data_file, index=False, header=False)" ], "metadata": { "id": "_1N70b_DJb2m" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "CSV_HEADER = []\n", "for x in columns:\n", " CSV_HEADER.append(x)\n", "CSV_HEADER.append(\"class\")\n", "\n", "# A list of the numerical feature names.\n", "NUMERIC_FEATURE_NAMES = columns\n", "# A dictionary of the categorical features and their vocabulary.\n", "CATEGORICAL_FEATURES_WITH_VOCABULARY = {\n", "}\n", "# A list of the columns to ignore from the dataset.\n", "IGNORE_COLUMN_NAMES = []\n", "# A list of the categorical feature names.\n", "CATEGORICAL_FEATURE_NAMES = list(CATEGORICAL_FEATURES_WITH_VOCABULARY.keys())\n", "# A list of all the input features.\n", "FEATURE_NAMES = NUMERIC_FEATURE_NAMES + CATEGORICAL_FEATURE_NAMES\n", "# A list of column default values for each feature.\n", "COLUMN_DEFAULTS = [\n", " [0.0] if feature_name in NUMERIC_FEATURE_NAMES + IGNORE_COLUMN_NAMES else [\"NA\"]\n", " for feature_name in CSV_HEADER\n", "]\n", "# The name of the target feature.\n", "TARGET_FEATURE_NAME = \"class\"\n", "# A list of the labels of the target features.\n", "TARGET_LABELS = [\"normal\", \"anomaly\"]\n", "\n", "from tensorflow.keras.layers import StringLookup\n", "\n", "target_label_lookup = StringLookup(\n", " vocabulary=TARGET_LABELS, mask_token=None, num_oov_indices=0\n", ")\n", "\n", "\n", "def get_dataset_from_csv(csv_file_path, shuffle=False, batch_size=128):\n", " dataset = tf.data.experimental.make_csv_dataset(\n", " csv_file_path,\n", " batch_size=batch_size,\n", " column_names=CSV_HEADER,\n", " column_defaults=COLUMN_DEFAULTS,\n", " label_name=TARGET_FEATURE_NAME,\n", " num_epochs=1,\n", " header=False,\n", " na_value=\"?\",\n", " shuffle=shuffle,\n", " ).map(lambda features, target: (features, target_label_lookup(target)))\n", " return dataset.cache()\n", "\n", "def create_model_inputs():\n", " inputs = {}\n", " for feature_name in FEATURE_NAMES:\n", " if feature_name in NUMERIC_FEATURE_NAMES:\n", " inputs[feature_name] = layers.Input(\n", " name=feature_name, shape=(), dtype=tf.float32\n", " )\n", " else:\n", " inputs[feature_name] = layers.Input(\n", " name=feature_name, shape=(), dtype=tf.string\n", " )\n", " return inputs\n", "\n", "def encode_inputs(inputs):\n", " encoded_features = []\n", " for feature_name in inputs:\n", " if feature_name in CATEGORICAL_FEATURE_NAMES:\n", " vocabulary = CATEGORICAL_FEATURES_WITH_VOCABULARY[feature_name]\n", " #print(vocabulary)\n", " # Create a lookup to convert a string values to an integer indices.\n", " # Since we are not using a mask token, nor expecting any out of vocabulary\n", " # (oov) token, we set mask_token to None and num_oov_indices to 0.\n", " lookup = StringLookup(\n", " vocabulary=vocabulary, mask_token=None, num_oov_indices=0\n", " )\n", " # Convert the string input values into integer indices.\n", " value_index = lookup(inputs[feature_name])\n", " embedding_dims = int(math.sqrt(lookup.vocabulary_size()))\n", " # Create an embedding layer with the specified dimensions.\n", " embedding = layers.Embedding(\n", " input_dim=lookup.vocabulary_size(), output_dim=embedding_dims\n", " )\n", " # Convert the index values to embedding representations.\n", " encoded_feature = embedding(value_index)\n", " else:\n", " # Use the numerical features as-is.\n", " encoded_feature = inputs[feature_name]\n", " if inputs[feature_name].shape[-1] is None:\n", " encoded_feature = tf.expand_dims(encoded_feature, -1)\n", "\n", " encoded_features.append(encoded_feature)\n", "\n", " encoded_features = layers.concatenate(encoded_features)\n", " return encoded_features\n", "\n", "class NeuralDecisionTree(keras.Model):\n", " def __init__(self, depth, num_features, used_features_rate, num_classes):\n", " super().__init__()\n", " self.depth = depth\n", " self.num_leaves = 2 ** depth\n", " self.num_classes = num_classes\n", "\n", " # Create a mask for the randomly selected features.\n", " num_used_features = int(num_features * used_features_rate)\n", " one_hot = np.eye(num_features)\n", " sampled_feature_indicies = np.random.choice(\n", " np.arange(num_features), num_used_features, replace=False\n", " )\n", " self.used_features_mask = one_hot[sampled_feature_indicies]\n", "\n", " # Initialize the weights of the classes in leaves.\n", " self.pi = tf.Variable(\n", " initial_value=tf.random_normal_initializer()(\n", " shape=[self.num_leaves, self.num_classes]\n", " ),\n", " dtype=\"float32\",\n", " trainable=True,\n", " )\n", "\n", " # Initialize the stochastic routing layer.\n", " self.decision_fn = layers.Dense(\n", " units=self.num_leaves, activation=\"sigmoid\", name=\"decision\"\n", " )\n", "\n", " def call(self, features):\n", " batch_size = tf.shape(features)[0]\n", "\n", " # Apply the feature mask to the input features.\n", " features = tf.matmul(\n", " features, self.used_features_mask, transpose_b=True\n", " ) # [batch_size, num_used_features]\n", " # Compute the routing probabilities.\n", " decisions = tf.expand_dims(\n", " self.decision_fn(features), axis=2\n", " ) # [batch_size, num_leaves, 1]\n", " # Concatenate the routing probabilities with their complements.\n", " decisions = layers.concatenate(\n", " [decisions, 1 - decisions], axis=2\n", " ) # [batch_size, num_leaves, 2]\n", "\n", " mu = tf.ones([batch_size, 1, 1])\n", "\n", " begin_idx = 1\n", " end_idx = 2\n", " # Traverse the tree in breadth-first order.\n", " for level in range(self.depth):\n", " mu = tf.reshape(mu, [batch_size, -1, 1]) # [batch_size, 2 ** level, 1]\n", " mu = tf.tile(mu, (1, 1, 2)) # [batch_size, 2 ** level, 2]\n", " level_decisions = decisions[\n", " :, begin_idx:end_idx, :\n", " ] # [batch_size, 2 ** level, 2]\n", " mu = mu * level_decisions # [batch_size, 2**level, 2]\n", " begin_idx = end_idx\n", " end_idx = begin_idx + 2 ** (level + 1)\n", "\n", " mu = tf.reshape(mu, [batch_size, self.num_leaves]) # [batch_size, num_leaves]\n", " probabilities = keras.activations.softmax(self.pi) # [num_leaves, num_classes]\n", " outputs = tf.matmul(mu, probabilities) # [batch_size, num_classes]\n", " return outputs\n", "\n", "class NeuralDecisionForest(keras.Model):\n", " def __init__(self, num_trees, depth, num_features, used_features_rate, num_classes):\n", " super().__init__()\n", " self.ensemble = []\n", " # Initialize the ensemble by adding NeuralDecisionTree instances.\n", " # Each tree will have its own randomly selected input features to use.\n", " for _ in range(num_trees):\n", " self.ensemble.append(\n", " NeuralDecisionTree(depth, num_features, used_features_rate, num_classes)\n", " )\n", "\n", " def call(self, inputs):\n", " # Initialize the outputs: a [batch_size, num_classes] matrix of zeros.\n", " batch_size = tf.shape(inputs)[0]\n", " outputs = tf.zeros([batch_size, num_classes])\n", "\n", " # Aggregate the outputs of trees in the ensemble.\n", " for tree in self.ensemble:\n", " outputs += tree(inputs)\n", " # Divide the outputs by the ensemble size to get the average.\n", " outputs /= len(self.ensemble)\n", " return outputs\n", "learning_rate = 0.01\n", "batch_size = 128\n", "num_epochs = 10\n", "\n", "\n", "def run_experiment(model):\n", "\n", " # model.compile(\n", " # optimizer=keras.optimizers.Adam(learning_rate=learning_rate),\n", " # loss=keras.losses.SparseCategoricalCrossentropy(),\n", " # metrics=[keras.metrics.SparseCategoricalAccuracy()],\n", " # )\n", " model.compile(\n", " optimizer=keras.optimizers.Adam(learning_rate=learning_rate),\n", " loss=keras.losses.SparseCategoricalCrossentropy(),\n", " metrics=[metrics.SparseCategoricalAccuracy()],\n", " )\n", " print(\"Start training the model...\")\n", " train_dataset = get_dataset_from_csv(\n", " train_data_file, shuffle=True, batch_size=batch_size\n", " )\n", "\n", " model.fit(train_dataset, epochs=num_epochs)\n", " print(\"Model training finished\")\n", "\n", " print(\"Evaluating the model on the test data...\")\n", " test_dataset = get_dataset_from_csv(test_data_file, batch_size=batch_size)\n", "\n", " _, accuracy = model.evaluate(test_dataset)\n", " print(f\"Test accuracy: {round(accuracy * 100, 2)}%\")\n", " return model" ], "metadata": { "id": "PGCEwlOPPpEP", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "64231f85-224e-4f74-a503-7635e02f0837" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stderr", "text": [ "/usr/local/lib/python3.10/dist-packages/numpy/core/numeric.py:2463: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison\n", " return bool(asarray(a1 == a2).all())\n" ] } ] }, { "cell_type": "code", "source": [ "num_trees = 25\n", "depth = 5\n", "used_features_rate = 0.5\n", "num_classes = len(TARGET_LABELS)\n", "\n", "\n", "def create_forest_model():\n", " inputs = create_model_inputs()\n", " features = encode_inputs(inputs)\n", " features = layers.BatchNormalization()(features)\n", " num_features = features.shape[1]\n", "\n", " forest_model = NeuralDecisionForest(\n", " num_trees, depth, num_features, used_features_rate, num_classes\n", " )\n", "\n", " outputs = forest_model(features)\n", " model = keras.Model(inputs=inputs, outputs=outputs)\n", " return model\n", "\n", "\n", "forest_model = create_forest_model()\n", "\n", "finalModel = run_experiment(forest_model)" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "vLjxBfBIQUKR", "outputId": "2edcd248-dae4-48e6-9f14-02351e014f56" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Start training the model...\n", "Epoch 1/10\n", "820/820 [==============================] - 76s 44ms/step - loss: 0.2196 - sparse_categorical_accuracy: 0.9087\n", "Epoch 2/10\n", "820/820 [==============================] - 18s 22ms/step - loss: 0.1533 - sparse_categorical_accuracy: 0.9303\n", "Epoch 3/10\n", "820/820 [==============================] - 16s 20ms/step - loss: 0.1423 - sparse_categorical_accuracy: 0.9361\n", "Epoch 4/10\n", "820/820 [==============================] - 18s 22ms/step - loss: 0.1370 - sparse_categorical_accuracy: 0.9392\n", "Epoch 5/10\n", "820/820 [==============================] - 17s 21ms/step - loss: 0.1336 - sparse_categorical_accuracy: 0.9417\n", "Epoch 6/10\n", "820/820 [==============================] - 21s 25ms/step - loss: 0.1315 - sparse_categorical_accuracy: 0.9439\n", "Epoch 7/10\n", "820/820 [==============================] - 18s 22ms/step - loss: 0.1298 - sparse_categorical_accuracy: 0.9454\n", "Epoch 8/10\n", "820/820 [==============================] - 17s 21ms/step - loss: 0.1286 - sparse_categorical_accuracy: 0.9464\n", "Epoch 9/10\n", "820/820 [==============================] - 18s 22ms/step - loss: 0.1276 - sparse_categorical_accuracy: 0.9471\n", "Epoch 10/10\n", "820/820 [==============================] - 17s 20ms/step - loss: 0.1262 - sparse_categorical_accuracy: 0.9484\n", "Model training finished\n", "Evaluating the model on the test data...\n", "274/274 [==============================] - 6s 14ms/step - loss: 0.1234 - sparse_categorical_accuracy: 0.9498\n", "Test accuracy: 94.98%\n" ] } ] }, { "cell_type": "code", "source": [ "test_dataset = get_dataset_from_csv(test_data_file, batch_size=batch_size)\n", "colnames=['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'target']\n", "data = pd.read_csv(\"test_data.csv\", names=colnames, header=None)\n", "data['target'].replace('normal', 0,inplace=True)\n", "data['target'].replace('anomaly', 1,inplace=True)\n", "y_test = data['target'].values # as a numpy array\n", "from sklearn.metrics import confusion_matrix\n", "y_prediction = finalModel.predict(test_dataset)\n", "y_prediction = np.argmax (y_prediction, axis = 1)\n", "result = confusion_matrix(y_test, y_prediction , normalize='pred')\n", "print(result)\n", "TP = result[0][0]\n", "FP = result[0][1]\n", "TN = result[1][1]\n", "FN = result[1][0]\n", "ACC = (TP+TN)/(TP+TN+FP+FN)\n", "PR = TP/(TP+FP) #precision\n", "TPR = TP/(TP+FN) #Recall or True positive rate\n", "FPR = FP/(FP+TN)\n", "F1Score = 2*(PR*TPR)/(PR+TPR)\n", "print(\"ACC: \" + str(ACC))\n", "print(\"PR: \" + str(PR))\n", "print(\"TPR: \" + str(TPR))\n", "print(\"FPR: \" + str(FPR))\n", "print(\"F1Score: \" + str(F1Score))\n" ], "metadata": { "id": "cIZKjFUeWRnr", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "cf4272ac-7502-4367-b5be-d6882609f39b" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "274/274 [==============================] - 5s 9ms/step\n", "[[0.92530258 0.02028617]\n", " [0.07469742 0.97971383]]\n", "ACC: 0.9525082065813029\n", "PR: 0.9785465208664409\n", "TPR: 0.9253025816840683\n", "FPR: 0.02028616852146264\n", "F1Score: 0.9511800287561489\n" ] } ] }, { "cell_type": "code", "source": [ "import matplotlib.pyplot as plt\n", "import numpy\n", "from sklearn import metrics\n", "\n", "\n", "cm_display = metrics.ConfusionMatrixDisplay(confusion_matrix = result, display_labels = [True, False])\n", "\n", "cm_display.plot()\n", "plt.show()" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 449 }, "id": "0go6QErNNH9Z", "outputId": "c6f718b2-dd4f-41be-fbae-7d4d3a244252" }, "execution_count": null, "outputs": [ { "output_type": "display_data", "data": { "text/plain": [ "
" ], "image/png": "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yipUwMXixYtd2nC33Xab/vvf/zo/16tXT0899ZQeeeQRvfrqq26PkZWVpeHDh6tx48aSpGuvvdb5XWpqqnr16qXHH3/c+d3UqVPVoUMHTZ8+3W9e2oTKiQQCFa5t27Yejd+5c6fOnz9fLOkoKChQq1atrAwN+EUdO3bU9OnTnZ9DQ0O1YsUKpaamatu2bcrOzlZRUZHy8vJ0/vx5VatWrdgxhg4dqv79++utt95ScnKy/vSnP+maa66RdLG98c0332j+/PnO8aZpyjAM7dmzR02aNCn7iwRKQAKBChcaGuryOSAgQD9/wnphYaHzvy/1k5csWaL4+HiXcbxbAOUpNDRUDRs2dH7eu3ev/vjHP+rRRx/V008/rRo1aigtLU0PP/ywCgoK3CYQ48aN0/33368lS5bok08+0dixY7VgwQLdeeedysnJ0V//+lcNGjSo2Lyrr766TK8N+DUkEKh0atasqe+++85lX0ZGhrMv3LRpU9ntdmVlZdGuQKWSnp4uwzD04osvKiDg4hKzd95551fnJSYmKjExUUOGDNF9992n2bNn684771Tr1q31/fffuyQpQGXBIkpUOrfeeqs2btyoefPmaceOHRo7dqxLQhEeHq5hw4ZpyJAhmjt3rnbt2qVNmzbplVde0dy5cyswcvi7hg0bqrCwUK+88op2796tt956SzNmzChx/IULF5SSkqLVq1dr3759Wrdunb766itna2LkyJFav369UlJSlJGRoR07dujDDz9kESUqBRIIVDpdunTR6NGjNWLECP3mN7/RuXPn1Lt3b5cxEydO1OjRo5WamqomTZqoa9euWrJkierXr19BUQNSixYtNHnyZD377LO6/vrrNX/+fKWmppY4PjAwUCdPnlTv3r2VmJionj176rbbbtP48eMlSc2bN9eaNWu0fft2tW/fXq1atdKYMWMUFxdXXpcElIjXeQMAAI9RgQAAAB4jgQAAAB4jgQAAAB4jgQAAAB4jgQAAAB4jgQAAAB4jgQAAAB4jgQAAAB4jgQB8RN++fdWjRw/n51tuucX5mufytHr1atlsNp05c6bEMTabTR988EGpjzlu3Di1bNnSq7j27t0rm82mjIwMr44DoHRIIAAv9O3bVzabTTabTUFBQWrYsKEmTJigoqKiMj/3e++9p4kTJ5ZqbGn+0QcAT/A2TsBLXbt21ezZs5Wfn6+lS5fqscceU9WqVfXEE08UG1tQUKCgoCBLzlujRg1LjgMAl4MKBOAlu92u2NhY1a1bV48++qiSk5P10UcfSfqx7fD0008rLi5OjRo1kiTt379fPXv2VPXq1VWjRg11795de/fudR7T4XBo6NChql69uq666iqNGDFCP39tzc9bGPn5+Ro5cqQSEhJkt9vVsGFDvfnmm9q7d686duwoSYqKipLNZlPfvn0lSYZhKDU1VfXr11dISIhatGihd9991+U8S5cuVWJiokJCQtSxY0eXOEtr5MiRSkxMVLVq1dSgQQONHj1ahYWFxca99tprSkhIULVq1dSzZ0+dPXvW5fs33nhDTZo0UXBwsBo3bqxXX33V41gAWIMEArBYSEiICgoKnJ9XrlypzMxMLV++XIsXL1ZhYaG6dOmi8PBwrV27VuvWrVNYWJi6du3qnPfiiy9qzpw5mjVrltLS0nTq1Cm9//77v3je3r176z//+Y+mTp2qrVu36rXXXlNYWJgSEhK0aNEiSVJmZqYOHz6sl19+WZKUmpqqefPmacaMGdqyZYuGDBmiBx54QGvWrJF0MdG566671K1bN2VkZKh///4aNWqUx/+bhIeHa86cOfr+++/18ssva+bMmXrppZdcxuzcuVPvvPOOPv74Yy1btkxff/21Bg4c6Px+/vz5GjNmjJ5++mlt3bpVkyZN0ujRo3mFO1BRTACXrU+fPmb37t1N0zRNwzDM5cuXm3a73Rw2bJjz+5iYGDM/P98556233jIbNWpkGobh3Jefn2+GhISYn376qWmaplm7dm3zueeec35fWFho1qlTx3ku0zTNDh06mIMHDzZN0zQzMzNNSeby5cvdxvnZZ5+ZkszTp0879+Xl5ZnVqlUz169f7zL24YcfNu+77z7TNE3ziSeeMJs2bery/ciRI4sd6+ckme+//36J3z///PNmmzZtnJ/Hjh1rBgYGmgcOHHDu++STT8yAgADz8OHDpmma5jXXXGO+/fbbLseZOHGimZSUZJqmae7Zs8eUZH799dclnheAdVgDAXhp8eLFCgsLU2FhoQzD0P33369x48Y5v2/WrJnLuofNmzdr586dCg8PdzlOXl6edu3apbNnz+rw4cNq166d87sqVaqobdu2xdoYl2RkZCgwMFAdOnQoddw7d+7U+fPn1alTJ5f9BQUFatWqlSRp69atLnFIUlJSUqnPccnChQs1depU7dq1Szk5OSoqKlJERITLmKuvvlrx8fEu5zEMQ5mZmQoPD9euXbv08MMPa8CAAc4xRUVFioyM9DgeAN4jgQC81LFjR02fPl1BQUGKi4tTlSqu/7cKDQ11+ZyTk6M2bdpo/vz5xY5Vs2bNy4ohJCTE4zk5OTmSpCVLlrj8wy1dXNdhlQ0bNqhXr14aP368unTposjISC1YsEAvvviix7HOnDmzWEITGBhoWawASo8EAvBSaGioGjZsWOrxrVu31sKFC1WrVq1if4VfUrt2bX3xxRe6+eabJV38Szs9PV2tW7d2O75Zs2YyDENr1qxRcnJyse8vVUAcDodzX9OmTWW325WVlVVi5aJJkybOBaGX/O9///v1i/yJ9evXq27dunryySed+/bt21dsXFZWlg4dOqS4uDjneQICAtSoUSPFxMQoLi5Ou3fvVq9evTw6P4CywSJKoJz16tVL0dHR6t69u9auXas9e/Zo9erVGjRokA4cOCBJGjx4sJ555hl98MEH2rZtmwYOHPiLz3CoV6+e+vTpo4ceekgffPCB85jvvPOOJKlu3bqy2WxavHixjh8/rpycHIWHh2vYsGEaMmSI5s6dq127dmnTpk165ZVXnAsTH3nkEe3YsUPDhw9XZmam3n77bc2ZM8ej67322muVlZWlBQsWaNeuXZo6darbBaHBwcHq06ePNm/erLVr12rQoEHq2bOnYmNjJUnjx49Xamqqpk6dqu3bt+vbb7/V7NmzNXnyZI/iAWANEgignFWrVk2ff/65rr76at11111q0qSJHn74YeXl5TkrEn//+9/14IMPqk+fPkpKSlJ4eLjuvPPOXzzu9OnTdc8992jgwIFq3LixBgwYoNzcXElSfHy8xo8fr1GjRikmJkYpKSmSpIkTJ2r06NFKTU1VkyZN1LVrVy1ZskT169eXdHFdwqJFi/TBBx+oRYsWmjFjhiZNmuTR9d5xxx0aMmSIUlJS1LJlS61fv16jR48uNq5hw4a66667dPvtt6tz585q3ry5y22a/fv31xtvvKHZs2erWbNm6tChg+bMmeOMFUD5spklrcoCAAAoARUIAADgMRIIAADgMRIIAADgMRIIAADgMRIIAADgMRIIAADgMRIIAADgMRIIAADgMRIIAADgMRIIAADgMRIIAADgsf8HvQT0Fi58f1EAAAAASUVORK5CYII=\n" }, "metadata": {} } ] }, { "cell_type": "markdown", "source": [ "## CISIDS2017" ], "metadata": { "id": "lx4RpN47ybt6" } }, { "cell_type": "code", "source": [ "from google.colab import drive\n", "drive.mount('/content/drive')" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "kHU-PAI6yhyz", "outputId": "4e95ed59-4691-4fcc-cf81-e13ee713dfdd" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Mounted at /content/drive\n" ] } ] }, { "cell_type": "code", "source": [ "import pandas as pd\n", "import glob\n", "import os\n", "\n", "path = r'C:\\DRO\\DCL_rawdata_files' # use your path\n", "all_files = glob.glob(os.path.join(path , \"/content/drive/MyDrive/datasets/CISIDS2017/*.csv\"))\n", "\n", "li = []\n", "\n", "for filename in all_files:\n", " df = pd.read_csv(filename, index_col=None, header=0)\n", " li.append(df)\n", "\n", "frame = pd.concat(li, axis=0, ignore_index=True)" ], "metadata": { "id": "orUywltc2yaK" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "column_headers = list(frame.columns.values)\n", "column_headers" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "eJcGQKBn4Abs", "outputId": "da7510b0-433e-4504-80e6-1923149d460d" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "[' Destination Port',\n", " ' Flow Duration',\n", " ' Total Fwd Packets',\n", " ' Total Backward Packets',\n", " 'Total Length of Fwd Packets',\n", " ' Total Length of Bwd Packets',\n", " ' Fwd Packet Length Max',\n", " ' Fwd Packet Length Min',\n", " ' Fwd Packet Length Mean',\n", " ' Fwd Packet Length Std',\n", " 'Bwd Packet Length Max',\n", " ' Bwd Packet Length Min',\n", " ' Bwd Packet Length Mean',\n", " ' Bwd Packet Length Std',\n", " 'Flow Bytes/s',\n", " ' Flow Packets/s',\n", " ' Flow IAT Mean',\n", " ' Flow IAT Std',\n", " ' Flow IAT Max',\n", " ' Flow IAT Min',\n", " 'Fwd IAT Total',\n", " ' Fwd IAT Mean',\n", " ' Fwd IAT Std',\n", " ' Fwd IAT Max',\n", " ' Fwd IAT Min',\n", " 'Bwd IAT Total',\n", " ' Bwd IAT Mean',\n", " ' Bwd IAT Std',\n", " ' Bwd IAT Max',\n", " ' Bwd IAT Min',\n", " 'Fwd PSH Flags',\n", " ' Bwd PSH Flags',\n", " ' Fwd URG Flags',\n", " ' Bwd URG Flags',\n", " ' Fwd Header Length',\n", " ' Bwd Header Length',\n", " 'Fwd Packets/s',\n", " ' Bwd Packets/s',\n", " ' Min Packet Length',\n", " ' Max Packet Length',\n", " ' Packet Length Mean',\n", " ' Packet Length Std',\n", " ' Packet Length Variance',\n", " 'FIN Flag Count',\n", " ' SYN Flag Count',\n", " ' RST Flag Count',\n", " ' PSH Flag Count',\n", " ' ACK Flag Count',\n", " ' URG Flag Count',\n", " ' CWE Flag Count',\n", " ' ECE Flag Count',\n", " ' Down/Up Ratio',\n", " ' Average Packet Size',\n", " ' Avg Fwd Segment Size',\n", " ' Avg Bwd Segment Size',\n", " ' Fwd Header Length.1',\n", " 'Fwd Avg Bytes/Bulk',\n", " ' Fwd Avg Packets/Bulk',\n", " ' Fwd Avg Bulk Rate',\n", " ' Bwd Avg Bytes/Bulk',\n", " ' Bwd Avg Packets/Bulk',\n", " 'Bwd Avg Bulk Rate',\n", " 'Subflow Fwd Packets',\n", " ' Subflow Fwd Bytes',\n", " ' Subflow Bwd Packets',\n", " ' Subflow Bwd Bytes',\n", " 'Init_Win_bytes_forward',\n", " ' Init_Win_bytes_backward',\n", " ' act_data_pkt_fwd',\n", " ' min_seg_size_forward',\n", " 'Active Mean',\n", " ' Active Std',\n", " ' Active Max',\n", " ' Active Min',\n", " 'Idle Mean',\n", " ' Idle Std',\n", " ' Idle Max',\n", " ' Idle Min',\n", " ' Label']" ] }, "metadata": {}, "execution_count": 3 } ] }, { "cell_type": "code", "source": [ "frame[' Label'] = frame[' Label'].str.replace(r\"^(.(?:1: FutureWarning: The default value of regex will change from True to False in a future version.\n", " frame[' Label'] = frame[' Label'].str.replace(r\"^(.(? (Q3 + 1.5 * IQR)))\n", " trueList = ~((df < (Q1 - 1.5 * IQR)) |(df > (Q3 + 1.5 * IQR))).any(axis=1)\n", " return trueList\n", "\n", "nonOutlierList = Remove_Outlier_Indices(df)\n", "new_data = df[nonOutlierList]\n", "\n" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "96cb862f-57f8-4621-e93a-f1d9d3994ae4", "id": "i_PXbZyt5-fR" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stderr", "text": [ ":2: FutureWarning: The default value of numeric_only in DataFrame.quantile is deprecated. In a future version, it will default to False. Select only valid columns or specify the value of numeric_only to silence this warning.\n", " Q1 = df.quantile(0.02)\n", ":3: FutureWarning: The default value of numeric_only in DataFrame.quantile is deprecated. In a future version, it will default to False. Select only valid columns or specify the value of numeric_only to silence this warning.\n", " Q3 = df.quantile(0.98)\n", ":6: FutureWarning: Automatic reindexing on DataFrame vs Series comparisons is deprecated and will raise ValueError in a future version. Do `left, right = left.align(right, axis=1, copy=False)` before e.g. `left == right`\n", " trueList = ~((df < (Q1 - 1.5 * IQR)) |(df > (Q3 + 1.5 * IQR))).any(axis=1)\n" ] } ] }, { "cell_type": "code", "source": [ "df = new_data\n", "df = df.reset_index(drop=True)\n", "del new_data\n", "del nonOutlierList\n", "del li\n", "del frame" ], "metadata": { "id": "FSPL95xg5-fT" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [], "metadata": { "id": "EeRnvhzX5-fV" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "from sklearn.ensemble import RandomForestClassifier\n", "X = df.drop([' Label'], axis=1)\n", "Y = df[\" Label\"].astype('category').cat.codes\n", "clf = RandomForestClassifier()\n", "clf.fit(X, Y)\n", "features = pd.Series(clf.feature_importances_, index=X.columns)\n", "features.sort_values(ascending=False, inplace=True)\n", "print(features.head(10))" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "ce896ae8-6065-417d-a3dc-796d8befbd13", "id": "uYcOxDMSW0qE" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ " Packet Length Variance 0.081247\n", " Packet Length Std 0.064861\n", " Packet Length Mean 0.056147\n", " Average Packet Size 0.050787\n", " Destination Port 0.049960\n", "Bwd Packet Length Max 0.043204\n", " Avg Bwd Segment Size 0.037427\n", "Total Length of Fwd Packets 0.034475\n", " Bwd Packet Length Mean 0.032600\n", "Init_Win_bytes_forward 0.030282\n", "dtype: float64\n" ] } ] }, { "cell_type": "code", "source": [ "fs = features.head(10).to_dict()\n", "fs" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "16d8a6f1-c828-4acf-8614-d93af9375a38", "id": "M3KuY2m0W0qF" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "{' Packet Length Variance': 0.08124749509423052,\n", " ' Packet Length Std': 0.06486087933746576,\n", " ' Packet Length Mean': 0.05614707208016595,\n", " ' Average Packet Size': 0.050786574411919236,\n", " ' Destination Port': 0.049960097719106485,\n", " 'Bwd Packet Length Max': 0.04320449616955384,\n", " ' Avg Bwd Segment Size': 0.037427049333999665,\n", " 'Total Length of Fwd Packets': 0.0344753595505703,\n", " ' Bwd Packet Length Mean': 0.03260012604646854,\n", " 'Init_Win_bytes_forward': 0.03028173100385773}" ] }, "metadata": {}, "execution_count": 10 } ] }, { "cell_type": "code", "source": [ "columns = []\n", "for k in fs:\n", " columns.append(str(k))\n", "columns" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "8aa71c49-3bf6-424c-dffd-892fbfa3ef84", "id": "Ds6Ia777W0qG" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "[' Packet Length Variance',\n", " ' Packet Length Std',\n", " ' Packet Length Mean',\n", " ' Average Packet Size',\n", " ' Destination Port',\n", " 'Bwd Packet Length Max',\n", " ' Avg Bwd Segment Size',\n", " 'Total Length of Fwd Packets',\n", " ' Bwd Packet Length Mean',\n", " 'Init_Win_bytes_forward']" ] }, "metadata": {}, "execution_count": 11 } ] }, { "cell_type": "code", "source": [ "principalDf = pd.DataFrame(data = X\n", " , columns = columns)\n", "finalDf = pd.concat([principalDf, df[\" Label\"]], axis = 1)\n", "finalDf" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 441 }, "outputId": "ad223296-2c36-4346-eb6b-50fb72488f8d", "id": "Js17hn8LW0qI" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ " Packet Length Variance Packet Length Std Packet Length Mean \\\n", "0 0.000000 0.000000 6.000000 \n", "1 0.000000 0.000000 6.000000 \n", "2 0.000000 0.000000 6.000000 \n", "3 0.000000 0.000000 6.000000 \n", "4 0.000000 0.000000 6.000000 \n", "... ... ... ... \n", "2539413 548.571429 23.421602 41.714286 \n", "2539414 5796.300000 76.133435 97.600000 \n", "2539415 267.333333 16.350331 17.000000 \n", "2539416 1792.000000 42.332021 53.333333 \n", "2539417 1037.142857 32.204702 65.857143 \n", "\n", " Average Packet Size Destination Port Bwd Packet Length Max \\\n", "0 9.000000 54865 0 \n", "1 9.000000 55054 6 \n", "2 9.000000 55055 6 \n", "3 9.000000 46236 6 \n", "4 9.000000 54863 0 \n", "... ... ... ... \n", "2539413 48.666667 53 76 \n", "2539414 122.000000 53 181 \n", "2539415 22.666667 58030 6 \n", "2539416 60.000000 53 128 \n", "2539417 76.833333 53 113 \n", "\n", " Avg Bwd Segment Size Total Length of Fwd Packets \\\n", "0 0.0 12 \n", "1 6.0 6 \n", "2 6.0 6 \n", "3 6.0 6 \n", "4 0.0 12 \n", "... ... ... \n", "2539413 76.0 112 \n", "2539414 181.0 84 \n", "2539415 6.0 31 \n", "2539416 128.0 192 \n", "2539417 113.0 188 \n", "\n", " Bwd Packet Length Mean Init_Win_bytes_forward Label \n", "0 0.0 33 BENIGN \n", "1 6.0 29 BENIGN \n", "2 6.0 29 BENIGN \n", "3 6.0 31 BENIGN \n", "4 0.0 32 BENIGN \n", "... ... ... ... \n", "2539413 76.0 -1 BENIGN \n", "2539414 181.0 -1 BENIGN \n", "2539415 6.0 1006 BENIGN \n", "2539416 128.0 -1 BENIGN \n", "2539417 113.0 -1 BENIGN \n", "\n", "[2539418 rows x 11 columns]" ], "text/html": [ "\n", "
\n", "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Packet Length VariancePacket Length StdPacket Length MeanAverage Packet SizeDestination PortBwd Packet Length MaxAvg Bwd Segment SizeTotal Length of Fwd PacketsBwd Packet Length MeanInit_Win_bytes_forwardLabel
00.0000000.0000006.0000009.0000005486500.0120.033BENIGN
10.0000000.0000006.0000009.0000005505466.066.029BENIGN
20.0000000.0000006.0000009.0000005505566.066.029BENIGN
30.0000000.0000006.0000009.0000004623666.066.031BENIGN
40.0000000.0000006.0000009.0000005486300.0120.032BENIGN
....................................
2539413548.57142923.42160241.71428648.666667537676.011276.0-1BENIGN
25394145796.30000076.13343597.600000122.00000053181181.084181.0-1BENIGN
2539415267.33333316.35033117.00000022.6666675803066.0316.01006BENIGN
25394161792.00000042.33202153.33333360.00000053128128.0192128.0-1BENIGN
25394171037.14285732.20470265.85714376.83333353113113.0188113.0-1BENIGN
\n", "

2539418 rows × 11 columns

\n", "
\n", "
\n", "\n", "
\n", " \n", "\n", " \n", "\n", " \n", "
\n", "\n", "\n", "
\n", " \n", "\n", "\n", "\n", " \n", "
\n", "
\n", "
\n" ] }, "metadata": {}, "execution_count": 12 } ] }, { "cell_type": "code", "source": [ "from sklearn.model_selection import train_test_split\n", "train_data, test_data = train_test_split(finalDf, test_size=0.25)\n", "train_data_file = \"train_data.csv\"\n", "test_data_file = \"test_data.csv\"\n", "\n", "train_data.to_csv(train_data_file, index=False, header=False)\n", "test_data.to_csv(test_data_file, index=False, header=False)" ], "metadata": { "id": "c85wOCBv5-fX" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "del finalDf\n", "del principalDf\n", "del train_data\n", "del test_data\n", "del clf\n", "del X\n", "del Y" ], "metadata": { "id": "szYkC6TjPd0U" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "import tensorflow as tf\n", "import numpy as np\n", "import pandas as pd\n", "from tensorflow import keras\n", "from tensorflow.keras import layers\n", "from keras import losses\n", "from keras import optimizers\n", "from keras import metrics\n", "import math\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "\n", "CSV_HEADER = []\n", "for x in columns:\n", " CSV_HEADER.append(x)\n", "CSV_HEADER.append(\" Label\")\n", "\n", "# A list of the numerical feature names.\n", "NUMERIC_FEATURE_NAMES = columns\n", "# A dictionary of the categorical features and their vocabulary.\n", "CATEGORICAL_FEATURES_WITH_VOCABULARY = {\n", "}\n", "# A list of the columns to ignore from the dataset.\n", "IGNORE_COLUMN_NAMES = []\n", "# A list of the categorical feature names.\n", "CATEGORICAL_FEATURE_NAMES = list(CATEGORICAL_FEATURES_WITH_VOCABULARY.keys())\n", "# A list of all the input features.\n", "FEATURE_NAMES = NUMERIC_FEATURE_NAMES + CATEGORICAL_FEATURE_NAMES\n", "# A list of column default values for each feature.\n", "COLUMN_DEFAULTS = [\n", " [0.0] if feature_name in NUMERIC_FEATURE_NAMES + IGNORE_COLUMN_NAMES else [\"NA\"]\n", " for feature_name in CSV_HEADER\n", "]\n", "# The name of the target feature.\n", "TARGET_FEATURE_NAME = \" Label\"\n", "# A list of the labels of the target features.\n", "TARGET_LABELS = [\"BENIGN\", \"ANOMALY\"]\n", "\n", "from tensorflow.keras.layers import StringLookup\n", "\n", "target_label_lookup = StringLookup(\n", " vocabulary=TARGET_LABELS, mask_token=None, num_oov_indices=0\n", ")\n", "\n", "\n", "def get_dataset_from_csv(csv_file_path, shuffle=False, batch_size=128):\n", " dataset = tf.data.experimental.make_csv_dataset(\n", " csv_file_path,\n", " batch_size=batch_size,\n", " column_names=CSV_HEADER,\n", " column_defaults=COLUMN_DEFAULTS,\n", " label_name=TARGET_FEATURE_NAME,\n", " num_epochs=1,\n", " header=False,\n", " na_value=\"?\",\n", " shuffle=shuffle,\n", " ).map(lambda features, target: (features, target_label_lookup(target)))\n", " return dataset.cache()\n", "\n", "def create_model_inputs():\n", " inputs = {}\n", " for feature_name in FEATURE_NAMES:\n", " if feature_name in NUMERIC_FEATURE_NAMES:\n", " inputs[feature_name] = layers.Input(\n", " name=feature_name, shape=(), dtype=tf.float32\n", " )\n", " else:\n", " inputs[feature_name] = layers.Input(\n", " name=feature_name, shape=(), dtype=tf.string\n", " )\n", " return inputs\n", "\n", "def encode_inputs(inputs):\n", " encoded_features = []\n", " for feature_name in inputs:\n", " if feature_name in CATEGORICAL_FEATURE_NAMES:\n", " vocabulary = CATEGORICAL_FEATURES_WITH_VOCABULARY[feature_name]\n", " #print(vocabulary)\n", " # Create a lookup to convert a string values to an integer indices.\n", " # Since we are not using a mask token, nor expecting any out of vocabulary\n", " # (oov) token, we set mask_token to None and num_oov_indices to 0.\n", " lookup = StringLookup(\n", " vocabulary=vocabulary, mask_token=None, num_oov_indices=0\n", " )\n", " # Convert the string input values into integer indices.\n", " value_index = lookup(inputs[feature_name])\n", " embedding_dims = int(math.sqrt(lookup.vocabulary_size()))\n", " # Create an embedding layer with the specified dimensions.\n", " embedding = layers.Embedding(\n", " input_dim=lookup.vocabulary_size(), output_dim=embedding_dims\n", " )\n", " # Convert the index values to embedding representations.\n", " encoded_feature = embedding(value_index)\n", " else:\n", " # Use the numerical features as-is.\n", " encoded_feature = inputs[feature_name]\n", " if inputs[feature_name].shape[-1] is None:\n", " encoded_feature = tf.expand_dims(encoded_feature, -1)\n", "\n", " encoded_features.append(encoded_feature)\n", "\n", " encoded_features = layers.concatenate(encoded_features)\n", " return encoded_features\n", "\n", "class NeuralDecisionTree(keras.Model):\n", " def __init__(self, depth, num_features, used_features_rate, num_classes):\n", " super().__init__()\n", " self.depth = depth\n", " self.num_leaves = 2 ** depth\n", " self.num_classes = num_classes\n", "\n", " # Create a mask for the randomly selected features.\n", " num_used_features = int(num_features * used_features_rate)\n", " one_hot = np.eye(num_features)\n", " sampled_feature_indicies = np.random.choice(\n", " np.arange(num_features), num_used_features, replace=False\n", " )\n", " self.used_features_mask = one_hot[sampled_feature_indicies]\n", "\n", " # Initialize the weights of the classes in leaves.\n", " self.pi = tf.Variable(\n", " initial_value=tf.random_normal_initializer()(\n", " shape=[self.num_leaves, self.num_classes]\n", " ),\n", " dtype=\"float32\",\n", " trainable=True,\n", " )\n", "\n", " # Initialize the stochastic routing layer.\n", " self.decision_fn = layers.Dense(\n", " units=self.num_leaves, activation=\"sigmoid\", name=\"decision\"\n", " )\n", "\n", " def call(self, features):\n", " batch_size = tf.shape(features)[0]\n", "\n", " # Apply the feature mask to the input features.\n", " features = tf.matmul(\n", " features, self.used_features_mask, transpose_b=True\n", " ) # [batch_size, num_used_features]\n", " # Compute the routing probabilities.\n", " decisions = tf.expand_dims(\n", " self.decision_fn(features), axis=2\n", " ) # [batch_size, num_leaves, 1]\n", " # Concatenate the routing probabilities with their complements.\n", " decisions = layers.concatenate(\n", " [decisions, 1 - decisions], axis=2\n", " ) # [batch_size, num_leaves, 2]\n", "\n", " mu = tf.ones([batch_size, 1, 1])\n", "\n", " begin_idx = 1\n", " end_idx = 2\n", " # Traverse the tree in breadth-first order.\n", " for level in range(self.depth):\n", " mu = tf.reshape(mu, [batch_size, -1, 1]) # [batch_size, 2 ** level, 1]\n", " mu = tf.tile(mu, (1, 1, 2)) # [batch_size, 2 ** level, 2]\n", " level_decisions = decisions[\n", " :, begin_idx:end_idx, :\n", " ] # [batch_size, 2 ** level, 2]\n", " mu = mu * level_decisions # [batch_size, 2**level, 2]\n", " begin_idx = end_idx\n", " end_idx = begin_idx + 2 ** (level + 1)\n", "\n", " mu = tf.reshape(mu, [batch_size, self.num_leaves]) # [batch_size, num_leaves]\n", " probabilities = keras.activations.softmax(self.pi) # [num_leaves, num_classes]\n", " outputs = tf.matmul(mu, probabilities) # [batch_size, num_classes]\n", " return outputs\n", "\n", "class NeuralDecisionForest(keras.Model):\n", " def __init__(self, num_trees, depth, num_features, used_features_rate, num_classes):\n", " super().__init__()\n", " self.ensemble = []\n", " # Initialize the ensemble by adding NeuralDecisionTree instances.\n", " # Each tree will have its own randomly selected input features to use.\n", " for _ in range(num_trees):\n", " self.ensemble.append(\n", " NeuralDecisionTree(depth, num_features, used_features_rate, num_classes)\n", " )\n", "\n", " def call(self, inputs):\n", " # Initialize the outputs: a [batch_size, num_classes] matrix of zeros.\n", " batch_size = tf.shape(inputs)[0]\n", " outputs = tf.zeros([batch_size, num_classes])\n", "\n", " # Aggregate the outputs of trees in the ensemble.\n", " for tree in self.ensemble:\n", " outputs += tree(inputs)\n", " # Divide the outputs by the ensemble size to get the average.\n", " outputs /= len(self.ensemble)\n", " return outputs\n", "learning_rate = 0.01\n", "batch_size = 128\n", "num_epochs = 10\n", "\n", "\n", "def run_experiment(model):\n", "\n", " # model.compile(\n", " # optimizer=keras.optimizers.Adam(learning_rate=learning_rate),\n", " # loss=keras.losses.SparseCategoricalCrossentropy(),\n", " # metrics=[keras.metrics.SparseCategoricalAccuracy()],\n", " # )\n", " model.compile(\n", " optimizer=keras.optimizers.Adam(learning_rate=learning_rate),\n", " loss=keras.losses.SparseCategoricalCrossentropy(),\n", " metrics=[metrics.SparseCategoricalAccuracy()],\n", " )\n", " print(\"Start training the model...\")\n", " train_dataset = get_dataset_from_csv(\n", " train_data_file, shuffle=True, batch_size=batch_size\n", " )\n", "\n", " model.fit(train_dataset, epochs=num_epochs)\n", " print(\"Model training finished\")\n", "\n", " print(\"Evaluating the model on the test data...\")\n", " test_dataset = get_dataset_from_csv(test_data_file, batch_size=batch_size)\n", "\n", " _, accuracy = model.evaluate(test_dataset)\n", " print(f\"Test accuracy: {round(accuracy * 100, 2)}%\")\n", " return model" ], "metadata": { "id": "8txIkhqk5-fX", "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "64697009-140c-4682-aeea-7ab2060fe6f6" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stderr", "text": [ "/usr/local/lib/python3.10/dist-packages/numpy/core/numeric.py:2463: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison\n", " return bool(asarray(a1 == a2).all())\n" ] } ] }, { "cell_type": "code", "source": [ "num_trees = 25\n", "depth = 5\n", "used_features_rate = 0.5\n", "num_classes = len(TARGET_LABELS)\n", "\n", "\n", "def create_forest_model():\n", " inputs = create_model_inputs()\n", " features = encode_inputs(inputs)\n", " features = layers.BatchNormalization()(features)\n", " num_features = features.shape[1]\n", "\n", " forest_model = NeuralDecisionForest(\n", " num_trees, depth, num_features, used_features_rate, num_classes\n", " )\n", "\n", " outputs = forest_model(features)\n", " model = keras.Model(inputs=inputs, outputs=outputs)\n", " return model\n", "\n", "\n", "forest_model = create_forest_model()\n", "\n", "finalModel = run_experiment(forest_model)" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "3843437d-e139-434d-9d9c-ef64cdf93845", "id": "cEIPaJMi5-fa" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Start training the model...\n", "Epoch 1/10\n", "14880/14880 [==============================] - 474s 29ms/step - loss: 0.2110 - sparse_categorical_accuracy: 0.8957\n", "Epoch 2/10\n", "14880/14880 [==============================] - 354s 24ms/step - loss: 0.1903 - sparse_categorical_accuracy: 0.9018\n", "Epoch 3/10\n", "14880/14880 [==============================] - 353s 24ms/step - loss: 0.1861 - sparse_categorical_accuracy: 0.9025\n", "Epoch 4/10\n", "14880/14880 [==============================] - 379s 25ms/step - loss: 0.1827 - sparse_categorical_accuracy: 0.9029\n", "Epoch 5/10\n", "14880/14880 [==============================] - 375s 25ms/step - loss: 0.1809 - sparse_categorical_accuracy: 0.9032\n", "Epoch 6/10\n", "14880/14880 [==============================] - 378s 25ms/step - loss: 0.1792 - sparse_categorical_accuracy: 0.9037\n", "Epoch 7/10\n", "14880/14880 [==============================] - 373s 25ms/step - loss: 0.1784 - sparse_categorical_accuracy: 0.9039\n", "Epoch 8/10\n", "14880/14880 [==============================] - 374s 25ms/step - loss: 0.1780 - sparse_categorical_accuracy: 0.9040\n", "Epoch 9/10\n", "14880/14880 [==============================] - 375s 25ms/step - loss: 0.1776 - sparse_categorical_accuracy: 0.9041\n", "Epoch 10/10\n", "14880/14880 [==============================] - 375s 25ms/step - loss: 0.1773 - sparse_categorical_accuracy: 0.9042\n", "Model training finished\n", "Evaluating the model on the test data...\n", "4960/4960 [==============================] - 62s 12ms/step - loss: 0.1709 - sparse_categorical_accuracy: 0.9068\n", "Test accuracy: 90.68%\n" ] } ] }, { "cell_type": "code", "source": [ "test_dataset = get_dataset_from_csv(test_data_file, batch_size=batch_size)\n", "colnames=['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'target']\n", "data = pd.read_csv(\"test_data.csv\", names=colnames, header=None)\n", "data['target'].replace('BENIGN', 0,inplace=True)\n", "data['target'].replace('ANOMALY', 1,inplace=True)\n", "y_test = data['target'].values # as a numpy array\n", "from sklearn.metrics import confusion_matrix\n", "y_prediction = finalModel.predict(test_dataset)\n", "y_prediction = np.argmax (y_prediction, axis = 1)\n", "result = confusion_matrix(y_test, y_prediction , normalize='pred')\n", "print(result)\n", "TP = result[0][0]\n", "FP = result[0][1]\n", "TN = result[1][1]\n", "FN = result[1][0]\n", "ACC = (TP+TN)/(TP+TN+FP+FN)\n", "PR = TP/(TP+FP) #precision\n", "TPR = TP/(TP+FN) #Recall or True positive rate\n", "FPR = FP/(FP+TN)\n", "F1Score = 2*(PR*TPR)/(PR+TPR)\n", "print(\"ACC: \" + str(ACC))\n", "print(\"PR: \" + str(PR))\n", "print(\"TPR: \" + str(TPR))\n", "print(\"FPR: \" + str(FPR))\n", "print(\"F1Score: \" + str(F1Score))\n", "import matplotlib.pyplot as plt\n", "import numpy\n", "from sklearn import metrics\n", "\n", "\n", "cm_display = metrics.ConfusionMatrixDisplay(confusion_matrix = result, display_labels = [True, False])\n", "\n", "cm_display.plot()\n", "plt.show()" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "EJpkW1Ibmy7w", "outputId": "782a7032-fb76-4a75-add6-f0e2d3099b1c" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "4960/4960 [==============================] - 59s 12ms/step\n", "[[0.90649143 0.090948 ]\n", " [0.09350857 0.909052 ]]\n", "ACC: 0.9077717104763482\n", "PR: 0.9088185189042338\n", "TPR: 0.906491425484989\n", "FPR: 0.09094800453229258\n", "F1Score: 0.9076534806135734\n" ] }, { "output_type": "display_data", "data": { "text/plain": [ "
" ], "image/png": "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xoJNlGh0fvnLX2rDhg1atmyZvv32W6WnpzsFFCEhIRo/frzGjRunF154QTt37tTGjRs1f/58vfDCCw24cjR1nTp1UmVlpebPn69du3bpxRdfVFZWVo39T548qdTUVK1evVrfffedPvnkE33++eeO0sSECRO0du1apaamatOmTfr222/173//m02UaBQIINDoJCUlafLkyXrkkUd05ZVX6tixYxoxYoRTnxkzZmjy5MnKyMhQt27dNGTIEK1cuVIdOnRooFUDUq9evTR37lz99a9/1eWXX67ly5crIyOjxv6+vr764YcfNGLECHXu3Fm33367rr/+ek2bNk2S1LNnT61Zs0bbt29X//791bt3b02ZMkVt27atr0sCasTrvAEAgNvIQAAAALcRQAAAALcRQAAAALcRQAAAALcRQAAAALcRQAAAALcRQAAAALcRQABeYuTIkbrlllscnwcOHOh4S2N9Wr16tSwWi44cOVJjH4vFojfffNPlc06dOlVxcXEerWvPnj2yWCzatGmTR+cB4BoCCMADI0eOlMVikcVikb+/vzp16qTp06erqqqqzud+/fXXNWPGDJf6uvJLHwDcwcu0AA8NGTJEzz//vMrLy/Xuu+/qgQcekJ+fnyZNmnRO34qKCvn7+5syb8uWLU05DwDUBhkIwENWq1VRUVFq37697r//fiUmJuqtt96SdLbsMGvWLLVt21ZdunSRJBUWFur2229XeHi4WrZsqZtvvll79uxxnNNmsyktLU3h4eG66KKL9Mgjj5zzivOfljDKy8s1YcIExcTEyGq1qlOnTnruuee0Z88eDRo0SJLUokULWSwWjRw5UpJkt9uVkZGhDh06KDAwUL169dKrr77qNM+7776rzp07KzAwUIMGDXJap6smTJigzp07KygoSB07dtTkyZOdXtF+xqJFixQTE6OgoCDdfvvtOnr0qNP3zz77rLp166aAgAB17dpVTz/9tNtrAWAOAgjAZIGBgaqoqHB8zsnJUX5+vj744AO98847qqysVFJSkkJCQvTxxx/rk08+UfPmzTVkyBDHuCeeeEJLly7VkiVLlJubq0OHDumNN94477wjRozQP//5T82bN09bt27VokWL1Lx5c8XExOi1116TJOXn52v//v166qmnJEkZGRlatmyZsrKytHnzZo0bN05333231qxZI+l0oHPrrbfqxhtv1KZNmzRq1ChNnDjR7f+dhISEaOnSpdqyZYueeuopLV68WE8++aRTnx07duiVV17R22+/rVWrVumLL77QmDFjHN8vX75cU6ZM0axZs7R161bNnj1bkydP5g2sQEMxANRacnKycfPNNxuGYRh2u9344IMPDKvVaowfP97xfWRkpFFeXu4Y8+KLLxpdunQx7Ha741h5ebkRGBhovP/++4ZhGEabNm2Mv/3tb47vKysrjXbt2jnmMgzDGDBggDF27FjDMAwjPz/fkGR88MEH1a7zv//9ryHJOHz4sOPYqVOnjKCgIGPt2rVOfe+9917jzjvvNAzDMCZNmmR0797d6fsJEyacc66fkmS88cYbNX4/Z84cIz4+3vE5PT3d8PX1Nfbu3es49t577xk+Pj7G/v37DcMwjEsuucR4+eWXnc4zY8YMIyEhwTAMw9i9e7chyfjiiy9qnBeAedgDAXjonXfeUfPmzVVZWSm73a677rpLU6dOdXzfo0cPp30PX375pXbs2KGQkBCn85w6dUo7d+7U0aNHtX//fvXr18/xXbNmzdSnT59zyhhnbNq0Sb6+vhowYIDL696xY4dOnDih6667zul4RUWFevfuLUnaunWr0zokKSEhweU5zsjOzta8efO0c+dOHT9+XFVVVQoNDXXqc/HFFys6OtppHrvdrvz8fIWEhGjnzp269957NXr0aEefqqoqhYWFub0eAJ4jgAA8NGjQIC1cuFD+/v5q27atmjVz/n+r4OBgp8/Hjx9XfHy8li9ffs65WrVqVas1BAYGuj3m+PHjkqSVK1c6/eKWTu/rMMu6des0fPhwTZs2TUlJSQoLC9OKFSv0xBNPuL3WxYsXnxPQ+Pr6mrZWAK4jgAA8FBwcrE6dOrnc/4orrlB2drZat259zl/hZ7Rp00afffaZrr32Wkmn/9LOy8vTFVdcUW3/Hj16yG63a82aNUpMTDzn+zMZEJvN5jjWvXt3Wa1WFRQU1Ji56Natm2ND6Bmffvrpz1/kj6xdu1bt27fXo48+6jj23XffndOvoKBA33//vdq2beuYx8fHR126dFFkZKTatm2rXbt2afjw4W7ND6BusIkSqGfDhw9XRESEbr75Zn388cfavXu3Vq9erQcffFB79+6VJI0dO1aPP/643nzzTW3btk1jxow57zMcYmNjlZycrHvuuUdvvvmm45yvvPKKJKl9+/ayWCx65513dPDgQR0/flwhISEaP368xo0bpxdeeEE7d+7Uxo0bNX/+fMfGxPvuu0/ffvutHn74YeXn5+vll1/W0qVL3breSy+9VAUFBVqxYoV27typefPmVbshNCAgQMnJyfryyy/18ccf68EHH9Ttt9+uqKgoSdK0adOUkZGhefPmafv27fr666/1/PPPa+7cuW6tB4A5CCCAehYUFKT/+7//08UXX6xbb71V3bp107333qtTp045MhJ//vOf9fvf/17JyclKSEhQSEiIhg4det7zLly4UL/97W81ZswYde3aVaNHj1ZZWZkkKTo6WtOmTdPEiRMVGRmp1NRUSdKMGTM0efJkZWRkqFu3bhoyZIhWrlypDh06SDq9L+G1117Tm2++qV69eikrK0uzZ89263pvuukmjRs3TqmpqYqLi9PatWs1efLkc/p16tRJt956q379619r8ODB6tmzp9NtmqNGjdKzzz6r559/Xj169NCAAQO0dOlSx1oB1C+LUdOuLAAAgBqQgQAAAG4jgAAAAG4jgAAAAG4jgAAAAG4jgAAAAG4jgAAAAG4jgAAAAG4jgAAAAG4jgAAAAG4jgAAAAG4jgAAAAG4jgAAAAG77f98eoW3YepzCAAAAAElFTkSuQmCC\n" }, "metadata": {} } ] }, { "cell_type": "markdown", "source": [ "## UNSW-NB15" ], "metadata": { "id": "XXzn-n1vx0F_" } }, { "cell_type": "code", "source": [ "import tensorflow as tf\n", "import numpy as np\n", "import pandas as pd\n", "from tensorflow import keras\n", "from tensorflow.keras import layers\n", "from keras import losses\n", "from keras import optimizers\n", "from keras import metrics\n", "import math\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns" ], "metadata": { "id": "_8cYJEktx9_V" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "from google.colab import drive\n", "drive.mount('/content/drive')" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "095fab46-02b6-4b4b-f4de-6e6afb89fb90", "id": "l68KcWcMx0GM" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Mounted at /content/drive\n" ] } ] }, { "cell_type": "code", "source": [ "CSV_HEADER = [\n", "\"srcip\",\n", "\"sport\",\n", "\"dstip\",\n", "\"dsport\",\n", "\"proto\",\n", "\"state\",\n", "\"dur\",\n", "\"sbytes\",\n", "\"dbytes\",\n", "\"sttl\",\n", "\"dttl\",\n", "\"sloss\",\n", "\"dloss\",\n", "\"service\",\n", "\"Sload\",\n", "\"Dload\",\n", "\"Spkts\",\n", "\"Dpkts\",\n", "\"swin\",\n", "\"dwin\",\n", "\"stcpb\",\n", "\"dtcpb\",\n", "\"smeansz\",\n", "\"dmeansz\",\n", "\"trans_depth\",\n", "\"res_bdy_len\",\n", "\"Sjit\",\n", "\"Djit\",\n", "\"Stime\",\n", "\"Ltime\",\n", "\"Sintpkt\",\n", "\"Dintpkt\",\n", "\"tcprtt\",\n", "\"synack\",\n", "\"ackdat\",\n", "\"is_sm_ips_ports\",\n", "\"ct_state_ttl\",\n", "\"ct_flw_http_mthd\",\n", "\"is_ftp_login\",\n", "\"ct_ftp_cmd\",\n", "\"ct_srv_src\",\n", "\"ct_srv_dst\",\n", "\"ct_dst_ltm\",\n", "\"ct_src_ ltm\",\n", "\"ct_src_dport_ltm\",\n", "\"ct_dst_sport_ltm\",\n", "\"ct_dst_src_ltm\",\n", "\"attack_cat\",\n", "\"Label\"\n", "]" ], "metadata": { "id": "IqHizbGsyBNd" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "import pandas as pd\n", "import glob\n", "import os\n", "\n", "path = r'C:\\DRO\\DCL_rawdata_files' # use your path\n", "all_files = glob.glob(os.path.join(path , \"/content/drive/MyDrive/datasets/UNSW-NB15/*.csv\"))\n", "\n", "li = []\n", "\n", "for filename in all_files:\n", " df = pd.read_csv(filename, index_col=None, header=None, names=CSV_HEADER,low_memory=False)\n", " li.append(df)\n", "\n", "frame = pd.concat(li, axis=0, ignore_index=True)\n", "pd.set_option('display.max_columns', None)\n", "frame" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 461 }, "id": "4YYTcz4sx0GM", "outputId": "5292e7a1-af35-42d3-cdfc-f471a7778a4b" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ " srcip sport dstip dsport proto state dur \\\n", "0 59.166.0.9 7045 149.171.126.7 25 tcp FIN 0.201886 \n", "1 59.166.0.9 9685 149.171.126.2 80 tcp FIN 5.864748 \n", "2 59.166.0.2 1421 149.171.126.4 53 udp CON 0.001391 \n", "3 59.166.0.2 21553 149.171.126.2 25 tcp FIN 0.053948 \n", "4 59.166.0.8 45212 149.171.126.4 53 udp CON 0.000953 \n", "... ... ... ... ... ... ... ... \n", "2540042 59.166.0.8 12520 149.171.126.6 31010 tcp FIN 0.020383 \n", "2540043 59.166.0.0 18895 149.171.126.9 80 tcp FIN 1.402957 \n", "2540044 59.166.0.0 30103 149.171.126.5 5190 tcp FIN 0.007108 \n", "2540045 59.166.0.6 30388 149.171.126.5 111 udp CON 0.004435 \n", "2540046 59.166.0.0 6055 149.171.126.5 54145 tcp FIN 0.072974 \n", "\n", " sbytes dbytes sttl dttl sloss dloss service Sload \\\n", "0 37552 3380 31 29 18 8 smtp 1.459438e+06 \n", "1 19410 1087890 31 29 2 370 http 2.640454e+04 \n", "2 146 178 31 29 0 0 dns 4.198418e+05 \n", "3 37812 3380 31 29 19 8 smtp 5.503374e+06 \n", "4 146 178 31 29 0 0 dns 6.128017e+05 \n", "... ... ... ... ... ... ... ... ... \n", "2540042 320 1874 31 29 1 2 - 1.047932e+05 \n", "2540043 19410 1087890 31 29 2 370 http 1.103783e+05 \n", "2540044 2158 2464 31 29 6 6 - 2.328644e+06 \n", "2540045 568 304 31 29 0 0 - 7.684329e+05 \n", "2540046 4238 60788 31 29 7 30 - 4.582454e+05 \n", "\n", " Dload Spkts Dpkts swin dwin stcpb dtcpb \\\n", "0 1.307669e+05 52 42 255 255 1422136554 3572668484 \n", "1 1.481983e+06 364 746 255 255 389619597 394688654 \n", "2 5.118620e+05 2 2 0 0 0 0 \n", "3 4.893601e+05 54 42 255 255 4047523379 1903327524 \n", "4 7.471144e+05 2 2 0 0 0 0 \n", "... ... ... ... ... ... ... ... \n", "2540042 6.436736e+05 6 8 255 255 3208686479 3225486168 \n", "2540043 6.195098e+06 364 746 255 255 283296697 2429736754 \n", "2540044 2.658413e+06 24 24 255 255 703293844 2848960529 \n", "2540045 4.112740e+05 4 4 0 0 0 0 \n", "2540046 6.571546e+06 72 72 255 255 1003293149 1003585034 \n", "\n", " smeansz dmeansz trans_depth res_bdy_len Sjit Djit \\\n", "0 722 80 0 0 456.043567 15.530109 \n", "1 53 1458 1 0 1031.366423 690.219581 \n", "2 73 89 0 0 0.000000 0.000000 \n", "3 700 80 0 0 65.909688 3.155258 \n", "4 73 89 0 0 0.000000 0.000000 \n", "... ... ... ... ... ... ... \n", "2540042 53 234 0 0 212.810729 3.079195 \n", "2540043 53 1458 1 3924 203.808900 114.173588 \n", "2540044 90 103 0 0 17.627831 0.432619 \n", "2540045 142 76 0 0 1.638604 1.390643 \n", "2540046 59 844 0 0 62.045310 61.899776 \n", "\n", " Stime Ltime Sintpkt Dintpkt tcprtt synack \\\n", "0 1424250009 1424250009 3.943843 4.912488 0.000590 0.000473 \n", "1 1424250003 1424250009 16.155447 7.871279 0.000771 0.000638 \n", "2 1424250009 1424250009 0.009000 0.002000 0.000000 0.000000 \n", "3 1424250009 1424250009 1.011547 1.302561 0.000674 0.000540 \n", "4 1424250009 1424250009 0.009000 0.004000 0.000000 0.000000 \n", "... ... ... ... ... ... ... \n", "2540042 1421955842 1421955842 4.007400 2.027429 0.006386 0.006189 \n", "2540043 1421955841 1421955842 3.864028 1.882421 0.000712 0.000550 \n", "2540044 1421955842 1421955842 0.274261 0.285478 0.000657 0.000532 \n", "2540045 1421955842 1421955842 1.165667 0.987333 0.000000 0.000000 \n", "2540046 1421955842 1421955842 1.022690 0.997042 0.002317 0.002173 \n", "\n", " ackdat is_sm_ips_ports ct_state_ttl ct_flw_http_mthd \\\n", "0 0.000117 0 0 NaN \n", "1 0.000133 0 0 1.0 \n", "2 0.000000 0 0 NaN \n", "3 0.000134 0 0 NaN \n", "4 0.000000 0 0 NaN \n", "... ... ... ... ... \n", "2540042 0.000197 0 0 0.0 \n", "2540043 0.000162 0 0 4.0 \n", "2540044 0.000125 0 0 0.0 \n", "2540045 0.000000 0 0 0.0 \n", "2540046 0.000144 0 0 0.0 \n", "\n", " is_ftp_login ct_ftp_cmd ct_srv_src ct_srv_dst ct_dst_ltm \\\n", "0 NaN 2 2 7 \n", "1 NaN 3 1 4 \n", "2 NaN 3 5 2 \n", "3 NaN 1 1 4 \n", "4 NaN 2 5 2 \n", "... ... ... ... ... ... \n", "2540042 0.0 0 8 20 7 \n", "2540043 0.0 0 1 1 2 \n", "2540044 0.0 0 13 13 6 \n", "2540045 0.0 0 10 13 6 \n", "2540046 0.0 0 13 13 6 \n", "\n", " ct_src_ ltm ct_src_dport_ltm ct_dst_sport_ltm ct_dst_src_ltm \\\n", "0 4 1 1 3 \n", "1 4 1 1 1 \n", "2 7 1 1 4 \n", "3 7 1 1 3 \n", "4 1 1 1 2 \n", "... ... ... ... ... \n", "2540042 5 1 1 4 \n", "2540043 7 2 2 2 \n", "2540044 7 2 1 2 \n", "2540045 5 1 1 3 \n", "2540046 7 1 1 2 \n", "\n", " attack_cat Label \n", "0 NaN 0 \n", "1 NaN 0 \n", "2 NaN 0 \n", "3 NaN 0 \n", "4 NaN 0 \n", "... ... ... \n", "2540042 NaN 0 \n", "2540043 NaN 0 \n", "2540044 NaN 0 \n", "2540045 NaN 0 \n", "2540046 NaN 0 \n", "\n", "[2540047 rows x 49 columns]" ], "text/html": [ "\n", "
\n", "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
srcipsportdstipdsportprotostatedursbytesdbytessttldttlslossdlossserviceSloadDloadSpktsDpktsswindwinstcpbdtcpbsmeanszdmeansztrans_depthres_bdy_lenSjitDjitStimeLtimeSintpktDintpkttcprttsynackackdatis_sm_ips_portsct_state_ttlct_flw_http_mthdis_ftp_loginct_ftp_cmdct_srv_srcct_srv_dstct_dst_ltmct_src_ ltmct_src_dport_ltmct_dst_sport_ltmct_dst_src_ltmattack_catLabel
059.166.0.97045149.171.126.725tcpFIN0.2018863755233803129188smtp1.459438e+061.307669e+055242255255142213655435726684847228000456.04356715.530109142425000914242500093.9438434.9124880.0005900.0004730.00011700NaNNaN2274113NaN0
159.166.0.99685149.171.126.280tcpFIN5.86474819410108789031292370http2.640454e+041.481983e+06364746255255389619597394688654531458101031.366423690.2195811424250003142425000916.1554477.8712790.0007710.0006380.000133001.0NaN3144111NaN0
259.166.0.21421149.171.126.453udpCON0.001391146178312900dns4.198418e+055.118620e+052200007389000.0000000.000000142425000914242500090.0090000.0020000.0000000.0000000.00000000NaNNaN3527114NaN0
359.166.0.221553149.171.126.225tcpFIN0.0539483781233803129198smtp5.503374e+064.893601e+05544225525540475233791903327524700800065.9096883.155258142425000914242500091.0115471.3025610.0006740.0005400.00013400NaNNaN1147113NaN0
459.166.0.845212149.171.126.453udpCON0.000953146178312900dns6.128017e+057.471144e+052200007389000.0000000.000000142425000914242500090.0090000.0040000.0000000.0000000.00000000NaNNaN2521112NaN0
......................................................................................................................................................
254004259.166.0.812520149.171.126.631010tcpFIN0.0203833201874312912-1.047932e+056.436736e+0568255255320868647932254861685323400212.8107293.079195142195584214219558424.0074002.0274290.0063860.0061890.000197000.00.0082075114NaN0
254004359.166.0.018895149.171.126.980tcpFIN1.40295719410108789031292370http1.103783e+056.195098e+06364746255255283296697242973675453145813924203.808900114.173588142195584114219558423.8640281.8824210.0007120.0005500.000162004.00.001127222NaN0
254004459.166.0.030103149.171.126.55190tcpFIN0.00710821582464312966-2.328644e+062.658413e+0624242552557032938442848960529901030017.6278310.432619142195584214219558420.2742610.2854780.0006570.0005320.000125000.00.00131367212NaN0
254004559.166.0.630388149.171.126.5111udpCON0.004435568304312900-7.684329e+054.112740e+0544000014276001.6386041.390643142195584214219558421.1656670.9873330.0000000.0000000.000000000.00.00101365113NaN0
254004659.166.0.06055149.171.126.554145tcpFIN0.0729744238607883129730-4.582454e+056.571546e+06727225525510032931491003585034598440062.04531061.899776142195584214219558421.0226900.9970420.0023170.0021730.000144000.00.00131367112NaN0
\n", "

2540047 rows × 49 columns

\n", "
\n", "
\n", "\n", "
\n", " \n", "\n", " \n", "\n", " \n", "
\n", "\n", "\n", "
\n", " \n", "\n", "\n", "\n", " \n", "
\n", "
\n", "
\n" ] }, "metadata": {}, "execution_count": 4 } ] }, { "cell_type": "code", "source": [ "frame.srcip = frame.srcip.astype('category').cat.codes\n", "frame.dstip = frame.dstip.astype('category').cat.codes\n", "frame.proto = frame.proto.astype('category').cat.codes\n", "frame.state = frame.state.astype('category').cat.codes\n", "frame.service = frame.service.astype('category').cat.codes\n", "frame.ct_flw_http_mthd = frame.ct_flw_http_mthd.astype('category').cat.codes\n", "frame.is_ftp_login = frame.is_ftp_login.astype('category').cat.codes\n", "frame.ct_ftp_cmd = frame.ct_ftp_cmd.astype('category').cat.codes\n", "frame['Label'] = frame['Label'].astype(str)\n", "frame['Label'] = frame['Label'].str.replace(\"1\", \"anomaly\")\n", "frame['Label'] = frame['Label'].str.replace(\"0\", \"normal\")\n", "frame = frame.drop('attack_cat', axis=1)\n", "frame" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 461 }, "id": "RqDbXr3nyRYg", "outputId": "91d7d17c-bfca-4f7a-981a-804c39f5844a" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ " srcip sport dstip dsport proto state dur sbytes dbytes \\\n", "0 42 7045 25 25 114 5 0.201886 37552 3380 \n", "1 42 9685 20 80 114 5 5.864748 19410 1087890 \n", "2 35 1421 22 53 120 2 0.001391 146 178 \n", "3 35 21553 20 25 114 5 0.053948 37812 3380 \n", "4 41 45212 22 53 120 2 0.000953 146 178 \n", "... ... ... ... ... ... ... ... ... ... \n", "2540042 41 12520 24 31010 114 5 0.020383 320 1874 \n", "2540043 33 18895 27 80 114 5 1.402957 19410 1087890 \n", "2540044 33 30103 23 5190 114 5 0.007108 2158 2464 \n", "2540045 39 30388 23 111 120 2 0.004435 568 304 \n", "2540046 33 6055 23 54145 114 5 0.072974 4238 60788 \n", "\n", " sttl dttl sloss dloss service Sload Dload Spkts \\\n", "0 31 29 18 8 9 1.459438e+06 1.307669e+05 52 \n", "1 31 29 2 370 5 2.640454e+04 1.481983e+06 364 \n", "2 31 29 0 0 2 4.198418e+05 5.118620e+05 2 \n", "3 31 29 19 8 9 5.503374e+06 4.893601e+05 54 \n", "4 31 29 0 0 2 6.128017e+05 7.471144e+05 2 \n", "... ... ... ... ... ... ... ... ... \n", "2540042 31 29 1 2 0 1.047932e+05 6.436736e+05 6 \n", "2540043 31 29 2 370 5 1.103783e+05 6.195098e+06 364 \n", "2540044 31 29 6 6 0 2.328644e+06 2.658413e+06 24 \n", "2540045 31 29 0 0 0 7.684329e+05 4.112740e+05 4 \n", "2540046 31 29 7 30 0 4.582454e+05 6.571546e+06 72 \n", "\n", " Dpkts swin dwin stcpb dtcpb smeansz dmeansz \\\n", "0 42 255 255 1422136554 3572668484 722 80 \n", "1 746 255 255 389619597 394688654 53 1458 \n", "2 2 0 0 0 0 73 89 \n", "3 42 255 255 4047523379 1903327524 700 80 \n", "4 2 0 0 0 0 73 89 \n", "... ... ... ... ... ... ... ... \n", "2540042 8 255 255 3208686479 3225486168 53 234 \n", "2540043 746 255 255 283296697 2429736754 53 1458 \n", "2540044 24 255 255 703293844 2848960529 90 103 \n", "2540045 4 0 0 0 0 142 76 \n", "2540046 72 255 255 1003293149 1003585034 59 844 \n", "\n", " trans_depth res_bdy_len Sjit Djit Stime \\\n", "0 0 0 456.043567 15.530109 1424250009 \n", "1 1 0 1031.366423 690.219581 1424250003 \n", "2 0 0 0.000000 0.000000 1424250009 \n", "3 0 0 65.909688 3.155258 1424250009 \n", "4 0 0 0.000000 0.000000 1424250009 \n", "... ... ... ... ... ... \n", "2540042 0 0 212.810729 3.079195 1421955842 \n", "2540043 1 3924 203.808900 114.173588 1421955841 \n", "2540044 0 0 17.627831 0.432619 1421955842 \n", "2540045 0 0 1.638604 1.390643 1421955842 \n", "2540046 0 0 62.045310 61.899776 1421955842 \n", "\n", " Ltime Sintpkt Dintpkt tcprtt synack ackdat \\\n", "0 1424250009 3.943843 4.912488 0.000590 0.000473 0.000117 \n", "1 1424250009 16.155447 7.871279 0.000771 0.000638 0.000133 \n", "2 1424250009 0.009000 0.002000 0.000000 0.000000 0.000000 \n", "3 1424250009 1.011547 1.302561 0.000674 0.000540 0.000134 \n", "4 1424250009 0.009000 0.004000 0.000000 0.000000 0.000000 \n", "... ... ... ... ... ... ... \n", "2540042 1421955842 4.007400 2.027429 0.006386 0.006189 0.000197 \n", "2540043 1421955842 3.864028 1.882421 0.000712 0.000550 0.000162 \n", "2540044 1421955842 0.274261 0.285478 0.000657 0.000532 0.000125 \n", "2540045 1421955842 1.165667 0.987333 0.000000 0.000000 0.000000 \n", "2540046 1421955842 1.022690 0.997042 0.002317 0.002173 0.000144 \n", "\n", " is_sm_ips_ports ct_state_ttl ct_flw_http_mthd is_ftp_login \\\n", "0 0 0 -1 -1 \n", "1 0 0 1 -1 \n", "2 0 0 -1 -1 \n", "3 0 0 -1 -1 \n", "4 0 0 -1 -1 \n", "... ... ... ... ... \n", "2540042 0 0 0 0 \n", "2540043 0 0 4 0 \n", "2540044 0 0 0 0 \n", "2540045 0 0 0 0 \n", "2540046 0 0 0 0 \n", "\n", " ct_ftp_cmd ct_srv_src ct_srv_dst ct_dst_ltm ct_src_ ltm \\\n", "0 8 2 2 7 4 \n", "1 8 3 1 4 4 \n", "2 8 3 5 2 7 \n", "3 8 1 1 4 7 \n", "4 8 2 5 2 1 \n", "... ... ... ... ... ... \n", "2540042 0 8 20 7 5 \n", "2540043 0 1 1 2 7 \n", "2540044 0 13 13 6 7 \n", "2540045 0 10 13 6 5 \n", "2540046 0 13 13 6 7 \n", "\n", " ct_src_dport_ltm ct_dst_sport_ltm ct_dst_src_ltm Label \n", "0 1 1 3 normal \n", "1 1 1 1 normal \n", "2 1 1 4 normal \n", "3 1 1 3 normal \n", "4 1 1 2 normal \n", "... ... ... ... ... \n", "2540042 1 1 4 normal \n", "2540043 2 2 2 normal \n", "2540044 2 1 2 normal \n", "2540045 1 1 3 normal \n", "2540046 1 1 2 normal \n", "\n", "[2540047 rows x 48 columns]" ], "text/html": [ "\n", "
\n", "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
srcipsportdstipdsportprotostatedursbytesdbytessttldttlslossdlossserviceSloadDloadSpktsDpktsswindwinstcpbdtcpbsmeanszdmeansztrans_depthres_bdy_lenSjitDjitStimeLtimeSintpktDintpkttcprttsynackackdatis_sm_ips_portsct_state_ttlct_flw_http_mthdis_ftp_loginct_ftp_cmdct_srv_srcct_srv_dstct_dst_ltmct_src_ ltmct_src_dport_ltmct_dst_sport_ltmct_dst_src_ltmLabel
0427045252511450.201886375523380312918891.459438e+061.307669e+055242255255142213655435726684847228000456.04356715.530109142425000914242500093.9438434.9124880.0005900.0004730.00011700-1-182274113normal
1429685208011455.8647481941010878903129237052.640454e+041.481983e+06364746255255389619597394688654531458101031.366423690.2195811424250003142425000916.1554477.8712790.0007710.0006380.000133001-183144111normal
2351421225312020.00139114617831290024.198418e+055.118620e+052200007389000.0000000.000000142425000914242500090.0090000.0020000.0000000.0000000.00000000-1-183527114normal
33521553202511450.053948378123380312919895.503374e+064.893601e+05544225525540475233791903327524700800065.9096883.155258142425000914242500091.0115471.3025610.0006740.0005400.00013400-1-181147113normal
44145212225312020.00095314617831290026.128017e+057.471144e+052200007389000.0000000.000000142425000914242500090.0090000.0040000.0000000.0000000.00000000-1-182521112normal
...................................................................................................................................................
25400424112520243101011450.020383320187431291201.047932e+056.436736e+0568255255320868647932254861685323400212.8107293.079195142195584214219558424.0074002.0274290.0063860.0061890.0001970000082075114normal
25400433318895278011451.4029571941010878903129237051.103783e+056.195098e+06364746255255283296697242973675453145813924203.808900114.173588142195584114219558423.8640281.8824210.0007120.0005500.000162004001127222normal
2540044333010323519011450.0071082158246431296602.328644e+062.658413e+0624242552557032938442848960529901030017.6278310.432619142195584214219558420.2742610.2854780.0006570.0005320.00012500000131367212normal
254004539303882311112020.00443556830431290007.684329e+054.112740e+0544000014276001.6386041.390643142195584214219558421.1656670.9873330.0000000.0000000.00000000000101365113normal
2540046336055235414511450.072974423860788312973004.582454e+056.571546e+06727225525510032931491003585034598440062.04531061.899776142195584214219558421.0226900.9970420.0023170.0021730.00014400000131367112normal
\n", "

2540047 rows × 48 columns

\n", "
\n", "
\n", "\n", "
\n", " \n", "\n", " \n", "\n", " \n", "
\n", "\n", "\n", "
\n", " \n", "\n", "\n", "\n", " \n", "
\n", "
\n", "
\n" ] }, "metadata": {}, "execution_count": 5 } ] }, { "cell_type": "code", "source": [ "column_headers = list(frame.columns.values)\n", "column_headers" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "129f02d4-aa06-4207-9bb2-155ec6037ae3", "id": "7SRWhQp9x0GN" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "['srcip',\n", " 'sport',\n", " 'dstip',\n", " 'dsport',\n", " 'proto',\n", " 'state',\n", " 'dur',\n", " 'sbytes',\n", " 'dbytes',\n", " 'sttl',\n", " 'dttl',\n", " 'sloss',\n", " 'dloss',\n", " 'service',\n", " 'Sload',\n", " 'Dload',\n", " 'Spkts',\n", " 'Dpkts',\n", " 'swin',\n", " 'dwin',\n", " 'stcpb',\n", " 'dtcpb',\n", " 'smeansz',\n", " 'dmeansz',\n", " 'trans_depth',\n", " 'res_bdy_len',\n", " 'Sjit',\n", " 'Djit',\n", " 'Stime',\n", " 'Ltime',\n", " 'Sintpkt',\n", " 'Dintpkt',\n", " 'tcprtt',\n", " 'synack',\n", " 'ackdat',\n", " 'is_sm_ips_ports',\n", " 'ct_state_ttl',\n", " 'ct_flw_http_mthd',\n", " 'is_ftp_login',\n", " 'ct_ftp_cmd',\n", " 'ct_srv_src',\n", " 'ct_srv_dst',\n", " 'ct_dst_ltm',\n", " 'ct_src_ ltm',\n", " 'ct_src_dport_ltm',\n", " 'ct_dst_sport_ltm',\n", " 'ct_dst_src_ltm',\n", " 'Label']" ] }, "metadata": {}, "execution_count": 6 } ] }, { "cell_type": "code", "source": [ "CSV_HEADER = column_headers\n", "df = frame\n", "def Remove_Outlier_Indices(df):\n", " Q1 = df.quantile(0.02)\n", " Q3 = df.quantile(0.98)\n", " IQR = Q3 - Q1\n", " #trueList = ~((df < (Q1 - 1.5 * IQR)) |(df > (Q3 + 1.5 * IQR)))\n", " trueList = ~((df < (Q1 - 1.5 * IQR)) |(df > (Q3 + 1.5 * IQR))).any(axis=1)\n", " return trueList\n", "\n", "nonOutlierList = Remove_Outlier_Indices(df)\n", "new_data = df[nonOutlierList]\n", "\n", "df = new_data\n", "df = df.reset_index(drop=True)\n", "del new_data\n", "del nonOutlierList\n", "del li\n", "del frame\n", "import gc\n", "gc.collect()" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "8c16535c-9431-40a2-d3e2-a73bc674d019", "id": "T5I2816Kx0GN" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stderr", "text": [ ":4: FutureWarning: The default value of numeric_only in DataFrame.quantile is deprecated. In a future version, it will default to False. Select only valid columns or specify the value of numeric_only to silence this warning.\n", " Q1 = df.quantile(0.02)\n", ":5: FutureWarning: The default value of numeric_only in DataFrame.quantile is deprecated. In a future version, it will default to False. Select only valid columns or specify the value of numeric_only to silence this warning.\n", " Q3 = df.quantile(0.98)\n", ":8: FutureWarning: Automatic reindexing on DataFrame vs Series comparisons is deprecated and will raise ValueError in a future version. Do `left, right = left.align(right, axis=1, copy=False)` before e.g. `left == right`\n", " trueList = ~((df < (Q1 - 1.5 * IQR)) |(df > (Q3 + 1.5 * IQR))).any(axis=1)\n" ] }, { "output_type": "execute_result", "data": { "text/plain": [ "49" ] }, "metadata": {}, "execution_count": 7 } ] }, { "cell_type": "code", "source": [ "from sklearn.ensemble import RandomForestClassifier\n", "X = df.drop(['Label'], axis=1)\n", "Y = df[\"Label\"].astype('category').cat.codes\n", "clf = RandomForestClassifier()\n", "clf.fit(X, Y)\n", "features = pd.Series(clf.feature_importances_, index=X.columns)\n", "features.sort_values(ascending=False, inplace=True)\n", "print(features.head(10))" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "a8522517-7f2d-41f5-a92c-8308f255382c", "id": "r7pBpWdLx0GP" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "ct_state_ttl 0.204888\n", "sttl 0.152128\n", "srcip 0.092287\n", "sbytes 0.076845\n", "smeansz 0.073014\n", "dttl 0.051160\n", "dstip 0.041404\n", "dmeansz 0.034131\n", "Dload 0.030871\n", "Dpkts 0.027830\n", "dtype: float64\n" ] } ] }, { "cell_type": "code", "source": [ "fs = features.head(10).to_dict()\n", "fs" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "c0183c77-c508-4c47-801c-853fe74567be", "id": "dlYFTup_x0GP" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "{'ct_state_ttl': 0.20488763325717038,\n", " 'sttl': 0.1521284631007735,\n", " 'srcip': 0.09228731567941095,\n", " 'sbytes': 0.07684506148216279,\n", " 'smeansz': 0.07301405337046245,\n", " 'dttl': 0.05116042357030089,\n", " 'dstip': 0.04140361232353322,\n", " 'dmeansz': 0.03413087150829379,\n", " 'Dload': 0.030870500320368942,\n", " 'Dpkts': 0.02783016435570764}" ] }, "metadata": {}, "execution_count": 10 } ] }, { "cell_type": "code", "source": [ "columns = []\n", "for k in fs:\n", " columns.append(str(k))\n", "columns" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "7ccaa0c4-5f38-43e8-9333-5411caedbfce", "id": "ZGxa18v3x0GQ" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "['ct_state_ttl',\n", " 'sttl',\n", " 'srcip',\n", " 'sbytes',\n", " 'smeansz',\n", " 'dttl',\n", " 'dstip',\n", " 'dmeansz',\n", " 'Dload',\n", " 'Dpkts']" ] }, "metadata": {}, "execution_count": 11 } ] }, { "cell_type": "code", "source": [ "principalDf = pd.DataFrame(data = X\n", " , columns = columns)\n", "finalDf = pd.concat([principalDf, df[\"Label\"]], axis = 1)\n", "finalDf" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 424 }, "outputId": "b8abcf74-2b37-4d21-ce8d-8a39d6377cda", "id": "y56kSk6wx0GQ" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ " ct_state_ttl sttl srcip sbytes smeansz dttl dstip dmeansz \\\n", "0 0 31 42 37552 722 29 25 80 \n", "1 0 31 42 19410 53 29 20 1458 \n", "2 0 31 35 146 73 29 22 89 \n", "3 0 31 35 37812 700 29 20 80 \n", "4 0 31 41 146 73 29 22 89 \n", "... ... ... ... ... ... ... ... ... \n", "2438669 0 31 41 320 53 29 24 234 \n", "2438670 0 31 33 19410 53 29 27 1458 \n", "2438671 0 31 33 2158 90 29 23 103 \n", "2438672 0 31 39 568 142 29 23 76 \n", "2438673 0 31 33 4238 59 29 23 844 \n", "\n", " Dload Dpkts Label \n", "0 1.307669e+05 42 normal \n", "1 1.481983e+06 746 normal \n", "2 5.118620e+05 2 normal \n", "3 4.893601e+05 42 normal \n", "4 7.471144e+05 2 normal \n", "... ... ... ... \n", "2438669 6.436736e+05 8 normal \n", "2438670 6.195098e+06 746 normal \n", "2438671 2.658413e+06 24 normal \n", "2438672 4.112740e+05 4 normal \n", "2438673 6.571546e+06 72 normal \n", "\n", "[2438674 rows x 11 columns]" ], "text/html": [ "\n", "
\n", "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ct_state_ttlsttlsrcipsbytessmeanszdttldstipdmeanszDloadDpktsLabel
003142375527222925801.307669e+0542normal
1031421941053292014581.481983e+06746normal
203135146732922895.118620e+052normal
303135378127002920804.893601e+0542normal
403141146732922897.471144e+052normal
....................................
2438669031413205329242346.436736e+058normal
2438670031331941053292714586.195098e+06746normal
24386710313321589029231032.658413e+0624normal
2438672031395681422923764.112740e+054normal
24386730313342385929238446.571546e+0672normal
\n", "

2438674 rows × 11 columns

\n", "
\n", "
\n", "\n", "
\n", " \n", "\n", " \n", "\n", " \n", "
\n", "\n", "\n", "
\n", " \n", "\n", "\n", "\n", " \n", "
\n", "
\n", "
\n" ] }, "metadata": {}, "execution_count": 13 } ] }, { "cell_type": "code", "source": [ "from sklearn.model_selection import train_test_split\n", "train_data, test_data = train_test_split(finalDf, test_size=0.25)\n", "train_data_file = \"train_data.csv\"\n", "test_data_file = \"test_data.csv\"\n", "\n", "train_data.to_csv(train_data_file, index=False, header=False)\n", "test_data.to_csv(test_data_file, index=False, header=False)" ], "metadata": { "id": "g9hl51Nyx0GQ" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "del finalDf\n", "del principalDf\n", "del train_data\n", "del test_data\n", "del clf\n", "del X\n", "del Y" ], "metadata": { "id": "wUSnhmoax0GR" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "CSV_HEADER = []\n", "for x in columns:\n", " CSV_HEADER.append(x)\n", "CSV_HEADER.append(\"Label\")\n", "\n", "# A list of the numerical feature names.\n", "NUMERIC_FEATURE_NAMES = columns\n", "# A dictionary of the categorical features and their vocabulary.\n", "CATEGORICAL_FEATURES_WITH_VOCABULARY = {\n", "}\n", "# A list of the columns to ignore from the dataset.\n", "IGNORE_COLUMN_NAMES = []\n", "# A list of the categorical feature names.\n", "CATEGORICAL_FEATURE_NAMES = list(CATEGORICAL_FEATURES_WITH_VOCABULARY.keys())\n", "# A list of all the input features.\n", "FEATURE_NAMES = NUMERIC_FEATURE_NAMES + CATEGORICAL_FEATURE_NAMES\n", "# A list of column default values for each feature.\n", "COLUMN_DEFAULTS = [\n", " [0.0] if feature_name in NUMERIC_FEATURE_NAMES + IGNORE_COLUMN_NAMES else [\"NA\"]\n", " for feature_name in CSV_HEADER\n", "]\n", "# The name of the target feature.\n", "TARGET_FEATURE_NAME = \"Label\"\n", "# A list of the labels of the target features.\n", "TARGET_LABELS = [\"normal\", \"anomaly\"]\n", "\n", "from tensorflow.keras.layers import StringLookup\n", "\n", "target_label_lookup = StringLookup(\n", " vocabulary=TARGET_LABELS, mask_token=None, num_oov_indices=0\n", ")\n", "\n", "\n", "def get_dataset_from_csv(csv_file_path, shuffle=False, batch_size=128):\n", " dataset = tf.data.experimental.make_csv_dataset(\n", " csv_file_path,\n", " batch_size=batch_size,\n", " column_names=CSV_HEADER,\n", " column_defaults=COLUMN_DEFAULTS,\n", " label_name=TARGET_FEATURE_NAME,\n", " num_epochs=1,\n", " header=False,\n", " na_value=\"?\",\n", " shuffle=shuffle,\n", " ).map(lambda features, target: (features, target_label_lookup(target)))\n", " return dataset.cache()\n", "\n", "def create_model_inputs():\n", " inputs = {}\n", " for feature_name in FEATURE_NAMES:\n", " if feature_name in NUMERIC_FEATURE_NAMES:\n", " inputs[feature_name] = layers.Input(\n", " name=feature_name, shape=(), dtype=tf.float32\n", " )\n", " else:\n", " inputs[feature_name] = layers.Input(\n", " name=feature_name, shape=(), dtype=tf.string\n", " )\n", " return inputs\n", "\n", "def encode_inputs(inputs):\n", " encoded_features = []\n", " for feature_name in inputs:\n", " if feature_name in CATEGORICAL_FEATURE_NAMES:\n", " vocabulary = CATEGORICAL_FEATURES_WITH_VOCABULARY[feature_name]\n", " #print(vocabulary)\n", " # Create a lookup to convert a string values to an integer indices.\n", " # Since we are not using a mask token, nor expecting any out of vocabulary\n", " # (oov) token, we set mask_token to None and num_oov_indices to 0.\n", " lookup = StringLookup(\n", " vocabulary=vocabulary, mask_token=None, num_oov_indices=0\n", " )\n", " # Convert the string input values into integer indices.\n", " value_index = lookup(inputs[feature_name])\n", " embedding_dims = int(math.sqrt(lookup.vocabulary_size()))\n", " # Create an embedding layer with the specified dimensions.\n", " embedding = layers.Embedding(\n", " input_dim=lookup.vocabulary_size(), output_dim=embedding_dims\n", " )\n", " # Convert the index values to embedding representations.\n", " encoded_feature = embedding(value_index)\n", " else:\n", " # Use the numerical features as-is.\n", " encoded_feature = inputs[feature_name]\n", " if inputs[feature_name].shape[-1] is None:\n", " encoded_feature = tf.expand_dims(encoded_feature, -1)\n", "\n", " encoded_features.append(encoded_feature)\n", "\n", " encoded_features = layers.concatenate(encoded_features)\n", " return encoded_features\n", "\n", "class NeuralDecisionTree(keras.Model):\n", " def __init__(self, depth, num_features, used_features_rate, num_classes):\n", " super().__init__()\n", " self.depth = depth\n", " self.num_leaves = 2 ** depth\n", " self.num_classes = num_classes\n", "\n", " # Create a mask for the randomly selected features.\n", " num_used_features = int(num_features * used_features_rate)\n", " one_hot = np.eye(num_features)\n", " sampled_feature_indicies = np.random.choice(\n", " np.arange(num_features), num_used_features, replace=False\n", " )\n", " self.used_features_mask = one_hot[sampled_feature_indicies]\n", "\n", " # Initialize the weights of the classes in leaves.\n", " self.pi = tf.Variable(\n", " initial_value=tf.random_normal_initializer()(\n", " shape=[self.num_leaves, self.num_classes]\n", " ),\n", " dtype=\"float32\",\n", " trainable=True,\n", " )\n", "\n", " # Initialize the stochastic routing layer.\n", " self.decision_fn = layers.Dense(\n", " units=self.num_leaves, activation=\"sigmoid\", name=\"decision\"\n", " )\n", "\n", " def call(self, features):\n", " batch_size = tf.shape(features)[0]\n", "\n", " # Apply the feature mask to the input features.\n", " features = tf.matmul(\n", " features, self.used_features_mask, transpose_b=True\n", " ) # [batch_size, num_used_features]\n", " # Compute the routing probabilities.\n", " decisions = tf.expand_dims(\n", " self.decision_fn(features), axis=2\n", " ) # [batch_size, num_leaves, 1]\n", " # Concatenate the routing probabilities with their complements.\n", " decisions = layers.concatenate(\n", " [decisions, 1 - decisions], axis=2\n", " ) # [batch_size, num_leaves, 2]\n", "\n", " mu = tf.ones([batch_size, 1, 1])\n", "\n", " begin_idx = 1\n", " end_idx = 2\n", " # Traverse the tree in breadth-first order.\n", " for level in range(self.depth):\n", " mu = tf.reshape(mu, [batch_size, -1, 1]) # [batch_size, 2 ** level, 1]\n", " mu = tf.tile(mu, (1, 1, 2)) # [batch_size, 2 ** level, 2]\n", " level_decisions = decisions[\n", " :, begin_idx:end_idx, :\n", " ] # [batch_size, 2 ** level, 2]\n", " mu = mu * level_decisions # [batch_size, 2**level, 2]\n", " begin_idx = end_idx\n", " end_idx = begin_idx + 2 ** (level + 1)\n", "\n", " mu = tf.reshape(mu, [batch_size, self.num_leaves]) # [batch_size, num_leaves]\n", " probabilities = keras.activations.softmax(self.pi) # [num_leaves, num_classes]\n", " outputs = tf.matmul(mu, probabilities) # [batch_size, num_classes]\n", " return outputs\n", "\n", "class NeuralDecisionForest(keras.Model):\n", " def __init__(self, num_trees, depth, num_features, used_features_rate, num_classes):\n", " super().__init__()\n", " self.ensemble = []\n", " # Initialize the ensemble by adding NeuralDecisionTree instances.\n", " # Each tree will have its own randomly selected input features to use.\n", " for _ in range(num_trees):\n", " self.ensemble.append(\n", " NeuralDecisionTree(depth, num_features, used_features_rate, num_classes)\n", " )\n", "\n", " def call(self, inputs):\n", " # Initialize the outputs: a [batch_size, num_classes] matrix of zeros.\n", " batch_size = tf.shape(inputs)[0]\n", " outputs = tf.zeros([batch_size, num_classes])\n", "\n", " # Aggregate the outputs of trees in the ensemble.\n", " for tree in self.ensemble:\n", " outputs += tree(inputs)\n", " # Divide the outputs by the ensemble size to get the average.\n", " outputs /= len(self.ensemble)\n", " return outputs\n", "learning_rate = 0.01\n", "batch_size = 128\n", "num_epochs = 10\n", "\n", "\n", "def run_experiment(model):\n", "\n", " # model.compile(\n", " # optimizer=keras.optimizers.Adam(learning_rate=learning_rate),\n", " # loss=keras.losses.SparseCategoricalCrossentropy(),\n", " # metrics=[keras.metrics.SparseCategoricalAccuracy()],\n", " # )\n", " model.compile(\n", " optimizer=keras.optimizers.Adam(learning_rate=learning_rate),\n", " loss=keras.losses.SparseCategoricalCrossentropy(),\n", " metrics=[metrics.SparseCategoricalAccuracy()],\n", " )\n", " print(\"Start training the model...\")\n", " train_dataset = get_dataset_from_csv(\n", " train_data_file, shuffle=True, batch_size=batch_size\n", " )\n", "\n", " model.fit(train_dataset, epochs=num_epochs)\n", " print(\"Model training finished\")\n", "\n", " print(\"Evaluating the model on the test data...\")\n", " test_dataset = get_dataset_from_csv(test_data_file, batch_size=batch_size)\n", "\n", " _, accuracy = model.evaluate(test_dataset)\n", " print(f\"Test accuracy: {round(accuracy * 100, 2)}%\")\n", " return model" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "icdTdlZHy2ki", "outputId": "8817a92e-a394-4017-d1c3-6e7924ac2240" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stderr", "text": [ "/usr/local/lib/python3.10/dist-packages/numpy/core/numeric.py:2463: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison\n", " return bool(asarray(a1 == a2).all())\n" ] } ] }, { "cell_type": "code", "source": [ "num_trees = 25\n", "depth = 5\n", "used_features_rate = 0.5\n", "num_classes = len(TARGET_LABELS)\n", "\n", "\n", "def create_forest_model():\n", " inputs = create_model_inputs()\n", " features = encode_inputs(inputs)\n", " features = layers.BatchNormalization()(features)\n", " num_features = features.shape[1]\n", "\n", " forest_model = NeuralDecisionForest(\n", " num_trees, depth, num_features, used_features_rate, num_classes\n", " )\n", "\n", " outputs = forest_model(features)\n", " model = keras.Model(inputs=inputs, outputs=outputs)\n", " return model\n", "\n", "\n", "forest_model = create_forest_model()\n", "\n", "finalModel = run_experiment(forest_model)" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "49d868c0-6ce9-4386-9317-c1c467f33ded", "id": "Q16_BCDcx0GS" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Start training the model...\n", "Epoch 1/10\n", "14290/14290 [==============================] - 467s 31ms/step - loss: 0.0292 - sparse_categorical_accuracy: 0.9877\n", "Epoch 2/10\n", "14290/14290 [==============================] - 400s 28ms/step - loss: 0.0240 - sparse_categorical_accuracy: 0.9881\n", "Epoch 3/10\n", "14290/14290 [==============================] - 375s 26ms/step - loss: 0.0235 - sparse_categorical_accuracy: 0.9881\n", "Epoch 4/10\n", "14290/14290 [==============================] - 377s 26ms/step - loss: 0.0233 - sparse_categorical_accuracy: 0.9885\n", "Epoch 5/10\n", "14290/14290 [==============================] - 374s 26ms/step - loss: 0.0231 - sparse_categorical_accuracy: 0.9887\n", "Epoch 6/10\n", "14290/14290 [==============================] - 364s 25ms/step - loss: 0.0230 - sparse_categorical_accuracy: 0.9887\n", "Epoch 7/10\n", "14290/14290 [==============================] - 376s 26ms/step - loss: 0.0230 - sparse_categorical_accuracy: 0.9887\n", "Epoch 8/10\n", "14290/14290 [==============================] - 373s 26ms/step - loss: 0.0229 - sparse_categorical_accuracy: 0.9888\n", "Epoch 9/10\n", "14290/14290 [==============================] - 371s 26ms/step - loss: 0.0228 - sparse_categorical_accuracy: 0.9888\n", "Epoch 10/10\n", "14290/14290 [==============================] - 372s 26ms/step - loss: 0.0227 - sparse_categorical_accuracy: 0.9888\n", "Model training finished\n", "Evaluating the model on the test data...\n", "4764/4764 [==============================] - 63s 13ms/step - loss: 0.0219 - sparse_categorical_accuracy: 0.9888\n", "Test accuracy: 98.88%\n" ] } ] }, { "cell_type": "code", "source": [ "test_dataset = get_dataset_from_csv(test_data_file, batch_size=batch_size)\n", "colnames=['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'target']\n", "data = pd.read_csv(\"test_data.csv\", names=colnames, header=None)\n", "data['target'].replace('normal', 0,inplace=True)\n", "data['target'].replace('anomaly', 1,inplace=True)\n", "y_test = data['target'].values # as a numpy array\n", "from sklearn.metrics import confusion_matrix\n", "y_prediction = finalModel.predict(test_dataset)\n", "y_prediction = np.argmax (y_prediction, axis = 1)\n", "result = confusion_matrix(y_test, y_prediction , normalize='pred')\n", "print(result)\n", "TP = result[0][0]\n", "FP = result[0][1]\n", "TN = result[1][1]\n", "FN = result[1][0]\n", "ACC = (TP+TN)/(TP+TN+FP+FN)\n", "PR = TP/(TP+FP) #precision\n", "TPR = TP/(TP+FN) #Recall or True positive rate\n", "FPR = FP/(FP+TN)\n", "F1Score = 2*(PR*TPR)/(PR+TPR)\n", "print(\"ACC: \" + str(ACC))\n", "print(\"PR: \" + str(PR))\n", "print(\"TPR: \" + str(TPR))\n", "print(\"FPR: \" + str(FPR))\n", "print(\"F1Score: \" + str(F1Score))\n", "import matplotlib.pyplot as plt\n", "import numpy\n", "from sklearn import metrics\n", "\n", "\n", "cm_display = metrics.ConfusionMatrixDisplay(confusion_matrix = result, display_labels = [True, False])\n", "\n", "cm_display.plot()\n", "plt.show()" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 588 }, "outputId": "a6eac3c6-c3ee-42e8-e321-a4c157407e4c", "id": "23gZJAZax0GS" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "4764/4764 [==============================] - 64s 13ms/step\n", "[[9.99784982e-01 8.43356819e-02]\n", " [2.15017664e-04 9.15664318e-01]]\n", "ACC: 0.957724650201963\n", "PR: 0.9222082147198775\n", "TPR: 0.9997849823364876\n", "FPR: 0.08433568193256165\n", "F1Score: 0.9594309959851927\n" ] }, { "output_type": "display_data", "data": { "text/plain": [ "
" ], "image/png": "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EI/n5ObmSpJWrFjh8otbKlnXYZUNGzaoT58+mjx5srp3767IyEgtXrxYL7zwgsexzp0794KEJjAw0LJYAZQdCQTgpdDQUDVt2rTM4zt06KAlS5aoTp06F3wLP6du3br63//+p+uvv15SyTfttLQ0dejQwe341q1byzAMrV27VklJSRe8f64C4nA4nPtatmwpu92uzMzMUisXLVq0cC4IPeerr7767Q/5C+vXr1fDhg31xBNPOPf9+OOPF4zLzMzUoUOHVK9ePed5AgIC1KxZM8XGxqpevXras2eP+vTp49H5AZQPFlECFaxPnz6qXbu2evXqpXXr1mnv3r1as2aNhg4dqgMHDkiShg0bpmeeeUbLli3Tjh07NHjw4Ivew6FRo0ZKTk7Www8/rGXLljmP+c4770iSGjZsKJvNpuXLl+vYsWPKzc1VeHi4Ro4cqeHDh2vBggXavXu3Nm/erJdfftm5MPHRRx/VDz/8oFGjRmnnzp16++23NX/+fI8+71VXXaXMzEwtXrxYu3fv1qxZs9wuCA0ODlZycrK2bt2qdevWaejQobrvvvsUFxcnSZo8ebJSUlI0a9Ys7dq1S999953efPNNzZgxw6N4AFiDBAKoYDVq1NAXX3yhK664QnfddZdatGihRx55RPn5+c6KxN/+9jc99NBDSk5OVufOnRUeHq4777zzosedPXu27rnnHg0ePFjNmzfXwIEDlZeXJ0mqX7++Jk+erLFjxyo2NlZDhgyRJE2ZMkXjx49XSkqKWrRooR49emjFihVq3LixpJJ1Ce+++66WLVumtm3bas6cOZo2bZpHn/f222/X8OHDNWTIELVr107r16/X+PHjLxjXtGlT3XXXXfrjH/+om2++WW3atHG5THPAgAF6/fXX9eabb6p169bq2rWr5s+f74wVQMWymaWtygIAACgFFQgAAOAxEggAAOAxEggAAOAxEggAAOAxEggAAOAxEggAAOAxEggAAOAxEggAAOAxEggAAOAxEggAAOAxEggAAOCx/w8/HTbCKb4l1gAAAABJRU5ErkJggg==\n" }, "metadata": {} } ] } ] }