From 831938f46d49b0b4538800b443f7a8bcb762e50c Mon Sep 17 00:00:00 2001 From: statmlben Date: Thu, 22 Aug 2024 21:54:50 +0800 Subject: [PATCH] plqERM_Ridge done --- .ipynb_checkpoints/FairSVM-checkpoint.ipynb | 223 ++++++++++++ .ipynb_checkpoints/QR-checkpoint.ipynb | 123 +++++++ .ipynb_checkpoints/SVM-checkpoint.ipynb | 108 ++++++ README.md | 2 +- doc/source/examples/FairSVM.ipynb | 223 ++++++++++++ doc/source/examples/QR.ipynb | 123 +++++++ doc/source/examples/ReHLine_QR.ipynb | 176 ---------- doc/source/examples/ReHLine_SVM_FairSVM.ipynb | 187 ---------- doc/source/examples/SVM.ipynb | 108 ++++++ doc/source/tutorials.rst | 39 +++ rehline/__init__.py | 7 +- rehline/_base.py | 290 +++++++++++++++- rehline/_class.py | 324 +++++------------- 13 files changed, 1319 insertions(+), 614 deletions(-) create mode 100644 .ipynb_checkpoints/FairSVM-checkpoint.ipynb create mode 100644 .ipynb_checkpoints/QR-checkpoint.ipynb create mode 100644 .ipynb_checkpoints/SVM-checkpoint.ipynb create mode 100644 doc/source/examples/FairSVM.ipynb create mode 100644 doc/source/examples/QR.ipynb delete mode 100644 doc/source/examples/ReHLine_QR.ipynb delete mode 100644 doc/source/examples/ReHLine_SVM_FairSVM.ipynb create mode 100644 doc/source/examples/SVM.ipynb diff --git a/.ipynb_checkpoints/FairSVM-checkpoint.ipynb b/.ipynb_checkpoints/FairSVM-checkpoint.ipynb new file mode 100644 index 0000000..422100e --- /dev/null +++ b/.ipynb_checkpoints/FairSVM-checkpoint.ipynb @@ -0,0 +1,223 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "50711dda-105e-4714-937b-e8be06370605", + "metadata": {}, + "source": [ + "# **FairSVM**\n", + "\n", + "[![Slides](https://img.shields.io/badge/🦌-ReHLine-blueviolet)](https://rehline-python.readthedocs.io/en/latest/)\n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1f_7t1t6FNxAooQOmpyhHCOVq0IKgMxe-?usp=sharing)\n", + "\n", + "The FairSVM solves the following optimization problem:\n", + "\n", + "$$\n", + "\\begin{align}\n", + " & \\min_{\\mathbf{\\beta} \\in \\mathbb{R}^d} \\frac{C}{n} \\sum_{i=1}^n ( 1 - y_i \\mathbf{\\beta}^\\intercal \\mathbf{x}_i )_+ + \\frac{1}{2} \\| \\mathbf{\\beta} \\|_2^2, \\nonumber \\\\\n", + " \\text{subject to } & \\quad \\frac{1}{n} \\sum_{i=1}^n \\mathbf{z}_i \\mathbf{\\beta}^\\intercal \\mathbf{x}_i \\leq \\mathbf{\\rho}, \\quad \\frac{1}{n} \\sum_{i=1}^n \\mathbf{z}_i \\mathbf{\\beta}^\\intercal \\mathbf{x}_i \\geq -\\mathbf{\\rho},\n", + "\\end{align}\n", + "$$\n", + "\n", + "where:\n", + "\n", + "* $\\mathbf{x}_i \\in \\mathbb{R}^d$ is a feature vector\n", + "* $y_i \\in \\{-1, 1\\}$ is a binary label\n", + "* $\\mathbf{z}_i$ is a collection of **centered sensitive features**, such as gender and/or race, satisfying:\n", + "\n", + "$$\\sum_{i=1}^n z_{ij} = 0,$$\n", + "\n", + "* $\\mathbf{z}_i \\in \\mathbb{R}^{d_0}$ is a $d_0$-length sensitive feature vector\n", + "* $\\mathbf{\\rho} \\in \\mathbb{R}_+^{d_0}$ is a vector of constants that trade-off predictive accuracy and fairness\n", + "\n", + "The constraints limit the correlation between the sensitive features and the decision function, ensuring fairness in the predictions.\n", + "> **Note.** Since the hinge loss is a plq function, and fairness constraints are linear, thus we can solve it by `rehline.plqERM_Ridge`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "e66268fa-403d-402b-9ea1-fbfe7573af40", + "metadata": {}, + "outputs": [], + "source": [ + "## simulate data\n", + "from sklearn.datasets import make_classification\n", + "from sklearn.preprocessing import StandardScaler\n", + "import numpy as np\n", + "\n", + "scaler = StandardScaler()\n", + "\n", + "n, d = 10000, 5\n", + "X, y = make_classification(n_samples=n, n_features=d, n_redundant=0)\n", + "## convert y to +1/-1\n", + "y = 2*y - 1\n", + "X = scaler.fit_transform(X)\n", + "\n", + "## we take the first column of X as sensetive features, and tol is 0.1\n", + "X_sen = X[:,0]\n", + "tol_sen = 0.1" + ] + }, + { + "cell_type": "markdown", + "id": "6a576a09-b700-49cd-b500-219f3a6e40b0", + "metadata": {}, + "source": [ + "## SVM as baseline" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "15531796-3a45-42b3-8a99-da0343be9d4d", + "metadata": {}, + "outputs": [], + "source": [ + "## we first run a SVM\n", + "from rehline import plqERM_Ridge\n", + "\n", + "clf = plqERM_Ridge(loss={'name': 'svm'}, C=1.0, max_iter=50000)\n", + "clf.fit(X=X, y=y)" + ] + }, + { + "cell_type": "markdown", + "id": "79bb275b-2dfd-4608-83e3-b4b4eb0fdb72", + "metadata": {}, + "source": [ + "## FairSVM" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "c43509f7-031b-4620-bc5e-fb5aea2ef1c2", + "metadata": {}, + "outputs": [], + "source": [ + "## solve FairSVM via `plqERM_Ridge` by adding `constraint`\n", + "from rehline import plqERM_Ridge\n", + "\n", + "fclf = plqERM_Ridge(loss={'name': 'svm'},\n", + " constraint=[{'name': 'fair',\n", + " 'X_sen': X_sen,\n", + " 'tol_sen': tol_sen}],\n", + " C=1.0,\n", + " max_iter=50000)\n", + "fclf.fit(X=X, y=y)" + ] + }, + { + "cell_type": "markdown", + "id": "794ede1f-13a4-4889-b6d9-f19a61faa510", + "metadata": {}, + "source": [ + "## Results" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "05dc3921-1837-474e-9a6d-4555a94ddc30", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Model Train Performance Correlation with Sensitive Features\n", + " SVM 0.8853 2.535203\n", + "FairSVM 0.5856 0.100212\n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "## score\n", + "score = clf.decision_function(X)\n", + "fscore = fclf.decision_function(X)\n", + "\n", + "svm_perf = len(y[score*y > 0])/n\n", + "fsvm_perf = len(y[fscore*y > 0])/n\n", + "\n", + "svm_corr = score.dot(X_sen) / n\n", + "fsvm_corr = fscore.dot(X_sen) / n\n", + "\n", + "# Create a pandas DataFrame to store the results\n", + "results = pd.DataFrame({\n", + " 'Model': ['SVM', 'FairSVM'],\n", + " 'Train Performance': [svm_perf, fsvm_perf],\n", + " 'Correlation with Sensitive Features': [svm_corr, fsvm_corr]\n", + "})\n", + "\n", + "# Print the results as a table\n", + "print(results.to_string(index=False))" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "ad5a863e-fbbb-4caf-876d-374f3ca9b891", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjsAAAHHCAYAAABZbpmkAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA37UlEQVR4nO3de1yUdd7/8TcgB09AIDBogqcSNY94wjY3FWHNNk06amZltuuiW1HpWpaHNu3WNt01ym3XsDbZXHetbdWtlFL3DizFzDN3dNsP0xnI1QFPgML1+6OHczcrjIDDzHDxej4e86i5rs91XZ/vzJRvr6OfYRiGAAAATMrf2w0AAAA0JsIOAAAwNcIOAAAwNcIOAAAwNcIOAAAwNcIOAAAwNcIOAAAwNcIOAAAwNcIOAAAwNcIOAAAwNcIOAJ+3b98+3XHHHYqPj1dISIg6dOig0aNHa8WKFdq9e7f8/Pw0d+7cWpf/6quv5Ofnp4yMDEnS/Pnz5efnJ39/fx09evSy+rKyMrVs2VJ+fn6aMWNGo40LgGcQdgD4tNzcXA0cOFBffvmlpk2bpldeeUUPP/yw/P399dvf/lYDBgxQQkKC/vznP9e6juzsbEnSfffd5zQ9ODi4xuXWr1/v3kEA8KoW3m4AAFx54YUXFBYWpp07dyo8PNxpXklJiSRp0qRJevbZZ7Vjxw4NHTr0snX8+c9/VkJCggYMGOA0/ZZbbtGf//xnzZo1y2l6dna2xo4dq7/97W/uHQwAr2DPDgCf9vXXX6tXr16XBR1Jio6OlvR92JH+bw/OD+Xn56ugoMBR80MTJ07Unj17dPjwYcc0m82mjz/+WBMnTnTTCAB4G2EHgE+Lj49Xfn6+9u/fX2tN586dNWzYMP3lL39RVVWV07xLAaim8DJ8+HBde+21TiFp7dq1atOmjcaOHeumEQDwNsIOAJ/25JNP6ty5c+rXr5+GDRum2bNn66OPPtKFCxec6iZNmqTi4mLl5OQ4plVXV2vt2rVKSkpSly5dLlu3n5+f7rnnHqfzdtasWaMJEyYoODi48QYFwKMIOwB82ujRo5WXl6fbbrtNX375pZYsWaLU1FR16NBB77//vqPu7rvvVmBgoNNemm3btunYsWM1HsK6ZOLEiSosLNTOnTsd/+QQFmAuhB0APm/QoEFav369Tp06pc8//1xz5szR6dOndccdd+jgwYOSpMjISKWmpurdd99VeXm5pO8PYbVo0UJ33XVXrevu37+/EhISlJ2drTVr1shisWjkyJEeGRcAzyDsAGgygoKCNGjQIC1atEivvfaaLly4oHXr1jnm33fffSorK9OGDRtUWVmpv/3tb0pJSVFUVJTL9U6cOFFr165Vdna27r77bvn7879GwEz4LxpAkzRw4EBJktVqdUy77bbb1LZtW2VnZ+uf//ynTp065fIQ1iUTJ06U1WrV//zP/3AICzAh7rMDwKd98sknuvnmm+Xn5+c0fdOmTZKk7t27O6a1bNlSt99+u9auXatz586pdevWGjdu3BW30bVrVy1fvlznz5/X4MGD3TsAAF5H2AHg02bOnKlz587p9ttvV0JCgiorK5Wbm6u1a9eqU6dOevDBB53q77vvPr311lv68MMPNWnSJLVu3bpO23n00Ucbo30APoCwA8CnvfTSS1q3bp02bdqk119/XZWVlYqLi9MvfvELzZ0797KbDY4cOVKxsbGyWq11OoQFwPz8DMMwvN0EAABAY+EEZQAAYGqEHQAAYGqEHQAAYGqEHQAAYGqEHQAAYGqEHQAAYGrcZ0dSdXW1jh8/rrZt2152l1YAAOCbDMPQ6dOn1b59e5fPtCPsSDp+/Lg6duzo7TYAAEADHD16VNdee22t8wk7ktq2bSvp+w8rNDTUy90AAIC6KCsrU8eOHR1/jteGsCM5Dl2FhoYSdgAAaGKudAoKJygDAABTI+wAAABTI+wAAABT45wdAACaiKqqKl24cMHbbXhMYGCgAgICrno9hB0AAHycYRiy2Wyy2+3ebsXjwsPDZbFYruo+eIQdAAB83KWgEx0drVatWjWLG+AahqFz586ppKREkhQbG9vgdRF2AADwYVVVVY6gExkZ6e12PKply5aSpJKSEkVHRzf4kBYnKAMA4MMunaPTqlUrL3fiHZfGfTXnKhF2AABoAprDoauauGPchB0AAGBqhB0AAGBqhB0AAGBqhB0AAGBqhB0AAODkrbfeUmRkpCoqKpymjx8/XpMnT/ZSVw1H2AEAAE7uvPNOVVVV6f3333dMKykp0caNG/XQQw95sbOG4aaCABwSBwyQ1Wp1WRMbG6v83bs91BEAb2jZsqUmTpyorKws3XnnnZKkt99+W3Fxcbr55pu921wDEHYAOFitVhV+sctlTbf+Az3UDQBvmjZtmgYNGqRjx46pQ4cOWr16tR544IEmeb8fwg4AALhM//791bdvX7311ltKSUnRgQMHtHHjRm+31SCEHQAAUKOHH35Yy5cv17Fjx5ScnKyOHTt6u6UG4QRlAABQo4kTJ+rbb7/VH/7whyZ5YvIlhB0AAFCjsLAwpaWlqU2bNho/fry322kwwg4AAKjVsWPHNGnSJAUHB3u7lQbjnB0AAHCZU6dOaevWrdq6dateffVVb7dzVQg7AADgMv3799epU6f0X//1X+revbu327kqhB0AAHCZb775xtstuA3n7AAAAFMj7AAAAFMj7AAAAFMj7AAAAFMj7AAAAFMj7AAAAFPj0nMAAJqooqIinThxwiPbateuneLi4jyyLXcj7AAA0AQVFRWpR48eOnfunEe216pVKx06dOiqAs/69eu1cuVK5efn6+TJk/riiy/Ur18/9zVZC8IOAABN0IkTJ3Tu3DmteuV36n7ddY26rYKvvtLUGb/UiRMnrirsnD17Vj/60Y901113adq0aW7s0DWvhp358+drwYIFTtO6d++uw4cPS5LKy8v1xBNP6J133lFFRYVSU1P16quvKiYmxlFfVFSk6dOn65NPPlGbNm00ZcoULV68WC1akOMAAObX/brr1L9Pb2+3USeTJ0+W5Pm7M3s9EfTq1UtbtmxxvP9hSHn88ce1ceNGrVu3TmFhYZoxY4YmTJigTz/9VJJUVVWlsWPHymKxKDc3V1arVffff78CAwO1aNEij48FAAD4Hq+HnRYtWshisVw2vbS0VKtWrVJ2drZGjhwpScrKylKPHj20Y8cODR06VB999JEOHjyoLVu2KCYmRv369dPzzz+v2bNna/78+QoKCvL0cAAAgI/x+qXnX331ldq3b68uXbpo0qRJKioqkiTl5+frwoULSk5OdtQmJCQoLi5OeXl5kqS8vDz17t3b6bBWamqqysrKdODAgVq3WVFRobKyMqcXAABwnzVr1qhNmzaO17/+9S+v9eLVPTtDhgzR6tWr1b17d1mtVi1YsEA33XST9u/fL5vNpqCgIIWHhzstExMTI5vNJkmy2WxOQefS/EvzarN48eLLzhUCAADuc9ttt2nIkCGO9x06dPBaL14NO2PGjHH8e58+fTRkyBDFx8frL3/5i1q2bNlo250zZ44yMjIc78vKytSxY8dG2x4AAM1N27Zt1bZt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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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iwrWcnd3x65duzBr1iyo1Wq0bt0aiYmJWLp0qTVDIyIyY80qzdau6ExASYket7Q3G2xXrNfZvxgSPavCzurVqzF9+nTIZLK79snlcsycORPvvPOOxWFHEIQG23h5eSE1NRWpqan1tgkNDcWePXssOiYR0b3YukozABgqK+1QkbiUld/5+W75fKPFfdwkElRU8u4ysp1VYefUqVN466236t0/fPhwvP32240uiojIWWxZpTnrxClMnvkcjEajnatr/ioq7/x8l7z4MiIjIhtsn51zFfNXLIPRyJWjyXZWhZ2CgoI6bzk3fVirVvjll18aXRQR0b1Y80ypktIym45hzSrN13+9zZssp1S0Q6B/QIPtiouLHFANiZ1VYadDhw44e/YswsPD69x/+vRpBAYGNklhROQaLA0WjXlQZbFeZ9H8jYJf7iwrYc0zpYr1d+riM6WIWi6rws5jjz2G119/HSNGjICXl5fZvoqKCixatAh/+MMfmrRAInIOax9WacuDKov1erhJJPh04zqrauverSciw7ta1Pby1csAtqCqqsqqYxCReFgVdhYsWIDt27eja9euSE5ORmTkneutFy9eRGpqKqqrqzF//ny7FEpEjmXtwyprH1RZpNNZfImp8JdbqBEELHt1PrqENfyQzh9O/Ii316XC3c0dPt4+Fh1DymdKEbV4VoWdgIAAHD58GLNmzUJKSorpbiqJRIK4uDikpqbe9ZwqImreah9W2RA3d3cAlj/OAPjf2SCFXG7R/I12CrlFn0tE9FtWLypYe5t3UVERrly5AkEQEBERgXbt2tmjPiJqJqx9nAHARxoQkWPYtIIyALRr1w73339/U9ZCRCJQ+zgDS9sSEdmbTc/GIiIiImoubD6zQ0TNjzXr05SVccVaIhIHhh2iFsDa28iB/00evl1dY7e6iIgcgWGHqAWw9jZygJOHiUg8GHaIWhBLbyMHxDd5mE/ZJmq5GHaISNRseco2wMdLEIkJww4RiZq1T9k+de4slr+3yqqVoBvzXDAisj+GHSJqESx9ynZO7k8AbFsJmmeDiFwTww4R0W/YshJ07XPBjFW37VgZEdmKYYeIqA5WrQTNh40SuTSuoExERESixrBDREREosbLWEQN0Gq10Ov1FrXNy8uzczVERGQthh2ie9BqtQgPD0dRUZFV/QyVlXaqiIiIrMWwQ3QPer0e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4sXo06cP/vGPfyAsLAxeXl4AAJ1Oh2effRbt27eHTCbD0KFDcerUKcf9IIioyfDMDhG5lHfffRddunTBRx99hOPHj6OqqgqdO3fGypUr8cQTT6CkpATffvstaq/Ar127FvPmzcOKFSsQHx+P4uJifP/99wCAmpoaU9D55ptvcPv2bSQlJeHpp59GRkaG6ZhXrlzB559/ju3bt8Pd3R0AMH78eHh7e+PLL7+EXC7Hhx9+iGHDhuH//u//oFQqHf5zIaJGcOZTSImI6rJq1SohNDRUEARByMrKEgAIP/30U51tg4KChPnz59e5b9++fYK7u7uQm5tr2nbu3DkBgHDs2DFBEARh0aJFgoeHh1BYWGhq8+233woymUyorKw0+7wuXboIH374YWOGRkROwMtYROTSevfujWHDhiE6Ohrjx4/Hxx9/jKKiIgBAYWEhbty4gWHDhtXZ98KFCwgODkZwcLBpW1RUFBQKBS5cuGDaFhoaivbt25venzp1CqWlpfD19UWbNm1Mr5ycHGRnZ9tppERkL7yMRUQuzd3dHfv378fhw4exb98+rFmzBvPnz8fRo0fh5+fXJMdo3bq12fvS0lIEBgaaXeqqpVAomuSYROQ4PLNDRC5PIpHgoYcewpIlS3DixAl4enpix44daNu2LTp16oT09PQ6+3Xv3h3Xrl3DtWvXTNvOnz8PnU6HqKioeo933333QaPRoFWrVggPDzd7NVXAIiLH4ZkdInJpR48eRXp6OoYPHw5/f38cPXoUv/zyC7p37w7gzt1Uzz33HPz9/REfH4+SkhJ8//33mD17NmJjYxEdHY2EhASsXr0at2/fxp///Gc88sgj6N+/f73HjI2NhVqtxpgxY7By5Up07doVN27cwO7du/HEE0/csy8RuR6GHSJyaTKZDIcOHcLq1auh1+sRGhqKv//974iPjwcAJCYmorKyEqtWrcKLL74IPz8/PPnkkwDunBH64osvMHv2bAwaNAhubm4YMWIE1qxZc89jSiQS7NmzB/Pnz8czzzyDX375BSqVCoMGDUJAQIDdx0xETYsrKBMREZGocc4OERERiRrDDhEREYkaww4RERGJGsMOERERiRrDDhEREYkaww4RERGJGsMOERERiRrDDhEREYkaww4RERGJGsMOERERiRrDDhEREYkaww4RERGJ2v8HTb4KY3qfgSMAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import seaborn as sns\n", + "import pandas as pd\n", + "import warnings\n", + "import matplotlib.pyplot as plt\n", + "warnings.filterwarnings(\"ignore\", \"is_categorical_dtype\")\n", + "warnings.filterwarnings(\"ignore\", \"use_inf_as_na\")\n", + "\n", + "df = pd.DataFrame({'score': score, 'fscore': fscore, 'y': y})\n", + "\n", + "sns.histplot(df, x=\"score\", hue=\"y\").set_title(\"SVM\")\n", + "plt.show()\n", + "sns.histplot(df, x=\"fscore\", hue=\"y\").set_title(\"FairSVM\")\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/.ipynb_checkpoints/QR-checkpoint.ipynb b/.ipynb_checkpoints/QR-checkpoint.ipynb new file mode 100644 index 0000000..75640d9 --- /dev/null +++ b/.ipynb_checkpoints/QR-checkpoint.ipynb @@ -0,0 +1,123 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e3a11293-4739-476e-a513-48a256d425a2", + "metadata": {}, + "source": [ + "## **Ridge Quantile Regression**\n", + "\n", + "[![Slides](https://img.shields.io/badge/🦌-ReHLine-blueviolet)](https://rehline-python.readthedocs.io/en/latest/)\n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1LwatjwjnMSB97eLVyuOiUY3sl3A3Ie__?usp=sharing)\n", + "\n", + "The regularized quantile regression solves the following optimization problem:\n", + "\n", + "$$\n", + "min_{\\beta \\in \\mathbb{R}^{d}} \\ C \\sum_{i=1}^n \\rho_\\kappa ( y_i - x^\\intercal_i \\beta ) + \\frac{1}{2} \\| \\beta \\|^2,\n", + "$$\n", + "\n", + "where $\\rho_\\kappa(u) = u\\cdot(\\kappa - \\mathbf{1}(u < 0))$ is the check loss,\n", + "$x_i \\in \\mathbb{R}^d$ is a feature vector, $y_i \\in \\mathbb{R}$ is the response variable.\n", + "\n", + "> **Note.** Since the check loss is a plq function, thus we can solve it by `rehline.plqERM_Ridge`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "b2dd4ce5-bc27-41a4-89ab-7920d393f377", + "metadata": {}, + "outputs": [], + "source": [ + "## simulate data\n", + "from sklearn.datasets import make_regression\n", + "from sklearn.preprocessing import StandardScaler\n", + "import numpy as np\n", + "\n", + "scaler = StandardScaler()\n", + "\n", + "n, d = 10000, 5\n", + "X, y = make_regression(n_samples=n, n_features=d, noise=1.0)\n", + "X = scaler.fit_transform(X)\n", + "## add intercept\n", + "X = np.hstack((X,np.ones((n,1))))\n", + "y = y/y.std()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "80129ee6-f886-4e27-a764-630f15826bca", + "metadata": {}, + "outputs": [], + "source": [ + "## solve QR with different `qt` via `plqERM_Ridge`\n", + "from rehline import plqERM_Ridge\n", + "\n", + "clf5 = plqERM_Ridge(loss={'name': 'QR', 'qt': 0.05}, C=10.0/n)\n", + "clf5.fit(X=X, y=y)\n", + "\n", + "clf95 = plqERM_Ridge(loss={'name': 'QR', 'qt': 0.95}, C=10.0/n)\n", + "clf95.fit(X=X, y=y)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "1d8b90e9-6af9-4856-9751-6fe6fbc7665c", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkIAAAGwCAYAAABFFQqPAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB0K0lEQVR4nO3dd3jT9drH8XeStulO94ICZYMgU5aoICiIoihyHCiiiIqiIrhwoCiKHhfHR484Dggq6vG4xYMDRY+KICBDluwyuuieSZvk+aNarU0ZJW2a5PO6rlza3/0bd1va3P1Og9PpdCIiIiLih4yeTkBERETEU1QIiYiIiN9SISQiIiJ+S4WQiIiI+C0VQiIiIuK3VAiJiIiI31IhJCIiIn4rwNMJNHcOh4NDhw4RERGBwWDwdDoiIiJyDJxOJ8XFxaSkpGA01t/uo0LoKA4dOkRqaqqn0xAREZEG2L9/Py1btqw3rkLoKCIiIoDqL2RkZKSHsxEREZFjUVRURGpqas37eH1UCB3F791hkZGRKoRERES8zNGGtWiwtIiIiPgtFUIiIiLit1QIiYiIiN/SGCE3sdvtVFZWejoNnxUYGIjJZPJ0GiIi4mNUCJ0gp9NJZmYmBQUFnk7F50VFRZGUlKT1nERExG1UCJ2g34ughIQEQkND9SbdCJxOJ2VlZWRnZwOQnJzs4YxERMRXqBA6AXa7vaYIio2N9XQ6Pi0kJASA7OxsEhIS1E0mIiJuocHSJ+D3MUGhoaEezsQ//P511lgsERFxFxVCbqDusKahr7OIiLibusZERETEIworCrE5bIQGhhIWGOaRHFQIiYiISJPKLc9jffZ6/vXLK2SXZdMtthvX97iBtpY0zAHmJs1FXWM+bu/evRgMBtavX3/M10ycOJExY8Yc8ZwhQ4Ywbdq0E8pNRET8T2FFMQt/Wci0Fbey6fAmssqyWL5/OZctvZSfszc0eT4qhHxcamoqGRkZdOvWzdOpiIiIcLj8MIu3LKpz3O60M2fVQ2SWZDdpPiqEfJjNZsNkMpGUlERAgHpBRUTE8345vAUnTpexfUX7KLAWNWk+KoSaiZdeeomUlBQcDket4xdccAHXXHMNu3bt4oILLiAxMZHw8HBOOeUUvvzyy1rntmnThocffpgJEyYQGRnJddddV6drzG63M2nSJNLS0ggJCaFTp0784x//cJnT7NmziY+PJzIykhtuuAGbzVZv/larldtvv50WLVoQFhZG//79WbFixQl9TURExPeYDIFHjBubuDRRIdRMjBs3jtzcXL7++uuaY3l5eSxbtozx48dTUlLCqFGjWL58OT///DMjR45k9OjRpKen17rPk08+SY8ePfj555+5//776zzH4XDQsmVL3nnnHbZs2cKsWbO45557+Pe//13rvOXLl7N161ZWrFjBm2++yXvvvcfs2bPrzX/q1KmsXLmSt956i40bNzJu3DhGjhzJjh07TvArIyIivqRLbGcCDK57KU6KPYmwgMgmzUeFUDMRHR3NOeecw5IlS2qO/ec//yEuLo6hQ4fSo0cPrr/+erp160aHDh14+OGHadeuHR999FGt+5x55pnMmDGDdu3a0a5duzrPCQwMZPbs2fTt25e0tDTGjx/P1VdfXacQCgoKYsGCBZx00kmce+65PPTQQzz77LN1WqwA0tPTWbhwIe+88w6nnXYa7dq14/bbb2fw4MEsXLjQTV8hERHxBbEB4cw65a46x8MDw5nd/z6SI5p2pwYNHGlGxo8fz+TJk/nnP/+J2WzmjTfe4NJLL8VoNFJSUsKDDz7I0qVLycjIoKqqivLy8jotQn379j3qc55//nkWLFhAeno65eXl2Gw2evbsWeucHj161Foxe+DAgZSUlLB//35at25d69xNmzZht9vp2LFjreNWq1Vbj4iISC1RlYWctWslXU97hrcOfsXB8sP0i+7EyPg+pKx4EuOoJyEsrsnyUSHUjIwePRqn08nSpUs55ZRT+N///sczzzwDwO23384XX3zBk08+Sfv27QkJCeHiiy+uM24nLOzIC1K99dZb3H777Tz11FMMHDiQiIgInnjiCVatWtXgvEtKSjCZTKxdu7bOHmDh4eENvq+IiPigjI2Er3mVThve5t4OI6gMi8W8+WuMex+qjg+9R4WQvwoODuaiiy7ijTfeYOfOnXTq1InevXsD8P333zNx4kQuvPBCoLr42Lt373E/4/vvv2fQoEHceOONNcd27dpV57wNGzZQXl5es9npjz/+SHh4OKmpqXXO7dWrF3a7nezsbE477bTjzklERPyI87chFpXlBGz5oG4h4nQ9o6yxaIxQMzN+/HiWLl3KggULGD9+fM3xDh068N5777F+/Xo2bNjA5Zdf7nK8ztF06NCBNWvW8Nlnn/Hrr79y//3389NPP9U5z2azMWnSJLZs2cKnn37KAw88wNSpUzEa6/6T6dixI+PHj2fChAm899577Nmzh9WrVzN37lyWLl163DmKiIgPS+kB9e0dGd8JQmKaNB0VQs3MmWeeSUxMDNu3b+fyyy+vOf70008THR3NoEGDGD16NCNGjKhpLToe119/PRdddBGXXHIJ/fv3Jzc3t1br0O+GDRtGhw4dOP3007nkkks4//zzefDBB+u978KFC5kwYQIzZsygU6dOjBkzhp9++olWrVodd44iIuLDwuJhyD11j5sC4fz/g/D4Jk3H4HQ2cRuUlykqKsJisVBYWEhkZO0pfRUVFezZs4e0tDSCg4M9lKH/0NdbRMRHlOVB5ib431NQfAha9odTb4HoNAgIcssjjvT+/WcaIyQiIiJNKzQG2p4BKb2gqgLMERAY4pFUVAiJiIiIZwRHAk27gOJfaYyQiIiI+C0VQiIiIuK3VAiJiIh4EafTSYmthPLKck+n4hM0RkhERMRL7C86yIr93/Bl+meEBIRyaafL6RzTmaQmnnLuS1QIiYiIeIH0wgNc+8U1ZJRm1Bz7/tB3jGh9Drf3vZOk8IZvS1FZVcXBkgw25GwivWgf3eO70S6qPamRye5IvVlTISQiItLMWatsvLb19VpF0O8+2/df/tbpkgYXQlV2O5sOb2bK8usoqyqrOZ4SlsL84S+RFtX6CFd7P40REhERaeayy3L5795P6o1/uPODBt/7YEkmt66YWqsIAjhUeoiHVj5Idmleg+/tDVQIidvs3bsXg8HA+vXrPZ2KiPiIA0XZrMvcxDvbPuL7A6tJLzyEw+F/GyI4nWB32OuN2512GrpRREbJIQqsBS5ja7LXUFhPzFd4VSH07bffMnr0aFJSUjAYDHzwwQdHPH/FihUYDIY6r8zMzKZJ+BjZHU5W7srlw/UHWbkrF7sf/pCLiPxVeuFBZnxzM1d9djkPrbqXG5ZP4urPr2R73g6/K4YsZgtnpp5Vb3xU2mgM9W1kehT51sIjxiuqrA26r7fwqjFCpaWl9OjRg2uuuYaLLrromK/bvn17rX1GEhISGiO9Bln2SwazP95CRmFFzbFkSzAPjO7KyG5NN0jNZrMRFOSe/V1ERE5UTmkBj/30GFvyttQ6nl2Wzc1f38iCs1+jlcX3B/L+zhIcynXdr+F/B1eQb82vFRuUPJD2lrYNvndbS5t6Y5FBkUSaPbvyc2Pzqhahc845hzlz5nDhhRce13UJCQkkJSXVvIzG5vFpL/slgymvr6tVBAFkFlYw5fV1LPul7qA4dxkyZAhTp05l2rRpxMXFMWLECH755RfOOeccwsPDSUxM5Morr+Tw4cN/5LtsGYMHDyYqKorY2FjOO+88du3addzPdjqdtG/fnieffLLW8fXr12MwGNi5c+cJf34i4t0Kbfl8d/Abl7Gssiyyy5pXy35TaJW7lzcHPMTkTpeRFplG15iuPNrrNua0OIuUqoavKRQTHM3I1me7jE09eQqJYb49Nb95VASNrGfPniQnJ3PWWWfx/fffH/Fcq9VKUVFRrVdjsDuczP54C64ad38/NvvjLY3aTbZo0SKCgoL4/vvveeyxxzjzzDPp1asXa9asYdmyZWRlZfG3v/2t5vzS0lKmT5/OmjVrWL58OUajkQsvvBCHw3FczzUYDFxzzTUsXLiw1vGFCxdy+umn0759e7d8fiLivcqrrDhd/oasllOe24TZNAOlhzF8fi8tFl3Ijbt+ZmHCEF6M6MHo714m/p1rYetHDb51fGUpdyWezpTOVxIRGAFAUlgSj/a+nXPKrQRbG+d9sLnwqq6x45WcnMz8+fPp27cvVquVV155hSFDhrBq1Sp69+7t8pq5c+cye/bsRs9t9Z68Oi1Bf+YEMgorWL0nj4HtYhslhw4dOvD3v/8dgDlz5tCrVy8effTRmviCBQtITU3l119/pWPHjowdO7bW9QsWLCA+Pp4tW7bQrVu343r2xIkTmTVrFqtXr6Zfv35UVlayZMmSOq1EIuKfIgLDCAkIobyelo7WkalNnJGHVVkhdyc4HQTs+JzYHZ/Xjh9aVz2iuiHjhPL3Evfvq7iu/Vlc1GsGlQHBmMtySfhxIRxcB60HQ1jD1yhq7ny6EOrUqROdOnWq+XjQoEHs2rWLZ555htdee83lNTNnzmT69Ok1HxcVFZGa6v4fuOzi+oughpzXEH369Kn5/w0bNvD1118THh5e57xdu3bRsWNHduzYwaxZs1i1ahWHDx+uaQlKT08/7kIoJSWFc889lwULFtCvXz8+/vhjrFYr48aNO7FPSkR8QmJYAld1ncD8jS/WifVP6k9ssO++MbsUYIa4DpC5yXW8RZ+GFUEAtlJwOgnY8TlJfy2wAOyVDbuvl/DpQsiVfv368d1339UbN5vNmM3mRs8jISLYrec1RFhYWM3/l5SUMHr0aB5//PE65yUnVw9IHD16NK1bt+bll18mJSUFh8NBt27dsNlsDXr+tddey5VXXskzzzzDwoULueSSSwgNDW3YJyMiPiXEXsFlYWkEdLmKhTv/Q2llKQGGAEa1Gs4trUaS6GjY7x2vFRYHZ94PS/5WNxYYCp3Pa/i94zpWF1Gupt+HxUNIdMPv7QX8rhBav359zRu7J/VLiyHZEkxmYYXLXnADkGQJpl9aTJPk07t3b959913atGlDQEDdfxa5ubls376dl19+mdNOOw3giAXlsRg1ahRhYWG88MILLFu2jG+//faE7iciPqT0MDFvjueajucwuvdMykyBBAMx2/5L6JLL4KpPwA+2f6gltR+MfByWz4bK3xY/jGwBf1sElhPouQiLh343wKoX6sbO+TtE+PbX2asKoZKSklozivbs2cP69euJiYmhVatWzJw5k4MHD7J48WIA5s2bR1paGieddBIVFRW88sorfPXVV3z+uYumvyZmMhp4YHRXpry+DgPUKoZ+b9x8YHRXTMYGNnUep5tuuomXX36Zyy67jDvvvJOYmBh27tzJW2+9xSuvvEJ0dDSxsbG89NJLJCcnk56ezt13331CzzSZTEycOJGZM2fSoUMHBg4c6KbPRkS8XmU5OJ0Ebv+UlO2f1o2XHq57zNeFREOfq6HzKCjNAWNgdRFzogVhcCScPgOSe8D/noCC/ZDUDYY/VH2smcy0bixe9dmtWbOGXr160atXLwCmT59Or169mDVrFgAZGRmkp6fXnG+z2ZgxYwbdu3fnjDPOYMOGDXz55ZcMGzbMI/n/1chuybxwRW+SLLW7v5IswbxwRe8mXUcoJSWF77//Hrvdztlnn0337t2ZNm0aUVFRGI1GjEYjb731FmvXrqVbt27cdtttPPHEEyf83EmTJmGz2bj66qvd8FmIiM8IjoSgsPrjMW2aLJVmJdAMUa2qxwQln+y+VrGweOh5GVz9X7hlPYx/F9JOq/4++DiDs6FrcvuJoqIiLBYLhYWFtRZlBKioqGDPnj2kpaURHNzwsTx2h5PVe/LILq4gIaK6O6ypWoI87X//+x/Dhg1j//79JCYmHvFcd329RcQLVFnh+3/A14/UjbU9E8a+AmGNM6NWfMOR3r//zKu6xnyVyWhotCnyzZXVaiUnJ4cHH3yQcePGHbUIEhE/E2CGvleDKQi+exoqCqv//+RLYehMFUHiNiqEpFHccMMNvP766y5jV1xxBQMGDGDSpEn07NmzZkyXiEgtYfEwcCp0GwuVpRAQDOGJEBji6czEh6hr7CiaomvMF2VnZ9e7KndkZGSD9nvT11tERI6VusbEoxISEprV5rYiIiKueNWsMRERERF3UiEkIiIifktdYyIiIs1Qia2E7LLD7MjfidlkJs3ShqTwBMymxt8Gyp+oEBIREWlmckrzWLx1MYs2L8D5294DQcYgHho0hzNSzyA8SPsyuou6xkRERJqZn7PX8ermf9UUQQA2h42Z393FgeIDHszM96gQEhERaUayS3P51+aXXcacOPnPr+9it9ubOCvfpUKoOXDYYc//YNN/qv/r8Pw/cKfTyaxZs0hOTiYkJIThw4ezY8eOWue0adMGg8FQ6/XYY495KGMREd9QUWUjqzSr3vjB0nTKq2xNmJFv0xghT9vyESy7C4oO/XEsMgVGPg5dz/dYWn//+9959tlnWbRoEWlpadx///2MGDGCLVu21FrM8KGHHmLy5Mk1H0dERHgiXRERnxFiCqFTdBd+yPjOZbx7bG+CAzRg2l3UIuRJWz6Cf0+oXQQBFGVUH9/yUaM9urS0lAkTJhAeHk5ycjJPPfUUQ4YMYdq0aTidTubNm8d9993HBRdcwMknn8zixYs5dOgQH3zwQa37REREkJSUVPMKCzvCbtEiInJU8eFR3NRjCgbqbr4dFhjGqLSzCTDp7dtd9JX0FIe9uiUIVzuc/HZs2d2N1k12xx138M033/Dhhx/y+eefs2LFCtatWwfAnj17yMzMZPjw4TXnWywW+vfvz8qVK2vd57HHHiM2NpZevXrxxBNPUFVV1Sj5ioj4k/aVNl4YOJvksOSaY51jOrNo0KO01Pggt1LXmKfs+6FuS1AtTig6WH1e2mlufXRJSQn/+te/eP311xk2bBgAixYtomXLlgBkZmYC1NkRPjExsSYGcMstt9C7d29iYmL44YcfmDlzJhkZGTz99NNuzVdExK+U5RG67C5OrbLy+oDrKAqPw4iRqOztxLw3FU66CM56CIxGSmwl5FXksbNgJ0HGINIsacSFxmmtoeOgQshTSuofCNeg847Drl27sNls9O/fv+ZYTEwMnTp1Oq77TJ8+veb/Tz75ZIKCgrj++uuZO3cuZrN+CEVEGqTKCvn7oDSHhA9ups6ujTlbwW4j31bO4i2L+demP6bZm01m5pz6CKennkZogNYaOhbqGvOU8MSjn3M857lRUlISAFlZtYuwrKysmpgr/fv3p6qqir179zZmeiIivi0wFBK61h9P7Q8BZtZm/cwrm16ptdaQ1W7lzm/v4GDxkXoc5M9UCHlK60HVs8NcDIarZoDIFtXnuVm7du0IDAxk1apVNcfy8/P59ddfAUhLSyMpKYnly5fXxIuKili1ahUDBw6s977r16/HaDRq13kRkRMRYoEz73MdCwyB7heTby3g5Y0vujzFiZN3f30Xh9PRiEn6DnWNeYrRVD1F/t8TqC6G/jxo+rfiaORj1ee5WXh4OJMmTeKOO+4gNjaWhIQE7r33XozG6rrYYDAwbdo05syZQ4cOHWqmz6ekpDBmzBgAVq5cyapVqxg6dCgRERGsXLmS2267jSuuuILo6Gi35ywicrzyKvIoqCjA4XQQaY4kIdSL/khL6ALjXoWl06Esr/pYdBqMfRksrSgtPUxmWWa9l+8r3outqpLgQA1TOBoVQp7U9Xz42+J61hF6rFHXEXriiScoKSlh9OjRREREMGPGDAoLC2vid955J6WlpVx33XUUFBQwePBgli1bVrOGkNls5q233uLBBx/EarWSlpbGbbfdVmvckIiIJzicDnbk7+De7+5le/52AJLCknhgwAP0TurtHWNnzBHQ5Xxo2Q/Kcqv/KA6NhYjq4QkmQzCdoruwMuN7l5efFN0bo0Fv8cfC4HQ6Xc3flt8UFRVhsVgoLCwkMjKyVqyiooI9e/aQlpZWa5HB4+awV88OK8mqHhPUelCjtAQdzZAhQ+jZsyfz5s1r8mcfC7d9vUXEpx0oPsDFH19MaWVpreMGDLx57pucFHeShzJzn+KKSnYXbOHKz66sNUYIqtcaemPEm7SNqV79318d6f37zzRGqDkwmqqnyHe/uPq/HiiCRER8gdPpZNmez+oUQVA9dua59c9RYivxQGbuFWEOoF3+fv454EGSwv6YxNIpuhOLBj1Cq4pCvy6CjofazURExGfY7DbWZq+pN74ldwullaWEB4U3YVaNoCSb8C/uZ3BQOG8MuI6i8FhMBhOWrO3EvHczzlYD4aIXwai3+aPRV0hqrFixwtMpiIicEAMmUkJT640nhSVht/tAq7vTAbYSyNtNwgdT66w1ZLAWVQ+7UCF0VOoaExERr1NkLWJXwS7e3PoWb2/7N3sL91JsKwaMnNPmQpf7dAGM7ziJIIMPbA4dEg0dR9UfP/kS0Masx0SlooiIeJW8ijxe2vASb2x7o9bxm3pM5bIul9LOHMYTp8zk3nVPYbVbATAajFzT4W8MiG6LJdQH3voCg+HUW2HL+1BRWDsW1wFa17/mm9TmA/8aRETEn2zI3lSnCAJ4fsNznJLUj1771zNk+yd8OOAR9lQWYXPYaB+WQswvHxK270EMF84Hk5ePEQKIbgOTv4JvnoBtn1S3APW+Ck6ZVL0grxwTFUIiIuI1Cq1FvLp5Qb3x17cuppu5HeadX9Ji55e0CImuHidTmlN9Qlg8WIvB7AOFkNEIse3hvGdg+APVx8LiwRTo2by8jAohERHxGiXWCnIrcuuN55TnYI0fRM3omPL82ieEJ/heoRAUWv2SBtFgaRER8RohAeH0iOtTb7xX3ABCknrXf4PB0yEsrhEyE2+lQkhcysrKYuLEiaSkpBAaGsrIkSPZsWNHrXOGDBmCwWCo9brhhhs8lLGI+IOY0FCu7noFZlPdGVERgRGM7XAugeHxcO7TYPjLW1yvCZB2ehNlKt5CXWPNgN1hZ132OnLKcogPjad3Qm9MHlxd2ul0MmbMGAIDA/nwww+JjIzk6aefZvjw4WzZsoWwsLCacydPnsxDDz1U83FoqJpnRaRxpeal89qpj/PIln+x4fAmAPol9mXWyVMJdFaxvnAvztTeJNy6jvjDuwkqSIdWAyEisXraucifeFUh9O233/LEE0+wdu1aMjIyeP/992t2Q6/PihUrmD59Ops3byY1NZX77ruPiRMnNkm+x+LLfV/y2OrHyCrLqjmWGJrI3f3uZnjr4Y323NLSUqZMmcJ7771HREQEt99+Ox9//DE9e/bkxhtv5Mcff+SXX37hpJOq9+R54YUXSEpK4s033+Taa6+tuU9oaChJSUn1PUZExL3K8jCvmEOXogye6zeJoo7jMTghvKKYDcV7uWfFUxRXFgMQEhDC3afcw7BuY7EE17/XlPg3r+oaKy0tpUePHjz//PPHdP6ePXs499xzGTp0KOvXr2fatGlce+21fPbZZ42c6bH5ct+XTF8xvVYRBJBdls30FdP5ct+XjfbsO+64g2+++YYPP/yQzz//nBUrVrBu3ToArNbqdTf+vLGp0WjEbDbz3Xff1brPG2+8QVxcHN26dWPmzJmUlZU1Ws4iIjiqqmd9FWcQtXwOrd64nNR/X83hyARuXfVQTREEUF5VzgMr72dXwW4PJizNnVe1CJ1zzjmcc845x3z+/PnzSUtL46mnngKgS5cufPfddzzzzDOMGDGisdI8JnaHncdWP1Zn12Co3hjQgIHHVz/O0NShbu8mKykp4V//+hevv/46w4YNA2DRokW0bNkSgM6dO9OqVStmzpzJiy++SFhYGM888wwHDhwgIyOj5j6XX345rVu3JiUlhY0bN3LXXXexfft23nvvPbfmKyJSIyQaOp4Dh/8Ys1jZaRRvpH+Bw+lwecnLm15ibtTjRAX7wIrS4nZeVQgdr5UrVzJ8eO3upREjRjBt2rR6r7FarTUtIgBFRUWNktu67HV1WoL+zImTzLJM1mWv45SkU9z67F27dmGz2ejfv3/NsZiYGDp16gRAYGAg7733HpMmTSImJgaTycTw4cM555xzcDr/KNyuu+66mv/v3r07ycnJDBs2jF27dtGuXTu35iwiAlRPfe97DaxbDBUFAJRbWrKrZF+9l6QX76PEVqZCSFzyqq6x45WZmUliYmKtY4mJiRQVFVFeXu7ymrlz52KxWGpeqan1b953InLKctx6nrv16dOH9evXU1BQQEZGBsuWLSM3N5e2bdvWe83vhdXOnTubKk0R8UfRbeDa5XDSRWAMIKToEJ0t9f/x1TayHaEBmsghrvl0IdQQM2fOpLCwsOa1f//+RnlOfGi8W887Hu3atSMwMJBVq1bVHMvPz+fXX3+tc67FYiE+Pp4dO3awZs0aLrjggnrvu379egCSk5PdnrOISA2DAeLawwXPwa0bCTzrYf7WZTwBhrqdHAYMTD55MjGhag0S13y6aywpKYmsrNrdT1lZWURGRhISEuLyGrPZjNnc+Dv29k7oTWJoItll2S7HCRkwkBiaSO+EIywM1kDh4eFMmjSJO+64g9jYWBISErj33nsxGv+oi9955x3i4+Np1aoVmzZt4tZbb2XMmDGcffbZQHX32pIlSxg1ahSxsbFs3LiR2267jdNPP52TTz7Z7TmLiNQRFFb9AlIP7+CfA2dzz8/PcLj8MAAWs4UHekylncP1TvQi4OOF0MCBA/n0009rHfviiy8YONDzu/KajCbu7nc301dMx4ChVjFkoPqH9q5+dzXaekJPPPEEJSUljB49moiICGbMmEFh4R87GGdkZDB9+nSysrJITk5mwoQJ3H///TXxoKAgvvzyS+bNm0dpaSmpqamMHTuW++67r1HyFRGpV5WN4O/m0T9rE28Nuom8UAtOnMTYKohb+SIBgSEwbhFoCr24YHD+efRrM1dSUlIz/qRXr148/fTTDB06lJiYmJpZTgcPHmTx4sVA9fT5bt26cdNNN3HNNdfw1Vdfccstt7B06dJjnjVWVFSExWKhsLCQyMjaP0QVFRXs2bOHtLS0WlPNj4erdYSSQpO4q99djbqOkCtDhgyhZ8+ezJs3r0mfe6zc8fUWER9UUQhvjIP9q1zHY9rCNcsgPNF1XHzSkd6//8yrWoTWrFnD0KFDaz6ePn06AFdddRWvvvoqGRkZpKen18TT0tJYunQpt912G//4xz9o2bIlr7zyisenzv/Z8NbDGZo6tFmtLC0i4lUCQ6FFn/oLoaQeEOgDu81Lo/CqQmjIkCEcqQHr1VdfdXnNzz//3IhZnTiT0eT2KfIiIn7j9yn1P70CdlvtmMEIp88Ac5jra8XveVUhJI1rxYoVnk5BRKRholvDhI/g/euh4Lc1hSKS4PznIEbrmkn9VAiJiIj3MwVB64Ew6XMoywWnE0JjICK5erq9G+RV5JFXkUd5VTmWIAuxwbGEBamlydupEHIDLxpv7tX0dfY+1iorOeU5lFSWEGwKJiY4hkizZu5II4pIqn4dQaG1kCJb9a4BliDLMf2b3F+8n9tX3M6WvC0AGA1GRrcdza29b22U9d6k6agQOgGBgYEAlJWV1bsukbjP7xu6/v51l+YttzyX17a8xmtbXsPmqB63MSB5AA8OepAW4S08nJ34oypHFbsLd/PoqkdZm7UWgH5J/ZjZfyZtLW0xGlyvMZxdls2NX97I3qK9NcccTgcf7vqQ8MAIbuszDXNA468/J43Dq6bPe8LRpt9lZGRQUFBAQkICoaGhGNzUBCt/cDqdlJWVkZ2dTVRUlFau9gKV9kpe3vQyL2x4oU6sQ1QHXjrrJeJC4zyQmfiz9KJ0Lv74Ysqram+xFB4Yzr9H/5vUCNdbKv2ctYEJy65wGTObzHx4wYe0iFBx39z45PT55igpqboJNjs728OZ+L6oqKiar7c0bznlOSzavMhlbEfBDjJKM1QISZOqqKpgydYldYoggJLKEt779X1u6nUjAca6b4v7i+vfaslqt1JsK3VrrtK0VAidIIPBQHJyMgkJCVRWVno6HZ8VGBiIyaS1lbxFWWUZZVVl9cZ3F+yhe3z3JsxI/FVOWQ6/5v9KXkUeP2b+WO95KzN+YGK3q7CYLXViCSEp9V4XZAwi0KgFXr2ZCiE3MZlMeqMW+Y3JEESgMZBKh+s/DhJC1b0pjS+nLIe7/3c3qzNXc2XXK4kJjmEXu1yeG2OOweFw/Ts8LjiRVhGtSC9OrxMb22EsocaoOsfzK/KpsFdgMpiIC4mrd/yReJ6+MyLidianhdFp57uMJYYmEhmgrQ6k8f2Y8SOrM1cD8Pnezzm/net/kwBj21+B0RnkMpZqgPl976FzTOeaY0aDkfNbj+DapNOI/lOTQqmtlDWZa5jy5RTO/s/Z/O3jv/H6ltdrNoKV5kctQiLidiEGE1PajSGvLJMVGd/XHG8R3oJ/9ptFjEkz/6RxFVQU8MbWN2o+zirLIrc8l7EdxvLujndrnXttx0vpHplCZIjrf5dBRXtIfWciL542jbxu11Nmr8RiDCR2x5eEvz4WblwFYdWDcddmr+Wm5TfVXJtbkcsTa55gfc56Zg2YRVRwlPs/WTkhKoRExO1inHmYXhvLnP7Xknv638ioyCUqKIL44mwS3r0e+9hXAXWPSeOpctix/WW7jXnr5jGh6wSeH/Y8W3O3EmEIYEBEGvHblhH208sYznqweruOvzAUZ0FpDjHL7iXG5cMqgOquuEdXPeoyny/2fcGUHlNUCDVDKoRExO1MlWVQdhjL149hMRhoGxQOleXgqKqO5+2A1D4ezlJ8mYkwTk0Zxo6CHbWOL96ymCVblzD3lLsYseVLDJvvAIcdUnqDtbh6Neq/iutQ/4PMkRBUvaFrSWUJB0sO1nvqxpxNdIg+wr3EIzRGSETcLzD4j7+snc7qN5jfiiAALK7XaxFxlwCjiTFtR5EQmlAnlhiWSI+AKAyb3qkuggAiW0BAPbO/IpIhdYDr2Gm316xkbcCEgfrXkgsPjDiuz0GahgohEXG/8AQ4+VLXsYgkiGnTpOmI/4kIDqRl5lYWn3I/V7W/mNjgWOJD4rm246Us7H0nyR/eUvuCU2+GoFDXNwuPh4sXQPdL4Pd1hoItcNYc6DW+pug3OsI4NeVUl7cINAbSKlytQc2RusZExP0CQ2HovVB0CHYt/+O4pSWM/0/1X98ijaksH/PKp2hx6Gdu7TSKCR2ugtj2xOTuJeC1i/9oCTIY4ayHIa7Tke9naQHnPQNn3gOVFWAOg/BkMP3xNhpsMHPXyVPYUbCDrLKsmuMmg4mn+t9HtFNtD82RCiERaRyRyTD2FSjJgvx9EBYHkSnVL5HG5rSDrRQcdgK3fkzC1o+rj/e6Ei57C/L3QkgMtOgNYQlgDj/6Pc1h1a96RDvySPjPdbw+/F42VRWxMm8LqcFxnBnfk6SVL2EamAy0csunJ+6jQkhEGk9oTPUroYunMxF/ExwFnc6D7K21j//8Gqx/Hc57Fk66CIzua6UJtFdAzlaS3ryCpOg0zorrAOWr4OBscDpxdBwJbQa67XniHmqnExER32MKgN5XQmhs3Vh4ErQb4tYiCKieJPD7gOv8PbDjczjwU/WEAcAY1dq9zxO3UCEkIiK+Kbo1XLscel4BgSEQFAZ9J8E1n0FUI3RRhSdA76tcx8LiIa69+58pJ8zgdP5WqopLRUVFWCwWCgsLiYyM9HQ6IiJyvGzlUJ4HBkN1V2190+TdoTgTPpkO25f+cSyyBVzxH0jo2njPlTqO9f1bY4RERMS3BYVAUPVMRafTSV55Lk6cRJmjCDC6+W0wIgkueB5KH4CC9OrCKyJZkwSaMRVCIiLiF7LLsvkq/Sve3v42lY5KRqWNYkz7MaSEu7lICY2ufsUfZUq+NAvqGjsKdY2JiHi/7LJsbll+C5vzNtc6nhCawGvnvOb+Ykg87ljfvzVYWkREfN767A11iiCoLpD+vf0dquxVLq4Sf6BCSEREfJq1ysoHO9+vN/7pnqXkW/ObMCNpTlQIiYiIT7M7nZiOMCjaZDRhq3I0YUbSnKgQEhERn2YggHNaXVRvfFSrCwk2WpowI2lOVAiJiIhPCwkMoGdMGmckD6oTaxeZxoXtRhAeHOiBzKQ50PR5ERHxeYkH1/Fg3AC2pg5jycGvsdltXJg8mFOCYohf9xrGM+4ETJ5OUzxAhZCIiPi2ygpMG14nbueXnGZpSd8OZ+EwRRD23YvVm7JaWkK/a6sXQxS/o0JIRER8m8Hwx7YahQcIWbOwdjzAXH2O+CWNERIREd8WYIZ+19Uf7zsJQuObLh9pVryuEHr++edp06YNwcHB9O/fn9WrV9d77quvvorBYKj1Cg5uxM32RESkeUroCieNrXs8uQecdCEYve7tUNzEq7rG3n77baZPn878+fPp378/8+bNY8SIEWzfvp2EhASX10RGRrJ9+/aajw1q/hQR8T/h8XDO49D3avjpZaiyQq8roUUfiEz2dHbiQV5VCD399NNMnjyZq6++GoD58+ezdOlSFixYwN133+3yGoPBQFLSsQ+As1qtWK3Wmo+LiopOLGkREWkewuOrX60GgNNR3WUmfs9r2gJtNhtr165l+PDhNceMRiPDhw9n5cqV9V5XUlJC69atSU1N5YILLmDz5rp7zfzZ3LlzsVgsNa/U1FS3fQ4i/iS3PJes0iyKrPpjQpoZU6CKIKnhNYXQ4cOHsdvtJCYm1jqemJhIZmamy2s6derEggUL+PDDD3n99ddxOBwMGjSIAwcO1PucmTNnUlhYWPPav3+/Wz8PEV+XV57Hf/f8l0mfT+KCDy/g1q9v5efsnym1lXo6NfFDlY5KnE6np9OQZsyrusaO18CBAxk4cGDNx4MGDaJLly68+OKLPPzwwy6vMZvNmM36S0GkIUpsJSz4ZQGLtiyqObYmaw0T/juBfwz9B0NTh2qc3nFyOB0YDV7zN2uzcajkEN8e+JbvD31Py/CWXNThIlLCUggLCvN0atLMeE0hFBcXh8lkIisrq9bxrKysYx4DFBgYSK9evdi5c2djpCjNnMPpwIBBb8SNKLcil8VbFruMPbLqEbrFdiMhzPXEBqktqzSbLblbWbr7Y0IDw7iw/UW0sbQiOjja06k1e3sK93DVf6+qtaP861tfZ86pczi79dmEBIZ4MDtpbrymEAoKCqJPnz4sX76cMWPGAOBwOFi+fDlTp049pnvY7XY2bdrEqFGjGjFTaW5yynLYkb+DD3d9SKAxkIs6XEQbSxtigmM8nZrP2VWwCyeuuyGyy7IptBW6tRByOp1kl2dTWFGIwWAgyhxFvA+sB5NRksm0r6exJe+PMY3v73yPcR3+xk29biI2xL//7ZbaSim0FeLESWRQJBFBETWxImsRD//4cK0i6HcP/vAgfRL70DKwZVOmK82c1xRCANOnT+eqq66ib9++9OvXj3nz5lFaWlozi2zChAm0aNGCuXPnAvDQQw8xYMAA2rdvT0FBAU888QT79u3j2muv9eSnIU0opyyH27+5g3XZa2uOfbjrQ85rex539L2DGD9/Q3E3s+nI63SZDO7by6miqoJ1WeuY9cMsssqqW4pbRrTk0cGP0i22G4Em79xE0+F08NGuT2oVQb97Z8e/ObfteX5dCO0r3MfTa59mxYEVOJwOBiYP5I5T7qCtpS0mo4kCawE/Zf7k8toqZxWbDv9CywgVQvIHr+p4vuSSS3jyySeZNWsWPXv2ZP369SxbtqxmAHV6ejoZGRk15+fn5zN58mS6dOnCqFGjKCoq4ocffqBr166e+hSkiX2V/lWtIuh3n+z+hF/zd3ggI9+WEppKSIDrboeuMV0xGyJcxhoivSidKcun1BRBAAeKDzDps0kcLDnotuc0tcySHN7d8e964//59R3sDnsTZtR8HCw5yJX/vZKv9n+Fw+kAYGXGSsZ/Or7me26zVx3xHqWVZY2ep3gXryqEAKZOncq+ffuwWq2sWrWK/v3718RWrFjBq6++WvPxM888U3NuZmYmS5cupVevXh7IWjwhrzyPN7e/WW/8zW1LsFZZ643L8bOXB/JY/wfqDO6NDIpkdr97sdtD3fKc8qpyXtn0Ss2b4Z9VOip5e/vbVB3lDbG5qnI4qKiqqDdeVlVKpb3u5+3rHE4H/939X5ddXuVV5by25TVsdhuBhlDaRLap9z6doro1YpbijbyuEBI5VhVVVUd5QymjyuGdb5bNVQtjCYPWvcUHpz/D5E6XMazVMGZ2v4F/93+ITp8/SKyzwC3PKassY0velnrjmw5voqzKO//yDw+I5NSU0+uNn9nyHIIDvbPb70SUVpby7cFv643/cOgHimxFhBgiua/P7S5n2o1NO5dogwZKS21eNUZI5HgEGcIYlDyEd3a84TJ+evIIjGipBHcKyv8V05aPSNu2lFvanI49LBbTjv/AoXUABFcWAi1O+Dlmk5kW4S3ZV7TPZTw1vDXBAd65r2BMWBjXd7+aFQeWU1JZUivWIao9pySe5KHMPMwZQFRQ/TPmosxROOwmoh15RP78Nm+d9jT/t+NtNuRuJj40nmvbjmGQzUGENQfQQrnyB7UIic8yBwRxeae/EWWOqhNLjUhlYFJfgkz6EXAnY8BvLRUOO+z+GtOmP4ogAJObvt6VlYFc1fnqeuN/63AplVVeukyC3U7LHd/w1qmPc0HrkUQERhAXEseUTuP5Z7cpJBdlHP0ePsjgDGBMu0vrjY9rfwUBhBJYVUbwz2/Q5d+T+Lszlve6XM+/4ody3v9eJObj2zBlbWrCrMUbqEVIfFZEcCDmPdtZMvARFuz7L58f/JZAYyBjWg3nksSBxFXkYjK18XSaPsUQ1xFMQWC31Q3GdwY3znZq5zAx8+QpPPnLK1Q6KgEINgXzQM+baVHlxOn00kKoLIeA756kdVku93a/mJu7TcVQZSN2y0eYPnsMuo2DFn39brf0MHMA3SOSuabD31jwl8HkF7Q6iwFxHYkICYTKIDCaoDyf8JX/JPwv9zGGJyLyZwan1h4/oqKiIiwWC4WFhURGRno6HTkeZXnw2hjI3or1pAspaHcGBoedmK2fELDjC5wnXYRhzAsQEOTpTH1HZTls/gA+uKH28cAQmPhfaOGeyQqO8gKMb15KeXgCuX2u5EBVMUaDiRZGM3GrXiEwIBTjRS9WP9fbFGXAi4Oh9LDreOfzYNwiMPnZ37H2KhzLH6IEB9kdhvJtznqqcHB6XE+SDqwjMnsHxvOfBZzw0c3wy7t17xEUDjeuhKhWTZ6+NL1jff/2s58k8St2W3UxZLdh3vg2iRvfrhU2lGaDoxJQIeQ2gSHQZTQkdoNV8yF/N7QaCL2uAIv73nyMdhuUZhOSvpKWWz6kZVB49W7iv0+NTu1X/f33xkIoNBq6nA9rFriO9xrvf0UQQGUZxv0/Erl/FZGr5tM+qTsYTJD5JFRVQExbsBVDeCKcNRuyt0H2n9ZiCgyF8e9ARIrnPgdplvzwp0n8RrAF2g6Bn19zHe94TvUvR3Evczgkd4fzngG7FQJCweS+hRQBCI6E1qdB7q7qj221BxXTdigE/rVTxEsEBMOgW2Dz+1D+l6niKb0g2U+XAAkwQ3Qa7F8F9ko4uK523JIKv69hZUmFK9+HvN1wcC1YWlZ/7SJb+GcRKUfkX53M4l8CQ+DUW123CoTFQZfzQPuONZ6AIDBHuL8Igt+KhanV//0rcyT0uKxxnttUotvA5BXQ73qISKpu7Th7Dly6BCKTPZ2dZwSYYeCN9cdPv6O6QP5dRCK0Hlj97+SkMRDdWkWQuKRCSHxbdBpM+hLaDK7+2GCsHmNxzWcaJ+DtotpUfx9Tev9xrPXg3763rT2WllsYDBDTBs5+uLogunoZDLgJIv28Wye6LVzwz+oB+b8zmuCshyFJCyVKw2iw9FFosLSPKM+HiqLqN5iQmOruG/ENpblQUfjb9zYKQrQ7u0+rLIeSbDi8AxxVEN8JwhMgKMzTmUkzo8HSIn8WEq03SF8VFlv98lFOp5OM0gxWHlrJykMraRvVllFpo0gOS8Yc4IcLggaGVHdzRXt5q580GyqERESasd2Fu5m4bCIF1oLqA/vgpY0vMW/oPAalDCLIpFmPIidCY4RExGtYq6wcKD7Aiv0r+Hzv5+wr2kfJX2eM+ZD8inzu//7+P4qg39iddu745g5yynM8k5iID1GLkIh4hVJbKV/v/5pZP8yqWUnagIGru13NxJMmEh3se12fBdYCNh12vSVEhb2CfYX7aBF+4nu3ifgztQiJiFc4VHqImd/NrCmCAJw4WfDLAn7O/tmDmTWeKkfVEePFlaVNlImI71IhJCLNnt1h5+1tb9cbf3Hji+RX5Ncbd5dKe+VRixN3MhvDSAhNqDfeNrJdk+Ui4qtUCIlIs1fpqGR/yf5649ll2bVaitwtszSTT3d/ym0rbuO+7+/j5+yfm6TwojKSu/vc5TJ0aYe/UWn1wi1ERJoZjRESkWYvwBBEj7i+/HDoB5fxrjHdMDldrDLtBpmlmUz+fDJ7i/bWHFu6eymXd76cKT2mEBUc1SjPBbAYyxhwYAOLBv+dZ7a9wdb8bSSFJXFd+7EMrjQQZCxvtGeL+Au1CIlIs1flcDCi1TDCXewfZjQYmXLyddir3D+NvNJeyRtb36hVBP1uybYlHCg54PZn/lm4vZCIr+bQ+4PbeC6sK5/0vJNXU0Zx/vf/IuajWwkp3teozxfxB2oREpFmL8hkIvFwOosGPcL9v7zIltwtALSMaMkD3afQcv9Ggrp2cPtz8yryeG/He/XGP9z5Ed3iGm9rB9Pv3X3FGVhWPI7lL3GjrajRni3iL1QIiUizZ3TYCF/7Ah0zNzK//3UUdL4ah9NBZHE28V88CtZi6DgMSHLrcx1OJza7rd54eVUjd00FWyAsHkpdrxdkiO/SuM8X8QMqhETECxhwGkwYig4R/cWD1FkxqJE20DU4Qxjc4gy+TP/MZfz0lLMb5bk1IpJh5OPw7jV1Yz3HV++xJSInRGOERKT5CwjCcIqLYuB3va6E0Hj3P9cRxE0nX09YYN0NPXvG9SAtLNX9z/wzoxHaD4cr3oXEk6qPRSTBOY/D8AerN5kVkROiFiER8Q6J3aHdMNi1vPbxmLbQ83Iwmdz+yJhgiP7hA94+9e8s3LuUFZmrCA0M5bLWIxkR1gZLeTbQxu3PrSXEUl0MJfUAuxUMpupiyGBo3OeK+AmD0+l0ejqJ5qyoqAiLxUJhYSGRkZGeTkfEq9jsNg6XHyajNAOH00FKWApxIXEN3zW9OBMOrIFV86uLgh6XQYezwdISgKzSLLbnb+fr9K+JDYnlnLRzSAxLdDnb7JgUHYLn+4HdhvWkCylsPQBjZTmxv7yPYf9qnH2uxjB6XsPuLSKN6ljfv9UiJCKNoqyyjG8PfMusH2bVDCoOMgZxd7+7GZk2koigiOO/aUQSdDkP2g4Bh726teQ3GaUZ3PDFDewu3F1z7MWNL3LfgPs4L+08woLqdm8dnROcDqiyYt7wFgkb3qoVNTjtDbiniDQnGiMkIo1if/F+7vz2zlozq2wOGw/9+BA7C3ae2M3N4bWKIGuVlQWbFtQqgn4358c5ZJdnN+w5wTFw0kX1x3tc1rD7ikizoUJI/FJZZRn7i/fzc/bPbM3dSnZpA98oxSWb3cbiLYtx4rrn/aWNL1Fqc9+GofnWfN7f+X698RX7VzTsxkEhcNoMCIurG+s4AmLbN+y+ItJsqGtM/E5eeR6LNi9i8ZbFVDmrN9BMCkviH0P/QeeYzhgN+vvgRFVUVbCvqP5Vj/cX76fCXkEYDemuqsvusGO1W+uN51cUNvzmMWlw7Vfw8+uw9UMICocBN0Kb0zR9XcQH6De++BWn08mX6V+yYPOCmiIIqveTmvTZJDJKMzyYne8wYqa9pf7F/tpbOoHDjVtiOMz0iOtZb/iUxEEndv/o1nDGXTBxafVU9u4XQ0Tiid1TRJoFFULiV3LKc5i/Yb7LWEllCT9n/9zEGfkmp9PIhe0uJsBYt9HZaDByZeeJ4HRfIWR0hnFnn+mYDHWn0PeI606y2Q0tN6aA6lWeQ+os5ygiXkyFkPiVSnslOeWutysA+DXv1ybMxndFBAeShoGXB84hOSy55nh8SDzP9X+Adk4DUaHuK4QijRW037yUJac9Sf/EvhgNRixmC1M6jefpDuNpWZHvtmeJiG/xukLo+eefp02bNgQHB9O/f39Wr159xPPfeecdOnfuTHBwMN27d+fTTz9tokylOQoyBZEUVv9+VJ1jujZhNj6sykb4qv+j7+cP83r7K3h38BP859QneLPLZAZ/8yyWbx7GVOm+wdJhjhJCv3uSrv+5nqcD2/BZv4d4t+tNXL9jNQlvjidox1K3PUtEfItXDZZ+++23mT59OvPnz6d///7MmzePESNGsH37dhIS6jZ9//DDD1x22WXMnTuX8847jyVLljBmzBjWrVtHt26Nt2O0NF8BTgs3dr+RWT/OqhOzmC10jlIh5BaVZRhztkL2VhI+uJk6P52x7aCyrHoavLsYjFB6mMj/Pc1fl04zmgLd9xwR8Sle1SL09NNPM3nyZK6++mq6du3K/PnzCQ0NZcGCBS7P/8c//sHIkSO544476NKlCw8//DC9e/fmueeeq/cZVquVoqKiWi/xHfYqK4MNodzSdSJm0x+rG6dFprFw4BxiyuvfaVyOQ2Bo9ZYQ9UnoCg1a4LAeoTHQ5YL6413Oc9+zRMSneE0hZLPZWLt2LcOHD685ZjQaGT58OCtXrnR5zcqVK2udDzBixIh6zweYO3cuFoul5pWa2sibKkqTirAXEP+fq5mwdyMfDpjDW6f+nfdOe4oFLUfT4b2phOxxvcu4HKeAIOh/HRhd7P9lMMBpt7u3EAoKg2H3Vw9m/qsBUyAixX3PEhGf0uCusZ07d7Jr1y5OP/10QkJCcDqdGBpxE8DDhw9jt9tJTKw9ZTUxMZFt27a5vCYzM9Pl+ZmZmfU+Z+bMmUyfPr3m46KiIhVDPiTI4ABbKebNH9Bi8we0+Es8sEwLK7pNVGsY/y68fx2U/PZ1DYmG0c82zkKEMW1h8lew5UPY9gmExMLAGyG+C4RqppeIuHbchVBubi6XXHIJX331FQaDgR07dtC2bVsmTZpEdHQ0Tz31VGPk2WTMZjNmcwM3hJRmzxAUDim94JDrafKGDsOaOCMfFhgMaWfA5G+g7HD1nl1hcRCeVD0VvTFEtYIBN0Hvq8AUCIEhjfMcEfEZx901dttttxEQEEB6ejqhoaE1xy+55BKWLVvm1uT+LC4uDpPJRFZWVq3jWVlZJCW5ngWUlJR0XOeLHwiLhRFzq7tn/sKZdDKGuI4eSMqHGY1gSYHkkyGlZ/Uu8Y1VBP35mcGRKoJE5JgcdyH0+eef8/jjj9OyZctaxzt06MC+ffUvqX+igoKC6NOnD8uXL6855nA4WL58OQMHDnR5zcCBA2udD/DFF1/Ue774ieSTYeJ/Ifm3wbyBIdDvOgyXvVW9u7mIiPiN4/7TrLS0tFZL0O/y8vIavUtp+vTpXHXVVfTt25d+/foxb948SktLufrqqwGYMGECLVq0YO7cuQDceuutnHHGGTz11FOce+65vPXWW6xZs4aXXnqpUfOUZi4oDFoPhCveB1tJ9YDesHgIUJeoiIi/Oe5C6LTTTmPx4sU8/PDDABgMBhwOB3//+98ZOnSo2xP8s0suuYScnBxmzZpFZmYmPXv2ZNmyZTUDotPT0zEa/2jkGjRoEEuWLOG+++7jnnvuoUOHDnzwwQdaQ0iqhcVWv6RR/b6ad1ZpFnannaSwJOJC4ggOCPZ0aiIiGJxOp/N4Lvjll18YNmwYvXv35quvvuL8889n8+bN5OXl8f3339OuXbvGytUjioqKsFgsFBYWEhn512XaRORIyqvK+eHgD9zz3T2UVZUBEGQM4vZTbufctHOJNOtnSkQax7G+fx/3GKFu3brx66+/MnjwYC644AJKS0u56KKL+Pnnn32uCBKRE3Og+AC3rbitpggCsDlsPLrqUX7N175uIuJ5DZq+YbFYuPfee92di4j4kEp7JW9uexMnrhud52+YzzPRzxBhjmjizERE/nDchdC33357xPjpp5/e4GRExHfY7Db2FO6pN76/eD8V9goiUCEkIp5z3IXQkCFD6hz784rSdrv9hBISEd8QHBBMl5iTWJO1xmW8Y3Qngk1a60dEPOu4xwjl5+fXemVnZ7Ns2TJOOeUUPv/888bIUUS8UEWlk1GtLyDQWHfndwMGJnaZhNXWyIsriogcxXH/FrJYLHWOnXXWWQQFBTF9+nTWrl3rlsRExLsZjZBks/HKoEeY+fM8DpUeAiA2OJYHTr6JVhVWjJbG259QRORYuO3PscTERLZv3+6u24mIlwsxODCvfZ64g2t4bdBUCiITcDohqqKI+O9ewGAyw2VvAlrIUkQ857gLoY0bN9b62Ol0kpGRwWOPPUbPnj3dlZeIeDu7FWP+XsjZTsKHN5Pw13hUK6iq8EBiIiJ/OO5CqGfPnhgMBv66DuOAAQNYsGCB2xITES8XGAqtBsG+713HU/pAkGaMiYhnHXchtGdP7emwRqOR+Ph4goO1XL6I/InRBL3Gw4/PQ2VZ3dgZd4A5zDO5iYj85rgLodatWzdGHiLiiyypcPV/4YMpkL2l+lh0Goz+B8S09WxuIiIcYyH07LPPHvMNb7nllgYnIyI+xhQAKT3hqo+gLA+cDgiJhogkT2cmIgIc46araWlpx3Yzg4Hdu3efcFLNiTZdFRER8T7H+v59TC1Cfx0XJCJyNOVV5eSW51JSWUJoQCixIbGEBWpMkIg0L1rWVUTcLqcsh/kb5/PejveoclRhNBgZ2WYk0/tOJzE00dPpiYjUaFAhdODAAT766CPS09Ox2Wy1Yk8//bRbEhMR71RWWcbz65/n3R3v1hxzOB18uudTimxFPDb4MSzBf6xQb62yUl5VTkhACOYALa4oIk3ruAuh5cuXc/7559O2bVu2bdtGt27d2Lt3L06nk969ezdGjiLiRXLLc/lg5wcuY98d/I7cilwswRbKKsvYX7yfVze/ys6CnXSK7sSEkyaQGp5KSKA2YxWRpnHcm67OnDmT22+/nU2bNhEcHMy7777L/v37OeOMMxg3blxj5CgiXqTAWoTdaa83nl12mCp7Fd8f+p5xH4/jk92fsC1vGx/u+pBxH4/jp6yfsDvqv15ExJ2OuxDaunUrEyZMACAgIIDy8nLCw8N56KGHePzxx92eoIh4F7PpyK05EYGR5JTn8MD3D+Ck9qRVh9PBfd/dR055TmOmKCJS47gLobCwsJpxQcnJyezatasmdvjwYfdlJiJeyeSMYEDSAJexDlEdCDREkluRS3Flsctz8q355FfkN2aKIiI1jrsQGjBgAN999x0Ao0aNYsaMGTzyyCNcc801DBjg+pefiPiPGFMgD558I91jT6p1vK2lLfNOmUmSKajOXoV/9deWIhGRxnLcg6WffvppSkpKAJg9ezYlJSW8/fbbdOjQQTPGRPyczW6jknyC96/m/1pfSG7XSWRU5BJvjiah8CBxSy7HcdVSYkNiCQ8Mp6SypM49osxRRJtjPJC97ymrLCO3IpeDxQcxB5hJCk0iPjSeAKNWThH53XH/NDz66KNcccUVQHU32fz5892elIh4F6fTyYGSA7yx5Q2+TP+SkIAQLms9guG2MDouewhKssBRBYCxNJswcxwP9LmdO3+cXav1x4CBh/reRZgz1FOfis/Ir8jntS2vseCXBTWD1yMCI3hqyFP0SexDkCnIwxmKNA/H3TWWk5PDyJEjSU1N5Y477mDDhg2NkZeIeJEDxQe4bOllvLHtDbLKsthbtJe5m15k+s43yBl2b00RBEBwFObyPE7btpy3T3+aEanD6BDVgVGthvPOGc/Qf+OHBGmw9Albnbmalze9XGsGX3FlMTcuv5GM0gwPZibSvBx3IfThhx+SkZHB/fffz08//UTv3r056aSTePTRR9m7d28jpCgizVlFVQX/+uVfFFoL68Q25P7CdrMZIltUH4jrCOHxGMsPE/bzG3R5ayJzSuy8EnUKs4tsdFpyJaEb/42xXIOlT0RueS4vbHjBZazKUcWyPcuaOCOR5uu4CyGA6OhorrvuOlasWMG+ffuYOHEir732Gu3bt3d3fiLSzBVaC/li3xf1xj/IXImj1QCIToPL3oLwRIxBv+05Zi0meN1iYr5+jOCfXwdbKQBGs7rGTkSVo4pDJYfqjf+a/+tRB6yL+IsTGjFXWVnJmjVrWLVqFXv37iUxUXsIifgbg8FAoDGw3rjZZMYw5B4wh0FEMgDG8HiIbQe5u+pekNitOi4NZjaZaR/Vnk2HN7mM90nsg8FgaOKsRJqnBrUIff3110yePJnExEQmTpxIZGQkn3zyCQcOHHB3fiLSzMUEx3BBuwvrjY9pfzGGuPY1RRCAMTIJxyVLIPwvfzxFpuAY96oKoRMUFRzF1B5TXcbCA8M5NXlwE2ck9bE77JTYSrBWWT2dit867hahFi1akJeXx8iRI3nppZcYPXo0ZrM2ShTxVwWlVYxrO5qv9y9nT9GeWrELWo8gOSDa5XXGhM4w+SscOdshZzskdMEY1xGjpUVTpO3TCspsdCi3MrfPHTz2y0s147faWtryeM9biS6rgCjP5ujvnE4nB0sO8unuT/nu0HckhCZwZdcraRPZBovZcvQbiNsYnMfZUfzyyy8zbtw4oqKiGiml5qWoqAiLxUJhYSGRkZGeTkek2SkqzCP08zs53H00ayvz+TjzR0JNwVzWcgjtcvcTYIwl7JRLMRkb1AAtDVCed4iQ18+lKiKZw/2vpSAomACDiejcPcT+8DwVnS/CPOJBdY950O6C3Vz53yspshXVOn5Lr1u4tPOlRARFeCgz33Gs79/H3SI0efLkE0pMRHxLqKOMgJ2fkbT5Hc6N78ywVv0xVlUQtGY6lOVCl/PhlEs9naZfCTA4oDiTgLzdJO37nqS/xIOL9oLTCSqEPKLIWsSjqx6tUwQBPPvzs5zV+iwVQk3Ia/5Ey8vLY/z48URGRhIVFcWkSZNqVriuz5AhQzAYDLVeN9xwQxNlLOIfAgICIfS3laBzthG8dhFBG96sLoIAR3giqDWoSQUGh+Ns0bveuL3dcH1PPKjQVsiqzFX1xn/K/KkJsxGv+UkYP348mzdv5osvvuCTTz7h22+/5brrrjvqdZMnTyYjI6Pm9fe//70JshV/Yq2yUlBRQEVVhadT8YzwBJwDbqo3bOxzVRMmIwCEROEcNtt1i09YPMZ2Q5o8JfnD0UakVDmrjhgX9/KKDWe2bt3KsmXL+Omnn+jbty8A//d//8eoUaN48sknSUlJqffa0NBQkpL+2jAscuIqqio4WHKQxZsXszl3M60jW3N1t6tpE9mG8KBwT6fXdAwGDF3Ph11fwq+f1Y6NnAtRrTyTl58zJnbBecX7GJZOh7zdADjTzsBw7lMYolI9nJ1/iwyK5OS4k9l4eKPLeJ+EU5o4I/923IOlPWHBggXMmDGD/Pw/VputqqoiODiYd955hwsvdD11d8iQIWzevBmn00lSUhKjR4/m/vvvJzS0/sXarFYrVusf0xiLiopITU3VYGmpxel0sjpzNTd8cUOdv97mnDqHkWkjMZua12zKElsJdqediKAIjIZGaAwuzYGC/bDzSzBHQPvhEJ4EwRrr4FHFWVBRCKYACImBkChPZ+T3cksqyCj9lYlfXIPVXnva/KXtx3JVl+tpGZNcz9VyrBptsLQnZGZmkpCQUOtYQEAAMTExZGZm1nvd5ZdfTuvWrUlJSWHjxo3cddddbN++nffee6/ea+bOncvs2bPdlrv4puyybO797l6XTdhzfpxD36S+tAhvHtPAD5cfZkPOBl7f8jrlVeWMbDOSEW1GkBzu5l+0YfHVryOMTREPiEisfkmzYbIW0WHN67xz+tMs2vspqw9vJCY4hmvbnMfJRXkElBWACqEm49FC6O677+bxxx8/4jlbt25t8P3/PIaoe/fuJCcnM2zYMHbt2kW7du1cXjNz5kymT59e8/HvLUIif1ZgLSCrLMtlrMJewaGSQ82iEMotz+XhlQ/z1f6vao5tzt3M61tfZ/E5i0kJr79b2ZvllueSWZrJ9vztJIQk0DaqLYmhiZiMJk+nJkKoo5ign14k7edXmdl1DMVJIwiqKCDy80ehYB/WkU9Dyy6eTtNveLQQmjFjBhMnTjziOW3btiUpKYns7Oxax6uqqsjLyzuu8T/9+/cHYOfOnfUWQmazWQtEis/YU7inVhH0u6yyLBZvWcz0PtMJMgV5ILPGk1mayR3f3MH6nPU1xyICI5h/1nxOij1JxZB4XMDvPdNVVswb38b8l6FCAdibPCd/5tFCKD4+nvj4oy+lP3DgQAoKCli7di19+vQB4KuvvsLhcNQUN8di/fr1ACQnq8lRTkxkUBSJoYkuW4WCTcEkhnj+35jT6eS9HfV3Ay/dvZSrT7qaxDDf6TYpryrnn+v/WasIAiiuLOb6L67nvfPfc3+XoMhxMoZE42zRF8PBNa7jbU9v4oz8m1dMn+/SpQsjR45k8uTJrF69mu+//56pU6dy6aWX1swYO3jwIJ07d2b16tUA7Nq1i4cffpi1a9eyd+9ePvroIyZMmMDpp5/OySef7MlPR3xAgCOCh/rdh8lQt3Xhnt7TMTuCPZDV8XHipNnPlDhOeeV5fLL7E5exksoSdhTsaOKMRFwIjcFw3tMQUPf3hLPfdRg0pqtJeUUhBPDGG2/QuXNnhg0bxqhRoxg8eDAvvfRSTbyyspLt27dTVlYGQFBQEF9++SVnn302nTt3ZsaMGYwdO5aPP/7YU5+C+JDQihx6/7SYd8/4B2PanEPH6I6clTqUN097kuF7fiLCWuDpFAE4s+V59cbOSh2J0RHWhNk0PqvDSqWjst54Zmn9kytEmlR8V7jhO+g9EWLaQmp/uOxtDGfcBSGu9+eTxuEVs8YAYmJiWLJkSb3xNm3a1FqkKjU1lW+++aYpUhM/FFRZQOAv79Ju21Lu63o+ZTF9CC7OIOTta8FaRGWn0UB7j+bodEKH8GROTx7Itxkra8XiQ+KZ2OUyHA7fGi8TbAohNjiW3Ipcl/EOUZ2aOCORegQEQlwHOOdxsBaBKUhLG3iI1xRCIs2JKSCw+n+qKjBv/Dd/HV5vDAxp8pz+ymg0kHToZ2YnnM66lNN5Lf2z6unzyQMZFduTxP/Nw37WHMDzubqLyWFhao+bmL3qoTqxk2K6EhUQ64GsRI4gMLj6JR6jQkikAYyhcRDbHnJ31g0GWzBZPD91HocD8/YPMW/5gLNj2zGgy2jsoWYit3yDac9DEBSOaejdgMXTmbpNUGUJw0pLMfS8lWe3vUZeRR4BhgBGpg7l1tbnEmrz021QRKReKoREGiIiAcb+C149F2x/2vzXGAAXL4SIZjAzyWiEiN+Wl8jdReR382rHQ2PAxWBvbxbuKCLw41u5sPWpnNr3RsrMYQQZjMRs+4zQJZdTee48SPZsl6WINC8qhEQaKqk7TPketn4M6Ssh4STocQlEtqzezqA56HUlrJrvOtZ/CoT71uyUgN/2GDXu+56kfd/XiZsc2sxSRGprJr+tRbyQ0QTRbWDQzTBgSnVrUHMTlQoj5sJnM2sfbzcMul3kendyL2YIsUBiN8j6xWXc2PrY1x0TEf/QDH9zi3ih5lgEAQRbqluF2g+HHZ9BRTF0PLt6R/jwhKNf723C4mH0PFh4Dtj/Mo1+wI0Q5oOfs4ickGb621tE3CY4ovoV39HTmTSNpB5w/Xfw3dOQ/mN1wXfaDGh5iqYni0gdKoRExLcEBEFC5+qWoYri6o+1QJ2I1EOFkIj4psDQ6peIyBGoEJImU2mvJKc8h7yKPIwGIzHBMcSHxGs3cBER8RgVQtIkSmwlrNi/gjmr5lBaWQqAxWxh7uC59Evqhzngr2szi4iIND6v2XRVvNvuwt3M/G5mTREEUGgt5OavbuZAyQEPZiYiIv5MhZA0ulJbKfM3uF7Uz+608+a2N6n861RnERGRJqBCSBpdub2cPUV76o3/mv8rVru1CTMSERGppkJIGl1IQAhtI9vWG+8U3RmzSWOERESk6akQkkYXGhDKtd2udRkzGUxc3H4sgabAJs5KREREhZA0gYIyGy3Kbfy9711EBEbUHI8JjuH5/g8QU1pBmU2bYfqLKkcV+4v38+bWN3lo5UN8vOtjDpUcwul0ejo1EfFDmj4vjc5cVUz0V/dzlimIngPuJi8wCAMGYsqLSPjmWRxRbbCPfg79c/R9doedjTkbue6L62rGhb3z6ztYzBZeHfkq7aPaezjDxmetspJRmsEX+75gb9FeBiYPpE9iH5LDkz2dmohf0juPNLpgox1KcwjI203y3v/x11/3xgAzAQaHR3KTppVTnsOtX99aZ3B8obWQu769i5fPepmYkBgPZdf4bHYbKzNWMu3radiddgA+2vURscGxvDryVdpY2pzQ/YttxeRV5PFr3q+YA8y0s7QjLjROY/BEjkCFkDQ6Y3AkjlaDMObtdhl3tD0TY5C2QvAHWaVZFFgLXMZ+zf+VfGu+TxdCOWU53P7N7TVF0O9yK3KZvXI284bOw2K2NOjeeeV5LPhlAYu3LMZJdTdjkDGIuafN5bSWpxESEHLC+Yv4Io0RksYXGILx1FvB1erR5kiMPf4G2mbDL5RXlR8xXunw7fWkdhbsrHepiDVZa+otEo/FT1k/sWjLopoiCMDmsHH7N7dzqORQg+8r4utUCEnTiGkDVy+D5J41h5ytT4VJn4GllcfSkqaVFJaC0eD6105EYAThgZFNnFHTKqksOWK8ytGwSQN55Xm8tPEllzEnTt7b8Z4Go4vUQ11j0jRMQdCiN1zxHlQUgMGAISQaQqI9nZk0oTBHEOPbX8xrO/5dJzbt5BsIdYZ7IKum0yGqU72xpLAkwgIa9vlXOirJLM2sN76vaB9VzioCDVqmQuSv1CIkTSssFmLbQUzbRi+CrFVWSmwlOJwaiN1chJfnc21QErN73kpyWPWw+baWtvxfv1mMyN5LSMVhD2fYuCwEc0HrES5jM3veirmBhWBoQChdY7rWGz8lsR+BRhVBIq6oRUh8ToG1gN0Fu3lty2vkW/MZ0nIIZ7U5ixbhLTydmt8LKDlEzCe3c1FKb07rcyX28HgC89OJ/fIxyN1JZcfzPZ1io4ooOMhtUT052dKeV3a9R05ZDl1jOnNb5yvovP1LTJaTICLtuO9bWRnEjT1uZFXmqlpjhAAigyI5Nek0nE4nBoPBXZ+KiM9QISQ+pchaxMJfFrLglwU1x9ZmrWXh5oUsPmcxrSNbezA7MYb9NiPs0DriD62rGzeHNXFGTSugYDexH93EuBZ9GNp7PPbQWIJzthP10R1QdJDK3q5XYD86J62y9/DPAQ/y0Kb5ZJRmANA1titzut1AXHE+lVGtCQpQISTyVyqExKdkl2XXKoJ+l1eRx7PrnuXhUx8mNFBT9T3FFJkEUa2hYF+dmLPNaZjC4zyQVdMxxVQX4oaDa4k/uLZ2MMCMqYGFYERVPoHLZzE4JIrXB1xPUVgsJoORqKwtRL87BUfaGRhb//NE0xfxSSqExKd8vf/remPL05czo+8MFUKeFJEM49+BxRdAccYfx+M7YbjgeZ8fPG+KaQOWllB4oE7M0XM8xojEBt030OiEyjIo3E/CB1NJ+EvcaCsGzRoTcUmFkPiUI00/1qDpZiK+E0xeDnl7oCAdYjtAVCpEJHk6s8YXmQJXvg9LLoE/LTDq7HIBxjPugsAGLnoYHI2z0zkYfn7dZdh58qUYTPp1L+KKfjLEpwxucTr/3OC6C2BgyiDCg3x7erbXiGxR/fJHcR3h6v9CSXb1UhIRyRjC4k6sNSwoBMPg6bDlI7AW1Y7Fd8HQsu8JpSziy1QIiU+JMUZwbquzWJr+Ra3jIQEhTO8xFZs1CIIa7/lVjiqyyrLYmruVjNIMusV1o2V4S+JD4xvvoeJ9IpLc3wIWnQbXfQ3fPgnbPoGAYOhzNfSZWN0SJSIuGZxabvSIioqKsFgsFBYWEhnp26ve+oKi7d9SWbqLtUGBLNy3lAJrAYPje3NFqxG0XPsGZUMeIiKmYeMwjqbKUcXGnI3c8OUNtbaS6BjdkeeGPVezbo5Io7KVQXkBGAwQFg/qEhM/dazv316zoOIjjzzCoEGDCA0NJSoq6piucTqdzJo1i+TkZEJCQhg+fDg7duxo3ETFo0LSvyH2o2mc/fUzzA/rzmtJI7k9K4PWiy/CtPEtgh1ljfbs7LJspnw5pc5+Wr/m/8q8tfMorzzyPlsibhEUCpYUiExWESRyDLymELLZbIwbN44pU6Yc8zV///vfefbZZ5k/fz6rVq0iLCyMESNGUFFR0YiZiicZf59+nbsTy4rHift8FuZf3gV7JQQEExDQeKvr7izYSVmV60Lrs72fkVuR22jPFhGRhvGaQmj27NncdtttdO/e/ZjOdzqdzJs3j/vuu48LLriAk08+mcWLF3Po0CE++OCDxk1WPMbY4azqLgEXHD0ux9CI69TklOXUG7M77djstkZ7toiINIzXFELHa8+ePWRmZjJ8+PCaYxaLhf79+7Ny5cp6r7NarRQVFdV6ifcwRCbB+c/XOe6M74zx9BnVA0gbSZfYLvXG4kLiCAv07VWTRUS8kc92IGdmVu/EnJhYe2BsYmJiTcyVuXPnMnv27EbNTRpRUDh0vQBS+8HWj6sX7es4AkPCSdVjJhpRfGAUfeJ6sPbwhjqxaT1uItbs26smi4h4I4+2CN19990YDIYjvrZt29akOc2cOZPCwsKa1/79+5v0+eIG5nCI6wCnTYdRT0D74Y1eBAFEFh3m8Y5XcEnb8zGbzAAkhibyWJ87GJKzj6rCQ42eg4iIHB+PtgjNmDGDiRMnHvGctm3bNujeSUnVa3RkZWWRnPzHm2BWVhY9e/as9zqz2YzZbG7QM8W/OQ6sJfGLO7ij6xiu6TuLSqOJ4LI8ElYuwHBwHdZ2ozydooiI/IVHC6H4+Hji4xtnobm0tDSSkpJYvnx5TeFTVFTEqlWrjmvmmcixMkbEg70S86Z3SNn0Tu2gwYghQAW2iEhz4zWDpdPT01m/fj3p6enY7XbWr1/P+vXrKSkpqTmnc+fOvP/++wAYDAamTZvGnDlz+Oijj9i0aRMTJkwgJSWFMWPGeOizEF9mSOwG9Wzoau90HoRpjJCISHPjNYOlZ82axaJFi2o+7tWrFwBff/01Q4YMAWD79u0UFhbWnHPnnXdSWlrKddddR0FBAYMHD2bZsmUEBzfezCHxXwFRKVRe8iaBb/0Nqqx/BGLb4zh7DkGhWplcRKS50RYbR6EtNuR4OCpt2AsP4tz3AxSkY2g1AGdcR4Ki/XSDURERDznW92+vaRES8QbGwCCMcWkQl+bpVERE5Bh4zRghEREREXdTISQiIiJ+S4WQiIiI+C0VQiIiIuK3VAiJiIiI31IhJCIiIn5LhZCIiIj4LRVCIiIi4rdUCImIiIjfUiEkIiIifkuFkIiIiPgtFUIiIiLit1QIiYiIiN9SISQiIiJ+S4WQiIiI+C0VQiIiIuK3VAiJiIiI3wrwdAIiIs1ZobWQvIo8SipLiAiMICY4hkhzpKfTEhE3USEkIlKPzNJMHvzhQb4/9H3NsTNanMF9A+8jKSzJg5mJiLuoa0xExIXCikJmfT+rVhEE8M3Bb5jz4xyKrEUeykxE3EmFkIiIC3nWPFZmrHQZ++bAN+RV5DVxRiLSGNQ1JuImVfYqcspzyCnPweF0kBCaQFxIHEGmIE+nJg1wtBaf4sriJspERBqTCiERNyivKueHQz9w33f3UVJZAkBIQAgz+83krNZnER4U7uEM5XhFBEUcOR545LiIeAd1jYm4wf7i/dz29W01RRBUF0ezfpjFroJdHsxMGirUGEG/xFNcxgannEqIUYWQiC9QISRygiodlSzZugQnTpfx+RvnU2orbeKs5ERFWMt4pPME+if2rXV8YFI/Hmj/N8Ir9D0V8QXqGhM5QRWVFUds9UkvSqfcXk4YYU2YlZyowLIckt68gqdOvYXc06+g2G4l0mQmJn01ljevwHblUqCVp9MUkROkQsgDbHYbh8sPY7VbCQ4IJj4kngCjvhXeKjggmC6xXVmfs95lvEN0B0IDQps2KTlhRiNgLcby1SNYXMUNTZ2RiDQGvfs2sZyyHF7b8hpvbX+L8qpyIoMimdRtEmPajyEmJMbT6UkDmAwBjGs/lne2/5sqZ1WtmAED1550DQGGYLc973D5YfIq8qioqiAmOIbYkFhCAkLcdn+pZoxIBHMkuJo9FhKNMTy+6ZMSEbfTGKEmVGQr4qk1T7Fw80LKq8prjj2z7hle3/o61iqrhzOUhiivtBN/cDvzBz5EQmhCzfFoczTz+t1Hi0NbKa+ocMuzdhXs4ppl1zD2o7GM/3Q8oz8YzfwN88mtyHXL/eUPxogkHOc/C4a/NP0YDDjOfw5jhFaWFvEFahFqQnnleSzds9RlbNHmRVzU4SJaRrRs4qzkRAXZSwhb+0/6WYt589SbyAuNxul0Em0rI/6H+ZjK8rC2OQM4se6xjJIMrvnsmloL+VU5qljwywLiQ+K5vMvlGA3628ZtTIEY258F132D87t5GA5vx5nQFcOpt2KMaQsm/foU8QX6SW5C2WXZ9cZsDhvFNi3Q5o0CAwJxBoZi2PcDCe/eQMJfT0jugTkw8ISfsz1/e72rGb+86WXOan0WiWGJJ/wc+RNzOCT3wDDmebCVYQgKg0B1Q4r4Eq/58/GRRx5h0KBBhIaGEhUVdUzXTJw4EYPBUOs1cuTIxk30CI62qJ45wNxEmYhbBYXh7HdDvWFHv+shLPaEH7M9f3u9sbyKPGx22wk/Q+oRGAphcSqCRHyQ1xRCNpuNcePGMWXKlOO6buTIkWRkZNS83nzzzUbK8OjigmNpEd7CZaxHfA9izBos7a2MKT1wnnRRnePOtDMwth/mlmd0jOpYbyzaHK2tPEREGsBrusZmz54NwKuvvnpc15nNZpKSmsegxpgqB8+dcg/XrpxVa3Bry4iWzO1+ExFVlR7MTk5IeAKGc/4O/a6Dta+Cowp6XYkhoQtEuKe7qqOlHdHmaPKt+XVi1540kfhQzWISETleXlMINdSKFStISEggOjqaM888kzlz5hAbW383hdVqxWr9Y/ZWUdGRN148LoUHaf/hNN4afj97DA7SSzNoF5FKK2sFCf+eSOX49zFpSq73Co+vfrUaAE7nbwvRuE98cR4LBj7EtHVPsq9oHwABhgDGtx/DecZIrAXZhEQ3j6JfRMRb+HQhNHLkSC666CLS0tLYtWsX99xzD+eccw4rV67EZDK5vGbu3Lk1rU/uZreVEpC7i6S3J5IUGsvAsDgozoKKAgAclZo+7xMMhrpTrt3A8esXtN+4mIWn3UZedCpWRyXRhkBiN39E6OeTsN60zu3PFBHxdR4thO6++24ef/zxI56zdetWOnfu3KD7X3rppTX/3717d04++WTatWvHihUrGDbM9biNmTNnMn369JqPi4qKSE1NbdDz/8oZ2QIMRnA6oCy3+vU7cwTOkGi3PEd8VFg8FKQT//Ft1Gk3DDBj0OrkIiLHzaO/OWfMmMHEiROPeE7btm3d9ry2bdsSFxfHzp076y2EzGYzZnPjzN6ymWOx95lC2Jrn68RKTp2JMyQB960/LL7G1PY0MJrAYa8TqzzpbxjD4zyQlYiId/NoIRQfH098fNONiTlw4AC5ubkkJyc32TP/LCIyisIBN1MQ3Y6o1U9D4QGIbU/ewJkY004jKkz7UckRRCZSeeG/CHzvmupWxd844rvAGXcSYNamriIix8tr2tLT09PJy8sjPT0du93O+vXrAWjfvj3h4dXr83Tu3Jm5c+dy4YUXUlJSwuzZsxk7dixJSUns2rWLO++8k/bt2zNixAiPfA4GgwFLbBI53ceT2WYY2CsxBJgxRiQQE6G2IDmyQHM4lR3OovLGn3D++jmGkkycaUMgvhNB0a6XZRARkSPzmkJo1qxZLFq0qObjXr16AfD1118zZMgQALZv305hYSEAJpOJjRs3smjRIgoKCkhJSeHss8/m4YcfbrSur2NhMBhIiAyGyDYey0G8V2BwOAS3h/j2nk5FRMQnGJxOp9PTSTRnRUVFWCwWCgsLiYyM9HQ6IiIicgyO9f3ba1aWFhEREXE3FUIiIiLit1QIiYiIiN9SISQiIiJ+S4WQiIiI+C2vmT4vUp8qRxVZpVlsPLyR/cX76R7XnbaWtiSGuWfXdxER8V0qhMSr2R12Nh/ezOQvJlNeVV5zvGV4S14++2VaRrT0YHYiItLcqWtMvFp2WTY3Lr+xVhEEcKDkAA//+DDFtmIPZSYiIt5AhZB4tf3F+ymyFbmM/XDoB/Iq8po4IxER8SbqGhOvlm/NP2LcZrc1USb+K78inwJrAZX2SiLNkcSHxGMymjydVqM5XH6Y7LJsMkszSQpLIiE0gbiQOE+nJSINpEJIvFr7qPr33IoyRxERFNGE2fif3QW7uee7e9icuxmAaHM0t59yO0NaDiHS7Htb0hwoPsDNX93MzoKdNcfaR7XnuTOfo0WENr4V8UbqGhOvFm0KZ1jLIS5jt/aYQnyw/lJvLBklGVz92dU1RRBUt9Dd+929bDy80YOZNY788nzu+PaOWkUQwM6Cndzx7R3kVxy5dVJEmicVQuLVIsoKuDdlONd1upywwDAAksOSebzPnQzPy8JemOnhDH3Xuux19Y7BenrN0+SW5zZxRo0rz5rHL4d/cRnbdHiTxqOJeCl1jYlXs2dtIf79a5jS4WzG9bwDm8lMcFkuCd+/AhnrsaadBWgKfWNYm7W23tiOgh1UOiqbMJvGV1pZekJxEWmeVAiJdwsKB6eDgF+XkfTrsrpxY2DT5+Qn2lra1htLDE3EZPCtAdMWswUDBpw468QMGLCYLR7ISkROlLrGxKsZ4ztCUJjLmCO1P46QmCbOyH+c0WIwgfUUmpO7TfK5mVQRhlBGthruMnZO67OIMIQ2cUYi4g4qhMSrOcMTqbjwVTD+pXEzLI6qc58lxBLvkbz8QYLNyvwBswkPDK85ZsDA39JGMzw4BYOPLV0QVl7IHclDGJt2LgG//XsLMAYwNu1cZiSdTlh5oYczFJGGMDidzrrtvFKjqKgIi8VCYWEhkZG+Nx3YF5SVlWIqPoR9y0cE5e/AmnoaAWmDMUWnEmBSrd9Y7J/Pwpn+PTmDb+WA0UlZVTmtQxKI/fULIta8StWNPxEQ7Tvjs2z7VhO06BzKe17O4S6jKDc4CXEaiNv6KSHrl2C76r8Ete7n6TRF5DfH+v6tMULi9UJDwyC0A5Wxt1HpcGA2GVUANQGHtYzAA2tIfutKkoPCIMAM5fngdILRhN3p8KlfMIagMHBUEbJuManrFruIh7u4SkSaO71biM8IDDASEhSgIqiJVHQc/ccHtlIoy6sugoDKdiOxmXxsMcvQOEjq7jqWdDLO0NimzUdE3ELvGCLSMLHtsbU+o+7xoDDKTruX4HDf6koOtCRSdfEiiE6rHYhOo+riVwmyJHomMRE5Ib7Uci0iTSksgZzh/yBk13+J2fQvsBZT0WY4BX1uJCAqjUCTb02fBwiIa4d94qc48/fhzN+LIboNhujWBFhSPJ2aiDSQBksfhQZLi9SvqLyS/FIrRbkZ4LQTFB5NVISFREuwp1MTET+nwdIi0ugiQwKJDAmEuA6eTkVEpEE0RkhERET8lgohERER8VsqhERERMRvqRASERERv6VCSERERPyWCiERERHxWyqERERExG+pEBIRERG/5RWF0N69e5k0aRJpaWmEhITQrl07HnjgAWw22xGvq6io4KabbiI2Npbw8HDGjh1LVlZWE2UtIiIizZ1XFELbtm3D4XDw4osvsnnzZp555hnmz5/PPffcc8TrbrvtNj7++GPeeecdvvnmGw4dOsRFF13URFmLiIhIc+e1e4098cQTvPDCC+zevdtlvLCwkPj4eJYsWcLFF18MVBdUXbp0YeXKlQwYMOCYnqO9xkRERLzPsb5/e0WLkCuFhYXExMTUG1+7di2VlZUMHz685ljnzp1p1aoVK1eurPc6q9VKUVFRrZeIiIj4Jq8shHbu3Mn//d//cf3119d7TmZmJkFBQURFRdU6npiYSGZmZr3XzZ07F4vFUvNKTU11V9oiIiLSzHi0ELr77rsxGAxHfG3btq3WNQcPHmTkyJGMGzeOyZMnuz2nmTNnUlhYWPPav3+/258hIiIizUOAJx8+Y8YMJk6ceMRz2rZtW/P/hw4dYujQoQwaNIiXXnrpiNclJSVhs9koKCio1SqUlZVFUlJSvdeZzWbMZvMx5S8iIiLezaOFUHx8PPHx8cd07sGDBxk6dCh9+vRh4cKFGI1Hbszq06cPgYGBLF++nLFjxwKwfft20tPTGThw4AnnLiIiIt7Po4XQsTp48CBDhgyhdevWPPnkk+Tk5NTEfm/dOXjwIMOGDWPx4sX069cPi8XCpEmTmD59OjExMURGRnLzzTczcODAY54xJo0juyybvIo8bHYbMcExxIXEERwQ7Om0RETED3lFIfTFF1+wc+dOdu7cScuWLWvFfp/9X1lZyfbt2ykrK6uJPfPMMxiNRsaOHYvVamXEiBH885//bNLc5Q8Op4NteduY9vU0MkozAAg0BnLdyddxSadLiA6O9nCGIiLib7x2HaGmonWE3OdgyUHGfjSW0srSOrFHBz/K6HajPZCViIj4Ip9fR0i8z+qM1S6LIIDn1z9PTlmOy5iIiEhjUSEkTWZz7uZ6YwdLDlLlqGrCbERERFQISRPqFtet3ljLiJYEmgKbMBsREREVQtKE+sT1Ijww3GXsppOnEBcS18QZiYiIv1MhJE0mrjifhYMeoWX4HzP/goxB3NJ1IoMqKikryvdgdiIi4o+8Yvq8+AbTxrfovOszFg+eRl5UMjZHFTGYiFv/FubNc7BO3QBoCr2IiDQdFULSdAKCIW838R/dQp31xAPMGAwGT2QlIiJ+TF1j0mQM3S6qN1bV7RICwjVGSEREmpYKIWkyBksLqvrfVDdgaQmnzcAYFNL0SYmIiF9T15g0mYDwWOynTaeqy2iMP72EsaKAqs4XQLszCYhp5en0RETED6kQkiZlCo+D8Dho2QcclQQEhXk6JRER8WMqhMQzAoKAIE9nISIifk5jhERERMRvqRASERERv6VCSERERPyWCiERERHxWyqERERExG+pEBIRERG/pUJIRERE/JYKIREREfFbKoRERETEb6kQEhEREb+lLTaOwul0AlBUVOThTERERORY/f6+/fv7eH1UCB1FcXExAKmpqR7ORERERI5XcXExFoul3rjBebRSyc85HA4OHTpEREQEBoPB0+kcVVFREampqezfv5/IyEhPpyPHSN8376PvmXfS9807NeT75nQ6KS4uJiUlBaOx/pFAahE6CqPRSMuWLT2dxnGLjIzUD7kX0vfN++h75p30ffNOx/t9O1JL0O80WFpERET8lgohERER8VsqhHyM2WzmgQcewGw2ezoVOQ76vnkffc+8k75v3qkxv28aLC0iIiJ+Sy1CIiIi4rdUCImIiIjfUiEkIiIifkuFkIiIiPgtFUI+au/evUyaNIm0tDRCQkJo164dDzzwADabzdOpyVE88sgjDBo0iNDQUKKiojydjtTj+eefp02bNgQHB9O/f39Wr17t6ZTkCL799ltGjx5NSkoKBoOBDz74wNMpyVHMnTuXU045hYiICBISEhgzZgzbt293+3NUCPmobdu24XA4ePHFF9m8eTPPPPMM8+fP55577vF0anIUNpuNcePGMWXKFE+nIvV4++23mT59Og888ADr1q2jR48ejBgxguzsbE+nJvUoLS2lR48ePP/8855ORY7RN998w0033cSPP/7IF198QWVlJWeffTalpaVufY6mz/uRJ554ghdeeIHdu3d7OhU5Bq+++irTpk2joKDA06nIX/Tv359TTjmF5557DqjekzA1NZWbb76Zu+++28PZydEYDAbef/99xowZ4+lU5Djk5OSQkJDAN998w+mnn+62+6pFyI8UFhYSExPj6TREvJrNZmPt2rUMHz685pjRaGT48OGsXLnSg5mJ+LbCwkIAt7+PqRDyEzt37uT//u//uP766z2diohXO3z4MHa7ncTExFrHExMTyczM9FBWIr7N4XAwbdo0Tj31VLp16+bWe6sQ8jJ33303BoPhiK9t27bVuubgwYOMHDmScePGMXnyZA9l7t8a8n0TEZFqN910E7/88gtvvfWW2+8d4PY7SqOaMWMGEydOPOI5bdu2rfn/Q4cOMXToUAYNGsRLL73UyNlJfY73+ybNV1xcHCaTiaysrFrHs7KySEpK8lBWIr5r6tSpfPLJJ3z77be0bNnS7fdXIeRl4uPjiY+PP6ZzDx48yNChQ+nTpw8LFy7EaFQDoKccz/dNmregoCD69OnD8uXLawbbOhwOli9fztSpUz2bnIgPcTqd3Hzzzbz//vusWLGCtLS0RnmOCiEfdfDgQYYMGULr1q158sknycnJqYnpr9bmLT09nby8PNLT07Hb7axfvx6A9u3bEx4e7tnkBIDp06dz1VVX0bdvX/r168e8efMoLS3l6quv9nRqUo+SkhJ27txZ8/GePXtYv349MTExtGrVyoOZSX1uuukmlixZwocffkhERETNGDyLxUJISIjbnqPp8z7q1VdfrfeXsr7lzdvEiRNZtGhRneNff/01Q4YMafqExKXnnnuOJ554gszMTHr27Mmzzz5L//79PZ2W1GPFihUMHTq0zvGrrrqKV199tekTkqMyGAwujy9cuPCoQw2O6zkqhERERMRfadCIiIiI+C0VQiIiIuK3VAiJiIiI31IhJCIiIn5LhZCIiIj4LRVCIiIi4rdUCImIiIjfUiEkIiIifkuFkIiIiPgtFUIi4vdWrFhB7969MZvNtG/fXlsuiPgRFUIi4tf27NnDueeey9ChQ1m/fj3Tpk3j2muv5bPPPvN0aiLSBLTXmIj4tJycHLp3784tt9zCPffcA8APP/zAkCFD+O9//8vnn3/O0qVL+eWXX2quufTSSykoKGDZsmWeSltEmohahETEp8XHx7NgwQIefPBB1qxZQ3FxMVdeeSVTp05l2LBhrFy5kuHDh9e6ZsSIEaxcudJDGYtIUwrwdAIiIo1t1KhRTJ48mfHjx9O3b1/CwsKYO3cuAJmZmSQmJtY6PzExkaKiIsrLywkJCfFEyiLSRNQiJCJ+4cknn6Sqqop33nmHN954A7PZ7OmURKQZUCEkIn5h165dHDp0CIfDwd69e2uOJyUlkZWVVevcrKwsIiMj1Rok4gfUNSYiPs9ms3HFFVdwySWX0KlTJ6699lo2bdpEQkICAwcO5NNPP611/hdffMHAgQM9lK2INCXNGhMRn3fHHXfwn//8hw0bNhAeHs4ZZ5yBxWLhk08+Yc+ePXTr1o2bbrqJa665hq+++opbbrmFpUuXMmLECE+nLiKNTIWQiPi0FStWcNZZZ/H1118zePBgAPbu3UuPHj147LHHmDJlCitWrOC2225jy5YttGzZkvvvv5+JEyd6NnERaRIqhERERMRvabC0iIiI+C0VQiIiIuK3VAiJiIiI31IhJCIiIn5LhZCIiIj4LRVCIiIi4rdUCImIiIjfUiEkIiIifkuFkIiIiPgtFUIiIiLit1QIiYiIiN/6fyUE37PI6UiWAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "## plot QR results\n", + "import pandas as pd\n", + "import seaborn as sns\n", + "import warnings\n", + "import matplotlib.pyplot as plt\n", + "warnings.filterwarnings(\"ignore\", \"is_categorical_dtype\")\n", + "\n", + "n_sample = 50\n", + "X_sample, y_sample = X[:n_sample], y[:n_sample]\n", + "q05_sample = clf5.decision_function(X_sample)\n", + "q95_sample = clf95.decision_function(X_sample)\n", + "\n", + "df = pd.DataFrame({'x0': X_sample[:,0], 'real_y': y_sample, 'q05': q05_sample, 'q95': q95_sample})\n", + "df = df.melt(id_vars='x0')\n", + "\n", + "sns.scatterplot(data=df, x='x0', y='value', hue='variable')\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/.ipynb_checkpoints/SVM-checkpoint.ipynb b/.ipynb_checkpoints/SVM-checkpoint.ipynb new file mode 100644 index 0000000..05e9fe0 --- /dev/null +++ b/.ipynb_checkpoints/SVM-checkpoint.ipynb @@ -0,0 +1,108 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "fbcb401d-6ca6-4933-abd5-f8f504282416", + "metadata": {}, + "source": [ + "# **SVM**\n", + "\n", + "[![Slides](https://img.shields.io/badge/🦌-ReHLine-blueviolet)](https://rehline-python.readthedocs.io/en/latest/)\n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1f_7t1t6FNxAooQOmpyhHCOVq0IKgMxe-?usp=sharing)\n", + "\n", + "SVMs solve the following optimization problem:\n", + "$$\n", + " \\min_{\\mathbf{\\beta} \\in \\mathbb{R}^d} \\ C \\sum_{i=1}^n ( 1 - y_i \\mathbf{\\beta}^\\intercal \\mathbf{x}_i )_+ + \\frac{1}{2} \\| \\mathbf{\\beta} \\|_2^2\n", + "$$\n", + "where $\\mathbf{x}_i \\in \\mathbb{R}^d$ is a feature vector, and $y_i \\in \\{-1, 1\\}$ is a binary label.\n", + "\n", + "> **Note.** Since the hinge loss is a plq function, thus we can solve it by `rehline.plqERM_Ridge`." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "2dd1c096-e0df-492f-be63-8ac272007237", + "metadata": {}, + "outputs": [], + "source": [ + "## simulate data\n", + "from sklearn.datasets import make_classification\n", + "from sklearn.preprocessing import StandardScaler\n", + "import numpy as np\n", + "\n", + "scaler = StandardScaler()\n", + "\n", + "n, d = 10000, 5\n", + "X, y = make_classification(n_samples=n, n_features=d)\n", + "X = scaler.fit_transform(X)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "aece9fbe-f9be-40ae-8179-b44849fb0fd3", + "metadata": {}, + "outputs": [], + "source": [ + "## solve SVM via `plqERM_Ridge`\n", + "from rehline import plqERM_Ridge\n", + "\n", + "clf = plqERM_Ridge(loss={'name': 'svm'}, C=1.0)\n", + "clf.fit(X=X, y=y)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "93719987-c6b3-4a9b-9b40-c35e5bf90ef0", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import seaborn as sns\n", + "import pandas as pd\n", + "import warnings\n", + "import matplotlib.pyplot as plt\n", + "warnings.filterwarnings(\"ignore\", \"is_categorical_dtype\")\n", + "warnings.filterwarnings(\"ignore\", \"use_inf_as_na\")\n", + "\n", + "score = clf.decision_function(X)\n", + "df = pd.DataFrame({'score': score, 'y': y})\n", + "sns.histplot(df, x=\"score\", hue=\"y\")\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/README.md b/README.md index c90a90c..47b744d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ReHLine +# ReHLine **ReHLine** is designed to be a computationally efficient and practically useful software package for large-scale empirical risk minimization (ERM) problems. diff --git a/doc/source/examples/FairSVM.ipynb b/doc/source/examples/FairSVM.ipynb new file mode 100644 index 0000000..422100e --- /dev/null +++ b/doc/source/examples/FairSVM.ipynb @@ -0,0 +1,223 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "50711dda-105e-4714-937b-e8be06370605", + "metadata": {}, + "source": [ + "# **FairSVM**\n", + "\n", + "[![Slides](https://img.shields.io/badge/🦌-ReHLine-blueviolet)](https://rehline-python.readthedocs.io/en/latest/)\n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1f_7t1t6FNxAooQOmpyhHCOVq0IKgMxe-?usp=sharing)\n", + "\n", + "The FairSVM solves the following optimization problem:\n", + "\n", + "$$\n", + "\\begin{align}\n", + " & \\min_{\\mathbf{\\beta} \\in \\mathbb{R}^d} \\frac{C}{n} \\sum_{i=1}^n ( 1 - y_i \\mathbf{\\beta}^\\intercal \\mathbf{x}_i )_+ + \\frac{1}{2} \\| \\mathbf{\\beta} \\|_2^2, \\nonumber \\\\\n", + " \\text{subject to } & \\quad \\frac{1}{n} \\sum_{i=1}^n \\mathbf{z}_i \\mathbf{\\beta}^\\intercal \\mathbf{x}_i \\leq \\mathbf{\\rho}, \\quad \\frac{1}{n} \\sum_{i=1}^n \\mathbf{z}_i \\mathbf{\\beta}^\\intercal \\mathbf{x}_i \\geq -\\mathbf{\\rho},\n", + "\\end{align}\n", + "$$\n", + "\n", + "where:\n", + "\n", + "* $\\mathbf{x}_i \\in \\mathbb{R}^d$ is a feature vector\n", + "* $y_i \\in \\{-1, 1\\}$ is a binary label\n", + "* $\\mathbf{z}_i$ is a collection of **centered sensitive features**, such as gender and/or race, satisfying:\n", + "\n", + "$$\\sum_{i=1}^n z_{ij} = 0,$$\n", + "\n", + "* $\\mathbf{z}_i \\in \\mathbb{R}^{d_0}$ is a $d_0$-length sensitive feature vector\n", + "* $\\mathbf{\\rho} \\in \\mathbb{R}_+^{d_0}$ is a vector of constants that trade-off predictive accuracy and fairness\n", + "\n", + "The constraints limit the correlation between the sensitive features and the decision function, ensuring fairness in the predictions.\n", + "> **Note.** Since the hinge loss is a plq function, and fairness constraints are linear, thus we can solve it by `rehline.plqERM_Ridge`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "e66268fa-403d-402b-9ea1-fbfe7573af40", + "metadata": {}, + "outputs": [], + "source": [ + "## simulate data\n", + "from sklearn.datasets import make_classification\n", + "from sklearn.preprocessing import StandardScaler\n", + "import numpy as np\n", + "\n", + "scaler = StandardScaler()\n", + "\n", + "n, d = 10000, 5\n", + "X, y = make_classification(n_samples=n, n_features=d, n_redundant=0)\n", + "## convert y to +1/-1\n", + "y = 2*y - 1\n", + "X = scaler.fit_transform(X)\n", + "\n", + "## we take the first column of X as sensetive features, and tol is 0.1\n", + "X_sen = X[:,0]\n", + "tol_sen = 0.1" + ] + }, + { + "cell_type": "markdown", + "id": "6a576a09-b700-49cd-b500-219f3a6e40b0", + "metadata": {}, + "source": [ + "## SVM as baseline" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "15531796-3a45-42b3-8a99-da0343be9d4d", + "metadata": {}, + "outputs": [], + "source": [ + "## we first run a SVM\n", + "from rehline import plqERM_Ridge\n", + "\n", + "clf = plqERM_Ridge(loss={'name': 'svm'}, C=1.0, max_iter=50000)\n", + "clf.fit(X=X, y=y)" + ] + }, + { + "cell_type": "markdown", + "id": "79bb275b-2dfd-4608-83e3-b4b4eb0fdb72", + "metadata": {}, + "source": [ + "## FairSVM" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "c43509f7-031b-4620-bc5e-fb5aea2ef1c2", + "metadata": {}, + "outputs": [], + "source": [ + "## solve FairSVM via `plqERM_Ridge` by adding `constraint`\n", + "from rehline import plqERM_Ridge\n", + "\n", + "fclf = plqERM_Ridge(loss={'name': 'svm'},\n", + " constraint=[{'name': 'fair',\n", + " 'X_sen': X_sen,\n", + " 'tol_sen': tol_sen}],\n", + " C=1.0,\n", + " max_iter=50000)\n", + "fclf.fit(X=X, y=y)" + ] + }, + { + "cell_type": "markdown", + "id": "794ede1f-13a4-4889-b6d9-f19a61faa510", + "metadata": {}, + "source": [ + "## Results" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "05dc3921-1837-474e-9a6d-4555a94ddc30", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Model Train Performance Correlation with Sensitive Features\n", + " SVM 0.8853 2.535203\n", + "FairSVM 0.5856 0.100212\n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "## score\n", + "score = clf.decision_function(X)\n", + "fscore = fclf.decision_function(X)\n", + "\n", + "svm_perf = len(y[score*y > 0])/n\n", + "fsvm_perf = len(y[fscore*y > 0])/n\n", + "\n", + "svm_corr = score.dot(X_sen) / n\n", + "fsvm_corr = fscore.dot(X_sen) / n\n", + "\n", + "# Create a pandas DataFrame to store the results\n", + "results = pd.DataFrame({\n", + " 'Model': ['SVM', 'FairSVM'],\n", + " 'Train Performance': [svm_perf, fsvm_perf],\n", + " 'Correlation with Sensitive Features': [svm_corr, fsvm_corr]\n", + "})\n", + "\n", + "# Print the results as a table\n", + "print(results.to_string(index=False))" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "ad5a863e-fbbb-4caf-876d-374f3ca9b891", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjsAAAHHCAYAAABZbpmkAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA37UlEQVR4nO3de1yUdd7/8TcgB09AIDBogqcSNY94wjY3FWHNNk06amZltuuiW1HpWpaHNu3WNt01ym3XsDbZXHetbdWtlFL3DizFzDN3dNsP0xnI1QFPgML1+6OHczcrjIDDzHDxej4e86i5rs91XZ/vzJRvr6OfYRiGAAAATMrf2w0AAAA0JsIOAAAwNcIOAAAwNcIOAAAwNcIOAAAwNcIOAAAwNcIOAAAwNcIOAAAwNcIOAAAwNcIOAAAwNcIOAJ+3b98+3XHHHYqPj1dISIg6dOig0aNHa8WKFdq9e7f8/Pw0d+7cWpf/6quv5Ofnp4yMDEnS/Pnz5efnJ39/fx09evSy+rKyMrVs2VJ+fn6aMWNGo40LgGcQdgD4tNzcXA0cOFBffvmlpk2bpldeeUUPP/yw/P399dvf/lYDBgxQQkKC/vznP9e6juzsbEnSfffd5zQ9ODi4xuXWr1/v3kEA8KoW3m4AAFx54YUXFBYWpp07dyo8PNxpXklJiSRp0qRJevbZZ7Vjxw4NHTr0snX8+c9/VkJCggYMGOA0/ZZbbtGf//xnzZo1y2l6dna2xo4dq7/97W/uHQwAr2DPDgCf9vXXX6tXr16XBR1Jio6OlvR92JH+bw/OD+Xn56ugoMBR80MTJ07Unj17dPjwYcc0m82mjz/+WBMnTnTTCAB4G2EHgE+Lj49Xfn6+9u/fX2tN586dNWzYMP3lL39RVVWV07xLAaim8DJ8+HBde+21TiFp7dq1atOmjcaOHeumEQDwNsIOAJ/25JNP6ty5c+rXr5+GDRum2bNn66OPPtKFCxec6iZNmqTi4mLl5OQ4plVXV2vt2rVKSkpSly5dLlu3n5+f7rnnHqfzdtasWaMJEyYoODi48QYFwKMIOwB82ujRo5WXl6fbbrtNX375pZYsWaLU1FR16NBB77//vqPu7rvvVmBgoNNemm3btunYsWM1HsK6ZOLEiSosLNTOnTsd/+QQFmAuhB0APm/QoEFav369Tp06pc8//1xz5szR6dOndccdd+jgwYOSpMjISKWmpurdd99VeXm5pO8PYbVo0UJ33XVXrevu37+/EhISlJ2drTVr1shisWjkyJEeGRcAzyDsAGgygoKCNGjQIC1atEivvfaaLly4oHXr1jnm33fffSorK9OGDRtUWVmpv/3tb0pJSVFUVJTL9U6cOFFr165Vdna27r77bvn7879GwEz4LxpAkzRw4EBJktVqdUy77bbb1LZtW2VnZ+uf//ynTp065fIQ1iUTJ06U1WrV//zP/3AICzAh7rMDwKd98sknuvnmm+Xn5+c0fdOmTZKk7t27O6a1bNlSt99+u9auXatz586pdevWGjdu3BW30bVrVy1fvlznz5/X4MGD3TsAAF5H2AHg02bOnKlz587p9ttvV0JCgiorK5Wbm6u1a9eqU6dOevDBB53q77vvPr311lv68MMPNWnSJLVu3bpO23n00Ucbo30APoCwA8CnvfTSS1q3bp02bdqk119/XZWVlYqLi9MvfvELzZ0797KbDY4cOVKxsbGyWq11OoQFwPz8DMMwvN0EAABAY+EEZQAAYGqEHQAAYGqEHQAAYGqEHQAAYGqEHQAAYGqEHQAAYGrcZ0dSdXW1jh8/rrZt2152l1YAAOCbDMPQ6dOn1b59e5fPtCPsSDp+/Lg6duzo7TYAAEADHD16VNdee22t8wk7ktq2bSvp+w8rNDTUy90AAIC6KCsrU8eOHR1/jteGsCM5Dl2FhoYSdgAAaGKudAoKJygDAABTI+wAAABTI+wAAABT45wdAACaiKqqKl24cMHbbXhMYGCgAgICrno9hB0AAHycYRiy2Wyy2+3ebsXjwsPDZbFYruo+eIQdAAB83KWgEx0drVatWjWLG+AahqFz586ppKREkhQbG9vgdRF2AADwYVVVVY6gExkZ6e12PKply5aSpJKSEkVHRzf4kBYnKAMA4MMunaPTqlUrL3fiHZfGfTXnKhF2AABoAprDoauauGPchB0AAGBqhB0AAGBqhB0AAGBqhB0AAGBqhB0AAODkrbfeUmRkpCoqKpymjx8/XpMnT/ZSVw1H2AEAAE7uvPNOVVVV6f3333dMKykp0caNG/XQQw95sbOG4aaCABwSBwyQ1Wp1WRMbG6v83bs91BEAb2jZsqUmTpyorKws3XnnnZKkt99+W3Fxcbr55pu921wDEHYAOFitVhV+sctlTbf+Az3UDQBvmjZtmgYNGqRjx46pQ4cOWr16tR544IEmeb8fwg4AALhM//791bdvX7311ltKSUnRgQMHtHHjRm+31SCEHQAAUKOHH35Yy5cv17Fjx5ScnKyOHTt6u6UG4QRlAABQo4kTJ+rbb7/VH/7whyZ5YvIlhB0AAFCjsLAwpaWlqU2bNho/fry322kwwg4AAKjVsWPHNGnSJAUHB3u7lQbjnB0AAHCZU6dOaevWrdq6dateffVVb7dzVQg7AADgMv3799epU6f0X//1X+revbu327kqhB0AAHCZb775xtstuA3n7AAAAFMj7AAAAFMj7AAAAFMj7AAAAFMj7AAAAFMj7AAAAFPj0nMAAJqooqIinThxwiPbateuneLi4jyyLXcj7AAA0AQVFRWpR48eOnfunEe216pVKx06dOiqAs/69eu1cuVK5efn6+TJk/riiy/Ur18/9zVZC8IOAABN0IkTJ3Tu3DmteuV36n7ddY26rYKvvtLUGb/UiRMnrirsnD17Vj/60Y901113adq0aW7s0DWvhp358+drwYIFTtO6d++uw4cPS5LKy8v1xBNP6J133lFFRYVSU1P16quvKiYmxlFfVFSk6dOn65NPPlGbNm00ZcoULV68WC1akOMAAObX/brr1L9Pb2+3USeTJ0+W5Pm7M3s9EfTq1UtbtmxxvP9hSHn88ce1ceNGrVu3TmFhYZoxY4YmTJigTz/9VJJUVVWlsWPHymKxKDc3V1arVffff78CAwO1aNEij48FAAD4Hq+HnRYtWshisVw2vbS0VKtWrVJ2drZGjhwpScrKylKPHj20Y8cODR06VB999JEOHjyoLVu2KCYmRv369dPzzz+v2bNna/78+QoKCvL0cAAAgI/x+qXnX331ldq3b68uXbpo0qRJKioqkiTl5+frwoULSk5OdtQmJCQoLi5OeXl5kqS8vDz17t3b6bBWamqqysrKdODAgVq3WVFRobKyMqcXAABwnzVr1qhNmzaO17/+9S+v9eLVPTtDhgzR6tWr1b17d1mtVi1YsEA33XST9u/fL5vNpqCgIIWHhzstExMTI5vNJkmy2WxOQefS/EvzarN48eLLzhUCAADuc9ttt2nIkCGO9x06dPBaL14NO2PGjHH8e58+fTRkyBDFx8frL3/5i1q2bNlo250zZ44yMjIc78vKytSxY8dG2x4AAM1N27Zt1bZt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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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iwrWcnd3x65duzBr1iyo1Wq0bt0aiYmJWLp0qTVDIyIyY80qzdau6ExASYket7Q3G2xXrNfZvxgSPavCzurVqzF9+nTIZLK79snlcsycORPvvPOOxWFHEIQG23h5eSE1NRWpqan1tgkNDcWePXssOiYR0b3YukozABgqK+1QkbiUld/5+W75fKPFfdwkElRU8u4ysp1VYefUqVN466236t0/fPhwvP32240uiojIWWxZpTnrxClMnvkcjEajnatr/ioq7/x8l7z4MiIjIhtsn51zFfNXLIPRyJWjyXZWhZ2CgoI6bzk3fVirVvjll18aXRQR0b1Y80ypktIym45hzSrN13+9zZssp1S0Q6B/QIPtiouLHFANiZ1VYadDhw44e/YswsPD69x/+vRpBAYGNklhROQaLA0WjXlQZbFeZ9H8jYJf7iwrYc0zpYr1d+riM6WIWi6rws5jjz2G119/HSNGjICXl5fZvoqKCixatAh/+MMfmrRAInIOax9WacuDKov1erhJJPh04zqrauverSciw7ta1Pby1csAtqCqqsqqYxCReFgVdhYsWIDt27eja9euSE5ORmTkneutFy9eRGpqKqqrqzF//ny7FEpEjmXtwyprH1RZpNNZfImp8JdbqBEELHt1PrqENfyQzh9O/Ii316XC3c0dPt4+Fh1DymdKEbV4VoWdgIAAHD58GLNmzUJKSorpbiqJRIK4uDikpqbe9ZwqImreah9W2RA3d3cAlj/OAPjf2SCFXG7R/I12CrlFn0tE9FtWLypYe5t3UVERrly5AkEQEBERgXbt2tmjPiJqJqx9nAHARxoQkWPYtIIyALRr1w73339/U9ZCRCJQ+zgDS9sSEdmbTc/GIiIiImoubD6zQ0TNjzXr05SVccVaIhIHhh2iFsDa28iB/00evl1dY7e6iIgcgWGHqAWw9jZygJOHiUg8GHaIWhBLbyMHxDd5mE/ZJmq5GHaISNRseco2wMdLEIkJww4RiZq1T9k+de4slr+3yqqVoBvzXDAisj+GHSJqESx9ynZO7k8AbFsJmmeDiFwTww4R0W/YshJ07XPBjFW37VgZEdmKYYeIqA5WrQTNh40SuTSuoExERESixrBDREREosbLWEQN0Gq10Ov1FrXNy8uzczVERGQthh2ie9BqtQgPD0dRUZFV/QyVlXaqiIiIrMWwQ3QPer0e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4sXo06cP/vGPfyAsLAxeXl4AAJ1Oh2effRbt27eHTCbD0KFDcerUKcf9IIioyfDMDhG5lHfffRddunTBRx99hOPHj6OqqgqdO3fGypUr8cQTT6CkpATffvstaq/Ar127FvPmzcOKFSsQHx+P4uJifP/99wCAmpoaU9D55ptvcPv2bSQlJeHpp59GRkaG6ZhXrlzB559/ju3bt8Pd3R0AMH78eHh7e+PLL7+EXC7Hhx9+iGHDhuH//u//oFQqHf5zIaJGcOZTSImI6rJq1SohNDRUEARByMrKEgAIP/30U51tg4KChPnz59e5b9++fYK7u7uQm5tr2nbu3DkBgHDs2DFBEARh0aJFgoeHh1BYWGhq8+233woymUyorKw0+7wuXboIH374YWOGRkROwMtYROTSevfujWHDhiE6Ohrjx4/Hxx9/jKKiIgBAYWEhbty4gWHDhtXZ98KFCwgODkZwcLBpW1RUFBQKBS5cuGDaFhoaivbt25venzp1CqWlpfD19UWbNm1Mr5ycHGRnZ9tppERkL7yMRUQuzd3dHfv378fhw4exb98+rFmzBvPnz8fRo0fh5+fXJMdo3bq12fvS0lIEBgaaXeqqpVAomuSYROQ4PLNDRC5PIpHgoYcewpIlS3DixAl4enpix44daNu2LTp16oT09PQ6+3Xv3h3Xrl3DtWvXTNvOnz8PnU6HqKioeo933333QaPRoFWrVggPDzd7NVXAIiLH4ZkdInJpR48eRXp6OoYPHw5/f38cPXoUv/zyC7p37w7gzt1Uzz33HPz9/REfH4+SkhJ8//33mD17NmJjYxEdHY2EhASsXr0at2/fxp///Gc88sgj6N+/f73HjI2NhVqtxpgxY7By5Up07doVN27cwO7du/HEE0/csy8RuR6GHSJyaTKZDIcOHcLq1auh1+sRGhqKv//974iPjwcAJCYmorKyEqtWrcKLL74IPz8/PPnkkwDunBH64osvMHv2bAwaNAhubm4YMWIE1qxZc89jSiQS7NmzB/Pnz8czzzyDX375BSqVCoMGDUJAQIDdx0xETYsrKBMREZGocc4OERERiRrDDhEREYkaww4RERGJGsMOERERiRrDDhEREYkaww4RERGJGsMOERERiRrDDhEREYkaww4RERGJGsMOERERiRrDDhEREYkaww4RERGJ2v8HTb4KY3qfgSMAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import seaborn as sns\n", + "import pandas as pd\n", + "import warnings\n", + "import matplotlib.pyplot as plt\n", + "warnings.filterwarnings(\"ignore\", \"is_categorical_dtype\")\n", + "warnings.filterwarnings(\"ignore\", \"use_inf_as_na\")\n", + "\n", + "df = pd.DataFrame({'score': score, 'fscore': fscore, 'y': y})\n", + "\n", + "sns.histplot(df, x=\"score\", hue=\"y\").set_title(\"SVM\")\n", + "plt.show()\n", + "sns.histplot(df, x=\"fscore\", hue=\"y\").set_title(\"FairSVM\")\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/doc/source/examples/QR.ipynb b/doc/source/examples/QR.ipynb new file mode 100644 index 0000000..75640d9 --- /dev/null +++ b/doc/source/examples/QR.ipynb @@ -0,0 +1,123 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e3a11293-4739-476e-a513-48a256d425a2", + "metadata": {}, + "source": [ + "## **Ridge Quantile Regression**\n", + "\n", + "[![Slides](https://img.shields.io/badge/🦌-ReHLine-blueviolet)](https://rehline-python.readthedocs.io/en/latest/)\n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1LwatjwjnMSB97eLVyuOiUY3sl3A3Ie__?usp=sharing)\n", + "\n", + "The regularized quantile regression solves the following optimization problem:\n", + "\n", + "$$\n", + "min_{\\beta \\in \\mathbb{R}^{d}} \\ C \\sum_{i=1}^n \\rho_\\kappa ( y_i - x^\\intercal_i \\beta ) + \\frac{1}{2} \\| \\beta \\|^2,\n", + "$$\n", + "\n", + "where $\\rho_\\kappa(u) = u\\cdot(\\kappa - \\mathbf{1}(u < 0))$ is the check loss,\n", + "$x_i \\in \\mathbb{R}^d$ is a feature vector, $y_i \\in \\mathbb{R}$ is the response variable.\n", + "\n", + "> **Note.** Since the check loss is a plq function, thus we can solve it by `rehline.plqERM_Ridge`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "b2dd4ce5-bc27-41a4-89ab-7920d393f377", + "metadata": {}, + "outputs": [], + "source": [ + "## simulate data\n", + "from sklearn.datasets import make_regression\n", + "from sklearn.preprocessing import StandardScaler\n", + "import numpy as np\n", + "\n", + "scaler = StandardScaler()\n", + "\n", + "n, d = 10000, 5\n", + "X, y = make_regression(n_samples=n, n_features=d, noise=1.0)\n", + "X = scaler.fit_transform(X)\n", + "## add intercept\n", + "X = np.hstack((X,np.ones((n,1))))\n", + "y = y/y.std()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "80129ee6-f886-4e27-a764-630f15826bca", + "metadata": {}, + "outputs": [], + "source": [ + "## solve QR with different `qt` via `plqERM_Ridge`\n", + "from rehline import plqERM_Ridge\n", + "\n", + "clf5 = plqERM_Ridge(loss={'name': 'QR', 'qt': 0.05}, C=10.0/n)\n", + "clf5.fit(X=X, y=y)\n", + "\n", + "clf95 = plqERM_Ridge(loss={'name': 'QR', 'qt': 0.95}, C=10.0/n)\n", + "clf95.fit(X=X, y=y)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "1d8b90e9-6af9-4856-9751-6fe6fbc7665c", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkIAAAGwCAYAAABFFQqPAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB0K0lEQVR4nO3dd3jT9drH8XeStulO94ICZYMgU5aoICiIoihyHCiiiIqiIrhwoCiKHhfHR484Dggq6vG4xYMDRY+KICBDluwyuuieSZvk+aNarU0ZJW2a5PO6rlza3/0bd1va3P1Og9PpdCIiIiLih4yeTkBERETEU1QIiYiIiN9SISQiIiJ+S4WQiIiI+C0VQiIiIuK3VAiJiIiI31IhJCIiIn4rwNMJNHcOh4NDhw4RERGBwWDwdDoiIiJyDJxOJ8XFxaSkpGA01t/uo0LoKA4dOkRqaqqn0xAREZEG2L9/Py1btqw3rkLoKCIiIoDqL2RkZKSHsxEREZFjUVRURGpqas37eH1UCB3F791hkZGRKoRERES8zNGGtWiwtIiIiPgtFUIiIiLit1QIiYiIiN/SGCE3sdvtVFZWejoNnxUYGIjJZPJ0GiIi4mNUCJ0gp9NJZmYmBQUFnk7F50VFRZGUlKT1nERExG1UCJ2g34ughIQEQkND9SbdCJxOJ2VlZWRnZwOQnJzs4YxERMRXqBA6AXa7vaYIio2N9XQ6Pi0kJASA7OxsEhIS1E0mIiJuocHSJ+D3MUGhoaEezsQ//P511lgsERFxFxVCbqDusKahr7OIiLibusZERETEIworCrE5bIQGhhIWGOaRHFQIiYiISJPKLc9jffZ6/vXLK2SXZdMtthvX97iBtpY0zAHmJs1FXWM+bu/evRgMBtavX3/M10ycOJExY8Yc8ZwhQ4Ywbdq0E8pNRET8T2FFMQt/Wci0Fbey6fAmssqyWL5/OZctvZSfszc0eT4qhHxcamoqGRkZdOvWzdOpiIiIcLj8MIu3LKpz3O60M2fVQ2SWZDdpPiqEfJjNZsNkMpGUlERAgHpBRUTE8345vAUnTpexfUX7KLAWNWk+KoSaiZdeeomUlBQcDket4xdccAHXXHMNu3bt4oILLiAxMZHw8HBOOeUUvvzyy1rntmnThocffpgJEyYQGRnJddddV6drzG63M2nSJNLS0ggJCaFTp0784x//cJnT7NmziY+PJzIykhtuuAGbzVZv/larldtvv50WLVoQFhZG//79WbFixQl9TURExPeYDIFHjBubuDRRIdRMjBs3jtzcXL7++uuaY3l5eSxbtozx48dTUlLCqFGjWL58OT///DMjR45k9OjRpKen17rPk08+SY8ePfj555+5//776zzH4XDQsmVL3nnnHbZs2cKsWbO45557+Pe//13rvOXLl7N161ZWrFjBm2++yXvvvcfs2bPrzX/q1KmsXLmSt956i40bNzJu3DhGjhzJjh07TvArIyIivqRLbGcCDK57KU6KPYmwgMgmzUeFUDMRHR3NOeecw5IlS2qO/ec//yEuLo6hQ4fSo0cPrr/+erp160aHDh14+OGHadeuHR999FGt+5x55pnMmDGDdu3a0a5duzrPCQwMZPbs2fTt25e0tDTGjx/P1VdfXacQCgoKYsGCBZx00kmce+65PPTQQzz77LN1WqwA0tPTWbhwIe+88w6nnXYa7dq14/bbb2fw4MEsXLjQTV8hERHxBbEB4cw65a46x8MDw5nd/z6SI5p2pwYNHGlGxo8fz+TJk/nnP/+J2WzmjTfe4NJLL8VoNFJSUsKDDz7I0qVLycjIoKqqivLy8jotQn379j3qc55//nkWLFhAeno65eXl2Gw2evbsWeucHj161Foxe+DAgZSUlLB//35at25d69xNmzZht9vp2LFjreNWq1Vbj4iISC1RlYWctWslXU97hrcOfsXB8sP0i+7EyPg+pKx4EuOoJyEsrsnyUSHUjIwePRqn08nSpUs55ZRT+N///sczzzwDwO23384XX3zBk08+Sfv27QkJCeHiiy+uM24nLOzIC1K99dZb3H777Tz11FMMHDiQiIgInnjiCVatWtXgvEtKSjCZTKxdu7bOHmDh4eENvq+IiPigjI2Er3mVThve5t4OI6gMi8W8+WuMex+qjg+9R4WQvwoODuaiiy7ijTfeYOfOnXTq1InevXsD8P333zNx4kQuvPBCoLr42Lt373E/4/vvv2fQoEHceOONNcd27dpV57wNGzZQXl5es9npjz/+SHh4OKmpqXXO7dWrF3a7nezsbE477bTjzklERPyI87chFpXlBGz5oG4h4nQ9o6yxaIxQMzN+/HiWLl3KggULGD9+fM3xDh068N5777F+/Xo2bNjA5Zdf7nK8ztF06NCBNWvW8Nln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y2iMP72EsaKAqs4XQLszCYhp5en0RETED6kQkiZlCo+D8Dho2QcclQQEhXk6JRER8WMqhMQzAoKAIE9nISIifk5jhERERMRvqRASERERv6VCSERERPyWCiERERHxWyqERERExG+pEBIRERG/pUJIRERE/JYKIREREfFbKoRERETEb6kQEhEREb+lLTaOwul0AlBUVOThTERERORY/f6+/fv7eH1UCB1FcXExAKmpqR7ORERERI5XcXExFoul3rjBebRSyc85HA4OHTpEREQEBoPB0+kcVVFREampqezfv5/IyEhPpyPHSN8376PvmXfS9807NeT75nQ6KS4uJiUlBaOx/pFAahE6CqPRSMuWLT2dxnGLjIzUD7kX0vfN++h75p30ffNOx/t9O1JL0O80WFpERET8lgohERER8VsqhHyM2WzmgQcewGw2ezoVOQ76vnkffc+8k75v3qkxv28aLC0iIiJ+Sy1CIiIi4rdUCImIiIjfUiEkIiIifkuFkIiIiPgtFUI+au/evUyaNIm0tDRCQkJo164dDzzwADabzdOpyVE88sgjDBo0iNDQUKKiojydjtTj+eefp02bNgQHB9O/f39Wr17t6ZTkCL799ltGjx5NSkoKBoOBDz74wNMpyVHMnTuXU045hYiICBISEhgzZgzbt293+3NUCPmobdu24XA4ePHFF9m8eTPPPPMM8+fP55577vF0anIUNpuNcePGMWXKFE+nIvV4++23mT59Og888ADr1q2jR48ejBgxguzsbE+nJvUoLS2lR48ePP/8855ORY7RN998w0033cSPP/7IF198QWVlJWeffTalpaVufY6mz/uRJ554ghdeeIHdu3d7OhU5Bq+++irTpk2joKDA06nIX/Tv359TTjmF5557DqjekzA1NZWbb76Zu+++28PZydEYDAbef/99xowZ4+lU5Djk5OSQkJDAN998w+mnn+62+6pFyI8UFhYSExPj6TREvJrNZmPt2rUMHz685pjRaGT48OGsXLnSg5mJ+LbCwkIAt7+PqRDyEzt37uT//u//uP766z2diohXO3z4MHa7ncTExFrHExMTyczM9FBWIr7N4XAwbdo0Tj31VLp16+bWe6sQ8jJ33303BoPhiK9t27bVuubgwYOMHDmScePGMXnyZA9l7t8a8n0TEZFqN910E7/88gtvvfWW2+8d4PY7SqOaMWMGEydOPOI5bdu2rfn/Q4cOMXToUAYNGsRLL73UyNlJfY73+ybNV1xcHCaTiaysrFrHs7KySEpK8lBWIr5r6tSpfPLJJ3z77be0bNnS7fdXIeRl4uPjiY+PP6ZzDx48yNChQ+nTpw8LFy7EaFQDoKccz/dNmregoCD69OnD8uXLawbbOhwOli9fztSpUz2bnIgPcTqd3Hzzzbz//vusWLGCtLS0RnmOCiEfdfDgQYYMGULr1q158sknycnJqYnpr9bmLT09nby8PNLT07Hb7axfvx6A9u3bEx4e7tnkBIDp06dz1VVX0bdvX/r168e8efMoLS3l6quv9nRqUo+SkhJ27txZ8/GePXtYv349MTExtGrVyoOZSX1uuukmlixZwocffkhERETNGDyLxUJISIjbnqPp8z7q1VdfrfeXsr7lzdvEiRNZtGhRneNff/01Q4YMafqExKXnnnuOJ554gszMTHr27Mmzzz5L//79PZ2W1GPFihUMHTq0zvGrrrqKV199tekTkqMyGAwujy9cuPCoQw2O6zkqhERERMRfadCIiIiI+C0VQiIiIuK3VAiJiIiI31IhJCIiIn5LhZCIiIj4LRVCIiIi4rdUCImIiIjfUiEkIiIifkuFkIiIiPgtFUIi4vdWrFhB7969MZvNtG/fXlsuiPgRFUIi4tf27NnDueeey9ChQ1m/fj3Tpk3j2muv5bPPPvN0aiLSBLTXmIj4tJycHLp3784tt9zCPffcA8APP/zAkCFD+O9//8vnn3/O0qVL+eWXX2quufTSSykoKGDZsmWeSltEmohahETEp8XHx7NgwQIefPBB1qxZQ3FxMVdeeSVTp05l2LBhrFy5kuHDh9e6ZsSIEaxcudJDGYtIUwrwdAIiIo1t1KhRTJ48mfHjx9O3b1/CwsKYO3cuAJmZmSQmJtY6PzExkaKiIsrLywkJCfFEyiLSRNQiJCJ+4cknn6Sqqop33nmHN954A7PZ7OmURKQZUCEkIn5h165dHDp0CIfDwd69e2uOJyUlkZWVVevcrKwsIiMj1Rok4gfUNSYiPs9ms3HFFVdwySWX0KlTJ6699lo2bdpEQkICAwcO5NNPP611/hdffMHAgQM9lK2INCXNGhMRn3fHHXfwn//8hw0bNhAeHs4ZZ5yBxWLhk08+Yc+ePXTr1o2bbrqJa665hq+++opbbrmFpUuXMmLECE+nLiKNTIWQiPi0FStWcNZZZ/H1118zePBgAPbu3UuPHj147LHHmDJlCitWrOC2225jy5YttGzZkvvvv5+JEyd6NnERaRIqhERERMRvabC0iIiI+C0VQiIiIuK3VAiJiIiI31IhJCIiIn5LhZCIiIj4LRVCIiIi4rdUCImIiIjfUiEkIiIifkuFkIiIiPgtFUIiIiLit1QIiYiIiN/6fyUE37PI6UiWAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "## plot QR results\n", + "import pandas as pd\n", + "import seaborn as sns\n", + "import warnings\n", + "import matplotlib.pyplot as plt\n", + "warnings.filterwarnings(\"ignore\", \"is_categorical_dtype\")\n", + "\n", + "n_sample = 50\n", + "X_sample, y_sample = X[:n_sample], y[:n_sample]\n", + "q05_sample = clf5.decision_function(X_sample)\n", + "q95_sample = clf95.decision_function(X_sample)\n", + "\n", + "df = pd.DataFrame({'x0': X_sample[:,0], 'real_y': y_sample, 'q05': q05_sample, 'q95': q95_sample})\n", + "df = df.melt(id_vars='x0')\n", + "\n", + "sns.scatterplot(data=df, x='x0', y='value', hue='variable')\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/doc/source/examples/ReHLine_QR.ipynb b/doc/source/examples/ReHLine_QR.ipynb deleted file mode 100644 index 92dcd4f..0000000 --- a/doc/source/examples/ReHLine_QR.ipynb +++ /dev/null @@ -1,176 +0,0 @@ -{ - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "provenance": [] - }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" - }, - "language_info": { - "name": "python" - } - }, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "## **Example: Solving Quantile Regression via ReHLine**\n", - "\n", - "[![Slides](https://img.shields.io/badge/🦌-ReHLine-blueviolet)](https://rehline-python.readthedocs.io/en/latest/)\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1LwatjwjnMSB97eLVyuOiUY3sl3A3Ie__?usp=sharing)" - ], - "metadata": { - "id": "l-wsw7CJor38" - } - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "adidty-mclLB", - "outputId": "91d57f24-91fe-4809-ec3d-bfbb5a9346d2" - }, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Collecting rehline\n", - " Downloading rehline-0.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (147 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m147.1/147.1 kB\u001b[0m \u001b[31m2.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hRequirement already satisfied: requests>=2.27.0 in /usr/local/lib/python3.10/dist-packages (from rehline) (2.31.0)\n", - "Collecting pybind11>=2.11.1 (from rehline)\n", - " Downloading pybind11-2.13.1-py3-none-any.whl (238 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m238.8/238.8 kB\u001b[0m \u001b[31m13.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hRequirement already satisfied: numpy>=1.23.5 in /usr/local/lib/python3.10/dist-packages (from rehline) (1.25.2)\n", - "Requirement already satisfied: scipy>=1.11.4 in /usr/local/lib/python3.10/dist-packages (from rehline) (1.11.4)\n", - "Requirement already satisfied: scikit-learn>=1.2.2 in /usr/local/lib/python3.10/dist-packages (from rehline) (1.2.2)\n", - "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests>=2.27.0->rehline) (3.3.2)\n", - "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests>=2.27.0->rehline) (3.7)\n", - "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests>=2.27.0->rehline) (2.0.7)\n", - "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests>=2.27.0->rehline) (2024.6.2)\n", - "Requirement already satisfied: joblib>=1.1.1 in /usr/local/lib/python3.10/dist-packages (from scikit-learn>=1.2.2->rehline) (1.4.2)\n", - "Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn>=1.2.2->rehline) (3.5.0)\n", - "Installing collected packages: pybind11, rehline\n", - "Successfully installed pybind11-2.13.1 rehline-0.0.3\n" - ] - } - ], - "source": [ - "!pip install rehline" - ] - }, - { - "cell_type": "code", - "source": [ - "## simulate data\n", - "from sklearn.datasets import make_regression\n", - "from sklearn.preprocessing import StandardScaler\n", - "import numpy as np\n", - "\n", - "scaler = StandardScaler()\n", - "\n", - "n, d = 10000, 5\n", - "X, y = make_regression(n_samples=n, n_features=d, noise=1.0)\n", - "X = scaler.fit_transform(X)\n", - "y = y/y.std()" - ], - "metadata": { - "id": "WYZq1rWWctNl" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "code", - "source": [ - "from rehline import ReHLine\n", - "\n", - "qt = [0.25, 0.5, 0.75]\n", - "\n", - "clf = ReHLine(C=1.0/n)\n", - "X_fake = clf.make_ReLHLoss(X=X, y=y, loss={'name':'QR', 'qt':qt})\n", - "clf.fit(X_fake)\n", - "\n", - "## the first d params are the linear coefficients\n", - "## and the last 3 params are the quantile-specific intercept\n", - "clf.coef_" - ], - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "tWpmFfCxdM7K", - "outputId": "3fb01216-8ab6-4411-c40e-d5d1e133cdea" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - "array([ 0.59688881, 0.39743301, 0.17675816, 0.45894896, 0.47806579,\n", - " -0.0134518 , -0.00152496, 0.01026677])" - ] - }, - "metadata": {}, - "execution_count": 47 - } - ] - }, - { - "cell_type": "code", - "source": [ - "score = [X.dot(clf.coef_[:d]) + clf.coef_[d+l] for l in range(len(qt))]\n", - "\n", - "## report Qs for some samples\n", - "X_sample, y_sample = X[:5], y[:5]\n", - "q_sample = [X_sample.dot(clf.coef_[:d]) + clf.coef_[d+l] for l in range(len(qt))]\n", - "\n", - "q_sample = np.array(q_sample).T\n", - "\n", - "print('fitted quantiles: %s \\n' %qt)\n", - "print(q_sample)\n", - "print('\\n Real Y: \\n')\n", - "print(y_sample[:,np.newaxis])" - ], - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "hFqz2fI-fAr9", - "outputId": "983ee042-1729-4de2-f9d7-74824d7894fd" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "fitted quantiles: [0.25, 0.5, 0.75] \n", - "\n", - "[[-0.20078214 -0.18885531 -0.17706358]\n", - " [-2.48210771 -2.47018088 -2.45838915]\n", - " [-1.31540194 -1.30347511 -1.29168338]\n", - " [-2.70665164 -2.69472481 -2.68293308]\n", - " [ 0.37165816 0.38358499 0.39537672]]\n", - "\n", - " Real Y: \n", - "\n", - "[[-0.19786913]\n", - " [-2.51954039]\n", - " [-1.33318935]\n", - " [-2.74956754]\n", - " [ 0.39217369]]\n" - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/doc/source/examples/ReHLine_SVM_FairSVM.ipynb b/doc/source/examples/ReHLine_SVM_FairSVM.ipynb deleted file mode 100644 index ea0b51c..0000000 --- a/doc/source/examples/ReHLine_SVM_FairSVM.ipynb +++ /dev/null @@ -1,187 +0,0 @@ -{ - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "provenance": [] - }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" - }, - "language_info": { - "name": "python" - } - }, - "cells": [ - { - "cell_type": "markdown", - "source": [ - "# **Example: solving SVM and FairSVM via ReHLine**\n", - "\n", - "[![Slides](https://img.shields.io/badge/🦌-ReHLine-blueviolet)](https://rehline-python.readthedocs.io/en/latest/)\n", - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1f_7t1t6FNxAooQOmpyhHCOVq0IKgMxe-?usp=sharing)" - ], - "metadata": { - "id": "hK6foc_zTi1U" - } - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "gYwC0MP9Cpze", - "outputId": "0eb3aed9-82dc-4dff-be33-232ee9c72deb" - }, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Collecting rehline\n", - " Downloading rehline-0.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (147 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m147.1/147.1 kB\u001b[0m \u001b[31m2.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hRequirement already satisfied: requests>=2.27.0 in /usr/local/lib/python3.10/dist-packages (from rehline) (2.31.0)\n", - "Collecting pybind11>=2.11.1 (from rehline)\n", - " Downloading pybind11-2.13.1-py3-none-any.whl (238 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m238.8/238.8 kB\u001b[0m \u001b[31m7.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hRequirement already satisfied: numpy>=1.23.5 in /usr/local/lib/python3.10/dist-packages (from rehline) (1.25.2)\n", - "Requirement already satisfied: scipy>=1.11.4 in /usr/local/lib/python3.10/dist-packages (from rehline) (1.11.4)\n", - "Requirement already satisfied: scikit-learn>=1.2.2 in /usr/local/lib/python3.10/dist-packages (from rehline) (1.2.2)\n", - "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests>=2.27.0->rehline) (3.3.2)\n", - "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests>=2.27.0->rehline) (3.7)\n", - "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests>=2.27.0->rehline) (2.0.7)\n", - "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests>=2.27.0->rehline) (2024.6.2)\n", - "Requirement already satisfied: joblib>=1.1.1 in /usr/local/lib/python3.10/dist-packages (from scikit-learn>=1.2.2->rehline) (1.4.2)\n", - "Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn>=1.2.2->rehline) (3.5.0)\n", - "Installing collected packages: pybind11, rehline\n", - "Successfully installed pybind11-2.13.1 rehline-0.0.3\n" - ] - } - ], - "source": [ - "## install package\n", - "!pip install rehline" - ] - }, - { - "cell_type": "code", - "source": [ - "## simulate data\n", - "import rehline\n", - "from rehline import ReHLine\n", - "import numpy as np\n", - "\n", - "n, d = 100000, 5\n", - "\n", - "X, y, X_sen = rehline.make_fair_classification(n_samples=n,\n", - " n_features=d,\n", - " ind_sensitive=0)" - ], - "metadata": { - "id": "BELfW8bYE_bp" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "code", - "source": [ - "## fitting SVMs\n", - "clf = ReHLine(loss={'name': 'svm'}, C=1./n)\n", - "\n", - "clf.make_ReLHLoss(X=X, y=y, loss={'name': 'svm'})\n", - "\n", - "clf.fit(X=X)" - ], - "metadata": { - "id": "gaMe2UYSFSFO" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "code", - "source": [ - "## report performance and sensitive correlation\n", - "score = X @ clf.coef_\n", - "\n", - "print('Classification Acc: %.3f' %(len(score[score*y > 0]) / len(score)))\n", - "print('Sensitive Correlation: %.3f' %( score.dot(X_sen) / n ))" - ], - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "8tjCbX4OGnKr", - "outputId": "d4cb9e3b-1b30-4583-9843-b137958e8980" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Classification Acc: 0.953\n", - "Sensitive Correlation: 0.898\n" - ] - } - ] - }, - { - "cell_type": "code", - "source": [ - "## fitting Fair-SVMs\n", - "\n", - "rho = 0.01\n", - "\n", - "clf_fair = ReHLine(loss={'name': 'svm'}, C=1./n)\n", - "\n", - "clf_fair.make_ReLHLoss(X=X, y=y, loss={'name': 'svm'})\n", - "A = np.repeat([X_sen @ X], repeats=[2], axis=0) / n\n", - "A[1] = -A[1]\n", - "b = np.array([rho, rho])\n", - "\n", - "clf_fair.A, clf_fair.b = A, b\n", - "clf_fair.fit(X=X)" - ], - "metadata": { - "id": "uSU-t7sEIVY_" - }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "code", - "source": [ - "## report performance and sensitive correlation\n", - "\n", - "score = X @ clf_fair.coef_\n", - "\n", - "print('Classification Acc: %.3f' %(len(score[score*y > 0]) / len(score)))\n", - "print('Sensitive Correlation: %.3f' %( score.dot(X_sen) / n ))" - ], - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "OXx4TwVYI6b6", - "outputId": "279e1c88-cae4-4cf5-f774-25ff9bc63b1e" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Classification Acc: 0.574\n", - "Sensitive Correlation: 0.010\n" - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/doc/source/examples/SVM.ipynb b/doc/source/examples/SVM.ipynb new file mode 100644 index 0000000..05e9fe0 --- /dev/null +++ b/doc/source/examples/SVM.ipynb @@ -0,0 +1,108 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "fbcb401d-6ca6-4933-abd5-f8f504282416", + "metadata": {}, + "source": [ + "# **SVM**\n", + "\n", + "[![Slides](https://img.shields.io/badge/🦌-ReHLine-blueviolet)](https://rehline-python.readthedocs.io/en/latest/)\n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1f_7t1t6FNxAooQOmpyhHCOVq0IKgMxe-?usp=sharing)\n", + "\n", + "SVMs solve the following optimization problem:\n", + "$$\n", + " \\min_{\\mathbf{\\beta} \\in \\mathbb{R}^d} \\ C \\sum_{i=1}^n ( 1 - y_i \\mathbf{\\beta}^\\intercal \\mathbf{x}_i )_+ + \\frac{1}{2} \\| \\mathbf{\\beta} \\|_2^2\n", + "$$\n", + "where $\\mathbf{x}_i \\in \\mathbb{R}^d$ is a feature vector, and $y_i \\in \\{-1, 1\\}$ is a binary label.\n", + "\n", + "> **Note.** Since the hinge loss is a plq function, thus we can solve it by `rehline.plqERM_Ridge`." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "2dd1c096-e0df-492f-be63-8ac272007237", + "metadata": {}, + "outputs": [], + "source": [ + "## simulate data\n", + "from sklearn.datasets import make_classification\n", + "from sklearn.preprocessing import StandardScaler\n", + "import numpy as np\n", + "\n", + "scaler = StandardScaler()\n", + "\n", + "n, d = 10000, 5\n", + "X, y = make_classification(n_samples=n, n_features=d)\n", + "X = scaler.fit_transform(X)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "aece9fbe-f9be-40ae-8179-b44849fb0fd3", + "metadata": {}, + "outputs": [], + "source": [ + "## solve SVM via `plqERM_Ridge`\n", + "from rehline import plqERM_Ridge\n", + "\n", + "clf = plqERM_Ridge(loss={'name': 'svm'}, C=1.0)\n", + "clf.fit(X=X, y=y)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "93719987-c6b3-4a9b-9b40-c35e5bf90ef0", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import seaborn as sns\n", + "import pandas as pd\n", + "import warnings\n", + "import matplotlib.pyplot as plt\n", + "warnings.filterwarnings(\"ignore\", \"is_categorical_dtype\")\n", + "warnings.filterwarnings(\"ignore\", \"use_inf_as_na\")\n", + "\n", + "score = clf.decision_function(X)\n", + "df = pd.DataFrame({'score': score, 'y': y})\n", + "sns.histplot(df, x=\"score\", hue=\"y\")\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/doc/source/tutorials.rst b/doc/source/tutorials.rst index 1822fee..fcbc63a 100644 --- a/doc/source/tutorials.rst +++ b/doc/source/tutorials.rst @@ -31,6 +31,45 @@ Loss # loss_kwargs: more keys and values for loss parameters loss = {'name': , <**loss_kwargs>} +.. list-table:: + + * - **SVM** + - | ``loss_name``: 'hinge' / 'svm' / 'SVM' + | + | *Example:* ``loss = {'name': 'SVM'}`` + + * - **Quantile Reg** + - | ``loss_name``: 'check' / 'quantile' / 'quantile regression' / 'QR' + | ``qt`` (*float*): qt + | + | *Example:* ``loss = {'name': 'QR', 'qt': 0.25}`` + + * - **Smooth SVM** + - | ``loss_name``: 'sSVM' / 'smooth SVM' / 'smooth hinge' + | + | *Example:* ``loss = {'name': 'sSVM'}`` + + * - **Huber** + - | ``loss_name``: 'huber' / 'Huber' + | + | *Example:* ``loss = {'name': 'huber'}`` + + * - **SVR** + - | ``loss_name``: 'SVR' / 'svr' + | ``epsilon`` (*float*): 0.1 + | + | *Example:* ``loss = {'name': 'svr', 'epsilon': 0.1}`` + +constraint +********** + +.. code:: python + + # list of + # name (str): name of the custom loss function + # loss_kwargs: more keys and values for loss parameters + constraint = [{'name': , <**loss_kwargs>}, ...] + .. list-table:: * - **SVM** diff --git a/rehline/__init__.py b/rehline/__init__.py index 5e95c1c..4ab3f85 100644 --- a/rehline/__init__.py +++ b/rehline/__init__.py @@ -1,9 +1,12 @@ # Import from internal C++ module -from ._base import ReHLine_solver, _BaseReHLine -from ._class import ReHLine +from ._base import ReHLine_solver, _BaseReHLine, _make_loss_rehline_param +from ._class import ReHLine, plqERM_Ridge from ._data import make_fair_classification from ._internal import rehline_internal, rehline_result __all__ = ("_BaseReHLine", "ReHLine", + "plqERM_Ridge", + "_make_loss_rehline_param", + "_make_constraint_rehline_param" "make_fair_classification") \ No newline at end of file diff --git a/rehline/_base.py b/rehline/_base.py index 9f7edd5..b4ed573 100644 --- a/rehline/_base.py +++ b/rehline/_base.py @@ -62,7 +62,6 @@ def __init__(self, C=1., self.A = A self.b = b self.L = U.shape[0] - self.n = U.shape[1] self.H = S.shape[0] self.K = A.shape[0] @@ -71,7 +70,6 @@ def auto_shape(self): Automatically generate the shape of the parameters of the ReHLine loss function. """ self.L = self.U.shape[0] - self.n = self.U.shape[1] self.H = self.S.shape[0] self.K = self.A.shape[0] @@ -89,9 +87,9 @@ def call_ReLHLoss(self, score): float ReHLine loss evaluation of the given score. """ - - relu_input = np.zeros((self.L, self.n)) - rehu_input = np.zeros((self.H, self.n)) + n = len(score) + relu_input = np.zeros((self.L, n)) + rehu_input = np.zeros((self.H, n)) if self.L > 0: relu_input = (self.U.T * score[:,np.newaxis]).T + self.V if self.H > 0: @@ -181,4 +179,284 @@ def ReHLine_solver(X, U, V, max_iter=1000, tol=1e-4, shrink=1, verbose=1, trace_freq=100): result = rehline_result() rehline_internal(result, X, A, b, U, V, S, T, Tau, max_iter, tol, shrink, verbose, trace_freq) - return result \ No newline at end of file + return result + + +def _make_loss_rehline_param(loss, X, y): + """The `_make_loss_rehline_param` function generates parameters for the ReHLine solver, based on the provided training data. + + The function supports various loss functions, including: + - 'hinge' + - 'svm' or 'SVM' + - 'check' or 'quantile' or 'quantile regression' or 'QR' + - 'sSVM' or 'smooth SVM' or 'smooth hinge' + - 'TV' + - 'huber' or 'Huber' + - 'SVR' or 'svr' + - Custom loss functions (manual setup required) + + Parameters + ---------- + loss : dict + A dictionary containing the loss function parameters. + + Keys: + - 'name' : str, the name of the loss function (e.g. 'hinge', 'svm', 'QR', etc.) + - 'loss_kwargs': more keys and values for loss parameters + + X : ndarray of shape (n_samples, n_features) + The generated samples. + + y : ndarray of shape (n_samples,) + The +/- labels for class membership of each sample. + """ + + n, d = X.shape + + ## initialization of ReHLine params + U=np.empty(shape=(0,0)) + V=np.empty(shape=(0,0)) + Tau=np.empty(shape=(0,0)) + S=np.empty(shape=(0,0)) + T=np.empty(shape=(0,0)) + + # _dummy_X = False + + if (loss['name'] == 'hinge') or (loss['name'] == 'svm')\ + or (loss['name'] == 'SVM'): + U = -y.reshape(1,-1) + V = (np.array(np.ones(n))).reshape(1,-1) + + elif (loss['name'] == 'check') \ + or (loss['name'] == 'quantile') \ + or (loss['name'] == 'quantile regression') \ + or (loss['name'] == 'QR'): + + qt = loss['qt'] + + U = np.ones((2, n)) + V = np.ones((2, n)) + + U[0] = - qt*U[0] + U[1] = (1-qt)*U[1] + V[0] = qt*V[0]*y + V[1] = -(1-qt)*V[1]*y + + # elif (loss['name'] == 'CQR') \ + + # n_qt = len(loss['qt']) + # U = np.ones((2, n*n_qt)) + # V = np.ones((2, n*n_qt)) + # X_fake = np.zeros((n*n_qt, d+n_qt)) + + # for l,qt_tmp in enumerate(loss['qt']): + # U[0,l*n:(l+1)*n] = - (qt_tmp*U[0,l*n:(l+1)*n]) + # U[1,l*n:(l+1)*n] = ((1.-qt_tmp)*U[1,l*n:(l+1)*n]) + + # V[0,l*n:(l+1)*n] = qt_tmp*V[0,l*n:(l+1)*n]*y + # V[1,l*n:(l+1)*n] = - (1.-qt_tmp)*V[1,l*n:(l+1)*n]*y + + # X_fake[l*n:(l+1)*n,:d] = X + # X_fake[l*n:(l+1)*n,d+l] = 1. + + elif (loss['name'] == 'sSVM') \ + or (loss['name'] == 'smooth SVM') \ + or (loss['name'] == 'smooth hinge'): + S = np.ones((1, n)) + T = np.ones((1, n)) + Tau = np.ones((1, n)) + S[0] = - y + + elif loss['name'] == 'TV': + U = np.ones((2, n)) + V = np.ones((2, n)) + U[1] = - U[1] + + V[0] = - X.dot(y) + V[1] = X.dot(y) + + elif (loss['name'] == 'huber') or (loss['name'] == 'Huber'): + S = np.ones((2, n)) + T = np.ones((2, n)) + Tau = loss['tau'] * np.ones((2, n)) + + S[0] = -S[0] + T[0] = y + T[1] = -y + + elif (loss['name'] in ['SVR', 'svr']): + U = np.ones((2, n)) + V = np.ones((2, n)) + U[1] = -U[1] + + V[0] = -(y + loss['epsilon']) + V[1] = (y - loss['epsilon']) + + else: + raise Exception("Sorry, ReHLine currently does not support this loss function, \ + but you can manually set ReHLine params to solve the problem via `ReHLine` class.") + + return U, V, Tau, S, T + +def _make_constraint_rehline_param(constraint, X, y=None): + """The `_make_constraint_rehline_param` function generates constraint parameters for the ReHLine solver. + + Parameters + ---------- + constraint : list of dict + A list of dictionaries, where each dictionary represents a constraint. + Each dictionary must contain a 'name' key, which specifies the type of constraint. + The following constraint types are supported: + * 'nonnegative' or '>=0': A non-negativity constraint. + * 'fair' or 'fairness': A fairness constraint. + * 'custom': A custom constraint, where the user must provide the constraint matrix 'A' and vector 'b'. + + X : array-like of shape (n_samples, n_features) + The design matrix. + + y : array-like of shape (n_samples,), default=None + The target variable. Not used in this function. + + Returns + ------- + A : array-like of shape (n_constraints, n_features) + The constraint matrix. + + b : array-like of shape (n_constraints,) + The constraint vector. + + Notes + ----- + This function iterates over the list of constraints and generates the constraint matrix 'A' and vector 'b' accordingly. + For 'nonnegative' and 'fair' constraints, the function generates the constraint parameters automatically. + For 'custom' constraints, the user must provide the constraint matrix 'A' and vector 'b' explicitly. + """ + + n, d = X.shape + + ## initialization + A=np.empty(shape=(0, 0)) + b=np.empty(shape=(0)) + + for constr_tmp in constraint: + if (constr_tmp['name'] == 'nonnegative') or (constr_tmp['name'] == '>=0'): + A_tmp = np.identity(d) + b_tmp = np.zeros(d) + elif (constr_tmp['name'] == 'fair') or (constr_tmp['name'] == 'fairness'): + X_sen = constr_tmp['X_sen'] + tol_sen = constr_tmp['tol_sen'] + tol_sen = np.array(tol_sen).reshape(-1) + + assert len(X_sen) == len(X), "X and X_sen must have the same length" + X_sen = X_sen.reshape(n,-1) + + assert X_sen.shape[1] == len(tol_sen), "dim of X_sen and len of tol_sen must be equal" + d_sen = X_sen.shape[1] + + A_tmp = np.repeat(X_sen.T @ X, repeats=[2], axis=0) / n + A_tmp[::2] = -A_tmp[::2] + b_tmp = np.repeat(tol_sen, repeats=[2], axis=0) + elif (constr_tmp['name'] == 'custom'): + A_tmp = constr_tmp['A'] + b_tmp = constr_tmp['b'] + else: + raise Exception("Sorry, ReHLine currently does not support this constraint, \ + but you can add it by manually setting A and b via {'name': 'custom', 'A': A, 'b': b}") + + A = np.vstack([A, A_tmp]) if A.size else A_tmp + b = np.hstack([b, b_tmp]) if b.size else b_tmp + + return A, b + +def _make_penalty_rehline_param(self, penalty=None, X=None): + """The `_make_penalty_rehline_param` function generates penalty parameters for the ReHLine solver. + """ + raise Exception("Sorry, `_make_penalty_rehline_param` feature is currently under development.") + + +# def append_l1(self, X, l1_pen=1.0): +# r""" +# This function appends the l1 penalty to the ReHLine problem. The formulation becomes: + +# .. math:: + +# \min_{\mathbf{\beta} \in \mathbb{R}^d} \sum_{i=1}^n \sum_{l=1}^L \text{ReLU}( u_{li} \mathbf{x}_i^\intercal \mathbf{\beta} + v_{li}) + \sum_{i=1}^n \sum_{h=1}^H {\text{ReHU}}_{\tau_{hi}}( s_{hi} \mathbf{x}_i^\intercal \mathbf{\beta} + t_{hi}) + \frac{1}{2} \| \mathbf{\beta} \|_2^2 + \lambda_1 \| \mathbf{\beta} \|_1, \\ \text{ s.t. } +# \mathbf{A} \mathbf{\beta} + \mathbf{b} \geq \mathbf{0}, + +# where :math:`\lambda_1` is associated with `l1_pen`. + +# Parameters +# ---------- + +# X : ndarray of shape (n_samples, n_features) +# The generated samples. + +# l1_pen : float, default=1.0 +# The l1 penalty level, which controls the complexity or sparsity of the resulting model. + +# Returns +# ------- + +# X_fake: ndarray of shape (n_samples+n_features, n_features) +# The manipulated data matrix. It has been padded with +# identity matrix, allowing the correctly structured data to be input +# into `self.fit` or other modelling processes. + +# Examples +# -------- + +# >>> import numpy as np +# >>> from rehline import ReHLine + +# >>> # simulate classification dataset +# >>> n, d, C, lam1 = 1000, 3, 0.5, 1.0 +# >>> np.random.seed(1024) +# >>> X = np.random.randn(1000, 3) +# >>> beta0 = np.random.randn(3) +# >>> y = np.sign(X.dot(beta0) + np.random.randn(n)) + +# >>> clf = ReHLine(loss={'name': 'svm'}, C=C) +# >>> clf.make_ReLHLoss(X=X, y=y, loss={'name': 'svm'}) +# >>> # save and fit with the manipulated data matrix +# >>> X_fake = clf.append_l1(X, l1_pen=lam1) +# >>> clf.fit(X=X_fake) +# >>> print('sol privided by rehline: %s' %clf.coef_) +# >>> sol privided by rehline: [ 7.17796629e-01 -1.87075728e-06 2.61965622e+00] #sparse sol +# >>> print(clf.decision_function([[.1,.2,.3]])) +# >>> [0.85767616] +# """ + +# n, d = X.shape +# l1_pen = l1_pen*np.ones(d) +# U_new = np.zeros((self.L+2, n+d)) +# V_new = np.zeros((self.L+2, n+d)) +# ## Block 1 +# if len(self.U): +# U_new[:self.L, :n] = self.U +# V_new[:self.L, :n] = self.V +# ## Block 2 +# U_new[-2,n:] = l1_pen +# U_new[-1,n:] = -l1_pen + +# if len(self.S): +# S_new = np.zeros((self.H, n+d)) +# T_new = np.zeros((self.H, n+d)) +# Tau_new = np.zeros((self.H, n+d)) + +# S_new[:,:n] = self.S +# T_new[:,:n] = self.T +# Tau_new[:,:n] = self.Tau + +# self.S = S_new +# self.T = T_new +# self.Tau = Tau_new + +# ## fake X +# X_fake = np.zeros((n+d, d)) +# X_fake[:n,:] = X +# X_fake[n:,:] = np.identity(d) + +# self.U = U_new +# self.V = V_new +# self.auto_shape() +# return X_fake \ No newline at end of file diff --git a/rehline/_class.py b/rehline/_class.py index d7d69e3..ed4c2a8 100644 --- a/rehline/_class.py +++ b/rehline/_class.py @@ -7,9 +7,11 @@ import numpy as np from sklearn.base import BaseEstimator -from sklearn.utils.validation import check_array, check_is_fitted, check_X_y +from sklearn.utils.validation import (_check_sample_weight, check_array, + check_is_fitted, check_X_y) -from ._base import ReHLine_solver, _BaseReHLine +from ._base import (ReHLine_solver, _BaseReHLine, + _make_constraint_rehline_param, _make_loss_rehline_param) class ReHLine(_BaseReHLine, BaseEstimator): @@ -126,8 +128,12 @@ def fit(self, X, sample_weight=None): An instance of the estimator. """ # X = check_array(X) + + if sample_weight is None: - sample_weight = np.ones(X.shape[0]) + sample_weight = self.C + else: + sample_weight = self.C*_check_sample_weight(sample_weight, X, dtype=X.dtype) if self.L > 0: U_weight = self.U * sample_weight @@ -180,18 +186,37 @@ def decision_function(self, X): X = check_array(X) return np.dot(X, self.coef_) - -class plqERM(_BaseReHLine, BaseEstimator): - r"""Empirical Risk Minimization (ERM) with a piecewise linear-quadratic (PLQ) objective. +class plqERM_Ridge(_BaseReHLine, BaseEstimator): + r"""Empirical Risk Minimization (ERM) with a piecewise linear-quadratic (PLQ) objective with a ridge penalty. .. math:: - \min_{\mathbf{\beta} \in \mathbb{R}^d} \sum_{i=1}^n \text{PLQ}(y_i, \mathbf{x}_i^T \mathbf{\beta}) + \text{pen}(\mathbf{\beta}) + \frac{1}{2} \| \mathbf{\beta} \|_2^2, \\ \text{ s.t. } + \min_{\mathbf{\beta} \in \mathbb{R}^d} \sum_{i=1}^n \text{PLQ}(y_i, \mathbf{x}_i^T \mathbf{\beta}) + \frac{1}{2} \| \mathbf{\beta} \|_2^2, \ \text{ s.t. } \ \mathbf{A} \mathbf{\beta} + \mathbf{b} \geq \mathbf{0}, - + + The function supports various loss functions, including: + - 'hinge', 'svm' or 'SVM' + - 'check' or 'quantile' or 'quantile regression' or 'QR' + - 'sSVM' or 'smooth SVM' or 'smooth hinge' + - 'TV' + - 'huber' or 'Huber' + - 'SVR' or 'svr' + + The following constraint types are supported: + * 'nonnegative' or '>=0': A non-negativity constraint. + * 'fair' or 'fairness': A fairness constraint. + * 'custom': A custom constraint, where the user must provide the constraint matrix 'A' and vector 'b'. + Parameters ---------- + loss : dict + A dictionary specifying the loss function parameters. + + constraint : list of dict + A list of dictionaries, where each dictionary represents a constraint. + Each dictionary must contain a 'name' key, which specifies the type of constraint. + C : float, default=1.0 Regularization parameter. The strength of the regularization is inversely proportional to C. Must be strictly positive. @@ -220,254 +245,54 @@ class plqERM(_BaseReHLine, BaseEstimator): Attributes ---------- + coef_ : array-like + The optimized model coefficients. - coef_ : array of shape (n_features,) - Weights assigned to the features (coefficients in the primal - problem). + n_iter_ : int + The number of iterations performed by the ReHLine solver. - n_iter_: int - Maximum number of iterations run across all classes. + opt_result_ : object + The optimization result object. - """ + dual_obj_ : array-like + The dual objective function values. + + primal_obj_ : array-like + The primal objective function values. + Methods + ------- + fit(X, y, sample_weight=None) + Fit the model based on the given training data. - def __init__(self, loss, - constraint = None, - penalty = None, + decision_function(X) + The decision function evaluated on the given dataset. + Notes + ----- + The `plqERM_Ridge` class is a subclass of `_BaseReHLine` and `BaseEstimator`, which suggests that it is part of a larger framework for implementing ReHLine algorithms. + + """ + + def __init__(self, loss, + constraint=[], C=1., + U=np.empty(shape=(0,0)), V=np.empty(shape=(0,0)), + Tau=np.empty(shape=(0,0)), + S=np.empty(shape=(0,0)), T=np.empty(shape=(0,0)), + A=np.empty(shape=(0,0)), b=np.empty(shape=(0)), max_iter=1000, tol=1e-4, shrink=1, verbose=0, trace_freq=100): + _BaseReHLine.__init__(self, C, U, V, Tau, S, T, A, b) self.loss = loss self.constraint = constraint - self.penalty = penalty self.max_iter = max_iter self.tol = tol self.shrink = shrink self.verbose = verbose self.trace_freq = trace_freq + self.dummy_n = 0 - def _make_loss_rehline_param(self, X, y): - """The `_make_loss_rehline_param` function generates parameters for the ReHLine solver, based on the provided training data. - - The function supports plq loss functions - like 'hinge', 'svm', 'SVM', 'check', 'quantile', 'quantile regression', - 'QR', 'sSVM', 'smooth SVM', 'smooth hinge', 'TV', 'huber', and 'custom'. - - Parameters - ---------- - - X : ndarray of shape (n_samples, n_features) - The generated samples. - - y : ndarray of shape (n_samples,) - The +/- labels for class membership of each sample. - """ - - n, d = X.shape - - if (self.loss['name'] == 'hinge') or (self.loss['name'] == 'svm')\ - or (self.loss['name'] == 'SVM'): - self.U = -(self.C*y).reshape(1,-1) - self.V = (self.C*np.array(np.ones(n))).reshape(1,-1) - return X - - elif (self.loss['name'] == 'check') \ - or (self.loss['name'] == 'quantile') \ - or (self.loss['name'] == 'quantile regression') \ - or (self.loss['name'] == 'QR'): - - n_qt = len(loss['qt']) - self.U = np.ones((2, n*n_qt)) - self.V = np.ones((2, n*n_qt)) - X_fake = np.zeros((n*n_qt, d+n_qt)) - - for l,qt_tmp in enumerate(loss['qt']): - self.U[0,l*n:(l+1)*n] = - (self.C*qt_tmp*self.U[0,l*n:(l+1)*n]) - self.U[1,l*n:(l+1)*n] = (self.C*(1.-qt_tmp)*self.U[1,l*n:(l+1)*n]) - - self.V[0,l*n:(l+1)*n] = self.C*qt_tmp*self.V[0,l*n:(l+1)*n]*y - self.V[1,l*n:(l+1)*n] = - self.C*(1.-qt_tmp)*self.V[1,l*n:(l+1)*n]*y - - X_fake[l*n:(l+1)*n,:d] = X - X_fake[l*n:(l+1)*n,d+l] = 1. - - self.auto_shape() - return X_fake - - elif (self.loss['name'] == 'sSVM') \ - or (self.loss['name'] == 'smooth SVM') \ - or (self.loss['name'] == 'smooth hinge'): - self.S = np.ones((1, n)) - self.T = np.ones((1, n)) - self.Tau = np.ones((1, n)) - - self.S[0] = - np.sqrt(self.C)*y - self.T[0] = np.sqrt(self.C) - self.Tau[0] = np.sqrt(self.C) - return X - - elif self.loss['name'] == 'TV': - self.U = np.ones((2, n))*self.C - self.V = np.ones((2, n))*self.C - self.U[1] = -self.U[1] - - self.V[0] = - X.dot(y)*self.C - self.V[1] = X.dot(y)*self.C - return X - - elif (self.loss['name'] == 'huber') or (self.loss['name'] == 'Huber'): - self.S = np.ones((2, n)) - self.T = np.ones((2, n)) - self.Tau = np.sqrt(self.C) * loss['tau'] * np.ones((2, n)) - - self.S[0] = - np.sqrt(self.C) - self.S[1] = np.sqrt(self.C) - self.T[0] = np.sqrt(self.C)*y - self.T[1] = -np.sqrt(self.C)*y - return X - elif (self.loss['name'] in ['SVR', 'svr']): - self.U = np.ones((2, n))*self.C - self.V = np.ones((2, n)) - self.U[1] = -self.U[1] - - self.V[0] = -self.C*(y + self.loss['epsilon']) - self.V[1] = self.C*(y - self.loss['epsilon']) - return X - elif (self.loss['name'] == 'custom'): - pass - else: - raise Exception("Sorry, plqERM currently does not support this loss function, \ - but you can manually set ReHLine params to solve the problem via ReHLine class.") - self.auto_shape() - - def _make_constraint_rehline_param(self): - """The `_make_constraint_rehline_param` function generates constraint parameters for the ReHLine solver. - """ - if (self.constraint['name'] == 'nonnegative') or (self.constraint['name'] == '>=0'): - A = np.repeat([X_sen @ X], repeats=[2], axis=0) / n - - - def append_l1(self, X, l1_pen=1.0): - r""" - This function appends the l1 penalty to the ReHLine problem. The formulation becomes: - - .. math:: - - \min_{\mathbf{\beta} \in \mathbb{R}^d} \sum_{i=1}^n \sum_{l=1}^L \text{ReLU}( u_{li} \mathbf{x}_i^\intercal \mathbf{\beta} + v_{li}) + \sum_{i=1}^n \sum_{h=1}^H {\text{ReHU}}_{\tau_{hi}}( s_{hi} \mathbf{x}_i^\intercal \mathbf{\beta} + t_{hi}) + \frac{1}{2} \| \mathbf{\beta} \|_2^2 + \lambda_1 \| \mathbf{\beta} \|_1, \\ \text{ s.t. } - \mathbf{A} \mathbf{\beta} + \mathbf{b} \geq \mathbf{0}, - - where :math:`\lambda_1` is associated with `l1_pen`. - - Parameters - ---------- - - X : ndarray of shape (n_samples, n_features) - The generated samples. - - l1_pen : float, default=1.0 - The l1 penalty level, which controls the complexity or sparsity of the resulting model. - - Returns - ------- - - X_fake: ndarray of shape (n_samples+n_features, n_features) - The manipulated data matrix. It has been padded with - identity matrix, allowing the correctly structured data to be input - into `self.fit` or other modelling processes. - - Examples - -------- - - >>> import numpy as np - >>> from rehline import ReHLine - - >>> # simulate classification dataset - >>> n, d, C, lam1 = 1000, 3, 0.5, 1.0 - >>> np.random.seed(1024) - >>> X = np.random.randn(1000, 3) - >>> beta0 = np.random.randn(3) - >>> y = np.sign(X.dot(beta0) + np.random.randn(n)) - - >>> clf = ReHLine(loss={'name': 'svm'}, C=C) - >>> clf.make_ReLHLoss(X=X, y=y, loss={'name': 'svm'}) - >>> # save and fit with the manipulated data matrix - >>> X_fake = clf.append_l1(X, l1_pen=lam1) - >>> clf.fit(X=X_fake) - >>> print('sol privided by rehline: %s' %clf.coef_) - >>> sol privided by rehline: [ 7.17796629e-01 -1.87075728e-06 2.61965622e+00] #sparse sol - >>> print(clf.decision_function([[.1,.2,.3]])) - >>> [0.85767616] - """ - - n, d = X.shape - l1_pen = l1_pen*np.ones(d) - U_new = np.zeros((self.L+2, n+d)) - V_new = np.zeros((self.L+2, n+d)) - ## Block 1 - if len(self.U): - U_new[:self.L, :n] = self.U - V_new[:self.L, :n] = self.V - ## Block 2 - U_new[-2,n:] = l1_pen - U_new[-1,n:] = -l1_pen - - if len(self.S): - S_new = np.zeros((self.H, n+d)) - T_new = np.zeros((self.H, n+d)) - Tau_new = np.zeros((self.H, n+d)) - - S_new[:,:n] = self.S - T_new[:,:n] = self.T - Tau_new[:,:n] = self.Tau - - self.S = S_new - self.T = T_new - self.Tau = Tau_new - - ## fake X - X_fake = np.zeros((n+d, d)) - X_fake[:n,:] = X - X_fake[n:,:] = np.identity(d) - - self.U = U_new - self.V = V_new - self.auto_shape() - return X_fake - - def auto_shape(self): - """ - Automatically generate the shape of the parameters of the ReHLine loss function. - """ - self.L = self.U.shape[0] - self.n = self.U.shape[1] - self.H = self.S.shape[0] - self.K = self.A.shape[0] - - def call_ReLHLoss(self, score): - """ - Return the value of the ReHLine loss of the `score`. - - Parameters - ---------- - score : ndarray of shape (n_samples, ) - The input score that will be evaluated through the ReHLine loss. - - Returns - ------- - float - ReHLine loss evaluation of the given score. - """ - - relu_input = np.zeros((self.L, self.n)) - rehu_input = np.zeros((self.H, self.n)) - if self.L > 0: - relu_input = (self.U.T * score[:,np.newaxis]).T + self.V - if self.H > 0: - rehu_input = (self.S.T * score[:,np.newaxis]).T + self.T - return np.sum(relu(relu_input), 0) + np.sum(rehu(rehu_input), 0) - - - def fit(self, X, sample_weight=None): + def fit(self, X, y, sample_weight=None): """Fit the model based on the given training data. Parameters @@ -477,6 +302,9 @@ def fit(self, X, sample_weight=None): Training vector, where `n_samples` is the number of samples and `n_features` is the number of features. + y : array-like of shape (n_samples,) + The target variable. + sample_weight : array-like of shape (n_samples,), default=None Array of weights that are assigned to individual samples. If not provided, then each sample is given unit weight. @@ -485,11 +313,23 @@ def fit(self, X, sample_weight=None): ------- self : object An instance of the estimator. + + """ + n, d = X.shape + + ## loss -> rehline params + self.U, self.V, self.Tau, self.S, self.T = _make_loss_rehline_param(loss=self.loss, X=X, y=y) + + ## constrain -> rehline params + self.A, self.b = _make_constraint_rehline_param(constraint=self.constraint, X=X, y=y) + self.auto_shape() - # X = check_array(X) + ## sample weight -> rehline params if sample_weight is None: - sample_weight = np.ones(X.shape[0]) + sample_weight = self.C + else: + sample_weight = self.C*_check_sample_weight(sample_weight, X, dtype=X.dtype) if self.L > 0: U_weight = self.U * sample_weight