From 04886adf3c5c305344524bec4b3af71bb0a0869c Mon Sep 17 00:00:00 2001 From: perib Date: Tue, 19 Mar 2024 15:31:40 -0700 Subject: [PATCH 1/7] new search space working with est --- Tutorial/2_Search_Spaces.ipynb | 832 +++++++++++ setup.py | 1 + tpot2/__init__.py | 4 +- tpot2/config/__init__.py | 22 +- tpot2/config/classifiers.py | 430 +++--- tpot2/config/get_configspace.py | 186 +++ tpot2/config/hyperparametersuggestor.py | 194 --- tpot2/config/imputers.py | 2 + tpot2/config/mdr_configs.py | 80 +- tpot2/config/selectors.py | 128 +- tpot2/config/transformers.py | 157 +-- tpot2/evolvers/__init__.py | 2 +- tpot2/evolvers/base_evolver.py | 26 +- tpot2/evolvers/steady_state_evolver.py | 16 +- .../individual.py | 14 +- tpot2/individual_representations/__init__.py | 5 - .../graph_pipeline_individual/__init__.py | 4 - .../graph_utils/__init__.py | 1 - .../graph_pipeline_individual/individual.py | 1222 ----------------- .../optuna_optimize.py | 228 --- .../graph_pipeline_individual/templates.py | 75 - .../subset_selector/__init__.py | 1 - .../subset_selector/subsetselector.py | 57 - tpot2/population.py | 120 +- tpot2/search_spaces/__init__.py | 4 + tpot2/search_spaces/base.py | 34 + tpot2/search_spaces/nodes/__init__.py | 2 + tpot2/search_spaces/nodes/estimator_node.py | 55 + .../nodes/estimator_node_simple.py | 64 + .../nodes/genetic_feature_selection.py | 178 +++ tpot2/search_spaces/pipelines/__init__.py | 6 + tpot2/search_spaces/pipelines/choice.py | 52 + .../search_spaces/pipelines/dynamic_linear.py | 97 ++ .../pipelines/genetic_sample_weight.py | 1 + tpot2/search_spaces/pipelines/graph.py | 645 +++++++++ .../pipelines}/graph_utils.py | 8 +- .../pipelines/hierarchical_individual.py | 1 + tpot2/search_spaces/pipelines/sequential.py | 62 + tpot2/search_spaces/pipelines/tree.py | 50 + tpot2/search_spaces/pipelines/wrapper.py | 84 ++ .../templates}/__init__.py | 0 tpot2/search_spaces/templates/autoqtl.py | 0 tpot2/search_spaces/templates/stc.py | 0 tpot2/selectors/lexicase_selection.py | 4 +- .../max_weighted_average_selector.py | 2 +- tpot2/selectors/nsgaii.py | 2 +- tpot2/selectors/random_selector.py | 4 +- tpot2/selectors/tournament_selection.py | 4 +- .../tournament_selection_dominated.py | 4 +- tpot2/tpot_estimator/estimator.py | 133 +- .../tpot_estimator/steady_state_estimator.py | 4 +- 51 files changed, 2803 insertions(+), 2504 deletions(-) create mode 100644 Tutorial/2_Search_Spaces.ipynb create mode 100644 tpot2/config/get_configspace.py delete mode 100644 tpot2/config/hyperparametersuggestor.py create mode 100644 tpot2/config/imputers.py rename tpot2/{individual_representations => }/individual.py (82%) delete mode 100644 tpot2/individual_representations/__init__.py delete mode 100644 tpot2/individual_representations/graph_pipeline_individual/__init__.py delete mode 100644 tpot2/individual_representations/graph_pipeline_individual/graph_utils/__init__.py delete mode 100644 tpot2/individual_representations/graph_pipeline_individual/individual.py delete mode 100644 tpot2/individual_representations/graph_pipeline_individual/optuna_optimize.py delete mode 100644 tpot2/individual_representations/graph_pipeline_individual/templates.py delete mode 100644 tpot2/individual_representations/subset_selector/__init__.py delete mode 100644 tpot2/individual_representations/subset_selector/subsetselector.py create mode 100644 tpot2/search_spaces/__init__.py create mode 100644 tpot2/search_spaces/base.py create mode 100644 tpot2/search_spaces/nodes/__init__.py create mode 100644 tpot2/search_spaces/nodes/estimator_node.py create mode 100644 tpot2/search_spaces/nodes/estimator_node_simple.py create mode 100644 tpot2/search_spaces/nodes/genetic_feature_selection.py create mode 100644 tpot2/search_spaces/pipelines/__init__.py create mode 100644 tpot2/search_spaces/pipelines/choice.py create mode 100644 tpot2/search_spaces/pipelines/dynamic_linear.py create mode 100644 tpot2/search_spaces/pipelines/genetic_sample_weight.py create mode 100644 tpot2/search_spaces/pipelines/graph.py rename tpot2/{individual_representations/graph_pipeline_individual/graph_utils => search_spaces/pipelines}/graph_utils.py (93%) create mode 100644 tpot2/search_spaces/pipelines/hierarchical_individual.py create mode 100644 tpot2/search_spaces/pipelines/sequential.py create mode 100644 tpot2/search_spaces/pipelines/tree.py create mode 100644 tpot2/search_spaces/pipelines/wrapper.py rename tpot2/{individual_representations/graph_pipeline_individual/test => search_spaces/templates}/__init__.py (100%) create mode 100644 tpot2/search_spaces/templates/autoqtl.py create mode 100644 tpot2/search_spaces/templates/stc.py diff --git a/Tutorial/2_Search_Spaces.ipynb b/Tutorial/2_Search_Spaces.ipynb new file mode 100644 index 00000000..853ca61f --- /dev/null +++ b/Tutorial/2_Search_Spaces.ipynb @@ -0,0 +1,832 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Everything can be done with the TPOTEstimator class. All other classes (TPOTRegressor, TPOTClassifier, TPOTSymbolicClassifier, TPOTSymbolicRegression, TPOTGeneticFeatureSetSelector, etc.) are actually just different default settings for TPOTEstimator.\n", + "\n", + "\n", + "By Default, TPOT will generate pipelines with a default set of classifiers or regressors as roots (this depends on whether classification is set to true or false). All other nodes are selected from a default list of selectors and transformers. Note: This differs from the TPOT1 behavior where by default classifiers and regressors can appear in locations other than the root. You can modify the the search space for leaves, inner nodes, and roots (final classifiers) separately through built in options or custom configuration dictionaries.\n", + "\n", + "In this tutorial we will walk through using the built in configurations, creating custom configurations, and using nested configurations." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# ConfigSpace\n", + "\n", + "Hyperparameter search spaces are defined using the [ConfigSpace package found here](https://github.com/automl/ConfigSpace). More information on how to set up a hyperparameter space can be found in their [documentation here](https://automl.github.io/ConfigSpace/main/guide.html)." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sampled hyperparameters\n", + "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 6, 'p': 2, 'weights': 'distance'}\n" + ] + } + ], + "source": [ + "from ConfigSpace import ConfigurationSpace\n", + "from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal\n", + "from sklearn.neighbors import KNeighborsClassifier\n", + "\n", + "knn_configspace = ConfigurationSpace(\n", + " space = {\n", + "\n", + " 'n_neighbors': Integer(\"n_neighbors\", bounds=(1, 10)),\n", + " 'weights': Categorical(\"weights\", ['uniform', 'distance']),\n", + " 'p': Integer(\"p\", bounds=(1, 3)),\n", + " 'metric': Categorical(\"metric\", ['euclidean', 'minkowski']),\n", + " 'n_jobs': 1,\n", + " }\n", + ")\n", + "\n", + "hyperparameters = dict(knn_configspace.sample_configuration())\n", + "print(\"sampled hyperparameters\")\n", + "print(hyperparameters)\n", + "\n", + "knn = KNeighborsClassifier(**hyperparameters)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# TPOT Search spaces\n", + "\n", + "TPOT allows you to both hyperparameter search spaces for individual methods as well as pipeline structure search spaces. For example, TPOT can create linear pipelines, trees, or graphs. \n", + "\n", + "TPOT search spaces are found in the `search_spaces` module. There are two primary kinds of search spaces, node and pipeline. Node search spaces specify the search space of a single sklearn `BaseEstimator`. Pipeline search spaces define the possible structures for a group of node search spaces. These take in node search spaces and produce a pipeline using nodes from that search space. Since sklearn Pipelines are also `BaseEstimator`, pipeline search spaces are also technically node search spaces. Meaning that pipeline search spaces can take in other pipeline search spaces in order to define more complex structures. The primary differentiating factor bewteen node and pipeline search spaces is that pipeline search spaces must take in another search space as input to feed its individual nodes. Therefore, all search spaces eventually end in a node search space at the lowest level. Note that parameters for pipeline search spaces can differ, some take in only a single search space, some take in a list, or some take in multiple defined parameters.\n", + "\n", + "search spaces can be found in tpot2.search_spaces.nodes and tpot2.search_spaces.pipelines\n", + "\n", + "### node search spaces\n", + "found in tpot2.search_spaces.nodes\n", + "\n", + "\n", + "EstimatorNode, GeneticFeatureSelector\n", + "| Name | Info |\n", + "| :--- | :----: |\n", + "| EstimatorNode | Takes in a ConfigSpace along with the class of the method. This node will optimize the hyperparameters for a single method. |\n", + "| GeneticFeatureSelector | Uses evolution to optimize a set of features, exports a basic sklearn Selector that simply selects the features chosen by the node. |\n", + "\n", + "\n", + "\n", + "\n", + "### pipeline search spaces\n", + "\n", + "found in tpot2.search_spaces.pipelines\n", + "\n", + "WrapperPipeline - This search space is for wrapping a sklearn estimator with a method that takes another estimator and hyperparameters as arguments.\n", + " For example, this can be used with sklearn.ensemble.BaggingClassifier or sklearn.ensemble.AdaBoostClassifier.\n", + "\n", + "\n", + "| Name | Info |\n", + "| :--- | :----: |\n", + "| ChoicePipeline | Takes in a list of search spaces. Will select one node from the search space. |\n", + "| SquentialPipeline | Takes in a list of search spaces. will produce a pipeline of Squential length. Each step in the pipeline will correspond to the the search space provided in the same index. |\n", + "| DynamicLinearPipeline | Takes in a single search space. Will produce a linear pipeline of variable length. Each step in the pipeline will be pulled from the search space provided. |\n", + "| TreePipeline |Generates a pipeline of variable length. Pipeline will have a tree structure similar to TPOT1. |\n", + "| GraphPipeline | Generates a directed acyclic graph of variable size. Search spaces for root, leaf, and inner nodes can be defined separately if desired. |\n", + "| WrapperPipeline | This search space is for wrapping a sklearn estimator with a method that takes another estimator and hyperparameters as arguments. For example, this can be used with sklearn.ensemble.BaggingClassifier or sklearn.ensemble.AdaBoostClassifier. |\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Estimator node example" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import tpot2\n", + "from ConfigSpace import ConfigurationSpace\n", + "from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal\n", + "from sklearn.neighbors import KNeighborsClassifier\n", + "\n", + "knn_configspace = ConfigurationSpace(\n", + " space = {\n", + "\n", + " 'n_neighbors': Integer(\"n_neighbors\", bounds=(1, 10)),\n", + " 'weights': Categorical(\"weights\", ['uniform', 'distance']),\n", + " 'p': Integer(\"p\", bounds=(1, 3)),\n", + " 'metric': Categorical(\"metric\", ['euclidean', 'minkowski']),\n", + " 'n_jobs': 1,\n", + " }\n", + ")\n", + "\n", + "\n", + "knn_node = tpot2.search_spaces.nodes.EstimatorNode(\n", + " method = KNeighborsClassifier,\n", + " space = knn_configspace,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can sample generate an individual with the generate() function. This individual samples from the search space as well as provides mutation and crossover functions to modify the current sample." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sampled hyperparameters\n", + "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 4, 'p': 3, 'weights': 'distance'}\n", + "mutated hyperparameters\n", + "{'metric': 'euclidean', 'n_jobs': 1, 'n_neighbors': 8, 'p': 2, 'weights': 'uniform'}\n" + ] + } + ], + "source": [ + "knn_individual = knn_node.generate()\n", + "\n", + "print(\"sampled hyperparameters\")\n", + "print(knn_individual.hyperparameters)\n", + "knn_individual.mutate() # mutate the individual\n", + "print(\"mutated hyperparameters\")\n", + "print(knn_individual.hyperparameters)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In TPOT2, crossover only modifies the individual calling the crossover function, the second individual remains the same" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "original hyperparameters for individual 1\n", + "{'metric': 'euclidean', 'n_jobs': 1, 'n_neighbors': 6, 'p': 3, 'weights': 'distance'}\n", + "original hyperparameters for individual 2\n", + "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 5, 'p': 2, 'weights': 'uniform'}\n", + "\n", + "post crossover hyperparameters for individual 1\n", + "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 5, 'p': 3, 'weights': 'distance'}\n", + "post crossover hyperparameters for individual 2\n", + "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 5, 'p': 2, 'weights': 'uniform'}\n" + ] + } + ], + "source": [ + "knn_individual1 = knn_node.generate()\n", + "knn_individual2 = knn_node.generate()\n", + "\n", + "print(\"original hyperparameters for individual 1\")\n", + "print(knn_individual1.hyperparameters)\n", + "\n", + "print(\"original hyperparameters for individual 2\")\n", + "print(knn_individual2.hyperparameters)\n", + "\n", + "print()\n", + "\n", + "knn_individual1.crossover(knn_individual2) # crossover the individuals\n", + "print(\"post crossover hyperparameters for individual 1\")\n", + "print(knn_individual1.hyperparameters)\n", + "print(\"post crossover hyperparameters for individual 2\")\n", + "print(knn_individual2.hyperparameters)\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "All search spaces have an export_pipeline function that returns an sklearn `BaseEstimator`" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
KNeighborsClassifier(n_jobs=1, p=3, weights='distance')
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + ], + "text/plain": [ + "KNeighborsClassifier(n_jobs=1, p=3, weights='distance')" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "knn_individual1.export_pipeline()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Pipeline Search Spaces" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## choice search space\n", + "\n", + "The simplest pipeline search space is the ChoicePipeline. This takes in a list of search spaces and simply selects and samples from one. In this example, we will construct a search space that takes in several options for a classifier." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "import tpot2\n", + "from ConfigSpace import ConfigurationSpace\n", + "from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal\n", + "from sklearn.neighbors import KNeighborsClassifier\n", + "from sklearn.linear_model import LogisticRegression\n", + "from sklearn.tree import DecisionTreeClassifier\n", + "\n", + "knn_configspace = ConfigurationSpace(\n", + " space = {\n", + "\n", + " 'n_neighbors': Integer(\"n_neighbors\", bounds=(1, 10)),\n", + " 'weights': Categorical(\"weights\", ['uniform', 'distance']),\n", + " 'p': Integer(\"p\", bounds=(1, 3)),\n", + " 'metric': Categorical(\"metric\", ['euclidean', 'minkowski']),\n", + " 'n_jobs': 1,\n", + " }\n", + ")\n", + "\n", + "lr_configspace = ConfigurationSpace(\n", + " space = {\n", + " 'solver': Categorical(\"solver\", ['newton-cg', 'lbfgs', 'liblinear', 'sag', 'saga']),\n", + " 'penalty': Categorical(\"penalty\", ['l1', 'l2']),\n", + " 'dual': Categorical(\"dual\", [True, False]),\n", + " 'C': Float(\"C\", bounds=(1e-4, 1e4), log=True),\n", + " 'class_weight': Categorical(\"class_weight\", ['balanced']),\n", + " 'n_jobs': 1,\n", + " 'max_iter': 1000,\n", + " }\n", + " )\n", + "\n", + "dt_configspace = ConfigurationSpace(\n", + " space = {\n", + " 'criterion': Categorical(\"criterion\", ['gini', 'entropy']),\n", + " 'max_depth': Integer(\"max_depth\", bounds=(1, 11)),\n", + " 'min_samples_split': Integer(\"min_samples_split\", bounds=(2, 21)),\n", + " 'min_samples_leaf': Integer(\"min_samples_leaf\", bounds=(1, 21)),\n", + " 'max_features': Categorical(\"max_features\", ['sqrt', 'log2']),\n", + " 'min_weight_fraction_leaf': 0.0,\n", + " }\n", + " )\n", + "\n", + "knn_node = tpot2.search_spaces.nodes.EstimatorNode(\n", + " method = KNeighborsClassifier,\n", + " space = knn_configspace,\n", + ")\n", + "\n", + "lr_node = tpot2.search_spaces.nodes.EstimatorNode(\n", + " method = LogisticRegression,\n", + " space = lr_configspace,\n", + ")\n", + "\n", + "dt_node = tpot2.search_spaces.nodes.EstimatorNode(\n", + " method = DecisionTreeClassifier,\n", + " space = dt_configspace,\n", + ")\n", + "\n", + "classifier_node = tpot2.search_spaces.pipelines.ChoicePipeline(\n", + " choice_list = [\n", + " knn_node,\n", + " lr_node,\n", + " dt_node,\n", + " ]\n", + ")\n", + "\n", + "\n", + "# tpot2.search_spaces.pipelines.ChoicePipeline(\n", + "# choice_list = [\n", + "# tpot2.search_spaces.nodes.EstimatorNode(\n", + "# method = KNeighborsClassifier,\n", + "# space = knn_configspace,\n", + "# ),\n", + "# tpot2.search_spaces.nodes.EstimatorNode(\n", + "# method = LogisticRegression,\n", + "# space = lr_configspace,\n", + "# ),\n", + "# tpot2.search_spaces.nodes.EstimatorNode(\n", + "# method = DecisionTreeClassifier,\n", + "# space = dt_configspace,\n", + "# ),\n", + "# ]\n", + "# )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Search space objects provided by pipeline search spaces work the same as with node search spaces. Note that crossover only works when both individuals have sampled the same method. " + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sampled pipeline\n" + ] + }, + { + "data": { + "text/html": [ + "
KNeighborsClassifier(metric='euclidean', n_jobs=1, n_neighbors=1, p=3)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + ], + "text/plain": [ + "KNeighborsClassifier(metric='euclidean', n_jobs=1, n_neighbors=1, p=3)" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "classifier_individual = classifier_node.generate()\n", + "\n", + "print(\"sampled pipeline\")\n", + "classifier_individual.export_pipeline()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "mutated pipeline\n" + ] + }, + { + "data": { + "text/html": [ + "
KNeighborsClassifier(metric='euclidean', n_jobs=1, n_neighbors=7, p=1)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + ], + "text/plain": [ + "KNeighborsClassifier(metric='euclidean', n_jobs=1, n_neighbors=7, p=1)" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(\"mutated pipeline\")\n", + "classifier_individual.mutate()\n", + "classifier_individual.export_pipeline()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "TPOT2 also comes with predefined search spaces. the helper function `tpot2.config.get_search_space` takes in a string or a list of strings, and returns either a EstimatorNode or a ChoicePipeline,respectively. \n", + "\n", + "strings can correspond to individual methods. Tehre are also special strings that return predefined lists of methods. \n", + "\n", + "Special strings are \"selectors\", \"classifiers\", \"transformers\"\n", + "\n", + "EstimatorNode, GeneticFeatureSelector\n", + "| Special String | Included methods |\n", + "| :--- | :----: |\n", + "| \"selectors\" | \"SelectFwe\", \"SelectPercentile\", \"VarianceThreshold\", \"RFE\", \"SelectFromModel\" |\n", + "| \"classifiers\" | \"LogisticRegression\", \"KNeighborsClassifier\", \"DecisionTreeClassifier\", \"SVC\", \"LinearSVC\", \"RandomForestClassifier\", \"GradientBoostingClassifier\", \"XGBClassifier\", \"LGBMClassifier\", \"ExtraTreesClassifier\", \"SGDClassifier\", \"MLPClassifier\", \"BernoulliNB\", \"MultinomialNB\" |\n", + "| \"transformers\" | \"Binarizer\", \"Normalizer\", \"PCA\", \"ZeroCount\", \"OneHotEncoder\", \"FastICA\", \"FeatureAgglomeration\", \"Nystroem\", \"RBFSampler\" |" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sampled pipeline 1\n" + ] + }, + { + "data": { + "text/html": [ + "
DecisionTreeClassifier(criterion='entropy', max_depth=4, max_features='sqrt',\n",
+       "                       min_samples_leaf=7, min_samples_split=5)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + ], + "text/plain": [ + "DecisionTreeClassifier(criterion='entropy', max_depth=4, max_features='sqrt',\n", + " min_samples_leaf=7, min_samples_split=5)" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "#same pipeline search space as before.\n", + "classifier_choice = tpot2.config.get_search_space([\"KNeighborsClassifier\", \"LogisticRegression\", \"DecisionTreeClassifier\"])\n", + "\n", + "print(\"sampled pipeline 1\")\n", + "classifier_choice.generate().export_pipeline()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sampled pipeline 2\n" + ] + }, + { + "data": { + "text/html": [ + "
LogisticRegression(C=0.22118566188988883, class_weight='balanced',\n",
+       "                   max_iter=1000, n_jobs=1, solver='sag')
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + ], + "text/plain": [ + "LogisticRegression(C=0.22118566188988883, class_weight='balanced',\n", + " max_iter=1000, n_jobs=1, solver='sag')" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(\"sampled pipeline 2\")\n", + "classifier_choice.generate().export_pipeline()" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sampled pipeline 1\n" + ] + }, + { + "data": { + "text/html": [ + "
KNeighborsClassifier(metric='euclidean', n_jobs=1, n_neighbors=89)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + ], + "text/plain": [ + "KNeighborsClassifier(metric='euclidean', n_jobs=1, n_neighbors=89)" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "#search space for all classifiers\n", + "classifier_choice = tpot2.config.get_search_space(\"classifiers\")\n", + "\n", + "print(\"sampled pipeline 1\")\n", + "classifier_choice.generate().export_pipeline()" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sampled pipeline 2\n" + ] + }, + { + "data": { + "text/html": [ + "
GradientBoostingClassifier(learning_rate=0.5981565344248039, max_depth=6,\n",
+       "                           max_features=0.14704006316550916,\n",
+       "                           min_samples_leaf=18, min_samples_split=14,\n",
+       "                           subsample=0.36853097212587516)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + ], + "text/plain": [ + "GradientBoostingClassifier(learning_rate=0.5981565344248039, max_depth=6,\n", + " max_features=0.14704006316550916,\n", + " min_samples_leaf=18, min_samples_split=14,\n", + " subsample=0.36853097212587516)" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(\"sampled pipeline 2\")\n", + "classifier_choice.generate().export_pipeline()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Sequential Example\n", + "\n", + "SequentialPipelines are of fixed length and sample from a predefined distribution for each step. Here is an example of the form Selector-Transformer-Classifer" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sampled pipeline\n" + ] + }, + { + "data": { + "text/html": [ + "
Pipeline(steps=[('selectfwe', SelectFwe(alpha=0.026228617618654658)),\n",
+       "                ('zerocount', ZeroCount()),\n",
+       "                ('bernoullinb', BernoulliNB(alpha=0.04656547221901433))])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + ], + "text/plain": [ + "Pipeline(steps=[('selectfwe', SelectFwe(alpha=0.026228617618654658)),\n", + " ('zerocount', ZeroCount()),\n", + " ('bernoullinb', BernoulliNB(alpha=0.04656547221901433))])" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "stc_pipeline = tpot2.search_spaces.pipelines.SequentialPipeline([\n", + " tpot2.config.get_search_space(\"selectors\"),\n", + " tpot2.config.get_search_space(\"transformers\"),\n", + " tpot2.config.get_search_space(\"classifiers\"),\n", + "])\n", + "\n", + "\n", + "print(\"sampled pipeline\")\n", + "stc_pipeline.generate().export_pipeline()" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sampled pipeline\n" + ] + }, + { + "data": { + "text/html": [ + "
Pipeline(steps=[('selectfwe', SelectFwe(alpha=0.0005298121736972592)),\n",
+       "                ('normalizer', Normalizer()),\n",
+       "                ('mlpclassifier',\n",
+       "                 MLPClassifier(alpha=0.00120637383824527,\n",
+       "                               learning_rate_init=0.001497725714419087))])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + ], + "text/plain": [ + "Pipeline(steps=[('selectfwe', SelectFwe(alpha=0.0005298121736972592)),\n", + " ('normalizer', Normalizer()),\n", + " ('mlpclassifier',\n", + " MLPClassifier(alpha=0.00120637383824527,\n", + " learning_rate_init=0.001497725714419087))])" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(\"sampled pipeline\")\n", + "stc_pipeline.generate().export_pipeline()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Optimize Search Space with TPOTEstimator\n", + "\n", + "Once you have constructed a search space, you can use TPOTEstimator to optimize a pipeline within that space." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Generation: 100%|██████████| 5/5 [00:48<00:00, 9.63s/it]\n" + ] + }, + { + "data": { + "text/html": [ + "
TPOTEstimator(classification=True, generations=5, max_eval_time_seconds=300,\n",
+       "              scorers=['roc_auc'], scorers_weights=[1],\n",
+       "              search_space=<tpot2.search_spaces.pipelines.graph.GraphPipeline object at 0x71f059a54400>,\n",
+       "              verbose=2)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + ], + "text/plain": [ + "TPOTEstimator(classification=True, generations=5, max_eval_time_seconds=300,\n", + " scorers=['roc_auc'], scorers_weights=[1],\n", + " search_space=,\n", + " verbose=2)" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import tpot2\n", + "import numpy as np\n", + "import sklearn\n", + "import sklearn.datasets\n", + "\n", + "# create dummy dataset\n", + "X, y = sklearn.datasets.make_classification(n_samples=2000, n_features=20, n_classes=2)\n", + "\n", + "# train test split\n", + "X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, test_size=0.5)\n", + "\n", + "\n", + "#define the search space\n", + "graph_search_space = tpot2.search_spaces.pipelines.GraphPipeline(\n", + " root_search_space= tpot2.config.get_search_space(\"classifiers\"),\n", + " leaf_search_space = tpot2.config.get_search_space(\"selectors\"), \n", + " inner_search_space = tpot2.config.get_search_space([\"transformers\",\"classifiers\"]),\n", + " max_size = 10,\n", + ")\n", + "\n", + "est = tpot2.TPOTEstimator(\n", + " scorers = [\"roc_auc\"],\n", + " scorers_weights = [1],\n", + " classification = True,\n", + " cv = 5,\n", + " search_space = graph_search_space,\n", + " generations = 5,\n", + " max_eval_time_seconds = 60*5,\n", + " verbose = 2,\n", + ")\n", + "\n", + "est.fit(X_train, y_train)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "auroc score 0.9569231877561475\n" + ] + } + ], + "source": [ + "# score the model\n", + "\n", + "auroc_scorer = sklearn.metrics.get_scorer(\"roc_auc\")\n", + "auroc_score = auroc_scorer(est, X_test, y_test)\n", + "\n", + "print(\"auroc score\", auroc_score)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAnYAAAHWCAYAAAD6oMSKAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAchElEQVR4nO3de5SV5WHv8d/s2dyG+zA43FEi3kEFL4loYpNjPStNbDRZptV0nTTG1mVsV9p4bdZJouc0tfHWE11H26IrNkcjaayW1OQkrhhzCpqoqJAoAiqCyCXiIMgAA3tmnz+QiUZQbkLm8fP5B9iz3/d99t7Der7rfff7vg31er0eAAB6vMr+HgAAAHuHsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADAChEdX8PYF/o7OxMW1tbVq1alVWrVuXllSvTsXFjujo7U2lsTJ9+/TJ8xIi0tramtbU1zc3NaWxs3N/DBgDehvn9rRrq9Xp9fw/i3bJmzZrMnTs3v3z88Wxqb0+9VsuAjRszuK0tvWq1VOr1dDU0ZEu1mrXNzVnfr18aqtX07d8/k6ZMydFHH52hQ4fu75cBALyB+X3Higy75cuX56FZs7J40aL02rAh45a+mJFtbRnc3p5enZ07XG5LY2PW9u+fFc3NWTpubLY0NeWgiRMz7ZRTMnLkyH34CgCA32Z+f2dFhV2tVsvs2bPz6OzZGbB6dQ5esjRjVq9OY1fXLq+rs1LJspaWPDt+XNa3tOT4adMybdq0VKvviaPXAPA7w/y+84oJu5UrV+a+mTOzZtlLOWzRokx86aVU9sJL62poyKLRo/PMxIlpHjM6Hz3jjIwYMWIvjBgAeCfm911TRNgtWbIk98yYkablKzJ1/vwM2rBhr29jXVNT5hx+eDaMGpUzP312xo8fv9e3AQD8hvl91/X4sFuyZEnu/s53MmzJ0pzw9NOp7sZu2Z1Vq1TyiyOPSNu4cfnkH/9xj//wAeB3lfl99/To69itXLky98yYkeYlS/P+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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "#plot the best pipeline\n", + "est.fitted_pipeline_.plot()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tpot2env", + "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.13" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/setup.py b/setup.py index 19f0f322..f0977acd 100644 --- a/setup.py +++ b/setup.py @@ -48,6 +48,7 @@ def calculate_version(): 'dask-ml>=2022.5.27', 'dask-jobqueue>=0.8.1', 'func_timeout>=4.3.5', + 'configspace>=0.7.1', ], extras_require={ 'skrebate': ['skrebate>=0.3.4'], diff --git a/tpot2/__init__.py b/tpot2/__init__.py index ddb8357a..aab11834 100644 --- a/tpot2/__init__.py +++ b/tpot2/__init__.py @@ -2,13 +2,15 @@ #TODO: are all the imports in the init files done correctly? #TODO clean up import organization +from .individual import BaseIndividual + from .graphsklearn import GraphPipeline from .population import Population from . import builtin_modules from . import utils from . import config -from . import individual_representations +from . import search_spaces from . import evolvers from . import objectives from . import selectors diff --git a/tpot2/config/__init__.py b/tpot2/config/__init__.py index e019b78e..7ee03ace 100644 --- a/tpot2/config/__init__.py +++ b/tpot2/config/__init__.py @@ -1,21 +1 @@ -#TODO: make configuration dictionaries optinally based on strings? -from .classifiers import make_classifier_config_dictionary -from .transformers import make_transformer_config_dictionary -from .regressors import make_regressor_config_dictionary -from .selectors import make_selector_config_dictionary -from .special_configs import make_arithmetic_transformer_config_dictionary, make_FSS_config_dictionary, make_passthrough_config_dictionary -from .autoqtl_builtins import make_FeatureEncodingFrequencySelector_config_dictionary, make_genetic_encoders_config_dictionary -from .hyperparametersuggestor import * - -try: - from .classifiers_sklearnex import make_sklearnex_classifier_config_dictionary - from .regressors_sklearnex import make_sklearnex_regressor_config_dictionary -except ModuleNotFoundError: #if optional packages are not installed - pass - -try: - from .mdr_configs import make_skrebate_config_dictionary, make_MDR_config_dictionary, make_ContinuousMDR_config_dictionary -except: #if optional packages are not installed - pass - -from .classifiers import * \ No newline at end of file +from .get_configspace import get_search_space \ No newline at end of file diff --git a/tpot2/config/classifiers.py b/tpot2/config/classifiers.py index 06ed2507..5816b6bb 100644 --- a/tpot2/config/classifiers.py +++ b/tpot2/config/classifiers.py @@ -1,270 +1,222 @@ -from sklearn.linear_model import SGDClassifier -from sklearn.ensemble import RandomForestClassifier, ExtraTreesClassifier, GradientBoostingClassifier -from sklearn.neural_network import MLPClassifier -from sklearn.tree import DecisionTreeClassifier -from xgboost import XGBClassifier -from sklearn.neighbors import KNeighborsClassifier -from sklearn.svm import SVC -from sklearn.linear_model import LogisticRegression -from lightgbm import LGBMClassifier -from sklearn.svm import LinearSVC - -from functools import partial -#import GaussianNB - -from sklearn.naive_bayes import GaussianNB, BernoulliNB, MultinomialNB - -import numpy as np - - - -def params_LogisticRegression(trial, random_state=None, name=None): - params = {} - params['solver'] = trial.suggest_categorical(name=f'solver_{name}', - choices=[f'newton-cg', 'lbfgs', 'liblinear', 'sag', 'saga']) - params['dual'] = False - params['penalty'] = 'l2' - params['C'] = trial.suggest_float(f'C_{name}', 1e-4, 1e4, log=True) - params['l1_ratio'] = None - if params['solver'] == 'liblinear': - params['penalty'] = trial.suggest_categorical(name=f'penalty_{name}', choices=['l1', 'l2']) - if params['penalty'] == 'l2': - params['dual'] = trial.suggest_categorical(name=f'dual_{name}', choices=[True, False]) - else: - params['penalty'] = 'l1' - - params['class_weight'] = trial.suggest_categorical(name=f'class_weight_{name}', choices=['balanced']) - param_grid = {'solver': params['solver'], - 'penalty': params['penalty'], - 'dual': params['dual'], - 'multi_class': 'auto', - 'l1_ratio': params['l1_ratio'], - 'C': params['C'], - 'n_jobs': 1, - 'max_iter': 1000, - 'random_state': random_state - } - return param_grid - - -def params_KNeighborsClassifier(trial, name=None, n_samples=10): - return { - 'n_neighbors': trial.suggest_int(f'n_neighbors_{name}', 1, n_samples, log=True ), - 'weights': trial.suggest_categorical(f'weights_{name}', ['uniform', 'distance']), - 'p': trial.suggest_int('p', 1, 3), - 'metric': str(trial.suggest_categorical(f'metric_{name}', ['euclidean', 'minkowski'])), - 'n_jobs': 1, - } +from ConfigSpace import ConfigurationSpace +from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal + + +def get_LogisticRegression_ConfigurationSpace(): + return ConfigurationSpace( + space = { + 'solver': Categorical("solver", ['newton-cg', 'lbfgs', 'liblinear', 'sag', 'saga']), + 'penalty': Categorical("penalty", ['l1', 'l2']), + 'dual': Categorical("dual", [True, False]), + 'C': Float("C", bounds=(1e-4, 1e4), log=True), + 'class_weight': Categorical("class_weight", ['balanced']), + 'n_jobs': 1, + 'max_iter': 1000, + } + ) -def params_DecisionTreeClassifier(trial, random_state=None, name=None): - return { - 'criterion': trial.suggest_categorical(f'criterion_{name}', ['gini', 'entropy']), - 'max_depth': trial.suggest_int(f'max_depth_{name}', 1, 11), - # 'max_depth_factor' : trial.suggest_float(f'max_depth_factor_{name}', 0, 2, step=0.1), - 'min_samples_split': trial.suggest_int(f'min_samples_split_{name}', 2, 21), - 'min_samples_leaf': trial.suggest_int(f'min_samples_leaf_{name}', 1, 21), - 'min_weight_fraction_leaf': 0.0, - 'max_features': trial.suggest_categorical(f'max_features_{name}', [ 'sqrt', 'log2']), - 'max_leaf_nodes': None, - 'random_state': random_state - } +def get_KNeighborsClassifier_ConfigurationSpace(n_samples=10): + return ConfigurationSpace( + space = { -def params_SVC(trial, random_state=None, name=None): - return { - 'kernel': trial.suggest_categorical(name=f'kernel_{name}', choices=['poly', 'rbf', 'linear', 'sigmoid']), - 'C': trial.suggest_float(f'C_{name}', 1e-4, 25, log=True), - #'gamma': trial.suggest_categorical(name='fgamma_{name}', choices=['scale', 'auto']), - 'degree': trial.suggest_int(f'degree_{name}', 1, 4), - 'class_weight': trial.suggest_categorical(name=f'class_weight_{name}', choices=[None, 'balanced']), - #'coef0': trial.suggest_float(f'coef0_{name}', 0, 10, step=0.1), - 'max_iter': 3000, - 'tol': 0.005, - 'probability': True, - 'random_state': random_state - } + 'n_neighbors': Integer("n_neighbors", bounds=(1, max(50,n_samples))), + 'weights': Categorical("weights", ['uniform', 'distance']), + 'p': Integer("p", bounds=(1, 3)), + 'metric': Categorical("metric", ['euclidean', 'minkowski']), + 'n_jobs': 1, + } + ) -def params_LinearSVC(trial, random_state=None, name=None): +def get_DecisionTreeClassifier_ConfigurationSpace(): + return ConfigurationSpace( + space = { + 'criterion': Categorical("criterion", ['gini', 'entropy']), + 'max_depth': Integer("max_depth", bounds=(1, 11)), + 'min_samples_split': Integer("min_samples_split", bounds=(2, 21)), + 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 21)), + 'max_features': Categorical("max_features", ['sqrt', 'log2']), + 'min_weight_fraction_leaf': 0.0, + } + ) - penalty = trial.suggest_categorical(name=f'penalty_{name}', choices=['l1', 'l2']) - if penalty == 'l1': - loss = 'squared_hinge' - else: - loss = trial.suggest_categorical(name=f'loss_{name}', choices=['hinge', 'squared_hinge']) - if loss == 'hinge' and penalty == 'l2': - dual = True - else: - dual = trial.suggest_categorical(name=f'dual_{name}', choices=[True, False]) - - return { - 'penalty': penalty, - 'loss': loss, - 'dual': dual, - 'C': trial.suggest_float(f'C_{name}', 1e-4, 25, log=True), - 'random_state': random_state - } +def get_SVC_ConfigurationSpace(): + return ConfigurationSpace( + space = { + 'kernel': Categorical("kernel", ['poly', 'rbf', 'linear', 'sigmoid']), + 'C': Float("C", bounds=(1e-4, 25), log=True), + 'degree': Integer("degree", bounds=(1, 4)), + #'class_weight': Categorical("class_weight", [None, 'balanced']), #TODO add class_weight. configspace doesn't allow None as a value. + 'max_iter': 3000, + 'tol': Float("tol", bounds=(0.001, 0.01)), + 'probability': Categorical("probability", [True]), # configspace doesn't allow bools as a default value? but does allow them as a value inside a Categorical + } + ) + +def get_LinearSVC_ConfigurationSpace(random_state=None,): + space = { + 'penalty': Categorical("penalty", ['l1', 'l2']), + 'loss': Categorical("loss", ['hinge', 'squared_hinge']), + 'dual': Categorical("dual", [True, False]), + 'C': Float("C", bounds=(1e-4, 25), log=True), + } + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space + ) -def params_RandomForestClassifier(trial, random_state=None, name=None): - params = { - 'n_estimators': 100, - 'criterion': trial.suggest_categorical(name=f'criterion_{name}', choices=['gini', 'entropy']), - #'max_features': trial.suggest_categorical('max_features_{name}', ['auto', 'sqrt', 'log2']), - 'bootstrap': trial.suggest_categorical(name=f'bootstrap_{name}', choices=[True, False]), - 'min_samples_split': trial.suggest_int(f'min_samples_split_{name}', 2, 20), - 'min_samples_leaf': trial.suggest_int(f'min_samples_leaf_{name}', 1, 20), - 'n_jobs': 1, - 'random_state': random_state - } - return params -def params_GradientBoostingClassifier(trial, random_state=None, n_classes=None, name=None): +def get_RandomForestClassifier_ConfigurationSpace(random_state=None): + space = { + 'criterion': Categorical("criterion", ['gini', 'entropy']), + 'min_samples_split': Integer("min_samples_split", bounds=(2, 20)), + 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 20)), + 'bootstrap': Categorical("bootstrap", [True, False]), + } + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space + ) + +def get_GradientBoostingClassifier_ConfigurationSpace(n_classes=None): + if n_classes is not None and n_classes > 2: loss = 'log_loss' else: - loss = trial.suggest_categorical(name=f'loss_{name}', choices=['log_loss', 'exponential']) - - params = { - 'n_estimators': 100, - 'loss': loss, - 'learning_rate': trial.suggest_float(f'learning_rate_{name}', 1e-3, 1, log=True), - 'min_samples_leaf': trial.suggest_int(f'min_samples_leaf_{name}', 1, 20), - 'min_samples_split': trial.suggest_int(f'min_samples_split_{name}', 2, 20), - 'subsample': trial.suggest_float(f'subsample_{name}', 0.1, 1.0), - 'max_features': trial.suggest_float(f'max_features_{name}', 0.1, 1.0), - 'max_depth': trial.suggest_int(f'max_depth_{name}', 1, 10), - 'tol': 1e-4, - 'random_state': random_state - } - return params - - -def params_XGBClassifier(trial, random_state=None, name=None): - return { - 'learning_rate': trial.suggest_float(f'learning_rate_{name}', 1e-3, 1, log=True), - 'subsample': trial.suggest_float(f'subsample_{name}', 0.1, 1.0), - 'min_child_weight': trial.suggest_int(f'min_child_weight_{name}', 1, 21), - #'booster': trial.suggest_categorical(name='booster_{name}', choices=['gbtree', 'dart']), - 'n_estimators': 100, - 'max_depth': trial.suggest_int(f'max_depth_{name}', 1, 11), - 'n_jobs': 1, - #'use_label_encoder' : True, - 'random_state': random_state - } + loss = Categorical("loss", ['log_loss', 'exponential']) + + return ConfigurationSpace( + space = { + 'n_estimators': 100, + 'loss': loss, + 'learning_rate': Float("learning_rate", bounds=(1e-3, 1), log=True), + 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 20)), + 'min_samples_split': Integer("min_samples_split", bounds=(2, 20)), + 'subsample': Float("subsample", bounds=(0.1, 1.0)), + 'max_features': Float("max_features", bounds=(0.1, 1.0)), + 'max_depth': Integer("max_depth", bounds=(1, 10)), + } + ) -def params_LGBMClassifier(trial, random_state=None, name=None): - params = { - 'objective': 'binary', - 'metric': 'binary_logloss', - 'boosting_type': trial.suggest_categorical(name=f'boosting_type_{name}', choices=['gbdt', 'dart', 'goss']), - 'num_leaves': trial.suggest_int(f'num_leaves_{name}', 2, 256), - 'max_depth': trial.suggest_int(f'max_depth_{name}', 1, 10), - 'n_estimators': trial.suggest_int(f'n_estimators_{name}', 10, 100), # 200-6000 by 200 - 'deterministic': True, - 'force_row_wise': True, - 'n_jobs': 1, - 'random_state': random_state +def get_XGBClassifier_ConfigurationSpace(random_state=None,): + + space = { + 'learning_rate': Float("learning_rate", bounds=(1e-3, 1), log=True), + 'subsample': Float("subsample", bounds=(0.1, 1.0)), + 'min_child_weight': Integer("min_child_weight", bounds=(1, 21)), + 'max_depth': Integer("max_depth", bounds=(1, 11)), + } - } - if 2 ** params['max_depth'] > params['num_leaves']: - params['num_leaves'] = 2 ** params['max_depth'] - return params - - -def params_ExtraTreesClassifier(trial, random_state=None, name=None): - params = { - 'n_estimators': 100, - 'criterion': trial.suggest_categorical(name=f'criterion_{name}', choices=["gini", "entropy"]), - 'max_features': trial.suggest_float('max_features', 0.05, 1.00), - 'min_samples_split': trial.suggest_int(f'min_samples_split_{name}', 2, 21,step=1), - 'min_samples_leaf': trial.suggest_int(f'min_samples_leaf_{name}', 1, 21, step=1), - 'bootstrap': trial.suggest_categorical(f'bootstrap_{name}', [True, False]), - 'n_jobs': 1, - 'random_state': random_state - } - return params - -def params_SGDClassifier(trial, random_state=None, name=None): - params = { - 'loss': trial.suggest_categorical(f'loss_{name}', ['log_loss', 'modified_huber',]), - 'penalty': 'elasticnet', - 'alpha': trial.suggest_float(f'alpha_{name}', 1e-5, 0.01, log=True), - 'learning_rate': trial.suggest_categorical(f'learning_rate_{name}', ['invscaling', 'constant']), - 'fit_intercept': True, - 'l1_ratio': trial.suggest_float(f'l1_ratio_{name}', 0.0, 1.0), - 'eta0': trial.suggest_float(f'eta0_{name}', 0.01, 1.0), - 'power_t': trial.suggest_float(f'power_t_{name}', 1e-5, 100.0, log=True), - 'n_jobs': 1, - 'random_state': random_state - } + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space + ) + +def get_LGBMClassifier_ConfigurationSpace(random_state=None,): + + space = { + 'objective': 'binary', + 'metric': 'binary_logloss', + 'boosting_type': Categorical("boosting_type", ['gbdt', 'dart', 'goss']), + 'num_leaves': Integer("num_leaves", bounds=(2, 256)), + 'max_depth': Integer("max_depth", bounds=(1, 10)), + 'n_estimators': Integer("n_estimators", bounds=(10, 100)), + 'n_jobs': 1, + } - return params + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state -def params_MLPClassifier_tpot(trial, random_state=None, name=None): - params = { - 'alpha': trial.suggest_float(f'alpha_{name}', 1e-4, 1e-1, log=True), - 'learning_rate_init': trial.suggest_float(f'learning_rate_init_{name}', 1e-3, 1., log=True), - 'random_state': random_state - } + return ConfigurationSpace( + space=space + ) - return params -def params_MLPClassifier_large(trial, name=None): - n_layers = trial.suggest_int(f'n_layers_{name}', 2, 3) - layers = [] - for i in range(n_layers): - layers.append(trial.suggest_int(f'n_neurons_{i}_{name}', 4, 128)) +def get_ExtraTreesClassifier_ConfigurationSpace(random_state=None): + space = { + 'n_estimators': Integer("n_estimators", bounds=(10, 500)), + 'criterion': Categorical("criterion", ["gini", "entropy"]), + 'max_features': Float("max_features", bounds=(0.05, 1.00)), + 'min_samples_split': Integer("min_samples_split", bounds=(2, 21)), + 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 21)), + 'bootstrap': Categorical("bootstrap", [True, False]), + 'n_jobs': 1, + } + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space + ) + + + +def get_SGDClassifier_ConfigurationSpace(random_state=None): + + space = { + 'loss': Categorical("loss", ['log_loss', 'modified_huber']), + 'penalty': 'elasticnet', + 'alpha': Float("alpha", bounds=(1e-5, 0.01), log=True), + 'learning_rate': Categorical("learning_rate", ['invscaling', 'constant']), + 'l1_ratio': Float("l1_ratio", bounds=(0.0, 1.0)), + 'eta0': Float("eta0", bounds=(0.01, 1.0)), + 'power_t': Float("power_t", bounds=(1e-5, 100.0), log=True), + 'n_jobs': 1, + 'fit_intercept': Categorical("fit_intercept", [True]), + } + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state - params = { - 'activation': trial.suggest_categorical(name=f'activation_{name}', choices=['identity', 'logistic', 'tanh', 'relu']), - 'solver': trial.suggest_categorical(name=f'solver_{name}', choices=['lbfgs', 'sgd', 'adam']), - 'alpha': trial.suggest_float(f'alpha_{name}', 0.0001, 1.0, log=True), - 'hidden_layer_sizes': tuple(layers), - 'max_iter' : 10000 - } + return ConfigurationSpace( + space = space + ) - return params -def params_BernoulliNB(trial, name=None): - params = { - 'alpha': trial.suggest_float(f'alpha_{name}', 1e-3, 100, log=True), - 'fit_prior': trial.suggest_categorical(f'fit_prior_{name}', [True, False]), +def get_MLPClassifier_ConfigurationSpace(random_state=None): + space = { + 'alpha': Float("alpha", bounds=(1e-4, 1e-1), log=True), + 'learning_rate_init': Float("learning_rate_init", bounds=(1e-3, 1.), log=True), } - return params + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space + ) + + +def get_BernoulliNB_ConfigurationSpace(): + return ConfigurationSpace( + space = { + 'alpha': Float("alpha", bounds=(1e-3, 100), log=True), + 'fit_prior': Categorical("fit_prior", [True, False]), + } + ) -def params_MultinomialNB(trial, name=None): - params = { - 'alpha': trial.suggest_float(f'alpha_{name}', 1e-3, 100, log=True), - 'fit_prior': trial.suggest_categorical(f'fit_prior_{name}', [True, False]), - } - return params - - -def make_classifier_config_dictionary(random_state=None, n_samples=10, n_classes=None): - n_samples = min(n_samples,100) #TODO optimize this - - return { - LogisticRegression: partial(params_LogisticRegression, random_state=random_state), - DecisionTreeClassifier: partial(params_DecisionTreeClassifier, random_state=random_state), - KNeighborsClassifier: partial(params_KNeighborsClassifier,n_samples=n_samples), - GradientBoostingClassifier: partial(params_GradientBoostingClassifier, random_state=random_state, n_classes=n_classes), - ExtraTreesClassifier: partial(params_ExtraTreesClassifier, random_state=random_state), - RandomForestClassifier: partial(params_RandomForestClassifier, random_state=random_state), - SGDClassifier: partial(params_SGDClassifier, random_state=random_state), - GaussianNB: {}, - BernoulliNB: params_BernoulliNB, - MultinomialNB: params_MultinomialNB, - XGBClassifier: partial(params_XGBClassifier, random_state=random_state), - #LinearSVC: partial(params_LinearSVC, random_state=random_state), - SVC: partial(params_SVC, random_state=random_state), - #: params_LGBMClassifier, # logistic regression and SVM/SVC are just special cases of this one? remove? - MLPClassifier: partial(params_MLPClassifier_tpot, random_state=random_state), +def get_MultinomialNB_ConfigurationSpace(): + return ConfigurationSpace( + space = { + 'alpha': Float("alpha", bounds=(1e-3, 100), log=True), + 'fit_prior': Categorical("fit_prior", [True, False]), } + ) diff --git a/tpot2/config/get_configspace.py b/tpot2/config/get_configspace.py new file mode 100644 index 00000000..7a9e552e --- /dev/null +++ b/tpot2/config/get_configspace.py @@ -0,0 +1,186 @@ +from ..search_spaces.nodes import EstimatorNode +from ..search_spaces.pipelines import ChoicePipeline + +from .classifiers import * +from .transformers import * +from .regressors import * +from .selectors import * + + +from sklearn.linear_model import SGDClassifier +from sklearn.ensemble import RandomForestClassifier, ExtraTreesClassifier, GradientBoostingClassifier +from sklearn.neural_network import MLPClassifier +from sklearn.tree import DecisionTreeClassifier +from xgboost import XGBClassifier +from sklearn.neighbors import KNeighborsClassifier +from sklearn.svm import SVC +from sklearn.linear_model import LogisticRegression +from lightgbm import LGBMClassifier +from sklearn.svm import LinearSVC +from sklearn.naive_bayes import GaussianNB, BernoulliNB, MultinomialNB +from sklearn.ensemble import ExtraTreesRegressor, ExtraTreesClassifier + + +from tpot2.builtin_modules import ZeroCount, OneHotEncoder, ColumnOneHotEncoder +from sklearn.preprocessing import Binarizer +from sklearn.decomposition import FastICA +from sklearn.cluster import FeatureAgglomeration +from sklearn.preprocessing import MaxAbsScaler +from sklearn.preprocessing import MinMaxScaler +from sklearn.preprocessing import Normalizer +from sklearn.kernel_approximation import Nystroem +from sklearn.decomposition import PCA +from sklearn.preprocessing import PolynomialFeatures +from sklearn.kernel_approximation import RBFSampler +from sklearn.preprocessing import RobustScaler +from sklearn.preprocessing import StandardScaler + + +from sklearn.feature_selection import SelectFwe +from sklearn.feature_selection import SelectPercentile +from sklearn.feature_selection import VarianceThreshold +from sklearn.feature_selection import RFE +from sklearn.feature_selection import SelectFromModel + +import sklearn.feature_selection + + +from sklearn.feature_selection import f_classif +from sklearn.feature_selection import f_regression + + + +from tpot2.builtin_modules import RFE_ExtraTreesClassifier, SelectFromModel_ExtraTreesClassifier, RFE_ExtraTreesRegressor, SelectFromModel_ExtraTreesRegressor + +STRING_TO_CLASS = { + #classifiers + "LogisticRegression": LogisticRegression, + "KNeighborsClassifier": KNeighborsClassifier, + "DecisionTreeClassifier": DecisionTreeClassifier, + "SVC": SVC, + "LinearSVC": LinearSVC, + "RandomForestClassifier": RandomForestClassifier, + "GradientBoostingClassifier": GradientBoostingClassifier, + "XGBClassifier": XGBClassifier, + "LGBMClassifier": LGBMClassifier, + "ExtraTreesClassifier": ExtraTreesClassifier, + "SGDClassifier": SGDClassifier, + "MLPClassifier": MLPClassifier, + "BernoulliNB": BernoulliNB, + "MultinomialNB": MultinomialNB, + + #transformers + "Binarizer": Binarizer, + "Normalizer": Normalizer, + "PCA": PCA, + "ZeroCount": ZeroCount, + "OneHotEncoder": ColumnOneHotEncoder, + "FastICA": FastICA, + "FeatureAgglomeration": FeatureAgglomeration, + "Nystroem": Nystroem, + "RBFSampler": RBFSampler, + + #selectors + "SelectFwe": SelectFwe, + "SelectPercentile": SelectPercentile, + "VarianceThreshold": VarianceThreshold, + "RFE": RFE, + "SelectFromModel": SelectFromModel, +} + + + + +def get_configspace(name, n_classes=3, n_samples=100, random_state=None): + match name: + #classifiers.py + case "LogisticRegression": + return get_LogisticRegression_ConfigurationSpace() + case "KNeighborsClassifier": + return get_KNeighborsClassifier_ConfigurationSpace(n_samples=n_samples) + case "DecisionTreeClassifier": + return get_DecisionTreeClassifier_ConfigurationSpace() + case "SVC": + return get_SVC_ConfigurationSpace() + case "LinearSVC": + return get_LinearSVC_ConfigurationSpace() + case "RandomForestClassifier": + return get_RandomForestClassifier_ConfigurationSpace(random_state=random_state) + case "GradientBoostingClassifier": + return get_GradientBoostingClassifier_ConfigurationSpace(n_classes=n_classes) + case "XGBClassifier": + return get_XGBClassifier_ConfigurationSpace(random_state=random_state) + case "LGBMClassifier": + return get_LGBMClassifier_ConfigurationSpace(random_state=random_state) + case "ExtraTreesClassifier": + return get_ExtraTreesClassifier_ConfigurationSpace(random_state=random_state) + case "SGDClassifier": + return get_SGDClassifier_ConfigurationSpace(random_state=random_state) + case "MLPClassifier": + return get_MLPClassifier_ConfigurationSpace(random_state=random_state) + case "BernoulliNB": + return get_BernoulliNB_ConfigurationSpace() + case "MultinomialNB": + return get_MultinomialNB_ConfigurationSpace() + + #transformers.py + case "Binarizer": + return Binarizer_configspace + case "Normalizer": + return Normalizer_configspace + case "PCA": + return PCA_configspace + case "ZeroCount": + return ZeroCount_configspace + case "OneHotEncoder": + return OneHotEncoder_configspace + case "FastICA": + return get_FastICA_configspace() + case "FeatureAgglomeration": + return get_FeatureAgglomeration_configspace() + case "Nystroem": + return get_Nystroem_configspace() + case "RBFSampler": + return get_RBFSampler_configspace() + + #selectors.py + case "SelectFwe": + return SelectFwe_configspace + case "SelectPercentile": + return SelectPercentile_configspace + case "VarianceThreshold": + return VarianceThreshold_configspace + case "RFE": + return RFE_configspace_part + case "SelectFromModel": + return SelectFromModel_configspace_part + + +def check_for_special(name): + match name: + case "selectors": + return ["SelectFwe", "SelectPercentile", "VarianceThreshold",] + case "classifiers": + return ["LogisticRegression", "KNeighborsClassifier", "DecisionTreeClassifier", "SVC", "RandomForestClassifier", "GradientBoostingClassifier", "XGBClassifier", "ExtraTreesClassifier", "SGDClassifier", "MLPClassifier", "BernoulliNB", "MultinomialNB"] + case "transformers": + return ["Binarizer", "Normalizer", "PCA", "ZeroCount", "OneHotEncoder", "FastICA", "FeatureAgglomeration", "Nystroem", "RBFSampler"] + + return name + + +def get_search_space(name, n_classes=3, n_samples=100, random_state=None): + name = check_for_special(name) + + #if list of names, return a list of EstimatorNodes + if isinstance(name, list) or isinstance(name, np.ndarray): + search_spaces = [get_search_space(n, n_classes=n_classes, n_samples=n_samples, random_state=random_state) for n in name] + return ChoicePipeline(choice_list=search_spaces) + else: + return get_estimatornode(name, n_classes=n_classes, n_samples=n_samples, random_state=random_state) + + +def get_estimatornode(name, n_classes=3, n_samples=100, random_state=None): + configspace = get_configspace(name, n_classes=n_classes, n_samples=n_samples, random_state=random_state) + + + return EstimatorNode(STRING_TO_CLASS[name], configspace) diff --git a/tpot2/config/hyperparametersuggestor.py b/tpot2/config/hyperparametersuggestor.py deleted file mode 100644 index 1d3ad1f0..00000000 --- a/tpot2/config/hyperparametersuggestor.py +++ /dev/null @@ -1,194 +0,0 @@ -# import random -# from scipy.stats import loguniform, logser #TODO: remove this dependency? -import numpy as np - -#function that selects selects items from a list with each having independent probability p of being selected -def select(items, p, rng_=None): - rng = np.random.default_rng(rng_) - - selected = [item for item in items if rng.random() < p] - #if selected is empty, select one item at random - if not selected: - return [rng.choice(items)] - return selected - - -class Trial(): - - def __init__(self, rng_=None, old_params=None, alpha=1, hyperparameter_probability=1): - self.rng = np.random.default_rng(rng_) - - self._params = dict() - - self.old_params = old_params - self.alpha = alpha - self.hyperparameter_probability = hyperparameter_probability - - if old_params is not None and len(old_params) > 0: - self.params_to_update = select(list(old_params.keys()), self.hyperparameter_probability, rng_=self.rng) - else: - self.params_to_update = None - - - #Replicating the API found in optuna: https://optuna.readthedocs.io/en/stable/reference/generated/optuna.trial.Trial.html - #copy-pasted some code - def suggest_categorical(self, name, choices): - if self.params_to_update == None or name in self.params_to_update or name not in self.old_params: #If this parameter is selected to be changed - choice = self.suggest_categorical_(name, choices) - else: #if this parameter is not selected to be changed - choice = self.old_params[name] - if choice not in choices: #if the old value is not in the choices, then we need to choose a value for it - choice = self.suggest_categorical_(name, choices) - - self._params[name] = choice - return choice - - def suggest_float(self, - name: str, - low: float, - high: float, - *, - step = None, - log = False, - ): - if self.params_to_update == None or name in self.params_to_update or name not in self.old_params: #If this parameter is selected to be changed - choice = self.suggest_float_(name, low=low, high=high, step=step, log=log) - if self.old_params is not None and name in self.old_params: - choice = self.alpha*choice + (1-self.alpha)*self.old_params[name] - else: #if this parameter is not selected to be changed - choice = self.old_params[name] - - self._params[name] = choice - return choice - - - - def suggest_discrete_uniform(self, name, low, high, q): - if self.params_to_update == None or name in self.params_to_update or name not in self.old_params: - choice = self.suggest_discrete_uniform_(name, low=low, high=high, q=q) - if self.old_params is not None and name in self.old_params: - choice = self.alpha*choice + (1-self.alpha)*self.old_params[name] - else: - choice = self.old_params[name] - - self._params[name] = choice - return choice - - - - def suggest_int(self, name, low, high, step=1, log=False): - if self.params_to_update == None or name in self.params_to_update or name not in self.old_params: - choice = self.suggest_int_(name, low=low, high=high, step=step, log=log) - if self.old_params is not None and name in self.old_params: - choice = int(self.alpha*choice + (1-self.alpha)*self.old_params[name]) - else: - choice = self.old_params[name] - - self._params[name] = choice - return choice - - - def suggest_uniform(self, name, low, high): - if self.params_to_update == None or name in self.params_to_update or name not in self.old_params: - choice = self.suggest_uniform_(name, low=low, high=high) - if self.old_params is not None and name in self.old_params: - choice = self.alpha*choice + (1-self.alpha)*self.old_params[name] - else: - choice = self.old_params[name] - - self._params[name] = choice - return choice - - - -#################################### - #Replicating the API found in optuna: https://optuna.readthedocs.io/en/stable/reference/generated/optuna.trial.Trial.html - #copy-pasted some code - def suggest_categorical_(self, name, choices): - - choice = self.rng.choice(choices) - return choice - - def suggest_float_(self, - name: str, - low: float, - high: float, - *, - step = None, - log = False, - ): - - if log and step is not None: - raise ValueError("The parameter `step` is not supported when `log` is true.") - - if low > high: - raise ValueError( - "The `low` value must be smaller than or equal to the `high` value " - "(low={}, high={}).".format(low, high) - ) - - if log and low <= 0.0: - raise ValueError( - "The `low` value must be larger than 0 for a log distribution " - "(low={}, high={}).".format(low, high) - ) - - if step is not None and step <= 0: - raise ValueError( - "The `step` value must be non-zero positive value, " "but step={}.".format(step) - ) - - #TODO check this produces correct output - if log: - value = self.rng.uniform(np.log(low),np.log(high)) - choice = np.e**value - return choice - - else: - if step is not None: - choice = self.rng.choice(np.arange(low,high,step)) - return choice - else: - choice = self.rng.uniform(low,high) - return choice - - - def suggest_discrete_uniform_(self, name, low, high, q): - choice = self.suggest_float(name, low, high, step=q) - return choice - - - def suggest_int_(self, name, low, high, step=1, log=False): - if low == high: #TODO check that this matches optuna's behaviour - return low - - if log and step >1: - raise ValueError("The parameter `step`>1 is not supported when `log` is true.") - - if low > high: - raise ValueError( - "The `low` value must be smaller than or equal to the `high` value " - "(low={}, high={}).".format(low, high) - ) - - if log and low <= 0.0: - raise ValueError( - "The `low` value must be larger than 0 for a log distribution " - "(low={}, high={}).".format(low, high) - ) - - if step is not None and step <= 0: - raise ValueError( - "The `step` value must be non-zero positive value, " "but step={}.".format(step) - ) - - if log: - value = self.rng.uniform(np.log(low),np.log(high)) - choice = int(np.e**value) - return choice - else: - choice = self.rng.choice(list(range(low,high,step))) - return choice - - def suggest_uniform_(self, name, low, high): - return self.suggest_float(name, low, high) \ No newline at end of file diff --git a/tpot2/config/imputers.py b/tpot2/config/imputers.py new file mode 100644 index 00000000..89bcb60d --- /dev/null +++ b/tpot2/config/imputers.py @@ -0,0 +1,2 @@ +from ConfigSpace import ConfigurationSpace +from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal \ No newline at end of file diff --git a/tpot2/config/mdr_configs.py b/tpot2/config/mdr_configs.py index 1fe7cc7a..4f872bd6 100644 --- a/tpot2/config/mdr_configs.py +++ b/tpot2/config/mdr_configs.py @@ -1,60 +1,52 @@ from mdr import MDR, ContinuousMDR from skrebate import ReliefF, SURF, SURFstar, MultiSURF from functools import partial +from ConfigSpace import ConfigurationSpace +from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal -#MDR -def params_MDR(trial, name=None): - return { - 'tie_break': trial.suggest_categorical(name=f'tie_break_{name}', choices=[0,1]), - 'default_label': trial.suggest_categorical(name=f'default_label_{name}', choices=[0,1]), - } -def params_ContinuousMDR(trial, name=None): - return { - 'tie_break': trial.suggest_categorical(name=f'tie_break_{name}', choices=[0,1]), - 'default_label': trial.suggest_categorical(name=f'default_label_{name}', choices=[0,1]), - } - -#skrebate -def params_skrebate_ReliefF(trial, name=None, n_features=10): - return { - 'n_features_to_select': trial.suggest_int(f'n_features_to_select_{name}', 1, n_features, log=True), - 'n_neighbors': trial.suggest_int(f'n_neighbors_{name}', 2, 500, log=True), +#MDR +MDR_configspace = ConfigurationSpace( + space = { + 'tie_break': Categorical('tie_break', [0,1]), + 'default_label': Categorical('default_label', [0,1]), } +) -def params_skrebate_SURF(trial, name=None, n_features=10): - return { - 'n_features_to_select': trial.suggest_int(f'n_features_to_select_{name}', 1, n_features, log=True), +MDR_configspace = ConfigurationSpace( + space = { + 'tie_break': Categorical('tie_break', [0,1]), + 'default_label': Categorical('default_label', [0,1]), } +) -def params_skrebate_SURFstar(trial, name=None, n_features=10): - return { - 'n_features_to_select': trial.suggest_int(f'n_features_to_select_{name}', 1, n_features, log=True), - } -def params_skrebate_MultiSURF(trial, name=None, n_features=10): - return { - 'n_features_to_select': trial.suggest_int(f'n_features_to_select_{name}', 1, n_features, log=True), +skrebate_ReliefF_configspace = ConfigurationSpace( + space = { + 'n_features_to_select': Integer('n_features_to_select', bounds=(1, 10), log=True), + 'n_neighbors': Integer('n_neighbors', bounds=(1,500), log=True), } +) +def make_skrebate_SURF_config_space(n_features=10): + return ConfigurationSpace( + space = { + 'n_features_to_select': Integer('n_features_to_select', bounds=(1, n_features), log=True), + } +) -def make_skrebate_config_dictionary(n_features=10): - return { - ReliefF : partial(params_skrebate_ReliefF, n_features=n_features), - SURF : partial(params_skrebate_SURF, n_features=n_features), - SURFstar : partial(params_skrebate_SURFstar, n_features=n_features), - MultiSURF: partial(params_skrebate_MultiSURF,n_features=n_features), - } - - -def make_MDR_config_dictionary(): - return { - MDR : params_MDR - } -def make_ContinuousMDR_config_dictionary(): - return { - ContinuousMDR : params_ContinuousMDR - } \ No newline at end of file +def make_skrebate_SURFstar_config_space(n_features=10): + return ConfigurationSpace( + space = { + 'n_features_to_select': Integer('n_features_to_select', bounds=(1, n_features), log=True), + } +) +def make_skrebate_MultiSURF_config_space(n_features=10): + return ConfigurationSpace( + space = { + 'n_features_to_select': Integer('n_features_to_select', bounds=(1, n_features), log=True), + } +) diff --git a/tpot2/config/selectors.py b/tpot2/config/selectors.py index 42589d83..9dc1ebe9 100644 --- a/tpot2/config/selectors.py +++ b/tpot2/config/selectors.py @@ -1,113 +1,41 @@ #TODO: how to best support transformers/selectors that take other transformers with their own hyperparameters? import numpy as np -from sklearn.feature_selection import SelectFwe -from sklearn.feature_selection import SelectPercentile -from sklearn.feature_selection import VarianceThreshold -from sklearn.feature_selection import RFE -from sklearn.feature_selection import SelectFromModel -import sklearn.feature_selection -from functools import partial -from sklearn.ensemble import ExtraTreesRegressor, ExtraTreesClassifier -from tpot2.builtin_modules import RFE_ExtraTreesClassifier, SelectFromModel_ExtraTreesClassifier, RFE_ExtraTreesRegressor, SelectFromModel_ExtraTreesRegressor +import sklearn -from .classifiers import params_ExtraTreesClassifier -from .regressors import params_ExtraTreesRegressor +from ConfigSpace import ConfigurationSpace +from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal -def params_sklearn_feature_selection_SelectFwe(trial, name=None): - return { - 'alpha': trial.suggest_float(f'alpha_{name}', 1e-4, 0.05, log=True), - 'score_func' : sklearn.feature_selection.f_classif, +SelectFwe_configspace = ConfigurationSpace( + space = { + 'alpha': Float('alpha', bounds=(1e-4, 0.05), log=True), } +) -def params_sklearn_feature_selection_SelectPercentile(trial, name=None): - return { - 'percentile': trial.suggest_float(f'percentile_{name}', 1, 100.0), - 'score_func' : sklearn.feature_selection.f_classif, - } -def params_sklearn_feature_selection_VarianceThreshold(trial, name=None): - return { - 'threshold': trial.suggest_float(f'threshold_{name}', 1e-4, .2, log=True) +SelectPercentile_configspace = ConfigurationSpace( + space = { + 'percentile': Float('percentile', bounds=(1, 100.0)), } +) - -#TODO add more estimator options? How will that interact with optuna? -def params_sklearn_feature_selection_RFE(trial, random_state=None, name=None, classifier=True): - - if classifier: - estimator = ExtraTreesClassifier(**params_ExtraTreesClassifier(trial, random_state=random_state, name=f"RFE_{name}")) - else: - estimator = ExtraTreesRegressor(**params_ExtraTreesRegressor(trial, random_state=random_state, name=f"RFE_{name}")) - - params = { - 'step': trial.suggest_float(f'step_{name}', 1e-4, 1.0, log=False), - 'estimator' : estimator, - } - - return params - - -def params_sklearn_feature_selection_SelectFromModel(trial, random_state=None, name=None, classifier=True): - - if classifier: - estimator = ExtraTreesClassifier(**params_ExtraTreesClassifier(trial, random_state=random_state, name=f"SFM_{name}")) - else: - estimator = ExtraTreesRegressor(**params_ExtraTreesRegressor(trial, random_state=random_state, name=f"SFM_{name}")) - - params = { - 'threshold': trial.suggest_float(f'threshold_{name}', 1e-4, 1.0, log=True), - 'estimator' : estimator, - } - - return params - - - -def params_sklearn_feature_selection_RFE_wrapped(trial, random_state=None, name=None, classifier=True): - - params = { - 'step': trial.suggest_float(f'step_{name}', 1e-4, 1.0, log=False), - } - - if classifier: - estimator_params = params_ExtraTreesClassifier(trial, random_state=random_state, name=f"RFE_{name}") - else: - estimator_params = params_ExtraTreesRegressor(trial, random_state=random_state, name=f"RFE_{name}") - - params.update(estimator_params) - - return params - - -def params_sklearn_feature_selection_SelectFromModel_wrapped(trial, random_state=None, name=None, classifier=True): - - params = { - 'threshold': trial.suggest_float(f'threshold_{name}', 1e-4, 1.0, log=True), - } - - if classifier: - estimator_params = params_ExtraTreesClassifier(trial, random_state=random_state, name=f"SFM_{name}") - else: - estimator_params = params_ExtraTreesRegressor(trial, random_state=random_state, name=f"SFM_{name}") - - params.update(estimator_params) - - return params - +VarianceThreshold_configspace = ConfigurationSpace( + space = { + 'threshold': Float('threshold', bounds=(1e-4, .2), log=True), + } +) -def make_selector_config_dictionary(random_state=None, classifier=True): - if classifier: - params = {RFE_ExtraTreesClassifier : partial(params_sklearn_feature_selection_RFE_wrapped, random_state=random_state, classifier=classifier), - SelectFromModel_ExtraTreesClassifier : partial(params_sklearn_feature_selection_SelectFromModel_wrapped, random_state=random_state, classifier=classifier), - } - else: - params = {RFE_ExtraTreesRegressor : partial(params_sklearn_feature_selection_RFE_wrapped, random_state=random_state, classifier=classifier), - SelectFromModel_ExtraTreesRegressor : partial(params_sklearn_feature_selection_SelectFromModel_wrapped, random_state=random_state, classifier=classifier), - } - params.update({ SelectFwe: params_sklearn_feature_selection_SelectFwe, - SelectPercentile: params_sklearn_feature_selection_SelectPercentile, - VarianceThreshold: params_sklearn_feature_selection_VarianceThreshold,}) +# Note the RFE_configspace_part and SelectFromModel_configspace_part are not complete, they both require the estimator to be set. +# These are indended to be used with the Wrapped search space. +RFE_configspace_part = ConfigurationSpace( + space = { + 'step': Float('step', bounds=(1e-4, 1.0)), + } +) - return params \ No newline at end of file +SelectFromModel_configspace_part = ConfigurationSpace( + space = { + 'threshold': Float('threshold', bounds=(1e-4, 1.0), log=True), + } +) diff --git a/tpot2/config/transformers.py b/tpot2/config/transformers.py index fe869411..fca4932c 100644 --- a/tpot2/config/transformers.py +++ b/tpot2/config/transformers.py @@ -1,103 +1,78 @@ -from functools import partial -import numpy as np - -from tpot2.builtin_modules import ZeroCount, OneHotEncoder, ColumnOneHotEncoder -from sklearn.preprocessing import Binarizer -from sklearn.decomposition import FastICA -from sklearn.cluster import FeatureAgglomeration -from sklearn.preprocessing import MaxAbsScaler -from sklearn.preprocessing import MinMaxScaler -from sklearn.preprocessing import Normalizer -from sklearn.kernel_approximation import Nystroem -from sklearn.decomposition import PCA -from sklearn.preprocessing import PolynomialFeatures -from sklearn.kernel_approximation import RBFSampler -from sklearn.preprocessing import RobustScaler -from sklearn.preprocessing import StandardScaler - - -def params_sklearn_preprocessing_Binarizer(trial, name=None): - return { - 'threshold': trial.suggest_float(f'threshold_{name}', 0.0, 1.0), +from ConfigSpace import ConfigurationSpace +from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal + + +Binarizer_configspace = ConfigurationSpace( + space = { + 'threshold': Float('threshold', bounds=(0.0, 1.0)), } +) + +Normalizer_configspace = ConfigurationSpace( + space={'norm': Categorical('norm', ['l1', 'l2', 'max'])} +) + +PCA_configspace = ConfigurationSpace( + space={'n_components': Float('n_components', bounds=(0.001, 0.999))} +) + +ZeroCount_configspace = ConfigurationSpace() + +OneHotEncoder_configspace = ConfigurationSpace() #TODO include the parameter for max unique values -def params_sklearn_decomposition_FastICA(trial, random_state=None, name=None, n_features=100): - return { - 'n_components': trial.suggest_int(f'n_components_{name}', 1, n_features), # number of components wrt number of features - 'algorithm': trial.suggest_categorical(f'algorithm_{name}', ['parallel', 'deflation']), +def get_FastICA_configspace(n_features=100, random_state=None): + + space = { + 'n_components': Integer('n_components', bounds=(1, n_features)), + 'algorithm': Categorical('algorithm', ['parallel', 'deflation']), 'whiten':'unit-variance', - 'random_state': random_state } - -def params_sklearn_cluster_FeatureAgglomeration(trial, name=None, n_features=100): - - linkage = trial.suggest_categorical(f'linkage_{name}', ['ward', 'complete', 'average']) - if linkage == 'ward': - metric = 'euclidean' - else: - metric = trial.suggest_categorical(f'metric_{name}', ['euclidean', 'l1', 'l2', 'manhattan', 'cosine']) - return { - 'linkage': linkage, - 'metric': metric, - 'n_clusters': trial.suggest_int(f'n_clusters_{name}', 2, n_features-1), #TODO perhaps a percentage of n_features + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space + + ) + +def get_FeatureAgglomeration_configspace(n_features=100): + return ConfigurationSpace( + space = { + 'linkage': Categorical('linkage', ['ward', 'complete', 'average']), + 'metric': Categorical('metric', ['euclidean', 'l1', 'l2', 'manhattan', 'cosine']), + 'n_clusters': Integer('n_clusters', bounds=(2, n_features-1)), + } + ) + +def get_Nystroem_configspace(n_features=100, random_state=None,): + + space = { + 'gamma': Float('gamma', bounds=(0.0, 1.0)), + 'kernel': Categorical('kernel', ['rbf', 'cosine', 'chi2', 'laplacian', 'polynomial', 'poly', 'linear', 'additive_chi2', 'sigmoid']), + 'n_components': Integer('n_components', bounds=(1, n_features)), } -def params_sklearn_preprocessing_Normalizer(trial, name=None): - return { - 'norm': trial.suggest_categorical(f'norm_{name}', ['l1', 'l2', 'max']), - } -def params_sklearn_kernel_approximation_Nystroem(trial, random_state=None, name=None, n_features=100): - return { - 'gamma': trial.suggest_float(f'gamma_{name}', 0.0, 1.0), - 'kernel': trial.suggest_categorical(f'kernel_{name}', ['rbf', 'cosine', 'chi2', 'laplacian', 'polynomial', 'poly', 'linear', 'additive_chi2', 'sigmoid']), - 'n_components': trial.suggest_int(f'n_components_{name}', 1, n_features), - 'random_state': random_state - } + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space -def params_sklearn_decomposition_PCA(trial, random_state=None, name=None, n_features=100): - # keep the number of components required to explain 'variance_explained' of the variance - variance_explained = 1.0 - trial.suggest_float(f'n_components_{name}', 0.001, 0.5, log=True) #values closer to 1 are more likely + ) - return { - 'n_components': variance_explained, - 'random_state': random_state - } +def get_RBFSampler_configspace(n_features=100, random_state=None): -def params_sklearn_kernel_approximation_RBFSampler(trial, random_state=None, name=None, n_features=100): - return { - 'n_components': trial.suggest_int(f'n_components_{name}', 1, n_features), - 'gamma': trial.suggest_float(f'gamma_{name}', 0.0, 1.0), - 'random_state': random_state + space = { + 'gamma': Float('gamma', bounds=(0.0, 1.0)), + 'n_components': Integer('n_components', bounds=(1, n_features)), } -def params_tpot_builtins_ZeroCount(trial, name=None): - - return {} - -def params_tpot_builtins_OneHotEncoder(trial, name=None): - - return {} - -def make_transformer_config_dictionary(random_state=None, n_features=10): - #n_features = min(n_features,100) #TODO optimize this - return { - Binarizer: params_sklearn_preprocessing_Binarizer, - FastICA: partial(params_sklearn_decomposition_FastICA, random_state=random_state, n_features=n_features), - FeatureAgglomeration: partial(params_sklearn_cluster_FeatureAgglomeration,n_features=n_features), - MaxAbsScaler: {}, - MinMaxScaler: {}, - Normalizer: params_sklearn_preprocessing_Normalizer, - Nystroem: partial(params_sklearn_kernel_approximation_Nystroem, random_state=random_state, n_features=n_features), - PCA: partial(params_sklearn_decomposition_PCA, random_state=random_state, n_features=n_features), - PolynomialFeatures: { - 'degree': 2, - 'include_bias': False, - 'interaction_only': False, - }, - RBFSampler: partial(params_sklearn_kernel_approximation_RBFSampler, random_state=random_state, n_features=n_features), - RobustScaler: {}, - StandardScaler: {}, - ZeroCount: params_tpot_builtins_ZeroCount, - ColumnOneHotEncoder: params_tpot_builtins_OneHotEncoder, - } + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space + + ) diff --git a/tpot2/evolvers/__init__.py b/tpot2/evolvers/__init__.py index cf130f80..1d6af1a9 100644 --- a/tpot2/evolvers/__init__.py +++ b/tpot2/evolvers/__init__.py @@ -1,2 +1,2 @@ from .base_evolver import * -from .steady_state_evolver import * \ No newline at end of file +#from .steady_state_evolver import * \ No newline at end of file diff --git a/tpot2/evolvers/base_evolver.py b/tpot2/evolvers/base_evolver.py index 9959f9ab..b8f64cbe 100644 --- a/tpot2/evolvers/base_evolver.py +++ b/tpot2/evolvers/base_evolver.py @@ -4,7 +4,7 @@ import tpot2 import typing import tqdm -from tpot2.individual_representations.individual import BaseIndividual +from tpot2 import BaseIndividual import time import numpy as np import copy @@ -20,13 +20,13 @@ import math from tpot2.utils.utils import get_thresholds, beta_interpolation, remove_items, equalize_list -def ind_mutate(ind, rng_): - rng = np.random.default_rng(rng_) - return ind.mutate(rng_=rng) +def ind_mutate(ind, rng): + rng = np.random.default_rng(rng) + return ind.mutate(rng=rng) -def ind_crossover(ind1, ind2, rng_): - rng = np.random.default_rng(rng_) - return ind1.crossover(ind2, rng_=rng) +def ind_crossover(ind1, ind2, rng): + rng = np.random.default_rng(rng) + return ind1.crossover(ind2, rng=rng) class BaseEvolver(): def __init__( self, @@ -87,7 +87,7 @@ def __init__( self, verbose = 0, periodic_checkpoint_folder = None, callback = None, - rng_=None, + rng=None, ) -> None: """ @@ -196,7 +196,7 @@ def __init__( self, If provided, training will resume from this checkpoint. callback : tpot2.CallBackInterface, default=None Callback object. Not implemented - rng_ : Numpy.Random.Generator, None, default=None + rng : Numpy.Random.Generator, None, default=None An object for reproducability of experiments. This value will be passed to numpy.random.default_rng() to create an instnce of the genrator to pass to other classes - Numpy.Random.Generator @@ -205,7 +205,7 @@ def __init__( self, Will be used to create Generator for 'numpy.random.default_rng()' where a fresh, unpredictable entropy will be pulled from the OS """ - self.rng = np.random.default_rng(rng_) + self.rng = np.random.default_rng(rng) if threshold_evaluation_early_stop is not None or selection_evaluation_early_stop is not None: if evaluation_early_stop_steps is None: @@ -521,7 +521,7 @@ def step(self,): columns_names=self.objective_names, n_survivors=n_survivors, inplace=True, - rng_=self.rng,) + rng=self.rng,) self.generate_offspring() self.evaluate_population() @@ -529,7 +529,7 @@ def step(self,): self.generation += 1 def generate_offspring(self, ): #your EA Algorithm goes here - parents = self.population.parent_select(selector=self.parent_selector, weights=self.objective_function_weights, columns_names=self.objective_names, k=self.cur_population_size, n_parents=2, rng_=self.rng) + parents = self.population.parent_select(selector=self.parent_selector, weights=self.objective_function_weights, columns_names=self.objective_names, k=self.cur_population_size, n_parents=2, rng=self.rng) p = np.array([self.crossover_probability, self.mutate_then_crossover_probability, self.crossover_then_mutate_probability, self.mutate_probability]) p = p / p.sum() var_op_list = self.rng.choice(["crossover", "mutate_then_crossover", "crossover_then_mutate", "mutate"], size=self.cur_population_size, p=p) @@ -538,7 +538,7 @@ def generate_offspring(self, ): #your EA Algorithm goes here if op == "mutate": parents[i] = parents[i][0] #mutations take a single individual - offspring = self.population.create_offspring2(parents, var_op_list, self.mutation_functions, self.mutation_function_weights, self.crossover_functions, self.crossover_function_weights, add_to_population=True, keep_repeats=False, mutate_until_unique=True, rng_=self.rng) + offspring = self.population.create_offspring2(parents, var_op_list, self.mutation_functions, self.mutation_function_weights, self.crossover_functions, self.crossover_function_weights, add_to_population=True, keep_repeats=False, mutate_until_unique=True, rng=self.rng) self.population.update_column(offspring, column_names="Generation", data=self.generation, ) diff --git a/tpot2/evolvers/steady_state_evolver.py b/tpot2/evolvers/steady_state_evolver.py index a45e4059..22a064fb 100644 --- a/tpot2/evolvers/steady_state_evolver.py +++ b/tpot2/evolvers/steady_state_evolver.py @@ -67,10 +67,10 @@ def __init__( self, periodic_checkpoint_folder = None, callback = None, - rng_=None + rng=None ) -> None: - self.rng = np.random.default_rng(rng_) + self.rng = np.random.default_rng(rng) self.max_evaluated_individuals = max_evaluated_individuals self.individuals_until_end_budget = individuals_until_end_budget @@ -176,7 +176,7 @@ def __init__( self, if self.population is None: self.population = tpot2.Population(column_names=init_names) initial_population = [next(self.individual_generator) for _ in range(self.initial_population_size)] - self.population.add_to_population(initial_population, rng_=self.rng) + self.population.add_to_population(initial_population, rng=self.rng) def optimize(self): @@ -404,13 +404,13 @@ def optimize(self): if len(cur_evaluated_population) > self.population_size: scores = evaluated[self.objective_names].to_numpy() weighted_scores = scores * self.objective_function_weights - new_population_index = np.ravel(self.survival_selector(weighted_scores, k=self.population_size, rng_=self.rng)) #TODO make it clear that we are concatenating scores... + new_population_index = np.ravel(self.survival_selector(weighted_scores, k=self.population_size, rng=self.rng)) #TODO make it clear that we are concatenating scores... #set new population try: cur_evaluated_population = np.array(cur_evaluated_population)[new_population_index] cur_evaluated_population = np.concatenate([cur_evaluated_population, unevaluated["Individual"].to_numpy()]) - self.population.set_population(cur_evaluated_population, rng_=self.rng) + self.population.set_population(cur_evaluated_population, rng=self.rng) except Exception as e: print("Exception in survival selection") print(e) @@ -447,11 +447,11 @@ def optimize(self): parents = [] for op in var_ops: if op == "mutate": - parents.extend(np.array(cur_evaluated_population)[self.parent_selector(weighted_scores, k=1, n_parents=1, rng_=self.rng)]) + parents.extend(np.array(cur_evaluated_population)[self.parent_selector(weighted_scores, k=1, n_parents=1, rng=self.rng)]) else: - parents.extend(np.array(cur_evaluated_population)[self.parent_selector(weighted_scores, k=1, n_parents=2, rng_=self.rng)]) + parents.extend(np.array(cur_evaluated_population)[self.parent_selector(weighted_scores, k=1, n_parents=2, rng=self.rng)]) - _offspring = self.population.create_offspring(parents, var_ops, rng_=self.rng, n_jobs=1, add_to_population=True) + _offspring = self.population.create_offspring(parents, var_ops, rng=self.rng, n_jobs=1, add_to_population=True) # If we don't have enough evaluated individuals to use as parents for variation, we create new individuals randomly # This can happen if the individuals in the initial population are invalid diff --git a/tpot2/individual_representations/individual.py b/tpot2/individual.py similarity index 82% rename from tpot2/individual_representations/individual.py rename to tpot2/individual.py index be61fdcb..db6807c3 100644 --- a/tpot2/individual_representations/individual.py +++ b/tpot2/individual.py @@ -13,8 +13,8 @@ def __init__(self) -> None: self.mutation_list = [] self.crossover_list = [] - def mutate(self, rng_=None): - rng = np.random.default_rng(rng_) + def mutate(self, rng=None): + rng = np.random.default_rng(rng) mutation_list_copy = self.mutation_list.copy() rng.shuffle(mutation_list_copy) for func in mutation_list_copy: @@ -22,8 +22,8 @@ def mutate(self, rng_=None): return True return False - def crossover(self, ind2, rng_=None): - rng = np.random.default_rng(rng_) + def crossover(self, ind2, rng=None): + rng = np.random.default_rng(rng) crossover_list_copy = self.crossover_list.copy() rng.shuffle(crossover_list_copy) for func in crossover_list_copy: @@ -32,10 +32,10 @@ def crossover(self, ind2, rng_=None): return False # a guided change of an individual when given an objective function - def optimize(self, objective_function, rng_=None , steps=5): - rng = np.random.default_rng(rng_) + def optimize(self, objective_function, rng=None , steps=5): + rng = np.random.default_rng(rng) for _ in range(steps): - self.mutate(rng_=rng) + self.mutate(rng=rng) #Return a hashable unique to this individual setup #For use when evaluating whether or not an individual is 'the same' and another individual diff --git a/tpot2/individual_representations/__init__.py b/tpot2/individual_representations/__init__.py deleted file mode 100644 index 77457504..00000000 --- a/tpot2/individual_representations/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .individual import BaseIndividual -from .subset_selector import SubsetSelector -from .graph_pipeline_individual import GraphIndividual - -from . import graph_pipeline_individual \ No newline at end of file diff --git a/tpot2/individual_representations/graph_pipeline_individual/__init__.py b/tpot2/individual_representations/graph_pipeline_individual/__init__.py deleted file mode 100644 index 3710b0c3..00000000 --- a/tpot2/individual_representations/graph_pipeline_individual/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .graph_utils import * -from .individual import * -from .templates import * -from .optuna_optimize import * diff --git a/tpot2/individual_representations/graph_pipeline_individual/graph_utils/__init__.py b/tpot2/individual_representations/graph_pipeline_individual/graph_utils/__init__.py deleted file mode 100644 index 758924a0..00000000 --- a/tpot2/individual_representations/graph_pipeline_individual/graph_utils/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .graph_utils import * \ No newline at end of file diff --git a/tpot2/individual_representations/graph_pipeline_individual/individual.py b/tpot2/individual_representations/graph_pipeline_individual/individual.py deleted file mode 100644 index f890e80f..00000000 --- a/tpot2/individual_representations/graph_pipeline_individual/individual.py +++ /dev/null @@ -1,1222 +0,0 @@ -import numpy as np -from tpot2 import config -import networkx as nx -from abc import abstractmethod -import matplotlib.pyplot as plt -import sklearn -import tpot2 -import sklearn.pipeline -from typing import Generator -import optuna -from itertools import combinations -from .graph_utils import graph_utils -import itertools -import baikal -import copy -from .. import BaseIndividual - -class NodeLabel(): - def __init__(self, *, - - #intialized, but may change later - method_class = None, #transformer or baseestimator - hyperparameters=None, - label=None, - ): - - #intializable, but may change later - self.method_class = method_class #transformer or baseestimator - self.hyperparameters = hyperparameters - self.label = label - self._params = None - - - -from functools import partial -#@https://stackoverflow.com/questions/20530455/isomorphic-comparison-of-networkx-graph-objects-instead-of-the-default-address - -class GraphKey(): - ''' - A class that can be used as a key for a graph. - - Parameters - ---------- - graph : (nx.Graph) - The graph to use as a key. Node Attributes are used for the hash. - matched_label : (str) - The node attribute to consider for the hash. - ''' - - def __init__(self, graph, matched_label='label') -> None:#['hyperparameters', 'method_class']) -> None: - - - self.graph = graph - self.matched_label = matched_label - self.node_match = partial(node_match, matched_labels=[matched_label]) - self.key = int(nx.weisfeiler_lehman_graph_hash(self.graph, node_attr=self.matched_label),16) #hash(tuple(sorted([val for (node, val) in self.graph.degree()]))) - - - #If hash is different, node is definitely different - # https://arxiv.org/pdf/2002.06653.pdf - def __hash__(self) -> int: - - return self.key - - #If hash is same, use __eq__ to know if they are actually different - def __eq__(self, other): - return nx.is_isomorphic(self.graph, other.graph, node_match=self.node_match) - -def node_match(n1,n2, matched_labels): - return all( [ n1[m] == n2[m] for m in matched_labels]) - - -class GraphIndividual(BaseIndividual): - ''' - An individual that contains a template for a graph sklearn pipeline. - - Parameters - ---------- - root_config_dict : {dict with format {method class: param_function}} - A dictionary of methods and functions that return a dictionary of hyperparameters. - Used to create the root node of the graph. - inner_config_dict : {dict with format {method class: param_function}} - A dictionary of methods and functions that return a dictionary of hyperparameters. - Used to create the inner nodes of the graph. If None, uses root_config_dict. - leaf_config_dict : {dict with format {method class: param_function}} - A dictionary of methods and functions that return a dictionary of hyperparameters. - Used to create the leaf nodes of the graph. If not None, then all leafs must be created from this dictionary. - Otherwise leaves will be created from inner_config_dict. - initial_graph : (nx.DiGraph or list): - A graph to initialize the individual with. - If a list, it will initialize a linear graph with the methods in the list in the sequence provided. - If the items in the list are dictionaries, nodes will be itialized with those dictionaries. - Strings in the list correspond to the default configuration files. They can be 'Selector', 'Regressor', 'Transformer', 'Classifier'. - max_depth : (int) - The maximum depth of the graph as measured by the shortest distance from the root. - max_size : (int) - The maximum number of nodes in the graph. - max_children : (int) - The maximum number of children a node can have. - name : (str) - The name of the individual. - crossover_same_depth : (bool) - If true, then crossover will only occur between nodes of the same depth as measured by the shortest distance from the root. - crossover_same_recursive_depth : (bool) - If the graph is recursive, then crossover will only occur between graphs of the same recursive depth as measured by the shortest distance from the root. - ''' - def __init__( - self, - root_config_dict, - inner_config_dict=None, - leaf_config_dict=None, - initial_graph = None, - max_size = np.inf, - linear_pipeline = False, - name=None, - crossover_same_depth = False, - crossover_same_recursive_depth = True, - - hyperparameter_probability = 1, - hyper_node_probability = 0, - hyperparameter_alpha = 1, - - unique_subset_values = None, - initial_subset_values = None, - rng_=None, - ): - - self.__debug = False - - rng = np.random.default_rng(rng_) - - self.root_config_dict = root_config_dict - self.inner_config_dict = inner_config_dict - self.leaf_config_dict = leaf_config_dict - - - self.max_size = max_size - self.name = name - - self.crossover_same_depth = crossover_same_depth - self.crossover_same_recursive_depth = crossover_same_recursive_depth - - self.unique_subset_values = unique_subset_values - self.initial_subset_values = initial_subset_values - - self.hyperparameter_probability = hyperparameter_probability - self.hyper_node_probability = hyper_node_probability - self.hyperparameter_alpha = hyperparameter_alpha - - if self.unique_subset_values is not None: - self.row_subset_selector = tpot2.representations.SubsetSelector(rng_=rng, values=unique_subset_values, initial_set=initial_subset_values,k=20) - - if isinstance(initial_graph, nx.DiGraph): - self.graph = initial_graph - self.root = list(nx.topological_sort(self.graph))[0] - - if self.leaf_config_dict is not None and len(self.graph.nodes) == 1: - first_leaf = create_node(self.leaf_config_dict, rng_=rng) - self.graph.add_edge(self.root,first_leaf) - - elif isinstance(initial_graph, list): - node_list = [] - for item in initial_graph: - if isinstance(item, dict): - node_list.append(create_node(item, rng_=rng)) - elif isinstance(item, str): - if item == 'Selector': - from tpot2.config import selector_config_dictionary - node_list.append(create_node(selector_config_dictionary, rng_=rng)) - elif item == 'Regressor': - from tpot2.config import regressor_config_dictionary - node_list.append(create_node(regressor_config_dictionary, rng_=rng)) - elif item == 'Transformer': - from tpot2.config import transformer_config_dictionary - node_list.append(create_node(transformer_config_dictionary, rng_=rng)) - elif item == 'Classifier': - from tpot2.config import classifier_config_dictionary - node_list.append(create_node(classifier_config_dictionary, rng_=rng)) - - self.graph = nx.DiGraph() - for child, parent in zip(node_list, node_list[1:]): - self.graph.add_edge(parent, child) - - self.root = node_list[-1] - - else: - self.graph = nx.DiGraph() - - self.root = create_node(self.root_config_dict, rng_=rng) - self.graph.add_node(self.root) - - if self.leaf_config_dict is not None: - first_leaf = create_node(self.leaf_config_dict, rng_=rng) - self.graph.add_edge(self.root,first_leaf) - - - - self.initialize_all_nodes(rng_=rng) - - #self.root =list(nx.topological_sort(self.graph))[0] - - - self.mutate_methods_list = [self._mutate_hyperparameters, - self._mutate_replace_node, - self._mutate_remove_node, - ] - - self.crossover_methods_list = [ - self._crossover_swap_branch, - ] - - - if self.inner_config_dict is not None: - self.mutate_methods_list.append(self._mutate_insert_inner_node) - self.crossover_methods_list.append(self._crossover_take_branch) #this is the only crossover method that can create inner nodes - if not linear_pipeline: - self.mutate_methods_list.append(self._mutate_insert_bypass_node) - self.mutate_methods_list.append(self._mutate_remove_edge) - self.mutate_methods_list.append(self._mutate_add_edge) - - if not linear_pipeline and (self.leaf_config_dict is not None or self.inner_config_dict is not None): - self.mutate_methods_list.append(self._mutate_insert_leaf) - - - - - if self.unique_subset_values is not None: - self.crossover_methods_list.append(self._crossover_row_subsets) - self.mutate_methods_list.append(self._mutate_row_subsets ) - - self.optimize_methods_list = [ #self._optimize_optuna_single_method_full_pipeline, - self._optimize_optuna_all_methods_full_pipeline] - - self.key = None - - def select_config_dict(self, node): - #check if the node is root, leaf, or inner - if len(list(self.graph.predecessors(node))) == 0: #root - return self.root_config_dict - elif self.leaf_config_dict is not None and len(list(self.graph.successors(node))) == 0: #leaf - return self.leaf_config_dict - else: #inner - return self.inner_config_dict - - - def initialize_all_nodes(self, rng_=None): - rng = np.random.default_rng(rng_) - for node in self.graph: - if isinstance(node,GraphIndividual): - continue - if node.method_class is None: - node.method_class = rng.choice(list(self.select_config_dict(node).keys())) - if node.hyperparameters is None: - get_hyperparameter(self.select_config_dict(node)[node.method_class], nodelabel=node, alpha=self.hyperparameter_alpha, hyperparameter_probability=self.hyperparameter_probability) - - - def fix_noncompliant_leafs(self, rng_=None): - rng = np.random.default_rng(rng_) - leafs = [node for node in self.graph.nodes if len(list(self.graph.successors(node)))==0] - compliant_leafs = [] - noncompliant_leafs = [] - for leaf in leafs: - if leaf.method_class in self.leaf_config_dict: - compliant_leafs.append(leaf) - else: - noncompliant_leafs.append(leaf) - - #find all good leafs. If no good leaves exist, create a new one - if len(compliant_leafs) == 0: - first_leaf = NodeLabel(config_dict=self.leaf_config_dict) - first_leaf.method_class = rng.choice(list(first_leaf.config_dict.keys())) #TODO: check when there is no new method - first_leaf.hyperparameters = first_leaf.config_dict[first_leaf.method_class](config.hyperparametersuggestor) - get_hyperparameter(self.select_config_dict(first_leaf)[first_leaf.method_class], nodelabel=first_leaf, alpha=self.hyperparameter_alpha, hyperparameter_probability=self.hyperparameter_probability) - compliant_leafs.append(first_leaf) - - #connect bad leaves to good leaves (making them internal nodes) - if len(noncompliant_leafs) > 0: - for node in noncompliant_leafs: - self.graph.add_edge(node, rng.choice(compliant_leafs)) - - - - - def _merge_duplicated_nodes(self): - - graph_changed = False - merged = False - while(not merged): - node_list = list(self.graph.nodes) - merged = True - for node, other_node in itertools.product(node_list, node_list): - if node is other_node or isinstance(node,GraphIndividual) or isinstance(other_node,GraphIndividual): - continue - - #If nodes are same class/hyperparameters - if node.method_class == other_node.method_class and node.hyperparameters == other_node.hyperparameters: - node_children = set(self.graph.successors(node)) - other_node_children = set(self.graph.successors(other_node)) - #if nodes have identical children, they can be merged - if node_children == other_node_children: - for other_node_parent in list(self.graph.predecessors(other_node)): - if other_node_parent not in self.graph.predecessors(node): - self.graph.add_edge(other_node_parent,node) - - self.graph.remove_node(other_node) - merged=False - graph_changed = True - break - - return graph_changed - - #returns a flattened pipeline - def flatten_pipeline(self,depth=0): - flattened_full_graph = self.graph.copy() - remove_list = [] - for node in flattened_full_graph: - if isinstance(node,GraphIndividual): - flattened = node.flatten_pipeline(depth+1) - roots = graph_utils.get_roots(flattened) - leaves = graph_utils.get_leaves(flattened) - - n1_s = flattened_full_graph.successors(node) - n1_p = flattened_full_graph.predecessors(node) - - remove_list.append(node) - - - flattened_full_graph = nx.compose(flattened_full_graph, flattened) - - - flattened_full_graph.add_edges_from([ (n2, n) for n in n1_s for n2 in leaves]) - flattened_full_graph.add_edges_from([ (n, n2) for n in n1_p for n2 in roots]) - else: - flattened_full_graph.nodes[node]['recursive depth'] = depth - - - for node in remove_list: - flattened_full_graph.remove_node(node) - - if self.unique_subset_values is not None: - for node in flattened_full_graph: - if "subset_values" not in flattened_full_graph.nodes[node]: - flattened_full_graph.nodes[node]["subset_values"] = list(self.row_subset_selector.subsets) - else: - #intersection - flattened_full_graph.nodes[node]["subset_values"] = list(set(flattened_full_graph.nodes[node]["subset_values"]) & set(self.row_subset_selector.subsets)) - - return flattened_full_graph - - def get_num_nodes(self,): - num_nodes = 0 - - for node in self.graph.nodes: - if isinstance(node, GraphIndividual): - num_nodes+= node.get_num_nodes() - else: - num_nodes += 1 - - return num_nodes - - - def export_nested_pipeline(self, **graph_pipeline_args): - - flattened_full_graph = self.graph.copy() - remove_list = [] - for node in list(flattened_full_graph.nodes): - if isinstance(node,GraphIndividual): - gp = node.export_pipeline(**graph_pipeline_args) - - n1_s = flattened_full_graph.successors(node) - n1_p = flattened_full_graph.predecessors(node) - - remove_list.append(node) - - flattened_full_graph.add_node(gp) - - - flattened_full_graph.add_edges_from([ (gp, n) for n in n1_s]) - flattened_full_graph.add_edges_from([ (n, gp) for n in n1_p]) - - - for node in remove_list: - flattened_full_graph.remove_node(node) - - estimator_graph = flattened_full_graph - - #mapping = {node:node.method_class(**node.hyperparameters) for node in estimator_graph} - label_remapping = {} - label_to_instance = {} - - for node in estimator_graph: - found_unique_label = False - i=1 - while not found_unique_label: - print(type(node)) - if type(node) is tpot2.GraphPipeline: - label = "GraphPipeline_{0}".format( i) - else: - label = "{0}_{1}".format(node.method_class.__name__, i) - if label not in label_to_instance: - found_unique_label = True - else: - i+=1 - - - if type(node) is tpot2.GraphPipeline: - label_remapping[node] = label - label_to_instance[label] = node - else: - label_remapping[node] = label - label_to_instance[label] = node.method_class(**node.hyperparameters) - - estimator_graph = nx.relabel_nodes(estimator_graph, label_remapping) - - for label, instance in label_to_instance.items(): - estimator_graph.nodes[label]["instance"] = instance - - return tpot2.GraphPipeline(graph=estimator_graph, **graph_pipeline_args) - - def export_pipeline(self, **graph_pipeline_args): - estimator_graph = self.flatten_pipeline() - - #mapping = {node:node.method_class(**node.hyperparameters) for node in estimator_graph} - label_remapping = {} - label_to_instance = {} - - for node in estimator_graph: - found_unique_label = False - i=1 - while not found_unique_label: - label = "{0}_{1}".format(node.method_class.__name__, i) - if label not in label_to_instance: - found_unique_label = True - else: - i+=1 - - label_remapping[node] = label - label_to_instance[label] = node.method_class(**node.hyperparameters) - - estimator_graph = nx.relabel_nodes(estimator_graph, label_remapping) - - for label, instance in label_to_instance.items(): - estimator_graph.nodes[label]["instance"] = instance - - return tpot2.GraphPipeline(graph=estimator_graph, **graph_pipeline_args) - - def export_baikal(self,): - graph = self.flatten_pipeline() - toposorted = list(nx.topological_sort(graph)) - toposorted.reverse() - node_outputs = {} - - X = baikal.Input('X') - y = baikal.Input('Target') - - for i in range(len(toposorted)): - node = toposorted[i] - if len(list(graph.successors(node))) == 0: #If this node had no inputs use X - this_inputs = X - else: #in node has inputs, get those - this_inputs = [node_outputs[child] for child in graph.successors(node)] - - this_output = baikal.make_step(node.method_class, class_name=node.method_class.__name__)(**node.hyperparameters)(this_inputs,y) - node_outputs[node] = this_output - - if i == len(toposorted)-1: #last method doesn't need transformed. - return baikal.Model(inputs=X, outputs=this_output, targets=y) - - - def plot(self): - G = self.flatten_pipeline().reverse() #self.graph.reverse() - #TODO clean this up - try: - pos = nx.planar_layout(G) # positions for all nodes - except: - pos = nx.shell_layout(G) - # nodes - options = {'edgecolors': 'tab:gray', 'node_size': 800, 'alpha': 0.9} - nodelist = list(G.nodes) - node_color = [plt.cm.Set1(G.nodes[n]['recursive depth']) for n in G] - - fig, ax = plt.subplots() - - nx.draw(G, pos, nodelist=nodelist, node_color=node_color, ax=ax, **options) - - - '''edgelist = [] - for n in n1.node_set: - for child in n.children: - edgelist.append((n,child))''' - - # edges - #nx.draw_networkx_edges(G, pos, width=3.0, arrows=True) - '''nx.draw_networkx_edges( - G, - pos, - edgelist=[edgelist], - width=8, - alpha=0.5, - edge_color='tab:red', - )''' - - - - # some math labels - labels = {} - for i, n in enumerate(G.nodes): - labels[n] = n.method_class.__name__ + "\n" + str(n.hyperparameters) - - - nx.draw_networkx_labels(G, pos, labels,ax=ax, font_size=7, font_color='black') - - plt.tight_layout() - plt.axis('off') - plt.show() - - - ############# - - #TODO currently does not correctly return false when adding a leaf causes a duplicate node that is later merged - def mutate(self, rng_=None): - rng = np.random.default_rng(rng_) - self.key = None - graph = self.select_graphindividual(rng_=rng) - return graph._mutate(rng_=rng) - - def _mutate(self, rng_=None): - rng = np.random.default_rng(rng_) - rng.shuffle(self.mutate_methods_list) - for mutate_method in self.mutate_methods_list: - if mutate_method(rng_=rng): - self._merge_duplicated_nodes() - - if self.__debug: - print(mutate_method) - - if self.root not in self.graph.nodes: - print('lost root something went wrong with ', mutate_method) - - if len(self.graph.predecessors(self.root)) > 0: - print('root has parents ', mutate_method) - - if any([n in nx.ancestors(self.graph,n) for n in self.graph.nodes]): - print('a node is connecting to itself...') - - if self.__debug: - try: - nx.find_cycle(self.graph) - print('something went wrong with ', mutate_method) - except: - pass - - return True - - return False - - def _mutate_row_subsets(self, rng_=None): - rng = np.random.default_rng(rng_) - if self.unique_subset_values is not None: - self.row_subset_selector.mutate(rng_=rng) - - - def _mutate_hyperparameters(self, rng_=None): - ''' - Mutates the hyperparameters for a randomly chosen node in the graph. - ''' - rng = np.random.default_rng(rng_) - sorted_nodes_list = list(self.graph.nodes) - rng.shuffle(sorted_nodes_list) - completed_one = False - for node in sorted_nodes_list: - if isinstance(node,GraphIndividual): - continue - if isinstance(self.select_config_dict(node)[node.method_class], dict): - continue - - if not completed_one: - _,_, completed_one = get_hyperparameter(self.select_config_dict(node)[node.method_class], rng_=rng, nodelabel=node, alpha=self.hyperparameter_alpha, hyperparameter_probability=self.hyperparameter_probability) - else: - if self.hyper_node_probability > rng.random(): - get_hyperparameter(self.select_config_dict(node)[node.method_class], rng_=rng, nodelabel=node, alpha=self.hyperparameter_alpha, hyperparameter_probability=self.hyperparameter_probability) - - return completed_one - - - - - def _mutate_replace_node(self, rng_=None): - ''' - Replaces the method in a randomly chosen node by a method from the available methods for that node. - - ''' - rng = np.random.default_rng(rng_) - sorted_nodes_list = list(self.graph.nodes) - rng.shuffle(sorted_nodes_list) - for node in sorted_nodes_list: - if isinstance(node,GraphIndividual): - continue - new_node = create_node(self.select_config_dict(node), rng_=rng) - #check if new node and old node are the same - #TODO: add attempts? - if node.method_class != new_node.method_class or node.hyperparameters != new_node.hyperparameters: - nx.relabel_nodes(self.graph, {new_node:node}, copy=False) - return True - - return False - - - def _mutate_remove_node(self, rng_=None): - ''' - Removes a randomly chosen node and connects its parents to its children. - If the node is the only leaf for an inner node and 'leaf_config_dict' is not none, we do not remove it. - ''' - rng = np.random.default_rng(rng_) - nodes_list = list(self.graph.nodes) - nodes_list.remove(self.root) - leaves = graph_utils.get_leaves(self.graph) - - while len(nodes_list) > 0: - node = rng.choice(nodes_list) - nodes_list.remove(node) - - if self.leaf_config_dict is not None and len(list(nx.descendants(self.graph,node))) == 0 : #if the node is a leaf - if len(leaves) <= 1: - continue #dont remove the last leaf - leaf_parents = self.graph.predecessors(node) - - # if any of the parents of the node has one one child, continue - if any([len(list(self.graph.successors(lp))) < 2 for lp in leaf_parents]): #dont remove a leaf if it is the only input into another node. - continue - - graph_utils.remove_and_stitch(self.graph, node) - graph_utils.remove_nodes_disconnected_from_node(self.graph, self.root) - return True - - else: - graph_utils.remove_and_stitch(self.graph, node) - graph_utils.remove_nodes_disconnected_from_node(self.graph, self.root) - return True - - return False - - def _mutate_remove_edge(self, rng_=None): - ''' - Deletes an edge as long as deleting that edge does not make the graph disconnected. - ''' - rng = np.random.default_rng(rng_) - sorted_nodes_list = list(self.graph.nodes) - rng.shuffle(sorted_nodes_list) - for child_node in sorted_nodes_list: - parents = list(self.graph.predecessors(child_node)) - if len(parents) > 1: # if it has more than one parent, you can remove an edge (if this is the only child of a node, it will become a leaf) - - for parent_node in parents: - # if removing the egde will make the parent_node a leaf node, skip - if self.leaf_config_dict is not None and len(list(self.graph.successors(parent_node))) < 2: - continue - - self.graph.remove_edge(parent_node, child_node) - return True - return False - - def _mutate_add_edge(self, rng_=None): - ''' - Randomly add an edge from a node to another node that is not an ancestor of the first node. - ''' - rng = np.random.default_rng(rng_) - sorted_nodes_list = list(self.graph.nodes) - rng.shuffle(sorted_nodes_list) - for child_node in sorted_nodes_list: - for parent_node in sorted_nodes_list: - if self.leaf_config_dict is not None: - if len(list(self.graph.successors(parent_node))) == 0: - continue - - # skip if - # - parent and child are the same node - # - edge already exists - # - child is an ancestor of parent - if (child_node is not parent_node) and not self.graph.has_edge(parent_node,child_node) and (child_node not in nx.ancestors(self.graph, parent_node)): - self.graph.add_edge(parent_node,child_node) - return True - - return False - - - def _mutate_insert_leaf(self, rng_=None): - rng = np.random.default_rng(rng_) - if self.max_size > self.graph.number_of_nodes(): - sorted_nodes_list = list(self.graph.nodes) - rng.shuffle(sorted_nodes_list) #TODO: sort by number of children and/or parents? bias model one way or another - for node in sorted_nodes_list: - #if leafs are protected, check if node is a leaf - #if node is a leaf, skip because we don't want to add node on top of node - if (self.leaf_config_dict is not None #if leafs are protected - and len(list(self.graph.successors(node))) == 0 #if node is leaf - and len(list(self.graph.predecessors(node))) > 0 #except if node is root, in which case we want to add a leaf even if it happens to be a leaf too - ): - - continue - - #If node *is* the root or is not a leaf, add leaf node. (dont want to add leaf on top of leaf) - if self.leaf_config_dict is not None: - new_node = create_node(self.leaf_config_dict, rng_=rng) - else: - new_node = create_node(self.inner_config_dict, rng_=rng) - - self.graph.add_node(new_node) - self.graph.add_edge(node, new_node) - return True - - return False - - def _mutate_insert_bypass_node(self, rng_=None): - rng = np.random.default_rng(rng_) - if self.max_size > self.graph.number_of_nodes(): - sorted_nodes_list = list(self.graph.nodes) - sorted_nodes_list2 = list(self.graph.nodes) - rng.shuffle(sorted_nodes_list) #TODO: sort by number of children and/or parents? bias model one way or another - rng.shuffle(sorted_nodes_list2) - for node in sorted_nodes_list: - for child_node in sorted_nodes_list2: - if child_node is not node and child_node not in nx.ancestors(self.graph, node): - if self.leaf_config_dict is not None: - #If if we are protecting leafs, dont add connection into a leaf - if len(list(nx.descendants(self.graph,node))) ==0 : - continue - - new_node = create_node(config_dict = self.inner_config_dict, rng_=rng) - - self.graph.add_node(new_node) - self.graph.add_edges_from([(node, new_node), (new_node, child_node)]) - return True - - return False - - - def _mutate_insert_inner_node(self, rng_=None): - rng = np.random.default_rng(rng_) - if self.max_size > self.graph.number_of_nodes(): - sorted_nodes_list = list(self.graph.nodes) - sorted_nodes_list2 = list(self.graph.nodes) - rng.shuffle(sorted_nodes_list) #TODO: sort by number of children and/or parents? bias model one way or another - rng.shuffle(sorted_nodes_list2) - for node in sorted_nodes_list: - #loop through children of node - for child_node in list(self.graph.successors(node)): - - if child_node is not node and child_node not in nx.ancestors(self.graph, node): - if self.leaf_config_dict is not None: - #If if we are protecting leafs, dont add connection into a leaf - if len(list(nx.descendants(self.graph,node))) ==0 : - continue - - new_node = create_node(config_dict = self.inner_config_dict, rng_=rng) - - self.graph.add_node(new_node) - self.graph.add_edges_from([(node, new_node), (new_node, child_node)]) - self.graph.remove_edge(node, child_node) - return True - - return False - - ###################################################### - # Crossover - - def get_graphs(self): - graphs = [self] - self.graph.graph['depth'] = 0 - self.graph.graph['recursive depth'] = 0 - for node in self.graph.nodes: - if isinstance(node, GraphIndividual): - node.graph.graph['depth'] = nx.shortest_path_length(self.graph, self.root, node) - graphs = graphs + node._get_graphs(depth=1) - - return graphs - - - def _get_graphs(self, depth=1): - graphs = [self] - self.graph.graph['recursive depth'] = depth - for node in self.graph.nodes: - if isinstance(node, GraphIndividual): - node.graph.graph['depth'] = nx.shortest_path_length(self.graph, self.root, node) - graphs = graphs + node._get_graphs(depth=depth+1) - - return graphs - - - def select_graphindividual(self, rng_=None): - rng = np.random.default_rng(rng_) - graphs = self.get_graphs() - weights = [g.graph.number_of_nodes() for g in graphs] - w_sum = sum(weights) - weights = [w / w_sum for w in weights] # generate probabilities based on sum of weights - return rng.choice(graphs, p=weights) - - - def select_graph_same_recursive_depth(self,ind1,ind2,rng_=None): - rng = np.random.default_rng(rng_) - - graphs1 = ind1.get_graphs() - weights1 = [g.graph.number_of_nodes() for g in graphs1] - w1_sum = sum(weights1) - weights1 = [w / w1_sum for w in weights1] - - graphs2 = ind2.get_graphs() - weights2 = [g.graph.number_of_nodes() for g in graphs2] - w2_sum = sum(weights2) - weights2 = [w / w2_sum for w in weights2] - - g1_sorted_graphs = random_weighted_sort(graphs1, weights1, rng) - g2_sorted_graphs = random_weighted_sort(graphs2, weights2, rng) - - for g1, g2 in zip(g1_sorted_graphs, g2_sorted_graphs): - if g1.graph.graph['depth'] == g2.graph.graph['depth'] and g1.graph.graph['recursive depth'] == g2.graph.graph['recursive depth']: - return g1, g2 - - return ind1,ind2 - - def crossover(self, ind2, rng_=None): - ''' - self is the first individual, ind2 is the second individual - If crossover_same_depth, it will select graphindividuals at the same recursive depth. - Otherwise, it will select graphindividuals randomly from the entire graph and its subgraphs. - - This does not impact graphs without subgraphs. And it does not impacts nodes that are not graphindividuals. Cros - ''' - - rng = np.random.default_rng(rng_) - - self.key = None - ind2.key = None - if self.crossover_same_recursive_depth: - # selects graphs from the same recursive depth and same depth from the root - g1, g2 = self.select_graph_same_recursive_depth(self, ind2, rng_=rng) - - - else: - g1 = self.select_graphindividual(rng_=rng) - g2 = ind2.select_graphindividual(rng_=rng) - - return g1._crossover(g2, rng_=rng) - - def _crossover(self, Graph, rng_=None): - rng = np.random.default_rng(rng_) - - rng.shuffle(self.crossover_methods_list) - for crossover_method in self.crossover_methods_list: - if crossover_method(Graph, rng_=rng): - self._merge_duplicated_nodes() - return True - - if self.__debug: - try: - nx.find_cycle(self.graph) - print('something went wrong with ', crossover_method) - except: - pass - - return False - - - def _crossover_row_subsets(self, G2, rng_=None): - rng = np.random.default_rng(rng_) - if self.unique_subset_values is not None and G2.unique_subset_values is not None: - self.row_subset_selector.crossover(G2.row_subset_selector, rng_=rng) - - - def _crossover_swap_node(self, G2, rng_=None): - ''' - Swaps randomly chosen node from Parent1 with a randomly chosen node from Parent2. - ''' - rng = np.random.default_rng(rng_) - - if self.crossover_same_depth: - pair_gen = graph_utils.select_nodes_same_depth(self.graph, self.root, G2.graph, G2.root, rng_=rng) - else: - pair_gen = graph_utils.select_nodes_randomly(self.graph, G2.graph, rng_=rng) - - for node1, node2 in pair_gen: - if not (node1 is self.root or node2 is G2.root): #TODO: allow root - - n1_s = self.graph.successors(node1) - n1_p = self.graph.predecessors(node1) - - n2_s = G2.graph.successors(node2) - n2_p = G2.graph.predecessors(node2) - - self.graph.remove_node(node1) - G2.graph.remove_node(node2) - - self.graph.add_node(node2) - - self.graph.add_edges_from([ (node2, n) for n in n1_s]) - G2.graph.add_edges_from([ (node1, n) for n in n2_s]) - - self.graph.add_edges_from([ (n, node2) for n in n1_p]) - G2.graph.add_edges_from([ (n, node1) for n in n2_p]) - - return True - return False - - - - def _crossover_swap_branch(self, G2, rng_=None): - ''' - swaps a branch from parent1 with a branch from parent2. does not modify parent2 - ''' - rng = np.random.default_rng(rng_) - - if self.crossover_same_depth: - pair_gen = graph_utils.select_nodes_same_depth(self.graph, self.root, G2.graph, G2.root, rng_=rng) - else: - pair_gen = graph_utils.select_nodes_randomly(self.graph, G2.graph, rng_=rng) - - for node1, node2 in pair_gen: - #TODO: if root is in inner_config_dict, then do use it? - if node1 is self.root or node2 is G2.root: #dont want to add root as inner node - continue - - #check if node1 is a leaf and leafs are protected, don't add an input to the leave - if self.leaf_config_dict is not None: #if we are protecting leaves, - node1_is_leaf = len(list(self.graph.successors(node1))) == 0 - node2_is_leaf = len(list(G2.graph.successors(node2))) == 0 - #if not ((node1_is_leaf and node1_is_leaf) or (not node1_is_leaf and not node2_is_leaf)): #if node1 is a leaf - if (node1_is_leaf and (not node2_is_leaf)) or ( (not node1_is_leaf) and node2_is_leaf): - #only continue if node1 and node2 are both leaves or both not leaves - continue - - temp_graph_1 = self.graph.copy() - temp_graph_1.remove_node(node1) - graph_utils.remove_nodes_disconnected_from_node(temp_graph_1, self.root) - - #isolating the branch - branch2 = G2.graph.copy() - n2_descendants = nx.descendants(branch2,node2) - for n in list(branch2.nodes): - if n not in n2_descendants and n is not node2: #removes all nodes not in the branch - branch2.remove_node(n) - - branch2 = copy.deepcopy(branch2) - branch2_root = graph_utils.get_roots(branch2)[0] - temp_graph_1.add_edges_from(branch2.edges) - for p in list(self.graph.predecessors(node1)): - temp_graph_1.add_edge(p,branch2_root) - - if temp_graph_1.number_of_nodes() > self.max_size: - continue - - self.graph = temp_graph_1 - - return True - return False - - #TODO: Currently returns true even if hyperparameters are blank - def _crossover_hyperparameters(self, G2, rng_=None): - ''' - Swaps the hyperparamters of one randomly chosen node in Parent1 with the hyperparameters of randnomly chosen node in Parent2. - ''' - rng = np.random.default_rng(rng_) - - if self.crossover_same_depth: - pair_gen = graph_utils.select_nodes_same_depth(self.graph, self.root, G2.graph, G2.root, rng_=rng) - else: - pair_gen = graph_utils.select_nodes_randomly(self.graph, G2.graph, rng_=rng) - - for node1, node2 in pair_gen: - if isinstance(node1,GraphIndividual) or isinstance(node2,GraphIndividual): - continue - - if node1.method_class == node2.method_class: - tmp = node1.hyperparameters - node1.hyperparameters = node2.hyperparameters - node2.hyperparameters = tmp - return True - - return False - - #not including the nodes, just their children - #Finds leaves attached to nodes and swaps them - def _crossover_swap_leaf_at_node(self, G2, rng_=None): - rng = np.random.default_rng(rng_) - - if self.crossover_same_depth: - pair_gen = graph_utils.select_nodes_same_depth(self.graph, self.root, G2.graph, G2.root, rng_=rng) - else: - pair_gen = graph_utils.select_nodes_randomly(self.graph, G2.graph, rng_=rng) - - success = False - for node1, node2 in pair_gen: - # if leaves are protected node1 and node2 must both be leaves or both be inner nodes - if self.leaf_config_dict is not None and not (len(list(self.graph.successors(node1)))==0 ^ len(list(G2.graph.successors(node2)))==0): - continue - #self_leafs = [c for c in nx.descendants(self.graph,node1) if len(list(self.graph.successors(c)))==0 and c is not node1] - node_leafs = [c for c in nx.descendants(G2.graph,node2) if len(list(G2.graph.successors(c)))==0 and c is not node2] - - # if len(self_leafs) >0: - # for c in self_leafs: - # if random.choice([True,False]): - # self.graph.remove_node(c) - # G2.graph.add_edge(node2, c) - # success = True - - if len(node_leafs) >0: - for c in node_leafs: - if rng.choice([True,False]): - G2.graph.remove_node(c) - self.graph.add_edge(node1, c) - success = True - - return success - - - def _crossover_take_branch(self, G2, rng_=None): - ''' - Takes a subgraph from Parent2 and add it to a randomly chosen node in Parent1. - ''' - rng = np.random.default_rng(rng_) - - if self.crossover_same_depth: - pair_gen = graph_utils.select_nodes_same_depth(self.graph, self.root, G2.graph, G2.root, rng_=rng) - else: - pair_gen = graph_utils.select_nodes_randomly(self.graph, G2.graph, rng_=rng) - - for node1, node2 in pair_gen: - #TODO: if root is in inner_config_dict, then do use it? - if node2 is G2.root: #dont want to add root as inner node - continue - - - #check if node1 is a leaf and leafs are protected, don't add an input to the leave - if self.leaf_config_dict is not None and len(list(self.graph.successors(node1))) == 0: - continue - - #icheck if node2 is graph individual - # if isinstance(node2,GraphIndividual): - # if not ((isinstance(node2,GraphIndividual) and ("Recursive" in self.inner_config_dict or "Recursive" in self.leaf_config_dict))): - # continue - - #isolating the branch - branch2 = G2.graph.copy() - n2_descendants = nx.descendants(branch2,node2) - for n in list(branch2.nodes): - if n not in n2_descendants and n is not node2: #removes all nodes not in the branch - branch2.remove_node(n) - - #if node1 plus node2 branch has more than max_children, skip - if branch2.number_of_nodes() + self.graph.number_of_nodes() > self.max_size: - continue - - branch2 = copy.deepcopy(branch2) - branch2_root = graph_utils.get_roots(branch2)[0] - self.graph.add_edges_from(branch2.edges) - self.graph.add_edge(node1,branch2_root) - - return True - return False - - #TODO: swap all leaf nodes - def _crossover_swap_all_leafs(self, G2, rng_=None): - pass - - - #TODO: currently ignores ensembles, make it include nodes inside of ensembles - def optimize(self, rng_, objective_function, steps=5): - rng = np.random.default_rng(rng_) - rng.shuffle(self.optimize_methods_list) #select an optimization method - for optimize_method in self.optimize_methods_list: - if optimize_method(rng, objective_function, steps=steps): - return True - - #optimize the hyperparameters of one method to improve the entire pipeline - def _optimize_optuna_single_method_full_pipeline(self, rng_, objective_function, steps=5): - rng = np.random.default_rng(rng_) - nodes_list = list(self.graph.nodes) - rng.shuffle(nodes_list) #TODO: sort by number of children and/or parents? bias model one way or another - for node in nodes_list: - if not isinstance(node, NodeLabel) or isinstance(self.select_config_dict(node)[node.method_class],dict): - continue - else: - study = optuna.create_study() - - def objective(trial): - params = self.select_config_dict(node)[node.method_class](trial) - node.hyperparameters = params - - trial.set_user_attr('params', params) - try: - return objective_function(self) - except: - return np.NAN - - study.optimize(objective, n_trials=steps) - node.hyperparameters = study.best_trial.user_attrs['params'] - return True - - - #optimize the hyperparameters of all methods simultaneously to improve the entire pipeline - def _optimize_optuna_all_methods_full_pipeline(self, rng_, objective_function, steps=5): - nodes_list = list(self.graph.nodes) - study = optuna.create_study() - nodes_to_optimize = [] - for node in nodes_list: - if not isinstance(node, NodeLabel) or isinstance(self.select_config_dict(node)[node.method_class],dict): - continue - else: - nodes_to_optimize.append(node) - - def objective(trial): - param_list = [] - for i, node in enumerate(nodes_to_optimize): - params = self.select_config_dict(node)[node.method_class](trial, name=f'node_{i}') - node.hyperparameters = params - param_list.append(params) - - trial.set_user_attr('params', param_list) - - try: - return objective_function(self) - except: - return np.NAN - - study.optimize(objective, n_trials=steps) - best_params = study.best_trial.user_attrs['params'] - - for node, params in zip(nodes_to_optimize,best_params): - node.hyperparameters = params - - return True - - - def _cached_transform(cache_nunber=0): - #use a cache for models at each CV fold? - #cache just transformations at each fold? - #TODO how to separate full model? - pass - - def __str__(self): - return self.export_pipeline().__str__() - - def unique_id(self) -> GraphKey: - if self.key is None: - g = self.flatten_pipeline() - for n in g.nodes: - if "subset_values" in g.nodes[n]: - g.nodes[n]['label'] = {n.method_class: n.hyperparameters, "subset_values":g.nodes[n]["subset_values"]} - else: - g.nodes[n]['label'] = {n.method_class: n.hyperparameters} - - g.nodes[n]['method_class'] = n.method_class #TODO making this transformation doesn't feel very clean? - g.nodes[n]['hyperparameters'] = n.hyperparameters - - g = nx.convert_node_labels_to_integers(g) - self.key = GraphKey(graph=g) - - return self.key - - def full_node_list(self): - node_list = list(self.graph.nodes) - for node in node_list: - if isinstance(node, GraphIndividual): - node_list.pop(node_list.index(node)) - node_list.extend(node.graph.nodes) - return node_list - - - - -def create_node(config_dict, rng_=None): - ''' - Takes a config_dict and returns a node with a random method_class and hyperparameters - ''' - rng = np.random.default_rng(rng_) - method_class = rng.choice(list(config_dict.keys())) - #if method_class == GraphIndividual or method_class == 'Recursive': - if method_class == 'Recursive': - node = GraphIndividual(**config_dict[method_class]) - else: - hyperparameters, params, _ = get_hyperparameter(config_dict[method_class], rng_=rng, nodelabel=None) - - node = NodeLabel( - method_class=method_class, - hyperparameters=hyperparameters - ) - node._params = params - - return node - - -def random_weighted_sort(l,weights, rng_=None): - rng = np.random.default_rng(rng_) - sorted_l = [] - indeces = {i: weights[i] for i in range(len(l))} - while len(indeces) > 0: - keys = list(indeces.keys()) - p = np.array([indeces[k] for k in keys]) - p = p / p.sum() - next_item = rng.choice(list(indeces.keys()), p=p) - indeces.pop(next_item) - sorted_l.append(l[next_item]) - - return sorted_l - - -def get_hyperparameter(config_func, rng_, nodelabel=None, alpha=1, hyperparameter_probability=1): - rng = np.random.default_rng(rng_) - changed = False - if isinstance(config_func, dict): - return config_func, None, changed - - if nodelabel is not None: - trial = config.hyperparametersuggestor.Trial(rng_=rng, old_params=nodelabel._params, alpha=alpha, hyperparameter_probability=hyperparameter_probability) - new_params = config_func(trial) - changed = trial._params != nodelabel._params - nodelabel._params = trial._params - nodelabel.hyperparameters = new_params - else: - trial = config.hyperparametersuggestor.Trial(rng_=rng, old_params=None, alpha=alpha, hyperparameter_probability=hyperparameter_probability) - new_params = config_func(trial) - - return new_params, trial._params, changed \ No newline at end of file diff --git a/tpot2/individual_representations/graph_pipeline_individual/optuna_optimize.py b/tpot2/individual_representations/graph_pipeline_individual/optuna_optimize.py deleted file mode 100644 index 0928b986..00000000 --- a/tpot2/individual_representations/graph_pipeline_individual/optuna_optimize.py +++ /dev/null @@ -1,228 +0,0 @@ -from tpot2.individual_representations.graph_pipeline_individual.individual import * -import optuna -import numpy as np -import copy -import dask -import traceback -import functools - -# labels all nodes in the graph with a unique ID. -# This allows use to identify exact nodes in the copies on the graph. -# This is necessary since copies of the graph use different NodeLabel object instances as keys, making it hard to identify which are the same nodes. -def label_nodes_in_graphindividual(graphindividual): - nodes_list = graphindividual.full_node_list() - for i, node in enumerate(nodes_list): - if not isinstance(node, NodeLabel): - continue - else: - node.label = f'node_{i}' - - -def optuna_optimize_full_graph(graphindividual, objective_function, objective_function_weights, steps=5, relabel=True, verbose=0, max_eval_time_seconds=60*5, max_time_seconds=60*20, n_returned_models='all', study=None, **objective_kwargs): - if relabel: - label_nodes_in_graphindividual(graphindividual) - - graphindividual = copy.deepcopy(graphindividual) - nodes_list = graphindividual.full_node_list() - - - nodes_to_optimize = [] - for node in nodes_list: - if not isinstance(node, NodeLabel) or isinstance(graphindividual.select_config_dict(node)[node.method_class],dict): - continue - else: - nodes_to_optimize.append(node) - - def objective(trial): - param_dict = dict() - graphindividual.key = None - for node in nodes_to_optimize: - params = graphindividual.select_config_dict(node)[node.method_class](trial, name=node.label) - node.hyperparameters = params - param_dict[node.label] = params - - trial.set_user_attr('params', param_dict) - - try: - scores = tpot2.objective_nan_wrapper(graphindividual, objective_function, verbose=verbose,timeout=max_eval_time_seconds,**objective_kwargs)#objective_function(graphindividual) - trial.set_user_attr('scores', list(scores)) - if scores[0] != "INVALID" and scores[0] != "TIMEOUT": - scores = np.array(scores) * objective_function_weights - scores = list(scores) - - except Exception as e: - print(e) - print(traceback.format_exc()) - scores = ['INVALID'] - trial.set_user_attr('scores', scores) - return scores - - study.optimize(objective, n_trials=steps, timeout=max_time_seconds) - - return study - -def graph_objective(trial, graphindividual, objective_function, objective_function_weights, verbose=0, max_eval_time_seconds=60*5, **objective_kwargs): - - graphindividual = copy.deepcopy(graphindividual) - nodes_list = graphindividual.full_node_list() - - - nodes_to_optimize = [] - for node in nodes_list: - if not isinstance(node, NodeLabel) or isinstance(graphindividual.select_config_dict(node)[node.method_class],dict): - continue - else: - nodes_to_optimize.append(node) - - param_dict = dict() - graphindividual.key = None - for node in nodes_to_optimize: - params = graphindividual.select_config_dict(node)[node.method_class](trial, name=node.label) - node.hyperparameters = params - param_dict[node.label] = params - - trial.set_user_attr('params', param_dict) - - try: - scores = tpot2.objective_nan_wrapper(graphindividual, objective_function, verbose=verbose,timeout=max_eval_time_seconds,**objective_kwargs)#objective_function(graphindividual) - trial.set_user_attr('scores', list(scores)) - if scores[0] != "INVALID" and scores[0] != "TIMEOUT": - scores = np.array(scores) * objective_function_weights - scores = list(scores) - - except Exception as e: - print(e) - print(traceback.format_exc()) - scores = ['INVALID'] - trial.set_user_attr('scores', scores) - - return scores - - -def simple_parallel_optuna(individuals, objective_function, objective_function_weights, client, storage, steps=5, verbose=0, max_eval_time_seconds=60*5, max_time_seconds=60*20, **objective_kwargs): - num_workers = len(client.scheduler_info()['workers']) - worker_per_individual = max(1,int(np.floor(num_workers/len(individuals)))) - remainder = num_workers%len(individuals) - - print(len(individuals)) - - directions = np.repeat('maximize',len(objective_function_weights)) - timeout = max(max_time_seconds/len(individuals), max_eval_time_seconds*2) - - studies = [] - for i, ind in enumerate(individuals): - label_nodes_in_graphindividual(ind) - print(ind) - - #study = optuna.create_study(directions=directions, storage=f"{storage}", load_if_exists=False) - backend_storage = optuna.storages.InMemoryStorage() - study = optuna.create_study(directions=directions, storage=backend_storage, load_if_exists=False) - studies.append(study) - - objective = functools.partial(graph_objective, graphindividual=ind, objective_function=objective_function, objective_function_weights=objective_function_weights, verbose=verbose, max_eval_time_seconds=max_eval_time_seconds, **objective_kwargs) - study.optimize(objective, n_trials=steps, timeout=timeout, n_jobs=num_workers) - - all_graphs = [] - all_scores = [] - for study, ind in zip(studies,individuals): - graphs, scores = get_all_individuals_from_study(study, ind) - all_graphs.extend(graphs) - all_scores.extend(scores) - - return all_graphs, all_scores - - - - -def simple_parallel_optuna_old(individuals, objective_function, objective_function_weights, client, storage, steps=5, verbose=0, max_eval_time_seconds=60*5, max_time_seconds=60*20, **objective_kwargs): - num_workers = len(client.scheduler_info()['workers']) - worker_per_individual = max(1,int(np.floor(num_workers/len(individuals)))) - remainder = num_workers%len(individuals) - - print(worker_per_individual) - print(remainder) - - directions = np.repeat('maximize',len(objective_function_weights)) - - - - futures = [] - studies = [] - for i, ind in enumerate(individuals): - label_nodes_in_graphindividual(ind) - #study = optuna.create_study(directions=directions, storage=f"{storage}", load_if_exists=False) - backend_storage = optuna.storages.InMemoryStorage() - dask_storage = optuna.integration.DaskStorage(storage=backend_storage, client=client) - study = optuna.create_study(directions=directions, storage=dask_storage, load_if_exists=False) - studies.append(study) - if i == 0: - n_futures = worker_per_individual + remainder - else: - n_futures = worker_per_individual - - trials_per_thread = int(np.ceil(steps/n_futures)) - - objective = functools.partial(graph_objective, graphindividual=ind, objective_function=objective_function, objective_function_weights=objective_function_weights, verbose=verbose, max_eval_time_seconds=max_eval_time_seconds) - for _ in range(n_futures): - #future = client.submit(study.optimize, objective, n_trials=trials_per_thread, pure=False, timeout=max_time_seconds,) - future = client.submit(submit_helper, study=study, objective=objective, n_trials=trials_per_thread, timeout=max_time_seconds, pure=False, **objective_kwargs) - futures.append(future) - #futures.append(client.submit(optuna_optimize_full_graph, graphindividual=ind, objective_function=objective_function, objective_function_weights=objective_function_weights, steps=trials_per_thread, verbose=verbose, max_eval_time_seconds=max_eval_time_seconds, max_time_seconds=max_time_seconds, study=study, relabel=False, pure=False, **objective_kwargs)) - - print(len(individuals)) - print(len(futures)) - dask.distributed.wait(futures) - - all_graphs = [] - all_scores = [] - for study, ind in zip(studies,individuals): - graphs, scores = get_all_individuals_from_study(study, ind) - all_graphs.extend(graphs) - all_scores.extend(scores) - - return all_graphs, all_scores - -def submit_helper(study, objective, n_trials, timeout, **kwargs): - objective = functools.partial(objective, **kwargs) - study.optimize(objective, n_trials=n_trials,timeout=timeout) - -def get_all_individuals_from_study(study, graphindividual, n_returned_models='all'): - all_graphs = [] - all_scores = [] - - if n_returned_models == 'pareto': - trials_list = study.best_trials - else: - trials_list = study.trials - - for trial in trials_list: - if not 'scores' in trial.user_attrs: - continue - - params = trial.user_attrs['params'] - scores = trial.user_attrs['scores'] - - newgraphindividual = copy.deepcopy(graphindividual) - newgraphindividual.key = None - try: - for node in newgraphindividual.full_node_list(): - if not isinstance(node, tpot2.NodeLabel): - continue - else: - if node.label in params: - node.hyperparameters = params[node.label] - - all_graphs.append(newgraphindividual) - all_scores.append(scores) - except Exception as e: - print('failed to create graphindividual from trial') - print(e) - print(traceback.format_exc()) - print(params) - print(newgraphindividual) - print(newgraphindividual.graph.nodes) - for node in newgraphindividual.full_node_list(): - print(node.label) - - - return all_graphs, all_scores \ No newline at end of file diff --git a/tpot2/individual_representations/graph_pipeline_individual/templates.py b/tpot2/individual_representations/graph_pipeline_individual/templates.py deleted file mode 100644 index 9b383141..00000000 --- a/tpot2/individual_representations/graph_pipeline_individual/templates.py +++ /dev/null @@ -1,75 +0,0 @@ - -import numpy as np -import tpot2 -import networkx as nx -from tpot2.individual_representations.graph_pipeline_individual import GraphIndividual - -from tpot2.individual_representations.graph_pipeline_individual.individual import create_node - - -# will randomly generate individuals (no predefined order) -def estimator_graph_individual_generator( - root_config_dict, - inner_config_dict=None, - leaf_config_dict=None, - max_size = np.inf, - linear_pipeline = False, - hyperparameter_probability = 1, - hyper_node_probability = 0, - hyperparameter_alpha = 1, - rng_=None, - **kwargs, - ) : - - rng = np.random.default_rng(rng_) - - while True: - - # if user specified limit, grab a random number between that limit - if max_size is not np.inf: - n_nodes = rng.integers(1,max_size+1) - # else, grab random number between 1,11 (theaksaini) - else: - n_nodes = rng.integers(1,11) - - graph = nx.DiGraph() - root = create_node(config_dict=root_config_dict, rng_=rng) # grab random root model method - graph.add_node(root) - - ind = GraphIndividual( rng_=rng, - inner_config_dict=inner_config_dict, - leaf_config_dict=leaf_config_dict, - root_config_dict=root_config_dict, - initial_graph = graph, - - max_size = max_size, - linear_pipeline = linear_pipeline, - hyperparameter_probability = hyperparameter_probability, - hyper_node_probability = hyper_node_probability, - hyperparameter_alpha = hyperparameter_alpha, - - **kwargs, - ) - - starting_ops = [] - if inner_config_dict is not None: - starting_ops.append(ind._mutate_insert_inner_node) - if leaf_config_dict is not None or inner_config_dict is not None: - starting_ops.append(ind._mutate_insert_leaf) - n_nodes -= 1 - - if len(starting_ops) > 0: - for _ in range(n_nodes-1): - func = rng.choice(starting_ops) - func(rng_=rng) - - yield ind - - -class BaggingCompositeGraphSklearn(): - def __init__(self) -> None: - pass - -class BoostingCompositeGraphSklearn(): - def __init__(self) -> None: - pass diff --git a/tpot2/individual_representations/subset_selector/__init__.py b/tpot2/individual_representations/subset_selector/__init__.py deleted file mode 100644 index e856439c..00000000 --- a/tpot2/individual_representations/subset_selector/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .subsetselector import SubsetSelector \ No newline at end of file diff --git a/tpot2/individual_representations/subset_selector/subsetselector.py b/tpot2/individual_representations/subset_selector/subsetselector.py deleted file mode 100644 index 5dc1d8af..00000000 --- a/tpot2/individual_representations/subset_selector/subsetselector.py +++ /dev/null @@ -1,57 +0,0 @@ -from numpy import iterable -import tpot2 -import numpy as np -from .. import BaseIndividual - -class SubsetSelector(BaseIndividual): - def __init__( self, - values, - rng_=None, - initial_set = None, - k=1, #step size for shuffling - ): - - rng = np.random.default_rng(rng_) - - if isinstance(values, int): - self.values = set(range(0,values)) - else: - self.values = set(values) - - - if initial_set is None: - self.subsets = set(rng.choices(values, k=k)) - else: - self.subsets = set(initial_set) - - self.k = k - - self.mutation_list = [self._mutate_add, self._mutate_remove] - self.crossover_list = [self._crossover_swap] - - def _mutate_add(self, rng_=None): - rng = np.random.default_rng(rng_) - not_included = list(self.values.difference(self.subsets)) - if len(not_included) > 1: - self.subsets.update(rng.choice(not_included, k=min(self.k, len(not_included)))) - return True - else: - return False - - def _mutate_remove(self, rng_=None): - rng = np.random.default_rng(rng_) - if len(self.subsets) > 1: - self.subsets = self.subsets - set(rng.choice(list(self.subsets), k=min(self.k, len(self.subsets)-1) )) - - def _crossover_swap(self, ss2, rng_=None): - rng = np.random.default_rng(rng_) - diffs = self.subsets.symmetric_difference(ss2.subsets) - - if len(diffs) == 0: - return False - for v in diffs: - self.subsets.discard(v) - ss2.subsets.discard(v) - rng.choice([self.subsets, ss2.subsets]).add(v) - - return True diff --git a/tpot2/population.py b/tpot2/population.py index a3a0c54c..5c102134 100644 --- a/tpot2/population.py +++ b/tpot2/population.py @@ -3,7 +3,7 @@ import copy import typing import tpot2 -from tpot2.individual_representations.individual import BaseIndividual +from tpot2 import BaseIndividual from traitlets import Bool import collections import pandas as pd @@ -12,32 +12,32 @@ import pickle import dask -def mutate(individual, rng_=None): - rng = np.random.default_rng(rng_) +def mutate(individual, rng=None): + rng = np.random.default_rng(rng) if isinstance(individual, collections.abc.Iterable): for ind in individual: - ind.mutate(rng_=rng) + ind.mutate(rng=rng) else: - individual.mutate(rng_=rng) + individual.mutate(rng=rng) return individual -def crossover(parents, rng_=None): - rng = np.random.default_rng(rng_) - parents[0].crossover(parents[1], rng_=rng) +def crossover(parents, rng=None): + rng = np.random.default_rng(rng) + parents[0].crossover(parents[1], rng=rng) return parents[0] -def mutate_and_crossover(parents, rng_=None): - rng = np.random.default_rng(rng_) - parents[0].crossover(parents[1], rng_=rng) - parents[0].mutate(rng_=rng) - parents[1].mutate(rng_=rng) +def mutate_and_crossover(parents, rng=None): + rng = np.random.default_rng(rng) + parents[0].crossover(parents[1], rng=rng) + parents[0].mutate(rng=rng) + parents[1].mutate(rng=rng) return parents -def crossover_and_mutate(parents, rng_=None): - rng = np.random.default_rng(rng_) +def crossover_and_mutate(parents, rng=None): + rng = np.random.default_rng(rng) for p in parents: - p.mutate(rng_=rng) - parents[0].crossover(parents[1], rng_=rng) + p.mutate(rng=rng) + parents[0].crossover(parents[1], rng=rng) return parents[0] @@ -90,19 +90,19 @@ def __init__( self, self.callback=callback self.population = [] - def survival_select(self, selector, weights, columns_names, n_survivors, rng_=None, inplace=True): - rng = np.random.default_rng(rng_) + def survival_select(self, selector, weights, columns_names, n_survivors, rng=None, inplace=True): + rng = np.random.default_rng(rng) weighted_scores = self.get_column(self.population, column_names=columns_names) * weights - new_population_index = np.ravel(selector(weighted_scores, k=n_survivors, rng_=rng)) #TODO make it clear that we are concatenating scores... + new_population_index = np.ravel(selector(weighted_scores, k=n_survivors, rng=rng)) #TODO make it clear that we are concatenating scores... new_population = np.array(self.population)[new_population_index] if inplace: - self.set_population(new_population, rng_=rng) + self.set_population(new_population, rng=rng) return new_population - def parent_select(self, selector, weights, columns_names, k, n_parents, rng_=None): - rng = np.random.default_rng(rng_) + def parent_select(self, selector, weights, columns_names, k, n_parents, rng=None): + rng = np.random.default_rng(rng) weighted_scores = self.get_column(self.population, column_names=columns_names) * weights - parents_index = selector(weighted_scores, k=k, n_parents=n_parents, rng_=rng) + parents_index = selector(weighted_scores, k=k, n_parents=n_parents, rng=rng) parents = np.array(self.population)[parents_index] return parents @@ -136,7 +136,7 @@ def remove_invalid_from_population(self, column_names, invalid_value = "INVALID" # returns a list of individuals added to the live population #TODO make keep repeats allow for previously evaluated individuals, #but make sure that the live population only includes one of each, no repeats - def add_to_population(self, individuals: typing.List[BaseIndividual], rng_=None, keep_repeats=False, mutate_until_unique=True): + def add_to_population(self, individuals: typing.List[BaseIndividual], rng=None, keep_repeats=False, mutate_until_unique=True): ''' Add individuals to the live population. Add individuals to the evaluated_individuals if they are not already there. @@ -149,7 +149,7 @@ def add_to_population(self, individuals: typing.List[BaseIndividual], rng_=None, If False, only add individuals that have not yet been added to geneology. ''' - rng = np.random.default_rng(rng_) + rng = np.random.default_rng(rng) if not isinstance(individuals, collections.abc.Iterable): individuals = [individuals] @@ -172,7 +172,7 @@ def add_to_population(self, individuals: typing.List[BaseIndividual], rng_=None, elif mutate_until_unique: #If its old and we don't want repeats, we can optionally mutate it until it is unique for _ in range(20): individual = copy.deepcopy(individual) - individual.mutate(rng_=rng) + individual.mutate(rng=rng) key = individual.unique_id() if key not in self.evaluated_individuals.index: self.evaluated_individuals.loc[key] = np.nan @@ -252,17 +252,17 @@ def get_unevaluated_individuals(self, column_names, individual_list=None): # return self.evaluated_individuals[~self.evaluated_individuals[column_names_to_check].isin(invalid_values).any(axis=1)] #the live population empied and is set to new_population - def set_population(self, new_population, rng_=None, keep_repeats=True): + def set_population(self, new_population, rng=None, keep_repeats=True): ''' sets population to new population for selection? ''' - rng = np.random.default_rng(rng_) + rng = np.random.default_rng(rng) self.population = [] - self.add_to_population(new_population, rng_=rng, keep_repeats=keep_repeats) + self.add_to_population(new_population, rng=rng, keep_repeats=keep_repeats) #TODO should we just generate one offspring per crossover? - def create_offspring(self, parents_list, var_op_list, rng_=None, add_to_population=True, keep_repeats=False, mutate_until_unique=True, n_jobs=1): + def create_offspring(self, parents_list, var_op_list, rng=None, add_to_population=True, keep_repeats=False, mutate_until_unique=True, n_jobs=1): ''' parents_list: a list of lists of parents. var_op_list: a list of var_ops to apply to each list of parents. Should be the same length as parents_list. @@ -280,9 +280,9 @@ def create_offspring(self, parents_list, var_op_list, rng_=None, add_to_populati - "mutate_and_crossover" : mutate_and_crossover - "cross_and_mutate" : cross_and_mutate ''' - rng = np.random.default_rng(rng_) + rng = np.random.default_rng(rng) new_offspring = [] - all_offspring = parallel_create_offspring(parents_list, var_op_list, rng_=rng, n_jobs=n_jobs) + all_offspring = parallel_create_offspring(parents_list, var_op_list, rng=rng, n_jobs=n_jobs) for parents, offspring, var_op in zip(parents_list, all_offspring, var_op_list): @@ -295,7 +295,7 @@ def create_offspring(self, parents_list, var_op_list, rng_=None, add_to_populati # offspring = offspring[0] if add_to_population: - added = self.add_to_population(offspring, rng_=rng, keep_repeats=keep_repeats, mutate_until_unique=mutate_until_unique) + added = self.add_to_population(offspring, rng=rng, keep_repeats=keep_repeats, mutate_until_unique=mutate_until_unique) if len(added) > 0: for new_child in added: parent_keys = [parent.unique_id() for parent in parents] @@ -320,9 +320,9 @@ def create_offspring(self, parents_list, var_op_list, rng_=None, add_to_populati #TODO should we just generate one offspring per crossover? - def create_offspring2(self, parents_list, var_op_list, mutation_functions,mutation_function_weights, crossover_functions,crossover_function_weights, rng_=None, add_to_population=True, keep_repeats=False, mutate_until_unique=True): + def create_offspring2(self, parents_list, var_op_list, mutation_functions,mutation_function_weights, crossover_functions,crossover_function_weights, rng=None, add_to_population=True, keep_repeats=False, mutate_until_unique=True): - rng = np.random.default_rng(rng_) + rng = np.random.default_rng(rng) new_offspring = [] all_offspring = [] @@ -332,29 +332,29 @@ def create_offspring2(self, parents_list, var_op_list, mutation_functions,mutati #TODO put this loop in population class if var_op == "mutate": mutation_op = rng.choice(mutation_functions, p=mutation_function_weights) - all_offspring.append(copy_and_mutate(parents[0], mutation_op, rng_=rng)) + all_offspring.append(copy_and_mutate(parents[0], mutation_op, rng=rng)) chosen_ops.append(mutation_op.__name__) elif var_op == "crossover": crossover_op = rng.choice(crossover_functions, p=crossover_function_weights) - all_offspring.append(copy_and_crossover(parents, crossover_op, rng_=rng)) + all_offspring.append(copy_and_crossover(parents, crossover_op, rng=rng)) chosen_ops.append(crossover_op.__name__) elif var_op == "mutate_then_crossover": mutation_op1 = rng.choice(mutation_functions, p=mutation_function_weights) mutation_op2 = rng.choice(mutation_functions, p=mutation_function_weights) crossover_op = rng.choice(crossover_functions, p=crossover_function_weights) - p1 = copy_and_mutate(parents[0], mutation_op1, rng_=rng) - p2 = copy_and_mutate(parents[1], mutation_op2, rng_=rng) - crossover_op(p1,p2,rng_=rng) + p1 = copy_and_mutate(parents[0], mutation_op1, rng=rng) + p2 = copy_and_mutate(parents[1], mutation_op2, rng=rng) + crossover_op(p1,p2,rng=rng) all_offspring.append(p1) chosen_ops.append(f"{mutation_op1.__name__} , {mutation_op2.__name__} , {crossover_op.__name__}") elif var_op == "crossover_then_mutate": crossover_op = rng.choice(crossover_functions, p=crossover_function_weights) - child = copy_and_crossover(parents, crossover_op, rng_=rng) + child = copy_and_crossover(parents, crossover_op, rng=rng) mutation_op = rng.choice(mutation_functions, p=mutation_function_weights) - mutation_op(child, rng_=rng) + mutation_op(child, rng=rng) all_offspring.append(child) chosen_ops.append(f"{crossover_op.__name__} , {mutation_op.__name__}") @@ -370,7 +370,7 @@ def create_offspring2(self, parents_list, var_op_list, mutation_functions,mutati # offspring = offspring[0] if add_to_population: - added = self.add_to_population(offspring, rng_=rng, keep_repeats=keep_repeats, mutate_until_unique=mutate_until_unique) + added = self.add_to_population(offspring, rng=rng, keep_repeats=keep_repeats, mutate_until_unique=mutate_until_unique) if len(added) > 0: for new_child in added: parent_keys = [parent.unique_id() for parent in parents] @@ -395,56 +395,56 @@ def create_offspring2(self, parents_list, var_op_list, mutation_functions,mutati def get_id(individual): return individual.unique_id() -def parallel_create_offspring(parents_list, var_op_list, rng_=None, n_jobs=1): - rng = np.random.default_rng(rng_) +def parallel_create_offspring(parents_list, var_op_list, rng=None, n_jobs=1): + rng = np.random.default_rng(rng) if n_jobs == 1: - return nonparallel_create_offpring(parents_list, var_op_list, rng_=rng) + return nonparallel_create_offpring(parents_list, var_op_list, rng=rng) else: delayed_offspring = [] for parents, var_op in zip(parents_list,var_op_list): #TODO put this loop in population class if var_op in built_in_var_ops_dict: var_op = built_in_var_ops_dict[var_op] - delayed_offspring.append(dask.delayed(copy_and_change)(parents, var_op, rng_=rng)) + delayed_offspring.append(dask.delayed(copy_and_change)(parents, var_op, rng=rng)) offspring = dask.compute(*delayed_offspring, num_workers=n_jobs, threads_per_worker=1) return offspring -def nonparallel_create_offpring(parents_list, var_op_list, rng_=None, n_jobs=1): - rng = np.random.default_rng(rng_) +def nonparallel_create_offpring(parents_list, var_op_list, rng=None, n_jobs=1): + rng = np.random.default_rng(rng) offspring = [] for parents, var_op in zip(parents_list,var_op_list): #TODO put this loop in population class if var_op in built_in_var_ops_dict: var_op = built_in_var_ops_dict[var_op] - offspring.append(copy_and_change(parents, var_op, rng_=rng)) + offspring.append(copy_and_change(parents, var_op, rng=rng)) return offspring -def copy_and_change(parents, var_op, rng_=None): - rng = np.random.default_rng(rng_) +def copy_and_change(parents, var_op, rng=None): + rng = np.random.default_rng(rng) offspring = copy.deepcopy(parents) - offspring = var_op(offspring, rng_=rng) + offspring = var_op(offspring, rng=rng) if isinstance(offspring, collections.abc.Iterable): offspring = offspring[0] return offspring -def copy_and_mutate(parents, var_op, rng_=None): - rng = np.random.default_rng(rng_) +def copy_and_mutate(parents, var_op, rng=None): + rng = np.random.default_rng(rng) offspring = copy.deepcopy(parents) - var_op(offspring, rng_=rng) + var_op(offspring, rng=rng) if isinstance(offspring, collections.abc.Iterable): offspring = offspring[0] return offspring -def copy_and_crossover(parents, var_op, rng_=None): - rng = np.random.default_rng(rng_) +def copy_and_crossover(parents, var_op, rng=None): + rng = np.random.default_rng(rng) offspring = copy.deepcopy(parents) - var_op(offspring[0],offspring[1], rng_=rng) + var_op(offspring[0],offspring[1], rng=rng) return offspring[0] def parallel_get_id(n_jobs, individual_list): diff --git a/tpot2/search_spaces/__init__.py b/tpot2/search_spaces/__init__.py new file mode 100644 index 00000000..e7478460 --- /dev/null +++ b/tpot2/search_spaces/__init__.py @@ -0,0 +1,4 @@ +from .base import * +from . import nodes +from . import pipelines +from . import templates \ No newline at end of file diff --git a/tpot2/search_spaces/base.py b/tpot2/search_spaces/base.py new file mode 100644 index 00000000..2c91beb6 --- /dev/null +++ b/tpot2/search_spaces/base.py @@ -0,0 +1,34 @@ +import tpot2 +import numpy as np +import pandas as pd +import sklearn +from tpot2 import config +from typing import Generator, List, Tuple, Union +import random +from sklearn.base import BaseEstimator + + +class SklearnIndividual(tpot2.BaseIndividual): + + def __init__(self,) -> None: + super().__init__() + + def mutate(self, rng=None): + return + + def crossover(self, other, rng=None): + return + + def export_pipeline(self) -> BaseEstimator: + return + + def unique_id(self): + return + + +class SklearnIndividualGenerator(): + def __init__(self,): + pass + + def generate(self, rng=None) -> SklearnIndividual: + pass \ No newline at end of file diff --git a/tpot2/search_spaces/nodes/__init__.py b/tpot2/search_spaces/nodes/__init__.py new file mode 100644 index 00000000..35cebf87 --- /dev/null +++ b/tpot2/search_spaces/nodes/__init__.py @@ -0,0 +1,2 @@ +from .estimator_node import * +from .genetic_feature_selection import * \ No newline at end of file diff --git a/tpot2/search_spaces/nodes/estimator_node.py b/tpot2/search_spaces/nodes/estimator_node.py new file mode 100644 index 00000000..e44dc4f1 --- /dev/null +++ b/tpot2/search_spaces/nodes/estimator_node.py @@ -0,0 +1,55 @@ +# try https://automl.github.io/ConfigSpace/main/api/hyperparameters.html +import tpot2 +import numpy as np +import pandas as pd +import sklearn +from tpot2 import config +from typing import Generator, List, Tuple, Union +import random +from ..base import SklearnIndividual, SklearnIndividualGenerator +from ConfigSpace import ConfigurationSpace + +class EstimatorNodeIndividual(SklearnIndividual): + def __init__(self, method: type, + space: ConfigurationSpace, + rng=None) -> None: + super().__init__() + self.method = method + self.space = space + + rng = np.random.default_rng(rng) + self.space.seed(rng.integers(0, 2**32)) + self.hyperparameters = self.space.sample_configuration().get_dictionary() + + def mutate(self, rng=None): + rng = np.random.default_rng(rng) + self.space.seed(rng.integers(0, 2**32)) + self.hyperparameters = self.space.sample_configuration().get_dictionary() + + return True + + def crossover(self, other, rng=None): + rng = np.random.default_rng(rng) + if self.method != other.method: + return False + + #loop through hyperparameters, randomly swap items in self.hyperparameters with items in other.hyperparameters + for hyperparameter in self.space: + if rng.choice([True, False]): + if hyperparameter in other.hyperparameters: + self.hyperparameters[hyperparameter] = other.hyperparameters[hyperparameter] + + def export_pipeline(self, **kwargs): + return self.method(**self.hyperparameters) + + def unique_id(self): + #return a dictionary of the method and the hyperparameters + return (self.method, self.hyperparameters) + +class EstimatorNode(SklearnIndividualGenerator): + def __init__(self, method, space): + self.method = method + self.space = space + + def generate(self, rng=None): + return EstimatorNodeIndividual(self.method, self.space) \ No newline at end of file diff --git a/tpot2/search_spaces/nodes/estimator_node_simple.py b/tpot2/search_spaces/nodes/estimator_node_simple.py new file mode 100644 index 00000000..0d876f0b --- /dev/null +++ b/tpot2/search_spaces/nodes/estimator_node_simple.py @@ -0,0 +1,64 @@ +# try https://automl.github.io/ConfigSpace/main/api/hyperparameters.html +import tpot2 +import numpy as np +import pandas as pd +import sklearn +from tpot2 import config +from typing import Generator, List, Tuple, Union +import random +from ..base import SklearnIndividual, SklearnIndividualGenerator + +class EstimatorNodeIndividual(SklearnIndividual): + def __init__(self, method, space ) -> None: + super().__init__() + self.method = method + self.space = space + + self._mutate_hyperparameters() + + def mutate(self, rng=None): + rng = np.random.default_rng(rng) + return self._mutate_hyperparameters(rng) + + def _mutate_hyperparameters(self, rng=None): + rng = np.random.default_rng(rng) + self.hyperparameters = {} + #sample new hyperparameters from the space + for hyperparameter in self.space: + hyperparameter_space = self.space[hyperparameter] + if isinstance(hyperparameter_space, list): + hp = rng.choice(hyperparameter_space) + elif isinstance(hyperparameter_space, tuple): + hp = rng.uniform(hyperparameter_space[0], hyperparameter_space[1]) + else: + hp = hyperparameter_space + + self.hyperparameters[hyperparameter] = hp + + return True + + def crossover(self, other, rng=None): + rng = np.random.default_rng(rng) + if self.method != other.method: + return False + + #loop through hyperparameters, randomly swap items in self.hyperparameters with items in other.hyperparameters + for hyperparameter in self.space: + if rng.choice([True, False]): + if hyperparameter in other.hyperparameters: + self.hyperparameters[hyperparameter] = other.hyperparameters[hyperparameter] + + def export_pipeline(self, **kwargs): + return self.method(**self.hyperparameters) + + def unique_id(self): + #return a dictionary of the method and the hyperparameters + return (self.method, self.hyperparameters) + +class EstimatorNode(SklearnIndividualGenerator): + def __init__(self, method, space): + self.method = method + self.space = space + + def generate(self, rng=None): + return EstimatorNodeIndividual(self.method, self.space) \ No newline at end of file diff --git a/tpot2/search_spaces/nodes/genetic_feature_selection.py b/tpot2/search_spaces/nodes/genetic_feature_selection.py new file mode 100644 index 00000000..54761123 --- /dev/null +++ b/tpot2/search_spaces/nodes/genetic_feature_selection.py @@ -0,0 +1,178 @@ +from numpy import iterable +import tpot2 +import numpy as np +import sklearn +import sklearn.datasets +import numpy as np + +import pandas as pd +import os, os.path +from sklearn.base import BaseEstimator +from sklearn.feature_selection._base import SelectorMixin + +from ..base import SklearnIndividual, SklearnIndividualGenerator + +class MaskSelector(BaseEstimator, SelectorMixin): + """Select predefined feature subsets.""" + + def __init__(self, mask): + self.mask = mask + + def fit(self, X, y=None): + return self + + def _get_support_mask(self): + return np.array(self.mask) + + +class GeneticFeatureSelectorIndividual(SklearnIndividual): + def __init__( self, + mask, + start_p=0.2, + mutation_rate = 0.5, + crossover_rate = 0.5, + mutation_rate_rate = 0, + crossover_rate_rate = 0, + rng=None, + ): + + self.start_p = start_p + self.mutation_rate = mutation_rate + self.crossover_rate = crossover_rate + self.mutation_rate_rate = mutation_rate_rate + self.crossover_rate_rate = crossover_rate_rate + + rng = np.random.default_rng(rng) + + if isinstance(mask, int): + #list of random bollean values + self.mask = rng.choice([True, False], size=mask, p=[self.start_p,1-self.start_p]) + else: + self.mask = mask + + self.mutation_list = [self._mutate_add, self._mutate_remove] + self.crossover_list = [self._crossover_swap] + + + def mutate(self, rng=None): + rng = np.random.default_rng(rng) + + if rng.uniform() < self.mutation_rate_rate: + self.mutation_rate = self.mutation_rate * rng.uniform(0.5, 2) + self.mutation_rate = min(self.mutation_rate, 2) + self.mutation_rate = max(self.mutation_rate, 1/len(self.mask)) + + return rng.choice(self.mutation_list)(rng) + + def crossover(self, other, rng=None): + rng = np.random.default_rng(rng) + + if rng.uniform() < self.crossover_rate_rate: + self.crossover_rate = self.crossover_rate * rng.uniform(0.5, 2) + self.crossover_rate = min(self.crossover_rate, .6) + self.crossover_rate = max(self.crossover_rate, 1/len(self.mask)) + + return rng.choice(self.crossover_list)(other, rng) + + + # def _mutate_add(self, rng=None): + # rng = np.random.default_rng(rng) + + # add_mask = rng.choice([True, False], size=self.mask.shape, p=[self.mutation_rate,1-self.mutation_rate]) + # self.mask = np.logical_or(self.mask, add_mask) + # return True + + # def _mutate_remove(self, rng=None): + # rng = np.random.default_rng(rng) + + # add_mask = rng.choice([False, True], size=self.mask.shape, p=[self.mutation_rate,1-self.mutation_rate]) + # self.mask = np.logical_and(self.mask, add_mask) + # return True + + def _mutate_add(self, rng=None): + rng = np.random.default_rng(rng) + + num_pos = np.sum(self.mask) + num_neg = len(self.mask) - num_pos + + if num_neg == 0: + return False + + to_add = int(self.mutation_rate * num_pos) + to_add = max(to_add, 1) + + p = to_add / num_neg + p = min(p, 1) + + add_mask = rng.choice([True, False], size=self.mask.shape, p=[p,1-p]) + if sum(np.logical_or(self.mask, add_mask)) == 0: + pass + self.mask = np.logical_or(self.mask, add_mask) + return True + + def _mutate_remove(self, rng=None): + rng = np.random.default_rng(rng) + + num_pos = np.sum(self.mask) + if num_pos == 1: + return False + + num_neg = len(self.mask) - num_pos + + to_remove = int(self.mutation_rate * num_pos) + to_remove = max(to_remove, 1) + + p = to_remove / num_pos + p = min(p, .5) + + remove_mask = rng.choice([True, False], size=self.mask.shape, p=[p,1-p]) + self.mask = np.logical_and(self.mask, remove_mask) + + + if sum(self.mask) == 0: + index = rng.choice(len(self.mask)) + self.mask[index] = True + + return True + + def _crossover_swap(self, ss2, rng=None): + rng = np.random.default_rng(rng) + mask = rng.choice([True, False], size=self.mask.shape, p=[self.crossover_rate,1-self.crossover_rate]) + + self.mask = np.where(mask, self.mask, ss2.mask) + + def export_pipeline(self): + return MaskSelector(mask=self.mask) + + + def unique_id(self): + return self.mask + + +class GeneticFeatureSelectorNode(SklearnIndividualGenerator): + def __init__(self, + mask, + start_p=0.2, + mutation_rate = 0.5, + crossover_rate = 0.5, + mutation_rate_rate = 0, + crossover_rate_rate = 0, + rng=None,): + + self.mask = mask + self.start_p = start_p + self.mutation_rate = mutation_rate + self.crossover_rate = crossover_rate + self.mutation_rate_rate = mutation_rate_rate + self.crossover_rate_rate = crossover_rate_rate + self.rng = rng + + def generate(self, rng=None) -> SklearnIndividual: + return GeneticFeatureSelectorIndividual( mask=self.mask, + start_p=self.start_p, + mutation_rate=self.mutation_rate, + crossover_rate=self.crossover_rate, + mutation_rate_rate=self.mutation_rate_rate, + crossover_rate_rate=self.crossover_rate_rate, + rng=self.rng + ) \ No newline at end of file diff --git a/tpot2/search_spaces/pipelines/__init__.py b/tpot2/search_spaces/pipelines/__init__.py new file mode 100644 index 00000000..ec90eb0e --- /dev/null +++ b/tpot2/search_spaces/pipelines/__init__.py @@ -0,0 +1,6 @@ +from .choice import * +from .dynamic_linear import * +from .sequential import * +from .graph import * +from .tree import * +from .wrapper import * \ No newline at end of file diff --git a/tpot2/search_spaces/pipelines/choice.py b/tpot2/search_spaces/pipelines/choice.py new file mode 100644 index 00000000..cdf0c6d6 --- /dev/null +++ b/tpot2/search_spaces/pipelines/choice.py @@ -0,0 +1,52 @@ +import tpot2 +import numpy as np +import pandas as pd +import sklearn +from tpot2 import config +from typing import Generator, List, Tuple, Union +import random +from ..base import SklearnIndividual, SklearnIndividualGenerator + +class ChoicePipelineIndividual(SklearnIndividual): + def __init__(self, choice_list : List[SklearnIndividualGenerator], rng=None) -> None: + super().__init__() + + self.choice_list = choice_list + self.node = np.random.default_rng(rng).choice(self.choice_list).generate() + + + def mutate(self, rng=None): + rng = np.random.default_rng(rng) + if rng.choice([True, False]): + return self._mutate_select_new_node(rng) + else: + return self._mutate_node(rng) + + def _mutate_select_new_node(self, rng=None): + self.node = random.choice(self.choice_list).generate() + return True + + def _mutate_node(self, rng=None): + return self.node.mutate(rng) + + def crossover(self, other, rng=None): + return self.node.crossover(other.node, rng) + + def export_pipeline(self): + return self.node.export_pipeline() + + def unique_id(self): + return self.node.unique_id() + + +class ChoicePipeline(SklearnIndividualGenerator): + def __init__(self, choice_list : List[SklearnIndividualGenerator] ) -> None: + self.choice_list = choice_list + + """ + Takes in a list of search spaces. Will select one node from the search space. + + """ + + def generate(self, rng=None): + return ChoicePipelineIndividual(self.choice_list) \ No newline at end of file diff --git a/tpot2/search_spaces/pipelines/dynamic_linear.py b/tpot2/search_spaces/pipelines/dynamic_linear.py new file mode 100644 index 00000000..7408fe8b --- /dev/null +++ b/tpot2/search_spaces/pipelines/dynamic_linear.py @@ -0,0 +1,97 @@ +import tpot2 +import numpy as np +import pandas as pd +import sklearn +from tpot2 import config +from typing import Generator, List, Tuple, Union +import random +from ..base import SklearnIndividual, SklearnIndividualGenerator + +import copy + +class DynamicLinearPipelineIndividual(SklearnIndividual): + # takes in a single search space. + # will produce a pipeline of variable length. Each step in the pipeline will be pulled from the search space provided. + + def __init__(self, search_space : SklearnIndividualGenerator, min_length: int, max_length: int ) -> None: + super().__init__() + + rng = np.random.default_rng() + + self.search_space = search_space + self.min_length = min_length + self.max_length = max_length + + self.pipeline = self._generate_pipeline(rng) + + def _generate_pipeline(self, rng=None): + rng = np.random.default_rng() + pipeline = [] + length = rng.integers(self.min_length, self.max_length) + for _ in range(length): + pipeline.append(self.search_space.generate(rng)) + return pipeline + + + def mutate(self, rng=None): + rng = np.random.default_rng() + options = [] + if len(self.pipeline) > self.min_length: + options.append(self._mutate_remove_node) + if len(self.pipeline) < self.max_length: + options.append(self._mutate_add_node) + options.append(self._mutate_step) + + return rng.choice(options)(rng) + + def _mutate_add_node(self, rng=None): + rng = np.random.default_rng() + new_node = self.search_space.generate(rng) + idx = rng.integers(len(self.pipeline)) + self.pipeline.insert(idx, new_node) + + def _mutate_remove_node(self, rng=None): + rng = np.random.default_rng() + idx = rng.integers(len(self.pipeline)) + self.pipeline.pop(idx) + + def _mutate_step(self, rng=None): + #choose a random step in the pipeline and mutate it + rng = np.random.default_rng() + step = rng.choice(self.pipeline) + return step.mutate(rng) + + + def crossover(self, other, rng=None): + rng = np.random.default_rng() + + if len(self.pipeline) < 2 or len(other.pipeline) < 2: + return False + + idx = rng.integers(1,len(self.pipeline)) + idx2 = rng.integers(1,len(other.pipeline)) + self.pipeline[idx:] = copy.deepcopy(other.pipeline[idx2:]) + + return True + + def export_pipeline(self, **graph_pipeline_args): + return [step.export_pipeline(**graph_pipeline_args) for step in self.pipeline] + + def unique_id(self): + return tuple([step.unique_id() for step in self.pipeline]) + + +class DynamicLinearPipeline(SklearnIndividualGenerator): + def __init__(self, search_space : SklearnIndividualGenerator, min_length: int, max_length: int ) -> None: + self.search_space = search_space + self.min_length = min_length + self.max_length = max_length + + """ + Takes in a single search space. Will produce a linear pipeline of variable length. Each step in the pipeline will be pulled from the search space provided. + + + """ + + def generate(self, rng=None): + return DynamicLinearPipelineIndividual(self.search_space, self.min_length, self.max_length) \ No newline at end of file diff --git a/tpot2/search_spaces/pipelines/genetic_sample_weight.py b/tpot2/search_spaces/pipelines/genetic_sample_weight.py new file mode 100644 index 00000000..db731a85 --- /dev/null +++ b/tpot2/search_spaces/pipelines/genetic_sample_weight.py @@ -0,0 +1 @@ +from ..base import SklearnIndividual, SklearnIndividualGenerator \ No newline at end of file diff --git a/tpot2/search_spaces/pipelines/graph.py b/tpot2/search_spaces/pipelines/graph.py new file mode 100644 index 00000000..9332a011 --- /dev/null +++ b/tpot2/search_spaces/pipelines/graph.py @@ -0,0 +1,645 @@ +import tpot2 +import numpy as np +import pandas as pd +import sklearn +from tpot2 import config +from typing import Generator, List, Tuple, Union +import random +from ..base import SklearnIndividual, SklearnIndividualGenerator +import networkx as nx +import copy +import matplotlib.pyplot as plt +import itertools +from .graph_utils import * +from ..nodes.estimator_node import EstimatorNodeIndividual + + +class GraphPipelineIndividual(SklearnIndividual): + def __init__(self, + root_search_space : SklearnIndividualGenerator, + leaf_search_space : SklearnIndividualGenerator = None, + inner_search_space : SklearnIndividualGenerator =None, + max_size: int = 10, + crossover_same_depth=False, + rng=None) -> None: + """ + Generates a tree shaped pipeline individual. Can be used to export a sklearn Pipeline that uses feature unions to merge branches of the pipeline. + + """ + super().__init__() + + self.__debug = False + + rng = np.random.default_rng(rng) + + self.root_search_space = root_search_space + self.leaf_search_space = leaf_search_space + self.inner_search_space = inner_search_space + self.max_size = max_size + self.crossover_same_depth = crossover_same_depth + + self.root = self.root_search_space.generate(rng) + self.graph = nx.DiGraph() + self.graph.add_node(self.root) + + if self.leaf_search_space is not None: + self.leaf = self.leaf_search_space.generate(rng) + self.graph.add_node(self.leaf) + self.graph.add_edge(self.root, self.leaf) + + self.mutate_methods_list = [self._mutate_insert_leaf, self._mutate_insert_inner_node, self._mutate_remove_node, self._mutate_node] + self.crossover_methods_list = [self._crossover_swap_branch, self._crossover_swap_node, self._crossover_take_branch] #TODO self._crossover_nodes, + + self.merge_duplicated_nodes_toggle = True + + + def mutate(self, rng=None): + rng = np.random.default_rng(rng) + self.key = None + + rng.shuffle(self.mutate_methods_list) + for mutate_method in self.mutate_methods_list: + if mutate_method(rng=rng): + + if self.merge_duplicated_nodes_toggle: + self._merge_duplicated_nodes() + + if self.__debug: + print(mutate_method) + + if self.root not in self.graph.nodes: + print('lost root something went wrong with ', mutate_method) + + if len(self.graph.predecessors(self.root)) > 0: + print('root has parents ', mutate_method) + + if any([n in nx.ancestors(self.graph,n) for n in self.graph.nodes]): + print('a node is connecting to itself...') + + if self.__debug: + try: + nx.find_cycle(self.graph) + print('something went wrong with ', mutate_method) + except: + pass + + return True + + return False + + + + + def _mutate_insert_leaf(self, rng=None): + rng = np.random.default_rng(rng) + if self.max_size > self.graph.number_of_nodes(): + sorted_nodes_list = list(self.graph.nodes) + rng.shuffle(sorted_nodes_list) #TODO: sort by number of children and/or parents? bias model one way or another + for node in sorted_nodes_list: + #if leafs are protected, check if node is a leaf + #if node is a leaf, skip because we don't want to add node on top of node + if (self.leaf_search_space is not None #if leafs are protected + and len(list(self.graph.successors(node))) == 0 #if node is leaf + and len(list(self.graph.predecessors(node))) > 0 #except if node is root, in which case we want to add a leaf even if it happens to be a leaf too + ): + + continue + + #If node *is* the root or is not a leaf, add leaf node. (dont want to add leaf on top of leaf) + if self.leaf_search_space is not None: + new_node = self.leaf_search_space.generate(rng) + else: + new_node = self.inner_search_space.generate(rng) + + self.graph.add_node(new_node) + self.graph.add_edge(node, new_node) + return True + + return False + + def _mutate_insert_inner_node(self, rng=None): + rng = np.random.default_rng(rng) + if self.max_size > self.graph.number_of_nodes(): + sorted_nodes_list = list(self.graph.nodes) + sorted_nodes_list2 = list(self.graph.nodes) + rng.shuffle(sorted_nodes_list) #TODO: sort by number of children and/or parents? bias model one way or another + rng.shuffle(sorted_nodes_list2) + for node in sorted_nodes_list: + #loop through children of node + for child_node in list(self.graph.successors(node)): + + if child_node is not node and child_node not in nx.ancestors(self.graph, node): + if self.leaf_search_space is not None: + #If if we are protecting leafs, dont add connection into a leaf + if len(list(nx.descendants(self.graph,node))) ==0 : + continue + + new_node = self.inner_search_space.generate(rng) + + self.graph.add_node(new_node) + self.graph.add_edges_from([(node, new_node), (new_node, child_node)]) + self.graph.remove_edge(node, child_node) + return True + + return False + + + def _mutate_remove_node(self, rng=None): + ''' + Removes a randomly chosen node and connects its parents to its children. + If the node is the only leaf for an inner node and 'leaf_search_space' is not none, we do not remove it. + ''' + rng = np.random.default_rng(rng) + nodes_list = list(self.graph.nodes) + nodes_list.remove(self.root) + leaves = get_leaves(self.graph) + + while len(nodes_list) > 0: + node = rng.choice(nodes_list) + nodes_list.remove(node) + + if self.leaf_search_space is not None and len(list(nx.descendants(self.graph,node))) == 0 : #if the node is a leaf + if len(leaves) <= 1: + continue #dont remove the last leaf + leaf_parents = self.graph.predecessors(node) + + # if any of the parents of the node has one one child, continue + if any([len(list(self.graph.successors(lp))) < 2 for lp in leaf_parents]): #dont remove a leaf if it is the only input into another node. + continue + + remove_and_stitch(self.graph, node) + remove_nodes_disconnected_from_node(self.graph, self.root) + return True + + else: + remove_and_stitch(self.graph, node) + remove_nodes_disconnected_from_node(self.graph, self.root) + return True + + return False + + + + def _mutate_node(self, rng=None): + ''' + Mutates the hyperparameters for a randomly chosen node in the graph. + ''' + rng = np.random.default_rng(rng) + sorted_nodes_list = list(self.graph.nodes) + rng.shuffle(sorted_nodes_list) + completed_one = False + for node in sorted_nodes_list: + if node.mutate(rng): + return True + return False + + def _mutate_remove_edge(self, rng=None): + ''' + Deletes an edge as long as deleting that edge does not make the graph disconnected. + ''' + rng = np.random.default_rng(rng) + sorted_nodes_list = list(self.graph.nodes) + rng.shuffle(sorted_nodes_list) + for child_node in sorted_nodes_list: + parents = list(self.graph.predecessors(child_node)) + if len(parents) > 1: # if it has more than one parent, you can remove an edge (if this is the only child of a node, it will become a leaf) + + for parent_node in parents: + # if removing the egde will make the parent_node a leaf node, skip + if self.leaf_search_space is not None and len(list(self.graph.successors(parent_node))) < 2: + continue + + self.graph.remove_edge(parent_node, child_node) + return True + return False + + def _mutate_add_edge(self, rng=None): + ''' + Randomly add an edge from a node to another node that is not an ancestor of the first node. + ''' + rng = np.random.default_rng(rng) + sorted_nodes_list = list(self.graph.nodes) + rng.shuffle(sorted_nodes_list) + for child_node in sorted_nodes_list: + for parent_node in sorted_nodes_list: + if self.leaf_search_space is not None: + if len(list(self.graph.successors(parent_node))) == 0: + continue + + # skip if + # - parent and child are the same node + # - edge already exists + # - child is an ancestor of parent + if (child_node is not parent_node) and not self.graph.has_edge(parent_node,child_node) and (child_node not in nx.ancestors(self.graph, parent_node)): + self.graph.add_edge(parent_node,child_node) + return True + + return False + + def _mutate_insert_bypass_node(self, rng=None): + rng = np.random.default_rng(rng) + if self.max_size > self.graph.number_of_nodes(): + sorted_nodes_list = list(self.graph.nodes) + sorted_nodes_list2 = list(self.graph.nodes) + rng.shuffle(sorted_nodes_list) #TODO: sort by number of children and/or parents? bias model one way or another + rng.shuffle(sorted_nodes_list2) + for node in sorted_nodes_list: + for child_node in sorted_nodes_list2: + if child_node is not node and child_node not in nx.ancestors(self.graph, node): + if self.leaf_search_space is not None: + #If if we are protecting leafs, dont add connection into a leaf + if len(list(nx.descendants(self.graph,node))) ==0 : + continue + + new_node = self.inner_search_space.generate(rng) + + self.graph.add_node(new_node) + self.graph.add_edges_from([(node, new_node), (new_node, child_node)]) + return True + + return False + + + def crossover(self, ind2, rng=None): + ''' + self is the first individual, ind2 is the second individual + If crossover_same_depth, it will select graphindividuals at the same recursive depth. + Otherwise, it will select graphindividuals randomly from the entire graph and its subgraphs. + + This does not impact graphs without subgraphs. And it does not impacts nodes that are not graphindividuals. Cros + ''' + + rng = np.random.default_rng(rng) + + rng.shuffle(self.crossover_methods_list) + + finished = False + + for crossover_method in self.crossover_methods_list: + if crossover_method(ind2, rng=rng): + self._merge_duplicated_nodes() + finished = True + break + + if self.__debug: + try: + nx.find_cycle(self.graph) + print('something went wrong with ', crossover_method) + except: + pass + + return finished + + + def _crossover_swap_branch(self, G2, rng=None): + ''' + swaps a branch from parent1 with a branch from parent2. does not modify parent2 + ''' + rng = np.random.default_rng(rng) + + if self.crossover_same_depth: + pair_gen = select_nodes_same_depth(self.graph, self.root, G2.graph, G2.root, rng=rng) + else: + pair_gen = select_nodes_randomly(self.graph, G2.graph, rng=rng) + + for node1, node2 in pair_gen: + #TODO: if root is in inner_config_dict, then do use it? + if node1 is self.root or node2 is G2.root: #dont want to add root as inner node + continue + + #check if node1 is a leaf and leafs are protected, don't add an input to the leave + if self.leaf_search_space is not None: #if we are protecting leaves, + node1_is_leaf = len(list(self.graph.successors(node1))) == 0 + node2_is_leaf = len(list(G2.graph.successors(node2))) == 0 + #if not ((node1_is_leaf and node1_is_leaf) or (not node1_is_leaf and not node2_is_leaf)): #if node1 is a leaf + if (node1_is_leaf and (not node2_is_leaf)) or ( (not node1_is_leaf) and node2_is_leaf): + #only continue if node1 and node2 are both leaves or both not leaves + continue + + temp_graph_1 = self.graph.copy() + temp_graph_1.remove_node(node1) + remove_nodes_disconnected_from_node(temp_graph_1, self.root) + + #isolating the branch + branch2 = G2.graph.copy() + n2_descendants = nx.descendants(branch2,node2) + for n in list(branch2.nodes): + if n not in n2_descendants and n is not node2: #removes all nodes not in the branch + branch2.remove_node(n) + + branch2 = copy.deepcopy(branch2) + branch2_root = get_roots(branch2)[0] + temp_graph_1.add_edges_from(branch2.edges) + for p in list(self.graph.predecessors(node1)): + temp_graph_1.add_edge(p,branch2_root) + + if temp_graph_1.number_of_nodes() > self.max_size: + continue + + self.graph = temp_graph_1 + + return True + return False + + + def _crossover_take_branch(self, G2, rng=None): + ''' + Takes a subgraph from Parent2 and add it to a randomly chosen node in Parent1. + ''' + rng = np.random.default_rng(rng) + + if self.crossover_same_depth: + pair_gen = select_nodes_same_depth(self.graph, self.root, G2.graph, G2.root, rng=rng) + else: + pair_gen = select_nodes_randomly(self.graph, G2.graph, rng=rng) + + for node1, node2 in pair_gen: + #TODO: if root is in inner_config_dict, then do use it? + if node2 is G2.root: #dont want to add root as inner node + continue + + + #check if node1 is a leaf and leafs are protected, don't add an input to the leave + if self.leaf_search_space is not None and len(list(self.graph.successors(node1))) == 0: + continue + + #icheck if node2 is graph individual + # if isinstance(node2,GraphIndividual): + # if not ((isinstance(node2,GraphIndividual) and ("Recursive" in self.inner_config_dict or "Recursive" in self.leaf_search_space))): + # continue + + #isolating the branch + branch2 = G2.graph.copy() + n2_descendants = nx.descendants(branch2,node2) + for n in list(branch2.nodes): + if n not in n2_descendants and n is not node2: #removes all nodes not in the branch + branch2.remove_node(n) + + #if node1 plus node2 branch has more than max_children, skip + if branch2.number_of_nodes() + self.graph.number_of_nodes() > self.max_size: + continue + + branch2 = copy.deepcopy(branch2) + branch2_root = get_roots(branch2)[0] + self.graph.add_edges_from(branch2.edges) + self.graph.add_edge(node1,branch2_root) + + return True + return False + + + + def _crossover_nodes(self, G2, rng=None): + ''' + Swaps the hyperparamters of one randomly chosen node in Parent1 with the hyperparameters of randnomly chosen node in Parent2. + ''' + rng = np.random.default_rng(rng) + + if self.crossover_same_depth: + pair_gen = select_nodes_same_depth(self.graph, self.root, G2.graph, G2.root, rng=rng) + else: + pair_gen = select_nodes_randomly(self.graph, G2.graph, rng=rng) + + for node1, node2 in pair_gen: + + #if both nodes are leaves + if len(list(self.graph.successors(node1)))==0 and len(list(G2.graph.successors(node2)))==0: + + try: + if node1.crossover(node2): + return True + except: + pass + + #if both nodes are inner nodes + if len(list(self.graph.successors(node1)))>0 and len(list(G2.graph.successors(node2)))>0: + if len(list(self.graph.predecessors(node1)))>0 and len(list(G2.graph.predecessors(node2))): + if node1.crossover(node2): + return True + + #if both nodes are root nodes + if node1 is self.root and node2 is G2.root: + if node1.crossover(node2): + return True + + + return False + + #not including the nodes, just their children + #Finds leaves attached to nodes and swaps them + def _crossover_swap_leaf_at_node(self, G2, rng=None): + rng = np.random.default_rng(rng) + + if self.crossover_same_depth: + pair_gen = select_nodes_same_depth(self.graph, self.root, G2.graph, G2.root, rng=rng) + else: + pair_gen = select_nodes_randomly(self.graph, G2.graph, rng=rng) + + success = False + for node1, node2 in pair_gen: + # if leaves are protected node1 and node2 must both be leaves or both be inner nodes + if self.leaf_search_space is not None and not (len(list(self.graph.successors(node1)))==0 ^ len(list(G2.graph.successors(node2)))==0): + continue + #self_leafs = [c for c in nx.descendants(self.graph,node1) if len(list(self.graph.successors(c)))==0 and c is not node1] + node_leafs = [c for c in nx.descendants(G2.graph,node2) if len(list(G2.graph.successors(c)))==0 and c is not node2] + + # if len(self_leafs) >0: + # for c in self_leafs: + # if random.choice([True,False]): + # self.graph.remove_node(c) + # G2.graph.add_edge(node2, c) + # success = True + + if len(node_leafs) >0: + for c in node_leafs: + if rng.choice([True,False]): + G2.graph.remove_node(c) + self.graph.add_edge(node1, c) + success = True + + return success + + + + + def _crossover_swap_node(self, G2, rng=None): + ''' + Swaps randomly chosen node from Parent1 with a randomly chosen node from Parent2. + ''' + rng = np.random.default_rng(rng) + + if self.crossover_same_depth: + pair_gen = select_nodes_same_depth(self.graph, self.root, G2.graph, G2.root, rng=rng) + else: + pair_gen = select_nodes_randomly(self.graph, G2.graph, rng=rng) + + for node1, node2 in pair_gen: + if node1 is self.root or node2 is G2.root: #TODO: allow root + continue + + #if leaves are protected + if self.leaf_search_space is not None: + #if one node is a leaf, the other must be a leaf + if not((len(list(self.graph.successors(node1)))==0) ^ (len(list(G2.graph.successors(node2)))==0)): + continue #only continue if both are leaves, or both are not leaves + + + n1_s = self.graph.successors(node1) + n1_p = self.graph.predecessors(node1) + + n2_s = G2.graph.successors(node2) + n2_p = G2.graph.predecessors(node2) + + self.graph.remove_node(node1) + G2.graph.remove_node(node2) + + self.graph.add_node(node2) + + self.graph.add_edges_from([ (node2, n) for n in n1_s]) + G2.graph.add_edges_from([ (node1, n) for n in n2_s]) + + self.graph.add_edges_from([ (n, node2) for n in n1_p]) + G2.graph.add_edges_from([ (n, node1) for n in n2_p]) + + return True + + return False + + + def _merge_duplicated_nodes(self): + + graph_changed = False + merged = False + while(not merged): + node_list = list(self.graph.nodes) + merged = True + for node, other_node in itertools.product(node_list, node_list): + if node is other_node or (not isinstance(node, EstimatorNodeIndividual)) or (not isinstance(other_node, EstimatorNodeIndividual)): #TODO make this account for other types of nodes. maybe add a __eq__ method to the nodes + continue + + #If nodes are same class/hyperparameters + if node.method == other_node.method and node.hyperparameters == other_node.hyperparameters: + node_children = set(self.graph.successors(node)) + other_node_children = set(self.graph.successors(other_node)) + #if nodes have identical children, they can be merged + if node_children == other_node_children: + for other_node_parent in list(self.graph.predecessors(other_node)): + if other_node_parent not in self.graph.predecessors(node): + self.graph.add_edge(other_node_parent,node) + + self.graph.remove_node(other_node) + merged=False + graph_changed = True + break + + return graph_changed + + + def export_pipeline(self, **graph_pipeline_args): + estimator_graph = self.graph.copy() + + #mapping = {node:node.method_class(**node.hyperparameters) for node in estimator_graph} + label_remapping = {} + label_to_instance = {} + + for node in estimator_graph: + this_pipeline_node = node.export_pipeline() + found_unique_label = False + i=1 + while not found_unique_label: + label = "{0}_{1}".format(this_pipeline_node.__class__.__name__, i) + if label not in label_to_instance: + found_unique_label = True + else: + i+=1 + + label_remapping[node] = label + label_to_instance[label] = this_pipeline_node + + estimator_graph = nx.relabel_nodes(estimator_graph, label_remapping) + + for label, instance in label_to_instance.items(): + estimator_graph.nodes[label]["instance"] = instance + + return tpot2.GraphPipeline(graph=estimator_graph, **graph_pipeline_args) + + + def plot(self): + G = self.graph.reverse() + #TODO clean this up + try: + pos = nx.planar_layout(G) # positions for all nodes + except: + pos = nx.shell_layout(G) + # nodes + options = {'edgecolors': 'tab:gray', 'node_size': 800, 'alpha': 0.9} + nodelist = list(G.nodes) + node_color = [plt.cm.Set1(G.nodes[n]['recursive depth']) for n in G] + + fig, ax = plt.subplots() + + nx.draw(G, pos, nodelist=nodelist, node_color=node_color, ax=ax, **options) + + + '''edgelist = [] + for n in n1.node_set: + for child in n.children: + edgelist.append((n,child))''' + + # edges + #nx.draw_networkx_edges(G, pos, width=3.0, arrows=True) + '''nx.draw_networkx_edges( + G, + pos, + edgelist=[edgelist], + width=8, + alpha=0.5, + edge_color='tab:red', + )''' + + + + # some math labels + labels = {} + for i, n in enumerate(G.nodes): + labels[n] = n.method_class.__name__ + "\n" + str(n.hyperparameters) + + + nx.draw_networkx_labels(G, pos, labels,ax=ax, font_size=7, font_color='black') + + plt.tight_layout() + plt.axis('off') + plt.show() + + + + + + + def unique_id(self): + return + + +class GraphPipeline(SklearnIndividualGenerator): + def __init__(self, root_search_space : SklearnIndividualGenerator, + leaf_search_space : SklearnIndividualGenerator = None, + inner_search_space : SklearnIndividualGenerator =None, + max_size: int = 10, + crossover_same_depth=False, + rng=None) -> None: + + """ + Generates a directed acyclic graph of variable size. Search spaces for root, leaf, and inner nodes can be defined separately if desired. + + + """ + + + self.search_space = root_search_space + self.leaf_search_space = leaf_search_space + self.inner_search_space = inner_search_space + self.max_size = max_size + self.crossover_same_depth = crossover_same_depth + + def generate(self, rng=None): + return GraphPipelineIndividual(self.search_space, self.leaf_search_space, self.inner_search_space, self.max_size, self.crossover_same_depth, rng=rng) \ No newline at end of file diff --git a/tpot2/individual_representations/graph_pipeline_individual/graph_utils/graph_utils.py b/tpot2/search_spaces/pipelines/graph_utils.py similarity index 93% rename from tpot2/individual_representations/graph_pipeline_individual/graph_utils/graph_utils.py rename to tpot2/search_spaces/pipelines/graph_utils.py index 1956d49d..2470a07b 100644 --- a/tpot2/individual_representations/graph_pipeline_individual/graph_utils/graph_utils.py +++ b/tpot2/search_spaces/pipelines/graph_utils.py @@ -55,8 +55,8 @@ def invert_dictionary(d): return inv_map -def select_nodes_same_depth(g1, node1, g2, node2, rng_=None): - rng = np.random.default_rng(rng_) +def select_nodes_same_depth(g1, node1, g2, node2, rng=None): + rng = np.random.default_rng(rng) g1_nodes = nx.shortest_path_length(g1, source=node1) g2_nodes = nx.shortest_path_length(g2, source=node2) @@ -86,8 +86,8 @@ def select_nodes_same_depth(g1, node1, g2, node2, rng_=None): for p in possible_pairs: yield p[0], p[1] -def select_nodes_randomly(g1, g2, rng_=None): - rng = np.random.default_rng(rng_) +def select_nodes_randomly(g1, g2, rng=None): + rng = np.random.default_rng(rng) sorted_self_nodes_list = list(g1.nodes) rng.shuffle(sorted_self_nodes_list) diff --git a/tpot2/search_spaces/pipelines/hierarchical_individual.py b/tpot2/search_spaces/pipelines/hierarchical_individual.py new file mode 100644 index 00000000..db731a85 --- /dev/null +++ b/tpot2/search_spaces/pipelines/hierarchical_individual.py @@ -0,0 +1 @@ +from ..base import SklearnIndividual, SklearnIndividualGenerator \ No newline at end of file diff --git a/tpot2/search_spaces/pipelines/sequential.py b/tpot2/search_spaces/pipelines/sequential.py new file mode 100644 index 00000000..4459a284 --- /dev/null +++ b/tpot2/search_spaces/pipelines/sequential.py @@ -0,0 +1,62 @@ +import tpot2 +import numpy as np +import pandas as pd +import sklearn +from tpot2 import config +from typing import Generator, List, Tuple, Union +import random +from ..base import SklearnIndividual, SklearnIndividualGenerator + +class SequentialPipelineIndividual(SklearnIndividual): + # takes in a list of search spaces. each space is a list of SklearnIndividualGenerators. + # will produce a pipeline of Sequential length. Each step in the pipeline will correspond to the the search space provided in the same index. + + def __init__(self, search_spaces : List[SklearnIndividualGenerator] ) -> None: + super().__init__() + self.search_spaces = search_spaces + self.pipeline = self._generate_pipeline() + + def _generate_pipeline(self, rng=None): + pipeline = [] + for space in self.search_spaces: + pipeline.append(space.generate(rng)) + return pipeline + + def mutate(self, rng=None): + rng = np.random.default_rng() + step = rng.choice(self.pipeline) + return step.mutate(rng) + + + def crossover(self, other, rng=None): + #swap a random step in the pipeline with the corresponding step in the other pipeline + + if len(self.pipeline) != len(other.pipeline): + return False + + if len(self.pipeline) < 2: + return False + + rng = np.random.default_rng() + idx = rng.integers(1,len(self.pipeline)) + + self.pipeline[idx], other.pipeline[idx] = other.pipeline[idx], self.pipeline[idx] + return True + + def export_pipeline(self): + return sklearn.pipeline.make_pipeline(*[step.export_pipeline() for step in self.pipeline]) + + def unique_id(self): + return tuple([step.unique_id() for step in self.pipeline]) + + +class SequentialPipeline(SklearnIndividualGenerator): + def __init__(self, search_spaces : List[SklearnIndividualGenerator] ) -> None: + """ + Takes in a list of search spaces. will produce a pipeline of Sequential length. Each step in the pipeline will correspond to the the search space provided in the same index. + """ + + self.search_spaces = search_spaces + + def generate(self, rng=None): + return SequentialPipelineIndividual(self.search_spaces) \ No newline at end of file diff --git a/tpot2/search_spaces/pipelines/tree.py b/tpot2/search_spaces/pipelines/tree.py new file mode 100644 index 00000000..de4c2aef --- /dev/null +++ b/tpot2/search_spaces/pipelines/tree.py @@ -0,0 +1,50 @@ +import tpot2 +import numpy as np +import pandas as pd +import sklearn +from tpot2 import config +from typing import Generator, List, Tuple, Union +import random +from ..base import SklearnIndividual, SklearnIndividualGenerator +import networkx as nx +import copy +import matplotlib.pyplot as plt + +from .graph import GraphPipelineIndividual, GraphPipeline + + +from .graph_utils import * + +class TreePipelineIndividual(GraphPipelineIndividual): + def __init__(self, + **kwargs) -> None: + super().__init__(**kwargs) + + self.crossover_methods_list = [self._crossover_swap_branch, self._crossover_swap_node, self._crossover_nodes] + self.mutate_methods_list = [self._mutate_insert_leaf, self._mutate_insert_inner_node, self._mutate_remove_node, self._mutate_node] + + + +class TreePipeline(SklearnIndividualGenerator): + def __init__(self, root_search_space : SklearnIndividualGenerator, + leaf_search_space : SklearnIndividualGenerator = None, + inner_search_space : SklearnIndividualGenerator =None, + min_size: int = 2, + max_size: int = 10, + crossover_same_depth=False, + rng=None) -> None: + + """ + Generates a pipeline of variable length. Pipeline will have a tree structure similar to TPOT1. + + """ + + self.search_space = root_search_space + self.leaf_search_space = leaf_search_space + self.inner_search_space = inner_search_space + self.min_size = min_size + self.max_size = max_size + self.crossover_same_depth = crossover_same_depth + + def generate(self, rng=None): + return TreePipelineIndividual(self.search_space, self.leaf_search_space, self.inner_search_space, self.min_size, self.max_size, self.crossover_same_depth, rng=rng) \ No newline at end of file diff --git a/tpot2/search_spaces/pipelines/wrapper.py b/tpot2/search_spaces/pipelines/wrapper.py new file mode 100644 index 00000000..3521d8dd --- /dev/null +++ b/tpot2/search_spaces/pipelines/wrapper.py @@ -0,0 +1,84 @@ + +import numpy as np +import pandas as pd +import sklearn +from tpot2 import config +from typing import Generator, List, Tuple, Union +import random +from ..base import SklearnIndividual, SklearnIndividualGenerator +from ConfigSpace import ConfigurationSpace + + +class WrapperPipelineIndividual(SklearnIndividual): + def __init__(self, + nodegen: SklearnIndividualGenerator, + method: type, + space: ConfigurationSpace, + rng=None) -> None: + + + + super().__init__() + + self.nodegen = nodegen + self.node = np.random.default_rng(rng).choice(self.nodegen).generate() + + + self.method = method + self.space = space + rng = np.random.default_rng(rng) + self.space.seed(rng.integers(0, 2**32)) + self.hyperparameters = self.space.sample_configuration().get_dictionary() + + + + + def mutate(self, rng=None): + rng = np.random.default_rng(rng) + if rng.choice([True, False]): + return self._mutate_hyperparameters(rng) + else: + return self._mutate_node(rng) + + def _mutate_hyperparameters(self, rng=None): + rng = np.random.default_rng(rng) + self.space.seed(rng.integers(0, 2**32)) + self.hyperparameters = self.space.sample_configuration().get_dictionary() + return True + + def _mutate_node(self, rng=None): + return self.node.mutate(rng) + + def crossover(self, other, rng=None): + return self.node.crossover(other.node, rng) + + def export_pipeline(self): + + est = self.node.export_pipeline() + wrapped_est = self.method(est, **self.hyperparameters) + return wrapped_est + + + def unique_id(self): + return self.node.unique_id() + + +class WrapperPipeline(SklearnIndividualGenerator): + def __init__(self, nodegen: SklearnIndividualGenerator, + method: type, + space: ConfigurationSpace, + ) -> None: + + """ + This search space is for wrapping a sklearn estimator with a method that takes another estimator and hyperparameters as arguments. + For example, this can be used with sklearn.ensemble.BaggingClassifier or sklearn.ensemble.AdaBoostClassifier. + + """ + + + self.nodegen = nodegen + self.method = method + self.space = space + + def generate(self, rng=None): + return WrapperPipelineIndividual(self.nodegen, self.method, self.space, rng) \ No newline at end of file diff --git a/tpot2/individual_representations/graph_pipeline_individual/test/__init__.py b/tpot2/search_spaces/templates/__init__.py similarity index 100% rename from tpot2/individual_representations/graph_pipeline_individual/test/__init__.py rename to tpot2/search_spaces/templates/__init__.py diff --git a/tpot2/search_spaces/templates/autoqtl.py b/tpot2/search_spaces/templates/autoqtl.py new file mode 100644 index 00000000..e69de29b diff --git a/tpot2/search_spaces/templates/stc.py b/tpot2/search_spaces/templates/stc.py new file mode 100644 index 00000000..e69de29b diff --git a/tpot2/selectors/lexicase_selection.py b/tpot2/selectors/lexicase_selection.py index 0afe1f34..cf3be98f 100644 --- a/tpot2/selectors/lexicase_selection.py +++ b/tpot2/selectors/lexicase_selection.py @@ -1,6 +1,6 @@ import numpy as np -def lexicase_selection(scores, k, rng_=None, n_parents=1,): +def lexicase_selection(scores, k, rng=None, n_parents=1,): """Select the best individual according to Lexicase Selection, *k* times. The returned list contains the indices of the chosen *individuals*. :param scores: The score matrix, where rows the individulas and the columns are the corresponds to scores on different objectives. @@ -8,7 +8,7 @@ def lexicase_selection(scores, k, rng_=None, n_parents=1,): This function uses the :func:`~random.choice` function from the python base :mod:`random` module. """ - rng = np.random.default_rng(rng_) + rng = np.random.default_rng(rng) chosen =[] for i in range(k*n_parents): candidates = list(range(len(scores))) diff --git a/tpot2/selectors/max_weighted_average_selector.py b/tpot2/selectors/max_weighted_average_selector.py index d142bafd..61848723 100644 --- a/tpot2/selectors/max_weighted_average_selector.py +++ b/tpot2/selectors/max_weighted_average_selector.py @@ -1,6 +1,6 @@ import numpy as np -def max_weighted_average_selector(scores,k, rng_=None, n_parents=1,): +def max_weighted_average_selector(scores,k, rng=None, n_parents=1,): ave_scores = [np.nanmean(s ) for s in scores ] #TODO make this more efficient chosen = np.argsort(ave_scores)[::-1][0:k] #TODO check this behavior with nans return np.reshape(chosen, (k, n_parents)) \ No newline at end of file diff --git a/tpot2/selectors/nsgaii.py b/tpot2/selectors/nsgaii.py index bb7bf76d..d708267f 100644 --- a/tpot2/selectors/nsgaii.py +++ b/tpot2/selectors/nsgaii.py @@ -87,7 +87,7 @@ def crowding_distance(matrix): -def survival_select_NSGA2(scores, k, rng_=None): +def survival_select_NSGA2(scores, k, rng=None): pareto_fronts = nondominated_sorting(scores) diff --git a/tpot2/selectors/random_selector.py b/tpot2/selectors/random_selector.py index 54b37978..7812396d 100644 --- a/tpot2/selectors/random_selector.py +++ b/tpot2/selectors/random_selector.py @@ -1,6 +1,6 @@ import numpy as np -def random_selector(scores, k, rng_=None, n_parents=1, ): - rng = np.random.default_rng(rng_) +def random_selector(scores, k, rng=None, n_parents=1, ): + rng = np.random.default_rng(rng) chosen = rng.choice(list(range(0,len(scores))), size=k*n_parents) return np.reshape(chosen, (k, n_parents)) \ No newline at end of file diff --git a/tpot2/selectors/tournament_selection.py b/tpot2/selectors/tournament_selection.py index a715a9dd..74a31742 100644 --- a/tpot2/selectors/tournament_selection.py +++ b/tpot2/selectors/tournament_selection.py @@ -1,6 +1,6 @@ import numpy as np -def tournament_selection(scores, k, rng_=None, n_parents=1, tournament_size=2, score_index=0): +def tournament_selection(scores, k, rng=None, n_parents=1, tournament_size=2, score_index=0): """Select the best individual among *tournsize* randomly chosen individuals, *k* times. The returned list contains the indices of the chosen *individuals*. :param scores: The score matrix, where rows the individulas and the columns are the corresponds to scores on different objectives. @@ -12,7 +12,7 @@ def tournament_selection(scores, k, rng_=None, n_parents=1, tournament_size=2, s :mod:`random` module. """ - rng = np.random.default_rng(rng_) + rng = np.random.default_rng(rng) if isinstance(score_index,int): key=lambda x:x[1][score_index] diff --git a/tpot2/selectors/tournament_selection_dominated.py b/tpot2/selectors/tournament_selection_dominated.py index 74556894..90ec371e 100644 --- a/tpot2/selectors/tournament_selection_dominated.py +++ b/tpot2/selectors/tournament_selection_dominated.py @@ -3,7 +3,7 @@ from.nsgaii import nondominated_sorting, crowding_distance, dominates #based on deap -def tournament_selection_dominated(scores, k, rng_=None, n_parents=2): +def tournament_selection_dominated(scores, k, rng=None, n_parents=2): """Select the best individual among *tournsize* randomly chosen individuals, *k* times. The returned list contains the indices of the chosen *individuals*. :param scores: The score matrix, where rows the individulas and the columns are the corresponds to scores on different objectives. @@ -15,7 +15,7 @@ def tournament_selection_dominated(scores, k, rng_=None, n_parents=2): :mod:`random` module. """ - rng = np.random.default_rng(rng_) + rng = np.random.default_rng(rng) pareto_fronts = nondominated_sorting(scores) # chosen = list(itertools.chain.from_iterable(fronts)) diff --git a/tpot2/tpot_estimator/estimator.py b/tpot2/tpot_estimator/estimator.py index 060539c7..50dfa6e0 100644 --- a/tpot2/tpot_estimator/estimator.py +++ b/tpot2/tpot_estimator/estimator.py @@ -38,14 +38,9 @@ def __init__(self, scorers, objective_function_names = None, bigger_is_better = True, - hyperparameter_probability = 1, - hyper_node_probability = 0, - hyperparameter_alpha = 1, - max_size = np.inf, - linear_pipeline = False, - root_config_dict= 'Auto', - inner_config_dict=["selectors", "transformers"], - leaf_config_dict= None, + search_space = None, + + cross_val_predict_cv = 0, categorical_features = None, subsets = None, @@ -101,7 +96,6 @@ def __init__(self, scorers, #debugging and logging parameters warm_start = False, - subset_column = None, periodic_checkpoint_folder = None, callback = None, @@ -148,69 +142,6 @@ def __init__(self, scorers, If True, the objective function is maximized. If False, the objective function is minimized. Use negative weights to reverse the direction. - max_size : int, default=np.inf - The maximum number of nodes of the pipelines to be generated. - - linear_pipeline : bool, default=False - If True, the pipelines generated will be linear. If False, the pipelines generated will be directed acyclic graphs. - - root_config_dict : dict, default='auto' - The configuration dictionary to use for the root node of the model. - If 'auto', will use "classifiers" if classification=True, else "regressors". - - 'selectors' : A selection of sklearn Selector methods. - - 'classifiers' : A selection of sklearn Classifier methods. - - 'regressors' : A selection of sklearn Regressor methods. - - 'transformers' : A selection of sklearn Transformer methods. - - 'arithmetic_transformer' : A selection of sklearn Arithmetic Transformer methods that replicate symbolic classification/regression operators. - - 'passthrough' : A node that just passes though the input. Useful for passing through raw inputs into inner nodes. - - 'feature_set_selector' : A selector that pulls out specific subsets of columns from the data. Only well defined as a leaf. - Subsets are set with the subsets parameter. - - 'skrebate' : Includes ReliefF, SURF, SURFstar, MultiSURF. - - 'MDR' : Includes MDR. - - 'ContinuousMDR' : Includes ContinuousMDR. - - 'genetic encoders' : Includes Genetic Encoder methods as used in AutoQTL. - - 'FeatureEncodingFrequencySelector': Includes FeatureEncodingFrequencySelector method as used in AutoQTL. - - list : a list of strings out of the above options to include the corresponding methods in the configuration dictionary. - - inner_config_dict : dict, default=["selectors", "transformers"] - The configuration dictionary to use for the inner nodes of the model generation. - Default ["selectors", "transformers"] - - 'selectors' : A selection of sklearn Selector methods. - - 'classifiers' : A selection of sklearn Classifier methods. - - 'regressors' : A selection of sklearn Regressor methods. - - 'transformers' : A selection of sklearn Transformer methods. - - 'arithmetic_transformer' : A selection of sklearn Arithmetic Transformer methods that replicate symbolic classification/regression operators. - - 'passthrough' : A node that just passes though the input. Useful for passing through raw inputs into inner nodes. - - 'feature_set_selector' : A selector that pulls out specific subsets of columns from the data. Only well defined as a leaf. - Subsets are set with the subsets parameter. - - 'skrebate' : Includes ReliefF, SURF, SURFstar, MultiSURF. - - 'MDR' : Includes MDR. - - 'ContinuousMDR' : Includes ContinuousMDR. - - 'genetic encoders' : Includes Genetic Encoder methods as used in AutoQTL. - - 'FeatureEncodingFrequencySelector': Includes FeatureEncodingFrequencySelector method as used in AutoQTL. - - list : a list of strings out of the above options to include the corresponding methods in the configuration dictionary. - - None : If None and max_depth>1, the root_config_dict will be used for the inner nodes as well. - - leaf_config_dict : dict, default=None - The configuration dictionary to use for the leaf node of the model. If set, leaf nodes must be from this dictionary. - Otherwise leaf nodes will be generated from the root_config_dict. - Default None - - 'selectors' : A selection of sklearn Selector methods. - - 'classifiers' : A selection of sklearn Classifier methods. - - 'regressors' : A selection of sklearn Regressor methods. - - 'transformers' : A selection of sklearn Transformer methods. - - 'arithmetic_transformer' : A selection of sklearn Arithmetic Transformer methods that replicate symbolic classification/regression operators. - - 'passthrough' : A node that just passes though the input. Useful for passing through raw inputs into inner nodes. - - 'feature_set_selector' : A selector that pulls out specific subsets of columns from the data. Only well defined as a leaf. - Subsets are set with the subsets parameter. - - 'skrebate' : Includes ReliefF, SURF, SURFstar, MultiSURF. - - 'MDR' : Includes MDR. - - 'ContinuousMDR' : Includes ContinuousMDR. - - 'genetic encoders' : Includes Genetic Encoder methods as used in AutoQTL. - - 'FeatureEncodingFrequencySelector': Includes FeatureEncodingFrequencySelector method as used in AutoQTL. - - list : a list of strings out of the above options to include the corresponding methods in the configuration dictionary. - - None : If None, a leaf will not be required (i.e. the pipeline can be a single root node). Leaf nodes will be generated from the inner_config_dict. - cross_val_predict_cv : int, default=0 Number of folds to use for the cross_val_predict function for inner classifiers and regressors. Estimators will still be fit on the full dataset, but the following node will get the outputs from cross_val_predict. @@ -378,9 +309,6 @@ def __init__(self, scorers, warm_start : bool, default=False If True, will use the continue the evolutionary algorithm from the last generation of the previous run. - subset_column : str or int, default=None - EXPERIMENTAL The column to use for the subset selection. Must also pass in unique_subset_values to GraphIndividual to function. - periodic_checkpoint_folder : str, default=None Folder to save the population to periodically. If None, no periodic saving will be done. If provided, training will resume from this checkpoint. @@ -441,14 +369,9 @@ def __init__(self, scorers, self.other_objective_functions_weights = other_objective_functions_weights self.objective_function_names = objective_function_names self.bigger_is_better = bigger_is_better - self.hyperparameter_probability = hyperparameter_probability - self.hyper_node_probability = hyper_node_probability - self.hyperparameter_alpha = hyperparameter_alpha - self.max_size = max_size - self.linear_pipeline = linear_pipeline - self.root_config_dict= root_config_dict - self.inner_config_dict= inner_config_dict - self.leaf_config_dict= leaf_config_dict + + self.search_space = search_space + self.cross_val_predict_cv = cross_val_predict_cv self.categorical_features = categorical_features self.subsets = subsets @@ -487,7 +410,6 @@ def __init__(self, scorers, self.selection_evaluation_early_stop = selection_evaluation_early_stop self.selection_evaluation_scaling = selection_evaluation_scaling self.warm_start = warm_start - self.subset_column = subset_column self.verbose = verbose self.periodic_checkpoint_folder = periodic_checkpoint_folder self.callback = callback @@ -501,7 +423,7 @@ def __init__(self, scorers, self.optuna_optimize_pareto_front_timeout = optuna_optimize_pareto_front_timeout self.optuna_storage = optuna_storage - # create random number generator based on rng_seed + # create random number generator based on rngseed self.rng = np.random.default_rng(random_state) # save random state passed to us for other functions that use random_state self.random_state = random_state @@ -675,17 +597,6 @@ def fit(self, X, y): else: self.feature_names = None - if self.root_config_dict == 'Auto': - if self.classification: - n_classes = len(np.unique(y)) - root_config_dict = get_configuration_dictionary("classifiers", n_samples, n_features, self.classification, self.random_state, self.cv_gen, subsets=self.subsets, feature_names=self.feature_names, n_classes=n_classes) - else: - root_config_dict = get_configuration_dictionary("regressors", n_samples, n_features, self.classification, self.random_state, self.cv_gen, subsets=self.subsets, feature_names=self.feature_names) - else: - root_config_dict = get_configuration_dictionary(self.root_config_dict, n_samples, n_features, self.classification, self.random_state, self.cv_gen, subsets=self.subsets,feature_names=self.feature_names) - - inner_config_dict = get_configuration_dictionary(self.inner_config_dict, n_samples, n_features, self.classification, self.random_state, self.cv_gen, subsets=self.subsets, feature_names=self.feature_names) - leaf_config_dict = get_configuration_dictionary(self.leaf_config_dict, n_samples, n_features, self.classification, self.random_state, self.cv_gen, subsets=self.subsets, feature_names=self.feature_names) def objective_function(pipeline_individual, @@ -697,7 +608,6 @@ def objective_function(pipeline_individual, other_objective_functions=self.other_objective_functions, memory=self.memory, cross_val_predict_cv=self.cross_val_predict_cv, - subset_column=self.subset_column, **kwargs): return objective_function_generator( pipeline_individual, @@ -709,21 +619,10 @@ def objective_function(pipeline_individual, other_objective_functions=other_objective_functions, memory=memory, cross_val_predict_cv=cross_val_predict_cv, - subset_column=subset_column, **kwargs, ) - self.individual_generator_instance = tpot2.individual_representations.graph_pipeline_individual.estimator_graph_individual_generator( - inner_config_dict=inner_config_dict, - root_config_dict=root_config_dict, - leaf_config_dict=leaf_config_dict, - max_size = self.max_size, - linear_pipeline=self.linear_pipeline, - hyperparameter_probability=self.hyperparameter_probability, - hyper_node_probability=self.hyper_node_probability, - hyperparameter_alpha=self.hyperparameter_alpha, - rng_=self.rng, - ) + if self.threshold_evaluation_early_stop is not None or self.selection_evaluation_early_stop is not None: evaluation_early_stop_steps = self.cv @@ -737,9 +636,14 @@ def objective_function(pipeline_individual, X_future = X y_future = y + def ind_generator(rng): + rng = np.random.default_rng(rng) + while True: + yield self.search_space.generate(rng) + #If warm start and we have an evolver instance, use the existing one if not(self.warm_start and self._evolver_instance is not None): - self._evolver_instance = self._evolver( individual_generator=self.individual_generator_instance, + self._evolver_instance = self._evolver( individual_generator=ind_generator(self.rng), objective_functions= [objective_function], objective_function_weights = self.objective_function_weights, objective_names=self.objective_names, @@ -781,7 +685,7 @@ def objective_function(pipeline_individual, mutate_then_crossover_probability= self.mutate_then_crossover_probability, crossover_then_mutate_probability= self.crossover_then_mutate_probability, - rng_=self.rng, + rng=self.rng, ) @@ -829,7 +733,7 @@ def objective_function(pipeline_individual, other_objective_functions=self.other_objective_functions, memory=self.memory, cross_val_predict_cv=self.cross_val_predict_cv, - subset_column=self.subset_column, + **kwargs: objective_function_generator( ind, X, @@ -840,7 +744,6 @@ def objective_function(pipeline_individual, other_objective_functions=other_objective_functions, memory=memory, cross_val_predict_cv=cross_val_predict_cv, - subset_column=subset_column, **kwargs, )] @@ -882,7 +785,6 @@ def objective_function(pipeline_individual, other_objective_functions=self.other_objective_functions, memory=self.memory, cross_val_predict_cv=self.cross_val_predict_cv, - subset_column=self.subset_column, **kwargs: val_objective_function_generator( ind, X, @@ -893,7 +795,6 @@ def objective_function(pipeline_individual, other_objective_functions=other_objective_functions, memory=memory, cross_val_predict_cv=cross_val_predict_cv, - subset_column=subset_column, **kwargs, )] @@ -920,7 +821,7 @@ def objective_function(pipeline_individual, self.selected_best_score = self.evaluated_individuals.loc[best_idx] - best_individual_pipeline = best_individual.export_pipeline(memory=self.memory, cross_val_predict_cv=self.cross_val_predict_cv, subset_column=self.subset_column) + best_individual_pipeline = best_individual.export_pipeline(memory=self.memory, cross_val_predict_cv=self.cross_val_predict_cv) if self.preprocessing: self.fitted_pipeline_ = sklearn.pipeline.make_pipeline(sklearn.base.clone(self._preprocessing_pipeline), best_individual_pipeline ) diff --git a/tpot2/tpot_estimator/steady_state_estimator.py b/tpot2/tpot_estimator/steady_state_estimator.py index 240b3a86..777c8cad 100644 --- a/tpot2/tpot_estimator/steady_state_estimator.py +++ b/tpot2/tpot_estimator/steady_state_estimator.py @@ -483,7 +483,7 @@ def __init__(self, scorers= [], self.optuna_optimize_pareto_front_timeout = optuna_optimize_pareto_front_timeout self.optuna_storage = optuna_storage - # create random number generator based on rng_seed + # create random number generator based on rngseed self.rng = np.random.default_rng(random_state) # save random state passed to us for other functions that use random_state self.random_state = random_state @@ -759,7 +759,7 @@ def objective_function(pipeline_individual, max_evaluated_individuals = self.max_evaluated_individuals, - rng_=self.rng, + rng=self.rng, ) From da1749e2bacc86d3eb1f688b173e01f8d8ad3a51 Mon Sep 17 00:00:00 2001 From: perib Date: Tue, 19 Mar 2024 19:49:34 -0700 Subject: [PATCH 2/7] edits --- tpot2/config/autoqtl_builtins.py | 32 +- tpot2/config/classifiers.py | 138 +++-- tpot2/config/classifiers_sklearnex.py | 128 ++-- tpot2/config/mdr_configs.py | 13 +- tpot2/config/regressors.py | 565 ++++++++---------- tpot2/config/regressors_sklearnex.py | 148 +++-- tpot2/config/special_configs.py | 89 +-- tpot2/config/transformers.py | 8 +- tpot2/search_spaces/nodes/estimator_node.py | 17 +- .../nodes/fss_node.py} | 0 10 files changed, 590 insertions(+), 548 deletions(-) rename tpot2/{config/all_single_modules.py => search_spaces/nodes/fss_node.py} (100%) diff --git a/tpot2/config/autoqtl_builtins.py b/tpot2/config/autoqtl_builtins.py index d3cc8dfc..b317fe70 100644 --- a/tpot2/config/autoqtl_builtins.py +++ b/tpot2/config/autoqtl_builtins.py @@ -3,22 +3,24 @@ import sklearn import numpy as np -def params_FeatureEncodingFrequencySelector(trial, name=None): - return { - 'threshold': trial.suggest_float(f'threshold_{name}', 0, .35) - } +from ConfigSpace import ConfigurationSpace +from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal +def get_FeatureEncodingFrequencySelector_ConfigurationSpace(): + return ConfigurationSpace( + space = { + 'threshold': Float("threshold", bounds=(0, .35)) + } + ) +def get_encoder_ConfigurationSpace(): + return ConfigurationSpace( + space = {} + ) +# genetic_encoders.DominantEncoder : {}, +# genetic_encoders.RecessiveEncoder : {}, +# genetic_encoders.HeterosisEncoder : {}, +# genetic_encoders.UnderDominanceEncoder : {}, +# genetic_encoders.OverDominanceEncoder : {}, -def make_FeatureEncodingFrequencySelector_config_dictionary(): - return {feature_encoding_frequency_selector.FeatureEncodingFrequencySelector: params_FeatureEncodingFrequencySelector} - -def make_genetic_encoders_config_dictionary(): - return { - genetic_encoders.DominantEncoder : {}, - genetic_encoders.RecessiveEncoder : {}, - genetic_encoders.HeterosisEncoder : {}, - genetic_encoders.UnderDominanceEncoder : {}, - genetic_encoders.OverDominanceEncoder : {}, - } diff --git a/tpot2/config/classifiers.py b/tpot2/config/classifiers.py index 5816b6bb..9649e463 100644 --- a/tpot2/config/classifiers.py +++ b/tpot2/config/classifiers.py @@ -2,17 +2,26 @@ from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal -def get_LogisticRegression_ConfigurationSpace(): +#TODO Conditional search space to prevent invalid combinations of hyperparameters +def get_LogisticRegression_ConfigurationSpace(random_state=None): + + space = { + 'solver': Categorical('solver', ['saga','liblinear']), + 'penalty': Categorical("penalty", ['elasticnet','l1', 'l2']), #TODO workaround to support None option? + 'dual': Categorical("dual", [True, False]), + 'C': Float("C", bounds=(1e-4, 1e4), log=True), + + #TODO workaround for including None as a value for class_weight + 'class_weight': Categorical("class_weight", ['balanced']), + 'n_jobs': 1, + 'max_iter': 1000, + } + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + return ConfigurationSpace( - space = { - 'solver': Categorical("solver", ['newton-cg', 'lbfgs', 'liblinear', 'sag', 'saga']), - 'penalty': Categorical("penalty", ['l1', 'l2']), - 'dual': Categorical("dual", [True, False]), - 'C': Float("C", bounds=(1e-4, 1e4), log=True), - 'class_weight': Categorical("class_weight", ['balanced']), - 'n_jobs': 1, - 'max_iter': 1000, - } + space = space ) @@ -21,7 +30,7 @@ def get_KNeighborsClassifier_ConfigurationSpace(n_samples=10): space = { - 'n_neighbors': Integer("n_neighbors", bounds=(1, max(50,n_samples))), + 'n_neighbors': Integer("n_neighbors", bounds=(1, min(100,n_samples)), log=True), 'weights': Categorical("weights", ['uniform', 'distance']), 'p': Integer("p", bounds=(1, 3)), 'metric': Categorical("metric", ['euclidean', 'minkowski']), @@ -30,33 +39,48 @@ def get_KNeighborsClassifier_ConfigurationSpace(n_samples=10): ) -def get_DecisionTreeClassifier_ConfigurationSpace(): +def get_DecisionTreeClassifier_ConfigurationSpace(random_state=None): + + space = { + 'criterion': Categorical("criterion", ['gini', 'entropy']), + 'max_depth': Integer("max_depth", bounds=(1, 20)), + 'min_samples_split': Integer("min_samples_split", bounds=(2, 21)), + 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 21)), + 'max_features': Categorical("max_features", ['sqrt', 'log2']), + 'min_weight_fraction_leaf': 0.0, + } + + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + return ConfigurationSpace( - space = { - 'criterion': Categorical("criterion", ['gini', 'entropy']), - 'max_depth': Integer("max_depth", bounds=(1, 11)), - 'min_samples_split': Integer("min_samples_split", bounds=(2, 21)), - 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 21)), - 'max_features': Categorical("max_features", ['sqrt', 'log2']), - 'min_weight_fraction_leaf': 0.0, - } + space = space ) -def get_SVC_ConfigurationSpace(): - return ConfigurationSpace( - space = { - 'kernel': Categorical("kernel", ['poly', 'rbf', 'linear', 'sigmoid']), - 'C': Float("C", bounds=(1e-4, 25), log=True), - 'degree': Integer("degree", bounds=(1, 4)), +def get_SVC_ConfigurationSpace(random_state=None): - #'class_weight': Categorical("class_weight", [None, 'balanced']), #TODO add class_weight. configspace doesn't allow None as a value. - 'max_iter': 3000, - 'tol': Float("tol", bounds=(0.001, 0.01)), - 'probability': Categorical("probability", [True]), # configspace doesn't allow bools as a default value? but does allow them as a value inside a Categorical - } + space = { + 'kernel': Categorical("kernel", ['poly', 'rbf', 'linear', 'sigmoid']), + 'C': Float("C", bounds=(1e-4, 25), log=True), + 'degree': Integer("degree", bounds=(1, 4)), + + #'class_weight': Categorical("class_weight", [None, 'balanced']), #TODO add class_weight. configspace doesn't allow None as a value. + 'max_iter': 3000, + 'tol': 0.001, + 'probability': Categorical("probability", [True]), # configspace doesn't allow bools as a default value? but does allow them as a value inside a Categorical + } + + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space ) +#TODO Conditional search spaces def get_LinearSVC_ConfigurationSpace(random_state=None,): space = { 'penalty': Categorical("penalty", ['l1', 'l2']), @@ -77,6 +101,7 @@ def get_LinearSVC_ConfigurationSpace(random_state=None,): def get_RandomForestClassifier_ConfigurationSpace(random_state=None): space = { + 'n_estimators': 100, 'criterion': Categorical("criterion", ['gini', 'entropy']), 'min_samples_split': Integer("min_samples_split", bounds=(2, 20)), 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 20)), @@ -90,34 +115,42 @@ def get_RandomForestClassifier_ConfigurationSpace(random_state=None): space = space ) -def get_GradientBoostingClassifier_ConfigurationSpace(n_classes=None): +def get_GradientBoostingClassifier_ConfigurationSpace(random_state=None, n_classes=None): if n_classes is not None and n_classes > 2: loss = 'log_loss' else: loss = Categorical("loss", ['log_loss', 'exponential']) + + space = { + 'n_estimators': 100, + 'loss': loss, + 'learning_rate': Float("learning_rate", bounds=(1e-3, 1), log=True), + 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 20)), + 'min_samples_split': Integer("min_samples_split", bounds=(2, 20)), + 'subsample': Float("subsample", bounds=(0.1, 1.0)), + 'max_features': Float("max_features", bounds=(0.1, 1.0)), + 'max_depth': Integer("max_depth", bounds=(1, 10)), + 'tol': 1e-4, + } + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state return ConfigurationSpace( - space = { - 'n_estimators': 100, - 'loss': loss, - 'learning_rate': Float("learning_rate", bounds=(1e-3, 1), log=True), - 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 20)), - 'min_samples_split': Integer("min_samples_split", bounds=(2, 20)), - 'subsample': Float("subsample", bounds=(0.1, 1.0)), - 'max_features': Float("max_features", bounds=(0.1, 1.0)), - 'max_depth': Integer("max_depth", bounds=(1, 10)), - } + space = space ) def get_XGBClassifier_ConfigurationSpace(random_state=None,): space = { + 'n_estimators': 100, 'learning_rate': Float("learning_rate", bounds=(1e-3, 1), log=True), 'subsample': Float("subsample", bounds=(0.1, 1.0)), 'min_child_weight': Integer("min_child_weight", bounds=(1, 21)), 'max_depth': Integer("max_depth", bounds=(1, 11)), + 'n_jobs': 1, } if random_state is not None: #This is required because configspace doesn't allow None as a value @@ -149,7 +182,7 @@ def get_LGBMClassifier_ConfigurationSpace(random_state=None,): def get_ExtraTreesClassifier_ConfigurationSpace(random_state=None): space = { - 'n_estimators': Integer("n_estimators", bounds=(10, 500)), + 'n_estimators': 100, 'criterion': Categorical("criterion", ["gini", "entropy"]), 'max_features': Float("max_features", bounds=(0.05, 1.00)), 'min_samples_split': Integer("min_samples_split", bounds=(2, 21)), @@ -207,7 +240,7 @@ def get_MLPClassifier_ConfigurationSpace(random_state=None): def get_BernoulliNB_ConfigurationSpace(): return ConfigurationSpace( space = { - 'alpha': Float("alpha", bounds=(1e-3, 100), log=True), + 'alpha': Float("alpha", bounds=(1e-2, 100), log=True), 'fit_prior': Categorical("fit_prior", [True, False]), } ) @@ -220,3 +253,20 @@ def get_MultinomialNB_ConfigurationSpace(): 'fit_prior': Categorical("fit_prior", [True, False]), } ) + + + +def get_AdaBoostClassifier_ConfigurationSpace(random_state=None): + space = { + 'n_estimators': Integer("n_estimators", bounds=(50, 500)), + 'learning_rate': Float("learning_rate", bounds=(0.01, 2), log=True), + 'algorithm': Categorical("algorithm", ['SAMME', 'SAMME.R']), + 'max_depth': Integer("max_depth", bounds=(1, 10)), + } + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space + ) \ No newline at end of file diff --git a/tpot2/config/classifiers_sklearnex.py b/tpot2/config/classifiers_sklearnex.py index 16983332..939df92f 100644 --- a/tpot2/config/classifiers_sklearnex.py +++ b/tpot2/config/classifiers_sklearnex.py @@ -5,76 +5,86 @@ from sklearnex.linear_model import LogisticRegression import numpy as np - +from ConfigSpace import ConfigurationSpace +from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal from functools import partial -def params_RandomForestClassifier(trial, random_state=None, name=None): - return { - 'n_estimators': 100, - 'bootstrap': trial.suggest_categorical(name=f'bootstrap_{name}', choices=[True, False]), - 'min_samples_split': trial.suggest_int(f'min_samples_split_{name}', 2, 20), - 'min_samples_leaf': trial.suggest_int(f'min_samples_leaf_{name}', 1, 20), - 'n_jobs': 1, - 'random_state': random_state - } +def get_RandomForestClassifier_ConfigurationSpace(random_state=None): + space = { + 'n_estimators': 100, #TODO make this a higher number? learned? + 'bootstrap': Categorical("bootstrap", [True, False]), + 'min_samples_split': Integer("min_samples_split", bounds=(2, 20)), + 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 20)), + 'n_jobs': 1, + + } + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space + ) -def params_KNeighborsClassifier(trial, name=None, n_samples=10): - n_neighbors_max = max(n_samples, 100) - return { - 'n_neighbors': trial.suggest_int(f'n_neighbors_{name}', 1, n_neighbors_max, log=True ), - 'weights': trial.suggest_categorical(f'weights_{name}', ['uniform', 'distance']), - } +def get_KNeighborsClassifier_ConfigurationSpace(n_samples=10): + return ConfigurationSpace( + space = { + 'n_neighbors': Integer("n_neighbors", bounds=(1, max(n_samples, 100)), log=True), + 'weights': Categorical("weights", ['uniform', 'distance']), + } + ) -def params_LogisticRegression(trial, random_state=None, name=None): - params = {} - params['dual'] = False - params['penalty'] = 'l2' - params['solver'] = trial.suggest_categorical(name=f'solver_{name}', choices=['liblinear', 'sag', 'saga']), - if params['solver'] == 'liblinear': - params['penalty'] = trial.suggest_categorical(name=f'penalty_{name}', choices=['l1', 'l2']) - if params['penalty'] == 'l2': - params['dual'] = trial.suggest_categorical(name=f'dual_{name}', choices=[True, False]) - else: - params['penalty'] = 'l1' - return { - 'solver': params['solver'], - 'penalty': params['penalty'], - 'dual': params['dual'], - 'C': trial.suggest_float(f'C_{name}', 1e-4, 1e4, log=True), + +#TODO add conditionals +def get_LogisticRegression_ConfigurationSpace(random_state=None): + space = { + 'solver': Categorical("solver", ['liblinear', 'sag', 'saga']), + 'penalty': Categorical("penalty", ['l1', 'l2']), + 'dual': Categorical("dual", [True, False]), + 'C': Float("C", bounds=(1e-4, 1e4), log=True), 'max_iter': 1000, - 'random_state': random_state } -def params_SVC(trial, random_state=None, name=None): - return { - 'kernel': trial.suggest_categorical(name=f'kernel_{name}', choices=['poly', 'rbf', 'linear', 'sigmoid']), - 'C': trial.suggest_float(f'C_{name}', 1e-4, 25, log=True), - 'degree': trial.suggest_int(f'degree_{name}', 1, 4), - 'class_weight': trial.suggest_categorical(name=f'class_weight_{name}', choices=[None, 'balanced']), + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space + ) + +def get_SVC_ConfigurationSpace(random_state=None): + space = { + 'kernel': Categorical("kernel", ['poly', 'rbf', 'linear', 'sigmoid']), + 'C': Float("C", bounds=(1e-4, 25), log=True), + 'degree': Integer("degree", bounds=(1, 4)), 'max_iter': 3000, - 'tol': 0.005, - 'probability': True, - 'random_state': random_state + 'tol': 0.001, + 'probability': Categorical("probability", [True]), # configspace doesn't allow bools as a default value? but does allow them as a value inside a Categorical } -def params_NuSVC(trial, random_state=None, name=None): - return { - 'nu': trial.suggest_float(f'subsample_{name}', 0.05, 1.0), - 'kernel': trial.suggest_categorical(name=f'kernel_{name}', choices=['poly', 'rbf', 'linear', 'sigmoid']), - 'C': trial.suggest_float(f'C_{name}', 1e-4, 25, log=True), - 'degree': trial.suggest_int(f'degree_{name}', 1, 4), - 'class_weight': trial.suggest_categorical(name=f'class_weight_{name}', choices=[None, 'balanced']), + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space + ) + +def get_NuSVC_ConfigurationSpace(random_state=None): + space = { + 'nu': Float("nu", bounds=(0.05, 1.0)), + 'kernel': Categorical("kernel", ['poly', 'rbf', 'linear', 'sigmoid']), + 'C': Float("C", bounds=(1e-4, 25), log=True), + 'degree': Integer("degree", bounds=(1, 4)), + #TODO work around for None value? + #'class_weight': Categorical("class_weight", [None, 'balanced']), 'max_iter': 3000, 'tol': 0.005, - 'probability': True, - 'random_state': random_state + 'probability': Categorical("probability", [True]), # configspace doesn't allow bools as a default value? but does allow them as a value inside a Categorical } -def make_sklearnex_classifier_config_dictionary(random_state=None, n_samples=10, n_classes=None): - return { - RandomForestClassifier: partial(params_RandomForestClassifier, random_state=random_state), - KNeighborsClassifier: partial(params_KNeighborsClassifier, n_samples=n_samples), - LogisticRegression: partial(params_LogisticRegression, random_state=random_state), - SVC: partial(params_SVC, random_state=random_state), - NuSVC: partial(params_NuSVC, random_state=random_state), - } \ No newline at end of file + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space + ) \ No newline at end of file diff --git a/tpot2/config/mdr_configs.py b/tpot2/config/mdr_configs.py index 4f872bd6..bbd7d487 100644 --- a/tpot2/config/mdr_configs.py +++ b/tpot2/config/mdr_configs.py @@ -22,12 +22,13 @@ ) -skrebate_ReliefF_configspace = ConfigurationSpace( - space = { - 'n_features_to_select': Integer('n_features_to_select', bounds=(1, 10), log=True), - 'n_neighbors': Integer('n_neighbors', bounds=(1,500), log=True), - } -) +def get_skrebate_SURF_config_space(n_features=10): + return ConfigurationSpace( + space = { + 'n_features_to_select': Integer('n_features_to_select', bounds=(1, n_features), log=True), + 'n_neighbors': Integer('n_neighbors', bounds=(2,500), log=True), + } + ) def make_skrebate_SURF_config_space(n_features=10): diff --git a/tpot2/config/regressors.py b/tpot2/config/regressors.py index ad7aa182..845f9ff1 100644 --- a/tpot2/config/regressors.py +++ b/tpot2/config/regressors.py @@ -22,375 +22,326 @@ from functools import partial +from ConfigSpace import ConfigurationSpace +from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal #TODO: fill in remaining #TODO check for places were we could use log scaling -def params_RandomForestRegressor(trial, random_state=None, name=None): - return { +def get_RandomForestRegressor_ConfigurationSpace(random_state=None): + space = { 'n_estimators': 100, - 'max_features': trial.suggest_float(f'max_features_{name}', 0.05, 1.0), - 'bootstrap': trial.suggest_categorical(name=f'bootstrap_{name}', choices=[True, False]), - 'min_samples_split': trial.suggest_int(f'min_samples_split_{name}', 2, 21), - 'min_samples_leaf': trial.suggest_int(f'min_samples_leaf_{name}', 1, 21), - 'random_state': random_state - } + 'max_features': Float("max_features", bounds=(0.05, 1.0)), + 'bootstrap': Categorical("bootstrap", [True, False]), + 'min_samples_split': Integer("min_samples_split", bounds=(2, 21)), + 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 21)), + } + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space + ) + + +def get_SGDRegressor_ConfigurationSpace(random_state=None): + space = { + 'loss': Categorical("loss", ['squared_error', 'huber', 'epsilon_insensitive', 'squared_epsilon_insensitive']), + 'penalty': 'elasticnet', + 'alpha': Float("alpha", bounds=(1e-5, 0.01), log=True), + 'learning_rate': Categorical("learning_rate", ['invscaling', 'constant']), + 'l1_ratio': Float("l1_ratio", bounds=(0.0, 1.0)), + 'eta0': Float("eta0", bounds=(0.01, 1.0)), + 'power_t': Float("power_t", bounds=(1e-5, 100.0), log=True), + 'fit_intercept': Categorical("fit_intercept", [True]), + } + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + return ConfigurationSpace( + space = space + ) -# SGDRegressor parameters -def params_SGDRegressor(trial, random_state=None, name=None): - params = { - 'loss': trial.suggest_categorical(f'loss_{name}', ['huber', 'squared_error', 'epsilon_insensitive', 'squared_epsilon_insensitive']), - 'penalty': 'elasticnet', - 'alpha': trial.suggest_float(f'alpha_{name}', 1e-5, 0.01, log=True), - 'learning_rate': trial.suggest_categorical(f'learning_rate_{name}', ['invscaling', 'constant']), - 'fit_intercept':True, - 'l1_ratio': trial.suggest_float(f'l1_ratio_{name}', 0.0, 1.0), - 'eta0': trial.suggest_float(f'eta0_{name}', 0.01, 1.0), - 'power_t': trial.suggest_float(f'power_t_{name}', 1e-5, 100.0, log=True), - 'random_state': random_state +def get_Ridge_ConfigurationSpace(random_state=None): + space = { + 'alpha': Float("alpha", bounds=(0.0, 1.0)), + 'fit_intercept': Categorical("fit_intercept", [True]), + 'tol': Float("tol", bounds=(1e-5, 1e-1), log=True), + 'solver': Categorical("solver", ['auto', 'svd', 'cholesky', 'lsqr', 'sparse_cg', 'sag', 'saga']), } - return params - -# Ridge parameters -def params_Ridge(trial, random_state=None, name=None): - params = { - 'alpha': trial.suggest_float(f'alpha_{name}', 0.0, 1.0), - 'fit_intercept': True, - - - #'max_iter': trial.suggest_int(f'max_iter_{name}', 100, 1000), - 'tol': trial.suggest_float(f'tol_{name}', 1e-5, 1e-1, log=True), - 'solver': trial.suggest_categorical(f'solver_{name}', ['auto', 'svd', 'cholesky', 'lsqr', 'sparse_cg', 'sag', 'saga']), - 'random_state': random_state + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space + ) + +def get_Lasso_ConfigurationSpace(random_state=None): + space = { + 'alpha': Float("alpha", bounds=(0.0, 1.0)), + 'fit_intercept': Categorical("fit_intercept", [True]), + 'tol': 0.0001, } - return params + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space + ) + +def get_ElasticNet_ConfigurationSpace(random_state=None): + space = { + 'alpha': Float("alpha", bounds=(0.0, 1.0)), + 'l1_ratio': Float("l1_ratio", bounds=(0.0, 1.0)), + } + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space + ) -# Lasso parameters -def params_Lasso(trial, random_state=None, name=None): - params = { - 'alpha': trial.suggest_float(f'alpha_{name}', 0.0, 1.0), - 'fit_intercept': True, - # 'normalize': trial.suggest_categorical(f'normalize_{name}', [True, False]), - 'precompute': trial.suggest_categorical(f'precompute_{name}', [True, False, 'auto']), +def get_Lars_ConfigurationSpace(random_state=None): + space = { + } + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state - #'max_iter': trial.suggest_int(f'max_iter_{name}', 100, 1000), - 'tol': trial.suggest_float(f'tol_{name}', 1e-5, 1e-1, log=True), + return ConfigurationSpace( + space = space + ) - 'positive': trial.suggest_categorical(f'positive_{name}', [True, False]), - 'selection': trial.suggest_categorical(f'selection_{name}', ['cyclic', 'random']), - 'random_state': random_state - } - return params - -# ElasticNet parameters -def params_ElasticNet(trial, random_state=None, name=None): - params = { - 'alpha': 1 - trial.suggest_float(f'alpha_{name}', 0.0, 1.0, log=True), - 'l1_ratio': 1- trial.suggest_float(f'l1_ratio_{name}',0.0, 1.0), - 'random_state': random_state +def get_OthogonalMatchingPursuit_ConfigurationSpace(): + return ConfigurationSpace( + space = { } - return params - -# Lars parameters -def params_Lars(trial, random_state=None, name=None): - params = { - 'fit_intercept': True, - 'verbose': trial.suggest_categorical(f'verbose_{name}', [True, False]), - 'normalize': trial.suggest_categorical(f'normalize_{name}', [True, False]), - - # 'precompute': trial.suggest_categorical(f'precompute_{name}', ['auto_{name}', True, False]), - 'n_nonzero_coefs': trial.suggest_int(f'n_nonzero_coefs_{name}', 1, 100), - 'eps': trial.suggest_float(f'eps_{name}', 1e-5, 1e-1, log=True), - 'copy_X': trial.suggest_categorical(f'copy_X_{name}', [True, False]), - 'fit_path': trial.suggest_categorical(f'fit_path_{name}', [True, False]), - # 'positive': trial.suggest_categorical(f'positive_{name}', [True, False]), - 'random_state': random_state - } - return params - -# OrthogonalMatchingPursuit parameters -def params_OrthogonalMatchingPursuit(trial, name=None): - params = { - 'n_nonzero_coefs': trial.suggest_int(f'n_nonzero_coefs_{name}', 1, 100), - 'tol': trial.suggest_float(f'tol_{name}', 1e-5, 1e-1, log=True), - 'fit_intercept': True, - 'normalize': trial.suggest_categorical(f'normalize_{name}', [True, False]), - 'precompute': trial.suggest_categorical(f'precompute_{name}', ['auto', True, False]), - } - return params - -# BayesianRidge parameters -def params_BayesianRidge(trial, name=None): - params = { - 'n_iter': trial.suggest_int(f'n_iter_{name}', 100, 1000), - 'tol': trial.suggest_float(f'tol_{name}', 1e-5, 1e-1, log=True), - 'alpha_1': trial.suggest_float(f'alpha_1_{name}', 1e-6, 1e-1, log=True), - 'alpha_2': trial.suggest_float(f'alpha_2_{name}', 1e-6, 1e-1, log=True), - 'lambda_1': trial.suggest_float(f'lambda_1_{name}', 1e-6, 1e-1, log=True), - 'lambda_2': trial.suggest_float(f'lambda_2_{name}', 1e-6, 1e-1, log=True), - 'compute_score': trial.suggest_categorical(f'compute_score_{name}', [True, False]), - 'fit_intercept': True, - 'normalize': trial.suggest_categorical(f'normalize_{name}', [True, False]), - 'copy_X': trial.suggest_categorical(f'copy_X_{name}', [True, False]), - } - return params - -# LassoLars parameters -def params_LassoLars(trial, random_state=None, name=None): - params = { - 'alpha': trial.suggest_float(f'alpha_{name}', 0.0, 1.0), - # 'fit_intercept': True, - # 'normalize': trial.suggest_categorical(f'normalize_{name}', [True, False]), - # 'precompute': trial.suggest_categorical(f'precompute_{name}', ['auto_{name}', True, False]), - #'max_iter': trial.suggest_int(f'max_iter_{name}', 100, 1000), - 'eps': trial.suggest_float(f'eps_{name}', 1e-5, 1e-1, log=True), - # 'copy_X': trial.suggest_categorical(f'copy_X_{name}', [True, False]), - # 'positive': trial.suggest_categorical(f'positive_{name}', [True, False]), - 'random_state': random_state - } - return params + ) + +def get_BayesianRidge_ConfigurationSpace(): + return ConfigurationSpace( + space = { + 'tol': 0.0001, + 'alpha_1': Float("alpha_1", bounds=(1e-6, 1e-1), log=True), + 'alpha_2': Float("alpha_2", bounds=(1e-6, 1e-1), log=True), + 'lambda_1': Float("lambda_1", bounds=(1e-6, 1e-1), log=True), + 'lambda_2': Float("lambda_2", bounds=(1e-6, 1e-1), log=True), + } + ) -# LassoLars parameters -def params_LassoLarsCV(trial, cv, name=None): - params = { - 'normalize': trial.suggest_categorical(f'normalize_{name}', [True, False]), - 'cv': cv, - } - return params - -# BaggingRegressor parameters -def params_BaggingRegressor(trial, random_state=None, name=None): - params = { - 'n_estimators': trial.suggest_int(f'n_estimators_{name}', 10, 100), - 'max_samples': trial.suggest_float(f'max_samples_{name}', 0.05, 1.00), - 'max_features': trial.suggest_float(f'max_features_{name}', 0.05, 1.00), - 'bootstrap': trial.suggest_categorical(f'bootstrap_{name}', [True, False]), - 'bootstrap_features': trial.suggest_categorical(f'bootstrap_features_{name}', [True, False]), - 'random_state': random_state - } - return params - -# ARDRegression parameters -def params_ARDRegression(trial, name=None): - params = { - 'n_iter': trial.suggest_int(f'n_iter_{name}', 100, 1000), - 'tol': trial.suggest_float(f'tol_{name}', 1e-5, 1e-1, log=True), - 'alpha_1': trial.suggest_float(f'alpha_1_{name}', 1e-6, 1e-1, log=True), - 'alpha_2': trial.suggest_float(f'alpha_2_{name}', 1e-6, 1e-1, log=True), - 'lambda_1': trial.suggest_float(f'lambda_1_{name}', 1e-6, 1e-1, log=True), - 'lambda_2': trial.suggest_float(f'lambda_2_{name}', 1e-6, 1e-1, log=True), - 'compute_score': trial.suggest_categorical(f'compute_score_{name}', [True, False]), - 'threshold_lambda': trial.suggest_int(f'threshold_lambda_{name}', 100, 1000), - 'fit_intercept': True, - 'normalize': trial.suggest_categorical(f'normalize_{name}', [True, False]), - 'copy_X': trial.suggest_categorical(f'copy_X_{name}', [True, False]), + +def get_LassoLars_ConfigurationSpace(random_state=None): + space = { + 'alpha': Float("alpha", bounds=(0.0, 1.0)), + 'eps': Float("eps", bounds=(1e-5, 1e-1), log=True), } - return params + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + return ConfigurationSpace( + space = space + ) -# TheilSenRegressor parameters -def params_TheilSenRegressor(trial, random_state=None, name=None): - params = { - 'n_subsamples': trial.suggest_int(f'n_subsamples_{name}', 10, 100), - 'max_subpopulation': trial.suggest_int(f'max_subpopulation_{name}', 100, 1000), - 'fit_intercept': True, - 'copy_X': trial.suggest_categorical(f'copy_X_{name}', [True, False]), - 'verbose': trial.suggest_categorical(f'verbose_{name}', [True, False]), - 'random_state': random_state - } - return params +def get_LassoLarsCV_ConfigurationSpace(cv): + return ConfigurationSpace( + space = { + 'cv': cv, + } + ) -# SVR parameters -def params_SVR(trial, name=None): - params = { - 'kernel': trial.suggest_categorical(name=f'kernel_{name}', choices=['poly', 'rbf', 'linear', 'sigmoid']), - 'C': trial.suggest_float(f'C_{name}', 1e-4, 25, log=True), - 'degree': trial.suggest_int(f'degree_{name}', 1, 4), - 'max_iter': 3000, - 'tol': 0.005, - } - return params - -# Perceptron parameters -def params_Perceptron(trial, random_state=None, name=None): - params = { - 'penalty': trial.suggest_categorical(f'penalty_{name}', [None, 'l2', 'l1', 'elasticnet']), - 'alpha': trial.suggest_float(f'alpha_{name}', 1e-5, 1e-1, log=True), - 'l1_ratio': trial.suggest_float(f'l1_ratio_{name}', 0.0, 1.0), - 'fit_intercept': True, - #'max_iter': trial.suggest_int(f'max_iter_{name}', 100, 1000), - 'tol': trial.suggest_float(f'tol_{name}', 1e-5, 1e-1, log=True), - 'shuffle': trial.suggest_categorical(f'shuffle_{name}', [True, False]), - 'verbose': trial.suggest_categorical(f'verbose_{name}', [0, 1, 2, 3, 4, 5]), - 'eta0': trial.suggest_float(f'eta0_{name}', 1e-5, 1e-1, log=True), - 'learning_rate': trial.suggest_categorical(f'learning_rate_{name}', ['constant', 'optimal', 'invscaling']), - 'early_stopping': trial.suggest_categorical(f'early_stopping_{name}', [True, False]), - 'validation_fraction': trial.suggest_float(f'validation_fraction_{name}', 0.05, 1.00), - 'n_iter_no_change': trial.suggest_int(f'n_iter_no_change_{name}', 1, 100), - 'class_weight': trial.suggest_categorical(f'class_weight_{name}', [None, 'balanced']), - 'warm_start': trial.suggest_categorical(f'warm_start_{name}', [True, False]), - 'average': trial.suggest_categorical(f'average_{name}', [True, False]), - 'random_state': random_state +def get_BaggingRegressor_ConfigurationSpace(random_state=None): + space = { + 'max_samples': Float("max_samples", bounds=(0.05, 1.00)), + 'max_features': Float("max_features", bounds=(0.05, 1.00)), + 'bootstrap': Categorical("bootstrap", [True, False]), + 'bootstrap_features': Categorical("bootstrap_features", [True, False]), } - return params -def params_MLPRegressor(trial, random_state=None, name=None): - params = { - 'alpha': trial.suggest_float(f'alpha_{name}', 1e-4, 1e-1, log=True), - 'learning_rate_init': trial.suggest_float(f'learning_rate_init_{name}', 1e-3, 1., log=True), - 'random_state': random_state - } + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state - return params + return ConfigurationSpace( + space = space + ) +def get_ARDRegression_ConfigurationSpace(): + return ConfigurationSpace( + space = { -#GradientBoostingRegressor parameters -def params_GradientBoostingRegressor(trial, random_state=None, name=None): - loss = trial.suggest_categorical(f'loss_{name}', ['ls', 'lad', 'huber', 'quantile']) + 'alpha_1': Float("alpha_1", bounds=(1e-6, 1e-1), log=True), + 'alpha_2': Float("alpha_2", bounds=(1e-6, 1e-1), log=True), + 'lambda_1': Float("lambda_1", bounds=(1e-6, 1e-1), log=True), + 'lambda_2': Float("lambda_2", bounds=(1e-6, 1e-1), log=True), + 'threshold_lambda': Integer("threshold_lambda", bounds=(100, 1000)), - params = { - - 'n_estimators': 100, - 'loss': loss, - 'learning_rate': trial.suggest_float(f'learning_rate_{name}', 1e-4, 1, log=True), - 'max_depth': trial.suggest_int(f'max_depth_{name}', 1, 11), - 'min_samples_split': trial.suggest_int(f'min_samples_split_{name}', 2, 21), - 'min_samples_leaf': trial.suggest_int(f'min_samples_leaf_{name}', 1, 21), - 'subsample': 1-trial.suggest_float(f'subsample_{name}', 0.05, 1.00, log=True), - 'max_features': 1-trial.suggest_float(f'max_features_{name}', 0.05, 1.00, log=True), - 'random_state': random_state + } + ) +def get_TheilSenRegressor_ConfigurationSpace(random_state=None): + space = { + 'n_subsamples': Integer("n_subsamples", bounds=(10, 100)), + 'max_subpopulation': Integer("max_subpopulation", bounds=(100, 1000)), } - if loss == 'quantile' or loss == 'huber': - alpha = trial.suggest_float(f'alpha_{name}', 0.05, 0.95) - params['alpha'] = alpha + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state - return params + return ConfigurationSpace( + space = space + ) +def get_SVR_ConfigurationSpace(): + return ConfigurationSpace( + space = { + 'kernel': Categorical("kernel", ['poly', 'rbf', 'linear', 'sigmoid']), + 'C': Float("C", bounds=(1e-4, 25), log=True), + 'degree': Integer("degree", bounds=(1, 4)), + 'max_iter': 3000, + 'tol': 0.005, + } + ) -def params_DecisionTreeRegressor(trial, random_state=None, name=None): - params = { - 'max_depth': trial.suggest_int(f'max_depth_{name}', 1,11), - 'min_samples_split': trial.suggest_int(f'min_samples_split_{name}', 2, 21), - 'min_samples_leaf': trial.suggest_int(f'min_samples_leaf_{name}', 1, 21), - # 'criterion': trial.suggest_categorical(f'criterion_{name}', ['squared_error', 'friedman_mse', 'absolute_error', 'poisson']), - # 'splitter': trial.suggest_categorical(f'splitter_{name}', ['best', 'random']), - #'max_features': trial.suggest_categorical(f'max_features_{name}', [None, 'auto', 'sqrt', 'log2']), - #'ccp_alpha': trial.suggest_float(f'ccp_alpha_{name}', 1e-1, 10.0), - 'random_state': random_state +def get_Perceptron_ConfigurationSpace(random_state=None): + space = { + 'penalty': Categorical("penalty", [None, 'l2', 'l1', 'elasticnet']), + 'alpha': Float("alpha", bounds=(1e-5, 1e-1), log=True), + 'l1_ratio': Float("l1_ratio", bounds=(0.0, 1.0)), + 'learning_rate': Categorical("learning_rate", ['constant', 'optimal', 'invscaling']), + 'validation_fraction': Float("validation_fraction", bounds=(0.05, 1.00)), } - return params - -def params_KNeighborsRegressor(trial, name=None, n_samples=100): - params = { - 'n_neighbors': trial.suggest_int(f'n_neighbors_{name}', 1, n_samples, log=True ), - 'weights': trial.suggest_categorical(f'weights_{name}', ['uniform', 'distance']), - 'p': trial.suggest_int(f'p_{name}', 1, 3), - 'metric': trial.suggest_categorical(f'metric_{name}', ['minkowski', 'euclidean', 'manhattan']), - } - return params + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state -def params_LinearSVR(trial, random_state=None, name=None): - params = { - 'epsilon': trial.suggest_float(f'epsilon_{name}', 1e-4, 1.0, log=True), - 'C': trial.suggest_float(f'C_{name}', 1e-4,25.0, log=True), - 'dual': trial.suggest_categorical(f'dual_{name}', [True,False]), - 'loss': trial.suggest_categorical(f'loss_{name}', ['epsilon_insensitive', 'squared_epsilon_insensitive']), - 'random_state': random_state + return ConfigurationSpace( + space = space + ) +def get_MLPRegressor_ConfigurationSpace(random_state=None): + space = { + 'alpha': Float("alpha", bounds=(1e-4, 1e-1), log=True), + 'learning_rate_init': Float("learning_rate_init", bounds=(1e-3, 1.), log=True), } - return params + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state -# XGBRegressor parameters -def params_XGBRegressor(trial, random_state=None, name=None): - return { - 'learning_rate': trial.suggest_float(f'learning_rate_{name}', 1e-3, 1, log=True), - 'subsample': trial.suggest_float(f'subsample_{name}', 0.05, 1.0), - 'min_child_weight': trial.suggest_int(f'min_child_weight_{name}', 1, 21), - #'booster': trial.suggest_categorical(name='booster_{name}', choices=['gbtree', 'dart']), - 'n_estimators': 100, - 'max_depth': trial.suggest_int(f'max_depth_{name}', 1, 11), - 'nthread': 1, - 'verbosity': 0, - 'objective': 'reg:squarederror', - 'random_state': random_state - } + return ConfigurationSpace( + space = space + ) -def params_AdaBoostRegressor(trial, random_state=None, name=None): - params = { +def get_GradientBoostingRegressor_ConfigurationSpace(random_state=None): + space = { 'n_estimators': 100, - 'learning_rate': trial.suggest_float(f'learning_rate_{name}', 1e-3, 1.0, log=True), - 'loss': trial.suggest_categorical(f'loss_{name}', ['linear', 'square', 'exponential']), - 'random_state': random_state + 'loss': Categorical("loss", ['ls', 'lad', 'huber', 'quantile']), + 'learning_rate': Float("learning_rate", bounds=(1e-4, 1), log=True), + 'max_depth': Integer("max_depth", bounds=(1, 11)), + 'min_samples_split': Integer("min_samples_split", bounds=(2, 21)), + 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 21)), + 'subsample': Float("subsample", bounds=(0.05, 1.00)), + 'max_features': Float("max_features", bounds=(0.05, 1.00)), + } + +def get_DecisionTreeRegressor_ConfigurationSpace(random_state=None): + space = { + 'max_depth': Integer("max_depth", bounds=(1, 11)), + 'min_samples_split': Integer("min_samples_split", bounds=(2, 21)), + 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 21)), } - return params -# ExtraTreesRegressor parameters -def params_ExtraTreesRegressor(trial, random_state=None, name=None): - params = { - 'n_estimators': 100, - 'max_features': trial.suggest_float(f'max_features_{name}', 0.05, 1.0), - 'min_samples_split': trial.suggest_int(f'min_samples_split_{name}', 2, 21), - 'min_samples_leaf': trial.suggest_int(f'min_samples_leaf_{name}', 1, 21), - 'bootstrap': trial.suggest_categorical(f'bootstrap_{name}', [True, False]), + return ConfigurationSpace( + space = space + ) - #'criterion': trial.suggest_categorical(f'criterion_{name}', ['squared_error', 'poisson', 'absolute_error', 'friedman_mse']), - #'max_depth': trial.suggest_int(f'max_depth_{name}', 1, 10), +def get_KNeighborsRegressor_ConfigurationSpace(n_samples=100): + return ConfigurationSpace( + space = { + 'n_neighbors': Integer("n_neighbors", bounds=(1, n_samples)), + 'weights': Categorical("weights", ['uniform', 'distance']), + 'p': Integer("p", bounds=(1, 3)), + 'metric': Categorical("metric", ['minkowski', 'euclidean', 'manhattan']), + } + ) + +def get_LinearSVR_ConfigurationSpace(random_state=None): + space = { + 'epsilon': Float("epsilon", bounds=(1e-4, 1.0), log=True), + 'C': Float("C", bounds=(1e-4, 25.0), log=True), + 'dual': Categorical("dual", [True, False]), + 'loss': Categorical("loss", ['epsilon_insensitive', 'squared_epsilon_insensitive']), + } - #'min_weight_fraction_leaf': trial.suggest_float(f'min_weight_fraction_leaf_{name}', 0.0, 0.5), - # 'max_features': trial.suggest_categorical(f'max_features_{name}', [None, 'auto', 'sqrt', 'log2']), - #'max_leaf_nodes': trial.suggest_int(f'max_leaf_nodes_{name}', 2, 100), - #'min_impurity_decrease': trial.suggest_float(f'min_impurity_decrease_{name}', 1e-5, 1e-1, log=True), - # 'min_impurity_split': trial.suggest_float(f'min_impurity_split_{name}', 1e-5, 1e-1, log=True), + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state - #if bootstrap is True - #'oob_score': trial.suggest_categorical(f'oob_score_{name}', [True, False]), + return ConfigurationSpace( + space = space + ) - #'ccp_alpha': trial.suggest_float(f'ccp_alpha_{name}', 1e-5, 1e-1, log=True), - # 'max_samples': trial.suggest_float(f'max_samples_{name}', 0.05, 1.00), - 'random_state': random_state +def get_XGBRegressor_ConfigurationSpace(random_state=None): + space = { + 'learning_rate': Float("learning_rate", bounds=(1e-3, 1), log=True), + 'subsample': Float("subsample", bounds=(0.05, 1.0)), + 'min_child_weight': Integer("min_child_weight", bounds=(1, 21)), + 'n_estimators': 100, + 'max_depth': Integer("max_depth", bounds=(1, 11)), + 'nthread': 1, + 'verbosity': 0, + 'objective': 'reg:squarederror', } - return params + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + return ConfigurationSpace( + space = space + ) -def make_regressor_config_dictionary(random_state=None, cv=None, n_samples=10): - n_samples = min(n_samples,100) #TODO optimize this +def get_AdaBoostRegressor_ConfigurationSpace(random_state=None): - regressor_config_dictionary = { - #ElasticNet: params_ElasticNet, - ElasticNetCV: { - 'l1_ratio': [.1, .5, .7, .9, .95, .99, 1], - 'cv': cv, - }, - ExtraTreesRegressor: partial(params_ExtraTreesRegressor, random_state=random_state), - GradientBoostingRegressor: partial(params_GradientBoostingRegressor, random_state=random_state), - AdaBoostRegressor: partial(params_AdaBoostRegressor, random_state=random_state), - DecisionTreeRegressor: partial(params_DecisionTreeRegressor, random_state=random_state), - KNeighborsRegressor: partial(params_KNeighborsRegressor,n_samples=n_samples), - LassoLarsCV: partial(params_LassoLarsCV, cv=cv), - SVR: params_SVR, - RandomForestRegressor: partial(params_RandomForestRegressor, random_state=random_state), - RidgeCV: {'cv': cv}, - XGBRegressor: partial(params_XGBRegressor, random_state=random_state), - SGDRegressor: partial(params_SGDRegressor, random_state= random_state), + space = { + 'n_estimators': Integer("n_estimators", bounds=(50, 100)), + 'learning_rate': Float("learning_rate", bounds=(1e-3, 1.0), log=True), + 'loss': Categorical("loss", ['linear', 'square', 'exponential']), + } + + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + return ConfigurationSpace( + space = space + ) + +def get_ExtraTreesRegressor_ConfigurationSpace(random_state=None): + space = { + 'n_estimators': 100, + 'max_features': Float("max_features", bounds=(0.05, 1.0)), + 'min_samples_split': Integer("min_samples_split", bounds=(2, 21)), + 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 21)), + 'bootstrap': Categorical("bootstrap", [True, False]), } - return regressor_config_dictionary \ No newline at end of file + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space + ) \ No newline at end of file diff --git a/tpot2/config/regressors_sklearnex.py b/tpot2/config/regressors_sklearnex.py index 279d2dba..298407cb 100644 --- a/tpot2/config/regressors_sklearnex.py +++ b/tpot2/config/regressors_sklearnex.py @@ -13,63 +13,100 @@ from functools import partial +from ConfigSpace import ConfigurationSpace +from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal -def params_RandomForestRegressor(trial, random_state=None, name=None): - return { + + +def get_RandomForestRegressor_ConfigurationSpace(random_state=None): + space = { 'n_estimators': 100, - 'max_features': trial.suggest_float(f'max_features_{name}', 0.05, 1.0), - 'bootstrap': trial.suggest_categorical(name=f'bootstrap_{name}', choices=[True, False]), - 'min_samples_split': trial.suggest_int(f'min_samples_split_{name}', 2, 21), - 'min_samples_leaf': trial.suggest_int(f'min_samples_leaf_{name}', 1, 21), - 'random_state': random_state + 'max_features': Float("max_features", bounds=(0.05, 1.0)), + 'bootstrap': Categorical("bootstrap", [True, False]), + 'min_samples_split': Integer("min_samples_split", bounds=(2, 21)), + 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 21)), } + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space + ) -def params_KNeighborsRegressor(trial, name=None, n_samples=100): - n_neighbors_max = max(n_samples, 100) - return { - 'n_neighbors': trial.suggest_int(f'n_neighbors_{name}', 1, n_neighbors_max), - 'weights': trial.suggest_categorical(f'weights_{name}', ['uniform', 'distance']), + +def get_KNeighborsRegressor_ConfigurationSpace(n_samples=100): + return ConfigurationSpace( + space = { + 'n_neighbors': Integer("n_neighbors", bounds=(1, max(n_samples, 100))), + 'weights': Categorical("weights", ['uniform', 'distance']), } + ) -def params_LinearRegression(trial, name=None): - return {} +LinearRegression_configspace = ConfigurationSpace() -def params_Ridge(trial, random_state=None, name=None): - return { - 'alpha': trial.suggest_float(f'alpha_{name}', 0.0, 1.0), - 'fit_intercept': True, - 'tol': trial.suggest_float(f'tol_{name}', 1e-5, 1e-1, log=True), - 'random_state': random_state + + +def get_Ridge_ConfigurationSpace(random_state=None): + space = { + 'alpha': Float("alpha", bounds=(0.0, 1.0)), + 'fit_intercept': Categorical("fit_intercept", [True]), + 'tol': Float("tol", bounds=(1e-5, 1e-1)), } -def params_Lasso(trial, random_state=None, name=None): - return { - 'alpha': trial.suggest_float(f'alpha_{name}', 0.0, 1.0), - 'fit_intercept': True, - 'precompute': trial.suggest_categorical(f'precompute_{name}', [True, False, 'auto']), - 'tol': trial.suggest_float(f'tol_{name}', 1e-5, 1e-1, log=True), - 'positive': trial.suggest_categorical(f'positive_{name}', [True, False]), - 'selection': trial.suggest_categorical(f'selection_{name}', ['cyclic', 'random']), - 'random_state': random_state + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space + ) + +def get_Lasso_ConfigurationSpace(random_state=None): + space = { + 'alpha': Float("alpha", bounds=(0.0, 1.0)), + 'fit_intercept': Categorical("fit_intercept", [True]), + 'precompute': Categorical("precompute", [True, False, 'auto']), + 'tol': 0.001, + 'positive': Categorical("positive", [True, False]), + 'selection': Categorical("selection", ['cyclic', 'random']), } -def params_ElasticNet(trial, random_state=None, name=None): - params = { - 'alpha': 1 - trial.suggest_float(f'alpha_{name}', 0.0, 1.0, log=True), - 'l1_ratio': 1- trial.suggest_float(f'l1_ratio_{name}',0.0, 1.0), - 'random_state': random_state - } - return params + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state -def params_SVR(trial, name=None): - params = { - 'kernel': trial.suggest_categorical(name=f'kernel_{name}', choices=['poly', 'rbf', 'linear', 'sigmoid']), - 'C': trial.suggest_float(f'C_{name}', 1e-4, 25, log=True), - 'degree': trial.suggest_int(f'degree_{name}', 1, 4), + return ConfigurationSpace( + space = space + ) + +def get_ElasticNet_ConfigurationSpace(random_state=None): + space = { + 'alpha': Float("alpha", bounds=(0.0, 1.0)), + 'l1_ratio': Float("l1_ratio", bounds=(0.0, 1.0)), + } + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space + ) + + +def get_SVR_ConfigurationSpace(random_state=None): + space = { + 'kernel': Categorical("kernel", ['poly', 'rbf', 'linear', 'sigmoid']), + 'C': Float("C", bounds=(1e-4, 25), log=True), + 'degree': Integer("degree", bounds=(1, 4)), 'max_iter': 3000, - 'tol': 0.005, + 'tol': 0.001, } - return params + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space + ) def params_NuSVR(trial, name=None): return { @@ -81,14 +118,19 @@ def params_NuSVR(trial, name=None): 'tol': 0.005, } -def make_sklearnex_regressor_config_dictionary(random_state=None, n_samples=10): - return { - RandomForestRegressor: partial(params_RandomForestRegressor, random_state=random_state), - KNeighborsRegressor: params_KNeighborsRegressor, - LinearRegression: params_LinearRegression, - Ridge: partial(params_Ridge, random_state=random_state), - Lasso: partial(params_Lasso, random_state=random_state), - ElasticNet: partial(params_ElasticNet, random_state=random_state), - SVR: params_SVR, - NuSVR: params_NuSVR, +def get_NuSVR_ConfigurationSpace(random_state=None): + space = { + 'nu': Float("nu", bounds=(0.05, 1.0)), + 'kernel': Categorical("kernel", ['poly', 'rbf', 'linear', 'sigmoid']), + 'C': Float("C", bounds=(1e-4, 25), log=True), + 'degree': Integer("degree", bounds=(1, 4)), + 'max_iter': 3000, + 'tol': 0.005, } + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + return ConfigurationSpace( + space = space + ) \ No newline at end of file diff --git a/tpot2/config/special_configs.py b/tpot2/config/special_configs.py index a6745b6f..cdecfe7b 100644 --- a/tpot2/config/special_configs.py +++ b/tpot2/config/special_configs.py @@ -4,60 +4,43 @@ import numpy as np from tpot2.builtin_modules import AddTransformer, mul_neg_1_Transformer, MulTransformer, SafeReciprocalTransformer, EQTransformer, NETransformer, GETransformer, GTTransformer, LETransformer, LTTransformer, MinTransformer, MaxTransformer, ZeroTransformer, OneTransformer, NTransformer -# ArithmeticTransformer -def params_arthmetic_operator(trial, name=None): - return { - 'function': trial.suggest_categorical(f'function_{name}', ["add", "mul_neg_1", "mul", "safe_reciprocal", "eq","ne","ge","gt","le","lt", "min","max","0","1"]), - } - -def make_arithmetic_transformer_config_dictionary(): - return { - AddTransformer: {}, - mul_neg_1_Transformer: {}, - MulTransformer: {}, - SafeReciprocalTransformer: {}, - EQTransformer: {}, - NETransformer: {}, - GETransformer: {}, - GTTransformer: {}, - LETransformer: {}, - LTTransformer: {}, - MinTransformer: {}, - MaxTransformer: {}, - } - +from ConfigSpace import ConfigurationSpace +from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal +def get_ArithmeticTransformer_ConfigurationSpace(): + return ConfigurationSpace( + space = { + 'function': Categorical("function", ["add", "mul_neg_1", "mul", "safe_reciprocal", "eq","ne","ge","gt","le","lt", "min","max","0","1"]), + } + ) -def params_feature_set_selector(trial, name=None, names_list = None, subset_dict=None): - """Create a dictionary of parameters for FeatureSetSelector. - Parameters - ---------- - trial: optuna.trial.Trial - A trial corresponds to the evaluation of a objective function. - name: string - Used for compatibility in when calling multiple optuna of multiple parameters at once. - names_list: list of string - List of names of the feature set selector. To more easily keep track of what the subsets represent. - Included to prevent repeat calls to list(subset_dict.keys()) which may be slow and/or have different orderings - subset_dict: dictionary - A dictionary of subsets. The keys are the names of the subsets and the values are the subsets. - - Returns - ------- - params: dictionary - A dictionary of parameters for FeatureSetSelector. - """ +# def make_arithmetic_transformer_config_dictionary(): +# return { +# AddTransformer: {}, +# mul_neg_1_Transformer: {}, +# MulTransformer: {}, +# SafeReciprocalTransformer: {}, +# EQTransformer: {}, +# NETransformer: {}, +# GETransformer: {}, +# GTTransformer: {}, +# LETransformer: {}, +# LTTransformer: {}, +# MinTransformer: {}, +# MaxTransformer: {}, +# } - subset_name = trial.suggest_categorical(f'subset_name_{name}', names_list) - params = {'name': subset_name, - 'sel_subset': subset_dict[subset_name], - } +def get_FeatureSetSelector_ConfigurationSpace(names_list = None, subset_dict=None): + return ConfigurationSpace( + space = { + 'name': Categorical("name", names_list), + } + ) - return params def make_FSS_config_dictionary(subsets=None, n_features=None, feature_names=None): """Create the search space of parameters for FeatureSetSelector. @@ -95,14 +78,8 @@ def make_FSS_config_dictionary(subsets=None, n_features=None, feature_names=None names_list = list(subset_dict.keys()) - return {FeatureSetSelector: partial(params_feature_set_selector, names_list = names_list, subset_dict=subset_dict)} - - - -from tpot2.builtin_modules import Passthrough - -def params_passthrough(trial, name=None): - return {} + return ConfigurationSpace({ + 'name': Categorical("name", names_list), + 'subset_dict': Categorical("subset", subset_dict), + }) -def make_passthrough_config_dictionary(): - return {Passthrough: params_passthrough} \ No newline at end of file diff --git a/tpot2/config/transformers.py b/tpot2/config/transformers.py index fca4932c..f74d5e18 100644 --- a/tpot2/config/transformers.py +++ b/tpot2/config/transformers.py @@ -13,12 +13,12 @@ ) PCA_configspace = ConfigurationSpace( - space={'n_components': Float('n_components', bounds=(0.001, 0.999))} + space={'n_components': Float('n_components', bounds=(0.5, 0.999))} ) -ZeroCount_configspace = ConfigurationSpace() +ZeroCount_configspace = {} -OneHotEncoder_configspace = ConfigurationSpace() #TODO include the parameter for max unique values +OneHotEncoder_configspace = {} #TODO include the parameter for max unique values def get_FastICA_configspace(n_features=100, random_state=None): @@ -35,7 +35,7 @@ def get_FastICA_configspace(n_features=100, random_state=None): space = space ) - +#TODO conditional parameters def get_FeatureAgglomeration_configspace(n_features=100): return ConfigurationSpace( space = { diff --git a/tpot2/search_spaces/nodes/estimator_node.py b/tpot2/search_spaces/nodes/estimator_node.py index e44dc4f1..6bea7615 100644 --- a/tpot2/search_spaces/nodes/estimator_node.py +++ b/tpot2/search_spaces/nodes/estimator_node.py @@ -11,17 +11,23 @@ class EstimatorNodeIndividual(SklearnIndividual): def __init__(self, method: type, - space: ConfigurationSpace, + space: ConfigurationSpace|dict, #TODO If a dict is passed, hyperparameters are fixed and not learned. Is this confusing? Should we make a second node type? rng=None) -> None: super().__init__() self.method = method self.space = space - rng = np.random.default_rng(rng) - self.space.seed(rng.integers(0, 2**32)) - self.hyperparameters = self.space.sample_configuration().get_dictionary() + if isinstance(space, dict): + self.space = space + else: + rng = np.random.default_rng(rng) + self.space.seed(rng.integers(0, 2**32)) + self.hyperparameters = self.space.sample_configuration().get_dictionary() def mutate(self, rng=None): + if isinstance(self.space, dict): + return False + rng = np.random.default_rng(rng) self.space.seed(rng.integers(0, 2**32)) self.hyperparameters = self.space.sample_configuration().get_dictionary() @@ -29,6 +35,9 @@ def mutate(self, rng=None): return True def crossover(self, other, rng=None): + if isinstance(self.space, dict): + return False + rng = np.random.default_rng(rng) if self.method != other.method: return False diff --git a/tpot2/config/all_single_modules.py b/tpot2/search_spaces/nodes/fss_node.py similarity index 100% rename from tpot2/config/all_single_modules.py rename to tpot2/search_spaces/nodes/fss_node.py From 5b5b22254c08bc7a6526a2897863c20a3fcd598e Mon Sep 17 00:00:00 2001 From: perib Date: Fri, 22 Mar 2024 15:10:31 -0700 Subject: [PATCH 3/7] edits --- Tutorial/2_Search_Spaces.ipynb | 178 +++++++++-------- Tutorial/5_GraphPipeline.ipynb | 4 +- .../Example_Search_Spaces/imputation.ipynb | 85 ++++++++ tpot2/config/classifiers.py | 21 +- tpot2/config/get_configspace.py | 188 ++++++++++-------- tpot2/config/imputers.py | 9 +- tpot2/config/mdr_configs.py | 3 - tpot2/graphsklearn.py | 92 ++------- .../nodes/estimator_node_custom_sampler.py | 52 +++++ tpot2/tpot_estimator/estimator.py | 26 +-- tpot2/tpot_estimator/estimator_utils.py | 9 +- .../templates/tpot_autoimputer.py | 0 12 files changed, 388 insertions(+), 279 deletions(-) create mode 100644 Tutorial/Example_Search_Spaces/imputation.ipynb create mode 100644 tpot2/search_spaces/nodes/estimator_node_custom_sampler.py create mode 100644 tpot2/tpot_estimator/templates/tpot_autoimputer.py diff --git a/Tutorial/2_Search_Spaces.ipynb b/Tutorial/2_Search_Spaces.ipynb index 853ca61f..e21d6c5c 100644 --- a/Tutorial/2_Search_Spaces.ipynb +++ b/Tutorial/2_Search_Spaces.ipynb @@ -31,7 +31,7 @@ "output_type": "stream", "text": [ "sampled hyperparameters\n", - "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 6, 'p': 2, 'weights': 'distance'}\n" + "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 1, 'p': 1, 'weights': 'distance'}\n" ] } ], @@ -78,7 +78,7 @@ "| Name | Info |\n", "| :--- | :----: |\n", "| EstimatorNode | Takes in a ConfigSpace along with the class of the method. This node will optimize the hyperparameters for a single method. |\n", - "| GeneticFeatureSelector | Uses evolution to optimize a set of features, exports a basic sklearn Selector that simply selects the features chosen by the node. |\n", + "| GeneticFeatureSelectorNode | Uses evolution to optimize a set of features, exports a basic sklearn Selector that simply selects the features chosen by the node. |\n", "\n", "\n", "\n", @@ -94,7 +94,7 @@ "| Name | Info |\n", "| :--- | :----: |\n", "| ChoicePipeline | Takes in a list of search spaces. Will select one node from the search space. |\n", - "| SquentialPipeline | Takes in a list of search spaces. will produce a pipeline of Squential length. Each step in the pipeline will correspond to the the search space provided in the same index. |\n", + "| SequentialPipeline | Takes in a list of search spaces. will produce a pipeline of Sequential length. Each step in the pipeline will correspond to the the search space provided in the same index. |\n", "| DynamicLinearPipeline | Takes in a single search space. Will produce a linear pipeline of variable length. Each step in the pipeline will be pulled from the search space provided. |\n", "| TreePipeline |Generates a pipeline of variable length. Pipeline will have a tree structure similar to TPOT1. |\n", "| GraphPipeline | Generates a directed acyclic graph of variable size. Search spaces for root, leaf, and inner nodes can be defined separately if desired. |\n", @@ -154,9 +154,9 @@ "output_type": "stream", "text": [ "sampled hyperparameters\n", - "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 4, 'p': 3, 'weights': 'distance'}\n", + "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 8, 'p': 1, 'weights': 'uniform'}\n", "mutated hyperparameters\n", - "{'metric': 'euclidean', 'n_jobs': 1, 'n_neighbors': 8, 'p': 2, 'weights': 'uniform'}\n" + "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 5, 'p': 2, 'weights': 'distance'}\n" ] } ], @@ -187,14 +187,14 @@ "output_type": "stream", "text": [ "original hyperparameters for individual 1\n", - "{'metric': 'euclidean', 'n_jobs': 1, 'n_neighbors': 6, 'p': 3, 'weights': 'distance'}\n", + "{'metric': 'euclidean', 'n_jobs': 1, 'n_neighbors': 9, 'p': 1, 'weights': 'uniform'}\n", "original hyperparameters for individual 2\n", - "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 5, 'p': 2, 'weights': 'uniform'}\n", + "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 3, 'p': 3, 'weights': 'distance'}\n", "\n", "post crossover hyperparameters for individual 1\n", - "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 5, 'p': 3, 'weights': 'distance'}\n", + "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 9, 'p': 3, 'weights': 'uniform'}\n", "post crossover hyperparameters for individual 2\n", - "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 5, 'p': 2, 'weights': 'uniform'}\n" + "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 3, 'p': 3, 'weights': 'distance'}\n" ] } ], @@ -233,10 +233,10 @@ { "data": { "text/html": [ - "
KNeighborsClassifier(n_jobs=1, p=3, weights='distance')
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + "
KNeighborsClassifier(n_jobs=1, n_neighbors=9, p=3)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ - "KNeighborsClassifier(n_jobs=1, p=3, weights='distance')" + "KNeighborsClassifier(n_jobs=1, n_neighbors=9, p=3)" ] }, "execution_count": 5, @@ -268,7 +268,18 @@ "cell_type": "code", "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "import tpot2\n", "from ConfigSpace import ConfigurationSpace\n", @@ -335,22 +346,22 @@ ")\n", "\n", "\n", - "# tpot2.search_spaces.pipelines.ChoicePipeline(\n", - "# choice_list = [\n", - "# tpot2.search_spaces.nodes.EstimatorNode(\n", - "# method = KNeighborsClassifier,\n", - "# space = knn_configspace,\n", - "# ),\n", - "# tpot2.search_spaces.nodes.EstimatorNode(\n", - "# method = LogisticRegression,\n", - "# space = lr_configspace,\n", - "# ),\n", - "# tpot2.search_spaces.nodes.EstimatorNode(\n", - "# method = DecisionTreeClassifier,\n", - "# space = dt_configspace,\n", - "# ),\n", - "# ]\n", - "# )" + "tpot2.search_spaces.pipelines.ChoicePipeline(\n", + " choice_list = [\n", + " tpot2.search_spaces.nodes.EstimatorNode(\n", + " method = KNeighborsClassifier,\n", + " space = knn_configspace,\n", + " ),\n", + " tpot2.search_spaces.nodes.EstimatorNode(\n", + " method = LogisticRegression,\n", + " space = lr_configspace,\n", + " ),\n", + " tpot2.search_spaces.nodes.EstimatorNode(\n", + " method = DecisionTreeClassifier,\n", + " space = dt_configspace,\n", + " ),\n", + " ]\n", + ")" ] }, { @@ -375,10 +386,13 @@ { "data": { "text/html": [ - "
KNeighborsClassifier(metric='euclidean', n_jobs=1, n_neighbors=1, p=3)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + "
LogisticRegression(C=0.4989834645092814, class_weight='balanced', dual=True,\n",
+       "                   max_iter=1000, n_jobs=1)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ - "KNeighborsClassifier(metric='euclidean', n_jobs=1, n_neighbors=1, p=3)" + "LogisticRegression(C=0.4989834645092814, class_weight='balanced', dual=True,\n", + " max_iter=1000, n_jobs=1)" ] }, "execution_count": 7, @@ -408,10 +422,13 @@ { "data": { "text/html": [ - "
KNeighborsClassifier(metric='euclidean', n_jobs=1, n_neighbors=7, p=1)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + "
DecisionTreeClassifier(max_depth=9, max_features='log2', min_samples_leaf=12,\n",
+       "                       min_samples_split=4)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ - "KNeighborsClassifier(metric='euclidean', n_jobs=1, n_neighbors=7, p=1)" + "DecisionTreeClassifier(max_depth=9, max_features='log2', min_samples_leaf=12,\n", + " min_samples_split=4)" ] }, "execution_count": 8, @@ -449,27 +466,16 @@ "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "sampled pipeline 1\n" + "ename": "TypeError", + "evalue": "unhashable type: 'list'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[9], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m#same pipeline search space as before.\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m classifier_choice \u001b[38;5;241m=\u001b[39m \u001b[43mtpot2\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mconfig\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget_search_space\u001b[49m\u001b[43m(\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mKNeighborsClassifier\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mLogisticRegression\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mDecisionTreeClassifier\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124msampled pipeline 1\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 5\u001b[0m classifier_choice\u001b[38;5;241m.\u001b[39mgenerate()\u001b[38;5;241m.\u001b[39mexport_pipeline()\n", + "File \u001b[0;32m~/common/Projects/TPOT_Dev/tpot2/tpot2/config/get_configspace.py:169\u001b[0m, in \u001b[0;36mget_search_space\u001b[0;34m(name, n_classes, n_samples, random_state)\u001b[0m\n\u001b[1;32m 168\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mget_search_space\u001b[39m(name, n_classes\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m3\u001b[39m, n_samples\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m100\u001b[39m, random_state\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[0;32m--> 169\u001b[0m name \u001b[38;5;241m=\u001b[39m \u001b[43mGROUPNAMES\u001b[49m\u001b[43m[\u001b[49m\u001b[43mname\u001b[49m\u001b[43m]\u001b[49m\n\u001b[1;32m 171\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m name \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 172\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n", + "\u001b[0;31mTypeError\u001b[0m: unhashable type: 'list'" ] - }, - { - "data": { - "text/html": [ - "
DecisionTreeClassifier(criterion='entropy', max_depth=4, max_features='sqrt',\n",
-       "                       min_samples_leaf=7, min_samples_split=5)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" - ], - "text/plain": [ - "DecisionTreeClassifier(criterion='entropy', max_depth=4, max_features='sqrt',\n", - " min_samples_leaf=7, min_samples_split=5)" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ @@ -482,7 +488,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -495,13 +501,10 @@ { "data": { "text/html": [ - "
LogisticRegression(C=0.22118566188988883, class_weight='balanced',\n",
-       "                   max_iter=1000, n_jobs=1, solver='sag')
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + "
KNeighborsClassifier(metric='euclidean', n_jobs=1, n_neighbors=96)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ - "LogisticRegression(C=0.22118566188988883, class_weight='balanced',\n", - " max_iter=1000, n_jobs=1, solver='sag')" + "KNeighborsClassifier(metric='euclidean', n_jobs=1, n_neighbors=96)" ] }, "execution_count": 10, @@ -516,28 +519,23 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "sampled pipeline 1\n" + "ename": "KeyError", + "evalue": "'AdaBoostClassifier'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[11], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m#search space for all classifiers\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m classifier_choice \u001b[38;5;241m=\u001b[39m \u001b[43mtpot2\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mconfig\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget_search_space\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mclassifiers\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124msampled pipeline 1\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 5\u001b[0m classifier_choice\u001b[38;5;241m.\u001b[39mgenerate()\u001b[38;5;241m.\u001b[39mexport_pipeline()\n", + "File \u001b[0;32m~/common/Projects/TPOT_Dev/tpot2/tpot2/config/get_configspace.py:180\u001b[0m, in \u001b[0;36mget_search_space\u001b[0;34m(name, n_classes, n_samples, random_state)\u001b[0m\n\u001b[1;32m 178\u001b[0m \u001b[38;5;66;03m#if list of names, return a list of EstimatorNodes\u001b[39;00m\n\u001b[1;32m 179\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(name, \u001b[38;5;28mlist\u001b[39m) \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(name, np\u001b[38;5;241m.\u001b[39mndarray):\n\u001b[0;32m--> 180\u001b[0m search_spaces \u001b[38;5;241m=\u001b[39m [get_search_space(n, n_classes\u001b[38;5;241m=\u001b[39mn_classes, n_samples\u001b[38;5;241m=\u001b[39mn_samples, random_state\u001b[38;5;241m=\u001b[39mrandom_state) \u001b[38;5;28;01mfor\u001b[39;00m n \u001b[38;5;129;01min\u001b[39;00m name]\n\u001b[1;32m 181\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m ChoicePipeline(choice_list\u001b[38;5;241m=\u001b[39msearch_spaces)\n\u001b[1;32m 182\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n", + "File \u001b[0;32m~/common/Projects/TPOT_Dev/tpot2/tpot2/config/get_configspace.py:180\u001b[0m, in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 178\u001b[0m \u001b[38;5;66;03m#if list of names, return a list of EstimatorNodes\u001b[39;00m\n\u001b[1;32m 179\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(name, \u001b[38;5;28mlist\u001b[39m) \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(name, np\u001b[38;5;241m.\u001b[39mndarray):\n\u001b[0;32m--> 180\u001b[0m search_spaces \u001b[38;5;241m=\u001b[39m [\u001b[43mget_search_space\u001b[49m\u001b[43m(\u001b[49m\u001b[43mn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mn_classes\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mn_classes\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mn_samples\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mn_samples\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrandom_state\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrandom_state\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mfor\u001b[39;00m n \u001b[38;5;129;01min\u001b[39;00m name]\n\u001b[1;32m 181\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m ChoicePipeline(choice_list\u001b[38;5;241m=\u001b[39msearch_spaces)\n\u001b[1;32m 182\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n", + "File \u001b[0;32m~/common/Projects/TPOT_Dev/tpot2/tpot2/config/get_configspace.py:183\u001b[0m, in \u001b[0;36mget_search_space\u001b[0;34m(name, n_classes, n_samples, random_state)\u001b[0m\n\u001b[1;32m 181\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m ChoicePipeline(choice_list\u001b[38;5;241m=\u001b[39msearch_spaces)\n\u001b[1;32m 182\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m--> 183\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mget_estimatornode\u001b[49m\u001b[43m(\u001b[49m\u001b[43mname\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mn_classes\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mn_classes\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mn_samples\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mn_samples\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrandom_state\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrandom_state\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/common/Projects/TPOT_Dev/tpot2/tpot2/config/get_configspace.py:190\u001b[0m, in \u001b[0;36mget_estimatornode\u001b[0;34m(name, n_classes, n_samples, random_state)\u001b[0m\n\u001b[1;32m 186\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mget_estimatornode\u001b[39m(name, n_classes\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m3\u001b[39m, n_samples\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m100\u001b[39m, random_state\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[1;32m 187\u001b[0m configspace \u001b[38;5;241m=\u001b[39m get_configspace(name, n_classes\u001b[38;5;241m=\u001b[39mn_classes, n_samples\u001b[38;5;241m=\u001b[39mn_samples, random_state\u001b[38;5;241m=\u001b[39mrandom_state)\n\u001b[0;32m--> 190\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m EstimatorNode(\u001b[43mSTRING_TO_CLASS\u001b[49m\u001b[43m[\u001b[49m\u001b[43mname\u001b[49m\u001b[43m]\u001b[49m, configspace)\n", + "\u001b[0;31mKeyError\u001b[0m: 'AdaBoostClassifier'" ] - }, - { - "data": { - "text/html": [ - "
KNeighborsClassifier(metric='euclidean', n_jobs=1, n_neighbors=89)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" - ], - "text/plain": [ - "KNeighborsClassifier(metric='euclidean', n_jobs=1, n_neighbors=89)" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ @@ -550,7 +548,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -599,7 +597,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -631,9 +629,23 @@ ], "source": [ "stc_pipeline = tpot2.search_spaces.pipelines.SequentialPipeline([\n", - " tpot2.config.get_search_space(\"selectors\"),\n", + " tpot2.config.get_search_space(\"selectors\"), \n", " tpot2.config.get_search_space(\"transformers\"),\n", " tpot2.config.get_search_space(\"classifiers\"),\n", + " \n", + "])\n", + "\n", + "stc_pipeline = tpot2.search_spaces.pipelines.SequentialPipeline([\n", + " tpot2.config.get_search_space(\"preprocessors1\"), \n", + " tpot2.config.get_search_space(\"imputation\"), \n", + " tpot2.config.get_search_space(\"selectors\"), \n", + " tpot2.search_spaces.pipelines.GraphPipeline(\n", + " root_search_space= tpot2.config.get_search_space(\"classifiers\"),\n", + " leaf_search_space = tpot2.config.get_search_space(\"selectors\"), \n", + " inner_search_space = tpot2.config.get_search_space([\"transformers\",\"classifiers\"]),\n", + " max_size = 10,\n", + " )\n", + " \n", "])\n", "\n", "\n", @@ -643,7 +655,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -696,7 +708,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -766,7 +778,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -788,7 +800,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "metadata": {}, "outputs": [ { diff --git a/Tutorial/5_GraphPipeline.ipynb b/Tutorial/5_GraphPipeline.ipynb index 47d48c9d..320c68f1 100644 --- a/Tutorial/5_GraphPipeline.ipynb +++ b/Tutorial/5_GraphPipeline.ipynb @@ -19,7 +19,7 @@ "outputs": [ { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "
" ] @@ -107,7 +107,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.11" + "version": "3.10.13" }, "orig_nbformat": 4, "vscode": { diff --git a/Tutorial/Example_Search_Spaces/imputation.ipynb b/Tutorial/Example_Search_Spaces/imputation.ipynb new file mode 100644 index 00000000..07532532 --- /dev/null +++ b/Tutorial/Example_Search_Spaces/imputation.ipynb @@ -0,0 +1,85 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from ConfigSpace import ConfigurationSpace\n", + "from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal\n", + "\n", + "simple_imputer = ConfigurationSpace(\n", + " space = {\n", + " 'strategy' : Categorical('strategy', [['mean','median',], ['most_frequent'] ]),\n", + " 'add_indicator' : Categorical('add_indicator', [True, False]), \n", + " }\n", + ")\n", + "\n", + "simple_imputer.sample_configuration()" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Configuration(values={\n", + " '2': 2,\n", + " 'a': 2,\n", + "})" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from ConfigSpace import ConfigurationSpace, EqualsCondition\n", + "import ConfigSpace\n", + "\n", + "cs = ConfigurationSpace({\n", + "\n", + " \"1\": [1,2,3],\n", + " \"2\": ConfigSpace.Constant(\"2\", 2),\n", + "\n", + " \"a\": [1, 2, 3],\n", + "\n", + "})\n", + "\n", + "cond = EqualsCondition(cs['1'], cs['a'], 1)\n", + "cond2 = EqualsCondition(cs['2'], cs['a'], 2)\n", + "\n", + "cs.add_condition(cond)\n", + "cs.add_condition(cond2)\n", + "\n", + "cs.sample_configuration()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tpot2env", + "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.13" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tpot2/config/classifiers.py b/tpot2/config/classifiers.py index 9649e463..14649f61 100644 --- a/tpot2/config/classifiers.py +++ b/tpot2/config/classifiers.py @@ -39,14 +39,14 @@ def get_KNeighborsClassifier_ConfigurationSpace(n_samples=10): ) -def get_DecisionTreeClassifier_ConfigurationSpace(random_state=None): +def get_DecisionTreeClassifier_ConfigurationSpace(random_state=None, n_featues=20): space = { 'criterion': Categorical("criterion", ['gini', 'entropy']), - 'max_depth': Integer("max_depth", bounds=(1, 20)), - 'min_samples_split': Integer("min_samples_split", bounds=(2, 21)), - 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 21)), - 'max_features': Categorical("max_features", ['sqrt', 'log2']), + 'max_depth': Integer("max_depth", bounds=(1, 2*n_featues)), + 'min_samples_split': Integer("min_samples_split", bounds=(2, 20)), + 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 20)), + 'max_features': Categorical("max_features", [1.0, 'sqrt', 'log2']), 'min_weight_fraction_leaf': 0.0, } @@ -126,11 +126,15 @@ def get_GradientBoostingClassifier_ConfigurationSpace(random_state=None, n_class 'n_estimators': 100, 'loss': loss, 'learning_rate': Float("learning_rate", bounds=(1e-3, 1), log=True), - 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 20)), + 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 200)), 'min_samples_split': Integer("min_samples_split", bounds=(2, 20)), 'subsample': Float("subsample", bounds=(0.1, 1.0)), 'max_features': Float("max_features", bounds=(0.1, 1.0)), 'max_depth': Integer("max_depth", bounds=(1, 10)), + + #TODO include max leaf nodes? + #TODO validation fraction + n_iter_no_change? maybe as conditional + 'tol': 1e-4, } @@ -185,8 +189,8 @@ def get_ExtraTreesClassifier_ConfigurationSpace(random_state=None): 'n_estimators': 100, 'criterion': Categorical("criterion", ["gini", "entropy"]), 'max_features': Float("max_features", bounds=(0.05, 1.00)), - 'min_samples_split': Integer("min_samples_split", bounds=(2, 21)), - 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 21)), + 'min_samples_split': Integer("min_samples_split", bounds=(2, 20)), + 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 20)), 'bootstrap': Categorical("bootstrap", [True, False]), 'n_jobs': 1, } @@ -236,6 +240,7 @@ def get_MLPClassifier_ConfigurationSpace(random_state=None): space = space ) +GaussianNB_ConfigurationSpace = {} def get_BernoulliNB_ConfigurationSpace(): return ConfigurationSpace( diff --git a/tpot2/config/get_configspace.py b/tpot2/config/get_configspace.py index 7a9e552e..0710c29e 100644 --- a/tpot2/config/get_configspace.py +++ b/tpot2/config/get_configspace.py @@ -1,11 +1,16 @@ from ..search_spaces.nodes import EstimatorNode -from ..search_spaces.pipelines import ChoicePipeline +from ..search_spaces.pipelines import ChoicePipeline, WrapperPipeline -from .classifiers import * -from .transformers import * -from .regressors import * -from .selectors import * +from . import classifiers +from . import transformers +from . import selectors +from . import regressors +from . import autoqtl_builtins +from . import imputers +from . import mdr_configs +from . import special_configs +import numpy as np from sklearn.linear_model import SGDClassifier from sklearn.ensemble import RandomForestClassifier, ExtraTreesClassifier, GradientBoostingClassifier @@ -49,138 +54,157 @@ from sklearn.feature_selection import f_regression +from sklearn.linear_model import SGDRegressor +from sklearn.linear_model import LinearRegression +from sklearn.linear_model import Ridge +from sklearn.linear_model import Lasso +from sklearn.linear_model import ElasticNet +from sklearn.linear_model import Lars +from sklearn.linear_model import LassoLars, LassoLarsCV +from sklearn.linear_model import RidgeCV + + +from sklearn.svm import SVR +from sklearn.svm import LinearSVR + +from sklearn.ensemble import AdaBoostRegressor, GradientBoostingRegressor,RandomForestRegressor +from sklearn.ensemble import BaggingRegressor +from sklearn.ensemble import ExtraTreesRegressor +from sklearn.tree import DecisionTreeRegressor +from sklearn.neighbors import KNeighborsRegressor +from sklearn.linear_model import ElasticNetCV + +from xgboost import XGBRegressor + from tpot2.builtin_modules import RFE_ExtraTreesClassifier, SelectFromModel_ExtraTreesClassifier, RFE_ExtraTreesRegressor, SelectFromModel_ExtraTreesRegressor + +all_methods = [SGDClassifier, RandomForestClassifier, ExtraTreesClassifier, GradientBoostingClassifier, MLPClassifier, DecisionTreeClassifier, XGBClassifier, KNeighborsClassifier, SVC, LogisticRegression, LGBMClassifier, LinearSVC, GaussianNB, BernoulliNB, MultinomialNB, ExtraTreesRegressor, RandomForestRegressor, GradientBoostingRegressor, BaggingRegressor, DecisionTreeRegressor, KNeighborsRegressor, XGBRegressor, RFE_ExtraTreesClassifier, SelectFromModel_ExtraTreesClassifier, RFE_ExtraTreesRegressor, SelectFromModel_ExtraTreesRegressor, ZeroCount, OneHotEncoder, ColumnOneHotEncoder, Binarizer, FastICA, FeatureAgglomeration, MaxAbsScaler, MinMaxScaler, Normalizer, Nystroem, PCA, PolynomialFeatures, RBFSampler, RobustScaler, StandardScaler, SelectFwe, SelectPercentile, VarianceThreshold, RFE, SelectFromModel, f_classif, f_regression, SGDRegressor, LinearRegression, Ridge, Lasso, ElasticNet, Lars, LassoLars, LassoLarsCV, RidgeCV, SVR, LinearSVR, AdaBoostRegressor, GradientBoostingRegressor, RandomForestRegressor, BaggingRegressor, ExtraTreesRegressor, DecisionTreeRegressor, KNeighborsRegressor, ElasticNetCV, + ] + STRING_TO_CLASS = { - #classifiers - "LogisticRegression": LogisticRegression, - "KNeighborsClassifier": KNeighborsClassifier, - "DecisionTreeClassifier": DecisionTreeClassifier, - "SVC": SVC, - "LinearSVC": LinearSVC, - "RandomForestClassifier": RandomForestClassifier, - "GradientBoostingClassifier": GradientBoostingClassifier, - "XGBClassifier": XGBClassifier, - "LGBMClassifier": LGBMClassifier, - "ExtraTreesClassifier": ExtraTreesClassifier, - "SGDClassifier": SGDClassifier, - "MLPClassifier": MLPClassifier, - "BernoulliNB": BernoulliNB, - "MultinomialNB": MultinomialNB, - - #transformers - "Binarizer": Binarizer, - "Normalizer": Normalizer, - "PCA": PCA, - "ZeroCount": ZeroCount, - "OneHotEncoder": ColumnOneHotEncoder, - "FastICA": FastICA, - "FeatureAgglomeration": FeatureAgglomeration, - "Nystroem": Nystroem, - "RBFSampler": RBFSampler, - - #selectors - "SelectFwe": SelectFwe, - "SelectPercentile": SelectPercentile, - "VarianceThreshold": VarianceThreshold, - "RFE": RFE, - "SelectFromModel": SelectFromModel, + t.__name__: t for t in all_methods } +GROUPNAMES = { + "selectors": ["SelectFwe", "SelectPercentile", "VarianceThreshold",], + "selectors_classification": ["SelectFwe", "SelectPercentile", "VarianceThreshold", "RFE_classification", "SelectFromModel_classification"], + "selectors_regression": ["SelectFwe", "SelectPercentile", "VarianceThreshold", "RFE_regression", "SelectFromModel_regression"], + "classifiers" : ["BernoulliNB", "DecisionTreeClassifier", "ExtraTreesClassifier", "GaussianNB", "GradientBoostingClassifier", "KNeighborsClassifier", "LinearDiscriminantAnalysis", "LinearSVC", "QuadraticDiscriminantAnalysis", "PassiveAggressiveClassifier", "LogisticRegression", "MLPClassifier", "MultinomialNB", "PassiveAggressiveClassifier", "Perceptron", "QuadraticDiscriminantAnalysis", "RandomForestClassifier", "RidgeClassifier", "SGDClassifier", "SVC", "XGBClassifier", "LGBMClassifier"], + "transformers": ["Binarizer", "Normalizer", "PCA", "ZeroCount", "OneHotEncoder", "FastICA", "FeatureAgglomeration", "Nystroem", "RBFSampler"], +} + def get_configspace(name, n_classes=3, n_samples=100, random_state=None): match name: #classifiers.py case "LogisticRegression": - return get_LogisticRegression_ConfigurationSpace() + return classifiers.get_LogisticRegression_ConfigurationSpace() case "KNeighborsClassifier": - return get_KNeighborsClassifier_ConfigurationSpace(n_samples=n_samples) + return classifiers.get_KNeighborsClassifier_ConfigurationSpace(n_samples=n_samples) case "DecisionTreeClassifier": - return get_DecisionTreeClassifier_ConfigurationSpace() + return classifiers.get_DecisionTreeClassifier_ConfigurationSpace() case "SVC": - return get_SVC_ConfigurationSpace() + return classifiers.get_SVC_ConfigurationSpace() case "LinearSVC": - return get_LinearSVC_ConfigurationSpace() + return classifiers.get_LinearSVC_ConfigurationSpace() case "RandomForestClassifier": - return get_RandomForestClassifier_ConfigurationSpace(random_state=random_state) + return classifiers.get_RandomForestClassifier_ConfigurationSpace(random_state=random_state) case "GradientBoostingClassifier": - return get_GradientBoostingClassifier_ConfigurationSpace(n_classes=n_classes) + return classifiers.get_GradientBoostingClassifier_ConfigurationSpace(n_classes=n_classes) case "XGBClassifier": - return get_XGBClassifier_ConfigurationSpace(random_state=random_state) + return classifiers.get_XGBClassifier_ConfigurationSpace(random_state=random_state) case "LGBMClassifier": - return get_LGBMClassifier_ConfigurationSpace(random_state=random_state) + return classifiers.get_LGBMClassifier_ConfigurationSpace(random_state=random_state) case "ExtraTreesClassifier": - return get_ExtraTreesClassifier_ConfigurationSpace(random_state=random_state) + return classifiers.get_ExtraTreesClassifier_ConfigurationSpace(random_state=random_state) case "SGDClassifier": - return get_SGDClassifier_ConfigurationSpace(random_state=random_state) + return classifiers.get_SGDClassifier_ConfigurationSpace(random_state=random_state) case "MLPClassifier": - return get_MLPClassifier_ConfigurationSpace(random_state=random_state) + return classifiers.get_MLPClassifier_ConfigurationSpace(random_state=random_state) case "BernoulliNB": - return get_BernoulliNB_ConfigurationSpace() + return classifiers.get_BernoulliNB_ConfigurationSpace() case "MultinomialNB": - return get_MultinomialNB_ConfigurationSpace() + return classifiers.get_MultinomialNB_ConfigurationSpace() #transformers.py case "Binarizer": - return Binarizer_configspace + return transformers.Binarizer_configspace case "Normalizer": - return Normalizer_configspace + return transformers.Normalizer_configspace case "PCA": - return PCA_configspace + return transformers.PCA_configspace case "ZeroCount": - return ZeroCount_configspace + return transformers.ZeroCount_configspace case "OneHotEncoder": - return OneHotEncoder_configspace + return transformers.OneHotEncoder_configspace case "FastICA": - return get_FastICA_configspace() + return transformers.get_FastICA_configspace() case "FeatureAgglomeration": - return get_FeatureAgglomeration_configspace() + return transformers.get_FeatureAgglomeration_configspace() case "Nystroem": - return get_Nystroem_configspace() + return transformers.get_Nystroem_configspace() case "RBFSampler": - return get_RBFSampler_configspace() + return transformers.get_RBFSampler_configspace() #selectors.py case "SelectFwe": - return SelectFwe_configspace + return selectors.SelectFwe_configspace case "SelectPercentile": - return SelectPercentile_configspace + return selectors.SelectPercentile_configspace case "VarianceThreshold": - return VarianceThreshold_configspace + return selectors.VarianceThreshold_configspace case "RFE": - return RFE_configspace_part + return selectors.RFE_configspace_part case "SelectFromModel": - return SelectFromModel_configspace_part + return selectors.SelectFromModel_configspace_part + + return None -def check_for_special(name): - match name: - case "selectors": - return ["SelectFwe", "SelectPercentile", "VarianceThreshold",] - case "classifiers": - return ["LogisticRegression", "KNeighborsClassifier", "DecisionTreeClassifier", "SVC", "RandomForestClassifier", "GradientBoostingClassifier", "XGBClassifier", "ExtraTreesClassifier", "SGDClassifier", "MLPClassifier", "BernoulliNB", "MultinomialNB"] - case "transformers": - return ["Binarizer", "Normalizer", "PCA", "ZeroCount", "OneHotEncoder", "FastICA", "FeatureAgglomeration", "Nystroem", "RBFSampler"] - - return name +def get_search_space(name, n_classes=3, n_samples=100, random_state=None): + name = GROUPNAMES[name] + if name is None: + return None -def get_search_space(name, n_classes=3, n_samples=100, random_state=None): - name = check_for_special(name) + if name not in STRING_TO_CLASS: + return None #if list of names, return a list of EstimatorNodes if isinstance(name, list) or isinstance(name, np.ndarray): search_spaces = [get_search_space(n, n_classes=n_classes, n_samples=n_samples, random_state=random_state) for n in name] + #remove Nones + search_spaces = [s for s in search_spaces if s is not None] + return ChoicePipeline(choice_list=search_spaces) else: - return get_estimatornode(name, n_classes=n_classes, n_samples=n_samples, random_state=random_state) + return get_node(name, n_classes=n_classes, n_samples=n_samples, random_state=random_state) + + +def get_node(name, n_classes=3, n_samples=100, random_state=None): + + #these are wrappers + if name == "RFE_classification": + rfe_sp = get_configspace(name, n_classes=n_classes, n_samples=n_samples, random_state=random_state) + ext = get_node("ExtraTreesClassifier", n_classes=n_classes, n_samples=n_samples, random_state=random_state) + return WrapperPipeline(nodegen=ext, method=RFE, configspace=rfe_sp) + if name == "RFE_regression": + rfe_sp = get_configspace(name, n_classes=n_classes, n_samples=n_samples, random_state=random_state) + ext = get_node("ExtraTreesRegressor", n_classes=n_classes, n_samples=n_samples, random_state=random_state) + return WrapperPipeline(nodegen=ext, method=RFE, configspace=rfe_sp) + if name == "SelectFromModel_classification": + sfm_sp = get_configspace(name, n_classes=n_classes, n_samples=n_samples, random_state=random_state) + ext = get_node("ExtraTreesClassifier", n_classes=n_classes, n_samples=n_samples, random_state=random_state) + return WrapperPipeline(nodegen=ext, method=SelectFromModel, configspace=sfm_sp) + if name == "SelectFromModel_regression": + sfm_sp = get_configspace(name, n_classes=n_classes, n_samples=n_samples, random_state=random_state) + ext = get_node("ExtraTreesRegressor", n_classes=n_classes, n_samples=n_samples, random_state=random_state) + return WrapperPipeline(nodegen=ext, method=SelectFromModel, configspace=sfm_sp) -def get_estimatornode(name, n_classes=3, n_samples=100, random_state=None): configspace = get_configspace(name, n_classes=n_classes, n_samples=n_samples, random_state=random_state) - - return EstimatorNode(STRING_TO_CLASS[name], configspace) diff --git a/tpot2/config/imputers.py b/tpot2/config/imputers.py index 89bcb60d..5d016d23 100644 --- a/tpot2/config/imputers.py +++ b/tpot2/config/imputers.py @@ -1,2 +1,9 @@ from ConfigSpace import ConfigurationSpace -from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal \ No newline at end of file +from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal + +simple_imputer = ConfigurationSpace( + space = { + 'strategy' : Categorical('strategy', ['mean','median', 'most_frequent', ]), + 'add_indicator' : Categorical('add_indicator', [True, False]), + } +) \ No newline at end of file diff --git a/tpot2/config/mdr_configs.py b/tpot2/config/mdr_configs.py index bbd7d487..abfe2a4d 100644 --- a/tpot2/config/mdr_configs.py +++ b/tpot2/config/mdr_configs.py @@ -1,6 +1,3 @@ -from mdr import MDR, ContinuousMDR -from skrebate import ReliefF, SURF, SURFstar, MultiSURF -from functools import partial from ConfigSpace import ConfigurationSpace from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal diff --git a/tpot2/graphsklearn.py b/tpot2/graphsklearn.py index 113c70aa..e0d500ae 100644 --- a/tpot2/graphsklearn.py +++ b/tpot2/graphsklearn.py @@ -62,47 +62,34 @@ def _method_name(name, estimator, method): return method -def estimator_fit_transform_override_cross_val_predict(estimator, X, y, cv=5, method='auto',subset_indexes=None, **fit_params): +def estimator_fit_transform_override_cross_val_predict(estimator, X, y, cv=5, method='auto', **fit_params): method = _method_name(name=estimator.__class__.__name__, estimator=estimator, method=method) if cv > 1: - #TODO subset indexes for cross val predict preds = sklearn.model_selection.cross_val_predict(estimator=estimator, X=X, y=y, cv=cv, method=method, **fit_params) estimator.fit(X,y, **fit_params) else: - if subset_indexes is None: - estimator.fit(X,y, **fit_params) - func = getattr(estimator,method) - preds = func(X) - else: - this_X = X[subset_indexes] - this_y = y[subset_indexes] - estimator.fit(this_X, this_y, **fit_params) - func = getattr(estimator,method) - preds = func(X) + estimator.fit(X,y, **fit_params) + func = getattr(estimator,method) + preds = func(X) + return preds, estimator # https://github.com/scikit-learn/scikit-learn/blob/7db5b6a98/sklearn/pipeline.py#L883 -def _fit_transform_one(model, X, y, fit_transform=True, subset_indexes=None, **fit_params): +def _fit_transform_one(model, X, y, fit_transform=True, **fit_params): """Fit and transform one step in a pipeline.""" - if subset_indexes is None: - if fit_transform and hasattr(model, "fit_transform"): - res = model.fit_transform(X, y, **fit_params) - else: - res = model.fit(X, y, **fit_params).transform(X) - #return model - + if fit_transform and hasattr(model, "fit_transform"): + res = model.fit_transform(X, y, **fit_params) else: - this_X = X[subset_indexes] - this_y = y[subset_indexes] - model.fit(this_X, this_y, **fit_params) - res = model.transform(X) + res = model.fit(X, y, **fit_params).transform(X) + #return model + return res, model @@ -110,7 +97,6 @@ def _fit_transform_one(model, X, y, fit_transform=True, subset_indexes=None, **f def fit_sklearn_digraph(graph: nx.DiGraph, X, y, - subset_col = None, method='auto', cross_val_predict_cv = 0, #func(est,X,y) -> transformed_X memory = None, @@ -137,22 +123,15 @@ def fit_sklearn_digraph(graph: nx.DiGraph, else: #in node has inputs, get those this_X = np.hstack([transformed_steps[child] for child in get_ordered_successors(graph, node)]) - - subset_indexes = None - if subset_col is not None and "subset_values" in graph.nodes[node]: - #get indexes of subset_col that are in subset_values - subset_values = graph.nodes[node]["subset_values"] - subset_indexes = np.where(np.isin(subset_col, subset_values))[0] - # Removed so that the cache is the same for all models. Not including transform would index it seperately #if i == len(topo_sort)-1: #last method doesn't need transformed. # instance.fit(this_X, y) if issubclass(type(instance), sklearn.base.RegressorMixin) or issubclass(type(instance), sklearn.base.ClassifierMixin): - transformed, instance = estimator_fit_transform_override_cross_val_predict_cached(instance, this_X, y, cv=cross_val_predict_cv, method=method,subset_indexes=subset_indexes) + transformed, instance = estimator_fit_transform_override_cross_val_predict_cached(instance, this_X, y, cv=cross_val_predict_cv, method=method) else: - transformed, instance = fit_transform_one_cached(instance, this_X, y, subset_indexes=subset_indexes)#instance.fit_transform(this_X,y) + transformed, instance = fit_transform_one_cached(instance, this_X, y)#instance.fit_transform(this_X,y) graph.nodes[node]["instance"] = instance @@ -253,8 +232,6 @@ def __init__( cross_val_predict_cv=0, #signature function(estimator, X, y=none) method='auto', memory=None, #TODO memory caching like sklearn.pipeline - subset_column = None, - drop_subset_column = True, use_label_encoder=False, **kwargs, ): @@ -277,13 +254,6 @@ def __init__( memory: str or object with the joblib.Memory interface, optional Used to cache the fitted transformers of the pipeline. By default, no caching is performed. If a string is given, it is the path to the caching directory. - subset_column: int, optional - The column of X that contains the subset values. If None, all rows of X are used. If not None, only the rows of X where X[:,subset_column] is in subset_values are used. - Used to evolve pipelines where recursive graphs use different subsets of rows. - - drop_subset_column: bool, optional - If True, the subset_column is dropped from X before being passed to the pipeline. If False, the subset_column is kept in X. - use_label_encoder: bool, optional If True, the label encoder is used to encode the labels to be 0 to N. If False, the label encoder is not used. Mainly useful for classifiers (XGBoost) that require labels to be ints from 0 to N. @@ -296,8 +266,6 @@ def __init__( self.cross_val_predict_cv = cross_val_predict_cv self.method = method self.memory = memory - self.subset_column = subset_column - self.drop_subset_column = drop_subset_column self.use_label_encoder = use_label_encoder setup_ordered_successors(graph) @@ -327,17 +295,8 @@ def __str__(self): else: return str(self.graph.nodes) - def fit(self, X, y, subset_col = None): - # if self.subset_column is not None and self.subset_values is not None: - - # if isinstance(X, pd.DataFrame): - # indeces_to_keep = X[self.subset_column].isin(self._subset_values) - # X = X[indeces_to_keep] - # y = y[indeces_to_keep] - # else: - # indeces_to_keep = np.isin(X[:,self.subset_column], self._subset_values) - # X = X[indeces_to_keep] - # y = y[indeces_to_keep] + def fit(self, X, y): + if self.use_label_encoder: if type(self.use_label_encoder) == LabelEncoder: @@ -345,11 +304,7 @@ def fit(self, X, y, subset_col = None): else: y = self.label_encoder.fit_transform(y) - if self.subset_column is not None: - subset_col = X[:,self.subset_column] - if self.drop_subset_column: - X = np.delete(X, self.subset_column, axis=1) fit_sklearn_digraph( graph=self.graph, X=X, @@ -358,7 +313,6 @@ def fit(self, X, y, subset_col = None): cross_val_predict_cv = self.cross_val_predict_cv, memory = self.memory, topo_sort = self.topo_sorted_nodes, - subset_col = subset_col, ) return self @@ -380,11 +334,7 @@ def __sklearn_is_fitted__(self): @available_if(_estimator_has('predict')) def predict(self, X, **predict_params): - if self.subset_column is not None: - subset_col = X[:,self.subset_column] - if self.drop_subset_column: - X = np.delete(X, self.subset_column, axis=1) this_X = get_inputs_to_node(self.graph, X, @@ -402,9 +352,7 @@ def predict(self, X, **predict_params): @available_if(_estimator_has('predict_proba')) def predict_proba(self, X, **predict_params): - if self.subset_column is not None: - if self.drop_subset_column: - X = np.delete(X, self.subset_column, axis=1) + this_X = get_inputs_to_node(self.graph, X, @@ -416,9 +364,7 @@ def predict_proba(self, X, **predict_params): @available_if(_estimator_has('decision_function')) def decision_function(self, X, **predict_params): - if self.subset_column is not None: - if self.drop_subset_column: - X = np.delete(X, self.subset_column, axis=1) + this_X = get_inputs_to_node(self.graph, X, self.root, @@ -429,10 +375,6 @@ def decision_function(self, X, **predict_params): @available_if(_estimator_has('transform')) def transform(self, X, **predict_params): - - if self.subset_column is not None: - if self.drop_subset_column: - X = np.delete(X, self.subset_column, axis=1) this_X = get_inputs_to_node(self.graph, X, diff --git a/tpot2/search_spaces/nodes/estimator_node_custom_sampler.py b/tpot2/search_spaces/nodes/estimator_node_custom_sampler.py new file mode 100644 index 00000000..c53d4715 --- /dev/null +++ b/tpot2/search_spaces/nodes/estimator_node_custom_sampler.py @@ -0,0 +1,52 @@ +# try https://automl.github.io/ConfigSpace/main/api/hyperparameters.html +import tpot2 +import numpy as np +import pandas as pd +import sklearn +from tpot2 import config +from typing import Generator, List, Tuple, Union +import random +from ..base import SklearnIndividual, SklearnIndividualGenerator +from ConfigSpace import ConfigurationSpace + +class EstimatorNodeCustomIndividual(SklearnIndividual): + def __init__(self, method: type, + sample_func : callable, + rng=None) -> None: + super().__init__() + self.method = method + self.sample_func = sample_func + + self.hyperparameters = self.sample_func(rng) + + def mutate(self, rng=None): + rng = np.random.default_rng(rng) + self.hyperparameters = self.sample_func(rng) + return True + + def crossover(self, other, rng=None): + rng = np.random.default_rng(rng) + if self.method != other.method: + return False + + #loop through hyperparameters, randomly swap items in self.hyperparameters with items in other.hyperparameters + for hyperparameter in self.space: + if rng.choice([True, False]): + if hyperparameter in other.hyperparameters: + self.hyperparameters[hyperparameter] = other.hyperparameters[hyperparameter] + + def export_pipeline(self, **kwargs): + return self.method(**self.hyperparameters) + + def unique_id(self): + #return a dictionary of the method and the hyperparameters + return (self.method, self.hyperparameters) + +class EstimatorNodeCustom(SklearnIndividualGenerator): + def __init__(self, method : type, + sample_func: callable): + self.method = method + self.sample_func = sample_func + + def generate(self, rng=None): + return EstimatorNodeCustomIndividual(self.method, self.sample_func) \ No newline at end of file diff --git a/tpot2/tpot_estimator/estimator.py b/tpot2/tpot_estimator/estimator.py index 50dfa6e0..7465564c 100644 --- a/tpot2/tpot_estimator/estimator.py +++ b/tpot2/tpot_estimator/estimator.py @@ -83,10 +83,7 @@ def __init__(self, scorers, stepwise_steps = 5, - optuna_optimize_pareto_front = False, - optuna_optimize_pareto_front_trials = 100, - optuna_optimize_pareto_front_timeout = 60*10, - optuna_storage = "sqlite:///optuna.db", + #dask parameters n_jobs=1, @@ -418,10 +415,7 @@ def __init__(self, scorers, self.scatter = scatter - self.optuna_optimize_pareto_front = optuna_optimize_pareto_front - self.optuna_optimize_pareto_front_trials = optuna_optimize_pareto_front_trials - self.optuna_optimize_pareto_front_timeout = optuna_optimize_pareto_front_timeout - self.optuna_storage = optuna_storage + # create random number generator based on rngseed self.rng = np.random.default_rng(random_state) @@ -694,19 +688,7 @@ def ind_generator(rng): self.make_evaluated_individuals() - if self.optuna_optimize_pareto_front: - pareto_front_inds = self.pareto_front['Individual'].values - all_graphs, all_scores = tpot2.individual_representations.graph_pipeline_individual.simple_parallel_optuna(pareto_front_inds, objective_function, self.objective_function_weights, _client, storage=self.optuna_storage, steps=self.optuna_optimize_pareto_front_trials, verbose=self.verbose, max_eval_time_seconds=self.max_eval_time_seconds, max_time_seconds=self.optuna_optimize_pareto_front_timeout, **{"X": X, "y": y}) - all_scores = tpot2.utils.eval_utils.process_scores(all_scores, len(self.objective_function_weights)) - if len(all_graphs) > 0: - df = pd.DataFrame(np.column_stack((all_graphs, all_scores,np.repeat("Optuna",len(all_graphs)))), columns=["Individual"] + self.objective_names +["Parents"]) - for obj in self.objective_names: - df[obj] = df[obj].apply(convert_to_float) - - self.evaluated_individuals = pd.concat([self.evaluated_individuals, df], ignore_index=True) - else: - print("WARNING NO OPTUNA TRIALS COMPLETED") tpot2.utils.get_pareto_frontier(self.evaluated_individuals, column_names=self.objective_names, weights=self.objective_function_weights, invalid_values=["TIMEOUT","INVALID"]) @@ -821,7 +803,9 @@ def ind_generator(rng): self.selected_best_score = self.evaluated_individuals.loc[best_idx] - best_individual_pipeline = best_individual.export_pipeline(memory=self.memory, cross_val_predict_cv=self.cross_val_predict_cv) + #TODO + #best_individual_pipeline = best_individual.export_pipeline(memory=self.memory, cross_val_predict_cv=self.cross_val_predict_cv) + best_individual_pipeline = best_individual.export_pipeline() if self.preprocessing: self.fitted_pipeline_ = sklearn.pipeline.make_pipeline(sklearn.base.clone(self._preprocessing_pipeline), best_individual_pipeline ) diff --git a/tpot2/tpot_estimator/estimator_utils.py b/tpot2/tpot_estimator/estimator_utils.py index 47f31450..36e5c53c 100644 --- a/tpot2/tpot_estimator/estimator_utils.py +++ b/tpot2/tpot_estimator/estimator_utils.py @@ -12,6 +12,7 @@ def convert_parents_tuples_to_integers(row, object_to_int): else: return np.nan +#TODO add kwargs def apply_make_pipeline(graphindividual, preprocessing_pipeline=None): try: if preprocessing_pipeline is None: @@ -100,8 +101,8 @@ def recursive_with_defaults(config_dict, n_samples, n_features, classification, -def objective_function_generator(pipeline, x,y, scorers, cv, other_objective_functions, memory=None, cross_val_predict_cv=None, subset_column=None, step=None, budget=None, generation=1,is_classification=True): - pipeline = pipeline.export_pipeline(memory=memory, cross_val_predict_cv=cross_val_predict_cv, subset_column=subset_column) +def objective_function_generator(pipeline, x,y, scorers, cv, other_objective_functions, step=None, budget=None, generation=1, is_classification=True, **pipeline_kwargs): + pipeline = pipeline.export_pipeline(**pipeline_kwargs) if budget is not None and budget < 1: if is_classification: x,y = sklearn.utils.resample(x,y, stratify=y, n_samples=int(budget*len(x)), replace=False, random_state=1) @@ -127,9 +128,9 @@ def objective_function_generator(pipeline, x,y, scorers, cv, other_objective_fun return np.concatenate([cv_obj_scores,other_scores]) -def val_objective_function_generator(pipeline, X_train, y_train, X_test, y_test, scorers, other_objective_functions, memory, cross_val_predict_cv, subset_column): +def val_objective_function_generator(pipeline, X_train, y_train, X_test, y_test, scorers, other_objective_functions, **pipeline_kwargs): #subsample the data - pipeline = pipeline.export_pipeline(memory=memory, cross_val_predict_cv=cross_val_predict_cv, subset_column=subset_column) + pipeline = pipeline.export_pipeline(**pipeline_kwargs) fitted_pipeline = sklearn.base.clone(pipeline) fitted_pipeline.fit(X_train, y_train) diff --git a/tpot2/tpot_estimator/templates/tpot_autoimputer.py b/tpot2/tpot_estimator/templates/tpot_autoimputer.py new file mode 100644 index 00000000..e69de29b From a28b62ff501e942a0fa2ae4cd6ef3cc8fec7e983 Mon Sep 17 00:00:00 2001 From: perib Date: Tue, 26 Mar 2024 19:06:45 -0700 Subject: [PATCH 4/7] tutorials fixes --- Tutorial/1_Estimators_Overview.ipynb | 967 +--- ...Defining_Search_Space_(config_dicts).ipynb | 478 -- Tutorial/2_Search_Spaces.ipynb | 4320 ++++++++++++++++- Tutorial/3_Feature_Set_Selector.ipynb | 1244 +++++ .../3_Genetic_Feature_Set_Selectors.ipynb | 1147 ----- ...mbolic_Regression_and_Classification.ipynb | 130 +- Tutorial/5_Genetic_Feature_Selection.ipynb | 596 +++ ...phPipeline.ipynb => 6_GraphPipeline.ipynb} | 0 Tutorial/7_dask_parallelization.ipynb | 107 +- ...ipynb => 8_SH_and_early_termination.ipynb} | 68 +- ...ynb => 9_Genetic_Algorithm_Overview.ipynb} | 0 setup.py | 3 +- tpot2/config/autoqtl_builtins.py | 15 +- tpot2/config/classifiers_sklearnex.py | 9 +- tpot2/config/get_configspace.py | 232 +- tpot2/config/mdr_configs.py | 15 +- tpot2/config/regressors_sklearnex.py | 28 - tpot2/config/special_configs.py | 28 +- tpot2/search_spaces/base.py | 2 +- tpot2/search_spaces/nodes/__init__.py | 3 +- tpot2/search_spaces/nodes/estimator_node.py | 2 +- tpot2/search_spaces/nodes/fss_node.py | 79 + .../nodes/genetic_feature_selection.py | 6 +- tpot2/search_spaces/pipelines/graph.py | 6 +- tpot2/search_spaces/pipelines/sequential.py | 2 +- tpot2/tpot_estimator/estimator_utils.py | 3 +- .../tpot_estimator/templates/tpottemplates.py | 50 +- 27 files changed, 6692 insertions(+), 2848 deletions(-) delete mode 100644 Tutorial/2_Defining_Search_Space_(config_dicts).ipynb create mode 100644 Tutorial/3_Feature_Set_Selector.ipynb delete mode 100644 Tutorial/3_Genetic_Feature_Set_Selectors.ipynb create mode 100644 Tutorial/5_Genetic_Feature_Selection.ipynb rename Tutorial/{5_GraphPipeline.ipynb => 6_GraphPipeline.ipynb} (100%) rename Tutorial/{6_SH_and_early_termination.ipynb => 8_SH_and_early_termination.ipynb} (97%) rename Tutorial/{8_Genetic_Algorithm_Overview.ipynb => 9_Genetic_Algorithm_Overview.ipynb} (100%) diff --git a/Tutorial/1_Estimators_Overview.ipynb b/Tutorial/1_Estimators_Overview.ipynb index bea7facb..33a71097 100644 --- a/Tutorial/1_Estimators_Overview.ipynb +++ b/Tutorial/1_Estimators_Overview.ipynb @@ -21,34 +21,100 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 2, "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Generation: : 0it [00:00, ?it/s]/home/ribeirop/common/Projects/TPOT_Dev/tpot2/tpot2/population.py:204: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '[0.9910779766159422, 0.9164180506462885, 0.9842489682733572, 0.99664936783213, 0.9913591306204854, 0.9785097508524515, 0.9843199854934415, 0.9981583597446381, 0.99559929270021, 0.9511441780591989, 0.9984744292898663, 0.9974402833866118, 0.9914842901220224, 0.9969100719668479, 0.9909145271063142, 0.9910709279190263, 0.9411694123791475, 0.9910354966095938, 0.9776626614599555, 0.9911887873368403, 0.9966903342486351, 0.9988343538601064, 0.9735967719140286, 0.9968575356141441, 0.9958485748358322, 0.9992471065344972, 0.9605917171252578, 0.9904942837739565, 0.9974574181131549, 0.9996403182930008, 0.9694102480973864, 0.9984821310846055, 0.9940551825220357, 0.9837735643634151, 0.9671044961833003, 0.9913835311537978, 0.9989793765342894, 0.9997847101769164, 0.991564988067797, 0.9988538844163573, 0.9895795999679059, 0.9750578580595717, 0.9971245111678281, 0.997177499370075, 0.9988702870584362, 'INVALID', 0.9131272065575761, 'INVALID', 0.9969386481385651, 'INVALID']' has dtype incompatible with float64, please explicitly cast to a compatible dtype first.\n", + " self.evaluated_individuals.loc[key,column_names] = data\n", + "Generation: : 1it [00:15, 15.14s/it]/home/ribeirop/common/Projects/TPOT_Dev/tpot2/tpot2/population.py:381: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value 'ind_crossover' has dtype incompatible with float64, please explicitly cast to a compatible dtype first.\n", + " self.evaluated_individuals.at[new_child.unique_id(),\"Variation_Function\"] = var_op\n", + "Generation: : 3it [01:30, 30.07s/it]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.9998423966736188\n" + ] + } + ], "source": [ - "### Best Practices\n", + "import tpot2\n", + "import sklearn\n", + "import sklearn.datasets\n", "\n", - "When running tpot from an .py script, it is important to protect code with `if __name__==\"__main__\":`" + "scorer = sklearn.metrics.get_scorer('roc_auc_ovo')\n", + "X, y = sklearn.datasets.load_digits(return_X_y=True)\n", + "X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n", + "\n", + "\n", + "est = tpot2.TPOTClassifier(n_jobs=4, max_time_seconds=60, verbose=2)\n", + "est.fit(X_train, y_train)\n", + "\n", + "\n", + "print(scorer(est, X_test, y_test))" ] }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "Evaluations: : 19it [00:30, 1.59s/it]\n" + "Generation: : 1it [00:03, 3.46s/it]/home/ribeirop/common/Projects/TPOT_Dev/tpot2/tpot2/population.py:381: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value 'ind_mutate' has dtype incompatible with float64, please explicitly cast to a compatible dtype first.\n", + " self.evaluated_individuals.at[new_child.unique_id(),\"Variation_Function\"] = var_op\n", + "/home/ribeirop/common/Projects/TPOT_Dev/tpot2/tpot2/population.py:204: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '['INVALID', -6039.425686838566, -3697.10004861336, -3609.502376363637, -5283.160282257095, -3081.9675902170966, -3159.936813075531, -6.206059150347736e+26, -2983.0962978018993, -3310.997549085264, -3255.911452949286, -3437.783411085481, -6124.753602783256, -2983.0962978018993, 'INVALID', -2951.7123134502217, -3172.715957996382, -3203.3174204236266, -3172.204242312273, -3132.3227284760787, -2904.2526718694835, 'INVALID', 'INVALID', -3065.7604709862753, -2887.332924732963, -3746.3702654907283, -4323.858875859902, -3277.5596662104786, -3687.211654454998, -3801.6275361827234, -3635.7122609143, -3657.0684867411182, -6050.448478793622, -3272.2301971831594, 'INVALID', 'INVALID', -3507.5157340117594, -3291.107812406151, -3673.8549030697295, 'INVALID', -3455.0876361736764, 'INVALID', -3201.783121939595, -2912.050142543978, -3808.6567781967847, 'INVALID', 'INVALID', -2895.0114530615692, -2947.503341616811, -3662.3274712695893]' has dtype incompatible with float64, please explicitly cast to a compatible dtype first.\n", + " self.evaluated_individuals.loc[key,column_names] = data\n", + "Generation: : 9it [00:32, 3.63s/it]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "0.9996046124012956\n" + "-3453.3557493847698\n" ] } ], + "source": [ + "import tpot2\n", + "import sklearn\n", + "import sklearn.metrics\n", + "import sklearn.datasets\n", + "\n", + "scorer = sklearn.metrics.get_scorer('neg_mean_squared_error')\n", + "X, y = sklearn.datasets.load_diabetes(return_X_y=True)\n", + "X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n", + "\n", + "est = tpot2.tpot_estimator.templates.TPOTRegressor(n_jobs=4, max_time_seconds=30, verbose=2)\n", + "est.fit(X_train, y_train)\n", + "\n", + "print(scorer(est, X_test, y_test))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Best Practices\n", + "\n", + "When running tpot from an .py script, it is important to protect code with `if __name__==\"__main__\":`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "#my_analysis.py\n", "\n", @@ -159,24 +225,9 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Evaluations: : 117it [00:30, 3.85it/s]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.9974747474747474\n" - ] - } - ], + "outputs": [], "source": [ "import tpot2\n", "import sklearn\n", @@ -202,20 +253,9 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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y5aDx/1yr9lVV12VSy65JB/84Adf9PczZcqUzJ49O42N3r6BF3LGDjjomWeeyTXXXJPf/OY3mTp16iHrTjvttJx22mm55557cuqppx5cftttt2XixIlZvnx59u/fn/POOy/vfe97D9n2uuuuy/XXX58rrrgi119//SHrHn/88SxdujTbtm3LwoUL8+EPfzhJsm7dutxwxRW59Ikd+fvH1uThPXsyvaUl/2vbtvzr7HPS0tSUq1evynmnnJxTmsfk8YGB3HTa6Tm9rS339fTklDHNue1tb8vIyEj6h4by8N69Ob2t7SXP5v2HU1tbc+fZs9PU0JClTz2V72zfnn/u6sq/7NiRL854axaMH59fzZiRJ3fvzk9+8pN84hOfyLXXXpv9+/cfcobt7W9/e7761a9m7dq1uemmm7Jly5bDes1Wr16d3/72txk3btzh/+IA/sKffMBBra2tmTNnTu64444XXf/FL34xX//61w9Z9stf/jK33nprzjnnnJx//vnZu3dvNm3adMjPrFixIu9///uTJFdfffUh6y6//PKMGTMmM2fOzJ49ew4uP3XmzEw86aQ0NzTkvR0dWfH003ls3zM5/+RTcsqYMTmxsTELOzqyYu/TSZK/aWnJ6W1tSZJZba1Zvndv/ufmzVn1zDMZ23z4f8PuHRzMP65fl/etXJGbtmzO43+5KfLcceNy05YtuX3njowcOJDnDhzI+eefn5tvvjnf+MY3smvXrpxwwgmHNcbLvWYLFy4UdcBrJuyAg5qamnL33Xfnnnvuya233vqC9XPnzs348eOzdOnSg8uGh4fz3e9+N6tWrcqqVauyZcuWzJ8//7DHPPHEE19y3X++d90rnGhLy386WzajpTX3zJmbU1tb8/XNm/J/du7MzNbW/GGgP8OvcD+8b27bmne1t+feuefmm6efkedG/vyJ3P8xdWq+1tWV/qGhXP+ze9Pz5JO55pprsmTJkpx44ol5z3vek5UrVx7GEb/8a9ba2npY+wB4McIOOMS4cePys5/9LF/72tdy7733vmD9l770pdx0000Hv7/00kvzne985+DtP9auXfuCW4HMnTs3P/3pT5MkP/rRjw5rHn98/PE8OTCQwZGR/LLnqZw7blzOHvtf8tDePdk7+HyeGx7Ovz31VOb91TVySdL97LNpbWrKVZ2d+e+TJmd9/778TUtLZrW2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AIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAU8X8BMlMHJ2fHGe0AAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "fitted_pipeline = est.fitted_pipeline_ # access best pipeline directly\n", "fitted_pipeline.plot()" @@ -223,228 +263,9 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
roc_auc_scoreParentsVariation_FunctionIndividualSubmitted TimestampCompleted TimestampPareto_FrontInstance
00.994405NaNNaN['LogisticRegression_1']1.692231e+091.692231e+09NaN['LogisticRegression_1']
10.954484NaNNaN['DecisionTreeClassifier_1']1.692231e+091.692231e+09NaN['DecisionTreeClassifier_1']
21.000000NaNNaN['KNeighborsClassifier_1']1.692231e+091.692231e+091.0['KNeighborsClassifier_1']
30.994048NaNNaN['GradientBoostingClassifier_1']1.692231e+091.692231e+09NaN['GradientBoostingClassifier_1']
40.989841NaNNaN['ExtraTreesClassifier_1']1.692231e+091.692231e+09NaN['ExtraTreesClassifier_1']
...........................
1120.997540(105, 106)crossover['MLPClassifier_1']1.692231e+091.692231e+09NaN['MLPClassifier_1']
1130.998214(15,)mutate['KNeighborsClassifier_1']1.692231e+091.692231e+09NaN['KNeighborsClassifier_1']
1140.997619(67, 67)crossover[('MLPClassifier_1', 'StandardScaler_1')]1.692231e+091.692231e+09NaN[('MLPClassifier_1', 'StandardScaler_1')]
1150.996944(81,)mutate[('ExtraTreesClassifier_1', 'RBFSampler_1'), (...1.692231e+091.692231e+09NaN[('ExtraTreesClassifier_1', 'RBFSampler_1'), (...
1161.000000(90, 73)crossover[('MLPClassifier_1', 'MinMaxScaler_1')]1.692231e+091.692231e+09NaN[('MLPClassifier_1', 'MinMaxScaler_1')]
\n", - "

117 rows × 8 columns

\n", - "
" - ], - "text/plain": [ - " roc_auc_score Parents Variation_Function \\\n", - "0 0.994405 NaN NaN \n", - "1 0.954484 NaN NaN \n", - "2 1.000000 NaN NaN \n", - "3 0.994048 NaN NaN \n", - "4 0.989841 NaN NaN \n", - ".. ... ... ... \n", - "112 0.997540 (105, 106) crossover \n", - "113 0.998214 (15,) mutate \n", - "114 0.997619 (67, 67) crossover \n", - "115 0.996944 (81,) mutate \n", - "116 1.000000 (90, 73) crossover \n", - "\n", - " Individual Submitted Timestamp \\\n", - "0 ['LogisticRegression_1'] 1.692231e+09 \n", - "1 ['DecisionTreeClassifier_1'] 1.692231e+09 \n", - "2 ['KNeighborsClassifier_1'] 1.692231e+09 \n", - "3 ['GradientBoostingClassifier_1'] 1.692231e+09 \n", - "4 ['ExtraTreesClassifier_1'] 1.692231e+09 \n", - ".. ... ... \n", - "112 ['MLPClassifier_1'] 1.692231e+09 \n", - "113 ['KNeighborsClassifier_1'] 1.692231e+09 \n", - "114 [('MLPClassifier_1', 'StandardScaler_1')] 1.692231e+09 \n", - "115 [('ExtraTreesClassifier_1', 'RBFSampler_1'), (... 1.692231e+09 \n", - "116 [('MLPClassifier_1', 'MinMaxScaler_1')] 1.692231e+09 \n", - "\n", - " Completed Timestamp Pareto_Front \\\n", - "0 1.692231e+09 NaN \n", - "1 1.692231e+09 NaN \n", - "2 1.692231e+09 1.0 \n", - "3 1.692231e+09 NaN \n", - "4 1.692231e+09 NaN \n", - ".. ... ... \n", - "112 1.692231e+09 NaN \n", - "113 1.692231e+09 NaN \n", - "114 1.692231e+09 NaN \n", - "115 1.692231e+09 NaN \n", - "116 1.692231e+09 NaN \n", - "\n", - " Instance \n", - "0 ['LogisticRegression_1'] \n", - "1 ['DecisionTreeClassifier_1'] \n", - "2 ['KNeighborsClassifier_1'] \n", - "3 ['GradientBoostingClassifier_1'] \n", - "4 ['ExtraTreesClassifier_1'] \n", - ".. ... \n", - "112 ['MLPClassifier_1'] \n", - "113 ['KNeighborsClassifier_1'] \n", - "114 [('MLPClassifier_1', 'StandardScaler_1')] \n", - "115 [('ExtraTreesClassifier_1', 'RBFSampler_1'), (... \n", - "116 [('MLPClassifier_1', 'MinMaxScaler_1')] \n", - "\n", - "[117 rows x 8 columns]" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "#view the summary of all evaluated individuals as a pandas dataframe\n", "est.evaluated_individuals" @@ -481,26 +302,9 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Evaluations: : 143it [00:30, 4.74it/s]\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_sag.py:350: ConvergenceWarning: The max_iter was reached which means the coef_ did not converge\n", - " warnings.warn(\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.9934232434232434\n" - ] - } - ], + "outputs": [], "source": [ "import tpot2\n", "import sklearn\n", @@ -526,20 +330,9 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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09azPWdv3Dgq59MdjOaRkZz9m42ZsL0nd69cmaGhofdkuwBvl7ADDntdXV0ZeKY7ix577F3fU/eHWoeHs2jjY+nv7s599933nm4b4K0SdsBhraenJw92dWXe5s3vyoUSB2Py4GA+sGlzfr1qVbZv335IZgA4GMIOOKzdt2pVJvb1ZU539yGdY253dyb29aVr1apDOgfAmxF2wGFrYGAgT23enJO2bH3Pzqs7kOaRkZy4ZWue2rQpAwMDh3QWgAMRdsBha/369RkzOJjj+voO9ShJkpl9fWkdHMyGDRsO9SgAb0jYAYelRqORR9auzayt297W14S9G1qGhzN727ZsWLMmjTf5HlqAQ0XYAW9qypQp73iNL3/5y3niiScO+PgNN9yQV155Zf/PF1xwQfr7+7N39+5M6+//o+d/bsOGXLzmoVy6dm0uX/dwNu7a9Y5nPFjTnn91rv43mOuhhx7K17/+9VHb1q9//euceeaZGTNmTO66665RWxeoS9gB77qbbropJ5544gEf/8Ow++Uvf5ne3t6MDA3lmANE29/MOzl3LlyYP586Lf/j6afe8YyNgzyHb/Lu3RkZGkpvb+8fPXbmmWdmxYoV73iW35s+fXq+//3v58orrxy1NYHahB3wlq1duzZnnXVWTjvttHzhC1/I3r17kyQ/+clPMnfu3HzoQx/Kl770pXzta19Lkpx//vl59NFH02g08rnPfS7z58/Paaedlptvvjk33nhjenp6cu6552bp0qVJXt1L2Nvbm4l79uR/bnk6n1y7JpeuXZOb3+DK2EWTJmXHyy8neTXO/tuTT+bydQ/n0rVrs/LZZ5Mkg41G/u3Gjfnnax7Kf9y0Kec/+OvsbjTywM6d+cIjG/Ll3zyaP9+wPoONRr6x6be5fN3Duezhh9P12kUS9+/c+doMa/PZNQ9l4p49Wb16dRYuXJgFCxZkwYIFefbZZ3PvvffmM5/5TJKkr68vl156aU4//fScf/75efrpp5MkV111Va699tqcc845mTNnTn71q18d8O983HHH5Ywzzkhzs49q4OC0HuoBgCPPF7/4xdx00005++yzs3z58nznO9/J8uXLc80116SrqytTp07NhRdemDPPPPN1r1u3bl2eeuqpbNy4MUnywgsvZPLkyVmxYkXuu+++TJw4cf9zn9uxI0+tX5/VO3fmjgUfzNjm5uzct++PZrm3vz8f63hfkuT23h35s7Fjc8eCD2Zvo5F/sX59zmtvz496d2TG+HH5zvz56do5kDue/ce9bY/u2pV/WLgonePG5VtPP50LOjry3+d+IP379uXKDevzfxYuys3d3flP7z8hi9vb89LQUDb2D+R/3357li9fnquvvjp79uxJS0vL6+a67rrrct555+XOO+/MbbfdlmuuuSYrV65MkvT39+f+++/PL37xi3zzm9/MPffcMzr/McCfPGEHvCU7d+7Myy+/nLPPPjtJ8vnPfz4rVqzIRz/60cybNy/HHXdckuSKK67Ili1bXvfaE044IT09PfnKV76ST33qU7nooosOuJ2X9+zJY888kys6p2bsa3usjhkzZv/j/+7xx/LK8HB2NRpZ+cGFSZKugYFsGhzMT557dU/drsZQtu3dm7UvvpR//dpci49pzzGt//jRt3DSpHSOG/fq63cO5N7+5/OdbduSJHsajfTt25eFkybl+qefzhN7BvPPphybsUNDOeH44/Otb30rzz//fJYtW5YTTjjhdfOvWrUqd999d5Jk2bJlufbaa/c/9ulPfzpJsmjRov178gBGg7ADRsXIQZyj1t7enkceeSR33313vv3tb+fnP/95rr/++jd87nCjkbzJmn8z7+TMaWvLf33qyfz1k0/kxpPnZzjJfznppJw1+Zg/nO6A60z4J4c5h0dG8r35p2TG+PGve86/mTkzH2lvz70D/Vm2fl3+86yZOevDF+Y//OVf5s4778zHP/7x3H777W/63puamvb/e9xrIdnS0uLqWmBUOXEDeEuOOeaYjBs3Lg8++GCS5NZbb81HPvKRzJs3L48//ni6u7vTaDRyxx13/NFr+/r6Mjw8nGXLluW6667LunXrkiRHH310Xnrppdc9t7mlJadNn56/792RV1673ckfHoptamrKv599fNa9+GKeHBzMkmPac+v27fsvhNi0e3caIyP54KRJ+YfX7oW3eufO7BwaesP3tri9PT/o6dn/8++vtt26Z09Onjgxy2fOyoltbendtTvPDwzkxBNPzFe/+tVcdNFF+w8v/96SJUvywx/+MEnyox/9KGedddb//48L8A7ZYwe8qYGBgf2HV5NkxYoVueWWW7J8+fLs3bs3CxYsyPLlyzN+/PjccMMNueCCCzJ58uTMmzcvkyZNet1a3d3dueqqqzI8PJzW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EcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUMT/A0Aw27cq4N4uAAAAAElFTkSuQmCC", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "fitted_pipeline = est.fitted_pipeline_ # access best pipeline directly\n", "fitted_pipeline.plot() #plot the best pipeline" @@ -554,240 +347,9 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
roc_auc_scorecomplexity_scorerParentsVariation_FunctionIndividualSubmitted TimestampCompleted TimestampPareto_FrontInstance
01.015.0NaNNaN['LogisticRegression_1']1.692231e+091.692231e+09NaN['LogisticRegression_1']
10.9661945.0NaNNaN['DecisionTreeClassifier_1']1.692231e+091.692231e+09NaN['DecisionTreeClassifier_1']
20.997467.0NaNNaN['KNeighborsClassifier_1']1.692231e+091.692231e+09NaN['KNeighborsClassifier_1']
30.99642915064.0NaNNaN['GradientBoostingClassifier_1']1.692231e+091.692231e+09NaN['GradientBoostingClassifier_1']
40.9957142802.0NaNNaN['ExtraTreesClassifier_1']1.692231e+091.692231e+09NaN['ExtraTreesClassifier_1']
..............................
1380.56.0(98,)mutate[('BernoulliNB_1', 'SelectFromModel_ExtraTrees...1.692231e+091.692231e+09NaN[('BernoulliNB_1', 'SelectFromModel_ExtraTrees...
1390.854961.0(82, 87)crossover['MultinomialNB_1']1.692231e+091.692231e+09NaN['MultinomialNB_1']
1400.9975798210.0(86,)mutate['ExtraTreesClassifier_1']1.692231e+091.692231e+09NaN['ExtraTreesClassifier_1']
1410.97900814.8(98,)mutate['SGDClassifier_1']1.692231e+091.692231e+09NaN['SGDClassifier_1']
1420.51500.0(2,)mutate['XGBClassifier_1']1.692231e+091.692231e+09NaN['XGBClassifier_1']
\n", - "

143 rows × 9 columns

\n", - "
" - ], - "text/plain": [ - " roc_auc_score complexity_scorer Parents Variation_Function \\\n", - "0 1.0 15.0 NaN NaN \n", - "1 0.96619 45.0 NaN NaN \n", - "2 0.99746 7.0 NaN NaN \n", - "3 0.996429 15064.0 NaN NaN \n", - "4 0.995714 2802.0 NaN NaN \n", - ".. ... ... ... ... \n", - "138 0.5 6.0 (98,) mutate \n", - "139 0.85496 1.0 (82, 87) crossover \n", - "140 0.997579 8210.0 (86,) mutate \n", - "141 0.979008 14.8 (98,) mutate \n", - "142 0.5 1500.0 (2,) mutate \n", - "\n", - " Individual Submitted Timestamp \\\n", - "0 ['LogisticRegression_1'] 1.692231e+09 \n", - "1 ['DecisionTreeClassifier_1'] 1.692231e+09 \n", - "2 ['KNeighborsClassifier_1'] 1.692231e+09 \n", - "3 ['GradientBoostingClassifier_1'] 1.692231e+09 \n", - "4 ['ExtraTreesClassifier_1'] 1.692231e+09 \n", - ".. ... ... \n", - "138 [('BernoulliNB_1', 'SelectFromModel_ExtraTrees... 1.692231e+09 \n", - "139 ['MultinomialNB_1'] 1.692231e+09 \n", - "140 ['ExtraTreesClassifier_1'] 1.692231e+09 \n", - "141 ['SGDClassifier_1'] 1.692231e+09 \n", - "142 ['XGBClassifier_1'] 1.692231e+09 \n", - "\n", - " Completed Timestamp Pareto_Front \\\n", - "0 1.692231e+09 NaN \n", - "1 1.692231e+09 NaN \n", - "2 1.692231e+09 NaN \n", - "3 1.692231e+09 NaN \n", - "4 1.692231e+09 NaN \n", - ".. ... ... \n", - "138 1.692231e+09 NaN \n", - "139 1.692231e+09 NaN \n", - "140 1.692231e+09 NaN \n", - "141 1.692231e+09 NaN \n", - "142 1.692231e+09 NaN \n", - "\n", - " Instance \n", - "0 ['LogisticRegression_1'] \n", - "1 ['DecisionTreeClassifier_1'] \n", - "2 ['KNeighborsClassifier_1'] \n", - "3 ['GradientBoostingClassifier_1'] \n", - "4 ['ExtraTreesClassifier_1'] \n", - ".. ... \n", - "138 [('BernoulliNB_1', 'SelectFromModel_ExtraTrees... \n", - "139 ['MultinomialNB_1'] \n", - "140 ['ExtraTreesClassifier_1'] \n", - "141 ['SGDClassifier_1'] \n", - "142 ['XGBClassifier_1'] \n", - "\n", - "[143 rows x 9 columns]" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "est.evaluated_individuals" ] @@ -801,113 +363,18 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
roc_auc_scorecomplexity_scorerParentsVariation_FunctionIndividualSubmitted TimestampCompleted TimestampPareto_FrontInstance
570.50.0(56,)mutate['LogisticRegression_1']1.692231e+091.692231e+091.0['LogisticRegression_1']
1371.01.0(82,)mutate[('MultinomialNB_1', 'SelectFromModel_ExtraTre...1.692231e+091.692231e+091.0[('MultinomialNB_1', 'SelectFromModel_ExtraTre...
\n", - "
" - ], - "text/plain": [ - " roc_auc_score complexity_scorer Parents Variation_Function \\\n", - "57 0.5 0.0 (56,) mutate \n", - "137 1.0 1.0 (82,) mutate \n", - "\n", - " Individual Submitted Timestamp \\\n", - "57 ['LogisticRegression_1'] 1.692231e+09 \n", - "137 [('MultinomialNB_1', 'SelectFromModel_ExtraTre... 1.692231e+09 \n", - "\n", - " Completed Timestamp Pareto_Front \\\n", - "57 1.692231e+09 1.0 \n", - "137 1.692231e+09 1.0 \n", - "\n", - " Instance \n", - "57 ['LogisticRegression_1'] \n", - "137 [('MultinomialNB_1', 'SelectFromModel_ExtraTre... " - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "est.pareto_front" ] }, { "cell_type": "code", - "execution_count": 27, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjcAAAGxCAYAAACeKZf2AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAw9ElEQVR4nO3de1RU9f7/8deAwKACagqiknjLWyqKSujx16kov9UxzU55uqhZ2vFaScsLmeIlxTqlttLSvGRlHamO2kUOaXQ8JlKUtzK85CW1AtRMUEtu8/n90XI6E6gwDoxsn4+19lrNZz6fvd/zyZxXe39mb5sxxggAAMAifLxdAAAAgCcRbgAAgKUQbgAAgKUQbgAAgKUQbgAAgKUQbgAAgKUQbgAAgKUQbgAAgKXU8HYBVc3hcOjHH39UUFCQbDabt8sBAADlYIzRqVOn1KhRI/n4XPjczBUXbn788UdFRER4uwwAAOCGI0eOqEmTJhfsc8WFm6CgIEm/TU5wcLCXqwEAAOWRn5+viIgI5/f4hVxx4ebcpajg4GDCDQAA1Ux5lpSwoBgAAFgK4QYAAFgK4QYAAFgK4QYAAFgK4QYAAFgK4QYAAFgK4QYAAFgK4QYAAFgK4QYAAFiKV8PNxo0b1adPHzVq1Eg2m01r1qy56JgNGzaoS5cuCggIUMuWLbV8+fJKrxMAAFxcicMoY/9Pem/7D8rY/5NKHMYrdXj18QtnzpxRp06d9NBDD6l///4X7X/w4EHdfvvtGj58uN58802lpaVp6NChCg8PV+/evaugYgAAUJbUndma9kGWsvPOOtvCQ+xK7NNO/3dteJXWYjPGeCdW/YHNZtPq1avVr1+/8/aZMGGC1q5dq507dzrb/va3v+nkyZNKTU0t13Hy8/MVEhKivLw8ni0FAIAHpO7M1ogVW/XHQHHuKVAvP9DlkgNORb6/q9Wam4yMDMXFxbm09e7dWxkZGV6qCACAK1uJw2jaB1mlgo0kZ9u0D7Kq9BJVtQo3OTk5CgsLc2kLCwtTfn6+fv311zLHFBQUKD8/32UDAACekXnwhMulqD8ykrLzzirz4Ikqq6lahRt3JCUlKSQkxLlFRER4uyQAACzj6KnzBxt3+nlCtQo3DRs2VG5urktbbm6ugoODFRgYWOaYhIQE5eXlObcjR45URakAAFwRQoPsHu3nCV79tVRFxcbGKiUlxaVt/fr1io2NPe+YgIAABQQEVHZpAABckbo3q6fwELty8s6Wue7GJqlhiF3dm9Wrspq8eubm9OnT2r59u7Zv3y7pt596b9++XYcPH5b021mXQYMGOfsPHz5cBw4c0Pjx47V792699NJLevvttzV27FhvlA8AwBXP18emxD7tJP3+66hzzr1O7NNOvj5/fLfyeDXcfPnll+rcubM6d+4sSYqPj1fnzp01ZcoUSVJ2drYz6EhSs2bNtHbtWq1fv16dOnXS888/ryVLlnCPGwAAvOj/rg3Xyw90UcMQ10tPDUPsHvkZeEVdNve5qSrc5wYAgMpR4jDKPHhCR0+dVWjQb5eiPHXGpiLf39VqzQ0AALh8+frYFNviKm+XUb1+LQUAAHAxhBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGAphBsAAGApXg83CxYsUGRkpOx2u2JiYpSZmXnevkVFRZo+fbpatGghu92uTp06KTU1tQqrBQAAlzuvhpvk5GTFx8crMTFRW7duVadOndS7d28dPXq0zP5PPfWUFi1apBdffFFZWVkaPny47rzzTm3btq2KKwcAAJcrmzHGeOvgMTEx6tatm+bPny9JcjgcioiI0JgxYzRx4sRS/Rs1aqRJkyZp1KhRzra77rpLgYGBWrFiRbmOmZ+fr5CQEOXl5Sk4ONgzHwQAAFSqinx/e+3MTWFhobZs2aK4uLjfi/HxUVxcnDIyMsocU1BQILvd7tIWGBioTZs2VWqtAACg+vBauDl+/LhKSkoUFhbm0h4WFqacnJwyx/Tu3Vtz5szRt99+K4fDofXr12vVqlXKzs4+73EKCgqUn5/vsgEAAOvy+oLiinjhhRfUqlUrtWnTRv7+/ho9erSGDBkiH5/zf4ykpCSFhIQ4t4iIiCqsGAAAVDWvhZv69evL19dXubm5Lu25ublq2LBhmWMaNGigNWvW6MyZMzp06JB2796t2rVrq3nz5uc9TkJCgvLy8pzbkSNHPPo5AADA5cVr4cbf31/R0dFKS0tztjkcDqWlpSk2NvaCY+12uxo3bqzi4mL961//Ut++fc/bNyAgQMHBwS4bAACwrhrePHh8fLwGDx6srl27qnv37po3b57OnDmjIUOGSJIGDRqkxo0bKykpSZL0+eef64cfflBUVJR++OEHTZ06VQ6HQ+PHj/fmxwAAAJcRr4abAQMG6NixY5oyZYpycnIUFRWl1NRU5yLjw4cPu6ynOXv2rJ566ikdOHBAtWvX1m233aY33nhDderU8dInAAAAlxuv3ufGG7jPDQAA1U+1uM8NAABAZSDcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAASyHcAAAAS7mkcFNYWKg9e/aouLjYU/UAAABcErfCzS+//KKHH35YNWvWVPv27XX48GFJ0pgxYzR79myPFggAAFARboWbhIQE7dixQxs2bJDdbne2x8XFKTk52WPFAQAAVFQNdwatWbNGycnJuu6662Sz2Zzt7du31/79+z1WHAAAQEW5debm2LFjCg0NLdV+5swZl7ADAABQ1dwKN127dtXatWudr88FmiVLlig2NtYzlQEAALjBrctSs2bN0q233qqsrCwVFxfrhRdeUFZWljZv3qz//ve/nq4RAACg3Nw6c/OnP/1JO3bsUHFxsTp06KB169YpNDRUGRkZio6O9nSNAAAA5VbhcFNUVKSHHnpINptNixcvVmZmprKysrRixQp16NChwgUsWLBAkZGRstvtiomJUWZm5gX7z5s3T61bt1ZgYKAiIiI0duxYnT17tsLHBQAA1lThcOPn56d//etfHjl4cnKy4uPjlZiYqK1bt6pTp07q3bu3jh49Wmb/t956SxMnTlRiYqJ27dqlpUuXKjk5WU8++aRH6gEAANWfW5el+vXrpzVr1lzywefMmaNhw4ZpyJAhateunRYuXKiaNWtq2bJlZfbfvHmzevbsqfvuu0+RkZG65ZZbdO+99170bA8AALhyuLWguFWrVpo+fbrS09MVHR2tWrVqubz/6KOPXnQfhYWF2rJlixISEpxtPj4+iouLU0ZGRpljevTooRUrVigzM1Pdu3fXgQMHlJKSooEDB573OAUFBSooKHC+zs/Pv2htAACg+nIr3CxdulR16tTRli1btGXLFpf3bDZbucLN8ePHVVJSorCwMJf2sLAw7d69u8wx9913n44fP64//elPMsaouLhYw4cPv+BlqaSkJE2bNq0cnwoAAFiBW+Hm4MGDnq6jXDZs2KBZs2bppZdeUkxMjPbt26fHHntMM2bM0OTJk8sck5CQoPj4eOfr/Px8RUREVFXJAACgirkVbv6XMUaSKnxn4vr168vX11e5ubku7bm5uWrYsGGZYyZPnqyBAwdq6NChkqQOHTrozJkzeuSRRzRp0iT5+JReQhQQEKCAgIAK1QYAAKovtxYUS9Lrr7+uDh06KDAwUIGBgerYsaPeeOONco/39/dXdHS00tLSnG0Oh0NpaWnnvcvxL7/8UirA+Pr6Svo9ZAEAgCubW2du5syZo8mTJ2v06NHq2bOnJGnTpk0aPny4jh8/rrFjx5ZrP/Hx8Ro8eLC6du2q7t27a968eTpz5oyGDBkiSRo0aJAaN26spKQkSVKfPn00Z84cde7c2XlZavLkyerTp48z5AAAgCubW+HmxRdf1Msvv6xBgwY52+644w61b99eU6dOLXe4GTBggI4dO6YpU6YoJydHUVFRSk1NdS4yPnz4sMuZmqeeeko2m01PPfWUfvjhBzVo0EB9+vTRzJkz3fkYAADAgmzGjes5drtdO3fuVMuWLV3av/32W3Xo0OGyvmNwfn6+QkJClJeXp+DgYG+XAwAAyqEi399urblp2bKl3n777VLtycnJatWqlTu7BAAA8Ai3LktNmzZNAwYM0MaNG51rbtLT05WWllZm6AEAAKgqbp25ueuuu/T555+rfv36WrNmjdasWaP69esrMzNTd955p6drBAAAKDe31txUZ6y5AQCg+qn0NTcpKSn66KOPSrV/9NFH+ve//+3OLgEAADzCrXAzceJElZSUlGo3xmjixImXXBQAAIC73Ao33377rdq1a1eqvU2bNtq3b98lFwUAAOAut8JNSEiIDhw4UKp93759qlWr1iUXBQAA4C63wk3fvn31+OOPa//+/c62ffv26YknntAdd9zhseIAAAAqyq1w8+yzz6pWrVpq06aNmjVrpmbNmqlt27a66qqr9Nxzz3m6RgAAgHJz6yZ+ISEh2rx5s9avX68dO3Y4nwr+//7f//N0fQAAABXisfvcnDx5UnXq1PHErioV97kBAKD6qfT73DzzzDNKTk52vr7nnnt01VVXqXHjxtqxY4c7uwQAAPAIt8LNwoULFRERIUlav3691q9fr3//+9+69dZbNW7cOI8WCAAAUBFurbnJyclxhpsPP/xQ99xzj2655RZFRkYqJibGowUCAABUhFtnburWrasjR45IklJTUxUXFyfptzsUl3XnYgAAgKri1pmb/v3767777lOrVq30008/6dZbb5Ukbdu2TS1btvRogQAAABXhVriZO3euIiMjdeTIET377LOqXbu2JCk7O1sjR470aIEAAAAV4bGfgpfl9ttv15IlSxQeHl5Zh6gwfgoOAED1U+k/BS+vjRs36tdff63MQwAAALio1HADAABQ1Qg3AADAUgg3AADAUgg3AADAUgg3AADAUio13Dz55JOqV69eZR4CAADAhVvhJikpScuWLSvVvmzZMj3zzDPO1wkJCapTp47bxQEAAFSUW+Fm0aJFatOmTan29u3ba+HChZdcFAAAgLvcCjc5OTll3nW4QYMGys7OvuSiAAAA3OVWuImIiFB6enqp9vT0dDVq1OiSiwIAAHCXWw/OHDZsmB5//HEVFRXpxhtvlCSlpaVp/PjxeuKJJzxaIAAAQEW4FW7GjRunn376SSNHjlRhYaEkyW63a8KECUpISPBogQAAABVxSU8FP336tHbt2qXAwEC1atVKAQEBnqytUvBUcAAAqp+KfH+7debmnNq1a6tbt26XsgsAAACPcivc3HDDDbLZbOd9/5NPPnG7IAAAgEvhVriJiopyeV1UVKTt27dr586dGjx4sCfqAgAAcItb4Wbu3Llltk+dOlWnT5++pIIAAAAuhUefLfXAAw+U+VgGAACAquLRcJORkSG73e7JXQIAAFSIW5el+vfv7/LaGKPs7Gx9+eWXmjx5skcKAwAAcIdb4SYkJMTltY+Pj1q3bq3p06frlltu8UhhAAAA7nAr3Lz66quergMAAMAjPLrmBgAAwNvcOnNTUlKiuXPn6u2339bhw4edz5c658SJEx4pDgAAoKLcOnMzbdo0zZkzRwMGDFBeXp7i4+PVv39/+fj4aOrUqR4uEQAAoPzcCjdvvvmmFi9erCeeeEI1atTQvffeqyVLlmjKlCn67LPPPF0jAABAubkVbnJyctShQwdJvz08My8vT5L0l7/8RWvXrvVcdQAAABXkVrhp0qSJsrOzJUktWrTQunXrJElffPGFAgICPFcdAABABbkVbu68806lpaVJksaMGaPJkyerVatWGjRokB566CGPFggAAFARNmOMudSdfPbZZ9q8ebNatWqlPn36eKKuSpOfn6+QkBDl5eUpODjY2+UAAIByqMj3t1s/Bf+j6667Ttddd12p9ttvv11LlixReHi4Jw4DAABwUZV6E7+NGzfq119/rcxDAAAAuOAOxQAAwFIINwAAwFIINwAAwFIINwAAwFIui3CzYMECRUZGym63KyYmRpmZmeft++c//1k2m63Udvvtt1dhxQAA4HJVqeHmySefVL169S7YJzk5WfHx8UpMTNTWrVvVqVMn9e7dW0ePHi2z/6pVq5Sdne3cdu7cKV9fX919992V8REAAEA149ZN/JKSkhQWFlbqbsTLli3TsWPHNGHChHLvKyYmRt26ddP8+fMlSQ6HQxERERozZowmTpx40fHz5s3TlClTlJ2drVq1al20PzfxAwCg+qnI97dbZ24WLVqkNm3alGpv3769Fi5cWO79FBYWasuWLYqLi/u9IB8fxcXFKSMjo1z7WLp0qf72t7+dN9gUFBQoPz/fZQMAANbl9lPBy7rrcIMGDZwP1CyP48ePq6SkRGFhYS7tYWFhysnJuej4zMxM7dy5U0OHDj1vn6SkJIWEhDi3iIiIctcHAACqH7fCTUREhNLT00u1p6enq1GjRpdcVHktXbpUHTp0UPfu3c/bJyEhQXl5ec7t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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "pareto_front = est.pareto_front\n", "\n", @@ -929,26 +396,9 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Generation: 100%|██████████| 5/5 [00:36<00:00, 7.20s/it]\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/neural_network/_multilayer_perceptron.py:686: ConvergenceWarning: Stochastic Optimizer: Maximum iterations (200) reached and the optimization hasn't converged yet.\n", - " warnings.warn(\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.998015873015873\n" - ] - } - ], + "outputs": [], "source": [ "import tpot2\n", "import sklearn\n", @@ -983,241 +433,6 @@ "The TPOTClassifier and TPOTRegressor are set default parameters for the TPOTEstimator for Classification and Regression.\n", "In the future, a metalearner will be used to predict the best values for a given dataset." ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "terminating parallel evaluation due to timeout\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 177.4640355714364, tolerance: 143.10199053030306\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 185.86338704440277, tolerance: 143.10199053030306\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 277.49028848926537, tolerance: 143.10199053030306\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 464.01662831846625, tolerance: 143.10199053030306\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 554.9558355270419, tolerance: 143.10199053030306\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 1480.8552755513228, tolerance: 143.10199053030306\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 2355.5063150407514, tolerance: 143.10199053030306\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 2081.571493001771, tolerance: 143.10199053030306\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 3868.126368656056, tolerance: 143.10199053030306\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 5331.3651033417555, tolerance: 143.10199053030306\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 6862.873289547279, tolerance: 143.10199053030306\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 8656.98141344823, tolerance: 143.10199053030306\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 4311.308985096635, tolerance: 143.10199053030306\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 5839.020132572099, tolerance: 143.10199053030306\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 5923.854209526442, tolerance: 143.10199053030306\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 280.8815573984757, tolerance: 168.2528\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 643.0934690993745, tolerance: 168.2528\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 565.7529498867225, tolerance: 168.2528\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 639.0793324268889, tolerance: 168.2528\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 796.3080264698947, tolerance: 168.2528\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 2132.9185444641626, tolerance: 168.2528\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 2674.6467641871423, tolerance: 168.2528\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 2568.991994333919, tolerance: 168.2528\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 1767.4389212469105, tolerance: 168.2528\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 1605.1388315662043, tolerance: 168.2528\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 1771.0119939564029, tolerance: 168.2528\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 1812.8362937605707, tolerance: 168.2528\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 2090.934535113978, tolerance: 168.2528\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 2720.6381011917256, tolerance: 168.2528\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 3694.640494319028, tolerance: 168.2528\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 5819.918714194559, tolerance: 168.2528\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 8499.700911721331, tolerance: 168.2528\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 9747.96645780711, tolerance: 168.2528\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 8925.452311816742, tolerance: 168.2528\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 242.2927812706912, tolerance: 166.10352603773586\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 520.11185573088, tolerance: 166.10352603773586\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 270.07291585509665, tolerance: 166.10352603773586\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 312.4193137688562, tolerance: 166.10352603773586\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 334.48251612263266, tolerance: 166.10352603773586\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 406.0909651533002, tolerance: 166.10352603773586\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 393.6330031697871, tolerance: 166.10352603773586\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 419.26211581844836, tolerance: 166.10352603773586\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 1105.061883097398, tolerance: 166.10352603773586\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 1492.2850051816786, tolerance: 166.10352603773586\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 1359.714203708456, tolerance: 166.10352603773586\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 1543.3692570256535, tolerance: 166.10352603773586\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 686.7691507576965, tolerance: 166.10352603773586\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 567.2123847292969, tolerance: 166.10352603773586\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 575.1844139498426, tolerance: 166.10352603773586\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 1196.4656488135224, tolerance: 166.10352603773586\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 2136.7159360550577, tolerance: 166.10352603773586\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 3161.7749671411, tolerance: 166.10352603773586\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 158.83327397913672, tolerance: 158.8069449056604\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 168.01972272712737, tolerance: 158.8069449056604\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 529.342575648101, tolerance: 158.8069449056604\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 811.6219812278869, tolerance: 158.8069449056604\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 601.5064170324476, tolerance: 158.8069449056604\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 459.8468100364553, tolerance: 158.8069449056604\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 171.07939504506066, tolerance: 158.8069449056604\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 475.0977421862772, tolerance: 158.8069449056604\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 1609.3130913197529, tolerance: 158.8069449056604\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 3371.636877565179, tolerance: 158.8069449056604\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 4893.275803661207, tolerance: 158.8069449056604\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 5689.945571509306, tolerance: 158.8069449056604\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 6327.594264068524, tolerance: 158.8069449056604\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 8071.667983187712, tolerance: 158.8069449056604\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 9214.471518416074, tolerance: 158.8069449056604\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 221.34985516022425, tolerance: 159.7256437735849\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 409.0736092341831, tolerance: 159.7256437735849\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 576.086710276315, tolerance: 159.7256437735849\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 759.3069202784682, tolerance: 159.7256437735849\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 788.3264070701553, tolerance: 159.7256437735849\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 1851.77406217705, tolerance: 159.7256437735849\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 1982.0810699927388, tolerance: 159.7256437735849\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 1990.0643707137788, tolerance: 159.7256437735849\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 1123.845644916175, tolerance: 159.7256437735849\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 466.2079415132757, tolerance: 159.7256437735849\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 1319.3072104484309, tolerance: 159.7256437735849\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 1714.5370268148836, tolerance: 159.7256437735849\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 1605.753956191009, tolerance: 159.7256437735849\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 5471.587720631971, tolerance: 159.7256437735849\n", - " model = cd_fast.enet_coordinate_descent_gram(\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_coordinate_descent.py:617: ConvergenceWarning: Objective did not converge. You might want to increase the number of iterations. Duality gap: 10655.474709162605, tolerance: 159.7256437735849\n", - " model = cd_fast.enet_coordinate_descent_gram(\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "-2514.9527497535055\n" - ] - } - ], - "source": [ - "import tpot2\n", - "import sklearn\n", - "import sklearn.metrics\n", - "import sklearn.datasets\n", - "\n", - "est = tpot2.tpot_estimator.templates.TPOTRegressor(n_jobs=4, max_time_seconds=10)\n", - "\n", - "\n", - "scorer = sklearn.metrics.get_scorer('neg_mean_squared_error')\n", - "X, y = sklearn.datasets.load_diabetes(return_X_y=True)\n", - "X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n", - "est.fit(X_train, y_train)\n", - "print(scorer(est, X_test, y_test))" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "terminating parallel evaluation due to timeout\n", - "0.9999694758670971\n" - ] - } - ], - "source": [ - "import tpot2\n", - "import sklearn\n", - "import sklearn.datasets\n", - "\n", - "est = tpot2.tpot_estimator.templates.TPOTClassifier(n_jobs=4, max_time_seconds=10)\n", - "\n", - "\n", - "scorer = sklearn.metrics.get_scorer('roc_auc_ovo')\n", - "X, y = sklearn.datasets.load_digits(return_X_y=True)\n", - "X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n", - "est.fit(X_train, y_train)\n", - "print(scorer(est, X_test, y_test))" - ] } ], "metadata": { @@ -1236,7 +451,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.11" + "version": "3.10.14" }, "orig_nbformat": 4, "vscode": { diff --git a/Tutorial/2_Defining_Search_Space_(config_dicts).ipynb b/Tutorial/2_Defining_Search_Space_(config_dicts).ipynb deleted file mode 100644 index efef82e8..00000000 --- a/Tutorial/2_Defining_Search_Space_(config_dicts).ipynb +++ /dev/null @@ -1,478 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Everything can be done with the TPOTEstimator class. All other classes (TPOTRegressor, TPOTClassifier, TPOTSymbolicClassifier, TPOTSymbolicRegression, TPOTGeneticFeatureSetSelector, etc.) are actually just different default settings for TPOTEstimator.\n", - "\n", - "\n", - "By Default, TPOT will generate pipelines with a default set of classifiers or regressors as roots (this depends on whether classification is set to true or false). All other nodes are selected from a default list of selectors and transformers. Note: This differs from the TPOT1 behavior where by default classifiers and regressors can appear in locations other than the root. You can modify the the search space for leaves, inner nodes, and roots (final classifiers) separately through built in options or custom configuration dictionaries.\n", - "\n", - "In this tutorial we will walk through using the built in configurations, creating custom configurations, and using nested configurations." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configuration Dictionaries\n", - "\n", - "The default configuration includes several machine learning estimators from sklearn. Sometimes we may want to change or restrict what is allowed. \n", - "\n", - "In TPOT2, we specify three different configuration dictionaries to indicate which modules can go where on the graph\n", - "\n", - "1. root_config_dict : Specifies the modules allowed to be placed in the root node. This is the final classifier or regressor. (It can also technically be used as a transformer if the scoring function knows that.). You are guaranteed a root node in every graph pipeline.\n", - "2. inner_config_dict : Specifies the modules allowed to be placed all nodes that are not the root node. If leaf_config_dict is set to None, then leaves will be pulled from this list. You are not guaranteed a node from this list however. It is still possible to end up with a graph that contains only a single root, or a root and a leaf even if this is set.\n", - "3. leaf_config_dict : Specifies the modules allowed to be placed as leafs. Unlike inner_config_dict, you are guaranteed to have a leaf node from this list if it is set. The smallest possible graph would thus be \\[leaf->root\\]. \n", - "\n", - "Note: TPOT1 internally divided the methods inside the configuration dictionary into selectors/transformers/estimators and treated them differently. TPOT2 does not. \n", - "\n", - "## Built in Defaults\n", - "\n", - "Each configuration dictionary parameter has access to the same default parameters. The default parameters can also be grouped into a list to combine their search spaces.\n", - "\n", - "- 'selectors' : A selection of sklearn Selector methods.\n", - "- 'classifiers' : A selection of sklearn Classifier methods.\n", - "- 'regressors' : A selection of sklearn Regressor methods.\n", - "- 'transformers' : A selection of sklearn Transformer methods.\n", - "- 'arithmetic_transformer' : A selection of sklearn Arithmetic Transformer methods that replicate symbolic classification/regression operators.\n", - "- 'passthrough' : A node that just passes though the input. Useful for passing through raw inputs into inner nodes.\n", - "- 'feature_set_selector' : A selector that pulls out specific subsets of columns from the data. Only well defined as a leaf.\n", - " Subsets are set with the subsets parameter.\n", - "- list : a list of strings out of the above options to include the corresponding methods in the configuration dictionary.\n", - "\n", - "\n", - "## Other search space parameters\n", - "\n", - "1. linear_pipeline : If True, pipelines will be linear\n", - "2. max_size : The maximum number of nodes in the pipeline.\n", - "\n", - "\n", - "\n", - "## defining configuration dictionaries\n", - "\n", - "Configuration dictionaries are python dictionaries where the keys are the method types and the values are optuna-compatible functions that take in a trial and return a hyperparameter dictionary.\n", - "\n", - "\n", - "Configuration dictionaries can also be nested. Meaning that the search space for that node, will be a graph defined by the nested dictionary. More on that later in the tutorial. \n", - "\n", - "With these three types of configuration dictionaries plus nesting, one can define very specific search spaces. More on nesting later." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1.0\n" - ] - }, - { - "data": { - "image/png": "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AIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgiP8PJSigRRDUHoIAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# A Linear pipeline starting with a selector, followed by 0 to 4 transformers, and ending with a classifier.\n", - "\n", - "import tpot2\n", - "import sklearn\n", - "import sklearn.datasets\n", - "\n", - "est = tpot2.TPOTEstimator( population_size=10,\n", - " generations=5,\n", - " scorers=['roc_auc_ovr'],\n", - " scorers_weights=[1],\n", - " classification=True,\n", - " root_config_dict=\"classifiers\",\n", - " inner_config_dict= \"transformers\",\n", - " leaf_config_dict=\"selectors\",\n", - " linear_pipeline=True,\n", - " max_size=6,\n", - "\n", - " early_stop=5,\n", - " verbose=0)\n", - "\n", - "scorer = sklearn.metrics.get_scorer('roc_auc_ovo')\n", - "X, y = sklearn.datasets.load_iris(return_X_y=True)\n", - "X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n", - "est.fit(X_train, y_train)\n", - "print(scorer(est, X_test, y_test))\n", - "est.fitted_pipeline_.plot()" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.9941520467836257\n" - ] - }, - { - "data": { - "image/png": "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DORHzz674/4xW3qzutHI2rVr9yvsvv3tb+/487XXXpsFCxa8bn0w5MJu9JYtO53XZt327RnfOizDml/Yqzxp+PC8b8LEfHnp0jzfaGRMa2tWbt2a5xqNzB0zJl9f9kw+2DEpc8eMSZI0NzXlw5Mn73Gbx43a8/8ZbN22Nc8/vzl9fdszvHtTOjo6doRdo9HIPffck9NPP/1AvHUA2GuDg4NZt25dkmTu3Ll55JFHsm7dumzYsCGNRmPHfS/p6enJiBEjXnb77uzpsZdddllmzpyZP/iDP8iNN96Ye+65J/Pnz8/Pf/7znHfeeRk1alTuvPPOnHrqqfnABz6Qu+++O1deeWXuu+++/OhHP0rPxo159JBDcsVRv9qDNjA4mMHBZDDJLWvX5q6uDenua2TssNb8l2OOyReefDJrtm1La1NTrpp5TOaMHp2uvu27vf22dc/mm8uXZ1hTc44YMTzXzjkh169amT+cNi0zRo5M8sJevwsmTdqx/db+gYzesiWf/exn09nZma1bt+biiy/OZz7zmfT39+fjH/94HnjggbS0tOTTn/50Lr744lx22WW59dZbM2LEiFxwwQX5/Oc/n/e85z355je/mWuvvTZPP/10zjzzzPzxH/9xFixYkA996EM555xzcv/99+fSSy/N5s2bM2XKlNxwww1pb2/PkUcemQ9/+MP58pe/vFef/ZAKu3WdnRm7y27YeePG5RvLl+V3Ft2feePG59zDD89bxozJu8e3584NG3J+R0f+Yf26/M6LZfxkb2/OO7xjdy+/1wYHB/Pss8+mf+BX+9zHb+pOxy71ff755+/XdgDgtdr1G7Ibbrhhj/e95Iorrtjr19/TY6+88sodf77llluSJE8++eRO2/3GN76RJLnwwguTJB+58MK8u7k53/v7v8+dzyzNW14Mra4tW7J129as7ezMtr6+PNbTkx+99eSMbm3NpY8/lt8/YlrmjhmTZ7ZsyWcefzw3v/Wt+W9PP73b269bsSLXzTkhR44cmedfvKrEk729uWTqEa/4Pg/t2pjz5s/PJ3//99NoNHLGGWfkoosuyrPPPpulS5fm0UcfTZJ0d3dnw4YNuemmm/LMM8+kubk53d3dO73WNddckx//+Me59957M3r06Cx48XQqfX19ufTSS3PLLbekvb09119/fb70pS/lq1/9apJk2rRpe/ORJBli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QtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABF/H/JqDCMIjPpWAAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# A Graph pipeline starting with at least one selector as a leaf, potentially followed by a series\n", - "# of stacking classifiers or transformers, and ending with a classifier. The graph will have at most 15 nodes.\n", - "\n", - "import tpot2\n", - "import sklearn\n", - "import sklearn.datasets\n", - "import numpy as np\n", - "\n", - "est = tpot2.TPOTEstimator( population_size=10,\n", - " generations=5,\n", - " scorers=['roc_auc_ovr'],\n", - " scorers_weights=[1],\n", - " classification=True,\n", - " root_config_dict=\"classifiers\",\n", - " inner_config_dict= [\"classifiers\",\"transformers\"],\n", - " leaf_config_dict=\"selectors\",\n", - " max_size=15,\n", - "\n", - " early_stop=5,\n", - " verbose=0)\n", - "\n", - "scorer = sklearn.metrics.get_scorer('roc_auc_ovo')\n", - "X, y = sklearn.datasets.load_iris(return_X_y=True)\n", - "X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n", - "est.fit(X_train, y_train)\n", - "print(scorer(est, X_test, y_test))\n", - "\n", - "est.fitted_pipeline_.plot()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Custom Configuration Dictionaries\n", - "\n", - "\n", - "Next, we will show how to use these features to define a graph pipeline search space similar to symbolic classification.\n", - "\n", - "The following defines a pipeline where leafs select a single feature, inner nodes perform arithmetic, and logistic regression is used as a final classifier.\n", - "\n", - "The arithmetic transformer and feature set selection of single columns are built in configurations with the \"arithmetic_transformer\" and \"feature_set_selector\" options respectively. \n", - "\n", - "There is not a built in configuration for a single logistic regression so we have to manually define one.\n", - "\n", - "### Parameter function\n", - "To start, we create a function that takes in a trial object. This object takes in a search space, and outputs a parameter. This is designed to be compatible with the optuna trial class. More information on available functions within trial can be found here: https://optuna.readthedocs.io/en/stable/reference/generated/optuna.trial.Trial.html\n", - "\n", - "The suggested parameters should be put into a dictionary that has the model parameters as keys with their corresponding values.\n", - "\n", - "Note: For optuna optimization to work, it is important to add '_{name}' to each of the names parameters. With large graphs, names of parameters will likely clash. The name parameter here allows TPOT2 to make sure each parameter for each node has a unique label. \n", - "\n", - "Note: This will be simplified in a future release.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "import tpot2\n", - "import numpy as np\n", - "def params_LogisticRegression(trial, name=None):\n", - " params = {}\n", - " params['solver'] = trial.suggest_categorical(name=f'solver_{name}',\n", - " choices=[f'newton-cg', 'lbfgs', 'liblinear', 'sag', 'saga'])\n", - " params['dual'] = False\n", - " params['penalty'] = 'l2'\n", - " params['C'] = trial.suggest_float(f'C_{name}', 1e-4, 1e4, log=True)\n", - " params['l1_ratio'] = None\n", - " if params['solver'] == 'liblinear':\n", - " params['penalty'] = trial.suggest_categorical(name=f'penalty_{name}', choices=['l1', 'l2'])\n", - " if params['penalty'] == 'l2':\n", - " params['dual'] = trial.suggest_categorical(name=f'dual_{name}', choices=[True, False])\n", - " else:\n", - " params['penalty'] = 'l1'\n", - "\n", - " params['class_weight'] = trial.suggest_categorical(name=f'class_weight_{name}', choices=['balanced'])\n", - " param_grid = {'solver': params['solver'],\n", - " 'penalty': params['penalty'],\n", - " 'dual': params['dual'],\n", - " 'multi_class': 'auto',\n", - " 'l1_ratio': params['l1_ratio'],\n", - " 'C': params['C'],\n", - " }\n", - " return param_grid" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### configuration dictionary\n", - "A configuration dictionary has the python Types for the designed estimator as keys, and the function as values." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "from sklearn.linear_model import LogisticRegression\n", - "root_config_dict = { LogisticRegression : params_LogisticRegression }" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_sag.py:350: ConvergenceWarning: The max_iter was reached which means the coef_ did not converge\n", - " warnings.warn(\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1.0\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAnYAAAHWCAYAAAD6oMSKAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAClUElEQVR4nOzdZ1wU1/s28GsLvRdFVECiYKGDJYIajSYBRA2xxr+KxJoYjbE3MKLYNRg1auwaE02MGn7WxBINJYqAINhQBAQEpfeyu/O8SNzHcUEFlp3d5f5+PnmRe2ZnblHx4pwzZ3gMwzAghBBCCCEqj891A4QQQgghRD4o2BFCCCGEqAkKdoQQQgghaoKCHSGEEEKImqBgRwghhBCiJijYEUIIIYSoCQp2hBBCCCFqgoIdIYQQQoiaoGBHCCGEEKImKNgRQgghhKgJCnaEEEIIIWqCgh0hhBBCiJqgYEcIIYQQoiYo2BFCCCGEqAkKdoQQQgghaoKCHSGEEEKImqBgRwghhBCiJijYEUIIIYSoCQp2hBBCCCFqgoIdIYQQQoiaoGBHCCGEEKImKNgRQgghhKgJCnaEEEIIIWqCgh0hhBBCiJqgYEcIIYQQoiYo2BFCCCGEqAkKdoQQQgghaoKCHSGEEEKImhBy3QAhhMiTWCxGQUEBcnNzkZubi+c5OaiurIRELAZfIICWjg5atWkDCwsLWFhYwNTUFAKBgOu2CSFELngMwzBcN0EIIU1VWFiIhIQE3I6LQ1V5ORiRCPqVlTAqKICGSAQ+w0DC46FWKESxqSnKdHTAEwqhracHJ3d3uLi4wMTEhOtfBiGENAkFO0KISsvOzkZURAQep6RAo6IC1hlPYFlQAKPycmiIxfV+rlYgQLGeHp6amiLD2gq1urqwtbODV9++sLS0VOCvgBBC5IeCHSFEJYlEIkRGRiImMhL6eXnolJ6B9nl5EEgkDb6WmM9Hprk5HtpYo8zcHD28vODl5QWhkFarEEJUCwU7QojKycnJwZnwcBRmZqFLSgrssrLAl8O3MgmPh5R27XDPzg6m7dvBd+hQtGnTRg4dE0KIYlCwI4SolPT0dJw8dgy62U/hcfcuDCsq5H6PEl1dxHbtioq2beE/ehRsbGzkfg9CCGkOFOwIISojPT0dv/38M8zSM9Dzzh0IGzHt+rZEfD6uO3RDgbU1hn/6KYU7QohKoH3sCCEqIScnByePHYNpegbeTU5u1lAHAEKJBL2TkmGakYGTx35BTk5Os96PEELkgYIdIUTpiUQinAkPh272U/S6c0cu6+neBp9h0Cv5DnSeZuNseDhEIpFC7ksIIY1FwY4QovQiIyNRmJkFj7t3m32k7lVCiQQed+6iICsLUVFRCr03IYQ0FAU7QohSy87ORkxkJLqkpDTLgxJvw6iiAp0fpOBGRASePn3KSQ+EEPI2KNgRQpRaVEQE9PPyYJeVxWkf9llZ0M/LQ2REBKd9EELI61CwI4QorcLCQjxOSUGn9AyFraurD59h0DE9A48fPEBhYSGnvRBCSH0o2BFClFZCQgI0KirQPi+P61YAAFZ5eRBWVCAxMZHrVgghpE4U7AghSkksFuN2XBysM5406jVhzUEgkcDmyRMkxsZC/Jr30BJCCFco2BFClFJBQQGqysthWVDAdSsslvn/9lWgZH0RQghAwY4Q8hpCoRCurq7S/yorKxt8jfXr1zfq3rm5uWBEIhiXlbHq2zLS4RsXC7+4WHxyKx5Pqqpee53dmU+a9Pme/0Sz/t+ovByMSITc3NzXfi4sLAw1NTWvPedt3Lp1C++++y4cHR3h7u6Ov/76q8nXJISoLyHXDRBClJexsTFu3brVpGusX78eCxYsaNBnxGIxcnNzoV9Zydq3Lq6kBNeLi/G7qxs0+HzkVFdDR/D6n093Z2ZiSnurRn/+VRpiMfQrK5GbmwtHR8d6zwsLC8PkyZOhqan5VteVSCTg82V70dPTw5EjR9CxY0fcuXMHfn5+SE1NbVDPhJCWg0bsCCENcuHCBfTu3Rtubm4YN26cdFRq6tSp8PDwgIODAzZu3AgAWLp0KYqKiuDq6orp06cjLS0N3bt3l15r3rx5OHDgAACgQ4cOWLRoEdzc3HD58mWcOH4cG/fvx5C4OKz+L8g8r6mBiVADGv8FoDZaWjASagAA/i4sxKiEWxgWH4d59++hRiLB5rQ0lIpEGBofh+CHKQ3+/Kt+yHyCT27FY+2ePdi/d6+0HhoaCicnJzg7O+Pbb7/F9u3bkZ2dDU9PTwwdOhQAcPjwYTg5OcHR0REbNmwAAKSlpcHJyQljxoxBt27d6hwRtbOzQ8eOHQEAXbt2RVlZGa3vI4TUi0bsCCH1ehHKAKB79+5Yu3YtNmzYgMuXL0NHRwfBwcHYvXs3ZsyYgbVr18LU1BQikQh9+/bF6NGjERoail27dklH/dLS0l57PysrK8THx+Pu3bu4ceMGQn180P1xGubfv48rBQXwMjbG1ox0+MTehJexCYa1bg0nAwMU1NZiT2YmDjk6QVsgwJb0NPySk4M5HTrgaM5ThLu5AwDKRKIGfX5c27bS3iIKC5FTXY3fXFwR9847CIm5gaSkJGRkZODy5cu4efMmtLS0UFBQAFNTU2zYsAFRUVHQ19dHVlYWvvnmG8TExEBXVxeenp54//33YWZmhrt37+LIkSNwdnZ+4+/HqVOn4OHhAYFA0KjfT0KI+qNgRwip16tTsadPn0ZiYiJ69+4NAKiursbgwYMBAD///DP27NkDsViMzMxM3Lt3D1ZWVg2638iRIwEAly5dwqPUVCx+/Bg6NTWoEkvgqK+PAaamOOXmjutFRYgqLkJgUhK2dOmCGkaC+xXlGJWYAACokUjQ39RU5vr6QmGjPx9RVIi/CgpxsyQelXeSUSEQ4MGDB4iIiEBgYCC0tLQAAKZ13DcmJgYDBw6UHhsxYgQiIiIwbNgw2Nvbv1WoS01NxYIFC3Du3LkGfEUJIS0NBTtCyFuTSCQYPHgw9u/fz6qnpqZi+/btiI6OhpGREUaMGIHq6mqZzwuFQkhemuJ89RxdXV3pfd7r2xefmprC7RF7PZmQx4OXiQm8TExgKtTAxYJ89DE2QX8TU6y1t3/jr6Gxn5cwwJfW1vjEwgLxHTuiqm8ffPLJJ4ho4psoXvyaX6egoADDhg3Drl270KlTpybdjxCi3miNHSHkrfXu3RtXrlxBeno6AKCkpASPHz9GaWkp9PX1YWhoiMzMTFy8eFH6GYFAIF0T1rp1a2RnZ6O0tBRlZWX4888/67zPwIEDERMbiwKRCACQX1ODZzU1SK2oQMZ/69AYhsGDinK01dKCm6EBrhcXIeu/J1zLRCLp064CHg/i/95a0ZjPv9DHxBi/5uagUixGjVCI4tJSFBcXY9CgQdi/f780pL7YBsXAwAClpaUAgJ49e+LSpUsoLCxEdXU1Tpw4gb59+77V17ympgb+/v6YO3cu3n///bf6DCGk5aIRO0LIW2vVqhV2796N4cOHo6amBnw+H2FhYejfvz+6du2KLl26oEOHDujTp4/0MwEBAXByckK/fv2wc+dOLFiwAG5ubrC2toaTk1Od93FwcMCEgACs2rMHYeXl0ODzsc7OHtWMBCGPHqHsv6DooKeP8ZZtoS0QYFUnO8y8dxe1Egl4PB6W2r4DK21t+Le2gF9cLHoYGWFUmzYN/vwL/UxM8bCiAqMSbqEk5QFM/4nGqE8/ha+vL2JjY+Hu7g4NDQ0EBgbiq6++wpQpUzBgwADY29sjPDwcy5cvR79+/cAwDAICAuDu7v7GNYcA8Msvv+Cff/5BcXExwsLCAPw7VW1mZtbI30VCiDrjMQzHL2AkhJA6JCUl4eyvv8Lv6jVoKNFToLUCAU6/1w++I0e+drsTQgjhAk3FEkKUkoWFBXhCIYr19LhuhaVYTw88oRAWFhZct0IIITJoKpYQopRMTU2hraeHp6amMC8p4bodqadm//ZV19OvTZGfn4+BAweyalpaWrh+/bpc70MIUW8U7AghSkkgEMDJ3R238vPRLSMDgjo2DFY0MZ+PdCsruDfDXnJmZmZNfssHIYTQVCwhRGm5uLigVlcXmebmrz2vViTCs+fPkP30KUpKm29074m5OUS6um+17xwhhHCBgh0hRGmZmJjA1s4OD22sIeHx6jxHwjAoKCiASCQCwKCsrAy1/22TIk8SHg+PbKxha28PExMTuV+fEELkgYIdIUSpefXtizJzc6S0a1fn8ZKSEojF8g9yr3rQrh3KzM3h9dJWLoQQomwo2BFClJqlpSV6eHnhnp0dSl55S0NVdTUqKspZNU1NLWgI5bt8uFhXF/ft7dCzTx9YWlrK9dqEECJPFOwIIUrPy8sLJu3bIbZrV4j4/37bkjAMioqKWOfxeHwYGxvL9d4iPh+x3brCtF07eHp6yvXahBAibxTsCCFKTygUYvDQoaho2xbXHbpBwuOhuLgYEgl742JDQ0MI5fi0qoTHw3WHbqi0bAvfoUMhlPNIICGEyBsFO0KISmjTpg38R49CgbU1Irp0RllNNeu4lpY29F6Zqm0KEZ+PaEcHFFhbw3/0KLRp00Zu1yaEkOZCwY4QojJsbGww0McHD/T0cKtfP1QYGgJ4MQVrJLf7FOvq4pq7G4o62GL4p5/CxsZGbtcmhJDmRO+KJYSoDIZhMHLkSPz9998YOngw2pmYwO7ePXR79hz62tpNvr6Ex8ODdu1w394Opu3awXfoUBqpI4SoFAp2hBCV8fPPP2Ps2LEA/n0zhaenJwZ4ecGyuhod0zNglZfXqDdUiPl8PDE3xyMba5SZm6Nnnz7w9PSkNXWEEJVDwY4QohKys7Ph6OiIwsJCac3MzAx//fUX7t29i8cPHkBYUQGbJ09gmV8Ao/JyaIjF9V6vViBAsZ4enpqZIt3KCiJdXdja28OLtjQhhKgw+nGUEKL0GIbB1KlTWaEOAHbs2AFHR0dp4EtMTERibCwelZeDEYmgX1kJw4JCaIpE4DMSSHh81AiFKDE1QZmODnhCIbT19ODu4QFnZ2d6owQhROXRiB0hROnt27cPkyZNYtVGjx6No0ePypwrFotRUFCA3Nxc5Obm4nlODmqqqiAWiSAQCqGprY1WbdrAwsICFhYWMDU1hUCOW6QQQgiXKNgRQpRaeno6nJycUFpaKq1ZWFggOTkZZmZmHHZGCCHKh7Y7IYQoLYlEgkmTJrFCHQDs3r2bQh0hhNSBgh0hRGnt3LkTly5dYtUmTpyIIUOGcNQRIYQoN5qKJYQopYcPH8LFxQUVFRXSWvv27ZGUlAQjI/ltRkwIIeqERuwIIUpHLBYjMDCQFeoAYO/evRTqCCHkNSjYEUKUTlhYGCIiIli16dOn48MPP+SoI0IIUQ00FUsIUSp3796Fm5sbqqurpTVbW1skJiZCX1+fw84IIUT50YgdIURpiEQiBAQEsEIdj8fDgQMHKNQRQshboGBHCFEa69atQ0xMDKs2e/Zs9OvXj6OOCCFEtdBULCFEKSQkJKBHjx6ora2V1jp37oz4+Hjo6Ohw2BkhhKgOGrEjhHCupqYGEyZMYIU6Pp+PgwcPUqgjhJAGoGBHCOFcSEgIEhMTWbWFCxeiV69eHHVECCGqiaZiCSGcunHjBjw9PSEWi6U1JycnxMTEQEtLi8POCCFE9VCwI4RwprKyEu7u7rh37560JhQKERMTA1dXV+4aI4QQFUVTsYQQzgQFBbFCHQAEBwdTqCOEkEaiETtCCCf+/vtvvPfee3j5W5CHhweio6OhoaHBYWeEEKK6KNgRQhSurKwMLi4uSE1Nlda0tLQQGxsLBwcHDjsjhBDVRlOxhBCFW7hwISvUAcDKlSsp1BFCSBPRiB0hRKEuXryIDz74gFXz9PTEtWvXIBAIOOqKEELUAwU7QojCFBcXw8nJCU+ePJHWdHR0kJCQADs7Ow47I4QQ9UBTsYQQhZkzZw4r1AHA+vXrKdQRQoic0IgdIUQhTp8+jSFDhrBqAwYMwMWLF8Hn08+YhBAiDxTsCCHNLj8/H46OjsjJyZHWDAwMkJiYiA4dOnDXGCGEqBn6MZkQ0uxmzpzJCnUAsHnzZgp1hBAiZzRiRwhpVsePH8fIkSNZNR8fH5w5cwY8Ho+jrgghRD1RsCOENJtnz57BwcEBeXl50pqxsTGSk5PRtm1bDjsjhBD1RFOxhJBmwTAMpk2bxgp1ALBt2zYKdYQQ0kwo2BFCmsWRI0dw6tQpVs3f3x9jx47lpiFCCGkBaCqWECJ3WVlZcHR0RFFRkbRmbm6O5ORktG7dmrvGCCFEzdGIHSFErhiGweTJk1mhDgB27txJoY4QQpoZBTtCiFzt3bsX58+fZ9XGjh2L4cOHc9QRIYS0HDQVSwiRm7S0NDg5OaGsrExas7S0RFJSEkxNTTnsjBBCWgYasSOEyIVEIsFnn33GCnUAsHv3bgp1hBCiIBTsCCFysX37dly5coVVmzRpEgYPHsxRR4QQ0vLQVCwhpMlSUlLg4uKCyspKac3a2hq3b9+GoaEhh50RQkjLQiN2hJAmEYvFCAgIYIU6ANi3bx+FOkIIUTAKdoSQJtm0aROio6NZtRkzZmDgwIEcdUQIIS0XTcUSQhotOTkZ7u7uqKmpkdY6duyIhIQE6OnpcdgZIYS0TDRiRwhplNraWkyYMIEV6ng8Hg4ePEihjhBCOELBjhDSKGvWrEFcXByrNnfuXHh5eXHUESGEEJqKJYQ0WFxcHHr16gWRSCStde3aFXFxcdDW1uawM0IIadloxI4Q0iDV1dUICAhghTqBQICDBw9SqCOEEI5RsCOENMg333yDpKQkVm3x4sXo0aMHRx0RQgh5gaZiCSFv7Z9//oGXlxckEom05uLighs3bkBTU5PDzgghhAAU7Aghb6miogJubm548OCBtKahoYGbN2/C2dmZw84IIYS8QFOxhJC3snTpUlaoA/6dlqVQRwghyoNG7Aghb3T16lX079+fVevZsyciIyMhFAq5aYoQQogMCnaEkNcqLS2Fi4sLHj9+LK1pa2sjPj4eXbp04bAzQgghr6KpWELIa82fP58V6gAgNDSUQh0hhCghGrEjhNTrwoUL8Pb2ZtX69u2LK1euQCAQcNQVIYSQ+lCwI4TUqaioCI6OjsjKypLWdHV1kZiYiI4dO3LYGSGEkPrQVCwhpE6zZ89mhToA2LhxI4U6QghRYjRiRwiRER4ejmHDhrFqgwYNwh9//AEej8dRV+RNxGIxCgoKkJubi9zcXDzPyUF1ZSUkYjH4AgG0dHTQqk0bWFhYwMLCAqampjSlToiaoWBHCGHJy8uDo6MjcnNzpTVDQ0Pcvn0b1tbWHHZG6lNYWIiEhATcjotDVXk5GJEI+pWVMCoogIZIBD7DQMLjoVYoRLGpKcp0dMATCqGtpwcnd3e4uLjAxMSE618GIUQOaAMqQgjLjBkzWKEOAMLCwijUKaHs7GxERUTgcUoKNCoqYJ3xBJYFBTAqL4eGWFzv52oFAhTr6eGpqSlu5ecjJjIStnZ28OrbF5aWlgr8FRBC5I1G7AghUseOHcOYMWNYNT8/P4SHh9MUrBIRiUSIjIxETGQk9PPy0Ck9A+3z8iB46R2+b0vM5yPT3BwPbaxRZm6OHl5e8PLyoo2nCVFRFOwIIQCAnJwcODg4oKCgQFozMTFBcnIyjeIokZycHJwJD0dhZha6pKTALisLfDl8G5fweEhp1w737Oxg2r4dfIcORZs2beTQMSFEkehHMkIIGIbBtGnTWKEOAL7//nsKdUokPT0dJ48dg272Uwy4exeGFRVyuzafYdA5MxOWBQWILemKo0XF8B89CjY2NnK7ByGk+dGIHSEEBw8exMSJE1m1ESNG4JdffqEpWCWRnp6O337+GWbpGeh55w6EjZh2fVsiPh/XHbqhwNoawz/9lMIdISqEgh0hLdyTJ0/g5OSE4uJiaa1169ZISkpCq1atOOyMvJCTk4Ojhw7B+HEaeicny2Xq9U0kPB6iHR1Q1MEWYyaMp2lZQlQEbVBMSAvGMAwmT57MCnUAsGvXLgp1SkIkEuFMeDh0s5+i1507Cgl1wL9Ts72S70DnaTbOhodDJBIp5L6EkKahYEdIC/bDDz/gjz/+YNXGjx+Pjz/+mJuGiIzIyEgUZmbB4+7dZp1+rYtQIoHHnbsoyMpCVFSUQu9NCGkcCnaEtFCpqamYO3cuq9a2bVts2bKFo47Iq7KzsxETGYkuKSlyfVCiIYwqKtD5QQpuRETg6dOnnPRACHl7FOwIaYEkEgkCAwNRXl7Oqu/du5feQKBEoiIioJ+XB7tX3tmraPZZWdDPy0NkRASnfRBC3oyCHSEt0HfffYdr166xalOmTIG3tzdHHZFXFRYW4nFKCjqlZyhsXV19+AyDjukZePzgAQoLCznthRDyehTsCGlh7t+/j8WLF7NqHTp0wKZNmzjqiNQlISEBGhUVaJ+Xx3UrAACrvDwIKyqQmJjIdSuEkNegYEdICyISiRAQEICqqipWfd++fTAwMOCoK/IqsViM23FxsM540qjXhDUHgUQCmydPkBgbC/Fr3kNLCOEWBTtCWpCNGzfi+vXrrNqsWbMwYMAAjjpSXkKhEK6urtL/KisrG3yN9evXN+reBQUFqCovh+UrbwLZlpEO37hY+MXF4pNb8XjySkB/1e7MJ036fM9/oln/b5n/b1+vvqHkVWFhYaipqXntOW/j/v37cHNzg6urK1xcXBAeHt7kaxKi7miDYkJaiNu3b8PDwwO1tbXSmp2dHW7dugVdXV0OO1NO5ubmyGviNGhjriEWi3H37l2c/fVXDPnrqnSLk7iSEnybnoZ9Do7Q4PORU10NHQEfRkKNeq/V859o3Hi3t1w+DwC1AgFOv9cPviNHwtHRsd7PdejQAUlJSdDX13+rX7NEIgGfLzvOUFVVBT6fD01NTeTm5sLd3R2ZmZn0NhRCXoNG7AhpAWpqahAQEMAKdXw+HwcOHKBQ1wAXLlxA79694ebmhnHjxklHpaZOnQoPDw84ODhg48aNAIClS5eiqKgIrq6umD59OtLS0tC9e3fptebNm4cDBw4A+DcILVq0CG5ubrh8+TJ+/PFHbP/hB/jfvInVqakAgOc1NTARakDjvwDURktLGsr+LizEqIRbGBYfh3n376FGIsHmtDSUikQYGh+H4IcpDf78q37IfILRsTex5fvvsXXrVmk9NDQUTk5OcHZ2xrfffovt27cjOzsbnp6eGDp0KADg8OHDcHJygqOjIzZs2AAASEtLg5OTE8aMGYNu3brVOSKqra0NTU1NAP+GPBqHIOTNhFw3QAhpfqGhoYiPj2fV5s2bB09PT446Un4vQhkAdO/eHWvXrsWGDRtw+fJl6OjoIDg4GLt378aMGTOwdu1amJqaQiQSoW/fvhg9ejRCQ0Oxa9cu3Lp1C8C/QeZ1rKysEB8fj7t37+LK5ctY6eOD3ikPMf/+fVwpKICXsTG2ZqTDJ/YmvIxNMKx1azgZGKCgthZ7MjNxyNEJ2gIBtqSn4ZecHMzp0AFHc54i3M0dAFAmEjXo8+PatpX2FlFYiJzqavzm4op/7O2xLiICSUlJyMjIwOXLl3Hz5k1oaWmhoKAApqam2LBhA6KioqCvr4+srCx88803iImJga6uLjw9PfH+++/DzMwMd+/exZEjR+Ds7Fzv1+XOnTsYPXo0Hj9+jB9//JFG6wh5Awp2hKi52NhYhIaGsmoODg5YsWIFRx2pBmNjY2koA4DTp08jMTERvXv/OzVZXV2NwYMHAwB+/vln7NmzB2KxGJmZmbh37x6srKwadL+RI0cCAC5duoSUlBQEP3wInZoaVIklcNTXxwBTU5xyc8f1oiJEFRchMCkJW7p0QQ0jwf2KcoxKTAAA1Egk6G9qKnN9faGw0Z+PKCrEXwWFuFkSj8o7yagQCvHgwQNEREQgMDAQWlpaAADTOu4bExODgQMHSo+NGDECERERGDZsGOzt7V8b6gCgW7duuH37Nh4+fIgJEybA29sb2traDfraEtKSULAjRI1VVVVhwoQJrKcYBQIBDh48SP84NpBEIsHgwYOxf/9+Vj01NRXbt29HdHQ0jIyMMGLECFRXV8t8XigUQvLSFOer57yYEpdIJOjXpw/GmZrCJfUx+xo8HrxMTOBlYgJToQYuFuSjj7EJ+puYYq29/Rt/DY39vIQBvrS2xicWFkh4xxalnp745JNPENHEDYsbsgygU6dOMDY2RlJSEmtKmxDCRmvsCFFjy5cvx507d1i1ZcuWwcPDg6OOVFfv3r1x5coVpKenAwBKSkrw+PFjlJaWQl9fH4aGhsjMzMTFixelnxEIBNJQ3bp1a2RnZ6O0tBRlZWX4888/67zPwIEDERMbi+L/gl9+TQ2e1dQgtaICGf+tQ2MYBg8qytFWSwtuhga4XlyErP+ecC0TiaRPuwp4PIj/W5fWmM+/0MfEGL/m5qBSLIaEx0dBURGKi4sxaNAg7N+/XxpSXzwta2BggNLSUgBAz549cenSJRQWFqK6uhonTpxA37593+prnpGRIb12dnY2kpKS0KFDh7f6LCEtFY3YEaKmoqKipAvVX3Bzc8PSpUs56ki1tWrVCrt378bw4cNRU1MDPp+PsLAw9O/fH127dkWXLl3QoUMH9OnTR/qZgIAAODk5oV+/fti5cycWLFgANzc3WFtbw8nJqc77ODg4wP/jjxFy7Bh0qqqgwedjnZ09qhkJQh49Qtl/QdFBTx/jLdtCWyDAqk52mHnvLmolEvB4PCy1fQdW2trwb20Bv7hY9DAywqg2bRr8+Rf6mZjiYUUFRiXcQtndu9CPjsK4iRPh6+uL2NhYuLu7Q0NDA4GBgfjqq68wZcoUDBgwAPb29ggPD8fy5cvRr18/MAyDgIAAuLu7v3HNIQDcunULS5cuhUAgAJ/Px5YtW2Bubt6E30VC1B9td0KIGiovL4erqysePnworWlqauLmzZv1BgqiPC5duoT7Fy7gg+h/uG5Fxp+930Xnjz7CwIEDuW6FEFIHmoolRA0tXryYFeoAYMWKFRTqVISFhQXKdHRQKxBw3QpLrUCAMh0dWFhYcN0KIaQeNBVLiJq5cuUKa58xAHj33Xcxb948jjoiDWVhYQGeUIhiPT2Yl5TUex4DQJGbfxTr6YEnFMo92OXn58uMAGppacm8JYUQ8mYU7AhRIyUlJQgMDGTVdHR0cPDgQQiF9NddVZiamkJbTw9PTU3rDXbl5eUoLSsDGAZGxsbQUcBTzk/N/u2rrm1NmsLMzIy1tQwhpPFoKpYQNTJ37lzpU5svrFmzBvZvsRUGUR4CgQBO7u7IsLaC+JVXbTEAiktKUFxSDIlEDAkjQVFRUbP3JObzkW5lBWcPDwiUbIqYEPL/UbAjRE2cO3cOe/bsYdXee+89zJw5k6OOSFO4uLigVlcXmS89BcowDAoLC1FeXiZzfnM/BffE3BwiXd03bihMCOEWBTtC1EBhYSEmT57Mqunr62P//v11vlydKD8TExPY2tnhoY01JDweJBIJ8gvyUVUl+05VIyPDZl1rJ+Hx8MjGGrb29jAxMWnGOxFCmoq+4xOiBmbNmoXs7GxWbdOmTbC1teWoIyIPXn37oszcHPcsLZGXn4eamppXzuDBxNgEujpv/waHxnjQrh3KzM3h9dIefYQQ5UTBjhAVd/LkSfz444+s2kcffYQpU6Zw1BGRF0tLS7R/5x3csmqPkldev8Xj8WFmZgYdHZ1m7aFYVxf37e3Qs08fWFpaNuu9CCFNR8GOEBX2/PlzTJs2jVUzMjLCnj17wOMpciMM0hwuXLiA6dOnI7OgAHc9PCD+76EFAV8Ac3NzaGlqNuv9RXw+Yrt1hWm7dvD09GzWexFC5IOCHSEqimEYfP7553j+/Dmr/t1336F9+/YcdUXk5cCBA/Dz80NxcTHCz5xBtq4u7vbqBb6GJszNzaHRzNvXSHg8XHfohkrLtvAdOpS2yyFERVCwI0RFHT16FL/99hurNmzYMIwfP56jjog8MAyDVatWITAwECKRCADw7Nkz/HrqFHIsLfGo/3tgNDSatQcRn49oRwcUWFvDf/QotGnTplnvRwiRH3pXLCEqKDs7G46OjigsLJTWzMzMkJycTK97UmEikQhffPEFdu/eLXNszJgxWLlyJU6fOAnd7Gx43L0Lw4oKufdQrKuL2G5dUWnZFv6jR8HGxkbu9yCENB8aWydExTAMg6lTp7JCHQDs2LGDQp0KKy8vx+jRo3HmzBmZY/PmzcO6devA5/MxZsJ4nAkPxxVDQ3RJSYFdVhb4cvj5XMLj4UG7drhvbwfTdu0wbOhQGqkjRAXRiB0hKmbfvn2YNGkSqzZ69GgcPXqUo45IUz179gx+fn6IiYlh1Xk8HsLCwjBr1ixWXSQSITIyEjGRkdDPy0PH9AxY5eVBIJE0+N5iPh9PzM3xyMYaZebm6NmnDzw9PWlNHSEqioIdISokPT0dTk5OKC0tldYsLCyQnJwMMzMzDjsjjfXw4UN4e3vj0aNHrLqWlhaOHDmC4cOH1/vZ7OxsREVG4vGDBxBWVMDmyRNY5hfAqLwcGmJxvZ+rFQhQrKeHp2amSLeygkhXF7b29vCiLU0IUXkU7AhRERKJBB9++CEuXbrEqoeHh2PIkCEcdUWa4vr16/Dz80NeXh6rbmJigv/973/w8vJ6q+sUFhYiMTERibGxqCovByMSQb+yEoYFhdAUicBnJJDw+KgRClFiaoIyHR3whEJo6+nB2cMDzs7O9EYJQtQEBTtCVMT333+PGTNmsGoTJ07E/v37OeqINEV4eDjGjBmDykr2K8JsbGxw/vx5dOnSpcHXFIvFKCgoQG5uLnJzc/E8Jwc1VVUQi0QQCIXQ1NZGqzZtYGFhAQsLC5iamkLw3954hBD1QMGOEBXw8OFDuLi4oOKlpyDbt2+PpKQkGBkZcdgZaYydO3dixowZkLyyJs7NzQ1nzpyh6VBCSKPRPnaEKDmxWIzAwEBWqAOAvXv3UqhTMQzDYOnSpfj8889lQt1HH32Eq1evUqgjhDQJPfZEiJILCwtDREQEqzZ9+nR8+OGHHHVEGqOmpgZTpkzBoUOHZI5NnDgRP/zwAzSaeeNhQoj6o6lYQpTY3bt34ebmhurqamnN1tYWiYmJ0NfX57Az0hAlJSUYMWIE/vzzT5ljQUFBWLFiBb3blxAiFzRiR4iSEolECAgIYIU6Ho+HAwcOUKhTIdnZ2fD19UVCQgKrzufzsWPHDkydOpWjzggh6oiCHSFKat26dTIb1s6ePRv9+vXjqCPSUHfu3IGPjw8yMjJYdV1dXRw7dgx+fn4cdUYIUVc0FUuIEkpISECPHj1QW1srrXXu3Bnx8fHQ0dHhsDPytv7++28MHToURUVFrHqrVq1w+vRp9OzZk5vGCCFqjZ6KJUTJ1NTUYMKECaxQx+fzcfDgQQp1KuL48eP44IMPZEJdp06dEBUVRaGOENJsKNgRomRCQkKQmJjIqi1cuBC9evXiqCPSEGFhYRg1ahRrbSQA9OrVC1FRUejUqRNHnRFCWgKaiiVEidy4cQOenp4Qv/SeTycnJ8TExEBLS4vDzsibSCQSzJ8/H5s3b5Y5NmTIEBw9ehS6urocdEYIaUko2BGiJCorK+Hu7o579+5Ja0KhEDExMXB1deWuMfJGVVVVCAgIwC+//CJzbNq0adi2bRuEQnpWjRDS/Og7DSFKIigoiBXqACA4OJhCnZIrLCzExx9/jGvXrskcCw0NxeLFi2mPOkKIwtCIHSFK4O+//8Z7772Hl/86enh4IDo6mt5GoMQyMjLg4+ODO3fusOpCoRB79+7FhAkTOOqMENJSUbAjhGNlZWVwcXFBamqqtKalpYXY2Fg4ODhw2Bl5nYSEBPj6+iI7O5tVNzAwwG+//YYPPviAo84IIS0ZTcUSwrGFCxeyQh0ArFy5kkKdErt06RL8/f1RWlrKqltaWuLs2bM0fU4I4QyN2BHCoYsXL8qM7Hh6euLatWsQCAQcdUVe58cff0RgYCBEIhGr3rVrV5w7dw42NjYcdUYIIRTsCOFMcXExnJyc8OTJE2lNR0cHCQkJsLOz47AzUheGYbBu3TosXrxY5ljfvn1x6tQpmJqactAZIYT8fzQVSwhH5syZwwp1ALB+/XoKdUpILBZj5syZ2LFjh8yxESNG4PDhw9DW1uagM0IIYaMRO0I4cPr0aQwZMoRVGzBgAC5evAg+n14Io0wqKiowduxY/P777zLHZs+ejU2bNtHvGSFEaVCwI0TB8vPz4ejoiJycHGnNwMAAiYmJ6NChA3eNERl5eXkYMmQI/vnnH5ljmzdvxtdff81BV4QQUj+aiiVEwWbOnMkKdcC/IYFCnXJJTU2Ft7c3UlJSWHVNTU0cPnwYo0aN4qgzQgipH43YEaJAx48fx8iRI1k1Hx8fnDlzht5OoERiYmLg5+eHZ8+eserGxsY4deoU3nvvPY46I4SQ16NgR4iCPHv2DA4ODsjLy5PWjI2NkZycjLZt23LYGXnZ2bNnMXLkSFRUVLDqVlZWOHfuHO0vSAhRarTilxAFYBgG06ZNY4U6ANi2bRuFOiWyd+9eDB06VCbUOTs7Izo6mkIdIUTpUbAjRAGOHDmCU6dOsWr+/v4YO3YsNw0RFoZh8M0332Dy5MkQi8WsYwMHDsTff/+Ndu3acdQdIYS8PZqKJaSZZWVlwdHREUVFRdKaubk5kpOT0bp1a+4aIwCA2tpaTJs2Dfv375c5Nm7cOOzduxeampocdEYIIQ1HI3aENCOGYTB58mRWqAOAnTt3UqhTAmVlZRg6dGidoW7RokU4dOgQhTpCiEqh7U4IaUZ79+7F+fPnWbWxY8di+PDhHHVEXsjJycHgwYMRFxfHqvP5fGzduhVffPEFR50RQkjj0VQsIc0kLS0NTk5OKCsrk9YsLS2RlJRE7xTl2P379+Ht7Y20tDRWXUdHBz///DOGDRvGTWOEENJENGJHSDOQSCT47LPPWKEOAPbs2UOhjmNRUVEYMmQICgoKWHUzMzOcPn0a7777LkedEUJI09EaO0Kawfbt23HlyhVWbdKkSfD19eWoIwIAJ0+exMCBA2VCna2tLaKioijUEUJUHk3FEiJnDx48gKurKyorK6U1a2tr3L59G4aGhhx21rJt27YNs2bNwqvf8rp3747Tp0/DwsKCo84IIUR+aMSOEDkSi8WYOHEiK9QBwL59+yjUcUQikWDhwoWYOXOmTKjz9fXFlStXKNQRQtQGrbEjRI42bdqE6OhoVm3GjBkYOHAgRx21bNXV1fjss8/w008/yRybPHkyduzYAaGQvg0SQtQHTcUSIifJyclwd3dHTU2NtNaxY0ckJCRAT0+Pw85apuLiYvj7+8usdQSAFStWICgoCDwej4POCCGk+dCPqoTIQW1tLSZMmMAKdTweDwcPHqRQx4HMzEz4+vri9u3brLpAIMDu3bsRGBjIUWeEENK8KNgRIgdr1qyR2eh27ty58PLy4qijlispKQk+Pj7IzMxk1fX09HD8+HF4e3tz1BkhhDQ/moolpIni4uLQq1cviEQiaa1r166Ii4uDtrY2h521PFeuXIG/vz+Ki4tZdQsLC5w5cwYeHh4cdUYIIYpBT8US0gTV1dUICAhghTqBQICDBw9SqFOwo0ePwtvbWybU2dvbIzo6mkIdIaRFoGBHSBN88803SEpKYtUWL16MHj16cNRRy8MwDDZu3IhPP/2UtcYRADw9PREVFQVbW1uOuiOEEMWiqVhCGumff/6Bl5cXJBKJtObi4oIbN25AU1OTw85aDrFYjDlz5uC7776TOebv748jR45AR0eHg84IIYQbFOwIaYSKigq4ubnhwYMH0pqGhgZu3rwJZ2dnDjtrOSorKzF+/Hj89ttvMse+/PJLhIWFQSAQcNAZIYRwh56KJaQRli5dygp1wL/TshTqFCM/Px/Dhg1DZGSkzLF169Zh/vz5tEcdIaRFohE7Qhro6tWr6N+/P6vWs2dPREZG0lsMFCAtLQ3e3t64f/8+q66hoYEDBw5g7NixHHVGCCHco2BHSAOUlpbCxcUFjx8/lta0tbURHx+PLl26cNhZyxAfHw9fX1/k5OSw6oaGhjh58iTef/99jjojhBDlQE/FEtIA8+fPZ4U6AAgNDaVQpwAXLlxAv379ZEJdu3btEBERQaGOEEJAI3aEvLULFy7IvLWgb9++uHLlCi3Sb2YHDhzAlClTWPsFAoCDgwPOnTsHKysrjjojhBDlQsGOkLdQVFQER0dHZGVlSWu6urpITExEx44dOexMvTEMg9DQUAQFBckc69+/P06ePAljY2PFN0YIIUqKVnoT8hZmz57NCnUAsHHjRgp1zUgkEuGLL77A7t27ZY6NGTMGBw4cgJaWFgedEUKI8qIRO0LeIDw8HMOGDWPVBg0ahD/++IO21Ggm5eXlGD16NM6cOSNzbN68eVi3bh34fFoiTAghr6JgR8hr5OXlwdHREbm5udKaoaEhbt++DWtraw47U1/Pnj2Dn58fYmJiWHUej4ewsDDMmjWLo84IIUT50VQsIa8xY8YMVqgDgLCwMAp1zeThw4fw9vbGo0ePWHUtLS0cOXIEw4cP56gzQghRDTRiR0g9jh07hjFjxrBqfn5+CA8PpynYZnD9+nX4+fkhLy+PVTcxMcH//vc/eHl5cdQZUQVisRgFBQXIzc1Fbm4unufkoLqyEhKxGHyBAFo6OmjVpg0sLCxgYWEBU1NTepqdqCUKdoTUIScnBw4ODigoKJDWTExMkJycDEtLSw47U0/h4eEYM2YMKisrWXUbGxucP3+e9gkk9SosLERCQgJux8WhqrwcjEgE/cpKGBUUQEMkAp9hIOHxUCsUotjUFGU6OuAJhdDW04OTuztcXFxgYmLC9S+DELmhqVhCXsEwDKZNm8YKdQDw/fffU6hrBjt37sSMGTMgkUhYdTc3N5w5c4a+5qRO2dnZiIqIwOOUFGhUVMA64wksCwpgVF4ODbG43s/VCgQo1tPDU1NT3MrPR0xkJGzt7ODVty/9WSNqgUbsCHnFwYMHMXHiRFZtxIgR+OWXX2gKVo4YhsGyZcuwevVqmWMfffQRfv31VxgYGHDQGVFmIpEIkZGRiImMhH5eHjqlZ6B9Xh4Er/xg8DbEfD4yzc3x0MYaZebm6OHlBS8vL3rnM1FpFOwIecmTJ0/g5OSE4uJiaa1169ZISkpCq1atOOxMvdTU1GDKlCk4dOiQzLGJEyfihx9+gIaGBgedEWWWk5ODM+HhKMzMQpeUFNhlZYEvh3/CJDweUtq1wz07O5i2bwffoUPRpk0bOXRMiOLRjyWE/IdhGEyePJkV6gBg165dFOrkqKSkBCNGjMCff/4pcywoKAgrVqygkVEiIz09HSePHYNu9lMMuHsXhhUVcrs2n2HQOTMTlgUFiC3piqNFxfAfPQo2NjZyuwchikIjdoT8Z9euXZg+fTqrNn78+DpHlUjjZGdnw9fXFwkJCaw6n8/Hjh07MHXqVI46I8osPT0dv/38M8zSM9Dzzh0IGzHt+rZEfD6uO3RDgbU1hn/6KYU7onIo2BECIDU1Fc7OzigvL5fW2rZti6SkJHpiTk7u3LkDHx8fZGRksOq6uro4duwY/Pz8OOqMKLOcnBwcPXQIxo/T0Ds5WS5Tr28i4fEQ7eiAog62GDNhPE3LEpVC7+QhLZ5EIkFgYCAr1AHA3r17KdTJyd9//w0vLy+ZUNeqVStcuXKFQh2pk0gkwpnwcOhmP0WvO3cUEuqAf6dmeyXfgc7TbJwND4dIJFLIfQmRBwp2pMX77rvvcO3aNVZtypQp8Pb25qgj9XL8+HF88MEHKCoqYtU7deqE6Oho9OzZk5vGiNKLjIxEYWYWPO7ebdbp17oIJRJ43LmLgqwsREVFKfTehDQFBTvSot2/fx+LFy9m1Tp06IBNmzZx1JF6CQsLw6hRo1BdXc2q9+rVC1FRUejYsSNHnRFll52djZjISHRJSZHrgxINYVRRgc4PUnAjIgJPnz7lpAdCGoqCHWmxRCIRAgICUFVVxarv27eP9k9rIolEgrlz5+Lrr7/Gq8t4hwwZgsuXL9OTxuS1oiIioJ+XB7usLE77sM/Kgn5eHiIjIjjtg5C3RcGOtFgbN27E9evXWbVZs2ZhwIABHHWkHqqqqvDpp59i8+bNMsemTZuGEydOQFdXl4POiKooLCzE45QUdErPUNi6uvrwGQYd0zPw+MEDFBYWctoLIW+Dgh1pkW7fvo3g4GBWzc7ODmvWrOGoI/VQWFiIjz76CL/88ovMsdDQUOzYsYN29SdvlJCQAI2KCrTPy+O6FQCAVV4ehBUVSExM5LoVQt6Igh1pcWpqahAQEIDa2lppjc/n48CBAzSS1AQZGRno06ePzIMoQqEQBw8exJIlS2jjYfJGYrEYt+PiYJ3xpFGvCWsOAokENk+eIDE2FuLXvIeWEGVAwY60OKGhoYiPj2fV5s2bB09PT446Un0JCQno3bs37ty5w6obGBjg7NmzmDBhAkedkcYQCoVwdXWV/ldZWdnga6xfv75R9y4oKEBVeTksCwpY9W0Z6fCNi4VfXCw+uRWPJ6+sjX3V7swnTfp8z3+iWf9vmf9vXwWv9PWqsLAw1NTUvPachnj69CkMDQ2xbds2uV2TqDeaEyEtSmxsLEJDQ1k1BwcHrFixgqOOVN+lS5fg7++P0tJSVt3S0hJnz56Fq6srN42RRjM2NsatW7eadI3169djwYIFDfqMWCxGbm4uGJEIxmVl0npcSQmuFxfjd1c3aPD5yKmuho7g9eMSuzMzMaW9VaM//yqj8nIwIhFyc3Nf++BPWFgYJk+eDE1Nzbe6rkQiAZ9ffy+LFi3CBx980KBeSctGI3akxaiqqsKECRNYUykCgQAHDx6EtrY2h52prh9//BHe3t4yoa5r166Ijo6mUKdGLly4gN69e8PNzQ3jxo2TjkpNnToVHh4ecHBwwMaNGwEAS5cuRVFREVxdXTF9+nSkpaWhe/fu0mvNmzcPBw4cAPDv9kKLFi2Cm5sbLl++jB9//BHbf/gB/jdvYnVqKgDgeU0NTIQa0PgvALXR0oKRUAMA8HdhIUYl3MKw+DjMu38PNRIJNqeloVQkwtD4OAQ/TGnw51/1Q+YTjI69iS3ff4+tW7dK66GhoXBycoKzszO+/fZbbN++HdnZ2fD09MTQoUMBAIcPH4aTkxMcHR2xYcMGAEBaWhqcnJwwZswYdOvWrd4R0WvXrsHQ0BBOTk6N+B0jLRUFO9JiLF++XGaqcNmyZfDw8OCoI9XFMAzWrl2L8ePHy+zK37dvX0RERNA7NlXYi1Dm6uqKyZMnIy8vDxs2bMDly5cRHx+Pd955B7t37wYArF27FrGxsUhISMBvv/2GJ0+eIDQ0VDrqt3Pnzjfez8rKCvHx8Wjfvj2uXL6MlT4++J+7Owpra3GloABexsZIrayAT+xNrHr0CLf/+0GioLYWezIzccjRCb+7ucNKWxu/5ORgTocOMBAKEe7mjpBOdg3+/MsiCguRU12N31xcsWbIUERERCApKQlnz57F5cuXcfPmTSQmJiIgIAAzZsxA27ZtERUVhfDwcGRlZeGbb77B1atXcfPmTfz888+IjY0FANy9exdLlizBvXv3oKOjI/M1EYlECA4OptkE0mA0FUtahKioKOlPyy+4ublh6dKlHHWkusRiMWbOnIkdO3bIHBsxYgQOHz5MI6Aq7tWp2NOnTyMxMRG9e/cGAFRXV2Pw4MEAgJ9//hl79uyBWCxGZmYm7t27Bysrqwbdb+TIkQD+ndZPSUlB8MOH0KmpQZVYAkd9fQwwNcUpN3dcLypCVHERApOSsKVLF9QwEtyvKMeoxAQAQI1Egv6mpjLX1xcKG/35iKJC/FVQiJsl8ai8k4wKoRAPHjxAREQEAgMDoaWlBQAwreO+MTExGDhwoPTYiBEjEBERgWHDhsHe3h7Ozs71fk22b9+OkSNH1nldQl6Hgh1Re+Xl5QgICGBtlKupqYmDBw9CQ0ODw85UT0VFBcaOHYvff/9d5tjs2bOxadOm164XIqpJIpFg8ODB2L9/P6uempqK7du3Izo6GkZGRhgxYoTMW0aAfx/GkLw0xfnqOS+eRpdIJOjXpw/GmZrCJfUx+xo8HrxMTOBlYgJToQYuFuSjj7EJ+puYYq29/Rt/DY39vIQBvrS2xicWFkh4xxalnp745JNPENHEDYvf9AT+jRs3EBERgQ0bNqCoqAgCgQC6urr47LPPmnRfov7oOzBRe4sXL8bDhw9ZtZCQEFq30kB5eXkYOHBgnaFu8+bN+PbbbynUqanevXvjypUrSE9PBwCUlJTg8ePHKC0thb6+PgwNDZGZmYmLFy9KPyMQCKTrWVu3bo3s7GyUlpairKwMf/75Z533GThwIGJiY1H8X/DLr6nBs5oapFZUIOO/dWgMw+BBRTnaamnBzdAA14uLkPXfE65lIpH0aVcBjwfxfz/MNebzL/QxMcavuTmoFIsh4fFRUFSE4uJiDBo0CPv375eG1BdPyxoYGEjXnPbs2ROXLl1CYWEhqqurceLECfTt2/etvuZHjhxBeno60tLSMHv2bKxYsYJCHXkrNGJH1Nrly5dZi50B4N1338W8efM46kg1PXr0CD4+PkhJSWHVNTU1cfjwYYwaNYqjzogitGrVCrt378bw4cNRU1MDPp+PsLAw9O/fH127dkWXLl3QoUMH9OnTR/qZgIAAODk5oV+/fti5cycWLFgANzc3WFtb1/tDlYODA/w//hghx45Bp6oKGnw+1tnZo5qRIOTRI5T9FxQd9PQx3rIttAUCrOpkh5n37qJWIgGPx8NS23dgpa0N/9YW8IuLRQ8jI4xq06bBn3+hn4kpHlZUYFTCLZTdvQv96CiMmzgRvr6+iI2Nhbu7OzQ0NBAYGIivvvoKU6ZMwYABA2Bvb4/w8HAsX74c/fr1A8MwCAgIgLu7O9LS0prvN4u0eDzm1Rc5EqImSkpK4OzsLB1lAAAdHR3cunUL9m8xdUP+FRMTAz8/Pzx79oxVNzY2xqlTp/Dee+9x1BlRR5cuXcL9CxfwQfQ/MsdqRSJUlJdDLJFAT08PWm+5pYi8/Nn7XXT+6CMMHDhQofclpCFoxI6orblz57JCHQCsWbOGQl0DnD17FiNHjkRFRQWrbmVlhXPnzsHBwYGjzoi6srCwQKyODmoFAmj8N8JWKxKhtLQUVVX/f1uQ6qoqtLawgEBB0/+1AgHKdHRgYWGhkPsR0lgU7IhaOnfuHPbs2cOqvffee5g5cyZHHamevXv3Ytq0aTKvUHJ2dsbZs2fRrl07jjoj6szCwgI8oRDFenowzM9HWVkpqup4SwQDBmKxCAK+YkbtivX0wBMK5R7s8vPzZUYAtbS0cP36dbneh7QcFOyI2iksLMTkyZNZNX19fezfv58W978FhmGwYsWKOvfPGjhwIE6cOAFDQ0MOOiMtgampKSAQ4JGODqzyntd7noaGJjSEinuq/amZKbT19OS+/YiZmVmT3/JByMvoXzmidmbNmoXs7GxWbdOmTbC1teWoI9VRW1uLSZMm1Rnqxo0bh7Nnz1KoI80mKioKgwcPxukLF5DWri3EdfwgxuPxYaBvADMzM/B4PIX0JebzkW5lBWcPDwgEAoXck5DGomBH1MrJkyfx448/smofffQRpkyZwlFHqqOsrAxDhw6V2asM+Pd9lYcOHXrr918S0hBXr17FoEGD4OXlhQsXLiAhIQEVGhrIb99eeg6fx4eBgSEsLCxgYGAAvoJCHQA8MTeHSFf3tRsKE6IsaCqWqI3nz59j2rRprJqRkRH27NmjsJ/sVVVOTg4GDx6MuLg4Vp3P52Pr1q344osvOOqMqCuGYXD58mWEhITg2rVrrGPFxcVIefwYZnZ2aJ2VBUNdPejq6Sk0zL0g4fHwyMYatvb2MDExUfj9CWkoGrEjaoFhGHz++ed4/py9Jue7775D+5d+6iey7t+/j969e8uEOh0dHZw4cYJCHZErhmFw4cIF9OnTB4MGDZIJdS/cuXcPla1bo8TdHfr6+pyEOgB40K4dyszN4fXSHn2EKDMKdkQtHD16FL/99hurNmzYMIwfP56jjlRDVFQUPD09ZTZMNTMzw+XLlzFs2DBuGiNqh2EYnDlzBu+++y68vb0RFRVV53lt27ZFWFgYYmJi0HfQINy3s0fJG16/1VyKdXVx394OPfv0gaWlJSc9ENJQtEExUXnZ2dlwdHREYWGhtGZmZobk5GTac+o1Tp48ibFjx8psJWFra4vz58/Tfn9ELhiGQXh4OEJCQmRGhV/Wvn17LF68GJ999hm0/3vzg0gkwsF9+yC+cxd94+MhfOl9s81NxOfjmrsbNLp2xYTPPoNQSCuXiGqgETui0hiGwdSpU1mhDgB27NhBoe41tm3bhuHDh8uEuu7duyM6OppCHWkyiUSC48ePw83NDR9//HG9oc7Gxga7du3Cw4cP8cUXX0hDHQAIhUIMHjoUFW3b4rpDN0gUNB0r4fFw3aEbKi3bwnfoUAp1RKVQsCMqbf/+/Thz5gyrNnr0aIwcOZKjjpSbRCLBwoULMXPmTLw6WO/r64srV65QICZNIhaLcfToUTg7O2PkyJFISEio87x33nkHe/fuRUpKCqZOnQotLa06z2vTpg38R49CgbU1oh0dIGrmvShFfD6iHR1QYG0N/9Gj0KZNm2a9HyHyRlOxRGWlp6fDyckJpaWl0pqFhQWSk5NhZmbGYWfKqbq6Gp999hl++uknmWOTJ0/Gjh07aGSCNJpIJMLRo0exatUq3L9/v97z7OzssGzZMowdO7ZBf97S09Nx8tgv0M3OhsfduzB85TV38lCsq4vYbl1RadkW/qNHwcbGRu73IKS5UbAjKkkikeDDDz/EpUuXWPXw8HAMGTKEo66UV3FxMfz9/XHlyhWZYytWrEBQUBBtCUMapba2FkeOHEFoaCgePnxY73ldu3bFsmXLMHr06EZv8puTk4Mz4eEozMxCl5QU2GVlgS+Hf8IkPB4etGuH+/Z2MG3XDr5Dh9JIHVFZFOyISvr+++8xY8YMVm3ixIl1bq7b0mVmZsLX1xe3b99m1QUCAXbv3o3AwECOOiOqrKamBocOHcLq1avx+PHjes9zdHREUFAQhg8fLpe3NohEIkRGRiImMhL6eXnomJ4Bq7w8CBrxYIWYz8cTc3M8srFGmbk5evbpA09PTxq5JiqNgh1ROQ8fPoSLiwsqXpqKad++PZKSkmBkZMRhZ8onKSkJPj4+yMzMZNX19PRw/PhxeHt7c9QZUVXV1dXYv38/1qxZg4yMjHrPc3V1RXBwMIYNG9Ys72jOzs5GVGQkHj94AGFFBWyePIFlfgGMysuhIRbX+7lagQDFenp4amaKdCsriHR1YWtvDy/a0oSoCQp2RKWIxWL0798fERERrPqFCxfw4YcfctSVcrpy5Qr8/f1RXFzMqltYWODMmTPw8PDgqDOiiiorK7Fnzx6sW7cOWVlZ9Z7XvXt3BAcHw8/PTyHT+4WFhUhMTERibCyqysvBiETQr6yEYUEhNEUi8BkJJDw+aoRClJiaoExHBzyhENp6enD28ICzszO9UYKoFQp2RKVs2rQJ8+bNY9WmT5+OHTt2cNSRcjp69CgCAgJQU1PDqtvb2+P8+fOwtbXlqDOiaioqKrBr1y6sX78eOTk59Z7Xq1cvLF++HN7e3pys1xSLxSgoKEBubi5yc3PxPCcHNVVVEItEEAiF0NTWRqs2bWBhYQELCwuYmprKZWqYEGVDwY6ojLt378LNzQ3V1dXSmq2tLRITE6Gvr89hZ8qDYRhs2rQJ8+fPlznm6emJ8PBwemKYvJWysjLs2LEDGzduxLNnz+o9r0+fPggODsagQYPoARxClACtECUqQSQSISAggBXqeDweDhw4QKHuP2KxGHPmzMF3330nc8zf3x9HjhyBjo4OB50RVVJSUoLt27dj06ZNyM/Pr/e8/v37Izg4GP3796dAR4gSoWBHVMK6desQExPDqs2ePRv9+vXjqCPlUllZifHjx8u8LxcAvvzyS4SFhdG0E3mtoqIifPfddwgLC5N5k8vLPvjgAwQFBaFv374K7I4Q8rZoKpYovYSEBPTo0QO1tbXSWufOnREfH08jUADy8/MxbNgwREZGyhxbt24d5s+fTyMqpF4FBQUICwvDli1bUFJSUu95Pj4+CAoKQu/evRXYHSGkoWjEjii1mpoaTJgwgRXq+Hw+Dh48SKEOQFpaGry9vWV2+tfQ0MCBAwcwduxYjjojyi4vLw+bN2/G1q1bUVZWVu95Q4YMQVBQEHr06KHA7gghjUXBjii1kJAQJCYmsmoLFy5Er169OOpIecTHx8PX11fmSUVDQ0OcPHkS77//PkedEWWWm5uLTZs24fvvv0d5eXm95/n7+yMoKAhubm4K7I4Q0lQ0FUuU1o0bN+Dp6QnxS5uNOjk5ISYmpt4XhrcUFy5cwIgRI2RGWtq1a4dz587BycmJo86Isnr69Ck2bNiAnTt3orKyss5zeDweRowYgWXLlsHZ2VnBHRJC5IGCHVFKlZWVcHd3x71796Q1oVCImJgYuLq6cteYEjhw4ACmTJkCkUjEqjs4OODcuXOwsrLiqDOijDIzM7Fu3Trs3r2b9VT5y/h8PsaMGYOlS5eiW7duCu6QECJPNBVLlFJQUBAr1AFAcHBwiw51DMNg1apVCA4OljnWv39/nDx5EsbGxopvjCil9PR0rF27Fvv27ZPZqPoFgUCA//u//8OSJUvQuXNnBXdICGkONGJHlM7ff/+N9957Dy//0fTw8EB0dDQ0NDQ47Iw7IpEIX3zxBXbv3i1zbMyYMThw4ECLn54m/0pNTcWaNWtw4MABmVHdF4RCISZMmIDFixejU6dOCu6QENKcKNgRpVJWVgYXFxekpqZKa1paWoiNjYWDgwOHnXGnvLwco0ePxpkzZ2SOzZ8/H2vXrm2Wl6wT1ZKSkoLVq1fj8OHDrHWpL9PQ0EBgYCAWLVpEr5UjRE3RVCxRKgsXLmSFOgBYuXJliw11z549g5+fn8zmzDweD1u2bMHMmTM56owoi3v37iE0NBQ//fQTJBJJnedoampi8uTJWLhwIaytrRXcISFEkWjEjiiNixcv4oMPPmDVPD09ce3atRb51oSHDx/C29sbjx49YtW1tLRw5MgRDB8+nKPOiDJITk7GqlWrcOzYMdT3bVxbWxtTp07FggUL0K5dOwV3SAjhAgU7ohSKi4vh5OSEJ0+eSGs6OjpISEiAnZ0dh51x4/r16/Dz80NeXh6rbmJigv/973/w8vLiqDPCtYSEBKxcubLO18e9oKOjgy+++ALz5s1DmzZtFNgdIYRrNBVLlMKcOXNYoQ4A1q9f3yJDXXh4OMaMGSOz15iNjQ3Onz+PLl26cNQZ4VJsbCxWrlyJ33//vd5z9PT08OWXX2LOnDlo3bq1ArsjhCgLGrEjnDt9+jSGDBnCqg0YMAAXL15scQ8F7Ny5EzNmzJBZK+Xm5oYzZ87A0tKSo84IV65fv46VK1fW+fDMCwYGBpg1axZmz54Nc3NzBXZHCFE2FOwIp/Lz8+Ho6Mh6LZaBgQESExPRoUMH7hpTMIZhsGzZMqxevVrm2EcffYRff/0VBgYGHHRGuBIZGYmVK1fiwoUL9Z5jZGSE2bNn46uvvoKJiYkCuyOEKCuaiiWcmjlzpsy7Tjdv3tyiQl1NTQ2mTJmCQ4cOyRybOHEifvjhhxa7f19LdPXqVYSEhODy5cv1nmNiYoI5c+Zg5syZMDIyUmB3hBBlRyN2hDPHjx/HyJEjWTUfHx+cOXMGPB6Po64Uq6SkBCNGjMCff/4pcywoKAgrVqxoMV+LloxhGFy+fBkhISG4du1aveeZm5tj3rx5+OKLL2gElxBSJwp2hBPPnj2Dg4MD66lPY2NjJCcno23bthx2pjjZ2dnw9fVFQkICq87n87Fjxw5MnTqVo86IojAMgz/++AMhISGIioqq97zWrVtjwYIFmD59OvT09BTYISFE1dBULFE4hmEwbdo0ma08tm3b1mJC3Z07d+Dj44OMjAxWXVdXF8eOHYOfnx9HnRFFYBgGZ8+eRUhICG7cuFHveZaWlli4cCGmTJkCXV1dBXZICFFVFOyIwh05cgSnTp1i1fz9/TF27FhuGlKwv//+G0OHDkVRURGr3qpVK5w+fRo9e/bkpjHS7BiGQXh4OEJCQhAXF1fvee3bt8eiRYswadIkaGtrK7BDQoiqo6lYolBZWVlwdHRkhRpzc3MkJye3iH23jh8/jnHjxqG6uppV79SpE86fP4+OHTty1BlpThKJBCdOnMCqVatkpt5fZmNjg8WLF2PixInQ0tJSYIeEEHVBI3ZEYRiGweTJk2VGqnbt2tUiQl1YWBjmzJkj8/qnXr164X//+x9atWrFUWekuYjFYvz6669YtWoVkpOT6z3vnXfewdKlSzF+/Hh6ApoQ0iQU7IjC7N27F+fPn2fVxo4di08++YSjjhRDIpFg3rx5+Pbbb2WODRkyBEePHqX1U2pGJBLh6NGjWLVqFe7fv1/veXZ2dli2bBnGjh0LoZC+HRNCmo6mYolCpKWlwcnJCWVlZdKapaUlkpKSYGpqymFnzauqqgoBAQH45ZdfZI5NmzYN27Zto3/Q1UhtbS2OHDmC0NBQPHz4sN7zunbtimXLlmH06NEQCAQK7JAQou7oXxTS7CQSCQIDA1mhDgD27Nmj1qGusLAQH3/8cZ37koWGhmLx4sW0R52aqKmpwaFDh7B69Wo8fvy43vMcHR0RFBSE4cOHU6AjhDQLCnak2W3fvh1//fUXqzZp0iT4+vpy05ACZGRkwMfHB3fu3GHVhUIh9u7diwkTJnDUGZGn6upq7N+/H2vWrJHZuuZlrq6uCAoKwscff9zi3n9MCFEsmoolzerBgwdwdXVFZWWltGZtbY3bt2/D0NCQw86aT0JCAnx9fZGdnc2qGxgY4LfffsMHH3zAUWdEXiorK7Fnzx6sW7cOWVlZ9Z7XvXt3BAcHw8/Pj0ZnCSEKQSN2pNmIxWJMnDiRFeoAYN++fWob6i5dugR/f3+Ulpay6paWljh79ixcXV25aYzIRUVFBXbt2oX169fLvOP4Zb169cLy5cvh7e1NgY4QolAU7Eiz2bRpE6Kjo1m1GTNmYODAgRx11Lx+/PFHBAYGQiQSsepdu3bFuXPnYGNjw1FnpKnKysqwY8cObNy4Ec+ePav3PC8vLyxfvhyDBg2iQEcI4QRNxZJmkZycDHd3d9TU1EhrHTt2REJCgtq965JhGKxbtw6LFy+WOda3b1+cOnVKrR8SUWclJSXYvn07Nm3ahPz8/HrP69+/P4KDg9G/f38KdIQQTtGIHZG72tpaTJgwgRXqeDweDh48qHahTiwWY+bMmdixY4fMsREjRuDw4cP0SigVVFRUhO+++w5hYWEoLCys97xBgwYhKCgI/fr1U2B3hBBSPwp2RO7WrFkj8x7MuXPnwsvLi6OOmkdFRQXGjh2L33//XebY7NmzsWnTJnoCUsUUFBQgLCwMW7ZsQUlJSb3n+fj4ICgoCL1791Zgd4QQ8mY0FUvkKi4uDr169WKtM+vatSvi4uLUauQqLy8PQ4YMwT///CNzbPPmzfj666856Io0Vl5eHjZv3oytW7fK7Lf4siFDhiAoKAg9evRQYHeEEPL2aMSOyE11dTUCAgJYoU4gEODgwYNqFeoePXoEHx8fpKSksOqampo4fPgwRo0axVFnpKFyc3OxadMmfP/99ygvL6/3PH9/fyxbtgzu7u4K7I4QQhqOgh2Rm2+++QZJSUms2uLFi9VqdCMmJgZ+fn4yT0YaGxvj1KlTeO+99zjqjDTE06dPsWHDBuzcuVNmO54XeDweRowYgWXLlsHZ2VnBHRJCSOPQVCyRi3/++QdeXl6QSCTSmouLC27cuAFNTU0OO5Ofs2fPYuTIkaioqGDVrayscO7cOTg4OHDUGXlbmZmZWLduHXbv3o3q6uo6z+Hz+Rg9ejSWLl1Kv6eEEJVDwY40WUVFBdzc3PDgwQNpTUNDAzdv3lSbkY69e/di2rRpEIvFrLqzszPOnTuHtm3bctQZeRvp6elYu3Yt9u3bx3pa+2V8Ph/jxo3DkiVL0LlzZwV3SAgh8kFTsaTJli5dygp1wL/TsuoQ6hiGwTfffIOQkBCZYwMHDsSJEyfU9i0a6iA1NRVr1qzBgQMHZDaOfkEoFGLChAlYvHgxOnXqpOAOCSFEvmjEjjTJ1atX0b9/f1atZ8+eiIyMhFCo2j831NbWYtq0adi/f7/MsXHjxmHv3r1qM82sblJSUrB69WocPnxYZpT1BQ0NDQQGBmLRokWwtbVVcIeEENI8KNiRRistLYWLiwseP34srWlrayM+Ph5dunThsLOmKysrw8iRI3H+/HmZY4sWLcLq1avpDQNK6N69ewgNDcVPP/3EWu/5Mk1NTUyePBkLFy6EtbW1gjskhJDmpdpDKoRT8+fPZ4U6AAgNDVX5UJeTk4PBgwfLbLLM5/OxdetWfPHFFxx1RuqTlJSEVatW4ZdffkF9P6tqa2tj6tSpWLBgAdq1a6fgDgkhRDFoxI40yoULF+Dt7c2q9e3bF1euXIFAIOCoq6a7f/8+vL29kZaWxqrr6Ojg559/xrBhw7hpjNQpISEBK1euxG+//VbvOTo6Ovj8888xb948WFpaKrA7QghRPAp2pMGKiorg6OiIrKwsaU1XVxeJiYno2LEjh501TVRUFIYMGYKCggJW3czMDKdPn8a7777LUWfkVbGxsVi5cmWdr3N7QU9PD19++SXmzJmD1q1bK7A7QgjhDk3FkgabPXs2K9QBwMaNG1U61J08eRJjx45FVVUVq25ra4vz58/D3t6eo87Iy65fv46VK1fizJkz9Z5jYGCAWbNmYfbs2TA3N1dgd4QQwj0asSMNEh4eLjMdOWjQIPzxxx8q+zDBtm3bMGvWLJm1Wd27d8fp06dhYWHBUWfkhcjISKxcuRIXLlyo9xwjIyPMnj0bX331FUxMTBTYHSGEKA8KduSt5eXlwdHREbm5udKaoaEhbt++rZJPF0okEixevBjr16+XOebr64tjx45BX1+fg87IC1evXkVISAguX75c7zkmJiaYM2cOZs6cCSMjIwV2RwghyoemYslbmzFjBivUAUBYWJhKhrrq6mp89tln+Omnn2SOTZ48GTt27FD5ffhUFcMwuHz5MkJCQnDt2rV6zzM3N8fcuXMxY8YMGBgYKLBDQghRXjRiR97KsWPHMGbMGFbNz88P4eHhKjcFW1xcDH9/f1y5ckXm2IoVKxAUFKRyvyZ1wDAM/vjjD4SEhCAqKqre81q3bo358+dj+vTpNKJKCCGvoGBH3ignJwcODg6sp0VNTEyQnJyscttHZGZmwtfXF7dv32bVBQIBdu/ejcDAQI46a7kYhsHZs2cREhKCGzdu1HuepaUlFi5ciClTpkBXV1eBHRJCiOqguSbyWgzDYNq0aTJbgHz//fcqF+qSkpLg4+ODzMxMVl1PTw/Hjx+X2ZePNC+GYRAeHo6QkBCZzaBf1r59eyxatAiTJk2Ctra2AjskhBDVQ8GOvNahQ4cQHh7Oqo0YMQKjR4/mqKPGuXLlCvz9/VFcXMyqW1hY4MyZM/Dw8OCos5ZHIpHgxIkTWLVqFRISEuo9z9raGkuWLMHEiROhpaWlwA4JUS1isRgFBQXIzc1Fbm4unufkoLqyEhKxGHyBAFo6OmjVpg0sLCxgYWEBU1NTld5InrweTcWSej158gROTk6sMNS6dWskJSWhVatWHHbWMEePHkVAQABqampY9c6dO+PcuXP0AngFEYvF+PXXX7Fq1SokJyfXe94777yDJUuWYPz48dDU1FRgh4SolsLCQiQkJOB2XByqysvBiETQr6yEUUEBNEQi8BkGEh4PtUIhik1NUaajA55QCG09PTi5u8PFxYW2BlJDNGJH6sQwDCZPniwzwrVr1y6VCXUMw2DTpk2YP3++zDFPT0+Eh4fDzMyMg85aFpFIhKNHj2LVqlW4f/9+vefZ2dlh6dKlGDt2LDQ0NBTYISGqJTs7G1EREXickgKNigpYZzyBZUEBjMrLoSEW1/u5WoEAxXp6eGpqilv5+YiJjIStnR28+vZVuaU1pH40YkfqtGvXLkyfPp1VGz9+PA4dOsRRRw0jFosxZ84cfPfddzLH/P39ceTIEejo6HDQWctRW1uLI0eOIDQ0FA8fPqz3vC5dumDZsmUYPXo0bTFDyGuIRCJERkYiJjIS+nl56JSegfZ5eRBIJA2+lpjPR6a5OR7aWKPM3Bw9vLzg5eVFfwfVAAU7IiM1NRXOzs4oLy+X1tq2bYukpCSVGLavrKzEuHHjcOLECZljX375JcLCwmh9STOqqanBoUOHsHr1ajx+/Lje8xwdHREUFIThw4fT7wchb5CTk4Mz4eEozMxCl5QU2GVlgS+Hf74lPB5S2rXDPTs7mLZvB9+hQ9GmTRs5dEy4QsGOsEgkEgwYMEBmY9hz586pxFOj+fn5GDZsGCIjI2WOrVu3DvPnz6c96ppJdXU19u3bh7Vr1yIjI6Pe81xcXBAcHIyPP/4YfD5fgR0SoprS09Nx8tgx6GY/hcfduzCsqJD7PUp0dRHbtSsq2raF/+hRsLGxkfs9iGJQsCMsYWFh+Prrr1m1KVOm4IcffuCoo7eXlpYGb29vmXVcGhoaOHDgAMaOHctRZ+qtsrISe/bswbp165CVlVXveR4eHggODsaQIUMoXBPyltLT0/Hbzz/DLD0DPe/cgbAR065vS8Tn47pDNxRYW2P4p59SuFNRFOyI1P379+Hq6oqqqipprUOHDkhMTFT6VzbFx8fD19cXOTk5rLqhoSFOnTqFAQMGcNSZ+qqoqMCuXbuwfv16ma/7y3r16oXg4GD4+PhQoCOkAXJycnD00CEYP05D7+RkuUy9vomEx0O0owOKOthizITxNC2rgmgehAD4d1FuQEAAK9QBwL59+5Q+1F24cAH9+vWTCRft2rVDREQEhTo5Kysrw4YNG2Bra4s5c+bUG+q8vLzwxx9/IDo6Gr6+vhTqCGkAkUiEM+Hh0M1+il537igk1AEAn2HQK/kOdJ5m42x4OEQikULuS+SHgh0BAGzcuBHXr19n1WbNmqX0oejAgQPw8/NDWVkZq+7g4IDo6Gg4OTlx1Jn6KSkpwZo1a9ChQwcsWLAAz549q/O8/v374/Lly/j777/xwQcfUKAjpBEiIyNRmJkFj7t3m3X6tS5CiQQed+6iICvrte9tJsqJpmIJbt++DQ8PD9TW1kprdnZ2uHXrltK+k5NhGKxatQrBwcEyx/r374+TJ0/C2NhY8Y2poaKiInz33XcICwtDYWFhvecNGjQIQUFB6NevnwK7I0T9ZGdn46cDB9DldhI6v/IKREW617497js54v8CA2mfOxVCI3YtXE1NDQICAlihjs/n48CBA0ob6kQiEaZNm1ZnqBszZgzOnz9PoU4OCgoKEBwcDBsbGyxfvrzeUOft7Y3IyEj8+eefFOoIkYOoiAjo5+XB7jUPIymCfVYW9PPyEBkRwWkfpGFoJ8IWLjQ0FPHx8azavHnz4OnpyVFHr1deXo7Ro0fjzJkzMsfmz5+PtWvX0hYaTZSXl4fNmzdj69atMlPcLxsyZAiWLVuGnj17KrA7QtRbYWEhHqekwC09Q2Hr6urDZxh0TM/ALTMzFBYWqsQ+poSCXYsWGxuL0NBQVs3BwQErVqzgqKPXe/bsGfz8/BATE8Oq83g8bNmyBTNnzuSoM/WQm5uLTZs24fvvv2dtTv0qf39/LFu2DO7u7grsjpCWISEhARoVFWifl8d1KwAAq7w8JFVUIDExEe+99x7X7ZC3QMGuhaqqqsKECRMgfum9ggKBAAcPHoS2tjaHndUtJSUFPj4+ePToEauupaWFI0eOYPjw4Rx1pvqePn2KDRs2YOfOnaisrKzzHB6PhxEjRmDZsmVwdnZWcIeEtAxisRi34+JgnfGkUa8Jaw4CiQQ2T54gMTYWffr0obfEqACas2qhli9fjjt37rBqy5Ytg4eHB0cd1e/69evw9PSUCXUmJia4dOkShbpGyszMxMyZM2Fra4tvv/22zlDH4/Hw6aef4vbt2/jll18o1BG1JxQK4erqKv2vvh92Xmf9+vWNundBQQGqysthWVDAqm/LSIdvXCz84mLxya14PHllW6pX7c580qTP9/wnmvX/lvn/9lXwSl+vCgsLQ01NzWvPeRtlZWUYOHAg9PX1MW/evCZfr6WhEbsWKCoqChs2bGDV3NzcsHTpUo46ql94eDjGjBkj883VxsYG58+fR5cuXTjqTHWlp6dj7dq12LdvX73fhPl8Pv7v//4PS5Ysoa8xaVGMjY1x69atJl1j/fr1WLBgQYM+IxaLkZubC0YkgvFLa1vjSkpwvbgYv7u6QYPPR051NXQErx+T2Z2ZiSntrRr9+VcZlZeDEYmQm5uLVq1a1XteWFgYJk+eDE1Nzbe6rkQiqXNNtIaGBpYvX47k5GSZH+jJm9GIXQtTXl6OgIAAvLzLjaamJg4dOgQNDQ0OO5O1c+dO+Pv7y4Q6Nzc3REdHU+BooNTUVEyZMgWdOnXCzp076wx1AoEAgYGBuH//Pg4dOkRfY0Lw7ybovXv3hpubG8aNGyf9uzN16lR4eHjAwcEBGzduBAAsXboURUVFcHV1xfTp05GWlobu3btLrzVv3jwcOHAAwL9v9lm0aBHc3Nxw+fJl/Pjjj9j+ww/wv3kTq1NTAQDPa2pgItSAxn8BqI2WFoyE/36v/ruwEKMSbmFYfBzm3b+HGokEm9PSUCoSYWh8HIIfpjT486/6IfMJRsfexJbvv8fWrVul9dDQUDg5OcHZ2Rnffvsttm/fjuzsbHh6emLo0KEAgMOHD8PJyQmOjo7SwYS0tDQ4OTlhzJgx6NatW50jolpaWujXrx90dHQa+TvWwjGkRZk5cyYDgPXf2rVruW6LRSKRMEuWLJHpEwDz0UcfMSUlJVy3qFIePHjATJw4kREIBHV+TQEwGhoazNSpU5nU1FSu2yWEUwKBgHFxcWFcXFyYSZMmMc+fP2cGDhzIVFRUMAzDMEFBQcy2bdsYhmGY/Px8hmEYpra2lnn33XeZjIwMhmEYxszMTHq9x48fMx4eHtL/nzt3LrN//36GYRjGxsZGeq07d+4wPXv0YA4GBjIP+vRlhrVqzezq5sDEvdubsdfVZTrq6DATLNsyv7m4Mg/69GX+6fUu09vImEns7ck86NOXmWFlxQS/05F50KcvYywUMg/69GUe9OnbpM/vc3BkxllaMve9+jAHAz9junXrxty+fZs5c+YM8/777zNVVVWsr4ONjQ1TWlrKMAzDZGZmMu+88w6Tn5/PVFZWMm5ubszNmzeZx48fMwKBgElISHjj78X+/fuZuXPnNvr3sqWiqdgW5PLly6yfuADg3XffVao1DDU1NZgyZQoOHTokc2zixIn44YcflG5kUVndu3cPoaGh+OmnnyCpZyG2pqYmJk+ejIULF8La2lrBHRKifF6dij19+jQSExPRu3dvAEB1dTUGDx4MAPj555+xZ88eiMViZGZm4t69e7CysmrQ/UaOHAkAuHTpElJSUhD88CF0ampQJZbAUV8fA0xNccrNHdeLihBVXITApCRs6dIFNYwE9yvKMSoxAQBQI5Ggv6mpzPX1hcJGfz6iqBB/FRTiZkk8Ku8ko0IoxIMHDxAREYHAwEBoaWkBAEzruG9MTAwGDhwoPTZixAhERERg2LBhsLe3p/W6zYiCXQtRUlKCzz77jFXT0dHBwYMHleYpp5KSEgwfPhwXL16UORYUFIQVK1bQ66neQlJSElatWoVffvmFNeX+Mm1tbUydOhULFixAu3btFNwhIapDIpFg8ODB2L9/P6uempqK7du3Izo6GkZGRhgxYgSqq6tlPi8UClk/WL16zouN4CUSCfr16YNxpqZwSX0sPS6WSCCqrYGTUAgnM3MY8QW4WJCPPsYm6G9iirX29m/8NQh5PHiZmMDLxASmQo23/ryEAb60tsYnFhZIeMcWpZ6e+OSTTxDRxA2LlXXze3VBa+xaiLlz5yI9PZ1VW7NmDezf4puCImRnZ6Nfv34yoY7P52PXrl0ICQmhUPcGCQkJGDFiBJycnHDs2LE6Q52Ojg6+/vprpKamYsuWLRTqCHmD3r1748qVK9LvnyUlJXj8+DFKS0uhr68PQ0NDZGZmsr53CQQC6VZSrVu3RnZ2NkpLS1FWVoY///yzzvsMHDgQN2JjUVBZhbLyMjx8/hzJ2VmIeZKB27m5KCsrRWlpCZIKC9BGUxNuhga4XlyErP+ecC0TiaRPuwp4PIj/+/ufWlGBjP/WsTEMgwcV5WirpfXaz7/Qx8QYv+bmoFIshoTHR0FREYqLizFo0CDs379fGlJfPC1rYGCA0tJSAEDPnj1x6dIlFBYWorq6GidOnEDfvn2b+LtB3gaN2LUA586dw549e1i19957T2k29L1z5w58fHyQkZHBquvq6uLYsWPw8/PjqDPVEBsbi5UrV+L333+v9xw9PT3MmDEDc+fORevWrRXYHSGqrVWrVti9ezeGDx+Ompoa8Pl8hIWFoX///ujatSu6dOmCDh06oE+fPtLPBAQEwMnJCf369cPOnTuxYMECuLm5wdraGk5OTgCA2tpa1NTUYO/evUhMTMSNGzfQwdoaK//8A5oVFdDg8bCodWvUMAy2PHuGiv9G/ey1tDDavBWMNTSxqpMdZt67i1qJBDweD0tt34GVtjb8W1vALy4WPYyMMKpNG4Q8eoSy/4Kmg54+xlu2hbZAUO/nX+hnYoqHFRUYlXALZXfvQj86CuMmToSvry9iY2Ph7u4ODQ0NBAYG4quvvsKUKVMwYMAA2NvbIzw8HMuXL0e/fv3AMAwCAgLg7u6OtLS0t/q6d+7cGc+fP0dtbS2OHj2Kf/75B+3bt5fT76p64zH1zdUQtVBYWAhHR0dkZ2dLa/r6+khMTIStrS2Hnf3r77//xtChQ1FUVMSqt2rVCmfOnEGPHj24aUwFXL9+HStXrqzz9WovGBgYYNasWZg9ezbMzc0V2B0h5AWGYZCamoobN25I/4uLi0PVKyNk77//Pj60s8O7dSxHeYHP46O1hQX4Cp7B+LP3u+j80UcYOHCgQu9LGo5G7NTcrFmzWKEOADZt2qQUoe748eMYN26czJqTTp064fz58+jYsSNHnSm3yMhIrFy5EhcuXKj3HCMjI8yePRuzZs2qc2EzIaT5PHv2DDdu3EBMTIw0yL1pc1/g39f6Vbq7QyQUQigSSes8Hh8aGhrQ1NSEnp6ewkNdrUCAMh0dWFhYKPS+pHEo2KmxkydP4scff2TVPvroI0yZMoWjjv6/sLAwzJkzR2YdWK9evfC///3vtZtgtlRXr15FSEgILl++XO85JiYmmDNnDmbOnAkjIyMFdkdIy1RWVoa4uDjWaNyr65nfVm5uLkQMg2ozMxiWlkFTQwMampoQCoXgcoVxsZ4eeEKh3INdfn6+zAiglpYWrl+/Ltf7tDQU7NTU8+fPMW3aNFbNyMgIe/bs4fQhBIlEgnnz5uHbb7+VOTZkyBAcPXqUnph6CcMwuHz5MkJCQnDt2rV6zzM3N8fcuXPxxRdfwNDQUIEdEtJyiEQiJCUlsUJccnJyvdsJvY1OnTqhZ8+e6NmzJ7p3747Y6GhUvdMRxm+5Fk0RnpqZQltPT+6j/2ZmZk1+yweRRcFODTEMg88//xzPnz9n1b/77jtOF59WVVUhICAAv/zyi8yxadOmYdu2bRAK6Y8k8O/v4R9//IGQkBBERUXVe17r1q0xf/58TJ8+Hfr6+grskBD1xjAMHj9+LLMurjHvjn2hVatW6NWrFyvImZmZsc6pra3FrcJCdMvIgKAJgfF1JAwDHo/3VqOAYj4f6VZWcPfwUJqtscjr0b+iaujo0aP47bffWLVhw4Zh/PjxHHX070McH3/8cZ2jTqtXr8aiRYtoOxP8+4/J2bNnERISghs3btR7nqWlJRYsWICpU6fSCCchcvD8+XPWmrgbN24gPz+/0dfT1dVF9+7dpSGuZ8+esLa2fuP3ORcXF8RERiLT3Bw2z541+v71KS4pQXl5Gfg8PkxMTaH1hve6PjE3h0hXlzYUViEU7NRMdnY2ZsyYwaqZmZlh165dnAWnjIwM+Pj44M6dO6y6UCjEvn37OA2cyoJhGISHhyMkJARxcXH1nte+fXssWrQIkyZNgvZL2xIQQt5eeXm5dF3cizD3+PHjN3+wHgKBAE5OTqwQ17Vr10bNQJiYmMDWzg4P8/Nh9fw5+HLcuEIkEqG8vAwAIGEkKCwoQKvWrSHg172lrYTHwyMba9ja28PExERufZDmRcFOjTAMg6lTp6KwsJBV37FjB2dPMyUkJMDX11fmyVwDAwP89ttv+OCDDzjpS1lIJBKcOHECq1atQkJCQr3nWVtbY8mSJZg4caL0NT6EkDcTiURITk5mjcQlJSU1aV3cO++8wwpxbm5uch059+rbF0cePkRKu3bonJkpt+vyXglwEkaC4qKietfOPWjXDmXm5hj20h59RPlRsFMj+/fvl9nTbPTo0dJ3ESraxYsX8cknn0h3In/B0tISZ8+ehaurKyd9KQOxWIxff/0Vq1atQnJycr3nvfPOO1iyZAnGjx8PzTdMmRDS0jEMg7S0NFaIi42NbdK6OHNzc5l1cc29J6SlpSV6eHkhpqoalgUFMKyokMt1BXw+dLR1UFn1/78eVdVVqKisgK4OO5gW6+rivr0devbpA0tLS7ncnygGbVCsJtLT0+Hk5MQKURYWFkhOTpZZnKsIP/74IwIDAyF6aS8mAOjatSvOnTsHGxsbhfekDEQiEY4ePYpVq1bh/v379Z5nZ2eHpUuXYuzYsdDQ0FBgh4Sojry8PJl1cXl5eY2+nq6uLjw8PFijcTY2NpwsYxGJRDi4bx/Ed+6ib3w8hHJ6kEIikeDZ8+eQSMTSGo/HR+tWraQPR4j4fFxzd4NG166Y8Nln9FCbiqHfLTUgkUgwadIkmZGx3bt3KzzUMQyDdevWYfHixTLH+vbti1OnTrXIDXNra2tx5MgRhIaG4uHDh/We16VLFyxbtgyjR4+mb6aEvKSiogLx8fGsEJeamtro6wkEAjg6OrJCXLdu3ZTm751QKMTgoUNxtKgY12uq0TspWS7r7fh8PoyNjFBQ+P83TGYYCYqKimBmZgYJj4frDt1QadkWw4YOVZqvB3l7NGKnBr7//nuZByYmTpyI/fv3K7QPsViMmTNnYseOHTLHRo4ciUOHDrW4Bf81NTU4dOgQVq9e/drF2Y6OjggKCsLw4cNpSwHS4olEIty5c4f1cMPt27chFovf/OF62NrayqyL09PTk2PXzSM9PR2//fwzTDMy0Cv5jtxG7gqLilBZyZ7i1Tcxxe0e3VFgbY3hn37aYmdWVB0FOxX38OFDuLi4oOKlNRjt27dHUlKSQt88UFFRgbFjx9b5IvrZs2dj06ZN4Nfz5JU6qq6uxv79+7FmzRpkZGTUe56LiwuCg4Px8ccft6ivDyEvMAyD9PR0mXVxFU1YV2ZmZoZevXqhR48e6NmzJ3r06KHSb7NJT0/HyWO/QDc7Gx5378plzZ2EYfD82TOI/5uSLTc0xL3u3SGxtcXIsWMp1KkwCnYqTCwWo3///oiIiGDVL1y4gA8//FBhfeTl5WHIkCH4559/ZI5t3rwZX3/9tcJ64VplZSX27NmDdevWISsrq97zPDw8EBwcjCFDhtD+faRFyc/Pl1kX9+pm6g2ho6Mjsy6uQ4cOavf3KicnB2fCw1GYmYUuKSmwy8pq8tRsdXU1nhcWINveHilduiCroACZOTk4ffo0zRyoMAp2KmzTpk2YN28eqzZ9+vQ6p0Kby6NHj+Dj44OUlBRWXVNTE4cPH8aoUaMU1guXKioqsGvXLqxfvx45OTn1nterVy8EBwfDx8dH7f7hIeRVlZWVMuviHj161Ojr8fl8mXVxDg4OLWYdmEgkQmRkJGIiI6Gfl4eO6Rmwystr1BsqxHw+npib404bC+RoayMyJgZRUVEQi8XYtGkT5syZ0wy/AqIIFOxU1N27d+Hm5obq6mppzdbWFomJiQp7tVRMTAz8/Pzw7JXd0Y2NjfH777+jX79+CumDS2VlZdixYwc2btwo83V4mZeXF5YvX45BgwZRoCNqSSwW4+7du6wQl5iY2KR1cR06dGCFOHd3d5VYF9fcsrOzERUZiccPHkBYUQGbJ09gmV8Ao/JyaLzm610rEKBYTw9PzUyRbmUFka4urGxtsXHzZsTGxkrP09LSQnx8PLp27aqIXw6RMwp2KkgkEsHT0xMxMTHSGo/Hw19//aWwMHX27FmMHDlSZh2MlZUVzp07BwcHB4X0wZWSkhJs374dmzZteu1rh/r374/g4GD079+fAh1RGwzDICMjQxrgYmJicPPmTZSXlzf6mqampqwQ16NHD7Ru3VqOXaufwsJCJCYmIjE2FlXl5WBEIuhXVsKwoBCaIhH4jAQSHh81QiFKTE1QpqMDnlAIbT09OHt4wNnZGSYmJrh69Sr69+/PunaPHj0QFRXVYkZD1QkFOxUUGhqKZcuWsWpff/01Nm/erJD77927F9OmTZP5SdzZ2Rnnzp1D27ZtFdIHF4qKirB161Z8++23Mm/4eNmgQYMQFBTUIkYtiforKCiQWRf3uhHqN9HW1mati+vRowfeeecd+uGnkcRiMQoKCpCbm4vc3Fw8z8lBTVUVxCIRBEIhNLW10apNG1hYWMDCwgKmpqYya+i+/vprhIWFsWqhoaFYsmSJAn8lRB4o2KmYhIQE9OjRA7W1tdJa586dER8fDx0dnWa9N8Mw+OabbxASEiJzbODAgThx4gQMDQ2btQeuFBQUYMuWLdiyZQuKi4vrPc/b2xtBQUHw9PRUYHeEyE9lZSVu3brFCnGv23vxTfh8PhwcHGTWxdHG28qlsrISrq6uePDggbSmoaGBmJgYuLi4cNgZaSgKdiqkpqYGPXr0QGJiorTG5/MRFRWFXr16Neu9a2trMW3atDr3xhs3bhz27t2rlq+8ysvLw+bNm7Ft2zaZDaBfNmTIECxbtgw9e/ZUYHeENI1YLMa9e/dk1sW9+saYhrCxsZFZF6eodb+kaf755x94eXmx3qPr4uKCGzduqOX3d3VFk+cqJCQkhBXqAGDhwoXNHurKysowcuRInD9/XubYokWLsHr1arWbQsnNzcWmTZvw/fffv3bdkL+/P5YtWwZ3d3cFdkdIwzEMg8zMTFaIu3nzJsrKyhp9TRMTE5l1cRYWFnLsmijSu+++iwULFmDt2rXSWkJCAlauXImVK1dy2BlpCBqxUxE3btyAp6cna12bk5MTYmJioKWl1Wz3zcnJweDBgxEXF8eq8/l8bN26FV988UWz3ZsLT58+xYYNG7Bz5856XxzO4/EwYsQILFu2DM7OzgrukJC3U1hYyFoXFxMT89qteN5ES0sL7u7urCDXsWNHtfuhrqWrrq5G9+7dkZSUJK0JBAJER0ejR48eHHZG3hYFOxVQWVkJd3d33Lt3T1oTCoWIiYmBq6trs933/v378Pb2RlpaGquuo6ODn3/+GcOGDWu2eytaZmYm1q9fjx9++IG1hczLeDwexowZg6VLl6r9U79EtVRVVcmsi3t1b8mG4PF4MuviHB0daV1cCxEfH4+ePXuypuS7du2KuLi4FvdaSFVEU7EqICgoiBXqACA4OLhZQ11UVBSGDBmCgoICVt3MzAynT5/Gu+++22z3VqT09HSsW7cOe/fuRU1NTZ3n8Pl8/N///R+WLFmCLl26KLhDQtjEYjHu37/PCnEJCQlNWhdnbW0tsy7OwMBAjl0TVeLm5oagoCAsX75cWrt79y6CgoKwYcMGDjsjb4NG7JTc33//jffeew8v/zZ5eHggOjq62X56PnnyJMaOHYuqqipW3dbWFufPn4e9vX2z3FeRUlNTsWbNGhw4cKDefxAFAgEmTJiAJUuWoFOnTgrukJB/18VlZWXJrIt73YM8b2JsbCyzLq5NmzZy7Jqog9raWvTu3Zu1cTGPx8O1a9fQp08fDjsjb0LBTomVlZXBxcUFqamp0pqWlhZiY2ObbSpw27ZtmDVrFl79Y9G9e3ecPn1a5RdGp6SkYPXq1Th8+HC9O+JraGggMDAQixYtgq2trYI7JC1ZUVERbt68yQpyT58+bfT1tLS04ObmxgpynTp1onVx5K0kJyfD3d2dNZvRsWNHJCQk0BtAlBhNxSqxhQsXskIdAKxcubJZQp1EIsHixYuxfv16mWO+vr44duyYSm9ZcO/ePYSGhuKnn35iPcr/Mk1NTUyePBkLFy6EtbW1gjskLU1VVRUSEhJYDzfcv3+/0dfj8Xjo2rUrK8Q5OTnRNhWk0RwcHLBq1SosWLBAWnv06BEWLlyIbdu2cdgZeR0asVNSFy9exAcffMCqeXp64tq1azI7hjdVdXU1PvvsM/z0008yxyZPnowdO3ao7GtlkpOTsWrVKhw7dkxmFPIFbW1tTJ06FQsWLEC7du0U3CFpCSQSSZ3r4l7eaLyhrKysZNbFqesG4YQ7YrEY/fr1Q1RUFKt+8eJFDBw4kKOuyOtQsFNCxcXFcHJywpMnT6Q1HR0dJCQkwM7OTq73KioqwieffIIrV67IHFuxYgWCgoJUctomISEBq1atwvHjx+s9R0dHB59//jnmzZsHS0tLBXZH1F1d6+JKSkoafT0jIyOZdXH0Z5YoSkpKClxcXFhbQFlbW+P27dv0w4QSUs1hGDU3Z84cVqgDgPXr18s91GVmZsLHx4e1XxHw70MDu3fvRmBgoFzvpwixsbFYuXIlfv/993rP0dPTw5dffok5c+bQS8ZJkxUXF8usi8vOzm709TQ1NetcF8fn8+XYNSFvz87ODuvWrcOsWbOktYyMDMyZMwd79uzhsDNSFxqxUzKnT5/GkCFDWLUBAwbg4sWLcv3GnpSUBB8fH2RmZrLqenp6OH78OLy9veV2L0W4ceMGQkJCcObMmXrPMTAwwKxZszB79myYm5srsDuiLqqrq5GYmMgKca9uRdQQPB4PXbp0YYU4Z2dnWhdHlI5EIsGgQYNkZndOnz6NwYMHc9QVqQsFOyWSn58PR0dH1u7wBgYGSExMRIcOHeR2nytXrsDf31/mZfYWFhY4c+YMPDw85Hav5hYVFYWQkBBcuHCh3nOMjIwwe/ZsfPXVVzAxMVFgd0SVSSQSPHjwQGZdXH37Hb6Ndu3asUKch4cHjIyM5Ng1Ic0nLS0NTk5OrNfQWVpaIikpCaamphx2Rl5GU7FKZObMmTKv/Nm8ebNcQ93Ro0cREBAg849T586dce7cOZXZ3uPatWsICQnBpUuX6j3HxMQEc+bMwcyZM+kfT/JG2dnZrBAXExPTpHVxhoaGMuvi2rZtK8eOCVGsDh064Ntvv8WUKVOktadPn2LmzJk4cuQIh52Rl9GInZI4fvw4Ro4cyar5+PjgzJkzcnl4gWEYbNq0CfPnz5c55unpifDwcJiZmTX5Ps2JYRhcuXIFISEhuHr1ar3nmZubY+7cuZgxYwbtnk/qVFJSIrMuLisrq9HX09TUhKurKyvI2dnZ0bo4onYYhsHgwYNx7tw5Vv23337DJ598wlFX5GUU7JTAs2fP4ODggLy8PGnN2NgYycnJcvkJXywW4+uvv8bWrVtljvn7++PIkSPQ0dFp8n2aC8Mw+PPPPxESEoLIyMh6z2vdujXmz5+P6dOnq/See0S+ampq6lwX15RvfXWti9PS0pJj14Qor6ysLDg6OqKoqEhaa9WqFZKSkuiBNCVAU7EcYxgG06ZNY4U64N83QMgj1FVWVmLcuHE4ceKEzLEvv/wSYWFhct8XT14YhsHZs2cREhKCGzdu1HuepaUlFi5ciClTpkBXV1eBHRJlI5FI8PDhQ1aIi4+Pb9K6OEtLS/Tq1Usa4rp3705T+6RFa9euHbZu3Yrx48dLa8+fP8fnn3+O48ePq+QWWeqERuw49uOPP7L+cgDAJ598Ipe/HPn5+Rg2bFido1zr1q3D/PnzlfIvIMMwCA8PR0hICOLi4uo9r3379li0aBEmTZoEbW1tBXZIlMXTp09l1sW9+lBQQxgaGqJ79+6s0TjatJoQWQzDYPjw4Th58iSrfuTIEYwdO5ajrghAwY5TdQ1nm5ubIzk5ucnD2WlpafD29pZ5RZGGhgYOHDiglH/xJBIJTp48iZUrVyIhIaHe86ytrbFkyRJMnDiRpr9akJKSEsTGxrKC3Kvb9TSEhoaGzLo4e3t7WhdHyFtq7mVEpHEo2HGEYRj4+vri/PnzrLo8FqDGx8fD19dX5glbQ0NDnDp1CgMGDGjS9eVNLBbj+PHjWLlyJZKTk+s975133sGSJUswfvx42udLzdXU1OD27dusEHf37t0mrYvr3LkzK8S5uLjQDwaENFFdD/75+vri9OnTSjkj1BJQsOPI7t27MXXqVFZt7NixTX5k/MKFCxgxYgRrnyHg3zUR586dg5OTU5OuL08ikQhHjx5FaGjoazd5tbOzw7JlyzB27FiVfWctqR/DMHWui6uurm70Ndu0aSOzLs7Y2Fh+TRNCpMaOHYuff/6ZVduzZw8mTZrEUUctGwU7DjTXJo8HDhzA5MmTIRaLWXUHBwecO3cOVlZWjb62PNXW1uLIkSMIDQ3Fw4cP6z2vS5cuCAoKwujRo5X2AQ/ScDk5OTLr4l5ejtBQBgYGda6Lo9ECQhSjoKAADg4OMpvr3759GzY2Nhx21jJRsFMwiUSCgQMH4q+//mLVz5w5A19f30Zdk2EYrFq1CsHBwTLH+vfvj5MnTyrFaEVNTQ0OHTqE1atX4/Hjx/We5+joiKCgIAwfPpwCnYorLS2VWRf36nuQG0JDQwMuLi7o0aOHNMR17tyZ/pwQwrEzZ87Az8+PVXv//ffx559/0rpVBaNgp2Bbt25lvUgZACZNmtToFymLRCJ88cUX2L17t8yxMWPG4MCBA5yvI6qursb+/fuxZs0aZGRk1Huei4sLgoOD8fHHH9M3AhVUW1srsy7uzp07TVoXZ29vL7Mujp6AJkQ5TZo0Cfv27WPVtm3bhhkzZnDUUctEwU6BHjx4AFdXV1RWVkpr1tbWuH37NgwNDRt8vfLycowePbrOF9/Pnz8fa9eu5TQgVVVVYc+ePVi7du1rd/X38PBAcHAwhgwZQtNnKoJhGDx69EhmXVxVVVWjr2lhYSGzLo7e7UuI6iguLoaTkxNrVF5XVxcJCQno1KkTh521LBTsFEQsFqNv376Ijo5m1S9evIiBAwc2+HrPnj2Dn58fYmJiWHUej4ctW7Zg5syZTeq3KSoqKrBr1y5s2LABT58+rfe8Xr16Yfny5fD29qZAp+Ryc3MRExPDCnKFhYWNvp6+vr7Murj27dvTnwNCVNzFixfxwQcfsGpeXl64evUqLZlQEAp2CrJ+/XosXLiQVZsxYwa2bdvW4GulpKTA29sbqamprLqWlhaOHDmC4cOHN6nXxiorK8OOHTuwceNGPHv2rN7zvLy8sHz5cgwaNIj+IVdCZWVlMuviXjeF/iZCoRDOzs7SANejRw907dqVvskToqZmzJiB77//nlXbsGED5s2bx1FHLQsFOwVITk6Gu7s767VGHTt2REJCAvT09Bp0revXr8PPz0/mFWQmJib43//+By8vL7n03BAlJSXYvn07Nm3ahPz8/HrP69+/P4KDg9G/f38KdEqitrYWSUlJMuviJBJJo69pZ2fHerjB1dVVqd9FTAiRr7KyMri6uuLRo0fSmpaWFuLi4tCtWzcOO2sZKNg1s9raWrz77rusV2PxeDz8/fffDQ5h4eHhGDNmDGuNHgDY2Njg/Pnz6NKli1x6fltFRUXYunUrvv3229dOyw0aNAhBQUHo16+fArsjr2IYBqmpqawQFxcX16R1ca1bt5ZZF9eULXsIIeohIiIC/fr1Yz081b17d0RHR9N+pM2MvrrNbM2aNTLvO507d26DQ93OnTsxY8YMmZEUNzc3nD17Fm3atGlyr2+roKAAW7ZswZYtW177Xk5vb28EBQXB09NTYb2R/+/Zs2cy6+IKCgoafT09PT2ZdXFWVlY0+koIkdGnTx/MmTMHmzZtktZu3ryJtWvXYtmyZRx2pv5oxK4ZxcXFoVevXhCJRNJa165dERcX99ZbNjAMg2XLlmH16tUyxz766CP8+uuvMDAwkFvPr5OXl4dvv/0WW7duRWlpab3nDRkyBMuWLUPPnj0V0hf59wnpuLg4VohLS0tr9PUEAgFrXVzPnj1pXRwhpEEqKyvh7u7OerOQUChETEwMXF1duWtMzVGwaybV1dXo3r07kpKSpDWBQIDo6Gj06NHjra5RU1ODyZMn4/DhwzLHJk6ciB9++AEaGhpy67k+ubm52LRpE77//nuUl5fXe56/vz+WLVsGd3f3Zu+pJROJRDLr4pKTk5u0Lq5jx46sEOfm5kbr4gghTXbjxg14enqy3ojk7OyMGzducL7Hqrqiqdhm8s0337BCHQAsXrz4rUNdSUkJhg8fjosXL8ocCwoKwooVK5p9Cuzp06fYsGEDdu7cKbOu7wUej4cRI0Zg2bJlcHZ2btZ+WiKGYfD48WOZdXH1/X68jVatWqFXr17SBxx69OgBMzMzOXZNCCH/6tmzJxYtWoTQ0FBpLTExESEhIawakR8asWsG//zzD7y8vFgjKC4uLrhx4wY0NTXf+Pns7Gz4+voiISGBVRcIBNixYwemTJki955flpmZifXr1+OHH36o90XsfD4fo0ePxtKlS+Hg4NCs/bQkz58/l1kX97onjd9EV1dXZl2ctbU1rYsjhChMTU0NevTogcTERGmNz+cjKioKvXr14rAz9UTBTs4qKirg5uaGBw8eSGsaGhq4efPmW41o3blzBz4+PjL7hunq6uKXX37B4MGD5d7zCxkZGVi7di327t3L2prlZXw+H+PGjcOSJUvQuXPnZuulJSgvL0d8fDwrxL3uHbpvIhAI4OTkJLMujp5AI4RwLSEhAT169EBtba201rlzZ8THx9OyDzmj7/hytnTpUlaoA/6dln2bUPf3339j6NChKCoqYtVbtWqFM2fOvPU0bkM9fvwYa9aswYEDB1h/6V4mFAoxYcIELF68mF4N0wgikQh37txhhbikpCTWupOGeuedd2TWxenq6sqxa0IIkQ8XFxcsX76c9UTs/fv3sXTpUmzevJnDztQPjdjJ0dWrV9G/f39WrWfPnoiMjHzjqMmvv/6K8ePHy0x9durUCefPn0fHjh3l3S5SUlKwevVqHD58uN6AoaGhgcDAQCxatAi2trZy70EdMQyDtLQ0mXVxFRUVjb6mubk5K8T16NED5ubmcuyaEEKal0gkgqenJ+tVmDweD3/99RftcypHFOzkpLS0FC4uLqypNG1tbcTHx79x4+CwsDDMmTMHr/5W9OrVC//73//QqlUrufZ67949hIaG4qeffqr3SUpNTU1MnjwZCxcuhLW1tVzvr27y8vJk1sW9+maQhtDV1YWHh4c0wPXs2RMdOnSgdXGEEJV39+5duLm5sQYxbG1tkZiYCH19fQ47Ux80FSsn8+fPl1kfFRoa+tpQJ5FIMG/ePHz77bcyx4YMGYKjR4/KdWotOTkZq1atwrFjx2RC5Ava2tqYOnUqFixYgHbt2snt3uqioqJCZl3cq+/sbQg+ny+zLq5bt260Lo4Qopa6du2K0NBQ1ntjHz9+jAULFsi8X5Y0Do3YycGFCxfg7e3NqvXt2xdXrlypd0PXqqoqBAQE4JdffpE5Nn36dGzdulVu/7gnJCRg1apVOH78eL3n6Ojo4PPPP8e8efNgaWkpl/uqOrFYLLMu7vbt201aF2drayuzLq6h7wsmhBBVJhaL0b9/f0RERLDqf/zxBz744AOOulIfFOyaqKioCI6OjsjKypLWdHV1kZiYWO+6uMLCQnz88ce4du2azLHVq1dj0aJFcpl2i4uLw8qVK3Hq1Kl6z9HT08OXX36JOXPmoHXr1k2+p6piGAbp6emsKdXY2NjXbsj8JmZmZjLr4uQ9rU4IIaro0aNHcHZ2Zq09bt++PZKSkmBkZMRhZ6qP5nuaaPbs2axQBwAbN26sN9RlZGTAx8cHd+7cYdWFQiH27duH8ePHN7mnGzduYOXKlTh9+nS95xgYGGDWrFmYPXt2i1yEn5+fL7Mu7vnz542+no6ODjw8PKRr4nr27AlbW1taF0cIIXXo2LEjNmzYgBkzZkhrmZmZmD17Nvbv389hZ6qPRuyaIDw8HMOGDWPVBg0ahD/++KPOf9ATEhLg4+ODp0+fsuoGBgb47bffmjwEHRUVhZCQEFy4cKHec4yMjDB79mx89dVXMDExadL9VEVlZaXMurhHjx41+np8Ph+Ojo6s0TgHBwdaF0cIIQ0gkUjw4Ycf4tKlS6x6eHg4hgwZwlFXqo+CXSPl5eXB0dERubm50pqhoSFu375d51OkFy9exCeffILS0lJW3dLSEmfPnm3SC5GvXbuGkJAQmb8cLzMxMcGcOXMwc+ZMtR7mFovFuHv3LivEJSYmNmldXIcOHVghzt3dndbFEUKIHGRkZMDR0ZH1b6OFhQWSk5PpVYeNREMMjTRjxgxWqAP+3bakrlD3448/IjAwECKRiFXv2rUrzp07Bxsbmwbfn2EYXLlyBSEhIbh69Wq955mbm2PevHn44ov/1969h0Vd5v8ff86B4XwYIIaTYAoCcvCAhxW01c2NrVRMUzP1q1vZ6rfdrq0r18rM3cptN3WzLcvlZ5uHLcU2UX6lv7Z2q1VMQ1QUFMETIqckGBCG0xx+f6jkcFAZUGR8P/7x4jOfuT+fYbiu++V9vz/3/b+4u7t3+jq3M4vFQlFRkVWIO3DgQJfq4ry9vdvUxd3JtYdCCHEzhYSEsHr1ah5//PGWY+Xl5fz6179m8+bNPXhnvZeM2NkgNTWVRx55xOrYhAkTSE9Pt5qCtVgs/PnPf+aFF15o08aYMWPYsWNHp6dDLRYLX3zxBa+88goZGRkdnufn58fvfvc7FixYYDejS5WVlS11cVf+bR2uO8PJyYmhQ4daBbl+/fpJXZwQQtxCFouFiRMn8tlnn1kd37p1K9OmTeuhu+q9JNh1UllZGdHR0VRWVrYc02q15ObmWi0TYjKZ+M1vfsN7773Xpo1p06axceNGnJycbvi6FouFXbt28corr7B///4OzwsICGDx4sXMnz+/V28vVV9fz+HDh61G406ePGlze0qlkujoaKuHG2JiYnBwcOjGuxZCCGGL0tJSoqOjqaqqajnm4+NDbm4uOp2uB++s95Fg1wkWi4XJkyeTnp5udXzz5s1WI3gGg4GZM2e2OQ8uPUW7atUqlErlDV8zPT2dV199laysrA7PCw4O5vnnn+fxxx/vVGC8HZhMJvLy8trUxbWeuu6M0NDQNnVxsqq5EELcvjZv3syjjz5qdSw5OZm0tDSZSekECXbXYTabaWxsxNnZmQ0bNjBv3jyr1x9++GG2bt3a8kdXUVHBxIkT2bdvX5u2/vKXv/DMM8/c8HXT0tJ49dVXyc7O7vC80NBQXnjhBebNm4ejo+ONf7AeYrFYOH/+fJu6uNraWpvb1Gq1beri5H94QgjRu1gsFqZNm8Ynn3xidXzjxo3MmTOH+vp6HB0db3hg5E4lwe4adu7cyaxZs6ivr2f69Ons2LGDmpqaltf9/PzIyclpWXT21KlT3H///RQUFFi1o9Fo2LRpE9OnT7/uNU0mE//85z959dVXyc3N7fC8fv36sWTJEubMmXNbTydWVVVx4MABqyBXVlZmc3uOjo5t6uL69+8v/5sTQgg7cOHCBaKjo63WFfXw8CA5OZmtW7fi7OzMhx9+yAMPPNCDd3l7k2B3DWFhYddc7ywtLY3JkycDkJmZyYQJE/j++++tzvHy8mLHjh3cc88917yW0WgkNTWV1157jby8vA7PCw8P56WXXuLRRx+97dZNa2hosKqLy8zMJD8/3+b2FAoFAwcOtApxsbGxt3WQFUII0TVpaWlMmTKlw9f79+/fpZpre3dHBDuTyURlZSXl5eWUl5dzoayMxvp6zCYTSpUKR2dn7vL3R6fTodPp8Pb25uLFi9d8YnXmzJl89NFHwKWRvWnTplltjQLQp08fdu3aRXR0dIftNDc389FHH7F8+fI2I31Xi4yMZOnSpcyYMaPD/WdvJbPZ3G5dXHNzs81thoSEtEyljhgxgvj4eLtbokUIIcT1zZw5ky1btnT4ul6vx9PT06b+/XboQ2+m22vIp5tVVVWRnZ3N0YMHaairw2I04lZfj2dlJc5GI0qLBbNCQbNazQlvb7KcnVGo1Ti5unJXYCCenp5UV1e32/b27dvZs2cPeXl5LFiwoM0CuHFxcezatYvAwMB239/U1MSmTZv44x//yOnTpzv8DDExMSxdupSpU6f22B+jxWKhuLi4TV1c68WWO8PLy6tNXZy/v3833rUQQojeqLi4+JoPCwLk5OTQ3NxsU/8eO3QogwYNstvdl+xyxK6kpIS9e/ZwpqAAB4OBkHNFBFRW4llXh8M1diBoVqmodnWl1Nub04EBVJpMFJw5w569e9utC3NwcGh3hOree+9l27ZteHh4tHmtsbGRDz74gNdff51z5851eC+DBw9m6dKlTJ48+ZYXiur1+jZ1ca23QesMR0dHhgwZYhXkwsLCpC5OCCFEG3PmzOEf//hHu6/5+/szOiGBwbGxuDY329S/nwvpQ7OLC3eHh5M4ZozVUmX2wK6CndFoJCMjg8yMDNwqKggrPEdwRQUqs7nTbVXXGzjj4cG58HAq3NzIyMxk7969192aavbs2bz//vtoNBqr4w0NDaxbt44//elPFBcXd/j+YcOG8fLLLzNhwoRbEnwaGxvJzs62CnEnTpywuT2FQkFUVFSburjWvw8hhBCiPcnJyW2WC1OpVCQkJJA4fDi+tbVEFBcTdrHWpv7dpFRy3teXk6Eh1Pr6MjwxkcTExNuubt1WdhPsysrK+Cw9narzxUQWFBBeXIyyCx+tqqqK+oZ6zAoFJQMGUBAZSXFlJek7d7Z5QOKKF154geXLl1sFMoPBQEpKCm+88cY1R71GjhzJsmXL+MUvfnHTAp3ZbObEiRNWIS47O7tLdXHBwcFWIS4+Pr7dkUohhBDiRuzfv5+kpKSWUig/Pz8mPfggQVot4Xl5BObn4+rkhNara1OpZoWCgqAg8sLD8Q4O4oFJk+yiJMgugl1hYSFpqam4lJQSf/w4Hq0eYrBFeXk5JvOPo3MGDw+Ox8dT4uLCx9u3t5lGjY2NJTs7uyWU1dbWsnbtWlasWNFhEARITExk2bJljB8/vtsDXXt1cVcv19JZnp6eVjVxw4cP77CGUAghhLBVaWkpS5cu5csvv2RacjIBBgNRWVm4XO7DVEpVt61XWuPiQlZUFIbAQB6aMd2m/dtvJ70+2BUWFvLJ5s34FJ5jxLFjqG0Ylm1PWXk5ZrP1tKtJpeL4yJGc8/Fhy7ZtbcLd888/z4svvsiaNWtYtWoVFRUVHbY/duxYXn75ZcaOHdstga66urpNXVxJSYnN7Wk0mnbr4mRhSCGEELdCYWEhWzZtwvPUKQbs3YvqqlIopVKFfzcuRG9UKtkfPZDKkBCmzpzZq8Ndrw52ZWVlbNm4Ea8zZxmVm9ulqdfWDAYD+upqwIICBRYutW1WKDg2ahRntFo2bdliNRrn6OiIs7Mzer2+w3bHjx/P0qVLr7uu3bU0NjZy5MgRqxB3rbXvrkehUBAZGWkV4uLi4qQuTgghRI9o3b831tej1+uxWMyAAi9Pz27fD92sUPBtTDT6vnfzyP/M6bXTsr022BmNRjb8/e+Yjh1nzKFD3TZSdzWT2YzRaKSmpobm5qYfj6tUHL7npxw3NvPBpk3XfaAC4P7772fp0qWMGjWqU/dgNpvJz88nMzOzJcQdPnyYpqam67+5A0FBQW3q4jw9PW1uTwghhOguHfXvFi4tFaZWq1HdpNkjo1LJf4cOwSEqiv957LFe+UBF77vjyzIyMqg6X8y448dvSqgDUCmVqDQalK2mSlUmE5FZB6gZN46EhAR2797dYRsTJ05k6dKlDB8+/IauWVJSYjUSl5mZ2aW6OA8Pj5YFf6/UxgUFBdncnhBCCHEzddS/KwDHmzyTpDabiT92nK89PNi7d2+XZtd6Sq8MdiUlJWRmZBBZUNAtD0pcj5dWS8WFC5jMZpQKBWaLBdeaGsLz8mgcPpyCgoI269w99NBDvPTSSwwdOrTDdmtqatrUxV1rKZTr0Wg0DB482Gr3hgEDBkhdnBBCiF7hVvfv7fE0GIjIL+A7R0fCw8N73Tp3vTLY7d2zB7eKCsK7EII6Q6VUotPpsMDlAHdp9jowP5+y4GASExL4ZNu2lvM1Gg0ff/yx1U4RTU1N7dbFdWUmvL26OEdHR5vbE0IIIXrSre7fOzKguJjiAH8y9uzh4WnTevReOqvXBbuqqirOFBQwpPBctz4scSOMzc2XCzcvUVos9Dl5kh+GDLHafqypqYl3330XrVbbEuIOHTrUpbq4gIAARo4c2RLihg0bJnVxQggh7EZP9u+tKS0W+hee47CPD1VVVb1q+7FeF+yys7NxMBgIvsZSIjeL2sGBS7P8P/7B+RYV4RIby6BBg/jvf//bcvzpp5+2+Tru7u5WdXEjRoyQujghhBB2rSf79/b0qaggx2DgyJEj/PSnP+3p27lhvSrYmUwmjh48SMi5Ipu2EekqBeDr40N1TQ1GoxGLxYzKbCa4sJChsbHs3r2701OrDg4ODBo0yCrERURESF2cEEKIO0ZP9+/tUZnNhBYVcSQri9GjR1uVV93ObEoPvr6+Xb7wE088walTpzp8ffXq1VZTl+PGjaOyspKGujoCKivbnD/7yBGSsg4w8eBBphw+xLHa2i7fY3s0Gg1KpdJqSta7tBRXJyd8fHyu+/6IiAjmzJnD22+/zb59+6ipqSEzM5M1a9Ywd+5coqKirhnqvvvuO4YNG4aDgwOffvppt3wmIYQQd4YNGzag0Wioqqpq9/W1a9eSmpoKwPr1663Wau3bty+13dy36vV6UlJSWvp3/enT/PnM6XbPbTKbmXToIJMOHSRh/z7GfLefSYcOMiP7cLfe0xW/zTvO77ZvZ/fu3VS2kztuVz02Yrdu3bprvr569WqeeOKJlkVyv/rqK3JycrAYjXh18If1dmQUA1xd2VpWxhtnz7A+JrZL92iyWFC1sytE61o5V70etUKBTqez2m3C19eXxMREq7o4Ly+vLt1TYGAg77//PqtWrepSO0IIIe48qampDB8+nLS0NB577DGr10wmEwsWLGj5ef369QwbNgw/P7+bdj9Xgl1CQgIWo5FRajVj7u7X7rkapZL0IZdWmvhrYSFaBwfmtNrWsqN+u7MuNDVRYDCQPnwEn8bFUV5ezl133XXN95jN5m6bbTOZTDaPEHZbsDt48CALFiygvr6eIUOGkJKSgpOTEzt27GDRokV4enoSFxeHVqtl5cqVjB07lnfeeYeoqCjmzp3LwYMHUalUPPvssxgMBkpKSkhISKBv376kp6fj6+tLamoqbvX1/J/Cs3x24QIKYIrOn1+2qj+L9/Dg78XngUtf8htnzpBZU02z2cL84GAm+flhMJl47sQJztQbGOTuwb5qPZ8NjSfn4kXWFJ1Do1RSbTSyISaWP5w6SYHBgMUCz/XtS6RGwz59FX+tqEAJqBQKHrtYi6urq9V9zJw5k5/85Cds2rSJRx55hJMnT7J48WKKi4vx8vLijTfeIDg4mEWLFuHu7k52djZVVVW8/vrrjBw5ssPftbu7O3V1dZSVlXH6dPv/sxFCCCGuptfryc3NZcWKFbzzzjuMHTuWt956i6KiIs6ePUt0dDTe3t5otVp0Oh2ZmZkkJyfj4uLCjh07MBqNLFmyhC+++AK1Wk1KSgp+fn4sWrQIV1dXjhw5gl6vZ+XKlaxfv55jx46RlJTEokWLAEhLS2PDhg00NzeTkJDAkiVLeOaZZ8jNzWXixIlEBwai01fzUVkpqyMiqTOZeOX0aU4Y6lAAL/cPY3g7Dw0uzj+Bk1JJTm0t4318CHNxYW1REUaLBT+NhlURkXio1SzOP4G7Sk127UX0zc0sDw9nhKcXJ+rqWJx/givzcB9Ex/BEbg5FDQ1MPZDJ5NAQPvvsM2bPno3RaOS+++5j1apVKBQKfHx8mDlzJt988w0ff/wxM2bMICYmhu+++47x48eTlJTE66+/Tl1dHWlpaYSHh3PhwgV+9atfce7cORwcHHj33XcZMmQI8+bNw9nZmaysLJKTk1myZIlN37NNO0/4+vq22Qc1NjaWdevWMXLkSBYuXEh4eDgLFy4kMjKSjIwM/P39GT9+PMOGDbMKdo2NjTz99NNkZGQAl/Y89fT0pG/fvuTk5ODm5tZyzXfeeousjRvZs38/66Jj0CiV6Jub8XJwYPaRI7zcvz8DXF15//x5KpubWXT33WwpK6XOaOLx4GAaTCamZWezMTaWf5aX8X1TE0v69SdDX8Uvc3I4NCqBnIsXWXj8GLuGxqNzdGTV2bNEu7nyC9+7qGxuZuaRbHYOGcpjhw8x1dOTYS4u1JpMFI8axcrMTApOnrTpixBCCCHuVDOnT2esSoXhm29Iq67mFX9/1v7wA2rgCR8fLChQubnh7+4OWI/YLc4/QYPZzOqISBQKBdXGZjxUahQKBRtLiqkzmVjYJ4TF+ScwWWBlRATf6vW8V3SOjbFxvHLqJBGurszwD6DBZEKpUPB9UxNP5x1n2+Ah7O7fn8Wf/l8yDxwgJCSEiRMn8vjjjzNlyhQUCgWffvopDz74IGfPnmXAgAEcPXqUsLAwYmJimDBhAitWrOBvf/sbx44d46233mLWrFn89re/ZfjldXBnz57N/v37mTdvHgaDgdTU1C7tId8tI3Z6vZ7GxsaWUaY5c+awYsUKfvaznxEZGUlwcDAAU6dOpbCw0Oq9/fr1o6SkhKeeeork5GTuu+++Dq/TWF/P8fPnmarzR3N5uNPLwaHl9d/kHafJbKbWZGoZrs2oqiLfYGDHhUt1ArUmI0UNDRysuciTl+8r0UuL11Xbhgz18EB3eT24DH0VX1f+wLtFRQDUm0ycraoixsmJlB9+oLCpibFubqibmgjQ6STYCSGEEJ3k5OiIutWCxFkGA8svLw6swIK5thaLuzvtRZ4kH9+WMFTS0MjTZ/L4obmJBrOZQZfDIMDPL9fCx7i5UdzYCMAQdw/eKTqHvtnI/Xf5EuLkbNV2ReUP6HQ6+vbtC8CsWbPYvXs3U6ZMwdnZmQcffLDl3IiICCIiIgCIiopi/PjxwKXBr507dwLw5Zdfkpub2/Keq+sdH3744S6FOrjJNXY3Mhio1Wo5evQoO3fu5M033+Rf//oXK1eubPdcs8kE12jz7cgowl1c+OOZ07x2+hRrogZiBl4NC2OEp1fru+uwHeer5sjNFgtrB0YT5OTUcqzm4kVmabWMdHHhW4OB/y0u5ncDBxIRFsZ/L488CiGEEOLGqJVKFNd5GvZagcdZ9WO//drpUyzsE8JorZavKn9gW3l5y2sa5aU2ruwiBTDRz484d3f+U1nJL3Ny+GtkFJ5XDfYoLJYOs4eLi4vVz1dvEqBUKlt+ViqVVvvKHzhwoN19aFu3Z4tuqfLz8vLC0dGRzMxMAD788EPuueceIiMjycvLo7i4GJPJxLardme4oqKiArPZzPTp0/n973/P4cOHgUt1ZBcvXrS+WZWK2MBAPikvo+nyH4C+udnqHIVCwbOhfTlcU8Npg4HRXlo+LC3FdPlLya+rw2SxMMTDg12Xp5O/1evRG43tfrZErZaNJSUtPx+rrcXd3Z1ys5kwR0fmaLWEOjhwwWCgSq/v/C9PCCGEuMMZzWYsrR48iHdxIf3ywv8KhRKVm1u7o3Wt1ZpM6DQaLBYL2696qrcj5xrqCXFy4pdBQYz28uJkq5HDAE8vyr//nsLCQsxmM5s3b+7SHrLjxo3jvffea/k5Ozvb5rbaY9OIXVVVVcv0KsCKFStYv349CxcupKGhgcGDB7Nw4UKcnJxYvXo148aNw9PTk8jISDw8PKzaKi4uZt68eZjNZtRqNatXrwZg/vz5jBs3jgEDBpCeng6Ao7MzMX37Ul9wksmHD6FWKJjqp2Nuq4cnnFUqHgsK5u/FxfwhLIzzDQ1MPnQQM3CXRsO66BhmBQTy3Ik8HjiYxSA3d3QaDU7tPM3yVJ8QXjt9iokHszBaLES7ubEyIpL0hgb26fVgNhOh0RDq78/OY8es3rt9+3acnJxYu3YtH330ERUVFTz55JOcP38erVZLSkoKoaGhPPnkk0yePJkHHniA2tpahg0bRl5eXru/+6NHj/LQQw+h1+txdnamf//+fP311537AoUQQtxRkpKSeOmllxgzZkzLseeee47U1FRefPFFFi5cCMBrr72Gj48PCxcuZPv27Sxbtgw3NzcyMjKIjIzkwIEDuLm5sXPnTrZv305KSopVH5abm8uzzz7L559/DsCkSZNYtmwZ8fHxbNmyhTfffBOLxYJGoyElJYWBAwcyd+5cvvjqK6r9/Jjs7Y1zYyMB/gEs8r2LZadO8WRpKUqFgpf79edGns/9dUgIvzp2DC8HNcM9PClpbLjm+TsvVJB+4XvUCgVBjo783MeHyqsHjZycmP/EEyQnJ7c8PDF58uTOfgUt3n77bRYsWMC6detoampi0qRJDBo0yOb2WrPp4YnOqK2txc3NDZPJxJQpU5g/fz4TJkywqa1///vfnPj8c37+7b4u35fRYsFssaBRKsm+eJE/nDrJtsFDbGqrqbmJ/zdsGDuPH+c///kPcGk4trS0tFdtQyKEEEL0hO7s37vbF6N+QkRSEvfee29P38oNuenr2L333nt8+OGHNDY2Mn78eKsiw87S6XRkOTvTrFLhcNVctS0MJhNzjx7FaLHgoFTw+/5hNrelcHLG5OPDU089RWBgIBcuXOC5556TUCeEEELcgO7s37tTs0pFrbMzOp2up2/lht30YLdo0aKWNWy6SqfToVCrqXZ1xbempktteajVpA2xbYSutWpXVxRqNWPGjGHKlCnd0ubnn3/O4sWLrY4lJiayZs2abmlfCCGEuF10Z//ena7077c62C1fvpyPP/7Y6tiVZxCup1ftFevt7Y2Tqyul3t631Rdf6nPpvry9vbutzaSkJJKSkrqtPSGEEOJ2dSf17zdiyZIlNi9Q3Kt2mlepVMQOHcq5kD6Yumnbjq4yKZUU9ulDXHx8r9kgWAghhLidSP/efW6P314nDBo0iGYXF877+vb0rQBQ5OuL0cWFuLi4nr4VIYQQoteS/r179Lpgp9VquTs8nJOhIZi7YaPfrjArFJwKDeHuAQPkQQkhhBCiC6R/7x69LtgBJI4ZQ62vLwWt1q+71fKDgqj19SVx9OgevQ8hhBDCHkj/3nW9MtgFBAQwPDGRvPBwarph+w1bVLu4cGJAOCNGjybg8l52QgghhLCd9O9d1yuDHVxa+kMbHERWVBTGW1xoaVQqyRoYhXdQEAkJCbf02kIIIYQ9k/69a3ptsFOr1Tw4aRKGwED2Rw+8ZfPxZoWC/dEDqQ8I5IFJk9rdxFcIIYQQtpH+vWt6bbAD8Pf356EZ06kMCeHbmOibnuyNSiXfxkRTGRLCQzOm4+/vf1OvJ4QQQtyJpH+33U3fK/ZWKCwsJC11Ky4lJcQfP46HwdDt16h2cSFrYBT1AYE8NGM6oaGh3X4NIYQQQvxI+vfOs4tgB1BWVsZn6elUnS8msqCA8OJilN3w0cwKBflBQZwYEI53UBAPTJrUq5O8EEII0ZtI/945dhPsAIxGIxkZGWRmZOBWUUH/wnP0qahAZTZ3ui2TUkmRry+nQkOo9fVlxOjRJCQk9No5dyGEEKK3kv79xtlVsLuipKSEvRkZnMnPR20wEFpURMAPlXjW1eFgMnX4vmaVimpXV0p9vCns0wejiwt3DxhAYi995FkIIYSwJ9K/X59dBrsrqqqqOHLkCEeysmioq8NiNOJWX49HZRUaoxGlxYxZoaRJrabGW0utszMKtRonV1fi4uOJi4vrdStOCyGEEPZO+veO2XWwu8JkMlFZWUl5eTnl5eVcKCujqaEBk9GISq1G4+TEXf7+6HQ6dDod3t7evWrDXyGEEOJOJP17W3dEsBNCCCGEuBP06nXshBBCCCHEjyTYCSGEEELYCQl2QgghhBB2QoKdEEIIIYSdkGAnhBBCCGEnJNgJIYQQQtgJCXZCCCGEEHZCgp0QQgghhJ2QYCeEEEIIYSck2AkhhBBC2AkJdkIIIYQQdkKCnRBCCCGEnZBgJ4QQQghhJyTYCSGEEELYCQl2QgghhBB2QoKdEEIIIYSdkGAnhBBCCGEnJNgJIYQQQtgJCXZCCCGEEHZCgp0QQgghhJ2QYCeEEEIIYSck2AkhhBBC2AkJdkIIIYQQdkKCnRBCCCGEnZBgJ4QQQghhJyTYCSGEEELYCQl2QgghhBB24v8DCr+MM2zTq9cAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "\n", - "\n", - "est = tpot2.TPOTEstimator(population_size=20,generations=10, \n", - " scorers=['roc_auc_ovr'],\n", - " scorers_weights=[1],\n", - " classification=True,\n", - " inner_config_dict= \"arithmetic_transformer\",\n", - " leaf_config_dict=\"feature_set_selector\",\n", - " root_config_dict=root_config_dict,\n", - " )\n", - "\n", - "#load iris\n", - "scorer = sklearn.metrics.get_scorer('roc_auc_ovo')\n", - "X, y = sklearn.datasets.load_iris(return_X_y=True)\n", - "X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n", - "est.fit(X_train, y_train)\n", - "print(scorer(est, X_test, y_test))\n", - "est.fitted_pipeline_.plot()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Recursive Configuration Dictionaries (EXPERIMENTAL)\n", - "\n", - "Configuration dictionaries can also be nested. If the string \"Recursive\" is used in place of a type, the node that would go in that place will now represent a graph with those restrictions. \n", - "\n", - "All inputs to the recursive node will be merged and input to all the leaves within the recursive graph. The output of the graph will be sent to the outputs of the node that represents it. \n", - "\n", - "This is handy for restricting the search space of the model as well as setting specific ensembling templates.\n", - "\n", - "(Currently) These are all flattened and merged into a single graph when exported as a graph pipeline. In the future these could be used for ensemble methods such as boosting/stacking/etc.\n", - "\n", - "Note that this is not a new instance of the TPOT2 estimator, and it does not independently run GP. Rather this recursive node just sets a search space restriction for that node." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "transformer_config_dictionary = \"transformers\"\n", - "selector_config_dictionary = \"feature_set_selector\"\n", - "classifier_config_dictionary = root_config_dict \n", - "\n", - "#Some example search spaces with nested graphs\n", - "\n", - "#pipelines of the shape selector->transformer\n", - "st_params = { \n", - " 'root_config_dict':transformer_config_dictionary,\n", - " 'leaf_config_dict':selector_config_dictionary,\n", - " 'inner_config_dict': None,\n", - " 'max_size' : 2, \n", - " 'linear_pipeline' : True}\n", - "\n", - "#pipelines of the shape (selector->transformer) -> classifier. \n", - "# This is equivalent to setting TPOT1 to use the 'Selector-Transformer-Classifier' template\n", - "st_c_params = { \n", - " 'root_config_dict': classifier_config_dictionary,\n", - " 'leaf_config_dict': {\"Recursive\" : st_params},\n", - " 'inner_config_dict': None,\n", - " 'max_size' : 2, \n", - " 'linear_pipeline' : True}\n", - "\n", - "#pipelines of the shape ((selector->transformer) -> classifier)*N) -> classifier\n", - "#This is like having an ensemble of 'Selector-Transformer-Classifier' models with a final meta classifier\n", - "st_c_ensemble_params = { \n", - " 'root_config_dict': classifier_config_dictionary,\n", - " 'leaf_config_dict': {\"Recursive\" : st_c_params},\n", - " 'inner_config_dict': None,\n", - " 'max_size' : 6, \n", - " 'linear_pipeline' : True}" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.9880174291938998\n" - ] - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# linear pipelines of the shape selector->transformer->classifier\n", - "est = tpot2.TPOTEstimator(population_size=20,generations=10, \n", - " scorers=['roc_auc_ovr'],\n", - " scorers_weights=[1],\n", - " classification=True,\n", - " **st_c_params,\n", - " )\n", - "\n", - "#load iris\n", - "scorer = sklearn.metrics.get_scorer('roc_auc_ovo')\n", - "X, y = sklearn.datasets.load_iris(return_X_y=True)\n", - "X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n", - "est.fit(X_train, y_train)\n", - "print(scorer(est, X_test, y_test))\n", - "est.fitted_pipeline_.plot()" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/svm/_base.py:1244: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.\n", - " warnings.warn(\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.9976851851851851\n" - ] - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# ensembles of linear pipelines of the shape selector->transformer->classifier ensemble pipeline with a final meta classifier\n", - "est = tpot2.TPOTEstimator(population_size=20,generations=10, \n", - " scorers=['roc_auc_ovr'],\n", - " scorers_weights=[1],\n", - " classification=True,\n", - " **st_c_ensemble_params,\n", - " )\n", - "\n", - "#load iris\n", - "scorer = sklearn.metrics.get_scorer('roc_auc_ovo')\n", - "X, y = sklearn.datasets.load_iris(return_X_y=True)\n", - "X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n", - "est.fit(X_train, y_train)\n", - "print(scorer(est, X_test, y_test))\n", - "est.fitted_pipeline_.plot()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "tpot_dev", - "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.11" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "7fe1fe9ef32cd5efd76326a08046147513534f0dd2318301a1a96ae9071c1c4e" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/Tutorial/2_Search_Spaces.ipynb b/Tutorial/2_Search_Spaces.ipynb index e21d6c5c..940509de 100644 --- a/Tutorial/2_Search_Spaces.ipynb +++ b/Tutorial/2_Search_Spaces.ipynb @@ -31,7 +31,7 @@ "output_type": "stream", "text": [ "sampled hyperparameters\n", - "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 1, 'p': 1, 'weights': 'distance'}\n" + "{'metric': 'euclidean', 'n_jobs': 1, 'n_neighbors': 1, 'p': 3, 'weights': 'uniform'}\n" ] } ], @@ -43,9 +43,9 @@ "knn_configspace = ConfigurationSpace(\n", " space = {\n", "\n", - " 'n_neighbors': Integer(\"n_neighbors\", bounds=(1, 10)),\n", + " 'n_neighbors': (1, 10),\n", " 'weights': Categorical(\"weights\", ['uniform', 'distance']),\n", - " 'p': Integer(\"p\", bounds=(1, 3)),\n", + " 'p': (1, 3),\n", " 'metric': Categorical(\"metric\", ['euclidean', 'minkowski']),\n", " 'n_jobs': 1,\n", " }\n", @@ -154,9 +154,9 @@ "output_type": "stream", "text": [ "sampled hyperparameters\n", - "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 8, 'p': 1, 'weights': 'uniform'}\n", + "{'metric': 'euclidean', 'n_jobs': 1, 'n_neighbors': 1, 'p': 1, 'weights': 'uniform'}\n", "mutated hyperparameters\n", - "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 5, 'p': 2, 'weights': 'distance'}\n" + "{'metric': 'euclidean', 'n_jobs': 1, 'n_neighbors': 7, 'p': 1, 'weights': 'distance'}\n" ] } ], @@ -187,14 +187,14 @@ "output_type": "stream", "text": [ "original hyperparameters for individual 1\n", - "{'metric': 'euclidean', 'n_jobs': 1, 'n_neighbors': 9, 'p': 1, 'weights': 'uniform'}\n", + "{'metric': 'euclidean', 'n_jobs': 1, 'n_neighbors': 7, 'p': 1, 'weights': 'distance'}\n", "original hyperparameters for individual 2\n", - "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 3, 'p': 3, 'weights': 'distance'}\n", + "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 5, 'p': 3, 'weights': 'uniform'}\n", "\n", "post crossover hyperparameters for individual 1\n", - "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 9, 'p': 3, 'weights': 'uniform'}\n", + "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 5, 'p': 1, 'weights': 'uniform'}\n", "post crossover hyperparameters for individual 2\n", - "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 3, 'p': 3, 'weights': 'distance'}\n" + "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 5, 'p': 3, 'weights': 'uniform'}\n" ] } ], @@ -233,10 +233,414 @@ { "data": { "text/html": [ - "
KNeighborsClassifier(n_jobs=1, n_neighbors=9, p=3)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + "
KNeighborsClassifier(n_jobs=1, p=1)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ - "KNeighborsClassifier(n_jobs=1, n_neighbors=9, p=3)" + "KNeighborsClassifier(n_jobs=1, p=1)" ] }, "execution_count": 5, @@ -272,7 +676,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 6, @@ -386,13 +790,417 @@ { "data": { "text/html": [ - "
LogisticRegression(C=0.4989834645092814, class_weight='balanced', dual=True,\n",
-       "                   max_iter=1000, n_jobs=1)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + "
LogisticRegression(C=0.5857355940220703, class_weight='balanced', dual=True,\n",
+       "                   max_iter=1000, n_jobs=1, penalty='l1', solver='saga')
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ - "LogisticRegression(C=0.4989834645092814, class_weight='balanced', dual=True,\n", - " max_iter=1000, n_jobs=1)" + "LogisticRegression(C=0.5857355940220703, class_weight='balanced', dual=True,\n", + " max_iter=1000, n_jobs=1, penalty='l1', solver='saga')" ] }, "execution_count": 7, @@ -422,13 +1230,417 @@ { "data": { "text/html": [ - "
DecisionTreeClassifier(max_depth=9, max_features='log2', min_samples_leaf=12,\n",
-       "                       min_samples_split=4)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + "
LogisticRegression(C=2.032340407557342, class_weight='balanced', max_iter=1000,\n",
+       "                   n_jobs=1)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ - "DecisionTreeClassifier(max_depth=9, max_features='log2', min_samples_leaf=12,\n", - " min_samples_split=4)" + "LogisticRegression(C=2.032340407557342, class_weight='balanced', max_iter=1000,\n", + " n_jobs=1)" ] }, "execution_count": 8, @@ -466,16 +1678,431 @@ "metadata": {}, "outputs": [ { - "ename": "TypeError", - "evalue": "unhashable type: 'list'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[9], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m#same pipeline search space as before.\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m classifier_choice \u001b[38;5;241m=\u001b[39m \u001b[43mtpot2\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mconfig\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget_search_space\u001b[49m\u001b[43m(\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mKNeighborsClassifier\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mLogisticRegression\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mDecisionTreeClassifier\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124msampled pipeline 1\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 5\u001b[0m classifier_choice\u001b[38;5;241m.\u001b[39mgenerate()\u001b[38;5;241m.\u001b[39mexport_pipeline()\n", - "File \u001b[0;32m~/common/Projects/TPOT_Dev/tpot2/tpot2/config/get_configspace.py:169\u001b[0m, in \u001b[0;36mget_search_space\u001b[0;34m(name, n_classes, n_samples, random_state)\u001b[0m\n\u001b[1;32m 168\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mget_search_space\u001b[39m(name, n_classes\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m3\u001b[39m, n_samples\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m100\u001b[39m, random_state\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[0;32m--> 169\u001b[0m name \u001b[38;5;241m=\u001b[39m \u001b[43mGROUPNAMES\u001b[49m\u001b[43m[\u001b[49m\u001b[43mname\u001b[49m\u001b[43m]\u001b[49m\n\u001b[1;32m 171\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m name \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 172\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n", - "\u001b[0;31mTypeError\u001b[0m: unhashable type: 'list'" + "name": "stdout", + "output_type": "stream", + "text": [ + "sampled pipeline 1\n" ] + }, + { + "data": { + "text/html": [ + "
DecisionTreeClassifier(max_depth=30, max_features='sqrt', min_samples_leaf=3,\n",
+       "                       min_samples_split=18)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + ], + "text/plain": [ + "DecisionTreeClassifier(max_depth=30, max_features='sqrt', min_samples_leaf=3,\n", + " min_samples_split=18)" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ @@ -488,7 +2115,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -501,10 +2128,417 @@ { "data": { "text/html": [ - "
KNeighborsClassifier(metric='euclidean', n_jobs=1, n_neighbors=96)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + "
DecisionTreeClassifier(max_depth=19, max_features='sqrt', min_samples_leaf=8,\n",
+       "                       min_samples_split=5)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ - "KNeighborsClassifier(metric='euclidean', n_jobs=1, n_neighbors=96)" + "DecisionTreeClassifier(max_depth=19, max_features='sqrt', min_samples_leaf=8,\n", + " min_samples_split=5)" ] }, "execution_count": 10, @@ -519,23 +2553,435 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, "outputs": [ { - "ename": "KeyError", - "evalue": "'AdaBoostClassifier'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[11], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m#search space for all classifiers\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m classifier_choice \u001b[38;5;241m=\u001b[39m \u001b[43mtpot2\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mconfig\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget_search_space\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mclassifiers\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 4\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124msampled pipeline 1\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 5\u001b[0m classifier_choice\u001b[38;5;241m.\u001b[39mgenerate()\u001b[38;5;241m.\u001b[39mexport_pipeline()\n", - "File \u001b[0;32m~/common/Projects/TPOT_Dev/tpot2/tpot2/config/get_configspace.py:180\u001b[0m, in \u001b[0;36mget_search_space\u001b[0;34m(name, n_classes, n_samples, random_state)\u001b[0m\n\u001b[1;32m 178\u001b[0m \u001b[38;5;66;03m#if list of names, return a list of EstimatorNodes\u001b[39;00m\n\u001b[1;32m 179\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(name, \u001b[38;5;28mlist\u001b[39m) \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(name, np\u001b[38;5;241m.\u001b[39mndarray):\n\u001b[0;32m--> 180\u001b[0m search_spaces \u001b[38;5;241m=\u001b[39m [get_search_space(n, n_classes\u001b[38;5;241m=\u001b[39mn_classes, n_samples\u001b[38;5;241m=\u001b[39mn_samples, random_state\u001b[38;5;241m=\u001b[39mrandom_state) \u001b[38;5;28;01mfor\u001b[39;00m n \u001b[38;5;129;01min\u001b[39;00m name]\n\u001b[1;32m 181\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m ChoicePipeline(choice_list\u001b[38;5;241m=\u001b[39msearch_spaces)\n\u001b[1;32m 182\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n", - "File \u001b[0;32m~/common/Projects/TPOT_Dev/tpot2/tpot2/config/get_configspace.py:180\u001b[0m, in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 178\u001b[0m \u001b[38;5;66;03m#if list of names, return a list of EstimatorNodes\u001b[39;00m\n\u001b[1;32m 179\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(name, \u001b[38;5;28mlist\u001b[39m) \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(name, np\u001b[38;5;241m.\u001b[39mndarray):\n\u001b[0;32m--> 180\u001b[0m search_spaces \u001b[38;5;241m=\u001b[39m [\u001b[43mget_search_space\u001b[49m\u001b[43m(\u001b[49m\u001b[43mn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mn_classes\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mn_classes\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mn_samples\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mn_samples\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrandom_state\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrandom_state\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mfor\u001b[39;00m n \u001b[38;5;129;01min\u001b[39;00m name]\n\u001b[1;32m 181\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m ChoicePipeline(choice_list\u001b[38;5;241m=\u001b[39msearch_spaces)\n\u001b[1;32m 182\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n", - "File \u001b[0;32m~/common/Projects/TPOT_Dev/tpot2/tpot2/config/get_configspace.py:183\u001b[0m, in \u001b[0;36mget_search_space\u001b[0;34m(name, n_classes, n_samples, random_state)\u001b[0m\n\u001b[1;32m 181\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m ChoicePipeline(choice_list\u001b[38;5;241m=\u001b[39msearch_spaces)\n\u001b[1;32m 182\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m--> 183\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mget_estimatornode\u001b[49m\u001b[43m(\u001b[49m\u001b[43mname\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mn_classes\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mn_classes\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mn_samples\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mn_samples\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrandom_state\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrandom_state\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/common/Projects/TPOT_Dev/tpot2/tpot2/config/get_configspace.py:190\u001b[0m, in \u001b[0;36mget_estimatornode\u001b[0;34m(name, n_classes, n_samples, random_state)\u001b[0m\n\u001b[1;32m 186\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mget_estimatornode\u001b[39m(name, n_classes\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m3\u001b[39m, n_samples\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m100\u001b[39m, random_state\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[1;32m 187\u001b[0m configspace \u001b[38;5;241m=\u001b[39m get_configspace(name, n_classes\u001b[38;5;241m=\u001b[39mn_classes, n_samples\u001b[38;5;241m=\u001b[39mn_samples, random_state\u001b[38;5;241m=\u001b[39mrandom_state)\n\u001b[0;32m--> 190\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m EstimatorNode(\u001b[43mSTRING_TO_CLASS\u001b[49m\u001b[43m[\u001b[49m\u001b[43mname\u001b[49m\u001b[43m]\u001b[49m, configspace)\n", - "\u001b[0;31mKeyError\u001b[0m: 'AdaBoostClassifier'" + "name": "stdout", + "output_type": "stream", + "text": [ + "sampled pipeline 1\n" ] + }, + { + "data": { + "text/html": [ + "
ExtraTreesClassifier(max_features=0.40389574491352287, min_samples_leaf=15,\n",
+       "                     min_samples_split=13, n_jobs=1)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + ], + "text/plain": [ + "ExtraTreesClassifier(max_features=0.40389574491352287, min_samples_leaf=15,\n", + " min_samples_split=13, n_jobs=1)" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ @@ -548,7 +2994,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -561,19 +3007,417 @@ { "data": { "text/html": [ - "
GradientBoostingClassifier(learning_rate=0.5981565344248039, max_depth=6,\n",
-       "                           max_features=0.14704006316550916,\n",
-       "                           min_samples_leaf=18, min_samples_split=14,\n",
-       "                           subsample=0.36853097212587516)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + "
SVC(C=7.943520510912431, degree=1, kernel='linear', max_iter=3000,\n",
+       "    probability=True)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ - "GradientBoostingClassifier(learning_rate=0.5981565344248039, max_depth=6,\n", - " max_features=0.14704006316550916,\n", - " min_samples_leaf=18, min_samples_split=14,\n", - " subsample=0.36853097212587516)" + "SVC(C=7.943520510912431, degree=1, kernel='linear', max_iter=3000,\n", + " probability=True)" ] }, "execution_count": 12, @@ -597,7 +3441,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -610,16 +3454,439 @@ { "data": { "text/html": [ - "
Pipeline(steps=[('selectfwe', SelectFwe(alpha=0.026228617618654658)),\n",
-       "                ('zerocount', ZeroCount()),\n",
-       "                ('bernoullinb', BernoulliNB(alpha=0.04656547221901433))])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + "
Pipeline(steps=[('variancethreshold',\n",
+       "                 VarianceThreshold(threshold=0.16682490562982172)),\n",
+       "                ('nystroem',\n",
+       "                 Nystroem(gamma=0.7638884024411401, kernel='linear',\n",
+       "                          n_components=98)),\n",
+       "                ('extratreesclassifier',\n",
+       "                 ExtraTreesClassifier(max_features=0.41763504253232936,\n",
+       "                                      min_samples_leaf=8, min_samples_split=17,\n",
+       "                                      n_jobs=1))])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ - "Pipeline(steps=[('selectfwe', SelectFwe(alpha=0.026228617618654658)),\n", - " ('zerocount', ZeroCount()),\n", - " ('bernoullinb', BernoulliNB(alpha=0.04656547221901433))])" + "Pipeline(steps=[('variancethreshold',\n", + " VarianceThreshold(threshold=0.16682490562982172)),\n", + " ('nystroem',\n", + " Nystroem(gamma=0.7638884024411401, kernel='linear',\n", + " n_components=98)),\n", + " ('extratreesclassifier',\n", + " ExtraTreesClassifier(max_features=0.41763504253232936,\n", + " min_samples_leaf=8, min_samples_split=17,\n", + " n_jobs=1))])" ] }, "execution_count": 13, @@ -635,27 +3902,13 @@ " \n", "])\n", "\n", - "stc_pipeline = tpot2.search_spaces.pipelines.SequentialPipeline([\n", - " tpot2.config.get_search_space(\"preprocessors1\"), \n", - " tpot2.config.get_search_space(\"imputation\"), \n", - " tpot2.config.get_search_space(\"selectors\"), \n", - " tpot2.search_spaces.pipelines.GraphPipeline(\n", - " root_search_space= tpot2.config.get_search_space(\"classifiers\"),\n", - " leaf_search_space = tpot2.config.get_search_space(\"selectors\"), \n", - " inner_search_space = tpot2.config.get_search_space([\"transformers\",\"classifiers\"]),\n", - " max_size = 10,\n", - " )\n", - " \n", - "])\n", - "\n", - "\n", "print(\"sampled pipeline\")\n", "stc_pipeline.generate().export_pipeline()" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -668,23 +3921,432 @@ { "data": { "text/html": [ - "
Pipeline(steps=[('selectfwe', SelectFwe(alpha=0.0005298121736972592)),\n",
-       "                ('normalizer', Normalizer()),\n",
-       "                ('mlpclassifier',\n",
-       "                 MLPClassifier(alpha=0.00120637383824527,\n",
-       "                               learning_rate_init=0.001497725714419087))])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + "
Pipeline(steps=[('variancethreshold',\n",
+       "                 VarianceThreshold(threshold=0.029163176782587025)),\n",
+       "                ('rbfsampler',\n",
+       "                 RBFSampler(gamma=0.3360335889875927, n_components=61)),\n",
+       "                ('randomforestclassifier',\n",
+       "                 RandomForestClassifier(min_samples_leaf=2,\n",
+       "                                        min_samples_split=5))])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ - "Pipeline(steps=[('selectfwe', SelectFwe(alpha=0.0005298121736972592)),\n", - " ('normalizer', Normalizer()),\n", - " ('mlpclassifier',\n", - " MLPClassifier(alpha=0.00120637383824527,\n", - " learning_rate_init=0.001497725714419087))])" + "Pipeline(steps=[('variancethreshold',\n", + " VarianceThreshold(threshold=0.029163176782587025)),\n", + " ('rbfsampler',\n", + " RBFSampler(gamma=0.3360335889875927, n_components=61)),\n", + " ('randomforestclassifier',\n", + " RandomForestClassifier(min_samples_leaf=2,\n", + " min_samples_split=5))])" ] }, "execution_count": 14, @@ -708,31 +4370,449 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "Generation: 100%|██████████| 5/5 [00:48<00:00, 9.63s/it]\n" + "Generation: 0%| | 0/5 [00:00#sk-container-id-10 {color: black;}#sk-container-id-10 pre{padding: 0;}#sk-container-id-10 div.sk-toggleable {background-color: white;}#sk-container-id-10 label.sk-toggleable__label {cursor: pointer;display: block;width: 100%;margin-bottom: 0;padding: 0.3em;box-sizing: border-box;text-align: center;}#sk-container-id-10 label.sk-toggleable__label-arrow:before {content: \"▸\";float: left;margin-right: 0.25em;color: #696969;}#sk-container-id-10 label.sk-toggleable__label-arrow:hover:before {color: black;}#sk-container-id-10 div.sk-estimator:hover label.sk-toggleable__label-arrow:before {color: black;}#sk-container-id-10 div.sk-toggleable__content {max-height: 0;max-width: 0;overflow: hidden;text-align: left;background-color: #f0f8ff;}#sk-container-id-10 div.sk-toggleable__content pre {margin: 0.2em;color: black;border-radius: 0.25em;background-color: #f0f8ff;}#sk-container-id-10 input.sk-toggleable__control:checked~div.sk-toggleable__content {max-height: 200px;max-width: 100%;overflow: auto;}#sk-container-id-10 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {content: \"▾\";}#sk-container-id-10 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-10 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-10 input.sk-hidden--visually {border: 0;clip: rect(1px 1px 1px 1px);clip: rect(1px, 1px, 1px, 1px);height: 1px;margin: -1px;overflow: hidden;padding: 0;position: absolute;width: 1px;}#sk-container-id-10 div.sk-estimator {font-family: monospace;background-color: #f0f8ff;border: 1px dotted black;border-radius: 0.25em;box-sizing: border-box;margin-bottom: 0.5em;}#sk-container-id-10 div.sk-estimator:hover {background-color: #d4ebff;}#sk-container-id-10 div.sk-parallel-item::after {content: \"\";width: 100%;border-bottom: 1px solid gray;flex-grow: 1;}#sk-container-id-10 div.sk-label:hover label.sk-toggleable__label {background-color: #d4ebff;}#sk-container-id-10 div.sk-serial::before {content: \"\";position: absolute;border-left: 1px solid gray;box-sizing: border-box;top: 0;bottom: 0;left: 50%;z-index: 0;}#sk-container-id-10 div.sk-serial {display: flex;flex-direction: column;align-items: center;background-color: white;padding-right: 0.2em;padding-left: 0.2em;position: relative;}#sk-container-id-10 div.sk-item {position: relative;z-index: 1;}#sk-container-id-10 div.sk-parallel {display: flex;align-items: stretch;justify-content: center;background-color: white;position: relative;}#sk-container-id-10 div.sk-item::before, #sk-container-id-10 div.sk-parallel-item::before {content: \"\";position: absolute;border-left: 1px solid gray;box-sizing: border-box;top: 0;bottom: 0;left: 50%;z-index: -1;}#sk-container-id-10 div.sk-parallel-item {display: flex;flex-direction: column;z-index: 1;position: relative;background-color: white;}#sk-container-id-10 div.sk-parallel-item:first-child::after {align-self: flex-end;width: 50%;}#sk-container-id-10 div.sk-parallel-item:last-child::after {align-self: flex-start;width: 50%;}#sk-container-id-10 div.sk-parallel-item:only-child::after {width: 0;}#sk-container-id-10 div.sk-dashed-wrapped {border: 1px dashed gray;margin: 0 0.4em 0.5em 0.4em;box-sizing: border-box;padding-bottom: 0.4em;background-color: white;}#sk-container-id-10 div.sk-label label {font-family: monospace;font-weight: bold;display: inline-block;line-height: 1.2em;}#sk-container-id-10 div.sk-label-container {text-align: center;}#sk-container-id-10 div.sk-container {/* jupyter's `normalize.less` sets `[hidden] { display: none; }` but bootstrap.min.css set `[hidden] { display: none !important; }` so we also need the `!important` here to be able to override the default hidden behavior on the sphinx rendered scikit-learn.org. See: https://github.com/scikit-learn/scikit-learn/issues/21755 */display: inline-block !important;position: relative;}#sk-container-id-10 div.sk-text-repr-fallback {display: none;}
TPOTEstimator(classification=True, generations=5, max_eval_time_seconds=300,\n",
-       "              scorers=['roc_auc'], scorers_weights=[1],\n",
-       "              search_space=<tpot2.search_spaces.pipelines.graph.GraphPipeline object at 0x71f059a54400>,\n",
-       "              verbose=2)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + "
TPOTEstimator(classification=True, generations=5, max_eval_time_seconds=300,\n",
+       "              population_size=10, processes=False, scorers=['roc_auc'],\n",
+       "              scorers_weights=[1],\n",
+       "              search_space=<tpot2.search_spaces.pipelines.graph.GraphPipeline object at 0x7ebd8bf94bb0>,\n",
+       "              verbose=2)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ "TPOTEstimator(classification=True, generations=5, max_eval_time_seconds=300,\n", - " scorers=['roc_auc'], scorers_weights=[1],\n", - " search_space=,\n", + " population_size=10, processes=False, scorers=['roc_auc'],\n", + " scorers_weights=[1],\n", + " search_space=,\n", " verbose=2)" ] }, @@ -755,10 +4835,17 @@ "\n", "\n", "#define the search space\n", + "# graph_search_space = tpot2.search_spaces.pipelines.GraphPipeline(\n", + "# root_search_space= tpot2.config.get_search_space(\"classifiers\"),\n", + "# leaf_search_space = tpot2.config.get_search_space(\"selectors\"), \n", + "# inner_search_space = tpot2.config.get_search_space([\"transformers\",\"classifiers\"]),\n", + "# max_size = 10,\n", + "# )\n", + "\n", "graph_search_space = tpot2.search_spaces.pipelines.GraphPipeline(\n", - " root_search_space= tpot2.config.get_search_space(\"classifiers\"),\n", + " root_search_space= tpot2.config.get_search_space([\"KNeighborsClassifier\", \"LogisticRegression\", \"DecisionTreeClassifier\"]),\n", " leaf_search_space = tpot2.config.get_search_space(\"selectors\"), \n", - " inner_search_space = tpot2.config.get_search_space([\"transformers\",\"classifiers\"]),\n", + " inner_search_space = tpot2.config.get_search_space([\"transformers\"]),\n", " max_size = 10,\n", ")\n", "\n", @@ -768,6 +4855,7 @@ " classification = True,\n", " cv = 5,\n", " search_space = graph_search_space,\n", + " population_size= 10,\n", " generations = 5,\n", " max_eval_time_seconds = 60*5,\n", " verbose = 2,\n", @@ -778,14 +4866,14 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "auroc score 0.9569231877561475\n" + "auroc score 0.9890552995391705\n" ] } ], @@ -800,12 +4888,12 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "metadata": {}, "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAnYAAAHWCAYAAAD6oMSKAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAchElEQVR4nO3de5SV5WHv8d/s2dyG+zA43FEi3kEFL4loYpNjPStNbDRZptV0nTTG1mVsV9p4bdZJouc0tfHWE11H26IrNkcjaayW1OQkrhhzCpqoqJAoAiqCyCXiIMgAA3tmnz+QiUZQbkLm8fP5B9iz3/d99t7Der7rfff7vg31er0eAAB6vMr+HgAAAHuHsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADACiEsAMAKISwAwAohLADAChEdX8PYF/o7OxMW1tbVq1alVWrVuXllSvTsXFjujo7U2lsTJ9+/TJ8xIi0tramtbU1zc3NaWxs3N/DBgDehvn9rRrq9Xp9fw/i3bJmzZrMnTs3v3z88Wxqb0+9VsuAjRszuK0tvWq1VOr1dDU0ZEu1mrXNzVnfr18aqtX07d8/k6ZMydFHH52hQ4fu75cBALyB+X3Higy75cuX56FZs7J40aL02rAh45a+mJFtbRnc3p5enZ07XG5LY2PW9u+fFc3NWTpubLY0NeWgiRMz7ZRTMnLkyH34CgCA32Z+f2dFhV2tVsvs2bPz6OzZGbB6dQ5esjRjVq9OY1fXLq+rs1LJspaWPDt+XNa3tOT4adMybdq0VKvviaPXAPA7w/y+84oJu5UrV+a+mTOzZtlLOWzRokx86aVU9sJL62poyKLRo/PMxIlpHjM6Hz3jjIwYMWIvjBgAeCfm911TRNgtWbIk98yYkablKzJ1/vwM2rBhr29jXVNT5hx+eDaMGpUzP312xo8fv9e3AQD8hvl91/X4sFuyZEnu/s53MmzJ0pzw9NOp7sZu2Z1Vq1TyiyOPSNu4cfnkH/9xj//wAeB3lfl99/To69itXLky98yYkeYlS/P+p556Vz/0JKl2deUDv3oqzUuX5p4Z383KlSvf1e0BwHuR+X339diwq9VquW/mzDQtX5ETn356rxxv3xmVej0nPvV0+q1Ynh/MnJlarbZPtgsA7wXm9z3TY8Nu9uzZWbPspUydP/9dL/nfVu3qytSn56ftpZfy0EMP7dNtA0DJzO97pkeG3fLly/Po7Nk5bNGid+WLlDtj8IYNOXThojwya1ZWrFixX8YAACUxv++5Hhl2D82alQGrV2fiSy/t13Ec8tJLGbB6dWbPmrVfxwEAJTC/77keF3Zr1qzJ4kWLcvCSpfvsuPuOVOr1vG/J0ixeuDBr1qzZr2MBgJ7M/L539Liwmzt3bnpt2JAxq1fv76EkScauXp3qhg2ZN2/e/h4KAPRY5ve9o0eFXWdnZ375+OMZt/TF3bqNyLuhsasr4198MfPmzEnn29ynDgDYPvP73rNbYffCCy/kuOOOS5LccsstmTFjxm5t/MEHH8wjjzzS/e+vfe1rGTNmTI455pgcfvjhueSSS970/La2tmxqb8/Itrbd2t4/L3ux+++rOjrypQXP7NZ6kmRdrZZLFy7Ihx99NBffc09u/sd/zG233ZZp06Zl2LBhmTlz5m6ve5vHHnus+z349a9/neOPPz7HHntsfvazn+Xcc8/d4/U/8sgjOe6449KrV6/8x3/8xx6vD4ByXXXVVTnqqKMyadKkHHfccVm8ePEOn9vS0rJL6942v//k0Uff9Pjhs/4zZzzxeP7g8Tn5y/nzs3FfB9aLy/KLX/wiba93x8yZM3PDDTckST772c/u1tz5hS98IQcccEB3R+1te3zH2wsuuGC3l33wwQfT0tKSE044ofuxyy+/PBdddFFqtVqOP/74zJs3L5MnT06SrFq1KvVaLUPWr9+t7f3zsmU5f8zYJElrnz657tDDdnvsly9cmCMG9M83Djk07bVabp54cK6++uo8//zzSZJPfvKTWbFixS7/cr/Rcccd1/3BP/DAA5k6dWpuueWWJMmHPvShnV5PZ2dnGhsb3/L4qFGjMn369Fx//fW7PUYAyvfQQw/lwQcfzJNPPplqtZply5alf//+e2392+b3O597Nl8cPrz78YHVamYeOyVJ8qUFz+Q7K1fkc6PHvOP6Ouv1NDY07PG41q1py9xVK7Nq1aoMHz48Z5xxxh6v85xzzsnnPve5/Pmf//ker2t7djrsFi5cmHPOOSeNjY055ZRTuh//2te+lpaWllx00UVZtGhRLrzwwrS1tWXYsGG5/fbbM3LkyJx66qk58cQT88ADD2Tjxo2ZMWNGBg0alFtuuSXVajXTp0/Pt7/97Tdtr6OjI1u2bMmgQYOSJD/+8Y9z4YUXZsPatZnfu0+uOOigNDQ05N5fr8r0ZcuSJJ84oDWfHzMm7Z2d+cv587Nqc0eS5LKDJuTRtWvzWq2WM554PFMGDcrnR4/JXz4zP/92zLH5t1Wr8rM1bVlbq2XZpk354xEjc96Yrb84Nyx5IT9avTpj+vZNPclnRo7MQf2asmBDe66dcFBeXr06W7ZszoSjj86BBx7YHXa1Wi3Tp0/P3XffnU2bNmXIkCG5+eab09ramlmzZuXLX/5yGhoa0qtXr9x///15+umn8xd/8Rfpen0X9He/+90sXLgwt956ay677LJcfPHF6ejoyM9//vPceuutOe+883L//fenVqvlq1/9ah577LFs3rw5l1xyST760Y/mrrvuyk9/+tO89tpraWpqyvTp09/ymfbp0yejR4/O5s2bs3bt2rz88ss7++sAwHvIggUL0tTU1H0iQZ8+fVKr1fKd73wn1113XTZt2pQpU6bkmmuuSaVSSb1e755Tbrjhhvzwhz9MR0dH/vRP/zSf/exnkyTXXnttZs6cmUqlkpNPPjkvzJ27dZ5+/PFMGTQwX5nwviRJV1dXGiqVHDdocBa0t6e9szNfe/bZPLdx6+VQvjxhQqYOGpxvLlmS1Vs2Z8nGTTm4qSnnjByZrz77bNbWauldacjtR01KQ0PDDpddubkjL2zcmJUdm/PXB47Px4YfkBufX5znNnfkE5/4RPe8/atf/SrXXnvtm96fRx55JF/60pfS3t6egw46KLfffnsGDBiw3fdy2rRpeeGFF/b2R9Rtp8Pui1/8Yv7mb/4mZ511Vi699NLtPufCCy/M9OnTM378+Pzrv/5rrrzyyu49TH369Mmjjz6a2267Lddff31uvfXWXHDBBd1RmCR33313rr766kyfPj3PP/98zj333Bx44IHZuHFjzj///Fz8V3+VIb/8ZW675978+JVXcvTAgblx6dLcffQx6dfYmE/PfTLvHzI4L23qyJBe1dx61FGp1+tp7+zMKUOH5q6VK7rLf9mmTW8a+8L29tx9zLGp1es5fc5j+ZNRozK/vT2z1qzJzGOn5LVaLf/18TnJyJF5tr09BzY25tU3nCkzdN3atP7W3rkrrrjiTf+eNGnSdt+3Aw444C2PHXnkkd1/f+Ou3tWrV3fvxdvectv+w+zMNt5odw+nA/De8XZzyVNPPfWmnTTbe+6ll1663YY4bOLEfHLChPz80Udzy8gRSZKVq1amq6tr659pyE9Xr86pLcPyv19cmtOGDcs1LYdmZUdHzn/qqXx/yta5fWH7hvzLpEnpXankrCefyJfGH5hpQ4dmfa2Wvo2NuWHJCztcdtmmTfmXSZOzfNOmfO6pX+Vjww/IX40fn2+ufy1/feWV+aNzz823vvWtt4x98+bNufjiizNz5swMHTo011xzTW666aZcfvnlu/Te7i07HXZz5szJfffdlyT5zGc+kwceeOBNP3/ttdcye/bs/OEf/mGSrYf/DjzwwO6fb3t86tSpueOOO3a4nW2HYjds2JAPf/jDmTVrVgYMGJBDDz00gwYMSJ/Oznx8+PDMWbc2lYbkA4OHZEivXkmS01taMmftunyweWi+vnhdvrF4cU4bNizHvr7X7+18YMiQNL1+uPKA3r3zypYteWLdupw2rCW9K5UM6907Jw4enCTZsHHjW5avbt6cvv36veN2AIA3GzxoUKrbuSDx+q6unPfi1u/HT+7bL59sHZE/mjs3/6+tLTe9uDRJ8mptSza/frTrI8Oa07tSyfpaLetqtUwbOjRJMqC6NXceWvPqDpf90NDmVBsaMq5fv6x7w+3EKvV6Nv/WzqA3WrBgQebNm5ff+73fS7I19E499dQ9eTv2yE6HXcM7HKuu1+sZMWJEnnzyye3+vE+fPkmSxsbGnTq7pKmpKaeeemoeeuih/P7v/36SpKuzM5V6PVuvbrP98TQ0JAf1a8q9xxybB9va8j+ffy6fOKA1fzJq1Ntur3flN+eRNDY0pLN7O294ja//OaFv3zzX0ZF6vd79vjTU66lu53tsAMDbq1YqadjO2bADKpXcOnbrd+Mb0pDelUrqqecfjzgyo/r2fcvz+1Z+Mw9vrxLebtk3dsCbF6qn823uG1uv1zNlypS37PDaX3b6rNgpU6bk3//935Mkd95551t+PmjQoAwfPjw/+MEPkiRbtmzJ/Pnz33adAwcOzGuvvbbdn3V2duaRRx7JhAkTcuihh2bhwoVpe/XV1JLc9/LLOW7QoEweMDAPr301a18v7vtfeSVTBw3Oqo6ONDU25szW1vy3UaMzv33ryRbbgm2nX/OggflJ2yvZ0tWVti2b8+jatUmSI1taMqFPn/yfV1/tfu6itrasWLVqp9cNAGzVvnFj6pXKDufpSqWSwUOGpCHJSUOG5o433Opr/nZOqBxQrWZQtZrZr39lan2tllq9vlPLvlH/amM21mpprO54P9hhhx2WJUuWdO/Yam9vz7PPPvu263037fQeu3/4h3/IOeeck7/7u7/L6aefvt3n3HHHHbngggtyxRVXpFar5ZJLLsnhhx++w3V+/OMfz6c+9anMmDGj+7j8tu/YdXR05NRTT81ZZ52VSqWSf/qnf8qfnX9+Otety39p6p/Thg1LQ0NDLho7LufOm5d6kjMPaM2RAwbkP9esyd8vfj6Vhob0rVTy9YkTk9d//rHH5+TEIUPy+Z04q+bogYPy/sFD8vEnHs+B/fpl0oCBGdBYTUNDQ26YNClXLlqUc5YuTe8kgzdtyvhjjnnT8n/7t3+b6dOnZ/jw4Tn++OPzxBNP5N57783ll1+e2bNnp1Kp5Nhjj811112Xm266Kd/73vdSrVYzduzY3HzzzXnyySdz66235rbbbstdd92V+fPn58orr8zSpUu7T57o7OzMVVddlQcffDD1ej0TJkzIt771rTc9f0cWLFiQs88+O2vXrk3fvn0zceLEfP/733/H9wWA95a5c+fm8ssvz/rXQ2jy5Mm59tpr8/DDD+frX/96tmzZkl69euX666/P5MmTc9hhh+WZZ7ZeUuzGG2/M9773vdTr9bS0tOTb3/52+vfvn+uuuy733ntvqtVqKtVqeg8YmLNaR+TPVqzIiYMH5ysT3pfKkiUZ0ToiDZVK9x64L4wbl//x3HP52ONz0lmv5wNDhuQrAw5+y5ivOeTQ/PdnF+XqxYvTp1LJ7ZMm7fSy2xza1D9b6vV85aqr0lmpbPfoZe/evXPXXXflwgsv7H5/vvGNb+Tgg7e/3s9//vO577778sorr2TMmDG58cYbc+aZZ+7Kx/G2Gur1/Xzfjl3wk5/8JAt+9KOc9vDP99k2N3R2pqmxMWtrW/KpJ5/MjKOPSfPr3+nbpqOjI//3hOPzw/nzu3fFVqvVPb7cCQC8F+yP+X1n3f+B9+fQ00/PRz7ykf09lJ2yx9ex25daW1szp1+/bGlsTK99dJHCKxYtzOKNG7OlqysXjB37lqhLkkpTU7qGDct5552XgQMH5uWXX85ll10m6gBgJ+yP+X1nbGlszPp+/dLa2rq/h7LTelzYNVSrWdu/f1rWrdsn2/xfh+34UPI2a/v3T0O1mtNOOy3nnHPOPhjVrvnRj36Uyy677E2PffCDH8w3v/nN/TQiAPiN/TG/74xt8/vuhN2ZZ575lrtz3HnnnTniiCP21vC2q0eFXXNzc/r2758Vzc2/Ux/8imFbx9Xc3Ly/h7Jdp59++g6/FwkA+1uJ8/s999zzLozone3WvWL3l8bGxkyaMiVLx41N545OS97HOiuVLBk7NpOnTt3ubbsAgLdnft97fjfevV1w9NFHZ0tTU5b9jnx/7cWWltSamrrvZwsA7Drz+97R48Ju6NChOWjixDw7fly69sINfvdEV0NDnhs/LgcdckiGvn51awBg15nf944eF3ZJMu2UU7K+pSWLRo/er+NYOHp01re0ZNrJJ+/XcQBACczve65Hht3IkSNz/LRpeWbixKxratovY1jb1JQFh0zMCSefnJEjR+6XMQBASczve65Hhl2STJs2LUPHjM6cww9PbR9/0bJWqWTOEYenefTonHTSSft02wBQMvP7numxYVetVvMHZ5yRDaNG5RdHHrHPjsd3NTTkF0cekY0jR+WjZ5yR6tvcPw4A2DXm9z3TY8MuSUaMGJEzP3122saNy8NHHfmul32tUsnDRx2ZtnHjcuanz86IESPe1e0BwHuR+X339ah7xe7IkiVLcs+M76Zp+fJMnT8/gzZs2OvbWNvUlDlHHJ6NI0flzE+fnfHjx+/1bQAAv2F+33VFhF2SrFy5MvfNnJk1y17KYYsWZeJLL6WyF15aV0NDFo4enQWHTEzz6NH56Bln9OiSB4CexPy+a4oJuySp1WqZPXt2Hp09OwNWr877lizN2NWr09jVtcvr6qxU8mJLS54bPy7rW1pywskn56STTuqxx9wBoKcyv++8osJum+XLl+eh2bOzeOHCVDdsyPgXX8zIV9oyuL09vTo7d7jclsbGrO3fPyuGNWfJ2LGpNTXloEMOybQeesozAJTE/P7Oigy7bdasWZN58+Zl3pw52dTennqtlgEbN2ZQ25r0rtVSqXelq6GSzdVq1jUPzfp+/dJQraZv//6ZPHVqJk+e3OOuOA0ApTO/71jRYbdNZ2dn2trasmrVqqxatSovr1yZzZs2pbNWS2O1mt59+2b4iBFpbW1Na2trmpube9QNfwHgvcj8/lbvibADAHgv6NHXsQMA4DeEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAhhB0AQCGEHQBAIYQdAEAh/j/Hg9UMHDe6CgAAAABJRU5ErkJggg==", + "image/png": "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", "text/plain": [ "
" ] @@ -836,7 +4924,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.13" + "version": "3.10.14" } }, "nbformat": 4, diff --git a/Tutorial/3_Feature_Set_Selector.ipynb b/Tutorial/3_Feature_Set_Selector.ipynb new file mode 100644 index 00000000..19767db2 --- /dev/null +++ b/Tutorial/3_Feature_Set_Selector.ipynb @@ -0,0 +1,1244 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Special Feature Selection nodes in TPOT2\n", + "\n", + "TPOT2 can use evolutionary algorithms to optimize feature selection simultaneously with pipeline optimization. There are two node search spaces included.\n", + "\n", + "1. FSSNode - (Feature Set Selector) This node is useful if you have predefined groups of features that you want to select from. For example, one group could include the first x columns, the next group could include the next y columns, etc. Each FeatureSetSelector Node will select a single group to be passed to the next step in the pipeline. This node is also useful if you want to select individual columns at a time, this will be used in tutorial 4 to create a symbolic regression search space. \n", + "\n", + "2. GeneticFeatureSelectorNode - Whereas FSSNode selects from a predefine list of subsets of features, this node instead uses evolutionary algorithms to optimize a novel subset from scratch. This is useful where there is no predefined grouping of features.\n", + "\n", + "\n", + "It may also be beneficial to pair these search spaces with a secondary objective function to minimize complexity. That would encourage TPOT to try to produce the simplest pipeline with the fewest number of features.\n", + "\n", + "tpot2.objectives.number_of_nodes_objective - This can be used as an other_objective_function that counts the number of nodes.\n", + "\n", + "tpot2.objectives.complexity_scorer - This is a scorer that can be used in the scorers parameter that tries to count the total number of learned parameters (number of coefficients, number of nodes in decision trees, etc.).\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Feature Set Selector\n", + "\n", + "The FeatureSetSelector is a subclass of sklearn.feature_selection.SelectorMixin that simply returns the manually specified columns. The parameter sel_subset specifies the name or index of the column that it selects. The transform function then simply indexes and returns the selected columns. You can also optionally name the group with the name parameter, though this is only for note keeping and does is not used by the class.\n", + "\n", + "\n", + "sel_subset: list or int\n", + " If X is a dataframe, items in sel_subset list must correspond to column names\n", + " If X is a numpy array, items in sel_subset list must correspond to column indexes\n", + " int: index of a single column\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "original DataFrame\n", + " a b c d e f\n", + "0 0 1 2 3 4 5\n", + "1 0 1 2 3 4 5\n", + "2 0 1 2 3 4 5\n", + "3 0 1 2 3 4 5\n", + "4 0 1 2 3 4 5\n", + "5 0 1 2 3 4 5\n", + "6 0 1 2 3 4 5\n", + "7 0 1 2 3 4 5\n", + "8 0 1 2 3 4 5\n", + "9 0 1 2 3 4 5\n", + "Transformed Data\n", + "[[0 1 2]\n", + " [0 1 2]\n", + " [0 1 2]\n", + " [0 1 2]\n", + " [0 1 2]\n", + " [0 1 2]\n", + " [0 1 2]\n", + " [0 1 2]\n", + " [0 1 2]\n", + " [0 1 2]]\n" + ] + } + ], + "source": [ + "import tpot2\n", + "import pandas as pd\n", + "import numpy as np\n", + "#make a dataframe with columns a,b,c,d,e,f\n", + "\n", + "#numpy array where columns are 1,2,3,4,5,6\n", + "data = np.repeat([np.arange(6)],10,0)\n", + "\n", + "df = pd.DataFrame(data,columns=['a','b','c','d','e','f'])\n", + "fss = tpot2.builtin_modules.FeatureSetSelector(name='test',sel_subset=['a','b','c'])\n", + "\n", + "print(\"original DataFrame\")\n", + "print(df)\n", + "print(\"Transformed Data\")\n", + "print(fss.fit_transform(df))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To use the FSS with TPOT2, you can simply pass it in to the configuration dictionary. Note that the FSS is only well defined when used in the leaf nodes of the graph. This is because downstream nodes will receive different transformations of the data such that the original indexes no longer correspond to the same columns in the raw data.\n", + "\n", + "TPOT2 includsing the string \"feature_set_selector\" in the leaf_config_dict parameter will include the FSS in the search space of the pipeline. By default, each FSS node will select a single column. You can also group columns into sets so that each node selects a set of features rather than a single feature.\n", + "\n", + "\n", + "\n", + "subsets : str or list, default=None\n", + " Sets the subsets that the FeatureSetSeletor will select from if set as an option in one of the configuration dictionaries.\n", + " - str : If a string, it is assumed to be a path to a csv file with the subsets. \n", + " The first column is assumed to be the name of the subset and the remaining columns are the features in the subset.\n", + " - list or np.ndarray : If a list or np.ndarray, it is assumed to be a list of subsets.\n", + " - None : If None, each column will be treated as a subset. One column will be selected per subset.\n", + " If subsets is None, each column will be treated as a subset. One column will be selected per subset.\n", + "\n", + "\n", + "Lets say you want to have three groups of features, each with three columns each. The following examples are equivalent:\n", + "\n", + "### str\n", + "\n", + "sel_subsets=simple_fss.csv\n", + "\n", + "\n", + "\\# simple_fss.csv\n", + "\n", + "group_one, 1,2,3\n", + "\n", + "group_two, 4,5,6\n", + "\n", + "group_three, 7,8,9\n", + "\n", + "\n", + "### dict\n", + "\n", + "\n", + "sel_subsets = { \"group_one\" : [1,2,3],\n", + " \"group_two\" : [4,5,6],\n", + " \"group_three\" : [7,8,9],\n", + " }\n", + "\n", + "\n", + "### list\n", + "\n", + "\n", + "sel_subsets = [[1,2,3],[4,5,6],[7,8,9]]\n", + "\n", + "\n", + "\n", + "(As the FSS is just another transformer, you could also pass it in with the standard configuration dictionary format (described in tutorial 2), in which you would have to define your own function that returns a hyperparameter. Similar to the params_LogisticRegression function below. )\n", + "\n", + "\n", + "(In the future, FSS will be treated as a special case node with its own mutation/crossover functions to make it more efficient when there are large numbers of features.)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
abcdefghi
01.412867-1.1271430.7240590.0127190.1376170.4687240.2879770.3496210.671031
1-1.4594650.698617-0.8197860.4572250.5673080.2366090.5318910.6822630.391485
2-0.176559-0.4373060.2618830.8143600.7403320.2443210.2876910.5474820.936978
31.834518-1.486981-0.0595760.5338450.1247670.3672450.7554940.8385290.018747
41.316622-0.4299211.5977240.8048680.6724300.2659850.2620430.4121930.974847
\n", + "
" + ], + "text/plain": [ + " a b c d e f g \\\n", + "0 1.412867 -1.127143 0.724059 0.012719 0.137617 0.468724 0.287977 \n", + "1 -1.459465 0.698617 -0.819786 0.457225 0.567308 0.236609 0.531891 \n", + "2 -0.176559 -0.437306 0.261883 0.814360 0.740332 0.244321 0.287691 \n", + "3 1.834518 -1.486981 -0.059576 0.533845 0.124767 0.367245 0.755494 \n", + "4 1.316622 -0.429921 1.597724 0.804868 0.672430 0.265985 0.262043 \n", + "\n", + " h i \n", + "0 0.349621 0.671031 \n", + "1 0.682263 0.391485 \n", + "2 0.547482 0.936978 \n", + "3 0.838529 0.018747 \n", + "4 0.412193 0.974847 " + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import tpot2\n", + "import sklearn.datasets\n", + "from sklearn.linear_model import LogisticRegression\n", + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "\n", + "X, y = sklearn.datasets.make_classification(n_samples=1000, n_features=3, n_informative=3, n_redundant=0, n_repeated=0, n_classes=2, n_clusters_per_class=2, weights=None, flip_y=0.01, class_sep=1.0, hypercube=True, shift=0.0, scale=1.0, shuffle=True, random_state=None)\n", + "X = np.hstack([X, np.random.rand(X.shape[0],6)]) #add six uninformative features\n", + "X = pd.DataFrame(X, columns=['a','b','c','d','e','f','g','h','i']) # a, b ,c the rest are uninformative\n", + "X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n", + "\n", + "X.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Feature Set Selector\n", + "\n", + "In this configuration, each FSS node considers a single column.\n", + "\n", + "The root node is a logistic regression and there are no other intermediate transformers. An additional objective function is included that seeks to minimize the number of leave nodes (i.e the number of selected features)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Generation: 0%| | 0/5 [00:00#sk-container-id-1 {\n", + " /* Definition of color scheme common for light and dark mode */\n", + " --sklearn-color-text: black;\n", + " --sklearn-color-line: gray;\n", + " /* Definition of color scheme for unfitted estimators */\n", + " --sklearn-color-unfitted-level-0: #fff5e6;\n", + " --sklearn-color-unfitted-level-1: #f6e4d2;\n", + " --sklearn-color-unfitted-level-2: #ffe0b3;\n", + " --sklearn-color-unfitted-level-3: chocolate;\n", + " /* Definition of color scheme for fitted estimators */\n", + " --sklearn-color-fitted-level-0: #f0f8ff;\n", + " --sklearn-color-fitted-level-1: #d4ebff;\n", + " --sklearn-color-fitted-level-2: #b3dbfd;\n", + " --sklearn-color-fitted-level-3: cornflowerblue;\n", + "\n", + " /* Specific color for light theme */\n", + " --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n", + " --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n", + " --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n", + " --sklearn-color-icon: #696969;\n", + "\n", + " @media (prefers-color-scheme: dark) {\n", + " /* Redefinition of color scheme for dark theme */\n", + " --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n", + " --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n", + " --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n", + " --sklearn-color-icon: #878787;\n", + " }\n", + "}\n", + "\n", + "#sk-container-id-1 {\n", + " color: var(--sklearn-color-text);\n", + "}\n", + "\n", + "#sk-container-id-1 pre {\n", + " padding: 0;\n", + "}\n", + "\n", + "#sk-container-id-1 input.sk-hidden--visually {\n", + " border: 0;\n", + " clip: rect(1px 1px 1px 1px);\n", + " clip: rect(1px, 1px, 1px, 1px);\n", + " height: 1px;\n", + " margin: -1px;\n", + " overflow: hidden;\n", + " padding: 0;\n", + " position: absolute;\n", + " width: 1px;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-dashed-wrapped {\n", + " border: 1px dashed var(--sklearn-color-line);\n", + " margin: 0 0.4em 0.5em 0.4em;\n", + " box-sizing: border-box;\n", + " padding-bottom: 0.4em;\n", + " background-color: var(--sklearn-color-background);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-container {\n", + " /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n", + " but bootstrap.min.css set `[hidden] { display: none !important; }`\n", + " so we also need the `!important` here to be able to override the\n", + " default hidden behavior on the sphinx rendered scikit-learn.org.\n", + " See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n", + " display: inline-block !important;\n", + " position: relative;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-text-repr-fallback {\n", + " display: none;\n", + "}\n", + "\n", + "div.sk-parallel-item,\n", + "div.sk-serial,\n", + "div.sk-item {\n", + " /* draw centered vertical line to link estimators */\n", + " background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n", + " background-size: 2px 100%;\n", + " background-repeat: no-repeat;\n", + " background-position: center center;\n", + "}\n", + "\n", + "/* Parallel-specific style estimator block */\n", + "\n", + "#sk-container-id-1 div.sk-parallel-item::after {\n", + " content: \"\";\n", + " width: 100%;\n", + " border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n", + " flex-grow: 1;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-parallel {\n", + " display: flex;\n", + " align-items: stretch;\n", + " justify-content: center;\n", + " background-color: var(--sklearn-color-background);\n", + " position: relative;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-parallel-item {\n", + " display: flex;\n", + " flex-direction: column;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-parallel-item:first-child::after {\n", + " align-self: flex-end;\n", + " width: 50%;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-parallel-item:last-child::after {\n", + " align-self: flex-start;\n", + " width: 50%;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-parallel-item:only-child::after {\n", + " width: 0;\n", + "}\n", + "\n", + "/* Serial-specific style estimator block */\n", + "\n", + "#sk-container-id-1 div.sk-serial {\n", + " display: flex;\n", + " flex-direction: column;\n", + " align-items: center;\n", + " background-color: var(--sklearn-color-background);\n", + " padding-right: 1em;\n", + " padding-left: 1em;\n", + "}\n", + "\n", + "\n", + "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n", + "clickable and can be expanded/collapsed.\n", + "- Pipeline and ColumnTransformer use this feature and define the default style\n", + "- Estimators will overwrite some part of the style using the `sk-estimator` class\n", + "*/\n", + "\n", + "/* Pipeline and ColumnTransformer style (default) */\n", + "\n", + "#sk-container-id-1 div.sk-toggleable {\n", + " /* Default theme specific background. It is overwritten whether we have a\n", + " specific estimator or a Pipeline/ColumnTransformer */\n", + " background-color: var(--sklearn-color-background);\n", + "}\n", + "\n", + "/* Toggleable label */\n", + "#sk-container-id-1 label.sk-toggleable__label {\n", + " cursor: pointer;\n", + " display: block;\n", + " width: 100%;\n", + " margin-bottom: 0;\n", + " padding: 0.5em;\n", + " box-sizing: border-box;\n", + " text-align: center;\n", + "}\n", + "\n", + "#sk-container-id-1 label.sk-toggleable__label-arrow:before {\n", + " /* Arrow on the left of the label */\n", + " content: \"▸\";\n", + " float: left;\n", + " margin-right: 0.25em;\n", + " color: var(--sklearn-color-icon);\n", + "}\n", + "\n", + "#sk-container-id-1 label.sk-toggleable__label-arrow:hover:before {\n", + " color: var(--sklearn-color-text);\n", + "}\n", + "\n", + "/* Toggleable content - dropdown */\n", + "\n", + "#sk-container-id-1 div.sk-toggleable__content {\n", + " max-height: 0;\n", + " max-width: 0;\n", + " overflow: hidden;\n", + " text-align: left;\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-unfitted-level-0);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-toggleable__content.fitted {\n", + " /* fitted */\n", + " background-color: var(--sklearn-color-fitted-level-0);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-toggleable__content pre {\n", + " margin: 0.2em;\n", + " border-radius: 0.25em;\n", + " color: var(--sklearn-color-text);\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-unfitted-level-0);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-toggleable__content.fitted pre {\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-fitted-level-0);\n", + "}\n", + "\n", + "#sk-container-id-1 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n", + " /* Expand drop-down */\n", + " max-height: 200px;\n", + " max-width: 100%;\n", + " overflow: auto;\n", + "}\n", + "\n", + "#sk-container-id-1 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n", + " content: \"▾\";\n", + "}\n", + "\n", + "/* Pipeline/ColumnTransformer-specific style */\n", + "\n", + "#sk-container-id-1 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n", + " color: var(--sklearn-color-text);\n", + " background-color: var(--sklearn-color-unfitted-level-2);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n", + " background-color: var(--sklearn-color-fitted-level-2);\n", + "}\n", + "\n", + "/* Estimator-specific style */\n", + "\n", + "/* Colorize estimator box */\n", + "#sk-container-id-1 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-unfitted-level-2);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n", + " /* fitted */\n", + " background-color: var(--sklearn-color-fitted-level-2);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-label label.sk-toggleable__label,\n", + "#sk-container-id-1 div.sk-label label {\n", + " /* The background is the default theme color */\n", + " color: var(--sklearn-color-text-on-default-background);\n", + "}\n", + "\n", + "/* On hover, darken the color of the background */\n", + "#sk-container-id-1 div.sk-label:hover label.sk-toggleable__label {\n", + " color: var(--sklearn-color-text);\n", + " background-color: var(--sklearn-color-unfitted-level-2);\n", + "}\n", + "\n", + "/* Label box, darken color on hover, fitted */\n", + "#sk-container-id-1 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n", + " color: var(--sklearn-color-text);\n", + " background-color: var(--sklearn-color-fitted-level-2);\n", + "}\n", + "\n", + "/* Estimator label */\n", + "\n", + "#sk-container-id-1 div.sk-label label {\n", + " font-family: monospace;\n", + " font-weight: bold;\n", + " display: inline-block;\n", + " line-height: 1.2em;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-label-container {\n", + " text-align: center;\n", + "}\n", + "\n", + "/* Estimator-specific */\n", + "#sk-container-id-1 div.sk-estimator {\n", + " font-family: monospace;\n", + " border: 1px dotted var(--sklearn-color-border-box);\n", + " border-radius: 0.25em;\n", + " box-sizing: border-box;\n", + " margin-bottom: 0.5em;\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-unfitted-level-0);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-estimator.fitted {\n", + " /* fitted */\n", + " background-color: var(--sklearn-color-fitted-level-0);\n", + "}\n", + "\n", + "/* on hover */\n", + "#sk-container-id-1 div.sk-estimator:hover {\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-unfitted-level-2);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-estimator.fitted:hover {\n", + " /* fitted */\n", + " background-color: var(--sklearn-color-fitted-level-2);\n", + "}\n", + "\n", + "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n", + "\n", + "/* Common style for \"i\" and \"?\" */\n", + "\n", + ".sk-estimator-doc-link,\n", + "a:link.sk-estimator-doc-link,\n", + "a:visited.sk-estimator-doc-link {\n", + " float: right;\n", + " font-size: smaller;\n", + " line-height: 1em;\n", + " font-family: monospace;\n", + " background-color: var(--sklearn-color-background);\n", + " border-radius: 1em;\n", + " height: 1em;\n", + " width: 1em;\n", + " text-decoration: none !important;\n", + " margin-left: 1ex;\n", + " /* unfitted */\n", + " border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n", + " color: var(--sklearn-color-unfitted-level-1);\n", + "}\n", + "\n", + ".sk-estimator-doc-link.fitted,\n", + "a:link.sk-estimator-doc-link.fitted,\n", + "a:visited.sk-estimator-doc-link.fitted {\n", + " /* fitted */\n", + " border: var(--sklearn-color-fitted-level-1) 1pt solid;\n", + " color: var(--sklearn-color-fitted-level-1);\n", + "}\n", + "\n", + "/* On hover */\n", + "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n", + ".sk-estimator-doc-link:hover,\n", + "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n", + ".sk-estimator-doc-link:hover {\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-unfitted-level-3);\n", + " color: var(--sklearn-color-background);\n", + " text-decoration: none;\n", + "}\n", + "\n", + "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n", + ".sk-estimator-doc-link.fitted:hover,\n", + "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n", + ".sk-estimator-doc-link.fitted:hover {\n", + " /* fitted */\n", + " background-color: var(--sklearn-color-fitted-level-3);\n", + " color: var(--sklearn-color-background);\n", + " text-decoration: none;\n", + "}\n", + "\n", + "/* Span, style for the box shown on hovering the info icon */\n", + ".sk-estimator-doc-link span {\n", + " display: none;\n", + " z-index: 9999;\n", + " position: relative;\n", + " font-weight: normal;\n", + " right: .2ex;\n", + " padding: .5ex;\n", + " margin: .5ex;\n", + " width: min-content;\n", + " min-width: 20ex;\n", + " max-width: 50ex;\n", + " color: var(--sklearn-color-text);\n", + " box-shadow: 2pt 2pt 4pt #999;\n", + " /* unfitted */\n", + " background: var(--sklearn-color-unfitted-level-0);\n", + " border: .5pt solid var(--sklearn-color-unfitted-level-3);\n", + "}\n", + "\n", + ".sk-estimator-doc-link.fitted span {\n", + " /* fitted */\n", + " background: var(--sklearn-color-fitted-level-0);\n", + " border: var(--sklearn-color-fitted-level-3);\n", + "}\n", + "\n", + ".sk-estimator-doc-link:hover span {\n", + " display: block;\n", + "}\n", + "\n", + "/* \"?\"-specific style due to the `` HTML tag */\n", + "\n", + "#sk-container-id-1 a.estimator_doc_link {\n", + " float: right;\n", + " font-size: 1rem;\n", + " line-height: 1em;\n", + " font-family: monospace;\n", + " background-color: var(--sklearn-color-background);\n", + " border-radius: 1rem;\n", + " height: 1rem;\n", + " width: 1rem;\n", + " text-decoration: none;\n", + " /* unfitted */\n", + " color: var(--sklearn-color-unfitted-level-1);\n", + " border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n", + "}\n", + "\n", + "#sk-container-id-1 a.estimator_doc_link.fitted {\n", + " /* fitted */\n", + " border: var(--sklearn-color-fitted-level-1) 1pt solid;\n", + " color: var(--sklearn-color-fitted-level-1);\n", + "}\n", + "\n", + "/* On hover */\n", + "#sk-container-id-1 a.estimator_doc_link:hover {\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-unfitted-level-3);\n", + " color: var(--sklearn-color-background);\n", + " text-decoration: none;\n", + "}\n", + "\n", + "#sk-container-id-1 a.estimator_doc_link.fitted:hover {\n", + " /* fitted */\n", + " background-color: var(--sklearn-color-fitted-level-3);\n", + "}\n", + "
Pipeline(steps=[('featuresetselector',\n",
+       "                 FeatureSetSelector(name='group_one',\n",
+       "                                    sel_subset=['a', 'b', 'c'])),\n",
+       "                ('graphpipeline',\n",
+       "                 GraphPipeline(graph=<networkx.classes.digraph.DiGraph object at 0x7ff98829b100>))])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + ], + "text/plain": [ + "Pipeline(steps=[('featuresetselector',\n", + " FeatureSetSelector(name='group_one',\n", + " sel_subset=['a', 'b', 'c'])),\n", + " ('graphpipeline',\n", + " GraphPipeline(graph=))])" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "est.fitted_pipeline_" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that if you want to include multiple subsets, you can instead include the node as a leaf in the graph search space. This will produce a pipeline where all leaves as FSSNodes and all FSSNodes appear in the leaves (to prevent inner nodes from also being FSSNodes). Since the graph search space allows for multiple leaves, this pipeline can select multiple feature sets. " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Generation: 0%| | 0/5 [00:00" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "est.fitted_pipeline_.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Other examples" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## dictionary" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Generation: 0%| | 0/5 [00:00\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
abcdefghi
0-1.290879-2.012016-1.0094340.0832512.350751-0.1922950.2665300.9893230.207050
1-2.329471-1.033893-2.656589-1.0254893.015554-1.1069470.5000590.8534730.596733
20.948998-0.1237830.530650-3.0253071.3910291.1761660.6624100.9452520.861687
3-3.2658662.1012295.1416770.5008880.613011-1.4708350.7347250.7188540.751557
4-2.232187-0.825902-1.4303462.3419290.8458660.3424700.2612210.9774950.732266
\n", - "" - ], - "text/plain": [ - " a b c d e f g \\\n", - "0 -1.290879 -2.012016 -1.009434 0.083251 2.350751 -0.192295 0.266530 \n", - "1 -2.329471 -1.033893 -2.656589 -1.025489 3.015554 -1.106947 0.500059 \n", - "2 0.948998 -0.123783 0.530650 -3.025307 1.391029 1.176166 0.662410 \n", - "3 -3.265866 2.101229 5.141677 0.500888 0.613011 -1.470835 0.734725 \n", - "4 -2.232187 -0.825902 -1.430346 2.341929 0.845866 0.342470 0.261221 \n", - "\n", - " h i \n", - "0 0.989323 0.207050 \n", - "1 0.853473 0.596733 \n", - "2 0.945252 0.861687 \n", - "3 0.718854 0.751557 \n", - "4 0.977495 0.732266 " - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import tpot2\n", - "import sklearn.datasets\n", - "from sklearn.linear_model import LogisticRegression\n", - "import numpy as np\n", - "import pandas as pd\n", - "\n", - "n_features = 6\n", - "X, y = sklearn.datasets.make_classification(n_samples=1000, n_features=n_features, n_informative=6, n_redundant=0, n_repeated=0, n_classes=2, n_clusters_per_class=2, weights=None, flip_y=0.01, class_sep=1.0, hypercube=True, shift=0.0, scale=1.0, shuffle=True, random_state=None)\n", - "X = np.hstack([X, np.random.rand(X.shape[0],3)]) #add three uninformative features\n", - "X = pd.DataFrame(X, columns=['a','b','c','d','e','f','g','h','i'])\n", - "X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n", - "\n", - "X.head()" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "def params_LogisticRegression(trial, name=None):\n", - " params = {}\n", - " params['solver'] = trial.suggest_categorical(name=f'solver_{name}',\n", - " choices=[f'newton-cg', 'lbfgs', 'liblinear', 'sag', 'saga'])\n", - " params['dual'] = False\n", - " params['penalty'] = 'l2'\n", - " params['C'] = trial.suggest_float(f'C_{name}', 1e-4, 1e4, log=True)\n", - " params['l1_ratio'] = None\n", - " if params['solver'] == 'liblinear':\n", - " params['penalty'] = trial.suggest_categorical(name=f'penalty_{name}', choices=['l1', 'l2'])\n", - " if params['penalty'] == 'l2':\n", - " params['dual'] = trial.suggest_categorical(name=f'dual_{name}', choices=[True, False])\n", - " else:\n", - " params['penalty'] = 'l1'\n", - "\n", - " params['class_weight'] = trial.suggest_categorical(name=f'class_weight_{name}', choices=['balanced'])\n", - " param_grid = {'solver': params['solver'],\n", - " 'penalty': params['penalty'],\n", - " 'dual': params['dual'],\n", - " 'multi_class': 'auto',\n", - " 'l1_ratio': params['l1_ratio'],\n", - " 'C': params['C'],\n", - " }\n", - " return param_grid\n", - "\n", - "\n", - "\n", - "root_config_dict = {LogisticRegression: params_LogisticRegression}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Feature selection for single classifier\n", - "\n", - "In this configuration, each FSS node considers a single column.\n", - "\n", - "The root node is a logistic regression and there are no other intermediate transformers. An additional objective function is included that seeks to minimize the number of leave nodes (i.e the number of selected features)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Generation: 100%|██████████| 20/20 [00:13<00:00, 1.52it/s]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.9074667008196723\n" - ] - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import tpot2\n", - "import sklearn.datasets\n", - "from sklearn.linear_model import LogisticRegression\n", - "import numpy as np\n", - "\n", - "\n", - "est = tpot2.TPOTEstimator(population_size=40,generations=20, \n", - " scorers=['roc_auc_ovr'],\n", - " scorers_weights=[1],\n", - " other_objective_functions=[tpot2.objectives.number_of_leaves_objective],\n", - " other_objective_functions_weights=[-1],\n", - " n_jobs=32,\n", - " classification=True,\n", - " leaf_config_dict=\"feature_set_selector\",\n", - " root_config_dict=root_config_dict,\n", - " inner_config_dict=None,\n", - " subsets=None,\n", - " verbose=1,\n", - " )\n", - "\n", - "\n", - "scorer = sklearn.metrics.get_scorer('roc_auc_ovo')\n", - "\n", - "est.fit(X_train, y_train)\n", - "print(scorer(est, X_test, y_test))\n", - "est.fitted_pipeline_.plot()" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "FeatureSetSelector_1 : FeatureSetSelector(name='3', sel_subset=['d'])\n", - "FeatureSetSelector_2 : FeatureSetSelector(name='4', sel_subset=['e'])\n", - "FeatureSetSelector_3 : FeatureSetSelector(name='5', sel_subset=['f'])\n" - ] - } - ], - "source": [ - "# print the selected features for each FSS\n", - "\n", - "#get leaves\n", - "leaves = [v for v, d in est.fitted_pipeline_.graph.out_degree() if d == 0]\n", - "for l in leaves:\n", - " print(l, \" : \", est.fitted_pipeline_.graph.nodes[l]['instance'])" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "LogisticRegression_1 : LogisticRegression(C=3371.8568398103916, solver='saga')\n", - "FeatureSetSelector_1 : FeatureSetSelector(name='3', sel_subset=['d'])\n", - "FeatureSetSelector_2 : FeatureSetSelector(name='4', sel_subset=['e'])\n", - "FeatureSetSelector_3 : FeatureSetSelector(name='5', sel_subset=['f'])\n" - ] - } - ], - "source": [ - "# print all hyperparameters\n", - "for n in est.fitted_pipeline_.graph.nodes:\n", - " print(n, \" : \", est.fitted_pipeline_.graph.nodes[n]['instance'])" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkAAAAGwCAYAAABB4NqyAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABBm0lEQVR4nO3de1yUdf7//+eAckgFNJGDsmDmapJnhUDbrEg8LKttn3LVPJW5HWzzkC2oiIdVdLcvUatlW2l+Kss+m9nuaphSVBpp4aHczCOlKeBpBcVAhffvj37ONgEG48CA1+N+u123nPe8r2teb6+Z5ul1va9rbMYYIwAAAAvxcHcBAAAAdY0ABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALKeRuwuoj8rLy3X06FE1a9ZMNpvN3eUAAIBqMMbozJkzCg0NlYfH5Y/xEIAqcfToUYWFhbm7DAAA4ITDhw+rTZs2l+1DAKpEs2bNJP3wF+jn5+fmagAAQHUUFRUpLCzM/j1+OQSgSlw67eXn50cAAgCgganO9BUmQQMAAMshAAEAAMshAAEAAMshAAEAAMshAAEAAMshAAEAAMshAAEAAMshAAEAAMshAAEAAMvhTtAAAKDOlJUbbc09pWNnStSqmY+i2raQp0fd//A4AQgAANSJjF15mvPPr5RXWGJvC/H3UUpCJw24MaROa+EUGAAAqHUZu/L00KvbHMKPJOUXluihV7cpY1dendZDAAIAALWqrNxozj+/kqnkuUttc/75lcrKK+tROwhAAACgVm3NPVXhyM+PGUl5hSXamnuqzmoiAAEAgFp17EzV4ceZfq5AAAIAALWqVTMfl/ZzBQIQAACoVVFtWyjE30dVXexu0w9Xg0W1bVFnNRGAAABArfL0sCkloZMkVQhBlx6nJHSq0/sBEYAAAECtG3BjiJ67t4eC/R1PcwX7++i5e3vU+X2AuBEiAACoEwNuDNEdnYK5EzQAALAWTw+bYtpd6+4yOAUGAACshwAEAAAshwAEAAAshwAEAAAshwAEAAAshwAEAAAshwAEAAAshwAEAAAshwAEAAAshwAEAAAshwAEAAAshwAEAAAshwAEAAAshwAEAAAshwAEAAAshwAEAAAsp14EoCVLligiIkI+Pj6Kjo7W1q1bq+x74cIFzZ07V+3atZOPj4+6du2qjIyMK9omAACwFrcHoFWrVmnKlClKSUnRtm3b1LVrV8XHx+vYsWOV9p85c6aef/55/fWvf9VXX32lBx98UHfeeae2b9/u9DYBAIC12Iwxxp0FREdHq3fv3lq8eLEkqby8XGFhYXr00UeVmJhYoX9oaKhmzJihRx55xN521113ydfXV6+++qpT2/ypoqIi+fv7q7CwUH5+fq4YJgAAqGU1+f526xGg8+fPKycnR3FxcfY2Dw8PxcXFKTs7u9J1SktL5ePj49Dm6+urTZs2XdE2i4qKHBYAAHD1cmsAOnHihMrKyhQUFOTQHhQUpPz8/ErXiY+PV1pamvbt26fy8nJt2LBBq1evVl5entPbTE1Nlb+/v30JCwtzwegAAEB95fY5QDX19NNPq3379urYsaO8vLw0ceJEjRs3Th4ezg8lKSlJhYWF9uXw4cMurBgAANQ3bg1ALVu2lKenpwoKChzaCwoKFBwcXOk6gYGBWrNmjYqLi/Xtt9/q66+/VtOmTXXdddc5vU1vb2/5+fk5LAAA4Orl1gDk5eWlnj17KjMz095WXl6uzMxMxcTEXHZdHx8ftW7dWhcvXtRbb72lIUOGXPE2AQCANTRydwFTpkzRmDFj1KtXL0VFRSk9PV3FxcUaN26cJGn06NFq3bq1UlNTJUlbtmzRkSNH1K1bNx05ckSzZ89WeXm5nnjiiWpvEwAAWJvbA9CwYcN0/PhxzZo1S/n5+erWrZsyMjLsk5gPHTrkML+npKREM2fO1MGDB9W0aVMNGjRIr7zyigICAqq9TQAAYG1uvw9QfcR9gAAAaHgazH2AAAAA3IEABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALMftAWjJkiWKiIiQj4+PoqOjtXXr1sv2T09PV4cOHeTr66uwsDBNnjxZJSUl9ufPnDmjSZMm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KbWC8TV8V/sYr/bxSVf/GBlfw1cfxsgkaAAAYDkcAQIAAJZDAAIAAJZDAAIAAJZDAAIAAJZDALoCH330kRISEhQaGiqbzaY1a9b87DpZWVnq0aOHvL29df311+vll1+u0GfJkiWKiIiQj4+PoqOjtXXrVtcXXw01Hd/q1at1xx13KDAwUH5+foqJidH69esd+syePVs2m81h6dixYy2O4vJqOsasrKwK9dtsNuXn5zv0a6j7cOzYsZWOLzIy0t6nPu3D1NRU9e7dW82aNVOrVq00dOhQ7dmz52fX+7//+z917NhRPj4+6ty5s9atW+fwvDFGs2bNUkhIiHx9fRUXF6d9+/bV1jCq5Mz4XnjhBd18881q3ry5mjdvrri4uArvv8r284ABA2pzKFVyZowvv/xyhfp9fHwc+jTkfdivX79KP4eDBw+296kv+/C5555Tly5d7Dc0jImJ0bvvvnvZderL548AdAWKi4vVtWtXLVmypFr9c3NzNXjwYN16663asWOHJk2apPHjxzuEhFWrVmnKlClKSUnRtm3b1LVrV8XHx+vYsWO1NYwq1XR8H330ke644w6tW7dOOTk5uvXWW5WQkKDt27c79IuMjFReXp592bRpU22UXy01HeMle/bscRhDq1at7M815H349NNPO4zr8OHDatGihe6++26HfvVlH3744Yd65JFH9Omnn2rDhg26cOGC+vfvr+Li4irX+eSTTzR8+HDdf//92r59u4YOHaqhQ4dq165d9j5//vOf9cwzz2jp0qXasmWLmjRpovj4eJWUlNTFsOycGV9WVpaGDx+uDz74QNnZ2QoLC1P//v115MgRh34DBgxw2Ievv/56bQ+nUs6MUfrhDsI/rv/bb791eL4h78PVq1c7jG3Xrl3y9PSs8DmsD/uwTZs2WrhwoXJycvT555/rtttu05AhQ/Tvf/+70v716vNn4BKSzNtvv33ZPk888YSJjIx0aBs2bJiJj4+3P46KijKPPPKI/XFZWZkJDQ01qampLq23pqozvsp06tTJzJkzx/44JSXFdO3a1XWFuVB1xvjBBx8YSeY///lPlX2upn349ttvG5vNZr755ht7W33eh8eOHTOSzIcfflhln3vuuccMHjzYoS06Otr8/ve/N8YYU15eboKDg81f/vIX+/OnT5823t7e5vXXX6+dwqupOuP7qYsXL5pm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/va3v1W6zfq0DysbmySHz9Utt9zi8B40xpg333zT/PKXvzReXl4mMjLSrF271uH58vJyk5ycbIKCgoy3t7e5/fbbzZ49e+pgRI6cGV94eHil66SkpBhjjDl37pzp37+/CQwMNI0bNzbh4eHmgQcecEtAN8a5MU6aNMn++QoKCjKDBg0y27Ztc9huQ96Hxhjz9ddfG0n2IPFj9Wkf3nfffSY8PNx4eXmZwMBAc/vttzvUXJ8/fzZjjHHRwSQAAIAGgTlAAADAcghAAADAcghAAADAcghAAADAcghAAADAcghAAADAcghAAADAcghAAADAcghAgAV88803stls2rFjh7tLsfv666910003ycfHR926dau115k9e3atbv9K1OfagKsdAQioA2PHjpXNZtPChQsd2tesWSObzeamqtwrJSVFTZo00Z49eyr8OOIlx48f10MPPaRf/OIX8vb2VnBwsOLj47V58+Y6rva/6jK0XAquP12q8+OY1RUREaH09HSXbQ9oKBq5uwDAKnx8fLRo0SL9/ve/V/Pmzd1djkucP39eXl5eTq174MABDR48WOHh4VX2ueuuu3T+/HmtWLFC1113nQoKCpSZmamTJ086W3KDtHHjRkVGRtof+/r6urGayl3JewFwB44AAXUkLi5OwcHBSk1NrbJPZUcX0tPTFRERYX88duxYDR06VAsWLFBQUJACAgI0d+5cXbx4UdOmTVOLFi3Upk0bLV++vML2v/76a8XGxsrHx0c33nijPvzwQ4fnd+3apYEDB6pp06YKCgrSqFGjdOLECfvz/fr108SJEzVp0iS1bNlS8fHxlY6jvLxcc+fOVZs2beTt7a1u3bopIyPD/rzNZlNOTo7mzp0rm82m2bNnV9jG6dOn9fHHH2vRokW69dZbFR4erqioKCUlJek3v/mNQ7/x48crMDBQfn5+uu2227Rz584q/44l6cUXX9QNN9wgHx8fdezYUc8++6zD8999952GDx+uFi1aqEmTJurVq5e2bNmil19+WXPmzNHOnTvtR2NefvnlatexcOFCBQUFqVmzZrr//vtVUlJy2TovufbaaxUcHGxf/P39q/WaBw4c0JAhQxQUFKSmTZuqd+/e2rhxo/35fv366dtvv9XkyZPt45Fq9j6cP3++QkND1aFDB0nS4cOHdc899yggIEAtWrTQkCFD9M0339jXy8rKUlRUlJo0aaKAgAD16dNH3377bbX+HgBXIgABdcTT01MLFizQX//6V3333XdXtK33339fR48e1UcffaS0tDSlpKTo17/+tZo3b64tW7bowQcf1O9///sKrzNt2jRNnTpV27dvV0xMjBISEuxHU06fPq3bbrtN3bt31+eff66MjAwVFBTonnvucdjGihUr5OXlpc2bN2vp0qWV1vf000/r//2//6cnn3xSX3zxheLj4/Wb3/xG+/btkyTl5eUpMjJSU6dOVV5enh5//PEK22jatKmaNm2qNWvWqLS0tMq/i7vvvlvHjh3Tu+++q5ycHPXo0UO33367Tp06VWn/1157TbNmzdL8+fO1e/duLViwQMnJyVqxYoUk6ezZs7rlllt05MgR/eMf/9DOnTv1xBNPqLy8XMOGDdPUqVMVGRmpvLw85eXladiwYdWq480339Ts2bO1YMECff755woJCakQvGrq517z7NmzGjRokDIzM7V9+3YNGDBACQkJOnTokCRp9erVatOmjebOnWsfT01kZmZqz5492rBhg/71r3/pwoULio+PV7NmzfTxxx9r8+bNatq0qQYMGKDz58/r4sWLGjp0qG655RZ98cUXys7O1oQJEyx7Ghhu5vLflwdQwZgxY8yQIUOMMcbcdNNN5r777jPGGPP222+bH38MU1JSTNeuXR3Wfeqpp0x4eLjDtsLDw01ZWZm9rUOHDubmm2+2P7548aJp0qSJef31140xxuTm5hpJZuHChfY+Fy5cMG3atDGLFi0yxhgzb948079/f4fXPnz4sJFk9uzZY4wx5pZbbjHdu3f/2fGGhoaa+fPnO7T17t3bPPzww/bHXbt2NSkpKZfdzt///nfTvHlz4+PjY2JjY01SUpLZuXOn/fmPP/7Y+Pn5mZKSEof12rVrZ55//nljTMW/03bt2pmVK1c69J83b56JiYkxxhjz/PPPm2bNmpmTJ09WWlNl+6g6dcTExDiM3xhjoqOjK2zrxy7tN19fX9OkSRP7sm3btmq9ZmUiIyPNX//6V/vj8PBw89RTT/3sGCt7HwYFBZnS0lJ72yuvvGI6dOhgysvL7W2lpaXG19fXrF+/3pw8edJIMllZWVXWB9QVjgABdWzRokVasWKFdu/e7fQ2IiMj5eHx349vUFCQOnfubH/s6empa6+9VseOHXNYLyYmxv7nRo0aqVevXvY6du7cqQ8++MB+5KVp06bq2LGjpB9OpVzSs2fPy9ZWVFSko0ePqk+fPg7tffr0qfGY77rrLh09elT/+Mc/NGDAAGVlZalHjx720047d+7U2bNnde211zrUnZub61DzJcXFxTpw4IDuv/9+h/5/+tOf7P137Nih7t27q0WLFtWuszp17N69W9HR0Q7r/Xh/XM6qVau0Y8cO+9KpU6dqvebZs2f1+OOP64YbblBAQICaNm2q3bt3248AXanOnTs7zPvZuXOn9u/fr2bNmtnradGihUpKSnTgwAG1aNFCY8eOVXx8vBISEvT000/X+KgT4CpMggbq2K9+9SvFx8crKSlJY8eOdXjOw8NDxhiHtgsXLlTYRuPGjR0e22y2StvKy8urXdfZs2eVkJCgRYsWVXguJCTE/ucmTZpUe5uu4OPjozvuuEN33HGHkpOTNX78eKWkpGjs2LE6e/asQkJClJWVVWG9gICACm1nz56VJL3wwgsVwoinp6ck5yYY17SOmgoLC9P1119f49d8/PHHtWHDBj355JO6/vrr5evrq//5n//R+fPnL/t61X0f/vS9cPbsWfXs2VOvvfZahb6BgYGSpOXLl+sPf/iDMjIytGrVKs2cOVMbNmzQTTfddNmaAFcjAAFusHDhQnXr1s0+cfSSwMBA5efnyxhjnxfhynv3fPrpp/rVr34lSbp48aJycnI0ceJESVKPHj301ltvKSIiQo0aOf+/Bj8/P4WGhmrz5s265ZZb7O2bN29WVFTUlQ1AUqdOnbRmzRpJP9Scn5+vRo0aOUzQrUpQUJBCQ0N18OBBjRw5stI+Xbp00YsvvqhTp05VehTIy8tLZWVlDm3VqeOGG27Qli1bNHr0aHvbp59++rM1V6U6r7l582aNHTtWd955p6QfAsqPJyRXNR5n34c9evTQqlWr1KpVK/n5+VXZr3v37urevbuSkpIUExOjlStXEoBQ5zgFBrhB586dNXLkSD3zzDMO7f369dPx48f15z//WQcOHNCSJUv07rvvuux1lyxZorfffltff/21HnnkEf3nP//RfffdJ0l65JFHdOrUKQ0fPlyfffaZDhw4oPXr12vcuHEVviB/zrRp07Ro0SKtWrVKe/bsUWJionbs2KHHHnus2ts4efKkbrvtNr366qv64osvlJubq//7v//Tn//8Zw0ZMkTSD1fWxcTEaOjQoXrvvff0zTff6JNPPtGMGTP0+eefV7rdOXPmKDU1Vc8884z27t2rL7/8UsuXL1daWpokafjw4QoODtbQoUO1efNmHTx4UG+99Zays7Ml/XDfnNzcXO3YsUMnTpxQaWlptep47LHHtGzZMi1fvlx79+5VSkqK/v3vf9fo7/XHqvOa7du31+rVq7Vjxw7t3LlTI0aMqHBUMCIiQh999JGOHDliv+LP2ffhyJEj1bJlSw0ZMkQff/yxcnNzlZWVpT/84Q/67rvvlJubq6SkJGVnZ+vbb7/Ve++9p3379umGG25w+u8BcJp7pyAB1vDjSdCX5ObmGi8vL/PTj+Fzzz1nwsLCTJMmTczo0aPN/PnzK0w+/em2brnlFvPYY485tP14cuulybQrV640UVFRxsvLy3Tq1Mm8//77Duvs3bvX3HnnnSYgIMD4+vqajh07mkmTJtkntVb2OpUpKyszs2fPNq1btzaNGzc2Xbt2Ne+++65Dn5+bBF1SUmISExNNjx49jL+/v7nmmmtMhw4dzMyZM825c+fs/YqKisyjjz5qQkNDTePGjU1YWJgZOXKkOXTokDGm8gm9r732munWrZvx8vIyzZs3N7/61a/M6tWr7c9/88035q677jJ+fn7mmmuuMb169TJbtmyx13XXXXeZgIAAI8ksX768WnUYY8z8+fNNy5YtTdOmTc2YMWPME088Ua1J0Nu3b6/0+Z97zdzcXHPrrbcaX19fExYWZhYvXlxhH2ZnZ5suXboYb29vh/eiM+9DY4zJy8szo0ePNi1btjTe3t7muuuuMw888IApLCw0+fn5ZujQoSYkJMR4eXmZ8PBwM2vWLIcJ/UBdsRnzkxO9AAAAVzlOgQEAAMshAAEAAMshAAEAAMshAAEAAMshAAEAAMshAAEAAMshAAEAAMshAAEAAMshAAEAAMshAAEAAMshAAEAAMv5/wA2MyFgtq0gPgAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "pareto_front = est.evaluated_individuals[est.evaluated_individuals['Pareto_Front'] == 1]\n", - "\n", - "#plot the pareto front of number_of_leaves_objective vs roc_auc_score\n", - "\n", - "import matplotlib.pyplot as plt\n", - "plt.scatter(pareto_front['number_of_leaves_objective'], pareto_front['roc_auc_score'])\n", - "plt.xlabel('Number of Selected Features')\n", - "plt.ylabel('roc_auc_score')\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Feature selection with arithmetic transformers to create features for final classifier \n", - "\n", - "here we include arithmetic operators in the inner nodes that can combine and transform the selected features. \n", - "\n", - "We now use the number of nodes objective to minimize the complexity of the resulting equation. This minimized the number of selected features and the number of arithmetic operators" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Generation: 100%|██████████| 20/20 [00:13<00:00, 1.44it/s]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.9307120901639344\n" - ] - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "\n", - "est = tpot2.TPOTEstimator(population_size=40,generations=20, \n", - " scorers=['roc_auc_ovr'],\n", - " scorers_weights=[1],\n", - " other_objective_functions=[tpot2.objectives.number_of_nodes_objective],\n", - " other_objective_functions_weights=[-1],\n", - " n_jobs=32,\n", - " classification=True,\n", - " leaf_config_dict=\"feature_set_selector\",\n", - " root_config_dict=root_config_dict,\n", - " inner_config_dict=\"arithmetic_transformer\",\n", - " subsets = None,\n", - " verbose=1,\n", - " )\n", - "\n", - "\n", - "est.fit(X_train,y_train)\n", - "print(sklearn.metrics.get_scorer('roc_auc_ovr')(est, X_test, y_test))\n", - "\n", - "est.fitted_pipeline_.plot()" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "FeatureSetSelector_1 : FeatureSetSelector(name='5', sel_subset=['f'])\n", - "FeatureSetSelector_2 : FeatureSetSelector(name='1', sel_subset=['b'])\n", - "FeatureSetSelector_3 : FeatureSetSelector(name='4', sel_subset=['e'])\n", - "FeatureSetSelector_4 : FeatureSetSelector(name='3', sel_subset=['d'])\n", - "FeatureSetSelector_5 : FeatureSetSelector(name='0', sel_subset=['a'])\n" - ] - } - ], - "source": [ - "# print the selected features for each FSS\n", - "\n", - "#get leaves\n", - "leaves = [v for v, d in est.fitted_pipeline_.graph.out_degree() if d == 0]\n", - "for l in leaves:\n", - " print(l, \" : \", est.fitted_pipeline_.graph.nodes[l]['instance'])" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "LogisticRegression_1 : LogisticRegression(C=1.3234861148420467, solver='liblinear')\n", - "FeatureSetSelector_1 : FeatureSetSelector(name='5', sel_subset=['f'])\n", - "FeatureSetSelector_2 : FeatureSetSelector(name='1', sel_subset=['b'])\n", - "FeatureSetSelector_3 : FeatureSetSelector(name='4', sel_subset=['e'])\n", - "mul_neg_1_Transformer_1 : mul_neg_1_Transformer()\n", - "EQTransformer_1 : EQTransformer()\n", - "FeatureSetSelector_4 : FeatureSetSelector(name='3', sel_subset=['d'])\n", - "NETransformer_1 : NETransformer()\n", - "FeatureSetSelector_5 : FeatureSetSelector(name='0', sel_subset=['a'])\n" - ] - } - ], - "source": [ - "# print all hyperparameters\n", - "for n in est.fitted_pipeline_.graph.nodes:\n", - " print(n, \" : \", est.fitted_pipeline_.graph.nodes[n]['instance'])" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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uvnmm80uxe/i4uL0l7/8xfW+USPr/BX07bffql+/frrjjju0fv16tWrVSgcOHND1119vdml+s3XrVlVXV7ve7969W3fffbcefPBBE6vyn/nz52vRokVavny54uLitG3bNo0ZM0ahoaF6+umnzS7PL8aNG6fdu3frjTfeUGRkpFasWKGkpCTt2bNHbdq08W8xBvzuxIkThiTj008/NbsUU11//fXG66+/bnYZfnXmzBkjJibG2Lhxo3HbbbcZzzzzjNkl+U1mZqbRrVs3s8swzXPPPWf079/f7DLqlWeeecbo0KGD4XQ6zS7FLwYNGmSMHTvWrW3IkCHGiBEjTKrIv77//nvDbrcbf/7zn93ae/bsaUydOtXv9fATmAnKysokSc2bNze5EnNUV1fr7bffVkVFhRISEswux69SUlI0aNAgJSUlmV2KKQ4cOKDIyEjdeOONGjFihI4cOWJ2SX7z/vvvq3fv3nrwwQcVFhamHj166LXXXjO7LNOcP39eK1as0NixY73+0On6KjExUXl5edq/f78kadeuXdq0aZMGDhxocmX+UVVVperqajkcDrf2oKAgbdq0yf8F+T1yWVx1dbUxaNAgo1+/fmaX4ndffvml0aRJE8NutxuhoaHG2rVrzS7Jr9566y2jS5cuxg8//GAYhmG5K0Dr1q0z3nnnHWPXrl1Gbm6ukZCQYNxwww1GeXm52aX5RWBgoBEYGGikp6cb27dvN5YsWWI4HA7jD3/4g9mlmWLVqlWG3W43jh49anYpflNdXW0899xzhs1mMxo1amTYbDZj7ty5ZpflVwkJCcZtt91mHD161KiqqjLeeOMNIyAgwOjYsaPfayEA+dn48eONdu3aGcXFxWaX4nfnzp0zDhw4YGzbts1IS0szWrZsafz97383uyy/OHLkiBEWFmbs2rXL1Wa1APTvvv32WyMkJMQyP4Ned911RkJCglvbU089Zdxyyy0mVWSue+65x/jZz35mdhl+9dZbbxlt27Y13nrrLePLL780/vjHPxrNmze3VAg+ePCgceuttxqSDLvdbvTp08cYMWKEERsb6/daCEB+lJKSYrRt29Y4dOiQ2aXUC3fddZfx2GOPmV2GX6xevdr1H/zFlyTDZrMZdrvdqKqqMrtEU/Tu3dtIS0szuwy/uOGGG4xf/epXbm2vvPKKERkZaVJF5vnHP/5hBAQEGGvWrDG7FL9q27atsWDBAre22bNnG506dTKpIvOcPXvWOHbsmGEYhjF06FDjvvvu83sNzAHyA8MwNGHCBK1evVofffSR2rdvb3ZJ9YLT6dS5c+fMLsMv7rrrLv3tb3/Tzp07Xa/evXtrxIgR2rlzp+x2u9kl+t3Zs2f11VdfKSIiwuxS/KJfv341lr/Yv3+/2rVrZ1JF5lm2bJnCwsI0aNAgs0vxq++//14BAe6nXbvdLqfTaVJF5mnSpIkiIiL07bffasOGDRo8eLDfa7DOPagmSklJ0cqVK/WnP/1JwcHBKi0tlSSFhoYqKCjI5Or8Iz09XQMHDtQNN9ygM2fOaOXKlfrkk0+0YcMGs0vzi+DgYHXp0sWtrUmTJmrRokWN9mvVr3/9ayUnJ6tdu3Y6duyYMjMzZbfb9dBDD5ldml9MmjRJiYmJmjt3roYOHaqCggK9+uqrevXVV80uza+cTqeWLVumUaNGWWoZBElKTk7WnDlzdMMNNyguLk47duxQdna2xo4da3ZpfrNhwwYZhqFOnTrp4MGDmjJlimJjYzVmzBj/F+P3a04WJKnW17Jly8wuzW/Gjh1rtGvXzmjcuLHRqlUr46677jI+/PBDs8syldXmAA0bNsyIiIgwGjdubLRp08YYNmyYcfDgQbPL8qsPPvjA6NKlixEYGGjExsYar776qtkl+d2GDRsMSUZRUZHZpfhdeXm58cwzzxg33HCD4XA4jBtvvNGYOnWqce7cObNL85tVq1YZN954o9G4cWOjdevWRkpKivHdd9+ZUovNMCy0BCUAAIB4FAYAALAgAhAAALAcAhAAALAcAhAAALAcAhAAALAcAhAAALAcAhAAALAcAhAAALAcAhAA0/3jH/+QzWbTzp07zS7FZd++fbrlllvkcDjUvXt3U2qIjo5WTk6OKd8NXOsIQAA0evRo2Ww2zZs3z619zZo1stlsJlVlrszMTDVp0kRFRUXKy8urtQ/HDWi4CEAAJEkOh0Pz58/Xt99+a3YpXnP+/HmPt/3qq6/Uv39/tWvXTi1atLhkv2vxuAFWQAACIElKSkpS69atlZWVdck+M2bMqPFzUE5OjqKjo13vR48erfvvv19z585VeHi4mjVrplmzZqmqqkpTpkxR8+bN1bZtWy1btqzG/vft26fExEQ5HA516dJFn376qdvnu3fv1sCBA9W0aVOFh4frkUce0alTp1yf33777ZowYYImTpyoli1basCAAbWOw+l0atasWWrbtq0CAwPVvXt35ebmuj632WwqLCzUrFmzZLPZNGPGjDodN0l69913FRcXp8DAQEVHR+t3v/ud2+cnTpxQcnKygoKC1L59e7355ps19vHdd99p3LhxatWqlUJCQnTnnXdq165drs937dqlO+64Q8HBwQoJCVGvXr20bdu2y9YFWBUBCIAkyW63a+7cuXr55Zf1f//3f3Xa10cffaRjx47ps88+U3Z2tjIzM/Wzn/1M119/vbZs2aLx48fr8ccfr/E9U6ZM0eTJk7Vjxw4lJCQoOTlZ33zzjaQfT/533nmnevTooW3btik3N1fHjx/X0KFD3faxfPlyNW7cWPn5+Vq8eHGt9b300kv63e9+pxdeeEFffvmlBgwYoJ///Oc6cOCAJKmkpERxcXGaPHmySkpK9Otf//qSY72S41ZYWKihQ4fql7/8pf72t79pxowZysjI0B/+8AdXn9GjR6u4uFgff/yx/vd//1evvPKKTpw44bafBx98UCdOnND69etVWFionj176q677tLp06clSSNGjFDbtm21detWFRYWKi0tTdddd90lawcszZRn0AOoV0aNGmUMHjzYMAzDuOWWW4yxY8cahmEYq1evNv71r4nMzEyjW7dubtu++OKLRrt27dz21a5dO6O6utrV1qlTJ+OnP/2p631VVZXRpEkT46233jIMwzAOHz5sSDLmzZvn6nPhwgWjbdu2xvz58w3DMIzZs2cb99xzj9t3FxcXG5KMoqIiwzAM47bbbjN69OjxH8cbGRlpzJkzx62tT58+xpNPPul6361bNyMzM/Oy+7nS4zZ8+HDj7rvvdtt2ypQpRufOnQ3DMIyioiJDklFQUOD6fO/evYYk48UXXzQMwzD++te/GiEhIUZlZaXbfjp06GAsWbLEMAzDCA4ONv7whz/8h9EDMAzD4AoQADfz58/X8uXLtXfvXo/3ERcXp4CAf/71Eh4erq5du7re2+12tWjRosYVjoSEBNc/N2rUSL1793bVsWvXLn388cdq2rSp6xUbGyvpx/k6F/Xq1euytZWXl+vYsWPq16+fW3u/fv3qNObLHbe9e/fW+n0HDhxQdXW19u7dq0aNGrnVHhsbq2bNmrne79q1S2fPnlWLFi3cjsHhw4dd409NTdW4ceOUlJSkefPmuR0XAO4IQADc3HrrrRowYIDS09NrfBYQECDDMNzaLly4UKPfv//sYrPZam1zOp1XXNfZs2eVnJysnTt3ur0OHDigW2+91dWvSZMmV7xPb7rccfOGs2fPKiIiosb4i4qKNGXKFEk/ztH6+9//rkGDBumjjz5S586dtXr1ap/UAzR0jcwuAED9M2/ePHXv3l2dOnVya2/VqpVKS0tlGIbrNm9vrt3zxRdfuMJMVVWVCgsLNWHCBElSz5499e677yo6OlqNGnn+V1dISIgiIyOVn5+v2267zdWen5+vvn371qn+Sx23m266Sfn5+W5t+fn56tixo+x2u2JjY13j7dOnjySpqKhI3333nat/z549VVpaqkaNGrlNOv93HTt2VMeOHTVp0iQ99NBDWrZsmR544IE6jQu4FnEFCEANXbt21YgRI/Q///M/bu233367Tp48qd/+9rf66quvtHDhQq1fv95r37tw4UKtXr1a+/btU0pKir799luNHTtWkpSSkqLTp0/roYce0tatW/XVV19pw4YNGjNmjKqrq6/qe6ZMmaL58+dr1apVKioqUlpamnbu3KlnnnmmTvVf6rhNnjxZeXl5mj17tvbv36/ly5drwYIFrsnVnTp10r333qvHH39cW7ZsUWFhocaNG6egoCDXPpKSkpSQkKD7779fH374of7xj3/o888/19SpU7Vt2zb98MMPmjBhgj755BN9/fXXys/P19atW3XTTTfVaUzAtYoABKBWs2bNqvET1U033aRXXnlFCxcuVLdu3VRQUHDZO6Su1rx58zRv3jx169ZNmzZt0vvvv6+WLVtKkuuqTXV1te655x517dpVEydOVLNmzdzmG12Jp59+WqmpqZo8ebK6du2q3Nxcvf/++4qJianzGGo7bj179tQ777yjt99+W126dNH06dM1a9YsjR492tVn2bJlioyM1G233aYhQ4boscceU1hYmOtzm82mdevW6dZbb9WYMWPUsWNH/fKXv9TXX3+t8PBw2e12ffPNNxo5cqQ6duyooUOHauDAgZo5c2adxwRci2zGv/+gDwAAcI3jChAAALAcAhAAALAcAhAAALAcAhAAALAcAhAAALAcAhAAALAcAhAAALAcAhAAALAcAhAAALAcAhAAALAcAhAAALCc/wfSt86g4cynRwAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "pareto_front = est.evaluated_individuals[est.evaluated_individuals['Pareto_Front'] == 1]\n", - "\n", - "#plot the pareto front of number_of_leaves_objective vs roc_auc_score\n", - "\n", - "plt.scatter(pareto_front['number_of_nodes_objective'], pareto_front['roc_auc_score'])\n", - "plt.xlabel('Number of Nodes')\n", - "plt.ylabel('roc_auc_score')\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Examples of FSS that select from groups of features rather than individual features" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## dictionary" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Generation: 100%|██████████| 20/20 [00:26<00:00, 1.31s/it]\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_sag.py:350: ConvergenceWarning: The max_iter was reached which means the coef_ did not converge\n", - " warnings.warn(\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.9699667008196722\n" - ] - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import tpot2\n", - "import pandas as pd\n", - "import numpy as np\n", - "from sklearn.linear_model import LogisticRegression\n", - "import sklearn\n", - "\n", - "subsets = { \"group_one\" : ['a','b','c'],\n", - " \"group_two\" : ['d','e','f'],\n", - " \"group_three\" : ['g','h','i'],\n", - " }\n", - "\n", - "est = tpot2.TPOTEstimator(population_size=40,generations=20, \n", - " scorers=['roc_auc_ovr',tpot2.objectives.complexity_scorer],\n", - " scorers_weights=[1,-1],\n", - " n_jobs=32,\n", - " classification=True,\n", - " leaf_config_dict=\"feature_set_selector\",\n", - " root_config_dict=root_config_dict,\n", - " inner_config_dict=\"transformers\",\n", - " subsets = subsets,\n", - " verbose=1,\n", - " )\n", - "\n", - "\n", - "est.fit(X_train,y_train)\n", - "print(sklearn.metrics.get_scorer('roc_auc_ovr')(est, X_test, y_test))\n", - "\n", - "est.fitted_pipeline_.plot()" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "FeatureSetSelector_1 : FeatureSetSelector(name='group_one', sel_subset=['a', 'b', 'c'])\n", - "FeatureSetSelector_2 : FeatureSetSelector(name='group_two', sel_subset=['d', 'e', 'f'])\n", - "FeatureSetSelector_3 : FeatureSetSelector(name='group_three', sel_subset=['g', 'h', 'i'])\n" - ] - } - ], - "source": [ - "# print the selected features for each FSS\n", - "\n", - "#get leaves\n", - "leaves = [v for v, d in est.fitted_pipeline_.graph.out_degree() if d == 0]\n", - "for l in leaves:\n", - " print(l, \" : \", est.fitted_pipeline_.graph.nodes[l]['instance'])" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "LogisticRegression_1 : LogisticRegression(C=0.06776401610163652, solver='saga')\n", - "FeatureSetSelector_1 : FeatureSetSelector(name='group_one', sel_subset=['a', 'b', 'c'])\n", - "PolynomialFeatures_1 : PolynomialFeatures(include_bias=False)\n", - "FeatureSetSelector_2 : FeatureSetSelector(name='group_two', sel_subset=['d', 'e', 'f'])\n", - "MaxAbsScaler_1 : MaxAbsScaler()\n", - "PCA_1 : PCA(n_components=0.9574868087370769)\n", - "FeatureSetSelector_3 : FeatureSetSelector(name='group_three', sel_subset=['g', 'h', 'i'])\n", - "MaxAbsScaler_2 : MaxAbsScaler()\n" - ] - } - ], - "source": [ - "# print all hyperparameters\n", - "for n in est.fitted_pipeline_.graph.nodes:\n", - " print(n, \" : \", est.fitted_pipeline_.graph.nodes[n]['instance'])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## list" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Generation: 100%|██████████| 20/20 [00:21<00:00, 1.07s/it]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.9712474385245903\n" - ] - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import tpot2\n", - "import pandas as pd\n", - "import numpy as np\n", - "from sklearn.linear_model import LogisticRegression\n", - "import sklearn\n", - "\n", - "subsets = [['a','b','c'],['d','e','f'],['g','h','i']]\n", - "\n", - "est = tpot2.TPOTEstimator(population_size=40,generations=20, \n", - " scorers=['roc_auc_ovr',tpot2.objectives.complexity_scorer],\n", - " scorers_weights=[1,-1],\n", - " n_jobs=32,\n", - " classification=True,\n", - " leaf_config_dict=\"feature_set_selector\",\n", - " root_config_dict=root_config_dict,\n", - " inner_config_dict=\"transformers\",\n", - " subsets = subsets,\n", - " verbose=1,\n", - " )\n", - "\n", - "\n", - "est.fit(X_train,y_train)\n", - "print(sklearn.metrics.get_scorer('roc_auc_ovr')(est, X_test, y_test))\n", - "\n", - "est.fitted_pipeline_.plot()" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "FeatureSetSelector_1 : FeatureSetSelector(name='1', sel_subset=['d', 'e', 'f'])\n", - "FeatureSetSelector_2 : FeatureSetSelector(name='0', sel_subset=['a', 'b', 'c'])\n" - ] - } - ], - "source": [ - "# print the selected features for each FSS\n", - "\n", - "#get leaves\n", - "leaves = [v for v, d in est.fitted_pipeline_.graph.out_degree() if d == 0]\n", - "for l in leaves:\n", - " print(l, \" : \", est.fitted_pipeline_.graph.nodes[l]['instance'])" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "LogisticRegression_1 : LogisticRegression(C=0.01924346331466653)\n", - "PolynomialFeatures_1 : PolynomialFeatures(include_bias=False)\n", - "OneHotEncoder_1 : OneHotEncoder()\n", - "FeatureSetSelector_1 : FeatureSetSelector(name='1', sel_subset=['d', 'e', 'f'])\n", - "FeatureSetSelector_2 : FeatureSetSelector(name='0', sel_subset=['a', 'b', 'c'])\n", - "FastICA_1 : FastICA(whiten='unit-variance')\n", - "PolynomialFeatures_2 : PolynomialFeatures(include_bias=False)\n" - ] - } - ], - "source": [ - "# print all hyperparameters\n", - "for n in est.fitted_pipeline_.graph.nodes:\n", - " print(n, \" : \", est.fitted_pipeline_.graph.nodes[n]['instance'])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## csv file\n", - "\n", - "note: watch for spaces in the csv file!" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Generation: 100%|██████████| 20/20 [00:46<00:00, 2.34s/it]\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_sag.py:350: ConvergenceWarning: The max_iter was reached which means the coef_ did not converge\n", - " warnings.warn(\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.9678534836065574\n" - ] - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import tpot2\n", - "import pandas as pd\n", - "import numpy as np\n", - "from sklearn.linear_model import LogisticRegression\n", - "import sklearn\n", - "\n", - "subsets = 'simple_fss.csv'\n", - "'''\n", - "# simple_fss.csv\n", - "one,a,b,c\n", - "two,d,e,f\n", - "three,g,h,i\n", - "'''\n", - "\n", - "est = tpot2.TPOTEstimator(population_size=40,generations=20, \n", - " scorers=['roc_auc_ovr',tpot2.objectives.complexity_scorer],\n", - " scorers_weights=[1,-1],\n", - " n_jobs=32,\n", - " classification=True,\n", - " leaf_config_dict=\"feature_set_selector\",\n", - " root_config_dict=root_config_dict,\n", - " inner_config_dict=\"transformers\",\n", - " subsets = subsets,\n", - " verbose=1,\n", - " )\n", - "\n", - "\n", - "est.fit(X_train,y_train)\n", - "print(sklearn.metrics.get_scorer('roc_auc_ovr')(est, X_test, y_test))\n", - "\n", - "est.fitted_pipeline_.plot()" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "FeatureSetSelector_1 : FeatureSetSelector(name='two', sel_subset=['d', 'e', 'f'])\n", - "FeatureSetSelector_2 : FeatureSetSelector(name='one', sel_subset=['a', 'b', 'c'])\n" - ] - } - ], - "source": [ - "# print the selected features for each FSS\n", - "\n", - "#get leaves\n", - "leaves = [v for v, d in est.fitted_pipeline_.graph.out_degree() if d == 0]\n", - "for l in leaves:\n", - " print(l, \" : \", est.fitted_pipeline_.graph.nodes[l]['instance'])" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "LogisticRegression_1 : LogisticRegression(C=90.92104183243647, solver='saga')\n", - "FeatureSetSelector_1 : FeatureSetSelector(name='two', sel_subset=['d', 'e', 'f'])\n", - "FeatureSetSelector_2 : FeatureSetSelector(name='one', sel_subset=['a', 'b', 'c'])\n", - "RBFSampler_1 : RBFSampler(gamma=0.9480907031133559)\n", - "Binarizer_1 : Binarizer(threshold=0.5204447023562712)\n", - "RBFSampler_2 : RBFSampler(gamma=0.07182739023710172)\n", - "MaxAbsScaler_1 : MaxAbsScaler()\n" - ] - } - ], - "source": [ - "# print all hyperparameters\n", - "for n in est.fitted_pipeline_.graph.nodes:\n", - " print(n, \" : \", est.fitted_pipeline_.graph.nodes[n]['instance'])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "note that all of the above is the same when using numpy X, but the column names are now int indeces" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[ 0.03418023 1.85703799 1.3321493 ... 0.61740176 0.03615026\n", - " 0.73457701]\n", - " [ 0.00655906 0.3495084 -2.86361395 ... 0.27195435 0.52330367\n", - " 0.47208072]\n", - " [ 1.84952258 -0.98538028 0.60941956 ... 0.14054112 0.77081219\n", - " 0.17160637]\n", - " ...\n", - " [ 0.02282946 0.55489649 -2.89758703 ... 0.04122268 0.66234341\n", - " 0.76367281]\n", - " [-1.34268913 2.73488335 -1.82542106 ... 0.59224411 0.94857147\n", - " 0.20810423]\n", - " [-0.46791145 2.53228934 -2.08802875 ... 0.82326686 0.23363656\n", - " 0.77884819]]\n" - ] - } - ], - "source": [ - "import tpot2\n", - "import sklearn.datasets\n", - "from sklearn.linear_model import LogisticRegression\n", - "import numpy as np\n", - "import pandas as pd\n", - "\n", - "n_features = 6\n", - "X, y = sklearn.datasets.make_classification(n_samples=1000, n_features=n_features, n_informative=6, n_redundant=0, n_repeated=0, n_classes=2, n_clusters_per_class=2, weights=None, flip_y=0.01, class_sep=1.0, hypercube=True, shift=0.0, scale=1.0, shuffle=True, random_state=None)\n", - "X = np.hstack([X, np.random.rand(X.shape[0],3)]) #add three uninformative features\n", - "\n", - "X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n", - "\n", - "print(X)" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Generation: 100%|██████████| 20/20 [00:44<00:00, 2.22s/it]\n", - "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_sag.py:350: ConvergenceWarning: The max_iter was reached which means the coef_ did not converge\n", - " warnings.warn(\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.9830226151579218\n" - ] - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import tpot2\n", - "import pandas as pd\n", - "import numpy as np\n", - "from sklearn.linear_model import LogisticRegression\n", - "import sklearn\n", - "\n", - "subsets = { \"group_one\" : [0,1,2],\n", - " \"group_two\" : [3,4,5],\n", - " \"group_three\" : [6,7,8],\n", - " }\n", - "\n", - "est = tpot2.TPOTEstimator(population_size=40,generations=20, \n", - " scorers=['roc_auc_ovr',tpot2.objectives.complexity_scorer],\n", - " scorers_weights=[1,-1],\n", - " n_jobs=32,\n", - " classification=True,\n", - " leaf_config_dict=\"feature_set_selector\",\n", - " root_config_dict=root_config_dict,\n", - " inner_config_dict=\"transformers\",\n", - " subsets = subsets,\n", - " verbose=1,\n", - " )\n", - "\n", - "\n", - "est.fit(X_train,y_train)\n", - "print(sklearn.metrics.get_scorer('roc_auc_ovr')(est, X_test, y_test))\n", - "\n", - "est.fitted_pipeline_.plot()" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "FeatureSetSelector_1 : FeatureSetSelector(name='group_one', sel_subset=[0, 1, 2])\n", - "FeatureSetSelector_2 : FeatureSetSelector(name='group_two', sel_subset=[3, 4, 5])\n", - "FeatureSetSelector_3 : FeatureSetSelector(name='group_three', sel_subset=[6, 7, 8])\n" - ] - } - ], - "source": [ - "# print the selected features for each FSS\n", - "\n", - "#get leaves\n", - "leaves = [v for v, d in est.fitted_pipeline_.graph.out_degree() if d == 0]\n", - "for l in leaves:\n", - " print(l, \" : \", est.fitted_pipeline_.graph.nodes[l]['instance'])" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "LogisticRegression_1 : LogisticRegression(C=0.13013559430004598, solver='sag')\n", - "FeatureSetSelector_1 : FeatureSetSelector(name='group_one', sel_subset=[0, 1, 2])\n", - "PCA_1 : PCA(n_components=0.9988096714708292)\n", - "PolynomialFeatures_1 : PolynomialFeatures(include_bias=False)\n", - "FeatureSetSelector_2 : FeatureSetSelector(name='group_two', sel_subset=[3, 4, 5])\n", - "FeatureSetSelector_3 : FeatureSetSelector(name='group_three', sel_subset=[6, 7, 8])\n", - "Normalizer_1 : Normalizer(norm='max')\n", - "RBFSampler_1 : RBFSampler(gamma=0.17772815448977386)\n" - ] - } - ], - "source": [ - "# print all hyperparameters\n", - "for n in est.fitted_pipeline_.graph.nodes:\n", - " print(n, \" : \", est.fitted_pipeline_.graph.nodes[n]['instance'])" - ] - } - ], - "metadata": { - "interpreter": { - "hash": "57aedbec84c390a3287b44649e400696ed2b6dcd408c8519583e8e995dbe6e9b" - }, - "kernelspec": { - "display_name": "Python 3.10.12 ('tpot2env2')", - "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.11" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "7fe1fe9ef32cd5efd76326a08046147513534f0dd2318301a1a96ae9071c1c4e" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/Tutorial/4_Symbolic_Regression_and_Classification.ipynb b/Tutorial/4_Symbolic_Regression_and_Classification.ipynb index 8b6c7254..80c4ef4a 100644 --- a/Tutorial/4_Symbolic_Regression_and_Classification.ipynb +++ b/Tutorial/4_Symbolic_Regression_and_Classification.ipynb @@ -24,26 +24,32 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "Generation: 100%|██████████| 50/50 [01:59<00:00, 2.39s/it]\n" + "Generation: 0%| | 0/20 [00:00" ] @@ -57,30 +63,32 @@ "import sklearn.datasets\n", "from sklearn.linear_model import LogisticRegression\n", "import numpy as np\n", - "from tpot2.builtin_modules import ZeroTransformer, OneTransformer\n", - "from tpot2.config.classifiers import params_LogisticRegression\n", "\n", - "root_config_dict = {LogisticRegression: params_LogisticRegression}\n", - "leaf_config_dict = [\"feature_set_selector\", {ZeroTransformer: {}, OneTransformer: {}}]\n", + "X, y = sklearn.datasets.make_classification(n_samples=1000, n_features=100, n_informative=6, n_redundant=0, n_repeated=0, n_classes=2, n_clusters_per_class=2, weights=None, flip_y=0.01, class_sep=1.0, hypercube=True, shift=0.0, scale=1.0, shuffle=True, random_state=None)\n", + "X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n", + "\n", + "n_features = X_train.shape[1]\n", "\n", + "graph_search_space = tpot2.search_spaces.pipelines.GraphPipeline(\n", + " root_search_space= tpot2.config.get_search_space(\"LogisticRegression\"),\n", + " leaf_search_space = tpot2.search_spaces.nodes.FSSNode(subsets=n_features), \n", + " inner_search_space = tpot2.config.get_search_space([\"arithmatic\"]),\n", + " max_size = 10,\n", + ")\n", "\n", - "est = tpot2.TPOTEstimator(population_size=100,generations=50, \n", - " scorers=['roc_auc'],\n", + "est = tpot2.TPOTEstimator(population_size=10,generations=20, \n", + " scorers=['roc_auc_ovr'],\n", " scorers_weights=[1],\n", " other_objective_functions=[tpot2.objectives.number_of_nodes_objective],\n", " other_objective_functions_weights=[-1],\n", - " classification=True,\n", - " inner_config_dict= \"arithmetic_transformer\",\n", - " leaf_config_dict=leaf_config_dict,\n", - " root_config_dict=root_config_dict,\n", " n_jobs=32,\n", + " classification=True,\n", + " search_space = graph_search_space ,\n", " verbose=1,\n", " )\n", "\n", - "#load iris\n", "scorer = sklearn.metrics.get_scorer('roc_auc_ovo')\n", - "X, y = sklearn.datasets.make_classification(n_samples=1000, n_features=100, n_informative=6, n_redundant=0, n_repeated=0, n_classes=2, n_clusters_per_class=2, weights=None, flip_y=0.01, class_sep=1.0, hypercube=True, shift=0.0, scale=1.0, shuffle=True, random_state=None)\n", - "X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n", + "\n", "est.fit(X_train, y_train)\n", "print(scorer(est, X_test, y_test))\n", "est.fitted_pipeline_.plot()" @@ -88,23 +96,20 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "LogisticRegression_1 : LogisticRegression(C=282.83015030119856, max_iter=1000, n_jobs=1, solver='sag')\n", - "FeatureSetSelector_1 : FeatureSetSelector(name='50', sel_subset=[50])\n", - "FeatureSetSelector_2 : FeatureSetSelector(name='16', sel_subset=[16])\n", - "MaxTransformer_1 : MaxTransformer()\n", + "LogisticRegression_1 : LogisticRegression(C=0.28751652817028706, class_weight='balanced',\n", + " max_iter=1000, n_jobs=1, solver='liblinear')\n", + "FeatureSetSelector_1 : FeatureSetSelector(name='93', sel_subset=[93])\n", + "FeatureSetSelector_2 : FeatureSetSelector(name='25', sel_subset=[25])\n", + "LETransformer_1 : LETransformer()\n", "LTTransformer_1 : LTTransformer()\n", - "FeatureSetSelector_3 : FeatureSetSelector(name='42', sel_subset=[42])\n", - "FeatureSetSelector_4 : FeatureSetSelector(name='21', sel_subset=[21])\n", - "MaxTransformer_2 : MaxTransformer()\n", - "LTTransformer_2 : LTTransformer()\n", - "MulTransformer_1 : MulTransformer()\n" + "MinTransformer_1 : MinTransformer()\n" ] } ], @@ -116,12 +121,12 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 9, "metadata": {}, "outputs": [ { "data": { - "image/png": "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I8MjxQEAAHiDWwFo9+7d6tSpkyRp//79Lq/ZbLY7rwoAAMCL3ApAX3zxhafrAAAAqDRurQECAACozghAAADAcghAAADAcghAAADAcghAAADAcghAAADAcghAAADAcghAAADAcghAAADAcghAAADAcghAAADAcghAAADAcghAAADAcghAAADAcghAAADAcghAAADAcghAAADAcghAAADAcghAAADAcghAAADAcghAAADAcghAAADAcghAAADAcghAAADAcqpEAFq4cKGioqLk7++vuLg45ebm3rR/ZmamoqOjFRAQoMjISE2cOFGlpaUV9p03b55sNpsmTJjghcoBAEB1ZHoAysrKUlJSktLS0rRz5061b99evXv31qlTpyrsv2LFCiUnJystLU179+7V0qVLlZWVpSlTppTru337dr322mtq166dt4cBAACqEdMDUEZGhp599lmNHDlSMTExWrJkiX70ox9p2bJlFfbfsmWLunfvrkGDBikqKkq9evXSwIEDy80aXbx4UYMHD9Ybb7yhu++++6Y1XL58WcXFxS4PAABQc5kagK5cuaK8vDwlJiY623x8fJSYmKitW7dWeExCQoLy8vKcgefw4cNas2aNnnjiCZd+Y8eOVd++fV3OfSPp6ekKDg52PiIjI+9gVAAAoKqrZeabnzlzRmVlZQoNDXVpDw0N1TfffFPhMYMGDdKZM2fUo0cPGYaha9eu6bnnnnO5BPbee+9p586d2r59+y3VkZKSoqSkJOfz4uJiQhAAADWY6ZfAbteGDRs0d+5cLVq0SDt37tSHH36o1atXa/bs2ZKkY8eOafz48Xr33Xfl7+9/S+f08/NTUFCQywMAANRcps4ANWzYUL6+vjp58qRL+8mTJxUWFlbhMampqRo6dKhGjx4tSYqNjVVJSYnGjBmjqVOnKi8vT6dOnVKnTp2cx5SVlenLL7/UggULdPnyZfn6+npvUAAAoMozdQaodu3a6ty5s3JycpxtDodDOTk5io+Pr/CYS5cuycfHtezrgcYwDD366KP6+uuvlZ+f73x06dJFgwcPVn5+PuEHAACYOwMkSUlJSRo+fLi6dOmibt26KTMzUyUlJRo5cqQkadiwYWrcuLHS09MlSXa7XRkZGerYsaPi4uJ08OBBpaamym63y9fXV4GBgWrbtq3Le9SpU0cNGjQo1w4AAKzJ9AA0YMAAnT59WtOnT1dhYaE6dOig7Oxs58Loo0ePusz4TJs2TTabTdOmTdPx48cVEhIiu92uOXPmmDUEAABQzdgMwzDMLqKqKS4uVnBwsIqKilgQDQBANXE739/V7ldgAAAAd4oABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALIcABAAALKdKBKCFCxcqKipK/v7+iouLU25u7k37Z2ZmKjo6WgEBAYqMjNTEiRNVWlrqfH3x4sVq166dgoKCFBQUpPj4eK1du9bbwwAAANWE6QEoKytLSUlJSktL086dO9W+fXv17t1bp06dqrD/ihUrlJycrLS0NO3du1dLly5VVlaWpkyZ4uzTpEkTzZs3T3l5edqxY4d69uypJ598Uv/4xz8qa1gAAKAKsxmGYZhZQFxcnLp27aoFCxZIkhwOhyIjI/Xiiy8qOTm5XP9x48Zp7969ysnJcbZNmjRJ27Zt06ZNm274PvXr19dvfvMbPfPMMz9YU3FxsYKDg1VUVKSgoCA3RgUAACrb7Xx/mzoDdOXKFeXl5SkxMdHZ5uPjo8TERG3durXCYxISEpSXl+e8THb48GGtWbNGTzzxRIX9y8rK9N5776mkpETx8fEV9rl8+bKKi4tdHgAAoOaqZeabnzlzRmVlZQoNDXVpDw0N1TfffFPhMYMGDdKZM2fUo0cPGYaha9eu6bnnnnO5BCZJX3/9teLj41VaWqq6detq5cqViomJqfCc6enpmjlzpmcGBQAAqjzT1wDdrg0bNmju3LlatGiRdu7cqQ8/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h4SEqLCwUIZhOH/m7cm9e7766itnmLl27Zry8vI0btw4SVKnTp30wQcfKCoqSrVquf+nKygoSBEREdq8ebMeeughZ/vmzZvVrVu3O6r/Rp9b69attXnzZpe2zZs3q2XLlvL19VWrVq2c4+3ataskad++fTp//ryzf6dOnVRYWKhatWq5LDr/Xy1btlTLli01ceJEDRw4UMuXL9dPf/rTOxoXUBMxAwSgnNjYWA0ePFi///3vXdoffvhhnT59Wi+//LIOHTqkhQsXau3atR5734ULF2rlypX65ptvNHbsWH333XcaNWqUJGns2LE6d+6cBg4cqO3bt+vQoUNat26dRo4cqbKystt6n8mTJ2v+/PnKysrSvn37lJycrPz8fI0fP/6O6r/R5zZp0iTl5ORo9uzZ2r9/v95++20tWLDAubg6Ojpajz/+uH7+859r27ZtysvL0+jRoxUQEOA8R2JiouLj4/XUU0/p008/1T//+U9t2bJFU6dO1Y4dO/Tvf/9b48aN04YNG/Ttt99q8+bN2r59u1q3bn1HYwJqKgIQgArNmjWr3CWq1q1ba9GiRVq4cKHat2+v3Nzcm/5C6nbNmzdP8+bNU/v27bVp0yZ99NFHatiwoSQ5Z23KysrUq1cvxcbGasKECapXr57LeqNb8dJLLykpKUmTJk1SbGyssrOz9dFHH6lFixZ3PIaKPrdOnTrp/fff13vvvae2bdtq+vTpmjVrlkaMGOHss3z5ckVEROihhx5Sv379NGbMGDVq1Mj5us1m05o1a/Tggw9q5MiRatmypX72s5/p22+/VWhoqHx9fXX27FkNGzZMLVu2VP/+/dWnTx/NnDnzjscE1EQ2438v6AMAANRwzAABAADLIQABAADLIQABAADLIQABAADLIQABAADLIQABAADLIQABAADLIQABAADLIQABAADLIQABAADLIQABAADL+f8hTZF8xDIfyQAAAABJRU5ErkJggg==", + "image/png": "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AAAA18MzSAAAACYEJAAAABMCEgAAgAkBCQAAwISABAAAYEJAAgAAMCEgAQAAmBCQAAAATAhIAFzCf//7X1ksFu3evdvZpdh88cUX6tevn3x8fBQZGemUGjp06KAlS5Y45b2BxoyABKBaHn30UVksFqWmptq1r1u3ThaLxUlVOVdycrKaNm2q3NzcCj+keQ2fG+CaCEgAqs3Hx0eLFi3SuXPnnF1Krbl8+bLDxx45ckT9+/dXSEiIWrVqVWW/xvi5AY0dAQlAtcXHxyswMFApKSlV9pk3b16F201LlixRhw4dbK8fffRRDR8+XM8++6wCAgLUvHlzLViwQN9//71mzZqlli1bqn379lq1alWF8b/44gvFxcXJx8dHPXr00NatW+3279+/X/fff79uvfVWBQQE6JFHHtGZM2ds+++8805NnTpVM2bMUOvWrTVo0KBK52G1WrVgwQK1b99e3t7eioyMVGZmpm2/xWJRTk6OFixYIIvFonnz5t3Q5yZJa9euVffu3eXt7a0OHTrohRdesNtfVFSkIUOGqEmTJurYsaNef/31CmMUFxdr0qRJatOmjXx9fXX33Xdrz549tv179uzRXXfdpWbNmsnX11dRUVHauXPndesCbkYEJADV5u7urmeffVZ//vOfdfLkyRsa64MPPtCpU6f00Ucf6cUXX1RycrJ+8YtfqEWLFvr000/1+OOPa8qUKRXeZ9asWZo5c6Y+//xzxcbGasiQIfrmm28kXQ0Hd999t3r16qWdO3cqMzNThYWFGjVqlN0Yq1evlpeXl7Zt26YVK1ZUWt8f//hHvfDCC3r++ee1d+9eDRo0SEOHDtXhw4clSfn5+erevbtmzpyp/Px8/c///E+Vc63O55aTk6NRo0bp4Ycf1r59+zRv3jzNmTNH6enptj6PPvqoTpw4oQ8//FD/+Mc/9Je//EVFRUV24/zyl79UUVGR3nvvPeXk5Kh379665557dPbsWUnSmDFj1L59e3322WfKyclRUlKSPD09q6wduGkZAFAN48ePN4YNG2YYhmH069fPeOyxxwzDMIy3337b+PE/JcnJyUZERITdsS+99JIREhJiN1ZISIhRXl5uawsNDTV+/vOf215///33RtOmTY2///3vhmEYxtGjRw1JRmpqqq3PlStXjPbt2xuLFi0yDMMwFi5caNx33312733ixAlDkpGbm2sYhmEMHDjQ6NWr10/Ot23btsYf/vAHu7bo6GjjySeftL2OiIgwkpOTrztOdT+3X/3qV8a9995rd+ysWbOM8PBwwzAMIzc315BkZGdn2/YfOnTIkGS89NJLhmEYxscff2z4+voaFy9etBunU6dOxsqVKw3DMIxmzZoZ6enpPzF7AFxBAlBjixYt0urVq3Xo0CGHx+jevbvc3H74JyggIEA9e/a0vXZ3d1erVq0qXCGJjY21/beHh4f69Oljq2PPnj368MMPdeutt9q2sLAwSVefF7omKirqurWVlpbq1KlTuuOOO+za77jjjhua8/U+t0OHDlX6focPH1Z5ebkOHTokDw8Pu9rDwsLUvHlz2+s9e/aorKxMrVq1svsMjh49apt/YmKiJk2apPj4eKWmptp9LgB+QEACUGMDBgzQoEGDNHv27Ar73NzcZBiGXduVK1cq9DPf1rFYLJW2Wa3WatdVVlamIUOGaPfu3Xbb4cOHNWDAAFu/pk2bVnvM2nS9z602lJWVKSgoqML8c3NzNWvWLElXnxE7cOCAHnzwQX3wwQcKDw/X22+/XSf1AK7Mw9kFAHBNqampioyMVGhoqF17mzZtVFBQIMMwbF9jr821i/7zn//Yws7333+vnJwcTZ06VZLUu3dvrV27Vh06dJCHh+P/vPn6+qpt27batm2bBg4caGvftm2b+vbte0P1V/W5devWTdu2bbNr27Ztm7p27Sp3d3eFhYXZ5hsdHS1Jys3NVXFxsa1/7969VVBQIA8PD7uH4s26du2qrl276umnn9bo0aO1atUqjRgx4obmBTQ2XEEC4JCePXtqzJgx+tOf/mTXfuedd+r06dN67rnndOTIES1btkzvvfderb3vsmXL9Pbbb+uLL77QU089pXPnzumxxx6TJD311FM6e/asRo8erc8++0xHjhzR+++/rwkTJqi8vLxG7zNr1iwtWrRIGRkZys3NVVJSknbv3q3f/OY3N1R/VZ/bzJkzlZWVpYULF+rLL7/U6tWrtXTpUtvD36GhoRo8eLCmTJmiTz/9VDk5OZo0aZKaNGliGyM+Pl6xsbEaPny4Nm3apP/+97/avn27fv/732vnzp367rvvNHXqVG3ZskXHjh3Ttm3b9Nlnn6lbt243NCegMSIgAXDYggULKtwC69atm/7yl79o2bJlioiIUHZ29nW/4VVTqampSk1NVUREhD755BO98847at26tSTZrvqUl5frvvvuU8+ePTVjxgw1b97c7nmn6pg+fboSExM1c+ZM9ezZU5mZmXrnnXfUpUuXG55DZZ9b79699eabb+qNN95Qjx49NHfuXC1YsECPPvqorc+qVavUtm1bDRw4UCNHjtSvf/1r+fv72/ZbLBa9++67GjBggCZMmKCuXbvq4Ycf1rFjxxQQECB3d3d98803GjdunLp27apRo0bp/vvv1/z58294TkBjYzHMDwsAAADc5LiCBAAAYEJAAgAAMCEgAQAAmBCQAAAATAhIAAAAJgQkAAAAEwISAACACQEJAADAhIAEAABgQkACAAAwISABAACY/H+JDqDXLwtdTgAAAABJRU5ErkJggg==", "text/plain": [ "
" ] @@ -151,26 +156,38 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "Generation: 100%|██████████| 50/50 [02:24<00:00, 2.89s/it]\n" + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/distributed/node.py:182: UserWarning: Port 8787 is already in use.\n", + "Perhaps you already have a cluster running?\n", + "Hosting the HTTP server on port 37681 instead\n", + " warnings.warn(\n", + "/home/ribeirop/common/Projects/TPOT_Dev/tpot2/tpot2/population.py:204: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '['INVALID', 'INVALID', 'INVALID', 'INVALID', 'INVALID', 'INVALID', 'INVALID', 'INVALID', 'INVALID', 'INVALID']' has dtype incompatible with float64, please explicitly cast to a compatible dtype first.\n", + " self.evaluated_individuals.loc[key,column_names] = data\n", + "/home/ribeirop/common/Projects/TPOT_Dev/tpot2/tpot2/population.py:204: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '['INVALID', 'INVALID', 'INVALID', 'INVALID', 'INVALID', 'INVALID', 'INVALID', 'INVALID', 'INVALID', 'INVALID']' has dtype incompatible with float64, please explicitly cast to a compatible dtype first.\n", + " self.evaluated_individuals.loc[key,column_names] = data\n", + "/home/ribeirop/common/Projects/TPOT_Dev/tpot2/tpot2/population.py:381: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value 'ind_crossover' has dtype incompatible with float64, please explicitly cast to a compatible dtype first.\n", + " self.evaluated_individuals.at[new_child.unique_id(),\"Variation_Function\"] = var_op\n", + "Generation: 100%|██████████| 20/20 [00:05<00:00, 3.52it/s]\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/linear_model/_stochastic_gradient.py:1575: ConvergenceWarning: Maximum number of iteration reached before convergence. Consider increasing max_iter to improve the fit.\n", + " warnings.warn(\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "-53.572578179092396\n" + "-6120.015400135764\n" ] }, { "data": { - "image/png": "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", + "image/png": "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oN/85jfuk7gLvfLmH//4hz766CPdc8898vvGCw8sFose7T9AO8+c0eGGBo3qHq63T56U46v73F9fL4fLpZSwMH341XvibKmuVvV5PtQ3Izxcb5WWuv/561fbHmts1NDQUP2sX6wGBQfrxNmzqva3qX9goKZ2765rgoN1tLlZcZGROshz4wAAuGSHjh5VbUuLJKnKbtdpu13HmptV8tXMz+KnErkUExCglLCu2lZTrZKvXuFaZ7e7X+1qtVjcPeBwQ4OOfdUvXC6X9jfUf+/tvzYqvLv+Vl6mRodDToufKqurVVNToxtvvFFvvvmmmpqaJMn9atmuXbuqtrZWkjRixAitW7dOVVVVampq0t///neNHj263f7uvq1dT+zq6ur0X//1Xzpz5owkKS0tTQ899JDmzJmjn//857riiivUs2dPhYaG6tlnn3Xf7q233tLatWvV0NCgIUOG6F//+pd69eqlgKAgOb/x6tUgq1U/7dNXy0tK9Osrr9SJs2f1o7xcOSX17NJFb8Qn6MfRMXp8316Nz81RUmhXRXXposBznKzN7Rer3xw+pAm5ObK7XIoPDdXCq4bozdISbaupkVVSYteuSgkL09ITJ5R16gtZJfXy89PokBAdHThQ6771cuiPPvpIxcXF2r17t1544YXz/j3t2rVLkyZNUnV1tYKCgjRo0CB9+umnrfibBwDg0vTr10/HjxvfLmTdunV6+umn1dLSIovFot///vcaM2aM5syZo+3bt6t///6y2WyaPXu2xo8fr6eeekoffvihMjIy9Kc//UlLlizR66+/rn79+ikiIkK33HKL7r33Xg0ZMkQ7duxQaGioJOmxn/9ci/72NwWePasufn76XVyc5HTp+cOHVGd3SBYpPiRU90bHKNBq1W+ujNNDe/eoxemUxWLRUwOvUL/AQE3qFaXbc3M0vFs33dW7t547dEh1X237Lub2XxsTHqGDDQ26K3+n6vbsUeiWzfrJffdp/PjxysnJUWpqqvz9/TVz5kz993//t+bMmaOxY8dq8ODBysrK0jPPPKMxY8a4XzyRmpqqo0ePXtTP4aqrrtKpU6fU0tKid999V1u3blXfr55SdjHa9cUTbW3dunXa9/HHumnL1ou+jd3lktPlUhc/P+XX1urXhw7q78kpbZqruaVZH11zjf65Z4/7bVsCAgJ08uRJhYeHt+ljAQDgay7n+t5R/nXdtbrq5ps1btw4s6NclHZ/jl1bioqKUk5QkFqsVvlf5LtANzgcyty1S3aXS/5+Fj076Mo2z2UJDJKjRw/NnTtXMTExOnXqlB5//HFKHQAAF+Fyru8docVqVV1QkOE9aT2d1xU7i82mmpAQRX613v0+YTabVqe07Qndt9WEhMhis2n06NGaPHnyeb/u448/1rx58wyzjIwMvfzyy+2aDwAAT3Y51/eO8PX1va2L3enTp79zAhgQEKBt27a1+r69qthFREQoMCREJyMiPOoHf7LHl7nO9bLnb7r55pt18803d1AqAAC8g7df3y9Vjx49Wv0pH+fjVe/PYbValZiaqmOx/eTwkLcWcfj5qbhfPw1LSzvnx4UBAIAL4/redjzjb+8SJCUlqSU4WCciI82OIkk6Hhkpe3Bwu7/hIAAAvozre9vwumIXHh6ugXFxOtg/1vDWJ2ZwWiw61D9WAwcP5oUSAAC0Atf3tuF1xU6SMkaPVl1kpA706WNqjv19+qguMlIZo0aZmgMAAF/A9b31vLLYRUdHa3hGhvbGxelMB3w8x7nUBAdr3+A4jRg1StHR0aZkAADAl3B9bz2vLHbSl28TEt63j3KGDpW9g59oaffzU87VQxXRp4/S09M79LEBAPBlXN9bx2uLnc1m02133KGGmBhti7+6w/bxTotF2+KvVmN0jMbfcYdsNq96xxgAADwa1/fW8dpiJ0m9e/fWpGl3qTI2VlsS4tu92dv9/LQlIV6VsbGaNO0u9e7du10fDwCAzojr++Xzqs+KPZ/i4mKtXvVXBZeWKm3PHoU1NLT5Y9QEByvn6qFqjI7RpGl3qX///m3+GAAA4D+4vl86nyh2klRWVqY1WVmqOlGiIQcOKK6kRH5t8K05LRbt79NH+wbHKaJPH42/4w6vbvIAAHgTru+XxmeKnSTZ7XZt2rRJ2Zs2KbSiQoOKj6lfRYWsTucl35fDz0/HIyN1qH+s6iIjNWLUKKWnp3vtzh0AAG/F9f3i+VSx+1ppaak2b9qkI/v3y9bQoP7Hjyv6dKW61dfL3+E47+1arFbVhIToZI8IFffrJ3twsAYOHqwML33JMwAAvoTr+/fzyWL3taqqKhUUFKggJ0dn6+vlstsV2tiosMoqdbHb5edyymnxU7PNpjMR4aoLCpLFZlNgSIiGpaVp2LBhXveO0wAA+Dqu7+fn08Xuaw6HQ5WVlSovL1d5eblOlZWp+exZOex2WW02dQkMVM/evRUVFaWoqChFRER41Qf+AgDQGXF9/65OUewAAAA6A69+HzsAAAD8B8UOAADAR1DsAAAAfATFDgAAwEdQ7AAAAHwExQ4AAMBHUOwAAAB8BMUOAADAR1DsAAAAfATFDgAAwEdQ7AAAAHwExQ4AAMBHUOwAAAB8BMUOAADAR1DsAAAAfATFDgAAwEdQ7AAAAHwExQ4AAMBHUOwAAAB8BMUOAADAR1DsAAAAfATFDgAAwEdQ7AAAAHwExQ4AAMBHUOwAAAB8BMUOAADAR1DsAAAAfMT/B/LJMi/sbVDNAAAAAElFTkSuQmCC", "text/plain": [ "
" ] @@ -182,33 +199,31 @@ "source": [ "import tpot2\n", "import sklearn.datasets\n", - "from sklearn.linear_model import SGDRegressor\n", - "import numpy as np\n", - "from tpot2.builtin_modules import ZeroTransformer, OneTransformer\n", - "from tpot2.config.regressors import params_SGDRegressor\n", "\n", - "root_config_dict = {SGDRegressor: params_SGDRegressor}\n", - "leaf_config_dict = [\"feature_set_selector\", {ZeroTransformer: {}, OneTransformer: {}}]\n", + "scorer = sklearn.metrics.get_scorer('neg_mean_squared_error')\n", + "X, y = sklearn.datasets.load_diabetes(return_X_y=True)\n", + "X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n", "\n", + "graph_search_space = tpot2.search_spaces.pipelines.GraphPipeline(\n", + " root_search_space= tpot2.config.get_search_space(\"SGDRegressor\"),\n", + " leaf_search_space = tpot2.search_spaces.nodes.FSSNode(subsets=n_features), \n", + " inner_search_space = tpot2.config.get_search_space([\"arithmatic\"]),\n", + " max_size = 10,\n", + ")\n", "\n", - "est = tpot2.TPOTEstimator(population_size=100,generations=50,\n", + "est = tpot2.TPOTEstimator(population_size=10,generations=20, \n", " scorers=['neg_mean_squared_error'],\n", " scorers_weights=[1],\n", " other_objective_functions=[tpot2.objectives.number_of_nodes_objective],\n", " other_objective_functions_weights=[-1],\n", " n_jobs=32,\n", " classification=False,\n", - " inner_config_dict= \"arithmetic_transformer\",\n", - " leaf_config_dict=leaf_config_dict,\n", - " root_config_dict=root_config_dict,\n", + " search_space = graph_search_space ,\n", " verbose=1,\n", - " processes=False,\n", " )\n", "\n", "\n", - "scorer = sklearn.metrics.get_scorer('neg_mean_squared_error')\n", - "X, y = sklearn.datasets.make_regression(n_samples=1000, n_features=100, n_informative=6)\n", - "X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n", + "\n", "est.fit(X_train, y_train)\n", "print(scorer(est, X_test, y_test))\n", "est.fitted_pipeline_.plot()" @@ -216,23 +231,16 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "SGDRegressor_1 : SGDRegressor(alpha=1.6814005088136593e-05, eta0=0.6868335822696461,\n", - " fit_intercept=False, l1_ratio=0.5144783118066449,\n", - " learning_rate='constant', loss='huber', penalty='elasticnet',\n", - " power_t=5.487407069184651)\n", - "FeatureSetSelector_1 : FeatureSetSelector(name='34', sel_subset=[34])\n", - "FeatureSetSelector_2 : FeatureSetSelector(name='17', sel_subset=[17])\n", - "FeatureSetSelector_3 : FeatureSetSelector(name='16', sel_subset=[16])\n", - "FeatureSetSelector_4 : FeatureSetSelector(name='3', sel_subset=[3])\n", - "FeatureSetSelector_5 : FeatureSetSelector(name='19', sel_subset=[19])\n", - "ZeroTransformer_1 : ZeroTransformer()\n" + "SGDRegressor_1 : SGDRegressor()\n", + "FeatureSetSelector_1 : FeatureSetSelector(name='7', sel_subset=[7])\n", + "FeatureSetSelector_2 : FeatureSetSelector(name='7', sel_subset=[7])\n" ] } ], @@ -244,12 +252,12 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 14, "metadata": {}, "outputs": [ { "data": { - "image/png": "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vvmmAgMDJUlnzpzRwIEDlZOTo02bNrm80P+1dOlSpaamqqSkRDVq1NCcOXP05JNPqqCgwF7T2LFjtWTJEuXm5kqS7rnnHp08eVKffvqpfT9du3ZVXFyc5s6dK8Mw1LBhQ6Wlpenxxx+XJNlsNkVGRmrBggW69957K62lpKREJSUl9vWioiLFxMQwBsqLZO35TX3e+P4P+737j65Kala/GioCAFS3Kh8DNXfuXK1YsUKNGjVScnKykpOT1ahRI61YsUJz5851qmgzjh07prffflvdunVTjRo1JElZWVm64YYb7OFJklJSUrRr1y4dP37c3ic5OdlhXykpKcrKypIk5eXlqaCgwKGP1WpVYmKivU9lpkyZIqvVal9iYmJcdqyoHl1i6ynaGqwLPRph0bmn8brE1qvOsgAAHsqpANWlSxft3btXzz77rH0M1OTJk7V371516dLF1TXajRkzRqGhoapfv77y8/MdXlpcUFCgyMhIh/7n1wsKCi7a5/fbf/+5yvpUJj09XTabzb7s37/fySOEu/j7WZTRq7UkVQhR59czerVmPigAgKTLeBdeaGioBg0apJkzZ2rmzJn6xz/+odDQUFP7GDt2rCwWy0WX87ffJGn06NHavHmzVq5cKX9/fz3wwANy4g6kywUFBSksLMxhgfe5rW205twfryhrsEN7lDWYKQwAAA6cmkhz4cKFatCggXr27ClJeuKJJzRv3jy1bt1a77777iU/fZeWlvaHs5o3bdrU/s8NGjRQgwYN1KJFC11zzTWKiYnR999/r6SkJEVFRenw4cMOnz2/HhUVZf+zsj6/336+LTo62qFPXFzcJR0TvNttbaN1a+soZiIHAFyU09MYhISESDo3rujVV1/VtGnT1KBBA40cOfKS9xMeHq5WrVpddPn9mKbfKy8vlyT74O2kpCR98803Onv2rL3PqlWr1LJlS/v7+ZKSkpSZmemwn1WrVikpKUnSuacIo6KiHPoUFRVp3bp19j7wff5+FiU1q6874/6kpGb1CU8AgIoMJ4SEhBg///yzYRiG8cQTTxj9+vUzDMMwcnJyjAYNGjizy4v6/vvvjVdeecXYvHmzsW/fPiMzM9Po1q2b0axZM+P06dOGYRhGYWGhERkZafTr18/Iyckx3nvvPaNmzZrG66+/bt/PmjVrjICAAOOFF14wdu7caWRkZBg1atQwtm/fbu8zdepUo06dOsZ//vMfY9u2bcadd95pxMbGGqdOnbrkem02myHJsNlsrjsJAACgSpn5/XbqClStWrX022+/SZJWrlypW2+9VZIUHBysU6dOuSrb2dWsWVMfffSRbrnlFrVs2VIDBgxQ+/bttXr1agUFBUk697TcypUrlZeXp4SEBKWlpWn8+PEaNGiQfT/dunXTO++8o3nz5qlDhw768MMPtWTJErVt29be54knntCwYcM0aNAgde7cWcXFxVq+fLmCg4Mr1AUAAK5MTs0D1bdvX+Xm5qpjx4569913lZ+fr/r162vp0qUaN26cw8SUVyLehQcAgPep8nmgXnvtNSUlJeno0aNavHix6tc/N7Fgdna2+vTp48wuAQAAvIZTV6Au1T//+U9NmjRJDRo0qKqv8EhcgQIAwPtU+RWoS/XWW2+pqKioKr8CAACg2lVpgKrCi1sAAABuU6UBCgAAwBcRoAAAAEwiQAEAAJhEgAIAADCpSgPU/fffz2P8AADA5wQ4+8HCwkKtX79eR44csb/Y97wHHnhAkjRnzpzLqw4AAMADORWgPvnkE/Xt21fFxcUKCwuTxfL/3lZvsVjsAQoAAMAXOXULLy0tTQ8//LCKi4tVWFio48eP25djx465ukYAAACP4lSAOnDggIYPH66aNWu6uh4AAACP51SASklJ0caNG11dCwAAgFdwagxUz549NXr0aO3YsUPt2rVTjRo1HLb37t3bJcUBAAB4IovhxAvr/PwufOHKYrGorKzssorydmbe5gwAADyDmd9vp65A/e+0BQAAAFcSZiIHAAAwyemJNE+ePKnVq1crPz9fZ86ccdg2fPjwyy4MAADAUzkVoDZv3qzbb79d//3vf3Xy5EnVq1dPv/76q2rWrKmIiAgCFAAA8GlO3cIbOXKkevXqpePHjyskJETff/+9fv75ZyUkJOiFF15wdY0AAAAexakAtWXLFqWlpcnPz0/+/v4qKSlRTEyMpk2bpnHjxrm6RgAAAI/iVICqUaOGfSqDiIgI5efnS5KsVqv279/vuuoAAAA8kFNjoDp27KgNGzbo6quv1o033qjx48fr119/1b///W+1bdvW1TUCAAB4FKeuQD333HOKjo6WJE2ePFl169bVkCFDdPToUc2bN8+lBQIAAHgap2Yix8UxEzkAAN7HzO+30xNplpaW6osvvtDr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DzORe4HVq1dr6NCh6ty5s0pLSzVu3Dh1795dO3bsUGhoqLvL8ymNGjXS1KlTdfXVV8swDC1cuFB33nmnNm/erDZt2ri7PJ+zYcMGvf7662rfvr27S/FZbdq00RdffGFfDwjgr31XO378uK699lrdfPPN+vzzzxUeHq7du3erbt267i7Np2zYsEFlZWX29ZycHN16662666673FIP0xh4oaNHjyoiIkKrV6/WDTfc4O5yfF69evU0ffp0DRgwwN2l+JTi4mLFx8dr9uzZevbZZxUXF6eXXnrJ3WX5lAkTJmjJkiXasmWLu0vxaWPHjtWaNWv07bffuruUK8qIESP06aefavfu3S5/7+yl4BaeF7LZbJLO/bCj6pSVlem9997TyZMnlZSU5O5yfM7QoUPVs2dPJScnu7sUn7Z79241bNhQTZs2Vd++fZWfn+/uknzO0qVL1alTJ911112KiIhQx44d9cYbb7i7LJ925swZvfXWW3r44YfdEp4kbuF5nfLyco0YMULXXnut2rZt6+5yfNL27duVlJSk06dPq1atWvr444/VunVrd5flU9577z1t2rRJGzZscHcpPi0xMVELFixQy5YtdejQIU2cOFHXX3+9cnJyVLt2bXeX5zP27t2rOXPmaNSoURo3bpw2bNig4cOHKzAwUA8++KC7y/NJS5YsUWFhofr37++2GriF52WGDBmizz//XN99950aNWrk7nJ80pkzZ5Sfny+bzaYPP/xQb775plavXk2IcpH9+/erU6dOWrVqlX3s00033cQtvGpQWFioxo0ba+bMmdySdqHAwEB16tRJa9eutbcNHz5cGzZsUFZWlhsr810pKSkKDAzUJ5984rYauIXnRR599FF9+umn+uqrrwhPVSgwMFDNmzdXQkKCpkyZog4dOujll192d1k+Izs7W0eOHFF8fLwCAgIUEBCg1atXa9asWQoICHAYJArXqlOnjlq0aKGffvrJ3aX4lOjo6Ar/B+uaa67hdmkV+fnnn/XFF19o4MCBbq2DW3hewDAMDRs2TB9//LG+/vprxcbGurukK0p5eblKSkrcXYbPuOWWW7R9+3aHtoceekitWrXSmDFj5O/v76bKfF9xcbH27Nmjfv36ubsUn3LttddWmFrmxx9/VOPGjd1UkW+bP3++IiIi1LNnT7fWQYDyAkOHDtU777yj//znP6pdu7YKCgokSVarVSEhIW6uzrekp6erR48euuqqq3TixAm98847+vrrr7VixQp3l+YzateuXWH8XmhoqOrXr8+4Phd7/PHH1atXLzVu3FgHDx5URkaG/P391adPH3eX5lNGjhypbt266bnnntPdd9+t9evXa968eZo3b567S/M55eXlmj9/vh588EG3T8lBgPICc+bMkXRunMjvzZ8/360D6HzRkSNH9MADD+jQoUOyWq1q3769VqxYoVtvvdXdpQGm/fLLL+rTp49+++03hYeH67rrrtP333+v8PBwd5fmUzp37qyPP/5Y6enpmjRpkmJjY/XSSy+pb9++7i7N53zxxRfKz8/Xww8/7O5SGEQOAABgFoPIAQAATCJAAQAAmESAAgAAMIkABQAAYBIBCgAAwCQCFAAAgEkEKAAAAJMIUAAAACYRoAB4vX379slisWjLli3uLsUuNzdXXbt2VXBwsOLi4txSQ5MmTfTSSy+55bsBX0eAAnDZ+vfvL4vFoqlTpzq0L1myRBaLxU1VuVdGRoZCQ0O1a9cuZWZmVtqH8wZ4LwIUAJcIDg7W888/r+PHj7u7FJc5c+aM05/ds2ePrrvuOjVu3Fj169e/YD9fPG/AlYAABcAlkpOTFRUVpSlTplywz4QJEyrcznrppZfUpEkT+3r//v2Vmpqq5557TpGRkapTp44mTZqk0tJSjR49WvXq1VOjRo00f/78CvvPzc1Vt27dFBwcrLZt22r16tUO23NyctSjRw/VqlVLkZGR6tevn3799Vf79ptuukmPPvqoRowYoQYNGiglJaXS4ygvL9ekSZPUqFEjBQUFKS4uTsuXL7dvt1gsys7O1qRJk2SxWDRhwoTLOm+StHjxYrVp00ZBQUFq0qSJZsyY4bD9yJEj6tWrl0JCQhQbG6u33367wj4KCws1cOBAhYeHKywsTH/+85+1detW+/atW7fq5ptvVu3atRUWFqaEhARt3LjxonUBVyoCFACX8Pf313PPPadXXnlFv/zyy2Xt68svv9TBgwf1zTffaObMmcrIyNAdd9yhunXrat26dXrkkUc0ePDgCt8zevRopaWlafPmzUpKSlKvXr3022+/SToXHv785z+rY8eO2rhxo5YvX67Dhw/r7rvvdtjHwoULFRgYqDVr1mju3LmV1vfyyy9rxowZeuGFF7Rt2zalpKSod+/e2r17tyTp0KFDatOmjdLS0nTo0CE9/vjjFzzWSzlv2dnZuvvuu3Xvvfdq+/btmjBhgp5++mktWLDA3qd///7av3+/vvrqK3344YeaPXu2jhw54rCfu+66S0eOHNHnn3+u7OxsxcfH65ZbbtGxY8ckSX379lWjRo20YcMGZWdna+zYsapRo8YFaweuaAYAXKYHH3zQuPPOOw3DMIyuXbsaDz/8sGEYhvHxxx8bv/9rJiMjw+jQoYPDZ1988UWjcePGDvtq3LixUVZWZm9r2bKlcf3119vXS0tLjdDQUOPdd981DMMw8vLyDEnG1KlT7X3Onj1rNGrUyHj++ecNwzCMZ555xujevbvDd+/fv9+QZOzatcswDMO48cYbjY4dO/7h8TZs2NCYPHmyQ1vnzp2Nf/7zn/b1Dh06GBkZGRfdz6Wet/vuu8+49dZbHT47evRoo3Xr1oZhGMauXbsMScb69evt23fu3GlIMl588UXDMAzj22+/NcLCwozTp0877KdZs2bG66+/bhiGYdSuXdtYsGDBHxw9AMMwDK5AAXCp559/XgsXLtTOnTud3kebNm3k5/f//nqKjIxUu3bt7Ov+/v6qX79+hSssSUlJ9n8OCAhQp06d7HVs3bpVX331lWrVqmVfWrVqJenceKXzEhISLlpbUVGRDh48qGuvvdah/dprr72sY77Yedu5c2el37d7926VlZVp586dCggIcKi9VatWqlOnjn1969atKi4uVv369R3OQV5env34R40apYEDByo5OVlTp051OC8AHBGgALjUDTfcoJSUFKWnp1fY5ufnJ8MwHNrOnj1bod//3jayWCyVtpWXl19yXcXFxerVq5e2bNnisOzevVs33HCDvV9oaOgl79OVLnbeXKG4uFjR0dEVjn/Xrl0aPXq0pHNj1H744Qf17NlTX375pVq3bq2PP/64SuoBvF2AuwsA4HumTp2quLg4tWzZ0qE9PDxcBQUFMgzD/pi+K+du+v777+1hqLS0VNnZ2Xr00UclSfHx8Vq8eLGaNGmigADn/+oLCwtTw4YNtWbNGt1444329jVr1qhLly6XVf+Fzts111yjNWvWOLStWbNGLVq0kL+/v1q1amU/3s6dO0uSdu3apcLCQnv/+Ph4FRQUKCAgwGHQ/v9q0aKFWrRooZEjR6pPnz6aP3++/vKXv1zWcQG+iCtQAFyuXbt26tu3r2bNmuXQftNNN+no0aOaNm2a9uzZo9dee02ff/65y773tdde08cff6zc3FwNHTpUx48f18MPPyxJGjp0qI4dO6Y+ffpow4YN2rNnj1asWKGHHnpIZWVlpr5n9OjRev7557Vo0SLt2rVLY8eO1ZYtW/TYY49dVv0XOm9paWnKzMzUM888ox9//FELFy7Uq6++ah+c3rJlS912220aPHiw1q1bp+zsbA0cOFAhISH2fSQnJyspKUmpqalauXKl9u3bp7Vr1+rJJ5/Uxo0bderUKT366KP6+uuv9fPPP2vNmjXasGGDrrnmmss6JsBXEaAAVIlJkyZVuMV2zTXXaPbs2XrttdfUoUMHrV+//qJPqJk1depUTZ06VR06dNB3332npUuXqkGDBpJkv2pUVlam7t27q127dhoxYoTq1KnjMN7qUgwfPlyjRo1SWlqa2rVrp+XLl2vp0qW6+uqrL/sYKjtv8fHxev/99/Xee++pbdu2Gj9+vCZNmqT+/fvb+8yfP18NGzbUjTfeqL/+9a8aNGiQIiIi7NstFouWLVumG264QQ899JBatGihe++9Vz///LMiIyPl7++v3377TQ888IBatGihu+++Wz169NDEiRMv+5gAX2Qx/ndAAgAAAC6KK1AAAAAmEaAAAABMIkABAACYRIACAAAwiQAFAABgEgEKAADAJAIUAACASQQoAAAAkwhQAAAAJhGgAAAATCJAAQAAmPT/AV2tg6OT7T8dAAAAAElFTkSuQmCC", + "image/png": "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48ePV5cuXfT999+rsLBQb775pr7//ntt2rRJGzZscHSNAAAATsWuK1B33XWXduzYocLCQrVs2VJr1qxRQECAMjIyFBER4egabRQUFCg8PFwWi0VZWVk2fZ9//rnatWsnHx8f+fv765FHHtHBgwet/evXr5fFYimxZWdn2xxnzpw5Cg0NlZeXl6KiorRly5YyPScAAOBaTAeoy5cvq1+/frJYLHrnnXe0ZcsWff/991qyZIlatmxZFjXaeOmll1S3bt0S7QcOHFCPHj3UqVMnZWVl6fPPP9epU6f08MMPlxi7d+9enThxwrr9fj1XamqqEhISlJycrG3btun2229XbGysTp48WabnBQAAXIfpAFW1alUtW7asLGr5U6tXr9aaNWv0xhtvlOjLzMxUUVGRxo0bpwYNGqhNmzYaOXKksrKydPnyZZuxAQEBqlOnjnVzc/u/aZg2bZoGDBigp59+Ws2aNdO8efPk7e2td99996p1FRQUKC8vz2YDAACVl1238OLi4rRixQoHl3JtOTk5GjBggBYvXixvb+8S/REREXJzc9OCBQtUVFSk3NxcLV68WDExMapatarN2PDwcAUFBem+++7Txo0bre2XLl1SZmamzQ8iu7m5KSYmRhkZGVetbcKECfLz87NuISEhDjhjAADgrOxaRH7bbbcpJSVFGzduVEREhKpXr27TP2zYMIcU9xvDMBQfH69BgwYpMjLSZl3Tb8LCwrRmzRo99thjGjhwoIqKihQdHa1Vq1ZZxwQFBWnevHmKjIxUQUGB5s+frw4dOmjz5s1q06aNTp06paKiIgUGBtocOzAw0Pqbf1cyatQoJSQkWF/n5eURogAAqMTsClD/+Mc/VLNmTWVmZiozM9Omz2KxlDpAJSUladKkSdccs3v3bq1Zs0b5+fkaNWrUVcdlZ2drwIAB6tu3r3r16qX8/HyNHj1ajz76qNauXSuLxaLGjRurcePG1n3at2+vn376SdOnT9fixYtLVfOVeHp6ytPT0+79AQCAa7ErQB04cMAhb56YmKj4+Phrjqlfv77WrVunjIyMEiElMjJSffr00aJFizRnzhz5+flp8uTJ1v4lS5YoJCREmzdvVrt27a54/LZt2+rrr7+WJN18881yd3dXTk6OzZicnBzVqVPHjjMEAACVkV0BylH8/f3l7+//p+NmzpypcePGWV8fP35c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ySefKD09XcXFxfrhhx80depUSdKJEyckSUFBQZo3b56WLVumZcuWKSQkRB06dNC2bdskSadOnVJRUZECAwNtjh0YGHjNxfETJkyQn5+fdQsJCXHEaQMAACdlV4AaO3as7r//fqWnp+vUqVM6c+aMzVZaSUlJV1zU/fttz549mjVrlvLz822uDP3RgAEDNHToUD300EPy8PBQu3bt1LNnz19P0u3X02zcuLEGDhyoiIgItW/fXu+++67at2+v6dOn2zMNVqNGjVJubq51O3LkyHUdDwAAODe7buHNmzdPCxcu1JNPPnldb56YmKj4+Phrjqlfv77WrVunjIwMeXp62vRFRkaqT58+WrRokSwWiyZNmqTx48crOztb/v7+Sk9Ptx7jatq2bauvv/5aknTzzTfL3d1dOTk5NmNycnJUp06dqx7D09OzRG0AAKDysitAXbp0Se3bt7/uN/f39y/VYw9mzpypcePGWV8fP35csbGxSk1NVVRUlM1Yd3d33XLLLZKkDz74QNHR0dd8j6ysLAUFBUmSPDw8FBERofT0dMXFxUmSiouLlZ6erqFDh5o9PQAAUEnZFaD69++v999/X6+++qqj67miW2+91eZ1jRo1JEkNGjRQcHCwpF/XL3388cfq0KGDLl68qAULFuijjz7Shg0brPvNmDFDYWFhat68uS5evKj58+dr3bp1WrNmjXVMQkKC+vbtq8jISLVt21YzZszQ+fPn9fTTT5fDmQIAAFdgV4C6ePGi/v73v+uLL75Qq1atVLVqVZv+adOmOaQ4sxYtWqSRI0fKMAxFR0dr/fr1atu2rbX/0qVLSkxM1LFjx+Tt7a1WrVrpiy++UMeOHa1jHn/8cf38888aPXq0srOzFR4errS0tBILywEAwI3LYhiGYXan3weOEge0WLRu3brrKsrV5eXlyc/PT7m5ufL19a3ocgAAQCmY+fy26wrUl19+aVdhAAAAlYHdz4ECAAC4URGgAAAATCJAAQAAmESAAgAAMIkABQAAYBIBCgAAwCQCFAAAgEkEKAAAAJMIUAAAACYRoAAAAEwiQAEAAJhEgAIAADCJAAUAAGASAQoAAMAkAhQAAIBJBCgAAACTCFAAAAAmEaAAAABMIkABAACYRIACAAAwiQAFAABgEgEKAADAJAIUAACASQQoAAAAkwhQAAAAJhGgAAAATCJAAQAA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vLwUHh5eITWEhoZqxowZFfLeQGVHgAJw3eLj42WxWDRx4kSb9hUrVshisVRQVRUrOTlZ1atX1969e0v80OlvmDfAdRGgADiEl5eXJk2apDNnzlR0KQ5z6dIlu/f96aefdNddd6levXq66aabrjquMs4bcCMgQAFwiJiYGNWpU0cTJky46pgxY8aUuJ01Y8YMhYaGWl/Hx8crLi5O48ePV2BgoGrWrKmUlBQVFhbqxRdfVO3atRUcHKwFCxaUOP6ePXvUvn17eXl5qUWLFtqwYYNN/65du9SlSxfVqFFDgYGBevLJJ3Xq1Clrf4cOHTR06FANHz5cN998s2JjY694HsXFxUpJSVFwcLA8PT0VHh6utLQ0a7/FYlFmZqZSUlJksVg0ZsyY65o3SVq2bJmaN28uT09PhYaGaurUqTb9J0+eVLdu3VStWjWFhYXpvffeK3GMs2fPqn///vL395evr686deqkHTt2WPt37Nihjh07ysfHR76+voqIiNA333xzzbqAGxUBCoBDuLu7a/z48Zo1a5aOHj16Xcdat26djh8/rq+++krTpk1TcnKyHnroIdWqVUubN2/WoEGDNHDgwBLv8+KLLyoxMVHbt29XdHS0unXrpv/973+Sfg0PnTp1UuvWrfXNN98oLS1NOTk5euyxx2yOsWjRInl4eGjjxo2aN2/eFet78803NXXqVL3xxhv69ttvFRsbq+7du2vfvn2SpBMnTqh58+ZKTEzUiRMnNHLkyKuea2nmLTMzU4899ph69uypnTt3asyYMXr11Ve1cOFC65j4+HgdOXJEX375pT7++GPNnTtXJ0+etDnOX//6V508eVKrV69WZmam2rRpo86dO+v06dOSpD59+ig4OFhbt25VZmamkpKSVLVq1avWDtzQDAC4Tn379jV69OhhGIZhtGvXzujXr59hGIaxfPly4/d/zSQnJxu33367zb7Tp0836tWrZ3OsevXqGUVFRda2xo0bG3fffbf1dWFhoVG9enXjgw8+MAzDMA4cOGBIMiZOnGgdc/nyZSM4ONiYNGmSYRiG8dprrxn333+/zXsfOXLEkGTs3bvXMAzDuPfee43WrVv/6fnWrVvXeP31123a7rjjDuO5556zvr799tuN5OTkax6ntPPWu3dv47777rPZ98UXXzSaNWtmGIZh7N2715BkbNmyxdq/e/duQ5Ixffp0wzAM4z//+Y/h6+trXLx40eY4DRo0MN5++23DMAzDx8fHWLhw4Z+cPQDDMAyuQAFwqEmTJmnRokXavXu33cdo3ry53Nz+76+nwMBAtWzZ0vra3d1dN910U4krLNHR0dZ/r1KliiIjI6117NixQ19++aVq1Khh3Zo0aSLp1/VKv4mIiLhmbXl5eTp+/LjuvPNOm/Y777zzus75WvO2e/fuK77fvn37VFRUpN27d6tKlSo2tTdp0kQ1a9a0vt6xY4fOnTunm266yWYODhw4YD3/hIQE9e/fXzExMZo4caLNvACwRYAC4FD33HOPYmNjNWrUqBJ9bm5uMgzDpu3y5cslxv3xtpHFYrliW3FxcanrOnfunLp166asrCybbd++fbrnnnus46pXr17qYzrStebNEc6dO6egoKAS57937169+OKLkn5do/bdd9/pwQcf1Lp169SsWTMtX768TOoBXF2Vii4AQOUzceJEhYeHq3Hjxjbt/v7+ys7OlmEY1q/pO/LZTf/973+tYaiwsFCZmZkaOnSoJKlNmzZatmyZQkNDVaWK/X/1+fr6qm7dutq4caPuvfdea/vGjRvVtm3b66r/avPWtGlTbdy40aZt48aNatSokdzd3dWkSRPr+d5xxx2SpL179+rs2bPW8W3atFF2draqVKlis2j/jxo1aqRGjRppxIgR6tWrlxYsWKC//OUv13VeQGXEFSgADteyZUv16dNHM2fOtGnv0KGDfv75Z02ePFk//fST5syZo9WrVzvsfefMmaPly5drz549GjJkiM6cOaN+/fpJkoYMGaLTp0+rV69e2rp1q3766Sd9/vnnevrpp1VUVGTqfV588UVNmjRJqamp2rt3r5KSkpSVlaUXXnjhuuq/2rwlJiYqPT1dr732mn744QctWrRIs2fPti5Ob9y4sR544AENHDhQmzdvVmZmpvr3769q1apZjxETE6Po6GjFxcVpzZo1OnjwoDZt2qS//e1v+uabb3ThwgUNHTpU69ev16FDh7Rx40Zt3bpVTZs2va5zAiorAhSAMpGSklLiFlvTpk01d+5czZkzR7fffru2bNlyzW+omTVx4kRNnDhRt99+u77++mt9+umnuvnmmyXJetWoqKhI999/v1q2bKnhw4erZs2aNuutSmPYsGFKSEhQYmKiWrZsqbS0NH366ae67bbbrvscrjRvbdq00dKlS/Xhhx+qRYsWGj16tFJSUhQfH28ds2DBAtWtW1f33nuvHn74YT377LMKCAiw9lssFq1atUr33HOPnn76aTVq1Eg9e/bUoUOHFBgYKHd3d/3vf//TU089pUaNGumxxx5Tly5dNHbs2Os+J6Ayshh/XJAAAACAa+IKFAAAgEkEKAAAAJMIUAAAACYRoAAAAEwiQAEAAJhEgAIAADCJAAUAAGASAQoAAMAkAhQAAIBJBCgAAACTCFAAAAAm/X9rvXat8Ec1MAAAAABJRU5ErkJggg==", "text/plain": [ "
" ] @@ -286,7 +294,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.11" + "version": "3.10.14" }, "orig_nbformat": 4, "vscode": { diff --git a/Tutorial/5_Genetic_Feature_Selection.ipynb b/Tutorial/5_Genetic_Feature_Selection.ipynb new file mode 100644 index 00000000..96bf78b1 --- /dev/null +++ b/Tutorial/5_Genetic_Feature_Selection.ipynb @@ -0,0 +1,596 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Genetic Feature Selection\n", + "\n", + "This example creates a pipeline where the first step selects a subset of features, and the following step is a graph pipeline" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Generation: 0%| | 0/5 [00:00#sk-container-id-1 {\n", + " /* Definition of color scheme common for light and dark mode */\n", + " --sklearn-color-text: black;\n", + " --sklearn-color-line: gray;\n", + " /* Definition of color scheme for unfitted estimators */\n", + " --sklearn-color-unfitted-level-0: #fff5e6;\n", + " --sklearn-color-unfitted-level-1: #f6e4d2;\n", + " --sklearn-color-unfitted-level-2: #ffe0b3;\n", + " --sklearn-color-unfitted-level-3: chocolate;\n", + " /* Definition of color scheme for fitted estimators */\n", + " --sklearn-color-fitted-level-0: #f0f8ff;\n", + " --sklearn-color-fitted-level-1: #d4ebff;\n", + " --sklearn-color-fitted-level-2: #b3dbfd;\n", + " --sklearn-color-fitted-level-3: cornflowerblue;\n", + "\n", + " /* Specific color for light theme */\n", + " --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n", + " --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n", + " --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n", + " --sklearn-color-icon: #696969;\n", + "\n", + " @media (prefers-color-scheme: dark) {\n", + " /* Redefinition of color scheme for dark theme */\n", + " --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n", + " --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n", + " --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n", + " --sklearn-color-icon: #878787;\n", + " }\n", + "}\n", + "\n", + "#sk-container-id-1 {\n", + " color: var(--sklearn-color-text);\n", + "}\n", + "\n", + "#sk-container-id-1 pre {\n", + " padding: 0;\n", + "}\n", + "\n", + "#sk-container-id-1 input.sk-hidden--visually {\n", + " border: 0;\n", + " clip: rect(1px 1px 1px 1px);\n", + " clip: rect(1px, 1px, 1px, 1px);\n", + " height: 1px;\n", + " margin: -1px;\n", + " overflow: hidden;\n", + " padding: 0;\n", + " position: absolute;\n", + " width: 1px;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-dashed-wrapped {\n", + " border: 1px dashed var(--sklearn-color-line);\n", + " margin: 0 0.4em 0.5em 0.4em;\n", + " box-sizing: border-box;\n", + " padding-bottom: 0.4em;\n", + " background-color: var(--sklearn-color-background);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-container {\n", + " /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n", + " but bootstrap.min.css set `[hidden] { display: none !important; }`\n", + " so we also need the `!important` here to be able to override the\n", + " default hidden behavior on the sphinx rendered scikit-learn.org.\n", + " See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n", + " display: inline-block !important;\n", + " position: relative;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-text-repr-fallback {\n", + " display: none;\n", + "}\n", + "\n", + "div.sk-parallel-item,\n", + "div.sk-serial,\n", + "div.sk-item {\n", + " /* draw centered vertical line to link estimators */\n", + " background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n", + " background-size: 2px 100%;\n", + " background-repeat: no-repeat;\n", + " background-position: center center;\n", + "}\n", + "\n", + "/* Parallel-specific style estimator block */\n", + "\n", + "#sk-container-id-1 div.sk-parallel-item::after {\n", + " content: \"\";\n", + " width: 100%;\n", + " border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n", + " flex-grow: 1;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-parallel {\n", + " display: flex;\n", + " align-items: stretch;\n", + " justify-content: center;\n", + " background-color: var(--sklearn-color-background);\n", + " position: relative;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-parallel-item {\n", + " display: flex;\n", + " flex-direction: column;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-parallel-item:first-child::after {\n", + " align-self: flex-end;\n", + " width: 50%;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-parallel-item:last-child::after {\n", + " align-self: flex-start;\n", + " width: 50%;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-parallel-item:only-child::after {\n", + " width: 0;\n", + "}\n", + "\n", + "/* Serial-specific style estimator block */\n", + "\n", + "#sk-container-id-1 div.sk-serial {\n", + " display: flex;\n", + " flex-direction: column;\n", + " align-items: center;\n", + " background-color: var(--sklearn-color-background);\n", + " padding-right: 1em;\n", + " padding-left: 1em;\n", + "}\n", + "\n", + "\n", + "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n", + "clickable and can be expanded/collapsed.\n", + "- Pipeline and ColumnTransformer use this feature and define the default style\n", + "- Estimators will overwrite some part of the style using the `sk-estimator` class\n", + "*/\n", + "\n", + "/* Pipeline and ColumnTransformer style (default) */\n", + "\n", + "#sk-container-id-1 div.sk-toggleable {\n", + " /* Default theme specific background. It is overwritten whether we have a\n", + " specific estimator or a Pipeline/ColumnTransformer */\n", + " background-color: var(--sklearn-color-background);\n", + "}\n", + "\n", + "/* Toggleable label */\n", + "#sk-container-id-1 label.sk-toggleable__label {\n", + " cursor: pointer;\n", + " display: block;\n", + " width: 100%;\n", + " margin-bottom: 0;\n", + " padding: 0.5em;\n", + " box-sizing: border-box;\n", + " text-align: center;\n", + "}\n", + "\n", + "#sk-container-id-1 label.sk-toggleable__label-arrow:before {\n", + " /* Arrow on the left of the label */\n", + " content: \"▸\";\n", + " float: left;\n", + " margin-right: 0.25em;\n", + " color: var(--sklearn-color-icon);\n", + "}\n", + "\n", + "#sk-container-id-1 label.sk-toggleable__label-arrow:hover:before {\n", + " color: var(--sklearn-color-text);\n", + "}\n", + "\n", + "/* Toggleable content - dropdown */\n", + "\n", + "#sk-container-id-1 div.sk-toggleable__content {\n", + " max-height: 0;\n", + " max-width: 0;\n", + " overflow: hidden;\n", + " text-align: left;\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-unfitted-level-0);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-toggleable__content.fitted {\n", + " /* fitted */\n", + " background-color: var(--sklearn-color-fitted-level-0);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-toggleable__content pre {\n", + " margin: 0.2em;\n", + " border-radius: 0.25em;\n", + " color: var(--sklearn-color-text);\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-unfitted-level-0);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-toggleable__content.fitted pre {\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-fitted-level-0);\n", + "}\n", + "\n", + "#sk-container-id-1 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n", + " /* Expand drop-down */\n", + " max-height: 200px;\n", + " max-width: 100%;\n", + " overflow: auto;\n", + "}\n", + "\n", + "#sk-container-id-1 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n", + " content: \"▾\";\n", + "}\n", + "\n", + "/* Pipeline/ColumnTransformer-specific style */\n", + "\n", + "#sk-container-id-1 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n", + " color: var(--sklearn-color-text);\n", + " background-color: var(--sklearn-color-unfitted-level-2);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n", + " background-color: var(--sklearn-color-fitted-level-2);\n", + "}\n", + "\n", + "/* Estimator-specific style */\n", + "\n", + "/* Colorize estimator box */\n", + "#sk-container-id-1 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-unfitted-level-2);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n", + " /* fitted */\n", + " background-color: var(--sklearn-color-fitted-level-2);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-label label.sk-toggleable__label,\n", + "#sk-container-id-1 div.sk-label label {\n", + " /* The background is the default theme color */\n", + " color: var(--sklearn-color-text-on-default-background);\n", + "}\n", + "\n", + "/* On hover, darken the color of the background */\n", + "#sk-container-id-1 div.sk-label:hover label.sk-toggleable__label {\n", + " color: var(--sklearn-color-text);\n", + " background-color: var(--sklearn-color-unfitted-level-2);\n", + "}\n", + "\n", + "/* Label box, darken color on hover, fitted */\n", + "#sk-container-id-1 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n", + " color: var(--sklearn-color-text);\n", + " background-color: var(--sklearn-color-fitted-level-2);\n", + "}\n", + "\n", + "/* Estimator label */\n", + "\n", + "#sk-container-id-1 div.sk-label label {\n", + " font-family: monospace;\n", + " font-weight: bold;\n", + " display: inline-block;\n", + " line-height: 1.2em;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-label-container {\n", + " text-align: center;\n", + "}\n", + "\n", + "/* Estimator-specific */\n", + "#sk-container-id-1 div.sk-estimator {\n", + " font-family: monospace;\n", + " border: 1px dotted var(--sklearn-color-border-box);\n", + " border-radius: 0.25em;\n", + " box-sizing: border-box;\n", + " margin-bottom: 0.5em;\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-unfitted-level-0);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-estimator.fitted {\n", + " /* fitted */\n", + " background-color: var(--sklearn-color-fitted-level-0);\n", + "}\n", + "\n", + "/* on hover */\n", + "#sk-container-id-1 div.sk-estimator:hover {\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-unfitted-level-2);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-estimator.fitted:hover {\n", + " /* fitted */\n", + " background-color: var(--sklearn-color-fitted-level-2);\n", + "}\n", + "\n", + "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n", + "\n", + "/* Common style for \"i\" and \"?\" */\n", + "\n", + ".sk-estimator-doc-link,\n", + "a:link.sk-estimator-doc-link,\n", + "a:visited.sk-estimator-doc-link {\n", + " float: right;\n", + " font-size: smaller;\n", + " line-height: 1em;\n", + " font-family: monospace;\n", + " background-color: var(--sklearn-color-background);\n", + " border-radius: 1em;\n", + " height: 1em;\n", + " width: 1em;\n", + " text-decoration: none !important;\n", + " margin-left: 1ex;\n", + " /* unfitted */\n", + " border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n", + " color: var(--sklearn-color-unfitted-level-1);\n", + "}\n", + "\n", + ".sk-estimator-doc-link.fitted,\n", + "a:link.sk-estimator-doc-link.fitted,\n", + "a:visited.sk-estimator-doc-link.fitted {\n", + " /* fitted */\n", + " border: var(--sklearn-color-fitted-level-1) 1pt solid;\n", + " color: var(--sklearn-color-fitted-level-1);\n", + "}\n", + "\n", + "/* On hover */\n", + "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n", + ".sk-estimator-doc-link:hover,\n", + "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n", + ".sk-estimator-doc-link:hover {\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-unfitted-level-3);\n", + " color: var(--sklearn-color-background);\n", + " text-decoration: none;\n", + "}\n", + "\n", + "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n", + ".sk-estimator-doc-link.fitted:hover,\n", + "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n", + ".sk-estimator-doc-link.fitted:hover {\n", + " /* fitted */\n", + " background-color: var(--sklearn-color-fitted-level-3);\n", + " color: var(--sklearn-color-background);\n", + " text-decoration: none;\n", + "}\n", + "\n", + "/* Span, style for the box shown on hovering the info icon */\n", + ".sk-estimator-doc-link span {\n", + " display: none;\n", + " z-index: 9999;\n", + " position: relative;\n", + " font-weight: normal;\n", + " right: .2ex;\n", + " padding: .5ex;\n", + " margin: .5ex;\n", + " width: min-content;\n", + " min-width: 20ex;\n", + " max-width: 50ex;\n", + " color: var(--sklearn-color-text);\n", + " box-shadow: 2pt 2pt 4pt #999;\n", + " /* unfitted */\n", + " background: var(--sklearn-color-unfitted-level-0);\n", + " border: .5pt solid var(--sklearn-color-unfitted-level-3);\n", + "}\n", + "\n", + ".sk-estimator-doc-link.fitted span {\n", + " /* fitted */\n", + " background: var(--sklearn-color-fitted-level-0);\n", + " border: var(--sklearn-color-fitted-level-3);\n", + "}\n", + "\n", + ".sk-estimator-doc-link:hover span {\n", + " display: block;\n", + "}\n", + "\n", + "/* \"?\"-specific style due to the `` HTML tag */\n", + "\n", + "#sk-container-id-1 a.estimator_doc_link {\n", + " float: right;\n", + " font-size: 1rem;\n", + " line-height: 1em;\n", + " font-family: monospace;\n", + " background-color: var(--sklearn-color-background);\n", + " border-radius: 1rem;\n", + " height: 1rem;\n", + " width: 1rem;\n", + " text-decoration: none;\n", + " /* unfitted */\n", + " color: var(--sklearn-color-unfitted-level-1);\n", + " border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n", + "}\n", + "\n", + "#sk-container-id-1 a.estimator_doc_link.fitted {\n", + " /* fitted */\n", + " border: var(--sklearn-color-fitted-level-1) 1pt solid;\n", + " color: var(--sklearn-color-fitted-level-1);\n", + "}\n", + "\n", + "/* On hover */\n", + "#sk-container-id-1 a.estimator_doc_link:hover {\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-unfitted-level-3);\n", + " color: var(--sklearn-color-background);\n", + " text-decoration: none;\n", + "}\n", + "\n", + "#sk-container-id-1 a.estimator_doc_link.fitted:hover {\n", + " /* fitted */\n", + " background-color: var(--sklearn-color-fitted-level-3);\n", + "}\n", + "
Pipeline(steps=[('maskselector',\n",
+       "                 MaskSelector(mask=array([ True,  True, False, False, False, False, False, False, False,\n",
+       "        True,  True, False,  True, False,  True,  True,  True, False,\n",
+       "       False, False,  True, False, False, False, False,  True, False,\n",
+       "        True,  True,  True,  True,  True,  True,  True,  True, False,\n",
+       "        True,  True, False, False,  True, False,  True,  True, False,\n",
+       "       False,  True, False,  True, False,  True, False,  True, Fa...\n",
+       "        True, False,  True, False,  True,  True,  True, False,  True,\n",
+       "        True,  True,  True,  True,  True,  True, False,  True,  True,\n",
+       "        True, False, False, False,  True,  True, False,  True,  True,\n",
+       "        True,  True, False, False, False,  True, False,  True, False,\n",
+       "        True, False, False,  True, False,  True, False, False, False,\n",
+       "        True]))),\n",
+       "                ('graphpipeline',\n",
+       "                 GraphPipeline(graph=<networkx.classes.digraph.DiGraph object at 0x76ebe05ee590>))])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + ], + "text/plain": [ + "Pipeline(steps=[('maskselector',\n", + " MaskSelector(mask=array([ True, True, False, False, False, False, False, False, False,\n", + " True, True, False, True, False, True, True, True, False,\n", + " False, False, True, False, False, False, False, True, False,\n", + " True, True, True, True, True, True, True, True, False,\n", + " True, True, False, False, True, False, True, True, False,\n", + " False, True, False, True, False, True, False, True, Fa...\n", + " True, False, True, False, True, True, True, False, True,\n", + " True, True, True, True, True, True, False, True, True,\n", + " True, False, False, False, True, True, False, True, True,\n", + " True, True, False, False, False, True, False, True, False,\n", + " True, False, False, True, False, True, False, False, False,\n", + " True]))),\n", + " ('graphpipeline',\n", + " GraphPipeline(graph=))])" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "est.fitted_pipeline_" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAnYAAAHWCAYAAAD6oMSKAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/H5lhTAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAawElEQVR4nO3de5CddZ3n8U9fcuuEhO5EOjcSEBJCgCAXZUxwhAGlSkcUmQV1xmXGkS1ZS6xyiG6tU1vOrmvtArOgM+qUixbLLE4x7mAZL6yzxWjt0iBiMiQIgQQMnXsmoTsJnU4n6cv+AUSBBEPSIfSX1+uvpPuc5/mdc7rq+67nec45DUNDQ0MBAGDEazzWCwAAYHgIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADACii+Vgv4LUwMDCQrq6ubNmyJVu2bMnWzZuzZ/fuDA4MpLGpKWPGjcubpk5Ne3t72tvb09bWlqampmO9bADgFZjvL9cwNDQ0dKwXcbR0d3dn+fLleWTZsvTt2pWh/v5M2L07k7q6Mqq/P41DQxlsaMi+5ubsaGtLz7hxaWhuztjx43PWuefm7LPPTmtr67F+GADAbzDfD65k2G3cuDH333df1qxenVG9vZm1dl2mdXVl0q5dGTUwcND77Wtqyo7x47OprS1rZ52YfS0tOXnOnCx6xzsybdq01/ARAAAvZb7/dqXCrr+/Px0dHXmooyMTtm3LqZ1rM3PbtjQNDr7qbQ00Nmb9lCl5cvas9EyZkrcuWpRFixalufkNcfYaAF43zPdDVybsNm/enB8uWZLu9Rsyb/XqzNmwIY3D8NAGGxqyesaMPD5nTtpmzsh7Lr88U6dOHYYVAwC/jfn+6pQIu87Oznz3rrvSsnFTzlu5MhN7e4d9HztbWrL09NPTO316rrj6qsyePXvY9wEA/Jr5/uqN+LDr7OzMP/zd32Vy59q87bHH0nwYh2UPVX9jYx48Y366Zs3KlR/+8Ih/8QHg9cp8Pzwj+nPsNm/enO/edVfaOtfmdx599Ki+6EnSPDiYt//y0bStXZvv3vX32bx581HdHwC8EZnvh2/Ehl1/f39+uGRJWjZuygWPPTYs59sPRePQUC549LGM27QxP1qyJP39/a/JfgHgjcB8PzIjNuw6OjrSvX5Dzlu58qiX/Es1Dw7mvMdWpmvDhtx///2v6b4BoDLz/ciMyLDbuHFjHuroyLzVq4/KhZSHYlJvb05btTo/v+++bNq06ZisAQAqMd+P3IgMu/vvuy8Ttm3LnA0bjuk65m7YkAnbtqXjvvuO6ToAoALz/ciNuLDr7u7OmtWrc2rn2tfsvPvBNA4N5ZTOtVmzalW6u7uP6VoAYCQz34fHiAu75cuXZ1Rvb2Zu23asl5IkOXHbtjT39mbFihXHeikAMGKZ78NjRIXdwMBAHlm2LLPWrjusrxE5GpoGBzN73bqsWLo0A6/wPXUAwIGZ78PnsMJuypQpR7zjj3/843nqqacO+vtbb701e/fu3f//iy++OF1dXenbtSvTurpedvs/WrEily39Rd63bFk++PA/57GeniNe46Ga9sxz6/rBD36Q97///Vm4cGGWLFmSJPnFL36RxYsXD9u+fv7zn+f888/PqFGj8oMf/GDYtgsAw6mhoSF//ud/vv//N9xwQ26//fYD3vaV5vvh+kpnZ/5248Ykyb9fvSprd+9+1dt4Yb53vcK6rrjiirS2tuYP/uAPDnutw+mYHbG77bbbcsoppxz09y8Nu5/85CfZsmVLhvr7c/xBou2v5p2e7597bj40dVpufHrNEa9x4BDP8Y/r6srunp58/vOfz5IlS/LAAw/kyiuvzLZt23L++efnpptuOuK1vGD69On55je/mQ9/+MPDtk0AGG4TJkzInXfemWefffa33vZA8/1QZ/Ch+NKcuZk1btyrvt+kXbsy1N+fLVu2ZPAgRxI//elP54477jjSJQ6b5uHa0LJly/KJT3wiu3fvzjnnnJNvfOMbGTt2bL73ve9l8eLFmTRpUhYsWJDW1tbcfPPNueiii/LXf/3XOf3003PNNddk2bJlaWpqymc+85n09vZm48aNWbhwYU466aQsWbIkU6ZMyV133ZUJu3fnv3c+nR9u3ZqGJB9sn5o/mTHjRWs5b+LEfGvD+iTP/WHcuGZNHtq5I/sGh3LtzJm5/IQT0jswkBueeCJrdvfm7OMm5mc7tueH556XXz77bL66bm1GNzZmR39//seZZ+Uvnnoyq3t7MzSU3HDSSVnU2pqfbd+e//jUk8ngYBqTfOzsszN+/Pj9a+jv78/3v//9tLa25pvf/Ga+9a1v5ZlnnsmnP/3prFu3Lq2trfnKV76SWbNm5VOf+lQmTpyYZcuWpaurK7fccksWLlx4wOd5zJgxmT59evbu3ZsdO3Zk69atw/USAsCwGT16dK644orceOONuf7669Pb25tnn302W7duzYoVK7J48eL09fXlzDPPzNVXX53xu3pz6YM/y3unTMl927fnhtkn5c9WPZH3velN6di+PSePG5ePzZiZv+x8Otv27s1Nc0/LORMn5uGdO/OlNb/K3sHBjG9qyo1zT8uMsWNftJY/WrEi/+GUU7Kury9fXtuZJOnpH8iMsWPyt2ctyP/r7s5fre3MnsHBzGlpyZfmzM3oxsYs6rgvC3buyNfvuCP33HNP5s2b97LHedFFF+WnP/3pa/GUHpJhC7trrrkmt912Wy644IJcd911+drXvpbrrrsu119/fTo6OjJ16tRceumlOf/88190v4cffjhr1qzJY489liTZsWNHJk2alJtuuin3339/JkyYsP+2Wzdvzprly/PA9u25+y3nZHRjY7bv2/eytfy0qyuXtE1Oknxny+acMHp07n7LOekbGMi/Wr4872htzf/asjkzxo7J1+bPT8f27tz9L1v23/+XPT2559zz0j5mTP7y6adzcVtb/uvc09K1b18+vGJ57jnn3PzNml/lE8cfn/NbWtIzMJANO3ek+yWHaj/2sY/t//cJJ5zwsnW+9Ll4wQc+8IHf8mw/56677jqk2wHAsXDLLbckSb74xS/u/9n111//otusXLkyT65alU/Mn5+BgYG07NmTr0+dmuzpy/b+/pzb0JBrZ8zM4i2b8z83bcy3z1qQju3b84316/L1+Wfk1JaW/N2Cs9PU0JB7n3kmX1u3Lv95zpwDrueSyZNzyeTJGRgayp/88pH86+kz0rVvX25bvz53nHlWxjY15cudT+fvN2/OH02fnu39/XnrlDfluj/7swNG3evRsITd9u3bs2fPnlxwwQVJko9+9KO56aab8nu/93uZN29eZs6cmSS58sor09nZ+aL7vvnNb87GjRvzyU9+Mu9///vz7ne/+6D72bN7d1auX58r26dmdONzZ5GPHzVq/+8/9fjK7B0cTM/AQJacc26SpKO7O6t6e/O9rf+SJOkZ6M+6vr4s2/ls/s3z61p0fGuOb/71U3HuxIlpHzPmuftv785Pu577Q0mS3QMDebq7O2eMGZNvPPNMOvfuzUUTJqR5795Ma2/P6iefPPwnEgDegJ7p6krz85dfXfQbZ7/GNTTknHHjMjg0mFmNjTlz0vFpbGjI3JaWrO/bkyTZ0d+fxaueyNq+vgwODWVS86gD7uM33dL5dM6ccFwunTw5P+l6Jk/07spVK5YnSfYODuaitrYkydjGxrxt+vT09fUN90M+aobtiN2BDB3C+fHW1tY88sgj+dGPfpRbbrkl//iP/5ibb775gLcdHBhIXmGbfzXv9OcOoa75Vb74q6fy1dPnZzDJfzr11Lxt0vEvXd1BtzOu8deXHg4ODeVv5p/xosO6O3fuzB+2tuaClpY80Nubf7thQz47f35OO/XU/N+Ojt/6mAGAX2tI0vD8NWxjfmMGj2po2P/vxjTsP6jT2NCQwefn+JfXduadbW350NRpWbVrV/7d6lWvuK+fdD2Th3c+m9vPOitJMjiUXNTalv8yd+7Lbju2sTGNQ4MZGEHfGzssb544/vjjM2bMmDz00ENJkjvvvDO/+7u/m3nz5uXxxx/Phg0bMjAwkLvvvvtl9922bVsGBwdz1VVX5Qtf+EIefvjhJMlxxx33sgsuG5uactb06fmHLZuz9/k/gJeeim1oaMhnZp+Uh3fuzK96e3Ph8a25c9Om/Rdhrtq1KwNDQzln4sTc8/xn5TywfXu2H+RFW9Tamjuef1dNkjzW05PjjjsumwcGcuqYMfloa2tmjxqVrb296d6+/dU/eQDwBtd+wgkZajx4kjQ2NmbM2DFpOMDvevoH0j76ubNsv3lZ1YGs7+vLjWvW5L/Nm5fm56PxnInH5cEd27Ph+aNyPf3Pndl7wWBDY5qaj+pxsGF1WCvt7u7ef3o1SW666abcfvvtue6669LX15e3vOUtue666zJ27NjceuutufjiizNp0qTMmzcvEydOfNG2NmzYkD/+4z/O4OBgmpubc+uttyZJrr322lx88cWZO3fu/o8OGTNuXM486aTsXv1kPvDwP6e5oSFXntCea17y5olxTU352IyZ+daGDfmLU0/N+r6+fOCfl2UwyZtGj85tZ5yZP5w2PTc88Xjes2xpzp5wXNpHj87YA/xRffLEWfnir57K+5YtTf/QUM6YMCE3nzYv39+zJz/r7k6GhnLa6NGZPXVqfvT8dYIv+Pa3v51x48a96M0T119/fdavX/+yN0+8733vy7vf/e709PTkne98Z5YuXXrA5/7RRx/NRz7ykezYsSNjx47NySefnHvuuefVvoQAcFS9cHAnSZ588slceOGFufXWW/OhD30oy5cvz2c/+9n9b5541yWXZPTPH0pTU1PaT2jP+KamJEljZ2emtk9NQ2Njmg/y3bHXzpyZz61alS93Pp13tLa94pq++y9b0t3fn48/+sskyZkTJuRLc+bmi6fOyaceX5l9g4NpaGjI509+c058/kzd3ubmjH7JmzF+06WXXprly5dn165dmTlzZr7zne/k7W9/+6t+voZLw9ChnC89Aj09PZkwYUIGBgbywQ9+MNdee21+//d//7C2de+99+aJH/8473rgZ0e8rv6hoQwODWV0Y2OWP/ts/uKpJ3P3W845rG3t2bMn//ttb809K1fmn/7pn5Ikzc3N2bRp07B85h8AVDac8324/Z+3/05Ou+yyXHLJJcd6KYfkqB9b/PrXv54777wze/bsyaWXXpr3vve9h72t9vb2LB03LvuamjLqCD8FundgINc88kj6h4YyqrEhXzjl1MPeVmNLSwYnT86f/umf5rjjjsvWrVvzuc99TtQBwCEYzvk+nPY1NaVn3Li0t7cf66UcsqMedosXLx62b15ob29PQ3Nzdowfnyk7dx7RtiY2N+e75xzeEbqX2jF+fBqam/Oud70rH/nIR4Zlmz/+8Y/zuc997kU/W7RoUb761a8Oy/YB4PViOOf7cHphvre3t+eCCy7Inj17XvT7e++9N5MnTz5GqzuwkXM1YJK2traMHT8+m9raXlcv/KbJz62rre2Vz+2/Gpdddlkuu+yyYdseALxejYT5/uCDDx7r5RySY/aVYoejqakpZ517btbOOjEDr/DumdfSQGNjOk88MQvOOy9Nz1/sCQAcOvN9+Lw+nr1X4eyzz86+lpasf51cv7ZuypT0t7RkwYIFx3opADBime/DY8SFXWtra06eMydPzp6VwYYDfaLNa2ewoSFPzZ6Vk+fOTWtr6zFdCwCMZOb78BhxYZcki97xjvRMmZLVL/n8utfaqhkz0jNlShZdeOExXQcAVGC+H7kRGXbTpk3LWxctyuNz5mRnS8sxWcOOlpY8MXdO3nbhhZk2bdoxWQMAVGK+H7kRGXbJcx/90TpzRpaefnr6X+MLLfsbG7N0/ulpmzEjCxcufE33DQCVme9HZsSGXXNzc957+eXpnT49D54x/zU7Hz/Y0JAHz5if3dOm5z2XX57mEfT9cQDweme+H5kRG3ZJMnXq1Fxx9VXpmjUrD5x5xlEv+/7Gxjxw5hnpmjUrV1x9VaZOnXpU9wcAb0Tm++E76t8V+1ro7OzMd+/6+7Rs3JjzVq7MxIN8UfCR2NHSkqXzT8/uadNzxdVXZfbs2cO+DwDg18z3V69E2CXJ5s2b88MlS9K9fkPmrV6dORs2pHEYHtpgQ0NWzZiRJ+bOSduMGXnP5ZeP6JIHgJHEfH91yoRdkvT396ejoyMPdXRkwrZtOaVzbU7cti1Ng4OvelsDjY1ZN2VKnpo9Kz1TpuRtF16YhQsXjthz7gAwUpnvh65U2L1g48aNub+jI2tWrUpzb29mr1uXac90ZdKuXRk1MHDQ++1rasqO8eOzaXJbOk88Mf0tLTl57twsGqFveQaASsz3365k2L2gu7s7K1asyIqlS9O3a1eG+vszYffuTOzqzuj+/jQODWawoTF7m5uzs601PePGpaG5OWPHj8+C887LggULRtwnTgNAdeb7wZUOuxcMDAykq6srW7ZsyZYtW7J18+bs7evLQH9/mpqbM3rs2Lxp6tS0t7envb09bW1tI+oLfwHgjch8f7k3RNgBALwRjOjPsQMA4NeEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAo4v8D/AQZY2yzkYYAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "est.fitted_pipeline_.steps[1][1].plot()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tpot2env", + "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.14" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Tutorial/5_GraphPipeline.ipynb b/Tutorial/6_GraphPipeline.ipynb similarity index 100% rename from Tutorial/5_GraphPipeline.ipynb rename to Tutorial/6_GraphPipeline.ipynb diff --git a/Tutorial/7_dask_parallelization.ipynb b/Tutorial/7_dask_parallelization.ipynb index cbbfc28d..4769c491 100644 --- a/Tutorial/7_dask_parallelization.ipynb +++ b/Tutorial/7_dask_parallelization.ipynb @@ -57,7 +57,27 @@ " scorer = sklearn.metrics.get_scorer('roc_auc_ovr')\n", " X, y = sklearn.datasets.load_digits(return_X_y=True)\n", " X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n", - " est = tpot2.TPOTEstimatorSteadyState( n_jobs=10,memory_limit=\"4GB\", classification=True, max_eval_time_seconds=60, max_time_seconds=120, scorers=['roc_auc_ovr'], scorers_weights=[1], verbose=1)\n", + " \n", + " graph_search_space = tpot2.search_spaces.pipelines.GraphPipeline(\n", + " root_search_space= tpot2.config.get_search_space([\"KNeighborsClassifier\", \"LogisticRegression\", \"DecisionTreeClassifier\"]),\n", + " leaf_search_space = tpot2.config.get_search_space(\"selectors\"), \n", + " inner_search_space = tpot2.config.get_search_space([\"transformers\"]),\n", + " max_size = 10,\n", + " )\n", + "\n", + " est = tpot2.TPOTEstimator(\n", + " scorers = [\"roc_auc\"],\n", + " scorers_weights = [1],\n", + " classification = True,\n", + " cv = 5,\n", + " search_space = graph_search_space,\n", + " population_size= 10,\n", + " generations = 5,\n", + " max_eval_time_seconds = 60*5,\n", + " verbose = 2,\n", + " )\n", + " \n", + " \n", " est.fit(X_train, y_train)\n", " print(scorer(est, X_test, y_test))" ] @@ -106,7 +126,27 @@ "X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n", "\n", "\n", - "est = tpot2.TPOTEstimatorSteadyState( n_jobs=10,memory_limit=\"4GB\", classification=True, max_eval_time_seconds=60, max_time_seconds=120, scorers=['roc_auc_ovr'], scorers_weights=[1], verbose=1)\n", + "graph_search_space = tpot2.search_spaces.pipelines.GraphPipeline(\n", + " root_search_space= tpot2.config.get_search_space([\"KNeighborsClassifier\", \"LogisticRegression\", \"DecisionTreeClassifier\"]),\n", + " leaf_search_space = tpot2.config.get_search_space(\"selectors\"), \n", + " inner_search_space = tpot2.config.get_search_space([\"transformers\"]),\n", + " max_size = 10,\n", + " )\n", + "\n", + "est = tpot2.TPOTEstimator(\n", + " scorers = [\"roc_auc\"],\n", + " scorers_weights = [1],\n", + " classification = True,\n", + " cv = 5,\n", + " search_space = graph_search_space,\n", + " population_size= 10,\n", + " generations = 5,\n", + " max_eval_time_seconds = 60*5,\n", + " verbose = 2,\n", + " n_jobs=10,\n", + " memory_limit=\"4GB\"\n", + ")\n", + "\n", "est.fit(X_train, y_train)\n", "print(scorer(est, X_test, y_test))" ] @@ -214,7 +254,27 @@ } ], "source": [ - "est = tpot2.TPOTEstimatorSteadyState( client=client, classification=True, max_eval_time_seconds=60, max_time_seconds=120, scorers=['roc_auc_ovr'], scorers_weights=[1], verbose=1)\n", + "graph_search_space = tpot2.search_spaces.pipelines.GraphPipeline(\n", + " root_search_space= tpot2.config.get_search_space([\"KNeighborsClassifier\", \"LogisticRegression\", \"DecisionTreeClassifier\"]),\n", + " leaf_search_space = tpot2.config.get_search_space(\"selectors\"), \n", + " inner_search_space = tpot2.config.get_search_space([\"transformers\"]),\n", + " max_size = 10,\n", + " )\n", + "\n", + "est = tpot2.TPOTEstimator(\n", + " client = client,\n", + " scorers = [\"roc_auc\"],\n", + " scorers_weights = [1],\n", + " classification = True,\n", + " cv = 5,\n", + " search_space = graph_search_space,\n", + " population_size= 10,\n", + " generations = 5,\n", + " max_eval_time_seconds = 60*5,\n", + " verbose = 2,\n", + ")\n", + "\n", + "\n", "# this is equivalent to: \n", "# est = tpot2.TPOTClassifier(population_size= 8, generations=5, n_jobs=4, memory_limit=\"4GB\", verbose=1)\n", "est.fit(X_train, y_train)\n", @@ -283,7 +343,25 @@ " threads_per_worker=1,\n", " memory_limit='4GB',\n", ") as cluster, Client(cluster) as client:\n", - " est = tpot2.TPOTEstimatorSteadyState(client=client, n_jobs=10,memory_limit=\"4GB\", classification=True, max_eval_time_seconds=60, max_time_seconds=120, scorers=['roc_auc_ovr'], scorers_weights=[1], verbose=1)\n", + " graph_search_space = tpot2.search_spaces.pipelines.GraphPipeline(\n", + " root_search_space= tpot2.config.get_search_space([\"KNeighborsClassifier\", \"LogisticRegression\", \"DecisionTreeClassifier\"]),\n", + " leaf_search_space = tpot2.config.get_search_space(\"selectors\"), \n", + " inner_search_space = tpot2.config.get_search_space([\"transformers\"]),\n", + " max_size = 10,\n", + " )\n", + "\n", + " est = tpot2.TPOTEstimator(\n", + " client = client,\n", + " scorers = [\"roc_auc\"],\n", + " scorers_weights = [1],\n", + " classification = True,\n", + " cv = 5,\n", + " search_space = graph_search_space,\n", + " population_size= 10,\n", + " generations = 5,\n", + " max_eval_time_seconds = 60*5,\n", + " verbose = 2,\n", + " )\n", " est.fit(X_train, y_train)\n", " print(scorer(est, X_test, y_test))" ] @@ -349,7 +427,26 @@ " X, y = sklearn.datasets.load_digits(return_X_y=True)\n", " X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n", "\n", - " est = tpot2.TPOTEstimatorSteadyState( client=client, classification=True, max_eval_time_seconds=60, max_time_seconds=120, scorers=['roc_auc_ovr'], scorers_weights=[1], verbose=1)\n", + " graph_search_space = tpot2.search_spaces.pipelines.GraphPipeline(\n", + " root_search_space= tpot2.config.get_search_space([\"KNeighborsClassifier\", \"LogisticRegression\", \"DecisionTreeClassifier\"]),\n", + " leaf_search_space = tpot2.config.get_search_space(\"selectors\"), \n", + " inner_search_space = tpot2.config.get_search_space([\"transformers\"]),\n", + " max_size = 10,\n", + " )\n", + "\n", + " est = tpot2.TPOTEstimator(\n", + " client = client,\n", + " scorers = [\"roc_auc\"],\n", + " scorers_weights = [1],\n", + " classification = True,\n", + " cv = 5,\n", + " search_space = graph_search_space,\n", + " population_size= 10,\n", + " generations = 5,\n", + " max_eval_time_seconds = 60*5,\n", + " verbose = 2,\n", + " )\n", + " est.fit(X_train, y_train)\n", " # this is equivalent to: \n", " # est = tpot2.TPOTClassifier(population_size= 8, generations=5, n_jobs=4, memory_limit=\"4GB\", verbose=1)\n", " est.fit(X_train, y_train)\n", diff --git a/Tutorial/6_SH_and_early_termination.ipynb b/Tutorial/8_SH_and_early_termination.ipynb similarity index 97% rename from Tutorial/6_SH_and_early_termination.ipynb rename to Tutorial/8_SH_and_early_termination.ipynb index 1b033644..8b6c2e49 100644 --- a/Tutorial/6_SH_and_early_termination.ipynb +++ b/Tutorial/8_SH_and_early_termination.ipynb @@ -186,26 +186,33 @@ "\n", "X, y = sklearn.datasets.load_iris(return_X_y=True)\n", "\n", - "est = tpot2.TPOTEstimator( \n", - " generations=5,\n", - " scorers=['roc_auc_ovr'],\n", - " scorers_weights=[1],\n", - " classification=True,\n", - " root_config_dict=\"classifiers\",\n", - " inner_config_dict= [\"transformers\"],\n", - " leaf_config_dict=\"selectors\",\n", - " n_jobs=32,\n", - " cv=2,\n", - " max_eval_time_seconds=30,\n", + "graph_search_space = tpot2.search_spaces.pipelines.GraphPipeline(\n", + " root_search_space= tpot2.config.get_search_space([\"KNeighborsClassifier\", \"LogisticRegression\", \"DecisionTreeClassifier\"]),\n", + " leaf_search_space = tpot2.config.get_search_space(\"selectors\"), \n", + " inner_search_space = tpot2.config.get_search_space([\"transformers\"]),\n", + " max_size = 10,\n", + " )\n", + "\n", + "est = tpot2.TPOTEstimator(\n", + " scorers = [\"roc_auc\"],\n", + " scorers_weights = [1],\n", + " classification = True,\n", + " cv = 5,\n", + " search_space = graph_search_space,\n", + " generations = 50,\n", + " max_eval_time_seconds = 60*5,\n", + " verbose = 2,\n", + "\n", + "\n", + " population_size=population_size,\n", + " initial_population_size=initial_population_size,\n", + " population_scaling = population_scaling,\n", + " generations_until_end_population = generations_until_end_population,\n", + " \n", + " budget_range = budget_range,\n", + " generations_until_end_budget=generations_until_end_budget,\n", + " )\n", "\n", - " population_size=population_size,\n", - " initial_population_size=initial_population_size,\n", - " population_scaling = population_scaling,\n", - " generations_until_end_population = generations_until_end_population,\n", - " \n", - " budget_range = budget_range,\n", - " generations_until_end_budget=generations_until_end_budget,\n", - " verbose=0)\n", "\n", "\n", "start = time.time()\n", @@ -296,14 +303,20 @@ } ], "source": [ + "graph_search_space = tpot2.search_spaces.pipelines.GraphPipeline(\n", + " root_search_space= tpot2.config.get_search_space([\"KNeighborsClassifier\", \"LogisticRegression\", \"DecisionTreeClassifier\"]),\n", + " leaf_search_space = tpot2.config.get_search_space(\"selectors\"), \n", + " inner_search_space = tpot2.config.get_search_space([\"transformers\"]),\n", + " max_size = 10,\n", + " )\n", + "\n", + "\n", "est = tpot2.TPOTEstimator( \n", " generations=5,\n", " scorers=['roc_auc_ovr'],\n", " scorers_weights=[1],\n", " classification=True,\n", - " root_config_dict=\"classifiers\",\n", - " inner_config_dict= [\"transformers\"],\n", - " leaf_config_dict=\"selectors\",\n", + " search_space = graph_search_space,\n", " n_jobs=32,\n", " cv=cv,\n", " \n", @@ -369,14 +382,15 @@ } ], "source": [ + "\n", + "\n", + "\n", "est = tpot2.TPOTEstimator( \n", " generations=5,\n", " scorers=['roc_auc_ovr'],\n", " scorers_weights=[1],\n", " classification=True,\n", - " root_config_dict=\"classifiers\",\n", - " inner_config_dict= [\"transformers\"],\n", - " leaf_config_dict=\"selectors\",\n", + " search_space = graph_search_space,\n", " n_jobs=32,\n", " cv=cv,\n", "\n", @@ -447,9 +461,7 @@ " scorers=['roc_auc_ovr'],\n", " scorers_weights=[1],\n", " classification=True,\n", - " root_config_dict=\"classifiers\",\n", - " inner_config_dict= [\"transformers\"],\n", - " leaf_config_dict=\"selectors\",\n", + " search_space = graph_search_space,\n", " n_jobs=32,\n", " cv=cv,\n", "\n", diff --git a/Tutorial/8_Genetic_Algorithm_Overview.ipynb b/Tutorial/9_Genetic_Algorithm_Overview.ipynb similarity index 100% rename from Tutorial/8_Genetic_Algorithm_Overview.ipynb rename to Tutorial/9_Genetic_Algorithm_Overview.ipynb diff --git a/setup.py b/setup.py index f0977acd..7deca183 100644 --- a/setup.py +++ b/setup.py @@ -14,6 +14,7 @@ def calculate_version(): setup( name='TPOT2', + python_requires='<3.12', #for configspace compatibility version=package_version, author='Pedro Ribeiro', packages=find_packages(), @@ -33,7 +34,7 @@ def calculate_version(): 'update_checker>=0.16', 'tqdm>=4.36.1', 'stopit>=1.1.1', - 'pandas>=1.5.3,<2.0.0', + 'pandas>=2.2.1', 'joblib>=1.1.1', 'xgboost>=1.7.0', 'matplotlib>=3.6.2', diff --git a/tpot2/config/autoqtl_builtins.py b/tpot2/config/autoqtl_builtins.py index b317fe70..d649bacd 100644 --- a/tpot2/config/autoqtl_builtins.py +++ b/tpot2/config/autoqtl_builtins.py @@ -6,17 +6,12 @@ from ConfigSpace import ConfigurationSpace from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal -def get_FeatureEncodingFrequencySelector_ConfigurationSpace(): - return ConfigurationSpace( - space = { - 'threshold': Float("threshold", bounds=(0, .35)) - } - ) +FeatureEncodingFrequencySelector_ConfigurationSpace = ConfigurationSpace( + space = { + 'threshold': Float("threshold", bounds=(0, .35)) + } +) -def get_encoder_ConfigurationSpace(): - return ConfigurationSpace( - space = {} - ) # genetic_encoders.DominantEncoder : {}, # genetic_encoders.RecessiveEncoder : {}, diff --git a/tpot2/config/classifiers_sklearnex.py b/tpot2/config/classifiers_sklearnex.py index 939df92f..a158a9a6 100644 --- a/tpot2/config/classifiers_sklearnex.py +++ b/tpot2/config/classifiers_sklearnex.py @@ -1,13 +1,6 @@ -from sklearnex.ensemble import RandomForestClassifier -from sklearnex.neighbors import KNeighborsClassifier -from sklearnex.svm import SVC -from sklearnex.svm import NuSVC -from sklearnex.linear_model import LogisticRegression - -import numpy as np from ConfigSpace import ConfigurationSpace from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal -from functools import partial + def get_RandomForestClassifier_ConfigurationSpace(random_state=None): space = { diff --git a/tpot2/config/get_configspace.py b/tpot2/config/get_configspace.py index 0710c29e..2c4485bf 100644 --- a/tpot2/config/get_configspace.py +++ b/tpot2/config/get_configspace.py @@ -1,3 +1,8 @@ +import importlib.util +import sys +import numpy as np +import warnings + from ..search_spaces.nodes import EstimatorNode from ..search_spaces.pipelines import ChoicePipeline, WrapperPipeline @@ -10,7 +15,14 @@ from . import mdr_configs from . import special_configs -import numpy as np +from . import classifiers_sklearnex +from . import regressors_sklearnex + + + +#autoqtl_builtins +from tpot2.builtin_modules import genetic_encoders +from tpot2.builtin_modules import feature_encoding_frequency_selector from sklearn.linear_model import SGDClassifier from sklearn.ensemble import RandomForestClassifier, ExtraTreesClassifier, GradientBoostingClassifier @@ -80,37 +92,117 @@ from tpot2.builtin_modules import RFE_ExtraTreesClassifier, SelectFromModel_ExtraTreesClassifier, RFE_ExtraTreesRegressor, SelectFromModel_ExtraTreesRegressor +from tpot2.builtin_modules import AddTransformer, mul_neg_1_Transformer, MulTransformer, SafeReciprocalTransformer, EQTransformer, NETransformer, GETransformer, GTTransformer, LETransformer, LTTransformer, MinTransformer, MaxTransformer, ZeroTransformer, OneTransformer, NTransformer + + +#MDR + + all_methods = [SGDClassifier, RandomForestClassifier, ExtraTreesClassifier, GradientBoostingClassifier, MLPClassifier, DecisionTreeClassifier, XGBClassifier, KNeighborsClassifier, SVC, LogisticRegression, LGBMClassifier, LinearSVC, GaussianNB, BernoulliNB, MultinomialNB, ExtraTreesRegressor, RandomForestRegressor, GradientBoostingRegressor, BaggingRegressor, DecisionTreeRegressor, KNeighborsRegressor, XGBRegressor, RFE_ExtraTreesClassifier, SelectFromModel_ExtraTreesClassifier, RFE_ExtraTreesRegressor, SelectFromModel_ExtraTreesRegressor, ZeroCount, OneHotEncoder, ColumnOneHotEncoder, Binarizer, FastICA, FeatureAgglomeration, MaxAbsScaler, MinMaxScaler, Normalizer, Nystroem, PCA, PolynomialFeatures, RBFSampler, RobustScaler, StandardScaler, SelectFwe, SelectPercentile, VarianceThreshold, RFE, SelectFromModel, f_classif, f_regression, SGDRegressor, LinearRegression, Ridge, Lasso, ElasticNet, Lars, LassoLars, LassoLarsCV, RidgeCV, SVR, LinearSVR, AdaBoostRegressor, GradientBoostingRegressor, RandomForestRegressor, BaggingRegressor, ExtraTreesRegressor, DecisionTreeRegressor, KNeighborsRegressor, ElasticNetCV, + AddTransformer, mul_neg_1_Transformer, MulTransformer, SafeReciprocalTransformer, EQTransformer, NETransformer, GETransformer, GTTransformer, LETransformer, LTTransformer, MinTransformer, MaxTransformer, ZeroTransformer, OneTransformer, NTransformer, ] + +#if mdr is installed +if 'mdr' in sys.modules: + from mdr import MDR, ContinuousMDR + all_methods.append(MDR) + all_methods.append(ContinuousMDR) + +if 'skrebate' in sys.modules: + from skrebate import ReliefF, SURF, SURFstar, MultiSURF + all_methods.append(ReliefF) + all_methods.append(SURF) + all_methods.append(SURFstar) + all_methods.append(MultiSURF) + +if 'sklearnex' in sys.modules: + from sklearnex.linear_model import LinearRegression + from sklearnex.linear_model import Ridge + from sklearnex.linear_model import Lasso + from sklearnex.linear_model import ElasticNet + from sklearnex.svm import SVR + from sklearnex.svm import NuSVR + from sklearnex.ensemble import RandomForestRegressor + from sklearnex.neighbors import KNeighborsRegressor + + from sklearnex.ensemble import RandomForestClassifier + from sklearnex.neighbors import KNeighborsClassifier + from sklearnex.svm import SVC + from sklearnex.svm import NuSVC + from sklearnex.linear_model import LogisticRegression + + + all_methods.append(LinearRegression) + all_methods.append(Ridge) + all_methods.append(Lasso) + all_methods.append(ElasticNet) + all_methods.append(SVR) + all_methods.append(NuSVR) + all_methods.append(RandomForestRegressor) + all_methods.append(KNeighborsRegressor) + + all_methods.append(RandomForestClassifier) + all_methods.append(KNeighborsClassifier) + all_methods.append(SVC) + all_methods.append(NuSVC) + all_methods.append(LogisticRegression) + + STRING_TO_CLASS = { t.__name__: t for t in all_methods } + + GROUPNAMES = { "selectors": ["SelectFwe", "SelectPercentile", "VarianceThreshold",], "selectors_classification": ["SelectFwe", "SelectPercentile", "VarianceThreshold", "RFE_classification", "SelectFromModel_classification"], "selectors_regression": ["SelectFwe", "SelectPercentile", "VarianceThreshold", "RFE_regression", "SelectFromModel_regression"], - "classifiers" : ["BernoulliNB", "DecisionTreeClassifier", "ExtraTreesClassifier", "GaussianNB", "GradientBoostingClassifier", "KNeighborsClassifier", "LinearDiscriminantAnalysis", "LinearSVC", "QuadraticDiscriminantAnalysis", "PassiveAggressiveClassifier", "LogisticRegression", "MLPClassifier", "MultinomialNB", "PassiveAggressiveClassifier", "Perceptron", "QuadraticDiscriminantAnalysis", "RandomForestClassifier", "RidgeClassifier", "SGDClassifier", "SVC", "XGBClassifier", "LGBMClassifier"], + "classifiers" : ["LogisticRegression", "DecisionTreeClassifier", "KNeighborsClassifier", "GradientBoostingClassifier", "ExtraTreesClassifier", "RandomForestClassifier", "SGDClassifier", "GaussianNB", "BernoulliNB", "MultinomialNB", "XGBClassifier", "SVC", "MLPClassifier"], + "regressors" : ["ElasticNetCV", "ExtraTreesRegressor", "GradientBoostingRegressor", "AdaBoostRegressor", "DecisionTreeRegressor", "KNeighborsRegressor", "LassoLarsCV", "SVR", "RandomForestRegressor", "RidgeCV", "XGBRegressor", "SGDRegressor" ], "transformers": ["Binarizer", "Normalizer", "PCA", "ZeroCount", "OneHotEncoder", "FastICA", "FeatureAgglomeration", "Nystroem", "RBFSampler"], + "arithmatic": ["AddTransformer", "mul_neg_1_Transformer", "MulTransformer", "SafeReciprocalTransformer", "EQTransformer", "NETransformer", "GETransformer", "GTTransformer", "LETransformer", "LTTransformer", "MinTransformer", "MaxTransformer", "ZeroTransformer", "OneTransformer", "NTransformer"], + "imputers": [], + "skrebate": ["ReliefF", "SURF", "SURFstar", "MultiSURF"], + "genetic_encoders": ["DominantEncoder", "RecessiveEncoder", "HeterosisEncoder", "UnderDominanceEncoder", "OverDominanceEncoder"], + + "classifiers_sklearnex" : ["RandomForestClassifier_sklearnex", "LogisticRegression_sklearnex", "KNeighborsClassifier_sklearnex", "SVC_sklearnex","NuSVC_sklearnex"], + "regressors_sklearnex" : ["LinearRegression_sklearnex", "Ridge_sklearnex", "Lasso_sklearnex", "ElasticNet_sklearnex", "SVR_sklearnex", "NuSVR_sklearnex", "RandomForestRegressor_sklearnex", "KNeighborsRegressor_sklearnex"], } -def get_configspace(name, n_classes=3, n_samples=100, random_state=None): +def get_configspace(name, n_classes=3, n_samples=100, n_features=100, random_state=None): match name: + + #autoqtl_builtins.py + case "FeatureEncodingFrequencySelector": + return autoqtl_builtins.FeatureEncodingFrequencySelector_ConfigurationSpace + case "DominantEncoder": + return {} + case "RecessiveEncoder": + return {} + case "HeterosisEncoder": + return {} + case "UnderDominanceEncoder": + return {} + case "OverDominanceEncoder": + return {} + + #classifiers.py case "LogisticRegression": - return classifiers.get_LogisticRegression_ConfigurationSpace() + return classifiers.get_LogisticRegression_ConfigurationSpace(random_state=random_state) case "KNeighborsClassifier": return classifiers.get_KNeighborsClassifier_ConfigurationSpace(n_samples=n_samples) case "DecisionTreeClassifier": - return classifiers.get_DecisionTreeClassifier_ConfigurationSpace() + return classifiers.get_DecisionTreeClassifier_ConfigurationSpace(random_state=random_state) case "SVC": - return classifiers.get_SVC_ConfigurationSpace() + return classifiers.get_SVC_ConfigurationSpace(random_state=random_state) case "LinearSVC": - return classifiers.get_LinearSVC_ConfigurationSpace() + return classifiers.get_LinearSVC_ConfigurationSpace(random_state=random_state) case "RandomForestClassifier": return classifiers.get_RandomForestClassifier_ConfigurationSpace(random_state=random_state) case "GradientBoostingClassifier": @@ -129,6 +221,8 @@ def get_configspace(name, n_classes=3, n_samples=100, random_state=None): return classifiers.get_BernoulliNB_ConfigurationSpace() case "MultinomialNB": return classifiers.get_MultinomialNB_ConfigurationSpace() + case "GaussianNB": + return {} #transformers.py case "Binarizer": @@ -142,13 +236,13 @@ def get_configspace(name, n_classes=3, n_samples=100, random_state=None): case "OneHotEncoder": return transformers.OneHotEncoder_configspace case "FastICA": - return transformers.get_FastICA_configspace() + return transformers.get_FastICA_configspace(n_features=n_features, random_state=random_state) case "FeatureAgglomeration": - return transformers.get_FeatureAgglomeration_configspace() + return transformers.get_FeatureAgglomeration_configspace(n_features=n_features,) case "Nystroem": - return transformers.get_Nystroem_configspace() + return transformers.get_Nystroem_configspace(n_features=n_features, random_state=random_state) case "RBFSampler": - return transformers.get_RBFSampler_configspace() + return transformers.get_RBFSampler_configspace(n_features=n_features, random_state=random_state) #selectors.py case "SelectFwe": @@ -162,32 +256,113 @@ def get_configspace(name, n_classes=3, n_samples=100, random_state=None): case "SelectFromModel": return selectors.SelectFromModel_configspace_part - return None + #special_configs.py + case "AddTransformer": + return {} + case "mul_neg_1_Transformer": + return {} + case "MulTransformer": + return {} + case "SafeReciprocalTransformer": + return {} + case "EQTransformer": + return {} + case "NETransformer": + return {} + case "GETransformer": + return {} + case "GTTransformer": + return {} + case "LETransformer": + return {} + case "LTTransformer": + return {} + case "MinTransformer": + return {} + case "MaxTransformer": + return {} + case "ZeroTransformer": + return {} + case "OneTransformer": + return {} + case "NTransformer": + return {} + + #imputers.py + + #mdr_configs.py + case "MDR": + return mdr_configs.MDR_configspace + case "ContinuousMDR": + return mdr_configs.MDR_configspace + case "ReliefF": + return mdr_configs.get_skrebate_ReliefF_config_space(n_features=n_features) + case "SURF": + return mdr_configs.get_skrebate_SURF_config_space(n_features=n_features) + case "SURFstar": + return mdr_configs.get_skrebate_SURFstar_config_space(n_features=n_features) + case "MultiSURF": + return mdr_configs.get_skrebate_MultiSURF_config_space(n_features=n_features) + + #classifiers_sklearnex.py + case "RandomForestClassifier_sklearnex": + return classifiers_sklearnex.get_RandomForestClassifier_ConfigurationSpace(random_state=random_state) + case "LogisticRegression_sklearnex": + return classifiers_sklearnex.get_LogisticRegression_ConfigurationSpace(random_state=random_state) + case "KNeighborsClassifier_sklearnex": + return classifiers_sklearnex.get_KNeighborsClassifier_ConfigurationSpace(n_samples=n_samples) + case "SVC_sklearnex": + return classifiers_sklearnex.get_SVC_ConfigurationSpace(random_state=random_state) + case "NuSVC_sklearnex": + return classifiers_sklearnex.get_NuSVC_ConfigurationSpace(random_state=random_state) + + #regressors_sklearnex.py + case "LinearRegression_sklearnex": + return {} + case "Ridge_sklearnex": + return regressors_sklearnex.get_Ridge_ConfigurationSpace(random_state=random_state) + case "Lasso_sklearnex": + return regressors_sklearnex.get_Lasso_ConfigurationSpace(random_state=random_state) + case "ElasticNet_sklearnex": + return regressors_sklearnex.get_ElasticNet_ConfigurationSpace(random_state=random_state) + case "SVR_sklearnex": + return regressors_sklearnex.get_SVR_ConfigurationSpace(random_state=random_state) + case "NuSVR_sklearnex": + return regressors_sklearnex.get_NuSVR_ConfigurationSpace(random_state=random_state) + case "RandomForestRegressor_sklearnex": + return regressors_sklearnex.get_RandomForestRegressor_ConfigurationSpace(random_state=random_state) + case "KNeighborsRegressor_sklearnex": + return regressors_sklearnex.get_KNeighborsRegressor_ConfigurationSpace(n_samples=n_samples) + + return {} -def get_search_space(name, n_classes=3, n_samples=100, random_state=None): - name = GROUPNAMES[name] +def get_search_space(name, n_classes=3, n_samples=100, n_features=100, random_state=None): - if name is None: - return None - - if name not in STRING_TO_CLASS: - return None #if list of names, return a list of EstimatorNodes if isinstance(name, list) or isinstance(name, np.ndarray): - search_spaces = [get_search_space(n, n_classes=n_classes, n_samples=n_samples, random_state=random_state) for n in name] + search_spaces = [get_search_space(n, n_classes=n_classes, n_samples=n_samples, n_features=n_features, random_state=random_state) for n in name] #remove Nones search_spaces = [s for s in search_spaces if s is not None] - return ChoicePipeline(choice_list=search_spaces) - else: - return get_node(name, n_classes=n_classes, n_samples=n_samples, random_state=random_state) + + if name in GROUPNAMES: + name_list = GROUPNAMES[name] + return get_search_space(name_list, n_classes=n_classes, n_samples=n_samples, n_features=n_features, random_state=random_state) + + if name is None: + return None + if name not in STRING_TO_CLASS: + return None + + return get_node(name, n_classes=n_classes, n_samples=n_samples, n_features=n_features, random_state=random_state) -def get_node(name, n_classes=3, n_samples=100, random_state=None): - #these are wrappers +def get_node(name, n_classes=3, n_samples=100, n_features=100, random_state=None): + + #these are wrappers that take in another estimator as a parameter if name == "RFE_classification": rfe_sp = get_configspace(name, n_classes=n_classes, n_samples=n_samples, random_state=random_state) ext = get_node("ExtraTreesClassifier", n_classes=n_classes, n_samples=n_samples, random_state=random_state) @@ -206,5 +381,10 @@ def get_node(name, n_classes=3, n_samples=100, random_state=None): return WrapperPipeline(nodegen=ext, method=SelectFromModel, configspace=sfm_sp) - configspace = get_configspace(name, n_classes=n_classes, n_samples=n_samples, random_state=random_state) + configspace = get_configspace(name, n_classes=n_classes, n_samples=n_samples, n_features=n_features, random_state=random_state) + if configspace is None: + #raise warning + warnings.warn(f"Could not find configspace for {name}") + return None + return EstimatorNode(STRING_TO_CLASS[name], configspace) diff --git a/tpot2/config/mdr_configs.py b/tpot2/config/mdr_configs.py index abfe2a4d..b99ec81e 100644 --- a/tpot2/config/mdr_configs.py +++ b/tpot2/config/mdr_configs.py @@ -11,15 +11,10 @@ } ) -MDR_configspace = ConfigurationSpace( - space = { - 'tie_break': Categorical('tie_break', [0,1]), - 'default_label': Categorical('default_label', [0,1]), - } -) -def get_skrebate_SURF_config_space(n_features=10): + +def get_skrebate_ReliefF_config_space(n_features=10): return ConfigurationSpace( space = { 'n_features_to_select': Integer('n_features_to_select', bounds=(1, n_features), log=True), @@ -28,7 +23,7 @@ def get_skrebate_SURF_config_space(n_features=10): ) -def make_skrebate_SURF_config_space(n_features=10): +def get_skrebate_SURF_config_space(n_features=10): return ConfigurationSpace( space = { 'n_features_to_select': Integer('n_features_to_select', bounds=(1, n_features), log=True), @@ -36,13 +31,13 @@ def make_skrebate_SURF_config_space(n_features=10): ) -def make_skrebate_SURFstar_config_space(n_features=10): +def get_skrebate_SURFstar_config_space(n_features=10): return ConfigurationSpace( space = { 'n_features_to_select': Integer('n_features_to_select', bounds=(1, n_features), log=True), } ) -def make_skrebate_MultiSURF_config_space(n_features=10): +def get_skrebate_MultiSURF_config_space(n_features=10): return ConfigurationSpace( space = { 'n_features_to_select': Integer('n_features_to_select', bounds=(1, n_features), log=True), diff --git a/tpot2/config/regressors_sklearnex.py b/tpot2/config/regressors_sklearnex.py index 298407cb..3473de56 100644 --- a/tpot2/config/regressors_sklearnex.py +++ b/tpot2/config/regressors_sklearnex.py @@ -1,18 +1,3 @@ -from sklearnex.linear_model import LinearRegression -from sklearnex.linear_model import Ridge -from sklearnex.linear_model import Lasso -from sklearnex.linear_model import ElasticNet - -from sklearnex.svm import SVR -from sklearnex.svm import NuSVR - -from sklearnex.ensemble import RandomForestRegressor -from sklearnex.neighbors import KNeighborsRegressor - -import numpy as np - -from functools import partial - from ConfigSpace import ConfigurationSpace from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal @@ -43,9 +28,6 @@ def get_KNeighborsRegressor_ConfigurationSpace(n_samples=100): } ) -LinearRegression_configspace = ConfigurationSpace() - - def get_Ridge_ConfigurationSpace(random_state=None): space = { @@ -108,16 +90,6 @@ def get_SVR_ConfigurationSpace(random_state=None): space = space ) -def params_NuSVR(trial, name=None): - return { - 'nu': trial.suggest_float(f'subsample_{name}', 0.05, 1.0), - 'kernel': trial.suggest_categorical(name=f'kernel_{name}', choices=['poly', 'rbf', 'linear', 'sigmoid']), - 'C': trial.suggest_float(f'C_{name}', 1e-4, 25, log=True), - 'degree': trial.suggest_int(f'degree_{name}', 1, 4), - 'max_iter': 3000, - 'tol': 0.005, - } - def get_NuSVR_ConfigurationSpace(random_state=None): space = { 'nu': Float("nu", bounds=(0.05, 1.0)), diff --git a/tpot2/config/special_configs.py b/tpot2/config/special_configs.py index cdecfe7b..38545f6c 100644 --- a/tpot2/config/special_configs.py +++ b/tpot2/config/special_configs.py @@ -17,21 +17,19 @@ def get_ArithmeticTransformer_ConfigurationSpace(): -# def make_arithmetic_transformer_config_dictionary(): -# return { -# AddTransformer: {}, -# mul_neg_1_Transformer: {}, -# MulTransformer: {}, -# SafeReciprocalTransformer: {}, -# EQTransformer: {}, -# NETransformer: {}, -# GETransformer: {}, -# GTTransformer: {}, -# LETransformer: {}, -# LTTransformer: {}, -# MinTransformer: {}, -# MaxTransformer: {}, -# } +# AddTransformer: {} +# mul_neg_1_Transformer: {} +# MulTransformer: {} +# SafeReciprocalTransformer: {} +# EQTransformer: {} +# NETransformer: {} +# GETransformer: {} +# GTTransformer: {} +# LETransformer: {} +# LTTransformer: {} +# MinTransformer: {} +# MaxTransformer: {} + def get_FeatureSetSelector_ConfigurationSpace(names_list = None, subset_dict=None): diff --git a/tpot2/search_spaces/base.py b/tpot2/search_spaces/base.py index 2c91beb6..88955ba7 100644 --- a/tpot2/search_spaces/base.py +++ b/tpot2/search_spaces/base.py @@ -23,7 +23,7 @@ def export_pipeline(self) -> BaseEstimator: return def unique_id(self): - return + return self class SklearnIndividualGenerator(): diff --git a/tpot2/search_spaces/nodes/__init__.py b/tpot2/search_spaces/nodes/__init__.py index 35cebf87..4026d02c 100644 --- a/tpot2/search_spaces/nodes/__init__.py +++ b/tpot2/search_spaces/nodes/__init__.py @@ -1,2 +1,3 @@ from .estimator_node import * -from .genetic_feature_selection import * \ No newline at end of file +from .genetic_feature_selection import * +from .fss_node import * \ No newline at end of file diff --git a/tpot2/search_spaces/nodes/estimator_node.py b/tpot2/search_spaces/nodes/estimator_node.py index 6bea7615..6e084b59 100644 --- a/tpot2/search_spaces/nodes/estimator_node.py +++ b/tpot2/search_spaces/nodes/estimator_node.py @@ -18,7 +18,7 @@ def __init__(self, method: type, self.space = space if isinstance(space, dict): - self.space = space + self.hyperparameters = space else: rng = np.random.default_rng(rng) self.space.seed(rng.integers(0, 2**32)) diff --git a/tpot2/search_spaces/nodes/fss_node.py b/tpot2/search_spaces/nodes/fss_node.py index e69de29b..fb039e12 100644 --- a/tpot2/search_spaces/nodes/fss_node.py +++ b/tpot2/search_spaces/nodes/fss_node.py @@ -0,0 +1,79 @@ +from numpy import iterable +import tpot2 +import numpy as np +import sklearn +import sklearn.datasets +import numpy as np + +import pandas as pd +import os, os.path +from sklearn.base import BaseEstimator +from sklearn.feature_selection._base import SelectorMixin + +from ..base import SklearnIndividual, SklearnIndividualGenerator + +from ...builtin_modules.feature_set_selector import FeatureSetSelector + +class FSSIndividual(SklearnIndividual): + def __init__( self, + subsets, + rng=None, + ): + + subsets = subsets + rng = np.random.default_rng(rng) + + if isinstance(subsets, str): + df = pd.read_csv(subsets,header=None,index_col=0) + df['features'] = df.apply(lambda x: list([x[c] for c in df.columns]),axis=1) + self.subset_dict = {} + for row in df.index: + self.subset_dict[row] = df.loc[row]['features'] + elif isinstance(subsets, dict): + self.subset_dict = subsets + elif isinstance(subsets, list) or isinstance(subsets, np.ndarray): + self.subset_dict = {str(i):subsets[i] for i in range(len(subsets))} + elif isinstance(subsets, int): + self.subset_dict = {"{0}".format(i):i for i in range(subsets)} + else: + raise ValueError("Subsets must be a string, dictionary, list, int, or numpy array") + + self.names_list = list(self.subset_dict.keys()) + + + self.selected_subset_name = rng.choice(self.names_list) + self.sel_subset = self.subset_dict[self.selected_subset_name] + + + def mutate(self, rng=None): + rng = np.random.default_rng(rng) + self.selected_subset_name = rng.choice(self.names_list) + self.sel_subset = self.subset_dict[self.selected_subset_name] + + + def crossover(self, other, rng=None): + self.selected_subset_name = other.selected_subset_name + self.sel_subset = other.sel_subset + + def export_pipeline(self): + return FeatureSetSelector(sel_subset=self.sel_subset, name=self.selected_subset_name) + + + def unique_id(self): + return self.selected_subset_name + + +class FSSNode(SklearnIndividualGenerator): + def __init__(self, + subsets, + rng=None, + ): + + self.subsets = subsets + self.rng = rng + + def generate(self, rng=None) -> SklearnIndividual: + return FSSIndividual( + subsets=self.subsets, + rng=rng, + ) \ No newline at end of file diff --git a/tpot2/search_spaces/nodes/genetic_feature_selection.py b/tpot2/search_spaces/nodes/genetic_feature_selection.py index 54761123..e51ff8ba 100644 --- a/tpot2/search_spaces/nodes/genetic_feature_selection.py +++ b/tpot2/search_spaces/nodes/genetic_feature_selection.py @@ -151,7 +151,7 @@ def unique_id(self): class GeneticFeatureSelectorNode(SklearnIndividualGenerator): def __init__(self, - mask, + n_features, start_p=0.2, mutation_rate = 0.5, crossover_rate = 0.5, @@ -159,7 +159,7 @@ def __init__(self, crossover_rate_rate = 0, rng=None,): - self.mask = mask + self.n_features = n_features self.start_p = start_p self.mutation_rate = mutation_rate self.crossover_rate = crossover_rate @@ -168,7 +168,7 @@ def __init__(self, self.rng = rng def generate(self, rng=None) -> SklearnIndividual: - return GeneticFeatureSelectorIndividual( mask=self.mask, + return GeneticFeatureSelectorIndividual( mask=self.n_features, start_p=self.start_p, mutation_rate=self.mutation_rate, crossover_rate=self.crossover_rate, diff --git a/tpot2/search_spaces/pipelines/graph.py b/tpot2/search_spaces/pipelines/graph.py index 9332a011..c8a5280f 100644 --- a/tpot2/search_spaces/pipelines/graph.py +++ b/tpot2/search_spaces/pipelines/graph.py @@ -48,7 +48,7 @@ def __init__(self, self.graph.add_edge(self.root, self.leaf) self.mutate_methods_list = [self._mutate_insert_leaf, self._mutate_insert_inner_node, self._mutate_remove_node, self._mutate_node] - self.crossover_methods_list = [self._crossover_swap_branch, self._crossover_swap_node, self._crossover_take_branch] #TODO self._crossover_nodes, + self.crossover_methods_list = [self._crossover_swap_branch,]#[self._crossover_swap_branch, self._crossover_swap_node, self._crossover_take_branch] #TODO self._crossover_nodes, self.merge_duplicated_nodes_toggle = True @@ -461,7 +461,7 @@ def _crossover_swap_leaf_at_node(self, G2, rng=None): - + #TODO edit so that G2 is not modified def _crossover_swap_node(self, G2, rng=None): ''' Swaps randomly chosen node from Parent1 with a randomly chosen node from Parent2. @@ -617,7 +617,7 @@ def plot(self): def unique_id(self): - return + return self class GraphPipeline(SklearnIndividualGenerator): diff --git a/tpot2/search_spaces/pipelines/sequential.py b/tpot2/search_spaces/pipelines/sequential.py index 4459a284..f542c023 100644 --- a/tpot2/search_spaces/pipelines/sequential.py +++ b/tpot2/search_spaces/pipelines/sequential.py @@ -47,7 +47,7 @@ def export_pipeline(self): return sklearn.pipeline.make_pipeline(*[step.export_pipeline() for step in self.pipeline]) def unique_id(self): - return tuple([step.unique_id() for step in self.pipeline]) + return self class SequentialPipeline(SklearnIndividualGenerator): diff --git a/tpot2/tpot_estimator/estimator_utils.py b/tpot2/tpot_estimator/estimator_utils.py index 36e5c53c..c0b79739 100644 --- a/tpot2/tpot_estimator/estimator_utils.py +++ b/tpot2/tpot_estimator/estimator_utils.py @@ -102,7 +102,8 @@ def recursive_with_defaults(config_dict, n_samples, n_features, classification, def objective_function_generator(pipeline, x,y, scorers, cv, other_objective_functions, step=None, budget=None, generation=1, is_classification=True, **pipeline_kwargs): - pipeline = pipeline.export_pipeline(**pipeline_kwargs) + #pipeline = pipeline.export_pipeline(**pipeline_kwargs) + pipeline = pipeline.export_pipeline() if budget is not None and budget < 1: if is_classification: x,y = sklearn.utils.resample(x,y, stratify=y, n_samples=int(budget*len(x)), replace=False, random_state=1) diff --git a/tpot2/tpot_estimator/templates/tpottemplates.py b/tpot2/tpot_estimator/templates/tpottemplates.py index 6da52dad..36b43c66 100644 --- a/tpot2/tpot_estimator/templates/tpottemplates.py +++ b/tpot2/tpot_estimator/templates/tpottemplates.py @@ -13,14 +13,7 @@ def __init__( self, other_objective_functions_weights = [], objective_function_names = None, bigger_is_better = True, - max_size = np.inf, - linear_pipeline = False, - root_config_dict= 'Auto', - inner_config_dict=["selectors", "transformers"], - leaf_config_dict= None, - cross_val_predict_cv = 0, categorical_features = None, - subsets = None, memory = None, preprocessing = False, max_time_seconds=3600, @@ -38,6 +31,15 @@ def __init__( self, """ See TPOTEstimator for documentation """ + + search_space = tpot2.search_spaces.pipelines.GraphPipeline( + root_search_space= tpot2.config.get_search_space("regressors"), + leaf_search_space = None, + inner_search_space = tpot2.config.get_search_space(["selectors","transformers","regressors"]), + max_size = 10, + ) + + super(TPOTRegressor,self).__init__( scorers=scorers, scorers_weights=scorers_weights, @@ -46,14 +48,10 @@ def __init__( self, other_objective_functions_weights = other_objective_functions_weights, objective_function_names = objective_function_names, bigger_is_better = bigger_is_better, - max_size = max_size, - linear_pipeline = linear_pipeline, - root_config_dict = root_config_dict, - inner_config_dict=inner_config_dict, - leaf_config_dict= leaf_config_dict, - cross_val_predict_cv = cross_val_predict_cv, + + search_space=search_space, + categorical_features = categorical_features, - subsets = subsets, memory = memory, preprocessing = preprocessing, max_time_seconds=max_time_seconds, @@ -79,14 +77,7 @@ def __init__( self, other_objective_functions_weights = [], objective_function_names = None, bigger_is_better = True, - max_size = np.inf, - linear_pipeline = False, - root_config_dict= 'Auto', - inner_config_dict=["selectors", "transformers"], - leaf_config_dict= None, - cross_val_predict_cv = 0, categorical_features = None, - subsets = None, memory = None, preprocessing = False, max_time_seconds=3600, @@ -105,6 +96,15 @@ def __init__( self, """ See TPOTEstimator for documentation """ + + search_space = tpot2.search_spaces.pipelines.GraphPipeline( + root_search_space= tpot2.config.get_search_space("classifiers"), + leaf_search_space = None, + inner_search_space = tpot2.config.get_search_space(["selectors","transformers","classifiers"]), + max_size = 10, + ) + + super(TPOTClassifier,self).__init__( scorers=scorers, scorers_weights=scorers_weights, @@ -113,14 +113,8 @@ def __init__( self, other_objective_functions_weights = other_objective_functions_weights, objective_function_names = objective_function_names, bigger_is_better = bigger_is_better, - max_size = max_size, - linear_pipeline = linear_pipeline, - root_config_dict = root_config_dict, - inner_config_dict=inner_config_dict, - leaf_config_dict= leaf_config_dict, - cross_val_predict_cv = cross_val_predict_cv, + search_space=search_space, categorical_features = categorical_features, - subsets = subsets, memory = memory, preprocessing = preprocessing, max_time_seconds=max_time_seconds, From 974582c064b7cd8542f7808424049f49edbc957e Mon Sep 17 00:00:00 2001 From: perib Date: Tue, 26 Mar 2024 19:15:47 -0700 Subject: [PATCH 5/7] edits to tutorials --- Tutorial/1_Estimators_Overview.ipynb | 129 +++++++++--------- Tutorial/2_Search_Spaces.ipynb | 192 ++++++++++++--------------- 2 files changed, 148 insertions(+), 173 deletions(-) diff --git a/Tutorial/1_Estimators_Overview.ipynb b/Tutorial/1_Estimators_Overview.ipynb index 33a71097..40651551 100644 --- a/Tutorial/1_Estimators_Overview.ipynb +++ b/Tutorial/1_Estimators_Overview.ipynb @@ -20,27 +20,60 @@ "2. `tpot2.TPOTRegressor` for regression tasks" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Scorers, Objective Functions, and multi objective optimization.\n", + "\n", + "There are two ways of passing objectives into TPOT2. \n", + "\n", + "1. `scorers`: Scorers are functions that have the signature (estimator, X, y). These can be produced with the [sklearn.metrics.make_scorer](https://scikit-learn.org/stable/modules/generated/sklearn.metrics.make_scorer.html) function. This function is used to evaluate the test folds during cross validation. These are passed into TPOT2 via the scorers parameter. This can take in the scorer itself or the string corresponding to a scoring function ([as listed here](https://scikit-learn.org/stable/modules/model_evaluation.html)). TPOT2 also supports passing in a list of several scorers for multiobjective optimization. \n", + "\n", + "2. `other_objective_functions` : Other objective functions in TPOT2 have the signature (estimator) and returns a float or list of floats. These get passed an unfitted estimator (in the case of TPOT2, a `tpot2.GraphPipeline`). \n", + "\n", + "\n", + "Each scorer and objective function must be accompanied by a list of weights corresponding to the list of objectives. By default, TPOT2 maximizes objective functions (this can be changed by `bigger_is_better=False`). Positive weights means that TPOT2 will seek to maximize that objective, and negative weights correspond to minimization.\n", + "\n", + "Here is an example of using two scorers\n", + "\n", + " scorers=['roc_auc_ovr',tpot2.objectives.complexity_scorer],\n", + " scorers_weights=[1,-1],\n", + "\n", + "\n", + "Here is an example with a scorer and a secondary objective function\n", + "\n", + " scorers=['roc_auc_ovr'],\n", + " scorers_weights=[1],\n", + " other_objective_functions=[tpot2.objectives.number_of_leaves_objective],\n", + " other_objective_functions_weights=[-1]," + ] + }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "Generation: : 0it [00:00, ?it/s]/home/ribeirop/common/Projects/TPOT_Dev/tpot2/tpot2/population.py:204: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '[0.9910779766159422, 0.9164180506462885, 0.9842489682733572, 0.99664936783213, 0.9913591306204854, 0.9785097508524515, 0.9843199854934415, 0.9981583597446381, 0.99559929270021, 0.9511441780591989, 0.9984744292898663, 0.9974402833866118, 0.9914842901220224, 0.9969100719668479, 0.9909145271063142, 0.9910709279190263, 0.9411694123791475, 0.9910354966095938, 0.9776626614599555, 0.9911887873368403, 0.9966903342486351, 0.9988343538601064, 0.9735967719140286, 0.9968575356141441, 0.9958485748358322, 0.9992471065344972, 0.9605917171252578, 0.9904942837739565, 0.9974574181131549, 0.9996403182930008, 0.9694102480973864, 0.9984821310846055, 0.9940551825220357, 0.9837735643634151, 0.9671044961833003, 0.9913835311537978, 0.9989793765342894, 0.9997847101769164, 0.991564988067797, 0.9988538844163573, 0.9895795999679059, 0.9750578580595717, 0.9971245111678281, 0.997177499370075, 0.9988702870584362, 'INVALID', 0.9131272065575761, 'INVALID', 0.9969386481385651, 'INVALID']' has dtype incompatible with float64, please explicitly cast to a compatible dtype first.\n", + "Generation: : 0it [00:00, ?it/s]/home/ribeirop/common/Projects/TPOT_Dev/tpot2/tpot2/population.py:204: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value '[0.9987673084964015, 0.9847336570645737, 0.9905041856662168, 0.9993763441765191, 0.9907964721472181, 0.9972029119373254, 0.998357673885215, 0.9990613894532178, 0.9694329382784964, 0.997532770251522, 0.9975246891516141, 0.9904661635101079, 0.5, 'INVALID', 0.9948797140575193, 0.5, 0.9849666992163886, 0.9824262822238007, 0.9985479308254266, 0.9972915114620106, 0.9694329382784964, 'INVALID', 0.9324417946451, 0.9901685392720255, 0.9978183481741485, 0.9973644125394717, 0.9905750509316356, 0.9819493383116706, 0.9699621501061083, 0.6072655018077077, 0.9694329382784964, 0.9838996235635504, 0.982122385127114, 0.9901266523287818, 0.9301526525124777, 0.9720743554304064, 0.994576960473181, 0.5, 0.5, 0.9948330115435265, 0.9990358447113457, 0.9945434259359371, 0.9375978779782033, 0.9993887714241577, 0.997164111114518, 'INVALID', 'INVALID', 0.9493406027781374, 0.9767172486252121, 0.9974530907820837]' has dtype incompatible with float64, please explicitly cast to a compatible dtype first.\n", " self.evaluated_individuals.loc[key,column_names] = data\n", - "Generation: : 1it [00:15, 15.14s/it]/home/ribeirop/common/Projects/TPOT_Dev/tpot2/tpot2/population.py:381: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value 'ind_crossover' has dtype incompatible with float64, please explicitly cast to a compatible dtype first.\n", + "Generation: : 1it [00:14, 14.92s/it]/home/ribeirop/common/Projects/TPOT_Dev/tpot2/tpot2/population.py:381: FutureWarning: Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value 'ind_mutate' has dtype incompatible with float64, please explicitly cast to a compatible dtype first.\n", " self.evaluated_individuals.at[new_child.unique_id(),\"Variation_Function\"] = var_op\n", - "Generation: : 3it [01:30, 30.07s/it]\n" + "Generation: : 4it [01:19, 19.94s/it]\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/feature_selection/_univariate_selection.py:112: UserWarning: Features [ 0 32 39] are constant.\n", + " warnings.warn(\"Features %s are constant.\" % constant_features_idx, UserWarning)\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/feature_selection/_univariate_selection.py:113: RuntimeWarning: invalid value encountered in divide\n", + " f = msb / msw\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "0.9998423966736188\n" + "0.9999588595957566\n" ] } ], @@ -125,19 +158,15 @@ "import numpy as np\n", "\n", "if __name__==\"__main__\":\n", - " scorer = sklearn.metrics.get_scorer('roc_auc_ovr')\n", + " scorer = sklearn.metrics.get_scorer('roc_auc_ovo')\n", " X, y = sklearn.datasets.load_digits(return_X_y=True)\n", " X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25)\n", - " est = tpot2.TPOTEstimatorSteadyState( \n", - " scorers=['roc_auc_ovr'], #scorers can be a list of strings or a list of scorers. These get evaluated during cross validation. \n", - " scorers_weights=[1],\n", "\n", - " classification=True,\n", "\n", - " max_eval_time_seconds=15,\n", - " max_time_seconds=30,\n", - " verbose=2)\n", + " est = tpot2.TPOTClassifier(n_jobs=4, max_time_seconds=60, verbose=2)\n", " est.fit(X_train, y_train)\n", + "\n", + "\n", " print(scorer(est, X_test, y_test))" ] }, @@ -174,13 +203,6 @@ " \n", " bigger_is_better : bool, default=True\n", " If True, the objective function is maximized. If False, the objective function is minimized. Use negative weights to reverse the direction.\n", - "\n", - " \n", - " max_size : int, default=np.inf\n", - " The maximum number of nodes of the pipelines to be generated.\n", - " \n", - " linear_pipeline : bool, default=False\n", - " If True, the pipelines generated will be linear. If False, the pipelines generated will be directed acyclic graphs.\n", " \n", " generations : int, default=50\n", " Number of generations to run\n", @@ -207,13 +229,19 @@ " 4. warnings\n", " >=5. full warnings trace\n", " 6. evaluations progress bar. (Temporary: This used to be 2. Currently, using evaluation progress bar may prevent some instances were we terminate a generation early due to it reaching max_time_seconds in the middle of a generation OR a pipeline failed to be terminated normally and we need to manually terminate it.)\n", - " \n", + " \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# TPOTEstimator and TPOTEstimatorSteadyState\n", "\n", - "The following configuration dictionaries are covered in the next tutorial:\n", + "TPOTEstimator and TPOTEstimatorSteadyState expose more parameters for customizing search spaces and evolutionary algorithms. The next tutorial will cover customizing search spaces in more detail.\n", "\n", - " root_config_dict\n", - " inner_config_dict\n", - " leaf_config_dict" + "The TPOTClassifier and TPOTRegressor set default parameters for the TPOTEstimator for Classification and Regression.\n", + "In the future, a metalearner will be used to predict the best values for a given dataset." ] }, { @@ -233,7 +261,16 @@ "import sklearn\n", "import sklearn.datasets\n", "\n", + "\n", + "graph_search_space = tpot2.search_spaces.pipelines.GraphPipeline(\n", + " root_search_space= tpot2.config.get_search_space([\"KNeighborsClassifier\", \"LogisticRegression\", \"DecisionTreeClassifier\"]),\n", + " leaf_search_space = tpot2.config.get_search_space(\"selectors\"), \n", + " inner_search_space = tpot2.config.get_search_space([\"transformers\"]),\n", + " max_size = 10,\n", + ")\n", + "\n", "est = tpot2.TPOTEstimatorSteadyState( \n", + " search_space = graph_search_space,\n", " scorers=['roc_auc_ovr'], #scorers can be a list of strings or a list of scorers. These get evaluated during cross validation. \n", " scorers_weights=[1],\n", "\n", @@ -271,35 +308,6 @@ "est.evaluated_individuals" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Scorers, Objective Functions, and multi objective optimization.\n", - "\n", - "There are two ways of passing objectives into TPOT2. \n", - "\n", - "1. `scorers`: Scorers are functions that have the signature (estimator, X, y). These can be produced with the [sklearn.metrics.make_scorer](https://scikit-learn.org/stable/modules/generated/sklearn.metrics.make_scorer.html) function. This function is used to evaluate the test folds during cross validation. These are passed into TPOT2 via the scorers parameter. This can take in the scorer itself or the string corresponding to a scoring function ([as listed here](https://scikit-learn.org/stable/modules/model_evaluation.html)). TPOT2 also supports passing in a list of several scorers for multiobjective optimization. \n", - "\n", - "2. `other_objective_functions` : Other objective functions in TPOT2 have the signature (estimator) and returns a float or list of floats. These get passed an unfitted estimator (in the case of TPOT2, a `tpot2.GraphPipeline`). \n", - "\n", - "\n", - "Each scorer and objective function must be accompanied by a list of weights corresponding to the list of objectives. By default, TPOT2 maximizes objective functions (this can be changed by `bigger_is_better=False`). Positive weights means that TPOT2 will seek to maximize that objective, and negative weights correspond to minimization.\n", - "\n", - "Here is an example of using two scorers\n", - "\n", - " scorers=['roc_auc_ovr',tpot2.objectives.complexity_scorer],\n", - " scorers_weights=[1,-1],\n", - "\n", - "\n", - "Here is an example with a scorer and a secondary objective function\n", - "\n", - " scorers=['roc_auc_ovr'],\n", - " scorers_weights=[1],\n", - " other_objective_functions=[tpot2.objectives.number_of_leaves_objective],\n", - " other_objective_functions_weights=[-1]," - ] - }, { "cell_type": "code", "execution_count": null, @@ -311,6 +319,7 @@ "import sklearn.datasets\n", "\n", "est = tpot2.TPOTEstimatorSteadyState( \n", + " search_space = graph_search_space,\n", " scorers=['roc_auc_ovr',tpot2.objectives.complexity_scorer],\n", " scorers_weights=[1,-1],\n", "\n", @@ -404,7 +413,9 @@ "import sklearn\n", "import sklearn.datasets\n", "\n", - "est = tpot2.TPOTEstimator( population_size=30,\n", + "est = tpot2.TPOTEstimator( \n", + " search_space = graph_search_space,\n", + " population_size=30,\n", " generations=5,\n", " scorers=['roc_auc_ovr'], #scorers can be a list of strings or a list of scorers. These get evaluated during cross validation. \n", " scorers_weights=[1],\n", @@ -425,14 +436,6 @@ "est.fit(X_train, y_train)\n", "print(scorer(est, X_test, y_test))" ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The TPOTClassifier and TPOTRegressor are set default parameters for the TPOTEstimator for Classification and Regression.\n", - "In the future, a metalearner will be used to predict the best values for a given dataset." - ] } ], "metadata": { diff --git a/Tutorial/2_Search_Spaces.ipynb b/Tutorial/2_Search_Spaces.ipynb index 940509de..c4aa8ab2 100644 --- a/Tutorial/2_Search_Spaces.ipynb +++ b/Tutorial/2_Search_Spaces.ipynb @@ -31,7 +31,7 @@ "output_type": "stream", "text": [ "sampled hyperparameters\n", - "{'metric': 'euclidean', 'n_jobs': 1, 'n_neighbors': 1, 'p': 3, 'weights': 'uniform'}\n" + "{'metric': 'euclidean', 'n_jobs': 1, 'n_neighbors': 5, 'p': 3, 'weights': 'uniform'}\n" ] } ], @@ -154,9 +154,9 @@ "output_type": "stream", "text": [ "sampled hyperparameters\n", - "{'metric': 'euclidean', 'n_jobs': 1, 'n_neighbors': 1, 'p': 1, 'weights': 'uniform'}\n", + "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 6, 'p': 2, 'weights': 'uniform'}\n", "mutated hyperparameters\n", - "{'metric': 'euclidean', 'n_jobs': 1, 'n_neighbors': 7, 'p': 1, 'weights': 'distance'}\n" + "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 4, 'p': 3, 'weights': 'distance'}\n" ] } ], @@ -187,14 +187,14 @@ "output_type": "stream", "text": [ "original hyperparameters for individual 1\n", - "{'metric': 'euclidean', 'n_jobs': 1, 'n_neighbors': 7, 'p': 1, 'weights': 'distance'}\n", + "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 7, 'p': 1, 'weights': 'uniform'}\n", "original hyperparameters for individual 2\n", - "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 5, 'p': 3, 'weights': 'uniform'}\n", + "{'metric': 'euclidean', 'n_jobs': 1, 'n_neighbors': 5, 'p': 2, 'weights': 'distance'}\n", "\n", "post crossover hyperparameters for individual 1\n", - "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 5, 'p': 1, 'weights': 'uniform'}\n", + "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 7, 'p': 2, 'weights': 'uniform'}\n", "post crossover hyperparameters for individual 2\n", - "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 5, 'p': 3, 'weights': 'uniform'}\n" + "{'metric': 'euclidean', 'n_jobs': 1, 'n_neighbors': 5, 'p': 2, 'weights': 'distance'}\n" ] } ], @@ -637,10 +637,10 @@ " /* fitted */\n", " background-color: var(--sklearn-color-fitted-level-3);\n", "}\n", - "
KNeighborsClassifier(n_jobs=1, p=1)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + "
KNeighborsClassifier(n_jobs=1, n_neighbors=7)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ - "KNeighborsClassifier(n_jobs=1, p=1)" + "KNeighborsClassifier(n_jobs=1, n_neighbors=7)" ] }, "execution_count": 5, @@ -676,7 +676,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 6, @@ -1194,13 +1194,10 @@ " /* fitted */\n", " background-color: var(--sklearn-color-fitted-level-3);\n", "}\n", - "
LogisticRegression(C=0.5857355940220703, class_weight='balanced', dual=True,\n",
-       "                   max_iter=1000, n_jobs=1, penalty='l1', solver='saga')
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + "
KNeighborsClassifier(metric='euclidean', n_jobs=1, n_neighbors=3)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ - "LogisticRegression(C=0.5857355940220703, class_weight='balanced', dual=True,\n", - " max_iter=1000, n_jobs=1, penalty='l1', solver='saga')" + "KNeighborsClassifier(metric='euclidean', n_jobs=1, n_neighbors=3)" ] }, "execution_count": 7, @@ -1634,13 +1631,10 @@ " /* fitted */\n", " background-color: var(--sklearn-color-fitted-level-3);\n", "}\n", - "
LogisticRegression(C=2.032340407557342, class_weight='balanced', max_iter=1000,\n",
-       "                   n_jobs=1)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + "
KNeighborsClassifier(metric='euclidean', n_jobs=1, n_neighbors=1, p=1)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ - "LogisticRegression(C=2.032340407557342, class_weight='balanced', max_iter=1000,\n", - " n_jobs=1)" + "KNeighborsClassifier(metric='euclidean', n_jobs=1, n_neighbors=1, p=1)" ] }, "execution_count": 8, @@ -2091,13 +2085,13 @@ " /* fitted */\n", " background-color: var(--sklearn-color-fitted-level-3);\n", "}\n", - "
DecisionTreeClassifier(max_depth=30, max_features='sqrt', min_samples_leaf=3,\n",
-       "                       min_samples_split=18)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + "
DecisionTreeClassifier(criterion='entropy', max_depth=22, max_features=1.0,\n",
+       "                       min_samples_leaf=16, min_samples_split=20)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ - "DecisionTreeClassifier(max_depth=30, max_features='sqrt', min_samples_leaf=3,\n", - " min_samples_split=18)" + "DecisionTreeClassifier(criterion='entropy', max_depth=22, max_features=1.0,\n", + " min_samples_leaf=16, min_samples_split=20)" ] }, "execution_count": 9, @@ -2532,13 +2526,10 @@ " /* fitted */\n", " background-color: var(--sklearn-color-fitted-level-3);\n", "}\n", - "
DecisionTreeClassifier(max_depth=19, max_features='sqrt', min_samples_leaf=8,\n",
-       "                       min_samples_split=5)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + "
KNeighborsClassifier(n_jobs=1, n_neighbors=4)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ - "DecisionTreeClassifier(max_depth=19, max_features='sqrt', min_samples_leaf=8,\n", - " min_samples_split=5)" + "KNeighborsClassifier(n_jobs=1, n_neighbors=4)" ] }, "execution_count": 10, @@ -2970,13 +2961,13 @@ " /* fitted */\n", " background-color: var(--sklearn-color-fitted-level-3);\n", "}\n", - "
ExtraTreesClassifier(max_features=0.40389574491352287, min_samples_leaf=15,\n",
-       "                     min_samples_split=13, n_jobs=1)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + "
MLPClassifier(alpha=0.09935758704160183,\n",
+       "              learning_rate_init=0.004466259151092733)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ - "ExtraTreesClassifier(max_features=0.40389574491352287, min_samples_leaf=15,\n", - " min_samples_split=13, n_jobs=1)" + "MLPClassifier(alpha=0.09935758704160183,\n", + " learning_rate_init=0.004466259151092733)" ] }, "execution_count": 11, @@ -3411,13 +3402,13 @@ " /* fitted */\n", " background-color: var(--sklearn-color-fitted-level-3);\n", "}\n", - "
SVC(C=7.943520510912431, degree=1, kernel='linear', max_iter=3000,\n",
-       "    probability=True)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + "
DecisionTreeClassifier(criterion='entropy', max_depth=11, max_features=1.0,\n",
+       "                       min_samples_leaf=12, min_samples_split=8)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ - "SVC(C=7.943520510912431, degree=1, kernel='linear', max_iter=3000,\n", - " probability=True)" + "DecisionTreeClassifier(criterion='entropy', max_depth=11, max_features=1.0,\n", + " min_samples_leaf=12, min_samples_split=8)" ] }, "execution_count": 12, @@ -3858,35 +3849,26 @@ " /* fitted */\n", " background-color: var(--sklearn-color-fitted-level-3);\n", "}\n", - "
Pipeline(steps=[('variancethreshold',\n",
-       "                 VarianceThreshold(threshold=0.16682490562982172)),\n",
-       "                ('nystroem',\n",
-       "                 Nystroem(gamma=0.7638884024411401, kernel='linear',\n",
-       "                          n_components=98)),\n",
-       "                ('extratreesclassifier',\n",
-       "                 ExtraTreesClassifier(max_features=0.41763504253232936,\n",
-       "                                      min_samples_leaf=8, min_samples_split=17,\n",
-       "                                      n_jobs=1))])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + "
Pipeline(steps=[('selectfwe', SelectFwe(alpha=0.007682074361801758)),\n",
+       "                ('fastica', FastICA(n_components=64)),\n",
+       "                ('randomforestclassifier',\n",
+       "                 RandomForestClassifier(bootstrap=False, criterion='entropy',\n",
+       "                                        min_samples_leaf=10,\n",
+       "                                        min_samples_split=6))])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ - "Pipeline(steps=[('variancethreshold',\n", - " VarianceThreshold(threshold=0.16682490562982172)),\n", - " ('nystroem',\n", - " Nystroem(gamma=0.7638884024411401, kernel='linear',\n", - " n_components=98)),\n", - " ('extratreesclassifier',\n", - " ExtraTreesClassifier(max_features=0.41763504253232936,\n", - " min_samples_leaf=8, min_samples_split=17,\n", - " n_jobs=1))])" + "Pipeline(steps=[('selectfwe', SelectFwe(alpha=0.007682074361801758)),\n", + " ('fastica', FastICA(n_components=64)),\n", + " ('randomforestclassifier',\n", + " RandomForestClassifier(bootstrap=False, criterion='entropy',\n", + " min_samples_leaf=10,\n", + " min_samples_split=6))])" ] }, "execution_count": 13, @@ -4325,28 +4307,28 @@ " /* fitted */\n", " background-color: var(--sklearn-color-fitted-level-3);\n", "}\n", - "
Pipeline(steps=[('variancethreshold',\n",
-       "                 VarianceThreshold(threshold=0.029163176782587025)),\n",
-       "                ('rbfsampler',\n",
-       "                 RBFSampler(gamma=0.3360335889875927, n_components=61)),\n",
-       "                ('randomforestclassifier',\n",
-       "                 RandomForestClassifier(min_samples_leaf=2,\n",
-       "                                        min_samples_split=5))])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" + "
Pipeline(steps=[('selectpercentile',\n",
+       "                 SelectPercentile(percentile=75.04535288452273)),\n",
+       "                ('nystroem',\n",
+       "                 Nystroem(gamma=0.4607961332716787, kernel='laplacian',\n",
+       "                          n_components=90)),\n",
+       "                ('bernoullinb',\n",
+       "                 BernoulliNB(alpha=2.4816194955956314, fit_prior=False))])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ - "Pipeline(steps=[('variancethreshold',\n", - " VarianceThreshold(threshold=0.029163176782587025)),\n", - " ('rbfsampler',\n", - " RBFSampler(gamma=0.3360335889875927, n_components=61)),\n", - " ('randomforestclassifier',\n", - " RandomForestClassifier(min_samples_leaf=2,\n", - " min_samples_split=5))])" + "Pipeline(steps=[('selectpercentile',\n", + " SelectPercentile(percentile=75.04535288452273)),\n", + " ('nystroem',\n", + " Nystroem(gamma=0.4607961332716787, kernel='laplacian',\n", + " n_components=90)),\n", + " ('bernoullinb',\n", + " BernoulliNB(alpha=2.4816194955956314, fit_prior=False))])" ] }, "execution_count": 14, @@ -4377,18 +4359,11 @@ "name": "stderr", "output_type": "stream", "text": [ - "Generation: 0%| | 0/5 [00:00
TPOTEstimator(classification=True, generations=5, max_eval_time_seconds=300,\n",
-       "              population_size=10, processes=False, scorers=['roc_auc'],\n",
-       "              scorers_weights=[1],\n",
-       "              search_space=<tpot2.search_spaces.pipelines.graph.GraphPipeline object at 0x7ebd8bf94bb0>,\n",
+       "              population_size=10, scorers=['roc_auc'], scorers_weights=[1],\n",
+       "              search_space=<tpot2.search_spaces.pipelines.graph.GraphPipeline object at 0x77c026a110c0>,\n",
        "              verbose=2)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
" ], "text/plain": [ "TPOTEstimator(classification=True, generations=5, max_eval_time_seconds=300,\n", - " population_size=10, processes=False, scorers=['roc_auc'],\n", - " scorers_weights=[1],\n", - " search_space=,\n", + " population_size=10, scorers=['roc_auc'], scorers_weights=[1],\n", + " search_space=,\n", " verbose=2)" ] }, @@ -4866,14 +4838,14 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "auroc score 0.9890552995391705\n" + "auroc score 0.9876518024288388\n" ] } ], @@ -4888,12 +4860,12 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 17, "metadata": {}, "outputs": [ { "data": { - "image/png": "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", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAnYAAAHWCAYAAAD6oMSKAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/H5lhTAAAACXBIWXMAAA9hAAAPYQGoP6dpAABeQ0lEQVR4nO3deVzUdeI/8NcwAzKAIIciiqgRGs3AcIgH3uuReaWZR1aiaVr5tXW11vxZ2fHw51rqul1q5UG7bet32yxWM/155H0gIHOkYoqAIAhyw3DM8fujmvrkESrwnuP1/G9fKLyo1nn5fjMzMqvVagUREREROTw30QWIiIiIqHlw2BERERE5CQ47IiIiIifBYUdERETkJDjsiIiIiJwEhx0RERGRk+CwIyIiInISHHZEREREToLDjoiIiMhJcNgREREROQkOOyIiIiInwWFHRERE5CQ47IiIiIicBIcdERERkZPgsCMiIiJyEhx2RERERE6Cw46IiIjISXDYERERETkJDjsiIiIiJ8FhR0REROQkOOyIiIiInASHHREREZGT4LAjIiIichIcdkREREROgsOOiIiIyElw2BERERE5CQ47IiIiIifBYUdERETkJBSiCxARNSez2YzS0lIUFRWhqKgIxYWFqDcaYTGb4SaXo41SifYdOyI4OBjBwcEICAiAXC4XXZuIqFnIrFarVXQJIqJ7VVZWhszMTOjS01FXUwOryQQfoxF+paVwN5ngZrXCIpOhUaFARUAAqpVKyBQKeHp7IyouDhqNBv7+/qK/DSKie8JhR0QOraCgAMeOHEH2hQtwr61FWG4eQkpL4VdTA3ez+Za/r1EuR4W3N64GBCA3rAsavbzQPSIC/QcOREhISCt+B0REzYfDjogckslkwtGjR5F69Ch8Skpwf04uQktKILdY7vhzmd3ccCUoCD90DUN1UBAS+vdH//79oVDwp1WIyLFw2BGRwyksLMTOlBSUXcnHAxcuICI/H27N8EeZRSbDhc6dcS4iAgGhnTF6/Hh07NixGRoTEbUODjsicig5OTnYvm0bvAquIv7sWfjW1jb716j08kJaZCRqO3XCxKlT0LVr12b/GkRELYHDjogcRk5ODv7z+ecIzMlF7++/h+Iurl2byuTmhpOqB1EaFoZJjz/OcUdEDoGvY0dEDqGwsBDbt21DQE4u+hoMLTrqAEBh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", "text/plain": [ "
" ] From 22fd8091d95efd3a7e46ec7fbab3725164002416 Mon Sep 17 00:00:00 2001 From: perib Date: Tue, 26 Mar 2024 19:33:27 -0700 Subject: [PATCH 6/7] edit --- Tutorial/5_Genetic_Feature_Selection.ipynb | 108 ++++++++++----------- 1 file changed, 51 insertions(+), 57 deletions(-) diff --git a/Tutorial/5_Genetic_Feature_Selection.ipynb b/Tutorial/5_Genetic_Feature_Selection.ipynb index 96bf78b1..a9afcf4b 100644 --- a/Tutorial/5_Genetic_Feature_Selection.ipynb +++ b/Tutorial/5_Genetic_Feature_Selection.ipynb @@ -18,20 +18,14 @@ "name": "stderr", "output_type": "stream", "text": [ - "Generation: 0%| | 0/5 [00:00
Pipeline(steps=[('maskselector',\n",
-       "                 MaskSelector(mask=array([ True,  True, False, False, False, False, False, False, False,\n",
-       "        True,  True, False,  True, False,  True,  True,  True, False,\n",
-       "       False, False,  True, False, False, False, False,  True, False,\n",
-       "        True,  True,  True,  True,  True,  True,  True,  True, False,\n",
-       "        True,  True, False, False,  True, False,  True,  True, False,\n",
-       "       False,  True, False,  True, False,  True, False,  True, Fa...\n",
-       "        True, False,  True, False,  True,  True,  True, False,  True,\n",
-       "        True,  True,  True,  True,  True,  True, False,  True,  True,\n",
-       "        True, False, False, False,  True,  True, False,  True,  True,\n",
-       "        True,  True, False, False, False,  True, False,  True, False,\n",
-       "        True, False, False,  True, False,  True, False, False, False,\n",
+       "                 MaskSelector(mask=array([False,  True, False, False, False,  True,  True, False, False,\n",
+       "        True, False,  True,  True, False, False, False,  True, False,\n",
+       "       False, False, False,  True, False, False, False, False, False,\n",
+       "        True, False, False, False,  True,  True,  True, False,  True,\n",
+       "        True,  True,  True,  True, False,  True,  True, False, False,\n",
+       "       False, False,  True, False,  True, False, False, Fa...\n",
+       "        True,  True,  True, False, False,  True,  True, False, False,\n",
+       "        True, False, False, False, False, False,  True, False,  True,\n",
+       "       False, False,  True, False, False, False,  True,  True,  True,\n",
+       "       False, False, False,  True, False, False,  True, False, False,\n",
+       "       False,  True, False, False, False,  True,  True, False, False,\n",
        "        True]))),\n",
        "                ('graphpipeline',\n",
-       "                 GraphPipeline(graph=<networkx.classes.digraph.DiGraph object at 0x76ebe05ee590>))])
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
MaskSelector(mask=array([False,  True, False, False, False,  True,  True, False, False,\n",
+       "        True, False,  True,  True, False, False, False,  True, False,\n",
+       "       False, False, False,  True, False, False, False, False, False,\n",
+       "        True, False, False, False,  True,  True,  True, False,  True,\n",
+       "        True,  True,  True,  True, False,  True,  True, False, False,\n",
+       "       False, False,  True, False,  True, False, False, False,  True,\n",
+       "        True,  True,  True, False, False,  True,  True, False, False,\n",
+       "        True, False, False, False, False, False,  True, False,  True,\n",
+       "       False, False,  True, False, False, False,  True,  True,  True,\n",
+       "       False, False, False,  True, False, False,  True, False, False,\n",
+       "       False,  True, False, False, False,  True,  True, False, False,\n",
+       "        True]))
[('KNeighborsClassifier_1', 'Normalizer_1')]
" ], "text/plain": [ "Pipeline(steps=[('maskselector',\n", - " MaskSelector(mask=array([ True, True, False, False, False, False, False, False, False,\n", - " True, True, False, True, False, True, True, True, False,\n", - " False, False, True, False, False, False, False, True, False,\n", - " True, True, True, True, True, True, True, True, False,\n", - " True, True, False, False, True, False, True, True, False,\n", - " False, True, False, True, False, True, False, True, Fa...\n", - " True, False, True, False, True, True, True, False, True,\n", - " True, True, True, True, True, True, False, True, True,\n", - " True, False, False, False, True, True, False, True, True,\n", - " True, True, False, False, False, True, False, True, False,\n", - " True, False, False, True, False, True, False, False, False,\n", + " MaskSelector(mask=array([False, True, False, False, False, True, True, False, False,\n", + " True, False, True, True, False, False, False, True, False,\n", + " False, False, False, True, False, False, False, False, False,\n", + " True, False, False, False, True, True, True, False, True,\n", + " True, True, True, True, False, True, True, False, False,\n", + " False, False, True, False, True, False, False, Fa...\n", + " True, True, True, False, False, True, True, False, False,\n", + " True, False, False, False, False, False, True, False, True,\n", + " False, False, True, False, False, False, True, True, True,\n", + " False, False, False, True, False, False, True, False, False,\n", + " False, True, False, False, False, True, True, False, False,\n", " True]))),\n", " ('graphpipeline',\n", - " GraphPipeline(graph=))])" + " GraphPipeline(graph=))])" ] }, "execution_count": 2, @@ -553,12 +547,12 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "metadata": {}, "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAnYAAAHWCAYAAAD6oMSKAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/H5lhTAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAawElEQVR4nO3de5CddZ3n8U9fcuuEhO5EOjcSEBJCgCAXZUxwhAGlSkcUmQV1xmXGkS1ZS6xyiG6tU1vOrmvtArOgM+qUixbLLE4x7mAZL6yzxWjt0iBiMiQIgQQMnXsmoTsJnU4n6cv+AUSBBEPSIfSX1+uvpPuc5/mdc7rq+67nec45DUNDQ0MBAGDEazzWCwAAYHgIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADACii+Vgv4LUwMDCQrq6ubNmyJVu2bMnWzZuzZ/fuDA4MpLGpKWPGjcubpk5Ne3t72tvb09bWlqampmO9bADgFZjvL9cwNDQ0dKwXcbR0d3dn+fLleWTZsvTt2pWh/v5M2L07k7q6Mqq/P41DQxlsaMi+5ubsaGtLz7hxaWhuztjx43PWuefm7LPPTmtr67F+GADAbzDfD65k2G3cuDH333df1qxenVG9vZm1dl2mdXVl0q5dGTUwcND77Wtqyo7x47OprS1rZ52YfS0tOXnOnCx6xzsybdq01/ARAAAvZb7/dqXCrr+/Px0dHXmooyMTtm3LqZ1rM3PbtjQNDr7qbQ00Nmb9lCl5cvas9EyZkrcuWpRFixalufkNcfYaAF43zPdDVybsNm/enB8uWZLu9Rsyb/XqzNmwIY3D8NAGGxqyesaMPD5nTtpmzsh7Lr88U6dOHYYVAwC/jfn+6pQIu87Oznz3rrvSsnFTzlu5MhN7e4d9HztbWrL09NPTO316rrj6qsyePXvY9wEA/Jr5/uqN+LDr7OzMP/zd32Vy59q87bHH0nwYh2UPVX9jYx48Y366Zs3KlR/+8Ih/8QHg9cp8Pzwj+nPsNm/enO/edVfaOtfmdx599Ki+6EnSPDiYt//y0bStXZvv3vX32bx581HdHwC8EZnvh2/Ehl1/f39+uGRJWjZuygWPPTYs59sPRePQUC549LGM27QxP1qyJP39/a/JfgHgjcB8PzIjNuw6OjrSvX5Dzlu58qiX/Es1Dw7mvMdWpmvDhtx///2v6b4BoDLz/ciMyLDbuHFjHuroyLzVq4/KhZSHYlJvb05btTo/v+++bNq06ZisAQAqMd+P3IgMu/vvuy8Ttm3LnA0bjuk65m7YkAnbtqXjvvuO6ToAoALz/ciNuLDr7u7OmtWrc2rn2tfsvPvBNA4N5ZTOtVmzalW6u7uP6VoAYCQz34fHiAu75cuXZ1Rvb2Zu23asl5IkOXHbtjT39mbFihXHeikAMGKZ78NjRIXdwMBAHlm2LLPWrjusrxE5GpoGBzN73bqsWLo0A6/wPXUAwIGZ78PnsMJuypQpR7zjj3/843nqqacO+vtbb701e/fu3f//iy++OF1dXenbtSvTurpedvs/WrEily39Rd63bFk++PA/57GeniNe46Ga9sxz6/rBD36Q97///Vm4cGGWLFmSJPnFL36RxYsXD9u+fv7zn+f888/PqFGj8oMf/GDYtgsAw6mhoSF//ud/vv//N9xwQ26//fYD3vaV5vvh+kpnZ/5248Ykyb9fvSprd+9+1dt4Yb53vcK6rrjiirS2tuYP/uAPDnutw+mYHbG77bbbcsoppxz09y8Nu5/85CfZsmVLhvr7c/xBou2v5p2e7597bj40dVpufHrNEa9x4BDP8Y/r6srunp58/vOfz5IlS/LAAw/kyiuvzLZt23L++efnpptuOuK1vGD69On55je/mQ9/+MPDtk0AGG4TJkzInXfemWefffa33vZA8/1QZ/Ch+NKcuZk1btyrvt+kXbsy1N+fLVu2ZPAgRxI//elP54477jjSJQ6b5uHa0LJly/KJT3wiu3fvzjnnnJNvfOMbGTt2bL73ve9l8eLFmTRpUhYsWJDW1tbcfPPNueiii/LXf/3XOf3003PNNddk2bJlaWpqymc+85n09vZm48aNWbhwYU466aQsWbIkU6ZMyV133ZUJu3fnv3c+nR9u3ZqGJB9sn5o/mTHjRWs5b+LEfGvD+iTP/WHcuGZNHtq5I/sGh3LtzJm5/IQT0jswkBueeCJrdvfm7OMm5mc7tueH556XXz77bL66bm1GNzZmR39//seZZ+Uvnnoyq3t7MzSU3HDSSVnU2pqfbd+e//jUk8ngYBqTfOzsszN+/Pj9a+jv78/3v//9tLa25pvf/Ga+9a1v5ZlnnsmnP/3prFu3Lq2trfnKV76SWbNm5VOf+lQmTpyYZcuWpaurK7fccksWLlx4wOd5zJgxmT59evbu3ZsdO3Zk69atw/USAsCwGT16dK644orceOONuf7669Pb25tnn302W7duzYoVK7J48eL09fXlzDPPzNVXX53xu3pz6YM/y3unTMl927fnhtkn5c9WPZH3velN6di+PSePG5ePzZiZv+x8Otv27s1Nc0/LORMn5uGdO/OlNb/K3sHBjG9qyo1zT8uMsWNftJY/WrEi/+GUU7Kury9fXtuZJOnpH8iMsWPyt2ctyP/r7s5fre3MnsHBzGlpyZfmzM3oxsYs6rgvC3buyNfvuCP33HNP5s2b97LHedFFF+WnP/3pa/GUHpJhC7trrrkmt912Wy644IJcd911+drXvpbrrrsu119/fTo6OjJ16tRceumlOf/88190v4cffjhr1qzJY489liTZsWNHJk2alJtuuin3339/JkyYsP+2Wzdvzprly/PA9u25+y3nZHRjY7bv2/eytfy0qyuXtE1Oknxny+acMHp07n7LOekbGMi/Wr4872htzf/asjkzxo7J1+bPT8f27tz9L1v23/+XPT2559zz0j5mTP7y6adzcVtb/uvc09K1b18+vGJ57jnn3PzNml/lE8cfn/NbWtIzMJANO3ek+yWHaj/2sY/t//cJJ5zwsnW+9Ll4wQc+8IHf8mw/56677jqk2wHAsXDLLbckSb74xS/u/9n111//otusXLkyT65alU/Mn5+BgYG07NmTr0+dmuzpy/b+/pzb0JBrZ8zM4i2b8z83bcy3z1qQju3b84316/L1+Wfk1JaW/N2Cs9PU0JB7n3kmX1u3Lv95zpwDrueSyZNzyeTJGRgayp/88pH86+kz0rVvX25bvz53nHlWxjY15cudT+fvN2/OH02fnu39/XnrlDfluj/7swNG3evRsITd9u3bs2fPnlxwwQVJko9+9KO56aab8nu/93uZN29eZs6cmSS58sor09nZ+aL7vvnNb87GjRvzyU9+Mu9///vz7ne/+6D72bN7d1auX58r26dmdONzZ5GPHzVq/+8/9fjK7B0cTM/AQJacc26SpKO7O6t6e/O9rf+SJOkZ6M+6vr4s2/ls/s3z61p0fGuOb/71U3HuxIlpHzPmuftv785Pu577Q0mS3QMDebq7O2eMGZNvPPNMOvfuzUUTJqR5795Ma2/P6iefPPwnEgDegJ7p6krz85dfXfQbZ7/GNTTknHHjMjg0mFmNjTlz0vFpbGjI3JaWrO/bkyTZ0d+fxaueyNq+vgwODWVS86gD7uM33dL5dM6ccFwunTw5P+l6Jk/07spVK5YnSfYODuaitrYkydjGxrxt+vT09fUN90M+aobtiN2BDB3C+fHW1tY88sgj+dGPfpRbbrkl//iP/5ibb775gLcdHBhIXmGbfzXv9OcOoa75Vb74q6fy1dPnZzDJfzr11Lxt0vEvXd1BtzOu8deXHg4ODeVv5p/xosO6O3fuzB+2tuaClpY80Nubf7thQz47f35OO/XU/N+Ojt/6mAGAX2tI0vD8NWxjfmMGj2po2P/vxjTsP6jT2NCQwefn+JfXduadbW350NRpWbVrV/7d6lWvuK+fdD2Th3c+m9vPOitJMjiUXNTalv8yd+7Lbju2sTGNQ4MZGEHfGzssb544/vjjM2bMmDz00ENJkjvvvDO/+7u/m3nz5uXxxx/Phg0bMjAwkLvvvvtl9922bVsGBwdz1VVX5Qtf+EIefvjhJMlxxx33sgsuG5uactb06fmHLZuz9/k/gJeeim1oaMhnZp+Uh3fuzK96e3Ph8a25c9Om/Rdhrtq1KwNDQzln4sTc8/xn5TywfXu2H+RFW9Tamjuef1dNkjzW05PjjjsumwcGcuqYMfloa2tmjxqVrb296d6+/dU/eQDwBtd+wgkZajx4kjQ2NmbM2DFpOMDvevoH0j76ubNsv3lZ1YGs7+vLjWvW5L/Nm5fm56PxnInH5cEd27Ph+aNyPf3Pndl7wWBDY5qaj+pxsGF1WCvt7u7ef3o1SW666abcfvvtue6669LX15e3vOUtue666zJ27NjceuutufjiizNp0qTMmzcvEydOfNG2NmzYkD/+4z/O4OBgmpubc+uttyZJrr322lx88cWZO3fu/o8OGTNuXM486aTsXv1kPvDwP6e5oSFXntCea17y5olxTU352IyZ+daGDfmLU0/N+r6+fOCfl2UwyZtGj85tZ5yZP5w2PTc88Xjes2xpzp5wXNpHj87YA/xRffLEWfnir57K+5YtTf/QUM6YMCE3nzYv39+zJz/r7k6GhnLa6NGZPXVqfvT8dYIv+Pa3v51x48a96M0T119/fdavX/+yN0+8733vy7vf/e709PTkne98Z5YuXXrA5/7RRx/NRz7ykezYsSNjx47NySefnHvuuefVvoQAcFS9cHAnSZ588slceOGFufXWW/OhD30oy5cvz2c/+9n9b5541yWXZPTPH0pTU1PaT2jP+KamJEljZ2emtk9NQ2Njmg/y3bHXzpyZz61alS93Pp13tLa94pq++y9b0t3fn48/+sskyZkTJuRLc+bmi6fOyaceX5l9g4NpaGjI509+c058/kzd3ubmjH7JmzF+06WXXprly5dn165dmTlzZr7zne/k7W9/+6t+voZLw9ChnC89Aj09PZkwYUIGBgbywQ9+MNdee21+//d//7C2de+99+aJH/8473rgZ0e8rv6hoQwODWV0Y2OWP/ts/uKpJ3P3W845rG3t2bMn//ttb809K1fmn/7pn5Ikzc3N2bRp07B85h8AVDac8324/Z+3/05Ou+yyXHLJJcd6KYfkqB9b/PrXv54777wze/bsyaWXXpr3vve9h72t9vb2LB03LvuamjLqCD8FundgINc88kj6h4YyqrEhXzjl1MPeVmNLSwYnT86f/umf5rjjjsvWrVvzuc99TtQBwCEYzvk+nPY1NaVn3Li0t7cf66UcsqMedosXLx62b15ob29PQ3Nzdowfnyk7dx7RtiY2N+e75xzeEbqX2jF+fBqam/Oud70rH/nIR4Zlmz/+8Y/zuc997kU/W7RoUb761a8Oy/YB4PViOOf7cHphvre3t+eCCy7Inj17XvT7e++9N5MnTz5GqzuwkXM1YJK2traMHT8+m9raXlcv/KbJz62rre2Vz+2/Gpdddlkuu+yyYdseALxejYT5/uCDDx7r5RySY/aVYoejqakpZ517btbOOjEDr/DumdfSQGNjOk88MQvOOy9Nz1/sCQAcOvN9+Lw+nr1X4eyzz86+lpasf51cv7ZuypT0t7RkwYIFx3opADBime/DY8SFXWtra06eMydPzp6VwYYDfaLNa2ewoSFPzZ6Vk+fOTWtr6zFdCwCMZOb78BhxYZcki97xjvRMmZLVL/n8utfaqhkz0jNlShZdeOExXQcAVGC+H7kRGXbTpk3LWxctyuNz5mRnS8sxWcOOlpY8MXdO3nbhhZk2bdoxWQMAVGK+H7kRGXbJcx/90TpzRpaefnr6X+MLLfsbG7N0/ulpmzEjCxcufE33DQCVme9HZsSGXXNzc957+eXpnT49D54x/zU7Hz/Y0JAHz5if3dOm5z2XX57mEfT9cQDweme+H5kRG3ZJMnXq1Fxx9VXpmjUrD5x5xlEv+/7Gxjxw5hnpmjUrV1x9VaZOnXpU9wcAb0Tm++E76t8V+1ro7OzMd+/6+7Rs3JjzVq7MxIN8UfCR2NHSkqXzT8/uadNzxdVXZfbs2cO+DwDg18z3V69E2CXJ5s2b88MlS9K9fkPmrV6dORs2pHEYHtpgQ0NWzZiRJ+bOSduMGXnP5ZeP6JIHgJHEfH91yoRdkvT396ejoyMPdXRkwrZtOaVzbU7cti1Ng4OvelsDjY1ZN2VKnpo9Kz1TpuRtF16YhQsXjthz7gAwUpnvh65U2L1g48aNub+jI2tWrUpzb29mr1uXac90ZdKuXRk1MHDQ++1rasqO8eOzaXJbOk88Mf0tLTl57twsGqFveQaASsz3365k2L2gu7s7K1asyIqlS9O3a1eG+vszYffuTOzqzuj+/jQODWawoTF7m5uzs601PePGpaG5OWPHj8+C887LggULRtwnTgNAdeb7wZUOuxcMDAykq6srW7ZsyZYtW7J18+bs7evLQH9/mpqbM3rs2Lxp6tS0t7envb09bW1tI+oLfwHgjch8f7k3RNgBALwRjOjPsQMA4NeEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAoQtgBABQh7AAAihB2AABFCDsAgCKEHQBAEcIOAKAIYQcAUISwAwAo4v8D/AQZY2yzkYYAAAAASUVORK5CYII=", + "image/png": "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csUVV6SzszNnnXVWtm7dmlNOOSW333577r333iTJ/fffn/e9731Zvnx5Lrvsspx55plJkjVr1uSqq6/O/+ruzu+2t+eS/X8nSfJ/ft2df1i+PENJTt+3I5+eNi3LN23Knz70YA5sa8vi3t7825HvyucffjjdWzYnSS7Z/3cyfcyYPNzbm28dfHAaz/3jmTV2bJLkrnXrtu3P/c88k8uWPp4tg4MZ29ycr886KFPHjMntq3+dy554Io2hoWx69NEcdMTh28Kuv78/xx13XG6++eZ0dnZm3rx5ufDCC3PhhRdm8+bNueOOOzJv3ryce+65+elPf5qBgYFceuml+cUvfpEtW7bkc5/7XD7+8Y/n2muvza233pp169Zln332edn/7YwePTpTpkzJli1bsn79+qxevXq4f1YA2CX22GOPnH766fn617+e888/P319ffnNb36T1atXZ+HChbnooouyadOmHHbYYTnjjDMytrcvH7jrF/nIxIm5Y926XLjfO/PnnY/kD97xjixYty77t7bmU1On5RtdT2TNli25fNZBOWqvvV5xjn6hcxYuzH874IA8uWlTvrmsK0myoX8gU8eMzj8ffkT+79q1+dtlXdk8OJiZbW25bOas7NHUlDkL7sgRz6zPd6++Orfccktmz5693X6efPLJ+dnPfvZm/EqHZdhhd/PNN2f69OkvWnbQQQfloIMOyg033JADDzxw2/J58+Zl8uTJueeee7Jx48a85z3vyYc//OEXrXvBBRfky1/+ck477bQXHapNksceeyzz58/PsmXLMnfu3G1h99BDD+Vrp52WDy1fkT/+1cLctW5d9mttzd8uW5Z/O/JdaW1uzhkP3J/37LN39mkZlcf6+nLFQbMze+zY/HjNmuwzqiXzDjssQ0ND6R0YyF3r12f22LGveDTveQe2teV7RxyZ5kYj859+Ot95clnO32vvzFuxPOe1t+fYtrbc/6535a6XHE174okncuihh277+YWnSdesWbPtKN4LY/l55513Xs4777ztlr/ce1/ouuuue9XXAeDN8vzlV1/5yle2LTv//PNf9J7Fixfn0c7O/Okhh2RgYCBtmzfnu5MmJZs3ZV1/f45uNPKZqdNyUfeqXPPUyvzvw4/IgnXr8vfLn8x3Dzn0ZeboJ/PVmTNfdjynTJiQUyZMyMDQUP7rol/lj6dMTc/WrfmH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2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQhLADAChC2AEAFCHsAACKEHYAAEUIOwCAIoQdAEARwg4AoAhhBwBQxP8H6n48pYDgLYIAAAAASUVORK5CYII=", "text/plain": [ "
" ] From 68378bcc758a04ae97c5d989f82bb4ed4497c6e2 Mon Sep 17 00:00:00 2001 From: perib Date: Tue, 26 Mar 2024 19:48:44 -0700 Subject: [PATCH 7/7] rng_ to rng --- tpot2/evolvers/steady_state_evolver.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tpot2/evolvers/steady_state_evolver.py b/tpot2/evolvers/steady_state_evolver.py index 7f03b5a3..5db3e502 100644 --- a/tpot2/evolvers/steady_state_evolver.py +++ b/tpot2/evolvers/steady_state_evolver.py @@ -23,13 +23,13 @@ import warnings -def ind_mutate(ind, rng_): - rng = np.random.default_rng(rng_) - return ind.mutate(rng_=rng) +def ind_mutate(ind, rng): + rng = np.random.default_rng(rng) + return ind.mutate(rng=rng) -def ind_crossover(ind1, ind2, rng_): - rng = np.random.default_rng(rng_) - return ind1.crossover(ind2, rng_=rng) +def ind_crossover(ind1, ind2, rng): + rng = np.random.default_rng(rng) + return ind1.crossover(ind2, rng=rng) class SteadyStateEvolver(): def __init__( self, @@ -480,16 +480,16 @@ def optimize(self): # parents = [] # for op in var_ops: # if op == "mutate": - # parents.extend(np.array(cur_evaluated_population)[self.parent_selector(weighted_scores, k=1, n_parents=1, rng_=self.rng)]) + # parents.extend(np.array(cur_evaluated_population)[self.parent_selector(weighted_scores, k=1, n_parents=1, rng=self.rng)]) # else: - # parents.extend(np.array(cur_evaluated_population)[self.parent_selector(weighted_scores, k=1, n_parents=2, rng_=self.rng)]) + # parents.extend(np.array(cur_evaluated_population)[self.parent_selector(weighted_scores, k=1, n_parents=2, rng=self.rng)]) - # #_offspring = self.population.create_offspring2(parents, var_ops, rng_=self.rng, add_to_population=True) - # offspring = self.population.create_offspring2(parents, var_ops, [ind_mutate], None, [ind_crossover], None, add_to_population=True, keep_repeats=False, mutate_until_unique=True, rng_=self.rng) + # #_offspring = self.population.create_offspring2(parents, var_ops, rng=self.rng, add_to_population=True) + # offspring = self.population.create_offspring2(parents, var_ops, [ind_mutate], None, [ind_crossover], None, add_to_population=True, keep_repeats=False, mutate_until_unique=True, rng=self.rng) if enough_parents_evaluated: - parents = self.population.parent_select(selector=self.parent_selector, weights=self.objective_function_weights, columns_names=self.objective_names, k=n_individuals_to_submit, n_parents=2, rng_=self.rng) + parents = self.population.parent_select(selector=self.parent_selector, weights=self.objective_function_weights, columns_names=self.objective_names, k=n_individuals_to_submit, n_parents=2, rng=self.rng) p = np.array([self.crossover_probability, self.mutate_then_crossover_probability, self.crossover_then_mutate_probability, self.mutate_probability]) p = p / p.sum() var_op_list = self.rng.choice(["crossover", "mutate_then_crossover", "crossover_then_mutate", "mutate"], size=n_individuals_to_submit, p=p) @@ -498,7 +498,7 @@ def optimize(self): if op == "mutate": parents[i] = parents[i][0] #mutations take a single individual - offspring = self.population.create_offspring2(parents, var_op_list, [ind_mutate], None, [ind_crossover], None, add_to_population=True, keep_repeats=False, mutate_until_unique=True, rng_=self.rng) + offspring = self.population.create_offspring2(parents, var_op_list, [ind_mutate], None, [ind_crossover], None, add_to_population=True, keep_repeats=False, mutate_until_unique=True, rng=self.rng) # If we don't have enough evaluated individuals to use as parents for variation, we create new individuals randomly # This can happen if the individuals in the initial population are invalid