{ "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/MILLxg1a9Y0vv76a7e65557zvD29jaysrJ+8zhyc3ON2rVrG9HR0caZM2fc5oqLiw3DMIzCwkIjKCjIaNu2rVvNypUrDUlGSkqKOda1a1eja9eupfaTkJBgNGnSxHx86NAhQ5JRr1494+TJk+b4Rx99ZEgyPvnkE3MsMTHR4J93oOLx8RyAKyI+Pl45OTlat26dOfb++++ruLhY8fHx5pi/v7/532fPntWJEyfUuXNnSdKOHTsqpJfly5fr1ltvVZ06dXTixAlzi42NVVFRkTZs2PCbz01LS9Pp06f13HPPyc/Pz22u5CzY9u3blZOTo6eeesqtJi4uTq1atdKqVavK3Xt8fLzq1KljPr711lslSd9991251wRgDR/PAbgi7r77bgUEBGjp0qW68847Jf3y0VxkZKSuv/56s+7kyZN6/vnntWTJEuXk5LitkZeXVyG9HDx4UF9++aUaNGhQ5vyv93uhb7/9VpLMjxPL8v3330uSbrjhhlJzrVq10n/+859LaddN48aN3R6XBKhTp06Ve00A1hCaAFwRvr6+6tGjh1asWKHXXntN2dnZ2rhxo/7617+61T3yyCPatGmTxowZo8jISNWqVUvFxcW6++67LV+k/WtFRUVuj4uLi3XXXXdp7NixZdZfGOIuN5vNJsMwSo3/uucS3t7eZY6XtQaAikVoAnDFxMfHa9GiRUpPT9e+fftkGIbbR3OnTp1Senq6nn/+eaWkpJjjBw8etLR+nTp1lJub6zZWWFioY8eOuY01b95cP/30k2JjYy/5GJo3by5J2rNnj1q0aFFmTZMmTSRJBw4c0B133OE2d+DAAXO+pOeyPlorOVtVHnxbDrg8uKYJwBUTGxurunXraunSpVq6dKk6deqkZs2amfMlZ1F+fdbkYt9mK9G8efNS1yPNnz+/1FmbRx55RBkZGVq9enWpNXJzc3X+/Pnf3Ef37t1Vu3ZtTZkyRWfPnnWbK+m7Q4cOCgoK0rx581RQUGDOf/bZZ9q3b5/i4uLcet6/f7+OHz9ujn3xxRfauHGjhSMuW82aNc1jAVBxONME4IqpXr26evbsqSVLlig/P18vvfSS27zdbtdtt92mqVOn6ty5c/rTn/6kNWvW6NChQ5bWHzx4sIYOHapevXrprrvu0hdffKHVq1erfv36bnVjxozRxx9/rPvuu0/9+/dXVFSU8vPztXv3br3//vs6fPhwqedc2OOMGTM0ePBgdezYUY8++qjq1KmjL774Qj///LMWLVqk6tWr6+9//7sGDBigrl27qk+fPuYtB5o2bapRo0aZ6w0cOFDTp0+Xw+HQoEGDlJOTo3nz5qlNmzZyuVyX+Ar/IioqSpL09NNPy+FwyNvbW7179y7XWgAu4NHv7gG45qSlpRmSDJvNZhw5cqTU/A8//GA8+OCDRmBgoBEQEGA8/PDDxtGjR0vdTqCsWw4UFRUZ48aNM+rXr2/UqFHDcDgcxjfffFPqlgOGYRinT582kpOTjRYtWhg+Pj5G/fr1jS5duhgvvfSSUVhYeNHj+Pjjj40uXboY/v7+ht1uNzp16mS89957bjVLly41brrpJsPX19eoW7eu0bdvX+OHH34otdY777xjXHfddYaPj48RGRlprF69+jdvOTBt2rRSz//1a3P+/HljxIgRRoMGDQybzcbtB4AKYjMMrh4EAAC4GK5pAgAAsIDQBAAAYAGhCQAAwAJCEwAAgAWEJgAAAAsITQAAABZwc8sKUlxcrKNHj6p27dr8hAEAAFWEYRg6ffq0QkND5eV1kXNJnrxJVJMmTQxJpbannnrKMAzDOHPmjPHUU08ZdevWNWrWrGn07NnTcDqdbmt8//33xr333mv4+/sbDRo0MEaPHm2cO3fOrebzzz83brrpJsPHx8do3ry5sWDBglK9zJ4922jSpInh6+trdOrUydiyZcslHcuRI0fKPBY2NjY2Nja2yr+VdbPdX/PomaZt27a5/SbUnj17dNddd+nhhx+WJI0aNUqrVq3S8uXLFRAQoOHDh6tnz57mbzIVFRUpLi5OISEh2rRpk44dO6Z+/fqpevXq5i+nHzp0SHFxcRo6dKjeffddpaena/DgwWrYsKEcDockaenSpUpKStK8efMUHR2tmTNnyuFw6MCBAwoKCrJ0LLVr15YkHTlyRHa7vcJeIwAAcPm4XC6FhYWZf8d/1yWdTrnMnnnmGaN58+ZGcXGxkZuba1SvXt1Yvny5Ob9v3z5DkpGRkWEYhmF8+umnhpeXl9vZp7lz5xp2u90oKCgwDMMwxo4da7Rp08ZtP/Hx8YbD4TAfd+rUyUhMTDQfFxUVGaGhocaUKVMs956Xl2dIMvLy8i7toAEAgMdcyt/vSnMheGFhod555x0NHDhQNptNmZmZOnfunGJjY82aVq1aqXHjxsrIyJAkZWRkKCIiQsHBwWaNw+GQy+XS3r17zZoL1yipKVmjsLBQmZmZbjVeXl6KjY01a8pSUFAgl8vltgEAgKtXpQlNH374oXJzc9W/f39JktPplI+PjwIDA93qgoOD5XQ6zZoLA1PJfMnc79W4XC6dOXNGJ06cUFFRUZk1JWuUZcqUKQoICDC3sLCwSz5mAABQdVSa0PTWW2/pnnvuUWhoqKdbsSQ5OVl5eXnmduTIEU+3BAAALqNKccuB77//Xv/617/0wQcfmGMhISEqLCxUbm6u29mm7OxshYSEmDVbt251Wys7O9ucK/nfkrELa+x2u/z9/eXt7S1vb+8ya0rWKIuvr698fX0v/WABAECVVCnONC1YsEBBQUGKi4szx6KiolS9enWlp6ebYwcOHFBWVpZiYmIkSTExMdq9e7dycnLMmrS0NNntdoWHh5s1F65RUlOyho+Pj6KiotxqiouLlZ6ebtYAAAB4/ExTcXGxFixYoISEBFWr9v/bCQgI0KBBg5SUlKS6devKbrdrxIgRiomJUefOnSVJ3bt3V3h4uB5//HFNnTpVTqdT48ePV2JionkWaOjQoZo9e7bGjh2rgQMHau3atVq2bJlWrVpl7ispKUkJCQnq0KGDOnXqpJkzZyo/P18DBgy4si8GAACovK7At/l+1+rVqw1JxoEDB0rNldzcsk6dOkaNGjWMBx980Dh27JhbzeHDh4177rnH8Pf3N+rXr288++yzZd7cMjIy0vDx8TGuu+66Mm9u+eqrrxqNGzc2fHx8jE6dOhmbN2++pOPglgMAAFQ9l/L322YYhuHh3HZVcLlcCggIUF5eHje3BACgiriUv9+V4pomAACAyo7QBAAAYAGhCQAAwAJCEwAAgAWEJgAAAAsITQAAABYQmgAAACzw+B3BcWmixvzD0y0AlU7mtH6ebgHANYAzTQAAABYQmgAAACwgNAEAAFhAaAIAALCA0AQAAGABoQkAAMACQhMAAIAFhCYAAAALCE0AAAAWEJoAAAAsIDQBAABYQGgCAACwgNAEAABgAaEJAADAAkITAACABYQmAAAACwhNAAAAFhCaAAAALCA0AQAAWEBoAgAAsIDQBAAAYAGhCQAAwAJCEwAAgAWEJgAAAAsITQAAABYQmgAAACwgNAEAAFhAaAIAALCA0AQAAGABoQkAAMACQhMAAIAFhCYAAAALCE0AAAAWeDw0/fe//9Vjjz2mevXqyd/fXxEREdq+fbs5bxiGUlJS1LBhQ/n7+ys2NlYHDx50W+PkyZPq27ev7Ha7AgMDNWjQIP30009uNV9++aVuvfVW+fn5KSwsTFOnTi3Vy/Lly9WqVSv5+fkpIiJCn3766eU5aAAAUOV4NDSdOnVKN998s6pXr67PPvtMX331lV5++WXVqVPHrJk6dapeeeUVzZs3T1u2bFHNmjXlcDh09uxZs6Zv377au3ev0tLStHLlSm3YsEFPPPGEOe9yudS9e3c1adJEmZmZmjZtmiZNmqT58+ebNZs2bVKfPn00aNAg7dy5Uz169FCPHj20Z8+eK/NiAACASs1mGIbhqZ0/99xz2rhxo/7973+XOW8YhkJDQ/Xss89q9OjRkqS8vDwFBwdr4cKF6t27t/bt26fw8HBt27ZNHTp0kCSlpqbq3nvv1Q8//KDQ0FDNnTtXf/7zn+V0OuXj42Pu+8MPP9T+/fslSfHx8crPz9fKlSvN/Xfu3FmRkZGaN2/eRY/F5XIpICBAeXl5stvtf+h1+T1RY/5x2dYGqqrMaf083QKAKupS/n579EzTxx9/rA4dOujhhx9WUFCQbrrpJr3xxhvm/KFDh+R0OhUbG2uOBQQEKDo6WhkZGZKkjIwMBQYGmoFJkmJjY+Xl5aUtW7aYNbfddpsZmCTJ4XDowIEDOnXqlFlz4X5Kakr282sFBQVyuVxuGwAAuHp5NDR99913mjt3rlq2bKnVq1dr2LBhevrpp7Vo0SJJktPplCQFBwe7PS84ONicczqdCgoKcpuvVq2a6tat61ZT1hoX7uO3akrmf23KlCkKCAgwt7CwsEs+fgAAUHV4NDQVFxerffv2+utf/6qbbrpJTzzxhIYMGWLp4zBPS05OVl5enrkdOXLE0y0BAIDLyKOhqWHDhgoPD3cba926tbKysiRJISEhkqTs7Gy3muzsbHMuJCREOTk5bvPnz5/XyZMn3WrKWuPCffxWTcn8r/n6+sput7ttAADg6uXR0HTzzTfrwIEDbmNff/21mjRpIklq1qyZQkJClJ6ebs67XC5t2bJFMTExkqSYmBjl5uYqMzPTrFm7dq2Ki4sVHR1t1mzYsEHnzp0za9LS0nTDDTeY39SLiYlx209JTcl+AADAtc2joWnUqFHavHmz/vrXv+qbb77R4sWLNX/+fCUmJkqSbDabRo4cqRdffFEff/yxdu/erX79+ik0NFQ9evSQ9MuZqbvvvltDhgzR1q1btXHjRg0fPly9e/dWaGioJOnRRx+Vj4+PBg0apL1792rp0qWaNWuWkpKSzF6eeeYZpaam6uWXX9b+/fs1adIkbd++XcOHD7/irwsAAKh8qnly5x07dtSKFSuUnJysyZMnq1mzZpo5c6b69u1r1owdO1b5+fl64oknlJubq1tuuUWpqany8/Mza959910NHz5cd955p7y8vNSrVy+98sor5nxAQIDWrFmjxMRERUVFqX79+kpJSXG7l1OXLl20ePFijR8/Xv/zP/+jli1b6sMPP1Tbtm2vzIsBAAAqNY/ep+lqwn2aAM/hPk0AyutS/n579EwTAOD/y5oc4ekWgEqnccpuT7dg8vhvzwEAAFQFhCYAAAALCE0AAAAWEJoAAAAsIDQBAABYQGgCAACwgNAEAABgAaEJAADAAkITAACABYQmAAAACwhNAAAAFhCaAAAALCA0AQAAWEBoAgAAsIDQBAAAYAGhCQAAwAJCEwAAgAWEJgAAAAsITQAAABYQmgAAACwgNAEAAFhAaAIAALCA0AQAAGABoQkAAMACQhMAAIAFhCYAAAALCE0AAAAWEJoAAAAsIDQBAABYQGgCAACwgNAEAABgAaEJAADAAkITAACABYQmAAAACwhNAAAAFhCaAAAALCA0AQAAWEBoAgAAsIDQBAAAYAGhCQAAwAKPhqZJkybJZrO5ba1atTLnz549q8TERNWrV0+1atVSr169lJ2d7bZGVlaW4uLiVKNGDQUFBWnMmDE6f/68W826devUvn17+fr6qkWLFlq4cGGpXubMmaOmTZvKz89P0dHR2rp162U5ZgAAUDV5/ExTmzZtdOzYMXP7z3/+Y86NGjVKn3zyiZYvX67169fr6NGj6tmzpzlfVFSkuLg4FRYWatOmTVq0aJEWLlyolJQUs+bQoUOKi4vT7bffrl27dmnkyJEaPHiwVq9ebdYsXbpUSUlJmjhxonbs2KF27drJ4XAoJyfnyrwIAACg0vN4aKpWrZpCQkLMrX79+pKkvLw8vfXWW5o+fbruuOMORUVFacGCBdq0aZM2b94sSVqzZo2++uorvfPOO4qMjNQ999yjF154QXPmzFFhYaEkad68eWrWrJlefvlltW7dWsOHD9dDDz2kGTNmmD1Mnz5dQ4YM0YABAxQeHq558+apRo0aevvtt6/8CwIAAColj4emgwcPKjQ0VNddd5369u2rrKwsSVJmZqbOnTun2NhYs7ZVq1Zq3LixMjIyJEkZGRmKiIhQcHCwWeNwOORyubR3716z5sI1SmpK1igsLFRmZqZbjZeXl2JjY82ashQUFMjlcrltAADg6uXR0BQdHa2FCxcqNTVVc+fO1aFDh3Trrbfq9OnTcjqd8vHxUWBgoNtzgoOD5XQ6JUlOp9MtMJXMl8z9Xo3L5dKZM2d04sQJFRUVlVlTskZZpkyZooCAAHMLCwsr12sAAACqhmqe3Pk999xj/veNN96o6OhoNWnSRMuWLZO/v78HO7u45ORkJSUlmY9dLhfBCQCAq5jHP567UGBgoK6//np98803CgkJUWFhoXJzc91qsrOzFRISIkkKCQkp9W26kscXq7Hb7fL391f9+vXl7e1dZk3JGmXx9fWV3W532wAAwNWrUoWmn376Sd9++60aNmyoqKgoVa9eXenp6eb8gQMHlJWVpZiYGElSTEyMdu/e7fYtt7S0NNntdoWHh5s1F65RUlOyho+Pj6KiotxqiouLlZ6ebtYAAAB4NDSNHj1a69ev1+HDh7Vp0yY9+OCD8vb2Vp8+fRQQEKBBgwYpKSlJn3/+uTIzMzVgwADFxMSoc+fOkqTu3bsrPDxcjz/+uL744gutXr1a48ePV2Jionx9fSVJQ4cO1XfffaexY8dq//79eu2117Rs2TKNGjXK7CMpKUlvvPGGFi1apH379mnYsGHKz8/XgAEDPPK6AACAysej1zT98MMP6tOnj3788Uc1aNBAt9xyizZv3qwGDRpIkmbMmCEvLy/16tVLBQUFcjgceu2118zne3t7a+XKlRo2bJhiYmJUs2ZNJSQkaPLkyWZNs2bNtGrVKo0aNUqzZs1So0aN9Oabb8rhcJg18fHxOn78uFJSUuR0OhUZGanU1NRSF4cDAIBrl80wDMPTTVwNXC6XAgIClJeXd1mvb4oa84/LtjZQVWVO6+fpFipE1uQIT7cAVDqNU3Zf1vUv5e93pbqmCQAAoLIiNAEAAFhAaAIAALCA0AQAAGABoQkAAMACQhMAAIAFhCYAAAALCE0AAAAWEJoAAAAsIDQBAABYQGgCAACwgNAEAABgAaEJAADAAkITAACABYQmAAAACwhNAAAAFhCaAAAALCA0AQAAWEBoAgAAsIDQBAAAYAGhCQAAwAJCEwAAgAWEJgAAAAsITQAAABYQmgAAACwgNAEAAFhAaAIAALCA0AQAAGABoQkAAMACQhMAAIAFhCYAAAALCE0AAAAWEJoAAAAsIDQBAABYQGgCAACwgNAEAABgAaEJAADAAkITAACABYQmAAAACwhNAAAAFhCaAAAALKg0oelvf/ubbDabRo4caY6dPXtWiYmJqlevnmrVqqVevXopOzvb7XlZWVmKi4tTjRo1FBQUpDFjxuj8+fNuNevWrVP79u3l6+urFi1aaOHChaX2P2fOHDVt2lR+fn6Kjo7W1q1bL8dhAgCAKqpShKZt27bp9ddf14033ug2PmrUKH3yySdavny51q9fr6NHj6pnz57mfFFRkeLi4lRYWKhNmzZp0aJFWrhwoVJSUsyaQ4cOKS4uTrfffrt27dqlkSNHavDgwVq9erVZs3TpUiUlJWnixInasWOH2rVrJ4fDoZycnMt/8AAAoErweGj66aef1LdvX73xxhuqU6eOOZ6Xl6e33npL06dP1x133KGoqCgtWLBAmzZt0ubNmyVJa9as0VdffaV33nlHkZGRuueee/TCCy9ozpw5KiwslCTNmzdPzZo108svv6zWrVtr+PDheuihhzRjxgxzX9OnT9eQIUM0YMAAhYeHa968eapRo4befvvtK/tiAACASsvjoSkxMVFxcXGKjY11G8/MzNS5c+fcxlu1aqXGjRsrIyNDkpSRkaGIiAgFBwebNQ6HQy6XS3v37jVrfr22w+Ew1ygsLFRmZqZbjZeXl2JjY80aAACAap7c+ZIlS7Rjxw5t27at1JzT6ZSPj48CAwPdxoODg+V0Os2aCwNTyXzJ3O/VuFwunTlzRqdOnVJRUVGZNfv37//N3gsKClRQUGA+drlcFzlaAABQlXnsTNORI0f0zDPP6N1335Wfn5+n2ii3KVOmKCAgwNzCwsI83RIAALiMPBaaMjMzlZOTo/bt26tatWqqVq2a1q9fr1deeUXVqlVTcHCwCgsLlZub6/a87OxshYSESJJCQkJKfZuu5PHFaux2u/z9/VW/fn15e3uXWVOyRlmSk5OVl5dnbkeOHCnX6wAAAKoGj4WmO++8U7t379auXbvMrUOHDurbt6/539WrV1d6err5nAMHDigrK0sxMTGSpJiYGO3evdvtW25paWmy2+0KDw83ay5co6SmZA0fHx9FRUW51RQXFys9Pd2sKYuvr6/sdrvbBgAArl4eu6apdu3aatu2rdtYzZo1Va9ePXN80KBBSkpKUt26dWW32zVixAjFxMSoc+fOkqTu3bsrPDxcjz/+uKZOnSqn06nx48crMTFRvr6+kqShQ4dq9uzZGjt2rAYOHKi1a9dq2bJlWrVqlbnfpKQkJSQkqEOHDurUqZNmzpyp/Px8DRgw4Aq9GgAAoLLz6IXgFzNjxgx5eXmpV69eKigokMPh0GuvvWbOe3t7a+XKlRo2bJhiYmJUs2ZNJSQkaPLkyWZNs2bNtGrVKo0aNUqzZs1So0aN9Oabb8rhcJg18fHxOn78uFJSUuR0OhUZGanU1NRSF4cDAIBrl80wDMPTTVwNXC6XAgIClJeXd1k/qosa84/LtjZQVWVO6+fpFipE1uQIT7cAVDqNU3Zf1vUv5e+3x+/TBAAAUBWUKzTdcccdpb7VJv2S1u64444/2hMAAEClU67QtG7dOvNnSi509uxZ/fvf//7DTQEAAFQ2l3Qh+Jdffmn+91dffWXedVv65cdzU1NT9ac//aniugMAAKgkLik0RUZGymazyWazlfkxnL+/v1599dUKaw4AAKCyuKTQdOjQIRmGoeuuu05bt25VgwYNzDkfHx8FBQXJ29u7wpsEAADwtEsKTU2aNJH0yx2zAQAAriXlvrnlwYMH9fnnnysnJ6dUiEpJSfnDjQEAAFQm5QpNb7zxhoYNG6b69esrJCRENpvNnLPZbIQmAABw1SlXaHrxxRf1l7/8RePGjavofgAAACqlct2n6dSpU3r44YcruhcAAIBKq1yh6eGHH9aaNWsquhcAAIBKq1wfz7Vo0UITJkzQ5s2bFRERoerVq7vNP/300xXSHAAAQGVRrtA0f/581apVS+vXr9f69evd5mw2G6EJAABcdcoVmg4dOlTRfQAAAFRq5bqmCQAA4FpTrjNNAwcO/N35t99+u1zNAAAAVFblCk2nTp1ye3zu3Dnt2bNHubm5Zf6QLwAAQFVXrtC0YsWKUmPFxcUaNmyYmjdv/oebAgAAqGwq7JomLy8vJSUlacaMGRW1JAAAQKVRoReCf/vttzp//nxFLgkAAFAplOvjuaSkJLfHhmHo2LFjWrVqlRISEiqkMQAAgMqkXKFp586dbo+9vLzUoEEDvfzyyxf9Zh0AAEBVVK7Q9Pnnn1d0HwAAAJVauUJTiePHj+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+Oj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GjZsqEceeUTnz59Xjx49NGTIEL333nsqLCzU1q1bZbPZJEl9+/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": "b19ef0b2-7d69-428c-fc19-cef6c9e6f076" }, "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": "073365f1-5924-45fc-cf1c-041d7b8e63a3" }, "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": 4 } ] }, { "cell_type": "markdown", "source": [ "## Feature Selection" ], "metadata": { "id": "FiXD9jj7JzAt" } }, { "cell_type": "code", "source": [ "from sklearn.feature_selection import SelectKBest\n", "from sklearn.feature_selection import f_regression\n", "\n", "selector = SelectKBest(f_regression, k=10)\n", "X = df.drop(['class'], axis=1)\n", "Y = df[\"class\"].astype('category').cat.codes\n", "X_new = selector.fit(X, Y)\n", "X.columns.values[selector.get_support()]" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "AeldOcvmp9oy", "outputId": "6c7920b9-7355-4dd1-f6ab-ddd17f3c73ed" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "array(['flag', 'logged_in', 'count', 'serror_rate', 'srv_serror_rate',\n", " 'same_srv_rate', 'dst_host_srv_count', 'dst_host_same_srv_rate',\n", " 'dst_host_serror_rate', 'dst_host_srv_serror_rate'], dtype=object)" ] }, "metadata": {}, "execution_count": 5 } ] }, { "cell_type": "code", "source": [ "columns = X.columns.values[selector.get_support()]" ], "metadata": { "id": "CcnIqyU6u_lU" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "columns = []\n", "for c in X.columns.values[selector.get_support()]:\n", " columns.append(str(c))" ], "metadata": { "id": "tUrifaWBvZpx" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "principalDf = pd.DataFrame(data = X\n", " , columns = X.columns.values[selector.get_support()])\n", "finalDf = pd.concat([principalDf, df[\"class\"]], axis = 1)\n", "finalDf" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 424 }, "id": "RzFbtWLlsmn0", "outputId": "cb087fd1-8a86-4031-b239-65c5902a96df" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ " flag logged_in count serror_rate srv_serror_rate same_srv_rate \\\n", "0 9 0 2 0.0 0.0 1.00 \n", "1 9 0 13 0.0 0.0 0.08 \n", "2 5 0 123 1.0 1.0 0.05 \n", "3 9 1 5 0.2 0.2 1.00 \n", "4 9 1 30 0.0 0.0 1.00 \n", "... ... ... ... ... ... ... \n", "139899 9 0 53 0.0 0.0 1.00 \n", "139900 9 1 1 0.0 0.0 1.00 \n", "139901 9 1 2 0.0 0.0 1.00 \n", "139902 9 0 4 0.0 0.0 1.00 \n", "139903 1 0 4 0.0 0.0 0.25 \n", "\n", " dst_host_srv_count dst_host_same_srv_rate dst_host_serror_rate \\\n", "0 25 0.17 0.00 \n", "1 1 0.00 0.00 \n", "2 26 0.10 1.00 \n", "3 255 1.00 0.03 \n", "4 255 1.00 0.00 \n", "... ... ... ... \n", "139899 255 1.00 0.00 \n", "139900 141 0.72 0.01 \n", "139901 255 1.00 0.01 \n", "139902 252 0.99 0.00 \n", "139903 21 0.08 0.00 \n", "\n", " dst_host_srv_serror_rate class \n", "0 0.00 normal \n", "1 0.00 normal \n", "2 1.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 0.00 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", "
flaglogged_incountserror_ratesrv_serror_ratesame_srv_ratedst_host_srv_countdst_host_same_srv_ratedst_host_serror_ratedst_host_srv_serror_rateclass
09020.00.01.00250.170.000.00normal
190130.00.00.0810.000.000.00normal
2501231.01.00.05260.101.001.00anomaly
39150.20.21.002551.000.030.01normal
491300.00.01.002551.000.000.00normal
....................................
13989990530.00.01.002551.000.000.00anomaly
1399009110.00.01.001410.720.010.00normal
1399019120.00.01.002551.000.010.00normal
1399029040.00.01.002520.990.000.00normal
1399031040.00.00.25210.080.000.00anomaly
\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": 8 } ] }, { "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": "3dbd7868-4ff2-43c1-ad87-85b0c0fa02ec" }, "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": "61de3670-6ab4-47e9-d43c-3a0ef8f65cbd" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Start training the model...\n", "Epoch 1/10\n", "820/820 [==============================] - 47s 27ms/step - loss: 0.2367 - sparse_categorical_accuracy: 0.9130\n", "Epoch 2/10\n", "820/820 [==============================] - 16s 20ms/step - loss: 0.1888 - sparse_categorical_accuracy: 0.9268\n", "Epoch 3/10\n", "820/820 [==============================] - 18s 21ms/step - loss: 0.1845 - sparse_categorical_accuracy: 0.9291\n", "Epoch 4/10\n", "820/820 [==============================] - 17s 21ms/step - loss: 0.1821 - sparse_categorical_accuracy: 0.9304\n", "Epoch 5/10\n", "820/820 [==============================] - 17s 20ms/step - loss: 0.1800 - sparse_categorical_accuracy: 0.9312\n", "Epoch 6/10\n", "820/820 [==============================] - 16s 20ms/step - loss: 0.1782 - sparse_categorical_accuracy: 0.9317\n", "Epoch 7/10\n", "820/820 [==============================] - 17s 20ms/step - loss: 0.1771 - sparse_categorical_accuracy: 0.9320\n", "Epoch 8/10\n", "820/820 [==============================] - 20s 24ms/step - loss: 0.1763 - sparse_categorical_accuracy: 0.9323\n", "Epoch 9/10\n", "820/820 [==============================] - 17s 20ms/step - loss: 0.1753 - sparse_categorical_accuracy: 0.9327\n", "Epoch 10/10\n", "820/820 [==============================] - 16s 20ms/step - loss: 0.1746 - sparse_categorical_accuracy: 0.9327\n", "Model training finished\n", "Evaluating the model on the test data...\n", "274/274 [==============================] - 6s 13ms/step - loss: 0.1681 - sparse_categorical_accuracy: 0.9350\n", "Test accuracy: 93.5%\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 }, "id": "Jwbb045sQ-BF", "outputId": "d086aa83-cb08-4595-9ec0-ce8d11c03244" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "274/274 [==============================] - 4s 9ms/step\n", "[[0.91360217 0.03922436]\n", " [0.08639783 0.96077564]]\n", "ACC: 0.9371889086346885\n", "PR: 0.95883368635097\n", "TPR: 0.9136021730045968\n", "FPR: 0.03922435573521981\n", "F1Score: 0.9356716119522974\n" ] }, { "output_type": "display_data", "data": { "text/plain": [ "
" ], "image/png": "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wzCUmZmpJk2aKDg4WMnJyZozZ07lfjjUeHa73eU7HhMToxdeeEGtWrVSSEiI4uPjNXDgQBUUFJR5jDVr1qhTp04KCwtTeHi4UlJStHLlSuf7WVlZ6tChg4KDgxUfH68HH3xQx44dq4yPB/whEghUuZEjR+rJJ5/UunXr1Lp163LNyczM1MyZMzVlyhT9/PPPGjx4sO644w4tXbq0gqMF/pifn58mTZqkn3/+WW+88Ya+/PJLDR8+vMzxvXr1UsOGDfXdd98pOztbI0eOVEBAgCRp06ZN6tatm2666Sb98MMPmj17trKyspSenl5ZHwcoE7ViVLnHHntMnTt3Lvf4oqIijR8/XosWLVJqaqokqWnTpsrKytIrr7yijh07VlSogIt58+a5tOGuvvpqvf/++87XjRs31uOPP6777rtPL7/8sttj5OXladiwYWrevLkk6cILL3S+l5mZqV69eumhhx5yvjdp0iR17NhRkydPrjEPbUL1RAKBKteuXTuPxufk5Oj48eNnJB3FxcVq27atlaEBf6hTp06aPHmy83VISIgWLVqkzMxMrV+/Xvn5+SotLVVhYaGOHz+u2rVrn3GMIUOGqH///nrzzTeVlpamv//977rgggsknWxv/PDDD5o1a5ZzvGmaMgxDmzdvVosWLSr+QwJlIIFAlQsJCXF57efnp9/fYb2kpMT571P95Pnz5ysuLs5lHM8WQGUKCQlRQkKC8/WWLVt07bXX6v7779cTTzyhOnXqKCsrS3fffbeKi4vdJhBjxozR7bffrvnz5+uzzz7T6NGj9e677+qGG25QQUGB7r33Xj344INnzDv//PMr9LMBf4YEAtVOvXr19NNPP7nsW716tbMvnJSUJLvdrry8PNoVqFays7NlGIaee+45+fmdXGL23nvv/em8xMREJSYmavDgwerZs6emT5+uG264QRdddJHWrl3rkqQA1QWLKFHtXHnllVq5cqVmzpypX375RaNHj3ZJKMLCwjR06FANHjxYb7zxhjZt2qRVq1bpxRdf1BtvvFGFkaOmS0hIUElJiV588UXl5ubqzTff1JQpU8ocf+LECaWnp2vJkiXaunWrvvnmG3333XfO1sSIESO0fPlypaena/Xq1frll1/08ccfs4gS1QIJBKqdrl27KiMjQ8OHD9df//pXHT16VL1793YZM27cOGVkZCgzM1MtWrRQt27dNH/+fDVp0qSKogak5ORkTZgwQU899ZRatmypWbNmKTMzs8zx/v7+OnDggHr37q3ExETdcsstuvrqqzV27FhJUuvWrbV06VJt3LhRHTp0UNu2bTVq1CjFxsZW1kcCysTjvAEAgMeoQAAAAI+RQAAAAI+RQAAAAI+RQAAAAI+RQAAAAI+RQAAAAI+RQAAAAI+RQAAAAI+RQAA+om/fvrr++uudr6+44grnY54r05IlS2Sz2XT48OEyx9hsNn300UflPuaYMWPUpk0br+LasmWLbDabVq9e7dVxAJQPCQTghb59+8pms8lmsykwMFAJCQl67LHHVFpaWuHn/uCDDzRu3LhyjS3PL30A8ARP4wS81K1bN02fPl1FRUVasGCBHnjgAQUEBOiRRx45Y2xxcbECAwMtOW+dOnUsOQ4AnA0qEICX7Ha7YmJi1KhRI91///1KS0vTJ598Iul02+GJJ55QbGysmjVrJknatm2bbrnlFkVGRqpOnTrq0aOHtmzZ4jymw+HQkCFDFBkZqfPOO0/Dhw/X7x9b8/sWRlFRkUaMGKH4+HjZ7XYlJCTo9ddf15YtW9SpUydJUlRUlGw2m/r27StJMgxDmZmZatKkiYKDg5WcnKw5c+a4nGfBggVKTExUcHCwOnXq5BJneY0YMUKJiYmqXbu2mjZtqoyMDJWUlJwx7pVXXlF8fLxq166tW265RUeOHHF5/7XXXlOLFi0UFBSk5s2b6+WXX/Y4FgDWIIEALBYcHKzi4mLn68WLF2vDhg364osvNG/ePJWUlKhr164KCwvTsmXL9M033yg0NFTdunVzznvuuec0Y8YMTZs2TVlZWTp48KA+/PDDPzxv79699c4772jSpElat26dXnnlFYWGhio+Pl5z586VJG3YsEG7du3SCy+8IEnKzMzUzJkzNWXKFP38888aPHiw7rjjDi1dulTSyUTnxhtvVPfu3bV69Wr1799fI0eO9Pi/SVhYmGbMmKG1a9fqhRde0NSpU/X888+7jMnJydF7772nTz/9VAsXLtT333+vgQMHOt+fNWuWRo0apSeeeELr1q3T+PHjlZGRwSPcgapiAjhrffr0MXv06GGapmkahmF+8cUXpt1uN4cOHep8Pzo62iwqKnLOefPNN81mzZqZhmE49xUVFZnBwcHm559/bpqmaTZo0MB8+umnne+XlJSYDRs2dJ7LNE2zY8eO5qBBg0zTNM0NGzaYkswvvvjCbZxfffWVKck8dOiQc19hYaFZu3Ztc/ny5S5j7777brNnz56maZrmI488YiYlJbm8P2LEiDOO9XuSzA8//LDM95955hkzJSXF+Xr06NGmv7+/uX37due+zz77zPTz8zN37dplmqZpXnDBBebbb7/tcpxx48aZqamppmma5ubNm01J5vfff1/meQFYhzUQgJfmzZun0NBQlZSUyDAM3X777RozZozz/VatWrmse1izZo1ycnIUFhbmcpzCwkJt2rRJR44c0a5du9S+fXvne7Vq1VK7du3OaGOcsnr1avn7+6tjx47ljjsnJ0fHjx9X586dXfYXFxerbdu2kqR169a5xCFJqamp5T7HKbNnz9akSZO0adMmFRQUqLS0VOHh4S5jzj//fMXFxbmcxzAMbdiwQWFhYdq0aZPuvvtuDRgwwDmmtLRUERERHscDwHskEICXOnXqpMmTJyswMFCxsbGqVcv1f6uQkBCX1wUFBUpJSdGsWbPOOFa9evXOKobg4GCP5xQUFEiS5s+f7/KLWzq5rsMqK1asUK9evTR27Fh17dpVERERevfdd/Xcc895HOvUqVPPSGj8/f0tixVA+ZFAAF4KCQlRQkJCucdfdNFFmj17turXr3/GX+GnNGjQQP/73/90+eWXSzr5l3Z2drYuuugit+NbtWolwzC0dOlSpaWlnfH+qQqIw+Fw7ktKSpLdbldeXl6ZlYsWLVo4F4Se8t///vfPP+RvLF++XI0aNdKjjz7q3Ld169YzxuXl5Wnnzp2KjY11nsfPz0/NmjVTdHS0YmNjlZubq169enl0fgAVg0WUQCXr1auX6tatqx49emjZsmXavHmzlixZogcffFDbt2+XJA0aNEhPPvmkPvroI61fv14DBw78w3s4NG7cWH369NFdd92ljz76yHnM9957T5LUqFEj2Ww2zZs3T/v27VNBQYHCwsI0dOhQDR48WG+88YY2bdqkVatW6cUXX3QuTLzvvvv0yy+/aNiwYdqwYYPefvttzZgxw6PPe+GFFyovL0/vvvuuNm3apEmTJrldEBoUFKQ+ffpozZo1WrZsmR588EHdcsstiomJkSSNHTtWmZmZmjRpkjZu3Kgff/xR06dP14QJEzyKB4A1SCCASla7dm19/fXXOv/883XjjTeqRYsWuvvuu1VYWOisSDz88MO688471adPH6WmpiosLEw33HDDHx538uTJuvnmmzVw4EA1b95cAwYM0LFjxyRJcXFxGjt2rEaOHKno6Gilp6dLksaNG6eMjAxlZmaqRYsW6tatm+bPn68mTZpIOrkuYe7cufroo4+UnJysKVOmaPz48R593uuuu06DBw9Wenq62rRpo+XLlysjI+OMcQkJCbrxxht1zTXXqEuXLmrdurXLZZr9+/fXa6+9punTp6tVq1bq2LGjZsyY4YwVQOWymWWtygIAACgDFQgAAOAxEggAAOAxEggAAOAxEggAAOAxEggAAOAxEggAAOAxEggAAOAxEggAAOAxEggAAOAxEggAAOAxEggAAOCx/wdrLZlLxGK5CAAAAABJRU5ErkJggg==\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": "ad0343de-eb95-4965-99b4-afddeef85d72" }, "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)\n", "frame" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 496 }, "id": "orUywltc2yaK", "outputId": "d3e81ead-2ee5-4627-d1e6-6c2c054d2489" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ " Destination Port Flow Duration Total Fwd Packets \\\n", "0 54865 3 2 \n", "1 55054 109 1 \n", "2 55055 52 1 \n", "3 46236 34 1 \n", "4 54863 3 2 \n", "... ... ... ... \n", "2830738 53 32215 4 \n", "2830739 53 324 2 \n", "2830740 58030 82 2 \n", "2830741 53 1048635 6 \n", "2830742 53 94939 4 \n", "\n", " Total Backward Packets Total Length of Fwd Packets \\\n", "0 0 12 \n", "1 1 6 \n", "2 1 6 \n", "3 1 6 \n", "4 0 12 \n", "... ... ... \n", "2830738 2 112 \n", "2830739 2 84 \n", "2830740 1 31 \n", "2830741 2 192 \n", "2830742 2 188 \n", "\n", " Total Length of Bwd Packets Fwd Packet Length Max \\\n", "0 0 6 \n", "1 6 6 \n", "2 6 6 \n", "3 6 6 \n", "4 0 6 \n", "... ... ... \n", "2830738 152 28 \n", "2830739 362 42 \n", "2830740 6 31 \n", "2830741 256 32 \n", "2830742 226 47 \n", "\n", " Fwd Packet Length Min Fwd Packet Length Mean \\\n", "0 6 6.0 \n", "1 6 6.0 \n", "2 6 6.0 \n", "3 6 6.0 \n", "4 6 6.0 \n", "... ... ... \n", "2830738 28 28.0 \n", "2830739 42 42.0 \n", "2830740 0 15.5 \n", "2830741 32 32.0 \n", "2830742 47 47.0 \n", "\n", " Fwd Packet Length Std ... min_seg_size_forward Active Mean \\\n", "0 0.00000 ... 20 0.0 \n", "1 0.00000 ... 20 0.0 \n", "2 0.00000 ... 20 0.0 \n", "3 0.00000 ... 20 0.0 \n", "4 0.00000 ... 20 0.0 \n", "... ... ... ... ... \n", "2830738 0.00000 ... 20 0.0 \n", "2830739 0.00000 ... 20 0.0 \n", "2830740 21.92031 ... 32 0.0 \n", "2830741 0.00000 ... 20 0.0 \n", "2830742 0.00000 ... 20 0.0 \n", "\n", " Active Std Active Max Active Min Idle Mean Idle Std \\\n", "0 0.0 0 0 0.0 0.0 \n", "1 0.0 0 0 0.0 0.0 \n", "2 0.0 0 0 0.0 0.0 \n", "3 0.0 0 0 0.0 0.0 \n", "4 0.0 0 0 0.0 0.0 \n", "... ... ... ... ... ... \n", "2830738 0.0 0 0 0.0 0.0 \n", "2830739 0.0 0 0 0.0 0.0 \n", "2830740 0.0 0 0 0.0 0.0 \n", "2830741 0.0 0 0 0.0 0.0 \n", "2830742 0.0 0 0 0.0 0.0 \n", "\n", " Idle Max Idle Min Label \n", "0 0 0 BENIGN \n", "1 0 0 BENIGN \n", "2 0 0 BENIGN \n", "3 0 0 BENIGN \n", "4 0 0 BENIGN \n", "... ... ... ... \n", "2830738 0 0 BENIGN \n", "2830739 0 0 BENIGN \n", "2830740 0 0 BENIGN \n", "2830741 0 0 BENIGN \n", "2830742 0 0 BENIGN \n", "\n", "[2830743 rows x 79 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", "
Destination PortFlow DurationTotal Fwd PacketsTotal Backward PacketsTotal Length of Fwd PacketsTotal Length of Bwd PacketsFwd Packet Length MaxFwd Packet Length MinFwd Packet Length MeanFwd Packet Length Std...min_seg_size_forwardActive MeanActive StdActive MaxActive MinIdle MeanIdle StdIdle MaxIdle MinLabel
054865320120666.00.00000...200.00.0000.00.000BENIGN
1550541091166666.00.00000...200.00.0000.00.000BENIGN
255055521166666.00.00000...200.00.0000.00.000BENIGN
346236341166666.00.00000...200.00.0000.00.000BENIGN
454863320120666.00.00000...200.00.0000.00.000BENIGN
..................................................................
2830738533221542112152282828.00.00000...200.00.0000.00.000BENIGN
2830739533242284362424242.00.00000...200.00.0000.00.000BENIGN
283074058030822131631015.521.92031...320.00.0000.00.000BENIGN
283074153104863562192256323232.00.00000...200.00.0000.00.000BENIGN
2830742539493942188226474747.00.00000...200.00.0000.00.000BENIGN
\n", "

2830743 rows × 79 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": 3 } ] }, { "cell_type": "code", "source": [ "column_headers = list(frame.columns.values)\n", "column_headers" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "eJcGQKBn4Abs", "outputId": "2ceaad2f-38fd-461e-9241-6f59c496f0a4" }, "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": 4 } ] }, { "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\"^(.(?" ], "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjcAAAHHCAYAAABDUnkqAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAsDElEQVR4nO3de1RVdcL/8c8R9Ihy8cpFI294y0zUvOA8421QNGpl+qg5+ojXpykrjdLieSYda4yavLUayxxBsyYlbdSyxmRowFLMTLE0L3nFFFBLrioo7N8f/ThPJ0AFkQNf36+19lqdvb97n+9mrYPv9tnnYLMsyxIAAIAharl6AgAAAJWJuAEAAEYhbgAAgFGIGwAAYBTiBgAAGIW4AQAARiFuAACAUYgbAABgFOIGAAAYhbgBcMutXLlSNptNJ06ccPVUANwGiBsAcKFPPvlEf/rTn1w9DcAoxA0AuNAnn3yiuXPnunoagFGIGwAAYBTiBoCTdevWyWazKSkpqcS2t956SzabTfv27ZMkffPNN5owYYJat26tunXryt/fX5MmTdKPP/543eex2Wylvh3TsmVLTZgwwWldZmamZsyYocDAQNntdgUFBemVV15RUVHRDZ3TP//5T/Xr109eXl7y9vZWjx499N577zmNWbt2rbp37y4PDw81adJE48aN0+nTp53G9O/fX/379y9x/AkTJqhly5aOxydOnJDNZtP8+fO1bNkytWnTRna7XT169NBXX33ltN+SJUscP4/iBcDNcXf1BABUL+Hh4fL09NT777+vfv36OW2Li4tTp06ddPfdd0uS4uPjdezYMU2cOFH+/v7av3+/li1bpv3792vHjh2V8g/1xYsX1a9fP50+fVqPPPKI7rzzTm3fvl1RUVFKS0vT4sWLr7n/ypUrNWnSJHXq1ElRUVFq0KCB9uzZo82bN+v3v/+9Y8zEiRPVo0cPRUdHKyMjQ6+99pq2bdumPXv2qEGDBhWa+3vvvaecnBw98sgjstls+stf/qLhw4fr2LFjql27th555BGdOXNG8fHxeueddyr0HABKYQHAr4wZM8by9fW1rl696liXlpZm1apVy3rhhRcc6y5evFhi39WrV1uSrK1btzrWrVixwpJkHT9+3LFOkjVnzpwS+7do0cKKiIhwPH7xxRet+vXrW4cPH3Ya99xzz1lubm5WampqmeeRmZlpeXl5Wb169bIuXbrktK2oqMiyLMsqKCiwfH19rbvvvttpzKZNmyxJ1uzZsx3r+vXrZ/Xr16/E80RERFgtWrRwPD5+/LglyWrcuLH1008/OdZv3LjRkmR99NFHjnXTpk2z+FUMVC7elgJQwujRo3X27FklJiY61q1bt05FRUUaPXq0Y52Hh4fjvy9fvqzz58+rd+/ekqTdu3dXylzWrl2r3/72t2rYsKHOnz/vWEJDQ1VYWKitW7eWuW98fLxycnL03HPPqW7duk7biq8q7dq1S2fPntVjjz3mNCY8PFwdOnTQxx9/XOG5jx49Wg0bNnQ8/u1vfytJOnbsWIWPCeD6buu42bp1qx544AE1a9ZMNptNGzZsKPcxLMvS/Pnz1a5dO9ntdjVv3lzz5s2r/MkCVWjIkCHy8fFRXFycY11cXJyCg4PVrl07x7qffvpJ06dPl5+fnzw8PNS0aVO1atVKkpSVlVUpc/n++++1efNmNW3a1GkJDQ2VJJ09e7bMfY8ePSpJjrfRSnPy5ElJUvv27Uts69Chg2N7Rdx5551Oj4tD58KFCxU+JoDru63vucnLy1OXLl00adIkDR8+vELHmD59urZs2aL58+erc+fO+umnn/TTTz9V8kyBqmW32zVs2DCtX79eb7zxhjIyMrRt2za99NJLTuNGjRql7du3a+bMmQoODpanp6eKioo0ZMiQG77Z99cKCwudHhcVFWnQoEGaNWtWqeN/GVu3ms1mk2VZJdb/es7F3NzcSl1f2jEAVJ7bOm6GDh2qoUOHlrk9Pz9f//u//6vVq1crMzNTd999t1555RXHpyUOHDigN998U/v27XP8X1/x/7UCNd3o0aP19ttvKyEhQQcOHJBlWU5vSV24cEEJCQmaO3euZs+e7Vj//fff39DxGzZsqMzMTKd1BQUFSktLc1rXpk0b5ebmOq7UlEebNm0kSfv27VNQUFCpY1q0aCFJOnTokAYOHOi07dChQ47txXMu7S2lm7m6w6ejgMp3W78tdT2PP/64kpOTtWbNGn3zzTcaOXKkhgwZ4vjl/dFHH6l169batGmTWrVqpZYtW2rKlClcuYERQkND1ahRI8XFxSkuLk49e/Z0ivfiqxK/vgpxvU8vFWvTpk2J+2WWLVtW4irIqFGjlJycrE8//bTEMTIzM3X16tUyn2Pw4MHy8vJSdHS0Ll++7LSteN733nuvfH19tXTpUuXn5zu2//Of/9SBAwcUHh7uNOeDBw/q3LlzjnV79+7Vtm3bbuCMS1e/fn3HuQCoHLf1lZtrSU1N1YoVK5SamqpmzZpJkp555hlt3rxZK1as0EsvvaRjx47p5MmTWrt2rVatWqXCwkI99dRT+s///E999tlnLj4D4ObUrl1bw4cP15o1a5SXl6f58+c7bff29lbfvn31l7/8RVeuXFHz5s21ZcsWHT9+/IaOP2XKFP3hD3/QiBEjNGjQIO3du1effvqpmjRp4jRu5syZ+vDDD3X//fdrwoQJ6t69u/Ly8vTtt99q3bp1OnHiRIl9fjnHRYsWacqUKerRo4d+//vfq2HDhtq7d68uXryot99+W7Vr19Yrr7yiiRMnql+/fhozZozjo+AtW7bUU0895TjepEmTtHDhQoWFhWny5Mk6e/asli5dqk6dOik7O7ucP+Gfde/eXZL05JNPKiwsTG5ubnr44YcrdCwA/58rP6pVnUiy1q9f73hc/DHQ+vXrOy3u7u7WqFGjLMuyrKlTp1qSrEOHDjn2+/rrry1J1sGDB6v6FIBKFx8fb0mybDabderUqRLbf/jhB+uhhx6yGjRoYPn4+FgjR460zpw5U+Jj3qV9FLywsNB69tlnrSZNmlj16tWzwsLCrCNHjpT4KLhlWVZOTo4VFRVlBQUFWXXq1LGaNGli9enTx5o/f75VUFBw3fP48MMPrT59+lgeHh6Wt7e31bNnT2v16tVOY+Li4qyuXbtadrvdatSokTV27Fjrhx9+KHGsd99912rdurVVp04dKzg42Pr000/L/Cj4q6++WmL/X/9srl69aj3xxBNW06ZNLZvNxsfCgUpgsyzubJN+ft97/fr1GjZsmKSfPxkyduxY7d+/v8RNgZ6envL399ecOXP00ksv6cqVK45tly5dUr169bRlyxYNGjSoKk8BAACIt6XK1LVrVxUWFurs2bOO76b4td/85je6evWqjh496rhx8fDhw5LkdBMiAACoOrf1lZvc3FwdOXJE0s8xs3DhQg0YMECNGjXSnXfeqXHjxmnbtm1asGCBunbtqnPnzikhIUH33HOPwsPDVVRUpB49esjT01OLFy9WUVGRpk2bJm9vb23ZssXFZwcAwO3pto6bxMREDRgwoMT6iIgIrVy5UleuXNGf//xnrVq1SqdPn1aTJk3Uu3dvzZ07V507d5YknTlzRk888YS2bNmi+vXra+jQoVqwYIEaNWpU1acDAAB0m8cNAAAwD99zAwAAjELcAAAAo9x2n5YqKirSmTNn5OXlxdeeAwBQQ1iWpZycHDVr1ky1al372sxtFzdnzpxRYGCgq6cBAAAq4NSpU7rjjjuuOea2ixsvLy9JP/9wvL29XTwbAABwI7KzsxUYGOj4d/xabru4KX4rytvbm7gBAKCGuZFbSrihGAAAGIW4AQAARiFuAACAUYgbAABgFOIGAAAYhbgBAABGIW4AAIBRiBsAAGAU4gYAABiFuAEAAEYhbgAAgFGIGwAAYBTiBgAAGIW4AQAARiFuAACAUdxdPQFTdZ+5ytVTAKqdr18d7+opALgNcOUGAAAYhbgBAABGIW4AAIBRiBsAAGAU4gYAABiFuAEAAEYhbgAAgFGIGwAAYBTiBgAAGIW4AQAARiFuAACAUYgbAABgFOIGAAAYhbgBAABGIW4AAIBRiBsAAGAU4gYAABiFuAEAAEYhbgAAgFGIGwAAYBTiBgAAGIW4AQAARiFuAACAUYgbAABgFOIGAAAYhbgBAABGIW4AAIBRiBsAAGAU4gYAABiFuAEAAEYhbgAAgFGIGwAAYBTiBgAAGIW4AQAARiFuAACAUYgbAABgFOIGAAAYxaVxEx0drR49esjLy0u+vr4aNmyYDh06dN391q5dqw4dOqhu3brq3LmzPvnkkyqYLQAAqAlcGjdJSUmaNm2aduzYofj4eF25ckWDBw9WXl5emfts375dY8aM0eTJk7Vnzx4NGzZMw4YN0759+6pw5gAAoLqyWZZluXoSxc6dOydfX18lJSWpb9++pY4ZPXq08vLytGnTJse63r17Kzg4WEuXLr3uc2RnZ8vHx0dZWVny9vautLn/WveZq27ZsYGa6utXx7t6CgBqqPL8+12t7rnJysqSJDVq1KjMMcnJyQoNDXVaFxYWpuTk5FLH5+fnKzs722kBAADmqjZxU1RUpBkzZug3v/mN7r777jLHpaeny8/Pz2mdn5+f0tPTSx0fHR0tHx8fxxIYGFip8wYAANVLtYmbadOmad++fVqzZk2lHjcqKkpZWVmO5dSpU5V6fAAAUL24u3oCkvT4449r06ZN2rp1q+64445rjvX391dGRobTuoyMDPn7+5c63m63y263V9pcAQBA9ebSKzeWZenxxx/X+vXr9dlnn6lVq1bX3SckJEQJCQlO6+Lj4xUSEnKrpgkAAGoQl165mTZtmt577z1t3LhRXl5ejvtmfHx85OHhIUkaP368mjdvrujoaEnS9OnT1a9fPy1YsEDh4eFas2aNdu3apWXLlrnsPAAAQPXh0is3b775prKystS/f38FBAQ4lri4OMeY1NRUpaWlOR736dNH7733npYtW6YuXbpo3bp12rBhwzVvQgYAALcPl165uZGv2ElMTCyxbuTIkRo5cuQtmBEAAKjpqs2npQAAACoDcQMAAIxC3AAAAKMQNwAAwCjEDQAAMApxAwAAjELcAAAAoxA3AADAKMQNAAAwCnEDAACMQtwAAACjEDcAAMAoxA0AADAKcQMAAIxC3AAAAKMQNwAAwCjEDQAAMApxAwAAjELcAAAAoxA3AADAKMQNAAAwCnEDAACMQtwAAACjEDcAAMAoxA0AADAKcQMAAIxC3AAAAKMQNwAAwCjEDQAAMApxAwAAjELcAAAAoxA3AADAKMQNAAAwCnEDAACMQtwAAACjEDcAAMAoxA0AADAKcQMAAIxC3AAAAKMQNwAAwCjEDQAAMApxAwAAjELcAAAAoxA3AADAKMQNAAAwCnEDAACMQtwAAACjEDcAAMAoxA0AADAKcQMAAIxC3AAAAKMQNwAAwCjEDQAAMApxAwAAjELcAAAAoxA3AADAKMQNAAAwCnEDAACMQtwAAACjEDcAAMAoxA0AADAKcQMAAIxC3AAAAKMQNwAAwCjEDQAAMApxAwAAjOLSuNm6daseeOABNWvWTDabTRs2bLjm+MTERNlsthJLenp61UwYAABUey6Nm7y8PHXp0kVLliwp136HDh1SWlqaY/H19b1FMwQAADWNuyuffOjQoRo6dGi59/P19VWDBg0qf0IAAKDGq5H33AQHBysgIECDBg3Stm3brjk2Pz9f2dnZTgsAADBXjYqbgIAALV26VB988IE++OADBQYGqn///tq9e3eZ+0RHR8vHx8exBAYGVuGMAQBAVXPp21Ll1b59e7Vv397xuE+fPjp69KgWLVqkd955p9R9oqKiFBkZ6XicnZ1N4AAAYLAaFTel6dmzp7744osyt9vtdtnt9iqcEQAAcKUa9bZUaVJSUhQQEODqaQAAgGrCpVducnNzdeTIEcfj48ePKyUlRY0aNdKdd96pqKgonT59WqtWrZIkLV68WK1atVKnTp10+fJlLV++XJ999pm2bNniqlMAAADVjEvjZteuXRowYIDjcfG9MREREVq5cqXS0tKUmprq2F5QUKCnn35ap0+fVr169XTPPffoX//6l9MxAADA7c1mWZbl6klUpezsbPn4+CgrK0ve3t637Hm6z1x1y44N1FRfvzre1VMAUEOV59/vGn/PDQAAwC8RNwAAwCjEDQAAMApxAwAAjELcAAAAoxA3AADAKMQNAAAwCnEDAACMQtwAAACjEDcAAMAoxA0AADAKcQMAAIxC3AAAAKMQNwAAwCjEDQAAMApxAwAAjELcAAAAoxA3AADAKMQNAAAwCnEDAACMQtwAAACjEDcAAMAoxA0AADAKcQMAAIxSobgZOHCgMjMzS6zPzs7WwIEDb3ZOAAAAFVahuElMTFRBQUGJ9ZcvX9bnn39+05MCAACoKPfyDP7mm28c//3dd98pPT3d8biwsFCbN29W8+bNK292AAAA5VSuuAkODpbNZpPNZiv17ScPDw+9/vrrlTY5AACA8ipX3Bw/flyWZal169bauXOnmjZt6thWp04d+fr6ys3NrdInCQAAcKPKFTctWrSQJBUVFd2SyQAAANyscsXNL33//ff697//rbNnz5aIndmzZ9/0xAAAACqiQnHzt7/9TY8++qiaNGkif39/2Ww2xzabzUbcAAAAl6lQ3Pz5z3/WvHnz9Oyzz1b2fAAAAG5Khb7n5sKFCxo5cmRlzwUAAOCmVShuRo4cqS1btlT2XAAAAG5ahd6WCgoK0vPPP68dO3aoc+fOql27ttP2J598slImBwAAUF4Viptly5bJ09NTSUlJSkpKctpms9mIGwAA4DIVipvjx49X9jwAAAAqRYXuuQEAAKiuKnTlZtKkSdfcHhsbW6HJAAAA3KwKxc2FCxecHl+5ckX79u1TZmZmqX9QEwAAoKpUKG7Wr19fYl1RUZEeffRRtWnT5qYnBQAAUFGVds9NrVq1FBkZqUWLFlXWIQEAAMqtUm8oPnr0qK5evVqZhwQAACiXCr0tFRkZ6fTYsiylpaXp448/VkRERKVMDAAAoCIqFDd79uxxelyrVi01bdpUCxYsuO4nqQAAAG6lCsXNv//978qeBwAAQKWoUNwUO3funA4dOiRJat++vZo2bVopkwIAAKioCt1QnJeXp0mTJikgIEB9+/ZV37591axZM02ePFkXL16s7DkCAADcsArFTWRkpJKSkvTRRx8pMzNTmZmZ2rhxo5KSkvT0009X9hwBAABuWIXelvrggw+0bt069e/f37Huvvvuk4eHh0aNGqU333yzsuYHAABQLhW6cnPx4kX5+fmVWO/r68vbUgAAwKUqFDchISGaM2eOLl++7Fh36dIlzZ07VyEhIZU2OQAAgPKq0NtSixcv1pAhQ3THHXeoS5cukqS9e/fKbrdry5YtlTpBAACA8qhQ3HTu3Fnff/+9/v73v+vgwYOSpDFjxmjs2LHy8PCo1AkCAACUR4XiJjo6Wn5+fpo6darT+tjYWJ07d07PPvtspUwOAACgvCp0z81bb72lDh06lFjfqVMnLV269KYnBQAAUFEVipv09HQFBASUWN+0aVOlpaXd9KQAAAAqqkJxExgYqG3btpVYv23bNjVr1uymJwUAAFBRFbrnZurUqZoxY4auXLmigQMHSpISEhI0a9YsvqEYAAC4VIXiZubMmfrxxx/12GOPqaCgQJJUt25dPfvss4qKiqrUCQIAAJRHheLGZrPplVde0fPPP68DBw7Iw8NDbdu2ld1ur+z5AQAAlEuF4qaYp6enevToUVlzAQAAuGkVuqEYAACguiJuAACAUYgbAABgFOIGAAAYxaVxs3XrVj3wwANq1qyZbDabNmzYcN19EhMT1a1bN9ntdgUFBWnlypW3fJ4AAKDmcGnc5OXlqUuXLlqyZMkNjT9+/LjCw8M1YMAApaSkaMaMGZoyZYo+/fTTWzxTAABQU9zUR8Fv1tChQzV06NAbHr906VK1atVKCxYskCR17NhRX3zxhRYtWqSwsLBbNU0AAFCD1Kh7bpKTkxUaGuq0LiwsTMnJyWXuk5+fr+zsbKcFAACYq0bFTXp6uvz8/JzW+fn5KTs7W5cuXSp1n+joaPn4+DiWwMDAqpgqAABwkRoVNxURFRWlrKwsx3Lq1ClXTwkAANxCLr3nprz8/f2VkZHhtC4jI0Pe3t7y8PAodR+73c7fvAIA4DZSo67chISEKCEhwWldfHy8QkJCXDQjAABQ3bg0bnJzc5WSkqKUlBRJP3/UOyUlRampqZJ+fktp/PjxjvF/+MMfdOzYMc2aNUsHDx7UG2+8offff19PPfWUK6YPAACqIZfGza5du9S1a1d17dpVkhQZGamuXbtq9uzZkqS0tDRH6EhSq1at9PHHHys+Pl5dunTRggULtHz5cj4GDgAAHFx6z03//v1lWVaZ20v79uH+/ftrz549t3BWAACgJqtR99wAAABcD3EDAACMQtwAAACjEDcAAMAoxA0AADAKcQMAAIxC3AAAAKMQNwAAwCjEDQAAMApxAwAAjELcAAAAoxA3AADAKMQNAAAwCnEDAACMQtwAAACjEDcAAMAoxA0AADAKcQMAAIxC3AAAAKMQNwAAwCjEDQAAMApxAwAAjELcAAAAoxA3AADAKMQNAAAwCnEDAACMQtwAAACjEDcAAMAoxA0AADAKcQMAAIxC3AAAAKMQNwAAwCjEDQAAMApxAwAAjELcAAAAoxA3AADAKMQNAAAwCnEDAACMQtwAAACjEDcAAMAoxA0AADAKcQMAAIxC3AAAAKMQNwAAwCjEDQAAMApxAwAAjELcAAAAoxA3AADAKMQNAAAwCnEDAACMQtwAAACjEDcAAMAoxA0AADAKcQMAAIxC3AAAAKMQNwAAwCjEDQAAMApxAwAAjELcAAAAoxA3AADAKMQNAAAwirurJwAANU3qC51dPQWg2rlz9reunoIDV24AAIBRiBsAAGAU4gYAABiFuAEAAEapFnGzZMkStWzZUnXr1lWvXr20c+fOMseuXLlSNpvNaalbt24VzhYAAFRnLo+buLg4RUZGas6cOdq9e7e6dOmisLAwnT17tsx9vL29lZaW5lhOnjxZhTMGAADVmcvjZuHChZo6daomTpyou+66S0uXLlW9evUUGxtb5j42m03+/v6Oxc/PrwpnDAAAqjOXxk1BQYG+/vprhYaGOtbVqlVLoaGhSk5OLnO/3NxctWjRQoGBgXrwwQe1f//+Msfm5+crOzvbaQEAAOZyadycP39ehYWFJa68+Pn5KT09vdR92rdvr9jYWG3cuFHvvvuuioqK1KdPH/3www+ljo+OjpaPj49jCQwMrPTzAAAA1YfL35Yqr5CQEI0fP17BwcHq16+f/vGPf6hp06Z66623Sh0fFRWlrKwsx3Lq1KkqnjEAAKhKLv3zC02aNJGbm5syMjKc1mdkZMjf3/+GjlG7dm117dpVR44cKXW73W6X3W6/6bkCAICawaVXburUqaPu3bsrISHBsa6oqEgJCQkKCQm5oWMUFhbq22+/VUBAwK2aJgAAqEFc/oczIyMjFRERoXvvvVc9e/bU4sWLlZeXp4kTJ0qSxo8fr+bNmys6OlqS9MILL6h3794KCgpSZmamXn31VZ08eVJTpkxx5WkAAIBqwuVxM3r0aJ07d06zZ89Wenq6goODtXnzZsdNxqmpqapV6/8uMF24cEFTp05Venq6GjZsqO7du2v79u266667XHUKAACgGrFZlmW5ehJVKTs7Wz4+PsrKypK3t/cte57uM1fdsmMDNdXXr4539RQqReoLnV09BaDauXP2t7f0+OX597vGfVoKAADgWogbAABgFOIGAAAYhbgBAABGIW4AAIBRiBsAAGAU4gYAABiFuAEAAEYhbgAAgFGIGwAAYBTiBgAAGIW4AQAARiFuAACAUYgbAABgFOIGAAAYhbgBAABGIW4AAIBRiBsAAGAU4gYAABiFuAEAAEYhbgAAgFGIGwAAYBTiBgAAGIW4AQAARiFuAACAUYgbAABgFOIGAAAYhbgBAABGIW4AAIBRiBsAAGAU4gYAABiFuAEAAEYhbgAAgFGIGwAAYBTiBgAAGIW4AQAARiFuAACAUYgbAABgFOIGAAAYhbgBAABGIW4AAIBRiBsAAGAU4gYAABiFuAEAAEYhbgAAgFGIGwAAYBTiBgAAGIW4AQAARiFuAACAUYgbAABgFOIGAAAYhbgBAABGIW4AAIBRiBsAAGAU4gYAABiFuAEAAEYhbgAAgFGIGwAAYBTiBgAAGIW4AQAARiFuAACAUYgbAABgFOIGAAAYhbgBAABGIW4AAIBRiBsAAGCUahE3S5YsUcuWLVW3bl316tVLO3fuvOb4tWvXqkOHDqpbt646d+6sTz75pIpmCgAAqjuXx01cXJwiIyM1Z84c7d69W126dFFYWJjOnj1b6vjt27drzJgxmjx5svbs2aNhw4Zp2LBh2rdvXxXPHAAAVEcuj5uFCxdq6tSpmjhxou666y4tXbpU9erVU2xsbKnjX3vtNQ0ZMkQzZ85Ux44d9eKLL6pbt27661//WsUzBwAA1ZFL46agoEBff/21QkNDHetq1aql0NBQJScnl7pPcnKy03hJCgsLK3M8AAC4vbi78snPnz+vwsJC+fn5Oa338/PTwYMHS90nPT291PHp6emljs/Pz1d+fr7jcVZWliQpOzv7ZqZ+XYX5l27p8YGa6Fa/7qpKzuVCV08BqHZu9eu7+PiWZV13rEvjpipER0dr7ty5JdYHBga6YDbA7c3n9T+4egoAbpVonyp5mpycHPn4XPu5XBo3TZo0kZubmzIyMpzWZ2RkyN/fv9R9/P39yzU+KipKkZGRjsdFRUX66aef1LhxY9lstps8A1R32dnZCgwM1KlTp+Tt7e3q6QCoRLy+by+WZSknJ0fNmjW77liXxk2dOnXUvXt3JSQkaNiwYZJ+jo+EhAQ9/vjjpe4TEhKihIQEzZgxw7EuPj5eISEhpY632+2y2+1O6xo0aFAZ00cN4u3tzS8/wFC8vm8f17tiU8zlb0tFRkYqIiJC9957r3r27KnFixcrLy9PEydOlCSNHz9ezZs3V3R0tCRp+vTp6tevnxYsWKDw8HCtWbNGu3bt0rJly1x5GgAAoJpwedyMHj1a586d0+zZs5Wenq7g4GBt3rzZcdNwamqqatX6vw919enTR++9957++Mc/6n/+53/Utm1bbdiwQXfffberTgEAAFQjNutGbjsGaqj8/HxFR0crKiqqxNuTAGo2Xt8oC3EDAACM4vJvKAYAAKhMxA0AADAKcQMAAIxC3AAAAKMQN3CpCRMmyGazOZbGjRtryJAh+uabbxxjfrn9l8uaNWskSYmJibLZbOrUqZMKC53/5k+DBg20cuVKx+OWLVtq8eLFTmP27Nmj0aNHKyAgQHa7XS1atND999+vjz76yPE3TE6cOCGbzSZfX1/l5OQ47R8cHKw//elPlfdDAWqg5ORkubm5KTw83Gl9eV87+/fv16hRo9S0aVPZ7Xa1a9dOs2fP1sWLF53GtWzZ0un3wC916tRJNpvN6bVfLDo6Wm5ubnr11VdLbFu5cmWJL3mdPHmyOnfurIKCAqf1n3zyierUqaPdu3eXOA5cj7iByw0ZMkRpaWlKS0tTQkKC3N3ddf/99zuNWbFihWNM8VL8rdbFjh07plWrVpXruTdu3KjevXsrNzdXb7/9tg4cOKDNmzfroYce0h//+EfHH1otlpOTo/nz51foPAGTxcTE6IknntDWrVt15syZEttv5LWzY8cO9erVSwUFBfr44491+PBhzZs3TytXrtSgQYNKBEZgYKBWrFhR4hjp6emqX79+qc8RGxurWbNmKTY29obOa9GiRcrJydGcOXMc6zIzMzV16lQ9//zz6tat2w0dB1WLuIHL2e12+fv7y9/fX8HBwXruued06tQpnTt3zjGmQYMGjjHFS926dZ2O88QTT2jOnDlOfwX+WvLy8jR58mSFh4fr448/1uDBg9W6dWt17NhRkydP1t69e0t81fcTTzyhhQsX6uzZszd/4oAhcnNzFRcXp0cffVTh4eGlXjG53mvHsixNnjxZHTt21D/+8Q/17NlTLVq00MiRI/XRRx8pOTlZixYtctpn7NixSkpK0qlTpxzrYmNjNXbsWLm7l/yO2qSkJF26dEkvvPCCsrOztX379uuem7e3t1asWKEFCxboyy+/lCTNmDFDzZs3V1RU1HX3h2sQN6hWcnNz9e677yooKEiNGzcu174zZszQ1atX9frrr9/Q+C1btujHH3/UrFmzyhzz6z+uOmbMGAUFBemFF14o19wAk73//vvq0KGD2rdvr3Hjxik2Nla//gq16712UlJS9N133ykyMtLpW+klqUuXLgoNDdXq1aud1vv5+SksLExvv/22JOnixYuKi4vTpEmTSn2OmJgYjRkzRrVr19aYMWMUExNzQ+c3YMAAPfbYY4qIiNDatWv1/vvva9WqVaUGFKoH4gYut2nTJnl6esrT01NeXl768MMPFRcX5/QLbsyYMY4xxUtqaqrTcerVq6c5c+YoOjq6xNtJpTl8+LAkqX379o51X331ldNzbNq0yWkfm82ml19+WcuWLdPRo0dv5rQBY8TExGjcuHGSfn6bOSsrS0lJSU5jrvfaKX49duzYsdTn6Nixo2PML02aNEkrV66UZVlat26d2rRpo+Dg4BLjsrOztW7dOsc8x40bp/fff1+5ubk3dI7Ff9/w4Ycf1ksvvaQOHTrc0H5wDeIGLjdgwAClpKQoJSVFO3fuVFhYmIYOHaqTJ086xixatMgxpngp7c/eT548WY0bN9Yrr7xSobncc889juPn5eXp6tWrJcaEhYXpP/7jP/T8889X6DkAkxw6dEg7d+7UmDFjJEnu7u4aPXp0qVdFbuS1U94vzQ8PD1dubq62bt2q2NjYMq/arF69Wm3atFGXLl0k/Xwzc4sWLRQXF3dDz+Ph4aFnnnlG9erV0/Tp08s1R1Q94gYuV79+fQUFBSkoKEg9evTQ8uXLlZeXp7/97W+OMf7+/o4xxUtpl4Td3d01b948vfbaa6Xe1PhLbdu2lfTzL+didrvdcfxrefnllxUXF6c9e/aU51QB48TExOjq1atq1qyZ3N3d5e7urjfffFMffPBBqVdQy3rttGvXTpJ04MCBUp/nwIEDjjG/5O7urv/6r//SnDlz9OWXX2rs2LFlznP//v2OObq7u+u777674RuLi5/Lzc2txNvVqH6IG1Q7NptNtWrV0qVLlyq0/8iRI9WpUyfNnTv3muMGDx6sRo0aVegqT8+ePTV8+HA999xzFZojYIKrV69q1apVWrBggdNV1b1796pZs2Yl7pGRyn7tBAcHq0OHDlq0aJGKioqctu3du1f/+te/HFeHfm3SpElKSkrSgw8+qIYNG5bY/u2332rXrl1KTEx0mmdiYqKSk5N18ODBm/gpoDribii4XH5+vtLT0yVJFy5c0F//+lfl5ubqgQcecIzJzMx0jCnm5eVV5sc9X375ZYWFhV3zeT09PbV8+XKNHj1a4eHhevLJJ9W2bVvl5uZq8+bNkiQ3N7cy9583b546derETYW4bW3atEkXLlzQ5MmTS3yycMSIEYqJidGQIUNK7Ffaa8dmsykmJkaDBg3SiBEjFBUVJX9/f3355Zd6+umnFRISohkzZpQ6j44dO+r8+fOqV69eqdtjYmLUs2dP9e3bt8S2Hj16KCYmxvG9N4WFhUpJSXEaY7fby7wXCNUTV27gcps3b1ZAQIACAgLUq1cvffXVV1q7dq369+/vGDNx4kTHmOLlWp+KGjhwoAYOHFjqPTO/9NBDD2n79u2qV6+exo8fr/bt22vgwIH67LPPtGbNmhLft/NL7dq106RJk3T58uVynzNggpiYGIWGhpYIG+nnuNm1a5eys7NLbCvrtdOnTx/t2LFDbm5uGjp0qIKCghQVFaWIiAjFx8fLbreXOZfGjRvLw8OjxPqCggK9++67GjFiRKn7jRgxQqtWrdKVK1ck/fyJza5duzotv/wfLdQMNqu8d28BAABUY1y5AQAARiFuAACAUYgbAABgFOIGAAAYhbgBAABGIW4AAIBRiBsAAGAU4gaAcWw2mzZs2HBTx5gwYYKGDRtWKfMBULWIGwDVSmJiomw2mzIzM109FQA1FHEDAACMQtwAqFG++uorDRo0SE2aNJGPj4/69eun3bt3lxiXlpamoUOHysPDQ61bt9a6deuctp86dUqjRo1SgwYN1KhRIz344IM6ceJEFZ0FgFuJuAFQo+Tk5CgiIkJffPGFduzYobZt2+q+++5TTk6O07jnn39eI0aM0N69ezV27Fg9/PDDOnDggCTpypUrCgsLk5eXlz7//HNt27ZNnp6eGjJkiAoKClxxWgAqkfv1hwBA9TFw4ECnx8uWLVODBg2UlJTk9FfcR44cqSlTpkiSXnzxRcXHx+v111/XG2+8obi4OBUVFWn58uWy2WySpBUrVqhBgwZKTEzU4MGDq+6EAFQ6rtwAqFEyMjI0depUtW3bVj4+PvL29lZubq5SU1OdxoWEhJR4XHzlZu/evTpy5Ii8vLzk6ekpT09PNWrUSJcvX9bRo0er7FwA3BpcuQFQo0REROjHH3/Ua6+9phYtWshutyskJKRcbyfl5uaqe/fu+vvf/15iW9OmTStzugBcgLgBUKNs27ZNb7zxhu677z5JP98YfP78+RLjduzYofHjxzs97tq1qySpW7duiouLk6+vr7y9vatm4gCqDG9LAaiWvv32W6WkpDiWvXv3SpLatm2rd955RwcOHNCXX36psWPHysPDo8T+a9euVWxsrA4fPqw5c+Zo586devzxxyVJY8eOVZMmTfTggw/q888/1/Hjx5WYmKgnn3xSP/zwQ5WeJ4DKx5UbANVS3759nR67ubnp6tWriomJ0X//93+rW7duCgwM1EsvvaRnnnmmxP5z587VmjVr9NhjjykgIECrV6/WXXfdJUmqV6+etm7dqmeffVbDhw9XTk6Omjdvrt/97ndcyQEMYLMsy3L1JAAAACoLb0sBAACjEDcAAMAoxA0AADAKcQMAAIxC3AAAAKMQNwAAwCjEDQAAMApxAwAAjELcAAAAoxA3AADAKMQNAAAwCnEDAACM8v8Ap0Rg9kpidMMAAAAASUVORK5CYII=\n" }, "metadata": {} } ] }, { "cell_type": "code", "source": [ "CSV_HEADER = column_headers" ], "metadata": { "id": "4czqBnTL5FSf" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "df = frame" ], "metadata": { "id": "qM0j2lyw582V" }, "execution_count": null, "outputs": [] }, { "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(df)\n", "new_data = df[nonOutlierList]\n", "\n" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "b30d4e39-807c-4290-f5db-67c7fd37a58b", "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", "df" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 496 }, "outputId": "34a87d68-8be6-4deb-9143-5f8d21a93428", "id": "FSPL95xg5-fT" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ " Destination Port Flow Duration Total Fwd Packets \\\n", "0 54865 3 2 \n", "1 55054 109 1 \n", "2 55055 52 1 \n", "3 46236 34 1 \n", "4 54863 3 2 \n", "... ... ... ... \n", "2539413 53 32215 4 \n", "2539414 53 324 2 \n", "2539415 58030 82 2 \n", "2539416 53 1048635 6 \n", "2539417 53 94939 4 \n", "\n", " Total Backward Packets Total Length of Fwd Packets \\\n", "0 0 12 \n", "1 1 6 \n", "2 1 6 \n", "3 1 6 \n", "4 0 12 \n", "... ... ... \n", "2539413 2 112 \n", "2539414 2 84 \n", "2539415 1 31 \n", "2539416 2 192 \n", "2539417 2 188 \n", "\n", " Total Length of Bwd Packets Fwd Packet Length Max \\\n", "0 0 6 \n", "1 6 6 \n", "2 6 6 \n", "3 6 6 \n", "4 0 6 \n", "... ... ... \n", "2539413 152 28 \n", "2539414 362 42 \n", "2539415 6 31 \n", "2539416 256 32 \n", "2539417 226 47 \n", "\n", " Fwd Packet Length Min Fwd Packet Length Mean \\\n", "0 6 6.0 \n", "1 6 6.0 \n", "2 6 6.0 \n", "3 6 6.0 \n", "4 6 6.0 \n", "... ... ... \n", "2539413 28 28.0 \n", "2539414 42 42.0 \n", "2539415 0 15.5 \n", "2539416 32 32.0 \n", "2539417 47 47.0 \n", "\n", " Fwd Packet Length Std ... min_seg_size_forward Active Mean \\\n", "0 0.00000 ... 20 0.0 \n", "1 0.00000 ... 20 0.0 \n", "2 0.00000 ... 20 0.0 \n", "3 0.00000 ... 20 0.0 \n", "4 0.00000 ... 20 0.0 \n", "... ... ... ... ... \n", "2539413 0.00000 ... 20 0.0 \n", "2539414 0.00000 ... 20 0.0 \n", "2539415 21.92031 ... 32 0.0 \n", "2539416 0.00000 ... 20 0.0 \n", "2539417 0.00000 ... 20 0.0 \n", "\n", " Active Std Active Max Active Min Idle Mean Idle Std \\\n", "0 0.0 0 0 0.0 0.0 \n", "1 0.0 0 0 0.0 0.0 \n", "2 0.0 0 0 0.0 0.0 \n", "3 0.0 0 0 0.0 0.0 \n", "4 0.0 0 0 0.0 0.0 \n", "... ... ... ... ... ... \n", "2539413 0.0 0 0 0.0 0.0 \n", "2539414 0.0 0 0 0.0 0.0 \n", "2539415 0.0 0 0 0.0 0.0 \n", "2539416 0.0 0 0 0.0 0.0 \n", "2539417 0.0 0 0 0.0 0.0 \n", "\n", " Idle Max Idle Min Label \n", "0 0 0 BENIGN \n", "1 0 0 BENIGN \n", "2 0 0 BENIGN \n", "3 0 0 BENIGN \n", "4 0 0 BENIGN \n", "... ... ... ... \n", "2539413 0 0 BENIGN \n", "2539414 0 0 BENIGN \n", "2539415 0 0 BENIGN \n", "2539416 0 0 BENIGN \n", "2539417 0 0 BENIGN \n", "\n", "[2539418 rows x 79 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", "
Destination PortFlow DurationTotal Fwd PacketsTotal Backward PacketsTotal Length of Fwd PacketsTotal Length of Bwd PacketsFwd Packet Length MaxFwd Packet Length MinFwd Packet Length MeanFwd Packet Length Std...min_seg_size_forwardActive MeanActive StdActive MaxActive MinIdle MeanIdle StdIdle MaxIdle MinLabel
054865320120666.00.00000...200.00.0000.00.000BENIGN
1550541091166666.00.00000...200.00.0000.00.000BENIGN
255055521166666.00.00000...200.00.0000.00.000BENIGN
346236341166666.00.00000...200.00.0000.00.000BENIGN
454863320120666.00.00000...200.00.0000.00.000BENIGN
..................................................................
2539413533221542112152282828.00.00000...200.00.0000.00.000BENIGN
2539414533242284362424242.00.00000...200.00.0000.00.000BENIGN
253941558030822131631015.521.92031...320.00.0000.00.000BENIGN
253941653104863562192256323232.00.00000...200.00.0000.00.000BENIGN
2539417539493942188226474747.00.00000...200.00.0000.00.000BENIGN
\n", "

2539418 rows × 79 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": "code", "source": [ "from sklearn.feature_selection import SelectKBest\n", "from sklearn.feature_selection import f_regression\n", "\n", "selector = SelectKBest(f_regression, k=10)\n", "X = df.drop([' Label'], axis=1)\n", "Y = df[\" Label\"].astype('category').cat.codes\n", "X_new = selector.fit(X, Y)\n", "X.columns.values[selector.get_support()]" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "2523add3-8fd4-4135-f882-ac825e80fad6", "id": "ck_o5gg35-fV" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "array(['Bwd Packet Length Max', ' Bwd Packet Length Mean',\n", " ' Bwd Packet Length Std', ' Max Packet Length',\n", " ' Packet Length Mean', ' Packet Length Std',\n", " ' Packet Length Variance', ' Average Packet Size',\n", " ' Avg Bwd Segment Size', ' Idle Min'], dtype=object)" ] }, "metadata": {}, "execution_count": 10 } ] }, { "cell_type": "code", "source": [ "columns = X.columns.values[selector.get_support()]" ], "metadata": { "id": "EeRnvhzX5-fV" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "columns = []\n", "for c in X.columns.values[selector.get_support()]:\n", " columns.append(str(c))" ], "metadata": { "id": "tLWiApuU5-fW" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "principalDf = pd.DataFrame(data = X\n", " , columns = X.columns.values[selector.get_support()])\n", "finalDf = pd.concat([principalDf, df[\" Label\"]], axis = 1)\n", "finalDf" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 441 }, "outputId": "5d9cbcdc-d1a2-4c38-da3c-8c6cda992c94", "id": "2_NJWPRf5-fW" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ " Bwd Packet Length Max Bwd Packet Length Mean \\\n", "0 0 0.0 \n", "1 6 6.0 \n", "2 6 6.0 \n", "3 6 6.0 \n", "4 0 0.0 \n", "... ... ... \n", "2539413 76 76.0 \n", "2539414 181 181.0 \n", "2539415 6 6.0 \n", "2539416 128 128.0 \n", "2539417 113 113.0 \n", "\n", " Bwd Packet Length Std Max Packet Length Packet Length Mean \\\n", "0 0.0 6 6.000000 \n", "1 0.0 6 6.000000 \n", "2 0.0 6 6.000000 \n", "3 0.0 6 6.000000 \n", "4 0.0 6 6.000000 \n", "... ... ... ... \n", "2539413 0.0 76 41.714286 \n", "2539414 0.0 181 97.600000 \n", "2539415 0.0 31 17.000000 \n", "2539416 0.0 128 53.333333 \n", "2539417 0.0 113 65.857143 \n", "\n", " Packet Length Std Packet Length Variance Average Packet Size \\\n", "0 0.000000 0.000000 9.000000 \n", "1 0.000000 0.000000 9.000000 \n", "2 0.000000 0.000000 9.000000 \n", "3 0.000000 0.000000 9.000000 \n", "4 0.000000 0.000000 9.000000 \n", "... ... ... ... \n", "2539413 23.421602 548.571429 48.666667 \n", "2539414 76.133435 5796.300000 122.000000 \n", "2539415 16.350331 267.333333 22.666667 \n", "2539416 42.332021 1792.000000 60.000000 \n", "2539417 32.204702 1037.142857 76.833333 \n", "\n", " Avg Bwd Segment Size Idle Min Label \n", "0 0.0 0 BENIGN \n", "1 6.0 0 BENIGN \n", "2 6.0 0 BENIGN \n", "3 6.0 0 BENIGN \n", "4 0.0 0 BENIGN \n", "... ... ... ... \n", "2539413 76.0 0 BENIGN \n", "2539414 181.0 0 BENIGN \n", "2539415 6.0 0 BENIGN \n", "2539416 128.0 0 BENIGN \n", "2539417 113.0 0 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", "
Bwd Packet Length MaxBwd Packet Length MeanBwd Packet Length StdMax Packet LengthPacket Length MeanPacket Length StdPacket Length VarianceAverage Packet SizeAvg Bwd Segment SizeIdle MinLabel
000.00.066.0000000.0000000.0000009.0000000.00BENIGN
166.00.066.0000000.0000000.0000009.0000006.00BENIGN
266.00.066.0000000.0000000.0000009.0000006.00BENIGN
366.00.066.0000000.0000000.0000009.0000006.00BENIGN
400.00.066.0000000.0000000.0000009.0000000.00BENIGN
....................................
25394137676.00.07641.71428623.421602548.57142948.66666776.00BENIGN
2539414181181.00.018197.60000076.1334355796.300000122.000000181.00BENIGN
253941566.00.03117.00000016.350331267.33333322.6666676.00BENIGN
2539416128128.00.012853.33333342.3320211792.00000060.000000128.00BENIGN
2539417113113.00.011365.85714332.2047021037.14285776.833333113.00BENIGN
\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": 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": "c85wOCBv5-fX" }, "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 = [\"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": "89b0fe40-f00c-448b-c361-662d4091ef07" }, "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": "06b9c0c7-51bf-4366-caa9-b7223b279103", "id": "cEIPaJMi5-fa" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Start training the model...\n", "Epoch 1/10\n", "14880/14880 [==============================] - 408s 26ms/step - loss: 0.2511 - sparse_categorical_accuracy: 0.8851\n", "Epoch 2/10\n", "14880/14880 [==============================] - 318s 21ms/step - loss: 0.2333 - sparse_categorical_accuracy: 0.8858\n", "Epoch 3/10\n", "14880/14880 [==============================] - 319s 21ms/step - loss: 0.2295 - sparse_categorical_accuracy: 0.8862\n", "Epoch 4/10\n", "14880/14880 [==============================] - 321s 22ms/step - loss: 0.2271 - sparse_categorical_accuracy: 0.8895\n", "Epoch 5/10\n", "14880/14880 [==============================] - 320s 21ms/step - loss: 0.2263 - sparse_categorical_accuracy: 0.8895\n", "Epoch 6/10\n", "14880/14880 [==============================] - 315s 21ms/step - loss: 0.2255 - sparse_categorical_accuracy: 0.8892\n", "Epoch 7/10\n", "14880/14880 [==============================] - 325s 22ms/step - loss: 0.2238 - sparse_categorical_accuracy: 0.8902\n", "Epoch 8/10\n", "14880/14880 [==============================] - 321s 22ms/step - loss: 0.2226 - sparse_categorical_accuracy: 0.8912\n", "Epoch 9/10\n", "14880/14880 [==============================] - 318s 21ms/step - loss: 0.2223 - sparse_categorical_accuracy: 0.8906\n", "Epoch 10/10\n", "14880/14880 [==============================] - 318s 21ms/step - loss: 0.2210 - sparse_categorical_accuracy: 0.8925\n", "Model training finished\n", "Evaluating the model on the test data...\n", "4960/4960 [==============================] - 50s 10ms/step - loss: 0.2180 - sparse_categorical_accuracy: 0.8961\n", "Test accuracy: 89.61%\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/", "height": 588 }, "id": "-wqDH6JHYGIy", "outputId": "0b8b51e3-7f3c-4f21-f3f9-175c5882ff32" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "4960/4960 [==============================] - 49s 9ms/step\n", "[[0.88787502 0.02786123]\n", " [0.11212498 0.97213877]]\n", "ACC: 0.9300068949681092\n", "PR: 0.969575052799438\n", "TPR: 0.8878750167992865\n", "FPR: 0.027861226863068127\n", "F1Score: 0.9269282446751819\n" ] }, { "output_type": "display_data", "data": { "text/plain": [ "
" ], "image/png": "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Vq9erT59+mjChAnq2bOnYmNjNX/+fD377LNexzpz5sxzEpqgoCDLYgVQeSQQgI8iIyOVmppa6fGXXHKJFixYoIYNG57zV/hZjRo10n//+19deeWVksr/0s7Ly9Mll1zicXy7du1kGIZWrFihrKysc74/WwFxOp2ufenp6XI4HNqzZ0+FlYs2bdq4FoSe9Z///OeXL/InVq1apaZNm+qxxx5z7fvuu+/OGbdnzx4dOHBAjRs3dp3HbrerVatWSkhIUOPGjbVz50716dPHq/MDqB4sogRqWJ8+fdSgQQPddNNNWrlypXbt2qXly5dryJAh2rdvnyTp4Ycf1pNPPqn33ntPmzdv1uDBg8/7DIeUlBT169dP9913n9577z3XMd966y1JUtOmTWWz2bRo0SIdOXJEhYWFio6O1vDhwzV06FC9/vrr2rFjh9auXasXXnjBtTDxgQce0LZt2zRixAht2bJFb775pubMmePV9bZs2VJ79uzR/PnztWPHDk2bNs3jgtCwsDD169dP69ev18qVKzVkyBDdcccdSkxMlCRNmDBBOTk5mjZtmrZu3aqvv/5as2fP1pQpU7yKB4A1SCCAGhYREaHPPvtMF198sW699Va1adNG999/v4qKilwViT/+8Y+699571a9fP2VmZio6Olq33HLLeY87ffp03X777Ro8eLBat26tQYMG6dSpU5KkpKQkTZgwQaNHj1ZCQoKys7MlSZMmTdKYMWOUk5OjNm3aqFevXlq8eLGaNWsmqXxdwttvv6333ntPHTp00IwZMzR58mSvrvfGG2/U0KFDlZ2drY4dO2rVqlUaM2bMOeNSU1N166236rrrrlOPHj3Uvn17t9s0Bw4cqFdffVWzZ89Wu3bt1K1bN82ZM8cVK4CaZTMrWpUFAABQASoQAADAayQQAADAayQQAADAayQQAADAayQQAADAayQQAADAayQQAADAayQQAADAayQQAADAayQQAADAayQQAADAa/8fNBsW72Yv0mcAAAAASUVORK5CYII=\n" }, "metadata": {} } ] }, { "cell_type": "markdown", "source": [ "# UNSWB-NB15" ], "metadata": { "id": "WoKuwfkLuv1A" } }, { "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": "Fdfw744ivFEM" }, "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": "a49e269c-cb9d-4f4a-9bc8-66dc6c943e22", "id": "QkqJfn8Uuv1B" }, "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": "bRkq-TJZvIyE" }, "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 }, "outputId": "8f6511b3-5a3b-4624-8eec-f0a21c8aee9e", "id": "-CqwxbhMuv1D" }, "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": "vw4HjZE0vT5x", "outputId": "4690412b-8c79-4afa-eb79-2ff1404cfda6" }, "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": "16e21026-d139-4863-9359-05b13cfd0a89", "id": "Evwnc93Kuv1G" }, "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": "433bb956-2df9-4a09-b5e8-252d5f47ab73", "id": "QVxZ09Cwuv1H" }, "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.feature_selection import SelectKBest\n", "from sklearn.feature_selection import f_regression\n", "\n", "selector = SelectKBest(f_regression, k=10)\n", "X = df.drop(['Label'], axis=1)\n", "Y = df[\"Label\"].astype('category').cat.codes\n", "X_new = selector.fit(X, Y)\n", "X.columns.values[selector.get_support()]" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "outputId": "d0993c78-0506-4f62-ca8e-dba2b2d11c16", "id": "oAqsvLnbuv1M" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "array(['state', 'sttl', 'ct_state_ttl', 'ct_srv_src', 'ct_srv_dst',\n", " 'ct_dst_ltm', 'ct_src_ ltm', 'ct_src_dport_ltm',\n", " 'ct_dst_sport_ltm', 'ct_dst_src_ltm'], dtype=object)" ] }, "metadata": {}, "execution_count": 8 } ] }, { "cell_type": "code", "source": [ "columns = X.columns.values[selector.get_support()]" ], "metadata": { "id": "pDyqDClMuv1N" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "columns = []\n", "for c in X.columns.values[selector.get_support()]:\n", " columns.append(str(c))" ], "metadata": { "id": "uCoBUv3Iuv1O" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "principalDf = pd.DataFrame(data = X\n", " , columns = X.columns.values[selector.get_support()])\n", "finalDf = pd.concat([principalDf, df[\"Label\"]], axis = 1)\n", "finalDf" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 424 }, "outputId": "d55db406-4fc0-4216-f20d-5f800751df3d", "id": "KcYVo5R7uv1O" }, "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ " state sttl ct_state_ttl ct_srv_src ct_srv_dst ct_dst_ltm \\\n", "0 5 31 0 2 2 7 \n", "1 5 31 0 3 1 4 \n", "2 2 31 0 3 5 2 \n", "3 5 31 0 1 1 4 \n", "4 2 31 0 2 5 2 \n", "... ... ... ... ... ... ... \n", "2438669 5 31 0 8 20 7 \n", "2438670 5 31 0 1 1 2 \n", "2438671 5 31 0 13 13 6 \n", "2438672 2 31 0 10 13 6 \n", "2438673 5 31 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", "2438669 5 1 1 4 \n", "2438670 7 2 2 2 \n", "2438671 7 2 1 2 \n", "2438672 5 1 1 3 \n", "2438673 7 1 1 2 \n", "\n", " Label \n", "0 normal \n", "1 normal \n", "2 normal \n", "3 normal \n", "4 normal \n", "... ... \n", "2438669 normal \n", "2438670 normal \n", "2438671 normal \n", "2438672 normal \n", "2438673 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", "
statesttlct_state_ttlct_srv_srcct_srv_dstct_dst_ltmct_src_ ltmct_src_dport_ltmct_dst_sport_ltmct_dst_src_ltmLabel
053102274113normal
153103144111normal
223103527114normal
353101147113normal
423102521112normal
....................................
2438669531082075114normal
243867053101127222normal
24386715310131367212normal
24386722310101365113normal
24386735310131367112normal
\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": 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": "AVvnjMP0uv1P" }, "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/" }, "outputId": "d0ee3730-614b-47ea-e65f-85ec9485d949", "id": "rwCv3SBwuv1P" }, "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": "624abaad-2a9d-4f6b-e3f0-d846d966418b", "id": "57cf8V6duv1R" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Start training the model...\n", "Epoch 1/10\n", "14290/14290 [==============================] - 390s 25ms/step - loss: 0.0363 - sparse_categorical_accuracy: 0.9887\n", "Epoch 2/10\n", "14290/14290 [==============================] - 307s 21ms/step - loss: 0.0309 - sparse_categorical_accuracy: 0.9902\n", "Epoch 3/10\n", "14290/14290 [==============================] - 301s 21ms/step - loss: 0.0306 - sparse_categorical_accuracy: 0.9903\n", "Epoch 4/10\n", "14290/14290 [==============================] - 277s 19ms/step - loss: 0.0303 - sparse_categorical_accuracy: 0.9903\n", "Epoch 5/10\n", "14290/14290 [==============================] - 307s 22ms/step - loss: 0.0301 - sparse_categorical_accuracy: 0.9904\n", "Epoch 6/10\n", "14290/14290 [==============================] - 297s 21ms/step - loss: 0.0298 - sparse_categorical_accuracy: 0.9904\n", "Epoch 7/10\n", "14290/14290 [==============================] - 303s 21ms/step - loss: 0.0296 - sparse_categorical_accuracy: 0.9904\n", "Epoch 8/10\n", "14290/14290 [==============================] - 303s 21ms/step - loss: 0.0294 - sparse_categorical_accuracy: 0.9904\n", "Epoch 9/10\n", "14290/14290 [==============================] - 307s 22ms/step - loss: 0.0293 - sparse_categorical_accuracy: 0.9905\n", "Epoch 10/10\n", "14290/14290 [==============================] - 317s 22ms/step - loss: 0.0292 - sparse_categorical_accuracy: 0.9904\n", "Model training finished\n", "Evaluating the model on the test data...\n", "4764/4764 [==============================] - 50s 10ms/step - loss: 0.0283 - sparse_categorical_accuracy: 0.9904\n", "Test accuracy: 99.04%\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": "5d4d957a-9bac-4808-ea5c-78a5a7cecff4", "id": "ZWRkWlYzuv1R" }, "execution_count": null, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "4764/4764 [==============================] - 47s 9ms/step\n", "[[0.99675629 0.05519602]\n", " [0.00324371 0.94480398]]\n", "ACC: 0.9707801335476056\n", "PR: 0.9475299209852083\n", "TPR: 0.9967562878472034\n", "FPR: 0.0551960207519923\n", "F1Score: 0.9715199360823916\n" ] }, { "output_type": "display_data", "data": { "text/plain": [ "
" ], "image/png": "iVBORw0KGgoAAAANSUhEUgAAAhAAAAGwCAYAAAD49Fz6AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA6F0lEQVR4nO3deXwU9f3H8fcmJJs7BAkJgcghcpXT0FJURPxFQCuKFxZRDoFWlIJQzlpuIZ4IWAWLchYLikoVlFZQkACtEgQPznAFuTFACJBrZ35/IItrEsywk2PZ1/PxmIfs7Pc781kfgf3k8/nOjMM0TVMAAAAWBJR3AAAAwPeQQAAAAMtIIAAAgGUkEAAAwDISCAAAYBkJBAAAsIwEAgAAWFapvAPwBYZh6NChQ4qMjJTD4SjvcAAAFpmmqTNnzighIUEBAaX3u3NOTo7y8vK8Pk5wcLBCQkJsiKj0kECUwKFDh5SYmFjeYQAAvHTgwAHVrFmzVI6dk5OjOrUidOSYy+tjxcfHa+/evRU6iSCBKIHIyEhJ0v5NtRUVQdcHV6cHfntLeYcAlJoCM09rTr7l/ve8NOTl5enIMZf2p9VWVOSVf1dknTFUK2mf8vLySCB83cW2RVREgFc/FEBFVikguLxDAEqPceE/ZdGGjoh0KCLyys9jyDda5SQQAADYyGUacnnxlCmXadgXTCkigQAAwEaGTBm68gzCm7lliXo8AACwjAoEAAA2MmTImyaEd7PLDgkEAAA2cpmmXOaVtyG8mVuWaGEAAADLqEAAAGAjf1lESQIBAICNDJly+UECQQsDAABYRgUCAAAb0cIAAACWcRUGAABAMahAAABgI0Py8kZSvoEEAgAAG7m8vArDm7lliQQCAAAbuUx5+TRO+2IpTayBAAAAllGBAADARqyBAAAAlhlyyCWHV/N9AS0MAABgGRUIAABsZJgXNm/m+wISCAAAbOTysoXhzdyyRAsDAABYRgUCAAAb+UsFggQCAAAbGaZDhunFVRhezC1LtDAAAIBlVCAAALARLQwAAGCZSwFyeVHgd9kYS2kigQAAwEaml2sgTNZAAACAqxUVCAAAbMQaCAAAYJnLDJDL9GINhI/cypoWBgAAsIwKBAAANjLkkOHF7+eGfKMEQQIBAICN/GUNBC0MAABgGRUIAABs5P0iSloYAAD4nQtrILx4mBYtDAAAcLWiAgEAgI0ML5+FwVUYAAD4IdZAAAAAywwF+MV9IFgDAQAALKMCAQCAjVymQy4vHsntzdyyRAIBAICNXF4uonTRwgAAAFcrKhAAANjIMANkeHEVhsFVGAAA+B9aGAAAAMWgAgEAgI0MeXclhWFfKKWKBAIAABt5fyMp32gO+EaUAACgQqECAQCAjbx/FoZv/G5PAgEAgI0MOWTImzUQ3IkSAAC/4y8VCN+IEgAAVChUIAAAsJH3N5Lyjd/tSSAAALCRYTpkeHMfCB95GqdvpDkAAKBCoQIBAICNDC9bGL5yIykSCAAAbOT90zh9I4HwjSgBAECFQgUCAAAbueSQy4ubQXkztyyRQAAAYCNaGAAAAMWgAgEAgI1c8q4N4bIvlFJFAgEAgI38pYVBAgEAgI14mBYAAPAZr776qmrXrq2QkBC1bt1aX3zxxWXHT506VQ0aNFBoaKgSExM1ePBg5eTklPh8JBAAANjIlEOGF5t5BesnFi9erCFDhmjs2LHatGmTmjdvro4dO+rYsWNFjn/rrbc0cuRIjR07Vtu2bdObb76pxYsX6y9/+UuJz0kCAQCAjS62MLzZJCkrK8tjy83NLfacU6ZMUb9+/dS7d281btxYM2fOVFhYmGbPnl3k+PXr1+umm27Sww8/rNq1a6tDhw7q1q3bL1YtfooEAgCACigxMVHR0dHuLSUlpchxeXl5SktLU3JysntfQECAkpOTtWHDhiLn3HjjjUpLS3MnDHv27NFHH32kO++8s8TxsYgSAAAb2fU47wMHDigqKsq93+l0Fjn+xIkTcrlciouL89gfFxen7du3Fznn4Ycf1okTJ3TzzTfLNE0VFBTo8ccfp4UBAEB5cf34NE5vNkmKiory2IpLIK7E6tWrNXnyZL322mvatGmT3nvvPS1fvlwTJ04s8TGoQAAA4MOqVq2qwMBAHT161GP/0aNHFR8fX+Sc0aNH69FHH1Xfvn0lSU2bNtXZs2f1hz/8QU8//bQCAn65vkAFAgAAG11sYXizWREcHKykpCStWrXqUgyGoVWrVqlNmzZFzjl37lyhJCEwMFCSZJpmic5LBQIAABsZCpDhxe/nVzJ3yJAh6tmzp1q1aqXf/OY3mjp1qs6ePavevXtLknr06KEaNWq4F2J27txZU6ZMUcuWLdW6dWulp6dr9OjR6ty5szuR+CUkEAAA+LiHHnpIx48f15gxY3TkyBG1aNFCK1ascC+szMjI8Kg4/PWvf5XD4dBf//pXHTx4ULGxsercubMmTZpU4nM6zJLWKvxYVlaWoqOjdXJnXUVF0vXB1enOpreVdwhAqSkw8rQqc65Onz7tcWWDnS5+V/Rfe5+cEUFXfJzc7HzNaPteqcZqByoQAADYyK7LOCs6EggAAGxkevk0TpOHaQEAgKsVFQgAAGzkkkOuK3gg1k/n+wISCAAAbGSY3q1jMHzk0gZaGAAAwDIqEKhQvvlvuN55rZp2fROmzKNBGvvmXt14x+nyDgso5K7ff6/7ex1QTNU87d0Rrhkp9bXz2+Ivubu5wzE9OmCv4hJydCgjVLNfvk4b117jfn/wM9t0+z1HPOZsTK2iMf2bu1/PWbFBcTVyPMbMmVpX77xZy6ZPBTsYXi6i9GZuWSKBQIWScy5AdX91Xh27ZWpCnzrlHQ5QpFs6HlW/Yen628QG2v51lLo8ekATX9+iP3RurdOZwYXGN2p+WiOe26q50+rqizXX6NbfHdXoad9oYNdW2p8e4R63MbWKXv5rQ/fr/PzCXyQL/lZHK5ZUd78+d45/xisaQw4ZXqxj8GZuWapQaY7D4bjsNm7cuPIOEaXs17edUa8RR3QTVQdUYPf2OKAV7ybok6XVdWBPuP42oYFyzweow72Hixx/zyPfK21dFb0791od2BuuBX+rq91bI9W520GPcfl5ATr5g9O9ZWcVvhnRubOBHmNyz5fstsOA3SpU6nr48KW/fIsXL9aYMWO0Y8cO976IiEuZummacrlcqlSpQn0EAFe5SpUM1Wucrbd/0jYwTYc2/7eKGjbPKnJOw+an9f78RI99aeurqM1txz32NW11Sm+tTlV2ViVt+SJG81+pqzOnPZOIB/tkqNsf9+n44RCt/ihO7y+oKcNVoX4X9Hsu0yGXF4sovZlblirUT118fLx7i46OlsPhcL/evn27IiMj9fHHHyspKUlOp1Opqanq1auXunTp4nGcp556Srfeeqv7tWEYSklJUZ06dRQaGqrmzZtryZIlZfvhAFwVomLyFVjJ1MkfPFsVp34IUpVrcoucE1M1T6cKjQ9WTNU89+u01Cp66elG+ku/Fpoz9To1bXVKE2ZsUUDApSX5H7xVQ88Na6yRfVrq43cS1LXffvUZstvGTwc7XFwD4c3mC3zu1/eRI0fqxRdfVN26dRUTE1OiOSkpKfrHP/6hmTNn6vrrr9fnn3+uRx55RLGxsWrXrl2h8bm5ucrNvfQPQVZW0b9VAIBdPl8R5/7zvl0R2rszQrM//q+a/vqktvyviiTp/fnXXhqzM0L5+QH605gdmjP1OhUUsV4CKE0+l0BMmDBBt99+e4nH5+bmavLkyVq5cqX7ueh169ZVamqqXn/99SITiJSUFI0fP962mAFcPbJOBslV4FDMNXke+ytfk6/MH5xFzjl5IliVC43P08kThRdcXnTk+1CdzgxSwrXnteV/RY/Z8U2UKgWZiquRo4P7wqx9EJQaQ14+C4NFlKWjVatWlsanp6fr3Llzuv322xUREeHe5s+fr927iy79jRo1SqdPn3ZvBw4csCN0AFeBgoIApW+NUPPWJ937HA5TLX57Utu3FH0Z5/Yt0Wrxk/GS1LJNprZviS72PNfE5Siycr4yjxedlEhS3YZn5HJJpzOv/MmPsJ/541UYV7qZPpJA+FwFIjw83ON1QECAfv5E8vz8fPefs7OzJUnLly9XjRo1PMY5nUX/xXQ6ncW+h9J1/myADu299P/+yIFg7f42VJGVC1StZv5lZgJl5/35iRoyabt2fRepnd9E6Z5Hv5cz1KVPll64vPLPk7bqh2NOzZ12nSTpX/+oqefmfKV7e2Toy7XXqF2nY7r+V2f0yvgGkqSQ0AI93H+f1q2M1ckTwaqeeF6PDdmtwxmhSlt3oX3RsPlpNWiapa+/iNH5c4Fq2Py0/jAsXZ8tiy/yag2UH57G6SNiY2P17bffeuzbvHmzgoIu/IVq3LixnE6nMjIyimxXoGLZuSVMwx+o5379+rgLSd/tXTM1dGpGeYUFePj833GKqpKvR5/cq5iqedqzPUJjHm/mXigZWz3X40tg25ZoPT+ysXoM2KNeg/bo4P4wTRzU1H0PCMNwqE79bCXffUThUQXKPObUpg0xWvC3uu61Dfl5AWrX6Zi699+noGBDRw+GaOmCRL33s6s7gLLi8wnEbbfdphdeeEHz589XmzZt9I9//EPffvutWrZsKUmKjIzU0KFDNXjwYBmGoZtvvlmnT5/WunXrFBUVpZ49e5bzJ8BPNb8xW/8+tLm8wwB+0bJ/1tSyf9Ys8r2Rj7UstC/1P9WU+p9qRY7Pyw3U6MdbXPZ8u7dFasgjSZbjRNnjTpQ+omPHjho9erSGDx+unJwcPfbYY+rRo4e++eYb95iJEycqNjZWKSkp2rNnjypXrqwbbrhBf/nLX8oxcgDA1chfWhgO8+cLCFBIVlaWoqOjdXJnXUVF+kZmCFh1Z9PbyjsEoNQUGHlalTlXp0+fVlRU8c8s8cbF74p7/vOYgsKLv8Lml+SfzdO/Oswu1Vjt4PMVCAAAKhJ/eRYGCQQAADbylxYG9XgAAGAZFQgAAGzkLxUIEggAAGzkLwkELQwAAGAZFQgAAGzkLxUIEggAAGxkyrtLMX3l5kwkEAAA2MhfKhCsgQAAAJZRgQAAwEb+UoEggQAAwEb+kkDQwgAAAJZRgQAAwEb+UoEggQAAwEam6ZDpRRLgzdyyRAsDAABYRgUCAAAbGXJ4dSMpb+aWJRIIAABs5C9rIGhhAAAAy6hAAABgI39ZREkCAQCAjfylhUECAQCAjfylAsEaCAAAYBkVCAAAbGR62cLwlQoECQQAADYyJZmmd/N9AS0MAABgGRUIAABsZMghB3eiBAAAVnAVBgAAQDGoQAAAYCPDdMjBjaQAAIAVpunlVRg+chkGLQwAAGAZFQgAAGzkL4soSSAAALARCQQAALDMXxZRsgYCAABYRgUCAAAb+ctVGCQQAADY6EIC4c0aCBuDKUW0MAAAgGVUIAAAsBFXYQAAAMvMHzdv5vsCWhgAAMAyKhAAANiIFgYAALDOT3oYtDAAALDTjxWIK910hRWIV199VbVr11ZISIhat26tL7744rLjT506pSeffFLVq1eX0+lU/fr19dFHH5X4fFQgAADwcYsXL9aQIUM0c+ZMtW7dWlOnTlXHjh21Y8cOVatWrdD4vLw83X777apWrZqWLFmiGjVqaP/+/apcuXKJz0kCAQCAjcrjTpRTpkxRv3791Lt3b0nSzJkztXz5cs2ePVsjR44sNH727NnKzMzU+vXrFRQUJEmqXbu2pXPSwgAAwEbetC9+ugAzKyvLY8vNzS3yfHl5eUpLS1NycrJ7X0BAgJKTk7Vhw4Yi53zwwQdq06aNnnzyScXFxalJkyaaPHmyXC5XiT8nCQQAABVQYmKioqOj3VtKSkqR406cOCGXy6W4uDiP/XFxcTpy5EiRc/bs2aMlS5bI5XLpo48+0ujRo/XSSy/pmWeeKXF8tDAAALCTFwsh3fMlHThwQFFRUe7dTqfT28jcDMNQtWrV9Pe//12BgYFKSkrSwYMH9cILL2js2LElOgYJBAAANrJrDURUVJRHAlGcqlWrKjAwUEePHvXYf/ToUcXHxxc5p3r16goKClJgYKB7X6NGjXTkyBHl5eUpODj4F89LCwMAAB8WHByspKQkrVq1yr3PMAytWrVKbdq0KXLOTTfdpPT0dBmG4d63c+dOVa9evUTJg0QCAQCAvUwbNouGDBmiWbNmad68edq2bZv69++vs2fPuq/K6NGjh0aNGuUe379/f2VmZmrQoEHauXOnli9frsmTJ+vJJ58s8TlpYQAAYKPyuJX1Qw89pOPHj2vMmDE6cuSIWrRooRUrVrgXVmZkZCgg4FLNIDExUf/+9781ePBgNWvWTDVq1NCgQYM0YsSIEp+zRAnEBx98UOID3n333SUeCwAA7DFgwAANGDCgyPdWr15daF+bNm303//+94rPV6IEokuXLiU6mMPhsHQNKQAAVyUfeZ6FN0qUQPx0kQUAACievzyN06tFlDk5OXbFAQDA1aEcFlGWB8sJhMvl0sSJE1WjRg1FRERoz549kqTRo0frzTfftD1AAABQ8VhOICZNmqS5c+fq+eef97hWtEmTJnrjjTdsDQ4AAN/jsGGr+CwnEPPnz9ff//53de/e3eMOVs2bN9f27dttDQ4AAJ9DC6NoBw8eVL169QrtNwxD+fn5tgQFAAAqNssJROPGjbV27dpC+5csWaKWLVvaEhQAAD7LTyoQlu9EOWbMGPXs2VMHDx6UYRh67733tGPHDs2fP1/Lli0rjRgBAPAdNj2Ns6KzXIG455579OGHH2rlypUKDw/XmDFjtG3bNn344Ye6/fbbSyNGAABQwVzRszDatm2rTz75xO5YAADweXY9zruiu+KHaW3cuFHbtm2TdGFdRFJSkm1BAQDgs7xdx3C1JhDff/+9unXrpnXr1qly5cqSpFOnTunGG2/UokWLVLNmTbtjBAAAFYzlNRB9+/ZVfn6+tm3bpszMTGVmZmrbtm0yDEN9+/YtjRgBAPAdFxdRerP5AMsViDVr1mj9+vVq0KCBe1+DBg30yiuvqG3btrYGBwCAr3GYFzZv5vsCywlEYmJikTeMcrlcSkhIsCUoAAB8lp+sgbDcwnjhhRf0pz/9SRs3bnTv27hxowYNGqQXX3zR1uAAAEDFVKIKRExMjByOSz2Zs2fPqnXr1qpU6cL0goICVapUSY899pi6dOlSKoECAOAT/ORGUiVKIKZOnVrKYQAAcJXwkxZGiRKInj17lnYcAADAh1zxjaQkKScnR3l5eR77oqKivAoIAACf5icVCMuLKM+ePasBAwaoWrVqCg8PV0xMjMcGAIBf85OncVpOIIYPH65PP/1UM2bMkNPp1BtvvKHx48crISFB8+fPL40YAQBABWO5hfHhhx9q/vz5uvXWW9W7d2+1bdtW9erVU61atbRw4UJ17969NOIEAMA3+MlVGJYrEJmZmapbt66kC+sdMjMzJUk333yzPv/8c3ujAwDAx1y8E6U3my+wnEDUrVtXe/fulSQ1bNhQb7/9tqQLlYmLD9cCAABXN8sJRO/evbVlyxZJ0siRI/Xqq68qJCREgwcP1rBhw2wPEAAAn+Iniygtr4EYPHiw+8/Jycnavn270tLSVK9ePTVr1szW4AAAQMXk1X0gJKlWrVqqVauWHbEAAODzHPLyaZy2RVK6SpRATJ8+vcQHHDhw4BUHAwAAfEOJEoiXX365RAdzOBxXdQJxb/2mquQIKu8wgFIxc/8H5R0CUGrOnDHU8ldldDI/uYyzRAnExasuAADAL+BW1gAAAEXzehElAAD4CT+pQJBAAABgI2/vJnnV3okSAACACgQAAHbykxbGFVUg1q5dq0ceeURt2rTRwYMHJUkLFixQamqqrcEBAOBz/ORW1pYTiHfffVcdO3ZUaGiovvrqK+Xm5kqSTp8+rcmTJ9seIAAAqHgsJxDPPPOMZs6cqVmzZiko6NJNlW666SZt2rTJ1uAAAPA1/vI4b8trIHbs2KFbbrml0P7o6GidOnXKjpgAAPBdfnInSssViPj4eKWnpxfan5qaqrp169oSFAAAPos1EEXr16+fBg0apP/9739yOBw6dOiQFi5cqKFDh6p///6lESMAAKhgLLcwRo4cKcMw9H//9386d+6cbrnlFjmdTg0dOlR/+tOfSiNGAAB8hr/cSMpyAuFwOPT0009r2LBhSk9PV3Z2tho3bqyIiIjSiA8AAN/iJ/eBuOIbSQUHB6tx48Z2xgIAAHyE5QSiffv2cjiKXyH66aefehUQAAA+zdtLMa/WCkSLFi08Xufn52vz5s369ttv1bNnT7viAgDAN9HCKNrLL79c5P5x48YpOzvb64AAAEDFZ9vTOB955BHNnj3brsMBAOCb/OQ+ELY9jXPDhg0KCQmx63AAAPgkLuMsxn333efx2jRNHT58WBs3btTo0aNtCwwAAFRclhOI6Ohoj9cBAQFq0KCBJkyYoA4dOtgWGAAAqLgsJRAul0u9e/dW06ZNFRMTU1oxAQDgu/zkKgxLiygDAwPVoUMHnroJAEAx/OVx3pavwmjSpIn27NlTGrEAAAAfYTmBeOaZZzR06FAtW7ZMhw8fVlZWlscGAIDfu8ov4ZQsrIGYMGGC/vznP+vOO++UJN19990et7Q2TVMOh0Mul8v+KAEA8BV+sgaixAnE+PHj9fjjj+uzzz4rzXgAAIAPKHECYZoXUqJ27dqVWjAAAPg6biRVhMs9hRMAAIgWRlHq16//i0lEZmamVwEBAICKz1ICMX78+EJ3ogQAAJfQwijC73//e1WrVq20YgEAwPf5SQujxPeBYP0DAAAV16uvvqratWsrJCRErVu31hdffFGieYsWLZLD4VCXLl0sna/ECcTFqzAAAMBleHMTqSusXixevFhDhgzR2LFjtWnTJjVv3lwdO3bUsWPHLjtv3759Gjp0qNq2bWv5nCVOIAzDoH0BAMAvsOtZGD+/03Nubm6x55wyZYr69eun3r17q3Hjxpo5c6bCwsI0e/bsYue4XC51795d48ePV926dS1/Tsu3sgYAAJdhUwUiMTFR0dHR7i0lJaXI0+Xl5SktLU3JycnufQEBAUpOTtaGDRuKDXPChAmqVq2a+vTpc0Uf09IiSgAAUDYOHDigqKgo92un01nkuBMnTsjlcikuLs5jf1xcnLZv317knNTUVL355pvavHnzFcdHAgEAgJ1sugojKirKI4Gwy5kzZ/Too49q1qxZqlq16hUfhwQCAAAblfV9IKpWrarAwEAdPXrUY//Ro0cVHx9faPzu3bu1b98+de7c2b3PMAxJUqVKlbRjxw5dd911v3he1kAAAODDgoODlZSUpFWrVrn3GYahVatWqU2bNoXGN2zYUN988402b97s3u6++261b99emzdvVmJiYonOSwUCAAA7lcONpIYMGaKePXuqVatW+s1vfqOpU6fq7Nmz6t27tySpR48eqlGjhlJSUhQSEqImTZp4zK9cubIkFdp/OSQQAADYqDxuZf3QQw/p+PHjGjNmjI4cOaIWLVpoxYoV7oWVGRkZCgiwt+lAAgEAwFVgwIABGjBgQJHvrV69+rJz586da/l8JBAAANjJT56FQQIBAICd/CSB4CoMAABgGRUIAABs5Phx82a+LyCBAADATn7SwiCBAADARuVxGWd5YA0EAACwjAoEAAB2ooUBAACuiI8kAd6ghQEAACyjAgEAgI38ZRElCQQAAHbykzUQtDAAAIBlVCAAALARLQwAAGAdLQwAAICiUYEAAMBGtDAAAIB1ftLCIIEAAMBOfpJAsAYCAABYRgUCAAAbsQYCAABYRwsDAACgaFQgAACwkcM05TCvvIzgzdyyRAIBAICdaGEAAAAUjQoEAAA24ioMAABgHS0MAACAolGBAADARrQwAACAdX7SwiCBAADARv5SgWANBAAAsIwKBAAAdqKFAQAAroSvtCG8QQsDAABYRgUCAAA7meaFzZv5PoAEAgAAG3EVBgAAQDGoQAAAYCeuwgAAAFY5jAubN/N9AS0MAABgGRUIeK1zrxN6oP8xVYkt0J6toXrtrzW0Y3NYsePb3nVKPYcfUVzNPB3c69Sbk6rry0+jfjLCVI9hR9Xp4R8UEeXS1o3hmj6ypg7tdbpHjJu7V9f96rwqX1OgM6cD9dXaSL05qboyjwZJkpq1ydZ9fziu+i3OKTzS0MG9wXrntWr67P2Y0vrfAD+3el51/efvNZR1PFg1G53VQ+N3q06L7CLHuvIdWvFaTW1YUk2njjoVV/e87hu5V7+69VSR41e8VlNLn6ut2x47qK5j95bip4At/KSF4ZMViLlz56py5crlHQYktbv7pP4w9pAWTonXkx3ra8/WEE16a4+ir8kvcnzjVmc16rX9WvHPKnqiQ32tXxGlsbP3qVaD8+4xXZ88rnseO65XRtbUoLuuV865AE1+a4+CnJfqelvWRWjSH2upT9uGeqZfbSXUztXoWfs8zrNna4ie6Vdbj/9fff1nURUNm56h1slZpfb/Av5r44dVteSZOrprUIb+suwr1Wx0Vq882kRZJ4KKHP+vF2vp84Xxemj8Ho1dmaZbuh/WzD80Usa34YXG7tsSobUL41Wj0dnS/hiwycWrMLzZfEG5JhC9evWSw+EotKWnp5dnWLDgvj+c0Iq3qug/i6soY1eIpo+oqdzzDnXsllnk+C59j2vjZ5FaMqOaDqSHaP4L1ZX+Taju6f3DjyNMdel7XP+cFqcN/47W3m2hen7gtbomLl83djrtPs77s2K1fVO4jh0M1taN4Vr8t2pqeMM5BVa68Ddv0Stxmv9CdW3dGK7D+51a+masNn4WqZvuPFXK/0fgj1a+UUM3/f6Ibux6TAn1z+vhyekKCnVp/dtxRY7/33uxuuPJ79X0tpOKvTZX7R49oibtT2rlrBoe43LOBmj2oAZ65LldCosuKIuPAjtcvA+EN5sPKPcKRKdOnXT48GGPrU6dOuUdFkqgUpCh65ud06a1ke59punQV2sj1TjpXJFzGiWd01c/GS9JaWsi1Sjpwm9X8dfm6Zq4Ao9jnjsTqO1fhalRMceMrFyg2+47qa0bw+QqcBQbb3iUS2dO0bWDvQryHMr4JkKNbj7l3hcQIDW6+ZT2bIosZk6AR0VNkoJCDKVvjPLYt2j0dWpyW6Ya3XxaQEVT7gmE0+lUfHy8xzZt2jQ1bdpU4eHhSkxM1BNPPKHs7KJ7iZK0ZcsWtW/fXpGRkYqKilJSUpI2btzofj81NVVt27ZVaGioEhMTNXDgQJ09W3w5MDc3V1lZWR4bCouq4lJgJenUcc8v5ZMnKikmtujflmJiC3TyxM/GH6+kmGoXxlf58b8/P+ap45VUpZpnW6TP04f0r/RvtGTrd4pNyNe43sUnnrd0PqX6zc/rP4uqlOzDASWUfTJIhsuhqKqeP5+RVfOVdTy4yDmNbzmplW8k6OjeEBmGtHVtZX214hplHbs0/ssPqirj2wjdO3xfaYaPUkALoxwFBARo+vTp+u677zRv3jx9+umnGj58eLHju3fvrpo1a+rLL79UWlqaRo4cqaCgC73H3bt3q1OnTrr//vv19ddfa/HixUpNTdWAAQOKPV5KSoqio6PdW2Jiou2fEd57Z0Y1PdGhvkb9vq4MQxo2LUNFrT5qfmO2/vzyAU0bVlP7d4aUfaDAz3Qdt0fV6uRo3G1JGlDvJi0eU1c3PnhUjh+/OTIPBevt8XX12LQdCgrxkW8TXGLasPmAcq/nLlu2TBEREe7Xd9xxh9555x3369q1a+uZZ57R448/rtdee63IY2RkZGjYsGFq2LChJOn66693v5eSkqLu3bvrqaeecr83ffp0tWvXTjNmzFBISOEvlFGjRmnIkCHu11lZWSQRRcjKDJSrQKr8s2pDTNUCnTxe9I/WyeOVFFP1Z+NjC3Ty2IXxmT/+t3JsgTKPXVqAVjm2QLu/C/3Z+SspK7OSDu5xKmOXUwvTtqlR0jltS7u0EK3pb7M1ft5ezRyboJVLqD7AfhEx+QoINAstmDxzIkhRsXlFzom8pkD9Z21Tfo5D2aeCVDkuT+8/W1tVr82RJGV8E6EzJ4I1+Xct3XMMl0Pp/4vS6nkJ+tuudQoILL3PBJREuScQ7du314wZM9yvw8PDtXLlSqWkpGj79u3KyspSQUGBcnJydO7cOYWFFb48cMiQIerbt68WLFig5ORkPfjgg7ruuuskXWhvfP3111q4cKF7vGmaMgxDe/fuVaNGjQodz+l0yul0FtoPTwX5Adr1dZha3nxGG1ZES5IcDlMtbs7WB3OvKXLOtrQwtWibrfffiHXvu+GWM+4v/SMZwfrhaCW1vPmM9vyYMIRFuNSw5Tktm1/0MSXJ8WMtLSj4UurerE22JszfqzcnVdfHC4ufC3ijUrCpa5tma/u6ymrR8cLiYcOQtq+rrFt7Hr7s3KAQUzHxeXLlO/TVx9co6a4TkqSGN53W6P9s8hg7f+j1ir/uvDr0/57koYLjWRhlJDw8XPXq1XNvubm5uuuuu9SsWTO9++67SktL06uvvipJyssrOpsfN26cvvvuO/3ud7/Tp59+qsaNG+v999+XJGVnZ+uPf/yjNm/e7N62bNmiXbt2uZMMXLn3/l5VdzycqeQHM5VYL0d/evZ7hYQZ7rUGw6ZlqPeoS/+ILn0jVq1uzdL9fzymxHo5euTPR3R9s/P615yLX/AOLX0jVt0GHdNvO5xW7YbnNWx6hn44GqT1PyYpDVqe1d29T6jur86rWo08Nb/pjEa9tl+H9gZrW9qFBLP5jdmauGCv/vVmVaUuj1ZMbL5iYvMVWZmV7LBfct+DSl0Urw1LqunwrlD98+nrlHcuUDc+eFSSNGdwfb3/XC33+L1fReirj6/R8Qyndn0Rpek9fiXTcKjDH7+XJIVEuFSjwTmPLTjMUHhMvmo0KHoxMSoQP7kKo9wrED+XlpYmwzD00ksvKSDgQn7z9ttv/+K8+vXrq379+ho8eLC6deumOXPm6N5779UNN9ygrVu3ql69eqUdul9a80GMoq9xqcewI4qJLdCe70L1dPc6OvVjOTe2Rp6Mnyw237oxXM8+WUs9RxxRr5FHdGivU+Mfq639Oy61J95+NVYhYYYGPf+9IqJc+u7LcD3dva7ycy/8POSeD9BNd5zWo38+opAwQ5nHgrTxs0hNmhan/LwLY5IfzFRImKHfDzym3w885j72lvXhGv4APwuwV6vOJ3TmhyB9OOXaCzeSanxWf5r/raJiLyyszDzklCPg0pdCfm6A/vViLZ04ECJnmEtN2p9U76k7FRbtKq+PAFjmMM3yS3V69eqlU6dOaenSpe59W7ZsUYsWLTR16lR17txZ69at06hRo3Tw4EGdPHlSlStX1ty5c/XUU0/p1KlTOn/+vIYNG6YHHnhAderU0ffff6+ePXvq/vvv13PPPaevv/5av/3tb/XYY4+pb9++Cg8P19atW/XJJ5/ob3/7W4nizMrKUnR0tG7VParkKPrGMICvm7k/tbxDAErNmTOGWv7qmE6fPq2oqKhfnnAFLn5XtLljgioFXfmC7YL8HG34eEypxmqHcm9h/Fzz5s01ZcoUPffcc2rSpIkWLlyolJSUYscHBgbqhx9+UI8ePVS/fn117dpVd9xxh8aPHy9JatasmdasWaOdO3eqbdu2atmypcaMGaOEhISy+kgAAH/iJ1dhlGsFwldQgYA/oAKBq1mZViA62VCBWFHxKxAVbg0EAAC+zF+uwiCBAADAToZ5YfNmvg8ggQAAwE48zhsAAKBoVCAAALCRQ16ugbAtktJFAgEAgJ28vZukj1wcSQsDAABYRgUCAAAb+ctlnFQgAACwUzndifLVV19V7dq1FRISotatW+uLL74oduysWbPUtm1bxcTEKCYmRsnJyZcdXxQSCAAAfNzixYs1ZMgQjR07Vps2bVLz5s3VsWNHHTt2rMjxq1evVrdu3fTZZ59pw4YNSkxMVIcOHXTw4MESn5MEAgAAGzlM0+vNqilTpqhfv37q3bu3GjdurJkzZyosLEyzZ88ucvzChQv1xBNPqEWLFmrYsKHeeOMNGYahVatWlficJBAAANjJsGHThWdr/HTLzc0t8nR5eXlKS0tTcnKye19AQICSk5O1YcOGEoV87tw55efnq0qVKiX+mCQQAABUQImJiYqOjnZvxT2Z+sSJE3K5XIqLi/PYHxcXpyNHjpToXCNGjFBCQoJHEvJLuAoDAAAbXWkb4qfzJenAgQMeT+N0Op1ex1aUZ599VosWLdLq1asVElLyp4iSQAAAYCebnoURFRVVosd5V61aVYGBgTp69KjH/qNHjyo+Pv6yc1988UU9++yzWrlypZo1a2YpTFoYAADY6eKdKL3ZLAgODlZSUpLHAsiLCyLbtGlT7Lznn39eEydO1IoVK9SqVSvLH5MKBAAAPm7IkCHq2bOnWrVqpd/85jeaOnWqzp49q969e0uSevTooRo1arjXUTz33HMaM2aM3nrrLdWuXdu9ViIiIkIRERElOicJBAAANiqPO1E+9NBDOn78uMaMGaMjR46oRYsWWrFihXthZUZGhgICLjUdZsyYoby8PD3wwAMexxk7dqzGjRtXonOSQAAAYKdyepjWgAEDNGDAgCLfW716tcfrffv2XdE5foo1EAAAwDIqEAAA2MhhXNi8me8LSCAAALBTObUwyhotDAAAYBkVCAAA7GTTjaQqOhIIAABsZNetrCs6WhgAAMAyKhAAANjJTxZRkkAAAGAnU5I3l2L6Rv5AAgEAgJ1YAwEAAFAMKhAAANjJlJdrIGyLpFSRQAAAYCc/WURJCwMAAFhGBQIAADsZkhxezvcBJBAAANiIqzAAAACKQQUCAAA7+ckiShIIAADs5CcJBC0MAABgGRUIAADs5CcVCBIIAADsxGWcAADAKi7jBAAAKAYVCAAA7MQaCAAAYJlhSg4vkgDDNxIIWhgAAMAyKhAAANiJFgYAALDOywRCvpFA0MIAAACWUYEAAMBOtDAAAIBlhimv2hBchQEAAK5WVCAAALCTaVzYvJnvA0ggAACwE2sgAACAZayBAAAAKBoVCAAA7EQLAwAAWGbKywTCtkhKFS0MAABgGRUIAADsRAsDAABYZhiSvLiXg+Eb94GghQEAACyjAgEAgJ1oYQAAAMv8JIGghQEAACyjAgEAgJ385FbWJBAAANjINA2ZXjxR05u5ZYkEAgAAO5mmd1UE1kAAAICrFRUIAADsZHq5BsJHKhAkEAAA2MkwJIcX6xh8ZA0ELQwAAGAZFQgAAOxECwMAAFhlGoZML1oYvnIZJy0MAABgGRUIAADsRAsDAABYZpiS4+pPIGhhAAAAy6hAAABgJ9OU5M19IHyjAkECAQCAjUzDlOlFC8MkgQAAwA+ZhryrQHAZJwAAuEpRgQAAwEa0MAAAgHV+0sIggSiBi9lggfK9ujcIUJGdOeMb/2gBVyI7+8LPd1n8du/td0WB8u0LphSRQJTAmTNnJEmp+qicIwFKT8tflXcEQOk7c+aMoqOjS+XYwcHBio+PV+oR778r4uPjFRwcbENUpcdh+kqzpRwZhqFDhw4pMjJSDoejvMPxC1lZWUpMTNSBAwcUFRVV3uEAtuLnu+yZpqkzZ84oISFBAQGld/1ATk6O8vLyvD5OcHCwQkJCbIio9FCBKIGAgADVrFmzvMPwS1FRUfwDi6sWP99lq7QqDz8VEhJS4b/47cJlnAAAwDISCAAAYBkJBCokp9OpsWPHyul0lncogO34+cbVgEWUAADAMioQAADAMhIIAABgGQkEAACwjAQCAMrQ3LlzVbly5fIOA/AaCQRKlcPhuOw2bty48g4RuCK9evUq8mc6PT29vEMDygR3okSpOnz4sPvPixcv1pgxY7Rjxw73voiICPefTdOUy+VSpUr8WMI3dOrUSXPmzPHYFxsbW07RAGWLCgRKVXx8vHuLjo6Ww+Fwv96+fbsiIyP18ccfKykpSU6nU6mpqerVq5e6dOnicZynnnpKt956q/u1YRhKSUlRnTp1FBoaqubNm2vJkiVl++Hg95xOp8fPeHx8vKZNm6amTZsqPDxciYmJeuKJJ5SdnV3sMbZs2aL27dsrMjJSUVFRSkpK0saNG93vp6amqm3btgoNDVViYqIGDhyos2fPlsXHAy6LBALlbuTIkXr22We1bds2NWvWrERzUlJSNH/+fM2cOVPfffedBg8erEceeURr1qwp5WiBywsICND06dP13Xffad68efr00081fPjwYsd3795dNWvW1Jdffqm0tDSNHDlSQUFBkqTdu3erU6dOuv/++/X1119r8eLFSk1N1YABA8rq4wDFolaMcjdhwgTdfvvtJR6fm5uryZMna+XKlWrTpo0kqW7dukpNTdXrr7+udu3alVaogIdly5Z5tOHuuOMOvfPOO+7XtWvX1jPPPKPHH39cr732WpHHyMjI0LBhw9SwYUNJ0vXXX+9+LyUlRd27d9dTTz3lfm/69Olq166dZsyY4TcPbULFRAKBcteqVStL49PT03Xu3LlCSUdeXp5atmxpZ2jAZbVv314zZsxwvw4PD9fKlSuVkpKi7du3KysrSwUFBcrJydG5c+cUFhZW6BhDhgxR3759tWDBAiUnJ+vBBx/UddddJ+lCe+Prr7/WwoUL3eNN05RhGNq7d68aNWpU+h8SKAYJBMpdeHi4x+uAgAD9/A7r+fn57j9f7CcvX75cNWrU8BjHswVQlsLDw1WvXj3363379umuu+5S//79NWnSJFWpUkWpqanq06eP8vLyikwgxo0bp4cffljLly/Xxx9/rLFjx2rRokW69957lZ2drT/+8Y8aOHBgoXnXXnttqX424JeQQKDCiY2N1bfffuuxb/Pmze6+cOPGjeV0OpWRkUG7AhVKWlqaDMPQSy+9pICAC0vM3n777V+cV79+fdWvX1+DBw9Wt27dNGfOHN1777264YYbtHXrVo8kBagoWESJCue2227Txo0bNX/+fO3atUtjx471SCgiIyM1dOhQDR48WPPmzdPu3bu1adMmvfLKK5o3b145Rg5/V69ePeXn5+uVV17Rnj17tGDBAs2cObPY8efPn9eAAQO0evVq7d+/X+vWrdOXX37pbk2MGDFC69ev14ABA7R582bt2rVL//rXv1hEiQqBBAIVTseOHTV69GgNHz5cv/71r3XmzBn16NHDY8zEiRM1evRopaSkqFGjRurUqZOWL1+uOnXqlFPUgNS8eXNNmTJFzz33nJo0aaKFCxcqJSWl2PGBgYH64Ycf1KNHD9WvX19du3bVHXfcofHjx0uSmjVrpjVr1mjnzp1q27atWrZsqTFjxighIaGsPhJQLB7nDQAALKMCAQAALCOBAAAAlpFAAAAAy0ggAACAZSQQAADAMhIIAABgGQkEAACwjAQCAABYRgIB+IhevXqpS5cu7te33nqr+zHPZWn16tVyOBw6depUsWMcDoeWLl1a4mOOGzdOLVq08Cquffv2yeFwaPPmzV4dB0DJkEAAXujVq5ccDoccDoeCg4NVr149TZgwQQUFBaV+7vfee08TJ04s0diSfOkDgBU8jRPwUqdOnTRnzhzl5ubqo48+0pNPPqmgoCCNGjWq0Ni8vDwFBwfbct4qVarYchwAuBJUIAAvOZ1OxcfHq1atWurfv7+Sk5P1wQcfSLrUdpg0aZISEhLUoEEDSdKBAwfUtWtXVa5cWVWqVNE999yjffv2uY/pcrk0ZMgQVa5cWddcc42GDx+unz+25uctjNzcXI0YMUKJiYlyOp2qV6+e3nzzTe3bt0/t27eXJMXExMjhcKhXr16SJMMwlJKSojp16ig0NFTNmzfXkiVLPM7z0UcfqX79+goNDVX79u094iypESNGqH79+goLC1PdunU1evRo5efnFxr3+uuvKzExUWFhYeratatOnz7t8f4bb7yhRo0aKSQkRA0bNtRrr71mORYA9iCBAGwWGhqqvLw89+tVq1Zpx44d+uSTT7Rs2TLl5+erY8eOioyM1Nq1a7Vu3TpFRESoU6dO7nkvvfSS5s6dq9mzZys1NVWZmZl6//33L3veHj166J///KemT5+ubdu26fXXX1dERIQSExP17rvvSpJ27Nihw4cPa9q0aZKklJQUzZ8/XzNnztR3332nwYMH65FHHtGaNWskXUh07rvvPnXu3FmbN29W3759NXLkSMv/TyIjIzV37lxt3bpV06ZN06xZs/Tyyy97jElPT9fbb7+tDz/8UCtWrNBXX32lJ554wv3+woULNWbMGE2aNEnbtm3T5MmTNXr0aB7hDpQXE8AV69mzp3nPPfeYpmmahmGYn3zyiel0Os2hQ4e634+LizNzc3PdcxYsWGA2aNDANAzDvS83N9cMDQ01//3vf5umaZrVq1c3n3/+eff7+fn5Zs2aNd3nMk3TbNeunTlo0CDTNE1zx44dpiTzk08+KTLOzz77zJRknjx50r0vJyfHDAsLM9evX+8xtk+fPma3bt1M0zTNUaNGmY0bN/Z4f8SIEYWO9XOSzPfff7/Y91944QUzKSnJ/Xrs2LFmYGCg+f3337v3ffzxx2ZAQIB5+PBh0zRN87rrrjPfeustj+NMnDjRbNOmjWmaprl3715TkvnVV18Ve14A9mENBOClZcuWKSIiQvn5+TIMQw8//LDGjRvnfr9p06Ye6x62bNmi9PR0RUZGehwnJydHu3fv1unTp3X48GG1bt3a/V6lSpXUqlWrQm2MizZv3qzAwEC1a9euxHGnp6fr3Llzuv322z325+XlqWXLlpKkbdu2ecQhSW3atCnxOS5avHixpk+frt27dys7O1sFBQWKioryGHPttdeqRo0aHucxDEM7duxQZGSkdu/erT59+qhfv37uMQUFBYqOjrYcDwDvkUAAXmrfvr1mzJih4OBgJSQkqFIlz79W4eHhHq+zs7OVlJSkhQsXFjpWbGzsFcUQGhpqeU52drYkafny5R5f3NKFdR122bBhg7p3767x48erY8eOio6O1qJFi/TSSy9ZjnXWrFmFEprAwEDbYgVQciQQgJfCw8NVr169Eo+/4YYbtHjxYlWrVq3Qb+EXVa9eXf/73/90yy23SLrwm3ZaWppuuOGGIsc3bdpUhmFozZo1Sk5OLvT+xQqIy+Vy72vcuLGcTqcyMjKKrVw0atTIvSD0ov/+97+//CF/Yv369apVq5aefvpp9779+/cXGpeRkaFDhw4pISHBfZ6AgAA1aNBAcXFxSkhI0J49e9S9e3dL5wdQOlhECZSx7t27q2rVqrrnnnu0du1a7d27V6tXr9bAgQP1/fffS5IGDRqkZ599VkuXLtX27dv1xBNPXPYeDrVr11bPnj312GOPaenSpe5jvv3225KkWrVqyeFwaNmyZTp+/Liys7MVGRmpoUOHavDgwZo3b552796tTZs26ZVXXnEvTHz88ce1a9cuDRs2TDt27NBbb72luXPnWvq8119/vTIyMrRo0SLt3r1b06dPL3JBaEhIiHr27KktW7Zo7dq1GjhwoLp27ar4+HhJ0vjx45WSkqLp06dr586d+uabbzRnzhxNmTLFUjwA7EECAZSxsLAwff7557r22mt13333qVGjRurTp49ycnLcFYk///nPevTRR9WzZ0+1adNGkZGRuvfeey973BkzZuiBBx7QE088oYYNG6pfv346e/asJKlGjRoaP368Ro4cqbi4OA0YMECSNHHiRI0ePVopKSlq1KiROnXqpOXLl6tOnTqSLqxLePfdd7V06VI1b95cM2fO1OTJky193rvvvluDBw/WgAED1KJFC61fv16jR48uNK5evXq67777dOedd6pDhw5q1qyZx2Waffv21RtvvKE5c+aoadOmateunebOneuOFUDZcpjFrcoCAAAoBhUIAABgGQkEAACwjAQCAABYRgIBAAAsI4EAAACWkUAAAADLSCAAAIBlJBAAAMAyEggAAGAZCQQAALCMBAIAAFj2/48bx9AK+uv/AAAAAElFTkSuQmCC\n" }, "metadata": {} } ] } ] }