diff --git a/.gitignore b/.gitignore index 66dd292..32b2b4b 100644 --- a/.gitignore +++ b/.gitignore @@ -160,14 +160,14 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ +# miscellaneous +.gitpod.yml +**/development/ # Data and models data/*/* !.gitkeep -#devops -.gitpod.yml - # Logs Directories outputs mlruns diff --git a/README.md b/README.md index 85d993a..d934173 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,23 @@ -# PROJECT / BUSINESS UNDERSTANDING +# Log Anomaly Detection + + +
+ Table of Contents +
    +
  1. About The Project
  2. +
  3. + Getting Started + +
  4. +
  5. Analysis and Report
  6. +
  7. Contact
  8. +
  9. Acknowledgments
  10. +
+
+ +## About The Project In the realm of computing, logging involves the process of creating a record detailing events that transpire within a computer system. These events encompass issues, errors, or even informative updates about ongoing operations. These occurrences might manifest within the operating system or other software components. For every such event, a message or entry is documented. @@ -16,6 +35,54 @@ The data containing the logs was provided. The dataset was in **JSON** format wh The labels for logs areĀ **"abnormal" and "normal"** -## Project Objectives +### Project Objectives 1. To train a machine learning model that can predict whether a given log is an anomaly or normal + +### Dataset +about the dataset +pic of samples of data + + +## Getting Started + +To get a local copy up and running follow these simple example steps. + +### Prerequisites + +In + +```bash +# Clone this repository +$ git clone + +# Go into the repository +$ cd + +# Install dependencies +$ make setup +``` + +### Runing the program + +train.py +different models +evalute.py + +## Analysis and Report + +

(back to top)

+ + +## Contact + +If you have questions or need assistance, feel free to reach out to: + +**Name:** **Ipadeola Ezekiel Ladipo** +**Email:** +**GitHub:** [@rileydrizzy](https://github.com/rileydrizzy) +**Linkdeln:** [Ipadeola Ladipo](https://www.linkedin.com/in/ladipo-ipadeola/) + +

(back to top)

+ +--- diff --git a/dev.txt b/dev.txt new file mode 100644 index 0000000..913981e --- /dev/null +++ b/dev.txt @@ -0,0 +1,7 @@ +evaluate and analysis c # +pretrained embedding # +learning rate scheduler # +class weight +github repo +Confusion Matrix for error analysis c # +Hyper-tunning of the model to be done \ No newline at end of file diff --git a/development/devscript.py b/development/devscript.py deleted file mode 100644 index 393d90c..0000000 --- a/development/devscript.py +++ /dev/null @@ -1,60 +0,0 @@ -## Heruristic Benchmark -# TODO implement HB pr Dummy Classifier -import numpy as np -from sklearn.model_selection import train_test_split -from sklearn.linear_model import LogisticRegression -from sklearn.metrics import ( - accuracy_score, - precision_score, - recall_score, - f1_score, - roc_auc_score, -) - -# Generate synthetic imbalanced data (replace this with your actual data) - -minority_class_size = 100 -majority_class_size = 1000 -minority_class = np.random.rand(minority_class_size, 2) + np.array([1, 1]) -majority_class = np.random.rand(majority_class_size, 2) - -# Calculate class proportion -class_proportion = majority_class_size // minority_class_size - -# Randomly sample majority class instances -sampled_majority_class_indices = np.random.choice( - majority_class_size, minority_class_size * class_proportion, replace=False -) -sampled_majority_class = majority_class[sampled_majority_class_indices] - -# Combine minority and sampled majority class instances -balanced_data = np.vstack((minority_class, sampled_majority_class)) -labels = np.hstack( - (np.ones(minority_class_size), np.zeros(minority_class_size * class_proportion)) -) - -# Split data into training and testing sets -X_train, X_test, y_train, y_test = train_test_split( - balanced_data, labels, test_size=0.2, random_state=42 -) - -# Train a logistic regression model -model = LogisticRegression() -model.fit(X_train, y_train) - -# Predictions -y_pred = model.predict(X_test) - -# Evaluate model performance -accuracy = accuracy_score(y_test, y_pred) -precision = precision_score(y_test, y_pred) -recall = recall_score(y_test, y_pred) -f1 = f1_score(y_test, y_pred) -roc_auc = roc_auc_score(y_test, y_pred) - -# Print results -print(f"Accuracy: {accuracy:.2f}") -print(f"Precision: {precision:.2f}") -print(f"Recall: {recall:.2f}") -print(f"F1-Score: {f1:.2f}") -print(f"AUC-ROC: {roc_auc:.2f}") diff --git a/development/new_dev.ipynb b/development/new_dev.ipynb deleted file mode 100644 index 1c5bc9a..0000000 --- a/development/new_dev.ipynb +++ /dev/null @@ -1,396 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from pathlib import Path\n", - "from time import strftime\n", - "\n", - "import matplotlib.pyplot as plt\n", - "import mlflow\n", - "import numpy as np\n", - "import seaborn as sns\n", - "import tensorflow as tf\n", - "from sklearn.metrics import auc, confusion_matrix, precision_recall_curve\n", - "from sklearn.model_selection import train_test_split\n", - "from tensorflow.keras.layers import Dense\n", - "from tensorflow.keras.models import Sequential" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "# Generate some example data (replace this with your actual data)\n", - "X, y = np.random.rand(1000, 10), np.random.randint(2, size=(1000,))\n", - "\n", - "# Split the data into training and testing sets\n", - "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "25/25 [==============================] - 0s 6ms/step - loss: 0.7012 - accuracy: 0.4888 - val_loss: 0.6993 - val_accuracy: 0.5200\n", - "7/7 [==============================] - 0s 939us/step\n" - ] - }, - { - "data": { - "image/png": "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e/duffnll87XWRgiIyM1fvx4/etf/1L58uVznefp6Znt7PG///1v/fLLLy5jWWGe0z8O8mvkyJE6fPiwFixYoClTpqhq1aqKiorK9X0EYJ5ie0kDAFxJjRo19O677+qhhx5S3bp1XX7T2qZNm/Tvf/9b0dHRkqSGDRsqKipKb775pk6fPq2IiAj997//1YIFC/TAAw/kesurq9GjRw+NHDlSXbt21VNPPaW0tDTNnDlTtWrVcvnSVmxsrNatW6f77rtP4eHhOn78uGbMmKFKlSrprrvuynX/r7zyiu699141b95cjz76qM6fP6/XX39dQUFBV7zU4Fp5eHjo//7v//5yXqdOnRQbG6u+ffvqzjvv1K5du7R48WJVr17dZV6NGjUUHBysWbNmKSAgQH5+frr99tvzfVJj9erVmjFjhsaOHeu8Tdq8efPUunVrjRkzRi+//HK+9gegeDLiDC8A5KRz587auXOnHnzwQX3yyScaPHiwRo0apUOHDmny5MmaPn26c+7bb7+tmJgYbd26VU8//bRWr16t0aNH6/333y/QNZUqVUoff/yxfH199eyzz2rBggWaNGmS7r///mxrr1KliubOnavBgwfrjTfeUKtWrbR69WoFBQXluv82bdro888/V6lSpfTCCy/o1Vdf1R133KGNGzdeFz8Be+655zRs2DB98cUXGjJkiLZt26YVK1aocuXKLvNKliypBQsWyNPTUwMHDlTPnj31zTff5OtYZ8+eVb9+/XTrrbfq+eefd463bNlSQ4YM0eTJk/Xtt98WyOsCcH2zWfn5ZgIAAABQzHCGFwAAAEYzInjXr1+v3r17q3nz5s4vPixatEgbNmxw88oAAADgbsU+eJcuXar27dvLx8dH27dvd37rNiUlRRMnTnTz6gAAAOBuxT54J0yYoFmzZumtt95SyZIlneMtWrTI16+pBAAAgJmKffDGx8erVatW2caDgoIK5P6NAAAAKN6KffCWL19eCQkJ2cY3bNiQ7b6OAAAAuPEU+1888dhjj2nIkCGaO3eubDabkpKStHnzZg0fPlxjxozJ0z7S09Oz/cYdu90uu91eGEsGAABAESr2wTtq1Cg5HA7dc889SktLU6tWrWS32zV8+HD94x//yNM+Jk2apJiYGJcxz3JNVbJCs8JYMgC4ReKaKe5eAgAUqLBgrzzNM+YXT1y8eFEJ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GjNmDAENFBA+C3P2zTff6Pz584qNjdWZM2e0adOmHOfNnj1blStXlq+vr7p3766UlBTnttzePwC4Wtd8Da+Pj4/Lv76//vprxcfHa9WqVVq+fLkyMjLUvn17BQQEaP369dq4caP8/f3VoUMH5/MmT56s+fPna+7cudqwYYNOnTqljz/++IrH7dOnj9577z1Nnz5de/fu1ezZs+Xv76/KlStr6dKlkqT4+HglJydr2rRpki7/mG3hwoWaNWuW9uzZo2eeeUa9e/fWN998I+nyX0bdunXT/fffr7i4OPXv31+jRo3K0/tQrVo1DRw4UKNHj5bD4chxzrRp0zR58mS9+uqr2rlzp9q3b6/OnTtr//79LvNGjRqlIUOGaO/evWrfvr0kafXq1UpKStK6des0ZcoUjR07Vp06dVJISIi2bNmigQMH6vHHH9eRI0ec+wkICND8+fP1ww8/aNq0aXrrrbc0derUPL0eAPnDZ+Flc+bMUc+ePVWyZEn17NlTc+bMyTYnISFBH3zwgT777DN9/vnn2r59uwYNGuQy58/vHwBcEysfoqKirC5duliWZVkOh8NatWqVZbfbreHDhzu3lytXzkpPT3c+Z9GiRVbt2rUth8PhHEtPT7d8fHysL774wrIsy6pQoYL18ssvO7dnZGRYlSpVch7LsiwrIiLCGjJkiGVZlhUfH29JslatWpXjOtesWWNJsn777Tfn2IULFyxfX19r06ZNLnMfffRRq2fPnpZlWdbo0aOtevXquWwfOXJktn39WXh4uDV16lTr+PHjVkBAgLVw4ULLsixryJAhVkREhHNeWFiY9eKLL7o8t2nTptagQYMsy7KsgwcPWpKs1157zWVOVFSUFR4ebmVmZjrHateubbVs2dL5+NKlS5afn5/13nvv5brOV155xbrtttucj8eOHWs1bNgw1/kAcsZnYc5SUlIsHx8fKy4uzrIsy9q+fbvl7+9vnT171jln7Nixlqenp3XkyBHn2H/+8x/Lw8PDSk5Otiwr5/cPAK5FifwG8vLly+Xv76+MjAw5HA716tVL48aNc25v0KCBvLy8nI937NihhISEbNecXbhwQYmJiUpJSVFycrJuv/1257YSJUqoSZMm2X6UlyUuLk6enp6KiIjI87oTEhKUlpamtm3buoxfvHhRt956qyRp7969LuuQpObNm+f5GGXKlNHw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sGGDZs+erYiICM2cOVM1atTQ5MmTJUm1a9fWrl279M9//rMA3zUAyD+CFwCK2PLly+Xv76+MjAw5HA716tVL48aN0+DBg9WgQQOX63Z37NihhIQEBQQEuOzjwoULSkxMVEpKipKTk3X77bc7t5UoUUJNmjTJdllDlri4OHl6eioiIiLPa05ISFBaWpratm3rMn7x4kXdeuutkqS9e/e6rEOSM44BwJ0IXgAoYpGRkZo5c6a8vLwUFhamEiV+/yj28/NzmZuamqrbbrtNixcvzrafMmXKXNXxfXx88v2c1NRUSdKKFStUsWJFl212u/2q1gEARYXgBYAi5ufnp5o1a+ZpbuPGjbVkyRKVLVtWgYGBOc6pUKGCtmzZolatWkmSLl26pO+//16NGzfOcX6DBg3kcDj0zTffOC9p+KOsM8yZmZnOsXr16slut+vw4cO5nhmuW7euPv30U5exb7/99q9fJAAUMr60BgDXsYcfflilS5dWly5dtH79eh08eFBr167VU089pSNHjkiShgwZopdeeknLli3Tvn37NGjQoCveQ7dq1aqKiopSv379tGzZMuc+P/jgA0lSeHi4bDabli9frhMnTig1NVUBAQEaPny4nnnmGS1YsECJiYnatm2bXn/9dS1YsECSNHDgQO3fv18jRoxQfHy83n33Xc2fP7+w3yIA+EsELwBcx3x9fbVu3TpVqVJF3bp1U926dfXoo4/qwoULzjO+w4YN0yOPPKKoqCg1b95cAQEB6tq16xX3O3PmTD344IMaNGiQ6tSpo8cee0znzp2TJFWsWFExMTEaNWqUypUrpyeffFKSNH78eI0ZM0aTJk1S3bp11aFDB61YsULVqlWTJFWpUkVLly7VsmXL1LBhQ82aNUsTJ04sxHcHAPLGZuX2rQYAAADAAJzhBQAAgNEIXgAAABiN4AUAAIDRCF4AAAAYjeAFAACA0QheAAAAGI3gBQAAgNEIXgAAABiN4AUAAIDRCF4AAAAYjeAFAACA0f4fYyM6sCD+6PYAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "def plot_confusion_matrix(model, X_test, y_test, threshold=0.5, save_path=None):\n", - " \"\"\"\n", - " Generate and plot the confusion matrix for a TensorFlow model.\n", - "\n", - " Parameters:\n", - " - model: The trained TensorFlow model.\n", - " - X_test: Test features.\n", - " - y_test: True labels for the test set.\n", - " - threshold: Decision threshold for binary classification.\n", - " - save_path: Optional path to save the plot as an image.\n", - "\n", - " Returns:\n", - " - None\n", - " \"\"\"\n", - " # Make predictions on the test set\n", - " y_pred = model.predict(X_test)\n", - "\n", - " # Apply threshold for binary classification\n", - " y_pred_binary = (y_pred > threshold).astype(int)\n", - "\n", - " # For binary classification, flatten the true labels\n", - " y_test = np.reshape(y_test, (-1,))\n", - "\n", - " # Calculate the confusion matrix\n", - " cm = confusion_matrix(y_test, y_pred_binary)\n", - "\n", - " # Plot the confusion matrix\n", - " plt.figure(figsize=(8, 6))\n", - " sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', cbar=False, annot_kws={\"size\": 14})\n", - " plt.xlabel('Predicted')\n", - " plt.ylabel('True')\n", - " plt.title('Confusion Matrix')\n", - " plt.xticks([0, 1], ['Predicted Normal', 'Predicted Abr'])\n", - " plt.yticks([0, 1], ['True 0', 'True 1'])\n", - " plt.show()\n", - "\n", - " return cm\n", - "\n", - "# Example usage:\n", - "# Assuming you have a trained model, test data (X_test, y_test), and a directory to save the plot\n", - "# Replace placeholders with your actual data and paths\n", - "\n", - "# Build a simple binary classification model (replace this with your actual model)\n", - "model = Sequential([\n", - " Dense(64, activation='relu', input_shape=(10,)),\n", - " Dense(1, activation='sigmoid')\n", - "])\n", - "\n", - "model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])\n", - "\n", - "# Train the model (replace this with your actual training process)\n", - "model.fit(X_train, y_train, epochs=1, batch_size=32, validation_data=(X_test, y_test))\n", - "\n", - "save_directory = \"path/to/save/directory\"\n", - "\n", - "# Call the function to generate and save the confusion matrix\n", - "anw = plot_confusion_matrix(model, X_test, y_test, save_path=f\"{save_directory}/confusion_matrix.png\")" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[101, 8],\n", - " [ 68, 23]])" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "anw" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Epoch 1/10\n", - "25/25 [==============================] - 2s 23ms/step - loss: 0.6976 - accuracy: 0.5188 - val_loss: 0.7043 - val_accuracy: 0.3650\n", - "Epoch 2/10\n", - "25/25 [==============================] - 0s 6ms/step - loss: 0.6897 - accuracy: 0.5462 - val_loss: 0.7046 - val_accuracy: 0.4050\n", - "Epoch 3/10\n", - "25/25 [==============================] - 0s 7ms/step - loss: 0.6882 - accuracy: 0.5575 - val_loss: 0.7025 - val_accuracy: 0.4450\n", - "Epoch 4/10\n", - "25/25 [==============================] - 0s 8ms/step - loss: 0.6871 - accuracy: 0.5550 - val_loss: 0.7036 - val_accuracy: 0.4300\n", - "Epoch 5/10\n", - "25/25 [==============================] - 0s 9ms/step - loss: 0.6859 - accuracy: 0.5713 - val_loss: 0.7053 - val_accuracy: 0.4300\n", - "Epoch 6/10\n", - "25/25 [==============================] - 0s 9ms/step - loss: 0.6847 - accuracy: 0.5900 - val_loss: 0.7054 - val_accuracy: 0.4350\n", - "Epoch 7/10\n", - "25/25 [==============================] - 0s 8ms/step - loss: 0.6839 - accuracy: 0.5775 - val_loss: 0.7060 - val_accuracy: 0.4400\n", - "Epoch 8/10\n", - "25/25 [==============================] - 0s 8ms/step - loss: 0.6837 - accuracy: 0.5838 - val_loss: 0.7075 - val_accuracy: 0.4500\n", - "Epoch 9/10\n", - "25/25 [==============================] - 0s 9ms/step - loss: 0.6824 - accuracy: 0.5775 - val_loss: 0.7055 - val_accuracy: 0.4450\n", - "Epoch 10/10\n", - "25/25 [==============================] - 0s 8ms/step - loss: 0.6814 - accuracy: 0.5750 - val_loss: 0.7079 - val_accuracy: 0.4450\n", - "7/7 [==============================] - 0s 2ms/step\n" - ] - }, - { - "ename": "FileNotFoundError", - "evalue": "[Errno 2] No such file or directory: 'path/to/save/directory/precision_recall_curve.png'", - "output_type": "error", - "traceback": [ - "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mFileNotFoundError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[1;32mIn[2], line 72\u001b[0m\n\u001b[0;32m 69\u001b[0m save_directory \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mpath/to/save/directory\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 71\u001b[0m \u001b[38;5;66;03m# Call the function to generate and save the precision-recall curve\u001b[39;00m\n\u001b[1;32m---> 72\u001b[0m \u001b[43mplot_precision_recall_curve\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmodel\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mX_test\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43my_test\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msave_path\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43mf\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;132;43;01m{\u001b[39;49;00m\u001b[43msave_directory\u001b[49m\u001b[38;5;132;43;01m}\u001b[39;49;00m\u001b[38;5;124;43m/precision_recall_curve.png\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", - "Cell \u001b[1;32mIn[2], line 46\u001b[0m, in \u001b[0;36mplot_precision_recall_curve\u001b[1;34m(model, X_test, y_test, save_path)\u001b[0m\n\u001b[0;32m 44\u001b[0m \u001b[38;5;66;03m# Save the plot if save_path is provided\u001b[39;00m\n\u001b[0;32m 45\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m save_path:\n\u001b[1;32m---> 46\u001b[0m \u001b[43mplt\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msavefig\u001b[49m\u001b[43m(\u001b[49m\u001b[43msave_path\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 47\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mPrecision-Recall Curve saved at: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00msave_path\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 48\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n", - "File \u001b[1;32mc:\\Users\\Yinka\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\matplotlib\\pyplot.py:1023\u001b[0m, in \u001b[0;36msavefig\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m 1020\u001b[0m \u001b[38;5;129m@_copy_docstring_and_deprecators\u001b[39m(Figure\u001b[38;5;241m.\u001b[39msavefig)\n\u001b[0;32m 1021\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21msavefig\u001b[39m(\u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[0;32m 1022\u001b[0m fig \u001b[38;5;241m=\u001b[39m gcf()\n\u001b[1;32m-> 1023\u001b[0m res \u001b[38;5;241m=\u001b[39m \u001b[43mfig\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msavefig\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 1024\u001b[0m fig\u001b[38;5;241m.\u001b[39mcanvas\u001b[38;5;241m.\u001b[39mdraw_idle() \u001b[38;5;66;03m# Need this if 'transparent=True', to reset colors.\u001b[39;00m\n\u001b[0;32m 1025\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m res\n", - "File \u001b[1;32mc:\\Users\\Yinka\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\matplotlib\\figure.py:3343\u001b[0m, in \u001b[0;36mFigure.savefig\u001b[1;34m(self, fname, transparent, **kwargs)\u001b[0m\n\u001b[0;32m 3339\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m ax \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39maxes:\n\u001b[0;32m 3340\u001b[0m stack\u001b[38;5;241m.\u001b[39menter_context(\n\u001b[0;32m 3341\u001b[0m ax\u001b[38;5;241m.\u001b[39mpatch\u001b[38;5;241m.\u001b[39m_cm_set(facecolor\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnone\u001b[39m\u001b[38;5;124m'\u001b[39m, edgecolor\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnone\u001b[39m\u001b[38;5;124m'\u001b[39m))\n\u001b[1;32m-> 3343\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcanvas\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mprint_figure\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfname\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[1;32mc:\\Users\\Yinka\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\matplotlib\\backend_bases.py:2366\u001b[0m, in \u001b[0;36mFigureCanvasBase.print_figure\u001b[1;34m(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)\u001b[0m\n\u001b[0;32m 2362\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m 2363\u001b[0m \u001b[38;5;66;03m# _get_renderer may change the figure dpi (as vector formats\u001b[39;00m\n\u001b[0;32m 2364\u001b[0m \u001b[38;5;66;03m# force the figure dpi to 72), so we need to set it again here.\u001b[39;00m\n\u001b[0;32m 2365\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m cbook\u001b[38;5;241m.\u001b[39m_setattr_cm(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfigure, dpi\u001b[38;5;241m=\u001b[39mdpi):\n\u001b[1;32m-> 2366\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43mprint_method\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 2367\u001b[0m \u001b[43m \u001b[49m\u001b[43mfilename\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 2368\u001b[0m \u001b[43m \u001b[49m\u001b[43mfacecolor\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfacecolor\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 2369\u001b[0m \u001b[43m \u001b[49m\u001b[43medgecolor\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43medgecolor\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 2370\u001b[0m \u001b[43m \u001b[49m\u001b[43morientation\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43morientation\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 2371\u001b[0m \u001b[43m \u001b[49m\u001b[43mbbox_inches_restore\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m_bbox_inches_restore\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 2372\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 2373\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[0;32m 2374\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m bbox_inches \u001b[38;5;129;01mand\u001b[39;00m restore_bbox:\n", - "File \u001b[1;32mc:\\Users\\Yinka\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\matplotlib\\backend_bases.py:2232\u001b[0m, in \u001b[0;36mFigureCanvasBase._switch_canvas_and_return_print_method..\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m 2228\u001b[0m optional_kws \u001b[38;5;241m=\u001b[39m { \u001b[38;5;66;03m# Passed by print_figure for other renderers.\u001b[39;00m\n\u001b[0;32m 2229\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mdpi\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfacecolor\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124medgecolor\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124morientation\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[0;32m 2230\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mbbox_inches_restore\u001b[39m\u001b[38;5;124m\"\u001b[39m}\n\u001b[0;32m 2231\u001b[0m skip \u001b[38;5;241m=\u001b[39m optional_kws \u001b[38;5;241m-\u001b[39m {\u001b[38;5;241m*\u001b[39minspect\u001b[38;5;241m.\u001b[39msignature(meth)\u001b[38;5;241m.\u001b[39mparameters}\n\u001b[1;32m-> 2232\u001b[0m print_method \u001b[38;5;241m=\u001b[39m functools\u001b[38;5;241m.\u001b[39mwraps(meth)(\u001b[38;5;28;01mlambda\u001b[39;00m \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: \u001b[43mmeth\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 2233\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43m{\u001b[49m\u001b[43mk\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mv\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mk\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mv\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mitems\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mk\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mskip\u001b[49m\u001b[43m}\u001b[49m\u001b[43m)\u001b[49m)\n\u001b[0;32m 2234\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m: \u001b[38;5;66;03m# Let third-parties do as they see fit.\u001b[39;00m\n\u001b[0;32m 2235\u001b[0m print_method \u001b[38;5;241m=\u001b[39m meth\n", - "File \u001b[1;32mc:\\Users\\Yinka\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\matplotlib\\backends\\backend_agg.py:509\u001b[0m, in \u001b[0;36mFigureCanvasAgg.print_png\u001b[1;34m(self, filename_or_obj, metadata, pil_kwargs)\u001b[0m\n\u001b[0;32m 462\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mprint_png\u001b[39m(\u001b[38;5;28mself\u001b[39m, filename_or_obj, \u001b[38;5;241m*\u001b[39m, metadata\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, pil_kwargs\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[0;32m 463\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m 464\u001b[0m \u001b[38;5;124;03m Write the figure to a PNG file.\u001b[39;00m\n\u001b[0;32m 465\u001b[0m \n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 507\u001b[0m \u001b[38;5;124;03m *metadata*, including the default 'Software' key.\u001b[39;00m\n\u001b[0;32m 508\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[1;32m--> 509\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_print_pil\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilename_or_obj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mpng\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mpil_kwargs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmetadata\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[1;32mc:\\Users\\Yinka\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\matplotlib\\backends\\backend_agg.py:458\u001b[0m, in \u001b[0;36mFigureCanvasAgg._print_pil\u001b[1;34m(self, filename_or_obj, fmt, pil_kwargs, metadata)\u001b[0m\n\u001b[0;32m 453\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m 454\u001b[0m \u001b[38;5;124;03mDraw the canvas, then save it using `.image.imsave` (to which\u001b[39;00m\n\u001b[0;32m 455\u001b[0m \u001b[38;5;124;03m*pil_kwargs* and *metadata* are forwarded).\u001b[39;00m\n\u001b[0;32m 456\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m 457\u001b[0m FigureCanvasAgg\u001b[38;5;241m.\u001b[39mdraw(\u001b[38;5;28mself\u001b[39m)\n\u001b[1;32m--> 458\u001b[0m \u001b[43mmpl\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mimage\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mimsave\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 459\u001b[0m \u001b[43m \u001b[49m\u001b[43mfilename_or_obj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbuffer_rgba\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mformat\u001b[39;49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfmt\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43morigin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mupper\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[0;32m 460\u001b[0m \u001b[43m \u001b[49m\u001b[43mdpi\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfigure\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdpi\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmetadata\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmetadata\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mpil_kwargs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mpil_kwargs\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[1;32mc:\\Users\\Yinka\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\matplotlib\\image.py:1689\u001b[0m, in \u001b[0;36mimsave\u001b[1;34m(fname, arr, vmin, vmax, cmap, format, origin, dpi, metadata, pil_kwargs)\u001b[0m\n\u001b[0;32m 1687\u001b[0m pil_kwargs\u001b[38;5;241m.\u001b[39msetdefault(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mformat\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;28mformat\u001b[39m)\n\u001b[0;32m 1688\u001b[0m pil_kwargs\u001b[38;5;241m.\u001b[39msetdefault(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mdpi\u001b[39m\u001b[38;5;124m\"\u001b[39m, (dpi, dpi))\n\u001b[1;32m-> 1689\u001b[0m \u001b[43mimage\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msave\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfname\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mpil_kwargs\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[1;32mc:\\Users\\Yinka\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\PIL\\Image.py:2428\u001b[0m, in \u001b[0;36mImage.save\u001b[1;34m(self, fp, format, **params)\u001b[0m\n\u001b[0;32m 2426\u001b[0m fp \u001b[38;5;241m=\u001b[39m builtins\u001b[38;5;241m.\u001b[39mopen(filename, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mr+b\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 2427\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m-> 2428\u001b[0m fp \u001b[38;5;241m=\u001b[39m \u001b[43mbuiltins\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mopen\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilename\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mw+b\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[0;32m 2430\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m 2431\u001b[0m save_handler(\u001b[38;5;28mself\u001b[39m, fp, filename)\n", - "\u001b[1;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: 'path/to/save/directory/precision_recall_curve.png'" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAArMAAAIjCAYAAAAQgZNYAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABwCUlEQVR4nO3deZyNZePH8e/MmA0z9jGWqcmWQhTxSNIyjCWlRUKWFpQ8iVKphAoppEVUslREKZIGaVAqJcLTYssSyVhjMGY99++P63fOzJgzY5Yz58zN5/16ndecuZdzX+dcM3znuq/Fz7IsSwAAAIAN+fu6AAAAAEBhEWYBAABgW4RZAAAA2BZhFgAAALZFmAUAAIBtEWYBAABgW4RZAAAA2BZhFgAAALZFmAUAAIBtEWYBXDD69u2r6OjoAp2zevVq+fn5afXq1cVSJru7/vrrdf3117u+37Nnj/z8/DRr1iyflQnAhYUwC6DYzJo1S35+fq5HSEiI6tWrp0GDBungwYO+Ll6J5wyGzoe/v78qVqyoDh06aO3atb4unkccPHhQjz/+uOrXr6/SpUurTJkyatq0qV588UUdP37c18UDYAOlfF0AAOe/559/XpdccomSk5P13XffaerUqYqLi9Nvv/2m0qVLe60c7777rhwOR4HOue6663TmzBkFBQUVU6nOrXv37urYsaMyMjK0fft2vfXWW7rhhhv0888/q1GjRj4rV1H9/PPP6tixo06dOqV77rlHTZs2lSStX79eL730kr799lt99dVXPi4lgJKOMAug2HXo0EHNmjWTJD3wwAOqVKmSJk2apM8//1zdu3d3e87p06dVpkwZj5YjMDCwwOf4+/srJCTEo+UoqKuuukr33HOP6/vWrVurQ4cOmjp1qt566y0flqzwjh8/rttuu00BAQHauHGj6tevn23/mDFj9O6773rkWsXxswSg5KCbAQCvu/HGGyVJu3fvlmT6spYtW1Y7d+5Ux44dFRYWpp49e0qSHA6HJk+erAYNGigkJERVq1bVgAED9O+//+Z43aVLl6pNmzYKCwtTeHi4rr76as2dO9e1312f2Xnz5qlp06aucxo1aqTXXnvNtT+3PrOffPKJmjZtqtDQUFWuXFn33HOP9u/fn+0Y5/vav3+/unTporJly6pKlSp6/PHHlZGRUejPr3Xr1pKknTt3Ztt+/PhxPfroo4qKilJwcLDq1Kmj8ePH52iNdjgceu2119SoUSOFhISoSpUqat++vdavX+86ZubMmbrxxhsVERGh4OBgXX755Zo6dWqhy3y2t99+W/v379ekSZNyBFlJqlq1qp599lnX935+fho1alSO46Kjo9W3b1/X986uLd98840GDhyoiIgI1axZUwsWLHBtd1cWPz8//fbbb65tW7du1Z133qmKFSsqJCREzZo10+LFi4v2pgEUC1pmAXidM4RVqlTJtS09PV2xsbG69tprNWHCBFf3gwEDBmjWrFm699579cgjj2j37t168803tXHjRn3//feu1tZZs2bpvvvuU4MGDTR8+HCVL19eGzdu1LJly9SjRw+35VixYoW6d++um266SePHj5ckbdmyRd9//70GDx6ca/md5bn66qs1btw4HTx4UK+99pq+//57bdy4UeXLl3cdm5GRodjYWLVo0UITJkzQ119/rYkTJ6p27dp66KGHCvX57dmzR5JUoUIF17akpCS1adNG+/fv14ABA3TRRRfphx9+0PDhw3XgwAFNnjzZdez999+vWbNmqUOHDnrggQeUnp6uNWvW6Mcff3S1oE+dOlUNGjTQLbfcolKlSumLL77QwIED5XA49PDDDxeq3FktXrxYoaGhuvPOO4v8Wu4MHDhQVapU0XPPPafTp0+rU6dOKlu2rD7++GO1adMm27Hz589XgwYN1LBhQ0nS77//rlatWqlGjRp66qmnVKZMGX388cfq0qWLPv30U912223FUmYAhWQBQDGZOXOmJcn6+uuvrcOHD1v79u2z5s2bZ1WqVMkKDQ21/v77b8uyLKtPnz6WJOupp57Kdv6aNWssSdacOXOybV+2bFm27cePH7fCwsKsFi1aWGfOnMl2rMPhcD3v06ePdfHFF7u+Hzx4sBUeHm6lp6fn+h5WrVplSbJWrVplWZZlpaamWhEREVbDhg2zXWvJkiWWJOu5557Ldj1J1vPPP5/tNa+88kqradOmuV7Taffu3ZYka/To0dbhw4ethIQEa82aNdbVV19tSbI++eQT17EvvPCCVaZMGWv79u3ZXuOpp56yAgICrL1791qWZVkrV660JFmPPPJIjutl/aySkpJy7I+NjbVq1aqVbVubNm2sNm3a5CjzzJkz83xvFSpUsBo3bpznMVlJskaOHJlj+8UXX2z16dPH9b3zZ+7aa6/NUa/du3e3IiIism0/cOCA5e/vn62ObrrpJqtRo0ZWcnKya5vD4bCuueYaq27duvkuMwDvoJsBgGIXExOjKlWqKCoqSnfffbfKli2rhQsXqkaNGtmOO7ul8pNPPlG5cuXUtm1bHTlyxPVo2rSpypYtq1WrVkkyLawnT57UU089laN/q5+fX67lKl++vE6fPq0VK1bk+72sX79ehw4d0sCBA7Ndq1OnTqpfv76+/PLLHOc8+OCD2b5v3bq1du3ale9rjhw5UlWqVFFkZKRat26tLVu2aOLEidlaNT/55BO1bt1aFSpUyPZZxcTEKCMjQ99++60k6dNPP5Wfn59GjhyZ4zpZP6vQ0FDX8xMnTujIkSNq06aNdu3apRMnTuS77LlJTExUWFhYkV8nN/369VNAQEC2bd26ddOhQ4eydRlZsGCBHA6HunXrJkk6duyYVq5cqbvuuksnT550fY5Hjx5VbGysduzYkaM7CQDfopsBgGI3ZcoU1atXT6VKlVLVqlV16aWXyt8/+9/SpUqVUs2aNbNt27Fjh06cOKGIiAi3r3vo0CFJmd0WnLeJ82vgwIH6+OOP1aFDB9WoUUPt2rXTXXfdpfbt2+d6zl9//SVJuvTSS3Psq1+/vr777rts25x9UrOqUKFCtj6/hw8fztaHtmzZsipbtqzr+/79+6tr165KTk7WypUr9frrr+foc7tjxw7973//y3Etp6yfVfXq1VWxYsVc36Mkff/99xo5cqTWrl2rpKSkbPtOnDihcuXK5Xn+uYSHh+vkyZNFeo28XHLJJTm2tW/fXuXKldP8+fN10003STJdDJo0aaJ69epJkv78809ZlqURI0ZoxIgRbl/70KFDOf4QA+A7hFkAxa558+auvpi5CQ4OzhFwHQ6HIiIiNGfOHLfn5Bbc8isiIkKbNm3S8uXLtXTpUi1dulQzZ85U7969NXv27CK9ttPZrYPuXH311a6QLJmW2KyDnerWrauYmBhJ0s0336yAgAA99dRTuuGGG1yfq8PhUNu2bfXEE0+4vYYzrOXHzp07ddNNN6l+/fqaNGmSoqKiFBQUpLi4OL366qsFnt7Mnfr162vTpk1KTU0t0rRnuQ2ky9qy7BQcHKwuXbpo4cKFeuutt3Tw4EF9//33Gjt2rOsY53t7/PHHFRsb6/a169SpU+jyAvA8wiyAEqt27dr6+uuv1apVK7fhJOtxkvTbb78VOGgEBQWpc+fO6ty5sxwOhwYOHKi3335bI0aMcPtaF198sSRp27ZtrlkZnLZt2+baXxBz5szRmTNnXN/XqlUrz+OfeeYZvfvuu3r22We1bNkySeYzOHXqlCv05qZ27dpavny5jh07lmvr7BdffKGUlBQtXrxYF110kWu7s1uHJ3Tu3Flr167Vp59+muv0bFlVqFAhxyIKqampOnDgQIGu261bN82ePVvx8fHasmWLLMtydTGQMj/7wMDAc36WAEoG+swCKLHuuusuZWRk6IUXXsixLz093RVu2rVrp7CwMI0bN07JycnZjrMsK9fXP3r0aLbv/f39dcUVV0iSUlJS3J7TrFkzRUREaNq0admOWbp0qbZs2aJOnTrl671l1apVK8XExLge5wqz5cuX14ABA7R8+XJt2rRJkvms1q5dq+XLl+c4/vjx40pPT5ck3XHHHbIsS6NHj85xnPOzcrYmZ/3sTpw4oZkzZxb4veXmwQcfVLVq1fTYY49p+/btOfYfOnRIL774ouv72rVru/r9Or3zzjsFnuIsJiZGFStW1Pz58zV//nw1b948W5eEiIgIXX/99Xr77bfdBuXDhw8X6HoAih8tswBKrDZt2mjAgAEaN26cNm3apHbt2ikwMFA7duzQJ598otdee0133nmnwsPD9eqrr+qBBx7Q1VdfrR49eqhChQravHmzkpKScu0y8MADD+jYsWO68cYbVbNmTf31119644031KRJE1122WVuzwkMDNT48eN17733qk2bNurevbtraq7o6GgNGTKkOD8Sl8GDB2vy5Ml66aWXNG/ePA0bNkyLFy/WzTffrL59+6pp06Y6ffq0fv31Vy1YsEB79uxR5cqVdcMNN6hXr156/fXXtWPHDrVv314Oh0Nr1qzRDTfcoEGDBqldu3auFusBAwbo1KlTevfddxUREVHgltDcVKhQQQsXLlTHjh3VpEmTbCuA/fLLL/roo4/UsmVL1/EPPPCAHnzwQd1xxx1q27atNm/erOXLl6ty5coFum5gYKBuv/12zZs3T6dPn9aECRNyHDNlyhRde+21atSokfr166datWrp4MGDWrt2rf7++29t3ry5aG8egGf5cioFAOc35zRJP//8c57H9enTxypTpkyu+9955x2radOmVmhoqBUWFmY1atTIeuKJJ6x//vkn23GLFy+2rrnmGis0NNQKDw+3mjdvbn300UfZrpN1aq4FCxZY7dq1syIiIqygoCDroosusgYMGGAdOHDAdczZU3M5zZ8/37ryyiut4OBgq2LFilbPnj1dU42d632NHDnSys8/v85prl555RW3+/v27WsFBARYf/75p2VZlnXy5Elr+PDhVp06daygoCCrcuXK1jXXXGNNmDDBSk1NdZ2Xnp5uvfLKK1b9+vWtoKAgq0qVKlaHDh2sDRs2ZPssr7jiCiskJMSKjo62xo8fb82YMcOSZO3evdt1XGGn5nL6559/rCFDhlj16tWzQkJCrNKlS1tNmza1xowZY504ccJ1XEZGhvXkk09alStXtkqXLm3FxsZaf/75Z65Tc+X1M7dixQpLkuXn52ft27fP7TE7d+60evfubUVGRlqBgYFWjRo1rJtvvtlasGBBvt4XAO/xs6w87sEBAAAAJRh9ZgEAAGBbhFkAAADYFmEWAAAAtkWYBQAAgG0RZgEAAGBbhFkAAADY1gW3aILD4dA///yjsLAw+fn5+bo4AAAAOItlWTp58qSqV68uf/+8214vuDD7zz//KCoqytfFAAAAwDns27dPNWvWzPOYCy7MhoWFSTIfTnh4eLFfLy0tTV999ZVrGU7YD3Vof9Sh/VGH9kb92Z+36zAxMVFRUVGu3JaXCy7MOrsWhIeHey3Mli5dWuHh4fwC2xR1aH/Uof1Rh/ZG/dmfr+owP11CGQAGAAAA2yLMAgAAwLYIswAAALCtC67PLABcKCzLUnp6ujIyMnxdlCJLS0tTqVKllJycfF68nwsN9Wd/xVGHgYGBCggIKPLrEGYB4DyUmpqqAwcOKCkpyddF8QjLshQZGal9+/YxR7gNUX/2Vxx16Ofnp5o1a6ps2bJFeh3CLACcZxwOh3bv3q2AgABVr15dQUFBtg8QDodDp06dUtmyZc85gTpKHurP/jxdh5Zl6fDhw/r7779Vt27dIrXQEmYB4DyTmpoqh8OhqKgolS5d2tfF8QiHw6HU1FSFhIQQhmyI+rO/4qjDKlWqaM+ePUpLSytSmOUnCgDOU4QGACWZp+4Y8S8dAAAAbIswCwAAANsizAIALmh+fn5atGiRx4+1u9WrV8vPz0/Hjx+XJM2aNUvly5f3aZmKy9GjRxUREaE9e/b4uijnjbvvvlsTJ070yrUIswCAEqFv377y8/OTn5+fgoKCVKdOHT3//PNKT08v1useOHBAHTp08PixRREdHe36LEqXLq1GjRpp+vTpxX5dT1i1apU6duyoSpUqqXTp0rr88sv1+OOP659//vF10XI1ZswY3XrrrYqOjs6xLzY2VgEBAfr5559z7Lv++uv16KOP5tjuLvgnJibqmWeeUf369RUSEqLIyEjFxMTos88+k2VZHnonOa1evVpXXXWVgoODVadOHc2aNSvf5/75558KCwvL8V5+//133XHHHa6f08mTJ+c499lnn9WYMWN04sSJor2BfCDMAgBKjPbt2+vAgQPasWOHHnvsMY0aNUqvvPKK22NTU1M9cs3IyEgFBwd7/Niiev7553XgwAH99ttvuueee9SvXz8tXbrUK9curLffflsxMTGKjIzUp59+qj/++EPTpk3TiRMnNGXKlEK/rqfq2p2kpCS99957uv/++3Ps27t3r3744QcNGjRIM2bMKPQ1jh8/rmuuuUbvv/++hg8frl9++UXffvutunXrpieeeKLYAt/u3bvVqVMn3XDDDdq0aZMeffRRPfDAA1q+fPk5z01LS1P37t3VunXrHPuSkpJUq1YtvfTSS4qMjHR7fsOGDVW7dm19+OGHRX4f50KYBYALgGVJp097/1HQBqfg4GBFRkbq4osv1kMPPaSYmBgtXrxYkjRw4EDddtttGjNmjKpXr65LL71UkrRv3z7dddddKl++vCpWrKhbb701x+3iGTNmqEGDBgoODla1atU0aNAg176sXQdSU1M1aNAgVatWTSEhIbr44os1btw4t8dK0q+//qobb7xRoaGhqlSpkvr3769Tp0659vft21ddunTRhAkTVK1aNVWqVEkPP/yw0tLSzvlZhIWFKTIyUrVq1dKTTz6pihUrasWKFa79x48f1wMPPKAqVaooPDxcN954ozZv3pztNb744gtdffXVCgkJUeXKlXXbbbe59n3wwQdq1qyZ6zo9evTQoUOHzlmu3Pz999965JFH9Mgjj2jGjBm6/vrrFR0dreuuu07vvvuunnjiCUnSqFGj1KRJk2znTp48OVurqPNzy1rXTz/9tFq0aJHjuo0bN9bzzz/v+n769Om67LLLFBISovr16+utt97Ks9xxcXEKDg7Wf/7znxz7Zs6cqZtvvlkPPfSQPvroI505c6YAn0imp59+Wnv27NFPP/2kPn366PLLL1e9evXUr18/bdq0qciLBuRm2rRpuuSSSzRx4kRddtllGjRokO688069+uqr5zz32WefVf369XXXXXfl2Hf11VfrlVde0d13353nH3edO3fWvHnzivQe8sOnYfbbb79V586dVb169Xz3QypKczkAXKiSkqSyZb3/KOoCZKGhodla5VauXKlt27ZpxYoVWrJkidLS0hQbG6uwsDCtWbNG33//vcqWLav27du7zps6daoefvhh9e/fX7/++qsWL16sOnXquL3e66+/rsWLF+vjjz/Wtm3bNGfOHLe3niXp9OnTio2NVYUKFfTzzz/rk08+0ddff50tKEvmtvvOnTu1atUqzZ49W7NmzSrQ/10Oh0Offvqp/v33XwUFBbm2d+3aVYcOHdLSpUu1YcMGXXXVVbrpppt07NgxSdKXX36p2267TR07dtTGjRsVHx+v5s2bu85PS0vTCy+8oM2bN2vRokXas2eP+vbtm+9yne2TTz5RamqqK7SerVy5cgV6vfj4+Gx13bNnT61bt047d+50HfP777/rf//7n3r06CFJmjNnjp577jmNGTNGW7Zs0dixYzVixAjNnj071+usWbNGTZs2zbHdsizNnDlT99xzj+rXr686depowYIFBXoPkqm/efPmqWfPnqpevXqO/WXLllWpUu6n/V+zZo3Kli2b52POnDm5Xnvt2rWKiYnJti02NlZr167Ns8wrV67UJ598UqTWdElq3ry51q1bp5SUlCK9zrn4dNGE06dPq3Hjxrrvvvt0++23n/N4Z3P5gw8+qDlz5ig+Pl4PPPCAqlWrptjYWC+UGADgDZZlKT4+XsuXL9d///tf1/YyZcpo+vTprlD34YcfyuFwaPr06a45K2fOnKny5ctr9erVateunV588UU99thjGjx4sOt1rr76arfX3bt3r+rWratrr71Wfn5+uvjii3Mt49y5c5WcnKz3339fZcqUkSS9+eab6ty5s8aPH6+qVatKkipUqKA333xTAQEBql+/vjp16qT4+Hj169cvz8/gySef1LPPPquUlBSlp6erYsWKeuCBByRJ3333ndatW6dDhw65WsYmTJigRYsWacGCBerfv7/GjBmju+++W6NHj3a9ZuPGjV3P77vvPtfzWrVq6fXXX9fVV1/tWuWpoHbs2KHw8HBVq1atwOe6c3ZdS6b8c+fO1YgR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V1xcnCIjIxUVFeVq5XUee//992v8+PF65JFHNHLkSB0+fFjDhw/XPffc47o17O49BQUF5fo+nfz9/XME9Mcff1xXXHGFtm/frltuuUUtW7ZU79699dJLL6levXr6559/FBcXpy5duqhZs2YaPXq02rZtq/r166tbt25KT0/X0qVL9cQTT+iyyy5TUFCQZs+erQEDBui3337TpEmTJJnW7/DwcFeDT1hYmMLDwxUSEiI/P79cy3355Zdr0qRJ+u9//6vk5GT16tVL0dHR+vvvv/XBBx8oKChIr732mtq3b69hw4bp7bff1h133KHly5crPj5e4eHhrtfO62ehV69eGj16tFJTUzVx4sRsx4waNUqPPvqoIiIiFBsbq5SUFK1fv17Hjx/XkCFD3Jb7lltu0fPPP6+MjAzXHyxz585V165dc8xwcNlll+n555/XDz/8oE6dOmnw4MGaPn26RowYofvvv1/BwcGKi4vTp59+qs8//9xVtpdffllr165Vu3bt9MILL6hZs2YKDAzUmjVrNH78eP30009u32t4eHiefWLP5ZFHHtH06dM1ZswY3XvvvVq1apUWLVqkL774wnW9KVOmaNGiRa4/9s/uT75161b5+/urRYsWOnnypMLCwpSWlqY//vhDkvkD8+jRo9q1a5fKli2brVvE+vXrFRsbm+vPTHJyskJDQ3Xdddfl+Lcqt7DtllVCSLIWLlyY5zGtW7e2Bg8enG3bjBkzrPDw8Hxf58SJE5Yk68SJE4UoZcE9+GC6JVnWs8+me+V68LzU1FRr0aJFVmpqqq+LgkK60OrwzJkz1h9//GGdOXPG10UpkD59+li33nqr230ZGRlW9+7drVtuuSXHvgMHDli9e/e2KleubAUHB1u1atWy+vXrl+3f+WnTplmXXnqpFRgYaFWrVs3673//69qX9f+fd955x2rSpIlVpkwZKzw83LrpppusX375xe2xlmVZ//vf/6wbbrjBCgkJsSpWrGj169fPOnnyZJ7vafDgwVabNm3y/Cwuvvhi69VXX82xPTY21urQoYNlWZaVmJho/fe//7WqV69uBQYGWlFRUVbPnj2tvXv3uo7/9NNPrSZNmlhBQUFW5cqVrdtvv921b+7cuVZ0dLQVHBxstWzZ0lq8eLElydq4caNlWZa1atUqS5L177//WpZlWTNnzrTKlSuXZ7kty7JW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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "import tensorflow as tf\n", - "from sklearn.metrics import auc, precision_recall_curve\n", - "from sklearn.model_selection import train_test_split\n", - "from tensorflow.keras.layers import Dense\n", - "from tensorflow.keras.models import Sequential\n", - "\n", - "\n", - "def plot_precision_recall_curve(model, X_test, y_test, save_path=None):\n", - " \"\"\"\n", - " Generate and plot the precision-recall curve for a TensorFlow model.\n", - "\n", - " Parameters:\n", - " - model: The trained TensorFlow model.\n", - " - X_test: Test features.\n", - " - y_test: True labels for the test set.\n", - " - save_path: Optional path to save the plot as an image.\n", - "\n", - " Returns:\n", - " - None\n", - " \"\"\"\n", - " # Make predictions on the test set\n", - " y_pred = model.predict(X_test)\n", - "\n", - " # For binary classification, flatten the true labels\n", - " y_test = np.reshape(y_test, (-1,))\n", - " y_pred = np.reshape(y_pred, (-1,))\n", - "\n", - " # Calculate precision and recall\n", - " precision, recall, thresholds = precision_recall_curve(y_test, y_pred)\n", - "\n", - " # Calculate the area under the precision-recall curve\n", - " auc_score = auc(recall, precision)\n", - "\n", - " # Plot the precision-recall curve\n", - " plt.figure(figsize=(8, 6))\n", - " plt.plot(recall, precision, label=f'Precision-Recall Curve (AUC = {auc_score:.2f})', color='b')\n", - " plt.xlabel('Recall')\n", - " plt.ylabel('Precision')\n", - " plt.title('Precision-Recall Curve')\n", - " plt.legend(loc='upper right')\n", - " plt.grid(True)\n", - "\n", - " # Save the plot if save_path is provided\n", - " if save_path:\n", - " plt.savefig(save_path)\n", - " print(f\"Precision-Recall Curve saved at: {save_path}\")\n", - " else:\n", - " plt.show()\n", - "\n", - "# Generate some example data (replace this with your actual data)\n", - "X, y = np.random.rand(1000, 10), np.random.randint(2, size=(1000,))\n", - "\n", - "# Split the data into training and testing sets\n", - "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n", - "\n", - "# Build a simple binary classification model (replace this with your actual model)\n", - "model = Sequential([\n", - " Dense(64, activation='relu', input_shape=(10,)),\n", - " Dense(1, activation='sigmoid')\n", - "])\n", - "\n", - "model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])\n", - "\n", - "# Train the model (replace this with your actual training process)\n", - "model.fit(X_train, y_train, epochs=10, batch_size=32, validation_data=(X_test, y_test))\n", - "\n", - "# Specify the directory to save the plot\n", - "save_directory = \"path/to/save/directory\"\n", - "\n", - "# Call the function to generate and save the precision-recall curve\n", - "plot_precision_recall_curve(model, X_test, y_test, save_path=f\"{save_directory}/precision_recall_curve.png\")" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Epoch 1/10\n", - "25/25 [==============================] - 3s 38ms/step - loss: 0.7014 - accuracy: 0.4863 - val_loss: 0.7011 - val_accuracy: 0.4850\n", - "Epoch 2/10\n", - "25/25 [==============================] - 0s 10ms/step - loss: 0.6968 - accuracy: 0.4888 - val_loss: 0.6990 - val_accuracy: 0.4650\n", - "Epoch 3/10\n", - "25/25 [==============================] - 0s 9ms/step - loss: 0.6937 - accuracy: 0.5188 - val_loss: 0.6979 - val_accuracy: 0.4550\n", - "Epoch 4/10\n", - "25/25 [==============================] - 0s 9ms/step - loss: 0.6936 - accuracy: 0.4975 - val_loss: 0.6979 - val_accuracy: 0.4750\n", - "Epoch 5/10\n", - "25/25 [==============================] - 0s 9ms/step - loss: 0.6907 - accuracy: 0.5250 - val_loss: 0.6979 - val_accuracy: 0.4950\n", - "Epoch 6/10\n", - "25/25 [==============================] - 0s 9ms/step - loss: 0.6892 - accuracy: 0.5337 - val_loss: 0.7022 - val_accuracy: 0.4900\n", - "Epoch 7/10\n", - "25/25 [==============================] - 0s 9ms/step - loss: 0.6874 - accuracy: 0.5375 - val_loss: 0.6984 - val_accuracy: 0.4850\n", - "Epoch 8/10\n", - "25/25 [==============================] - 0s 9ms/step - loss: 0.6860 - accuracy: 0.5512 - val_loss: 0.7012 - val_accuracy: 0.4750\n", - "Epoch 9/10\n", - "25/25 [==============================] - 0s 8ms/step - loss: 0.6863 - accuracy: 0.5462 - val_loss: 0.7008 - val_accuracy: 0.4850\n", - "Epoch 10/10\n", - "25/25 [==============================] - 0s 7ms/step - loss: 0.6846 - accuracy: 0.5362 - val_loss: 0.7001 - val_accuracy: 0.4900\n", - "7/7 [==============================] - 0s 3ms/step\n" - ] - }, - { - "ename": "FileNotFoundError", - "evalue": "[Errno 2] No such file or directory: 'path/to/save/directory/precision_recall_curve_with_thresholds.png'", - "output_type": "error", - "traceback": [ - "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mFileNotFoundError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[1;32mIn[3], line 76\u001b[0m\n\u001b[0;32m 73\u001b[0m save_directory \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mpath/to/save/directory\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 75\u001b[0m \u001b[38;5;66;03m# Call the function to generate and save the precision-recall curve with threshold points\u001b[39;00m\n\u001b[1;32m---> 76\u001b[0m \u001b[43mplot_precision_recall_curve\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmodel\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mX_test\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43my_test\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msave_path\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43mf\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;132;43;01m{\u001b[39;49;00m\u001b[43msave_directory\u001b[49m\u001b[38;5;132;43;01m}\u001b[39;49;00m\u001b[38;5;124;43m/precision_recall_curve_with_thresholds.png\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", - "Cell \u001b[1;32mIn[3], line 52\u001b[0m, in \u001b[0;36mplot_precision_recall_curve\u001b[1;34m(model, X_test, y_test, save_path)\u001b[0m\n\u001b[0;32m 50\u001b[0m \u001b[38;5;66;03m# Save the plot if save_path is provided\u001b[39;00m\n\u001b[0;32m 51\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m save_path:\n\u001b[1;32m---> 52\u001b[0m \u001b[43mplt\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msavefig\u001b[49m\u001b[43m(\u001b[49m\u001b[43msave_path\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 53\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mPrecision-Recall Curve saved at: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00msave_path\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 54\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n", - "File \u001b[1;32mc:\\Users\\Yinka\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\matplotlib\\pyplot.py:1023\u001b[0m, in \u001b[0;36msavefig\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m 1020\u001b[0m \u001b[38;5;129m@_copy_docstring_and_deprecators\u001b[39m(Figure\u001b[38;5;241m.\u001b[39msavefig)\n\u001b[0;32m 1021\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21msavefig\u001b[39m(\u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[0;32m 1022\u001b[0m fig \u001b[38;5;241m=\u001b[39m gcf()\n\u001b[1;32m-> 1023\u001b[0m res \u001b[38;5;241m=\u001b[39m \u001b[43mfig\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msavefig\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 1024\u001b[0m fig\u001b[38;5;241m.\u001b[39mcanvas\u001b[38;5;241m.\u001b[39mdraw_idle() \u001b[38;5;66;03m# Need this if 'transparent=True', to reset colors.\u001b[39;00m\n\u001b[0;32m 1025\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m res\n", - "File \u001b[1;32mc:\\Users\\Yinka\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\matplotlib\\figure.py:3343\u001b[0m, in \u001b[0;36mFigure.savefig\u001b[1;34m(self, fname, transparent, **kwargs)\u001b[0m\n\u001b[0;32m 3339\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m ax \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39maxes:\n\u001b[0;32m 3340\u001b[0m stack\u001b[38;5;241m.\u001b[39menter_context(\n\u001b[0;32m 3341\u001b[0m ax\u001b[38;5;241m.\u001b[39mpatch\u001b[38;5;241m.\u001b[39m_cm_set(facecolor\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnone\u001b[39m\u001b[38;5;124m'\u001b[39m, edgecolor\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mnone\u001b[39m\u001b[38;5;124m'\u001b[39m))\n\u001b[1;32m-> 3343\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcanvas\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mprint_figure\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfname\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[1;32mc:\\Users\\Yinka\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\matplotlib\\backend_bases.py:2366\u001b[0m, in \u001b[0;36mFigureCanvasBase.print_figure\u001b[1;34m(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)\u001b[0m\n\u001b[0;32m 2362\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m 2363\u001b[0m \u001b[38;5;66;03m# _get_renderer may change the figure dpi (as vector formats\u001b[39;00m\n\u001b[0;32m 2364\u001b[0m \u001b[38;5;66;03m# force the figure dpi to 72), so we need to set it again here.\u001b[39;00m\n\u001b[0;32m 2365\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m cbook\u001b[38;5;241m.\u001b[39m_setattr_cm(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfigure, dpi\u001b[38;5;241m=\u001b[39mdpi):\n\u001b[1;32m-> 2366\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43mprint_method\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 2367\u001b[0m \u001b[43m \u001b[49m\u001b[43mfilename\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 2368\u001b[0m \u001b[43m \u001b[49m\u001b[43mfacecolor\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfacecolor\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 2369\u001b[0m \u001b[43m \u001b[49m\u001b[43medgecolor\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43medgecolor\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 2370\u001b[0m \u001b[43m \u001b[49m\u001b[43morientation\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43morientation\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 2371\u001b[0m \u001b[43m \u001b[49m\u001b[43mbbox_inches_restore\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m_bbox_inches_restore\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 2372\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 2373\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[0;32m 2374\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m bbox_inches \u001b[38;5;129;01mand\u001b[39;00m restore_bbox:\n", - "File \u001b[1;32mc:\\Users\\Yinka\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\matplotlib\\backend_bases.py:2232\u001b[0m, in \u001b[0;36mFigureCanvasBase._switch_canvas_and_return_print_method..\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m 2228\u001b[0m optional_kws \u001b[38;5;241m=\u001b[39m { \u001b[38;5;66;03m# Passed by print_figure for other renderers.\u001b[39;00m\n\u001b[0;32m 2229\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mdpi\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfacecolor\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124medgecolor\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124morientation\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[0;32m 2230\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mbbox_inches_restore\u001b[39m\u001b[38;5;124m\"\u001b[39m}\n\u001b[0;32m 2231\u001b[0m skip \u001b[38;5;241m=\u001b[39m optional_kws \u001b[38;5;241m-\u001b[39m {\u001b[38;5;241m*\u001b[39minspect\u001b[38;5;241m.\u001b[39msignature(meth)\u001b[38;5;241m.\u001b[39mparameters}\n\u001b[1;32m-> 2232\u001b[0m print_method \u001b[38;5;241m=\u001b[39m functools\u001b[38;5;241m.\u001b[39mwraps(meth)(\u001b[38;5;28;01mlambda\u001b[39;00m \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: \u001b[43mmeth\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 2233\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43m{\u001b[49m\u001b[43mk\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mv\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mk\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mv\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mkwargs\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mitems\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mk\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mskip\u001b[49m\u001b[43m}\u001b[49m\u001b[43m)\u001b[49m)\n\u001b[0;32m 2234\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m: \u001b[38;5;66;03m# Let third-parties do as they see fit.\u001b[39;00m\n\u001b[0;32m 2235\u001b[0m print_method \u001b[38;5;241m=\u001b[39m meth\n", - "File \u001b[1;32mc:\\Users\\Yinka\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\matplotlib\\backends\\backend_agg.py:509\u001b[0m, in \u001b[0;36mFigureCanvasAgg.print_png\u001b[1;34m(self, filename_or_obj, metadata, pil_kwargs)\u001b[0m\n\u001b[0;32m 462\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mprint_png\u001b[39m(\u001b[38;5;28mself\u001b[39m, filename_or_obj, \u001b[38;5;241m*\u001b[39m, metadata\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m, pil_kwargs\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[0;32m 463\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m 464\u001b[0m \u001b[38;5;124;03m Write the figure to a PNG file.\u001b[39;00m\n\u001b[0;32m 465\u001b[0m \n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 507\u001b[0m \u001b[38;5;124;03m *metadata*, including the default 'Software' key.\u001b[39;00m\n\u001b[0;32m 508\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[1;32m--> 509\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_print_pil\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilename_or_obj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mpng\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mpil_kwargs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmetadata\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[1;32mc:\\Users\\Yinka\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\matplotlib\\backends\\backend_agg.py:458\u001b[0m, in \u001b[0;36mFigureCanvasAgg._print_pil\u001b[1;34m(self, filename_or_obj, fmt, pil_kwargs, metadata)\u001b[0m\n\u001b[0;32m 453\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m 454\u001b[0m \u001b[38;5;124;03mDraw the canvas, then save it using `.image.imsave` (to which\u001b[39;00m\n\u001b[0;32m 455\u001b[0m \u001b[38;5;124;03m*pil_kwargs* and *metadata* are forwarded).\u001b[39;00m\n\u001b[0;32m 456\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m 457\u001b[0m FigureCanvasAgg\u001b[38;5;241m.\u001b[39mdraw(\u001b[38;5;28mself\u001b[39m)\n\u001b[1;32m--> 458\u001b[0m \u001b[43mmpl\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mimage\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mimsave\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 459\u001b[0m \u001b[43m \u001b[49m\u001b[43mfilename_or_obj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbuffer_rgba\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mformat\u001b[39;49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfmt\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43morigin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mupper\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[0;32m 460\u001b[0m \u001b[43m \u001b[49m\u001b[43mdpi\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfigure\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdpi\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmetadata\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmetadata\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mpil_kwargs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mpil_kwargs\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[1;32mc:\\Users\\Yinka\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\matplotlib\\image.py:1689\u001b[0m, in \u001b[0;36mimsave\u001b[1;34m(fname, arr, vmin, vmax, cmap, format, origin, dpi, metadata, pil_kwargs)\u001b[0m\n\u001b[0;32m 1687\u001b[0m pil_kwargs\u001b[38;5;241m.\u001b[39msetdefault(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mformat\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;28mformat\u001b[39m)\n\u001b[0;32m 1688\u001b[0m pil_kwargs\u001b[38;5;241m.\u001b[39msetdefault(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mdpi\u001b[39m\u001b[38;5;124m\"\u001b[39m, (dpi, dpi))\n\u001b[1;32m-> 1689\u001b[0m \u001b[43mimage\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msave\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfname\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mpil_kwargs\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[1;32mc:\\Users\\Yinka\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\PIL\\Image.py:2428\u001b[0m, in \u001b[0;36mImage.save\u001b[1;34m(self, fp, format, **params)\u001b[0m\n\u001b[0;32m 2426\u001b[0m fp \u001b[38;5;241m=\u001b[39m builtins\u001b[38;5;241m.\u001b[39mopen(filename, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mr+b\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 2427\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m-> 2428\u001b[0m fp \u001b[38;5;241m=\u001b[39m \u001b[43mbuiltins\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mopen\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfilename\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mw+b\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[0;32m 2430\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m 2431\u001b[0m save_handler(\u001b[38;5;28mself\u001b[39m, fp, filename)\n", - "\u001b[1;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: 'path/to/save/directory/precision_recall_curve_with_thresholds.png'" - ] - }, - { - "data": { - "image/png": "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/8Lb29tokCUiIiKi/M2q48yOHj0a3333HVavXo3z589j6NChePHihX50gz59+hhcIDZ06FA8efIEI0aMwL///ovt27dj+vTpGD58uLUOgYiIiIisyKp9Zrt3746HDx9i0qRJiIqKgr+/P3bu3Km/KOzmzZuwsUnN2z4+Pti1axdGjRqFWrVqoVSpUhgxYgTGjh1rrUMgIiIiIiuy+gVgISEhCAkJMTpv//79GaY1bNgQf/zxRx6XioiIiIiUwOq3syUiIiIiMhXDLBEREREpFsMsERERESkWwywRERERKRbDLBEREREpFsMsERERESkWwywRERERKRbDLBEREREplkk3TUhJScGqVasQERGBBw8eQKvVGszfu3evWQpHRERERJQVk8LsiBEjsGrVKrRv3x41atSASqUyd7mIiIiIiF7KpDC7bt06/PzzzwgODjZ3eYiIiIiIss2kPrP29vaoWLGiuctCRERERJQjJoXZjz76CAsWLIAQwtzlISIiIiLKNpO6GRw6dAj79u3Djh07UL16ddjZ2RnM37x5s1kKR0RERESUFZPCrLu7Ozp37mzushARERER5YhJYXblypXmLgcRERERUY6ZFGZ1Hj58iIsXLwIAqlSpghIlSpilUERERERE2WHSBWAvXrzAe++9B29vbzRr1gzNmjVDyZIlMWDAAMTFxZm7jERERERERpkUZkePHo3ff/8dv/76K6KjoxEdHY1ffvkFv//+Oz766CNzl5GIiIiIyCiTuhls2rQJGzduRIsWLfTTgoOD4eTkhLfffhtLliwxV/mIiIiIiDJlUstsXFwcPD09M0z38PBgNwMiIiIishiTwmzDhg0RGhqKhIQE/bT4+HhMmTIFDRs2NFvhiIiIiIiyYlI3gwULFiAoKAilS5dG7dq1AQBnzpyBo6Mjdu3aZdYCEhERERFlxqQwW6NGDVy6dAlr1qzBhQsXAAA9evRAr1694OTkZNYCEhERERFlxuRxZp2dnTFo0CBzloWIiIiIKEeyHWa3bduGdu3awc7ODtu2bcty2TfeeCPXBSMiIiIieplsh9lOnTohKioKHh4e6NSpU6bLqVQqpKSkmKNsRERERERZynaY1Wq1Rn8nIiIiIrIWk4bmMiY6OtpcmyIiIiIiyhaTwuysWbOwfv16/fNu3bqhaNGiKFWqFM6cOWO2whERERERZcWkMLt06VL4+PgAAMLDw7Fnzx7s3LkT7dq1w5gxY8xaQCIiIiKizJg0NFdUVJQ+zP722294++230aZNG/j6+qJBgwZmLSARERERUWZMapktUqQIbt26BQDYuXMnAgMDAQBCCI5kQEREREQWY1LLbJcuXdCzZ09UqlQJjx8/Rrt27QAAp0+fRsWKFc1awPwqKQk4dQqoVw9Qq61dGiIiIiJlMqll9quvvkJISAiqVauG8PBwuLi4AADu3buHYcOGmbWA+dXs2UDDhsDKldYuCREREZFymdQya2dnh48//jjD9FGjRuW6QAXFtWvy55071i0HERERkZLxdrZWkpho7RIQERERKR9vZ2slDLNEREREuZftPrNarRYeHh763zN7MMhmT0KCtUtARC+TmAjcvZtxelQUcOxY9raRkgL8+iswfz7/iSUiygtmu50t5Qz/qBG9uh4+BD7/HChTBihdOjW4Hj8OvPuunN6gAbBvX+bbePAAmDEDKF8eeOMNYNQoIDzcMuUnIipITLoA7MMPP0TFihXx4YcfGkz/5ptvcPnyZcyfP98cZcvXGGaJXj0XLwJffQWsXm149mTJEmDkSODoUcPl7983fC4E8McfwKJFwIYNcgi+tOLi8qTYRFZ3/z5w8CDg4gK0bSunpaQAZ88Chw4BhQqpULSodctI+ZdJYXbTpk1GLwJr1KgRZs6cyTCbDexmQGR5d+8CN28CTZoAKpWcJgTw++/A3LnAb7+lLlu3rmyhvXEDWLVKTrOzA7p3B06eBM6fT132xQvgp59kiI2MTJ1evz4wbBiwdKkMuUT5gRByRJ6DB+XjwAHg0qXU+f36ya44R44AMTG6qbZYtMjFCqV99UVFAYUKAa6u1i6JcpkUZh8/fozChQtnmO7m5oZHjx7lulAFAVtmiSzn0iVg0aLa+P13WyQlAbt3Ay1ayNbTuXPlDUwAGXA7dgQ++gho2hTo0UOGWU9P4P335cPLC2jVSobZy5dl94GVK4Fnz+Q2HB2Bd94Bhg+XgRjgeNKWcOEC8Msvsh5CQwEHB2uXSJmePZMtqb//Lh+FCgHbtwNXrsjQqguw6YeVVKlkyAVS//kDZEttQgKQnAy8eGEYObRauZ7uH8tXyZMnsnvRH3/In56ewLffys/9sWOyy5Fu+vbtgG0209TDh/Kf4RMnUh937gBubsCtW/InANmsffAgcO8e4O0tv5CseYelqCjg//4PWLAAqFRJvjBeXtYrTzomhdmKFSti586dCAkJMZi+Y8cOlC9f3iwFy+8YZony3unTwMyZwMaNttBqffXTFy4E3nsPuH1bPndykq1JI0cClSunrr9kiVyueXPj4WjixNTfy5cHhg4F+vcHihXLi6OhtFJS5N/TX36Rj3//TZ3XujXQsmXqc92ZMEdHy5ZRCaKjZWb6/Xdg/375mdFqDZdxds64np2d/GetaVOgWTOgUSPg66+BH34AAgLk2Y8mTYCaNWX2uX4dSEy0xb59Kvz5J3D4sOy24+YG/PNPmhBnBUlJwF9/yffTn3/KR9qWZp20IT2tc+cAe3sZcI8flwHVywtYtkyeqUkbXG/cML6NmBj5fVStGoDNm4ERI1K/oADZeX/BAqBLl1werQkKFZJ9pJyc5PMHD2TAdnaWp6VeASaF2dGjRyMkJAQPHz5Eq1atAAARERGYO3cuuxhkE7sZEOUNIeQf5xkzgJ07dVNVqFs3CrGxnrh4UaXvTuDpCYSEyBbX4sUzbqtIEaBNm4zTCxX6b6sqIDhYtsIGBQE2vKQ2TyUm2uDXX1XYvl2OEPHwYeo8OztZH0lJ8nHvnmwx++03eeFdSopsSS9d2nrlfxU8eWIYXiMjU1tUdSpUkGcuvv8+dVqhQvKulU2bykeDBhlD7qRJ8pGZiRMbZ5gWEyNb1YsWlQH3yBH5cHcH9uwxfwu7EDJY60Lrn3/KMzPGGpgqVpSNkf/7X+o0V1d5G/p69YDFi4HYWPlaGPubvmWL8TJUqSL/EdA93ngDePr0v5mbNwNduwJCQADQN1rfuSOnb9yImMAucHAwfG2Sk2Wf/yJFgJIls34N4uNlX+bISPl5CA7OfNlo55I4E18XkfDHqaS60KyvDP3icXHyjfEKBFqTwux7772HxMRETJs2DVOnTgUA+Pr6YsmSJejTp49ZC5hfsWWWyLyEkMFl5kz5xxCQ4bJ7d+CjjzS4fftPbNnSERcvqlC9OjB6NNCzp2mtdbNny9baLl1ki2x+oLt47d49oFOnVyeYP3ok63XLFjV27myHpKTUP1uFCwPt2wNvvikvOmrRQrYsDh4s+0and+5cwQuzjx/L7gH798sA+9dfGcNr5cry/dyihfxZqpScPnKkDL4BAcBrr8l/GEzh7S0DJACULi3QpIkKjRvLEUMePgRefx14/jzjemfPAuXKyfelLuQWLQqsX5/90/rPnsnW0rTh9cGDjMsVKSJDqe5Rv37qGZbQUNk1oHZt+VrpPhvbtwN//y2DrJOTfI3q1ZMNqDoVKhgG19dek+/btHS9B65fScE/g3bjpJiOkwjAKdSBM+KwDIPxt6ghp3X3x6VkufxXX8kzEqdPA2fOyJBaqJBs0HV3l8tER8vQeuqUXO70afnPg24UVRsbeS2Bh4f8zERGpnmcTMb1+DRjE6YA+An4wqEUyuOynBYXJ7sgWLnLgUlhFgCGDh2KoUOH4uHDh3BycoKLCzt25wTDLJF5JCfLP24zZ8o/LIA85de/PzBmjPxjotHIL/ivv07BqFE2qFUrd/30qlaVj/zg/HlgzRpg7drU22zv2yeDzcsIIf8oZjdYpJeSIlvjNm2SLYaLF8uWr8uXU7sPHD6sO+1tA8AGZcoIvPmmCm++KU9vpw1Y9vbypy7I1q8v+0B//31qmMrvnjyRwVX3OHs24zJ+fqnhtVmzzFvyatSQj9xatw44diwZ0dF70bdvS9j9V2nffy/D7PPnsu7q1ZPdFZYvly2Vb75pfJznv/6SofLYMdlV4Y8/ZMj97jv5D0va4Hr+fMbwbmsL+PsbhtdKlTL/TqhYUT7S27BBBuVatYDq1VM/B599JvdbowZyNIJD+zfUAJYaTHuCYgjGjtQJyam/jhqVcRsvXsjuT3fvyuCq+0ynV7y4/EdHqwU6dJCfuejo9EvJA/LFNfgjEr+hA5JhhyTYGy7m7y8DrRWZHGaTk5Oxf/9+XLlyBT179gQA3L17F25ubgy22cAwS5Q7SUmyD9vMmalf2K6ust/qyJGyNSg9R0fZulKQCJHxj/SdOzJgrFkj/+Clp7uYTaORp+jXrZNDL61dK/84nzwJ/PyzfDx5IrdRoUL2yqPRyBbCjRvladi0rWTnzslWrnPnDNfx9wc6dEhB0aIHMHx4E9jbG28inDJFlqlxY3nqVNdYtGlT9sqmRLGx8oKtiAhg717j3QaqVTMMr5ZuRCtTBvD2FggLizeYvnq1bPmtU0c+dKfNw8JkmNUF2SpVZMjdtEl2S+jaVfY9Td+394cfjO+/bFnZXUAXXF97LbX7Z274+clHeiVKyEd2Vaokz0DY26agZvJpBOAkAnASUxCKuyiFcriKujiBOjiFAJzE8v9bjv1Xy6JmzdTXrk4deYxPnwLffGO4/bJl5TGnfZQqJR/37sm+vID8x7B6dfl58/cH/Me0Ri3NCRRBNADADc8QCyOfvYwp2OJMCrM3btxA27ZtcfPmTSQmJqJ169ZwdXXFrFmzkJiYiKVLl758IwUc+8wSmSYxUY4OMGNGagtc8eIywA4fnnp6rSC7c0f28/vhB/n7iRPyj+umTTLA7tuXGnhsbeUp+l69gFmzZBg6dkwGio0bZVjVadFCnlW8etVwf+fOZR1mExNl2Nq4Uba2pt2mu3vq30LdqBK2tjJ8vfmm7E9Ytiyg0WgRFhaTZYt6UJB8KNHNm7KPqJ0d0Lt35sslJMjWyL175ePYMXl2Iq1q1eQFcLrw+t/NO185tWrJR3rffSffL6+9JgOa7nT/H3/IMKv757VMGdmPd/361HVdXWWLfNpWV0/PvD+W3Ni9W545qHzvEOzbtNBPH4TvEAdnFILhANWtZ1wDWpTNsJ0RI2SX25o1U0Orv3/mrcPffSe/C2rUkMtVq5Z6dgMAMOMscD9a/3SxXQhODBsBj28fGm7oFfjSNSnMjhgxAnXr1sWZM2dQLM1lu507d8agQYPMVrj8KiUltb8KEWVPYiKwYoUMsbduyWne3sDYscCgQcavuH5VCSFPTy5bJv8wr12b+z+4cXGypfOHH2QoSttq1bWr7CeX9oxQ48YywHbrlnrxm66v3/Tpqct5eqbeHELXjcPZWZ6aPHo0tS7Si4+XF+Bt2iQv1kodb1QG606dZLlatpR9Epctk30n33xTtqq+An8f89SzZzJIhIfL+ko7GkOtWqlnEDQa+c+ILrwePpzxzF758nK4uFatZIA1dlZCSRo2lI/0li6Vr5cu5Oq6R0ydKj9PtWvLllJrjmBlCheX/7pzVG0iO3XfuQMIARVgGGRVKjm/aVOj2wkNlY/sat9ePjIVGWnwZupuuxGuLd+C67LnGZezMpPC7MGDB3HkyBHY2xv2m/D19cWd9IPPUQbsYkCUfQkJsm/dzJmpI9WULAmMGydDrJKGW3r2TPYLXbZMXrChc/CgDHY6iYly2bQtasnJMhzevCmP285OBtZDh+Tp2g0b5ClnnSZNZFC+cyd1X1Wrytvx9ughL6xJTxeoixQB3npLjpfbogXw6afy1GXbtvKCuvbt5YUmDRoYhtnnz1NbdMPCDC9y9vaWF8y99Zb8W5y2n+306YYB+lUVHS3/ESlSJHVabKzsm6pSyYAPyCB/8KBscVOr5T9gycmyZXHPHhnIjh0z/IfDxkZuQ9ePOCJCPg4cyHhxlLe3DP6tWsl/Bnx98/jAXxHNmslHepUqyYfiqdXyP8quXQ0H7gVS+wrNn2+5tO7lJf9zzerWhc7OVr/4CzAxzGq1WqQYaVq8ffs2XHkLi5diFwOil0tIkBeCzJyZOkB7qVIyxA4cqKwQqzN4cOrvDg4ykD5/nvo3659/5DH/8EPqVdjOzrJbxerVqddY2NjI33/4wfACj3LlgD595GnqChXk38WlS2X47NVLnkrM6jT9ypWy9bVBA8PTjbNmyUdmwsLkPxy7dhl+v5UpI8Nr166yJe1VGSFBN0ZtWJh87adONZ4PtFrZ6LRjh3wcPixD+ObNsq527ZLTNBq5fJ8+sg/igQOGjRbLlsll0o9gVLmyHBM3MFCG0oYN5YVDw4cbLle0qJyva32tUuXVvNEAmUGXLvK/QWPjzM6fb/lxZl+8SB1nNj2ljzPbpk0bzJ8/H8uWLQMAqFQqPH/+HKGhoQjOasAyAsCWWVKuxER5UYaxVj1zSUiQfblmzky9AKR0aWD8eHkDAyWG2LTjQVarJkNt795A584y+Pz6KzBvXsZb3r7xhuHfM52hQ1N/d3WVXQX69pWtsWkD44gR8pFdRYpkegYzS2kvk6hYUQbYt96SQxG9SqFrxw75T8HOnYb9dtu1Sz3uJ09ky+mOHXI5XRcLneRkWS/GpL0AqXTp1LrT9QkuUUIGV92jTBnD9StVkmHWxUX2GdaF11q1Xp1/BMgCunSR/W1elTuAvXiRegcwQJ4yyg93AJszZw7atm2LatWqISEhAT179sSlS5dQvHhx/PTTT+YuY77DMEtKI4QMXCNHypbAvXsN77BkDomJMsROny6/vwHAxyc1xCr59qShofIq4S5dZF/V9AHvxx/lT1tbOZTUX3/J24fevi3/frVrJ1+DxYvlaWobGxmG+vaVfU+t1V9YNzxStWqpLbA1a75aATattPf0cXeX77n4eNkCvn+/DK9//GF4+r9QIXlKv1072cJ6+nRqlwLdBWcrV8q7xf3f/8mbbAQFyb6bmzfLkSAaNJAtsDVrZh1KdcOjVali+piulE+o1dkbH89SvLzkbdHCwuTPV+wNalKY9fHxwZkzZ7B+/XqcOXMGz58/x4ABA9CrVy84mWO8i3yO3QxISS5flq17YWGp065eNV+YTUmRYW7y5NRbPZYpI/tp9uun7BCr06iRfKSna5mrXFl2nejTR/ZbXblSBvtOnWQLru4ajJo15UVDwcGpA9tbk64v86tQlqzUrSu7C9SsmXrRy//9n5x+5gzw0UeGy1evLsNr27aytVv3HuzVS/4dr1HDsBvGtGnykZ6uhTq7ChUyz7iuRAVNjsOsRqOBn58ffvvtN/Tq1Qu9evXKi3Lla2yZLZi0WuDLL2Vrzf/+9+pfsBAXJ1tJv/xSjulqZyfvn/74sXm2L4R8LSZOlKdWARnaJk4EBgxIN0RMPvXdd7IPcLVqhq2Z/fvLR3qZDd5uLfb2r36QBWSL6rx5sktGWhUryjDr4iJbunUBNv3pfx1XVzmeJxG9WnIcZu3s7JDApsVcYZgteGJjZSvj5s3yeXj4qxtmhZBDPI0alTqOa5s2wMKFwMcfy1uL5nb74eGy5fXkSTmtaFEZ6oYPV9YQW7nl6ChbASlvqVQZgywgW8A/+0zWQUH454kovzKpS/nw4cMxa9YsJKcfqZmyhf8LFCyXL8urlHVBFsh4h568ptGknsLPysWLsmXqrbdkkC1TRo4TunOn7MeXW0eOyO4JQUEyyLq4AJMmyW4LY8YUrCBL1ufqKscsZZAlUjaT+sweP34cERER2L17N2rWrIlChQoZzN+c9q82ZcCW2YJj5045pmd0tDyFXry48Xul56U//pAXD50/Ly8eev31jMs8fw588YU8FavRyD/un3wiL74yR8A8c0a2gG3fLp87OADDhsnt5+S2j0REROmZFGbd3d3xVk56tZMBhtn8Twhg9mwZ1oSQLbObNgEffmi5MBsfL1s9581LvTr7yhXDMCuEHGz/o49ShxEKDpbjk5qjb+bly7IP7Lp18rlaLYP1xIlypAIiIqLcylGY1Wq1+PLLL/Hvv/8iKSkJrVq1wuTJkzmCQQ6xm0H+Fh8vL2DSjVI3aBDw9demXZV/7JgcmH3ECHnxVXYdPixDo+4WmcZu4nLlihyvNDxcPi9XTobYDh1yNrRSSoocX/OPP+QFY8WKAQ8eyIHoly5NvW/8O+8AU6bIK/eJiIjMJUdhdtq0aZg8eTICAwPh5OSEhQsX4uHDh1ixYkVelS9fYsts/hUVJYdT+vNPOWbo118D77+f8+2kpMghj0JD5e9lysgxRV8mLk6ezl+wQLa6liwJfPutvKvUL7/IZZKSgDlzZNhMSJAhe/x42a0gp/+X7t0LjB6dervUOnWAR4/k3aJ0t1Zt106GXH//nG2biIgoO3IUZn/44QcsXrwYQ4YMAQDs2bMH7du3x/Lly2HD25NkG8Ns/hQZKe8MdOuWvDp/0ybTxry+exd49105nqhOVrfG1jlwQLYIX74sn/fvL7sYuLvLMAvIlt6FC+WtOAE5HNGSJTnvUvDvv/JYf/3VcPrw4TJ8AzLYfvmlvIMRERFRXslRAr1586bB7WoDAwOhUqlwV3fPScoWhtn8Z+tWeWenW7fkVf9//mlakN2+HahdWwbZQoUyH+8yrRcvZF/c5s1lkC1dWt6Kc8UKGWTT+v57GWRLlJBj3e7ebVrf2NmzZZBVq4EPPpDHDsgg6+sr72R0/DiDLBER5b0chdnk5GQ4prsxup2dHTQajVkLld+xz2z+IYQ8pd6li2w9bd1a9h3NaUBMTJSn6zt0kKfpX3sNOHVK3qEoK/v2ybsaff21fD5wIPD333J4rbTS3tJ74EDgwgV5N6Oc3nY07XY6dJD7WrhQbrNKFdkSfOGCHMGBJ2uIiMgSctTNQAiBfv36wSHNlSwJCQl4//33DYbn4tBcWWPLbP6g0QBDhsiB1wE51NSCBbKvbE5cuwa8/TZw4oR8PmKEDMhZXTAWGwuMHSu7CACyBXf5chmmjRk+XP4cORJo2jRn5Uu/HbVaXjgWGJg6vV8/+SAiIrK0HP3Z7WvkCpR3333XbIUpKBhmlS8mBujaVY4EYGMjQ2xISM63s22bvLArOlr2s121CujYMet1Dh8GeveWIRiQF5jNnm38Dkc6rVqZ55R/69aZB2YiIiJryFGYXalrgqJcYTcDZbtzR47F+tdfsl/r+vVA+/Y524ZGI0cP+PJL+fz//k9uJ6s+sklJwOTJstVWqwXKlpX9YtkvlYiICjKTbppAucOWWeU6e1YG2du3AU9PecFWQEDOtzNxorzrFiBP/c+alfUtNc+dAxo0kCMmALI1d+HCnI09S0RElB8xzFoBw6yhxETTbihgaRERKnTvLrsYVK0KhIXJK/dN8fy5DKIrV8qLx17mm2/kz2LF5LixvAEfERGRxOuNrYDdDFJNmiT7eqYdU/VVtG9faXTsqEZMjBwC6/Bh04Js0aLyp260guwEWZ127WTLMIMsERFRKoZZK2DLrLR/v7wLlUYDnDxp7dJk7quvbLBgQQCSk1Xo0UPeXrZIEdO2NX06sGEDcOQIUKHCy5fv2FH2o128WHZp8PY2bb9ERET5FbsZWAHDrBxa6lUfykkIYNw4YPZsObjqyJEpmDtXnavxU4sVk6MgZBeHvCIiIsraK9Eyu2jRIvj6+sLR0RENGjTAsWPHsrXeunXroFKp0KlTp7wtoJmxm4G8QcCNG9YuReaSk+WtYWfPls/79PkHs2ZpeSMAIiKiV4zV/zSvX78eo0ePRmhoKE6dOoXatWsjKCgIDx48yHK969ev4+OPP0bT3IwAbyUFvWU2LEwO8K9SybtGvWri42W/1JUr5Riyy5Ylo0uXyzm+WxYRERHlPauH2Xnz5mHQoEHo378/qlWrhqVLl8LZ2RkrVqzIdJ2UlBT06tULU6ZMQfny5S1YWvMoyGH2yRN561NADknVoIFVi5NBdDQQFCRvZuDoCGzeDPTrJ6xdLCIiIsqEVfvMJiUl4eTJkxg/frx+mo2NDQIDA3H06NFM1/v888/h4eGBAQMG4ODBg1nuIzExEYlp0mNMTAwAQKPRQKPR5PIIXk6rBQA1tFotNBotACA+Xg3d/xEpKSn66QXBsGFq3LtngypVBCZPTsbw4fK1eBVeh4cPgeBgW5w5o0LhwgKbN6egaVOhf59Y4v1CeYN1qHysQ2Vj/SmfpeswJ/uxaph99OgRUlJS4OnpaTDd09MTFy5cMLrOoUOH8P333yNSN3r8S8yYMQNTpkzJMH337t1wdnbOcZlz6tatWgDK4cqVKwgLuwgAePiwBYDCAIBLly7pp+d3hw+XxPr19WBjo8WAAQexb1807tx5DUAZXLhwHmFhV6xWtidPHDFpUiPcvu2KwoUTMHnyUcTGxiAsLHWZ8PBwq5WPzIN1qHysQ2Vj/SmfpeowLi4u28sqajSD2NhY9O7dG9999x2KFy+erXXGjx+P0aNH65/HxMTAx8cHbdq0gZsFbp+kC0MVKlRAcLAci2ns2NSXvVKlSvrp+VlUFDBggDzusWMFRo5sBADYuFGOFODnVxXBwdbpQHv9OtC2rS1u31ahdGmBnTvVqFy5iX6+RqNBeHg4WrduDTs7O6uUkXKHdah8rENlY/0pn6XrUHcmPTusGmaLFy8OtVqN+/fvG0y/f/8+vLy8Mix/5coVXL9+HR07dtRP08rz+LC1tcXFixdRId3gnQ4ODnAwcnspOzs7i1SGjU3Kfz9tYGcng1tSUup8tVqtn56fjRgBPH4M+PsDkyenHrNudABrvQ7//gu8/rq8PW358vIuX76+xt8XlnrPUN5hHSof61DZWH/KZ6k6zMk+rHoBmL29PQICAhAREaGfptVqERERgYYNG2ZY3s/PD2fPnkVkZKT+8cYbb6Bly5aIjIyEj4+PJYtvsoI2NNeWLfJhawusWgXY21u7RNLZs0CzZjLI+vkBBw6YfntaIiIisg6rdzMYPXo0+vbti7p166J+/fqYP38+Xrx4gf79+wMA+vTpg1KlSmHGjBlwdHREjRo1DNZ3d3cHgAzTX2UFaTSDZ8+AkBD5+5gxQO3a1i2PzsmTQJs2cnSF2rWB3bsBDw9rl4qIiIhyyuphtnv37nj48CEmTZqEqKgo+Pv7Y+fOnfqLwm7evAmbfDZSfUEKs+PHA3fvAhUrAhMnWrs00okTQOvWchiuBg2AHTtMvz0tERERWZfVwywAhISEIETXfJfO/v37s1x31apV5i9QHiso3QwOHwaWLJG/f/st4ORk3fIAhkG2USMZZC1wHSARERHlkfzV5KkAKSnykd8lJgKDB8vf+/cHWrUyz3a1WuDaNdPWTR9kd+5kkCUiIlI6hlkLKyhdDGbPBs6dA0qUAL780jzb1GqBrl3lqAPbtuVsXWNB1tXVPOUiIiIi62GYtbCC0MXgwgXgiy/k7wsWAMWKmWe7EybIUREA4PJlw3m//AK0a2e81ZZBloiIKP9imLWw/N4yq9XK7gVJSTJcvvOOadvRaIBjx1K7ZKxZA8yYYXzZPXuAbt1kSE3fYnvyJIMsERFRfsYwa2H5PcyuWAEcPAg4OwOLFwMqVc63kZICdOggRxrYtAn4809gwAA5z9HRcNnTp4HOnWX4BWSY1jl7Vg6/xSBLRESUfzHMWlh+7mbw5Akwbpz8fepU029AMGWKHPcVAI4eBTp1kv8EdOwIdOmSutz160BwMPD8ecZt/PsvEBgoy9SgAYMsERFRfsUwa2H5uWV20iR5y9rq1YEPPjBtG9u3yyCss2ABEBUF1Kghuxrohhx+/Bho21bOq1lTtuTqXL8ub1H74IG8fe6OHQyyRERE+RXDrIXl1zB75kzqmLJffw2Yctvma9eA3r3l77b/jYAsBFC8uOwLmzaQzp4NXLwI+PjIsFq4sJx+544MsrdvA1WryhZe3hCBiIgo/2KYtbD8GGaFkC2xWq28EKtly5xvIyFBDrv19ClQv35qdwJbW9lvtlw5w+WTkwF3d9l9oFSp1Olz5wJXr8rhu8LD5dBgRERElH8xzFpYfuwzu26dvOjLyQmYM8e0bcybB5w6JYfx2rABaNFCXuy1bBnQrFnG5R0cgF9/BapVyzivdGkgIsIw5BIREVH+xDBrYfmtZfb5c+Djj+Xvn34KlClj2naePpUjH6xdK7cxdCjw7Jm8e1hawcGya8G6dUCTJqnTdd0SPD1lkDX14jMiIiJSFltrF6CgyW9hdto04O5d2Q1AF2pNNXmyHEpLx94+4zI9eshHekOGyFbviROBypVzVw4iIiJSDoZZC8tP3QwuXZLdAwDgq68yjgGbHW5u8me7dvIOX6Zq2FA+iIiIqGBhmLWw/NQyO2qUvNNXUBDwxhumbWPcODnqQO/eqcNuEREREWUXw6yF5Zcwu327fNjZybFgTbnTFyAv0ho2zLxlIyIiooKDbWEWlh+6GSQnp/aPHTkSqFLFqsUhIiKiAoxh1sLyQ8vsihXAhQtyGK3PPrN2aYiIiKggY5i1MKWH2efPgdBQ+fukSal33iIiIiKyBoZZC1N6N4N584CoKHmHrffft3ZpiIiIqKBjmLUwpbXMJicDx48DKSnA/fvA7Nly+vTpxseBJSIiIrIkjmZgYUoLs8OHy1vK/vSTvGXtixdAvXpAt27WLhkRERERw6zFKambwZUrwPffy9937QJ+/FH+Pns2x4QlIiKiVwPDrIUpqWV2xgzZvQAAfvgB0GqBDh2AFi2sWiwiIiIiPbavWZhSwuz168Dq1anPtVrZGjtzptWKRERERJQBw6yF6cKsnZ11y/EyM2bIi7/S6t8fqF7dOuUhIiIiMoZh1sJ0fWYdHKxbjqzcvAmsXCl/9/OTP52cgClTrFcmIiIiImMYZi1M1zLr6GjdcqS3ZYsMrpGRwKxZgEYDtGyZOmrBxx8DpUpZtYhEREREGTDMWpguzFqrZfbXX4Fq1YAzZ1KnJScDI0cCFy8Cy5fLByDv8DV2LPD772yVJSIiolcTw6yFWbObgRDAuHHA+fPA9u2p07dulV0LANm9ICkJaNoUaN4cKFQIaNYMUKksX14iIiKil2GYtTBrdjM4cQI4dy7j9K++Sv09Lk7+nDSJAZaIiIhefQyzFmbNbga6i7rSOnYMOHLEcFrDhsDrr1umTERERES5wTBrYdbqZpCQIG9Jm978+RmnsVWWiIiIlIJh1sKs1TK7bRsQHW047fZtYMMG+XtAgPxZvz4QFGTRohERERGZjGHWwqzVZ3bVqozTvvlGjmTQvDkwdSpQsyawcCFbZYmIiEg5GGYtzBrdDO7eBXbtkr83by5/vngBLFsmfx81CmjXDvjrL6BBA8uVi4iIiCi3GGYtKCVFPgDLhtn//Q/QaoHGjYFKleS0NWuAp0+B8uWBDh0sVxYiIiIic2KYtSBdFwPAct0MhEgdxaBfv9TpN27InyNGAGq1ZcpCREREZG4Msxak62IAWK5l9tgx4MIFwMkJePttw3lubkD//pYpBxEREVFeYJi1IF3LrEoF2NpaZp+6C7/eekuG17QGDgRcXS1TDiIiIqK8YKFIRYDhSAZ5OmJASgpw8CASbtzHTz++BcDWoIsBANjYAB98kIdlICIiIrIAtsxakEXGmN28GfD1BVq2xC/9NuPZC1v4qO+g5dPNAFJbhDt3losRERERKRlbZi0oz4fl2rwZ6NpVXvUFYBX6AQD6pqyEzduTgI0bMWRIF8TGAl98kUdlICIiIrIgtsxaUJ7eMCElRQ5N8F+QvYOS2I02AIC+WCWXGTkS/jVT8L//sVWWiIiI8geGWQvK024GBw/K+9P+50f0hhZqNMFBVMQVGXJv3ZLLEREREeUTDLMWlKfdDO7dM3i6Du8AAPrghyyXIyIiIlIyhlkLytNuBt7e+l8vojLOwB+20KALNme6HBEREZHSMcxaUJ52M2jaFChdGlCpsAHdAACB2INieCLnq1SAj49cjoiIiCifYJi1oDztZqBWAwsWAAB+hrzV19v4Wc7TDWo7fz7vXUtERET5CsOsBeX5OLNduuD8/F04i1qwQxI6YaucXro0sHEj0KVLHu2YiIiIyDo4zqwF5Wmf2f9siG4NAGjdIBZFRiyWfWSbNmWLLBEREeVLDLMWlOc3TQDw8389C94eWgzo0SPvdkRERET0CmA3AwvK624G//wjH3Z2wJtv5s0+iIiIiF4lDLMWlNfdDDZskD+DggB397zZBxEREdGrhGHWgvKyZVaINF0M3jb/9omIiIheRQyzFpSbPrMJCcDRo0BKivH5//wDnD8P2NsDb7xhehmJiIiIlIRh1oJy083giy+ARo2AlSuNz9e1yrZtCxQubFr5iIiIiJSGYdaCctPNYM8e+fPevYzz2MWAiIiICiqGWQsytZtBUhIQGZn5/LNngYsX5XY7djS5eERERESKwzBrQaZ2Mzh7NnVdY3Stsu3aAW5uppWNiIiISIkYZi3I1G4Gx45lPo9dDIiIiKggY5i1IFO7GRw/nvm8M2eAS5dka2+HDqaXjYiIiEiJGGYtyNRuBlm1zOpaZYODAVdX08pFREREpFQMsxZkSjeD2Fjg3LnM52/bJn++9Zbp5SIiIiJSKoZZCzKlm8GpU7JfrDHXrsmbJajVcnxZIiIiooKGYdaCTGmZzaqLwW+/yZ+NGwNFi5peLiIiIiKlYpi1IFP6zGZ18ZcuzHJsWSIiIiqoGGYtyJRuBrqW2cqVDafHxgL798vfOYoBERERFVQMsxaU024GDx4AN24AKhUQEGA4Lzxc3hmsYkWgShXzlpOIiIhIKRhmLSin3Qx0XQz8/DLe2evXX+XPDh1k2CUiIiIqiBhmLSinLbO6Lgb16xtO12qB7dvl7+wvS0RERAUZw6yFpKQAycny9+yGWV3LbL16htOPHQMePpSttU2amK+MRERERErDMGshulZZIHvdDITIvGVWN4pBUBBgb2+e8hEREREpEcOshSQlpf6enZbZ69eBx48BOzugVi3Debr+suxiQERERAXdKxFmFy1aBF9fXzg6OqJBgwY4lsWdAr777js0bdoURYoUQZEiRRAYGJjl8q8K3bBcKhVga/vy5XWH5O9vGH5v3gT++guwsQHatTN7MYmIiIgUxephdv369Rg9ejRCQ0Nx6tQp1K5dG0FBQXjw4IHR5ffv348ePXpg3759OHr0KHx8fNCmTRvcuXPHwiXPmcREOeSAo2P2Rh/Qhdn0/WXDwuTPhg2B4sXNWEAiIiIiBbJ6mJ03bx4GDRqE/v37o1q1ali6dCmcnZ2xYsUKo8uvWbMGw4YNg7+/P/z8/LB8+XJotVpERERYuOQ5k9ORDHQXf6XvL3v3rvzJGyUQERERAdk44Z13kpKScPLkSYwfP14/zcbGBoGBgTh69Gi2thEXFweNRoOiRYsanZ+YmIjENFdfxcTEAAA0Gg00Gk0uSp89Wi0AqBEfrwVgAwcHAY0mGVqtDQA1UlJSoNFoDdZJTgZOnrQFoMJrr2mg0UC/vE7btnI65T3d+8QS7xfKG6xD5WMdKhvrT/ksXYc52Y9Vw+yjR4+QkpICT09Pg+menp64cOFCtrYxduxYlCxZEoGBgUbnz5gxA1OmTMkwfffu3XB2ds55oXPo1q1aAMrhzp3HADyh1cYjLCwcN27I6ZcuXUJY2EWDda5fd0VcXCs4OWlw5UoYrl0Dbt6UywOAh8cLXL++Bzdu5HnxKY3w8HBrF4FyiXWofKxDZWP9KZ+l6jAuLi7by1o1zObWzJkzsW7dOuzfvx+OmYx3NX78eIwePVr/PCYmRt/P1i39bbXygK6Pq6ur7ODq7u6E4OBg7Nghe3hUqlQJwcEVDNZZuVJ2qm3QQI0OHYIBANu3p/YI6dbNEe3bB+d10ek/Go0G4eHhaN26Nezs7KxdHDIB61D5WIfKxvpTPkvXoe5MenZYNcwWL14carUa9+/fN5h+//59eHl5ZbnunDlzMHPmTOzZswe10o9dlYaDgwMcjHRUtbOzs0hl2NikAACSknQXgKlgZ2cHm/+yqVqthp2d2mCdkyflz/r1bWBnZ/PfdlLnv/FGxnUo71nqPUN5h3WofKxDZWP9KZ+l6jAn+7DqBWD29vYICAgwuHhLdzFXw4YNM11v9uzZmDp1Knbu3Im6detaoqi5lpMLwDK7+AsAXFyA5s3NVy4iIiIiJbN6N4PRo0ejb9++qFu3LurXr4/58+fjxYsX6N+/PwCgT58+KFWqFGbMmAEAmDVrFiZNmoS1a9fC19cXUVFRAAAXFxe4uLhY7TheJu3QXFmJj5fjyALGw2ybNtkfEYGIiIgov7N6mO3evTsePnyISZMmISoqCv7+/ti5c6f+orCbN2/CJs059iVLliApKQldu3Y12E5oaCgmT55syaLnSHZbZk+fBlJSAE9PoHTp1OmtWgE//wwMHZp3ZSQiIiJSGquHWQAICQlBSEiI0Xn79+83eH79+vW8L1AeyG6YTdvFIO3NFd5+G+jWLXs3XCAiIiIqKKx+04SCQhdmX9bNQHfxl7GuwAyyRERERIYYZi0kuy2zZ8/Kn1kM0EBERERE/2GYtZCEBPkzqzCbnAycPy9/r1kz78tEREREpHQMsxaSndEMLl+WLbjOzkC5chYqGBEREZGCMcxaWFYts7ouBtWrG94kgYiIiIiMY2SysKzC7N9/y5/sYkBERESUPQyzFpZVNwNdyyzDLBEREVH2MMxaWHa6GTDMEhEREWUPw6yFZRZm4+KAK1fk7zVqWK48RERERErGMGthmYXZc+cAIYASJeStbImIiIjo5RhmLSyzPrPsYkBERESUcwyzFpZZy6wuzLKLAREREVH2McxaWGZhlsNyEREREeUcw6yFsZsBERERkfkwzFqYsZbZR4+AqCj5e7Vqli0PERERkZIxzFqYsTCr62JQrhzg6mrZ8hAREREpGcOshRnrZsAuBkRERESmYZi1MGMtsxzJgIiIiMg0DLMWllU3A7bMEhEREeUMw6yFpe9mIATDLBEREZGpGGYtLH3L7I0bQGwsYGcHVK5snTIRERERKRXDrIWlD7O6/rJ+fjLQEhEREVH2McxaWPpuBufOyZ/sYkBERESUcwyzFpa+ZTY+Xv5kmCUiIiLKOYZZC1KpAFtb4/M4LBcRERFRzjHMWpCDgwy0xrBlloiIiCjnGGYtyNjdvwB5C9syZSxbFiIiIqL8gGHWgozdMAGQXQwya7ElIiIioswxzFpQZmGWXQyIiIiITMMwa0GZdTNgmCUiIiIyDcOsBWXVzYCIiIiIco5h1oLYzYCIiIjIvBhmLchYNwNvb6BYMcuXhYiIiCg/YJi1IGMts+xiQERERGQ6hlkLShtm1Wr5k10MiIiIiEyXyc1VKS+k7WbQvz8QFQUMHWq98hAREREpHcOsBaVtma1TB9iwwXplISIiIsoP2M3AgjIbzYCIiIiITMMwa0GZ3TSBiIiIiEzDMGtBbJklIiIiMi+GWQtimCUiIiIyL4ZZC2KYJSIiIjIvhlkLYp9ZIiIiIvNimLUgtswSERERmRfDrAUxzBIRERGZF8OsBbGbAREREZF5McxaEFtmiYiIiMyLYdaCGGaJiIiIzIth1oLYzYCIiIjIvBhmLYgts0RERETmxTBrQQyzRERERObFMGtB7GZAREREZF4MsxbEllkiIiIi82KYtSCGWSIiIiLzYpi1IIZZIiIiIvNimLUg9pklIiIiMi+GWQtiyywRERGReTHMWhDDLBEREZF5McxaELsZEBEREZkXw6yFqFQCtrbWLgURERFR/sIwayEODoBKZe1SEBEREeUvDLMWwi4GRERERObHMGshvPiLiIiIyPwYZi2EYZaIiIjI/BhmLYRhloiIiMj8GGYthGGWiIiIyPwYZi3EwUFYuwhERERE+Q7DrIVwNAMiIiIi82OYtRB2MyAiIiIyP4ZZC2GYJSIiIjI/hlkLsbe3dgmIiIiI8h+GWQthn1kiIiIi82OYtRB2MyAiIiIyv1cizC5atAi+vr5wdHREgwYNcOzYsSyX37BhA/z8/ODo6IiaNWsiLCzMQiU1HYfmIiIiIjI/q4fZ9evXY/To0QgNDcWpU6dQu3ZtBAUF4cGDB0aXP3LkCHr06IEBAwbg9OnT6NSpEzp16oS///7bwiXPGXYzICIiIjI/q4fZefPmYdCgQejfvz+qVauGpUuXwtnZGStWrDC6/IIFC9C2bVuMGTMGVatWxdSpU1GnTh188803Fi55zvACMCIiIiLzs7XmzpOSknDy5EmMHz9eP83GxgaBgYE4evSo0XWOHj2K0aNHG0wLCgrC1q1bjS6fmJiIxMRE/fOYmBgAgEajgUajyeURvJxKBQBqODqmQKPR5vn+yPx07xNLvF8ob7AOlY91qGysP+WzdB3mZD9WDbOPHj1CSkoKPD09DaZ7enriwoULRteJiooyunxUVJTR5WfMmIEpU6ZkmL579244OzubWPLsq1TJDY0aVUbp0ucRFvYiz/dHeSc8PNzaRaBcYh0qH+tQ2Vh/ymepOoyLi8v2slYNs5Ywfvx4g5bcmJgY+Pj4oE2bNnBzc8vz/Ws0GpQvH47WrVvDzs4uz/dH5qfRaBAezjpUMtah8rEOlY31p3yWrkPdmfTssGqYLV68ONRqNe7fv28w/f79+/Dy8jK6jpeXV46Wd3BwgIORcbHs7Ows+oGy9P7I/FiHysc6VD7WobKx/pTPUnWYk31Y9QIwe3t7BAQEICIiQj9Nq9UiIiICDRs2NLpOw4YNDZYHZJN3ZssTERERUf5l9W4Go0ePRt++fVG3bl3Ur18f8+fPx4sXL9C/f38AQJ8+fVCqVCnMmDEDADBixAg0b94cc+fORfv27bFu3TqcOHECy5Yts+ZhEBEREZEVWD3Mdu/eHQ8fPsSkSZMQFRUFf39/7Ny5U3+R182bN2Fjk9qA3KhRI6xduxYTJkzAp59+ikqVKmHr1q2oUaOGtQ6BiIiIiKzE6mEWAEJCQhASEmJ03v79+zNM69atG7p165bHpSIiIiKiV53Vb5pARERERGQqhlkiIiIiUiyGWSIiIiJSLIZZIiIiIlIshlkiIiIiUiyGWSIiIiJSLIZZIiIiIlIshlkiIiIiUiyGWSIiIiJSLIZZIiIiIlIshlkiIiIiUiyGWSIiIiJSLIZZIiIiIlIsW2sXwNKEEACAmJgYi+xPo9EgLi4OMTExsLOzs8g+ybxYh8rHOlQ+1qGysf6Uz9J1qMtputyWlQIXZmNjYwEAPj4+Vi4JEREREWUlNjYWhQsXznIZlchO5M1HtFot7t69C1dXV6hUqjzfX0xMDHx8fHDr1i24ubnl+f7I/FiHysc6VD7WobKx/pTP0nUohEBsbCxKliwJG5use8UWuJZZGxsblC5d2uL7dXNz4wdY4ViHysc6VD7WobKx/pTPknX4shZZHV4ARkRERESKxTBLRERERIrFMJvHHBwcEBoaCgcHB2sXhUzEOlQ+1qHysQ6VjfWnfK9yHRa4C8CIiIiIKP9gyywRERERKRbDLBEREREpFsMsERERESkWwywRERERKRbDrBksWrQIvr6+cHR0RIMGDXDs2LEsl9+wYQP8/Pzg6OiImjVrIiwszEIlpczkpA6/++47NG3aFEWKFEGRIkUQGBj40jqnvJfTz6HOunXroFKp0KlTp7wtIL1UTuswOjoaw4cPh7e3NxwcHFC5cmV+n1pRTutv/vz5qFKlCpycnODj44NRo0YhISHBQqWl9A4cOICOHTuiZMmSUKlU2Lp160vX2b9/P+rUqQMHBwdUrFgRq1atyvNyGiUoV9atWyfs7e3FihUrxD///CMGDRok3N3dxf37940uf/jwYaFWq8Xs2bPFuXPnxIQJE4SdnZ04e/ashUtOOjmtw549e4pFixaJ06dPi/Pnz4t+/fqJwoULi9u3b1u45KST0zrUuXbtmihVqpRo2rSpePPNNy1TWDIqp3WYmJgo6tatK4KDg8WhQ4fEtWvXxP79+0VkZKSFS05C5Lz+1qxZIxwcHMSaNWvEtWvXxK5du4S3t7cYNWqUhUtOOmFhYeKzzz4TmzdvFgDEli1bslz+6tWrwtnZWYwePVqcO3dOfP3110KtVoudO3dapsBpMMzmUv369cXw4cP1z1NSUkTJkiXFjBkzjC7/9ttvi/bt2xtMa9CggRgyZEielpMyl9M6TC85OVm4urqK1atX51UR6SVMqcPk5GTRqFEjsXz5ctG3b1+GWSvLaR0uWbJElC9fXiQlJVmqiJSFnNbf8OHDRatWrQymjR49WjRu3DhPy0nZk50w+8knn4jq1asbTOvevbsICgrKw5IZx24GuZCUlISTJ08iMDBQP83GxgaBgYE4evSo0XWOHj1qsDwABAUFZbo85S1T6jC9uLg4aDQaFC1aNK+KSVkwtQ4///xzeHh4YMCAAZYoJmXBlDrctm0bGjZsiOHDh8PT0xM1atTA9OnTkZKSYqli039Mqb9GjRrh5MmT+q4IV69eRVhYGIKDgy1SZsq9VynP2Fp8j/nIo0ePkJKSAk9PT4Ppnp6euHDhgtF1oqKijC4fFRWVZ+WkzJlSh+mNHTsWJUuWzPChJsswpQ4PHTqE77//HpGRkRYoIb2MKXV49epV7N27F7169UJYWBguX76MYcOGQaPRIDQ01BLFpv+YUn89e/bEo0eP0KRJEwghkJycjPfffx+ffvqpJYpMZpBZnomJiUF8fDycnJwsVha2zBLlwsyZM7Fu3Tps2bIFjo6O1i4OZUNsbCx69+6N7777DsWLF7d2cchEWq0WHh4eWLZsGQICAtC9e3d89tlnWLp0qbWLRtmwf/9+TJ8+HYsXL8apU6ewefNmbN++HVOnTrV20UiB2DKbC8WLF4darcb9+/cNpt+/fx9eXl5G1/Hy8srR8pS3TKlDnTlz5mDmzJnYs2cPatWqlZfFpCzktA6vXLmC69evo2PHjvppWq0WAGBra4uLFy+iQoUKeVtoMmDK59Db2xt2dnZQq9X6aVWrVkVUVBSSkpJgb2+fp2WmVKbU38SJE9G7d28MHDgQAFCzZk28ePECgwcPxmeffQYbG7a1veoyyzNubm4WbZUF2DKbK/b29ggICEBERIR+mlarRUREBBo2bGh0nYYNGxosDwDh4eGZLk95y5Q6BIDZs2dj6tSp2LlzJ+rWrWuJolImclqHfn5+OHv2LCIjI/WPN954Ay1btkRkZCR8fHwsWXyCaZ/Dxo0b4/Lly/p/RADg33//hbe3N4OshZlSf3FxcRkCq+4fEyFE3hWWzOaVyjMWv+Qsn1m3bp1wcHAQq1atEufOnRODBw8W7u7uIioqSgghRO/evcW4ceP0yx8+fFjY2tqKOXPmiPPnz4vQ0FAOzWVlOa3DmTNnCnt7e7Fx40Zx7949/SM2NtZah1Dg5bQO0+NoBtaX0zq8efOmcHV1FSEhIeLixYvit99+Ex4eHuKLL76w1iEUaDmtv9DQUOHq6ip++ukncfXqVbF7925RoUIF8fbbb1vrEAq82NhYcfr0aXH69GkBQMybN0+cPn1a3LhxQwghxLhx40Tv3r31y+uG5hozZow4f/68WLRoEYfmUrKvv/5alClTRtjb24v69euLP/74Qz+vefPmom/fvgbL//zzz6Jy5crC3t5eVK9eXWzfvt3CJab0clKHZcuWFQAyPEJDQy1fcNLL6ecwLYbZV0NO6/DIkSOiQYMGwsHBQZQvX15MmzZNJCcnW7jUpJOT+tNoNGLy5MmiQoUKwtHRUfj4+Ihhw4aJp0+fWr7gJIQQYt++fUb/tunqrW/fvqJ58+YZ1vH39xf29vaifPnyYuXKlRYvtxBCqIRgez4RERERKRP7zBIRERGRYjHMEhEREZFiMcwSERERkWIxzBIRERGRYjHMEhEREZFiMcwSERERkWIxzBIRERGRYjHMEhEREZFiMcwSERVgKpUKW7duBQBcv34dKpUKkZGRVi0TEVFOMMwSEVlJv379oFKpoFKpYGdnh3LlyuGTTz5BQkKCtYtGRKQYttYuABFRQda2bVusXLkSGo0GJ0+eRN++faFSqTBr1ixrF42ISBHYMktEZEUODg7w8vKCj48POnXqhMDAQISHhwMAtFotZsyYgXLlysHJyQm1a9fGxo0bDdb/559/0KFDB7i5ucHV1RVNmzbFlStXAADHjx9H69atUbx4cRQuXBjNmzfHqVOnLH6MRER5iWGWiOgV8ffff+PIkSOwt7cHAMyYMQM//PADli5din/++QejRo3Cu+++i99//x0AcOfOHTRr1gwODg7Yu3cvTp48iffeew/JyckAgNjYWPTt2xeHDh3CH3/8gUqVKiE4OBixsbFWO0YiInNjNwMiIiv67bff4OLiguTkZCQmJsLGxgbffPMNEhMTMX36dOzZswcNGzYEAJQvXx6HDh3Ct99+i+bNm2PRokUoXLgw1q1bBzs7OwBA5cqV9dtu1aqVwb6WLVsGd3d3/P777+jQoYPlDpKIKA8xzBIRWVHLli2xZMkSvHjxAl999RVsbW3x1ltv4Z9//kFcXBxat25tsHxSUhJee+01AEBkZCSaNm2qD7Lp3b9/HxMmTMD+/fvx4MEDpKSkIC4uDjdv3szz4yIishSGWSIiKypUqBAqVqwIAFixYgVq166N77//HjVq1AAAbN++HaVKlTJYx8HBAQDg5OSU5bb79u2Lx48fY8GCBShbtiwcHBzQsGFDJCUl5cGREBFZB8MsEdErwsbGBp9++ilGjx6Nf//9Fw4ODrh58yaaN29udPlatWph9erV0Gg0RltnDx8+jMWLFyM4OBgAcOvWLTx69ChPj4GIyNJ4ARgR0SukW7duUKvV+Pbbb/Hxxx9j1KhRWL16Na5cuYJTp07h66+/xurVqwEAISEhiImJwTvvvIMTJ07g0qVL+PHHH3Hx4kUAQKVKlfDjjz/i/Pnz+PPPP9GrV6+XtuYSESkNW2aJiF4htra2CAkJwezZs3Ht2jWUKFECM2bMwNWrV+Hu7o46derg008/BQAUK1YMe/fuxZgxY9C8eXOo1Wr4+/ujcePGAIDvv/8egwcPRp06deDj44Pp06fj448/tubhERGZnUoIIaxdCCIiIiIiU7CbAREREREpFsMsERERESkWwywRERERKRbDLBEREREpFsMsERERESkWwywRERERKRbDLBEREREpFsMsERERESkWwywRERERKRbDLBEREREpFsMsERERESnW/wOKAU25Bw8FnQAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Example usage:\n", - "# Assuming you have a trained model, test data (X_test, y_test), and a directory to save the plot\n", - "# Replace placeholders with your actual data and paths\n", - "\n", - "# Build a simple binary classification model (replace this with your actual model)\n", - "model = Sequential([\n", - " Dense(64, activation='relu', input_shape=(10,)),\n", - " Dense(1, activation='sigmoid')\n", - "])\n", - "\n", - "model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])\n", - "\n", - "# Train the model (replace this with your actual training process)\n", - "model.fit(X_train, y_train, epochs=10, batch_size=32, validation_data=(X_test, y_test))\n", - "\n", - "# Specify the directory to save the plot\n", - "save_directory = \"path/to/save/directory\"\n", - "\n", - "# Call the function to generate and save the precision-recall curve with threshold points\n", - "plot_precision_recall_curve(model, X_test, y_test, save_path=f\"{save_directory}/precision_recall_curve_with_thresholds.png\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/development/train_dev.ipynb b/development/train_dev.ipynb deleted file mode 100644 index 26c0369..0000000 --- a/development/train_dev.ipynb +++ /dev/null @@ -1,568 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from pathlib import Path\n", - "from time import strftime\n", - "\n", - "import mlflow\n", - "import pandas as pd\n", - "import tensorflow as tf" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'c:\\\\Main Workspace\\\\LogAnomalyDetect\\\\src'" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "pwd()" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "c:\\Main Workspace\\LogAnomalyDetect\\src\n" - ] - } - ], - "source": [ - "%cd src" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[nltk_data] Downloading package stopwords to\n", - "[nltk_data] C:\\Users\\Yinka\\AppData\\Roaming\\nltk_data...\n", - "[nltk_data] Package stopwords is already up-to-date!\n" - ] - } - ], - "source": [ - "from dataset_loader import get_dataset, preprocess_and_encode, get_vectorization_layer\n", - "from utils.common_utils import get_device_strategy\n", - "from src.utils.common_utils import set_seed" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Random seed set as 42\n" - ] - } - ], - "source": [ - "set_seed()" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "data_path = 'development/dev.gzip'\n", - "clean_dev = \"development/clean_dev.gzip\"" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "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", - "
LogTarget
01119803499 2005.06.26 R03-M0-NC-C:J03-U01 200...normal
11119803105 2005.06.26 R04-M0-NE-C:J06-U11 200...normal
21121496169 2005.07.15 R06-M0-N6-C:J03-U01 200...normal
31120968564 2005.07.09 R26-M0-N0-C:J03-U01 200...normal
41120953205 2005.07.09 R27-M0-N7-C:J11-U01 200...normal
\n", - "
" - ], - "text/plain": [ - " Log Target\n", - "0 1119803499 2005.06.26 R03-M0-NC-C:J03-U01 200... normal\n", - "1 1119803105 2005.06.26 R04-M0-NE-C:J06-U11 200... normal\n", - "2 1121496169 2005.07.15 R06-M0-N6-C:J03-U01 200... normal\n", - "3 1120968564 2005.07.09 R26-M0-N0-C:J03-U01 200... normal\n", - "4 1120953205 2005.07.09 R27-M0-N7-C:J11-U01 200... normal" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "df = pd.read_parquet(data_path)\n", - "df.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(1000, 2)" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "df.shape" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "#preprocess_and_encode(data_path, \"development/clean_dev.gzip\")" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "df_dataset = get_dataset(data_path)" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(, )\n", - "(, )\n", - "(, )\n" - ] - } - ], - "source": [ - "for idx in df_dataset.take(3):\n", - " print(idx)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "clean_dataset = get_dataset(clean_dev)" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(, )\n", - "(, )\n" - ] - } - ], - "source": [ - "for idx in clean_dataset.take(2):\n", - " print(idx)" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [], - "source": [ - "token_layer, vocab_s = get_vectorization_layer(clean_dataset)" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "420" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "vocab_s" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "a = token_layer(\"k e r n e l i n f o\")" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "S = get_device_strategy()\n", - "S.num_replicas_in_sync" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [], - "source": [ - "from src.models.model_loader import ModelLoader" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [], - "source": [ - "loader = ModelLoader()\n", - "test_model_loader = loader.get_model(\"cnn\")" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [], - "source": [ - "test_model = test_model_loader(embedding_vocab= vocab_s, embedding_dim= 5,\n", - " vectorization_layer=token_layer)" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": {}, - "outputs": [], - "source": [ - "loss = tf.keras.losses.BinaryCrossentropy()\n", - "optim = tf.keras.optimizers.Adadelta(learning_rate=0.01)\n", - "tensorb = tf.keras.callbacks.TensorBoard()\n", - "f1_score = tf.keras.metrics.F1Score()" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": {}, - "outputs": [], - "source": [ - "test_model.compile(loss=loss, optimizer=optim)" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Epoch 1/20\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "500/500 [==============================] - 6s 9ms/step - loss: 0.6863 - val_loss: 0.6790\n", - "Epoch 2/20\n", - "500/500 [==============================] - 4s 7ms/step - loss: 0.6715 - val_loss: 0.6627\n", - "Epoch 3/20\n", - "500/500 [==============================] - 3s 7ms/step - loss: 0.6536 - val_loss: 0.6418\n", - "Epoch 4/20\n", - "500/500 [==============================] - 3s 7ms/step - loss: 0.6304 - val_loss: 0.6179\n", - "Epoch 5/20\n", - "500/500 [==============================] - 3s 7ms/step - loss: 0.6084 - val_loss: 0.5926\n", - "Epoch 6/20\n", - "500/500 [==============================] - 4s 7ms/step - loss: 0.5784 - val_loss: 0.5634\n", - "Epoch 7/20\n", - "500/500 [==============================] - 4s 7ms/step - loss: 0.5538 - val_loss: 0.5329\n", - "Epoch 8/20\n", - "500/500 [==============================] - 3s 6ms/step - loss: 0.5198 - val_loss: 0.4995\n", - "Epoch 9/20\n", - "500/500 [==============================] - 3s 6ms/step - loss: 0.4875 - val_loss: 0.4642\n", - "Epoch 10/20\n", - "500/500 [==============================] - 3s 7ms/step - loss: 0.4586 - val_loss: 0.4288\n", - "Epoch 11/20\n", - "500/500 [==============================] - 3s 6ms/step - loss: 0.4300 - val_loss: 0.3940\n", - "Epoch 12/20\n", - "500/500 [==============================] - 4s 9ms/step - loss: 0.3902 - val_loss: 0.3593\n", - "Epoch 13/20\n", - "500/500 [==============================] - 3s 6ms/step - loss: 0.3676 - val_loss: 0.3279\n", - "Epoch 14/20\n", - "500/500 [==============================] - 3s 6ms/step - loss: 0.3465 - val_loss: 0.3010\n", - "Epoch 15/20\n", - "500/500 [==============================] - 3s 6ms/step - loss: 0.3201 - val_loss: 0.2765\n", - "Epoch 16/20\n", - "500/500 [==============================] - 3s 7ms/step - loss: 0.3088 - val_loss: 0.2555\n", - "Epoch 17/20\n", - "500/500 [==============================] - 5s 10ms/step - loss: 0.2840 - val_loss: 0.2389\n", - "Epoch 18/20\n", - "500/500 [==============================] - 4s 9ms/step - loss: 0.2773 - val_loss: 0.2236\n", - "Epoch 19/20\n", - "500/500 [==============================] - 4s 8ms/step - loss: 0.2587 - val_loss: 0.2123\n", - "Epoch 20/20\n", - "500/500 [==============================] - 5s 10ms/step - loss: 0.2585 - val_loss: 0.2023\n" - ] - }, - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 42, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "test_model.fit(clean_dataset,epochs=20, validation_data= clean_dataset)" - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Epoch 1/2\n", - "500/500 [==============================] - 10s 15ms/step - loss: 0.6919 - val_loss: 0.6830\n", - "Epoch 2/2\n", - "500/500 [==============================] - 4s 8ms/step - loss: 0.6743 - val_loss: 0.6653\n" - ] - } - ], - "source": [ - "with S.scope():\n", - " token_layer, vocab_s = get_vectorization_layer(clean_dataset)\n", - " new_test_model = test_model_loader(embedding_vocab= vocab_s, embedding_dim= 5,\n", - " vectorization_layer=token_layer)\n", - " new_test_model.compile(loss=loss, optimizer=optim)\n", - " new_test_model.fit(clean_dataset,epochs=2, validation_data= clean_dataset)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "test_model.fit()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(, )\n" - ] - } - ], - "source": [ - "\"\"\"def vectorize_text(text, label):\n", - " text = tf.expand_dims(text, -1)\n", - " return tokenizer(text), label\n", - "final_dataset = dataset.map(vectorize_text)\n", - "for sample in final_dataset.take(1):\n", - " print(sample)\"\"\"" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "env", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.2" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/src/config/config.yaml b/src/config/config.yaml index 0514019..53af5d0 100644 --- a/src/config/config.yaml +++ b/src/config/config.yaml @@ -23,6 +23,8 @@ params: total_epochs: 1 learning_rate: 0.01 cm_threshold: 0.5 + majority_class_weight : 0 + minority_class_weight : 0 model_name: "11DCNN" diff --git a/src/main.py b/src/main.py index 86e1456..c73c33f 100644 --- a/src/main.py +++ b/src/main.py @@ -34,9 +34,14 @@ # Importing functions and classes from other modules from dataset_loader import get_dataset, get_vectorization_layer from models.model_loader import ModelLoader -from utils.common_utils import (get_device_strategy, plot_confusion_matrix, - plot_precision_recall_curve, - set_mlflow_tracking, set_seed, tensorboard_dir) +from utils.common_utils import ( + get_device_strategy, + plot_confusion_matrix, + plot_precision_recall_curve, + set_mlflow_tracking, + set_seed, + tensorboard_dir, +) from utils.logging import logger @@ -58,6 +63,7 @@ def main(cfg: DictConfig): # Set up MLflow tracking for the experiment experiment_id = set_mlflow_tracking(cfg.model_name) + # Callbacks for model training checkpoint_path = f"artifacts/{cfg.model_name}/model_checkpoints" @@ -95,7 +101,7 @@ def main(cfg: DictConfig): ) logger.info(f"Retrieving the model: {cfg.model_name}") - load_model_func = ModelLoader().get_model(cfg.model_name) + build_model_func = ModelLoader().get_model(cfg.model_name) loss_func = tf.keras.losses.BinaryCrossentropy() optim = tf.keras.optimizers.Adam(learning_rate=cfg.params.learning_rate) f1_score_metrics = tf.keras.metrics.F1Score( @@ -113,15 +119,20 @@ def main(cfg: DictConfig): ): mlflow.set_tag("model_name", cfg.model_name) + # Class weigth + class_weight = { + 0: cfg.params.majority_class_weight, + 1: cfg.params.minority_class_weight, + } + # Training the model within the distributed strategy scope with strategy.scope(): tokenizer, vocab_size = get_vectorization_layer(dataset=train_data) - model = load_model_func( + model = build_model_func( vectorization_layer=tokenizer, embedding_vocab=vocab_size ) model.compile( - loss=loss_func, - optimizer=optim, + loss=loss_func, optimizer=optim, metrics=[f1_score_metrics] ) logger.info( f" Training {cfg.model_name} for {cfg.params.total_epochs} epochs"