{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "import torch" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "PyTorch version: 2.6.0+cu118\n", "CUDA available: True\n", "Current device: NVIDIA GeForce RTX 3050 6GB Laptop GPU\n" ] } ], "source": [ "import torch\n", "print(f\"PyTorch version: {torch.__version__}\")\n", "print(f\"CUDA available: {torch.cuda.is_available()}\")\n", "print(f\"Current device: {torch.cuda.get_device_name(0)}\")" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "df = pd.read_csv('spam_ham_india.csv')" ] }, { "cell_type": "code", "execution_count": 4, "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
MsgLabel
0CONGRATULATIONS! FREE 2GB data is yours! Claim...spam
1Hi! Thank you for being with Vi-India's FASTES...spam
2As part of Cyber Swachhta Pakhwada, CERT-In Go...spam
3I will try to manage took tabletsham
4Study from Home with Vi!! Watch Kite Victers C...spam
.........
2262Dear Customer, You have a missed call from +91...spam
2263Dear User, Vistor Id - 7538XXX. Loan Applicati...spam
2264Congrats, Y0UR Received Rs.592000 L0AN is Appr...spam
2265Hi! 50% off on proc. fees will be gone soon! A...spam
2266Congrats User, Rs.15OOO Bonus is Credited to y...spam
\n", "

2267 rows × 2 columns

\n", "
" ], "text/plain": [ " Msg Label\n", "0 CONGRATULATIONS! FREE 2GB data is yours! Claim... spam\n", "1 Hi! Thank you for being with Vi-India's FASTES... spam\n", "2 As part of Cyber Swachhta Pakhwada, CERT-In Go... spam\n", "3 I will try to manage took tablets ham\n", "4 Study from Home with Vi!! Watch Kite Victers C... spam\n", "... ... ...\n", "2262 Dear Customer, You have a missed call from +91... spam\n", "2263 Dear User, Vistor Id - 7538XXX. Loan Applicati... spam\n", "2264 Congrats, Y0UR Received Rs.592000 L0AN is Appr... spam\n", "2265 Hi! 50% off on proc. fees will be gone soon! A... spam\n", "2266 Congrats User, Rs.15OOO Bonus is Credited to y... spam\n", "\n", "[2267 rows x 2 columns]" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n", "Msg\n", "?image omitted 27\n", "Ok 18\n", "Hi team 13\n", "?? 12\n", "Joined 11\n", " ..\n", "THE BIG BULL starring Abhishek Bachchan dekhiye Disney+ Hotstar VIP par. Vi ke naye 401 recharge pe paiye 1yr Disney+ Hotstar VIP & 100GB data (3GB/day +16GB) + Unlimited Calls; 28days. Click bit.ly/ViHotstar1 1\n", "Biggest Diwali Offers@ VISHAL Mixer Grinder 3Jar(Mrp 2499) 45%OFF- Rs 1374 ONLY TODAY + FREE-VIP Trolley,Duffle Bag + 7.5% SBI Cashback TnC 1\n", "Don't miss out on FREE rewards on Airtel Thanks App. To claim your rewards, visit i.airtel.in/Thanks_rewards 1\n", "The index adjustment usually follows a gradual inclusion process, and the inflow of funds is also carried out in stages 1\n", "Rs. 11,350 Welcome Bonus is waiting for you on Junglee Rummy. Unlock it now by making your first deposit. Claim http://Kx6.in/U8oBZo T&CA*-Sports League 1\n", "Name: count, Length: 2062, dtype: int64\n", "\n", "\n", "Label\n", "ham 1522\n", "spam 745\n", "Name: count, dtype: int64\n" ] } ], "source": [ "for col in df.columns:\n", " print('\\n')\n", " print(f'{df[col].value_counts()}')" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "x=df['Msg']\n", "y=df['Label']" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "import re\n", "import nltk\n", "from nltk.corpus import stopwords\n", "from nltk.tokenize import word_tokenize\n", "from nltk.stem import WordNetLemmatizer" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "[nltk_data] Downloading package stopwords to\n", "[nltk_data] C:\\Users\\Shiva\\AppData\\Roaming\\nltk_data...\n", "[nltk_data] Package stopwords is already up-to-date!\n", "[nltk_data] Downloading package punkt to\n", "[nltk_data] C:\\Users\\Shiva\\AppData\\Roaming\\nltk_data...\n", "[nltk_data] Package punkt is already up-to-date!\n", "[nltk_data] Downloading package wordnet to\n", "[nltk_data] C:\\Users\\Shiva\\AppData\\Roaming\\nltk_data...\n", "[nltk_data] Package wordnet is already up-to-date!\n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nltk.download('stopwords')\n", "nltk.download('punkt')\n", "nltk.download('wordnet')\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "[nltk_data] Downloading package punkt to\n", "[nltk_data] C:\\Users\\Shiva\\AppData\\Roaming\\nltk_data...\n", "[nltk_data] Package punkt is already up-to-date!\n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import nltk\n", "nltk.download('punkt')" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "def clean_text(text):\n", "\n", " if isinstance(text, float): # Handle NaN or float values\n", " text = \"\"\n", "\n", " text = text.lower() #convert to lowercase\n", " text = re.sub(r'\\W', ' ', text) # remove special characters\n", " text = re.sub(r'\\s+' , ' ' , text).strip() #remove extra spaces\n", " \n", " tokens = word_tokenize(text)\n", " tokens = [word for word in tokens if word not in stopwords.words('english')] #removing stop words\n", "\n", " lemmatizer = WordNetLemmatizer()\n", " tokens = [lemmatizer.lemmatize(word) for word in tokens] # Lemmatization\n", "\n", " return ' '.join(tokens)\n", "\n" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "[nltk_data] Downloading package punkt_tab to\n", "[nltk_data] C:\\Users\\Shiva\\AppData\\Roaming\\nltk_data...\n", "[nltk_data] Package punkt_tab is already up-to-date!\n", "[nltk_data] Downloading package stopwords to\n", "[nltk_data] C:\\Users\\Shiva\\AppData\\Roaming\\nltk_data...\n", "[nltk_data] Package stopwords is already up-to-date!\n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import nltk\n", "nltk.download('punkt_tab')\n", "nltk.download('stopwords')\n" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "x = x.apply(clean_text)\n", "\n" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 congratulation free 2gb data claim airtel than...\n", "1 hi thank vi india fastest 4g ookla verified lo...\n", "2 part cyber swachhta pakhwada cert goi advises ...\n", "3 try manage took tablet\n", "4 study home vi watch kite victers channel free ...\n", " ... \n", "2262 dear customer missed call 918431333249 last mi...\n", "2263 dear user vistor id 7538xxx loan application r...\n", "2264 congrats y0ur received r 592000 l0an approve 1...\n", "2265 hi 50 proc fee gone soon apply olyv formerly s...\n", "2266 congrats user r 15ooo bonus credited wallet xx...\n", "Name: Msg, Length: 2267, dtype: object" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "from sklearn.feature_extraction.text import TfidfVectorizer\n", "\n", "vectorizer = TfidfVectorizer()\n", "x = vectorizer.fit_transform(x)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "from sklearn.model_selection import train_test_split\n", "\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": 16, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "C:\\Users\\Shiva\\AppData\\Local\\Temp\\ipykernel_21476\\3251901739.py:1: FutureWarning: Downcasting behavior in `replace` is deprecated and will be removed in a future version. To retain the old behavior, explicitly call `result.infer_objects(copy=False)`. To opt-in to the future behavior, set `pd.set_option('future.no_silent_downcasting', True)`\n", " y_train = y_train.replace({'ham':0 , 'spam':1})\n", "C:\\Users\\Shiva\\AppData\\Local\\Temp\\ipykernel_21476\\3251901739.py:2: FutureWarning: Downcasting behavior in `replace` is deprecated and will be removed in a future version. To retain the old behavior, explicitly call `result.infer_objects(copy=False)`. To opt-in to the future behavior, set `pd.set_option('future.no_silent_downcasting', True)`\n", " y_test = y_test.replace({'ham':0 , 'spam':1})\n" ] } ], "source": [ "y_train = y_train.replace({'ham':0 , 'spam':1})\n", "y_test = y_test.replace({'ham':0 , 'spam':1})\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "XGB" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "from sklearn.model_selection import StratifiedKFold\n", "from sklearn.metrics import precision_score , recall_score , f1_score\n", "from sklearn.model_selection import cross_val_score\n", "from bayes_opt import BayesianOptimization\n", "import xgboost as xgb\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "using bayesian optimizer to find the best hyper parameters available" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "def xgb_csv(max_depth , learning_rate , n_estimators , gamma , min_child_weight, subsample , colsample_bytree):\n", " params = {\n", " 'max_depth' : int(max_depth),\n", " 'learning_rate' : learning_rate,\n", " 'n_estimators' : int(n_estimators),\n", " 'gamma' : gamma,\n", " 'min_child_weight' : min_child_weight,\n", " 'subsample' : subsample,\n", " 'colsample_bytree' : colsample_bytree,\n", " 'objective' : 'binary:logistic',\n", " 'eval_metric' : 'logloss'\n", "\n", " } \n", "\n", " cv_results = cross_val_score(\n", " xgb.XGBClassifier(**params),\n", " x_train , y_train,\n", " scoring='accuracy',\n", " cv=5\n", " )\n", "\n", " return cv_results.mean()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "set the range for hyperparameters and initialize bayesian optimization\n" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "pbounds = {\n", " 'max_depth': (3, 10),\n", " 'learning_rate': (0.01, 0.05),\n", " 'n_estimators': (50, 500),\n", " 'gamma': (0, 5),\n", " 'min_child_weight': (1, 10),\n", " 'subsample': (0.5, 1),\n", " 'colsample_bytree': (0.5, 1)\n", "}\n", "\n", "optimizer = BayesianOptimization(\n", " f=xgb_csv,\n", " pbounds = pbounds,\n", " random_state = 42\n", "\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "run the optimization" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "| iter | target | colsam... | gamma | learni... | max_depth | min_ch... | n_esti... | subsample |\n", "-------------------------------------------------------------------------------------------------------------\n", "| \u001b[39m1 \u001b[39m | \u001b[39m0.9719 \u001b[39m | \u001b[39m0.6873 \u001b[39m | \u001b[39m4.754 \u001b[39m | \u001b[39m0.03928 \u001b[39m | \u001b[39m7.191 \u001b[39m | \u001b[39m2.404 \u001b[39m | \u001b[39m120.2 \u001b[39m | \u001b[39m0.529 \u001b[39m |\n", "| \u001b[39m2 \u001b[39m | \u001b[39m0.9415 \u001b[39m | \u001b[39m0.9331 \u001b[39m | \u001b[39m3.006 \u001b[39m | \u001b[39m0.03832 \u001b[39m | \u001b[39m3.144 \u001b[39m | \u001b[39m9.729 \u001b[39m | \u001b[39m424.6 \u001b[39m | \u001b[39m0.6062 \u001b[39m |\n", "| \u001b[39m3 \u001b[39m | \u001b[39m0.9708 \u001b[39m | \u001b[39m0.5909 \u001b[39m | \u001b[39m0.917 \u001b[39m | \u001b[39m0.02217 \u001b[39m | \u001b[39m6.673 \u001b[39m | \u001b[39m4.888 \u001b[39m | \u001b[39m181.1 \u001b[39m | \u001b[39m0.8059 \u001b[39m |\n", "| \u001b[39m4 \u001b[39m | \u001b[39m0.9542 \u001b[39m | \u001b[39m0.5697 \u001b[39m | \u001b[39m1.461 \u001b[39m | \u001b[39m0.02465 \u001b[39m | \u001b[39m6.192 \u001b[39m | \u001b[39m8.067 \u001b[39m | \u001b[39m139.9 \u001b[39m | \u001b[39m0.7571 \u001b[39m |\n", "| \u001b[35m5 \u001b[39m | \u001b[35m0.984 \u001b[39m | \u001b[35m0.7962 \u001b[39m | \u001b[35m0.2323 \u001b[39m | \u001b[35m0.0343 \u001b[39m | \u001b[35m4.194 \u001b[39m | \u001b[35m1.585 \u001b[39m | \u001b[35m477.0 \u001b[39m | \u001b[35m0.9828 \u001b[39m |\n", "| \u001b[39m6 \u001b[39m | \u001b[39m0.9586 \u001b[39m | \u001b[39m0.9042 \u001b[39m | \u001b[39m1.523 \u001b[39m | \u001b[39m0.01391 \u001b[39m | \u001b[39m7.79 \u001b[39m | \u001b[39m4.961 \u001b[39m | \u001b[39m104.9 \u001b[39m | \u001b[39m0.7476 \u001b[39m |\n", "| \u001b[39m7 \u001b[39m | \u001b[39m0.9724 \u001b[39m | \u001b[39m0.5172 \u001b[39m | \u001b[39m4.547 \u001b[39m | \u001b[39m0.02035 \u001b[39m | \u001b[39m7.638 \u001b[39m | \u001b[39m3.805 \u001b[39m | \u001b[39m284.0 \u001b[39m | \u001b[39m0.7734 \u001b[39m |\n", "| \u001b[39m8 \u001b[39m | \u001b[39m0.963 \u001b[39m | \u001b[39m0.5924 \u001b[39m | \u001b[39m4.848 \u001b[39m | \u001b[39m0.04101 \u001b[39m | \u001b[39m9.576 \u001b[39m | \u001b[39m9.053 \u001b[39m | \u001b[39m319.1 \u001b[39m | \u001b[39m0.9609 \u001b[39m |\n", "| \u001b[39m9 \u001b[39m | \u001b[39m0.9708 \u001b[39m | \u001b[39m0.5442 \u001b[39m | \u001b[39m0.9799 \u001b[39m | \u001b[39m0.01181 \u001b[39m | \u001b[39m5.277 \u001b[39m | \u001b[39m4.498 \u001b[39m | \u001b[39m172.1 \u001b[39m | \u001b[39m0.9144 \u001b[39m |\n", "| \u001b[39m10 \u001b[39m | \u001b[39m0.9575 \u001b[39m | \u001b[39m0.6784 \u001b[39m | \u001b[39m1.405 \u001b[39m | \u001b[39m0.03171 \u001b[39m | \u001b[39m3.986 \u001b[39m | \u001b[39m8.22 \u001b[39m | \u001b[39m83.55 \u001b[39m | \u001b[39m0.9934 \u001b[39m |\n", "| \u001b[39m11 \u001b[39m | \u001b[39m0.9741 \u001b[39m | \u001b[39m0.5334 \u001b[39m | \u001b[39m0.04341 \u001b[39m | \u001b[39m0.01633 \u001b[39m | \u001b[39m9.163 \u001b[39m | \u001b[39m3.159 \u001b[39m | \u001b[39m486.6 \u001b[39m | \u001b[39m0.7986 \u001b[39m |\n", "| \u001b[39m12 \u001b[39m | \u001b[39m0.9785 \u001b[39m | \u001b[39m0.9389 \u001b[39m | \u001b[39m0.236 \u001b[39m | \u001b[39m0.03486 \u001b[39m | \u001b[39m3.699 \u001b[39m | \u001b[39m1.508 \u001b[39m | \u001b[39m476.4 \u001b[39m | \u001b[39m0.6112 \u001b[39m |\n", "| \u001b[39m13 \u001b[39m | \u001b[39m0.9823 \u001b[39m | \u001b[39m0.6875 \u001b[39m | \u001b[39m0.6845 \u001b[39m | \u001b[39m0.01345 \u001b[39m | \u001b[39m5.858 \u001b[39m | \u001b[39m1.26 \u001b[39m | \u001b[39m478.6 \u001b[39m | \u001b[39m0.6794 \u001b[39m |\n", "| \u001b[39m14 \u001b[39m | \u001b[39m0.9697 \u001b[39m | \u001b[39m0.5702 \u001b[39m | \u001b[39m2.362 \u001b[39m | \u001b[39m0.02998 \u001b[39m | \u001b[39m3.694 \u001b[39m | \u001b[39m3.997 \u001b[39m | \u001b[39m479.6 \u001b[39m | \u001b[39m0.9518 \u001b[39m |\n", "| \u001b[39m15 \u001b[39m | \u001b[39m0.9708 \u001b[39m | \u001b[39m0.9236 \u001b[39m | \u001b[39m0.2229 \u001b[39m | \u001b[39m0.02412 \u001b[39m | \u001b[39m6.407 \u001b[39m | \u001b[39m4.062 \u001b[39m | \u001b[39m477.1 \u001b[39m | \u001b[39m0.8208 \u001b[39m |\n", "| \u001b[39m16 \u001b[39m | \u001b[39m0.9823 \u001b[39m | \u001b[39m0.7058 \u001b[39m | \u001b[39m0.2799 \u001b[39m | \u001b[39m0.02269 \u001b[39m | \u001b[39m4.772 \u001b[39m | \u001b[39m1.258 \u001b[39m | \u001b[39m483.5 \u001b[39m | \u001b[39m0.6561 \u001b[39m |\n", "| \u001b[39m17 \u001b[39m | \u001b[39m0.9818 \u001b[39m | \u001b[39m0.5727 \u001b[39m | \u001b[39m1.206 \u001b[39m | \u001b[39m0.02441 \u001b[39m | \u001b[39m3.503 \u001b[39m | \u001b[39m1.411 \u001b[39m | \u001b[39m486.3 \u001b[39m | \u001b[39m0.8341 \u001b[39m |\n", "| \u001b[39m18 \u001b[39m | \u001b[39m0.9779 \u001b[39m | \u001b[39m0.8587 \u001b[39m | \u001b[39m4.31 \u001b[39m | \u001b[39m0.03847 \u001b[39m | \u001b[39m7.171 \u001b[39m | \u001b[39m1.359 \u001b[39m | \u001b[39m484.3 \u001b[39m | \u001b[39m0.8057 \u001b[39m |\n", "| \u001b[39m19 \u001b[39m | \u001b[39m0.9636 \u001b[39m | \u001b[39m0.8722 \u001b[39m | \u001b[39m2.142 \u001b[39m | \u001b[39m0.04917 \u001b[39m | \u001b[39m3.717 \u001b[39m | \u001b[39m5.565 \u001b[39m | \u001b[39m486.5 \u001b[39m | \u001b[39m0.7253 \u001b[39m |\n", "| \u001b[35m20 \u001b[39m | \u001b[35m0.9851 \u001b[39m | \u001b[35m0.5769 \u001b[39m | \u001b[35m1.237 \u001b[39m | \u001b[35m0.03922 \u001b[39m | \u001b[35m6.516 \u001b[39m | \u001b[35m1.421 \u001b[39m | \u001b[35m489.4 \u001b[39m | \u001b[35m0.9954 \u001b[39m |\n", "| \u001b[39m21 \u001b[39m | \u001b[39m0.9752 \u001b[39m | \u001b[39m0.7641 \u001b[39m | \u001b[39m4.181 \u001b[39m | \u001b[39m0.04341 \u001b[39m | \u001b[39m5.635 \u001b[39m | \u001b[39m1.58 \u001b[39m | \u001b[39m489.8 \u001b[39m | \u001b[39m0.5462 \u001b[39m |\n", "| \u001b[39m22 \u001b[39m | \u001b[39m0.9757 \u001b[39m | \u001b[39m0.6894 \u001b[39m | \u001b[39m0.2106 \u001b[39m | \u001b[39m0.04727 \u001b[39m | \u001b[39m4.271 \u001b[39m | \u001b[39m2.735 \u001b[39m | \u001b[39m491.1 \u001b[39m | \u001b[39m0.889 \u001b[39m |\n", "| \u001b[39m23 \u001b[39m | \u001b[39m0.9823 \u001b[39m | \u001b[39m0.9431 \u001b[39m | \u001b[39m0.02563 \u001b[39m | \u001b[39m0.01637 \u001b[39m | \u001b[39m9.957 \u001b[39m | \u001b[39m1.044 \u001b[39m | \u001b[39m490.4 \u001b[39m | \u001b[39m0.5163 \u001b[39m |\n", "| \u001b[39m24 \u001b[39m | \u001b[39m0.9851 \u001b[39m | \u001b[39m0.8898 \u001b[39m | \u001b[39m0.2608 \u001b[39m | \u001b[39m0.04607 \u001b[39m | \u001b[39m9.089 \u001b[39m | \u001b[39m1.472 \u001b[39m | \u001b[39m494.6 \u001b[39m | \u001b[39m0.8525 \u001b[39m |\n", "| \u001b[39m25 \u001b[39m | \u001b[39m0.9801 \u001b[39m | \u001b[39m0.8824 \u001b[39m | \u001b[39m4.13 \u001b[39m | \u001b[39m0.01894 \u001b[39m | \u001b[39m8.767 \u001b[39m | \u001b[39m1.07 \u001b[39m | \u001b[39m497.1 \u001b[39m | \u001b[39m0.9883 \u001b[39m |\n", "| \u001b[39m26 \u001b[39m | \u001b[39m0.9586 \u001b[39m | \u001b[39m0.5349 \u001b[39m | \u001b[39m0.7981 \u001b[39m | \u001b[39m0.01001 \u001b[39m | \u001b[39m9.273 \u001b[39m | \u001b[39m5.791 \u001b[39m | \u001b[39m496.1 \u001b[39m | \u001b[39m0.5543 \u001b[39m |\n", "| \u001b[39m27 \u001b[39m | \u001b[39m0.979 \u001b[39m | \u001b[39m0.7409 \u001b[39m | \u001b[39m4.207 \u001b[39m | \u001b[39m0.03979 \u001b[39m | \u001b[39m9.862 \u001b[39m | \u001b[39m1.251 \u001b[39m | \u001b[39m491.2 \u001b[39m | \u001b[39m0.6014 \u001b[39m |\n", "| \u001b[39m28 \u001b[39m | \u001b[39m0.9823 \u001b[39m | \u001b[39m0.913 \u001b[39m | \u001b[39m1.662 \u001b[39m | \u001b[39m0.01581 \u001b[39m | \u001b[39m5.638 \u001b[39m | \u001b[39m1.176 \u001b[39m | \u001b[39m497.4 \u001b[39m | \u001b[39m0.7463 \u001b[39m |\n", "| \u001b[39m29 \u001b[39m | \u001b[39m0.9542 \u001b[39m | \u001b[39m0.9524 \u001b[39m | \u001b[39m0.3562 \u001b[39m | \u001b[39m0.03616 \u001b[39m | \u001b[39m9.998 \u001b[39m | \u001b[39m8.921 \u001b[39m | \u001b[39m235.7 \u001b[39m | \u001b[39m0.7347 \u001b[39m |\n", "| \u001b[35m30 \u001b[39m | \u001b[35m0.9868 \u001b[39m | \u001b[35m0.5985 \u001b[39m | \u001b[35m0.7864 \u001b[39m | \u001b[35m0.04041 \u001b[39m | \u001b[35m6.719 \u001b[39m | \u001b[35m1.086 \u001b[39m | \u001b[35m493.1 \u001b[39m | \u001b[35m0.7211 \u001b[39m |\n", "| \u001b[39m31 \u001b[39m | \u001b[39m0.9388 \u001b[39m | \u001b[39m0.8034 \u001b[39m | \u001b[39m1.064 \u001b[39m | \u001b[39m0.04039 \u001b[39m | \u001b[39m4.194 \u001b[39m | \u001b[39m9.596 \u001b[39m | \u001b[39m268.8 \u001b[39m | \u001b[39m0.5393 \u001b[39m |\n", "| \u001b[39m32 \u001b[39m | \u001b[39m0.9829 \u001b[39m | \u001b[39m0.5673 \u001b[39m | \u001b[39m1.002 \u001b[39m | \u001b[39m0.02492 \u001b[39m | \u001b[39m4.22 \u001b[39m | \u001b[39m1.226 \u001b[39m | \u001b[39m295.6 \u001b[39m | \u001b[39m0.864 \u001b[39m |\n", "| \u001b[39m33 \u001b[39m | \u001b[39m0.9741 \u001b[39m | \u001b[39m0.6253 \u001b[39m | \u001b[39m0.2635 \u001b[39m | \u001b[39m0.02813 \u001b[39m | \u001b[39m9.792 \u001b[39m | \u001b[39m3.48 \u001b[39m | \u001b[39m297.9 \u001b[39m | \u001b[39m0.9044 \u001b[39m |\n", "| \u001b[39m34 \u001b[39m | \u001b[39m0.952 \u001b[39m | \u001b[39m0.8655 \u001b[39m | \u001b[39m4.046 \u001b[39m | \u001b[39m0.01326 \u001b[39m | \u001b[39m3.331 \u001b[39m | \u001b[39m6.524 \u001b[39m | \u001b[39m294.5 \u001b[39m | \u001b[39m0.5228 \u001b[39m |\n", "| \u001b[39m35 \u001b[39m | \u001b[39m0.984 \u001b[39m | \u001b[39m0.7028 \u001b[39m | \u001b[39m0.4766 \u001b[39m | \u001b[39m0.03975 \u001b[39m | \u001b[39m4.893 \u001b[39m | \u001b[39m1.448 \u001b[39m | \u001b[39m302.3 \u001b[39m | \u001b[39m0.9415 \u001b[39m |\n", "| \u001b[39m36 \u001b[39m | \u001b[39m0.9857 \u001b[39m | \u001b[39m0.616 \u001b[39m | \u001b[39m0.7114 \u001b[39m | \u001b[39m0.03481 \u001b[39m | \u001b[39m7.711 \u001b[39m | \u001b[39m1.497 \u001b[39m | \u001b[39m307.9 \u001b[39m | \u001b[39m0.7906 \u001b[39m |\n", "| \u001b[39m37 \u001b[39m | \u001b[39m0.9735 \u001b[39m | \u001b[39m0.6085 \u001b[39m | \u001b[39m3.238 \u001b[39m | \u001b[39m0.03775 \u001b[39m | \u001b[39m3.264 \u001b[39m | \u001b[39m2.691 \u001b[39m | \u001b[39m308.7 \u001b[39m | \u001b[39m0.6298 \u001b[39m |\n", "| \u001b[39m38 \u001b[39m | \u001b[39m0.9746 \u001b[39m | \u001b[39m0.7109 \u001b[39m | \u001b[39m4.958 \u001b[39m | \u001b[39m0.049 \u001b[39m | \u001b[39m9.189 \u001b[39m | \u001b[39m2.343 \u001b[39m | \u001b[39m304.2 \u001b[39m | \u001b[39m0.7759 \u001b[39m |\n", "| \u001b[39m39 \u001b[39m | \u001b[39m0.9652 \u001b[39m | \u001b[39m0.7393 \u001b[39m | \u001b[39m0.4108 \u001b[39m | \u001b[39m0.03426 \u001b[39m | \u001b[39m9.853 \u001b[39m | \u001b[39m6.315 \u001b[39m | \u001b[39m310.4 \u001b[39m | \u001b[39m0.9339 \u001b[39m |\n", "| \u001b[39m40 \u001b[39m | \u001b[39m0.9812 \u001b[39m | \u001b[39m0.8096 \u001b[39m | \u001b[39m2.452 \u001b[39m | \u001b[39m0.04292 \u001b[39m | \u001b[39m3.497 \u001b[39m | \u001b[39m1.255 \u001b[39m | \u001b[39m370.2 \u001b[39m | \u001b[39m0.9049 \u001b[39m |\n", "| \u001b[39m41 \u001b[39m | \u001b[39m0.9564 \u001b[39m | \u001b[39m0.5138 \u001b[39m | \u001b[39m0.4386 \u001b[39m | \u001b[39m0.02242 \u001b[39m | \u001b[39m3.518 \u001b[39m | \u001b[39m6.453 \u001b[39m | \u001b[39m366.7 \u001b[39m | \u001b[39m0.5615 \u001b[39m |\n", "| \u001b[39m42 \u001b[39m | \u001b[39m0.9785 \u001b[39m | \u001b[39m0.782 \u001b[39m | \u001b[39m4.686 \u001b[39m | \u001b[39m0.04302 \u001b[39m | \u001b[39m5.804 \u001b[39m | \u001b[39m1.202 \u001b[39m | \u001b[39m375.1 \u001b[39m | \u001b[39m0.763 \u001b[39m |\n", "| \u001b[39m43 \u001b[39m | \u001b[39m0.979 \u001b[39m | \u001b[39m0.696 \u001b[39m | \u001b[39m4.353 \u001b[39m | \u001b[39m0.04918 \u001b[39m | \u001b[39m9.795 \u001b[39m | \u001b[39m1.556 \u001b[39m | \u001b[39m369.2 \u001b[39m | \u001b[39m0.6996 \u001b[39m |\n", "| \u001b[39m44 \u001b[39m | \u001b[39m0.979 \u001b[39m | \u001b[39m0.8399 \u001b[39m | \u001b[39m0.1021 \u001b[39m | \u001b[39m0.0265 \u001b[39m | \u001b[39m9.375 \u001b[39m | \u001b[39m2.102 \u001b[39m | \u001b[39m374.7 \u001b[39m | \u001b[39m0.8001 \u001b[39m |\n", "| \u001b[39m45 \u001b[39m | \u001b[39m0.9779 \u001b[39m | \u001b[39m0.9025 \u001b[39m | \u001b[39m0.179 \u001b[39m | \u001b[39m0.01531 \u001b[39m | \u001b[39m3.253 \u001b[39m | \u001b[39m1.254 \u001b[39m | \u001b[39m379.8 \u001b[39m | \u001b[39m0.9159 \u001b[39m |\n", "| \u001b[39m46 \u001b[39m | \u001b[39m0.9708 \u001b[39m | \u001b[39m0.5313 \u001b[39m | \u001b[39m1.774 \u001b[39m | \u001b[39m0.02195 \u001b[39m | \u001b[39m9.183 \u001b[39m | \u001b[39m4.644 \u001b[39m | \u001b[39m382.3 \u001b[39m | \u001b[39m0.7547 \u001b[39m |\n", "| \u001b[39m47 \u001b[39m | \u001b[39m0.9752 \u001b[39m | \u001b[39m0.9848 \u001b[39m | \u001b[39m0.5169 \u001b[39m | \u001b[39m0.0445 \u001b[39m | \u001b[39m9.282 \u001b[39m | \u001b[39m1.955 \u001b[39m | \u001b[39m304.5 \u001b[39m | \u001b[39m0.5716 \u001b[39m |\n", "| \u001b[39m48 \u001b[39m | \u001b[39m0.9641 \u001b[39m | \u001b[39m0.7114 \u001b[39m | \u001b[39m3.453 \u001b[39m | \u001b[39m0.04483 \u001b[39m | \u001b[39m3.116 \u001b[39m | \u001b[39m6.852 \u001b[39m | \u001b[39m377.0 \u001b[39m | \u001b[39m0.7762 \u001b[39m |\n", "| \u001b[35m49 \u001b[39m | \u001b[35m0.9873 \u001b[39m | \u001b[35m0.6876 \u001b[39m | \u001b[35m0.1516 \u001b[39m | \u001b[35m0.02992 \u001b[39m | \u001b[35m8.222 \u001b[39m | \u001b[35m1.003 \u001b[39m | \u001b[35m289.5 \u001b[39m | \u001b[35m0.8819 \u001b[39m |\n", "| \u001b[39m50 \u001b[39m | \u001b[39m0.9796 \u001b[39m | \u001b[39m0.7216 \u001b[39m | \u001b[39m0.327 \u001b[39m | \u001b[39m0.04785 \u001b[39m | \u001b[39m4.992 \u001b[39m | \u001b[39m1.28 \u001b[39m | \u001b[39m290.6 \u001b[39m | \u001b[39m0.5316 \u001b[39m |\n", "| \u001b[39m51 \u001b[39m | \u001b[39m0.9801 \u001b[39m | \u001b[39m0.5188 \u001b[39m | \u001b[39m4.328 \u001b[39m | \u001b[39m0.01756 \u001b[39m | \u001b[39m9.631 \u001b[39m | \u001b[39m1.769 \u001b[39m | \u001b[39m291.5 \u001b[39m | \u001b[39m0.8925 \u001b[39m |\n", "| \u001b[39m52 \u001b[39m | \u001b[39m0.9746 \u001b[39m | \u001b[39m0.6555 \u001b[39m | \u001b[39m0.73 \u001b[39m | \u001b[39m0.02015 \u001b[39m | \u001b[39m8.404 \u001b[39m | \u001b[39m1.029 \u001b[39m | \u001b[39m50.42 \u001b[39m | \u001b[39m0.7015 \u001b[39m |\n", "| \u001b[39m53 \u001b[39m | \u001b[39m0.9332 \u001b[39m | \u001b[39m0.7043 \u001b[39m | \u001b[39m2.547 \u001b[39m | \u001b[39m0.0292 \u001b[39m | \u001b[39m4.58 \u001b[39m | \u001b[39m8.345 \u001b[39m | \u001b[39m56.53 \u001b[39m | \u001b[39m0.5057 \u001b[39m |\n", "| \u001b[39m54 \u001b[39m | \u001b[39m0.9796 \u001b[39m | \u001b[39m0.618 \u001b[39m | \u001b[39m0.3163 \u001b[39m | \u001b[39m0.01544 \u001b[39m | \u001b[39m4.464 \u001b[39m | \u001b[39m1.009 \u001b[39m | \u001b[39m395.7 \u001b[39m | \u001b[39m0.6756 \u001b[39m |\n", "| \u001b[39m55 \u001b[39m | \u001b[39m0.979 \u001b[39m | \u001b[39m0.5146 \u001b[39m | \u001b[39m0.7955 \u001b[39m | \u001b[39m0.03833 \u001b[39m | \u001b[39m9.635 \u001b[39m | \u001b[39m2.553 \u001b[39m | \u001b[39m402.0 \u001b[39m | \u001b[39m0.8944 \u001b[39m |\n", "| \u001b[39m56 \u001b[39m | \u001b[39m0.9636 \u001b[39m | \u001b[39m0.909 \u001b[39m | \u001b[39m4.047 \u001b[39m | \u001b[39m0.04827 \u001b[39m | \u001b[39m3.482 \u001b[39m | \u001b[39m6.175 \u001b[39m | \u001b[39m400.3 \u001b[39m | \u001b[39m0.7183 \u001b[39m |\n", "| \u001b[39m57 \u001b[39m | \u001b[39m0.9829 \u001b[39m | \u001b[39m0.7324 \u001b[39m | \u001b[39m0.8024 \u001b[39m | \u001b[39m0.03683 \u001b[39m | \u001b[39m9.646 \u001b[39m | \u001b[39m1.755 \u001b[39m | \u001b[39m393.3 \u001b[39m | \u001b[39m0.8042 \u001b[39m |\n", "| \u001b[39m58 \u001b[39m | \u001b[39m0.9779 \u001b[39m | \u001b[39m0.8798 \u001b[39m | \u001b[39m4.539 \u001b[39m | \u001b[39m0.01824 \u001b[39m | \u001b[39m3.338 \u001b[39m | \u001b[39m1.183 \u001b[39m | \u001b[39m390.1 \u001b[39m | \u001b[39m0.8711 \u001b[39m |\n", "| \u001b[39m59 \u001b[39m | \u001b[39m0.9658 \u001b[39m | \u001b[39m0.5658 \u001b[39m | \u001b[39m0.1958 \u001b[39m | \u001b[39m0.01485 \u001b[39m | \u001b[39m5.355 \u001b[39m | \u001b[39m6.713 \u001b[39m | \u001b[39m390.7 \u001b[39m | \u001b[39m0.8032 \u001b[39m |\n", "| \u001b[39m60 \u001b[39m | \u001b[39m0.9757 \u001b[39m | \u001b[39m0.8597 \u001b[39m | \u001b[39m4.349 \u001b[39m | \u001b[39m0.02945 \u001b[39m | \u001b[39m9.732 \u001b[39m | \u001b[39m1.785 \u001b[39m | \u001b[39m396.7 \u001b[39m | \u001b[39m0.6886 \u001b[39m |\n", "=============================================================================================================\n", "{'target': np.float64(0.9873126036862854), 'params': {'colsample_bytree': np.float64(0.6875760635879894), 'gamma': np.float64(0.15163263655810333), 'learning_rate': np.float64(0.029923689735900552), 'max_depth': np.float64(8.222271942435441), 'min_child_weight': np.float64(1.0026864337114683), 'n_estimators': np.float64(289.52342829152406), 'subsample': np.float64(0.8818589329767135)}}\n" ] } ], "source": [ "optimizer.maximize(\n", " init_points = 10,\n", " n_iter = 50\n", ")\n", "\n", "print(optimizer.max)" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "x_test1 = x_test\n", "y_test1 = y_test" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Mean Precision on training data: 0.9879\n", "Mean Recall on training data: 0.9711\n", "Mean F1-Score on training data: 0.9794\n" ] } ], "source": [ "import xgboost as xgb\n", "import numpy as np\n", "from sklearn.model_selection import cross_val_score, StratifiedKFold\n", "from sklearn.metrics import make_scorer, precision_score, recall_score, f1_score\n", "\n", "# Your parameters\n", "params = {\n", " 'max_depth': 3,\n", "\n", " 'objective': 'binary:logistic',\n", " 'eval_metric': 'logloss',\n", " 'colsample_bytree': 0.6875760635879894, \n", " 'gamma': 0.15163263655810333,\n", " 'learning_rate': 0.029923689735900552,\n", " 'max_depth': 8, # Changed from float to int\n", " 'min_child_weight': 1.0026864337114683,\n", " 'n_estimators': 290, # Changed from float to int\n", " 'subsample': 0.8818589329767135\n", "}\n", "\n", "# Initialize model\n", "model = xgb.XGBClassifier(**params)\n", "\n", "# Define scoring metrics\n", "scoring = {\n", " 'precision': make_scorer(precision_score),\n", " 'recall': make_scorer(recall_score),\n", " 'f1': make_scorer(f1_score)\n", "}\n", "\n", "# Initialize k-fold cross validation\n", "skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)\n", "\n", "# Perform cross-validation for each metric\n", "precision_scores = cross_val_score(model, x_train, y_train, scoring=scoring['precision'], cv=skf)\n", "recall_scores = cross_val_score(model, x_train, y_train, scoring=scoring['recall'], cv=skf)\n", "f1_scores = cross_val_score(model, x_train, y_train, scoring=scoring['f1'], cv=skf)\n", "\n", "# Print results\n", "print(f\"Mean Precision on training data: {np.mean(precision_scores):.4f}\")\n", "print(f\"Mean Recall on training data: {np.mean(recall_scores):.4f}\")\n", "print(f\"Mean F1-Score on training data: {np.mean(f1_scores):.4f}\")" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(1813, 4615)\n", "(454, 4615)\n" ] } ], "source": [ "print(x_train.shape)\n", "print(x_test.shape)" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
XGBClassifier(base_score=None, booster=None, callbacks=None,\n",
       "              colsample_bylevel=None, colsample_bynode=None,\n",
       "              colsample_bytree=0.6875760635879894, device=None,\n",
       "              early_stopping_rounds=None, enable_categorical=False,\n",
       "              eval_metric='logloss', feature_types=None,\n",
       "              gamma=0.15163263655810333, grow_policy=None, importance_type=None,\n",
       "              interaction_constraints=None, learning_rate=0.029923689735900552,\n",
       "              max_bin=None, max_cat_threshold=None, max_cat_to_onehot=None,\n",
       "              max_delta_step=None, max_depth=8, max_leaves=None,\n",
       "              min_child_weight=1.0026864337114683, missing=nan,\n",
       "              monotone_constraints=None, multi_strategy=None, n_estimators=290,\n",
       "              n_jobs=None, num_parallel_tree=None, random_state=None, ...)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ "XGBClassifier(base_score=None, booster=None, callbacks=None,\n", " colsample_bylevel=None, colsample_bynode=None,\n", " colsample_bytree=0.6875760635879894, device=None,\n", " early_stopping_rounds=None, enable_categorical=False,\n", " eval_metric='logloss', feature_types=None,\n", " gamma=0.15163263655810333, grow_policy=None, importance_type=None,\n", " interaction_constraints=None, learning_rate=0.029923689735900552,\n", " max_bin=None, max_cat_threshold=None, max_cat_to_onehot=None,\n", " max_delta_step=None, max_depth=8, max_leaves=None,\n", " min_child_weight=1.0026864337114683, missing=nan,\n", " monotone_constraints=None, multi_strategy=None, n_estimators=290,\n", " n_jobs=None, num_parallel_tree=None, random_state=None, ...)" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "model.fit(x_train , y_train)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [], "source": [ "y_pred = model.predict(x_test1)" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "from sklearn.metrics import make_scorer, precision_score, recall_score, f1_score, classification_report, confusion_matrix" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Test Set Results:\n", "\n", "Classification Report:\n", " precision recall f1-score support\n", "\n", " 0 0.99 0.99 0.99 297\n", " 1 0.98 0.97 0.98 157\n", "\n", " accuracy 0.98 454\n", " macro avg 0.98 0.98 0.98 454\n", "weighted avg 0.98 0.98 0.98 454\n", "\n" ] } ], "source": [ "# Calculate metrics on test set\n", "print(\"\\nTest Set Results:\")\n", "print(\"\\nClassification Report:\")\n", "print(classification_report(y_test, y_pred))" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Confusion Matrix:\n", "[[294 3]\n", " [ 4 153]]\n" ] } ], "source": [ "print(\"\\nConfusion Matrix:\")\n", "print(confusion_matrix(y_test, y_pred))" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [], "source": [ "test_precision = precision_score(y_test, y_pred)\n", "test_recall = recall_score(y_test, y_pred)\n", "test_f1 = f1_score(y_test, y_pred)\n" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Test Set Metrics Summary:\n", "Precision: 0.9808\n", "Recall: 0.9745\n", "F1-Score: 0.9776\n" ] } ], "source": [ "print(\"\\nTest Set Metrics Summary:\")\n", "print(f\"Precision: {test_precision:.4f}\")\n", "print(f\"Recall: {test_recall:.4f}\")\n", "print(f\"F1-Score: {test_f1:.4f}\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exporting the model\n" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [], "source": [ "import joblib" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['tfidf_vectorizer.joblib']" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# After training your model\n", "joblib.dump(model, 'spam_detector_model.joblib')\n", "joblib.dump(vectorizer, 'tfidf_vectorizer.joblib')" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2025-02-11 22:56:57.414 WARNING streamlit.runtime.scriptrunner_utils.script_run_context: Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:56:57.415 WARNING streamlit.runtime.state.session_state_proxy: Session state does not function when running a script without `streamlit run`\n", "2025-02-11 22:56:57.416 WARNING streamlit.runtime.scriptrunner_utils.script_run_context: Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:56:57.416 WARNING streamlit.runtime.scriptrunner_utils.script_run_context: Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:56:57.416 WARNING streamlit.runtime.scriptrunner_utils.script_run_context: Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" ] } ], "source": [ "# After training your model, before running streamlit\n", "import streamlit as st\n", "st.session_state.model = model # your trained model\n", "st.session_state.vectorizer = vectorizer # your TF-IDF vectorizer" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2025-02-11 22:58:38.280 WARNING streamlit.runtime.scriptrunner_utils.script_run_context: Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.281 WARNING streamlit.runtime.scriptrunner_utils.script_run_context: Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.281 WARNING streamlit.runtime.scriptrunner_utils.script_run_context: Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.523 \n", " \u001b[33m\u001b[1mWarning:\u001b[0m to view this Streamlit app on a browser, run it with the following\n", " command:\n", "\n", " streamlit run C:\\Users\\Shiva\\AppData\\Roaming\\Python\\Python310\\site-packages\\ipykernel_launcher.py [ARGUMENTS]\n", "2025-02-11 22:58:38.524 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.525 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.526 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.526 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.527 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.528 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.528 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.529 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.529 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.530 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.530 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.532 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.533 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.533 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.534 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.534 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.535 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.536 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.536 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.537 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.537 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.538 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.539 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.539 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.541 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.541 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.542 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.543 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.543 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.544 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.544 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.544 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.545 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.546 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.546 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.546 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.547 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.547 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.548 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.548 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n", "2025-02-11 22:58:38.549 Thread 'MainThread': missing ScriptRunContext! This warning can be ignored when running in bare mode.\n" ] } ], "source": [ "import streamlit as st\n", "import pandas as pd\n", "import numpy as np\n", "import nltk\n", "from nltk.corpus import stopwords\n", "from nltk.tokenize import word_tokenize\n", "import string\n", "import re\n", "import xgboost as xgb\n", "from sklearn.feature_extraction.text import TfidfVectorizer\n", "\n", "# Download required NLTK data\n", "try:\n", " nltk.data.find('tokenizers/punkt')\n", " nltk.data.find('corpora/stopwords')\n", "except LookupError:\n", " nltk.download('punkt')\n", " nltk.download('stopwords')\n", "\n", "def preprocess_text(text):\n", " \"\"\"\n", " Preprocess text using the same steps as training data\n", " \"\"\"\n", " # Convert to lowercase\n", " text = text.lower()\n", " \n", " # Remove punctuation\n", " text = ''.join([char for char in text if char not in string.punctuation])\n", " \n", " # Remove numbers\n", " text = re.sub(r'\\d+', '', text)\n", " \n", " # Remove extra whitespace\n", " text = ' '.join(text.split())\n", " \n", " # Tokenization\n", " tokens = word_tokenize(text)\n", " \n", " # Remove stopwords\n", " stop_words = set(stopwords.words('english'))\n", " tokens = [token for token in tokens if token not in stop_words]\n", " \n", " # Join tokens back into text\n", " return ' '.join(tokens)\n", "\n", "# Initialize variables to store model and vectorizer\n", "if 'model' not in st.session_state:\n", " st.session_state.model = None\n", "if 'vectorizer' not in st.session_state:\n", " st.session_state.vectorizer = None\n", "\n", "# Streamlit interface\n", "st.title(\"📧 Spam Message Detector\")\n", "\n", "# Add model upload section\n", "st.sidebar.header(\"Model Configuration\")\n", "model_upload = st.sidebar.file_uploader(\"Upload trained model (optional)\", type=['joblib'])\n", "vectorizer_upload = st.sidebar.file_uploader(\"Upload vectorizer (optional)\", type=['joblib'])\n", "\n", "if model_upload is not None and vectorizer_upload is not None:\n", " import joblib\n", " st.session_state.model = joblib.load(model_upload)\n", " st.session_state.vectorizer = joblib.load(vectorizer_upload)\n", " st.sidebar.success(\"Model and vectorizer loaded successfully!\")\n", "\n", "# Option to use current model\n", "if st.session_state.model is None:\n", " st.warning(\"\"\"Please either:\n", " 1. Upload your saved model and vectorizer using the sidebar, or\n", " 2. Run this script in the same session where you trained your model\n", " \n", " Using format:\n", " ```python\n", " # After training your model\n", " st.session_state.model = your_trained_model\n", " st.session_state.vectorizer = your_trained_vectorizer\n", " ```\n", " \"\"\")\n", "else:\n", " st.write(\"\"\"\n", " This app detects whether a message is spam or not. \n", " Enter your message below and click 'Analyze' to check!\n", " \"\"\")\n", "\n", " # Create text input\n", " message = st.text_area(\"Enter your message:\", height=100)\n", "\n", " if st.button(\"Analyze\"):\n", " if message:\n", " # Preprocess the input\n", " processed_text = preprocess_text(message)\n", " \n", " # Vectorize the text\n", " text_vectorized = st.session_state.vectorizer.transform([processed_text])\n", " \n", " # Make prediction\n", " prediction = st.session_state.model.predict(text_vectorized)[0]\n", " probability = st.session_state.model.predict_proba(text_vectorized)[0]\n", " \n", " # Display result with proper formatting\n", " st.markdown(\"### Analysis Result\")\n", " \n", " if prediction == 1:\n", " st.error(\"🚨 This message is likely SPAM!\")\n", " st.write(f\"Confidence: {probability[1]:.2%}\")\n", " else:\n", " st.success(\"✅ This message appears to be legitimate.\")\n", " st.write(f\"Confidence: {probability[0]:.2%}\")\n", " \n", " # Add explanation\n", " st.markdown(\"### Message Processing Details\")\n", " with st.expander(\"See preprocessing steps\"):\n", " st.write(\"Original message:\", message)\n", " st.write(\"Processed message:\", processed_text)\n", " \n", " else:\n", " st.warning(\"Please enter a message to analyze.\")\n", "\n", " # Add information about the model\n", " with st.sidebar:\n", " st.header(\"About the Model\")\n", " st.write(\"\"\"\n", " This spam detector uses an XGBoost classifier trained on a dataset of spam and legitimate messages.\n", " \n", " Model Performance:\n", " - Training Accuracy: 99.7%\n", " - Testing Accuracy: 98.9%\n", " \"\"\")\n", " \n", " st.markdown(\"### How it works\")\n", " st.write(\"\"\"\n", " 1. Your message is preprocessed (cleaning, removing stopwords)\n", " 2. The cleaned text is converted to numerical features\n", " 3. The XGBoost model analyzes these features\n", " 4. A prediction is made based on learned patterns\n", " \"\"\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Random forest\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from sklearn.metrics import f1_score , recall_score , precision_score , precision_recall_curve, confusion_matrix , classification_report, make_scorer\n", "from sklearn.model_selection import StratifiedKFold , cross_val_score\n", "from sklearn.ensemble import RandomForestClassifier \n", "from bayes_opt import BayesianOptimization\n" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "def rf_csv(n_estimators , max_depth , min_samples_split , min_samples_leaf , max_features , random_state ):\n", "\t\n", "\tparams = {\n", "\t'n_estimators' : int(n_estimators),\n", "\t'max_depth' : int(max_depth),\n", "\t'min_samples_split' : int(min_samples_split),\n", "\t'min_samples_leaf' : int(min_samples_leaf),\n", " 'max_features': float(max_features), \n", "\t'random_state' : int(random_state)\n", "\t}\n", "\n", "\tcv_results = cross_val_score(\n", " RandomForestClassifier(**params),\n", " x_train , y_train,\n", " scoring='accuracy',\n", " cv=5\n", " )\n", "\n", "\treturn cv_results.mean()\n", "\n", "\n", "#setting hyperparameters for initializing bayesian optimization\n", "\n", "pbounds={\n", "\t'n_estimators' : (100 , 1000),\n", "\t'max_depth' : (3 , 50),\n", "\t'min_samples_split' : (2,20),\n", "\t'min_samples_leaf' : (1,10),\n", "\t'max_features': (0.1, 1.0),\n", "\t'random_state' : (42,50)\n", "}\n", "\n", "optimizer = BayesianOptimization(\n", "\tf = rf_csv,\n", "\tpbounds = pbounds,\n", "\trandom_state = 42\n", ")" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "| iter | target | max_depth | max_fe... | min_sa... | min_sa... | n_esti... | random... |\n", "-------------------------------------------------------------------------------------------------\n", "| \u001b[39m1 \u001b[39m | \u001b[39m0.9763 \u001b[39m | \u001b[39m20.6 \u001b[39m | \u001b[39m0.9556 \u001b[39m | \u001b[39m7.588 \u001b[39m | \u001b[39m12.78 \u001b[39m | \u001b[39m240.4 \u001b[39m | \u001b[39m43.25 \u001b[39m |\n", "| \u001b[39m2 \u001b[39m | \u001b[39m0.9697 \u001b[39m | \u001b[39m5.73 \u001b[39m | \u001b[39m0.8796 \u001b[39m | \u001b[39m6.41 \u001b[39m | \u001b[39m14.75 \u001b[39m | \u001b[39m118.5 \u001b[39m | \u001b[39m49.76 \u001b[39m |\n", "| \u001b[35m3 \u001b[39m | \u001b[35m0.9895 \u001b[39m | \u001b[35m42.12 \u001b[39m | \u001b[35m0.2911 \u001b[39m | \u001b[35m2.636 \u001b[39m | \u001b[35m5.301 \u001b[39m | \u001b[35m373.8 \u001b[39m | \u001b[35m46.2 \u001b[39m |\n", "| \u001b[39m4 \u001b[39m | \u001b[39m0.9801 \u001b[39m | \u001b[39m23.3 \u001b[39m | \u001b[39m0.3621 \u001b[39m | \u001b[39m6.507 \u001b[39m | \u001b[39m4.511 \u001b[39m | \u001b[39m362.9 \u001b[39m | \u001b[39m44.93 \u001b[39m |\n", "| \u001b[39m5 \u001b[39m | \u001b[39m0.9879 \u001b[39m | \u001b[39m24.44 \u001b[39m | \u001b[39m0.8067 \u001b[39m | \u001b[39m2.797 \u001b[39m | \u001b[39m11.26 \u001b[39m | \u001b[39m633.2 \u001b[39m | \u001b[39m42.37 \u001b[39m |\n", "| \u001b[35m6 \u001b[39m | \u001b[35m0.9912 \u001b[39m | \u001b[35m31.55 \u001b[39m | \u001b[35m0.2535 \u001b[39m | \u001b[35m1.585 \u001b[39m | \u001b[35m19.08 \u001b[39m | \u001b[35m969.1 \u001b[39m | \u001b[35m48.47 \u001b[39m |\n", "| \u001b[39m7 \u001b[39m | \u001b[39m0.9824 \u001b[39m | \u001b[39m17.32 \u001b[39m | \u001b[39m0.1879 \u001b[39m | \u001b[39m7.158 \u001b[39m | \u001b[39m9.923 \u001b[39m | \u001b[39m209.8 \u001b[39m | \u001b[39m45.96 \u001b[39m |\n", "| \u001b[39m8 \u001b[39m | \u001b[39m0.9641 \u001b[39m | \u001b[39m4.616 \u001b[39m | \u001b[39m0.9184 \u001b[39m | \u001b[39m3.329 \u001b[39m | \u001b[39m13.93 \u001b[39m | \u001b[39m380.5 \u001b[39m | \u001b[39m46.16 \u001b[39m |\n", "| \u001b[39m9 \u001b[39m | \u001b[39m0.9785 \u001b[39m | \u001b[39m28.7 \u001b[39m | \u001b[39m0.2664 \u001b[39m | \u001b[39m9.726 \u001b[39m | \u001b[39m15.95 \u001b[39m | \u001b[39m945.5 \u001b[39m | \u001b[39m49.16 \u001b[39m |\n", "| \u001b[39m10 \u001b[39m | \u001b[39m0.9901 \u001b[39m | \u001b[39m31.1 \u001b[39m | \u001b[39m0.9297 \u001b[39m | \u001b[39m1.796 \u001b[39m | \u001b[39m5.528 \u001b[39m | \u001b[39m140.7 \u001b[39m | \u001b[39m44.6 \u001b[39m |\n", "| \u001b[39m11 \u001b[39m | \u001b[39m0.9912 \u001b[39m | \u001b[39m30.34 \u001b[39m | \u001b[39m0.3035 \u001b[39m | \u001b[39m1.372 \u001b[39m | \u001b[39m5.127 \u001b[39m | \u001b[39m141.6 \u001b[39m | \u001b[39m45.75 \u001b[39m |\n", "| \u001b[39m12 \u001b[39m | \u001b[39m0.9862 \u001b[39m | \u001b[39m35.63 \u001b[39m | \u001b[39m0.3391 \u001b[39m | \u001b[39m3.082 \u001b[39m | \u001b[39m5.352 \u001b[39m | \u001b[39m154.2 \u001b[39m | \u001b[39m49.33 \u001b[39m |\n", "| \u001b[39m13 \u001b[39m | \u001b[39m0.9912 \u001b[39m | \u001b[39m29.41 \u001b[39m | \u001b[39m0.2109 \u001b[39m | \u001b[39m1.099 \u001b[39m | \u001b[39m19.64 \u001b[39m | \u001b[39m985.2 \u001b[39m | \u001b[39m46.41 \u001b[39m |\n", "| \u001b[39m14 \u001b[39m | \u001b[39m0.9906 \u001b[39m | \u001b[39m47.74 \u001b[39m | \u001b[39m0.8754 \u001b[39m | \u001b[39m1.78 \u001b[39m | \u001b[39m19.09 \u001b[39m | \u001b[39m979.6 \u001b[39m | \u001b[39m42.01 \u001b[39m |\n", "| \u001b[39m15 \u001b[39m | \u001b[39m0.9807 \u001b[39m | \u001b[39m33.58 \u001b[39m | \u001b[39m0.2945 \u001b[39m | \u001b[39m7.06 \u001b[39m | \u001b[39m2.349 \u001b[39m | \u001b[39m982.9 \u001b[39m | \u001b[39m48.02 \u001b[39m |\n", "| \u001b[39m16 \u001b[39m | \u001b[39m0.9857 \u001b[39m | \u001b[39m13.08 \u001b[39m | \u001b[39m0.5614 \u001b[39m | \u001b[39m3.371 \u001b[39m | \u001b[39m2.048 \u001b[39m | \u001b[39m150.6 \u001b[39m | \u001b[39m42.61 \u001b[39m |\n", "| \u001b[39m17 \u001b[39m | \u001b[39m0.9719 \u001b[39m | \u001b[39m11.27 \u001b[39m | \u001b[39m0.9694 \u001b[39m | \u001b[39m8.007 \u001b[39m | \u001b[39m19.21 \u001b[39m | \u001b[39m974.6 \u001b[39m | \u001b[39m44.29 \u001b[39m |\n", "| \u001b[39m18 \u001b[39m | \u001b[39m0.9741 \u001b[39m | \u001b[39m49.2 \u001b[39m | \u001b[39m0.9081 \u001b[39m | \u001b[39m8.855 \u001b[39m | \u001b[39m19.36 \u001b[39m | \u001b[39m969.3 \u001b[39m | \u001b[39m49.78 \u001b[39m |\n" ] }, { "ename": "KeyboardInterrupt", "evalue": "", "output_type": "error", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[1;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", "Cell \u001b[1;32mIn[21], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m \u001b[43moptimizer\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmaximize\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 2\u001b[0m \u001b[43m \u001b[49m\u001b[43minit_points\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m10\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[0;32m 3\u001b[0m \u001b[43m \u001b[49m\u001b[43mn_iter\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m15\u001b[39;49m\n\u001b[0;32m 4\u001b[0m \u001b[43m)\u001b[49m\n\u001b[0;32m 6\u001b[0m \u001b[38;5;28mprint\u001b[39m(optimizer\u001b[38;5;241m.\u001b[39mmax)\n", "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python310\\site-packages\\bayes_opt\\bayesian_optimization.py:338\u001b[0m, in \u001b[0;36mBayesianOptimization.maximize\u001b[1;34m(self, init_points, n_iter)\u001b[0m\n\u001b[0;32m 336\u001b[0m x_probe \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msuggest()\n\u001b[0;32m 337\u001b[0m iteration \u001b[38;5;241m+\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m1\u001b[39m\n\u001b[1;32m--> 338\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mprobe\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx_probe\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mlazy\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m)\u001b[49m\n\u001b[0;32m 340\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_bounds_transformer \u001b[38;5;129;01mand\u001b[39;00m iteration \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[0;32m 341\u001b[0m \u001b[38;5;66;03m# The bounds transformer should only modify the bounds after\u001b[39;00m\n\u001b[0;32m 342\u001b[0m \u001b[38;5;66;03m# the init_points points (only for the true iterations)\u001b[39;00m\n\u001b[0;32m 343\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mset_bounds(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_bounds_transformer\u001b[38;5;241m.\u001b[39mtransform(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_space))\n", "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python310\\site-packages\\bayes_opt\\bayesian_optimization.py:270\u001b[0m, in \u001b[0;36mBayesianOptimization.probe\u001b[1;34m(self, params, lazy)\u001b[0m\n\u001b[0;32m 268\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_queue\u001b[38;5;241m.\u001b[39mappend(params)\n\u001b[0;32m 269\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m--> 270\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_space\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mprobe\u001b[49m\u001b[43m(\u001b[49m\u001b[43mparams\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 271\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdispatch(Events\u001b[38;5;241m.\u001b[39mOPTIMIZATION_STEP)\n", "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python310\\site-packages\\bayes_opt\\target_space.py:418\u001b[0m, in \u001b[0;36mTargetSpace.probe\u001b[1;34m(self, params)\u001b[0m\n\u001b[0;32m 416\u001b[0m error_msg \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mNo target function has been provided.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 417\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(error_msg)\n\u001b[1;32m--> 418\u001b[0m target \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mtarget_func(\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mdict_params)\n\u001b[0;32m 420\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_constraint \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m 421\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mregister(x, target)\n", "Cell \u001b[1;32mIn[20], line 12\u001b[0m, in \u001b[0;36mrf_csv\u001b[1;34m(n_estimators, max_depth, min_samples_split, min_samples_leaf, max_features, random_state)\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21mrf_csv\u001b[39m(n_estimators , max_depth , min_samples_split , min_samples_leaf , max_features , random_state ):\n\u001b[0;32m 3\u001b[0m \tparams \u001b[38;5;241m=\u001b[39m {\n\u001b[0;32m 4\u001b[0m \t\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mn_estimators\u001b[39m\u001b[38;5;124m'\u001b[39m : \u001b[38;5;28mint\u001b[39m(n_estimators),\n\u001b[0;32m 5\u001b[0m \t\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mmax_depth\u001b[39m\u001b[38;5;124m'\u001b[39m : \u001b[38;5;28mint\u001b[39m(max_depth),\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 9\u001b[0m \t\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mrandom_state\u001b[39m\u001b[38;5;124m'\u001b[39m : \u001b[38;5;28mint\u001b[39m(random_state)\n\u001b[0;32m 10\u001b[0m \t}\n\u001b[1;32m---> 12\u001b[0m \tcv_results \u001b[38;5;241m=\u001b[39m \u001b[43mcross_val_score\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 13\u001b[0m \u001b[43m \u001b[49m\u001b[43mRandomForestClassifier\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mparams\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 14\u001b[0m \u001b[43m \u001b[49m\u001b[43mx_train\u001b[49m\u001b[43m \u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43my_train\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 15\u001b[0m \u001b[43m \u001b[49m\u001b[43mscoring\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43maccuracy\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[0;32m 16\u001b[0m \u001b[43m \u001b[49m\u001b[43mcv\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m5\u001b[39;49m\n\u001b[0;32m 17\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 19\u001b[0m \t\u001b[38;5;28;01mreturn\u001b[39;00m cv_results\u001b[38;5;241m.\u001b[39mmean()\n", "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python310\\site-packages\\sklearn\\utils\\_param_validation.py:216\u001b[0m, in \u001b[0;36mvalidate_params..decorator..wrapper\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m 210\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m 211\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m config_context(\n\u001b[0;32m 212\u001b[0m skip_parameter_validation\u001b[38;5;241m=\u001b[39m(\n\u001b[0;32m 213\u001b[0m prefer_skip_nested_validation \u001b[38;5;129;01mor\u001b[39;00m global_skip_validation\n\u001b[0;32m 214\u001b[0m )\n\u001b[0;32m 215\u001b[0m ):\n\u001b[1;32m--> 216\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m func(\u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m 217\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m InvalidParameterError \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[0;32m 218\u001b[0m \u001b[38;5;66;03m# When the function is just a wrapper around an estimator, we allow\u001b[39;00m\n\u001b[0;32m 219\u001b[0m \u001b[38;5;66;03m# the function to delegate validation to the estimator, but we replace\u001b[39;00m\n\u001b[0;32m 220\u001b[0m \u001b[38;5;66;03m# the name of the estimator by the name of the function in the error\u001b[39;00m\n\u001b[0;32m 221\u001b[0m \u001b[38;5;66;03m# message to avoid confusion.\u001b[39;00m\n\u001b[0;32m 222\u001b[0m msg \u001b[38;5;241m=\u001b[39m re\u001b[38;5;241m.\u001b[39msub(\n\u001b[0;32m 223\u001b[0m \u001b[38;5;124mr\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mparameter of \u001b[39m\u001b[38;5;124m\\\u001b[39m\u001b[38;5;124mw+ must be\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[0;32m 224\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mparameter of \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfunc\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__qualname__\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m must be\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[0;32m 225\u001b[0m \u001b[38;5;28mstr\u001b[39m(e),\n\u001b[0;32m 226\u001b[0m )\n", "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python310\\site-packages\\sklearn\\model_selection\\_validation.py:684\u001b[0m, in \u001b[0;36mcross_val_score\u001b[1;34m(estimator, X, y, groups, scoring, cv, n_jobs, verbose, params, pre_dispatch, error_score)\u001b[0m\n\u001b[0;32m 681\u001b[0m \u001b[38;5;66;03m# To ensure multimetric format is not supported\u001b[39;00m\n\u001b[0;32m 682\u001b[0m scorer \u001b[38;5;241m=\u001b[39m check_scoring(estimator, scoring\u001b[38;5;241m=\u001b[39mscoring)\n\u001b[1;32m--> 684\u001b[0m cv_results \u001b[38;5;241m=\u001b[39m \u001b[43mcross_validate\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 685\u001b[0m \u001b[43m \u001b[49m\u001b[43mestimator\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mestimator\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 686\u001b[0m \u001b[43m \u001b[49m\u001b[43mX\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mX\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 687\u001b[0m \u001b[43m \u001b[49m\u001b[43my\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43my\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 688\u001b[0m \u001b[43m \u001b[49m\u001b[43mgroups\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mgroups\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 689\u001b[0m \u001b[43m \u001b[49m\u001b[43mscoring\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m{\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mscore\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mscorer\u001b[49m\u001b[43m}\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 690\u001b[0m \u001b[43m \u001b[49m\u001b[43mcv\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcv\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 691\u001b[0m \u001b[43m \u001b[49m\u001b[43mn_jobs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mn_jobs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 692\u001b[0m \u001b[43m \u001b[49m\u001b[43mverbose\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mverbose\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 693\u001b[0m \u001b[43m \u001b[49m\u001b[43mparams\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mparams\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 694\u001b[0m \u001b[43m \u001b[49m\u001b[43mpre_dispatch\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mpre_dispatch\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 695\u001b[0m \u001b[43m \u001b[49m\u001b[43merror_score\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43merror_score\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 696\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 697\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m cv_results[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mtest_score\u001b[39m\u001b[38;5;124m\"\u001b[39m]\n", "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python310\\site-packages\\sklearn\\utils\\_param_validation.py:216\u001b[0m, in \u001b[0;36mvalidate_params..decorator..wrapper\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m 210\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m 211\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m config_context(\n\u001b[0;32m 212\u001b[0m skip_parameter_validation\u001b[38;5;241m=\u001b[39m(\n\u001b[0;32m 213\u001b[0m prefer_skip_nested_validation \u001b[38;5;129;01mor\u001b[39;00m global_skip_validation\n\u001b[0;32m 214\u001b[0m )\n\u001b[0;32m 215\u001b[0m ):\n\u001b[1;32m--> 216\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m func(\u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m 217\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m InvalidParameterError \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[0;32m 218\u001b[0m \u001b[38;5;66;03m# When the function is just a wrapper around an estimator, we allow\u001b[39;00m\n\u001b[0;32m 219\u001b[0m \u001b[38;5;66;03m# the function to delegate validation to the estimator, but we replace\u001b[39;00m\n\u001b[0;32m 220\u001b[0m \u001b[38;5;66;03m# the name of the estimator by the name of the function in the error\u001b[39;00m\n\u001b[0;32m 221\u001b[0m \u001b[38;5;66;03m# message to avoid confusion.\u001b[39;00m\n\u001b[0;32m 222\u001b[0m msg \u001b[38;5;241m=\u001b[39m re\u001b[38;5;241m.\u001b[39msub(\n\u001b[0;32m 223\u001b[0m \u001b[38;5;124mr\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mparameter of \u001b[39m\u001b[38;5;124m\\\u001b[39m\u001b[38;5;124mw+ must be\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[0;32m 224\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mparameter of \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfunc\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__qualname__\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m must be\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[0;32m 225\u001b[0m \u001b[38;5;28mstr\u001b[39m(e),\n\u001b[0;32m 226\u001b[0m )\n", "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python310\\site-packages\\sklearn\\model_selection\\_validation.py:411\u001b[0m, in \u001b[0;36mcross_validate\u001b[1;34m(estimator, X, y, groups, scoring, cv, n_jobs, verbose, params, pre_dispatch, return_train_score, return_estimator, return_indices, error_score)\u001b[0m\n\u001b[0;32m 408\u001b[0m \u001b[38;5;66;03m# We clone the estimator to make sure that all the folds are\u001b[39;00m\n\u001b[0;32m 409\u001b[0m \u001b[38;5;66;03m# independent, and that it is pickle-able.\u001b[39;00m\n\u001b[0;32m 410\u001b[0m parallel \u001b[38;5;241m=\u001b[39m Parallel(n_jobs\u001b[38;5;241m=\u001b[39mn_jobs, verbose\u001b[38;5;241m=\u001b[39mverbose, pre_dispatch\u001b[38;5;241m=\u001b[39mpre_dispatch)\n\u001b[1;32m--> 411\u001b[0m results \u001b[38;5;241m=\u001b[39m \u001b[43mparallel\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 412\u001b[0m \u001b[43m \u001b[49m\u001b[43mdelayed\u001b[49m\u001b[43m(\u001b[49m\u001b[43m_fit_and_score\u001b[49m\u001b[43m)\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 413\u001b[0m \u001b[43m \u001b[49m\u001b[43mclone\u001b[49m\u001b[43m(\u001b[49m\u001b[43mestimator\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 414\u001b[0m \u001b[43m \u001b[49m\u001b[43mX\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 415\u001b[0m \u001b[43m \u001b[49m\u001b[43my\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 416\u001b[0m \u001b[43m \u001b[49m\u001b[43mscorer\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mscorers\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 417\u001b[0m \u001b[43m \u001b[49m\u001b[43mtrain\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mtrain\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 418\u001b[0m \u001b[43m \u001b[49m\u001b[43mtest\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mtest\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 419\u001b[0m \u001b[43m \u001b[49m\u001b[43mverbose\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mverbose\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 420\u001b[0m \u001b[43m \u001b[49m\u001b[43mparameters\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[0;32m 421\u001b[0m \u001b[43m \u001b[49m\u001b[43mfit_params\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrouted_params\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mestimator\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfit\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 422\u001b[0m \u001b[43m \u001b[49m\u001b[43mscore_params\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrouted_params\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mscorer\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mscore\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 423\u001b[0m \u001b[43m \u001b[49m\u001b[43mreturn_train_score\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mreturn_train_score\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 424\u001b[0m \u001b[43m \u001b[49m\u001b[43mreturn_times\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[0;32m 425\u001b[0m \u001b[43m \u001b[49m\u001b[43mreturn_estimator\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mreturn_estimator\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 426\u001b[0m \u001b[43m \u001b[49m\u001b[43merror_score\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43merror_score\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 427\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 428\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mtrain\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mtest\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mindices\u001b[49m\n\u001b[0;32m 429\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 431\u001b[0m _warn_or_raise_about_fit_failures(results, error_score)\n\u001b[0;32m 433\u001b[0m \u001b[38;5;66;03m# For callable scoring, the return type is only know after calling. If the\u001b[39;00m\n\u001b[0;32m 434\u001b[0m \u001b[38;5;66;03m# return type is a dictionary, the error scores can now be inserted with\u001b[39;00m\n\u001b[0;32m 435\u001b[0m \u001b[38;5;66;03m# the correct key.\u001b[39;00m\n", "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python310\\site-packages\\sklearn\\utils\\parallel.py:77\u001b[0m, in \u001b[0;36mParallel.__call__\u001b[1;34m(self, iterable)\u001b[0m\n\u001b[0;32m 72\u001b[0m config \u001b[38;5;241m=\u001b[39m get_config()\n\u001b[0;32m 73\u001b[0m iterable_with_config \u001b[38;5;241m=\u001b[39m (\n\u001b[0;32m 74\u001b[0m (_with_config(delayed_func, config), args, kwargs)\n\u001b[0;32m 75\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m delayed_func, args, kwargs \u001b[38;5;129;01min\u001b[39;00m iterable\n\u001b[0;32m 76\u001b[0m )\n\u001b[1;32m---> 77\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__call__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43miterable_with_config\u001b[49m\u001b[43m)\u001b[49m\n", "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python310\\site-packages\\joblib\\parallel.py:1918\u001b[0m, in \u001b[0;36mParallel.__call__\u001b[1;34m(self, iterable)\u001b[0m\n\u001b[0;32m 1916\u001b[0m output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_get_sequential_output(iterable)\n\u001b[0;32m 1917\u001b[0m \u001b[38;5;28mnext\u001b[39m(output)\n\u001b[1;32m-> 1918\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m output \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mreturn_generator \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;28;43mlist\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43moutput\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 1920\u001b[0m \u001b[38;5;66;03m# Let's create an ID that uniquely identifies the current call. If the\u001b[39;00m\n\u001b[0;32m 1921\u001b[0m \u001b[38;5;66;03m# call is interrupted early and that the same instance is immediately\u001b[39;00m\n\u001b[0;32m 1922\u001b[0m \u001b[38;5;66;03m# re-used, this id will be used to prevent workers that were\u001b[39;00m\n\u001b[0;32m 1923\u001b[0m \u001b[38;5;66;03m# concurrently finalizing a task from the previous call to run the\u001b[39;00m\n\u001b[0;32m 1924\u001b[0m \u001b[38;5;66;03m# callback.\u001b[39;00m\n\u001b[0;32m 1925\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_lock:\n", "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python310\\site-packages\\joblib\\parallel.py:1847\u001b[0m, in \u001b[0;36mParallel._get_sequential_output\u001b[1;34m(self, iterable)\u001b[0m\n\u001b[0;32m 1845\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mn_dispatched_batches \u001b[38;5;241m+\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m1\u001b[39m\n\u001b[0;32m 1846\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mn_dispatched_tasks \u001b[38;5;241m+\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m1\u001b[39m\n\u001b[1;32m-> 1847\u001b[0m res \u001b[38;5;241m=\u001b[39m func(\u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m 1848\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mn_completed_tasks \u001b[38;5;241m+\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m1\u001b[39m\n\u001b[0;32m 1849\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mprint_progress()\n", "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python310\\site-packages\\sklearn\\utils\\parallel.py:139\u001b[0m, in \u001b[0;36m_FuncWrapper.__call__\u001b[1;34m(self, *args, **kwargs)\u001b[0m\n\u001b[0;32m 137\u001b[0m config \u001b[38;5;241m=\u001b[39m {}\n\u001b[0;32m 138\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m config_context(\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mconfig):\n\u001b[1;32m--> 139\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfunction(\u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n", "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python310\\site-packages\\sklearn\\model_selection\\_validation.py:866\u001b[0m, in \u001b[0;36m_fit_and_score\u001b[1;34m(estimator, X, y, scorer, train, test, verbose, parameters, fit_params, score_params, return_train_score, return_parameters, return_n_test_samples, return_times, return_estimator, split_progress, candidate_progress, error_score)\u001b[0m\n\u001b[0;32m 864\u001b[0m estimator\u001b[38;5;241m.\u001b[39mfit(X_train, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mfit_params)\n\u001b[0;32m 865\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m--> 866\u001b[0m estimator\u001b[38;5;241m.\u001b[39mfit(X_train, y_train, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mfit_params)\n\u001b[0;32m 868\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m:\n\u001b[0;32m 869\u001b[0m \u001b[38;5;66;03m# Note fit time as time until error\u001b[39;00m\n\u001b[0;32m 870\u001b[0m fit_time \u001b[38;5;241m=\u001b[39m time\u001b[38;5;241m.\u001b[39mtime() \u001b[38;5;241m-\u001b[39m start_time\n", "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python310\\site-packages\\sklearn\\base.py:1389\u001b[0m, in \u001b[0;36m_fit_context..decorator..wrapper\u001b[1;34m(estimator, *args, **kwargs)\u001b[0m\n\u001b[0;32m 1382\u001b[0m estimator\u001b[38;5;241m.\u001b[39m_validate_params()\n\u001b[0;32m 1384\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m config_context(\n\u001b[0;32m 1385\u001b[0m skip_parameter_validation\u001b[38;5;241m=\u001b[39m(\n\u001b[0;32m 1386\u001b[0m prefer_skip_nested_validation \u001b[38;5;129;01mor\u001b[39;00m global_skip_validation\n\u001b[0;32m 1387\u001b[0m )\n\u001b[0;32m 1388\u001b[0m ):\n\u001b[1;32m-> 1389\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m fit_method(estimator, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n", "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python310\\site-packages\\sklearn\\ensemble\\_forest.py:487\u001b[0m, in \u001b[0;36mBaseForest.fit\u001b[1;34m(self, X, y, sample_weight)\u001b[0m\n\u001b[0;32m 476\u001b[0m trees \u001b[38;5;241m=\u001b[39m [\n\u001b[0;32m 477\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_make_estimator(append\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mFalse\u001b[39;00m, random_state\u001b[38;5;241m=\u001b[39mrandom_state)\n\u001b[0;32m 478\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(n_more_estimators)\n\u001b[0;32m 479\u001b[0m ]\n\u001b[0;32m 481\u001b[0m \u001b[38;5;66;03m# Parallel loop: we prefer the threading backend as the Cython code\u001b[39;00m\n\u001b[0;32m 482\u001b[0m \u001b[38;5;66;03m# for fitting the trees is internally releasing the Python GIL\u001b[39;00m\n\u001b[0;32m 483\u001b[0m \u001b[38;5;66;03m# making threading more efficient than multiprocessing in\u001b[39;00m\n\u001b[0;32m 484\u001b[0m \u001b[38;5;66;03m# that case. However, for joblib 0.12+ we respect any\u001b[39;00m\n\u001b[0;32m 485\u001b[0m \u001b[38;5;66;03m# parallel_backend contexts set at a higher level,\u001b[39;00m\n\u001b[0;32m 486\u001b[0m \u001b[38;5;66;03m# since correctness does not rely on using threads.\u001b[39;00m\n\u001b[1;32m--> 487\u001b[0m trees \u001b[38;5;241m=\u001b[39m \u001b[43mParallel\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 488\u001b[0m \u001b[43m \u001b[49m\u001b[43mn_jobs\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[43mn_jobs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 489\u001b[0m \u001b[43m \u001b[49m\u001b[43mverbose\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[43mverbose\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 490\u001b[0m \u001b[43m \u001b[49m\u001b[43mprefer\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mthreads\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[0;32m 491\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 492\u001b[0m \u001b[43m \u001b[49m\u001b[43mdelayed\u001b[49m\u001b[43m(\u001b[49m\u001b[43m_parallel_build_trees\u001b[49m\u001b[43m)\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 493\u001b[0m \u001b[43m \u001b[49m\u001b[43mt\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 494\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbootstrap\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 495\u001b[0m \u001b[43m \u001b[49m\u001b[43mX\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 496\u001b[0m \u001b[43m \u001b[49m\u001b[43my\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 497\u001b[0m \u001b[43m \u001b[49m\u001b[43msample_weight\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 498\u001b[0m \u001b[43m \u001b[49m\u001b[43mi\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 499\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mlen\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mtrees\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 500\u001b[0m \u001b[43m \u001b[49m\u001b[43mverbose\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[43mverbose\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 501\u001b[0m \u001b[43m \u001b[49m\u001b[43mclass_weight\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[43mclass_weight\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 502\u001b[0m \u001b[43m \u001b[49m\u001b[43mn_samples_bootstrap\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mn_samples_bootstrap\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 503\u001b[0m \u001b[43m \u001b[49m\u001b[43mmissing_values_in_feature_mask\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmissing_values_in_feature_mask\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 504\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 505\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mi\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mt\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43menumerate\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mtrees\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 506\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 508\u001b[0m \u001b[38;5;66;03m# Collect newly grown trees\u001b[39;00m\n\u001b[0;32m 509\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mestimators_\u001b[38;5;241m.\u001b[39mextend(trees)\n", "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python310\\site-packages\\sklearn\\utils\\parallel.py:77\u001b[0m, in \u001b[0;36mParallel.__call__\u001b[1;34m(self, iterable)\u001b[0m\n\u001b[0;32m 72\u001b[0m config \u001b[38;5;241m=\u001b[39m get_config()\n\u001b[0;32m 73\u001b[0m iterable_with_config \u001b[38;5;241m=\u001b[39m (\n\u001b[0;32m 74\u001b[0m (_with_config(delayed_func, config), args, kwargs)\n\u001b[0;32m 75\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m delayed_func, args, kwargs \u001b[38;5;129;01min\u001b[39;00m iterable\n\u001b[0;32m 76\u001b[0m )\n\u001b[1;32m---> 77\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__call__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43miterable_with_config\u001b[49m\u001b[43m)\u001b[49m\n", "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python310\\site-packages\\joblib\\parallel.py:1918\u001b[0m, in \u001b[0;36mParallel.__call__\u001b[1;34m(self, iterable)\u001b[0m\n\u001b[0;32m 1916\u001b[0m output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_get_sequential_output(iterable)\n\u001b[0;32m 1917\u001b[0m \u001b[38;5;28mnext\u001b[39m(output)\n\u001b[1;32m-> 1918\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m output \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mreturn_generator \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;28;43mlist\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43moutput\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 1920\u001b[0m \u001b[38;5;66;03m# Let's create an ID that uniquely identifies the current call. If the\u001b[39;00m\n\u001b[0;32m 1921\u001b[0m \u001b[38;5;66;03m# call is interrupted early and that the same instance is immediately\u001b[39;00m\n\u001b[0;32m 1922\u001b[0m \u001b[38;5;66;03m# re-used, this id will be used to prevent workers that were\u001b[39;00m\n\u001b[0;32m 1923\u001b[0m \u001b[38;5;66;03m# concurrently finalizing a task from the previous call to run the\u001b[39;00m\n\u001b[0;32m 1924\u001b[0m \u001b[38;5;66;03m# callback.\u001b[39;00m\n\u001b[0;32m 1925\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_lock:\n", "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python310\\site-packages\\joblib\\parallel.py:1847\u001b[0m, in \u001b[0;36mParallel._get_sequential_output\u001b[1;34m(self, iterable)\u001b[0m\n\u001b[0;32m 1845\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mn_dispatched_batches \u001b[38;5;241m+\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m1\u001b[39m\n\u001b[0;32m 1846\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mn_dispatched_tasks \u001b[38;5;241m+\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m1\u001b[39m\n\u001b[1;32m-> 1847\u001b[0m res \u001b[38;5;241m=\u001b[39m func(\u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m 1848\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mn_completed_tasks \u001b[38;5;241m+\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m1\u001b[39m\n\u001b[0;32m 1849\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mprint_progress()\n", "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python310\\site-packages\\sklearn\\utils\\parallel.py:139\u001b[0m, in \u001b[0;36m_FuncWrapper.__call__\u001b[1;34m(self, *args, **kwargs)\u001b[0m\n\u001b[0;32m 137\u001b[0m config \u001b[38;5;241m=\u001b[39m {}\n\u001b[0;32m 138\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m config_context(\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mconfig):\n\u001b[1;32m--> 139\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfunction(\u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n", "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python310\\site-packages\\sklearn\\ensemble\\_forest.py:189\u001b[0m, in \u001b[0;36m_parallel_build_trees\u001b[1;34m(tree, bootstrap, X, y, sample_weight, tree_idx, n_trees, verbose, class_weight, n_samples_bootstrap, missing_values_in_feature_mask)\u001b[0m\n\u001b[0;32m 186\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m class_weight \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mbalanced_subsample\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[0;32m 187\u001b[0m curr_sample_weight \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m=\u001b[39m compute_sample_weight(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mbalanced\u001b[39m\u001b[38;5;124m\"\u001b[39m, y, indices\u001b[38;5;241m=\u001b[39mindices)\n\u001b[1;32m--> 189\u001b[0m \u001b[43mtree\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_fit\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 190\u001b[0m \u001b[43m \u001b[49m\u001b[43mX\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 191\u001b[0m \u001b[43m \u001b[49m\u001b[43my\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 192\u001b[0m \u001b[43m \u001b[49m\u001b[43msample_weight\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcurr_sample_weight\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 193\u001b[0m \u001b[43m \u001b[49m\u001b[43mcheck_input\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[0;32m 194\u001b[0m \u001b[43m \u001b[49m\u001b[43mmissing_values_in_feature_mask\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmissing_values_in_feature_mask\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 195\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 196\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m 197\u001b[0m tree\u001b[38;5;241m.\u001b[39m_fit(\n\u001b[0;32m 198\u001b[0m X,\n\u001b[0;32m 199\u001b[0m y,\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 202\u001b[0m missing_values_in_feature_mask\u001b[38;5;241m=\u001b[39mmissing_values_in_feature_mask,\n\u001b[0;32m 203\u001b[0m )\n", "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python310\\site-packages\\sklearn\\tree\\_classes.py:472\u001b[0m, in \u001b[0;36mBaseDecisionTree._fit\u001b[1;34m(self, X, y, sample_weight, check_input, missing_values_in_feature_mask)\u001b[0m\n\u001b[0;32m 461\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m 462\u001b[0m builder \u001b[38;5;241m=\u001b[39m BestFirstTreeBuilder(\n\u001b[0;32m 463\u001b[0m splitter,\n\u001b[0;32m 464\u001b[0m min_samples_split,\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 469\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmin_impurity_decrease,\n\u001b[0;32m 470\u001b[0m )\n\u001b[1;32m--> 472\u001b[0m \u001b[43mbuilder\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbuild\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtree_\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mX\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43my\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msample_weight\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmissing_values_in_feature_mask\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 474\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mn_outputs_ \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m1\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m is_classifier(\u001b[38;5;28mself\u001b[39m):\n\u001b[0;32m 475\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mn_classes_ \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mn_classes_[\u001b[38;5;241m0\u001b[39m]\n", "\u001b[1;31mKeyboardInterrupt\u001b[0m: " ] } ], "source": [ "optimizer.maximize(\n", " init_points = 10,\n", " n_iter = 15\n", ")\n", "\n", "print(optimizer.max)" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [], "source": [ "rf_params = {\n", " 'n_estimators': 969,\n", " 'max_depth': 31,\n", " 'min_samples_split': 19,\n", " 'min_samples_leaf': 2,\n", " 'max_features': 0.2535,\n", " 'random_state': 48,\n", " 'n_jobs': -1 # Use all available cores\n", "}" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [], "source": [ "rf_model = RandomForestClassifier(**rf_params)" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [], "source": [ "scoring = {\n", " 'precision' : make_scorer(precision_score),\n", " 'recall' : make_scorer(recall_score),\n", " 'f1' : make_scorer(f1_score)\n", "}" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [], "source": [ "skf = StratifiedKFold(n_splits=5 , shuffle=True , random_state=43)" ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [], "source": [ "precision_scores = cross_val_score(rf_model , x_train , y_train , scoring=scoring['precision'] , cv=skf)\n", "recall_scores = cross_val_score(rf_model , x_train , y_train , scoring=scoring['recall'] , cv=skf)\n", "f1_scores = cross_val_score(rf_model , x_train , y_train , scoring=scoring['f1'] , cv=skf)\n", "auc_scores = cross_val_score(rf_model , x_train , y_train , scoring='roc_auc' , cv=skf)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Roc and auc curves also" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [], "source": [ "from sklearn.metrics import roc_auc_score , roc_curve , auc , RocCurveDisplay" ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Cross-validation results on training data: \n", "Mean Precision 0.9914 (+/- 0.0107)\n", "Mean Recall 0.9762 (+/- 0.0198)\n", "Mean F1-Score 0.9837 (+/- 0.0101)\n", "Mean F1-Score: 0.9837 (+/- 0.0101)\n", "Mean AUC-ROC: 0.9987 (+/- 0.0021)\n" ] } ], "source": [ "print(\"Cross-validation results on training data: \")\n", "print(f\"Mean Precision {np.mean(precision_scores):.4f} (+/- {np.std(precision_scores) * 2:.4f})\")\n", "print(f\"Mean Recall {np.mean(recall_scores):.4f} (+/- {np.std(recall_scores) * 2:.4f})\")\n", "print(f\"Mean F1-Score {np.mean(f1_scores):.4f} (+/- {np.std(f1_scores) * 2:.4f})\")\n", "print(f\"Mean F1-Score: {np.mean(f1_scores):.4f} (+/- {np.std(f1_scores) * 2:.4f})\")\n", "print(f\"Mean AUC-ROC: {np.mean(auc_scores):.4f} (+/- {np.std(auc_scores) * 2:.4f})\")" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
RandomForestClassifier(max_depth=31, max_features=0.2535, min_samples_leaf=2,\n",
       "                       min_samples_split=19, n_estimators=969, n_jobs=-1,\n",
       "                       random_state=48)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ "RandomForestClassifier(max_depth=31, max_features=0.2535, min_samples_leaf=2,\n", " min_samples_split=19, n_estimators=969, n_jobs=-1,\n", " random_state=48)" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rf_model.fit(x_train , y_train)" ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [], "source": [ "#get probability and predictions\n", "train_probs = rf_model.predict_proba(x_train)[: , 1]\n", "test_probs = rf_model.predict_proba(x_test)[: , 1]\n", "y_pred = rf_model.predict(x_test)" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [], "source": [ "#calculate roc curves\n", "train_fpr , train_tpr , _ =roc_curve(y_train , train_probs)\n", "test_fpr , test_tpr , _ = roc_curve(y_test , test_probs)" ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [], "source": [ "# Calculate AUC scores\n", "train_auc = auc(train_fpr, train_tpr)\n", "test_auc = auc(test_fpr, test_tpr)\n" ] }, { "cell_type": "code", "execution_count": 70, "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Plot ROC curves\n", "plt.figure(figsize=(10, 8))\n", "plt.plot(train_fpr, train_tpr, label=f'Training (AUC = {train_auc:.3f})', color='blue', linestyle='--')\n", "plt.plot(test_fpr, test_tpr, label=f'Test (AUC = {test_auc:.3f})', color='red')\n", "plt.plot([0, 1], [0, 1], 'k--', label='Random')\n", "plt.xlabel('False Positive Rate')\n", "plt.ylabel('True Positive Rate')\n", "plt.title('ROC Curves - Random Forest')\n", "plt.legend(loc='lower right')\n", "plt.grid(True)\n", "plt.show()\n" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [], "source": [ "y_pred = rf_model.predict(x_test)" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", " Test Set Results : \n", "\n", " Classification Report : \n", " precision recall f1-score support\n", "\n", " 0 0.98 0.99 0.99 297\n", " 1 0.99 0.97 0.98 157\n", "\n", " accuracy 0.98 454\n", " macro avg 0.99 0.98 0.98 454\n", "weighted avg 0.98 0.98 0.98 454\n", "\n" ] } ], "source": [ "print(\"\\n Test Set Results : \")\n", "print(\"\\n Classification Report : \")\n", "print(classification_report(y_test , y_pred))" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", " Confusion Matrix\n", "[[295 2]\n", " [ 5 152]]\n" ] } ], "source": [ "print(\"\\n Confusion Matrix\")\n", "print(confusion_matrix(y_test , y_pred))" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [], "source": [ "test_precision = precision_score(y_test , y_pred)\n", "test_recall = recall_score(y_test , y_pred)\n", "test_f1 = f1_score(y_test , y_pred)" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Test Set Metrics Summary:\n", "Precision: 0.9870\n", "Recall: 0.9682\n", "F1-Score: 0.9775\n", "AUC-ROC: 0.9988\n" ] } ], "source": [ "print(\"\\nTest Set Metrics Summary:\")\n", "print(f\"Precision: {test_precision:.4f}\")\n", "print(f\"Recall: {test_recall:.4f}\")\n", "print(f\"F1-Score: {test_f1:.4f}\")\n", "print(f\"AUC-ROC: {test_auc:.4f}\")" ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [], "source": [ "#optional plot feature importance\n", "feature_importance = rf_model.feature_importances_\n", "if hasattr(x_train, 'columns'): # If using pandas DataFrame\n", " plt.figure(figsize=(10, 6))\n", " importances = pd.DataFrame({\n", " 'feature': x_train.columns,\n", " 'importance': feature_importance\n", " }).sort_values('importance', ascending=False)\n", " \n", " plt.bar(range(len(importances)), importances['importance'])\n", " plt.xticks(range(len(importances)), importances['feature'], rotation=45, ha='right')\n", " plt.xlabel('Features')\n", " plt.ylabel('Importance')\n", " plt.title('Feature Importance in Random Forest Model')\n", " plt.tight_layout()\n", " plt.show()" ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [], "source": [ "# Function to get optimal threshold based on various criteria\n", "def get_optimal_threshold(y_true, y_prob):\n", " fpr, tpr, thresholds = roc_curve(y_true, y_prob)\n", " \n", " # Youden's J statistic (max(tpr - fpr))\n", " j_scores = tpr - fpr\n", " optimal_idx = np.argmax(j_scores)\n", " optimal_threshold = thresholds[optimal_idx]\n", " \n", " return optimal_threshold, fpr[optimal_idx], tpr[optimal_idx]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Meaning: Youden's J statistic measures the difference between TPR and FPR. It is used to find the threshold that maximizes the trade-off between sensitivity (TPR) and specificity (1 - FPR)." ] }, { "cell_type": "code", "execution_count": 75, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Optimal threshold (Youden's J): 0.3648\n", "At optimal threshold - FPR: 0.0067, TPR: 0.9809\n" ] } ], "source": [ "# Get optimal threshold\n", "optimal_threshold, opt_fpr, opt_tpr = get_optimal_threshold(y_test, test_probs)\n", "print(f\"\\nOptimal threshold (Youden's J): {optimal_threshold:.4f}\")\n", "print(f\"At optimal threshold - FPR: {opt_fpr:.4f}, TPR: {opt_tpr:.4f}\")\n" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Metrics at optimal threshold:\n", " precision recall f1-score support\n", "\n", " 0 0.99 0.99 0.99 297\n", " 1 0.99 0.98 0.98 157\n", "\n", " accuracy 0.99 454\n", " macro avg 0.99 0.99 0.99 454\n", "weighted avg 0.99 0.99 0.99 454\n", "\n" ] } ], "source": [ "# Create predictions using optimal threshold\n", "y_pred_optimal = (test_probs >= optimal_threshold).astype(int)\n", "print(\"\\nMetrics at optimal threshold:\")\n", "print(classification_report(y_test, y_pred_optimal))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Calculate ROC Curve:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fpr, tpr, thresholds = roc_curve(y_true, y_prob)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Input: y_true (true labels), y_prob (predicted probabilities).\n", "\n", "Output: FPR (False Positive Rate), TPR (True Positive Rate), thresholds for different decision points." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Compute Youden's J Statistic:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "j_scores = tpr - fpr\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Meaning: Youden's J statistic measures the difference between TPR and FPR. It is used to find the threshold that maximizes the trade-off between sensitivity (TPR) and specificity (1 - FPR)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Find Optimal Threshold Index:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "optimal_idx = np.argmax(j_scores)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Meaning: Identifies the index where Youden's J statistic is highest." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Get Optimal Threshold:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "optimal_threshold = thresholds[optimal_idx]\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Meaning: The decision threshold corresponding to the highest Youden's J statistic." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Return Optimal Threshold and Corresponding FPR/TPR:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "return optimal_threshold, fpr[optimal_idx], tpr[optimal_idx]\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Usage of the Function" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Calculate Optimal Threshold:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "optimal_threshold, opt_fpr, opt_tpr = get_optimal_threshold(y_test, test_probs)\n", "print(f\"\\nOptimal threshold (Youden's J): {optimal_threshold:.4f}\")\n", "print(f\"At optimal threshold - FPR: {opt_fpr:.4f}, TPR: {opt_tpr:.4f}\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Explanation: This calculates the optimal threshold using the get_optimal_threshold function for the test data." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Create Predictions Using Optimal Threshold:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "y_pred_optimal = (test_probs >= optimal_threshold).astype(int)\n", "print(\"\\nMetrics at optimal threshold:\")\n", "print(classification_report(y_test, y_pred_optimal))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Explanation: This creates new predictions (y_pred_optimal) using the optimal threshold and evaluates the performance using the classification report." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Benefits of Using Optimal Threshold:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Improved Model Performance:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Optimizes the trade-off between sensitivity and specificity.\n", "\n", "Reduces false positives and false negatives." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Application-Specific Customization:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Adjust thresholds based on the importance of precision or recall in your specific application.\n", "\n", "For example, in medical diagnostics, you might prioritize sensitivity to catch as many positive cases as possible." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Visualizing ROC Curve with Optimal Threshold:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can visually inspect the ROC curve to understand how the optimal threshold improves the balance between TPR and FPR." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Key Points to Remember:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Threshold selection is crucial in binary classification problems, especially where class imbalance or different costs of false positives and false negatives exist.\n", "\n", "Youden's J statistic provides a straightforward method to select a threshold that maximizes overall accuracy by balancing TPR and FPR." ] }, { "cell_type": "markdown", "metadata": {}, "source": [] } ], "metadata": { "kernelspec": { "display_name": "tf-gpu-2", "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.10.16" } }, "nbformat": 4, "nbformat_minor": 2 }