diff --git a/lab-hyperparameter-tuning-completed.ipynb b/lab-hyperparameter-tuning-completed.ipynb new file mode 100644 index 0000000..42b0621 --- /dev/null +++ b/lab-hyperparameter-tuning-completed.ipynb @@ -0,0 +1,448 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# LAB | Hyperparameter Tuning" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Load the data**\n", + "\n", + "Finally step in order to maximize the performance on your Spaceship Titanic model.\n", + "\n", + "The data can be found here:\n", + "\n", + "https://raw.githubusercontent.com/data-bootcamp-v4/data/main/spaceship_titanic.csv\n", + "\n", + "Metadata\n", + "\n", + "https://github.com/data-bootcamp-v4/data/blob/main/spaceship_titanic.md" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "So far we've been training and evaluating models with default values for hyperparameters.\n", + "\n", + "Today we will perform the same feature engineering as before, and then compare the best working models you got so far, but now fine tuning it's hyperparameters." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# Libraries\n", + "import pandas as pd\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "\n", + "from sklearn.compose import ColumnTransformer\n", + "from sklearn.ensemble import RandomForestClassifier\n", + "from sklearn.impute import SimpleImputer\n", + "from sklearn.metrics import accuracy_score, classification_report, confusion_matrix\n", + "from sklearn.model_selection import GridSearchCV, train_test_split\n", + "from sklearn.pipeline import Pipeline\n", + "from sklearn.preprocessing import OneHotEncoder, StandardScaler" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
PassengerIdHomePlanetCryoSleepCabinDestinationAgeVIPRoomServiceFoodCourtShoppingMallSpaVRDeckNameTransported
00001_01EuropaFalseB/0/PTRAPPIST-1e39.0False0.00.00.00.00.0Maham OfracculyFalse
10002_01EarthFalseF/0/STRAPPIST-1e24.0False109.09.025.0549.044.0Juanna VinesTrue
20003_01EuropaFalseA/0/STRAPPIST-1e58.0True43.03576.00.06715.049.0Altark SusentFalse
30003_02EuropaFalseA/0/STRAPPIST-1e33.0False0.01283.0371.03329.0193.0Solam SusentFalse
40004_01EarthFalseF/1/STRAPPIST-1e16.0False303.070.0151.0565.02.0Willy SantantinesTrue
\n", + "
" + ], + "text/plain": [ + " PassengerId HomePlanet CryoSleep Cabin Destination Age VIP \\\n", + "0 0001_01 Europa False B/0/P TRAPPIST-1e 39.0 False \n", + "1 0002_01 Earth False F/0/S TRAPPIST-1e 24.0 False \n", + "2 0003_01 Europa False A/0/S TRAPPIST-1e 58.0 True \n", + "3 0003_02 Europa False A/0/S TRAPPIST-1e 33.0 False \n", + "4 0004_01 Earth False F/1/S TRAPPIST-1e 16.0 False \n", + "\n", + " RoomService FoodCourt ShoppingMall Spa VRDeck Name \\\n", + "0 0.0 0.0 0.0 0.0 0.0 Maham Ofracculy \n", + "1 109.0 9.0 25.0 549.0 44.0 Juanna Vines \n", + "2 43.0 3576.0 0.0 6715.0 49.0 Altark Susent \n", + "3 0.0 1283.0 371.0 3329.0 193.0 Solam Susent \n", + "4 303.0 70.0 151.0 565.0 2.0 Willy Santantines \n", + "\n", + " Transported \n", + "0 False \n", + "1 True \n", + "2 False \n", + "3 False \n", + "4 True " + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "spaceship = pd.read_csv(\"https://raw.githubusercontent.com/data-bootcamp-v4/data/main/spaceship_titanic.csv\")\n", + "spaceship.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now perform the same as before:\n", + "- Feature Scaling\n", + "- Feature Selection\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "# Separate the target from the predictors.\n", + "X = spaceship.drop(columns=\"Transported\")\n", + "y = spaceship[\"Transported\"].astype(int)\n", + "\n", + "# Feature selection: remove identifiers and high-cardinality text fields.\n", + "# Cabin is retained in a more useful form by extracting deck and side.\n", + "X = X.copy()\n", + "X[\"CabinDeck\"] = X[\"Cabin\"].str.split(\"/\").str[0]\n", + "X[\"CabinSide\"] = X[\"Cabin\"].str.split(\"/\").str[2]\n", + "X = X.drop(columns=[\"PassengerId\", \"Name\", \"Cabin\"])\n", + "\n", + "numeric_features = X.select_dtypes(include=np.number).columns.tolist()\n", + "categorical_features = X.select_dtypes(exclude=np.number).columns.tolist()\n", + "\n", + "numeric_transformer = Pipeline(steps=[\n", + " (\"imputer\", SimpleImputer(strategy=\"median\")),\n", + " (\"scaler\", StandardScaler())\n", + "])\n", + "\n", + "categorical_transformer = Pipeline(steps=[\n", + " (\"imputer\", SimpleImputer(strategy=\"most_frequent\")),\n", + " (\"encoder\", OneHotEncoder(handle_unknown=\"ignore\"))\n", + "])\n", + "\n", + "preprocessor = ColumnTransformer(transformers=[\n", + " (\"num\", numeric_transformer, numeric_features),\n", + " (\"cat\", categorical_transformer, categorical_features)\n", + "])\n", + "\n", + "# Stratification keeps the target proportions similar in both sets.\n", + "X_train, X_test, y_train, y_test = train_test_split(\n", + " X, y, test_size=0.20, random_state=42, stratify=y\n", + ")\n", + "\n", + "print(\"Training shape:\", X_train.shape)\n", + "print(\"Test shape:\", X_test.shape)\n", + "print(\"Numerical features:\", numeric_features)\n", + "print(\"Categorical features:\", categorical_features)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Now let's use the best model we got so far in order to see how it can improve when we fine tune it's hyperparameters." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Build one pipeline so preprocessing is fitted only on training data.\n", + "baseline_model = Pipeline(steps=[\n", + " (\"preprocessor\", preprocessor),\n", + " (\"classifier\", RandomForestClassifier(\n", + " n_estimators=200, random_state=42, n_jobs=-1\n", + " ))\n", + "])\n", + "\n", + "baseline_model.fit(X_train, y_train)\n", + "baseline_predictions = baseline_model.predict(X_test)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Evaluate your model" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "baseline_accuracy = accuracy_score(y_test, baseline_predictions)\n", + "print(f\"Baseline Random Forest accuracy: {baseline_accuracy:.4f}\")\n", + "print(\"\\nClassification report:\\n\")\n", + "print(classification_report(y_test, baseline_predictions, target_names=[\"Not transported\", \"Transported\"]))\n", + "\n", + "baseline_cm = confusion_matrix(y_test, baseline_predictions)\n", + "sns.heatmap(baseline_cm, annot=True, fmt=\"d\", cmap=\"Blues\", cbar=False)\n", + "plt.title(\"Baseline Random Forest – Confusion Matrix\")\n", + "plt.xlabel(\"Predicted label\")\n", + "plt.ylabel(\"Actual label\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Grid/Random Search**" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For this lab we will use Grid Search." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Define hyperparameters to fine tune." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Parameter names use classifier__ because the model is inside a pipeline.\n", + "parameter_grid = {\n", + " \"classifier__n_estimators\": [100, 200],\n", + " \"classifier__max_depth\": [None, 10, 20],\n", + " \"classifier__min_samples_split\": [2, 5],\n", + " \"classifier__min_samples_leaf\": [1, 2],\n", + " \"classifier__max_features\": [\"sqrt\", \"log2\"]\n", + "}\n", + "\n", + "print(\"Number of combinations:\", np.prod([len(values) for values in parameter_grid.values()]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Run Grid Search" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "grid_search = GridSearchCV(\n", + " estimator=baseline_model,\n", + " param_grid=parameter_grid,\n", + " scoring=\"accuracy\",\n", + " cv=5,\n", + " n_jobs=-1,\n", + " verbose=1\n", + ")\n", + "\n", + "grid_search.fit(X_train, y_train)\n", + "\n", + "print(\"Best hyperparameters:\", grid_search.best_params_)\n", + "print(f\"Best mean cross-validation accuracy: {grid_search.best_score_:.4f}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Evaluate your model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tuned_model = grid_search.best_estimator_\n", + "tuned_predictions = tuned_model.predict(X_test)\n", + "tuned_accuracy = accuracy_score(y_test, tuned_predictions)\n", + "\n", + "print(f\"Baseline test accuracy: {baseline_accuracy:.4f}\")\n", + "print(f\"Tuned test accuracy: {tuned_accuracy:.4f}\")\n", + "print(f\"Change: {(tuned_accuracy - baseline_accuracy) * 100:+.2f} percentage points\")\n", + "print(\"\\nClassification report for tuned model:\\n\")\n", + "print(classification_report(y_test, tuned_predictions, target_names=[\"Not transported\", \"Transported\"]))\n", + "\n", + "tuned_cm = confusion_matrix(y_test, tuned_predictions)\n", + "sns.heatmap(tuned_cm, annot=True, fmt=\"d\", cmap=\"Greens\", cbar=False)\n", + "plt.title(\"Tuned Random Forest – Confusion Matrix\")\n", + "plt.xlabel(\"Predicted label\")\n", + "plt.ylabel(\"Actual label\")\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/lab-hyperparameter-tuning-updated.ipynb b/lab-hyperparameter-tuning-updated.ipynb new file mode 100644 index 0000000..8a7545b --- /dev/null +++ b/lab-hyperparameter-tuning-updated.ipynb @@ -0,0 +1,555 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# LAB | Hyperparameter Tuning" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Load the data**\n", + "\n", + "Finally step in order to maximize the performance on your Spaceship Titanic model.\n", + "\n", + "The data can be found here:\n", + "\n", + "https://raw.githubusercontent.com/data-bootcamp-v4/data/main/spaceship_titanic.csv\n", + "\n", + "Metadata\n", + "\n", + "https://github.com/data-bootcamp-v4/data/blob/main/spaceship_titanic.md" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "So far we've been training and evaluating models with default values for hyperparameters.\n", + "\n", + "Today we will perform the same feature engineering as before, and then compare the best working models you got so far, but now fine tuning it's hyperparameters." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# Libraries\n", + "import pandas as pd\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "\n", + "from sklearn.compose import ColumnTransformer\n", + "from sklearn.ensemble import RandomForestClassifier\n", + "from sklearn.impute import SimpleImputer\n", + "from sklearn.metrics import accuracy_score, classification_report, confusion_matrix\n", + "from sklearn.model_selection import GridSearchCV, train_test_split\n", + "from sklearn.pipeline import Pipeline\n", + "from sklearn.preprocessing import OneHotEncoder, StandardScaler" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dataset loaded successfully\n", + "Dataset shape: (8693, 14)\n" + ] + }, + { + "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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
PassengerIdHomePlanetCryoSleepCabinDestinationAgeVIPRoomServiceFoodCourtShoppingMallSpaVRDeckNameTransported
00001_01EuropaFalseB/0/PTRAPPIST-1e39.0False0.00.00.00.00.0Maham OfracculyFalse
10002_01EarthFalseF/0/STRAPPIST-1e24.0False109.09.025.0549.044.0Juanna VinesTrue
20003_01EuropaFalseA/0/STRAPPIST-1e58.0True43.03576.00.06715.049.0Altark SusentFalse
30003_02EuropaFalseA/0/STRAPPIST-1e33.0False0.01283.0371.03329.0193.0Solam SusentFalse
40004_01EarthFalseF/1/STRAPPIST-1e16.0False303.070.0151.0565.02.0Willy SantantinesTrue
\n", + "
" + ], + "text/plain": [ + " PassengerId HomePlanet CryoSleep Cabin Destination Age VIP \\\n", + "0 0001_01 Europa False B/0/P TRAPPIST-1e 39.0 False \n", + "1 0002_01 Earth False F/0/S TRAPPIST-1e 24.0 False \n", + "2 0003_01 Europa False A/0/S TRAPPIST-1e 58.0 True \n", + "3 0003_02 Europa False A/0/S TRAPPIST-1e 33.0 False \n", + "4 0004_01 Earth False F/1/S TRAPPIST-1e 16.0 False \n", + "\n", + " RoomService FoodCourt ShoppingMall Spa VRDeck Name \\\n", + "0 0.0 0.0 0.0 0.0 0.0 Maham Ofracculy \n", + "1 109.0 9.0 25.0 549.0 44.0 Juanna Vines \n", + "2 43.0 3576.0 0.0 6715.0 49.0 Altark Susent \n", + "3 0.0 1283.0 371.0 3329.0 193.0 Solam Susent \n", + "4 303.0 70.0 151.0 565.0 2.0 Willy Santantines \n", + "\n", + " Transported \n", + "0 False \n", + "1 True \n", + "2 False \n", + "3 False \n", + "4 True " + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "\n", + "url = \"https://raw.githubusercontent.com/data-bootcamp-v4/data/main/spaceship_titanic.csv\"\n", + "\n", + "spaceship = pd.read_csv(url)\n", + "\n", + "print(\"Dataset loaded successfully\")\n", + "print(\"Dataset shape:\", spaceship.shape)\n", + "\n", + "spaceship.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now perform the same as before:\n", + "- Feature Scaling\n", + "- Feature Selection\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Training shape: (6954, 12)\n", + "Test shape: (1739, 12)\n", + "Numerical features: ['Age', 'RoomService', 'FoodCourt', 'ShoppingMall', 'Spa', 'VRDeck']\n", + "Categorical features: ['HomePlanet', 'CryoSleep', 'Destination', 'VIP', 'CabinDeck', 'CabinSide']\n" + ] + } + ], + "source": [ + "# Separate the target from the predictors.\n", + "X = spaceship.drop(columns=\"Transported\")\n", + "y = spaceship[\"Transported\"].astype(int)\n", + "\n", + "# Feature selection: remove identifiers and high-cardinality text fields.\n", + "# Cabin is retained in a more useful form by extracting deck and side.\n", + "X = X.copy()\n", + "X[\"CabinDeck\"] = X[\"Cabin\"].str.split(\"/\").str[0]\n", + "X[\"CabinSide\"] = X[\"Cabin\"].str.split(\"/\").str[2]\n", + "X = X.drop(columns=[\"PassengerId\", \"Name\", \"Cabin\"])\n", + "\n", + "numeric_features = X.select_dtypes(include=np.number).columns.tolist()\n", + "categorical_features = X.select_dtypes(exclude=np.number).columns.tolist()\n", + "\n", + "numeric_transformer = Pipeline(steps=[\n", + " (\"imputer\", SimpleImputer(strategy=\"median\")),\n", + " (\"scaler\", StandardScaler())\n", + "])\n", + "\n", + "categorical_transformer = Pipeline(steps=[\n", + " (\"imputer\", SimpleImputer(strategy=\"most_frequent\")),\n", + " (\"encoder\", OneHotEncoder(handle_unknown=\"ignore\"))\n", + "])\n", + "\n", + "preprocessor = ColumnTransformer(transformers=[\n", + " (\"num\", numeric_transformer, numeric_features),\n", + " (\"cat\", categorical_transformer, categorical_features)\n", + "])\n", + "\n", + "# Stratification keeps the target proportions similar in both sets.\n", + "X_train, X_test, y_train, y_test = train_test_split(\n", + " X, y, test_size=0.20, random_state=42, stratify=y\n", + ")\n", + "\n", + "print(\"Training shape:\", X_train.shape)\n", + "print(\"Test shape:\", X_test.shape)\n", + "print(\"Numerical features:\", numeric_features)\n", + "print(\"Categorical features:\", categorical_features)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Now let's use the best model we got so far in order to see how it can improve when we fine tune it's hyperparameters." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# Build one pipeline so preprocessing is fitted only on training data.\n", + "baseline_model = Pipeline(steps=[\n", + " (\"preprocessor\", preprocessor),\n", + " (\"classifier\", RandomForestClassifier(\n", + " n_estimators=200, random_state=42, n_jobs=-1\n", + " ))\n", + "])\n", + "\n", + "baseline_model.fit(X_train, y_train)\n", + "baseline_predictions = baseline_model.predict(X_test)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Evaluate your model" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Baseline Random Forest accuracy: 0.8033\n", + "\n", + "Classification report:\n", + "\n", + " precision recall f1-score support\n", + "\n", + "Not transported 0.78 0.84 0.81 863\n", + " Transported 0.83 0.77 0.80 876\n", + "\n", + " accuracy 0.80 1739\n", + " macro avg 0.81 0.80 0.80 1739\n", + " weighted avg 0.81 0.80 0.80 1739\n", + "\n" + ] + }, + { + "data": { + "image/png": "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3Rx3knz3yscff3zHfrt37zZt27Y13t7extXV1VStWjXFHS93O+cbNmwwISEhxsfHx7i4uJhChQqZkJAQe/9r166Zbt26mSpVqpjcuXMbDw8PExgYaAYNGmTi4uLs2zl79qxp166dyZMnj7HZbCYrfwyeP3/eDB061NSoUcPkzJnTuLi4mKJFi5oOHTqYjRs3OvT96aefTJMmTYyXl5fx8PAw9erVM998841Dn4y8/1O728gYYy5cuGA6d+5s/P39jZeXl2nbtq05dOiQw2ctvefy9ruNjLl5x9C7775rihUrZlxcXExAQIDp3r17ijv0MvJZSE163p+p3TG0d+9e06xZM5MrVy6TN29e8/TTT5sjR46k+FljjDF9+/Y1BQsWNE5OTg7nN63ak5cl3210+fJlU65cOVOhQgWH82aMMa+99ppxcXExW7ZsueuxIiWbMZk0ZR4AAOABYM4LAACwFMILAACwFMILAACwFMILAACwFMILAACwFMILAACwFMILAACwlH/kE3Y9qr+e3SUAyCLnto6/eycAluSezlTCyAsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAUwgsAALAU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derkcHlNunmr8+2S25KSkjRkyBA9+eSTKfq4u7vfd50ACC/Av4aXl5dKly6d7v41atRQTEyMcuTIoeLFi6fap3z58tq8ebNefPFFe9vmzZvT3GaZMmXk4eGhNWvWqHPnzimWu7q6Sro5UpHM399fhQoV0oEDB/T888+nut0KFSpo9uzZunr1qj0g3amO1ERFRSkhIUH/+c9/5OR0czrgggULUvRLSEhQVFSU6tSpI0nat2+fzp8/r3Llykm6ed727duXoXMNIGMILwBS9dhjjykoKEhhYWEaOXKkAgMD9ffff+vbb79VWFiYatWqpTfffFPh4eGqVauWHnnkEX3xxRf6/fffVbJkyVS36e7urnfffVd9+vSRq6urGjRooNjYWP3+++/q1KmT/Pz85OHhoZUrV6pw4cJyd3eXt7e3Bg8erB49eih37txq1aqVrl+/rqioKJ07d05vv/22nnvuOfXr10+dOnVS//79dejQIY0ePTpDx1uqVCklJCQoIiJCbdu21caNGzVp0qQU/VxcXPTGG29o3LhxcnFx0euvv6569erZw8zAgQPVpk0bFSlSRE8//bScnJz022+/affu3frwww8z/o0AkAJ3GwFIlc1m07fffqtHH31UL7/8ssqWLatnn31Whw4dst8d9Mwzz2jgwIF69913VbNmTR0+fFjdu3e/43YHDBigXr16aeDAgSpfvryeeeYZnTp1StLN+STjxo3T5MmTVbBgQYWGhkqSOnfurGnTpikyMlKVK1dWo0aNFBkZab+1OmfOnPrmm2+0d+9eVa9eXf369dPIkSMzdLzVqlXTmDFjNHLkSFWqVElffPGFRowYkaKfp6en3n33XT333HMKCgqSh4eHvvzyS/vyFi1aaPny5Vq1apVq166tevXqacyYMSpWrFiG6gGQNpvJjIvFAAAADwgjLwAAwFIILwAAwFIILwAAwFIILwAAwFIILwAAwFIILwAAwFIILwAAwFIILwAAwFIILwAAwFIILwAAwFIILwAAwFIILwAAwFL+P4Au2EV37zhpAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "baseline_accuracy = accuracy_score(y_test, baseline_predictions)\n", + "print(f\"Baseline Random Forest accuracy: {baseline_accuracy:.4f}\")\n", + "print(\"\\nClassification report:\\n\")\n", + "print(classification_report(y_test, baseline_predictions, target_names=[\"Not transported\", \"Transported\"]))\n", + "\n", + "baseline_cm = confusion_matrix(y_test, baseline_predictions)\n", + "sns.heatmap(baseline_cm, annot=True, fmt=\"d\", cmap=\"Blues\", cbar=False)\n", + "plt.title(\"Baseline Random Forest – Confusion Matrix\")\n", + "plt.xlabel(\"Predicted label\")\n", + "plt.ylabel(\"Actual label\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Grid/Random Search**" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For this lab we will use Grid Search." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Define hyperparameters to fine tune." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of combinations: 48\n" + ] + } + ], + "source": [ + "# Parameter names use classifier__ because the model is inside a pipeline.\n", + "parameter_grid = {\n", + " \"classifier__n_estimators\": [100, 200],\n", + " \"classifier__max_depth\": [None, 10, 20],\n", + " \"classifier__min_samples_split\": [2, 5],\n", + " \"classifier__min_samples_leaf\": [1, 2],\n", + " \"classifier__max_features\": [\"sqrt\", \"log2\"]\n", + "}\n", + "\n", + "print(\"Number of combinations:\", np.prod([len(values) for values in parameter_grid.values()]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Run Grid Search" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Fitting 5 folds for each of 48 candidates, totalling 240 fits\n", + "Best hyperparameters: {'classifier__max_depth': 10, 'classifier__max_features': 'sqrt', 'classifier__min_samples_leaf': 1, 'classifier__min_samples_split': 5, 'classifier__n_estimators': 200}\n", + "Best mean cross-validation accuracy: 0.8004\n" + ] + } + ], + "source": [ + "grid_search = GridSearchCV(\n", + " estimator=baseline_model,\n", + " param_grid=parameter_grid,\n", + " scoring=\"accuracy\",\n", + " cv=5,\n", + " n_jobs=-1,\n", + " verbose=1\n", + ")\n", + "\n", + "grid_search.fit(X_train, y_train)\n", + "\n", + "print(\"Best hyperparameters:\", grid_search.best_params_)\n", + "print(f\"Best mean cross-validation accuracy: {grid_search.best_score_:.4f}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Evaluate your model" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Baseline test accuracy: 0.8033\n", + "Tuned test accuracy: 0.8016\n", + "Change: -0.17 percentage points\n", + "\n", + "Classification report for tuned model:\n", + "\n", + " precision recall f1-score support\n", + "\n", + "Not transported 0.79 0.81 0.80 863\n", + " Transported 0.81 0.79 0.80 876\n", + "\n", + " accuracy 0.80 1739\n", + " macro avg 0.80 0.80 0.80 1739\n", + " weighted avg 0.80 0.80 0.80 1739\n", + "\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "tuned_model = grid_search.best_estimator_\n", + "tuned_predictions = tuned_model.predict(X_test)\n", + "tuned_accuracy = accuracy_score(y_test, tuned_predictions)\n", + "\n", + "print(f\"Baseline test accuracy: {baseline_accuracy:.4f}\")\n", + "print(f\"Tuned test accuracy: {tuned_accuracy:.4f}\")\n", + "print(f\"Change: {(tuned_accuracy - baseline_accuracy) * 100:+.2f} percentage points\")\n", + "print(\"\\nClassification report for tuned model:\\n\")\n", + "print(classification_report(y_test, tuned_predictions, target_names=[\"Not transported\", \"Transported\"]))\n", + "\n", + "tuned_cm = confusion_matrix(y_test, tuned_predictions)\n", + "sns.heatmap(tuned_cm, annot=True, fmt=\"d\", cmap=\"Greens\", cbar=False)\n", + "plt.title(\"Tuned Random Forest – Confusion Matrix\")\n", + "plt.xlabel(\"Predicted label\")\n", + "plt.ylabel(\"Actual label\")\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python [conda env:anaconda3] *", + "language": "python", + "name": "conda-env-anaconda3-py" + }, + "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.13.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}