{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "xSWYNZ1zvzCA" }, "source": [ "# ElasticNet Compatible Estimators\n", "\n", "[](https://rehline-python.readthedocs.io/en/latest/)\n", "\n", "The core class `plqERM_ElasticNet` serves as a base implementation for both classification and regression tasks. Its subclasses, `plq_ElasticNet_Classifier` and `plq_ElasticNet_Regressor`, extend the Ridge-based variants by introducing an additional `l1_ratio` parameter that controls the mix between L1 and L2 regularization. These estimators integrate seamlessly with scikit-learn utilities such as `Pipeline`, `cross_val_score`, and `GridSearchCV`." ] }, { "cell_type": "markdown", "metadata": { "id": "HDGBmNUmxZtn" }, "source": [ "ElasticNet regularization solves the following optimization problem:\n", "\n", "$$\n", "\\min_{\\beta \\in \\mathbb{R}^d} \\; C \\sum_{i=1}^{n} \\text{PLQ}(y_i, \\mathbf{x}_i^T \\beta) + \\ell_1\\text{ratio} \\|\\beta\\|_1 + \\frac{1}{2}(1 - \\ell_1\\text{ratio})\\|\\beta\\|_2^2, \\quad \\text{s.t.} \\quad \\mathbf{A}\\beta + \\mathbf{b} \\geq \\mathbf{0},\n", "$$\n", "\n", "where\n", "\n", "- $\\text{PLQ}(\\cdot)$ is a piecewise linear-quadratic loss function (e.g., SVM hinge, quantile, Huber),\n", "- $\\mathbf{x}_i \\in \\mathbb{R}^d$ is a feature vector,\n", "- $y_i$ is the response variable (class label or continuous value),\n", "- $C > 0$ is the regularization strength (larger $C$ = less regularization),\n", "- $\\ell_1\\text{ratio} \\in [0, 1]$ is the mixing parameter: $\\ell_1\\text{ratio} = 1$ gives Lasso, $\\ell_1\\text{ratio} = 0$ gives Ridge,\n", "- $\\mathbf{A}\\beta + \\mathbf{b} \\geq \\mathbf{0}$ represents optional linear constraints on $\\beta$." ] }, { "cell_type": "markdown", "metadata": { "id": "L_j1q7cFEBxy" }, "source": [ "#### Classification Example with GridSearchCV and Pipeline\n", "\n", "Here we show a classification example using `Pipeline`, `cross_val_score`, and `GridSearchCV`. Compared to the Ridge classifier, the key difference is the additional `l1_ratio` parameter in `param_grid`.\n", "\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "id": "39IeObaaHBDz" }, "outputs": [], "source": [ "import numpy as np\n", "from sklearn.datasets import make_classification\n", "from sklearn.model_selection import train_test_split, GridSearchCV, cross_val_score\n", "from sklearn.pipeline import Pipeline\n", "from sklearn.preprocessing import StandardScaler\n", "from sklearn.metrics import accuracy_score, classification_report, confusion_matrix" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "id": "Rc33Ym8ZHB6a" }, "outputs": [], "source": [ "# generate the dataset\n", "X, y = make_classification(\n", " n_samples=2000,\n", " n_features=20,\n", " n_informative=8,\n", " n_redundant=4,\n", " n_repeated=0,\n", " n_classes=2,\n", " weights=[0.7, 0.3],\n", " class_sep=1.2,\n", " flip_y=0.01,\n", " random_state=42,\n", ")\n", "\n", "X_train, X_test, y_train, y_test = train_test_split(\n", " X, y, test_size=0.25, stratify=y, random_state=42\n", ")" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "id": "Q54w-eLSHDlq" }, "outputs": [], "source": [ "from rehline import plq_ElasticNet_Classifier\n", "\n", "# set the pipeline\n", "pipe = Pipeline([\n", " (\"scaler\", StandardScaler()),\n", " (\"clf\", plq_ElasticNet_Classifier(loss={\"name\": \"svm\"})),\n", "])" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "id": "c8hG_-p5HFRk" }, "outputs": [], "source": [ "# set the parameter grid\n", "param_grid = {\n", " \"clf__loss\": [{\"name\": \"svm\"}, {\"name\": \"sSVM\"}],\n", " \"clf__C\": [0.1, 1.0, 3.0],\n", " \"clf__l1_ratio\": [0.0, 0.3, 0.5, 0.8],\n", " \"clf__fit_intercept\": [True, False],\n", " \"clf__intercept_scaling\": [0.5, 1.0, 2.0],\n", " \"clf__max_iter\": [5000, 10000],\n", " \"clf__class_weight\": [None, \"balanced\", {0: 1.0, 1: 2.0}],\n", " \"clf__constraint\": [\n", " [],\n", " [{\"name\": \"nonnegative\"}],\n", " [{\"name\": \"fair\", \"sen_idx\": [0], \"tol_sen\": 0.1}],\n", " ],\n", "}" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "TrRcyQP8HILw", "outputId": "3d4e7e02-2a0a-4f36-b71e-5283f59d8f2f" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CV scores: [0.79666667 0.82 0.82666667 0.81 0.81 ]\n" ] } ], "source": [ "# cross_val_score\n", "cv_scores = cross_val_score(\n", " pipe,\n", " X_train, y_train,\n", " cv=5,\n", " scoring=\"accuracy\",\n", " n_jobs=-1,\n", ")\n", "print(\"CV scores:\", cv_scores)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 207 }, "id": "hMvSW0ifHJnZ", "outputId": "eaeb9c6f-e206-401c-fd61-9e6de3f41499" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Fitting 5 folds for each of 2592 candidates, totalling 12960 fits\n" ] }, { "data": { "text/html": [ "
GridSearchCV(cv=5,\n",
" estimator=Pipeline(steps=[('scaler', StandardScaler()),\n",
" ('clf',\n",
" plq_ElasticNet_Classifier(loss={'name': 'svm'}))]),\n",
" n_jobs=-1,\n",
" param_grid={'clf__C': [0.1, 1.0, 3.0],\n",
" 'clf__class_weight': [None, 'balanced',\n",
" {0: 1.0, 1: 2.0}],\n",
" 'clf__constraint': [[], [{'name': 'nonnegative'}],\n",
" [{'name': 'fair', 'sen_idx': [0],\n",
" 'tol_sen': 0.1}]],\n",
" 'clf__fit_intercept': [True, False],\n",
" 'clf__intercept_scaling': [0.5, 1.0, 2.0],\n",
" 'clf__l1_ratio': [0.0, 0.3, 0.5, 0.8],\n",
" 'clf__loss': [{'name': 'svm'}, {'name': 'sSVM'}],\n",
" 'clf__max_iter': [5000, 10000]},\n",
" scoring='accuracy', verbose=1)In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. GridSearchCV(cv=5,\n",
" estimator=Pipeline(steps=[('scaler', StandardScaler()),\n",
" ('clf',\n",
" plq_ElasticNet_Classifier(loss={'name': 'svm'}))]),\n",
" n_jobs=-1,\n",
" param_grid={'clf__C': [0.1, 1.0, 3.0],\n",
" 'clf__class_weight': [None, 'balanced',\n",
" {0: 1.0, 1: 2.0}],\n",
" 'clf__constraint': [[], [{'name': 'nonnegative'}],\n",
" [{'name': 'fair', 'sen_idx': [0],\n",
" 'tol_sen': 0.1}]],\n",
" 'clf__fit_intercept': [True, False],\n",
" 'clf__intercept_scaling': [0.5, 1.0, 2.0],\n",
" 'clf__l1_ratio': [0.0, 0.3, 0.5, 0.8],\n",
" 'clf__loss': [{'name': 'svm'}, {'name': 'sSVM'}],\n",
" 'clf__max_iter': [5000, 10000]},\n",
" scoring='accuracy', verbose=1)Pipeline(steps=[('scaler', StandardScaler()),\n",
" ('clf',\n",
" plq_ElasticNet_Classifier(C=0.1,\n",
" constraint=[{'name': 'fair',\n",
" 'sen_idx': [0],\n",
" 'tol_sen': 0.1}],\n",
" l1_ratio=0.0, loss={'name': 'sSVM'},\n",
" max_iter=5000))])StandardScaler()
plq_ElasticNet_Classifier(C=0.1,\n",
" constraint=[{'name': 'fair', 'sen_idx': [0],\n",
" 'tol_sen': 0.1}],\n",
" l1_ratio=0.0, loss={'name': 'sSVM'}, max_iter=5000)GridSearchCV(cv=5,\n",
" estimator=Pipeline(steps=[('scaler', StandardScaler()),\n",
" ('reg', plq_ElasticNet_Regressor())]),\n",
" n_jobs=-1,\n",
" param_grid={'reg__C': [0.1, 1.0, 10.0],\n",
" 'reg__constraint': [[], [{'name': 'nonnegative'}],\n",
" [{'name': 'fair', 'sen_idx': [0],\n",
" 'tol_sen': 0.1}]],\n",
" 'reg__fit_intercept': [True, False],\n",
" 'reg__intercept_scaling': [0.5, 1.0],\n",
" 'reg__l1_ratio': [0.0, 0.3, 0.5, 0.8],\n",
" 'reg__loss': [{'name': 'QR', 'qt': 0.5},\n",
" {'name': 'huber', 'tau': 1.0},\n",
" {'epsilon': 0.1, 'name': 'SVR'}],\n",
" 'reg__max_iter': [5000, 8000]},\n",
" scoring='r2', verbose=1)In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. GridSearchCV(cv=5,\n",
" estimator=Pipeline(steps=[('scaler', StandardScaler()),\n",
" ('reg', plq_ElasticNet_Regressor())]),\n",
" n_jobs=-1,\n",
" param_grid={'reg__C': [0.1, 1.0, 10.0],\n",
" 'reg__constraint': [[], [{'name': 'nonnegative'}],\n",
" [{'name': 'fair', 'sen_idx': [0],\n",
" 'tol_sen': 0.1}]],\n",
" 'reg__fit_intercept': [True, False],\n",
" 'reg__intercept_scaling': [0.5, 1.0],\n",
" 'reg__l1_ratio': [0.0, 0.3, 0.5, 0.8],\n",
" 'reg__loss': [{'name': 'QR', 'qt': 0.5},\n",
" {'name': 'huber', 'tau': 1.0},\n",
" {'epsilon': 0.1, 'name': 'SVR'}],\n",
" 'reg__max_iter': [5000, 8000]},\n",
" scoring='r2', verbose=1)Pipeline(steps=[('scaler', StandardScaler()),\n",
" ('reg',\n",
" plq_ElasticNet_Regressor(C=0.1,\n",
" constraint=[{'name': 'nonnegative'}],\n",
" l1_ratio=0.0,\n",
" loss={'name': 'huber', 'tau': 1.0},\n",
" max_iter=5000))])StandardScaler()
plq_ElasticNet_Regressor(C=0.1, constraint=[{'name': 'nonnegative'}],\n",
" l1_ratio=0.0, loss={'name': 'huber', 'tau': 1.0},\n",
" max_iter=5000)