{ "cells": [ { "cell_type": "markdown", "id": "6a3fc02e", "metadata": {}, "source": [ "# Lesson 26: multilayer perceptron activity\n", "\n", "## Notebook set up\n", "### Imports" ] }, { "cell_type": "code", "execution_count": null, "id": "a4186739", "metadata": {}, "outputs": [], "source": [ "# Third party imports\n", "import matplotlib.pyplot as plt\n", "import pandas as pd\n", "\n", "from sklearn.linear_model import LogisticRegression\n", "from sklearn.metrics import f1_score, roc_auc_score, log_loss, ConfusionMatrixDisplay\n", "from sklearn.model_selection import train_test_split\n", "from sklearn.neural_network import MLPClassifier\n", "from sklearn.preprocessing import StandardScaler, OrdinalEncoder, OneHotEncoder, MinMaxScaler" ] }, { "cell_type": "markdown", "id": "ced7ed87", "metadata": {}, "source": [ "## 1. Data preparation\n", "\n", "### 1.1. Load diabetes dataset" ] }, { "cell_type": "code", "execution_count": null, "id": "c04a2b33", "metadata": {}, "outputs": [], "source": [ "training_df = pd.read_csv('https://media.githubusercontent.com/media/gperdrizet/fullstack-2605/refs/heads/main/data/diabetes_train.csv')\n", "testing_df = pd.read_csv('https://media.githubusercontent.com/media/gperdrizet/fullstack-2605/refs/heads/main/data/diabetes_test.csv')" ] }, { "cell_type": "code", "execution_count": null, "id": "a94313df", "metadata": {}, "outputs": [], "source": [ "training_df.head()" ] }, { "cell_type": "code", "execution_count": null, "id": "4a397236", "metadata": {}, "outputs": [], "source": [ "training_df.info()" ] }, { "cell_type": "markdown", "id": "735e8b6c", "metadata": {}, "source": [ "Define the label and feature columns below. The label is `diabetes` (binary: 0 or 1). Using separate lists for numerical, nominal and ordinal features makes preprocessing easier later on." ] }, { "cell_type": "code", "execution_count": null, "id": "b0e50bde", "metadata": {}, "outputs": [], "source": [ "# Define the label\n", "label = # YOUR CODE HERE\n", "\n", "# Define lists of numerical, ordinal and nominal features\n", "# YOUR CODE HERE\n", "\n", "# Complete feature list\n", "features = # YOUR CODE HERE" ] }, { "cell_type": "markdown", "id": "a1981de6", "metadata": {}, "source": [ "### 1.2. Preprocess numerical features\n", "\n", "#### 1.2.1. Standard scale\n", "\n", "Neural networks perform better when features are scaled. Use `StandardScaler` to fit on the training features and transform both training and testing features.\n", "\n", "**Hint:** Fit the scaler on `training_df[numerical_features]`, then transform both `training_df[numerical_features]` and `testing_df[numerical_features]`." ] }, { "cell_type": "code", "execution_count": null, "id": "232a25d1", "metadata": {}, "outputs": [], "source": [ "feature_scaler = StandardScaler()\n", "\n", "# YOUR CODE HERE: fit and transform the numerical features" ] }, { "cell_type": "markdown", "id": "44f79845", "metadata": {}, "source": [ "#### 1.2.2. Clip outliers with IQR method" ] }, { "cell_type": "code", "execution_count": null, "id": "65fa125c", "metadata": {}, "outputs": [], "source": [ "# YOUR CODE HERE: implement IQR clipping for numerical features" ] }, { "cell_type": "markdown", "id": "c0d62a8a", "metadata": {}, "source": [ "### 1.3. Preprocess categorical features\n", "\n", "#### 1.3.1. Ordinal feature encoding" ] }, { "cell_type": "code", "execution_count": null, "id": "58b0061a", "metadata": {}, "outputs": [], "source": [ "# YOUR CODE HERE: create and fit OrdinalEncoder, then transform both training and testing data\n", "# Make sure to remove the original, un-encoded features!" ] }, { "cell_type": "markdown", "id": "13ebb926", "metadata": {}, "source": [ "#### 1.3.2. Nominal feature encoding" ] }, { "cell_type": "code", "execution_count": null, "id": "e7a5e92e", "metadata": {}, "outputs": [], "source": [ "# YOUR CODE HERE: create and fit OneHotEncoder, transform features, and concatenate back to dataframes.\n", "# Make sure to remove the original, un-encoded features!" ] }, { "cell_type": "markdown", "id": "756b7139", "metadata": {}, "source": [ "#### 1.3.3. Update the feature list" ] }, { "cell_type": "code", "execution_count": null, "id": "68bcea8b", "metadata": {}, "outputs": [], "source": [ "# YOUR CODE HERE: update the features list to include encoded features" ] }, { "cell_type": "markdown", "id": "0ac64191", "metadata": {}, "source": [ "#### 1.3.4. Min/max scale categorical features" ] }, { "cell_type": "code", "execution_count": null, "id": "ba67e611", "metadata": {}, "outputs": [], "source": [ "# YOUR CODE HERE: create MinMaxScaler with feature_range=(-1, 1), fit on categorical features, and transform" ] }, { "cell_type": "code", "execution_count": null, "id": "90b784ce", "metadata": {}, "outputs": [], "source": [ "training_df.info()" ] }, { "cell_type": "markdown", "id": "80005c89", "metadata": {}, "source": [ "## 2. Logistic regression model\n", "\n", "Logistic regression is a linear model for classification. It serves as a good baseline before trying more complex models like neural networks.\n", "\n", "### 2.1. Fit\n", "\n", "Create a `LogisticRegression` model and fit it on the training data. Use `max_iter=1000` to ensure convergence." ] }, { "cell_type": "code", "execution_count": null, "id": "ca564d0f", "metadata": {}, "outputs": [], "source": [ "logistic_model = # YOUR CODE HERE\n", "fit_result = # YOUR CODE HERE" ] }, { "cell_type": "markdown", "id": "689f8ca7", "metadata": {}, "source": [ "### 2.2. Test set evaluation\n", "\n", "For classification, we can use accuracy, F1 score and/or AUC-ROC (and others) instead of R². Use sklearn's [`metrics`](https://scikit-learn.org/stable/api/sklearn.metrics.html) module ." ] }, { "cell_type": "code", "execution_count": null, "id": "22fa11d9", "metadata": {}, "outputs": [], "source": [ "logistic_predictions = # YOUR CODE HERE\n", "logistic_accuracy = # YOUR CODE HERE\n", "logistic_f1 = # YOUR CODE HERE\n", "logistic_auc = # YOUR CODE HERE\n", "print(f'Logistic regression accuracy on test set: {logistic_accuracy:.4f}')\n", "print(f'Logistic regression F1 score on test set: {logistic_f1:.4f}')\n", "print(f'Logistic regression AUC-ROC score on test set: {logistic_auc:.4f}')" ] }, { "cell_type": "markdown", "id": "919e06cf", "metadata": {}, "source": [ "### 2.3. Performance analysis\n", "\n", "For classification, visualize performance using a confusion matrix." ] }, { "cell_type": "code", "execution_count": null, "id": "8c92bc5a", "metadata": {}, "outputs": [], "source": [ "# YOUR CODE HERE" ] }, { "cell_type": "markdown", "id": "879ce5e3", "metadata": {}, "source": [ "## 3. Multilayer perceptron (MLP) classifier\n", "\n", "Now let's build a neural network classifier using sklearn's [`MLPClassifier`](https://scikit-learn.org/stable/modules/generated/sklearn.neural_network.MLPClassifier.html).\n", "\n", "### 3.1. Single epoch training function\n", "\n", "Complete the training function below. It should:\n", "1. Split the data into training and validation sets\n", "2. Call `partial_fit` on the model (remember to pass `classes=[0, 1]` on the first call)\n", "3. Record training and validation [`log_loss`](https://scikit-learn.org/stable/modules/generated/sklearn.metrics.log_loss.html) (aka binary cross-entropy) in the history dictionary\n", "\n", "**Hint:** Use `model.partial_fit(X, y, classes=[0, 1])` for the first epoch. For subsequent epochs, `partial_fit` remembers the classes." ] }, { "cell_type": "code", "execution_count": null, "id": "605c3197", "metadata": {}, "outputs": [], "source": [ "def train(model: MLPClassifier, df: pd.DataFrame, features: list, label: str, history: dict, classes: list = None) -> tuple[MLPClassifier, dict]:\n", " '''Trains sklearn MLP classifier model on given dataframe using validation split.\n", " Returns the updated model and training history dictionary containing training and\n", " validation log loss. If classes are not provided, assumes 0 and 1.'''\n", "\n", " df, val_df = train_test_split(df, random_state=315)\n", " \n", " # YOUR CODE HERE: call partial_fit on the model\n", " # If classes is provided, pass it to partial_fit\n", " \n", " # YOUR CODE HERE: append training and validation log loss to history\n", " \n", " return model, history" ] }, { "cell_type": "markdown", "id": "17bf5072", "metadata": {}, "source": [ "### 3.2. Model training\n", "\n", "Create an `MLPClassifier` with:\n", "- `hidden_layer_sizes=(64, 32)` - two hidden layers\n", "- `activation='relu'` - ReLU activation function\n", "- `learning_rate_init=0.001` - initial learning rate\n", "- `warm_start=True` - keep weights between calls to fit\n", "- `random_state=315` - for reproducibility\n", "\n", "Train for 10 epochs using the training function above." ] }, { "cell_type": "code", "execution_count": null, "id": "734cccf3", "metadata": {}, "outputs": [], "source": [ "epochs = 10\n", "\n", "training_history = {\n", " 'training_loss': [],\n", " 'validation_loss': []\n", "}\n", "\n", "mlp_model = # YOUR CODE HERE: create MLPClassifier\n", "\n", "for epoch in range(epochs):\n", "\n", " # YOUR CODE HERE" ] }, { "cell_type": "markdown", "id": "76005d01", "metadata": {}, "source": [ "### 3.3. Learning curves\n", "\n", "Plot the training and validation loss over epochs to visualize the learning process." ] }, { "cell_type": "code", "execution_count": null, "id": "f7c94bd0", "metadata": {}, "outputs": [], "source": [ "# YOUR CODE HERE: plot training and validation loss\n", "# Use plt.plot() for each curve\n", "# Add title, xlabel, ylabel, and legend" ] }, { "cell_type": "markdown", "id": "c2e947e3", "metadata": {}, "source": [ "### 3.4. Test set evaluation\n", "\n", "Evaluate the MLP model on the test set, similar to how you evaluated the logistic regression model." ] }, { "cell_type": "code", "execution_count": null, "id": "36bc02a1", "metadata": {}, "outputs": [], "source": [ "mlp_predictions = # YOUR CODE HERE\n", "mlp_accuracy = # YOUR CODE HERE\n", "mlp_f1 = # YOUR CODE HERE\n", "mlp_auc = # YOUR CODE HERE\n", "print(f'MLP accuracy on test set: {mlp_accuracy:.4f}')\n", "print(f'MLP F1 score on test set: {mlp_f1:.4f}')\n", "print(f'MLP AUC-ROC score on test set: {mlp_auc:.4f}')" ] }, { "cell_type": "markdown", "id": "42cff8e0", "metadata": {}, "source": [ "### 3.5. Performance analysis\n", "\n", "Create a confusion matrix for the MLP model predictions." ] }, { "cell_type": "code", "execution_count": null, "id": "77851967", "metadata": {}, "outputs": [], "source": [ "# YOUR CODE HERE: create confusion matrix for MLP predictions\n", "# Follow the same pattern as the logistic regression confusion matrix" ] }, { "cell_type": "markdown", "id": "40f1a0e4", "metadata": {}, "source": [ "## 4. Model comparison\n", "\n", "Compare the performance of both models side by side." ] }, { "cell_type": "code", "execution_count": null, "id": "809e2c9e", "metadata": {}, "outputs": [], "source": [ "print(f'Logistic Regression accuracy on test set: {logistic_accuracy:.4f}')\n", "print(f'MLP accuracy on test set: {mlp_accuracy:.4f}')" ] }, { "cell_type": "markdown", "id": "4bada64a", "metadata": {}, "source": [ "Create a side-by-side comparison of the confusion matrices for both models." ] }, { "cell_type": "code", "execution_count": null, "id": "d06ec0a7", "metadata": {}, "outputs": [], "source": [ "# YOUR CODE HERE" ] } ], "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.12.12" } }, "nbformat": 4, "nbformat_minor": 5 }