diff --git a/.gitignore b/.gitignore index bff01e19..aa4eabb7 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ dask-worker-space/ target/ .venv/ build/* -*.egg \ No newline at end of file +*.egg +*.coverage* \ No newline at end of file diff --git a/README.md b/README.md index 6f30b08d..04920a14 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,34 @@ conda create --name tpot2env python=3.10 conda activate tpot2env ``` +### Packages Used + +python version <3.12 +numpy +scipy +scikit-learn +update_checker +tqdm +stopit +pandas +joblib +xgboost +matplotlib +traitlets +lightgbm +optuna +baikal +jupyter +networkx> +dask +distributed +dask-ml +dask-jobqueue +func_timeout +configspace + +Many of the hyperparameter ranges used in our configspaces were adapted from either the original TPOT package or the AutoSklearn package. + ### Note for M1 Mac or other Arm-based CPU users You need to install the lightgbm package directly from conda using the following command before installing TPOT2. @@ -159,16 +187,6 @@ Setting `verbose` to 5 can be helpful during debugging as it will print out the We welcome you to check the existing issues for bugs or enhancements to work on. If you have an idea for an extension to TPOT2, please file a new issue so we can discuss it. -### Known issues -* TPOT2 uses the func_timeout package to terminate long running pipelines. The early termination signal may fail on particular estimators and cause TPOT2 to run for longer than intended. If you are using your own custom configuration dictionaries, and are noticing that TPOT2 is running for longer than intended, this may be the issue. We are currently looking into it. Sometimes restarting TPOT2 resolves the issue. -* Periodic checkpoint folder may not correctly resume if using budget and/or initial_population size. -* Population class is slow to add new individuals. The Population class needs to be updated to use a dictionary for storage rather than a pandas dataframe. -* Crossover may sometimes go over the size restrictions. -* Memory caching with GraphPipeline may miss some nodes where the ordering on inputs happens to be different between two nodes. - - - - ### Support for TPOT2 TPOT2 was developed in the [Artificial Intelligence Innovation (A2I) Lab](http://epistasis.org/) at Cedars-Sinai with funding from the [NIH](http://www.nih.gov/) under grants U01 AG066833 and R01 LM010098. We are incredibly grateful for the support of the NIH and the Cedars-Sinai during the development of this project. diff --git a/Tutorial/1_Estimators_Overview.ipynb b/Tutorial/1_Estimators_Overview.ipynb index bea7facb..7da6be38 100644 --- a/Tutorial/1_Estimators_Overview.ipynb +++ b/Tutorial/1_Estimators_Overview.ipynb @@ -20,6 +20,807 @@ "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": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Generation: 0%| | 0/1 [00:00 \n", + " Pipeline has none of the following attributes: predict_proba. \n", + " Traceback (most recent call last):\n", + " File \"/home/ribeirop/common/Projects/TPOT_Dev/tpot2/tpot2/utils/eval_utils.py\", line 53, in objective_nan_wrapper\n", + " value = func_timeout.func_timeout(timeout, objective_function, args=[individual], kwargs=objective_kwargs)\n", + " File \"/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/func_timeout/dafunc.py\", line 108, in func_timeout\n", + " raise_exception(exception)\n", + " File \"/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/func_timeout/py3_raise.py\", line 7, in raise_exception\n", + " raise exception[0] from None\n", + " File \"/home/ribeirop/common/Projects/TPOT_Dev/tpot2/tpot2/tpot_estimator/estimator.py\", line 620, in objective_function\n", + " return objective_function_generator(\n", + " File \"/home/ribeirop/common/Projects/TPOT_Dev/tpot2/tpot2/tpot_estimator/estimator_utils.py\", line 55, in objective_function_generator\n", + " cv_obj_scores = cross_val_score_objective(sklearn.base.clone(pipeline),x,y,scorers=scorers, cv=cv , fold=step)\n", + " File \"/home/ribeirop/common/Projects/TPOT_Dev/tpot2/tpot2/tpot_estimator/cross_val_utils.py\", line 31, in cross_val_score_objective\n", + " this_fold_scores = [sklearn.metrics.get_scorer(scorer)(this_fold_pipeline, X_test, y_test) for scorer in scorers]\n", + " File \"/home/ribeirop/common/Projects/TPOT_Dev/tpot2/tpot2/tpot_estimator/cross_val_utils.py\", line 31, in \n", + " this_fold_scores = [sklearn.metrics.get_scorer(scorer)(this_fold_pipeline, X_test, y_test) for scorer in scorers]\n", + " File \"/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/metrics/_scorer.py\", line 253, in __call__\n", + " return self._score(partial(_cached_call, None), estimator, X, y_true, **_kwargs)\n", + " File \"/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/metrics/_scorer.py\", line 344, in _score\n", + " response_method = _check_response_method(estimator, self._response_method)\n", + " File \"/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/utils/validation.py\", line 2106, in _check_response_method\n", + " raise AttributeError(\n", + "AttributeError: Pipeline has none of the following attributes: predict_proba.\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Generation: 100%|██████████| 1/1 [00:07<00:00, 7.82s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Generation: 1\n", + "Best roc_auc_score score: 0.9938492063492064\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "2024-06-28 17:22:24,449 - distributed.scheduler - ERROR - Removing worker 'tcp://127.0.0.1:33053' caused the cluster to lose scattered data, which can't be recovered: {'ndarray-71df36028cf839ff98696c18d6668a27', 'ndarray-809a54d2fd885201030a189763e7bd92'} (stimulus_id='handle-worker-cleanup-1719620544.4491522')\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1.0\n" + ] + } + ], + "source": [ + "import tpot2\n", + "import sklearn\n", + "import sklearn.datasets\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", + "\n", + "\n", + "est = tpot2.TPOTClassifier(n_jobs=40, max_time_seconds=30, verbose=5, generations=1, population_size=5)\n", + "est.fit(X_train, y_train)\n", + "\n", + "\n", + "print(scorer(est, X_test, y_test))" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
Pipeline(steps=[('robustscaler',\n",
+       "                 RobustScaler(quantile_range=(0.16675428907107737,\n",
+       "                                              0.7012433303146526))),\n",
+       "                ('passthrough', Passthrough()),\n",
+       "                ('featureunion-1',\n",
+       "                 FeatureUnion(transformer_list=[('skiptransformer',\n",
+       "                                                 SkipTransformer()),\n",
+       "                                                ('passthrough',\n",
+       "                                                 Passthrough())])),\n",
+       "                ('featureunion-2',\n",
+       "                 FeatureUnion(transformer_list=[('skiptransformer',\n",
+       "                                                 SkipTransformer()),\n",
+       "                                                ('passthrough',\n",
+       "                                                 Passthrough())])),\n",
+       "                ('bernoullinb',\n",
+       "                 BernoulliNB(alpha=0.7637690262115946, 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=[('robustscaler',\n", + " RobustScaler(quantile_range=(0.16675428907107737,\n", + " 0.7012433303146526))),\n", + " ('passthrough', Passthrough()),\n", + " ('featureunion-1',\n", + " FeatureUnion(transformer_list=[('skiptransformer',\n", + " SkipTransformer()),\n", + " ('passthrough',\n", + " Passthrough())])),\n", + " ('featureunion-2',\n", + " FeatureUnion(transformer_list=[('skiptransformer',\n", + " SkipTransformer()),\n", + " ('passthrough',\n", + " Passthrough())])),\n", + " ('bernoullinb',\n", + " BernoulliNB(alpha=0.7637690262115946, fit_prior=False))])" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "est._evolver_instance.population.evaluated_individuals.iloc[0]['Individual'].export_pipeline()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Generation: : 1it [00:35, 35.93s/it]\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/covariance/_empirical_covariance.py:102: UserWarning: Only one sample available. You may want to reshape your data array\n", + " warnings.warn(\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-5421.324324324324\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, cv=5)\n", + "est.fit(X_train, y_train)\n", + "\n", + "print(scorer(est, X_test, y_test))" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -31,21 +832,21 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "Evaluations: : 19it [00:30, 1.59s/it]\n" + "Generation: : 1it [01:05, 65.90s/it]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "0.9996046124012956\n" + "0.9994639357871052\n" ] } ], @@ -59,19 +860,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))" ] }, @@ -108,13 +905,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", @@ -141,13 +931,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." ] }, { @@ -159,21 +955,23 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "Evaluations: : 117it [00:30, 3.85it/s]\n" + "Evaluations: : 77it [00:30, 2.54it/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.9974747474747474\n" + "1.0\n" ] } ], @@ -182,7 +980,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", @@ -202,12 +1009,12 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 6, "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/778TUtLZrW25dvbtx28TnBjf38e3bv3kG33DQ6l84Q/x+bdf+o+uHzb/v05Y+zYfGLqtEw55ZT07tmTTZs2ZebMmfnkJz+ZSy+9NOvWHd4naw/nNQN4LVxjB7zApEmTsmTJkixcuDB33333IesuvvjiTJs27eD31157bTZv3pw5c+ZkeHg4EydOzM9//vNDtrnlllvy0Y9+NF/+8pdz0UUXHda/Gk+dOTP/+6GHcmNvb/62vT3vOPmUJMk/Tp2Wj6xZk5EkV76lM2eNHZsn/vIBh/+wYWAgN2zelMaGhpzU2JivdXUlSb4xqyv/vGlT3v3oo2ltasyEE0/M9W+dme5nn/1/xzNlSj6/YUO+uXVLLhrffnD5v+zckYf37k1Tks4pU3LmWWflrrvuyh133JExY8Zk+vTpufLKK7N8+fJXPLbDec1eypo1a3LZZZelr68vS5YsSVdXVx566KHD2haor2Hkrz/iBnCEDQwMpKWlJQ0NDbnxxhvT3d19yFm/F7N06dL84b778p6HfvMGzfLw/dv55+W0hQvz7ne/e7SnAnAIZ+yAo+6RRx7Jddddl6GhoUyZMiW33377K27T2dmZFS0teb6pKWOOoX9TPt/UlH0tLens7BztqQC8gLADjrpLLrkkq1atelXbdHZ2pqG5OXvb2tLx9NNHZ2Kvwd62tjQ0Nx/1sLvvvvvy+c9//pBlCxYsyLe//e2jOi5wfBN2wDGpvb09J7W1ZVd7+zEVdrve9Od5tbe3v/IPvw4LFy7MwoULj+oYQD0+FQsck5qamvL2uXOzbdrUDB0jT1kYamzM1qlTc/a5577o474ARtux8W4J8CJmz56d51tb80RHx2hPJUmyvaMjg62tOfvss0d7KgAvStgBx6zx48dnRldX/jh9WoZf6Q7FR9lwQ0Menz4tM2bNyvjx40d1LgAvRdgBx7QFF12UfR0d2Th58qjOY8PkydnX0ZEFF144qvMAeDnCDjimTZw4MfMXLMjvu7ry9Cg9bmtva2v+MKsr77jwwkycOHFU5gBwOIQdcMxbsGBBxk+ZnBVnnJHBN/iDFIONjVlx5hlpnzw5F1xwwRs6NsCrJeyAY15zc3Pet2hRBiZNysNnnfmGXW833NCQh886M/snTsplixaludkdooBjm7ADjgsTJkzIlVd/KL3TpuWht5111M/cDTY25qG3nZXeadNy5dUfyoQJE47qeABHgmfFAseVrVu35sd3/TCtO3fm3PXrM25g4IiPsbe1NSvOPCP7J07KlVd/KNOnTz/iYwAcDcIOOO7s3r079y5enL4nduT0jRvTtWNHGo/AW9lwQ0M2TJ6cP8zqSvvkybls0SJn6oDjirADjkuDg4NZtmxZli9blrE9PZm5dVum9vSkaXj4Ve9rqLEx2zs68vj0adnX0ZF3XHhhLrjgAtfUAccdYQcc13bu3JkHly3L5g0b0jwwkOnbt2fiU705ub8/Y4aGXnK755uasretLbve1J6tU6dmsLU1M2bNygK3NAGOY8IOKKGvry9r1qzJmhUrcqC/PyODgxm7f3/G9fblhMHBNI4MZ7ihMc81N+fp9vHZ19KShubmnNTWlrPPPTdnn322J0oAxz1hB5QyNDSU3t7edHd3p7u7O0/u3p3nDhzI0OBgmpqbc8JJJ+XNEyaks7MznZ2daW9vT1NT02hPG+CIEHYAAEW4jx0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAUIewAAIoQdgAARQg7AIAihB0AQBHCDgCgCGEHAFCEsAMAKELYAQAU8X8BMlMHJ2fHGe0AAAAASUVORK5CYII=", + "image/png": "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IyYmBq6urgZMR0RERI2FO3aPIDk5GS+99JJg1rNnT+zevRsSicTgeQ4ePIiEhATs2rVL5xonJyfExMTgnXfegbOzswHTERERUWNjsWugoqIiBAQEoLCwUDtzdHTEyZMnDf4p0v379yMhIQF79uzRucbZ2RlTpkzBpEmT4OTkZMB0REREZCg8im2giRMnCkodACxbtsxgpU6j0WDfvn1ISEjAb7/9pnOdq6srpk2bhrfeegsODg4GyUZERETi4I5dA2zbtg2vvfaaYPbiiy8iOTm50Y9gNRoN/ve//yEhIQEpKSk617m5ueHdd9/F+PHjYWdn16iZiIiIyDiw2D2kgoIC+Pv7o7i4WDtzdnZGVlZWo14mRKPR4KeffkJCQgIOHz6sc52npydmzJiBMWPGwNbWttHyEBERkfHhUexD0Gg0GDdunKDUAcDKlSsbrdRpNBokJycjMTERqampOtf5+Phg5syZGDVqFKytrRslCxERERk3Frt/oFarUVNTAxsbG2zevBnJycmCX3/llVcwePDgRnne7du3IzExEenp6TrXtWzZErNmzcKIESNgZWWl9xxERERkOngUex87d+7EG2+8gaqqKrz66qv44YcfUF5erv11Nzc3ZGZmokWLFnp7TpVKhW+++QaJiYnIysrSue7xxx/HnDlzMHToUFhYWOjt+YmIiMh0sdjdR9u2bfHnn3/q/PXt27ejf//+enkupVKJbdu2Yd68eThz5ozOdX5+foiNjcWQIUMgl3PDlYiIiP7WJIqdSqVCcXExCgsLUVhYiOsFBaipqoJapYJUJoOVjQ1aeHjA3d0d7u7ucHFxwc2bN+97Ad/XX38dX3zxxSNnq6urwxdffIGkpCRkZ2frXNehQwfExcVh8ODBRnH/WSIiIrE15PXd3F9DzXrLp6SkBOnp6TiZlobqykpolErYV1XBqbgYNkolpBoN1BIJ6uRynHVxQaqNDSRyOazt7NDCywtOTk4oKyur97GPHTuGvLw8eHt7NyhbbW0tPv/8c8yfPx8XLlzQuS4gIABxcXEYOHCg2f9lJCIiehCP8voeGBaG4OBgs737klnu2OXn5+PggQPIyc6GhUIB30uX4VlcDKfKSlioVDq/rk4mQ5mdHa66uOCClyeKVSpk5+TgwMGDKCgouGf9m2++ic8///yhstXU1GDDhg1YsGABLl26pHNd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", "text/plain": [ "
" ] @@ -223,7 +1030,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -253,6 +1060,7 @@ " Individual\n", " Submitted Timestamp\n", " Completed Timestamp\n", + " Eval Error\n", " Pareto_Front\n", " Instance\n", " \n", @@ -260,58 +1068,63 @@ " \n", " \n", " 0\n", - " 0.994405\n", + " 0.914484\n", " NaN\n", " NaN\n", - " ['LogisticRegression_1']\n", - " 1.692231e+09\n", - " 1.692231e+09\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", " NaN\n", - " ['LogisticRegression_1']\n", + " [('DecisionTreeClassifier_1', 'SelectFwe_1'), ...\n", " \n", " \n", " 1\n", - " 0.954484\n", + " 0.966071\n", " NaN\n", " NaN\n", - " ['DecisionTreeClassifier_1']\n", - " 1.692231e+09\n", - " 1.692231e+09\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", " NaN\n", - " ['DecisionTreeClassifier_1']\n", + " [('DecisionTreeClassifier_1', 'SelectPercentil...\n", " \n", " \n", " 2\n", - " 1.000000\n", + " 0.735952\n", " NaN\n", " NaN\n", - " ['KNeighborsClassifier_1']\n", - " 1.692231e+09\n", - " 1.692231e+09\n", - " 1.0\n", - " ['KNeighborsClassifier_1']\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", + " NaN\n", + " [('DecisionTreeClassifier_1', 'PassKBinsDiscre...\n", " \n", " \n", " 3\n", - " 0.994048\n", + " 0.991534\n", " NaN\n", " NaN\n", - " ['GradientBoostingClassifier_1']\n", - " 1.692231e+09\n", - " 1.692231e+09\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", " NaN\n", - " ['GradientBoostingClassifier_1']\n", + " [('LogisticRegression_1', 'SelectPercentile_1'...\n", " \n", " \n", " 4\n", - " 0.989841\n", + " 0.997540\n", " NaN\n", " NaN\n", - " ['ExtraTreesClassifier_1']\n", - " 1.692231e+09\n", - " 1.692231e+09\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", " NaN\n", - " ['ExtraTreesClassifier_1']\n", + " [('LogisticRegression_1', 'VarianceThreshold_1...\n", " \n", " \n", " ...\n", @@ -323,124 +1136,130 @@ " ...\n", " ...\n", " ...\n", + " ...\n", " \n", " \n", - " 112\n", - " 0.997540\n", - " (105, 106)\n", - " crossover\n", - " ['MLPClassifier_1']\n", - " 1.692231e+09\n", - " 1.692231e+09\n", + " 72\n", + " 0.992910\n", + " (19, 19)\n", + " ind_mutate\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", " NaN\n", - " ['MLPClassifier_1']\n", + " [('KNeighborsClassifier_1', 'ColumnOneHotEncod...\n", " \n", " \n", - " 113\n", - " 0.998214\n", - " (15,)\n", - " mutate\n", - " ['KNeighborsClassifier_1']\n", - " 1.692231e+09\n", - " 1.692231e+09\n", + " 73\n", + " 0.983743\n", + " (8, 8)\n", + " ind_mutate\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", " NaN\n", - " ['KNeighborsClassifier_1']\n", + " [('KNeighborsClassifier_1', 'VarianceThreshold...\n", " \n", " \n", - " 114\n", - " 0.997619\n", - " (67, 67)\n", - " crossover\n", - " [('MLPClassifier_1', 'StandardScaler_1')]\n", - " 1.692231e+09\n", - " 1.692231e+09\n", + " 74\n", + " 0.997540\n", + " (63, 63)\n", + " ind_mutate\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", " NaN\n", - " [('MLPClassifier_1', 'StandardScaler_1')]\n", + " [('LogisticRegression_1', 'VarianceThreshold_1...\n", " \n", " \n", - " 115\n", - " 0.996944\n", - " (81,)\n", - " mutate\n", - " [('ExtraTreesClassifier_1', 'RBFSampler_1'), (...\n", - " 1.692231e+09\n", - " 1.692231e+09\n", + " 75\n", + " 0.978929\n", + " (63, 63)\n", + " ind_mutate\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", " NaN\n", - " [('ExtraTreesClassifier_1', 'RBFSampler_1'), (...\n", + " [('LogisticRegression_1', 'SelectFwe_1'), ('Lo...\n", " \n", " \n", - " 116\n", - " 1.000000\n", - " (90, 73)\n", - " crossover\n", - " [('MLPClassifier_1', 'MinMaxScaler_1')]\n", - " 1.692231e+09\n", - " 1.692231e+09\n", + " 76\n", + " 0.997540\n", + " (65, 42)\n", + " ind_crossover\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", " NaN\n", - " [('MLPClassifier_1', 'MinMaxScaler_1')]\n", + " [('LogisticRegression_1', 'VarianceThreshold_1...\n", " \n", " \n", "\n", - "

117 rows × 8 columns

\n", + "

77 rows × 9 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]" + " roc_auc_score Parents Variation_Function \\\n", + "0 0.914484 NaN NaN \n", + "1 0.966071 NaN NaN \n", + "2 0.735952 NaN NaN \n", + "3 0.991534 NaN NaN \n", + "4 0.997540 NaN NaN \n", + ".. ... ... ... \n", + "72 0.992910 (19, 19) ind_mutate \n", + "73 0.983743 (8, 8) ind_mutate \n", + "74 0.997540 (63, 63) ind_mutate \n", + "75 0.978929 (63, 63) ind_mutate \n", + "76 0.997540 (65, 42) ind_crossover \n", + "\n", + " Individual Submitted Timestamp \\\n", + "0 " ] @@ -554,7 +1345,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -585,6 +1376,7 @@ " Individual\n", " Submitted Timestamp\n", " Completed Timestamp\n", + " Eval Error\n", " Pareto_Front\n", " Instance\n", " \n", @@ -592,63 +1384,68 @@ " \n", " \n", " 0\n", - " 1.0\n", - " 15.0\n", + " 0.976746\n", + " 16.0\n", " NaN\n", " NaN\n", - " ['LogisticRegression_1']\n", - " 1.692231e+09\n", - " 1.692231e+09\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", " NaN\n", - " ['LogisticRegression_1']\n", + " [('LogisticRegression_1', 'FastICA_1'), ('Fast...\n", " \n", " \n", " 1\n", - " 0.96619\n", - " 45.0\n", " NaN\n", " NaN\n", - " ['DecisionTreeClassifier_1']\n", - " 1.692231e+09\n", - " 1.692231e+09\n", " NaN\n", - " ['DecisionTreeClassifier_1']\n", + " NaN\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " INVALID\n", + " NaN\n", + " [('DecisionTreeClassifier_1', 'FeatureAgglomer...\n", " \n", " \n", " 2\n", - " 0.99746\n", - " 7.0\n", + " 0.995556\n", + " 15.0\n", " NaN\n", " NaN\n", - " ['KNeighborsClassifier_1']\n", - " 1.692231e+09\n", - " 1.692231e+09\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", " NaN\n", - " ['KNeighborsClassifier_1']\n", + " [('LogisticRegression_1', 'VarianceThreshold_1')]\n", " \n", " \n", " 3\n", - " 0.996429\n", - " 15064.0\n", - " NaN\n", + " 0.985615\n", + " 4.0\n", " NaN\n", - " ['GradientBoostingClassifier_1']\n", - " 1.692231e+09\n", - " 1.692231e+09\n", " NaN\n", - " ['GradientBoostingClassifier_1']\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", + " 1.0\n", + " [('KNeighborsClassifier_1', 'SelectPercentile_...\n", " \n", " \n", " 4\n", - " 0.995714\n", - " 2802.0\n", + " 0.651905\n", + " 90.0\n", " NaN\n", " NaN\n", - " ['ExtraTreesClassifier_1']\n", - " 1.692231e+09\n", - " 1.692231e+09\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", " NaN\n", - " ['ExtraTreesClassifier_1']\n", + " [('KNeighborsClassifier_1', 'SelectFwe_1'), ('...\n", " \n", " \n", " ...\n", @@ -661,129 +1458,148 @@ " ...\n", " ...\n", " ...\n", + " ...\n", " \n", " \n", - " 138\n", - " 0.5\n", - " 6.0\n", - " (98,)\n", - " mutate\n", - " [('BernoulliNB_1', 'SelectFromModel_ExtraTrees...\n", - " 1.692231e+09\n", - " 1.692231e+09\n", + " 82\n", " NaN\n", - " [('BernoulliNB_1', 'SelectFromModel_ExtraTrees...\n", + " NaN\n", + " (23, 23)\n", + " ind_mutate\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " INVALID\n", + " NaN\n", + " [('KNeighborsClassifier_1', 'SelectPercentile_...\n", " \n", " \n", - " 139\n", - " 0.85496\n", - " 1.0\n", - " (82, 87)\n", - " crossover\n", - " ['MultinomialNB_1']\n", - " 1.692231e+09\n", - " 1.692231e+09\n", + " 83\n", + " 0.966706\n", + " 4.0\n", + " (66, 26)\n", + " ind_mutate , ind_mutate , ind_crossover\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", " NaN\n", - " ['MultinomialNB_1']\n", + " [('KNeighborsClassifier_1', 'SelectPercentile_...\n", " \n", " \n", - " 140\n", - " 0.997579\n", - " 8210.0\n", - " (86,)\n", - " mutate\n", - " ['ExtraTreesClassifier_1']\n", - " 1.692231e+09\n", - " 1.692231e+09\n", + " 84\n", " NaN\n", - " ['ExtraTreesClassifier_1']\n", + " NaN\n", + " (44, 44)\n", + " ind_mutate\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " INVALID\n", + " NaN\n", + " [('DecisionTreeClassifier_1', 'SelectPercentil...\n", " \n", " \n", - " 141\n", - " 0.979008\n", - " 14.8\n", - " (98,)\n", - " mutate\n", - " ['SGDClassifier_1']\n", - " 1.692231e+09\n", - " 1.692231e+09\n", + " 85\n", + " 0.998730\n", + " 308.8\n", + " (63, 63)\n", + " ind_mutate\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", " NaN\n", - " ['SGDClassifier_1']\n", + " [('LogisticRegression_1', 'SelectPercentile_2'...\n", " \n", " \n", - " 142\n", - " 0.5\n", - " 1500.0\n", - " (2,)\n", - " mutate\n", - " ['XGBClassifier_1']\n", - " 1.692231e+09\n", - " 1.692231e+09\n", + " 86\n", + " 0.998889\n", + " 301.0\n", + " (24, 24)\n", + " ind_mutate\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", " NaN\n", - " ['XGBClassifier_1']\n", + " [('LogisticRegression_1', 'QuantileTransformer...\n", " \n", " \n", "\n", - "

143 rows × 9 columns

\n", + "

87 rows × 10 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]" + " roc_auc_score complexity_scorer Parents \\\n", + "0 0.976746 16.0 NaN \n", + "1 NaN NaN NaN \n", + "2 0.995556 15.0 NaN \n", + "3 0.985615 4.0 NaN \n", + "4 0.651905 90.0 NaN \n", + ".. ... ... ... \n", + "82 NaN NaN (23, 23) \n", + "83 0.966706 4.0 (66, 26) \n", + "84 NaN NaN (44, 44) \n", + "85 0.998730 308.8 (63, 63) \n", + "86 0.998889 301.0 (24, 24) \n", + "\n", + " Variation_Function \\\n", + "0 NaN \n", + "1 NaN \n", + "2 NaN \n", + "3 NaN \n", + "4 NaN \n", + ".. ... \n", + "82 ind_mutate \n", + "83 ind_mutate , ind_mutate , ind_crossover \n", + "84 ind_mutate \n", + "85 ind_mutate \n", + "86 ind_mutate \n", + "\n", + " Individual Submitted Timestamp \\\n", + "0 Individual\n", " Submitted Timestamp\n", " Completed Timestamp\n", + " Eval Error\n", " Pareto_Front\n", " Instance\n", " \n", " \n", " \n", " \n", - " 57\n", - " 0.5\n", - " 0.0\n", - " (56,)\n", - " mutate\n", - " ['LogisticRegression_1']\n", - " 1.692231e+09\n", - " 1.692231e+09\n", + " 3\n", + " 0.985615\n", + " 4.0\n", + " NaN\n", + " NaN\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", + " 1.0\n", + " [('KNeighborsClassifier_1', 'SelectPercentile_...\n", + " \n", + " \n", + " 14\n", + " 0.997540\n", + " 8.0\n", + " NaN\n", + " NaN\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", + " 1.0\n", + " [('KNeighborsClassifier_1', 'SelectPercentile_...\n", + " \n", + " \n", + " 24\n", + " 1.000000\n", + " 23.0\n", + " NaN\n", + " NaN\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", + " 1.0\n", + " [('LogisticRegression_1', 'SelectFwe_1'), ('Lo...\n", + " \n", + " \n", + " 25\n", + " 0.997619\n", + " 17.4\n", + " NaN\n", + " NaN\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", + " 1.0\n", + " [('LogisticRegression_1', 'FastICA_1'), ('Logi...\n", + " \n", + " \n", + " 42\n", + " 0.990556\n", + " 6.0\n", + " NaN\n", + " NaN\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", " 1.0\n", - " ['LogisticRegression_1']\n", + " [('LogisticRegression_1', 'VarianceThreshold_1...\n", " \n", " \n", - " 137\n", + " 44\n", + " 0.993313\n", + " 7.4\n", + " NaN\n", + " NaN\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", " 1.0\n", + " [('LogisticRegression_1', 'SelectPercentile_1'...\n", + " \n", + " \n", + " 63\n", + " 0.965675\n", " 1.0\n", - " (82,)\n", - " mutate\n", - " [('MultinomialNB_1', 'SelectFromModel_ExtraTre...\n", - " 1.692231e+09\n", - " 1.692231e+09\n", + " (9, 42)\n", + " ind_crossover\n", + " <tpot2.search_spaces.pipelines.graph.GraphPipe...\n", + " 1.719621e+09\n", + " 1.719621e+09\n", + " None\n", " 1.0\n", - " [('MultinomialNB_1', 'SelectFromModel_ExtraTre...\n", + " [('KNeighborsClassifier_1', 'VarianceThreshold...\n", " \n", " \n", "\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", + " roc_auc_score complexity_scorer Parents Variation_Function \\\n", + "3 0.985615 4.0 NaN NaN \n", + "14 0.997540 8.0 NaN NaN \n", + "24 1.000000 23.0 NaN NaN \n", + "25 0.997619 17.4 NaN NaN \n", + "42 0.990556 6.0 NaN NaN \n", + "44 0.993313 7.4 NaN NaN \n", + "63 0.965675 1.0 (9, 42) ind_crossover \n", "\n", - " Individual Submitted Timestamp \\\n", - "57 ['LogisticRegression_1'] 1.692231e+09 \n", - "137 [('MultinomialNB_1', 'SelectFromModel_ExtraTre... 1.692231e+09 \n", + " Individual Submitted Timestamp \\\n", + "3 " ] @@ -929,23 +1833,21 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 13, "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" + "Generation: 100%|██████████| 5/5 [01:12<00:00, 14.45s/it]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "0.998015873015873\n" + "0.9971509971509972\n" ] } ], @@ -954,7 +1856,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", @@ -975,249 +1879,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." - ] - }, - { - "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 +1897,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": "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", - "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": "iVBORw0KGgoAAAANSUhEUgAAAnYAAAHWCAYAAAD6oMSKAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAACCXElEQVR4nO3deVhUZfsH8O8s7MgygmwCKossigKuoJaZ+UvLXMp2bU+0tF3TTHPfKt9yqyzLMrPUzNI3LS0L3AFBBQRRQVaBGdZhm+X3hzqvI6MCDhxm+H6uq+vy3GfOOfdw0nNzn3OeR6TVarUgIiIiIpMnFjoBIiIiIjIOFnZEREREZoKFHREREZGZYGFHREREZCZY2BERERGZCRZ2RERERGaChR0RERGRmWBhR0RERGQmWNgRERERmQkWdkRERERmgoUdERERkZlgYUdERERkJljYEREREZkJFnZEREREZoKFHREREZGZYGFHREREZCZY2BERERGZCRZ2RERERGaChR0RERGRmWBhR0RERGQmWNgRERERmQkWdkRERERmgoUdERERkZlgYUdERERkJljYEREREZkJFnZEREREZoKFHREREZGZYGFHREREZCakQidARG2DWq2GXC5HYWEhCgsLUVRQgNrqamjUaoglEljZ2MDV3R1ubm5wc3ODTCaDRCIROm0iIrqOSKvVaoVOgoiEo1AokJSUhFMJCaipqoJWpYJ9dTUc5XJYqFQQa7XQiESol0pRJpOh0sYGIqkU1nZ26BkRgV69esHZ2Vnor0FERGBhR9Ru5eXl4VBsLC5kZMBCqYRP9iV4yOVwrKqChVp90+3qJRKU2dkhXyZDto836m1t0TUgANGDB8PDw6MVvwEREd2IhR1RO6NSqRAXF4fjcXGwLy6Gf1Y2OhcXQ6LRNHlfarEYOS4uOOfrg0oXF/SNjkZ0dDSkUj7lQUQkBBZ2RO1IQUEBdu/aBUVOLoIyMhCQmwuxEf4J0IhEyPDyQlpAAGSdvTBy9Gi4u7sbIWMiImoKFnZE7URWVhZ+3roVtnn5iExNhYNSafRjlNvaIj44GEpPT4x9dAJ8fX2NfgwiIro5FnZE7UBWVha2b9mCjlnZ6JeSAmkzbrs2lkosxtHQEMh9fDD+8cdZ3BERtSKOY0dk5goKCvDz1q2QZWVjwJkzLVrUAYBUo8HA02cgy87Gz1t/REFBQYsej4iI/oeFHZEZU6lU2L1rF2zz8tE/JcUoz9M1hlirRf8zKbDJz8OeXbugUqla5bhERO0dCzsiMxYXFwdFTi4iU1NbvFN3I6lGg8iUVMhzc3Ho0KFWPTYRUXvFwo7ITOXl5eF4XByCMjJa5EWJxnBUKtE9PQPHYmORn58vSA5ERO0JCzsiM3UoNhb2xcUIyM0VNI/A3FzYFxcjLjZW0DyIiNoDFnZEZkihUOBCRgb8s7Jb7bm6mxFrtfDLysaF9HQoFApBcyEiMncs7IjMUFJSEiyUSnQuLhY6FQCAd3ExpEolkpOThU6FiMissbAjMjNqtRqnEhLgk32pWdOEtQSJRgPfS5eQHB8P9S3moSUiojvDwo7IzMjlctRUVcFDLhc6FT0eJVfykrexvIiIzAln6iYyM4WFhdCqVHCqrNTFgmP/RYCdHdRaLfxsbLEsMBA2EgkKamux4Hwm0qqq4CiVorOVNd7384OLpSUAYGZ6OtKVVdjRO/yWx3zjbBrOVFZCKhJhqEyGt7p0bfAZx6oqaFUqFBYWwtXV1bhfmoiIALBjR2R2CgsLYV9drTduXQepFLvCI7A7IhIWYhG2FORDq9UiJiUFdzvLsL9PX+zoHY6nPT0hr68HANRpNDhaVoo6jQbZNdW3POaYTp3we0QkdvYOR2J5OQ6Xljb4jIVaDfvqahQWFhr1+xIR0f+wsCMyM0UFBXC8xe3OPg6OyK6uwaGyUthKxHjE3V23rq+jIwLt7AAAsQoF+jg4YpSrK/YU3foljCHOMohEIliIxQi2s0dhXa3BzznIFSjiFGNERC2GhR2RmamtrobFTabwUmm1+EchR6CdLTKVSoTa2990P3uKi3C/iwtGubhiT3FRo45dqVLhb4Uc/R2dDK63VKlQV1PTqH0REVHTsbAjMjMatbrB2HUVKhVGJyZg3MlEeFpZ42E395tsfUWtRoNjZWUY5OwMHxsbSEUinL/N7BVarRYzM9LxuLsHPKysDH5GrNVAzXljiYhaDF+eIDIzYokEGpFIL3btGbvr+dnYYl9xicF9/C2Xo1ylwoj4EwCASrUae4qL8IqP702Pu+LiRThIpXi+c+ebfkYjEkMi5T87REQthR07IjNjZWOD+kYUT1FOTqhUq7DjupcZTpSVIb2qCnuKi7CyexD+6tsPf/Xth+29e2PPLQY73pKfj5SqSnzg53/LY9ZJpbC0tm78lyEioiZhYUdkZlzd3VEmk932cyKRCGuDQ/BHSQmGnTiOkQnx+DY/D3YSCY6UlmKQk5Pusz7WNpBAhPSqKoP7mp95Drk1NRifdBKjExOwvdDwCxLlMme4ut/6NjARETUf74kQmRk3NzfE29igXiKBxdVZHo4NGGjws57W1lgXEtIgftTA53+NiGgQuyZ10ODb5lUvkaDSxgZubm63/SwRETUPO3ZEZsbNzQ0iqRRlV4ctaSvK7OwgkkpZ2BERtSB27IjMjEwmg7WdHfJlMriUlxt13/MyzyHhhn2+3aUrBjs733bb/I5X8pI14jYxERE1Dws7IjMjkUjQMyICJ0tKEJKdDcl1M1DcqXm3eTniZtRiMbK8vRERGQmJRGK0fIiISB9vxRKZoV69eqHe1hY5Li5G33d9fT2qq6uhbkLBeMnFBSpbW4SFhRk9HyIi+h927IjMkLOzM7oGBOBcSQm8i4oaDFjcXNU1NVAorkxXJpFI4eLiAon41r8fakQiZPr6oGtgIJwbccuWiIiajx07IjMVPXgwKl1ckOHlZbR9VldX6/6sVqtQ0Yhn+NK9vFDp4oLoQYOMlgcRERnGwo7ITHl4eKBvdDTSAgJQbmtrlH1Kbxj4WFldjfpbTBFWZmuLs4EB6DdoEDw8PIySAxER3RwLOyIzFh0dDefOXogPDobqhlumarUaFZWVqKuvb/T+7OzsIBJdvx8tym/StVOJxYgPCYbMywtRUVHNSZ+IiJqIhR2RGZNKpRg1ejSUnp44Ghqim0O2SqlE4eXLqKgoR3FxEapuMqPEjSRiMezt9cfHq62tQW1dnV5MIxLhaGgIqj08MXL06AadPiIiahks7IjMnLu7O8Y+OgFyHx8cDg1BcXk5yspKAfzvhYrKRhZ2AGBnZw+xWH/Ikuu7diqxGId7hELu44Oxj06AO6cQIyJqNSzsiNoBX19fdAsKQpqNDQ736wulg4PeetHVTl5jiEUidOjQQS9WX1+H6poalNna4p+IcJR26Yrxjz8OX19fo+RPRESNw8KOyMxptVrMmTMH48aNw8bNm5GqVuPo0KHI6d5dd2tWKm3aoMG2traQSv53e1UjEuG0uxv+7t8PFsHBeGzi0yzqiIgEINJqjTTAFRG1ST///DPGjRunW5ZIJIiKikJ0375wqayE97lz8C4ugcsNXbzbqa6pQXFZKYq9vXHJ3x/F9vZwdnPDG2+8wWfqiIgEwn99icxcenq63rJarca///6LjIwMREdFoSQ8HOcBBF6+DI8SORyrqmChVt90f/USCcrs7JDftQvSXFxQKRYj/cIFxO3aBbVajZiYmAa3aomIqHWwY0dk5s6fP48BAwagqKjI4HpHR0dMnjwZXp06oaaqClqVCvbV1XCQK2CpUkGs1UAjEqNOKkW5zBmVNjYQSaWwtrODQ8eOmD59OsrKynT7e//99/HBBx+01tcjIqLrsLAjagfOnz+P3r17o6KiwuD6d999FwsWLIBcLkdhYSEKCwtRVFCAupoaqFUqSKRSWFpbw9XdHW5ubnBzc4NMJoNEIsGDDz6I3377TbcvOzs7nDt3jm/DEhEJgLdiidqB//73vzct6gAgJCQEEokErq6ucHV1RY8ePRq976VLl2LPnj3QaDQAgKqqKsyfPx9r166947yJiKhp2LEjMnMVFRXw8/PTuxUbFBQEd3d3xMfHY/z48fjss89gaWnZ7GM8//zz+Oqrr3TLEokEKSkpCAwMvKPciYioaVjYEZm5uXPnYv78+XqxgwcPYsiQIdBqtU0aw+5mcnJyEBAQgJqaGl1s/Pjx2LZt2x3vm4iIGo/j2BGZsYKCAnz44Yd6sQceeABDhgwB0LSBiW+lc+fOmD59ul5s+/btOHLkiFH2T0REjcOOHZEZmzJlCtatW6dbFovFSE5ORmhoqNGPVVpaCj8/P8jlcl1s8ODBOHjwoNEKSCIiujV27IjMVHp6Oj7//HO92DPPPNMiRR0AODk5Yfbs2Xqxf//9F7t3726R4xERUUPs2BGZqYcffhjbt2/XLVtbWyMjIwOdO3dusWPW1taie/fuyMrK0sVCQ0ORlJQEiaRp05YREVHTsWNHZIaOHDmiV9QBwPTp01u0qAMAKysrLFy4UC925swZfPPNNy16XCIiuoIdOyIzo9Vqcdddd+Hff//VxWQyGTIzM+Hk5NTix9doNIiIiEBSUpIu5uXlhfT0dNja2rb48YmI2jN27IjMzO7du/WKOgCYPXt2qxR1wJUXNJYtW6YXy83NxSeffNIqxycias/YsSMyI2q1Gr169cKZM2d0MV9fX5w9exZWVlatlodWq8Xw4cOxf/9+XczR0RGZmZno2LFjq+VBRNTesGNHZEa++eYbvaIOABYuXNiqRR1wZXy8G7t2ZWVlWLx4cavmQUTU3rBjR2QmlEolAgMDkZubq4v16tULCQkJEIuF+R3uiSeewJYtW3TLlpaWOHv2LLp06SJIPkRE5o4dOyIz8cknn+gVdQCwbNkywYo64Eq30MLCQrdcV1eHOXPmCJYPEZG5Y8eOyAyUlJTAz88PZWVlutiwYcPwxx9/CD7rw/Tp0/VenBCJREhISEDv3r2FS4qIyEyxY0dkBhYvXqxX1AFXunVCF3UA8N5776FDhw66Za1Wi5kzZwqYERGR+WJhR2TiLl68iNWrV+vFHn/8cURGRgqUkT5XV1fMmDFDL7Z37169N2aJiMg4eCuWyMQ9/fTT+O6773TLFhYWSEtLQ7du3QTMSl9VVRUCAgKQn5+vi0VEROD48eOCPgNIRGRu+C8qkQk7efIkNm/erBeLiYlpU0UdANjZ2WHevHl6sYSEBGzdulWYhIiIzBQ7dkQmbMSIEdi3b59uuUOHDsjMzISrq6uAWRmmUqnQs2dPpKWl6WJdu3ZFampqq4+zR0RkrtixIzJRf/75p15RBwAzZsxok0UdAEilUixZskQvduHCBaxfv16gjIiIzA87dkQmSKPRoG/fvkhISNDFPDw8kJGRATs7OwEzuzWtVotBgwbh0KFDuljHjh2RmZkJR0dHATMjIjIP7NgRmaCtW7fqFXUAMG/evDZd1AFXxrBbvny5XqykpAQrVqwQKCMiIvPCjh2RiamtrUVwcDAuXLigiwUFBeHUqVOQSqUCZtZ4Y8aMwS+//KJbtrGxwblz5+Dp6SlgVkREpo8dOyITs379er2iDgCWLFliMkUdcCXf64c5qa6ubvDWLBERNR07dkQmpKysDH5+figpKdHFoqKiEBsb2yZmmWiKl156CV988YVuWSwW4/Tp0wgODhYwKyIi08aOHZEJWbFihV5Rdy1makUdcOWZQBsbG92yRqPBrFmzBMyIiMj0sbAjMhF5eXn46KOP9GIPPfQQoqKiBMroznh6euKNN97Qi+3cuRNxcXECZUREZPp4K5bIRJjjrcvy8nJ069bNLG4tExG1BezYEZmA1NRUfPnll3qx559/3qSLOgBwcHDAnDlz9GKHDh3Se2OWiIgajx07IhNgzsODmMPwLUREbQU7dkRtXFxcXIMO1htvvGEWRR0AWFlZYdGiRXqxtLQ0bNy4UaCMiIhMFzt2RG3YzabgOn/+PBwcHATMzLg0Gg369euH+Ph4XcwUpkgjImpr2LEjasN++eUXvaIOAObMmWNWRR1w5UWQZcuW6cXy8/OxatUqYRIiIjJR7NgRtVEqlQo9e/ZEWlqaLta1a1ekpqbCyspKwMxazogRI7Bv3z7dcocOHZCZmQlXV1cBsyIiMh3s2BG1UV999ZVeUQcAixYtMtuiDgCWLVumN8xJRUUFFi5cKGBGRESmhR07ojaoqqoK/v7+KCgo0MUiIyNx7NgxvTlWzdHTTz+N7777TrdsYWGBtLQ0dOvWTcCsiIhMg3lfIYhM1KpVq/SKOuBKN8vcizoAWLBgASwtLXXL9fX1eO+99wTMiIjIdLBjR9TGFBUVwc/PDxUVFbrYiBEj8PvvvwuYVet68803G0yfduLECURGRgqUERGRaTD/X/+JTMzChQv1ijqRSISlS5cKmFHrmzVrFhwdHfViM2bMAH8PJSK6NRZ2RG3I+fPnsW7dOr3Yk08+id69ewuTkEA6duyImTNn6sX279+v98YsERE1xFuxRG3I448/jh9++EG3bGlpibNnz6JLly7CJSWQ6upqBAQEIDc3Vxfr1asXEhIS2sWzhkREzcF/HYnaiPj4eL2iDgBeeeWVdlnUAVfmw50/f75eLCkpCd9//71AGRERtX3s2BG1AVqtFsOHD8f+/ft1MUdHR2RmZqJjx44CZiYstVqNXr164cyZM7qYr68v0tLSYG1tLWBmRERtEzt2RG3Avn379Io6AJg5c2a7LuoAQCKRYMmSJXqxrKwsrF27VqCMiIjaNnbsiASm0WgQERGBpKQkXczLywsZGRmwsbERMLO2QavV4q677sK///6ri8lkMmRmZsLJyUm4xIiI2iB27IgEtnnzZr2iDgDmz5/Pou4qkUiE5cuX68Xkcnm7GwKGiKgx2LEjElBNTQ26d++O7OxsXSw0NBRJSUmQSCQCZtb2PPzww9i+fbtu2draGunp6fD29hYwKyKitoUdOyIBrV27Vq+oA4ClS5eyqDNg8eLFej+XmpoazJs3T7iEiIjaIHbsiARSWloKPz8/yOVyXWzIkCH4+++/IRKJBMys7YqJicH69et1y2KxGMnJyQgNDRUwKyKitoMdOyKBLF26VK+oA4Bly5axqLuFuXPnws7OTres0WgazFBBRNSesbAjEsClS5fwn//8Ry82fvx4DBgwQKCMTIO7uzvefPNNvdhvv/2Gf/75R6CMiIjaFt6KJboDarUacrkchYWFKCwsRFFBAWqrq6FRqyGWSGBlYwNXd3e4ubnBzc0NMpkMEokEzz//PL766ivdfiQSCVJSUhAYGCjgtzENFRUV8Pf3x+XLl3Wx/v374/Dhw+x2ElG7JxU6ASJTpFAokJSUhFMJCaipqoJWpYJ9dTUc5XLYqFQQa7XQiESol0pxViZDvI0NRFIprO3s4O7tjZ9//llvfy+99BKLukbq0KED3n//fbzyyiu62NGjR7Fjxw6MHz9ewMyIiITHjh1RE+Tl5eFQbCwuZGTAQqmET/YleMjlcKyqgoVafdPt6iUSlNnZIV8mw9lOrqgQiZBx4QJiDx1CRUUFzp07B3d391b8Jqatvr4eISEhOHfunC4WEBCAM2fOwMLCQsDMiIiExcKOqBFUKhXi4uJwPC4O9sXF8M/KRufiYkg0mibtp7auDpcVcpR07ozsgAAU29vD0t4ec+bMgVTKBnpT/Pjjj3j00Uf1YmvXrkVMTIxAGRERCY+FHdFtFBQUYPeuXVDk5CIoIwMBubkQN/OvTVFxMerr6wAAGpEI+d27IzciAh07d8bI0aPZtWsCrVaL/v374/jx47qYm5sbzp07B3t7ewEzIyISDt+KJbqFrKws/LBpE9QpqRh69Ci65+Q0u6irrqnRFXUAINZqEZKfj3uOHoMqJRU/bPoWWVlZxkrd7BmaaqywsBAffvihQBkREQmPHTuim8jKysL2LVvQMSsb/VJSIG3ibdfraQEUXb4MlVqli0kkUnTq1AkiACqxGEdDQyD38cH4xx+Hr6/vnX+BdmLUqFHYs2ePbtne3h7nzp2Dm5ubgFkREQmDHTsiAwoKCvDz1q2QZWVjwJkzd1TUAYBSqdQr6gDAwcEB1wbnkGo0GHj6DGTZ2fh5648oKCi4o+O1J0uXLtUb5qSyshILFiwQMCMiIuGwsCO6gUqlwu5du2Cbl4/+KSnNvvV6vYqKCr1lCwtL2Fhb68XEWi36n0mBTX4e9uzaBZVKvxAkw3r27ImJEyfqxT777DNkZGQIlBERkXBY2BHdIC4uDoqcXESmpt5xpw4A1BoNNBr9oVAcHBwMflaq0SAyJRXy3FwcOnTojo/dXsyfPx9WVla6ZZVKhdmzZwuYERGRMFjYEV0nLy8Px+PiEJSRAQel0ij7lIjFsLCw1C3b2NjCytLypp93VCrRPT0Dx2JjkZ+fb5QczJ2Pjw+mTZumF/vpp59w7NgxgTIiIhIGCzui6xyKjYV9cTECcnONul+Xjh3h6OgEJydnODk53fbzgbm5sC8uRlxsrFHzMGfvvvsunJ2d9WLvvPMO+H4YEbUnLOyIrlIoFLiQkQH/rGyjPFd3PZFIBDtbW9ja2KAxs5mKtVr4ZWXjQno6FAqFUXMxV87Ozpg1a5Ze7ODBg/jvf/8rUEZERK2PhR3RVUlJSbBQKtG5uFjoVAAA3sXFkCqVSE5OFjoVk/HKK6/A29tbLzZjxgyobzHdGxGROWFhRwRArVbjVEICfLIvNXmasJYi0Wjge+kSkuPjWZg0krW1dYOhTk6fPo1vv/1WoIyIiFoXCztqM6RSKXr37q37r7q6usn7uHEmgsaSy+WoqaqCh1yuF1+dnYWRCfF4ICEe404m4lJNzS3380XOpTvavt+Rw3rLHiVX8pLfkNeNVq1ahbq6ult+pjFOnjyJAQMGoEePHoiIiMDff/99x/tsbU899RR69uypF5szZ06z/n8iIjI1LOyozXBycsLJkyd1/9nY2DR5H80p7NRqNQoLC6FVqeBUWamLJ5SX42hZGX7pHY7fIiKxNjgEDlLJLff1RU7OHW1/I8eqKmhVKhQWFt7yc00t7DQ36Ura2dlh8+bNOH36NL777js899xzTcq3LZBIJFi2bJleLCcnB59++qlAGRERtR4WdtSm7d27FwMHDkR4eDieeuopXfHy0ksvITIyEqGhoVi5ciUAYPbs2SgtLUXv3r0xefJkXLx4EX369NHt66233sLXX38NAOjSpQtmzpyJ8PBwHDhwAN999x3WfP45xp44gcXnzwMAiurq4Cy1gIX4yl8TdysrOEotAAD/KhSYkHQSDyUm4K2zaajTaPDRxYuoUKkwOjEB75/LaPL2N/o85xIejT+B/6xdq1eULFq0CD179kRYWBg+/vhjrFmzBnl5eYiKisLo0aMBAN9++y169uyJHj16YMWKFQCAixcvomfPnnjssccQEhJisIMVEBAAPz8/AEBwcDAqKytN8jbw//3f/2Ho0KF6sSVLlty280lEZOpY2FGbca0o6927N1544QUUFxdjxYoVOHDgABITE9GtWzd88cUXAK5MIxUfH4+kpCRs374dly5dwqJFi3Rdv/Xr19/2eN7e3khMTETnzp3x14EDWHD//fg1IgKK+nr8JZcj2skJ56uVuD/+BBZmZuLU1dkj5PX12JCTg009euKX8Ah4W1vjx4ICvNGlCzpIpdgVHoH5/gFN3v56sQoFCmprsb1Xbyx5cDRiY2Nx+vRp7NmzBwcOHMCJEyeQnJyMSZMmYerUqfD09MShQ4ewa9cu5ObmYt68eTh48CBOnDiBLVu2ID4+HgCQmpqKWbNmIS0t7bYd0Z07dyIyMhISSdO6jG2BSCRq0LUrLS3FkiVLBMqIiKh1SIVOgOiaa0XZNb/99huSk5MxcOBAAEBtbS1GjRoFANiyZQs2bNgAtVqNnJwcpKWlNXgb8nYeeeQRAMD+/fuRkZGB98+dg01dHWrUGvSwt8dQmQw7wyNwtLQUh8pK8ezp0/hPUBDqtBqcVVZhQnISAKBOo8HdMlmD/dtLpc3ePrZUgb/lCpwoT0R1yhkopVKkp6cjNjYWzz77rG6WBZmB4x4/fhzDhg3TrXv44YcRGxuLhx56CIGBgQgLC7vtz+b8+fN45513THqokL59+2LChAn48ccfdbFPP/0Ur776Knx8fATMjIio5bCwozZLo9Fg1KhR2Lhxo178/PnzWLNmDQ4fPgxHR0c8/PDDqK2tbbC9VCrVe5bsxs/Y2trqjjNk0CA8JZOh1/kL+vsQiRDt7IxoZ2fIpBb4U16CQU7OuNtZhqWBgbf9Ds3dXqMFXvHxwTg3NyR164qKqCiMGzcOsXc4YPG173wrcrkcDz30ED777DP4+/vf0fGEtmjRIuzYsUM3725tbS3mzJmDb775RuDMiIhaBm/FUps1cOBA/PXXX8jKygIAlJeX48KFC6ioqIC9vT0cHByQk5ODP//8U7eNRCLRPRPWqVMn5OXloaKiApWVlfjjjz8MHmfYsGE4Hh+PsquFX0ldHS7X1eG8Uonsq8+habVapCur4GllhXCHDjhaVorcq2+4VqpUurddJSIR1FcHN27O9tcMcnbCT4UFqFaroRGJIS8tRVlZGe69915s3LhRV6Ree2asQ4cOqLh6q7dfv37Yv38/FAoFamtrsWPHDgwePLhRP/O6ujqMHTsWb775Ju65555GbdOW+fv7Y/LkyXqxb7/9FklJSQJlRETUstixozbL1dUVX3zxBcaPH4+6ujqIxWKsWrUKd999N4KDgxEUFIQuXbpg0KBBum0mTZqEnj17YsiQIVi/fj3eeecdhIeHw8fHp8EQGNeEhoZi7JgxmL91K2xqamAhFmNZQCBqtRrMz8xE5dVCMdTOHk97eMJaIsFC/wC8mpaKeo0GIpEIs7t2g7e1NcZ2csMDCfHo6+iICe7uTd7+miHOMpxTKjEh6SQqU1Nhf/gQnnrmGYwcORLx8fGIiIiAhYUFnn32WUyfPh0vvvgihg4disDAQOzatQtz587FkCFDoNVqMWnSJERERODixYu3/Zn/+OOPOHLkCMrKyrBq1SoAV25Vd+zYsZlnUXhz5szB119/jcqrbzxrtVq8++672LNnj8CZEREZn0jLiRSJsH//fpzduxfDDx8ROpUG/hg4AN1HjMCwYcOETsVkzZ8/H3PnztWLHThwoMGbs0REpo63YokAuLm5odLGBvVt7A3QeokElTY2cHNzEzoVk/bGG280+Bm+8847Nx3Pj4jIVLGwI8KVwk4klaLMzk7oVPSU2dlBJJUavbArKSnRm+Wjd+/e6N+/v1GP0ZbY29tj3rx5erETJ07gp59+EiYhIqIWwluxRLgy+8Ta//wHXokn0bMRz6K1llNduyC3d29MmT7dJMeTa0vq6+vRo0cPpKen62LdunVDamoqLC0tBcyMiMh42LEjwpW3aXtGRCDbxxtqcdv4a6EWi5Hl7Y0wEx0kuK2xsLBoMEDx+fPn8dlnnwmUERGR8bWNKxhRG9CrVy/U29oix8VF6FQAAJdcXKCytW3UgMLUOGPHjsWAAQP0YvPnz0d5eblAGRERGRcLO6KrnJ2d0TUgAOd8faARiQTNRSMSIdPXB10DA+Hs7CxoLuZEJBJh+fLlerHi4mLdfMNERKaOhR3RdaIHD0aliwsyvLwEzSPdywuVLi6Ivm6MPjKOwYMH48EHH9SLffjhh8jPzxcoIyIi42FhR3QdDw8P9I2ORlpAAMobMf1WSyiztcXZwAD0GzQIHh4eguRg7pYuXQrxdc9SKpVKfPDBBwJmRERkHCzsiG4QHR0N585eiA8OhqqVX6RQicWIDwmGzMsLUVFRrXrs9iQkJATPPvusXmzDhg04e/asQBkRERkHCzuiG0ilUowaPRpKT08cDQ1pteftNCIRjoaGoNrDEyNHj4ZUyhn/WtIHH3wA6+umcVOr1Zg1a5aAGRER3TkWdkQGuLu7Y+yjEyD38cHhHqEt3rlTicU43CMUch8fjH10Atzd3Vv0eAR4eXnhtdde04vt2LEDhw8fFiYhIiIj4ADFRLeQlZWFn7f+CNu8PESmpsJBqTT6McpsbREfEoxqD0+MfXQCfH19jX4MMqy0tBR+fn6Qy+W62KBBg/DPP/9AJPCb0UREzcGOHdEt+Pr64rGJT0MSEoy/+vfH2c6djXZrViMSIa1zZ/w9oD8sgoPx2MSnWdS1MicnJ7z33nt6sdjYWPz6668CZUREdGfYsSNqBJVKhbi4OByPi4N9cTH8srLhXVwMSTMmkVeLxbjk4oJMXx9Uurig36BBiIqK4jN1AqmtrUVQUBAuXjeVXHBwMJKTk3lOiMjksLAjaoK8vDwciovDhfR0SJVK+F66BI8SORyrqmChVt90u3qJBGV2dsjvKEOWtzdUtrboGhiIaA5p0iZs3rwZTz31lF5sw4YNeP755wXKiIioeVjYETWDQqFAcnIykuPjUVNVBa1KBfvqajjIFbBUqSDWaqARiVEnlaJc5oxKGxuIpFJY29khLDISYWFhnFGiDdFoNIiMjMTJkyd1MU9PT2RkZMBWoPEMiYiag4Ud0R1Qq9WQy+UoLCxEYWEhigoKUFdTA7VKBYlUCktra7i6u8PNzQ1ubm6QyWSQSCRCp00G7Nu3DyNGjNCLLV68GO+++65AGRERNR0LOyKiq4YPH44///xTt+zg4IDMzEy4uLgImBURUePxrVgioquWLVumt1xeXo5FixYJlA0RUdOxY0dEdJ0nn3wS33//vW7ZwsICZ8+eRdeuXQXMioiocdixIyK6zsKFC2FhYaFbrq+vx5w5cwTMiIio8VjYERFdp2vXrpgyZYpebPPmzUhMTBQoIyKixuOtWCKiGxQXF8PPzw/l5eW62PDhw7Fv3z4BsyIiuj127IiIbuDi4oIZM2boxf744w/88ccfAmVERNQ47NgRERmgVCoREBCAvLw8XSw8PBwnTpyAWMzfiYmobeK/TkREBtja2uKDDz7QiyUmJuKHH34QKCMiottjx46I6CZUKhXCwsKQmpqqi3Xp0gVpaWmwsrISMDMiIsPYsSMiugmpVIolS5boxS5evIh169YJlBER0a2xY0dEdAtarRaDBw9GXFycLtaxY0dkZmbC0dFRwMyIiBpix46I6BZEIhGWL1+uFyspKWkw/RgRUVvAjh0RUSOMGzcOP//8s27ZxsYGGRkZ8PLyEjArIiJ97NgRETXCkiVLIJFIdMvV1dWYN2+ecAkRERnAwo6IqBG6d++O559/Xi/21VdfISUlRaCMiIga4q1YIqJGys/Ph7+/P5RKpS42evRo/PLLLwJmRUT0P+zYERE1koeHB9544w292K5duxAbGytQRkRE+tixIyJqgvLycvj5+aG4uFgXGzhwIOLi4iASiQTMjIiIHTsioiZxcHDA+++/rxc7fPgwdu7cKUxCRETXYceOiKiJ6urqEBwcjPPnz+ti3bt3x+nTpyGVSgXMjIjaO3bsiIiayNLSEosWLdKLnT17Fl9++aVAGRERXcGOHRFRM2g0GvTr1w/x8fG6mLu7O86dOwc7OzsBMyOi9owdOyKiZhCLxQ2mGisoKMDHH38sUEZEROzYERHdkfvvvx+///67brlDhw7IzMyEq6urgFkRUXvFjh0R0R1YunSp3jAnFRUVWLBggYAZEVF7xo4dEdEdmjhxIr799lvdsoWFBVJTU+Hn5ydgVkTUHrGwIyK6Q1lZWQgMDERdXZ0u9uijj+KHH34QMCsiao94K5aI6A75+vri1Vdf1Ytt3boVJ06cECgjImqv2LEjIjICuVwOPz8/lJaW6mL33HMP/vzzT041RkSthh07IiIjkMlkePfdd/ViBw4cwN69ewXKiIjaI3bsiIiMpLq6GoGBgcjJydHFwsLCkJCQAIlEImBmRNResGNHRGQkNjY2mD9/vl4sOTkZmzdvFigjImpv2LEjIjIitVqN3r174/Tp07qYj48Pzp49C2trawEzI6L2gB07IiIjkkgkWLp0qV4sOzsba9asESgjImpP2LEjIjIyrVaLoUOH4uDBg7qYs7MzMjMz4ezsLGBmRGTu2LEjIjIykUiE5cuX68UUCkWDTh4RkbGxY0dE1EIeeeQRbNu2TbdsZWWFjIwMeHt7C5gVEZkzduyIiFrI4sWLIZVKdcu1tbV4//33BcyIiMwdCzsiohYSEBCAl156SS/2zTff4NSpUwJlRETmjrdiiYhaUGFhIfz9/VFZWamLjRo1Cr/99puAWRGRuWLHjoioBbm5ueGtt97Si+3evVvvjVkiImNhx46IqIVVVFTA398fly9f1sX69euHI0eOQCQSCZgZEZkbduyIiFpYhw4dMHfuXL3YsWPH9N6YJSIyBnbsiIhaQX19PUJDQ5GRkaGL+fv7IyUlBRYWFgJmRkTmhB07IqJWYGFhgcWLF+vFzp07hy+++EKgjIjIHLFjR0TUSrRaLQYOHIijR4/qYp06dcK5c+fQoUMHATMjInPBjh0RUSsRiURYtmyZXuzy5cv48MMPBcqIiMwNO3ZERK3sgQcewO7du3XLdnZ2OHfuHNzd3QXMiojMATt2REStbOnSpRCL//fPb1VVFZ5//nkMHjwYPj4+7OARUbOxY0dEJIDnnnsOGzduvOn6U6dOoUePHq2YERGZA3bsiIgE8Mwzz+h17W6UnJzcitkQkbmQCp0AEVF7c+jQIdx///3QaDQ3/Ux1dTWKiopQWFiIwsJCFBUUoLa6Ghq1GmKJBFY2NnB1d4ebmxvc3Nwgk8kgkUha8Vu0P2q1GnK5nOeE2jTeiiUiamUTJ07Et99+a3Cdo6MjevXqhfuGDoWlRAKtSgX76mo4yuWwUKkg1mqhEYlQL5WiTCZDpY0NRFIprO3s0DMiAr169YKzs3MrfyPzplAokJSUhFMJCaipquI5oTaNHTsiolbm6+vbIObu7o5BUVEI6NoVtvX18EtLg29FJRyrqmChVt90X/USCcrs7JAvk+FkSQmOx8Wha0AAogcPhoeHR0t+DbOXl5eHQ7GxuJCRAQulEj7Zl+Ahl/OcUJvGjh0RUSurrq7GhAkT8Ntvv0EikSAqKgrRffvCpbISPhkZ6JiTAyc7e3Swt2/SftViMXJcXHDO1weVLi7oGx2N6OhoSKX8Hb4pVCoV4uLicDwuDvbFxfDPykbn4mJIbnHr/GZ4Tqi1sbAjIhKAVqvF6tWrcfrkSXg4OSEgLQ2e6ekQX/0n2cbaptm37zQiETK8vJAWEABZZy+MHD2aY+Q1UkFBAXbv2gVFTi6CMjIQkJurOyd3gueEWgsLOyIiAWRlZeHnrVthnZsLv8NHYCkv0VtvZWWNjjLZHR2j3NYW8cHBUHp6YuyjEwzeAqb/uXZObPPyEZmaCgel0ujH4DmhlsbCjoiolWVlZWH7li3omJWNfikpkGg0KCsrhfK6QqJjRxdYWVre8bFUYjGOhoZA7uOD8Y8/zkLiJm48J9Jm3HZtLJ4Takks7IiIWlFBQQF+2LQJThcuYuCZM3q3+Wrr6lBTUwNbW1tYGPEZLI1IhMM9QlHapSsem/g0bwHe4FbnpKXwnFBL4QDFREStRKVSYfeuXbDNy0f/lJQGBYSVpSUcHRyMWtQBgFirRf8zKbDJz8OeXbugUqmMun9Tdrtz0lJ4TqilsLAjImolcXFxUOTkIjI1tUVv9Rki1WgQmZIKeW4uDh061KrHbst4TsjcsLAjImoFeXl5OB4Xh6CMjBZ5KL8xHJVKdE/PwLHYWOTn5wuSQ1vCc0LmiIUdEVErOBQbC/viYgTk5gqaR2BuLuyLixEXGytoHm0BzwmZIxZ2REQtTKFQ4EJGBvyzslvtGa6bEWu18MvKxoX0dCgUCkFzERLPCZkrFnZERC0sKSkJFkolOhcXC50KAMC7uBhSpRLJyclCpyIYnhMyVyzsiIhakFqtxqmEBPhkX2rWlFQtQaLRwPfSJSTHx0N9izlPzRXPCZkzFnZE1G64uLjc8T5eeOEFZGZm3nT9qlWrUFdXp1u+6667UFNVBQ+53ODnn0pOxoj4E3gwIQHjTiYipbLyjnNsDI8SOWqqqiC/SV4nTpzA22+/bbTj/fDDDwgLC0Pv3r0xaNAgpKWlGW3fTSWXyw2ek+DYfzE6MQGjEuIxLTUV1VcLrILaWkxNTcGwE8cx7mQipqWmovi6czwzPR3jTibe9rhrs7Nx1/Fj6HfksMH1tzsnRI3Bwo6IqAk2bNgAPz+/m66/sbBbv349tCoVnG5RsH0aFIxfIyLwmLsHll+8cMc5qhvxzJhjVRW0KhUKCwsNru/Tpw9WrFhxx7lcM2rUKCQlJeHkyZOYMWMGZsyYYbR9N1VhYaHBc9JBKsWu8AjsjoiEhViELQX50Gq1iElJwd3OMuzv0xc7eofjaU9PyOvrAQB1Gg2OlpWiTqNBdk31LY87yNkZP/XqfdP1tzsnRI3Bwo6I2rWEhAT069cPPXv2xMSJE1FTUwMA+OWXXxAYGIi+ffvi+eefx1tvvQUAuPvuu3H69Gmo1Wo89dRTCAkJQc+ePbFx40asWbMGeXl5iIqKwujRowEA0dHRsK+uhlSjwbpL2XggIR4PJsRjo4E3MSMdHFBQWwvgSnG25Px5jDuZiAcTErDr8mUAgFKtxpSUFNwffwIz09Nx9/FjqFKrcbS0FBNPJeOFM6fxWHISlGo1ZqSfxbiTiRibmIi4qw/lHyktxQMJ8Rh3/DjWfPYZCgsLcerUKURERKB3797o3bs3Ll++jL///hsPP/wwAKC4uBgPPvggwsLCcPfdd+PixYsAgGeeeQbTp0/HgAEDEBAQgIMHD97059yhQweIRKIr30Gp1P1ZCIWFhbpzcjN9HByRXV2DQ2WlsJWI8ch1M0P0dXREoJ0dACBWoUAfB0eMcnXFnqJbP68X1qEDOt1imjgLtRr21dUs7OiOGHd4cyIiEzNp0iRs2LAB/fv3R0xMDNauXYuYmBhMmzYNcXFxcHd3x7333os+ffrobXfy5ElcuHABKSkpAICysjI4OjpixYoVOHToEOzt7QEAGo0GjnI5/pbLcbi0FDt6h8NSLEbp1Y7P9f6WyzFM1hEA8FNhATpZWmJH73DUqNV4JCkJg52dsa2wAF7WVlgbEoK4UgV2XP5fEXC6shL/jYiEm5UVPrx4EUNlMiwL7A55fT0eT07C7xGR2Jibi3e7dkO0szP+6toVRQUF2LlzJ2JiYvDiiy+iuroaEolEL6958+Zh8ODB+PXXX7F161ZMmzYNu3btAnDltuaRI0dw4MABzJ8/H/v377/pz3rTpk1YsGABqqur8ffffzf9ZBlJUUEBHG9xu1Ol1eIfhRyDnZ2RqVQi9Oq5NGRPcRFGurjC39YWr6SlYrK39x3l5iBXoKig4I72Qe0bO3ZE1G6VlpaitrYW/fv3BwA8/fTT+Pfff3H27FkEBQWhc+fOkEqlGD9+fINtu3Xrhry8PEydOhX79u2Do6PjTY9joVLhUGkpxru5w1J85Z9dJwsL3fpX01Ix9PgxrM+5hKc8PQEAcQoFfiwswOjEBExITkKlWoVLNTVIKK/ASBdXAEC0kzOcrpt+LMLBAW5WVle2L1VgTXY2Ricm4JnTp1CtVqO4vh4RDg5YefEiNuXlQltTg7qaGgwcOBAffvghli5divz8fFje0FWKjY3FU089BQCYMGECjh07pls3ZswYAEBkZKSuk3czEydOREZGBj755BMsWLDglp9tSbXV1bAwMIVXhUqF0YlXnnX0tLLGw263nr+1VqPBsbIyDHJ2ho+NDaQiEc7f4UDHlioV6q52jYmagx07IqIbaBvxjJqzszNOnTqFPXv24OOPP8a+ffuwcuVKQzu77ThpnwYFI8DWFosvnMfC85lYExwCDYAF/v7o5+h04w5vuh8b8f9+V9dotVgfEgova2u9z7zs7Y0hzs74WyHHe3t2492IcLwxcyb69euHX3/9FcOHD8dPP/10y3yvv41qdbWQlEgkjX6bc9y4cXj55Zcb9dmWoFGrDZ6Ta8/YXc/Pxhb7iksM7udvuRzlKhVGxJ8AAFSq1dhTXIRXfHybnZtYq4Ga88bSHWDHjojaLScnJ1hZWeH48eMAgM2bN2PIkCEICgpCWloacnNzoVarsWPHjgbbFhcXQ6PRYMKECZg3bx5OnjwJ4MqzZBUVFf/7oEgEjUiEKCcnbC8sQN3V57puvBUrEonwhm8XnCwvx3mlEoOcnLE5P1/3IkR6VRXUWi3CHRzw36tjrx0uLUXpTYqAaGdnbMrL0y1fe9s2u7oawfb2iPH2QWcnJ8hLS3H+/Hn4+fnh9ddfx3333ae7vXzNoEGD8P333wMAtm3bhn79+jXq53u9jIwM3Z//+OMP+Po2v/i5U2KJBJpGPuMX5eSESrUKO6577u1EWRnSq6qwp7gIK7sH4a++/fBX337Y3rs39tzhuHgakRgSKXsu1Hz8v4eI2g2FQoHOnTvrllesWIGvv/4aMTExqKmpQe/evRETEwNra2usWrUKQ4cOhaOjI4KCguDg4KC3r9zcXDzzzDPQaDSQSqVYtWoVAODFF1/E0KFDERgYqHsOrV4qxd0yGc5UVmLMyURIRSKM7+SGSV5eevu0kUjwnFdnfJWbiw/8/ZFTU4MxiQnQAHC1tMSG0B540sMTb51Nw8iEePSy7wA3S0tYixv+jj7V2wcLz2fiwYR4qLRahNrbY2X3IGzMy8XRsjJIALh17oyQ0FBs3boV3333HSwsLODr64uxY8fqil3gyjN2zzzzDDZt2gSZTIavv/66yT/77777Dtu2bYOFhQWcnZ3xzTffNHkfxmJlY4P6RhZPIpEIa4NDsOD8eay5lA0rsRgBtrZ4p0tXHCktxbKAQN1nfaxtIIEI6VVVupcrrrcq6yK2FxaiXKXC4GNH8ayXF57z6qz3mTqpFJY3dFmJmkKkbcw9ByKidqayshL29vZQq9UYN24cXnzxRTzwwANN3s/+/ftxdu9eDD98xCh5qbRaaLRaWIrFSKqowAeZ57Cjd3iz9vXHwAHoPmIEhg0bZpTcTIWxz4kxtddzQsbDjh0RkQHr1q3D5s2bUVtbi3vvvRejRo1q1n7c3NwQb2ODeokEFgaeQVOpVKiqqoJao4G9nV2DFxdupFSrMenUKai0WliIRZjn59+svOolElTa2MDNza1Z25uy250TobTnc0LGw8KOiMiAt99+2ygzL7i5uUEklaLMzg4u5eW6uFarRUVlJSorK3HthYi62lp0cnOD+BbPfzlIpfg5vHkduuuV2dlBJJUavYjYu3dvg8GHo6OjsWbNGqMe507c7JwYy7zMc0i4Yb9vd+mKwc7Ot9yupc4JtS8s7IiIWpBMJoO1nR3yZTJdEVFTW4uysjKo1fovPmi0Gmg0GohvGEeuJeR3vJKXTCYz6n5HjBiBESNGGHWfxmbonBhTc7uoLXVOqH3hW7FERC1IIpGgZ0QEsn28UQdArlBALi9pUNQBV4YOuXFw4JagFouR5e2NsMjIVjleW3P9OVEbePFECO39nJDxtI3/o4mIzFiPHj1QKZEgxdISNQbnExXB3s4eMmcZWmOirUsuLlDZ2iIsLKwVjtY29erVC/W2tshxcTH6vrUAlNXVqK6uvsWog/p4TshYWNgREbWgo0ePYvjw4Ug6cwbZ/n4Nxk+ztLSCq6srHBwcWmX+VI1IhExfH3QNDITzbZ75MmfOzs7oGhCAc74+jR7TrrEUcjlKSxVQlCqguDpH763wnJAxsbAjImoBCoUCMTExGDhwIE6ePInYQ4dQbG+PvMAr456JRWI4OTqhY8eOsGjFAWnTvbxQ6eKC6EGDWu2YbVX04MGodHFBxg3jCd4JLbSoqf3flGA1NdV6y4bwnJAx8eUJIiIj0mq12Lx5M958801cvnxZFy8oKEDc8eOw6tsXngoF3EViiFv5+a4yW1ucDQxAv0GD4OHh0arHbos8PDzQNzoax2tq4SGXw+EO53kFABFEkEotoFL9b2aR8vIKWLlaG7zNznNCxsaOHRGRkaSlpWHYsGF4+umn9Yq6a8rKyuDk6YmMfv2haeVpo1RiMeJDgiHz8kJUVFSrHrsti46OhnNnL8QHB0NloNCurqlBeXk51FengmsMe3t7vWWVqh7VBopGnhNqCSzsiIjuUHV1Nd577z2EhYXhr7/+arDe1tYWK1aswIkTJ/DYk09C6emJo6EhRn+262Y0IhGOhoag2sMTI0ePhpRzkepIpVKMGj26wTnRaLUoLimBQiFHZVUlCgsLG13c2djYwEJqoRcrr6jA9RM98ZxQS2FhR0R0B/bs2YPQ0FAsWrQI9fX1DdaPGTMGqampeOutt2BhYQF3d3eMfXQC5D4+ONwj1GCXyJhUYjEO9wiF3McHYx+dAHd39xY9nim68ZwoVSoUXb6Murra6z6lhbKqqlH7EwEN5hbWaNSovLo9zwm1JM4VS0TUDDk5OXjttdewfft2g+t9fX3x6aef4sEHHzS4PisrCz9v/RG2eXmITE01yvNdNyqztUV8SDCqPTwx9tEJ8PX1NfoxzMm5c+ew+euv0aGwEMHx8bC9YfBiRwdH2NnZNXp/JSUlqL2uOBSJxLDp0gUJPUJ5TqjFsLAjImoClUqFTz75BHPnzr06HZg+qVSKt956C++9995ti4CCggLs3rULipxcBGVkICA3F2Ij/JOsEYmQ7uWFs4EBkHl5YeTo0ewK3UZeXh5GjRqFvLw8jB41Cl7OzghIS4NnerrunMicZbC2tm70Puvr61FUXATgyjnJCwzExZ494dGtG88JtRgWdkREjXT48GHExMQgKSnJ4PohQ4Zg7dq1CA0NbfQ+VSoV4uLicDwuDvbFxfDLyoZ3cTEkTXhY/xq1WIxLLi7I9PVBpYsL+g0ahKioKD6/1Qhjx47Fzp07AVyZmSIqKgrRffvCpbIS3ufOweXSJXRycoaVlVWT9ltcXo5LLh1xyd8fxfb2OJKQgA0bNsDPz68FvgURCzsiotuSy+WYOXMmvvjiC4PrXVxcsHLlSkycOLHZgwzn5eXhUFwcLqSnQ6pUwvfSJXiUyOFYVQULtfqm29VLJCizs0N+RxmyvL2hsrVF18BARHP4jCaJiIhAYmKiXszd3R3RUVEI7NoVtioVAouK4F1a1qRzcrFzZxTX1SH9wgXEHTqEgoICPPnkk/juu+9a+itRO8XCjojoJrRaLTZt2oS33noLxcXFBj/z4osvYsmSJejYsaNRjqlQKJCcnIzk+HjUVFVBq1LBvroaDnIFLFUqiLUaaERi1EmlKJc5o9LGBiKpFNZ2dgiLjERYWBhnL2iGb7/9FpMmTYKhS6KjoyN69eqFB//v/6BVqZp8Tn755Rd8/PHHevtMSEhAeHh4a309akdY2BERGZCSkoIpU6bg4MGDBteHhYVh3bp1LTb+mFqthlwuR2FhIQoLC1FUUIC6mhqoVSpIpFJYWlvD1d0dbm5ucHNzg0wm4+Txd2jNmjV45ZVXbrr+5MmT8PT0bPI5KS4uhp+fH8qvexnjvvvuw969e1vja1E7w8KOiOg6SqUSCxYswMqVK6FSqRqst7Ozw/z58zFt2jQ+u2ZGtFot+vfvj+PHjxtcb2Njg5KSEtjY2DRr/4sXL8bs2bP1Yn/88QfuvffeZu2P6GZY2BERXfXbb7/h1VdfxcWLFw2uHzduHFatWgVvb+/WTYxa3I8//ohHH31UL/bkk0/izJkzqKqqwtKlSzFu3Lhm71+pVCIgIAB5eXm6WHh4OE6cONHqU8uReWNhR0TtXnZ2NqZPn657K/JGXbp0werVqzFq1KjWTYxaRX19PYKDg5GZmamLBQYG4vTp07CwsLjFlk2zYcMGvPjii3qxzZs344knnjDaMYj4awIRtVv19fVYuXIlQkJCDBZ1FhYWmDVrFs6cOcOizox9/vnnekUdcOXWqTGLOgB45plnEBQUpBebPXs2amtrb7IFUdOxY0dE7VJcXBxiYmJw6tQpg+vvuusurFu3DsHBwa2cGbWmiooK+Pn5oaioSBfr378/Dh8+3Oyha27ll19+wZgxY/Riq1atwvTp041+LGqf2LEjonalpKQEL7zwAgYNGmSwqHN1dcWmTZvw119/sahrB1auXKlX1AHA8uXLW6SoA4DRo0cjOjpaL7ZgwQKUlZW1yPGo/WFhR0TtglarxcaNG9G9e3d8+eWXDdaLRCK8/PLLOHv2LJ5++ukWu7BT21FQUIAPP/xQL/bAAw9gyJAhLXZMkUiE5cuX68VKSkoaxIiai7diicjsnT59GjExMYiNjTW4vlevXli/fj0GDBjQypmRkKZMmYJ169bplsViMZKTk5s0JVxzjRs3Dj///LNu2cbGBhkZGfDy8mrxY5N5Y8eOiMxWVVUVZsyYgfDwcINFnb29PT7++GOcOHGCRV07k56ejs8//1wv9swzz7RKUQdceTnj+gGlq6urMW/evFY5Npk3duyIyCzt2rULr776KrKzsw2uf/jhh7Fq1Sp2SNqphx9+GNu3b9ctW1tbIyMjA507d261HF5++WW94lIsFuP06dN8tpPuCDt2RGRWsrKy8NBDD+Ghhx4yWNR169YNe/bswU8//cSirp06cuSIXlEHANOnT2/Vog4A5s2bB1tbW92yRqPBu+++26o5kPlhYUdEZqG+vh7Lly9HSEgIdu3a1WC9hYUF3nvvPZw+fRr333+/ABlSW6DVavHOO+/oxWQyGWbOnNnquXh4eOCNN97Qi/3yyy+Ii4tr9VzIfPBWLBGZvH///RcxMTE4c+aMwfVDhw7F2rVrGwwOS+3Pb7/9hgcffFAv9uGHHzYosFpLeXk5/Pz8UFxcrIsNHDgQcXFxfDObmoUdOyIyWcXFxXjuuecwZMgQg0Vdp06d8N1332H//v0s6ghqtbpBZ87X1xdTp04VKCPAwcEBc+bM0YsdPnz4ptPbEd0OCzsiMjkajQZffvklunfvjo0bNzZYLxKJEBMTg7S0NDz55JPsfBAA4JtvvmnwC8DChQthZWUlUEZXTJ48Gd26ddOLvfvuu1CpVAJlRKaMt2KJyKScOnUKkydPxqFDhwyuDw8Px/r169GvX79WzozaMqVSicDAQOTm5upivXr1QkJCAsRi4XscP/zwAx5//HG92GeffYaXXnpJoIzIVAn/fzMRUSNUVlbi7bffRnh4uMGirkOHDvjPf/6DY8eOsaijBj799FO9og4Ali1b1iaKOgCYMGECIiMj9WJz585FVVWVQBmRqWob/0cTEd2EVqvFzp07ERISgpUrV0KtVjf4zIQJE5CWloZp06ZBKpUKkCW1ZSUlJViyZIlebNiwYbjvvvsEyqghsVjcYFqxgoICfPzxxwJlRKaKhR0RtVkXL17E6NGjMXbsWFy6dKnBej8/P+zduxdbt26Fp6enABmSKVi8eDHKysr0YsuWLWtzz17ec889GDFihF5s+fLlKCoqEigjMkUs7Iiozamrq8PSpUsREhKC3377rcF6S0tLvP/++zh16lSb6rpQ23Px4kWsXr1aL/b44483uO3ZVtxYcFZUVGDBggUCZkSmhi9PEFGbcvDgQUyZMgUpKSkG1997771Ys2YNAgMDWzkzMkVPP/00vvvuO92yhYUF0tLSGryF2pZMnDgR3377rW7ZwsICqamp8PPzEzArMhXs2BFRm1BUVIRnnnkGd999t8Gizs3NDd9//z327dvHoo4a5eTJk9i8ebNeLCYmpk0XdQCwYMECWFpa6pbr6+vx3nvvCZgRmRIWdkQkKI1Gg88//xzdu3fHN99802C9SCTC1KlTkZaWhscff7zNPRdFbdfMmTNx/U2pDh06mESB5Ovri1dffVUv9sMPP+DEiRMCZUSmhIUdEQkmKSkJgwYNwssvvwyFQtFgfWRkJI4dO4bVq1fDycmp9RMkk7V//37s3btXLzZjxgy4uroKlFHTzJo1C46OjnqxGTNmgE9P0e2wsCOiVldRUYE333wTkZGROHz4cIP1Dg4O+PTTT3H06FH06dNHgAzJlGk0Grzzzjt6MQ8PD7z22mvCJNQMMpkM7777rl7swIEDDYpVohvx5QkiajVarRY7duzA9OnTGwwWe81jjz2Gjz76CB4eHq2cHZmLLVu24IknntCLmeIsDtXV1QgMDEROTo4uFhYWhsTExDYzsDK1PSzsiKhVXLhwAa+88gr27NljcL2/vz/Wrl2L4cOHt3JmZE7q6uoQFBSECxcu6GJBQUE4deqUSQ5evXHjRjz33HN6sU2bNuHpp58WKCNq61jyE1GLqqurw+LFixESEmKwqLOyssK8efNw6tQpFnV0x9avX69X1AHAkiVLTLKoA64MfdKjRw+92HvvvYeamhqBMqK2jh07Imoxf//9N2JiYpCWlmZw/fDhw7FmzRoEBAS0cmZkjsrLy+Hn54fi4mJdLCoqCrGxsSb9NvXu3bvxwAMP6MVWrlyJN998U6CMqC1jx46IjO7y5cuYOHEihg4darCo8/DwwA8//IC9e/eyqCOjWb58uV5Rdy1mykUdAIwcORJDhgzRiy1atMjgm+RELOyIyGg0Gg0+++wzdO/eXW/k/GvEYjFeffVVpKam4tFHHzX5Cy61HXl5efjoo4/0Yg899BCio6MFysh4RCIRli9frhdTKBRYtmyZQBlRW8ZbsURkFImJiYiJicHRo0cNru/bty/WrVvXZufoJNP20ksv4YsvvtAti8VinD59GsHBwQJmZVyPPPIItm3bplu2trZGeno6vL29BcyK2hp27IjojlRUVOD1119Hnz59DBZ1jo6OWLNmDQ4fPsyijlpEWloavvzyS73Y888/b1ZFHQAsXrxY7yWQmpoazJ07V8CMqC1ix46ImkWr1WL79u2YPn068vLyDH7miSeewIcffgh3d/dWzo7ak7Fjx2Lnzp26ZRsbG5w7dw6enp7CJdVCpk6dirVr1+qWxWIxkpKSGrw5S+0XO3ZE1GSZmZkYOXIkHnnkEYNFXWBgIP78809s3ryZRR21qLi4OL2iDgDeeOMNsyzqAOD999+HnZ2dblmj0WDmzJkCZkRtDQs7Imq02tpaLFy4ED169MDvv//eYL2VlRXmz5+P5ORkDBs2TIAMqT3RarUNpg7r2LFjg5g5cXNzw1tvvaUX2717Nw4ePChQRtTW8FYsETXKgQMHMGXKFJw9e9bg+v/7v//D6tWr4efn18qZUXu1c+dOjB07Vi+2atUqTJ8+XaCMWkdFRQX8/f1x+fJlXax///44fPgw3zQnduyI6NYKCwvx1FNPYdiwYQaLOk9PT/z444/Ys2cPizpqNSqVCu+++65erGvXrpg8ebJAGbWeDh06NHhp4ujRo9i+fbtAGVFbwsKOiAxSq9VYu3Ytunfvjs2bNzdYLxaLMX36dKSmpuKRRx5hp4Ba1caNGxsMfr1o0SJYWVkJlFHrevHFF+Hv768XmzVrFurr6wXKiNoK3oologYSEhIwefJkHD9+3OD6fv36Yf369QgPD2/lzIiAqqoqBAQEID8/XxeLjIzEsWPHIBa3n37FTz/9hAkTJujF1qxZgylTpgiUEbUF7edvABHdVllZGaZNm4a+ffsaLOqcnJywbt06HDp0iEUdCWbVqlV6RR0ALF26tF0VdQDw8MMPo1+/fnqxDz74AJWVlQJlRG1B+/pbQEQGabVabN26FcHBwfj000+h0WgafOapp55CWloaJk+eDIlEIkCWREBRUVGDqbTuu+8+3HvvvQJlJBxDU41dvnwZH374oUAZUVvAW7FE7dy5c+cwdepU7Nu3z+D67t27Y926dRg6dGgrZ0bU0GuvvYb//Oc/umWRSISEhAT07t1buKQE9sADD2D37t26ZTs7O2RmZsLNzU3ArEgo7NgRtVM1NTX44IMP0KNHD4NFnbW1NRYuXIikpCQWddQmnD9/Xm/WBQB48skn23VRB1y5DX39y0tVVVWYP3++gBmRkNixI2qH/vzzT0yZMgUZGRkG199///1YvXo1unXr1sqZEd3cE088gS1btuiWLS0tcfbsWXTp0kW4pNqIZ599Fl9//bVuWSqVIiUlBQEBAcIlRYJgx46oHcnPz8fjjz+O4cOHGyzqvLy8sG3bNuzevZtFHbUp8fHxekUdALzyyiss6q6aP38+rK2tdcsqlQqzZs0SMCMSCgs7onZArVZj9erVCAoKwg8//NBgvUQiwRtvvIHU1FSMHz+eY9JRm6LVajFjxgy9mKOjIwuX63h7e2PatGl6sW3btuHo0aMCZURC4a1YIjN34sQJTJ48GfHx8QbXDxgwAOvXr0evXr1aOTOixtm3bx9GjBihF1uyZAlmzpwpUEZtk0KhgJ+fHxQKhS5211134a+//uIva+0IO3ZEZqqsrAyvvPIK+vXrZ7Coc3Z2xmeffYa4uDgWddRmaTSaBt06Ly8vs58PtjmcnZ0xe/ZsvdjBgwexZ88egTIiIbCwIzIzWq0WW7ZsQVBQENasWQNDTflJkyYhLS0NL730Ursb1JVMy/fff4+TJ0/qxebPnw8bGxthEmrjpk6dCh8fH73YzJkzoVarBcqIWhtvxRKZkfT0dEydOhV//vmnwfXBwcFYt24d7rrrrlbOjKjpampqEBQUhKysLF0sNDQUSUlJHCT7FjZt2oRJkybpxb766is8++yzAmVErYm/qhOZgZqaGsydOxc9e/Y0WNTZ2NhgyZIlOHnyJIs6Mhlr167VK+qAK2O2sai7tSeffBJhYWF6sffffx/V1dUCZUStiR07IhO3b98+TJ06FefOnTO4ftSoUfj000/RtWvXVs6M6Aq1Wg25XI7CwkIUFhaiqKAAtdXV0KjVEEsksLKxgau7O9zc3ODm5gaZTIaKigr4+flBLpfr9jN48GAcPHiQLwI0wu+//477779fL7Zs2TK88847AmVErYWFHZGJysvLw+uvv44ff/zR4PrOnTvjk08+wZgxY3ghJEEoFAokJSXhVEICaqqqoFWpYF9dDUe5HBYqFcRaLTQiEeqlUpTJZKi0sYFIKoW1nR0K5XKsXr0aZWVluv0dPnwYAwYMEPAbmQ6tVot7770XBw4c0MWcnJyQmZkJmUwmYGbU0ljYEZkYlUqFtWvX4r333kNFRUWD9RKJBK+//jrmzp0Le3t7ATKk9i4vLw+HYmNxISMDFkolfLIvwUMuh2NVFSxu8RB/vUSCMjs75Do7Ic3FBUqpFBkXLiD20CFER0dj27ZtrfgtTN+JEyfQt29fvdibb76JlStXCpQRtQYWdkQm5NixY5g8eTISExMNro+KisK6desaPF9D1BpUKhXi4uJwPC4O9sXF8M/KRufiYkg0mibtp7S0FBW1NSjp3BnZAQEosbdHn+hojB49GlKptIWyN0+PPfYYtm7dqlu2tLREeno6fH19BcyKWhILOyITUFpailmzZmH9+vUGhy+RyWRYvnw5nn32WQ5fQoIoKCjA7l27oMjJRVBGBgJycyFuxuWlXqVCUVERgCvbakQiFIX2QHZYGGSdvTBy9Gi4u7sbOXvzlZmZieDgYNTX1+tiEydOxDfffCNgVtSSWNgRtWFarRbff/893njjDVy+fNngZ5599lksW7YMrq6urZwd0RVZWVn4eetW2OblIzI1FQ5KZbP3JZfLUVNbo1sWicTo1KkTquztER8cDKWnJ8Y+OoEdpyaYNm0aPv30U92ySCRCYmIiByY3UyzsiNqos2fPYsqUKXoPP18vNDQU69atw+DBg1s5M6L/ycrKwvYtW9AxKxv9UlIgbeJt1+vV1tWhpKRYL9bBvgM6dOgAAFCJxTgaGgK5jw/GP/44i7tGKioqgp+fn94zuf/3f/+H//73vwJmRS2F92yI2pjq6mrMmTMHYWFhBos6GxsbLF26FAkJCSzqSFAFBQX4eetWyLKyMeDMmTsq6gCgorxcb1kslsDuuheApBoNBp4+A1l2Nn7e+iMKCgru6Hjthaura4NhTn7//feb/tJIpo0dO6I25Pfff8fUqVNx/vx5g+tHjx6NTz75hJ0KEpxKpcI3X30FdUoqBicm3nFRV1tXi5KSEr2Yo6Mj7GztGh5bLMY/EeGwCA7GxOee4wsVjVBVVQV/f3+9YjgyMhLHjh3jc7lmhmeTqA3Izc3FI488gvvvv99gUeft7Y2dO3fil19+YVFHbUJcXBwUObmITE2946IOAOrrVXrLEokUtgaKOuBK5y4yJRXy3FwcOnTojo/dHtjZ2WHevHl6sfj4+JuOg0mmi4UdkYBUKhVWrVqFoKAgg2N0SaVSvPPOO0hNTcVDDz0kQIZEDeXl5eF4XByCMjLu6EWJ61lbW0OEawNpi+Dk6IhbDavtqFSie3oGjsXGIj8/3yg5mLvnn38e3bt314vNnj0bdXV1AmVELYGFHZFAjhw5gr59++L1119HZWVlg/WDBg1CYmIili1bBjs7w50LIiEcio2FfXExAnJzjbZPqUSCTp06wdHRCa6urrCysrrtNoG5ubAvLkZcbKzR8jBnUqkUS5Ys0YudP38en332mUAZUUtgYUfUyhQKBSZPnoyoqCicPHmywfqOHTviyy+/xMGDB9GjR4/WT5DoFhQKBS5kZMA/K7tZ49TdikQigZ2tLSwa+cycWKuFX1Y2LqSnQ6FQGDUXczVmzBgMHDhQLzZ//nyU3/DiCpkuFnZErUSr1WLTpk3o3r07PvvsM4MDDT///PM4e/YsnnvuOT7QTG1SUlISLJRKdC4uvv2HW4F3cTGkSiWSk5OFTsUkiEQiLF++XC9WXFyMFStWCJQRGRuvHEStIDU1Fffccw8mTZp0dVR9fT169EBsbCw2bNiAjh07CpAh0e2p1WqcSkiAT/alJk8T1lIkGg18L11Ccnw81LeYh5b+Z9CgQRg9erRe7KOPPuKzimaChR1RC1IqlZg9ezZ69eqFv//+u8F6W1tbLF++HAkJCYiOjm79BMnkSKVS9O7dW/dfdXV1k/dxY8emseRyOWqqquAhl+vFV2dnYWRCPB5IiMe4k4m4VFNzkz1c8UXOpTvavt+Rw3rLHiVX8pLfkNeNVq1aZZQXBSorKzFs2DDY29vjrbfeuuP9CWHJkiV6dwWUSiU++OADATMiY2FhR9RCdu/ejdDQUCxevFhvnsZrxowZg9TUVLz99tuwsLAQIEMyRU5OTjh58qTuPxsbmybvozmFnVqtRmFhIbQqFZyue9knobwcR8vK8EvvcPwWEYm1wSFwkEpuua8vcnLuaPsbOVZVQatSobCw8Jafa2php7lJV9LCwgJz58416duXISEheO655/RiGzZswNmzZwXKiIyFhR2RkeXk5GD8+PF44IEHcPHixQbrfX19sWvXLvz888/w8fFp/QTJ7OzduxcDBw5EeHg4nnrqKV3x8tJLLyEyMhKhoaFYuXIlgCvDW5SWlqJ3796YPHkyLl68iD59+uj29dZbb+Hrr78GAHTp0gUzZ85EeHg4Dhw4gO+++w5rPv8cY0+cwOKr4y0W1dXBWWoBi6vdH3crKzhKr/yi8q9CgQlJJ/FQYgLeOpuGOo0GH128iAqVCqMTE/D+uYwmb3+jz3Mu4dH4E/jP2rV686EuWrQIPXv2RFhYGD7++GOsWbMGeXl5iIqK0t2G/Pbbb9GzZ0/06NFDV6RdvHgRPXv2xGOPPYaQkBCDHVErKysMGTKkWUV1WzJv3jy976BWq/Huu+8KmBEZAws7IiNRqVT46KOPEBQUhB07djRYL5VKMXPmTJw5cwYPPvigABmSObhWlPXu3RsvvPCC7sH3AwcOIDExEd26dcMXX3wBAFi6dCni4+ORlJSE7du349KlS1i0aJGu67d+/frbHs/b2xuJiYno3Lkz/jpwAAvuvx+/RkRAUV+Pv+RyRDs54Xy1EvfHn8DCzEycujofqby+HhtycrCpR0/8Eh4Bb2tr/FhQgDe6dEEHqRS7wiMw3z+gydtfL1ahQEFtLbb36o0lD45GbGwsTp8+jT179uDAgQM4ceIEkpOTMWnSJEydOhWenp44dOgQdu3ahdzcXMybNw8HDx7EiRMnsGXLFsTHxwO48kzsrFmzkJaWZvLF2614eXnhtdde04v9/PPPHPTZxHEeFiIjOHz4MCZPnnzTN/OGDBmCtWvXIjQ0tJUzI3NzrSi75rfffkNycrJuCIva2lqMGjUKALBlyxZs2LABarUaOTk5SEtLg7e3d5OO98gjjwAA9u/fj4yMDLx/7hxs6upQo9agh709hspk2BkegaOlpThUVopnT5/Gf4KCUKfV4KyyChOSkwAAdRoN7pbJGuzfXipt9vaxpQr8LVfgRHkiqlPOQCmVIj09HbGxsXj22Wd1Y+HJDBz3+PHjGDZsmG7dww8/jNjYWDz00EMIDAxEWFhYk35OpmrGjBn4/PPP9aZze+edd/Dvv/9CJLrVENHUVrGwI7oDcrkcM2fO1HVIbuTi4oKVK1di4sSJ/EeSWoRGo8GoUaOwceNGvfj58+exZs0aHD58GI6Ojnj44YdRW1vbYHupVKr3LNmNn7G1tdUdZ8igQXhKJkOv8xf09yESIdrZGdHOzpBJLfCnvASDnJxxt7MMSwMDb/sdmru9Rgu84uODcW5uSOrWFRVRURg3bhxi73DA4mvfuT1wdHTEe++9h9dff10Xi4uLw6+//trgzVkyDbwVS9QMWq0W33zzDbp3737Tou7FF19EWloaJk2axKKOWszAgQPx119/ISsrCwBQXl6OCxcuoKKiAvb29nBwcEBOTg7+/PNP3TYSiUQ3NEinTp2Ql5eHiooKVFZW4o8//jB4nGHDhuF4fDzKrhZ+JXV1uFxXh/NKJbKvPoem1WqRrqyCp5UVwh064GhZKXKvvuFaqVLp3naViERQXx3HsTnbXzPI2Qk/FRagWq2GRiSGvLQUZWVluPfee7Fx40ZdkXrtbdkOHTqg4uqt3n79+mH//v1QKBSora3Fjh07MHjw4GafB1MWExODLl266MVmzpwJlUpleANq09ixI2qilJQUxMTE4J9//jG4PiwsDOvWrUNUVFQrZ0btkaurK7744guMHz8edXV1EIvFWLVqFe6++24EBwcjKCgIXbp0waBBg3TbTJo0CT179sSQIUOwfv16vPPOOwgPD4ePjw969uxp8DihoaEYO2YM5m/dCpuaGliIxVgWEIharQbzMzNRebVQDLWzx9MenrCWSLDQPwCvpqWiXqOBSCTC7K7d4G1tjbGd3PBAQjz6Ojpigrt7k7e/ZoizDOeUSkxIOonK1FTYHz6Ep555BiNHjkR8fDwiIiJgYWGBZ599FtOnT8eLL76IoUOHIjAwELt27cLcuXMxZMgQaLVaTJo0CREREQZfeDKke/fuKCoqQn19PX744QccOXIEnTt3buZZFJaVlRUWLVqEJ598UhdLTU3F119/jRdeeEHAzKg5RFpDw98TUQNKpRILFizAypUrDf4ma2dnh/nz52PatGmQNnJKJCJTsn//fpzduxfDDx8ROpUG/hg4AN1HjMCwYcOETsUkaTQa9OnTB4mJibqYp6cnMjIy2tWtaXPAW7FEjfDbb78hJCQES5cuNVjUjRs3DqmpqXjjjTdY1JHZcnNzQ6WNDeolTRtnrqXVSySotLGBm5ub0KmYLLFYjGXLlunF8vLysGrVKmESomZjYUd0C9nZ2Rg7diwefPBB3TNM1+vSpQt+++03bN++vclvGxKZGjc3N4ikUpTZ2Qmdip4yOzuIpFKjF3YlJSV6s3z07t0b/fv3N+ox2pLhw4dj+PDherFly5ahuI3MC0yNw8KOyID6+nqsXLkSwcHB2LlzZ4P1FhYWmDVrFs6cOaMbWoLI3MlkMljb2SHfwPAhQsrveCUvQ8Oa3ImOHTvqzfJx8uRJHD161KjHaGtu7NqVl5dj0aJFAmVDzcHCjugGcXFxiIiIwNtvvw2lUtlg/V133YWkpCQsWrSIz55QuyKRSNAzIgLZPt5Qi9vG5UMtFiPL2xthkZGQtLFbxKYoPDxc7yUKAFizZg0uXLhwky2orWkbfzOJ2oCSkhK88MILGDRoEE6fPt1gvaurKzZt2oS//voLwcHBAmRIJLxevXqh3tYWOS4uQqcCALjk4gKVrW27GVC4NSxcuBCWlpa65fr6erz33nsCZkRNwcKO2j2NRoONGzeie/fu+PLLLxusF4lEePnll5GWloann36aY9JRu+bs7IyuAQE45+sDjcB/FzQiETJ9fdA1MBDOzs6C5mJOunTpgilTpujFvv/+eyQkJAiUETUFCztq106fPo277roLzz33nN6UOtf06tULhw4dwvr1643+/A6RqYoePBiVLi7I8PISNI90Ly9Uurgg+rox+sg4Zs+eDQcHB73YjBkzBMqGmoKFHbVLVVVVmDFjBsLDww1OP2Rvb4+PP/4YJ06cwIABAwTIkKjt8vDwQN/oaKQFBKBcoOdMy2xtcTYwAP0GDYKHh4cgOZgzFxcXzJw5Uy/2559/3nRmEmo7OEAxtTu//PILpk2bhuzsbIPrH374YaxatQpeAncjiNoylUqFb776CuqUVAxOTIT0uvlmW/zYYjH+iQiHRXAwJj73HMeObCFKpRKBgYHIzc3VxcLDw3HixAmI28jLM9QQzwy1G1lZWXjooYcwZswYg0Vd165dsWfPHvz0008s6ohuQyqVYtTo0VB6euJoaEirPW+nEYlwNDQE1R6eGDl6NIu6FmRra4sPPvhAL5aYmIgtW7YIlBE1Bjt2ZPbq6+vx0UcfYf78+QaHL7GwsMCMGTMwa9Ys2NjYCJAhkenKysrC9i1bIMvORv8zKS3auVOJxTgaGgK5jw/GP/44fH19W+xYdIVKpUKvXr2QkpKii3Xp0gVpaWmwsrISMDO6GXbsyKz9+++/CA8Px8yZMw0WdUOHDkVycjIWLFjAoo6oGXx9fTH+8cdR2qUr/g0Pb7Fn7spsbfFPRDhKu3RlUdeKpFIplixZohe7ePEi1q5dK1BGdDvs2JFZKi4uxjvvvIONGzcaXN+pUyd89NFHeOKJJzh8CZERFBQUYPeuXVDk5CIoIwMBubkQG+HyohGJkO7lhbOBAZB5eWHk6NFwd3c3QsbUWFqtFkOGDNF70UwmkyEzMxNOTk7CJUYGsbAjs6LRaPDVV19hxowZkMvlDdaLRCJMnjwZixYt4rhXREamUqkQFxeH43FxsC8uhl9WNryLiyFpxu1ZtViMSy4uyPT1QaWLC/oNGoSoqCg+UyeQw4cPIyoqSi/27rvvYvHixQJlRDfDwo7MxqlTpzB58mQcOnTI4Prw8HCsX78e/fr1a+XMiNqXvLw8HIqLw4X0dEiVSvheugSPEjkcq6pgoVbfdLt6iQRldnbI7yhDlrc3VLa26BoYiGgOadImjB8/Hjt27NAt29jYICMjgy+btTEs7MjkVVZWYt68eVi1ahXUBi4aHTp0wMKFCzFlyhT+tk/UihQKBZKTk5EcH4+aqipoVSrYV1fDQa6ApUoFsVYDjUiMOqkU5TJnVNrYQCSVwtrODmGRkQgLC2NnvQ05e/YsQkND9f6dff7557FhwwYBs6IbsbAjk6XVarFz505MmzYNOTk5Bj8zYcIEfPzxx/D09Gzl7IjoGrVaDblcjsLCQhQWFqKooAB1NTVQq1SQSKWwtLaGq7s73Nzc4ObmBplMBolEInTaZMDkyZPx2Wef6ZbFYjFOnTqFkJAQAbOi67GwI5N04cIFvPrqq9i9e7fB9X5+flizZg1GjBjRypkREZmv/Px8+Pv7640y8OCDD2LXrl0CZkXX43AnZFLq6uqwZMkShIaGGizqLC0t8f777+PUqVMs6oiIjMzDwwNvvvmmXuzXX3/Fv//+K1BGdCN27MhkHDx4EDExMUhNTTW4ftiwYVi7di0CAwNbOTMiovajoqICfn5+KCoq0sUGDBiAQ4cOcfioNoAdO2rzLl++jEmTJuHuu+82WNS5ubnh+++/xx9//MGijoiohXXo0AHvv/++XuzIkSP4+eefBcqIrseOHbVZGo0GGzZswMyZM6FQKBqsF4lEmDJlChYuXMhBMomIWlFdXR1CQkKQmZmpiwUGBuL06dOwsLAQMDNix47apKSkJAwaNAgvv/yywaIuMjISR48exerVq1nUERG1MktLSyxatEgvlp6eji+//FKgjOgaduyoTamoqMDcuXPxySefGByTzsHBAYsWLUJMTAyHQyAiEpBGo0H//v1x4sQJXczNzQ3nzp2Dvb29gJm1b+zYUZug1Wqxfft2BAcH4+OPPzZY1D322GNIS0vDK6+8wqKOiEhgYrEYy5cv14sVFhbi448/FigjAtixozbg/PnzeOWVV/Df//7X4Hp/f3+sXbsWw4cPb+XMiIjodkaOHKn377e9vT0yMzPRqVMnAbNqv9ixI8HU1tZi0aJFCA0NNVjUWVpaYt68eTh16hSLOiKiNmrp0qV6w5xUVlZiwYIFAmbUvrFjR4L466+/MGXKFKSlpRlcP3z4cKxZswYBAQGtnBkRETXVpEmTsGnTJt2yVCpFamoq/P39BcyqfWLHjlpVYWEhnn76adxzzz0Gizp3d3f88MMP2Lt3L4s6IiITsWDBAlhZWemWVSoVZs+eLWBG7RcLO2oVGo0G69evR1BQEL777rsG68ViMV599VWkpaXh0Ucf5ejlREQmxMfHB6+++qpe7Mcff8Tx48cFyqj94q1YanGJiYmYPHkyjh07ZnB9nz59sH79ekRGRrZyZkREZCxyuRx+fn4oLS3VxYYOHYr9+/fzl/VWxI4dtZjy8nK89tpr6NOnj8GiztHREWvWrMGRI0dY1BERmTiZTIZZs2bpxf766y/8/vvvAmXUPrFjR0an1Wqxbds2vPbaa8jLyzP4mSeeeAIffvgh3N3dWzk7IiJqKdXV1QgMDEROTo4u1rNnTyQmJnL80VbCjh0ZVWZmJkaOHIkJEyYYLOoCAwPx559/YvPmzSzqiIjMjI2NTYOhTk6dOmXw2WpqGezYkVHU1tZi+fLlWLx4MWpqahqst7KywuzZs/HOO+/ovTlFRETmRa1WIzw8HKdOndLFvL29kZ6eDmtrawEzax/YsaM7duDAAYSFheH99983WNSNGDECp0+fxpw5c1jUERGZOYlEgqVLl+rFLl26hNWrVwuUUfvCjh01W0FBAd588018//33Btd7enpi1apVePjhh/lGFBFRO6LVanHPPffg77//1sWcnZ2RmZkJZ2dn4RJrB9ixoyZTq9VYu3YtgoKCDBZ1YrEY06dPR2pqKh555BEWdURE7YxIJMLy5cv1YgqFAkuWLBEoo/aDHTtqkvj4eMTExNx00Ml+/fph/fr1CA8Pb+XMiIiorZkwYQJ++ukn3bKVlRXS09Ph4+MjYFbmjR07apSysjJMmzYN/fr1M1jUOTk5Yd26dTh06BCLOiIiAgAsXrwYUqlUt1xbW4u5c+cKmJH5Y2FHt6TVarF161YEBwfj008/hUajafCZp556CmlpaZg8eTLHKSIiIh1/f3+8/PLLerFvvvlG741ZMi7eiqWbysjIwCuvvIJ9+/YZXN+9e3esXbsW99xzTytnRkREpuLy5cvw8/NDZWWlLjZy5Ejs3r1bwKzMFzt21EBNTQ0++OAD9OzZ02BRZ21tjYULFyIpKYlFHRER3VKnTp3w9ttv68X27Nmj98YsGQ87dqTnjz/+wNSpU5GRkWFw/f3334/Vq1ejW7durZwZERGZqsrKSvj7+6OwsFAX69u3L44ePcqRE4yMHTsCAOTn5+Pxxx/HfffdZ7Co8/LywrZt27B7924WdURE1CT29vYNXpo4fvy43huzZBzs2LVzarUa69atw+zZs1FeXt5gvUQiwbRp0/DBBx+gQ4cOAmRIRETmoL6+Hj169EB6erou5ufnh9TUVFhYWAiYmXlhx64dO3HiBPr3749XX33VYFE3YMAAnDhxAh999BGLOiIiuiMWFhZYvHixXiwzMxOff/65QBmZJ3bs2qGysjLMnj0ba9euhaHT7+zsjKVLl+KFF16AWMzan4iIjEOr1SIqKgpHjhzRxVxdXZGZmckGgpHwqt2OaLVabNmyBUFBQVizZo3Bom7ixIlIS0vDSy+9xKKOiIiMSiQSYdmyZXqxoqIirFy5UqCMzA87du1Eeno6pkyZgv379xtcHxwcjHXr1uGuu+5q5cyIiKi9efDBB/Hbb7/plu3s7HDu3Dm4u7sLmJV5YEvGzNXU1GDu3Lno2bOnwaLO2toaixcvxsmTJ1nUERFRq1i6dKneXaGqqirMnz9fwIzMBzt2Zmzv3r2YOnUqMjMzDa4fNWoUPv30U3Tt2rWVMyMiovbu+eefx1dffaVblkgkSElJQWBgoIBZmT4WdmYoLy8Pr7/+On788UeD6zt37oxPPvkEY8aM4cCQREQkiJycHAQEBKCmpkYXGz9+PLZt2yZgVqaPt2LNiEqlwieffIKgoCCDRZ1EIsGbb76J1NRUjB07lkUdEREJpnPnznjttdf0Ytu3b9d7Y5aajh07M3Hs2DFMnjwZiYmJBtcPHDgQ69evR1hYWCtnRkREZFhpaSn8/Pwgl8t1scGDB+PgwYNsPjQTO3YmrrS0FFOmTMGAAQMMFnUymQxffPEFYmNjWdQREVGb4uTkhNmzZ+vF/v33X703Zqlp2LEzUVqtFt9//z3eeOMNXL582eBnnnnmGSxfvhyurq6tnB0REVHj1NbWonv37sjKytLFQkJCkJycDIlEImBmpokdOxOUlpaGe++9F0899ZTBoi4kJAQHDx7Exo0bWdQREVGbZmVlhYULF+rFUlJS8M033wiUkWljx86EVFdXY/HixVi2bBnq6+sbrLexscHcuXPx+uuvw9LSUoAMiYiImk6j0SAiIgJJSUm6mJeXF9LT02FraytgZqaHHTsT8d///hc9evTAwoULDRZ1Dz74IFJSUjBjxgwWdUREZFLEYnGDqcZyc3PxySefCJSR6WLHro3Lzc3Fa6+9dtNxfby9vfHpp5/ioYceauXMiIiIjEer1WL48OF6syQ5OjoiMzMTHTt2FDAz08KOXRulUqmwatUqBAUFGSzqpFIp3n77baSkpLCoIyIikycSiRp07crKyrB48WKBMjJN7Ni1QUeOHEFMTAxOnjxpcH10dDTWrVuHnj17tm5iRERELeyJJ57Ali1bdMuWlpY4e/YsunTpIlxSJoQduzZEoVBg8uTJiIqKMljUdezYEV9++SX++ecfFnVERGSWFi1aBAsLC91yXV0d5syZI2BGpoWFXRug1WqxadMmdO/eHZ999hkMNVGfe+45pKWl4bnnnoNYzNNGRETmqWvXrpgyZYpebPPmzTe9i0X6eCtWYKmpqZgyZQr+/vtvg+t79OiBdevWYdCgQa2bGBERkUCKiorg5+eHiooKXey+++7D3r17odVqOd3YLbD1IxClUolZs2ahV69eBos6W1tbLF++HAkJCSzqiIioXXF1dcWMGTP0Yvv27cOjjz4KT09PBAQE4N9//xUou7aNHTsB7N69G6+88gouXrxocP2YMWPwn//8Bz4+Pq2bGBERURtRVVWFgIAA5OfnG1wfFBSE1NTUVs6q7WPHrhXl5ORg/PjxeOCBBwwWdb6+vti1axd+/vlnFnVERNSu2dra4v/+7/9uuj4tLQ0ajaYVMzINUqETaA1qtRpyuRyFhYUoLCxEUUEBaquroVGrIZZIYGVjA1d3d7i5ucHNzQ0ymcyoEw+rVCp88skneP/991FVVdVgvVQqxZtvvok5c+bAzs7OaMclIiIyVdOnT8fGjRtv+RmlUonq6mrBru9tkVnfilUoFEhKSsKphATUVFVBq1LBvroajnI5LFQqiLVaaEQi1EulKJPJUGljA5FUCms7O/SMiECvXr3g7Ox8RzkcPnwYkydPRnJyssH1gwcPxrp16xAaGnpHxyEiIjIXKpUKdnZ2qKurM7je0dERvXr1wtgHHkB9TY0g1/e2yiwLu7y8PByKjcWFjAxYKJXwyb4ED7kcjlVVsFCrb7pdvUSCMjs75MtkyPbxRr2tLboGBCB68GB4eHg0KQe5XI6ZM2fiiy++MLjexcUFK1aswKRJk/h2DxER0Q1CQkIaPEPn7u6OQVFRCOjaFbb19QgqkcNLoWjV63tbZ1aFnUqlQlxcHI7HxcG+uBj+WdnoXFwMSTPuwavFYuS4uOCcrw8qXVzQNzoa0dHRkEr/d/daq9WiuLgYrq6uerFNmzbhrbfeQnFxscF9v/DCC1i6dCnnviMiIrqJa1Nmnjt3DhKJBFFRUYju2xculZXwychAx5wceLi4QtrEW6uNub6bMrMp7AoKCrB71y4ocnIRlJGBgNxciI3w1TQiETK8vJAWEABZZy+MHD0a7u7uuHjxIkaMGIH09HQMHjwYv/76K3JycjBlyhT8888/BvfVs2dPrF+/HlFRUXecFxERkbmrrKzEO++8g/qaGng5OyMgLQ2e6em667urqysspBa32YthN7u+mzqzKOyysrLw89atsM3LR2RqKhyUSqMfo9zWFvHBwVB6euKhRx7GpEmT9MbQ6d27N06fPg2VStVgWzs7O3zwwQeYNm2a3jQpREREdHPXru+Wly6hW9wh2JSX6a13cXGF5R1eV6+/vo99dAJ8fX3vaH9CM/nCLisrC9u3bEHHrGz0S0mBtAVffVaJxTgaGoJcV1d8/vXXyM7Ovu02Y8eOxX/+8x94e3u3WF5ERETm5sbru0ilQklJCVSqegCAWCyBu5ubUY517fou9/HB+McfN+nizqQLu4KCAvywaROcLlzEwDNnjHLr9XZUWi0O+HXDBScnfPvDD7h8+bLBz3Xp0gWrV6/GqFGjWjwnIiIic3Kr63t1TTXUag3s7OxgzFcPNSIRDvcIRWmXrnhs4tMme1vWZAcoVqlU2L1rF2zz8tE/JaVVijoAqCgrQ/CRI/BUVmP0yJENxsMRi8V49913cebMGRZ1RERETXS767uNtQ3sjVzUAYBYq0X/Mymwyc/Dnl27DD5aZQpMtrCLi4uDIicXkampLXr79XrVNTWorlZColYjKP4EvGSyBi9CWFlZ4bXXXoOtrW2r5ERERGROhLi+XyPVaBCZkgp5bi4OHTrUqsc2FpMs7PLy8nA8Lg5BGRkt8qLEzZSVlur+bFdejoC0NET37avXrq2ursb+/ftbLSciIiJzIdT1/XqOSiW6p2fgWGzsTeepbctMsrA7FBsL++JiBOTmtupxNTe0gz3T0+FSWYno67p2YrGYs0gQERE1g1DX9xsF5ubCvrgYcbGxgubRHCZX2CkUClzIyIB/VnarPVd3ja2Njd6yWKuF97lz6N6tGyIjI/F///d/2L59O8LCwlo1LyIiIlMn5PX9RmKtFn5Z2biQng6FQiFoLk1lcsMsJyUlwUKpROebzOrQkpycnGBrZwdVfT0sLCwglkjQSaVGrqUlPvzwQ9x1112tnhMREZE5EPL6boh3cTFOK5VITk42qeu7SXXs1Go1TiUkwCf7UrOmCTMGSwsL2NrawsLCAhKxGFKNBr6XLiE5Ph7qW8xTR0RERIa1hev7jSQmen1vVmHn4uJyxwd+4YUXkJmZedP1q1atQl1dnW556NChkMvlqKmqgodc3uDzTyUnY0T8CTyYkIBxJxORUll5xzk2lkfJlby2bt2Ke+65B2FhYfjhhx8AACdOnMDbb79ttGMdO3YMffr0gYWFBX777Tej7ZeIiIjXd33Xru9yA3kZ+/r+3XffoWfPnggLC8O9996LnJycZu2nWQMUu7i43HSCe2Pp0qULTp8+DXt7e13s9OnT2PPTT3jw74MNXoF+KjkZ7/v5IdDODj8WFGBPcRG+7tHzjnJQa7WQiG49Uo4WQGV9PX4dMgQ/7dmNM2fOALjyEkVeXh7cjDQq9jU5OTkoKSnBhx9+iAkTJuCBBx4w6v6JiKj94vVdX71Egt/uGoKRjzyCHj163NExb+fw4cMIDg6Gk5MTPv/8c8TGxmLTpk1N3o/RnrFLSEjA5MmTUV1djfDwcHz++eewtrbGL7/8grfffhuOjo4ICwuDs7MzVq5cibvvvhurV69GcHAwJk2ahISEBEgkErzxxhtQKpXIy8tDVFQUunTpgl27dsHFxQVbt26FfXU1vsi6iN1FRRABGOfmjme9vPRyiXRwwFe5VypdtVaL5Rcu4Hh5Geo1WrzYuTNGd+oEpVqNt86exYVqJXp1cMCRslLsjojE6YoKrLmUDUuxGGUqFb7p0RMfZJ5DhlIJrRZ4q0sXRDs743BpKRacOwetVgMJgOd694adnZ0uB41Gg+3bt8PJyQnffvst1qxZA7lcjhkzZiA3NxdOTk5Yvnw5OnfujLfffhsdOnRAUlISFAoFlixZgv79+9/0Z92hQwdUVVWhoKAA58+fN9YpJCKidk6j0TS4rpw+fRpz5sxBTU0NQkJCsHjxYlhZWeGPP/7A0qVL0aFDBwQFBcHBwQGzZs3CE088gblz58Lf3x9vv/02zpw5A7FYjOeffx7V1dXIy8tDnz590LlzZ3z++efo06cPvvjiC9hVVWH9hQv4b3ExRCJgrGsnTPL0hBZaXOtBtcb1/UhpKRaez4QIIliIRYjp1xeHDx/GxIkTobladO7btw8pKSlYvXo1tm3bhuLiYjz77LPIysqCTCbD119/jS5duuCZZ56Bo6Mjjh49ipKSEmzYsOGmz+sNHDhQ9+e+ffti69atzTqHRivsJk2ahA0bNqB///6IiYnB2rVrERMTg2nTpiEuLg7u7u6499570adPH73tTp48iQsXLiAlJQUAUFZWBkdHR6xYsQKHDh3Sq+iLCgpwISkJh0tLsaN3OCzFYpTW1zfI5W+5HMNkHQEAPxUWoJOlJXb0DkeNWo1HkpIw2NkZ2woL4GVthbUhIYgrVWDH5ULd9qcrK/HfiEi4WVnhw4sXMVQmw7LA7pDX1+Px5CTsCY/AZxfOY7KzE/rY2qJSrUZueRkUN7Rqp06dqvuzn59fgzxvdnKfeOKJ2/24AQA7duxo1OeIiIgay9D16pr09HTs3LmzQfzUqVMAgC+//BIAMHLkyAafmTFjhu7PZ8+exdmzZ3XH2v7jj+hYWIhDxUVY4+EBS5EI5Wo1Ll8uRF1dHYpLStBZLG7x6/vvEZHYmJuLd7t2Q7SzMypUKqTIFfj2p58QExODF198EdXV1Q1mnZo3bx4GDx6MX3/9FVu3bsW0adOwa9cuAIBcLseRI0dw4MABzJ8/v1Fj3X799de47777bvs5Q4xS2JWWlqK2tlbXZXr66aexYsUK3HPPPQgKCkLnzp0BAOPHj0dWVpbett26dUNeXh6mTp2Khx566JZfpLa6Gqk5ORjv5g5L8ZXHA50sLHTrX01LRZ1Gg0q1GrvCIwAAcQoF0pVK/FJ0ZU7XSrUKl2pqkFBegZeu5hXt5Awn6f9+FBEODnCzsrqyfakCf8tLsPbSJQBAtVqNiwoFQq2s8HlJCbLq6nC3vT2kdXXwcHNDxrlzzf9BEhERtUPWVlY4m5uL/+vQAZZXb5E6XFc8vV+Qj7r8PNQALXp9L66vR4SDA1ZevIjMaiX+z8UVlioVunXpgg8//BAlJSWYMGECunXrppd/bGws9uzZAwCYMGECpk+frls3ZswYAEBkZCQuXrx425/Fzp07cfjwYfz777+N/fHpadHhThrz+J6zszNOnTqFPXv24OOPP8a+ffuwcuVKg5/VqNXALfb5aVAwAmxtsfjCeSw8n4k1wSHQAFjg749+jk43ZnfT/diI//dOiUarxfqQUHhZW+ti5RUVeNLZGf1tbXFYqcSU3Fy8ExKC7v7++Ccu7rbfmYiIiP5HKhZDdIvr+wdubuhmZY2vqpUten0HgJe9vTHE2Rl/K+SYkHQS7/t4o9/Ae/HmO+/g119/xfDhw/HTTz/d8vuIrnt+z+pqISmRSG77du3x48cxc+ZMHDhwQLddUxlluBMnJydYWVnh+PHjAIDNmzdjyJAhCAoKQlpaGnJzc6FWqw3eOiwuLoZGo8GECRMwb948nDx5EsCV58gqKir0k5VI0NPTE9sLC1B39T73jbdiRSIR3vDtgpPl5TivVGKQkzM25+dDffV/mPSqKqi1WoQ7OOC/Vx8QPVxaitKbTPYb7eyMTXl5uuWUykp06NABhRoN/K2s8LSzM3wtLFCkVEJx3ZRjRERE1DgqjQbBbm74vaICdVev1+XXFUESsQTOzs54s4Wv7wCQXV2NYHt7xHj7wM/WFoWVVShRKODn54fXX38d9913n+7xsWsGDRqE77//HgCwbds29OvXr8k/g4sXL+LJJ5/Ejz/+CE9PzyZvf02zOnYKhUJ3exUAVqxYga+//hoxMTGoqalB7969ERMTA2tra6xatQpDhw6Fo6Oj7uHK6+Xm5uKZZ56BRqOBVCrFqlWrAAAvvvgihg4disDAQN19aisbG/To0gXVGecw5mQipCIRxndyw6QbXp6wkUjwnFdnfJWbiw/8/ZFTU4MxiQnQAHC1tMSG0B540sMTb51Nw8iEePSy7wA3S0tYixvWuVO9fbDwfCYeTIiHSqtFqL09VnYPwq6aGhwpLQU0GnS3tISvuzv23HCid+7cCWtra6xfvx7ff/89iouL8dJLLyEnJwfOzs74/PPP4evri5deegljxozByJEjUVlZiT59+iAtLc3gz/7UqVMYO3YsSktLYWNjAz8/P/z9999NO4FEREQG2Nvb681/vnjxYvj7+2P69Omora1FWFgYVq9erXs5cs6cOXBwcED37t3h5+eHWbNmYcSIEfjoo4+gVqvx0ksvQavVQiKRYMWKFYiOjsbatWvx+eefw9/fH9u2bYO3tzeG3XsvtJaW0ObkYEp+AaRiEcZ16oRJ7h6wKipCx44dYXO1s9bS1/eNebk4WlYGCYCeHTqgi4cH9iQmokePHrCwsICvry/Gjh2ra2YBV56xe+aZZ7Bp0ybdyxNNtXDhQpSUlGDixIkAgK5du+Lnn39u8n6aNdxJU1RWVsLe3h5qtRrjxo3Diy++2OwhOvbv34+ze/di+OEjd5yXSquFRquFpViMpIoKfJB5Djt6hzdrX3X1dfi9Tx/sSU3FgQMHAFxpvebn58PZ2fmOcyUiImpr2sP1HQD+GDgA3UeMwLBhw+44t9bQ4lOKrVu3Dps3b0ZtbS3uvfdejBo1qtn7cnNzQ7yNDeolEljc4SjQSrUak06dgkqrhYVYhHl+/s3el8jaBuqOHTF16lR4enqiqKgIb731Fos6IiIyW+3h+l4vkaDSxsboY9K2pBbv2BlTUVERvl6/HoOOHIVLebnQ6egUOzggdkB/PDN5MlxdXY2yz7179+q9Gg4A0dHRWLNmjVH2T0RE1Fbw+m6863uLd+yMSSaTwdrODvkyWZs68fkdr+Qlk8mMts8RI0ZgxIgRRtsfERFRW8Xru/EY5a3Y1iKRSNAzIgLZPt5QG3gQUghqsRhZ3t4Ii4xsMGAhERER3R6v78bTNn56TdCrVy/U29oixwgTFRvDJRcXqGxtERYWJnQqREREJovXd+MwucLO2dkZXQMCcM7XB5pGTODbkjQiETJ9fdA1MJAvShAREd0BXt+Nw+QKOwCIHjwYlS4uyLhh/LrWlu7lhUoXF0QPGiRoHkREROaA1/c7Z5KFnYeHB/pGRyMtIADltraC5FBma4uzgQHoN2gQPDw8BMmBiIjInPD6fudMsrADrrwa7NzZC/HBwVC18oOWKrEY8SHBkHl5ISoqqlWPTUREZM54fb8zJlvYSaVSjBo9GkpPTxwNDWm1+/EakQhHQ0NQ7eGJkaNHQyo1qRFjiIiI2jRe3++MyRZ2AODu7o6xj06A3McHh3uEtnhlrxKLcbhHKOQ+Phj76AS9+fSIiIjIOHh9bz6TmnniZrKysvDz1h9hm5eHyNRUOCiVRj9Gma0t4kOCUe3hibGPToCvr6/Rj0FERET/w+t705lFYQcABQUF2L1rFxQ5uQjKyEBAbi7ERvhqGpEI6V5eOBsYAJmXF0aOHm3SlTwREZEp4fW9acymsAMAlUqFuLg4HI+Lg31xMfyysuFdXAyJRtPkfanFYlxycUGmrw8qXVzQb9AgREVFmew9dyIiIlPF63vjmVVhd01eXh4OxcXhQno6pEolfC9dgkeJHI5VVbBQq2+6Xb1EgjI7O+R3lCHL2xsqW1t0DQxEtIm+8kxERGROeH2/PbMs7K5RKBRITk5Gcnw8aqqqoFWpYF9dDQe5ApYqFcRaDTQiMeqkUpTLnFFpYwORVAprOzuERUYiLCzM5EacJiIiMne8vt+cWRd216jVasjlchQWFqKwsBBFBQWoq6mBWqWCRCqFpbU1XN3d4ebmBjc3N8hkMpOa8JeIiKg94vW9oXZR2BERERG1ByY9jh0RERER/Q8LOyIiIiIzwcKOiIiIyEywsCMiIiIyEyzsiIiIiMwECzsiIiIiM8HCjoiIiMhMsLAjIiIiMhMs7IiIiIjMBAs7IiIiIjPBwo6IiIjITLCwIyIiIjITLOyIiIiIzAQLOyIiIiIzwcKOiIiIyEywsCMiIiIyEyzsiIiIiMwECzsiIiIiM8HCjoiIiMhMsLAjIiIiMhMs7IiIiIjMBAs7IiIiIjPBwo6IiIjITLCwIyIiIjITLOyIiIiIzAQLOyIiIiIzwcKOiIiIyEz8P1fmRm7urk3xAAAAAElFTkSuQmCC", - "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 new file mode 100644 index 00000000..84a869af --- /dev/null +++ b/Tutorial/2_Search_Spaces.ipynb @@ -0,0 +1,6790 @@ +{ + "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": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sampled hyperparameters\n", + "{'metric': 'euclidean', 'n_jobs': 1, 'n_neighbors': 10, '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': (1, 10),\n", + " 'weights': Categorical(\"weights\", ['uniform', 'distance']),\n", + " 'p': (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", + "| 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", + "\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", + "| 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", + "| 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": 16, + "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.\n", + "\n", + "Note that ConfigurationSpace does not support None as a parameter. Instead, use the special string \"\\\". TPOT will automatically replace instances of this string with the Python None." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sampled hyperparameters\n", + "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 6, 'p': 3, 'weights': 'distance'}\n", + "mutated hyperparameters\n", + "{'metric': 'euclidean', 'n_jobs': 1, 'n_neighbors': 6, 'p': 1, '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": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "original hyperparameters for individual 1\n", + "{'metric': 'euclidean', 'n_jobs': 1, 'n_neighbors': 7, 'p': 2, 'weights': 'uniform'}\n", + "original hyperparameters for individual 2\n", + "{'metric': 'minkowski', 'n_jobs': 1, 'n_neighbors': 1, 'p': 2, 'weights': 'uniform'}\n", + "\n", + "post crossover hyperparameters for individual 1\n", + "{'metric': 'euclidean', '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': 1, '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": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
KNeighborsClassifier(metric='euclidean', 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(metric='euclidean', n_jobs=1, n_neighbors=7)" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "knn_individual1.export_pipeline()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If a dictionary of parameters is passed instead of of a ConfigSpace, then the hyperparameters will be fixed and not learned." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
KNeighborsClassifier(n_neighbors=10)
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_neighbors=10)" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "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", + "space = {\n", + "\n", + " 'n_neighbors':10,\n", + "}\n", + "\n", + "knn_node = tpot2.search_spaces.nodes.EstimatorNode(\n", + " method = KNeighborsClassifier,\n", + " space = space,\n", + ")\n", + "\n", + "knn_node.generate().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": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "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", + " search_spaces=[\n", + " knn_node,\n", + " lr_node,\n", + " dt_node,\n", + " ]\n", + ")\n", + "\n", + "\n", + "tpot2.search_spaces.pipelines.ChoicePipeline(\n", + " search_spaces = [\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": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sampled pipeline\n" + ] + }, + { + "data": { + "text/html": [ + "
LogisticRegression(C=174.83656421187536, 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=174.83656421187536, class_weight='balanced', dual=True,\n", + " max_iter=1000, n_jobs=1, penalty='l1', solver='saga')" + ] + }, + "execution_count": 22, + "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": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "mutated pipeline\n" + ] + }, + { + "data": { + "text/html": [ + "
KNeighborsClassifier(metric='euclidean', n_jobs=1, n_neighbors=3,\n",
+       "                     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(metric='euclidean', n_jobs=1, n_neighbors=3,\n", + " weights='distance')" + ] + }, + "execution_count": 23, + "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 current search spaces were adapted from a combination of the original TPOT package as well as the search spaces used in [AutoSklearn](https://github.com/automl/auto-sklearn/tree/development/autosklearn/pipeline/components). 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": 24, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sampled pipeline 1\n" + ] + }, + { + "data": { + "text/html": [ + "
LogisticRegression(C=0.09214193108798754, l1_ratio=0.6425731475282531,\n",
+       "                   max_iter=1000, n_jobs=1, penalty='elasticnet',\n",
+       "                   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.09214193108798754, l1_ratio=0.6425731475282531,\n", + " max_iter=1000, n_jobs=1, penalty='elasticnet',\n", + " solver='saga')" + ] + }, + "execution_count": 24, + "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": 25, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sampled pipeline 2\n" + ] + }, + { + "data": { + "text/html": [ + "
DecisionTreeClassifier(class_weight='balanced', max_depth=1, min_samples_leaf=8,\n",
+       "                       min_samples_split=9)
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(class_weight='balanced', max_depth=1, min_samples_leaf=8,\n", + " min_samples_split=9)" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(\"sampled pipeline 2\")\n", + "classifier_choice.generate().export_pipeline()" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sampled pipeline 1\n" + ] + }, + { + "data": { + "text/html": [ + "
LinearDiscriminantAnalysis(shrinkage=0.6166902161314916, solver='eigen')
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": [ + "LinearDiscriminantAnalysis(shrinkage=0.6166902161314916, solver='eigen')" + ] + }, + "execution_count": 26, + "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": 27, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sampled pipeline 2\n" + ] + }, + { + "data": { + "text/html": [ + "
LogisticRegression(C=0.13397662986842293, max_iter=1000, n_jobs=1, penalty='l1',\n",
+       "                   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.13397662986842293, max_iter=1000, n_jobs=1, penalty='l1',\n", + " solver='saga')" + ] + }, + "execution_count": 27, + "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": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sampled pipeline\n" + ] + }, + { + "data": { + "text/html": [ + "
Pipeline(steps=[('selectpercentile',\n",
+       "                 SelectPercentile(percentile=67.96672316882378)),\n",
+       "                ('columnonehotencoder', ColumnOneHotEncoder()),\n",
+       "                ('logisticregression',\n",
+       "                 LogisticRegression(C=5839.203596349427,\n",
+       "                                    class_weight='balanced', max_iter=1000,\n",
+       "                                    n_jobs=1, 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": [ + "Pipeline(steps=[('selectpercentile',\n", + " SelectPercentile(percentile=67.96672316882378)),\n", + " ('columnonehotencoder', ColumnOneHotEncoder()),\n", + " ('logisticregression',\n", + " LogisticRegression(C=5839.203596349427,\n", + " class_weight='balanced', max_iter=1000,\n", + " n_jobs=1, solver='saga'))])" + ] + }, + "execution_count": 28, + "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": 29, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sampled pipeline\n" + ] + }, + { + "data": { + "text/html": [ + "
Pipeline(steps=[('selectpercentile',\n",
+       "                 SelectPercentile(percentile=64.13487865074181)),\n",
+       "                ('rbfsampler',\n",
+       "                 RBFSampler(gamma=0.34856830184683274, n_components=74)),\n",
+       "                ('gaussiannb', GaussianNB())])
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=[('selectpercentile',\n", + " SelectPercentile(percentile=64.13487865074181)),\n", + " ('rbfsampler',\n", + " RBFSampler(gamma=0.34856830184683274, n_components=74)),\n", + " ('gaussiannb', GaussianNB())])" + ] + }, + "execution_count": 29, + "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": 31, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Generation: 100%|██████████| 5/5 [00:17<00:00, 3.52s/it]\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/decomposition/_fastica.py:595: UserWarning: n_components is too large: it will be set to 10\n", + " warnings.warn(\n" + ] + }, + { + "data": { + "text/html": [ + "
TPOTEstimator(classification=True, generations=5, max_eval_time_seconds=300,\n",
+       "              population_size=10, scorers=['roc_auc'], scorers_weights=[1],\n",
+       "              search_space=<tpot2.search_spaces.pipelines.graph.GraphPipeline object at 0x716246e21cf0>,\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, scorers=['roc_auc'], scorers_weights=[1],\n", + " search_space=,\n", + " verbose=2)" + ] + }, + "execution_count": 31, + "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=200, n_features=10, 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", + "\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", + "est.fit(X_train, y_train)" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "auroc score 0.9551820728291317\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": 33, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAnYAAAHWCAYAAAD6oMSKAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuNSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/xnp5ZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAC+oklEQVR4nOzdd1zW5f748dc92IgyFBEEFy5AFNzgyoapWZZmWYojpXEav2yeTiePnb6tU6d9us1d5iizLFunnOBCQBAc4IgpKNzsfY/fH9p9/IiiKHAz3s/Ho8cj3vf1+VzvDxa+ua7PdV0qs9lsRgghhBBCtHhqaycghBBCCCEahhR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthBR2QgghhBCthNbaCQghREMyGo3o9Xpyc3PJzc3lXE4OVRUVmIxG1BoNdg4OdOzcGU9PTzw9PXFzc0Oj0Vg7bSGEaBAqs9lstnYSQghxowoKCkhISOBwXByVZWWYDQacKypor9djYzCgNpsxqVTUaLUUublR6uCASqvF3smJoJAQgoODcXV1tfZjCCHEDZHCTgjRomVnZ7MnKorTqanYlJfjm56Bl15P+7IybIzGK15Xo9FQ5OTEGTc30n27UuPoSHd/f8JGjcLLy6sJn0AIIRqOFHZCiBbJYDAQHR1NTHQ0znl59EpLxycvD43JVO97GdVqMj08OOHnS6mHB0PCwggLC0OrlbdVhBAtixR2QogWJycnh61btlCQmUXf1FT8s7JQN8CPMpNKRaq3N8f8/XHz8WbilCl07ty5ATIWQoimIYWdEKJFSUtLY/OGDThmnyH06FFcyssbvI9iR0di+/WjvEsXps64Fz8/vwbvQwghGoMUdkKIFiMtLY1N69bhnpbO0CNH0F7HtOu1MqjV7A/oj97Xl3vuv1+K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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "#plot the best pipeline\n", + "est.fitted_pipeline_.plot()" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
TPOTEstimator(classification=True, generations=5, max_eval_time_seconds=300,\n",
+       "              population_size=10, scorers=['roc_auc'], scorers_weights=[1],\n",
+       "              search_space=<tpot2.search_spaces.pipelines.graph.GraphPipeline object at 0x716246e21cf0>,\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, scorers=['roc_auc'], scorers_weights=[1],\n", + " search_space=,\n", + " verbose=2)" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "est" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Combined Search Space Example" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Generation: 100%|██████████| 5/5 [00:33<00:00, 6.70s/it]\n" + ] + }, + { + "data": { + "text/html": [ + "
TPOTEstimator(classification=True, generations=5, max_eval_time_seconds=300,\n",
+       "              population_size=10, scorers=['roc_auc'], scorers_weights=[1],\n",
+       "              search_space=<tpot2.search_spaces.pipelines.sequential.SequentialPipeline object at 0x716238dc50f0>,\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, scorers=['roc_auc'], scorers_weights=[1],\n", + " search_space=,\n", + " verbose=2)" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from tpot2.search_spaces.pipelines import *\n", + "from tpot2.config import get_search_space\n", + "\n", + "selectors = get_search_space([\"selectors\",\"selectors_classification\", \"Passthrough\"])\n", + "estimators = get_search_space([\"classifiers\"])\n", + "\n", + "\n", + "# this allows us to wrap the classifiers in the EstimatorTransformer\n", + "# this is necessary so that classifiers can be used inside of sklearn pipelines\n", + "wrapped_estimators = WrapperPipeline(tpot2.builtin_modules.EstimatorTransformer, {}, estimators)\n", + "\n", + "scalers = get_search_space([\"scalers\",\"Passthrough\"])\n", + "\n", + "transformers_layer =UnionPipeline([\n", + " ChoicePipeline([\n", + " DynamicUnionPipeline(get_search_space([\"transformers\"])),\n", + " get_search_space(\"SkipTransformer\"),\n", + " ]),\n", + " get_search_space(\"Passthrough\")\n", + " ]\n", + " )\n", + "\n", + "inner_estimators_layer = UnionPipeline([\n", + " ChoicePipeline([\n", + " DynamicUnionPipeline(wrapped_estimators),\n", + " get_search_space(\"SkipTransformer\"),\n", + " ]),\n", + " get_search_space(\"Passthrough\")]\n", + " )\n", + "\n", + "\n", + "search_space = SequentialPipeline(search_spaces=[\n", + " scalers,\n", + " selectors, \n", + " transformers_layer,\n", + " inner_estimators_layer,\n", + " estimators,\n", + " ])\n", + "\n", + "est = tpot2.TPOTEstimator(\n", + " scorers = [\"roc_auc\"],\n", + " scorers_weights = [1],\n", + " classification = True,\n", + " cv = 5,\n", + " search_space = search_space,\n", + " population_size= 10,\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": 36, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
Pipeline(steps=[('maxabsscaler', MaxAbsScaler()),\n",
+       "                ('selectfwe', SelectFwe(alpha=0.035185091169059365)),\n",
+       "                ('featureunion-1',\n",
+       "                 FeatureUnion(transformer_list=[('featureunion',\n",
+       "                                                 FeatureUnion(transformer_list=[('columnonehotencoder',\n",
+       "                                                                                 ColumnOneHotEncoder())])),\n",
+       "                                                ('passthrough',\n",
+       "                                                 Passthrough())])),\n",
+       "                ('featureunion-2',\n",
+       "                 FeatureUnion(transformer_list=[('featureunion',\n",
+       "                                                 Feature...ortransformer-1',\n",
+       "                                                                                 EstimatorTransformer(estimator=KNeighborsClassifier(n_jobs=1,\n",
+       "                                                                                                                                     n_neighbors=4,\n",
+       "                                                                                                                                     p=1))),\n",
+       "                                                                                ('estimatortransformer-2',\n",
+       "                                                                                 EstimatorTransformer(estimator=BernoulliNB(alpha=0.38931927157292817,\n",
+       "                                                                                                                            fit_prior=False)))])),\n",
+       "                                                ('passthrough',\n",
+       "                                                 Passthrough())])),\n",
+       "                ('logisticregression',\n",
+       "                 LogisticRegression(C=0.599616111675462, max_iter=1000,\n",
+       "                                    n_jobs=1, 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": [ + "Pipeline(steps=[('maxabsscaler', MaxAbsScaler()),\n", + " ('selectfwe', SelectFwe(alpha=0.035185091169059365)),\n", + " ('featureunion-1',\n", + " FeatureUnion(transformer_list=[('featureunion',\n", + " FeatureUnion(transformer_list=[('columnonehotencoder',\n", + " ColumnOneHotEncoder())])),\n", + " ('passthrough',\n", + " Passthrough())])),\n", + " ('featureunion-2',\n", + " FeatureUnion(transformer_list=[('featureunion',\n", + " Feature...ortransformer-1',\n", + " EstimatorTransformer(estimator=KNeighborsClassifier(n_jobs=1,\n", + " n_neighbors=4,\n", + " p=1))),\n", + " ('estimatortransformer-2',\n", + " EstimatorTransformer(estimator=BernoulliNB(alpha=0.38931927157292817,\n", + " fit_prior=False)))])),\n", + " ('passthrough',\n", + " Passthrough())])),\n", + " ('logisticregression',\n", + " LogisticRegression(C=0.599616111675462, max_iter=1000,\n", + " n_jobs=1, solver='saga'))])" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "est.fitted_pipeline_" + ] + } + ], + "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/3_Feature_Set_Selector.ipynb b/Tutorial/3_Feature_Set_Selector.ipynb new file mode 100644 index 00000000..82bcf6c4 --- /dev/null +++ b/Tutorial/3_Feature_Set_Selector.ipynb @@ -0,0 +1,1246 @@ +{ + "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
0-2.1708541.2453542.1390220.3353940.4590810.7003360.5789170.0926620.161226
1-1.2490920.278109-0.4983710.3814430.5519280.4785240.6568720.9750680.497428
2-0.9975271.527997-1.3608140.4389200.2572160.9959950.4118370.0443390.073172
31.511913-1.3744122.4228070.8056760.0519170.6407610.0948810.7534520.214523
4-1.1205791.033842-1.0998840.0594720.6822450.6059320.7458000.8242540.903524
\n", + "
" + ], + "text/plain": [ + " a b c d e f g \\\n", + "0 -2.170854 1.245354 2.139022 0.335394 0.459081 0.700336 0.578917 \n", + "1 -1.249092 0.278109 -0.498371 0.381443 0.551928 0.478524 0.656872 \n", + "2 -0.997527 1.527997 -1.360814 0.438920 0.257216 0.995995 0.411837 \n", + "3 1.511913 -1.374412 2.422807 0.805676 0.051917 0.640761 0.094881 \n", + "4 -1.120579 1.033842 -1.099884 0.059472 0.682245 0.605932 0.745800 \n", + "\n", + " h i \n", + "0 0.092662 0.161226 \n", + "1 0.975068 0.497428 \n", + "2 0.044339 0.073172 \n", + "3 0.753452 0.214523 \n", + "4 0.824254 0.903524 " + ] + }, + "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": 3, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/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 39005 instead\n", + " warnings.warn(\n", + "Generation: 100%|██████████| 5/5 [04:09<00:00, 49.87s/it]\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/preprocessing/_data.py:2762: UserWarning: n_quantiles (842) is greater than the total number of samples (750). n_quantiles is set to n_samples.\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/preprocessing/_data.py:2762: UserWarning: n_quantiles (1803) is greater than the total number of samples (750). n_quantiles is set to n_samples.\n", + " warnings.warn(\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.9390338164251208\n" + ] + } + ], + "source": [ + "import tpot2\n", + "import sklearn.datasets\n", + "from sklearn.linear_model import LogisticRegression\n", + "import numpy as np\n", + "\n", + "subsets = { \"group_one\" : ['a','b','c',],\n", + " \"group_two\" : ['d','e','f'],\n", + " \"group_three\" : ['g','h','i'],\n", + " }\n", + "\n", + "fss_search_space = tpot2.search_spaces.nodes.FSSNode(subsets=subsets)\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 = None, \n", + " inner_search_space = tpot2.config.get_search_space([\"transformers\"]),\n", + " max_size = 10,\n", + ")\n", + "\n", + "combined_search_space = tpot2.search_spaces.pipelines.SequentialPipeline([fss_search_space, graph_search_space])\n", + "\n", + "\n", + "est = tpot2.TPOTEstimator(population_size=10,generations=5, \n", + " scorers=['roc_auc_ovr'],\n", + " scorers_weights=[1],\n", + " n_jobs=32,\n", + " classification=True,\n", + " search_space = combined_search_space,\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))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
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 0x7c2c2831c100>))])
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": [ + "/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 42397 instead\n", + " warnings.warn(\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Generation: 100%|██████████| 5/5 [00:22<00:00, 4.46s/it]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.8384541062801932\n" + ] + } + ], + "source": [ + "import tpot2\n", + "import sklearn.datasets\n", + "from sklearn.linear_model import LogisticRegression\n", + "import numpy as np\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.search_spaces.nodes.FSSNode(subsets=X_train.columns.tolist()), \n", + " inner_search_space = tpot2.config.get_search_space([\"transformers\"]),\n", + " max_size = 10,\n", + ")\n", + "\n", + "est = tpot2.TPOTEstimator(population_size=10,generations=5, \n", + " scorers=['roc_auc_ovr'],\n", + " scorers_weights=[1],\n", + " n_jobs=32,\n", + " classification=True,\n", + " search_space = graph_search_space ,\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))" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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wcGGcwYGBmrs2LHf+z2ci1ZP7IKDg9XY2Kjf/va3Sk1N1ZQpU1RZWanRo0dr9+7dmjt3rgICAjRv3jy5XC7V1taqurraE+zuvfdeJSUl6c4771RjY6NsNps2bdrkCXa/+tWvFBsbq4cfflgrV67UW2+9pczMTM2cOVNOp1Nvvvmmfvfb32rV8uV6d9Alnr6+PbEbl71Ns/v01c9799b+hgb9ufiQ/jRkqOwWix76/HNNuOgi9QsKOm295GSjjpw8qd9ePOh73/s/Kio8E7saR4vCrDYdPXpUf686pga3W7dHRGh6ZaVS09L03gcfnPcvAwCAzqhf3756JD1dl23frv+oqNC4sDAlBwdrTmmpXG63fhQSqpt79dLIiy5SVUuL/nXPHr1y2WUKstn05+JDigroop/37m2YuNU5HLqtIF9Ot1vp3SM0qWdPJXbt2qrnZ1VXa33VMT128SBtGTxYz23dopUrV+rw4cNasGCB3n//fQUGBqqqqkqRkZGGiV1paanGjh2r7OxshYSEKC0tTUuWLFFUVJQuueQS5ebmKikp6aw/j+XLl6uwsLB9J3bfWLNmjd577z39/ve/lyTV19eroqJCa9euVWZmpqSv15Dh4eGGtwhfccUVeuqpp3Ts2DHdeuutuvhi41ozKytL77//viTp1ltv1a9//WvP12666SZJUp9evXSstvas/V3fo4ckafPx49pRW6vJO/IkSSedLiWEhenIyZOnrbd2a1t2skm/379Plc3NanK5dNmpjxqLi4zU9tzc1h0EAAB4HKuq0jMffqgujY1qcrs1ODBQV4SGaknfvtrR2Kicxkbds2+vFtntanG79HlDvW4tyJckNbtcuioy8nvHDLPb9e6wVG09flybao5rVmGh/jxkiJpb8fys49X6pKpa20/kqXFXkRrsdu3du1dZWVmaNWuWAk9N/yJPc97s7GyNHz/e87UpU6YoKytLkyZN0uDBg38w1F2oVge7IUOGSPp6VPs///M/6t+//zmdaPr06Ro5cqT+53/+R9dcc43+/ve/n/Xxlm8lrW9+gHK75fqBAWPQqdedueXWrb166d5YY59vlJWetr6xulprKo/94Pfx+wNfKKNnTyVYLNpUX68PTwXNGSkp+ufJk8pkYgcAwDkZEhenX118sS7+zq1F7BaLfhQSoh+FhKi7za51Vcc0unuEroqI1LODB//gce0Wi9IjIpQeEaFIe4DWtvL5Lrf0q9hYTY6OVv7FA1WblqbJkycr6wJvWNwRL49q9btif/WrX0mSrr32Wi1atMhT37FjhyTp6quv1ksvvSTp6/BXU1NjeP6BAwc0aNAg/eu//quuvfZa7dq1y/D10aNH6y9/+Ysk6Z133tHIkSO/3+xp3ixwJleEd9f7R4+quqVFknSsuVlfNTefsZ7WvbvqnA79o6LCc4ztNTXaW19vOG6d06kB4d1ls9m05lvTw68aGhQVFdXq/gAAwNe+OHRItaeuy9UOh445HDrc3KzSUzWrxapSudU7MFDDunXV1prjKj31Dtc6h8PzblebxSLnqQHQgYYGHT71zl632629DfU/+PxvjI7orr9XlKvR6ZTLYlXV8eOqqanR1Vdfrddff11NTU2S5Hm3bNeuXVV7KhOMHDlS69atU3V1tZqamvSPf/xDY8aMabef3Xe1emI3e/ZsSdJjjz2mX//610pKSpLD4VBqaqr+67/+S4899pjuueceJSYmymazafHixerbt6/n+StXrtR//dd/KSAgQP3799fNN9+s7Oxsz9effPJJzZw5U2+88YbnzRPfFRgcLHcrd6ZxoaH6Zb9YZRTulNvtVoDVqufiBp+x3rNLF7049DI9feCAXig5rECrVXEhIXrsO6+5+1VsrO7ZtUvdA+xK7dpVJaeC38qiIh38zpp44sSJ2r9/v2w2m1JTU/XCCy/oL3/5i3bt2qVnnnlGv//97xUVFaVf/vKX2rBhg15++WX95S9/UX19vR588EHl5OTI4XAoJSVFy5YtMzz+TD788EP96le/UmVlpbp376709HS99dZbrfqZAQA6r379+qmkxHi7kHXr1unxxx9XS0uLLBaL/vCHP2js2LG68847tW3bNvXv3192u1133HGHJkyYoEcffVQffPCB0tPT9Z//+Z9atGiRXn31VfXr10+RkZG67rrrdPvtt2vIkCHavn27512kD/zrv2rB3/+uoJMn1cVq1bNxcZLLracPfKE6h1OySPGhYbo9preCbDb9/pI43btnt1pcLlksFj068GL1CwrSzT2jdUNujkaEh+vWXr301BdfqO7UPeha8/xvjI2I1P6GBt2av0N1u3crbPMm/XzmTE2YMEE5OTlKTU1VQECAZs2apV//+te68847NW7cOA0ePFiZmZl64oknNHbsWM+bJ1JTU3Xo0KFW/R4uvfRSHT16VC0tLfrrX/+qLVu2GPLUD2n1mye8wbp16/T5Rx/pms1bzG7FoLmlWR/+6Ed6f/durV+/XtLX6+Mvv/xSERERJncHAIB389bruyT97xWX69Kf/ETjx483u5VWOec3T5gpOjpaOcHBarHZFOBFd4G2BAXLGRWlOXPmqHfv3jp69KgefPBBQh0AAK3grdf3FptNdcHBZ70nrbfxuWBnsdtVExqqHidOmN2OR01oqCx2u8aMGaPJkyd3yDlff/11/fnPfzbUpk6dqkcffbRDzg8AQFvx9ut7Wwe7Y8eOfW8CGBgYqK1bt17wsX0q2EVGRiooNFRfRkZ61S/+y6iv+zrd257by6xZszRr1qwOOx8AAO2ls13fo6KiPG8+bWvt+lmxbc1msykxNVWHY/vJeZqP4jCD02pVcb9+Sho+/LQf8QUAAM6O63vb8Y6f3jlITk5WS0iIjpy6EbHZSnr0kCMkpN1vOAgAgD/j+t42fC7YRUREaGBcnPb3j5WrtR8X0U5cFou+6B+rgYMH80YJAAAuANf3tuFzwU6S0seMUV2PHtrXp4+pfezt00d1PXooffRoU/sAAMAfcH2/cD4Z7GJiYjQiPV174uJ0ogM+nuN0akJC9PngOI0cPVoxMTGm9AAAgD/h+n7hfDLYSVJ6eroi+vZRztChcnTwCy0dVqtyLhuqyD59lJaW1qHnBgDAn3F9vzA+G+zsdrt+euONaujdW1vjL+uwfbzLYtHW+MvUGNNbE268UXa7T90xBgAAr8b1/cL4bLCTpF69eunmabeqKjZWmxPi2z3ZO6xWbU6IV1VsrG6edqt69erVrucDAKAz4vp+/nzqs2LPpLi4WKtW/k0hZWUavnu3ujU0tPk5akJClHPZUDXG9NbN025V//792/wcAADg/3B9P3d+Eewkqby8XKszM1V9pFRD9u1TXGmprG3wrbksFu3t00efD45TZJ8+mnDjjT6d5AEA8CVc38+N3wQ7SXI4HNq4caOyN25UWGWlBhUfVr/KStlcrnM+ltNqVUmPHvqif6zqevTQyNGjlZaW5rM7dwAAfBXX99bzq2D3jbKyMm3auFEH9+6VvaFB/UtKFHOsSuH19QpwOs/4vBabTTWhofoyKlLF/frJERKigYMHK91H3/IMAIA/4fr+w/wy2H2jurpaBQUFKsjJ0cn6erkdDoU1NqpbVbW6OByyul1yWaxqttt1IjJCdcHBstjtCgoNVdLw4UpKSvK5O04DAODvuL6fmV8Hu284nU5VVVWpoqJCFRUVOlperuaTJ+V0OGSz29UlKEgX9eql6OhoRUdHKzIy0qc+8BcAgM6I6/v3dYpgBwAA0Bn49H3sAAAA8H8IdgAAAH6CYAcAAOAnCHYAAAB+gmAHAADgJwh2AAAAfoJgBwAA4CcIdgAAAH6CYAcAAOAnCHYAAAB+gmAHAADgJwh2AAAAfoJgBwAA4CcIdgAAAH6CYAcAAOAnCHYAAAB+gmAHAADgJwh2AAAAfoJgBwAA4CcIdgAAAH6CYAcAAOAnCHYAAAB+gmAHAADgJwh2AAAAfoJgBwAA4CcIdgAAAH6CYAcAAOAn/j81OISqJ1MLGAAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "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: 100%|██████████| 5/5 [00:44<00:00, 8.81s/it]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.9549114331723028\n" + ] + } + ], + "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", + "fss_search_space = tpot2.search_spaces.nodes.FSSNode(subsets=subsets)\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 = None, \n", + " inner_search_space = tpot2.config.get_search_space([\"transformers\"]),\n", + " max_size = 10,\n", + ")\n", + "\n", + "combined_search_space = tpot2.search_spaces.pipelines.SequentialPipeline([fss_search_space, graph_search_space])\n", + "\n", + "\n", + "est = tpot2.TPOTEstimator(population_size=10,generations=5, \n", + " scorers=['roc_auc_ovr'],\n", + " scorers_weights=[1],\n", + " n_jobs=32,\n", + " classification=True,\n", + " search_space = combined_search_space,\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))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## list" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Generation: 100%|██████████| 5/5 [09:02<00:00, 108.52s/it]\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/decomposition/_fastica.py:595: UserWarning: n_components is too large: it will be set to 3\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/decomposition/_fastica.py:128: ConvergenceWarning: FastICA did not converge. Consider increasing tolerance or the maximum number of iterations.\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/decomposition/_fastica.py:595: UserWarning: n_components is too large: it will be set to 24\n", + " warnings.warn(\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.9765539452495974\n" + ] + } + ], + "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", + "fss_search_space = tpot2.search_spaces.nodes.FSSNode(subsets=subsets)\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 = None, \n", + " inner_search_space = tpot2.config.get_search_space([\"transformers\"]),\n", + " max_size = 10,\n", + ")\n", + "\n", + "combined_search_space = tpot2.search_spaces.pipelines.SequentialPipeline([fss_search_space, graph_search_space])\n", + "\n", + "\n", + "est = tpot2.TPOTEstimator(population_size=10,generations=5, \n", + " scorers=['roc_auc_ovr'],\n", + " scorers_weights=[1],\n", + " n_jobs=32,\n", + " classification=True,\n", + " search_space = combined_search_space,\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))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## csv file\n", + "\n", + "note: watch for spaces in the csv file!" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Generation: 100%|██████████| 5/5 [00:41<00:00, 8.27s/it]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.9754589371980676\n" + ] + } + ], + "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", + "fss_search_space = tpot2.search_spaces.nodes.FSSNode(subsets=subsets)\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 = None, \n", + " inner_search_space = tpot2.config.get_search_space([\"transformers\"]),\n", + " max_size = 10,\n", + ")\n", + "\n", + "combined_search_space = tpot2.search_spaces.pipelines.SequentialPipeline([fss_search_space, graph_search_space])\n", + "\n", + "\n", + "est = tpot2.TPOTEstimator(population_size=10,generations=5, \n", + " scorers=['roc_auc_ovr'],\n", + " scorers_weights=[1],\n", + " n_jobs=32,\n", + " classification=True,\n", + " search_space = combined_search_space,\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))" + ] + }, + { + "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": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[-0.51289317 -2.65333383 -0.68124034 ... 0.66358004 0.25051107\n", + " 0.23444287]\n", + " [-2.51236119 -2.04422708 -0.40301026 ... 0.19208918 0.18041725\n", + " 0.33265205]\n", + " [ 1.49065721 -3.24328369 1.58423463 ... 0.678225 0.27643945\n", + " 0.78710293]\n", + " ...\n", + " [ 0.07953312 -1.10920624 1.0985733 ... 0.68578896 0.87562184\n", + " 0.28616797]\n", + " [-2.43045085 0.42769074 2.57608083 ... 0.0447371 0.31649605\n", + " 0.52711618]\n", + " [-2.50001651 -0.46482725 2.0546322 ... 0.34574358 0.96130892\n", + " 0.93289141]]\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": 11, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Generation: 100%|██████████| 5/5 [00:34<00:00, 6.92s/it]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.8762676829662166\n" + ] + } + ], + "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", + "fss_search_space = tpot2.search_spaces.nodes.FSSNode(subsets=subsets)\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 = None, \n", + " inner_search_space = tpot2.config.get_search_space([\"transformers\"]),\n", + " max_size = 10,\n", + ")\n", + "\n", + "combined_search_space = tpot2.search_spaces.pipelines.SequentialPipeline([fss_search_space, graph_search_space])\n", + "\n", + "\n", + "est = tpot2.TPOTEstimator(population_size=10,generations=5, \n", + " scorers=['roc_auc_ovr'],\n", + " scorers_weights=[1],\n", + " n_jobs=32,\n", + " classification=True,\n", + " search_space = combined_search_space,\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))" + ] + } + ], + "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.14" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "7fe1fe9ef32cd5efd76326a08046147513534f0dd2318301a1a96ae9071c1c4e" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Tutorial/3_Genetic_Feature_Set_Selectors.ipynb b/Tutorial/3_Genetic_Feature_Set_Selectors.ipynb deleted file mode 100644 index 59ae0a79..00000000 --- a/Tutorial/3_Genetic_Feature_Set_Selectors.ipynb +++ /dev/null @@ -1,1147 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "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
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": "iVBORw0KGgoAAAANSUhEUgAAAnYAAAHWCAYAAAD6oMSKAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAADoGUlEQVR4nOzdd1gUZ9cH4N8W+tKRIgI2ivRiA8RYYkzU6GvXqIklxiQaib3FEhN7A9ub2BNNjLEl1tdYo4CiAoLSVYpUpXeW3Z3vD3U/x0WlLMwC574ur2TP7MycXYbdwzNP4TEMw4AQQgghhDR5fK4TIIQQQgghykGFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIM0GFHSGEEEJIMyHkOgFCSNMmlUqRl5eH7OxsZGdn41lWFirLyyGTSsEXCKChpYVW5uYwMzODmZkZjIyMIBAIuE6bEEKaJR7DMAzXSRBCmp78/HxERkbifng4KkpLwUgkEJWXQz8vD2oSCfgMAxmPhyqhEIVGRijR0gJPKISmjg5cPD3h5uYGQ0NDrl8GIYQ0K1TYEUJqJSMjAyFBQUhKTIRaWRmsU5/AIi8P+qWlUJNK37hflUCAQh0dZBoZIdXaClXa2mhnawtfPz9YWFg04isghJDmiwo7QkiNSCQSBAcH405wMEQ5OeiYkoo2OTkQyGS1PpaUz0eaiQke2lijxMQEXXx94evrC6GQeocQQkh9UGFHCHmnrKwsnD11Cvlp6XBITIRtejr4SvjokPF4SLS0RJytLYzaWGLA4MEwNzdXQsaEENIyUWFHCHmrlJQUnDxyBNoZmfCKjYVeWZnSz1GkrY2wTp1Q1ro1ho4eBRsbG6WfgxBCWgIq7Aghb5SSkoLjhw/DOCUVXWNiIKzDbdeakvD5CHVyRJ61NYaPHUvFHSGE1AHNY0cIqVZWVhZOHjkCo5RUdI+ObtCiDgCEMhm8H0TDKDUVJ4/8iaysrAY9HyGENEdU2BFCFEgkEpw9dQraGZnoFhOjlP50NcFnGHSLjoFWZgbOnToFiUTSKOclhJDmggo7QoiC4OBg5Kelwys2tsFb6l4nlMngFROLvPR0hISENOq5CSGkqaPCjhDCkpGRgTvBwXBITGyQgRI1oV9WBvuERNwOCkJmZiYnORBCSFNEhR0hhCUkKAiinBzYpqdzmoddejpEOTkIDgriNA9CCGlKqLAjhMjl5+cjKTERHVNSG61f3ZvwGQYdUlKRlJCA/Px8TnMhhJCmggo7QohcZGQk1MrK0CYnh+tUAABWOTkQlpUhKiqK61QIIaRJoMKOEAIAkEqluB8eDuvUJ3VaJqwhCGQy2Dx5gqiwMEjfsg4tIYSQ56iwI4QAAPLy8lBRWgqLvDyuU2GxyH2eV56K5UUIIaqICjtCVIhQKIS7u7v8X3l5ea2PsX79+jqdOzs7G4xEAoOSElZ8e2oKBoSHYVB4GIbdi8CTioq3Hmd32pN67d/11k3WY/3SUjASCbKzs9+6X0BAAMRi8VufUxuZmZnQ09PD9u3blXZMQghpaEKuEyCE/D8DAwPcu3evXsdYv3495s+fX6t9pFIpsrOzISovZ81bF15UhNDCQvzt7gE1Ph9ZlZXQErz978HdaWmY2saqzvu/Tk0qhai8HNnZ2XB2dn7j8wICAvD5559DXV29RseVyWTg89+cy8KFC9GvX79a5UoIIVyjFjtCVNyFCxfg7e0NDw8PjB8/Xt4q9cUXX8DLywtOTk7YuHEjAGDJkiUoKCiAu7s7vvzySyQnJ6Nz587yY82dOxcHDhwAALRt2xYLFy6Eh4cHrly5ghPHjmHj/v34ODwcqx8/BgA8E4thKFSD2osCyFxDA/pCNQDAjfx8jIq8hyER4ZgbHwexTIbNyckolkgwOCIcyx4m1nr/1+1Ke4Jh9yKwds8e7N+7Vx5ftWoVXFxc4Orqii1btmDHjh3IyMiAj48PBg8eDAA4ePAgXFxc4OzsjA0bNgAAkpOT4eLigjFjxsDR0fGNLaLXr1+Hnp4eXFxc6vATI4QQ7lCLHSEq5GVRBgCdO3fG2rVrsWHDBly5cgVaWlpYtmwZdu/ejenTp2Pt2rUwMjKCRCKBn58fRo8ejVWrVuHnn3+Wt/olJye/9XxWVlaIiIhAbGwsbt++jVUffYTOScmYFx+Pq3l58DUwwLbUFHwUdhe+BoYYYmoKF11d5FVVYU9aGn51doGmQIDAlGT8mZWF2W3b4o+sTJzy8AQAlEgktdp/fOvW8tyC8vORVVmJ427uCG/fHivv3MaDBw+QmpqKK1eu4O7du9DQ0EBeXh6MjIywYcMGhISEQCQSIT09HStWrMCdO3egra0NHx8f9OnTB8bGxoiNjcVvv/0GV1fXat8TiUSCZcuW4cSJE9i6dWu9f6aEENKYqLAjRIW8fiv2zJkziIqKgre3NwCgsrISAwcOBAAcPnwYe/bsgVQqRVpaGuLi4mBlZVWr840cORIAcPnyZTx6/BiLkpKgJRajQiqDs0iE3kZG+MvDE6EFBQgpLMCkBw8Q6OAAMSNDfFkpRkVFAgDEMhl6GRkpHF8kFNZ5/6CCfFzLy8fdogiUx0SjTCBAQkICgoKCMGnSJGhoaAAAjKo57507d9C3b1/5thEjRiAoKAhDhgyBnZ3dG4s6ANixYwdGjhxZ7XEJIUTVUWFHiAqTyWQYOHAg9u/fz4o/fvwYO3bswM2bN6Gvr48RI0agsrJSYX+hUAjZK7c4X3+Otra2/Dzv+flhrJERPB49Zh+Dx4OvoSF8DQ1hJFTDpbxc9DAwRC9DI6y1s3vna6jr/jIGmGFtjWFmZojo0AEVfj0wbNgwBNVzJYqXr/lNbt++jaCgIGzYsAEFBQUQCATQ1tbG5MmT63VeQghpDNTHjhAV5u3tjatXryIlJQUAUFRUhKSkJBQXF0MkEkFPTw9paWm4dOmSfB+BQCCf883U1BQZGRkoLi5GSUkJLl68WO15+vbtizthYciTSAAAuWIxnorFeFxWhtQX/dAYhkFCWSlaa2jAQ08XoYUFSH8xwrVEIpGPdhXweJC+WLWiLvu/1MPQAEezs1AulUIsFKKwuBiFhYV4//33sX//fnmR+nIaFF1dXRQXFwMAunbtisuXLyM/Px+VlZU4ceIE/Pz8avSe//bbb0hJSUFycjK+/fZbfP/991TUEUKaDGqxI0SFtWrVCrt378bw4cMhFovB5/MREBCAXr16oVOnTnBwcEDbtm3Ro0cP+T6fffYZXFxc0LNnT/z000+YP38+PDw8YG1t/cbBAE5OTvj0s8/w4549CCgthRqfj3W2dqhkZFj56BFKXhSKTjoiTLBoDU2BAD92tMU3cbGoksnA4/GwpF17WGlqYqipGQaFh6GLvj5GmZvXev+Xehoa4WFZGUZF3kNRYgKMbt3EqLFjMWDAAISFhcHT0xNqamqYNGkS/P39MXXqVPTu3Rt2dnY4deoUli9fjp49e4JhGHz22Wfw9PR8Z59DQghp6ngMw/GCkIQQlfDgwQOcO3oUg/69DjUVWuWhSiDAmfd6YsDIkW+d7oQQQgjdiiWEvGBmZgaeUIhCHR2uU2Ep1NEBTyiEmZkZ16kQQojKo1uxhBAAz0eXauroINPICCZFRVynI5dp/DwvZY9Szc3NRd++fVkxDQ0NhIaGKvU8hBDSmKiwI4QAeD7owsXTE/dyc+GYmgpBNRMGNzYpn48UKyt4enlBIBAo9djGxsb1XuWDEEJUDd2KJYTIubm5oUpbG2kmJu98rkwmQ3FJMYpLSiCrQVddGcOguKTkxfNrVjQ+MTGBRFv7rfPOEUII+X/UYkcIkTM0NEQ7W1s8zM2F1bNn4L+hYGMYBjm5uZBIqgAAVWLxO2+VPp965PmUJuXl5WjVqhV4b3m+jMfDIxtrtLOzg6GhYZ1eDyGEtDTUYkcIYfH180OJiQkSLS3f+JyS0lJ5UQcAlS/Wr30TBoD4lcmRJZIqlJaUvHWfBEtLlJiYwPeVqVwIIYS8HRV2hBAWCwsLdPH1RZytLYqqWaVBKpWi5MVEwC+pq6u/9Zg8AOoa7OcUl5RA+oZ+fIXa2oi3s0XXHj1gYWFRuxdACCEtGBV2hBAFvr6+MGxjibBOnSDhsz8mioqKwODVW7Q86OnpvfOYurp6wCs3XxlGhqJqRt9K+HyEOXaCkaUlfHx86voSCCGkRaLCjhCiQCgUYuDgwShr3RqhTo6Q8Z4XZJViMcorylnP1dbWhprw3d111dXUoK2txYqVl5dBXPX/t3RlPB5CnRxRbtEaAwYPhrAGxyWEEPL/qLAjhFTL3NwcQ0ePQp61NW46O6GKz0dRYSHrOXweH3q6ujU+pq6uHng89sdOYWEhGDxvqbvp7IQ8a2sMHT0K5ubmyngZhBDSolBhRwh5IxsbGwwfOxYFbdvhqoszCl9rcdPV1QWfX/OPEQGfD93XCsGqKjGe8vm47umBgrbtMHzsWNjY2Cglf0IIaWmosCOEvJWNjQ0GDv0PosVihPbujTR7e8h4PAiFatCuw/JjOjo68lusMh4Pafb2uNylM/h2dhjz6QQq6gghpB6oAwsh5J127tyJXfv2wcfHB5VduiCrTRs4ZmXBuKCw1itU8ADoGBgiQUcbTzp2RI5IhODbt+EtENDtV0IIqScew9RgynhCSIsVGxsLV1dXSCQSAM/73o0eORKWZmYQlpXB5skTWOTmQb+0FGpS6RuPUyUQoFBHB5nGRkixskKBTIbohAQEh4QgKysLampqiI6Ohq2tbWO9NEIIaXaosCOEvBHDMPjwww/xzz//yGMaGhqIiYmBoaEhoqKiEBUWhorSUjASCUTl5dDLy4e6RAI+I4OMx4dYKESRkSFKtLTAEwqhqaMDVy8v6Orqonv37qh6ZVTsoEGDcPr0aS5eKiGENAtU2BFC3uj06dMYPHgwK7Z48WKsWrVK/lgqlSIvLw/Z2dnIzs7Gs6wsiCsqIJVIIBAKoa6piVbm5jAzM4OZmRmMjIwgEAgAAPPmzcPGjRtZxz9//jw+/PDDhn9xhBDSDFFhRwipVmVlJZycnPDo0SN5rHXr1oiPj4dIJFLKOYqKimBnZ4fs7Gx5zMHBAVFRUVBTU1PKOQghpCWhUbGEkGoFBASwijoAWL9+vdKKOgDQ09PDmjVrWLG4uDhs375daecghJCWhFrsCCEKMjMzYWdnh5KSEnnM29sbwcHB4PF4b9mz9mQyGbp164a7d+/KY3p6ekhMTISpqalSz0UIIc0dtdgRQhQsWrSIVdTxeDxs3bpV6UUdAPD5fGzdupUVKyoqwnfffaf0cxFCSHNHLXaEEJbQ0FB0796dFZs8eTL27t3boOedMGECDh06JH/M4/Fw9+5deHp6Nuh5CSGkOaHCjhAiJ5PJ4O3tjdu3b8tjurq6SExMhJmZWYOeOz09Hfb29igtLZXHfH19cePGjQZpKSSEkOaIbsUSQuQOHjzIKuoAYNmyZQ1e1AGApaUlFi9ezIoFBwfjyJEjDX5uQghpLqjFjhACACguLoadnR2ysrLkMTs7O9y/fx/q6uqNkkNFRQWcnJzw+PFjeaxNmzaIi4uDTh3WpSWEkJaGWuwIIQCAVatWsYo6ANiyZUujFXUAoKmpiU2bNrFiaWlpWLduXaPlQAghTRm12BFC8PDhQzg5OUEsFstjAwYMwNmzZxs9F4Zh0K9fP1y+fFke09TURGxsLNq2bdvo+RBCSFNCLXaEEMyZM4dV1AmFQmzevJmTXHg8HgIDA+XLjgHPb9HOmzePk3wIIaQpocKOkBbun3/+walTp1gxf39/2Nvbc5QR4OTkhK+//poVO3bsGK5evcpRRoQQ0jTQrVhCWrCqqiq4ubkhNjZWHjM1NUVCQgL09fU5zAzIz8+Hra0tcnNz5TEXFxeEh4dDKBRymBkhhKguarEjpAXbsWMHq6gDgNWrV3Ne1AGAoaEhfvjhB1bs/v372LVrF0cZEUKI6qMWO0JaqGfPnsHW1haFhYXymJeXF27fvg0+XzX+5pNKpfD09ERUVJQ8ZmRkhMTERBgZGXGYGSGEqCbV+PQmhDS67777jlXUAcDWrVtVpqgDAIFAoLCObF5eHpYvX85RRoQQotqoxY6QFigiIgJeXl549dd/3LhxrLVaVcmoUaNw9OhR+WOBQIB79+7B2dmZw6wIIUT1UGFHSAvDMAzee+893LhxQx7T1tZGfHw82rRpw2Fmb5aSkgIHBwdUVFTIY3369MGlS5doHVlCCHmF6txzIYQ0mDt37mDatGlYs2YN9u3bxyrqAGDx4sUqW9QBgI2NDebPn8+KXblyBX/99Rc3CRFCiIqiFjtCmrmnT5+ibdu2KC8vBwDw+XzIZDL59rZt2yI2NhaamppcpVgjZWVlsLe3R1pamjzWrl07xMTEqHzuhBDSWKjFjpBm7tq1a/KiDgCrqAOATZs2NYnCSFtbGxs2bGDFkpKSFNaWJYSQloxa7AhpYqRSKfLy8pCdnY3s7Gw8y8pCZXk5ZFIp+AIBNLS00MrcHGZmZjAzM8OxY8cUVnF41YQJE7Bnzx6oq6s34quomzf1D0xISIClpSWHmTUdtb1+jIyMWMu7EUJUGxV2hDQR+fn5iIyMxP3wcFSUloKRSCAqL4d+Xh7UJBLwGQYyHg9VQiEKjYxQoqUFnlCIiqoqXPr3X0RGRipMb/LSpk2bMHv27EZ+RXXT1Eb0qoq6Xj+aOjpw8fSEm5sbDA0NuX4ZhJB3oMKOEBWXkZGBkKAgJCUmQq2sDNapT2CRlwf90lKoSaVv3K9KIEChjg6SRTp4ZG6OMjU1JCYlISgkBFlZWaznzp49u0nd0pw2bZrCChTBwcHw8fHhKCPVVd/rJ9PICKnWVqjS1kY7W1v4+vnBwsKiEV8BIaQ2qLAjREVJJBIEBwfjTnAwRDk56JiSijY5ORC81kfuXQoKC1FcUY7cNm2QamuLHJEIwXfuICQkBFKpFIaGhggJCYGDg0MDvRLlawqrZnBNWdcPAEj5fKSZmOChjTVKTEzQxdcXvr6+tGYvISqICjtCVFBWVhbOnjqF/LR0OCQmwjY9Hfw6/qo+y8lBVZUYACDj8ZBhZ4dEBwek5+Uhp6AAAQEBsLGxUWb6jSIgIACzZs1ixfbu3YvJkydzlJHqUOb18yoZj4dES0vE2drCqI0lBgweDHNzcyVkTAhRFirsCFExKSkpOHnkCLQzMuEVGwu9srJ6HS87OxtSGfuWW5mePh77eKPK2hpDR49ukoVdVVUV3NzcEBsbK4+ZmpoiISEB+vr6HGbGLWVfP9Up0tZGWKdOKGvdGkNHj2qS1w8hzRXdsyBEhaSkpOD44cMwTEqGX0SEUr6U1V4b7aqupo52Wlroc/8BDJKTcfzwYaSkpNT7PI1NTU0NW7ZsYcWePn2KH374gaOMuNcQ10919MrK4BcRAYPkpCZ7/RDSXFGLHSEqIisrC3/8+isMkpLhHR2tlFtnAMAAKCgoQFVVFbS0tKArEsm3yXg83HR2QkHbdhjz6YQmeVtt8ODBOH36tPyxUCjEgwcPYG9vz2FWja+hrp+3aQ7XDyHNDbXYEaICJBIJzp46Be2MTHSLiVHqlzIPgKGBAUxbtWIVdQDAZxh0i46BVmYGzp06BYlEorTzNpbNmzez5uCTSCRNZuoWZWnI6+dtmsP1Q0hzQ4UdISogODgY+Wnp8IqNhbAOoxbrQyiTwSsmFnnp6QgJCWnUcytDx44dFQZRnDt3DufOneMoo8ZH1w8h5CUq7AjhWEZGBu4EB8MhMbHB+kS9i35ZGewTEnE7KAiZmZmc5FAfS5YsUbgNOGvWLIjFYo4yajx0/RBCXkWFHSEcCwkKgignB7bp6ZzmYZeeDlFODoKDgjjNoy50dXWxdu1aViwhIQHbtm3jKKPGQ9cPIeRVVNgRwqH8/HwkJSaiY0pqo/WLehM+w6BDSiqSEhKQn5/PaS51MWHCBHTt2pUVW7lyJbKzsznKqOHR9UMIeR0VdoRwKDIyEmplZWiTk8N1KgAAq5wcCMvKEBUVxXUqtcbn87F161ZWrKioCIsXL+Yoo4ZH1w8h5HVU2BHCEalUivvh4bBOfVKnZZ4agkAmg82TJ4gKC4P0LeuIqqpu3brh008/ZcX279+Pu3fvcpRRw6HrhxBSHSrsCKkBoVAId3d3+b9ff/0VwPNbYZ988gk6duyI9u3bY9q0aSgvL0dISIj8uSKRCA4ODnB3d8fMmTPlx8zLy0NFaSks8vLqlVtelRgj7t3DkIhwxJeW1utYAGCR+zyvvHrmNX36dJiZmaFz5871zqk21q5dC9Er07owDIOZM2eC6yk7eTwevvvuO/njuXPn4sCBAwCAiRMnon379vJrZsSIEdi6dav88avX38trT1nXDwDcKSzEwPAwjLh3r97HUtb1U1JSgr59+0IkEmHu3Ln1zouQloJWcCakBgwMDHCvmi+9yZMno3Pnzvj9998hk8nw1VdfYd68edi+fbv8+b169cL27dvh7OzM2jcjIwOMRAKDkpJ65RZSUAAXXRGWd+hYo+dLGQYCHu+N2/VLS8FIJMjOzkarVq3efTypFAKBQCH+ySefYPLkyZg2bVqN8lIWCwsLfPfdd1i4cKE8dvPmTfz+++8YN25co+byKpFIhN9++w0LFiyArq6uwvatW7di0KBBrNjLPwRMTEwUrr/MzEylXD8AcPrZU3xjbY0PTd798wbefg3V9vqRyWTg8xXbGNTU1LB8+XJER0fj0aNHNcqLEEKFHSF19vDhQ0RGRuL48eMAnvfx2rhxI6ytrRVajV5q27YtxowZgwsXLmDs2LG4dvEiDt6/j0qZDL4GBljcvgMAoPed2xhqaoZLebkQ8nj4ydEJpurqOPPsKbanpkKNx0cbTQ18a9MWG5KTUSmTIbK4GCfcPbAr7Qn+fvoUPABftLHCYFNThBYUYMeTVKjz+SiUSDDW3AJX83JRKJEgtaICM61tkFZRgYu5OTBRV8dITw9kZ2ejoqICc+bMQUlJCVq3bo1ffvkFRkZGrNexfv169OvXT+G1+vr6Ijk5uSF/BG/07bffYvfu3ayCYP78+RgyZEi1P5fGoKGhgXHjxmHnzp1YsGBBnY6RnJyMjz/+GE5OTggJCcHs4cPx9f37eCYWQ8zIMO3FzzutogJfxcSgk0gHUcXFsNfRQYC9A3g8HtYlPcaVvDyo8/j4yMQEZhrqOJ+Tg6D8AoQUFGBxu/b47uFDxJeWQJ3Pxw8dbeEoEmFrSgrSKiuQUl6OTiIRyqVSaAsEuF9cgkJJFdbb2eOXjHTElpbCtqIcXj17wtnZGQcPHsTWrVshFovRt29fbN68mfU67t27h4iICGhpaSm8Xz179sTjx4+V8fYT0mLQrVhCaqCgoIB1K/bq1auIiYmBm5sbq7VBV1cXbdu2xcOHD994LCsrK0RERMDa0hJDOnTACXcPnPHwREZlJcKKCuXPM9fQwCkPT7xnaIijWVkAgJ+ePMFPjk447emJ9Xb2sNfRgb+1Df5jaooT7h6IKi7G+Wc5OOHugUMurghMTUF2ZSUA4EFJCVZ1tMVRN3cAwMOyMvzs6ITDrm5Y+eghbHW0cdrTCwZCNSRF3UdmWhrmzJmDkydPIiwsDEOHDsWaNWsUXkd1RR3XNDQ0sHnzZlYsIyODlT8X/P39sWvXLlRUVChsmzlzpvz6en3C5VfFxsZi8eLFWL9mDVoVF2O9nR1OenjgqJs7/vskFeIX/e0el5fhizZtcN7TC7niKtwtKkJ+VRXO5eTgvKcXTnt6YkLr1hhuZo4+RkZY2qE9Vna0xW+ZmRAJBDjt6YWl7TtgQUKC/Nyp5RU46OKKFS9ah0ulUhxzd8c31jaYFhONeW3b4bSHJyJiYvAoMRGxsbH4+++/cfPmTURGRiInJwdnz55lvY64uDiFoo4QUnfUYkdIDVR3K/bUqVN1OtbIkSMBAJXl5Yh78gQ77kVALJMht6oKfoaG8NLTBwD0MzYGADiJdHElLxcA4Kmnh6UPE/FxK1N8aGKicOzwoiJ8YGIMDT4fGnw+vPUNcL+kBLoCATz19GCmoSF/rreBAbQEAmgJBFDj89HX6Pn57HS0kVtYiNSUFERGRqJPnz4Ani9b5eTkpPA6VNXHH3+MDz74AP/88488tmnTJkyZMgXt27fnJKdWrVph0KBB2Ldvn8K26m7FVsfOzg6urq6IuH0bWhIJDmSk43Lu8/5smZWVyKishJDHQzstLXTU1gEAOIp0kF5ZAQ89PegKBFiUmID3jY3R+8XP/FV3i4owtU0bAIC7nh4qZTIUv1gqrK+xEdRf+UPm/68ZHbTV0oKlpiYAwEKki+zMTFy+fBm3bt2S97MsKyuDl5cXnJyc5K+DEKJcVNgRUkedOnVCZGQkq49QcXExsrOz37oAvba2NgCgsqICv9y+jdMurjBVV8fapMcQy/6/g//LL1AB73mfJgD4vkNH3CsuxpW8PAy7F4EzHp41zlfrtX5Mr35B8155zAcPDCODpKoKHh4euHr16ltfh6ri8XgICAiAi4uLfIRmZWUl5s6dixMnTnCW19y5c/H+++/jo48+qtP+L993mVSKmMxMhBcV4Zi7OzT4fAx78UeCUCBg/Xz5PB5kDCDk8XDC3QNB+fk4m/MMp54+xbZOjjU+tyaf3ZdSnf+8nx0fgDrvleuJB1RVVUEmk2Hq1KlYvnw5a7/k5GSVv34IaaroViwhdWRrawsXFxesX78ewPNO4PPnz8dXX31Vo1tLEpkMPB4PBkIhiiUSXMrNfec+Tyqet7rMtrGBGo+HgtcWXffS08PF3FyIZTIUSqpwq7AArtV01H8XBjy0adMGT548QVhYGIDnRVFcXFytj8WlTp06YcaMGazYyZMncfnyZY4yen4L29fXV943s674AgFKq6pgIFSDBp+PmJISxL1jVHSpVIpiiQR9jI2xqF17xFbz/M56ejj97CkAILK4GJoCPnSFtW8DEAgE6Nu3L44cOYLcF9f206dPackxQhoYFXaE1MDrfey2bNkC4PkcaZGRkejYsSNMTExQUVGBpUuX1uiYRsbG6GFriwHhYZgWEw33GhRga5OSMCg8DIMiwtHP2ATmr9xaBQAXXV18aGKCofciMC4qCjOtbWCqrl7r1ysR8KEtEuHIkSPw9/eHm5sbvLy8EBkZWeNjTJw4Ed7e3oiKikKbNm1w9OjRWuehDCtWrIDJa7et/f39IXmtKG5MCxYsQEZGBiv2ah+73r17v/MYGlpacLKxQalUio/C7uKnJ0/g9I6BIaVSKb6IicbH4eGY9OAB5rVtp/CccRYWKJZI8HF4GFY+eoi1tna1e3EAZDw+hOrqcHJywpIlS9C3b1+4urpi4MCBtZoGxd7eHrNnz8bPP/+MNm3aIC0trda5ENLS8BiuJ3cipJkICwvD8OHDcezYsRrN3Xb58mXEX7iAfjdvNUJ2/4/B81uvb3PRuzvs+/dH3759GyOlBrdr1y6FaVe2bdum0JrXlHB1/dREc7t+CGlKqMWOECXx8vJCcnJyjSfkNTMzQ4mWFqqqmQOuIchkMuTk5iIrMxO5eXmoqqqq9nlVAgFKtLRgZmbWKHk1hilTpsDd3Z0VW7ZsmfwWYVPU2NdPTTXH64eQpoQKO0I4YmZmBp5QiEIdnUY5X0lpKcTiSjBgUFlZgWc5OSgoKID0teWoCnV0wBMKa/zFPHToUNZtand3d9y/f78hXkKdCQQChXVk8/Pza3zbXBU19vVTU7W9fnJzcxWun27dujVwloQ0XzQqlhCOGBkZQVNHB5lGRjApKuIgAwZl5WUor6iArkgEHZEIPACZxs/zMjIyqtFRTp482bBpKomfnx9Gjx6NI0eOyGM///wzvvzyyyY57Qb310/1anv9GBsbV7uqCyGkbqjFjhCOCAQCuHh6ItXaCtJqllRSNpGODoRCNYU4w8hQVFyEp0+fokQsRoqVFVy9vKpdJqypW79+PWvEskwmg7+/P+fryNZFY18/NSHl85v19UNIU6AanwaEtFBubm6o0tZGWjWTDb9OKpOhqLgYxSUlkNWhEOHz+WjVygS6unrg8RR/9aVSCRJFOsiXSqGmplgANgfW1tYKy3ldu3at3lOPcKU214+MkaG4uBjFxcV1un5q4omJCSTa2k2yBZSQ5oIKO0I4ZGhoiHa2tnhoYw3ZGxZVBwCGYZCbk4OSkmIUFxehID+/TufjgQddkQimpqbQ1tLGq+NjZTwennTsiJjERPj5+WHatGl4+vRpnc6jyubNmwdra2tWbO7cuSgvL+coo7qr8fUDBjk5uSguKUZxSTHyazHlSE3JeDw8srFGOzs7GBoaKv34hJCaocKOEI75+vmhxMQEiZaWb3xOcUkJJNL/n3etUiyu1zkFfD4MDAxgYmIC9Rfz3GXY2SFHJEJwSAhkMhl27doFW1tbbNq0CeJ6nk+VaGtrY+PGjaxYSkqKQqypqMn1U1JSConk/0dBi8ViKLvNLsHSEiUmJvDt0UPJRyaE1AYVdoRwzMLCAl18fRFna4uiapZZkkilKC0pYcU0XpuYuK7U1dRgYmwCYZs2eNTJEcF37iArK0u+vaioCHPnzoWTkxNOnTrVJPuiVWfEiBF47733WLE1a9bgyZMnHGVUd++6fqQyGUpeu37UNTTeOZdhbRRqayPezhZde/SAhYWFEo9MCKktKuwIUQG+vr4wbGOJsE6dIHmtI3xRUREYVvsKD7p1WCbsTSR8Ph54eKB1xw54//33q13D8+HDhxgyZAg++OADPHjwQGnn5gqPx0NgYKB8jV8AKC8vx/z58znMqu7eef0wr05pw4NeLa+foqIiPMvJQWk1S5BJ+HyEOXaCkaUlfHx86pI+IUSJqLAjRAUIhUIMHDwYZa1bI9TJUd5fqlIsRkUFu++Xjo4O1Oqwdmd1ZDweQp0cUW7RGoP+8x8sW7YM8fHxGD9+fLXPv3TpEtzc3DB9+nTk5OQoJQeuuLm54YsvvmDF/vjjD9y4cYOjjOruTdePuEqM8vIy1nO1tbVrNTimsLAQJaUlqKoSo7CoELm5uZC9mPvw1etnwODBECrpuiSE1B0tKUaICklJScHxw4dhlJqKrtExyM/OZvWN4vP4MDUzA/8tHeVrSsLnI9TJEXnW1hg+dixsbGxY22/duoVvv/0WoaGh1e5vYGCAFStW4Ouvv26yo2hzcnJga2uLgoICeczDwwN37txpktN1vH79FDx9iqqq/+8fyePxYWpqCkEtpkfJfvoUUil7XV0+nw+RoSEivbzeeP0QQrhBLXaEqBAbGxsMHzsWBW3b4aqLM4q0tVjbdfX0lFLUFWpr47qnBwratnvjl3L37t0REhKCQ4cOwbKajvkFBQX49ttv4erqivPnz9c7Jy6YmJjg+++/Z8UiIiKwb98+jjKqn9evn0ItTdZ2XV3dWhV1AKotcItFIlx1dcMjPT30+uADKuoIUSHUYkeICoqPj0fAxo0w09ODbVwcWickQEMghEmrVvXq9C7j8ZBgaYl4O1sYWVpiwODBMDc3f+d+paWlWL9+PdavX4+Kiopqn/PRRx9h06ZN6NSpUz0ybHxVVVVwd3dHTEyMPGZiYoLExEQYGBhwl1g9PHr0CJvWr4epSCS/ftQFQrSqw/VTWFSE0tLngy9kPB4y7OyQ6OCA9Lw8nDp3DjKZDEFBQbC3t1f+CyGE1BoVdoSooBkzZuCnn36Cj48PfLt0gUlJCRyzstGuoACC19Z2rQkpn48nJiZ4ZGONEhMTdO3RAz4+PrXuE5WamooFCxbgjz/+qHa7QCDA9OnTsXz58hovKaUKLl26hH79+rFis2bNwubNmznKqH7mzZuHLVu2sK6fTllZaF9QWOvrp7i4GAVlpcixssKTjh2fT4lz5w5CQkIglUoBANOnT8f27dsb4qUQQmqJCjtCVMz9+/fh7u4u76Bubm6OsaNHw6JVKwjLymDz5AkscvOgX1oKtRdfrNWpEghQqKODTGMjpFhZQaKtjXZ2dvBVwpQUwcHB8Pf3R1hYWLXbjYyMsHLlSkybNq3JdKgfOnQo/vrrL/ljoVCIqKioJtcCmZCQAGdnZ1RVPe+baW5ujpEjRsDK3LxO10+KrgiJ5uYoFwqRkJSE4JAQ1pQ4ALB06VKsXLmyQV8XIaRmqLAjRIUwDIO+ffvi6tWr8piWlhbi4uKgq6uLqKgoRIWFoaK0FIxEAlF5OfTy8qEukYDPyCDj8SEWClFkZIgSLS3whEJo6ujA1csLrq6uSl0RQCaT4ddff8WiRYsUvuhfcnJywpYtWxRaw1TRo0eP4OjoyJqMuX///jh//jx4SujX2FgGDRqEs2fPyh+rqakhOjoaJiYmdbp+qmQyXLhyBZGRkSgsLFQ4X79+/XD8+HGlTsFDCKk7KuwIUSHHjx/HiBEjWLEVK1Zg+fLl8sdSqRR5eXnIzs5GdnY2nmVlQVxRAalEAoFQCHVNTbQyN4eZmRnMzMxgZGTUoCM8i4uLsWbNGmzevBmVlZXVPufjjz/Gxo0bYWdn12B5KMPixYuxZs0aVuzUqVP4+OOPOcqods6fP48BAwawYvPnz8e6devkj2t7/YSEhOA///nPG8957NgxDB8+vKFeEiGklqiwI0RFlJeXw9HREcnJyfKYlZUV4uLiqp00WNUkJSVh/vz5OHbsWLXb1dTU8M0332Dp0qUqOyihpKQE9vb2yMjIkMc6duyIBw8eKG21j4YiFovh6uqK+Ph4eczMzAwJCQnQ09Or83FzcnLQpk0bedHO4/FYK5C0bdsWsbGx0NTUfNMhCCGNiKY7IURFbNq0iVXUAcDGjRubRFEHAO3atcPRo0dx7do1uLu7K2yvqqrC5s2bYWtri59//lne8V6ViEQiVusW8HzVjcDAQI4yqrnt27ezijoAWLt2bb2KOuD5COFLly5h9OjRmD17NrZu3cranpycjE2bNtXrHIQQ5aEWO0JUQFpaGuzt7VFW9v+rBPTs2RPXrl1rUv27XpJKpdi/fz+WLFmCp0+fVvscV1dXBAQEoHfv3o2c3dvJZDL4+vri1q1b8phIJEJCQoLKroOanZ0NOzs7FBUVyWNdu3bFzZs3WcumKQPDMHjvvfdYK3Roa2sjPj4ebdq0Ueq5CCG1Ry12hKiA+fPns4o6Pp+PwMDAJlnUAc+nPfn888+RmJiIefPmVbsyRVRUFPr06YNhw4bh0aNHHGRZPT6fr9AqVVJSgsWLF3OU0bstWbKEVdQBUFgLV1lerrP76rVZVlaGBQsWKP1chJA6YAghnLpx4wYDgPVv2rRpXKelVImJicyQIUMUXufLf+rq6sz8+fOZwsJCrlOVmzRpkkKeoaGhXKel4O7duwyPx2PlOWHChAY/79SpUxXen6CgoAY/LyHk7ehWLCEckkql6Nq1K8LDw+UxAwMDJCYmwsTEhMPMGsbly5fx7bff4sGDB9VuNzMzw6pVqzBx4kTO12rNysqCnZ0diouL5bFu3bohJCSkQVrC6oJhGPj5+SE4OFge09HRQUJCAlq3bt2g53727BlsbW1ZU6B4eXnh9u3bKvP+ENIS0W8fIRzav38/q6gDgO+//75ZFnUA0LdvX0REROC///1vta8xOzsbn3/+Obp06cLqw8UFc3NzLF26lBULDQ3FoUOHOMpI0eHDh1lFHQB89913DV7UAUCrVq2wYsUKViwsLAz79+9v8HMTQt6MWuwI4UhhYSFsbW3x7NkzeczR0RH37t2rtk9ac1NQUICVK1di27ZtkEgk1T5n5MiRWL9+Pdq2bdu4yb0gFovh7OyMxMREeczCwgLx8fGcT8hbWloKe3t7pKeny2Pt27dHdHR0o009UlVVBVdXV8TFxcljpqamSEhIgL6+fqPkQAhhoxY7QjiycuVKVlEHAAEBAS2iqAOe33LevHkzHjx4gIEDB1b7nKNHj8LBwQFLlixBSUlJI2cIqKurY8uWLaxYZmYmVq9e3ei5vG7t2rWsog4ANm/e3KjzyampqSEgIIAVe/r0KX744YdGy4EQwkYtdoRwIC4uDi4uLqyWqiFDhrDWKm1pLly4gFmzZiE2Nrba7RYWFlizZg0mTJjQqH24GIbBgAED8L///U8eU1dXR3R0NDp27NhoebwqKSkJnTp1Yq300a9fP1y4cIGTkdSDBw/G6dOn5Y+FQiEePHgAe3v7Rs+FkJaOWuwIaWQMw2DWrFmsok5dXb3FT/Lav39/REZGYtu2bdWuaZuZmYmJEyeie/fuCAkJabS8eDwetmzZAqFQKI+JxWLMmTOn0XJ43dy5c1lFnUAgQEBAAGfT42zevJnV0iyRSDBr1ixOciGkpaPCjpBGdu7cOVbrDwDMnj0bHTp04Cgj1aGmpoYZM2bg4cOH+Oabb6odGXvnzh34+vrik08+wZMnTxolLwcHB8ycOZMVO3XqFP75559GOf+rrly5ghMnTrBi06dPh6OjY6Pn8lLHjh0VCrnz58/j3LlzHGVESMtFt2IJaUSq3BlfFcXExGD27Nm4cOFCtdu1tLQwf/58zJ8/v8GXXqtusEunTp0QGRnZaP0iJRIJPDw8WNPFGBsbIzExsdpWzsZUXFwMOzs7ZGVlyWN2dna4f/8+1NXVOcyMkJaFWuwIaUSBgYGsog4A1q1bR0XdGzg6OuL8+fM4c+YM7OzsFLaXl5fj+++/h729PX777Tc05N+p+vr6CoMmYmNjsXPnzgY75+t+/vlnhTkAV61axXlRBwC6urpYu3YtK5aQkKCwigchpGFRix0hjaQpTHirysRiMXbu3IkVK1awJsV9Vffu3REYGIiuXbs2SA7VTSitr6+PxMREtGrVqkHO+VJubi5sbW2Rn58vj7m5uSEsLIzzyZxfkslk8Pb2xu3bt+UxXV1dJCQkwNzcnMPMCGk56NuEkEayePFiVlEHAFu3bqWirobU1dXx7bff4uHDh/jqq6+qfd9u3bqFbt264dNPP1WYCkQZBAKBQgtUYWGhwkTGDWH58uWsog54fv2oSlEHVL/ObnFxMZYsWcJRRoS0PNRiR0gjuHPnjkIr0sSJE2mW/nq4f/8+Zs2ahcuXL1e7XVtbG4sWLcKcOXOgpaWl1HN/8sknOHz4sPwxj8dDeHg43N3dlXqel+7fvw93d3fIZDJ5bNSoUThy5EiDnK++PvvsM/z666/yxzweD7dv30bnzp05zIqQloEKO0IamEwmg6+vL27duiWPiUQiJCYm0u2pemIYBqdOncKcOXPw6NGjap9jY2OD9evXY+TIkUqbDiQtLQ329vYoKyuTx/z8/PDvv/8qfcoRhmHQt29fXL16VR7T1NREfHw8rK2tlXouZcnMzISdnR1rUmlvb28EBwdzNiULIS0F3QMipIH9/vvvrKIOAJYuXUpFnRLweDwMGTIE0dHR2LBhA/T09BSek5KSgtGjR6Nnz54ICwtTynnbtGmDRYsWsWI3btzA3r178f3332Py5MkIDQ2t8/Hv3LmDzz//HCtWrMCBAwdYRR0ALFiwQGWLOuD5SO/Xb7/evHkTv//+O0cZEdJyUIsdIQ2opKQE9vb2yMjIkMc6duyIBw8eQENDg8PMmqfs7GwsXboUe/bsqXaELI/Hw8SJE7F69ep6F9bl5eVwdHREcnIy6/gvz6ulpYXk5GSYmprW6ri5ubmwtraWtwa+ekwAsLKyQlxcXINP71JflZWVcHJyYrWktm7dGvHx8RCJRBxmRkjzRi12hDSg1atXs4o6ANiyZQsVdQ3EzMwMu3btQnh4ON577z2F7QzDYP/+/bC1tcXatWtRUVFR53NpaWlh48aNCsd/qby8HDdu3Kj1cYOCgli3eF8vUDdu3KjyRR0AaGhoYPPmzaxYRkYG1qxZw1FGhLQM1GJHSAN5/PgxOnXqBLFYLI/1798f58+fp35GjYBhGJw4cQJz585ltaq9ql27dti4cSOGDh1a659JRUUFPvvsM/z5559vfM6uXbswefJk5OXlITs7G9nZ2XiWlYXK8nLIpFLwBQJoaGmhlbk5zMzMYGZmhtOnT2PKlClvPObw4cNx8OBBpQ8IaQgMw+DDDz9krdChoaGBmJgYtG/fnsPMCGm+qLAjpIEMHToUf/31l/yxUCjE/fv34eDgwF1SLVBFRQW2bNmC1atXszrzv6pXr14ICAiAm5tbjY+7bt06LFy48I3b9fX1MW/ePOhpaaGitBSMRAJReTn08/KgJpGAzzCQ8XioEgpRaGSEEi0t8IRCVEokuHjtGiIjI984X9+PP/7YZKYQiYmJgaurK6RSqTw2dOhQhWXRCCHKQYUdIQ3g0qVL6NevHys2a9YshVtTpPFkZmZiyZIlOHDgQLX97/h8Pj7//HP88MMPNeoX9+233yIwMFAhbm5ujh4+PrBt1w76PB5ss7JhkZcH/dJSqL1S3LyuSiBAoY4OkkU6eGRujjI1NSQmJSEoJIS1TBfwfG3Y7du31+BVq4bq3qtLly6hb9++HGVESPNFhR0hSlZVVQV3d3fExMTIY61atUJCQgIMDAy4S4wAAMLCwuDv74/g4OBqt+vp6WHZsmX45ptv3rrGaVxcHHx9fZGXlwfg+eTFPj4+8O3SBSYlJbBOTIRVXj6Ma7lcXGFRIYrKy5Hbpg1SbW2RIxIh+M4dhISEQCqVwsDAAEFBQXBycqrVcbmUn58POzs75OTkyGNOTk64d+8ehEIhh5kR0vxQYUeIkm3btg0zZ85kxXbt2oWpU6dylBF5HcMw+PPPPzF//nykpqZW+5yOHTti06ZN+Pjjj8Hj8VBZWYlDhw6hqKgIn376KYyNjZGamopp06YhPDwcgwcOhKWhIWzj4tA6IQF8hoG6mjpMTExqlVtObi7E4koAgIzHQ4adHRIdHJCel4ecggIEBATAxsam3u9BY/v555/x5ZdfsmLbtm3DjBkzOMqIkOaJCjtClCgnJwe2trYoKCiQxzw8PHDnzh2VWvqJPFdeXo6NGzdi7dq1rJGor3r//fexZcsW/PDDD/KBEp06dcLt27chEomQnJyM3w8cgFZGBhzu3oV2UZF8X4FACLNaTneS/fQppFIJK1amp4/HPt6osrbG0NGjm2RhJ5VK0blzZ9y7d08eMzQ0RGJiIoyNjblLjJBmhqY7IUSJli5dyirqANVbz5P8Py0tLSxduhTx8fGYMGFCtc+5dOkSXF1dWaNfY2NjsWjRIqSkpODEH3/AMvsp+sXGweC16VPediv3TTRe20ddTR3ttLTQ5/4DGCQn4/jhw0hJSan1cblW3Tq7+fn5jbLOLiEtCbXYEaIkkZGR8PT0ZK3nOWbMGNaaokS1hYaG4ttvv1VYKaQ6pqammP3NNzBLz4B3dDT4Lz5KS0pKUFZeDnU1NegbGKC2E9swAAoLCyAWV0FLSwu6r0zmK+PxcNPZCQVt22HMpxOa5OolY8aMYa1xy+fzER4eXqsRyYSQN6PCjhAlYBgGvXv3xr///iuPaWlpIT4+HlZWVhxmRmpLJpPh8OHDWLBgAdLT06t9jkAgwKRPP4WTmho+iI2DWiN+jEr4fFz39IBap074dPLkJjf4IDU1FQ4ODigvL5fHevXqhStXrtD8joQoAd2KJUQJjh07xirqAGDRokVU1DVBfD4f48aNQ3x8PObOnVvtc3x8fGBpaAj7O3dQkp/fqPkJZTJ4xcQiLz0dISEhjXpuZbC2tsaCBQtYsWvXruH48eMcZURI80KFHSH1VFZWplAA2NjYvLEoIE2Djo5Otas7mJubw7dLF9jGxUG7qAjl5WWs1qfGoF9WBvuERNwOCkJmZmajnlsZ5s2bB2tra1Zs7ty5jf4+EtIcUWFHSD1t3LhRYcqMjRs3Nokln8jb3b9/XyHWw8cHJiUlaJ2QII+VlpY2ZloAALv0dIhychAcFNTo564vbW1thXV2U1JSsGHDBo4yIqT5oMKOkHpITU3F2rVrWbFevXph+PDhHGVElOn1kbL6+vqwbdcO1omJ8sESACBUa/x+bnyGQYeUVCQlJCC/kW8HK8OIESPw3nvvsWJr167FkydPOMqIkOaBBk8QUg/VjfCLiIiAq6srh1kRZQoPD8fVq1dRUFAAsVgMQ6kUPS/8A5lYDEYmg5q6OoyMjGo9+lUZpHw+zvfwhecHHygUSU0BjSQnRPmosCOkjq5fv67wZfrVV19h586dHGVEGpJUKsXOwEBYRtyDS3Iy1+nI3W/XFunu7vja379Jzpf41Vdf4aeffmLFrl+/Dj8/P44yIqRpo1uxhNSBVCqFv78/K2ZoaIgffviBo4xangMHDjTqAJW8vDxUlJbCIi8PkcXFGHYvAo7BQbial/vGff6X8wyDI8IxOCIcTsFB+Dg8DIMjwvGzEm83WuQ+zys0NBTu7u7w8PBAbu6bc2psQ4cOhaGhIUaMGFHt9h9++EFhDWV/f39IpdJGyI6Q5ocKO0LqYO/evaylkQBg5cqVtDRSM5adnQ1GIoFBSQnM1NWxqqMtBpq0eus+H5q0wikPT5zy8ISpujr+cHPHKQ9PTHtlGhxZPW+a6JeWgpFI8Oeff2L8+PGIiIio0XWo7MLp1dupr/L398evv/76xv1MTEzw/fffs2IRERHYt2+fUvMjpKWgwo6QWsrPz8eSJUtYMScnJ4UFzsm7JScnw83NDePGjYOtrS2++uor/PXXX+jWrRucnZ2RmJiIiRMn4syZMwCer+rQtm3bGh27V69eWLBgATp37gxnZ2dER0cDeD6CdeLEiejSpQu8vLxw8eJFAM8Lt969e8PZ2RmLFi2CiYkJ63jZ2dkQlZdDKJPBXEMDnUQi8OvYsa7rrZv4/tFDDAoPQ1J5Ob5LTMTQiAgMCA/D3rQ01vPWJj3GoPAwfHo/CmUvirED6enoH3YXH4eH48f4OKTGxODXX39FQEAABg8eDIZh8O2338LZ2Rnu7u64dOnS8/0OHMCwYcPQq1cvjBw5EitWrMDkyZPRo0cPtGvXDv/73//w1VdfwdHREePHj5fnceHCBXh7e8PDwwPjx4+HWCwGABgbG2PGjBlwcXFBwiujhF//Oejq6r71/Xh5zlctXrxYYXk+Qsi7UWFHSC2tXLkSOTk5rFhgYGCTWwFAVcTGxmLZsmWIi4vDtWvXEBwcjNDQUHzzzTfYvn17vY6tpqaGu3fvYvbs2di8eTMAYNWqVRg0aBDu3LmDCxcu4JtvvgHDMPj+++8xZMgQPHjwAB06dFA41rOsLOjn5dUrn5cKJBL0NDTCGU8vdNDWxty2bXHSwwOnPDxxITcHmZWV8uf5GRrijKcXzNQ18E/u8+tux5NUnHT3wGlPT8xt2w7d9Q3Qp1cvLFy4EKdOncLx48fx8OFDREVF4a+//sLnn3+Oihfr2EZGRuLUqVM4ceIEgOfTjPz77784dOgQRowYgUmTJiE6OhqPHz9GREQEcnJysGHDBly5cgURERFo3749du/eDeD57emPPvoI9+/fh4ODQ53fDzU1NQQGBrJiOTk5Ci15hJB3o8KOkFqIjY1VKDaGDh2Kvn37cpRR02dvbw97e3sIBAJ06tQJ77//PgDAxcUFyfUcpDB06FAAgJeXl/xY//zzD1auXAl3d3e8//77KC0tRXZ2NkJCQjB69GgAkP/3VZXl5VCTSOqVz0uafD56GxnJH5959gxDIsIxNCIcSeXleFxWBgDQEQjga2AIAHAWiZBe8bzgcxXpYm58PE4/fQohjwd1iQTSV3ILCgrCJ598Aj6fj7Zt28LOzg7x8fEAgP79+0NPT0/+3AEDBkAgEMDFxQW6urro2rUreDwenJ2dkZycjFu3biEqKgre3t5wd3fH0aNHkZSUBOD5snkDBw5Uynvy/vvvY8iQIazY9u3bERsbq5TjE9JSUBMDITX08vaW5JUvUA0NDWzatInDrJo+DQ0N+f/z+Xz5Yz6fD6lUCqFQKO+/VfmiJau2xxYIBPI+ZTKZDKdPn4aNjQ3rue+aIEAmlbLmrqsPTf7//02dWlGO3zIz8KebO3SFQnwTGwMx8/z1qr2ydiqfx4P0xfl3OTkhtLAAl3JzsT8jHcttO76xj9vrtLW1WY9ffb9f/1lIpVIIBAIMHDgQ+/fvf+ex6mvTpk04f/68/FavRCLBrFmzcP78eVpHlpAaohY7Qmro9OnT+Oeff1ixuXPnol27dhxl1DLY2NjIB6q8vH1YHx988AG2bt0qf/zy2D4+Pjh69CgAyP/7Kr5AAFkDFBelEim0BQKIBAJkVVYi5B39ymQMg8zKSvgYGGJhu/bIrKyEhHleiL3Uo0cP/PHHH2AYBikpKUhMTIS9vX2d8vP29sbVq1eRkpICACgqKpK32Clbhw4dMGfOHFbswoUL8j6WhJB3o8KOkBqorKzE7NmzWbHWrVtj4cKFHGXUcnz++ec4c+YM3N3dFZZuq4ulS5eisLAQrq6ucHR0lC9ttXz5chw/fhwuLi6Ijo5m3a4EAA0tLVS96EcZV1oKv9uh+F9ODhYkJGBU5L0659NJJEIHbW18GB6GxYkJ8NLTf+vzpQyDufHx+Dg8DMPuRWC6lTUk6uoQvNLHc9iwYWjfvj1cXFwwZMgQ7N69G5qamnXKr1WrVti9ezeGDx8OV1dX9OzZU17k1cT777+PkSNH4ty5c2jTpg1u3rz51ucvXrwYFhYWrNjs2bNr3VpLSEtFExQTUgPr1q1TKOIOHTqEcePGcZQRUbaKigqoqalBIBDg6NGjOHLkCI4dOybffvnyZcRfuIB+N29xmGX1Lnp3h33//s2mr+fBgwfx6aefsmLr1q3D/PnzOcqIkKaDWuwIeYfMzEz8+OOPrJiPjw8++eQTjjIiDSE5ORmdO3eGq6srtm7dqrAGsJmZGUq0tFClYqs7VAkEKNHSgpmZGdepKM24cePQvXt3VuyHH35AZmYmRxkR0nTQ4AlC3mHhwoUoKSmRP+bxeAgMDKTO3Cpi1apVCn3i/P39MWnSpFodx8HBAREREazYhQsXsGDBAgDPO/Ln5eYiVF0Da62tFfa/kZ+PDclJAMMAL64NTz09rOjQsVZ51Fahjg54QiHnhV23bt0Ubpdevny5TpN28/l8bN26FV27dpXHSkpKsGjRIhw4cKC+qRLSrNGtWELeIjQ0VKHlYPLkydi7dy9HGRGuvGutWIZhUFBYiPLycgCAoYEBtLS0Gjyvpr5W7NtMmjRJoZALDQ1lFXyEEDa6FUvIG8hkMsycOZMV09PTw+rVqznKiHBJIBDAxdMTqdZWkPLZH51SmQw5ubkoLy8DwABgUFxc3OA5Sfl8pFhZwdXLq9kVdQCwZs0ahVUrZs6cWeOpXQhpiaiwI+QNDh48iNu3b7Niy5Yt4/yWF+GOm5sbqrS1kfbKcmNVEglycp6hqkrMei6P3/Afr09MTCDR1oarq2uDn4sL5ubmWLp0KSsWGhqKQ4cOcZQRIaqPbsUSUo3i4mLY2dkhKytLHrOzs8P9+/ehrq7OYWaEa8f+/BM5t26h990wVFVUIC8/HwzDbkHi8fgwNjaGuppag+Uh4/FwtbMXTLy9MWLkyAY7D9fEYrF83eCXzM3NkZCQ8M41aAlpiajFjpBqrFq1ilXUAcCWLVuoqCPw9fNDiYkJolu1Qm5enkJRJxAI0crEpEGLOgBIsLREiYkJfHv0aNDzcE1dXV2+zu9LWVlZ1CWCkDegwo6Q1zx8+BBbtmxhxQYMGIABAwZwlBFRJWZmZigoK0NUWxuU6bFbjNTV1GFiYgKhsP4TDlRJJMjOzkZmVhby8/Mhe6WALNTWRrydLbr26KEwmW9zNHDgQHz44Yes2ObNm/Hw4UOOMiJEdVFhR8hrZs+eLV+rEgCEQqFCiwFpmcrLyzFmzBisXbsW6fn5iPXygvTFoAVNTS0YGxtDoKS+dXl5eZDKpGAYGcorypGdlY2ioiJUAghz7AQjS0v4+Pgo5VyqjsfjYcuWLayCWSwWKyw/Rgihwo4QlgsXLuD06dOsmL+/f53X2STNx7Nnz9C3b18cPXoUUqkUp86eRYa2NmK7dYO2ri4MDQ2VOrfh6yM/GTAoKivFv+3bIVNbG55duyqlZbCpcHBwUBilfurUKYX1mwlp6WjwBCEvVFVVwc3NDbGxsfKYqakpEhISoK//9vU7SfMWFxeHgQMH4vHjx6x427Zt8fmnn8Ly2TN0i46BUInTcOTk5ED8ykhbqUCA2G7dkGpsjMPHj+PZs2c4evQoBg0apLRzqrqCggLY2dnh2bNn8linTp0QGRkJtQbu00hIU0EtdoS8sGPHDlZRBzyfR4uKupbt2rVr8Pb2Vijq9PT0sGvXLoyfPBkFbdvhhocHirS1lXZezVcmNy7V08O9nu8hydAQh48fx5MnT1BRUYHly5cr7XxNgYGBgcKgidjYWOzcuZOjjAhRPdRiRwie32aztbVFYWGhPObl5YXbt2+D3wjzkRHV9Ouvv+Lzzz9HVVUVK25tbY2zZ8/C2dkZwPNRmmdPnUJ+WjocEhNhm54Ofj0/WivFYjzLy0WGnR0SHRyQnpeHU+fO4enTp/LnvPfee7h27Vq9ztPUSKVSdO3aFeHh4fKYvr4+EhMT0apVKw4zI0Q1UGFHCIBp06Zh165drFhwcHCL6ZxO2BiGwYoVK7By5UqFbV5eXjh9+rTCaFSJRILg4GDcCQ6GKCcHHVJSYZWTA0Edbs9K+XykmhjjvokJckQiBN+5g5CQEEilUvlzDA0N8e+//8LFxaX2L7CJCwoKgp+fHys2bdo0/PTTTxxlRIjqoMKOtHgRERHw8vLCq78K48aNo9ntW6jKykpMmTIFv/32m8K2IUOG4LfffoOOjs4b98/IyEBIcDCSEhIgLCuDzZMnsMjNg35pKdReKcxeVyUQoFBHB5nGRkixsoJEWxsxCQn43z//KMypCACtW7dGZGQkTF5ZBaMl+eSTT3D48GH5Yx6Ph/DwcLi7u3OXFCEqgAo70qIxDIOePXsiKChIHtPW1kZCQgIsLS05zIxwITc3F0OHDsWNGzcUts2aNQsbNmyo8Zqs+fn5iIqKQlRYGCpKS8FIJBCVl0MvLx/qEgn4jAwyHh9ioRBFRoYo0dICTyiEpo4OXL284OrqihkzZuD3339/4zn69euH8+fPN8t1Yt8lLS0N9vb2KCsrk8f8/Pzw77//KnV0MiFNDRV2pEU7cuQIxowZw4r9+OOPWLJkCUcZEa48fPgQAwYMYC1dBQB8Ph9bt27F9OnT63RcqVSKvLw8ZGdnIzs7G8+ysiCuqIBUIoFAKIS6piZamZvDzMwMZmZmMDIykhdqoaGh6N27N8rLyyESiWBtbY2YmBjW8ZcuXVrtLeOW4Mcff1RYS/bIkSMYNWoURxkRwj0q7EiLVVZWBgcHBzx58kQea9euHWJiYqCpqclhZqSxBQcHY8iQIcjNzWXFdXR0cOTIEQwcOJCjzIDk5GRERESgZ8+eqKqqgqenJzIzM1nPOXPmDKc5cqW8vByOjo5ITk6Wx6ysrBAXFwdtJY5QJqQpoeF+pMVat24dq6gDgE2bNlFR18L88ccf6NOnj0JR17p1awQFBXFeMLVt2xZDhw6FsbExzM3NcfToUYWJiSdMmICkpCSOMuSOlpYWNm7cyIo9efIE69ev5ygjQrhHLXakRUpJSYGDgwMqKirksT59+uDSpUvUP6eFYBgGq1evxnfffaewzc3NDWfOnEGbNm04yOzdAgICMGvWLFbMw8MDISEhLe4PE4Zh0LdvX1y9elUe09TURFxcHGxsbDjMjBBuUIsdaZHmzZvHKuoEAgECAwOpqGshxGIxpkyZUm1RN2DAANy4cUNlizrg+TJ3I0eOZMUiIiLwzTffcJQRd3g8HgIDA1nzTVZUVGD+/PkcZkUId6iwIy3OtWvXcPToUVbsq6++kk82S5q3goICfPTRR9i/f7/Ctq+//hp///03dHV1Ocis5ng8Hvbu3QsHBwdWfM+ePdi3bx9HWXHHxcUFX375JSv2559/4t9//+UoI0K4Q7diSYsikUjg5eWFqKgoeczIyAiJiYkwMjLiMDPSGJKSkjBw4ECFpeN4PB42bdqEb7/9tkm12sbExKBr164oLS2VxzQ0NHDz5k14eHhwmFnjy83Nha2tLfLz8+UxNzc3hIWFtcjpYEjLRS12pEXZs2cPq6gDgB9++IGKuhYgNDQU3bt3VyjqtLS0cOLECcyaNatJFXUA4OjoiD179rBilZWVGD58OKvAaQmMjY3xww8/sGKRkZHYvXs3RxkRwg1qsSMtRn5+PmxtbVmjH11cXBAeHq4wypA0L8ePH8f48eNZ/SoBwMzMDGfOnEHnzp05ykw5/P39sXXrVlZs0KBB+Pvvv1vUWscSiQQeHh548OCBPGZsbIzExEQYGhpymBkhjafl/MaTFm/58uUKU1oEBgZSUdeMMQyDjRs3YuTIkQpFnZOTE0JDQ5t8UQcAGzZsUFjX+MyZM1izZg1HGXFDKBQiMDCQFcvNzcWKFSu4SYgQDlCLHWkRHjx4AHd3d9Yi6sOHD8exY8c4zIo0JIlEghkzZuDnn39W2NavXz8cPXoU+vr6HGTWMNLT0+Hh4YFnz57JYzweDxcuXEC/fv04zKzxDR8+HCdOnJA/FggEiIyMhJOTE4dZEdI4qLAjzR7DMOjXrx8uX74sj2lqaiI2NhZt27blLjHSYIqKijBq1ChcuHBBYdvnn3+OnTt3Qk1NjYPMGtaVK1fQr18/yGQyeczExATh4eGwsrLiMLPGlZSUhE6dOqGyslIe69evHy5cuNDk+lESUlt0K5Y0e3///TerqAOez2NHRV3z9OTJE/To0aPaom7t2rXYtWtXsyzqgOeTbK9evZoVy8nJwciRI1lFTnPXrl07zJ07lxW7ePEiTp06xVFGhDQearEjzVpFRQUcHR1Zyy21adMGcXFx0NHR4TAz0hDCwsLw8ccfK6ylqqGhgYMHDypM6tscMQyDoUOH4u+//2bFp0+fju3bt3OUVeMrLS2Fvb090tPT5bH27dsjOjq6xa3OQVoWarEjzdqWLVsU1tBcv349FXXN0OnTp9GzZ0+Foq5Vq1a4evVqiyjqgOf96g4cOIAOHTqw4jt27MBvv/3GUVaNT0dHR2HN2MePH2PLli0cZURI46AWO9Jspaenw97enjV5a48ePXD9+nXqZ9PMbN26Fd9++y1e/zizt7fHuXPn0L59e44y405kZCS8vb1RXl4uj2lrayM0NLTFrLLCMAz8/PwQHBwsj+no6CAhIQGtW7fmMDNCGg612JFma+HChayi7uWaklTUNR9SqRQzZ86Ev7+/QlHXq1cv3Lx5s0UWdcDzVRd++uknVqysrAzDhg1DUVERR1k1rup+50tLS7Fw4UIOsyKkYVFhR5qlmzdv4tChQ6zYlClT4OnpyVFGRNlKSkrwn//8B9u2bVPY9umnn+LChQstflLaTz/9FNOmTWPFEhMTMWnSJIVCuLny8vLC5MmTWbGDBw/i1q1bHGVESMOiW7Gk2ZHJZOjWrRvu3r0rj+nr6yMhIQGmpqYcZkaUJSMjA4MGDUJERITCtpUrV+K7776jltkXKioq4Ofnx/p9AICNGzdizpw5HGXVuLKzs2FnZ8dqqezSpQtu3brVolbmIC0DXdGk2fnll18UvsSWL19ORV0zERUVhW7duikUderq6jh06BCWLl1KRd0rNDU1cezYMYX1kBcsWIDr169zlFXjMjMzw7Jly1ixO3fu4Ndff+UoI0IaDrXYkWalqKgIdnZ2yM7OlsccHBwQFRXVbOcua0n+97//YeTIkSgpKWHFjYyMcPLkSfTs2ZOjzFTf//73PwwYMIB1C9bMzAwRERGwsLDgMLPGIRaL4erqivj4eHnMzMwMCQkJ0NPT4zAzQpSLWuxIs/Ljjz+yijrg+ZQnVNQ1fT/99BMGDRqkUNR17NgRN2/epKLuHT788EMsX76cFcvOzsaoUaNQVVXFUVaNR11dXWGqk+zsbPz4448cZURIw6AWO9JsJCQkwNnZmfUlNWjQIJw+fZrDrEh9yWQyzJ8/H5s2bVLY5uvri7/++gsmJiYcZFYzUqkUeXl5yM7ORnZ2Np5lZaGyvBwyqRR8gQAaWlpoZW4OMzMzmJmZwcjICAKBoEFykclkGDRoEM6fP8+Kz549u9r3tzkaNGgQzp49K3+spqaG6Oho2NracpgVIcpDhR1pNugDu/kpKyvDhAkTWAu6vzR27Fjs27dPZVcRyM/PR2RkJO6Hh6OitBSMRAJReTn08/KgJpGAzzCQ8XioEgpRaGSEEi0t8IRCaOrowMXTE25ubg0yqjc3NxdeXl5ISUlhxY8ePYoRI0Yo/Xyqhv4AJM0dFXakWTh//jwGDBjAis2fPx/r1q3jKCNSX9nZ2Rg8eDBu376tsG3JkiVYuXKlSo5ozMjIQEhQEJISE6FWVgbr1CewyMuDfmkp1KTSN+5XJRCgUEcHmUZGSLW2QpW2NtrZ2sLXz0/pfeDu3r0LX19fiMVieUwkEuHu3buwt7dX6rlU0bx587Bx40ZW7Pz58/jwww85yogQ5aHCjjR51Cm6+YmJicGAAQMUWpWEQiF27dqFSZMmcZTZm0kkEgQHB+NOcDBEOTnomJKKNjk5EMhktT6WlM9HmokJHtpYo8TEBF18feHr6wuhUKi0fHft2qUwx52joyNCQ0MhEomUdh5VVN0gK3t7e0RFRUFdXZ3DzAipP9X7c5eQWtq+fTurqAOAtWvXUlHXRF2+fBk+Pj4KRZ2+vj4uXLigkkVdVlYWftm3D3cuX4HD/QfofTcMNk+f1qmoAwCBTAabp0/R+24YHO4/wJ3LV/Drvn3IyspSWs5Tp07FxIkTWbGYmBh88cUXzX7yYj09PaxZs4YVi4+Px44dOzjKiBDloRY70qTRxKPNy759+zBt2jRIJBJWvG3btjh79iwcHR05yuzNUlJScPLIEWhnZMIrNhZ6ZWVKP0eRtjbCOnVCWevWGDp6FGxsbJRy3LKyMvj4+CAyMpIV37ZtG2bMmKGUc6iq6iYy19PTQ2JiIs15SZo0+uYjTdqSJUsU1r3cunUrFXVNjEwmw5IlSzBlyhSFoq5r1664deuWyhZ1xw8fhmFSMvwiIhqkqAMAvbIy+EVEwCA5CccPH1ZozawrbW1tHD9+HPr6+qz47NmzcfPmTaWcQ1Xx+Xxs3bqVFSsqKsKSJUs4yogQ5aBvP9JkhYWFYd++fazYhAkT0L17d44yInVRUVGBTz75BKtXr1bYNnz4cFy9ehVmZmYcZPZ2WVlZOHnkCIxSUtE9OhrCOt52rSmhTAbvB9EwSk3FySN/Ku22bIcOHRRWYKiqqsLIkSPx9OlTpZxDVXl7e2P8+PGs2N69exEeHs5RRoTUHxV2pEliGAb+/v6svkA6OjpYu3Yth1mR2nr27Bn69u2LI0eOKGybN28e/vzzT2hra3OQ2dtJJBKcPXUK2hmZ6BYTA34j9WjhMwy6RcdAKzMD506dUmjdrKvBgwdj0aJFrFh6ejrGjh0L6VtG8jYHa9euhY6OjvwxwzCYOXNms+9nSJovKuxIk3T48GEEBwezYt999x1at27NUUaktuLj4+Ht7Y2QkBBWXCAQ4KeffsL69etV9pZ6cHAw8tPS4RUb2+Atda8TymTwiolFXnq6wntXHytXrkSfPn1YsStXriissdrcWFpaYvHixaxYcHAw/vjjD44yIqR+aPAEaXJKS0thb2+P9PR0eax9+/aIjo5W2clqCdv169fxn//8B/n5+ay4rq4ujh49iv79+3OU2btlZGTg9wMH4HD/AezT0jjLI65NG8S7OGPcpElKm+fu6dOn8PT0ZP1uAcDff/+NwYMHK+UcqqiiogJOTk54/PixPGZpaYn4+HhWax4hTYFq/jlMyFusXbtW4Ytn8+bNVNQ1EYcOHcL777+vUNRZWVkhODhYpYs6AAgJCoIoJwe2r12Djc0uPR2inBwEBwUp7ZimpqY4evSownx5n376KR49eqS086gaTU1NhSXV0tPTaYJz0iRRYUealKSkJGzYsIEV69evX7NuTWguGIbB999/jwkTJigsOu/p6Ylbt27BxcWFo+xqJj8/H0mJieiYktpo/erehM8w6JCSiqSEBIUiuT68vb2xefNmVqywsBDDhw9HeXm50s6jaoYMGYK+ffuyYhs2bEBycjI3CRFSR1TYkSZl7ty5qKyslD8WCAQICAgAj8fjMCvyLpWVlfjss8+wYsUKhW0ff/wxrl+/3iT6R0ZGRkKtrAxtcnK4TgUAYJWTA2FZGaKiopR63BkzZmDs2LGsWGRkJL7++utmO6iAx+MhMDAQAoFAHquoqMDcuXM5zIqQ2qPCjjQZV65cUVgMfvr06So5vxn5f3l5eejfvz8OHjyosM3f3x8nT55sEv2YpFIp7oeHwzr1SZ1XlFA2gUwGmydPEBUWptTRqzweD7t27VL43Tpw4AD27NmjtPOoGicnJ3z99des2PHjx3H16lWOMiKk9qiwI02CRCKBv78/K2ZsbFxtCxBRHY8ePYKPjw/+/fdfVvzl5LABAQGsFpJ3EQqFcHd3l/+ry63B9evX13of4HmBWlFaCou8PFZ8e2oKBoSHYVB4GIbdi8CTioq3Hmd32pN67d/1FnviYIvc53nlvZbX6wICAiAWi9/6nFeJRCIcP35cYd3Yr7/+Gi4uLnB2doanpyeuXbtW42M2Bd9//z2MjY1ZMX9/f6VNLUNIQ6PCjjQJP//8Mx48eMCKrVq1CoaGhhxlRN4lJCQE3bt3V1jHV0dHB3///Te++eabWh/TwMAA9+7dk//T0tKq9THqUthJpVJkZ2eDkUhgUFIij4cXFSG0sBB/u3vgjKcXdnZyhJ7w7YXq7ldG0tZl/9fpl5aCkUhYC9pXp7aFnUwmg4ODA/bv38+KSyQS5OXl4fr16zh06BAmT55cq3xVnaGhIX744QdW7P79+9i1axdHGRFSO1TYEZWXm5uLpUuXsmJubm74/PPPOcqIvMuRI0fQp08f5LzWF83CwgLXr1/HoEGDlHauCxcuwNvbGx4eHhg/fry8ePniiy/g5eUFJycnbNy4EcDzJegKCgrg7u6OL7/8EsnJyejcubP8WHPnzsWBAwcAPF+fduHChfDw8MCVK1dw6NAh7Ni1C0Pv3sXqF9NiPBOLYShUg9qL+fbMNTSgL1QDANzIz8eoyHsYEhGOufFxEMtk2JycjGKJBIMjwrHsYWKt93/drrQnGB12F4E7d2Lbtm3y+KpVq+Di4gJXV1ds2bIFO3bsQEZGBnx8fOQDjQ4ePChveXs5ICk5ORkuLi4YM2YMHB0dUV5ejhEjRmD27Nms82ZkZGD8+PGwt7dHSUlJs5vE+IsvvoCrqysrtnTp0ne2ihKiEhhCVNz06dMZAKx///77L9dpkWrIZDJm9erVCj8vAIyrqyuTmppar+MLBALGzc2NcXNzY6ZMmcI8e/aM6du3L1NWVsYwDMMsXbqU2b59O8MwDJObm8swDMNUVVUx3bt3l5/b2NhYfrykpCTGy8tL/njOnDnM/v37GYZhGBsbG/mxYmJimK5dujC/TJrEJPTwY4a0MmV+dnRiwrt7M3ba2kwHLS3mU4vWzHE3dyahhx9zq1t3xlvfgIny9mESevgx062smGXtOzAJPfwYA6GQSejhxyT08KvX/vucnJnxFhZMvG8P5pdJkxlHR0fm/v37zNmzZ5k+ffowFRUVrPfBxsaGKS4uZhiGYdLS0pj27dszubm5THl5OePh4cHcvXuXSUpKYgQCARMZGcl638ViMdOjRw+Fn+mYMWOYDz/8sF4/U1V19epVhdc7Y8YMrtMi5J2ECpUeISrk/v37+O9//8uKjRo1Cj179uQoI/ImVVVV+Oqrr7B3716FbR9++CH+/PNP6Orq1uscL2/FvnTmzBlERUXB29sbwPPRtwMHDgTwfHWSPXv2QCqVIi0tDXFxcbCysqrV+UaOHAkAuHz5MhITE7Hs4UNoicWokMrgLBKht5ER/vLwRGhBAUIKCzDpwQMEOjhAzMgQX1aKUVGRAACxTIZeRkYKxxcJhXXeP6ggH9fy8nG3KALlMdEoEwqRkJCAoKAgTJo0CRoaGgAAo2rOe+fOHfTt21e+bcSIEQgKCsKQIUNgZ2en0FqlpqaGP//8Ex4eHqxbvn/88YfCes3NRa9evTBixAgcO3ZMHvvvf/+LadOmwdnZmcPMCHk7KuyIymJerAcre+UWlKampsI8doR7BQUFGDFiBC5fvqyw7csvv8S2bdsUJr1VBplMhoEDByr0A3v8+DF27NiBmzdvQl9fHyNGjGBNk/OSUChkXV+vP+flOrUymQw9e/TAeCMjuD1OYh+Dx4OvoSF8DQ1hJFTDpbxc9DAwRC9DI6y1s3vna6jr/jIGmGFtjWFmZohs3w7FPj4YNmwYguo5YfGb1ua1sLDAn3/+iT59+rBuvc6dOxd9+vSBjY1Nvc6rijZu3IgzZ86g4sWAFqlUCn9/f1y6dImmWCIqi/rYEZV18uRJhWkGFixYAGtra44yItVJTk6Gr6+vQlHH4/GwadMm7Ny5s0GKOuD5ZLpXr15FSkoKAKCoqAhJSUkoLi6GSCSCnp4e0tLScOnSJfk+AoFAXpiYmpoiIyMDxcXFKCkpwcWLF6s9T9++fXEnLAyFLwq/XLEYT8ViPC4rQ+qLkbkMwyChrBStNTTgoaeL0MICpL8oCEokEvloVwGPB+mLueDqsv9LPQwNcDQ7C+VSKWQ8PvIKClBYWIj3338f+/fvlxepL/uF6erqori4GADQtWtXXL58Gfn5+aisrMSJEyfg5+f3zve7Z8+e+PHHH1mxvLy8NxbOTZ2NjQ3mz5/Pil25cgUnT57kKCNC3o1a7IhKKi8vx5w5c1gxKysrhQ9Zwq07d+7g448/VhiRqaWlhd9++w1Dhw5t0PO3atUKu3fvxvDhwyEWi8Hn8xEQEIBevXqhU6dOcHBwQNu2bdGjRw/5Pp999hlcXFzQs2dP/PTTT5g/fz48PDxgbW39xpUvnJycMPQ//8HKI0egVVEBNT4f62ztUMnIsPLRI5S8KBSddESYYNEamgIBfuxoi2/iYlElk4HH42FJu/aw0tTEUFMzDAoPQxd9fYwyN6/1/i/1NDTCw7IyjIq8h5LYWIhuhmD8xIkYMGAAwsLC4OnpCTU1NUyaNAn+/v6YOnUqevfuDTs7O5w6dQrLly9Hz549wTAMPvvsM3h6etZolQVLS0vw+XxWS+fdu3fx7bffKnSbaA4WLFiAffv2Ie2V0cxz5szBgAEDaBlDopJ4DNNMpxEnTdqPP/6oMBL2yJEjGDVqFEcZkdedPHkS48aNU5hLztTUFKdPn0bXrl05yqxhXL58GfEXLqDfzVtcp6Lgond32Pfvr7AkVkMpLCxEly5dkJiYyIr/8ssv+PTTTxslh8b0xx9/KKzE8eOPP2LJkiUcZUTIm9GtWKJy0tLSsGbNGlasZ8+e8o7shFsMw2Dz5s3Vrh3q6OiI0NDQZlfUAYCZmRlKtLRQ9YYJlaUyGUpLS1FWXt6oy25VCQQo0dKCmZlZo51TX18fJ06cUOiPN23aNERGRjZaHo1l9OjRCreqV69ezWrFI0RVUGFHVM6CBQtQVlYmf8zn8xEYGEidlVWARCLB9OnTMWfOHIXipW/fvggODkbbtm25Sa6BmZmZgScUovC15c8YAKVlZXj69CkKiwpRUJCPwqKiRsurUEcHPKFQ6YVdbm4ua5UPd3d3dOvWTb7d2dlZYdLeiooKDB8+HAUFBUrNhWsv15F99TOorKwMCxcu5DArQqpHhR1RKcHBwfj9999ZsalTp8Ld3Z2bhIhccXExBg8eXG0/qsmTJ+P8+fMwMDBo/MQaiZGRETR1dJD5yvQhEokEubm5KCwsAMO8Mrr2HcuCKVOm8fO8qpvWpD6MjY1Zq3zcu3cPoaGhrOeMGzdOYW3VR48eYeLEiY3aatkYPDw8FCZF/+233xASEsJRRoRUjwo7ojKkUilmzpzJihkYGCgs70MaX1paGnr06IHz588rbFu9ejX27NkDNTU1DjJrPAKBAC6enki1toKEz0dxSQmePXsGsVhxNKj6iznkGpqUz0eKlRVcvbxqteauMm3evJnVkgcAf//9d53X5FVlq1atgr6+Pis2c+ZM1kASQrhGhR1RGQcOHEB4eDgrtmLFCrRq1YqjjAgAREREoFu3boiKimLFNTQ08Mcff2DRokUt5ja5m5sbKjQ0EKuujuLiIjBQbJXS1tKGwWtf/g3liYkJJNraChMKNyYNDQ0cPXoUxsbGrPjixYsVpitq6lq1aoUVK1awYmFhYQrzKBLCJRoVS1RCYWEh7Ozs8PTpU3nM0dER9+7da/YtQarszJkzGDNmDEpLS1lxExMT/P333/Dx8eEos8ZXVlaGFStWIPnxY3QzNobnlSvgv/LxKRAIYaCvL1/xoaHJeDxc7ewFE29vjFCBgUUXL15E//79WbdgTU1NER4eDktLSw4zU66qqiq4uroiLi5OHjM1NUVCQoJCax4hXKAWO6ISVq5cySrqACAgIICKOg5t374dQ4YMUSjq7OzscOvWrRZV1F27dg1ubm7YsGEDbgQHI0ckQoZ8VQgedHREMG3VqtGKOgBIsLREiYkJfF+Zo49L/fr1w8qVK1mxp0+fYtSoURCLxRxlpXxqamoICAhgxZ4+fUpdRojKoMKOcC4uLg5bt25lxQYPHox+/fpxlFHLJpVK8e233+Kbb75R6DvUs2dP3Lx5Ex06dOAou8ZVUFCAL774Ar1798bDhw8BAFlZWQi+cweJDg6oNDSCiYkJ9PX0GvV2dKG2NuLtbNG1Rw9YWFg02nnfZfHixfK1el8KCQlpdhOL9+/fHx9//DErFhgYiPj4eI4yIuT/UWFHODd79mxIJBL5Y3V1dWzevJnDjFqu0tJSDBs2DIGBgQrbJkyYgH/++Ufpoy9V1V9//QVHR0fs3r1bYdudO3fA19FBkq8P+I3YSgcAEj4fYY6dYGRpqXKtpnw+HwcPHlSY8iYwMBBHjhzhJqkGsnnzZqirq8sfSyQSzJ49m8OMCHmOCjvCqbNnzyqMtJw9e3aLaRFSJZmZmXjvvfdw6tQphW0rVqzAL7/80qi3GrmSnZ2NUaNGYejQocjMzFTY7u3tjbCwMHzx9dcob22JUCdHyBqptU7G4yHUyRHlFq0xYPDgBluDtz4MDQ1x/PhxhWtlypQpiI2N5Sgr5evYsSNmzZrFip07dw7nzp3jKCNCnqPBE4QzYrEYzs7OrGWJLCwsEB8fD11dXQ4za3nu37+PgQMH4smTJ6y4mpoa9u3bh/Hjx3OUWeNhGAa//PILZs+ejfz8fIXtOjo6WLNmDb7++mv51CIpKSk4fvgwjFJT0S06BsIGnPZCwucj1MkRedbWGD52LGxsbBrsXMqwb98+TJkyhRVzcHDA7du3m83vd3FxMezs7JCVlSWP2dnZ4f79+6zWPEIaE7XYEc5s3bpVYa3JtWvXNpsP/abiwoUL8PX1VSjqDA0NcfHixRZR1CUnJ6N///6YNGlStUVd//79ER0djW+++YY1X5yNjQ2Gjx2LgrbtcMPDA0WvLbGlLIXa2rju6YGCtu2aRFEHPJ+0+vXCLi4uDp9//nmzmbxYV1cXa9euZcUSEhIU+gwT0pioxY5wIisrC3Z2diguLpbHunXrhpCQEPD59PdGY9m1axe+/vprSKVSVrx9+/Y4d+4c7O3tOcqscUilUmzbtg1LlixhLWP3kpGREQICAjB+/Pi3Do7IysrC2VOnkJ+WDofERNimp7OmQqkrGY+HBEtLxNvZwsjSEgMGD4a5uXm9j9tYysvL4evri4iICFY8ICAA/v7+HGWlXDKZDN7e3rh9+7Y8pquri8TExEZdv5eQl6iwI5yYPHmywqSezXXxeFUkk8mwaNGialcH8PHxwV9//dXsJ4Z+8OABPv/8c4Vlsl4aPXo0tm7dClNT0xodTyKRIDg4GHeCgyHKyUGHlFRY5eRAUIfbs1I+H09MTPDIxholJibo2qMHfHx8VLJP3bskJSXB09OTtX6sUCjEtWvX4Ovry11iShQaGoru3buzYpMnT8bevXs5yoi0ZFTYkUZ3584dhQLus88+w4EDB7hJqIUpLy/HhAkTcPz4cYVto0ePxoEDB6CpqclBZo2jsrISa9aswerVq1FVVaWw3dLSEv/9738VprOoqYyMDIQEByMpIQHCsjLYPHkCi9w86JeWQu21ltFXVQkEKNTRQaaxEVKsrCDR1kY7Ozv4qtiUJnVx9uxZDBo0iBWzsLBAREREs2nV+uyzz/Drr7/KH/N4PNy+fRudO3fmMCvSElFhRxoVwzDw8fHBrVu35DGRSISEhIQm/+XVFDx9+hSDBw+utpVq8eLF+OGHH5r1rfBbt25hypQpiImJqXb7l19+ibVr1yplBYH8/HxERUUhKiwMFaWlYCQSiMrLoZeXD3WJBHxGBhmPD7FQiCIjQ5RoaYEnFEJTRweuXl5wdXWFoaFhvfNQFUuXLsWPP/7IivXq1QsXL15ski2Rr8vMzISdnR1KSkrkMW9vbwQHB7eYJfeIaqDCjjSqQ4cOYcKECazYunXrmt0EpqooNjYWAwYMQHJyMisuFArx888/Y/Lkydwk1ghKSkrw3XffYevWrdV23Le1tcXu3bvx3nvvKf3cUqkUeXl5yM7ORnZ2Np5lZUFcUQGpRAKBUAh1TU20MjeHmZkZzMzMYGRkxBqg0VxIpVJ89NFHuHjxIis+f/58rFu3jqOslGvdunVYuHAhK3bo0CGMGzeOo4xIS0SFHWk0JSUlsLe3R0ZGhjzWsWNHPHjwoEXMj8alq1evYtiwYax+TgCgp6eH48eP4/333+cmsUbwzz//4IsvvkBKSorCNoFAgHnz5mHZsmXQ0tLiILuWJScnB56engojsE+cOIGhQ4dylJXyVFZWwsnJCY8ePZLHWrdujfj4eIhEIg4zIy1J873nQlTOmjVrWEUd8Hz2dirqGtaBAwfwwQcfKBR1NjY2CAkJabZFXW5uLj777DP079+/2qLOw8MDd+7cwZo1a6ioayQmJiY4evSowhrQEydOVJj6qCnS0NBQWDUnIyMDa9as4Sgj0iIxhDSCR48eMRoaGgwA+b/+/fszMpmM69SaLZlMxnz33Xes9/zlvy5dujBZWVlcp9ggZDIZc+TIEcbU1LTa166pqcmsXbuWqaqq4jrVFmvHjh0KPxcXFxemtLSU69TqTSaTMR988AHrtWloaDCPHj3iOjXSQlBhRxrFf/7zH9YHnVAoZGJjY7lOq9kqLy9nPvnkk2oLm6FDhzaLL9DqpKWlMYMHD672dQNg3nvvPSYhIYHrNFs8mUzGjB8/XuHnM2HChGbxx15MTAwjEAgUfu8IaQxU2JEGd/HiRYUP8FmzZnGdVrP17NkzpkePHtUWNnPmzGEkEgnXKSqdVCplfv75Z0ZPT6/a162np8f8/PPPjFQq5TpV8kJJSQnj7Oys8LP673//y3VqSuHv76/w2i5dusR1WqQFoMETpEFJJBK4ubmxppdo1aoVEhISYGBgwF1izVRiYiIGDBiAhw8fsuJ8Ph/bt2/HV199xVFmDScxMRFTp07Fv//+W+32wYMHY+fOnbC0tGzkzMi7JCQkoEuXLigqKpLH1NXVcePGjSY/WXlBQQFsbW2Rk5Mjjzk5OeHevXvNYnoXorpo8ARpUP/9738V5gxbtWoVFXUN4MaNG+jevbtCUScSiXDmzJlmV9RJJBKsX78erq6u1RZ1pqamOHLkCP766y8q6lSUnZ2dwsTkYrEYI0aMYBVETZGBgQFWrVrFikVHR+Onn37iKCPSUlCLHWkwOTk5sLW1ZY3GfDkSsTnO08Wl33//HZMmTYJYLGbFLS0tcfbsWbi5uXGUWcO4d+8epkyZgvDw8Gq3f/rpp9i8eTOMjY0bOTNSF/Pnz8eGDRtYsQ8++ADnzp1r0p8VUqkUnTt3xr179+QxQ0NDJCYm0rVJGgy12JEGs3TpUoUpNgIDA5v0B7WqYRgGP/74I8aNG6dQ1Hl4eCA0NLRZFXXl5eVYtGgROnfuXG1RZ2Njg//973/45Zdf6IuzCVm9ejV69uzJiv3zzz9YuXIlRxkph0AgwNatW1mx/Px8LF26lKOMSIvAaQ8/0mzdu3eP4fP5rI7DY8aM4TqtZqWyspL57LPPqh0sMGjQIKa4uJjrFJXq33//ZWxtbat9vTwej/H39292r7klyczMZCwsLBR+tmfPnuU6tXobPXo06zXx+Xzm3r17XKdFmim6FUuUjmEY9O7dm9XvSUtLC/Hx8bCysuIws+YjPz8fw4cPx9WrVxW2ffPNN9iyZUuzaRktKirCggUL3tg3ydHREXv27IG3t3cjZ0aULSgoCL169YJUKpXHDA0NERYWhnbt2nGYWf2kpqbCwcEB5eXl8livXr1w5coVWkeWKB3diiVKd+zYMYXO7IsWLaKiTkkeP34MHx8fhaKOx+MhMDAQW7dubTZF3enTp+Ho6FhtUaempobly5cjPDycirpmokePHgp97fLz8zFixAhUVFRwlFX9WVtbY8GCBazYtWvXcPz4cY4yIs0ZtdgRpSovL4eDgwNSU1PlMRsbG8TGxtKyTUpw69YtDB48GM+ePWPFtbW1cfjwYQwePJijzJTr6dOn8Pf3xx9//FHt9m7dumHPnj1wdnZu5MxIQ2MYBqNHj8bRo0dZ8alTp2LXrl0cZVV/9NlIGgu12BGl2rBhA+uDCwA2btxIH1xKcPToUfTu3VuhqDM3N8f169ebRVHHMAwOHjyITp06VVvUaWtrY8uWLQgODqairpni8XjYu3cv7O3tWfHdu3dj//79HGVVf1paWti4cSMrlpKSohAjpL6oxY4oDfUjaRgMw2DDhg0Kt3IAwMXFBWfOnIG1tTUHmSlXSkoKpk2bhgsXLlS7vV+/fvj555+bdF8rUnMxMTHo2rUrSktL5TFNTU2EhITAw8ODw8zqjvofk8ZALXZEaebPn88q6vh8PgIDA6moq4eqqipMmzat2qLugw8+QFBQUJMv6qRSKbZt2wYnJ6dqizpDQ0McOHAAFy5coKKuBXk5KOZVFRUVGDFiBPLz8znKqn5e9oPl8///q7e8vBzz58/nMCvS7HA0Gpc0M9evX1eYpuCrr77iOq0mraCggPnggw+qnd7jiy++YMRiMdcp1lt0dDTj7e1d7WsEwIwcOZLJzMzkOk3CoZkzZ1Y7nU9TXvf3yy+/VHhN169f5zot0kzQrVhSbzS7uvKlpqZi4MCBePDggcK2DRs2YM6cOU26JVQsFmPdunX48ccfFSZWBoDWrVtj586dGDJkCAfZEVUiFovRq1cv3Lx5kxVftWoVFi9ezFFW9UOr8pCGRLdiSb3t3buXVdQBwMqVK6moq6OwsDB069ZNoajT1NTEsWPHMHfu3CZd1N2+fRteXl5YtmxZtUXd1KlTER0dTUUdAQCoq6vjzz//RKtWrVjxpUuX4tKlSxxlVT8mJib4/vvvWbGIiAjs27ePo4xIc0ItdqReCgoKYGtry1qw28nJCffu3YNQKOQws6bp77//xieffIKysjJW3NTUFKdOnUK3bt04yqz+SktLsXTpUgQGBkImkyls79ixI3bt2oXevXtzkB1RdVeuXEG/fv1Y146JiQnCw8Ob5MCDqqoquLu7IyYmRh4zMTFBYmIiDAwMuEuMNHnUYkfq5fvvv2cVdcDz9WCpqKsdhmEQEBCAoUOHKhR1nTp1wq1bt5p0UXfp0iW4uLhgy5YtCkUdn8/H/PnzERUVRUUdeaM+ffpg1apVrFhOTg5GjhxZbcuvqlNTU0NgYCArlpOT0+TXxyXcoxY7UmexsbFwdXWFRCKRx4YOHYoTJ05wmFXTI5FIMGvWLGzfvl1hW58+fXDs2DEYGhpykFn95efnY86cOW+cf8zNzQ179+6Fl5dXI2fWckmlUuTl5SE7OxvZ2dl4lpWFyvJyyKRS8AUCaGhpoZW5OczMzGBmZgYjIyOV6fclk8kwdOhQnDp1ihWfMWMGtm3bxlFW9TN06FD89ddf8sdCoRBRUVHo1KkTd0mRJo0KO1InDMPgww8/xD///COPaWhoICYmBu3bt+cws6alpKQEY8aMwdmzZxW2TZw4ET///DPU1dU5yKz+jh8/junTpyM7O1thm4aGBpYvX465c+dCTU2Ng+xanvz8fERGRuJ+eDgqSkvBSCQQlZdDPy8PahIJ+AwDGY+HKqEQhUZGKNHSAk8ohKaODlw8PeHm5qYSf2AUFBSgc+fOePToESv+22+/4ZNPPuEoq7p79OgRHB0dWa2O/fv3x/nz55t0X1rCHSrsSJ2cPn1aYaWDxYsXK9wqIW+Wnp6OQYMGKQw8AZ6P+Fu0aFGT/GDPzMzE9OnTcfLkyWq39+jRA3v27FFYWYA0jIyMDIQEBSEpMRFqZWWwTn0Ci7w86JeWQk0qfeN+VQIBCnV0kGlkhFRrK1Rpa6OdrS18/fxgYWHRiK9AUWRkJLp3785aP1ZbWxuhoaFNckWSxYsXY82aNazYqVOn8PHHH3OUEWnKqLAjtVZZWQknJyfWX8ytW7dGfHw8RCIRh5k1HZGRkRg4cCDS09NZcXV1dRw4cABjx47lKLO6YxgGe/fuxdy5c1FYWKiwXVdXF+vWrcO0adNYE7SShiGRSBAcHIw7wcEQ5eSgY0oq2uTkQFDNwJV3kfL5SDMxwUMba5SYmKCLry98fX057Uv7yy+/YOLEiayYnZ0d7ty5Az09PW6SqqOSkhLY29sjIyNDHuvYsSMePHgADQ0NDjMjTRF9upJaCwgIULgNsn79eirqaujcuXPo0aOHQlFnbGyMy5cvN8mi7uHDh+jbty+mTp1abVE3cOBAREdH46uvvqKirhFkZWXhl337cOfyFTjcf4Ded8Ng8/RpnYo6ABDIZLB5+hS974bB4f4D3Ll8Bb/u24esrCwlZ15zn332Gb744gtWLCEhAZMnT0ZTa68QiURYt24dK/bw4UOFwRWE1AS12JFayczMhJ2dHUpKSuQxb29vBAcHN8nbho3tv//9L2bMmKEwMtTW1hZnz56Fra0tR5nVjUQiQUBAAJYtW8ZaTu4lExMTbN26FWPGjKHro5GkpKTg5JEj0M7IhFdsLPReG2WtDEXa2gjr1AllrVtj6OhRsLGxUfo5aqKiogJ+fn64e/cuK75x40bMmTOHk5zqSiaTwdfXF7du3ZLHRCIREhISOL/1TZoW+tOZ1MqiRYtYRR2Px8PWrVvpS/sdpFIp5syZg6+//lqhqPPz88PNmzebXFEXGRkJb29vzJs3r9qibvz48YiNjcXYsWPp+mgkKSkpOH74MAyTkuEXEdEgRR0A6JWVwS8iAgbJSTh++DBSUlIa5DzvoqmpiaNHj8LIyIgVX7BgAa5fv85JTnXF5/OxdetWVqykpKTJrq5BuEOFHamx0NBQ/PLLL6zYpEmT0LlzZ44yahpKS0sxYsQIbN68WWHbuHHjcPHixSa1SkdFRQW+++47dO7cWaGlBACsrKxw7tw5HDx4ECYmJhxk2DJlZWXh5JEjMEpJRffoaAjreNu1poQyGbwfRMMoNRUnj/zJ2W3Ztm3b4rfffmP98SCVSjF69GhkZmZyklNddenSBZMmTWLFDhw4gNu3b3OUEWmKqLAjNSKTyTBz5kxWTFdXF6tXr+Yoo6YhKysLvXr1Ys1T9dKyZctw8ODBJtU5OigoCB4eHli1ahVr/kLgeevtjBkzEB0djY8++oijDFsmiUSCs6dOQTsjE91iYsBvpB42fIZBt+gYaGVm4NypUwrXRGP58MMPsWzZMlYsKysLo0ePRlVVFSc51dXq1auhq6vLis2cObPa1VoIqQ4VdqRGDh06pPBX47Jly2BmZsZRRqrvwYMH6Natm0KrlpqaGn755Rd8//33TeYWZVFREaZPnw4/Pz/ExcUpbHdwcEBQUBC2bdum8KVEGl5wcDDy09LhFRvb4C11rxPKZPCKiUVeejpCQkIa9dyvWrZsGT788ENW7MaNG1i0aBFHGdWNubk5li5dyoqFhobi0KFDHGVEmhoaPEHeqbi4GHZ2dqxbLXZ2drh//36TnTy3oV28eBEjRoxAUVERK25gYICTJ0+iV69e3CRWB+fOncOXX36JJ0+eKGwTCoVYuHAhlixZAk1NTQ6yIxkZGfj9wAE43H8A+7Q0zvKIa9MG8S7OGDdpEmed/XNzc+Hp6YnU1FRW/OjRoxgxYgQnOdWFWCyGs7MzEhMT5TELCwvEx8fTH07knajFjrzTqlWrFPrPbNmyhYq6N9izZw8GDBigUNS1a9cON2/ebDJF3bNnzzBu3DgMHDiw2qKuc+fOCAsLww8//EBFHYdCgoIgysmB7WvT5zQ2u/R0iHJyEBwUxFkOxsbGOHbsmMJn0+TJkxEfH89RVrWnrq6OLVu2sGKZmZnU9YXUCBV25K0ePnyo8AHz0UcfYcCAARxlpLpkMhkWLVqEqVOnKvQ16t69O0JDQ+Hg4MBRdjXHMAx+//13ODo64vfff1fYrqWlhY0bN+LmzZtwdXXlIEPyUn5+PpISE9ExJbXR+tW9CZ9h0CElFUkJCcjPz+csjy5duiisG1tcXIzhw4ejtLSUo6xqb8CAAQq3ljdv3oyHDx9ylBFpKqiwI281Z84c1hqGQqFQodAjQHl5OcaOHYu1a9cqbBs5ciSuXLmCVq1acZBZ7Tx58gSDBg3CuHHjkJOTo7C9T58+uH//PubMmcPpqgPkucjISKiVlaFNNT8rLljl5EBYVoaoqChO85g6dSo+++wzViw6OhpTp05tMpMX83g8bNmyhfV7JhaLMXfuXA6zIk0BFXbkjf755x+cOnWKFfP396c1Pl/z7Nkz9O3bF3/++afCtoULF+KPP/6AlpYWB5nVnEwmw44dO+Do6Ihz584pbNfX18fevXtx6dIldOjQgYMMyeukUinuh4fDOvVJnVeUUDaBTAabJ08QFRYG6VvWoW1oPB4PO3fuVGhRPnz4MHbu3MlRVrXn4OCgMBvB33//jYsXL3KUEWkKqLAj1aqqqsK3337LipmamiqM1mrp4uLi0L17d9y8eZMVFwgE2LVrF9asWaPyS2jFxcXhvffew4wZM1iTT780bNgwxMbGYvLkyU1mFG9jEQqFcHd3l/+rbqLmd1m/fn2dzp2Xl4eK0lJY5OWx4ttTUzAgPAyDwsMw7F4EnlRUvPU4u9PY/Sdru3/XW+xr3yL3eV55r+X1uoCAANbdgPp49ecwbtw4AIC2tjaOHz8OfX191nNnzZrFWt1B1S1btkyhtd/f37/JTeNCGo9qf+MQzuzcuROxsbGs2OrVqxU+JFuya9euwcfHB48fP2bF9fT0cP78eUydOpWjzGqmqqoKq1atgpubG4Kq6fBubm6O48eP4/jx47Sk0RsYGBjg3r178n91aZmtS2EnlUqRnZ0NRiKBwSvFeHhREUILC/G3uwfOeHphZydH6AkFbz3W7ldG0tZl/9fpl5aCkUiQnZ391ufVtrB72zxur/4cfvvtN3m8Y8eOCpOqV1VVYcSIEXj27FmNz80lfX19hUETsbGxTarlkTQuKuyIgmfPnmH58uWsmJeXl8KM6C3Zr7/+ig8++EChk7i1tTWCg4PRr18/jjKrmbt376Jz58747rvvqv1ynTx5MmJiYjBs2DAOsmvaLly4AG9vb3h4eGD8+PHy9/eLL76Al5cXnJycsHHjRgDAkiVLUFBQAHd3d3z55ZdITk5mreQyd+5cHDhwAMDzFRYWLlwIDw8PXLlyBYcOHcKOXbsw9O5drH7xx8UzsRiGQjWovWglNtfQgL5QDQBwIz8foyLvYUhEOObGx0Esk2FzcjKKJRIMjgjHsoeJtd7/dbvSnmB02F0E7tzJGsCwatUquLi4wNXVFVu2bMGOHTuQkZEBHx8fDB48GABw8OBBuLi4wNnZGRs2bAAAJCcnw8XFBWPGjIGjo2OdWkSHDBmChQsXsmLp6ekYO3Ysp7eLa2PSpEnw9PRkxZYvX95kilPSyBhCXvPFF18wAFj/goODuU5LJchkMmbZsmUK7w8ApnPnzkxGRgbXKb5VaWkpM3fuXIbP51f7Gtq3b89cunSJ6zSbDIFAwLi5uTFubm7MlClTmGfPnjF9+/ZlysrKGIZhmKVLlzLbt29nGIZhcnNzGYZhmKqqKqZ79+5MamoqwzAMY2xsLD9eUlIS4+XlJX88Z84cZv/+/QzDMIyNjY38WDExMUzXLl2YXyZNYhJ6+DFDWpkyPzs6MeHdvRk7bW2mg5YW86lFa+a4mzuT0MOPudWtO+Otb8BEefswCT38mOlWVsyy9h2YhB5+jIFQyCT08GMSevjVa/99Ts7MeAsLJt63B/PLpMmMo6Mjc//+febs2bNMnz59mIqKCtb7YGNjwxQXFzMMwzBpaWlM+/btmdzcXKa8vJzx8PBg7t69yyQlJTECgYCJjIx8689BTU2N8fT0ZHx8fJgLFy4obK+qqmJ69+6tcL0vXry4dj9wDgUFBSnkP23aNK7TIiqIhrURloiICOzevZsVGzduHHx8fDjKSHVUVlZiypQprFs9Lw0ZMgS//fYbdHR0OMisZq5cuYIvvvgCjx49UtjG5/Mxa9YsrFy5Etra2hxk1zS9vAX40pkzZxAVFQVvb28Az6+ZgQMHAnjecX/Pnj2QSqVIS0tDXFwcrKysanW+kSNHAgAuX76MxMRELHv4EFpiMSqkMjiLROhtZIS/PDwRWlCAkMICTHrwAIEODhAzMsSXlWJUVCQAQCyToZeRkcLxRUJhnfcPKsjHtbx83C2KQHlMNMqEQiQkJCAoKAiTJk2SL51nVM1579y5g759+8q3jRgxAkFBQRgyZAjs7OzeOa1OUlISLC0tkZCQgA8++AC3b9+GqampfLtQKMQff/wBDw8PZGRkyOOrV69G9+7d8fHHH7/zveear68vxo4di8OHD8tju3btwpdffgl3d3fuEiMqhwo7IscwDPz9/VnTAWhra1c7hUdLk5ubi6FDh+LGjRsK22bPno3169dDIKhdX6TGUlBQgHnz5mHPnj3VbndxccHevXvRpUuXRs6s+ZHJZBg4cCD279/Pij9+/Bg7duzAzZs3oa+vjxEjRqCyslJhf6FQyOpL9vpzXhbdMpkMPXv0wHgjI7g9TmIfg8eDr6EhfA0NYSRUw6W8XPQwMEQvQyOstbN752uo6/4yBphhbY1hZmaIbN8OxT4+GDZsWLX9N2ujJn9oWFpaAni+Ik6XLl0QExPDKuyA54O/jh49ivfee481z+SECRMQHh6O9u3b1yvPxrB+/Xr8/fffKCsrA/D8M3vmzJn4999/aWATkaM+dkTuzz//VChcFi9ejDZt2nCUkWp4+PAhvL29Fd4bPp+PHTt2YNOmTSpb1J08eRKOjo7VFnXq6ur44YcfcPfuXSrqlMTb2xtXr15FSkoKgOdr7CYlJaG4uBgikQh6enpIS0vDpUuX5PsIBAJ5Xy9TU1NkZGSguLgYJSUlb5zWom/fvrgTFobCF4VfrliMp2IxHpeVIfVFPzSGYZBQVorWGhrw0NNFaGEB0l+McC2RSOSjXQU8HqQv/piry/4v9TA0wNHsLJRLpZDx+MgrKEBhYSHef/997N+/X16kvhwtq6uri+LiYgBA165dcfnyZeTn56OyshInTpyAn59fjd7zl/sAQHZ2NsLCwmBra1vtc318fLBp0yZWrLCwEMOHD69T/73G1qZNG4W1b2/cuIGjR49ylBFRSZzeCCYqo7S0lLGysmL132jbti1TXl7OdWqcCgoKYoyNjRX6tohEIubs2bNcp/dGmZmZzIgRI6rtRweA8fHxYWJiYrhOs8l7tX/cS//88w/j5eXFuLi4MG5ubszVq1cZhmGYTz/9lLGzs2M++OADZuDAgczp06cZhmGYefPmMZ06dZL3l9q0aRPToUMHpnfv3syIESNYfexe9kljGIaZ/tVXjLWREWOvrc04i0TMWQ9P5oS7O+Mm0mXaa2oy7TQ0mAEGBsy97t5MQg8/Zr+TM+MsEjH22tqMg44Oc9DZhUno4cd8btmG6aClxYwxN2dOuLsz7rq6TEdtbaajtjYzpJWpvF/dm/Z/tY/ewnbtGHttbcbSwJCxt7NjsrKyGIZhmJUrVzKOjo6Mm5sbExAQwDAMwwQGBjL29vbMxx9/zDAMw/zyyy+Ms7Mz4+TkxKxfv55hGMU+h9UJDg5mnJ2dGVdXV8bV1ZU5fPjwW58vk8mYMWPGKPxOTJo0iZHJZG/dVxWUlZUxbdu2ZeVuZWXFlJaWcp0aURE8hmki03CTBrV8+XKsXLmSFTt+/HiLHhX5xx9/YOLEiQq3wywtLXHmzBmV7NfCMAwOHDiAOXPmVLusk0gkwpo1a/D111+r/Px65O0uX76M+AsX0O/mLTAAKisqUFZWhorKSjz/vn9OW1sbBvoGjZrbRe/usO/fH3379m3U89ZUSUkJunbtqjCl0+7du/H5559zlFXNHT9+HCNGjGDFli9fjhUrVnCTEFEp9MlOkJKSojCXVp8+fTB06FCOMuIWwzBYvXo1xo4dq1DUubm54datWypZ1D1+/BgffPABJk+eXG1R99FHHyE6OhozZsygoq4ZMDMzQ7GmJnJLS5GdnY28/DxUVFbg1aIOACRVkuoP0ECqBAKUaGnBzMysUc9bGyKRCCdOnIBIJGLFZ8yYgbCwMI6yqrlhw4ahd+/erNi6deuQmprKUUZEldCnO8G8efNQ8Up/GYFAgMDAwBbZGVcsFmPKlClYsmSJwrYBAwbgxo0bKtfnUCqVYsuWLXBxcWH13XrJ2NgYBw8exNmzZ2Ftbc1BhkSZCgsLsWvXLvj7+6O4rAxP1YSQyd40HxsP2o08UrtQRwc8oVDphV1ubi5rlQ93d/f/a+++w5q6+jiAfzNYYe89RDYogpvhqqsOrOKeEEe1tbXW1dY6q7Z11F13EPeoe1WrdaKigltkiWyQvVeS8/5h5TUmYSaEcT7Pw9NybnLvLwi539x7Bjp37lzn/Tk5OYHH44m0lZWVYfjw4dWumqFoDAYDGzduFPmAVlpainnz5imwKqqxoMGuhbtx44ZYx9sZM2bAzc1NQRUpTm5uLj7//HOxEY0A8PXXX+PMmTPQ1NRUQGXSPX/+HF5eXvj+++8rR8p9bMyYMYiIiMD48eNbZFBvLoRCIW7cuIGJEyfC1NQUX375Ja5fv46i0lLkmJmJPZ7BYIKjxoGBgQE4DbxOcaq+HlTV1SVOa1If+vr6Iqt8PHnyBKGhofXa54gRIzB79myRtrdv32L8+PFVrnTRGLRp0wYzZswQaTt27Bhu3rypoIqoxoL2sWvBBAIBPD098ezZs8o2PT09REdHy/xNubGLi4vDwIEDxfrcMBgM/PHHH5g1a1ajCkZlZWVYtWoVVq1aJTJ1wwcWFhbYtm0bBg0apIDqKFlJSEhAcHAw9u7dK7Z0HQB069YNfdq1g9elS2AJhVBWVgZHjQNVNTUwFfD7KmAyccnHG559+6J79+4Nfvy6qKioQM+ePRESEiLSvnz58ka/NnZWVhbs7e1Ful64u7sjLCys0Y7Up+SPXrFrwXbt2iUS6gDgl19+aXGhLjQ0FF26dBELdWpqajh58iS+++67RhXq7t27Bw8PDyxfvlxiqJsxYwZevnxJQ10TVVpaiqNHj6Jfv36wsbHB4sWLJYY6AHj69ClKVVRQZGcHI0MjGOgbgMPhKCTUAUCigQH4HE61Ewo3JkpKSjh27JjYreMlS5bg8uXLCqqqZvT19fHLL7+ItD19+lTqnJVUy0Cv2LVQOTk5sLe3R1ZWVmVbmzZtEB4eDja75cxbfeLECYwfP16kjyEAmJiY4Ny5cyLrdipaYWEhfvrpJ2zZsgWS/mwdHBywe/fuGs//RTUujx8/Bo/Hw8GDByUOfvkYm83G4MGDweVyUVxUhOzQUPR8FAamAt/OhQwGrndoD4OuXTH8vxUympKbN2/is88+E1k/Vk9PD+Hh4bC2tlZgZVXj8/nw8PDAixcvKtv09fURHR0NXV1dBVZGKQq9YtdCLVmyRCTUAcDGjRtbTKgjhGDt2rUYMWKEWKhzdXXF/fv3G1Wou3z5Mtzc3LB582axUMdisfDTTz/h6dOnNNQ1MVlZWdi8eTM8PDzg6emJLVu2VBnqXF1dsW7dOiQnJ+PkyZMYNGgQfLt1Q6GBAaL/W31BUaLMzVFoYABvHx+F1lFX3bt3x6+//irSlp2djREjRkhcJaSxYLPZ2Lhxo0hbVlYWnfqkBaNX7Fqgly9fwt3dXeSTqb+/P/766y8FVtVw+Hw+Zs6ciR07doht69OnD44fPw5tbW0FVCYuKysLs2fPxv79+yVu9/T0xJ49exrl9CuUZAKBAFevXgWPx8Pp06dRXl5e5eO1tLQwduxYcLlcdOjQQWK3gJs3b+LhtX/RMzQUWhIG0chbHoeDG106o9Nnn6Fbt24NfnxZIYTA398fp06dEmmfMWMG/vzzTwVVVTP+/v44efJk5fcsFgvPnj2Di4uLAquiFIEGuxaGEIK+ffuKTIuhqqqKiIgI2NjYKK6wBpKfn49Ro0bh77//Fts2depUbN26FUpKSgqoTBQhBMeOHcM333yDjIwMse2qqqpYvnw5Zs+e3WKusjZ1sbGxCAoKQnBwMJKSkqp9fK9evcDlcjF06NBq10vl8/kI5vEgeBUB38ePwW7AEZ18JhO3PD2g5OyMiVxuk/99zMvLQ8eOHREdHS3Svm/fPkyYMEFBVVUvLi4Ozs7OIlcX+/Tpg8uXLzeqPsKU/NFbsS3MmTNnxOY6mzdvXosIdYmJifDx8ZEY6n7//Xfs2LGjUYS6pKQkDBkyBKNHj5YY6nr06IHnz59j3rx5Tf4k2twVFRVh37596NGjB+zs7LBy5coqQ52lpWXlYIlr165h3Lhx1YY64P3tuIF+fig2M0OoqwuEDXQiFzIYCHV1QYmpGQb4+TWL30dtbW2cOHECap9ME/Pll1+KDTZrTFq1aiU2j90///yDs2fPKqgiSlHoFbsWpLS0FK6uriIj7CwsLPD69WuoN/Akpg0tPDwcgwYNQmpqqki7iooK9u/fjxGNoLO3UCjEzp07MX/+/MrF0T+mpaWFtWvXYsqUKfQTeCNGCEFoaCh4PB6OHDki8d/yYyoqKhg6dCi4XC569epVr2kq4uPjceLwYeglJKDzy1dyvXLHZzIR6uqCbCsr+I8Z06gHGNTFgQMHxK7Q2dnZ4dGjR42mq8anioqK4OjoiOTk5Mo2W1tbvHz5EqqqqgqsjGpI9IpdC7J+/XqxaRNWr17d7EPduXPn4OvrKxbqDA0Ncf369UYR6qKiotCzZ0/MmDFDYhD44osvEBERgalTp9JQ10ilp6dj7dq1cHV1RdeuXbFr164qQ1379u2xZcsWpKSk4PDhw+jTp0+95x6ztraG/5gxyLVphdseHsivwdW+usjjcHDL0wO5Nq2aZagDgPHjx4tNABwTE4OAgACJo9IbA3V1dbHlId+8eYMNGzYopiBKIegVuxYiOTkZjo6OKCoqqmzz8fHBrVu3mnVQ2LRpE2bPni02i7yTkxMuXLgAW1tbBVX2XkVFBdatW4elS5dKHHlnZGSErVu3wt/fv1n/OzVVFRUVuHjxIoKCgnD+/HmRAUmS6OvrY/z48QgMDIS7u7vc6kpLS8OFs2eRk5QMp+ho2Ccny2QqFCGDgShzc0Q62EPP3BwD/PxgYmIig4obp7KyMnTr1g0PHjwQaf/9998xf/58BVVVNUIIfH19RSZcVldXR1RUFMwkrFJCNT802LUQEyZMwIEDByq/ZzAYePToETw9PRVYlfwIBALMnj0bmzdvFtvWo0cPnDx5UuFzPIWHh2Py5Ml48uSJxO0BAQFYt25di5swuimIiIgAj8fDvn378O7duyofy2Qy0a9fP3C5XAwePBgqKioNUiOfz0dISAgehoRAIzMTreMTYJmZCVYdbs8KmEwkGhgg1toKhQYG6OTjAy8vr2bRp646CQkJ8PT0FJkeislk4tq1a+jRo4fiCqtCWFgYOnbsKHJlccKECdi3b58Cq6IaCg12LcC9e/fg5eUl0jZlyhTs2rVLQRXJV2FhIcaOHYtz586JbZs0aRJ27twJZWVlBVT2XklJCZYtW4a1a9dKvMJjY2ODnTt3ok+fPgqojpImPz8fR48eBY/Hw/3796t9fOvWrcHlcjFx4kRYWFg0QIWSpaSk4G5ICOKiosAuLoZ1YiJMs7KhXVQEpSquMFawWMhTV0eqvh7iLS3B53DQysEB3j4+MDU1bcBXoHhXrlxB//79RYKSkZERwsPDYa7g+QOlmTJlCvbs2SPSdu/ePXTp0kVBFVENhQa7Zk4oFKJLly54+PBhZZu2tjaioqJgZGSkwMrkIyUlBYMGDcLjx4/Fti1fvhw///yzQm9p3rx5E1OnThWbSgF4fxV11qxZWLFiRbPv99hUEEJw69Yt8Hg8HD9+HCUlJVU+nsPhYMSIEeByufD19W1Ut89zcnLw7NkzPAsLQ2lREQifD42SEmhl50CZzweTCCFkMFHOZiNfTxeFampgsNlQVVdH2/bt0bZtW4Vf5VakFStWiK0d6+XlhRs3bjSK0fSfevfuHezt7ZGfn1/Z1rFjR9y/fx9MJu1e35zRYNfMBQUFgcvlirT98ccfmD17toIqkp9nz55h4MCBYtNJKCsrg8fjYdy4cQqq7P3cWAsWLJA4KTLwfkWBPXv2oHPnzg1cGSVJUlISgoODERQUhNjY2Gof7+XlBS6Xi5EjR0JTU7MBKqw7gUCA7OxspKenIz09HRlpaSgvLYWAzweLzYayqioMTUxgbGwMY2Nj6Onp0QXl8f5D8uDBg3Hx4kWR9u+++w7r169XUFVV++OPPzBnzhyRtqCgIAQEBCimIKphEKrZysvLI8bGxgRA5ZejoyMpKytTdGkyd+nSJaKpqSnyWgEQPT09cuvWLYXWdubMGWJmZiZWGwCipKREli1b1iz/TZqa0tJScuzYMdK/f3/CYDAk/nt9/GVsbEzmz59PIiIiFF061UCysrKIjY2N2O/C0aNHFV2aRGVlZcTR0VHs9zYvL0/RpVFyRINdMzZv3jyxN6BLly4puiyZ2759O2GxWGKv1c7OjkRGRiqsrrS0NDJy5EipwaBLly7kxYsXCquPeu/x48fkm2++IXp6etWGOTabTb744gty9uxZUl5erujSKQUICwsjKioqIr8X6urq5NWrV4ouTaKLFy+K/R7Pnz9f0WVRckSDXTMVGRlJlJSURP6YBw0apOiyZEogEJC5c+dKPAF7e3uTjIwMhdQlFApJcHCw1KCgrq5ONm7cSPh8vkLqo95fedm8eTPx8PCoNswBIC4uLmTt2rUkLS1N0aVTjcDu3bvFfkecnJxIfn6+okuTaODAgWJ3CqKiohRdFiUnNNg1U839D7moqIgMGzZM4kl4zJgxpKSkRCF1xcXFkb59+0oNCH379iVxcXEKqa2l4/P55PLly2TUqFFEWVm52jCnqalJpk2bRu7fv0+EQqGiy6caGS6XK/Y7M2rUqEb5u9ISPuhT/0eDXTMk6dL7vHnzFF2WzKSlpZFOnTpJPBn//PPPCnlj5fP5ZMOGDURdXV1iXXp6eiQ4OLhRvuk3d7GxsWTRokXE0tKyRlfnevbsSfbt20eKiooUXTrViBUXF0u84rthwwZFlyaRpLsbzbFrDkWDXbPT3DvLvnz5klhbW0vs+8Tj8RRWU5cuXaQGhVGjRtFbeA2sqKiI7Nu3j/Ts2bNGYc7S0pIsWrSIxMbGKrp0qgmJjY0lOjo6Yu9Fd+7cUXRpYqQNpqN9RZsfGuyamXXr1omdtIKCghRdlkxcvXqVaGtri70+bW1tcu3atQavp6ysjCxdulTsFseHLzMzM3LmzJkGr6ulEgqF5P79+2TatGlES0ur2jCnrKxMRo0aRS5fvkz7O1J1du7cOYl/+43xwxyPxxOr9Y8//lB0WZSM0WDXjKSlpYmd0Dp27EgEAoGiS6u3PXv2EDabLfamZGNjo5DRaPfv3ydubm5SQ8OXX35JcnNzG7yuligtLY2sXbuWuLi41OjqnIeHB9m8eTPJyspSdOlUM7Fw4UKx37MePXqQiooKRZcmQiAQkA4dOojUqaWlRdLT0xVdGiVDNNg1I1OmTBF7c7l3756iy6oXgUBAfvrpJ4kn6M6dOzf4p+LCwkLy3XffSZ3nzM7Ojty4caNBa2qJKioqyNmzZ8kXX3whMfB/+qWrq0u++eYbEh4erujSqWaIz+eT3r17i/3eLViwQNGlibl7965YnVOnTlV0WZQM0WDXTDx69EgsbEyYMEHRZdVLSUkJGT16tMQTtb+/PykuLm7Qeq5cuSJxclIAhMVikQULFjR4TS1NREQEmT9/PjExMak2zDEYDNK/f39y9OhRhY2SplqOd+/eEQsLC7Hfw1OnTim6NDHjx48X+1sJCwtTdFmUjNBg1wwIhULi7e0t8oeqrq5OkpOTFV1anWVkZBAvLy+JJ+z58+c36O3lrKwsEhAQIDVAtGvXjr4pylFeXh7ZtWsX6dq1a41utdra2pIVK1aQhIQERZdOtTD3798X63OrpaVFoqOjFV2aiOTkZLER/N7e3nTUfjNBg10zcOjQIbGT26pVqxRdVp1FRkaS1q1bS7wqtmPHjgarQygUkmPHjomNJPvwpaKiQn799Vc6qkwOhEIhuXnzJpk0aRLhcDjVhjk1NTUyceJEcuPGjWbRp5RqurZu3Sr2+9m2bdtGN33OqlWrxOo8dOiQosuiZIAGuyausLCQmJubi12xaKq3nm7evEl0dXXF3nA0NTXJ33//3WB1JCcnkyFDhkgNEt26dVPocmXNVVJSElm5ciWxs7Or0dW5Ll26kJ07dzab6Xyopk8oFJJx48aJ/a5OmDChUV0RKykpIba2tiI1WlhYkMLCQkWXRtUTDXZN3M8//yz2BnL69GlFl1Un+/fvlzh1iKWlJXn27FmD1CAUCsnOnTslTqvyIWBu376dXhWSodLSUnL8+HHy+eefEyaTWW2YMzIyIvPmzWu0a3NSVGFhIXF1dRX73d2+fbuiSxNx6tQpsRoXLVqk6LKoeqLBrgl78+aN2GLUffr0aVSfCmtCKBSSpUuXSjyJt2/fnqSkpDRIHdHR0aRHjx5SA8XgwYNJYmJig9TSEjx58oTMmjWL6OvrVxvmWCwWGTJkCDlz5gy99U01CZGRkURTU1Pk91hZWZk8ePBA0aVVEgqFYqN5VVVV6bKHTRwNdk2Yv7+/2Mnv5cuXii6rVsrKysjEiRMlnsz9/Pwa5LZARUUFWb16NVFVVZVYh6GhITly5EiTC8yNUXZ2NtmyZQtp3759jW61Ojk5kTVr1pDU1FRFl05RtXbixAmx32krKyuSkZGh6NIqvXjxgrBYLJEahw8fruiyqHqgwa6J+vfff8XeML799ltFl1UrWVlZUq+QzZo1q0FWA3j8+HGVIWPChAkkMzNT7nU0ZwKBgFy5coWMHj1a7AqztNvdU6dOJffu3aNhmmryJK3R2rdv30a12sk333wjVuO///6r6LKoOqLBrgmqqKgQW/VAX1+fZGdnK7q0GouJiRFb0xYAYTKZZNOmTXI/fklJCfnxxx/FPql+/KmaLpBdP2/evCGLFy8mVlZWNbo61717dxIcHEw7b1PNSkVFBenWrZvY7/vixYsVXVql7OxssS4Rbdq0aXQrZ1A1Q4NdE7RlyxaxN4lt27YpuqwaCwkJIQYGBmKvQV1dnZw7d07ux7916xZxcHCQGC4YDAb59ttvSUFBgdzraI6Ki4vJgQMHSK9evWoU5iwsLMjChQsb3TxfFCVLqampEifVvnDhgqJLq7Rt2zax+rZu3arosqg6oMGuicnMzBSbDsTd3b1RXdavytGjRyXejjMzM5P7ck95eXlkxowZUkOGs7MzuXv3rlxraI6EQiF58OABmT59utTRxB9/KSsrk5EjR5K///67yfzeUlR93bp1S+wOga6ubqMZqMDn80nbtm1F6tPT06NrKjdBNNg1MV9//bXYifLmzZuKLqtaQqGQ/PrrrxJP9G3btpX7aNPz589LXO4HAGGz2WTx4sWktLRUrjU0N+np6WTdunUSp3WQ9OXu7k42bdpE+yxSLda6devE/i7at2/faOYdvXHjhlh9M2fOVHRZVC0xCCEEVJPw/PlztGvXDkKhsLJt5MiROHr0qAKrql5FRQVmzJiBPXv2iG37/PPPcfToUWhqasrl2BkZGZg1axYOHz4scXunTp2we/dutGnTRi7Hb274fD7+/vtv8Hg8nDt3Dnw+v8rH6+rqYty4ceByufDw8GigKimqcSKEYOTIkfjrr79E2qdOnYqdO3dKfI5AIEB2djbS09ORnp6OjLQ0lJWUQCgQgMliQUVNDYYmJjA2NoaxsTH09PTAYrHqXOPIkSNx/Pjxyu9ZLBaePHkCNze3Ou+Talg02DURhBB89tlnuH79emWbqqoqXr9+DWtrawVWVrW8vDwMHz4cV69eFds2Y8YMbNq0CWw2W+bHJYTg0KFDmDVrFrKyssS2czgcrFixAt9++2293gRbisjISAQFBSE4OBhpaWlVPpbBYKBPnz7gcrkYMmQIVFVVG6hKimr88vPz0alTJ0RGRoq083g8BAYGVn6fk5ODp0+f4nl4OEqLikD4fGiUlEA7OxtKfD6YhEDIYKCCzUaenh4K1dTAYLOhqq6ONp6ecHd3h66ubq3ri4+Ph5OTE0pLSyvbevXqhatXr4LBYNT9hVMNhga7JuLkyZPw9/cXaVuyZAmWLl2qmIJqID4+HgMGDMCrV69E2hkMBtauXYvZs2fL5Y0iISEB06dPx6VLlyRu7927N3bu3IlWrVrJ/NjNSUFBAY4fPw4ej4eQkJBqH9+qVSsEBgZi0qRJsLKyaoAKKappevnyJTp16oTi4uLKNlVVVdy7dw9GRka4e+cO4qKjoVRcDKuERJhmZ0O7qAhKAoHUfVawWMhTV0eqnh4SrCxRweGglb09vH19YWpqWqv6lixZguXLl4u0nTx5EkOHDq3dC6UUgga7JqCkpAQuLi54+/ZtZZulpSVev34NDoejuMKq8PDhQwwePBjp6eki7Wpqajh48KBc3iCEQiH+/PNP/PjjjygsLBTbrqOjgz/++AMBAQH0k6cUhBDcuXMHQUFBOHbsGIqKiqp8vJqaGoYPHw4ul4tu3bqByWQ2UKUU1bQdPnwYY8eOrfyexWJh0KBB8O7QAZrZ2bCLT4BFZiZYH3W9qSkBk4kkAwPEWFuh0MAAHb294e3tXeO7I8XFxXByckJiYmJlW6tWrfDq1St6Bb4JoMGuCVixYgUWLVok0nbkyBGMGjVKQRVV7dSpUxg3bhxKSkpE2o2MjHDu3Dl06tRJ5seMiIjAlClTcPfuXYnbhw8fjs2bN8PExETmx24OkpOTsW/fPgQFBSE6Orrax3fu3BlcLhejRo2CtrZ2A1RIUc3Pt99+i82bN8PIyAh+AwfCXFcXzjExcM/JBUsGp2Yhg4Foc3O8treHnoU5Bvj51fg98OjRoxg9erRI24oVK7Bw4cJ610XJFw12jVxSUhIcHR1FLtl369YNN27caHRXnQghWL9+PebOnYtPf61cXFxw4cIF2NjYyPSY5eXlWL16NX755ReUl5eLbTc1NcXWrVvpLQQJysvLce7cOfB4PPz9998ig3IkMTQ0xMSJExEYGAhXV9cGqpKimq/y8nIMGTIEbg4OMC0uhnNYGDj5+dDU1IKmhobMjpPP4SDM2RnFZmYYOmpkjfplE0LQvXt33L59u7KNw+EgKioK5ubmMquNkj0a7Bq5cePG4dChQ5XfM5lMhIWFoV27doorSgI+n49Zs2bhzz//FNvWu3dvHD9+HDo6OjI95sOHDzF58mQ8f/5c4vYpU6ZgzZo1Mj9uU/f8+XPweDwcOHAAmZmZVT6WxWJhwIAB4HK5GDhwIJSUlBqoSopq/uLj43HswEGoR0XC6f59sCr70DGgr68HFWUVmR2Lz2Qi1NUF2VZW8B8zpkbh7vHjx2jfvr3IB/Vx48bhwIEDMquLkj0a7BqxkJAQ+Pj4iLR9+eWX2L59u4IqkqygoACjRo2SOFhh8uTJ2LZtm0wDQXFxMRYvXoz169dLvMrUunVr7Nq1Cz179pTZMZu63NxcHD58GDweD48ePar28Y6Ojpg8eTImTJhAb19TlBykpaXhyL590Il7C8/H4cjJzML7qePeYzJZMDQ0AIspu1H7QgYD99xckWvTCqMn1uxv+8svvxSbiiUkJAReXl4yq4uSLRrsGimhUIiOHTsiPDy8sk1bWxvR0dEwNDRUYGWikpKSMGjQIDx9+lRs26pVq/DDDz/I9JbxtWvXMG3aNLx580ZsG5PJxPfff49ly5Y12kElDUkoFOL69evg8Xg4efKkyPQFkmhoaGD06NHgcrno0qVLo7vVT1HNBZ/PRzCPB8GrCPg+fgy2UIiCwkIUFOSLPE5ZSRn6BgaQ5V8in8nELU8PKDk7YyKXW+2AioyMDNjb2yMvL6+yrX379njw4AEdLNVI0X+VRiooKEgk1AHAsmXLGlWoe/z4MTp37iwW6lRUVHDkyBH8+OOPMgsHOTk5mDx5Mnr37i0x1LVt2xahoaFYs2ZNiw91b9++xdKlS2Fra4vevXvj0KFDVYa6bt26Ye/evUhLS8OuXbvQtWtXGuooSo5CQkKQk5SM9hERYP9310FDQwOqKqIjTssryqsdmV5bbKEQ7V9FIDs5Wepgs48ZGhqKTasVFhaGvXv3yrQuSnboFbtGKC8vDw4ODnj37l1lm4uLC548edJo+jidP38eo0ePFnvTMTAwwJkzZ2R6mf7kyZP4+uuvJU6Mq6ysjCVLlmDevHmN5mejCCUlJTh16hR4PB6uXbtW7ePNzMwQEBCAgIAA2NvbN0CFFEUBQEpKCg7t3Qun5y/gmJQksk1ICDIyMiAQ/H9FF1VVVejp6sm8jtcWFohs44ZxgYHVznNXUVEBd3d3REREVLYZGRkhKiqKjopvhOgVu0Zo+fLlIqEOADZs2NBogsuWLVswZMgQsVDn6OiI+/fvyyzUpaamwt/fH/7+/hJDnY+PD54+fYqffvqp0fxsGhIhBI8ePcJXX30FU1NTjBs3rspQp6SkhOHDh+PixYtISEjAypUraaijqAZ2984daGRmwj45WWwbk8GAnp4uGIz/n5rlNW+cQ3IyNDIzEXLnTrWPVVJSwoYNG0Ta3r17h19++UUutVH1Q6/YNTKRkZFwc3MTWYPTz88PZ86cUWBV7wkEAsydO1fsDxwAunfvjpMnT0JPr/6fLAkhCAoKwpw5c5Cbmyu2XUNDA7///jumT5/eIvt4ZGRk4ODBg+DxeFJHBH+sbdu2mDx5MsaOHQsDA4MGqJCiKElycnKw+88/4RH+GNaffHj/mEAgQElpKdhsNlRVZDcy9lNvjYzwxNMDU776qkbLjw0ZMgRnz56t/J7NZuPFixdwdHSUW41U7dFg18gMGDBAZHSpsrIyXr16hdatWyuwKqCoqAjjxo2TGDAnTJiAXbt2QUUGb0Bv3rzBtGnTpF55GjBgALZt29bilqzi8/m4cuUKeDwezp49i4qKiiofr6Ojg3HjxoHL5cLDw4P2maOoRuDGjRt48s8/6H8npE4rSsiagMnEJR9vePbti+7du1f7+JiYGLi6uorMGTpgwABcuHBBnmVStdTyLnc0YhcuXBCbMuT7779XeKhLTU1F9+7dJYa6pUuXIjg4uN6hTiAQYN26dXBzc5MY6gwMDHDw4EGcP3++RYW6qKgo/Pjjj7CyssLAgQNx4sQJqaGOwWCgT58+OHz4MFJTU7FlyxZ4enrSUEdRjYBAIMDz8HBYJSQ2ilAHACyhENaJiXgWFgZBFevQfmBnZ4fZs2eLtF28eBEXL16UV4lUHdBg10iUl5eL/cGYmprip59+UlBF7z1//hydO3dGWFiYSLuSkhL279+PJUuW1Ds4PHv2DF27dsXcuXPFliED3k+I+erVK4wdO7ZFhJTCwkIEBQXB19cXjo6O+O2335Camir18TY2Nli2bBni4uJw5coVjB49mq7nSFESsNlstGvXrvJL0vtNdVavXl2nY2dnZ6O0qAim2dki7VsS4jEgPAyDwsMw7MljJFYzLdGupESR72v7/E7374l8b5r1vq7sT+r61IYNG1BeXo6FCxeKzX83e/ZsiSv/SFJYWIjPPvsMGhoamDt3bo2eQ9VOzVYEpuRu06ZNYmt0/vbbb9DU1FRQRcCVK1cwfPhwFBQUiLTr6uri1KlTNbp0X5WysjKsWLECv/32m0ifwg8sLS2xfft2DBgwoF7HaQoIIbh79y54PB6OHj1a7RQHqqqq8Pf3B5fLRY8ePVpkX0OKqi0dHR08efKkXvtYvXo15s+fX6vnCAQCpKeng/D50CksrGwPz89HaF4ezrTzgBKTibSyMqixqv5b3pWUhKkWlnV+/qe0i4pA+Hykp6dXOZ3Whg0bMGXKFGhqauK3335DQEBA5baoqChs3rwZc+bMqWwTCoUS35eUlJSwZMkSvHz5ErGxsbWqlaoZGuwagbS0NCxfvlykrXPnzhg/fryCKgJ27dqFGTNmiF2et7W1xcWLF+vdWfbu3buYMmWKyPD5j3399df49ddfFRpsG0JKSgr2798PHo+HqKioah/fqVMnBAYGYvTo0XSpNIqSgcuXL2Pp0qUoLS2Fq6sreDwelJWVMW3aNISFhaG0tBSBgYGYO3cuFi5ciNzcXLRr1w5dunTBDz/8gOHDh1eu5jJ37ly4ubkhICAANjY2GD16NC5fvozVq1fj2rVrOBwcDF5+Abrq6OAnW1tklJdDl60Epf8CkMlHXVpu5+Rgc0I8yoRC2HM4WGXvgC0JCSjg8+H3OBztNDXhraNbq+crfxK0diYl4u/MTGS/fIHYtDTs2LEDALBy5UocOXIEDAYDgYGBUFZWRkpKCry8vGBjY4PTp09j+fLlInOKLl++HN26dQOXy4WrqyuePHmCx48fQ01NTeSYKioq6Natm8T5SCkZIZTCBQYGErxfS6byKzQ0VCG1CAQCMn/+fLF6ABAvLy/y7t27eu0/Pz+fzJw5kzAYDInHcHJyInfu3JHRq2mcysrKyIkTJ8jAgQMJk8mU+HP4+MvQ0JB8//335Pnz54ounaKaNBaLRdzd3Ym7uzuZPHkyycjIIJ999hkpLi4mhBCyaNEismXLFkIIIVlZWYQQQioqKkiXLl1IQkICIYQQfX39yv3FxcWR9u3bV34/Z84cEhQURAghxNraunJfr169Ip06diTBgYEkyseXDDE0IjtcXEl4l67EgcMhrdXUyERTM3LCvR2J8vEl9zt3IV21dcizrl4kyseXfG1pSRbbtiZRPr5Eh80mUT6+JMrHt17P57m6kfGmpiTS24cEB3KJi4sLef78Oblw4QLp1asXKS0tFfk5WFtbk4KCAkIIIUlJScTc3FzsvWrEiBGExWKRp0+fVvtvERQURObMmVPnf0tKOnrFTsEePnyIoKAgkbZJkyahU6dODV5LSUkJJk6ciL/++kts26hRo7B379569d26dOkSpk+fjoSEBLFtbDYbCxYswM8//9xs+4e9ePECPB4P+/fvR2ZmZpWPZTKZGDBgALhcLgYOHAhlZeUGqpKimq9Pb8WeP3++so8v8L57yMCBAwEAhw8fxu7duyEQCJCUlITXr1/D0tKyVscbMWIEgPdLIUZHR2NxTAzUystRKhDCTUMD3XV1cdytDe7l5OBBQQECXzzHRidnlBMhIouLMPLZ+1V9yoVC9JAwlZQGm43THp4Izc3F3bxcBL54gY1OTjV6/p3cHNzIzsGj/McoefUSxWw2oqKicOfOHQQGBlYOiJM0hdXDhw8xYMAAlJeXIzg4uLL9+PHjaNWqFdq2bVurnxMlWzTYKRAhBLNmzRJp09DQwK+//trgtbx79w5+fn4IDQ0V2/bTTz/hl19+qXM/rszMTMyePRsHDhyQuL1Dhw7YvXs33N3d67T/xiw3NxdHjhwBj8fDw4cPq328g4MDuFwuJkyYADMzswaokKJaLqFQiIEDB4p9uH7z5g22bt2Ke/fuQVtbG8OHD0dZWZnY89lsNoQfjXD99DEcDgcFBQVISEhAB09PBBgZwe7FSwgEfPD5AqSlvR8U5QTASZ0DjlAfV7Oz4KOjix66evjNwaHa18BmMOCtqwtvXV3osZVq/HwhAWZaWWGYsTGe2rZCgZcXhg0bhjs1mLD4g19//RUnTpxA4Uf9Bt+9ewdCSIsY6NZY0R7XCnTw4EHcuyc6QmnRokXVLu8iaxEREejSpYtYqGOz2dizZw9WrlxZp1BHCMHhw4fh7OwsMdSpqalh7dq1uHfvXrMKdUKhEP/++y/Gjx8PU1NTzJgxo8pQp66ujsDAQNy5cwevX7/GggULaKijqAbQtWtXXL9+HfHx8QCA/Px8xMXFoaCgABoaGtDS0kJSUhKuXr1a+RwWi1XZ99jIyAgpKSlITU3F3bt3cfLkSZw/fx5cLhdpaWmwtbWFlpYW1qxZgwdhYcgsLERJSTHSi4uRUV6GhPJyJP83fREhBNElJTBRUoaHliZC83KR/N8I10I+v3K0K4vBgOC/6WffFBcj4b+RvYQQRBUXwUxFpcrnf+Cjq4Pj6WkoEQggZDCRnZuLvLw89O7dG0FBQZUh9cNoWU1NzcqBdJ06dcK1a9egqqqKH374QWS/RUVFOHTokCz+eai6UuiN4BasoKCAmJmZifRPsLOzq+zX0FD+/fdfoqOjI9ZXQltbm1y9erXO+01ISCCDBg2S2m+sZ8+eJCYmRoavRPHevn1Lli1bRmxsbKrtNweA+Pr6kqCgoMp+KxRFydfH/eM+uHLlCmnfvj1p06YNcXd3J9evXyeEEDJx4kTi4OBA+vbtSwYOHEiOHTtGnjx5Qvz8/IihoSFxdHQkvr6+RFNTs0Z/7107dyYWOjrEVlmZOKqokCBLS7LDwoK4qKgQGyUlYqOkRPppapKnXbqSKB9fEuTqRtw0NIgjh0Oc1NXJfrc2JMrHl0wxtyCt1dTIaBMTcrJdO9JOU5PYcTjEjsMhQwyNKvvVSXv+x330fmjVijhyOMRcR5c4OjiQtLQ0Qgghy5cvJy4uLsTd3Z1s2LCBEELIxo0biaOjIxk8eDAhhJDg4GDi5uZGXFxciL6+vshrNTMzq/J9zcHBgejq6hINDQ1ibm5OEhMTZfwv3bLRlScUZOHChVi1apVI29mzZzF48OAGqyE4OBhTpkwRm2rE2toaFy9ehIuLS633KRQKsWPHDixYsEBsmhQA0NbWxrp168DlcpvFpfrS0lKcOnUKQUFBuHr1Kqr7czI1NUVAQAACAgLgUIPbLBRFNZyioiLExsYiOjoa0dHRiImJqfz/quaSrIlevXqhr709unx09e89BlgsFpSUlKCpoaGQda//6doFjv364bPPPqvT88+dOwc/Pz+RtoULF2LFihWyKI+qJdrHTgHevHmDdevWibT169cPgwYNapDjE0KwZMkSiQs4d+rUCWfPnoWxsXGt9xsZGYmpU6fi9u3bErcPHToUW7ZsafK3GQkhCA8PB4/Hw6FDhySuZ/sxJSUl+Pn5gcvlom/fvmCz6Z8dRSlKcXGx1PCWkpIil2MyGAwIBAKUaWtDWVMLqgwG2GwW2Cw2WGw2FPkRt4LFQqGaWp3e8z8YNGgQ+vbtiytXrlS2rV27FlwuF7a2trIok6oFeoZRgDlz5oh0smWz2Vi/fn2DXMEqKysDl8uV2Adi2LBh2L9/PzgcTq32WVFRgTVr1mD58uUSOxgbGxtj69at8Pf3r3PdjUFmZiYOHjwIHo+HZ8+eVfv4Nm3agMvlYty4cVVO/ElRlGyVlJRIDW/JyclyOSaDwYClpSXs7Oxgb29f+WVnZwdbW1sUFBRg7/btgJkZNPLz5VJDXeSpq4PBZtcr2DEYDGzYsAFt27atvANUVlYGT09P2NjYVD5ORUVF4gA9SrZosGtgV69exenTp0XaZs6cCWdnZ7kfOzMzE0OHDpU46mnu3Ln4/fffaz1IIiwsDJMnT8bTp08lbudyuVi7di10dXXrVLOiCQQCXLlyBTweD2fOnJG6TusH2traGDt2LLhcLtq3b98sbjdTVGNUWloqNbwlJSXJ7bhVhbdPJ+P9mJKSElTV1ZGqpweDRhTsUvX1oKquLnFak9pwdnbGzJkzsWHDhsq2vLw8rFu3rs63eKm6oX3sGhCfz0e7du3w8uXLyjZDQ0NERUXJfRWB6OhoDBgwADExMSLtLBYLW7ZswfTp02u1v+LiYixduhR//PGHxMWjW7VqhZ07d6J37971qltRYmJiEBQUhODg4Bp9wu/duze4XC6++OKLKt/cKYqqudLSUrx580ZqeJPX6cvCwkJieGvdunW9/r5v3LiBJ//8g/53QsD6aJoURREwmbjk4w3Pvn3rvUQk8H56J3t7e5F5Oj+sQkG7oDQc+pNuQNu2bRMJdcD7pVvkHepu376NL774QmyRZw0NDRw/fhz9+/ev1f5u3LiBqVOnioVE4P3Eut999x2WL18OdXX1etXd0IqKivDXX3+Bx+Ph1q1b1T7e2toagYGBmDRpksjtBoqiaq6srExqeEtMTJRbeDM3N5ca3mrbHaWm3N3d8TAkBEkGBrB+904ux6iNRAMD8DkcmU0orKOjg5UrV+LLL7+sbHv58iW2b9+OmTNnyuQYVPXoFbsGkpmZCXt7e5GO9h4eHnj48CFYLJbcjnvo0CEEBgaivLxcpN3CwgLnz5+v1fxxubm5mD9/Pnbt2iVxu5ubG/bs2aOQVTPqihCCe/fugcfj4ejRoyITbUqioqICf39/cLlc9OzZs86TNlNUS1JeXi41vCUkJMgtvJmZmYmEtw//37p1a4V98Pzr2DFk3r+Pno/CwFTg6VfIYOB6h/Yw6NoVw/9bIUMWBAIBOnbsiMePH1e26erqIjo6Gvr6+jI7DiUdvWLXQBYvXiw2enLjxo1yC3WEEKxcuRKLFi0S2+bh4YHz58/XanTq6dOn8dVXX0kc8q+srIyff/4ZCxYsaDJLX6WmpmL//v3g8XiIjIys9vEdOnQAl8vF6NGjm2x/QYqSp/LycsTFxUkNb0I53Xo0NTWVGN7s7Owa5V0Db19fHIyJQbS5ORzl2BewOlHm5ig0MMAQHx+Z7pfFYmHjxo3o1q1bZVtOTg4WL16MrVu3yvRYlGT0il0DePr0KTw9PUXe2EaPHo3Dhw/L5Xjl5eX48ssvsXfvXrFtgwYNwuHDh6GhoVGjfaWnp+Obb77B8ePHJW738vLCrl276jTnXUOrqKjAhQsXwOPxcPHiRYl9Az+mr6+PCRMmIDAwkK59SFF4/zf0Ibx9HNyio6MRHx8vt/BmYmIiNbzV9L2sMbl58yYeXvsXPUNDoVVc3ODHz+NwcKNLZ3T67DORACZLo0ePxtGjRyu/ZzKZePz4MX0vbQA02MkZIQQ9e/bEzZs3K9vU1NQQGRlZ6wWlayInJwf+/v64fv262LZvvvkG69evr9FVQkIIgoOD8f333yMnJ0dsu7q6On777Td89dVXjf525MuXLxEUFIT9+/fjXTX9WphMJj7//HMEBgZi8ODBTeYKJEXJSkVFBd6+fSs1vFX3gaiujI2NpYY3TU1NuRxTUfh8PoJ5PAheRcD38WOwG3AgBZ/JxC1PDyg5O2Milyu3QQ2JiYlwdHREyX9LngFAjx498O+//9LZAuSM3oqVs7/++ksk1AHADz/8IJdQ9+bNGwwcOBCvX78WaWcymVi/fj2+/fbbGu3n7du3mDZtGv755x+J2/v374/t27fD2tq63jXLS15eHo4cOYKgoKAazZtkb28PLpeLiRMnNvkJlCmqOnw+X2p4e/v2rdzCm6GhodhghQ//1dLSkssxGyM2m42Bfn44kpuH0PIydH3xskH62wkZDIS6uqDE1AxD/PzkOlLV0tISP/zwA5YsWVLZduPGDZw4cQLDhw+X23EpesVOrkpKSuDk5ISEhITKNmtra0RERMh8Soz79+/Dz88PGRkZIu0cDgdHjhyp0VJlAoEAmzdvxsKFC1Es4faAnp4eNm7ciHHjxjXKT1xCoRA3b94Ej8fDiRMnRD4pSqKuro6RI0eCy+XC29u7Ub4miqorPp+P+Ph4qeHt06UEZcXAwEBqeNPW1pbLMZuq+Ph4nDh8GHoJCej88pVcr9zxmUyEurog28oK/mPGNMgH84Y8B1L/R4OdHC1fvlzk0woAHD9+XOafVv766y9MmDABpaWlIu0mJiY4f/482rdvX+0+Xr58icmTJ0u9ujV69Ghs3LgRRkZGMqlZlhISEhAcHIygoCDExcVV+3hvb29wuVyMGDGi2d3ioVoWgUAgNbzFxcXJLbzp6+tLDW/ynr6puYmPj8epo8fASUlB+4gIufS5y+NwEObijBJTMwwdNbJB77YcP34cI0eOFGlbvny5xIF9lGzQYCcnCQkJcHJykmv/AkII1qxZgwULFohta9OmDc6fPw8rK6sq91FWVoZff/0Vq1atkriqgrm5ObZv395g69jWVGlpKc6cOQMej4d//vmn2ukSTE1NMXHiRAQGBsLR0bGBqqSo+hMIBEhISBALbzExMXjz5k21q6HUlZ6entTwRkeGy1ZaWhounD2LnKRkOEVHwz45WSa3ZoUMBqLMzRHpYA89c3MM8PODiYmJDCquuYbuZ07RYCc3Y8aMwZEjRyq/l/WIoIqKCsycORM7d+4U29avXz8cO3as2j4r9+/fx+TJk/Hq1SuJ22fMmIHffvutUfV9efz4MXg8Hg4ePChxUMfH2Gw2/Pz8wOVy0a9fPzrzOdVoCQQCJCYmSg1vn85DKSu6urpSw1t9l5iiaofP5yMkJAQPQ0KgkZmJ1vEJsMzMrNMKFQImE4kGBoi1tkKhgQE6+fjAy8tLYe+BkmaGGDNmjMQ1y6n6o8FODm7fvi02hHzGjBn4888/ZbL/vLw8jBw5EleuXBHb9uWXX2Lz5s1QUlKS+vzCwkL8/PPP2LRpk8QrXQ4ODti1a5fchsHXVlZWFg4ePAgejyd1TdqPubq6YvLkyRg3blyjvHVMtUxCoVBqeIuNjZVbeNPR0RELbh++aHhrfFJSUnA3JARxUVFgFxfDOjERplnZ0C4qglIVg1oqWCzkqasjVV8P8ZaW4HM4aOXgAG8fH5iamjbgK5BsxowZ2L59u0jb7du34SPjefQoGuxkTiAQoEOHDnjy5Ellmyxn3U5ISMDAgQPx4sULsW1r1qzBnDlzqrzVe+XKFUybNg3x8fFi21gsFubPn4/FixdDVVW13rXWh0AgwD///AMej4czZ85Ue9LT0tLC2LFjweVy0aFDBzoQglIIoVCIpKQksf5uH8JbWVmZXI6rra0tMbzZ2dlBX1+f/j00QTk5OXj27BmehYWhtKgIhM+HRkkJtLJzoMzng0mEEDKYKGezka+ni0I1NTDYbKiqq6Nt+/Zo27Zto7plrqjVl1oiGuxkbOfOnSLr5AHApk2b8M0339R732FhYRg0aBDS0tJE2lVVVXHgwAH4+/tLfW52dja+//57BAcHS9zu6emJ3bt3w8PDo9511kdsbCyCgoIQHByMpBrMyt6rVy9wuVwMHTpUbus7UtTHhEIhkpOTpYa3TwcxyYqWlpbU8GZgYEDDWzMlEAiQnZ2N9PR0pKenIyMtDeWlpRDw+WCx2VBWVYWhiQmMjY1hbGwMPT29RhuUNm/eLDbt1s6dOzF16lQFVdQ80WAnQ7m5ubC3t0dmZmZlm6urK548eVLvvg1nzpzB2LFjxaYhMTIywtmzZ9G5c2eJzyOE4Pjx4/jmm28kTs6rqqqKZcuW4fvvv1dY/4uioiKcOHECPB5PbM4/SaysrBAYGIhJkyahVatWDVAh1dIIhUKkpKRIDW/VTaVTV5qamlLDm6GhIQ1vVJPG5/PRrl07vHz5srLN0NAQUVFRdDS1DNHe5DK0bNkykVAHvF8Ptj6BiRCCTZs2Yfbs2WL94ZydnXHhwgWp4SY5ORlfffUVzp49K3F79+7dsWvXLtjb29e5vroihCA0NBQ8Hg9HjhxBQUFBlY9XUVHBsGHDwOVy0atXr0a/2gXV+BFCpIa3mJgYuYU3DQ0NqeHNyMiIhjeq2WKz2diwYQP69OlT2ZaRkYHly5fjjz/+UGBlzQu9YicjERERaNu2rci8UUOHDsXJkyfrvE8+n4/Zs2djy5YtYtt69eqFEydOSPyUIxQKsXv3bsybNw/5+fli27W0tLBmzRpMmTKlwQNSeno69u/fDx6Ph4iIiGof3759e3C5XIwZM6ZR9RehmgZCCFJTU6WGN0kTccuCurq61PBmbGxMwxvVog0dOhSnT5+u/J7NZuPZs2dwdnZWXFHNCA12MkAIQf/+/UVGqaqoqODVq1ewtbWt0z4LCwsxevRoXLhwQWxbYGAgtm/fLnEd0+joaEydOlXqLU0/Pz/8+eefMDc3r1NddVFRUYGLFy+Cx+PhwoUL1S5XpK+vj/HjxyMwMBDu7u4NVCXVVBFCkJaWJjbS9MN/i4qK5HJcDocjcaSpnZ0dTExMaHijKCnevHkDFxcXkcFE/fr1w6VLl+jfjQzQW7EycP78ebGpR+bMmVPnUJecnIxBgwaJjKz9YOXKlfjxxx/Ffvn5fD7++OMPLFmyRGLnbSMjI2zevBkjRoxosD+cV69eISgoCPv27ZPYv+9jTCYT/fr1A5fLxeDBg6GiotIgNVJNAyEE6enpUsNbYWGhXI7L4XBgZ2cnMbyZmprSkxBF1YGtrS3mzJmDVatWVbZdvnwZFy5caHST4TdF9IpdPZWVlcHV1RWxsbGVbWZmZoiMjISGhkat9/f06VMMHDgQycnJIu3KysrYu3cvxowZI/acJ0+eYPLkyQgPD5e4z0mTJmHdunUymW6lOvn5+Th69Ch4PB7u379f7eNbt24NLpeLiRMnwsLCQu71UY0XIQTv3r2TGt6q64dZV2pqalLDm5mZGQ1vFCUHhYWFcHR0REpKSmWbnZ0dXrx4QT/Y1xO9YldPGzZsEAl1ALB69eo6hbqLFy9i1KhRYlcf9PX1cfr0abGJHEtKSrB8+XKsWbNG4u1NGxsb7NixA3379q11LbVBCMGtW7fA4/Fw/PjxajudczgcjBw5EoGBgfD19aUnzhaEEIKMjAyp4U1Sn1BZUFVVrTK80cE4FNWwNDQ08Pvvv2PChAmVbTExMdi4cSPmz5+vwMqaPnrFrh5SU1Ph4OAgEsS6du2KkJCQWoeVbdu2YebMmSJLrgCAvb09Ll68CDs7O5H2W7duYcqUKYiOjhbbF4PBwLfffosVK1bUKWDWVFJSEoKDgxEUFCQWbiXx8vICl8vFyJEjoampKbe6KMUihCArK0tssMKH/5dXeFNRUZEa3szNzWl4o6hGhhACb29v3Lt3r7JNQ0MD0dHRDb6mbXNCg109BAQEiEz4y2Aw8ODBA3To0KHG+xAIBJg/f77Eod6+vr44deqUyC3U/Px8LFiwQGxplg9cXFywZ88edOnSpRavpObKyspw5swZ8Hg8XLlyReKSZB8zNjbGpEmTEBgYCCcnJ7nURDU8Qgiys7Olhre8vDy5HFdFRQWtW7cWG2lqb28PCwsLGt4oqol5+PAhOnXqJNIWEBCAoKAgBVXU9NFgV0ehoaFi4YnL5WLPnj013kdxcTHGjx+PU6dOiW0bN24c9uzZI9LX4Ny5c5gxY4ZY/zsAUFJSwk8//YQff/xRLv0Tnjx5Ah6Ph4MHDyI7O7vKx7LZbAwaNAhcLhf9+/evct1aqnH7OLx9OmXIx0sDyZKysnKV4a2xzqpPUVTdcLlcsSAXGhoqFviomqHBrg6EQiG6du2KBw8eVLZpamoiKiqqxpeP09LS4Ofnh4cPH4ptW7JkCZYsWVJ5O/fdu3eYNWsWjhw5InFfnTt3xp49e+Dq6lqHVyNddnY2Dh06BB6Ph8ePH1f7eBcXF3C5XIwfPx7GxsYyrYWSn5ycHKnhLScnRy7HVFJSkhreLC0taXijqBYkLS0NDg4OIgOkOnfujLt379Kr8HVAB0/UwYEDB0RCHQAsXry4xqHu5cuXGDBgABISEkTalZSUsHv3bkycOBHA+9tdBw4cwHfffSfxKhmHw8GqVaswc+ZMmZ0IBQIBrl27Bh6Ph1OnTqG8vLzKx2tpaWHMmDEIDAxEp06d6ECIRio3N1dieIuJiUFWVpZcjslms2FrayvW383e3h5WVlY0vFEUBQAwMTHB4sWLMW/evMq20NBQHDx4UGRwBVUz9IpdLRUUFMDBwQFpaWmVbQ4ODnj+/LnECYM/dfXqVfj7+4t1INfR0cGpU6fQo0cPAEB8fDymT5+Ov//+W+J++vTpgx07dshsrdQ3b94gKCgIwcHBSExMrPbxPXv2BJfLxbBhw8DhcGRSA1U/eXl5UsPbp0vdyQqbzUarVq2khjdFrT9MUVTTUl5eDjc3N5EBgaampoiMjKSD7WqJvuvW0sqVK0VCHQCsX7++RqFuz549mD59usiyY8D7yRovXLgAJycnCAQC/Pnnn/jxxx8lzpivq6uL9evXY+LEifW+OlZcXIwTJ06Ax+Phxo0b1T7e0tISAQEBCAgIqPPky1T95OfnSw1vGRkZcjkmi8WSGt6sra1peKMoqt6UlZWxfv16kQmKU1NTsWrVKvz6668KrKzpoVfsaiEmJgaurq4ityc///xzXLx4scrnCYVCLFy4EL/99pvYtq5du+LMmTMwNDTEq1evMGXKFJGh3x8bMWIENm/eXK/+a4QQPHjwADweD0eOHKl26gllZWUMHToUXC4Xn332Gb191gAKCgok9neLiYmpdgWPumKxWLCxsZG4vqm1tTUdAENRVIMYMGAALl26VPm9srIyXr16hdatWyuwqqaFBrtaGDJkCM6ePVv5PZvNxosXL+Do6Cj1OSUlJQgICMCxY8fEto0YMQLBwcFgsVj4/fffsWLFCol92szMzPDnn39iyJAhda49PT0dBw4cAI/Hw6tXr6p9vIeHB7hcLsaOHQs9Pb06H5eSrKCgoHIh+k/DW3p6ulyOyWQypYY3GxsbGt4oilK4yMhIuLm5idzZGjJkCE6fPq24opoYGuxq6MqVK+jXr59I25w5c7B27Vqpz8nIyMCQIUMkXoH74YcfsHLlSjx69AiTJ0/GixcvJO5j2rRpWL16NbS1tWtdM5/Px6VLl8Dj8XD+/HmxW8Cf0tPTw/jx4xEYGIh27drV+niUqMLCQqnh7dPb+bLCZDJhbW0tNbzVpMsARVGUIs2ZM0dsbtcrV66gT58+CqqoaaHBrgYqKirg7u6OiIiIyjYjIyNERUVJDVyRkZEYMGAA3rx5I9LOYrGwfft2jBkzBosWLcLGjRvFVpsA3q+Zt2vXrsrBFLXx+vVr8Hg87N+/v9oAwWAw0K9fP3C5XPj5+dE1+mqpqKhILLx9+P/U1FS5HJPBYFQZ3ui/IUVRTVleXh4cHBxEup44Ozvj6dOn9M5CDdBez1KcP38ee/bsgb29PTQ0NERCHQCsWrVKaqi7efMmhg4dKjYHmJaWFv766y8wGAy0adMGcXFxYs9lsViYM2cOli5dCjU1tRrXm5+fj2PHjoHH40nto/cxW1tbcLlcTJw4EZaWljU+TktUXFwsNbx9vIC1LDEYDFhZWUkMb61ataLhjaKoZktbWxurVq3ClClTKtsiIiKwbds2fPvttwqsrGmgV+wkiIqKgrOzs8QraQDQvn17PHjwQOLEifv378fkyZNRUVEh0m5lZYVDhw5hz549UpdKadeuHfbs2QNPT88a1UkIwe3bt8Hj8XD8+HEUFxdX+Xg1NTWMGDECXC4Xvr6+dOLHj5SUlCA2NlbiElmSVvqQBQaDAUtLS7Hg9iG8qaqqyuW4FEVRjZ1AIECnTp0QHh5e2aajo4OoqCgYGhoqsLLGj16xk+DWrVtSQx0AbNy4USwUEUKwbNkyLFu2TOzxHTp0wJdffgl/f3+JHeNVVFSwZMkSzJ07t0aXmZOSkrBv3z4EBQUhJiam2sd36dIFXC4Xo0aNgpaWVrWPb65KSkrw5s0bieEtKSlJbseVFt5sbW1peKMoipKAxWJh06ZN8PHxqWzLzc3FokWLpK6VTr3XIq7YCQQCZGdnIz09Henp6chIS0NZSQmEAgGYLBZU1NRgaGICY2NjGBsbIygoCAsWLJC6v6FDh+LQoUOVJ+WysjJMmTIFBw4cEHvsh7VSz507J3Ffvr6+2LVrV5Ujaz8c49y5c+DxeLh8+XKVwRN43wdw0qRJCAwMhLOzc5WPbU5KS0urDG/y+nW3sLCQGt5qc0udoiiK+r9x48bh0KFDld8zGAyEh4dXDvCr7fldT0+v2U/b1ayDXU5ODp4+fYrn4eEoLSoC4fOhUVIC7exsKPH5YBICIYOBCjYbeXp6KFRTA4PNRnFZGf69fRtPnz5FXl6exH137twZR44cgZaWFoYOHYpbt26JPaZPnz4IDQ2VOFecpqYmVq9ejWnTplV5S/Tp06cICgrCgQMHql36icViYdCgQeByufj888+bbSfTsrIykfD2cd+3xMREuYU3c3NzsfBmZ2eH1q1b09U3KIqi5CApKQmOjo4iXY26deuG06dP1+n8rqqujjaennB3d4eurq4CX5n8NMtgl5KSgrt37iAuOhpKxcWwSkiEaXY2tIuKoCQQSH1eBYuFPHV1xKlz8MbUFMVKSoiOi8Odu3clji41MjKCurq62CAIJpMJOzs7REVFSTzOoEGDsG3bNlhYWEjcnpOTg0OHDiEoKAhhYWHVvl5nZ2dwuVyMHz++xuvVNnZlZWWIi4uTGN4SEhLkFt7MzMykhjd1dXW5HJOiKIqSbsWKFVi0aBGA9+vK+nh5oZ2bG9T5/Fqf31P19JBgZYkKDget7O3h7esLU1PThnopDaJZBTs+n4+QkBA8DAmBRmYm7OITYJGZCVY1ty0/lZOTg8LyMmRZWCDB3h6ZGhoIefgQd+/ehaCKXxzgfX85QojEiYYNDQ2xadMmjBo1Smw5MKFQiGvXroHH4+HUqVMoKyur8jiampoYPXo0uFwuOnfuXO/lxRShvLy8yvBW3e3mujI1NRW7ZWpnZwc7Ozsa3iiKohqZkpISuLm5wdzcHN4dO8KgsBA2sbFwKa8Auw4RRsBkIsnAADHWVig0MEBHb294e3s3m+URm02wS0tLw4WzZ5GTlAyn6GjYJyeDWceX9i4jA3z++1GtQgYDKQ4OiHZyQnJ2Ns5evCh1WSclJSWx0bAfTJgwAX/88QcMDAxE2uPi4rB3717s3bsXCQkJ1dbWvXt3cLlc+Pv7N4kQUlFRITW8xcfHyy28mZiYSA1vGhoacjkmRVEUJXtpaWnYH7QXRZkZsH/9GmZRUWASAk1NTWhqaNZ5v0IGA9Hm5nhtbw89C3MM8PNrFne9mkWwi4+Px6mjR8FJSUX7iAhoVTPtR3XS0tMhFIpemSvR1sErTw+kcDg4fvp0jUIY8H6akx07dqB///7/31dJCU6ePAkej4d///232n1YWFhg0qRJCAgIgJ2dXe1eTAOoqKjA27dvpYa36q5y1pWxsbHYLdMP/9XUrPsfO0VRFNU4fHx+t713F0of9TVngAEjI6N6D4bI53AQ5uyMYjMzDB01EtbW1vUtW6GafLCLj4/HicOHoR+fgE6vXoEtgytA2Tk5KC0tqfz+/ahGBgrLyxDRuTMS9PVx5OTJKsMdg8HAzJkzsXLlSmhqaoIQgkePHoHH4+Hw4cNSB2V8oKysjC+++AJcLhe9e/dW+CgePp9fGd4+XSLr7du3cgtvRkZGUsNbS566haIoqrn79PxOysuRkZEB4P+xRU1VTSaDIPhMJkJdXZBtZQX/MWOadLhr0sEuLS0NR/btg07cW3R9+bLOt14/RQDk5+Whgs+HOocDFRUVpKWnA3g/yuZV166I09XF/iNHJN6WdXJywp49e+Dl5YV3797hwIED4PF4ePnyZbXHdnd3x+TJkzF27Fjo6+vL5PXUFJ/PR3x8vNTwVt1as3VlaGgoNbzVZY1ciqIoqmmTdn7Py8tDUXGRyGP19Q2gIoN1sIUMBu65uSLXphVGT5zQZG/LNtlgx+fzEczjQfAqAr6PH8vkSp00n/4iCVgsPOnWHRH8CgTt3y9ytcrS0hIRERG4fv06eDwezp07V20g0tXVxbhx48DlcuHh4SG31wG8/7klJCRIDG9xcXFyC2/6+vpi/d0+/FdHR0cux6QoiqKanqrO70IixLv0dxCS/7cpKymL9V+v87GZTNzy9ICSszMmcrlNckBF06v4PyEhIchJSkbPiAi5hjoAYlNrsAQCOIU9Qn7PnvDy8sLt27crtyUmJsLW1lbqAIsPGAwG+vTpAy6XiyFDhsh0BQKBQFBleJM2wKO+9PT0pIa35jpfEEVRFCVbVZ3fmQwmNLU0RbozlVdUgBAik9kh2EIh2r+KwA0tLdy9exfdunWr9z4bWpMMdikpKXgYEgKn6Oh6D5SoCS0tLZSUloJ89AlBPT8f9q9fo6xjR0RHR4vMc1dVqGvVqhUCAwMxadIkWFlZ1bkmgUCAxMREsfAWExODN2/eSJxuRRZ0dXUlLkxvZ2cHPT09uRyToiiKahlqcn7ncNRRUlyC8or35zllZSWZTvmlXVwMx6hoPFBRgb29fZOb565JBru7d+5AIzMT9nJanP1TTCYTxsbGSEtLFWk3i4pCmoUFvL28cOLkSanPV1NTw/Dhw8HlctGtW7cqV5r4mFAoRGJiothVt5iYGMTGxsotvOno6EgNbw3d74+iKIpqOWpyfmcA0NPXR1HR+y5S8pj6yyE5GcmmJgi5cwfDR4yQ+f7lqckFu5ycHMRFR8MjPkFmgyVqgslgQENDE4WFhfgwIodJCCxjYpDl4QFtbW2xka6dO3cGl8vFqFGjpA4CEAqFSEpKkhreqpuouK60tbWrDG9NccJjiqIoqumqzfmdyWBAU45zkjIJQev4BDzR10dOTk6T6k7U5ILd06dPoVRcDIvMzAY/tpamJvh8vshUKAaJieC0aQN3d3eR9WI9PT1x//59ANVfeSstLZVPvVpaUsObgYEBDW8URVFUo6HI87sklpmZeFFcjGfPnqF79+6KLqfGmlSwEwgEeB4eDquExFovEyYrn0YhllAIi/h4eLZpg9u3b1cOtHj16hWGDRuG6OhoxMbGoqSkRHxnMqCpqSkW3D58b2hoSMMbRVEU1eg1hvP7p1hCIawTE/EsLAw+Pj4Kn0+2pmrW2esTshhWPGXKFMTGxkrdvmHDBpE+ZD179kR2djZKi4pgmp0t9vjxz56hX9gjDA4Px7Anj/GqsLDeNUqipa0NJlP0H1cvNRXqqqoi/c9KS0tx6tQpvHjxot6hTkNDAx4eHhgxYgQCAgJgY2MDFouFffv2IS8vD2FhYTh69ChWrFiBSZMmwcvLC0ZGRjTUURRFUTXGZrPRrl27yq+6nLtWr15dp2NLO79vSYjHgPAwDAoPw7Anj5FYzR2uXUmJ9Xp+p/v3RL43zXpfV7aE3PGxTzNLXUVGRsLDwwPt2rWDu7s7zp49W+t9KOyK3e7du6vcvmHDBkyZMgXK/006eP36dbx48QKEz4eOlNC22ckZDurqOJaWhtVv47DXrU29ahQQAtYn4YjFZEJTQwN5+f/vT6eemws2gwFjY2Nk1vESsrq6usRbpvb29jA2Nq4MaUlJScjKysK6deugq6tLwxtFURQlEzo6Onjy5Em99rF69WrMnz+/Vs8RCARIT08XO7+H5+cjNC8PZ9p5QInJRFpZGdRYVV+P2pWUhKkWlnV+/qe0i4pA+Hykp6fD0NBQ6uM+zSzVEQqFEgdSWltbIzQ0FMrKykhPT4enpycGDx5cq3O9zIJdeHg4pk+fjpKSEnh4eGDnzp1QVVXFmTNnMG/ePGhra6Nt27bQ1dXF2rVr0aNHD2zZsgXOzs6YNGkSwsPDwWKx8P3336O4uBgpKSnw8vKCjY0Nzp49CwMDAxw9ehQaJSXYFf8WFzIywAAwzNgEgebmIrW019ICLzkJwPtwtjouDg/z81AhJJhqYQE/IyMUCwSYGxmJuJJiuGtq4X5eLi54tseLggJsTUyAMpOJPD4fwW5tsCw2BtHFxSAEmGtjAwcBH49LSrApMxNMACwGA9yCwmpH5qioqIDNZoPBYEBTUxMTJkyAh4cH9u/fD0NDQzx9+hQPHz7EsGHD0LlzZwBAcXEx4uLiRPajqamJoqIipKWl4c2bN7L6J6QoiqJaMKFQKHZOuXXrFjZt2oSysjLY29vjt99+g7KyMn766Se8ePEC5eXl8Pf3x9SpU7F27Vrk5ubCxcUF7dq1w/Tp0/H111/jzJkzAIBVq1bBwcEBw4cPR7du3TBo0CDcvn0bCxYswKNHj3Dm2DHwCgrQRVsbC2xaIb20FNosNhiEgBACExWVyrpu5+Rgc0I8yoRC2HM4WGXvgC0JCSjg8+H3OBztNDXhraMLXbYSlP4LUNU9X/mToLUzKRF/Z2Yi++ULxKalYceOHQCAlStX4siRI2AwGAgMDISysrJYZtm/fz9Wr14NQggmTZqEefPm4e3btxg8eDBcXV3x5MkTPH78+L8lS//v4zltS0tLxebRrRFSB/r6+mJtbm5u5P79+4QQQqZPn07WrVtHiouLiZWVFUlMTCQVFRWke/fuZM6cOYQQQrp3706eP39OHj16RLy8vCr3k5ubSwghxNramhQUFIgc8/CBA2Ru376ki7Y2eeHlTaJ8fMmDzl1IlI8v6aSlTc57eJIoH1+ywKYVmWpuQaJ8fMlyOzuywKYVifLxJc+6ehFHjjoJ7dyFzLOxIZPMzEiUjy8JcnMjAMjjrl5kv1sbosFikdsdO5EoH1/ypYUl2eTkRKJ8fMn9zl1IKzU18sCtDenK4ZC1pqbkRuvW5LyNDdkxZgyxt7MjeD9kln7RL/pFv+gX/aJfNfyytLAgf44aRW60bk36amiQX01MyMVWrUgrZWViraRE/LV1yAHH/5+Lu2rrkGddvUiUjy/52tKSLLZtTaJ8fIkOm02ifHxJlI8vCe/SlThwOKS1mhqZaGpGTri3q/Hzea5uZLypKYn09iHBgVzi4uJCnj9/Ti5cuEB69epFSktLCSGEZGVliWWWpKQkYmtrS7KyskhJSQnx8PAgjx49InFxcYTFYpGnT59WmbFevnxJ3NzciLq6Ojl16lStM5pMrtjl5uairKys8irThAkTsGbNGvTq1QtOTk6wsLAAAPj7+yM+Pl7kuba2tkhJScHXX3+NIUOGoG/fvlKPU1ZSgoikJPgbm1Qmax0lpcrt37yOQLlQiEKBAGc9PAEAITk5iCouxpmM95MGFwr4SCwtRXh+Aab9V5e3ji50Plo2xFNLC8b/JfuQ3BzcyM7Cn4nv79uXCATIEgjgpqqKnVlZiC8vRw8NDbDLy2FqbIzomJi6/yApiqIoqgXKys7Gr3//DeWSEpQRAgcVFXRVV8cuCws8KSlBWEkJpkdHYRObjQoiRGRxEUY+ewoAKBcK0UPCBPkabDZOe3giNDcXd/NyEfjiBTY6OaG8Bs+/k5uDG9k5eJT/GCWvXqKYzUZUVBTu3LmDwMBAqPyXESRNzP/w4UN89tlnlduGDx+OO3fuYMiQIXBwcEDbtm2r/Fm4uLjg+fPniImJwcSJE9G/f/9arU4l1z52pAaXEHV1dfH8+XNcvHgR69evx5UrV7B27VqJjxUKBEAV+9zs5Pz+kmrcG6x4E4utzi4QAvjFzg6dtHU+rU7qftQ+uhwrJATbXVxh/tEPNb+gAON0ddGZw8G94mJ8lZyM+S4ucLSzw62QkGpfM0VRFEVR/+dkb4+ZtrawffZMpJ3NYKADh4MOHA50WGxcy86Cj44ueujq4TcHh2r3y2Yw4K2rC29dXeixlXC1hs8XEmCmlRWGGRvjqW0rFHh5YdiwYbhz5069XieHw6nxYz+spf7ixQt06NChxs+r06jYT+no6EBFRQUPHz4EABw8eBDdunWDk5MTXr9+jeTkZAgEApyUsDpDZmYmhEIhRo4ciaVLl1Z23NTU1ERBQYFosSwW2piZ4UR6Gsr/Gw6d+8m6pwwGA99b2+BJfj7eFBfDR0cXB1NTIfgvEEYVFUFACDy0tHDpv4EO93JzkcvnS3xt3rq62JeSUvn9q8JCaGpqIl0ohJ2KCibo6sJaSQkZxcXIyc2t/Q+PoiiKolq42LdvUfDf+TyHz0cWn4+E8nIk/9fGZDCRDAIzFRV4aGkiNC8Xyf+NcC3k8ytHu7IYjMrz/ZviYiT8N7KXEIKo4qJqn/+Bj64OjqenoUQggJDBRHZuLvLy8tC7d28EBQVVLh7wYbTsx5mlU6dOuHbtGnJyclBWVoaTJ0/C19e3Rj+HhISEyn2npKTgxYsXsLGxqdXPsk5X7HJycipvrwLAmjVrsHfvXsyYMQOlpaVo164dZsyYAVVVVWzYsAE9e/aEtrY2nJycoKWlJbKv5ORkBAQEQCgUgs1mY8OGDQCAqVOnomfPnnBwcKgc7quipgY3GxuURMfgiyePwWYw4G9kjEmfDJ5QY7HANbcALzkZy+zskFRaii8eh0MIwFBZGbtd3TDO1AxzI19jQHgY3DU0YaysDFUJI1S+trTCijexGBweBj4hcNXQwFpHJ5wtLcX93FxAKISjsjKsTUxw8dUrkeeePn0aqqqq2L59Ow4dOoTMzExMmzYNSUlJ0NXVxc6dO2FtbY1p06bhiy++wIABA1BYWIgOHTrg9evXEn/2z58/x9ChQ5Gbmws1NTW0bt0aN27cqN0/IEVRFEV9wtLSEomJotOFXLt2DYsXL0ZFRQUYDAbWrFmDbt26YerUqXjw4AGsra3BZrMxZcoUDBgwAAsXLsSlS5fg7e2NzZs3Y9OmTdi5cycsLS2hp6eH/v37Y8KECXBycsKjR4+g8d/qEXNmz8a648ehWloKZSYTv9nbA0KCX97EopAvABiAq7oGJpiaQZXFwgo7e3zzOgIVQiEYDAYWtrKFpaoqhhoZY1B4GDpqa2OkiQmWx8aiUCAAULPnf9BNVw8xxcUY+fQJCiMioHHvLsYHBGDAgAEICwuDp6cnlJSUEBgYiFmzZollliVLlqBbt26Vgyc8PT3x9u3bav8Nnjx5goULF4LFYoHJZGLjxo21nmKOQWpyv7QeCgsLoaGhAYFAgGHDhmHq1KkYNGhQnfZ17do1RF6+jD737te7Lj4hEBICZSYTTwsKsCw2BifbedRpX+UV5fi7QwdcjIjAv//+C+D9CNjU1NQmtQwJRVEURSmCLM/vsvZP1y5w7NcPn332maJLqRG5z2O3bds2HDx4EGVlZejduzcGDhxY530ZGxsjTE0NFSwWlP5L4HVVLBBg0vPn4BMCJSYDS1vb1XlfDFU1CPT18fXXX8PMzAwZGRmYO3cuDXUURVEUVQOyPL/LUgWLhUI1NRgbGyu6lBqTe7CbN28e5s2bJ5N9GRsbg8FmI09dHQb5+fXalxabjVMedbtC96k8dXUw2Gz4+vpi2LBhMtnn5cuXsWDBApE2b29vbN26VSb7pyiKoqjGQpbnd1n6cH6XdbDLysoSuwKooqKC0NDQeu+7Sa0Vq6enB1V1daTq6TWqf/hU/fd1SRr2XFf9+vVDv379ZLY/iqIoimqsWtL5HQD09fXrvcqHNDIZFdtQWCwW2nh6IsHKEgIJAx0UQcBkIt7SEm3bt28yCwRTFEVRVGNCz++y0zh+erXg7u6OCg4HSbUcJSIviQYG4HM41U44SFEURVGUdPT8LhtNLtjp6uqilb09YqytIKzForjyIGQwEGtthVYODnSgBEVRFEXVAz2/y0aTC3YA4O3ri0IDA0R/Mn9dQ4syN0ehgQG8fXwUWgdFURRFNQf0/F5/TTLYmZqaoqO3N17b2yO/FstzyFIeh4NIB3t08vGBqampQmqgKIqiqOaEnt/rr0kGO+D91B+6FuYIc3YGv4E7WvKZTIS5OEPP3BxeXl4NemyKoiiKas7o+b1+mmywY7PZGOjnh2IzM4S6ujTY/Xghg4FQVxeUmJphgJ8f2OwmNWMMRVEURTVq9PxeP0022AGAiYkJho4aiWwrK9xzc5V7suczmbjn5opsKysMHTUSJiYmcj0eRVEURbVE9Pxed3JfK7YhxMfH49TRY+CkpKB9RAS0iotlfow8DgdhLs4oMTXD0FEjYW1tLfNjUBRFURT1f/T8XnvNItgBQFpaGi6cPYucpGQ4RUfDPjkZTBm8NCGDgShzc0Q62EPP3BwD/PyadJKnKIqiqKaEnt9rp9kEOwDg8/kICQnBw5AQaGRmonV8AiwzM8ESCmu9LwGTiUQDA8RaW6HQwACdfHzg5eXVZO+5UxRFUVRTRc/vNdesgt0HKSkpuBsSgrioKLCLi2GdmAjTrGxoFxVBSSCQ+rwKFgt56upI1ddDvKUl+BwOWjk4wLuJDnmmKIqiqOaEnt+r1yyD3Qc5OTl49uwZnoWFobSoCITPh0ZJCbSyc6DM54NJhBAymChns5Gvp4tCNTUw2Gyoqqujbfv2aNu2bZObcZqiKIqimjt6fpeuWQe7DwQCAbKzs5Geno709HRkpKWhvLQUAj4fLDYbyqqqMDQxgbGxMYyNjaGnp9ekFvylKIqiqJaInt/FtYhgR1EURVEU1RI06XnsKIqiKIqiqP+jwY6iKIqiKKqZoMGOoiiKoiiqmaDBjqIoiqIoqpmgwY6iKIqiKKqZoMGOoiiKoiiqmaDBjqIoiqIoqpmgwY6iKIqiKKqZoMGOoiiKoiiqmaDBjqIoiqIoqpmgwY6iKIqiKKqZoMGOoiiKoiiqmaDBjqIoiqIoqpmgwY6iKIqiKKqZoMGOoiiKoiiqmaDBjqIoiqIoqpmgwY6iKIqiKKqZoMGOoiiKoiiqmaDBjqIoiqIoqpmgwY6iKIqiKKqZoMGOoiiKoiiqmaDBjqIoiqIoqpmgwY6iKIqiKKqZoMGOoiiKoiiqmaDBjqIoiqIoqpmgwY6iKIqiKKqZoMGOoiiKoiiqmfgfeQTt/4Gh2xkAAAAASUVORK5CYII=", - "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..3c5661e4 100644 --- a/Tutorial/4_Symbolic_Regression_and_Classification.ipynb +++ b/Tutorial/4_Symbolic_Regression_and_Classification.ipynb @@ -24,26 +24,26 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "Generation: 100%|██████████| 50/50 [01:59<00:00, 2.39s/it]\n" + "Generation: 100%|██████████| 20/20 [00:12<00:00, 1.57it/s]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "0.8397174152569836\n" + "0.8500096024582293\n" ] }, { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "
" ] @@ -57,30 +57,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 +90,17 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "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", - "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" + "LogisticRegression_1 : LogisticRegression(C=1.7363936958422204, class_weight='balanced', max_iter=1000,\n", + " n_jobs=1, solver='saga')\n", + "AddTransformer_1 : AddTransformer()\n", + "FeatureSetSelector_1 : FeatureSetSelector(name='84', sel_subset=[84])\n" ] } ], @@ -116,12 +112,12 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "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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NXHiRD355JOlzjNjxgxNnz5dX331lYKDgxUeHq4ff/xR0i8Bon///urevbu+/PJLJSUl6fTp0xo6dKjDMd566y3Vq1dPu3bt0ooVK8qs75VXXtFLL72kF198Uf/9738VGhqqBx98UN99950k6dSpU+rYsaOmT5+uU6dO6ZlnnrnqtZZn3lJTUzV06FD96U9/0oEDBzRv3jxFR0frzTfftPcZO3asMjMz9emnn2rjxo1avny5cnJyHI7zxz/+UTk5Ofr444+VmpqqHj16aMCAATp79qwkaeTIkWrRooVSUlKUmpqqqKgo1a1b96q1A79ZBgBUkDFjxhgPPfSQYRiG0bt3b+Oxxx4zDMMwNm3aZPzf/9zExMQYXbt2dRj78ssvG61atXI4VqtWrYzi4mJ7W7t27Yy77rrL/r6oqMioX7++8Y9//MMwDMM4duyYIcmIi4uz97l8+bLRokUL4/nnnzcMwzAWLlxoDBw40OHcmZmZhiTjyJEjhmEYxj333GN07979utfbrFkzY9GiRQ5td955p/HUU0/Z33ft2tWIiYm55nHKO28jRowwfv/73zuMnTFjhhEQEGAYhmEcOXLEkGTs27fP/vmhQ4cMScbLL79sGIZh/Oc//zE8PT2NixcvOhyndevWxsqVKw3DMAwPDw/jzTffvM7VA2AlCkCleP755/XWW2/p0KFDpo/RsWNHubj873+mfH191blzZ/t7q9WqRo0alVppCQ4Otv/fderUUWBgoL2O/fv369NPP9Wtt95qf7Vv317SL/uXrujZs+c1a7PZbMrKylLfvn0d2vv27XtT13yteTt06FCZ5/vuu+9UXFysQ4cOqU6dOg61t2/fXrfddpv9/f79+3XhwgU1atTIYQ6OHTtmv/7IyEiNHz9eISEhiouLc5gXAP+LEAWgUtx9990KDQ3VzJkzS33m4uIiwzAc2i5fvlyq369vIVksljLbSkpKyl3XhQsXFB4ervT0dIfXd999p7vvvtver379+uU+ZkW61rxVhAsXLqhp06alrv/IkSOaMWOGpF/2rH399dcKCwvT9u3bFRAQoE2bNlVKPUBNVsfZBQCoveLi4tStWze1a9fOod3b21vZ2dkyDMP+J/wV+WynL774wh6IioqKlJqaqkmTJkmSevTooffee0/+/v6qU8f8fwI9PT3VrFkz7dq1S/fcc4+9fdeuXerVq9dN1X+1eevQoYN27drl0LZr1y61bdtWVqtV7du3t1/vnXfeKUk6cuSIzp07Z+/fo0cPZWdnq06dOg4b+X+tbdu2atu2raZNm6bhw4drzZo1evjhh2/quoDahpUoAJWmc+fOGjlypP761786tN977706c+aMXnjhBX3//fdatmyZPv744wo777Jly7Rp0yYdPnxYERER+umnn/TYY49JkiIiInT27FkNHz5cKSkp+v777/Wvf/1L48aNU3Fx8Q2dZ8aMGXr++eeVmJioI0eOKCoqSunp6ZoyZcpN1X+1eZs+fbqSk5O1cOFCffvtt3rrrbe0dOlS+4b1du3aadCgQXryySe1d+9epaamavz48XJ3d7cfIyQkRMHBwRoyZIg++eQTHT9+XLt379bs2bP15Zdf6ueff9akSZO0Y8cO/fDDD9q1a5dSUlLUoUOHm7omoDYiRAGoVAsWLCh1u61Dhw5avny5li1bpq5du2rfvn3X/Mu1GxUXF6e4uDh17dpVn3/+uTZv3qzGjRtLkn31qLi4WAMHDlTnzp01depU3XbbbQ77r8rj6aefVmRkpKZPn67OnTsrKSlJmzdvVps2bW76Gsqatx49euidd97Rhg0b1KlTJ82dO1cLFizQ2LFj7X3WrFmjZs2a6Z577tEjjzyiCRMmyMfHx/65xWLR1q1bdffdd2vcuHFq27at/vSnP+mHH36Qr6+vrFarfvzxR40ePVpt27bV0KFDdf/992v+/Pk3fU1AbWMxfr0xAQAAANfFShQAAIAJhCgAAAATCFEAAAAmEKIAAABMIEQBAACYQIgCAAAwgRAFAABgAiEKAADABEIUAACACYQoAAAAEwhRAAAAJvw/WnYg5rhg4zYAAAAASUVORK5CYII=", "text/plain": [ "
" ] @@ -151,26 +147,26 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "Generation: 100%|██████████| 50/50 [02:24<00:00, 2.89s/it]\n" + "Generation: 100%|██████████| 20/20 [00:09<00:00, 2.09it/s]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "-53.572578179092396\n" + "-4348.811587281301\n" ] }, { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "
" ] @@ -182,33 +178,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=X_train.shape[1]), \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", - " verbose=1,\n", - " processes=False,\n", + " search_space = graph_search_space ,\n", + " verbose=2,\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 +210,25 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "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(alpha=6.014583593220849e-05, epsilon=2.109266488257155e-05,\n", + " eta0=0.06363149574923024, l1_ratio=2.519434640584705e-06,\n", + " learning_rate='constant', loss='squared_epsilon_insensitive',\n", + " penalty='elasticnet')\n", + "FeatureSetSelector_1 : FeatureSetSelector(name='8', sel_subset=[8])\n", + "FeatureSetSelector_2 : FeatureSetSelector(name='2', sel_subset=[2])\n", + "FeatureSetSelector_3 : FeatureSetSelector(name='9', sel_subset=[9])\n", + "GTTransformer_1 : GTTransformer()\n", + "LTTransformer_1 : LTTransformer()\n", + "LTTransformer_2 : LTTransformer()\n", + "FeatureSetSelector_4 : FeatureSetSelector(name='1', sel_subset=[1])\n", + "MaxTransformer_1 : MaxTransformer()\n" ] } ], @@ -244,12 +240,12 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "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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sWHDBqNjx47G0KFD3V1SuZk4caJx1113ubsMtxk9erSRmJjo7jI8xtChQ42GDRsaxcXF7i6l3HTr1s3o16+fQ9vvf/97o3fv3m6qqHz9/PPPhq+vr/Hxxx87tMfHxxvPPfdcudfDLTwvlJubK0mqUaOGmytxj6KiIi1fvlwXL15UQkKCu8spV4MHD1a3bt2UlJTk7lLc4tChQ4qIiFCDBg3Uu3dvHT9+3N0llZs1a9aodevWeuihhxQSEqKWLVvqb3/7m7vLcotffvlFS5cuVb9+/Vz+g+2erH379srIyNB3330nSdqzZ4+++uordenSxc2VlY/CwkIVFRXJarU6tAcEBOirr74q/4LKPbLhlhQVFRndunUzOnTo4O5Syt3evXuNwMBAw9fX17DZbMYnn3zi7pLK1XvvvWfExMQYly5dMgzDqHBXoNauXWu8//77xp49e4x169YZCQkJRt26dY28vDx3l1Yu/P39DX9/fyMtLc3YuXOn8frrrxtWq9VYvHixu0srdytWrDB8fX2NkydPuruUclVUVGSMHj3asFgshp+fn2GxWIzp06e7u6xylZCQYHTs2NE4efKkUVhYaLzzzjuGj4+P0bhx43KvhQDlZZ566imjXr16xokTJ9xdSrkrKCgwDh06ZGzfvt0YM2aMUatWLePbb791d1nl4vjx40ZISIixZ88ee1tFC1D/7dy5c0a1atUqzG3cSpUqGQkJCQ5tzzzzjNGuXTs3VeQ+DzzwgPG73/3O3WWUu/fee8+oXbu28d577xl79+413n77baNGjRoVKkR///33xj333GNIMnx9fY02bdoYvXv3NqKjo8u9FgKUFxk8eLBRu3Zt44cffnB3KR7hvvvuMwYMGODuMsrFBx98YP8H49pDkmGxWAxfX1+jsLDQ3SW6RevWrY0xY8a4u4xyUbduXaN///4Oba+++qoRERHhporc4+jRo4aPj4+Rnp7u7lLKXe3atY0FCxY4tE2dOtVo0qSJmypyn/z8fOPUqVOGYRjGww8/bHTt2rXca2ANlBcwDENPP/20PvjgA33xxReKjIx0d0keobi4WAUFBe4uo1zcd999+uabb7R79277o3Xr1urdu7d2794tX19fd5dY7vLz83X48GGFh4e7u5Ry0aFDhxLbl3z33XeqV6+emypyj7feekshISHq1q2bu0spdz///LN8fBw/tn19fVVcXOymitwnMDBQ4eHhOnfunD777DP17Nmz3GuoON8B9mKDBw/WsmXL9OGHHyooKEg5OTmSJJvNpoCAADdXVz7S0tLUpUsX1a1bVxcuXNCyZcv0j3/8Q5999pm7SysXQUFBiomJcWgLDAxUzZo1S7Tfrp599ll1795d9erV06lTpzRx4kT5+vqqV69e7i6tXAwfPlzt27fX9OnT9fDDD2vr1q1atGiRFi1a5O7Syk1xcbHeeust/elPf6pQW1hc0717dz3//POqW7eumjdvrl27dmnevHnq16+fu0srN5999pkMw1CTJk30/fffa+TIkYqOjtYTTzxR/sWU+zUvmCbpuo+33nrL3aWVm379+hn16tUzKleubAQHBxv33XefsX79eneX5VYVbQ3UI488YoSHhxuVK1c27rzzTuORRx4xvv/+e3eXVa4++ugjIyYmxvD39zeio6ONRYsWubukcvXZZ58ZkoyDBw+6uxS3yMvLM4YOHWrUrVvXsFqtRoMGDYznnnvOKCgocHdp5WbFihVGgwYNjMqVKxthYWHG4MGDjfPnz7ulFothVKAtTAEAAFyANVAAAAAmEaAAAABMIkABAACYRIACAAAwiQAFAABgEgEKAADAJAIUAACASQQoAAAAkwhQALze0aNHZbFYtHv3bneXYnfgwAG1a9dOVqtVcXFxbqmhfv36evHFF93y2sDtjgAF4Jb17dtXFotFM2fOdGhPT0+XxWJxU1XuNXHiRAUGBurgwYPKyMi47hjeN8B7EaAAuITVatWsWbN07tw5d5fiMr/88ovTzz18+LASExNVr1491axZ84bjbsf3DagICFAAXCIpKUlhYWGaMWPGDcdMmjSpxO2sF198UfXr17cf9+3bVykpKZo+fbpCQ0N1xx13aMqUKSosLNTIkSNVo0YN1a5dW2+99VaJ8x84cEDt27eX1WpVTEyMNm7c6NCflZWlLl26qGrVqgoNDdUf//hH/fTTT/b+e++9V08//bSGDRumWrVqKTk5+brzKC4u1pQpU1S7dm35+/srLi5O69ats/dbLBbt2LFDU6ZMkcVi0aRJk27pfZOkVatWqXnz5vL391f9+vU1d+5ch/4zZ86oe/fuCggIUGRkpN59990S5zh//ryefPJJBQcHq1q1avrtb3+rPXv22Pv37NmjTp06KSgoSNWqVVOr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", "text/plain": [ "
" ] @@ -286,7 +282,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..d062c5b4 --- /dev/null +++ b/Tutorial/5_Genetic_Feature_Selection.ipynb @@ -0,0 +1,626 @@ +{ + "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": [ + "/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 35727 instead\n", + " warnings.warn(\n", + "Generation: 100%|██████████| 5/5 [04:07<00:00, 49.49s/it]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0.9554814292129066\n" + ] + } + ], + "source": [ + "import tpot2\n", + "import sklearn.datasets\n", + "from sklearn.linear_model import LogisticRegression\n", + "import numpy as np\n", + "\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", + "genetic_feature_selection_search_space = tpot2.search_spaces.nodes.GeneticFeatureSelectorNode(n_features=n_features)\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 = None, \n", + " inner_search_space = tpot2.config.get_search_space([\"transformers\"]),\n", + " max_size = 10,\n", + ")\n", + "\n", + "combined_search_space = tpot2.search_spaces.pipelines.SequentialPipeline([genetic_feature_selection_search_space, graph_search_space])\n", + "\n", + "\n", + "est = tpot2.TPOTEstimator(population_size=10,generations=5, \n", + " scorers=['roc_auc_ovr',tpot2.objectives.complexity_scorer],\n", + " scorers_weights=[1,-1],\n", + " n_jobs=32,\n", + " classification=True,\n", + " search_space = combined_search_space,\n", + " verbose=1,\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))" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
Pipeline(steps=[('maskselector',\n",
+       "                 MaskSelector(mask=array([ True,  True, False,  True,  True,  True,  True,  True,  True,\n",
+       "        True, False,  True,  True,  True,  True,  True,  True,  True,\n",
+       "        True,  True, False,  True,  True,  True,  True,  True,  True,\n",
+       "        True,  True,  True, False, False, False,  True, False,  True,\n",
+       "        True,  True, False, False, False, False,  True, False,  True,\n",
+       "       False,  True, False,  True,  True,  True,  True,  True,  True,\n",
+       "       False,  True,  True,  True,  True, False, False,  True,  True,\n",
+       "        True, False, False,  True,  True, False, False, False, False,\n",
+       "        True,  True, False,  True,  True,  True, False,  True,  True,\n",
+       "        True, False,  True,  True,  True, False,  True,  True,  True,\n",
+       "        True,  True,  True,  True,  True, False, False, False,  True,\n",
+       "        True]))),\n",
+       "                ('graphpipeline',\n",
+       "                 GraphPipeline(graph=<networkx.classes.digraph.DiGraph object at 0x73a335f391e0>))])
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, True, True, True, True, True, True,\n", + " True, False, True, True, True, True, True, True, True,\n", + " True, True, False, True, True, True, True, True, True,\n", + " True, True, True, False, False, False, True, False, True,\n", + " True, True, False, False, False, False, True, False, True,\n", + " False, True, False, True, True, True, True, True, True,\n", + " False, True, True, True, True, False, False, True, True,\n", + " True, False, False, True, True, False, False, False, False,\n", + " True, True, False, True, True, True, False, True, True,\n", + " True, False, True, True, True, False, True, True, True,\n", + " True, True, True, True, True, False, False, False, True,\n", + " True]))),\n", + " ('graphpipeline',\n", + " GraphPipeline(graph=))])" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "est.fitted_pipeline_" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "plt.imshow([est.fitted_pipeline_.steps[0][1].mask])" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "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 99% rename from Tutorial/5_GraphPipeline.ipynb rename to Tutorial/6_GraphPipeline.ipynb index 47d48c9d..810c6890 100644 --- a/Tutorial/5_GraphPipeline.ipynb +++ b/Tutorial/6_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.14" }, "orig_nbformat": 4, "vscode": { diff --git a/Tutorial/6_SH_and_early_termination.ipynb b/Tutorial/6_SH_and_early_termination.ipynb deleted file mode 100644 index 1b033644..00000000 --- a/Tutorial/6_SH_and_early_termination.ipynb +++ /dev/null @@ -1,506 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Welcome to this Jupyter Notebook tutorial parameters relating to computational resources. In this tutorial, we will cover the following parameters:\n", - "\n", - "`population_size`\n", - "\n", - "`initial_population_size`\n", - "\n", - "`population_scaling`\n", - "\n", - "`generations_until_end_population`\n", - "\n", - "`budget_range`\n", - "\n", - "`generations_until_end_budget`\n", - "\n", - "`budget_scaling`\n", - "\n", - "`stepwise_steps`\n", - "\n", - "Population size is the number of individuals evaluated each generation. Budget refers to the proportion of data to sample. By manipulating these parameters, we can control how quickly the budget increases and how population size changes over time. Most often, this will be used to start the algorithm by evaluating a large number of pipelines on small subsets of the data to quickly narrow now best models, before later getting a better estimate with larger samples on fewer datasets. This can reduce overall computational cost by not spending as much time evaluating poor performing pipelines.\n", - "\n", - "`population_size` determines the number of individuals to evalaute each generation. Sometimes we may want to evaluate more or fewer individuals in the earlier generations. The `initial_population_size` parameter specifies the starting size of the population. The population size will gradually move from `initial_population_size` to `population_size` over the course of `generations_until_end_population` generations. `population_scaling` dictates how fast that scaling takes place. The interpolation over `generations_until_end_population` is done stepwise with the number of steps specified by `stepwise_steps`.\n", - "\n", - "The same process goes for the budget scaling. \n", - "\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The following cell illustrates how the population size and budget change over time with the given settings." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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W1RUpLy8PGPJz8+vtH0AAAAAaotqHR68vb0VExOjadOm6ddff5XZbNY777yj1NRUHTt2TNnZ2ZIkf39/q/f5+/sb28qTmJgoHx8fYwkPD6/U/QAAAABqg2odHiRp6dKlslgsCgoKkpubm+bOnav7779fTk6XX/r48eN1+vRpY8nIyLBjxQAAAEDtVO3DQ6tWrbR+/XoVFBTo8OHDSktLU0lJia666ioFBARIknJycqzek5OTY2wrj5ubmxo0aGAs3t7elboPAAAAQG1Q7cPDH7y8vBQYGKhTp05pzZo16tOnj0JDQxUQEKCUlBSjX15enjZt2qSYmBgHVgsAAADUPvUcXcDfWbNmjSwWi8LCwrRv3z49/fTTateunQYPHiyTyaQxY8bo+eefV5s2bRQaGqqJEyeqWbNm6tu3r6NLBwAAAGqVah8eTp8+rfHjx+vIkSNq3Lix7rnnHr3wwgtycXGRJD3zzDMqLCzUI488otzcXN1www1avXr1BTM0AQAAALgy1T483Hfffbrvvvsuut1kMmnq1KmaOnVqFVYFAAAA1D015p4HAAAAAI5FeAAAAHXGvHnzFBISInd3d0VHRystLe2S/ZOSkhQWFiYPDw8FBwfrySef1Llz56qoWqD6ITwAAIA6Yfny5UpISNDkyZO1detWderUSXFxcTp+/Hi5/d99912NGzdOkydP1u7du7Vw4UItX75c//rXv6q4cqD6IDwAAIA6YdasWRo2bJgGDx6s8PBwJScny9PTU2+99Va5/Tds2KBu3brpgQceUEhIiO644w7df//9f3u2AqjNCA8AAKBGy8/PV15enrEUFRVd0Ke4uFjp6emKjY012pycnBQbG6vU1NRyx+3atavS09ONsPDLL7/o888/V8+ePStnR4AagPAAAABqtPDwcPn4+BhLYmLiBX1+++03mc1m+fv7W7X7+/srOzu73HEfeOABTZ06VTfccINcXFzUqlUrde/encuWUKdV+6lagT87U2x2dAl1hoeLs0wmk6PLqBssFunMGUdXUTcUFjq6AlSCjIwMBQUFGetubm52GXfdunWaPn26/vOf/yg6Olr79u3T6NGjNW3aNE2cONEunwHUNIQH1CiRz3/l6BLqjMiWjfThYzEEiMpmsUg33CBt2ODoSoAay9vbWw0aNLhkH19fXzk7OysnJ8eqPScnRwEBAeW+Z+LEiXrooYc0dOhQSVKHDh2MB9P++9//lpMTF3Cg7uGnHtWeh4uzIls2cnQZdc6WQ6d0toQzPZXuzBmCgyN06yZ5ejq6ClQhV1dXRUREKCUlxWgrKytTSkqKYmJiyn3PmTNnLggIzs7OkiSLxVJ5xQLVGGceUO2ZTCZ9+FgMX2SryJliM2d4HCUnR/LycnQVdYOnp8RZtTonISFB8fHxioyMVFRUlJKSklRYWKjBgwdLkgYOHKigoCDjnom7775bs2bNUpcuXYzLliZOnKi7777bCBFAXUN4QI1gMpnk6cqPK2o5Ly/CA1CJ+vXrpxMnTmjSpEnKzs5W586dtXr1auMm6qysLKszDRMmTJDJZNKECRN09OhRNW3aVHfffbdeeOEFR+0C4HAmC+fddOTIEQUHB+vw4cNq3ry5o8sBHOpMcanCJ62RJGVMjSO0VbbCQql+/fOvCwoID0AF8PsbqHrc8wAAAADAJoQHAAAAADYhPAAAAACwCeEBAAAAgE0IDwAAAABsQngAAAAAYBPCAwAAAACbEB4AAAAA2ITwAAAAAMAmhAcAAAAANiE8AAAAALAJ4QEAAACATQgPAAAAAGxCeAAAAABgk8sKD/v379eECRN0//336/jx45KkL774Qj/99JNdiwMAAABQfVQ4PKxfv14dOnTQpk2btGLFChUUFEiSfvzxR02ePNnuBQIAAACoHiocHsaNG6fnn39ea9eulaurq9F+6623auPGjXYtDgAAAED1UeHwsHPnTv3jH/+4oN3Pz0+//fabXYoCAAAAUP1UODw0bNhQx44du6B927ZtCgoKsktRAAAAAKqfCoeH/v3769lnn1V2drZMJpPKysr0ww8/aOzYsRo4cKBdizObzZo4caJCQ0Pl4eGhVq1aadq0abJYLEafQYMGyWQyWS09evSwax0AAAAApHoVfcP06dM1YsQIBQcHy2w2Kzw8XGazWQ888IAmTJhg1+JefPFFzZ8/X0uWLNHVV1+tLVu2aPDgwfLx8dETTzxh9OvRo4cWLVpkrLu5udm1DgAAAACXER5cXV31xhtvaNKkSdq5c6cKCgrUpUsXtWnTxu7FbdiwQX369FGvXr0kSSEhIXrvvfeUlpZm1c/NzU0BAQF2/3wAAAAA/6fCly19++23On78uIKDg9WzZ0/dd999atOmjUpKSvTtt9/atbiuXbsqJSVFe/bskXR+Otjvv/9ed955p1W/devWyc/PT2FhYRo+fLhOnjx5yXGLioqUl5dnLPn5+XatGwAAAKiNKnzmoXv37vL399fHH3+s66+/3mj//fffdcstt8hsNtutuHHjxikvL0/t2rWTs7OzzGazXnjhBQ0YMMDo06NHD/3zn/9UaGio9u/fr3/961+68847lZqaKmdn53LHTUxM1HPPPWe3OgEAAIC64LKeMN2/f3/ddtttWrx4sVX7n29ktocPPvhAy5Yt07vvvqutW7dqyZIleuWVV7RkyRKrWnr37q0OHTqob9+++uyzz7R582atW7fuouOOHz9ep0+fNpaMjAy71g0AAADURhU+82AymTR+/HjdeOONGjhwoHbs2KGZM2ca2+zp6aef1rhx49S/f39JUocOHXTo0CElJiYqPj6+3PdcddVV8vX11b59+3TbbbeV28fNzc3qpuq8vDy71g0AAADURhU+8/DH2YV//vOf+u677/Tf//5Xd955p3Jzc+1dm86cOSMnJ+sSnZ2dVVZWdtH3HDlyRCdPnlRgYKDd6wEAAADqssu6bOkPXbp0UVpamnJzcy/6V/4rcffdd+uFF17QqlWrdPDgQX388ceaNWuW8YTrgoICPf3009q4caMOHjyolJQU9enTR61bt1ZcXJzd6wEAAADqsgqHh/j4eHl4eBjrAQEBWr9+vW677Ta1aNHCrsW9+uqr+p//+R89/vjjat++vcaOHatHH31U06ZNk3T+LMSOHTvUu3dvtW3bVkOGDFFERIS+++47nvUAAAAA2JnJYu+7nGugI0eOKDg4WIcPH1bz5s0dXQ7gUGeKSxU+aY0kKWNqnDxdK3xrFCqisFCqX//864ICycvLsfUANQi/v4GqZ9O3gh07duiaa66Rk5OTduzYccm+HTt2tEthAAAAAKoXm8JD586dlZ2dLT8/P3Xu3Fkmk8lqWtY/1k0mk12f8wAAAACg+rApPBw4cEBNmzY1XgMAAACoe2wKDy1btiz3NQAAAIC6o8KzLS1ZskSrVq0y1p955hk1bNhQXbt21aFDh+xaHAAAAIDqo8LhYfr06cZUrampqXrttdf00ksvydfXV08++aTdCwQAAABQPVR4DsbDhw+rdevWkqSVK1fqf/7nf/TII4+oW7du6t69u73rAwAAAFBNVPjMQ/369XXy5ElJ0pdffqnb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XQ7gWIWFUv36518XFHDPQyU7U1yq8ElrJEkZU+Pk6XrZz+4E6pya8Pvbw8ND27ZtU7t27azaf/75Z3Xp0kVnz57VwYMHFR4erjNnzjioSsB2Ff4t1bJlS0nn70fIyspScXGx1faOHTvapzIAAIAarl27dpoxY4YWLFggV1dXSVJJSYlmzJhhBIqjR49eMO08UF1VODycOHFCgwcP1hdffFHudrPZfMVFAQAA1Abz5s1T79691bx5c+MPrDt37pTZbNZnn30mSfrll1/0+OOPO7JMwGYVDg9jxoxRbm6uNm3apO7du+vjjz9WTk6Onn/+ec2cObMyagQAAKiRunbtqgMHDmjZsmXas2ePJOnee+/VAw88YEx7/9BDDzmyRKBCKhwevv76a33yySeKjIyUk5OTWrZsqdtvv10NGjRQYmKievXqVRl1AgAA1Eje3t567LHHHF0GYBcVDg+FhYXG8xwaNWqkEydOqG3bturQoYO2bt1q9wIBAABqqrfffvuS2wcOHFhFlQD2UeHwEBYWpszMTIWEhKhTp056/fXXFRISouTkZKunPAMAANR1o0ePtlovKSnRmTNn5OrqKk9PT8IDapwKh4fRo0fr2LFjkqTJkyerR48eWrZsmVxdXbV48WJ71wcAAFBjnTp16oK2vXv3avjw4Xr66acdUBFwZSocHh588EHjdUREhA4dOqSff/5ZLVq0kK+vr12LAwAAqG3atGmjGTNm6MEHH9TPP//s6HKACrnipxF5enrq2muvtUctAAAAdUK9evX066+/OroMoMJsCg8JCQk2Dzhr1qzLLgYAAKA2+fTTT63WLRaLjh07ptdee03dunVzUFXA5bMpPGzbts2mwUwm0xUVAwAAUJv07dvXat1kMqlp06a69dZbeT4WaiSbwsM333xT2XUAAADUOmVlZRe8dnJyclQ5wBXjpxcAAKASLVy4UNdcc408PDzk4eGha665Rm+++aajywIuS4VvmL7lllsueXnS119/fUUFAQAAVJZ58+bp5ZdfVnZ2tjp16qRXX31VUVFRF+2fm5urf//731qxYoV+//13tWzZUklJSerZs6dNnzdp0iTNmjVLo0aNUkxMjCQpNTVVTz75pLKysjR16lS77BdQVSocHjp37my1XlJSou3bt2vXrl2Kj4+3V10AAAB2tXz5ciUkJCg5OVnR0dFKSkpS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Wrlype69915J57/ov/baa4qOjpZ0PpC0b99eaWlpioqKuqy6Hn74YeP1VVddpblz5+q6665TQUGB6tevb1w94+fnZ3XPw59VpP7k5GS1atVK0vkv9FOnTr1obVlZWerSpYsiIyMlSSEhIRft6+PjIx8fH0nn78d4/fXX9dVXXykgIEBZWVlatGiRsrKyjHuSx44dq9WrV2vRokWaPn26DUeqfJd10ZTZbNbKlSu1e/duSdLVV1+t3r17X/JpdAAAALgyHi7Oypga57DProjrr7/e6sFjMTExmjlzpsxms3bv3q169eoZoUCSmjRporCwMOP7pSTVq1dP1113nbHerl07NWzYULt3777s8JCenq4pU6boxx9/1KlTp1RWVibp/Bf38PBwm8awtX5PT08jOEhSYGCgcYlUeYYPH6577rlHW7du1R133KG+ffsa4eRitm3bpoceekivvfaaunXrJknauXOnzGaz2rZta9W3qKhITZo0sWkfL6bC4WHfvn3q2bOnjh49akzXmpiYqODgYK1atcrqANlDSEiIDh06dEH7448/rnnz5ql79+5av3691bZHH31UycnJdq0DAADA0UwmU4UuHartnJycLrgXo7xZQf9QWFiouLg4xcXFadmyZWratKmysrIUFxen4uJiu9f319mZTCbTJe8dufPOO3Xo0CF9/vnnWrt2rW677TaNGDHiopMSZWdnq3fv3ho6dKiGDBlitBcUFMjZ2Vnp6ekX/HG/fv36V7BHl3HD9BNPPKFWrVrp8OHD2rp1q7Zu3aqsrCyFhobqiSeeuKJiyrN582YdO3bMWNauXStJxukgSRo2bJhVn5deesnudQAAAMB2mzZtslrfuHGj2rRpI2dnZ7Vv316lpaVWfU6ePKnMzEyrv/6XlpZazfCZmZmp3NxctW/fXpLUtGnTC25AvtRzEH7++WedPHlSM2bM0I033qh27dpdcCbA1dVV0vkrbS7G1vovR9OmTRUfH6933nlHSUlJWrBgQbn9zp07pz59+qhdu3aaNWuW1bYuXbrIbDbr+PHjat26tdUSEBBwRfVd1kPiXnrpJavZlJo0aaIZM2ZccAbAHpo2baqAgABj+eyzz9SqVSvdfPPNRh9PT0+rPg0aNLB7HQAAALBdVlaWEhISlJmZqffee0+vvvqqRo8eLUlq06aN+vTpo2HDhun777/Xjz/+qAcffFBBQUHq06ePMYaLi4tGjRqlTZs2KT09XYMGDdL1119vXLJ06623asuWLXr77be1d+9eTZ48Wbt27bpoTS1atJCrq6teffVV/fLLL/r00081bdo0qz4tW7aUyWTSZ599phMnTqigoOCCcWytv6ImTZqkTz75RPv27dNPP/2kzz77zAhKf/Xoo4/q8OHDmjt3rk6cOKHs7GxlZ2eruLhYbdu21YABAzRw4ECtWLFCBw4cUFpamhITE7Vq1arLrk+6jPDg5uam/Pz8C9oLCgqMpFZZiouL9c477+jhhx+2uoZu2bJl8vX11TXXXKPx48frzJkzlxynqKhIeXl5xlLe/gAAAODyDRw4UGfPnlVUVJRGjBih0aNH65FHHjG2L1q0SBEREbrrrrsUExMji8Wizz//3OpSH09PTz377LN64IEH1K1bN9WvX1/Lly83tsfFxWnixIl65plndN111yk/P18DBw68aE1NmzbV4sWL9eGHHyo8PFwzZsy44JKgoKAgPffccxo3bpz8/f01cuTIcseypf6KcnV11fjx49WxY0fddNNNcnZ21vvvv19u3/Xr1+vYsWMKDw9XYGCgsWzYsMGob+DAgXrqqacUFhamvn37avPmzWrRosVl1ydJJktFJu3V+R+ErVu3auHChUbq27Rpk4YNG6aIiAgtXrz4igq6lA8++EAPPPCA1Z3jCxYsUMuWLdWsWTPt2LFDzz77rKKiorRixYqLjjNlyhQ999xzF7QfPnxYzZs3r7T6gRqhsFD643rIggLJy8ux9dRyZ4pLjWkXKzoNIlDXHTlyRMHBwbX29/e5c+d04MABhYaGyt3d3dHlVEj37t3VuXPnK5rif/HixRozZoxyc3PtVhcuztaftwr/lpo7d67i4+MVExNjJKvS0lL17t1bc+bMufyKbbBw4ULdeeedRnCQZJVgO3TooMDAQN12223av3//RW/eHj9+vBISEoz1o0ePXvH1aQAAAEBtV+Hw0LBhQ33yySfau3evdu/eLZPJpPbt26t169aVUZ/h0KFD+uqrry55RkGSMWXWvn37Lhoe/ngy9h/y8vLsVygAAABQS132+fE2bdoYgeHP9x9UlkWLFsnPz0+9evW6ZL8/7rAPDAys9JoAAABwoXXr1l3xGIMGDdKgQYOueBzYV4VvmJbOXz50zTXXyN3dXe7u7rrm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4++hQFUgPFSivLw8+fj46PTp02rQoIGjy6n1ON5Vi+NdtTjeVYvjXbU43kDNwQ3TAAAAAGxCeAAAAABgE8JDJXJzc9PkyZPl5ubm6FLqBI531eJ4Vy2Od9XieFctjjdQc3DPAwAAAACbcOYBAAAAgE0IDwAAAABsQngAAAAAYBPCAwAAAACbEB4q0bx58xQSEiJ3d3dFR0crLS3N0SXVCt9++63uvvtuNWvWTCaTSStXrrTabrFYNGnSJAUGBsrDw0OxsbHau3evY4qt4RITE3XdddfJ29tbfn5+6tu3rzIzM636nDt3TiNGjFCTJk1Uv3593XPPPcrJyXFQxTXf/Pnz1bFjRzVo0EANGjRQTEyMvvjiC2M7x7vyzJgxQyaTSWPGjDHaON72NWXKFJlMJqulXbt2xnaON1D9ER4qyfLly5WQkKDJkydr69at6tSpk+Li4nT8+HFHl1bjFRYWqlOnTpo3b16521966SXNnTtXycnJ2rRpk7y8vBQXF6dz585VcaU13/r16zVixAht3LhRa9euVUlJie644w4VFhYafZ588kn97//+rz788EOtX79ev/76q/75z386sOqarXnz5poxY4bS09O1ZcsW3XrrrerTp49++uknSRzvyrJ582a9/vrr6tixo1U7x9v+rr76ah07dsxYvv/+e2MbxxuoASyoFFFRUZYRI0YY62az2dKsWTNLYmKiA6uqfSRZPv74Y2O9rKzMEhAQYHn55ZeNttzcXIubm5vlvffec0CFtcvx48ctkizr16+3WCznj62Li4vlww8/NPrs3r3bIsmSmprqqDJrnUaNGlnefPNNjnclyc/Pt7Rp08aydu1ay80332wZPXq0xWLh57syTJ482dKpU6dyt3G8gZqBMw+VoLi4WOnp6YqNjTXanJycFBsbq9TUVAdWVvsdOHBA2dnZVsfex8dH0dHRHHs7OH36tCSpcePGkqT09HSVlJRYHe927dqpRYsWHG87MJvNev/991VYWKiYmBiOdyUZMWKEevXqZXVcJX6+K8vevXvVrFkzXXXVVRowYICysrIkcbyBmqKeowuojX777TeZzWb5+/tbtfv7++vnn392UFV1Q3Z2tiSVe+z/2IbLU1ZWpjFjxqhbt2665pprJJ0/3q6urmrYsKFVX473ldm5c6diYmJ07tw51a9fXx9//LHCw8O1fft2jredvf/++9q6das2b958wTZ+vu0vOjpaixcvVlhYmI4dO6bnnntON954o3bt2sXxBmoIwgMAm4wYMUK7du2yuj4ZlSMsLEzbt2/X6dOn9d///lfx8fFav369o8uqdQ4fPqzRo0dr7dq1cnd3d3Q5dcKdd95pvO7YsaOio6PVsmVLffDBB/Lw8HBgZQBsxWVLlcDX11fOzs4XzBCRk5OjgIAAB1VVN/xxfDn29jVy5Eh99tln+uabb9S8eXOjPSAgQMXFxcrNzbXqz/G+Mq6urmrdurUiIiKUmJioTp06ac6cORxvO0tPT9fx48d17bXXql69eqpXr57Wr1+vuXPnql69evL39+d4V7KGDRuqbdu22rdvHz/fQA1BeKgErq6uioiIUEpKitFWVlamlJQUxcTEOLCy2i80NFQBAQFWxz4vL0+bNm3i2F8Gi8WikSNH6uOPP9bXX3+t0NBQq+0RERFycXGxOt6ZmZnKysrieNtRWVmZioqKON52dtttt2nnzp3avn27sURGRmrAgAHGa4535SooKND+/fsVGBjIzzdQQ3DZUiVJSEhQfHy8IiMjFRUVpaSkJBUWFmrw4MGOLq3GKygo0L59+4z1AwcOaPv27WrcuLFatGihMWPG6Pnnn1ebNm0UGhqqiRMnqlmzZurbt6/jiq6hRowYoXfffVeffPKJvL29jeuOfXx85OHhIR8fHw0ZMkQJCQlq3LixGjRooFGjRikmJkbXX3+9g6uvmcaPH68777xTLVq0UH5+vt59912tW7dOa9as4Xjbmbe3t3H/zh+8vLzUpEkTo53jbV9jx47V3XffrZYtW+rXX3/V5MmT5ezsrPvvv5+fb6CmcPR0T7XZq6++amnRooXF1dXVEhUVZdm4caOjS6oVvvnmG4ukC5b4+HiLxXJ+utaJEyda/P39LW5ubpbbbrvNkpmZ6diia6jyjrMky6JFi4w+Z8+etTz++OOWRo0aWTw9PS3/+Mc/LMeOHXNc0TXcww8/bGnZsqXF1dXV0rRpU8ttt91m+fLLL43tHO/K9eepWi0Wjre99evXzxIYGGhxdXW1BAUFWfr162fZt2+fsZ3jDVR/JovFYnFQbgEAAABQg3DPAwAAAACbEB4AAAAA2ITwAAAAAMAmhAcAAAAANiE8AAAAALAJ4QEAAACATQgPAAAAAGxCeAAAAABgE8IDANhg3bp1MplMys3NdXQpAAA4DE+YBoC/6N69uzp37qykpCSjrbi4WL///rv8/f1lMpkcVxwAAA7EmQcAdUZJScllv9fV1VUBAQEEBwBAnUZ4AGB3+fn5GjBggLy8vBQYGKjZs2ere/fuGjNmjCSpqKhIY8eOVVBQkLy8vBQdHa1169YZ71+8eLEaNmyoNWvWqH379qpfv7569OihY8eOWX3Om2++qfbt28vd3V3t2rXTf/7zH2PbwYMHZTKZtHz5ct18881yd3fXsmXLdPLkSd1///0KCgqSp6enOnTooPfee89436BBg7R+/XrNmTNHJpNJJpNJBw8eLPeypY8++khXX3213NzcFBISopkzZ1rVFxISounTp+vhhx+Wt7e3WrRooQULFtjvQAMAUMUIDwDsLiEhQT/88IM+/fRTrV27Vt999522bt1qbB85cqRSU1P1/vvva8eOHbr33nvVo0cP7d271+hz5swZvfLKK1q6dKm+/fZbZWVlaezYscb2ZcuWadKkSXrhhRe0e/duTZ8+XRMnTtSSJUusahk3bpxGjx6t3bt3Ky4uTufOnVNERIRWrVqlXbt26ZFHHtFDDz2ktLQ0SdKcOXMUExOjYcOG6dixYzp27JiCg4Mv2Mf09HTdd9996t+/v3bu3KkpU6Zo4sSJWrx4sVW/mTNnKjIyUtu2bdPjjz+u4cOHKzMz0x6HGQCAqmcBADvKy8uzuLi4WD788EOjLTc31+Lp6WkZPXq05dChQxZnZ2fL0aNHrd532223WcaPH2+xWCyWRYsWWSRZ9u3bZ2yfN2+exd/f31hv1aqV5d1337UaY9q0aZaYmBiLxWKxHDhwwCLJkpSU9Lc19+rVy/LUU08Z6zfffLNl9OjRVn2++eYbiyTLqVOnLBaLxfLAAw9Ybr/9dqs+Tz/9tCU8PNxYb9mypeXBBx801svKyix+fn6W+fPn/21NAABUR/UcnF0A1DK//PKLSkpKFBUVZbT5+PgoLCxMkrRz506ZzWa1bdvW6n1FRUVq0qSJse7p6alWrVoZ64GBgTp+/LgkqbCwUPv379eQIUM0bNgwo09paal8fHysxo2MjLRaN5vNmj59uj744AMdPXpUxcXFKioqkqenZ4X2c/fu3erTp49VW7du3ZSUlCSz2SxnZ2dJUseOHY3tJpNJAQEBxn4AAFDTEB4AVKmCggI5OzsrPT3d+IL9h/r16xuvXVxcrLaZTCZZ/v/kcAUFBZKkN954Q9HR0Vb9/jqml5eX1frLL7+sOXPmKCkpSR06dJCXl5fGjBmj4uLiK9uxiyhvP8rKyirlswAAqGyEBwB2ddVVV8nFxUWbN29WixYtJEmnT5/Wnj17dNNNN6lLly4ym806fvy4brzxxsv6DH9/fzVr1ky//PKLBgwYUKH3/vDDD+rTp48efPBBSVJZWZn27Nmj8PBwo4+rq6vMZvMlx2nfvr1++OGHC8Zu27btBQEGAIDagvAAwK68vb0VHx+vp59+Wo0bN5afn58mT54sJycnmUwmtW3bVgMGDNDAgQM1c+ZMdenSRSdOnFBKSoo6duyoXr162fQ5zz33nJ544gn5+PioR48eKioq0pYtW3Tq1CklJCRc9H1t2rTRf//7X23YsEGNGjXSrFmzlJOTYxUeQkJCtGnTJh08eFD169dX48aNLxjnqaee0nXXXadp06apX79+Sk1N1WuvvWY14xMAALUNsy0BsLtZs2YpJiZGd911l2JjY9WtWzdjSlVJWrRokQYOHKinnnpKYWFh6tu3r9WZClsMHTpUb775phYtWqQOHTro5ptv1uLFixUaGnrJ902YMEHXXnut4uLi1L17dwUEBKhv375WfcaOHStnZ2eFh4eradOmysrKumCca6+9Vh988IHef/99XXPNNZo0aZKmTp2qQYMG2bwPAADUNDxhGkClKywsVFBQkGbOnKkhQ4Y4uhwAAHCZuGwJgN1t27ZNP//8s6KionT69GlNnTpVki6YnQgAANQshAcAleKVV15RZmamXF1dFRERoe+++06+vr6OLgsAAFwBLlsCAAAAYBNumAYAAABgE8IDAAAAAJsQHgAAAADYhPAAAAAAwCaEBwAAAAA2ITwAAAAAsAnhAQAAAIBNCA8AAAAAbPL/AJVj/XxW73tFAAAAAElFTkSuQmCC", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import matplotlib.pyplot as plt\n", - "import tpot2\n", - "\n", - "population_size=60\n", - "initial_population_size=100\n", - "population_scaling = .5\n", - "generations_until_end_population = 50\n", - "\n", - "budget_range = [.3,1]\n", - "generations_until_end_budget=50\n", - "budget_scaling = .5\n", - "stepwise_steps = 5\n", - "\n", - "#Population and budget use stepwise\n", - "fig, ax1 = plt.subplots()\n", - "ax2 = ax1.twinx()\n", - "\n", - "interpolated_values_population = tpot2.utils.beta_interpolation(start=initial_population_size, end=population_size, n=generations_until_end_population, n_steps=stepwise_steps, scale=population_scaling)\n", - "interpolated_values_budget = tpot2.utils.beta_interpolation(start=budget_range[0], end=budget_range[1], n=generations_until_end_budget, n_steps=stepwise_steps, scale=budget_scaling)\n", - "ax1.step(list(range(len(interpolated_values_population))), interpolated_values_population, label=f\"population size\")\n", - "ax2.step(list(range(len(interpolated_values_budget))), interpolated_values_budget, label=f\"budget\", color='r')\n", - "ax1.set_xlabel(\"generation\")\n", - "ax1.set_ylabel(\"population size\")\n", - "ax2.set_ylabel(\"bugdet\")\n", - "\n", - "ax1.legend(loc='center left', bbox_to_anchor=(1.1, 0.4))\n", - "ax2.legend(loc='center left', bbox_to_anchor=(1.1, 0.3))\n", - "plt.show()\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2023-06-14 11:49:45,920 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-q6nay1zr', purging\n", - "2023-06-14 11:49:45,921 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-wni1q2fv', purging\n", - "2023-06-14 11:49:45,921 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-kunoeg91', purging\n", - "2023-06-14 11:49:45,921 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-40sr99dr', purging\n", - "2023-06-14 11:49:45,922 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-_b9njy2q', purging\n", - "2023-06-14 11:49:45,922 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-qft6b6eq', purging\n", - "2023-06-14 11:49:45,922 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-cgnqe8s_', purging\n", - "2023-06-14 11:49:45,922 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-mcu4ugbz', purging\n", - "2023-06-14 11:49:45,923 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-za145tll', purging\n", - "2023-06-14 11:49:45,923 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-3qdbpmh_', purging\n", - "2023-06-14 11:49:45,923 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-54ch2nwd', purging\n", - "2023-06-14 11:49:45,923 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-0zc92jfw', purging\n", - "2023-06-14 11:49:45,923 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-ub9p6598', purging\n", - "2023-06-14 11:49:45,924 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-8peu6bbu', purging\n", - "2023-06-14 11:49:45,924 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-1qp5dr29', purging\n", - "2023-06-14 11:49:45,924 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-hfh3inka', purging\n", - "2023-06-14 11:49:45,924 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-b1yl5oa1', purging\n", - "2023-06-14 11:49:45,924 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-epp_nuw_', purging\n", - "2023-06-14 11:49:45,925 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-q1qdqc8g', purging\n", - "2023-06-14 11:49:45,925 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-ek1b28f4', purging\n", - "2023-06-14 11:49:45,925 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-1806jovl', purging\n", - "2023-06-14 11:49:45,925 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-p0cuouft', purging\n", - "2023-06-14 11:49:45,925 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-wh0g6edf', purging\n", - "2023-06-14 11:49:45,926 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-1o1ws1of', purging\n", - "2023-06-14 11:49:45,926 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-_zh96wch', purging\n", - "2023-06-14 11:49:45,926 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-wd9vzw4h', purging\n", - "2023-06-14 11:49:45,926 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-jy7obwb9', purging\n", - "2023-06-14 11:49:45,926 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-f6ildiiw', purging\n", - "2023-06-14 11:49:45,927 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-4ddayasf', purging\n", - "2023-06-14 11:49:45,927 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-fn6vfz6t', purging\n", - "2023-06-14 11:49:45,927 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-eyc403bk', purging\n", - "2023-06-14 11:49:45,927 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-fr7a5y2z', purging\n", - "2023-06-14 11:49:45,927 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-9kejqh6s', purging\n", - "2023-06-14 11:49:45,927 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-_xaoujzg', purging\n", - "2023-06-14 11:49:45,928 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-zimc_s51', purging\n", - "2023-06-14 11:49:45,928 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-vtsv2zit', purging\n", - "2023-06-14 11:49:45,928 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-hj0s47vd', purging\n", - "2023-06-14 11:49:45,928 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-dpti5p3r', purging\n", - "2023-06-14 11:49:45,928 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-4cplddft', purging\n", - "2023-06-14 11:49:45,929 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-poszaeet', purging\n", - "2023-06-14 11:49:45,929 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-cjx6kkgn', purging\n", - "2023-06-14 11:49:45,929 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-u096a9iq', purging\n", - "2023-06-14 11:49:45,929 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-0k3omqwi', purging\n", - "2023-06-14 11:49:45,929 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-zk0s6ywn', purging\n", - "2023-06-14 11:49:45,930 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-bwn757sx', purging\n", - "2023-06-14 11:49:45,930 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-2nu35fgy', purging\n", - "2023-06-14 11:49:45,930 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-w6b4di6m', purging\n", - "2023-06-14 11:49:45,930 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-asj0iobm', purging\n", - "2023-06-14 11:49:45,930 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-gxxzxsyi', purging\n", - "2023-06-14 11:49:45,931 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-qa8099ky', purging\n", - "2023-06-14 11:49:45,931 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-_uypy41h', purging\n", - "2023-06-14 11:49:45,931 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-a4ujlka7', purging\n", - "2023-06-14 11:49:45,931 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-dwhz05x3', purging\n", - "2023-06-14 11:49:45,931 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-kgug_o6d', purging\n", - "2023-06-14 11:49:45,932 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-rnbpw5ka', purging\n", - "2023-06-14 11:49:45,932 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-i52qfiid', purging\n", - "2023-06-14 11:49:45,932 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-_5el2wab', purging\n", - "2023-06-14 11:49:45,932 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-mqhhdxip', purging\n", - "2023-06-14 11:49:45,932 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-i6xplvqh', purging\n", - "2023-06-14 11:49:45,933 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-_dmc4eb5', purging\n", - "2023-06-14 11:49:45,933 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-mok5p0dw', purging\n", - "2023-06-14 11:49:45,933 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-ugwiqoc3', purging\n", - "2023-06-14 11:49:45,933 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-e97he6cf', purging\n", - "2023-06-14 11:49:45,933 - distributed.diskutils - INFO - Found stale lock file and directory '/tmp/dask-worker-space/worker-an5jredd', purging\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "total time: 17.05474090576172\n" - ] - } - ], - "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 and a max depth of 6.\n", - "\n", - "import tpot2\n", - "import sklearn\n", - "import sklearn.datasets\n", - "import numpy as np\n", - "import time\n", - "import tpot2\n", - "import pandas as pd\n", - "import numpy as np\n", - "from sklearn.linear_model import LogisticRegression\n", - "import sklearn\n", - "\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", - "\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", - "est.fit(X, y)\n", - "print(f\"total time: {time.time()-start}\")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Tutorial on early termination of evaluating CV scores.\n", - "\n", - "We can further reduce computational load by terminating the evaluation of individual pipelines early if the first few CV scores are not promising. Note that this is different than early stopping of the full algorithm. In this section we will cover:\n", - "\n", - "`threshold_evaluation_early_stop`\n", - "\n", - "`threshold_evaluation_scaling`\n", - "\n", - "`min_history_threshold`\n", - "\n", - "`selection_evaluation_early_stop`\n", - "\n", - "`selection_evaluation_scaling`\n", - "\n", - "Threshold early stopping uses previous scores to identify and terminate the cross validation evaluation of poorly performing pipelines. We calculate the percentile scores from the previously evaluated pipelines. A pipeline must reach the given percentile each fold for the next to be evaluated, otherwise the pipeline is discarded.\n", - "\n", - "The `threshold_evaluation_early_stop` parameter is a list that specifies the starting and ending percentiles to use as a threshold for the evaluation early stopping. W The `threshold_evaluation_scaling` parameter is a float that controls the rate at which the threshold moves from the start to end percentile. The `min_history_threshold` parameter specifies the minimum number of previous scores needed before using threshold early stopping. This ensures that the algorithm has enough historical data to make an informed decision about when to stop evaluating pipelines.\n", - "\n", - "Selection early stopping uses a selection algorithm after each fold to select which algorithms will be evaluated for the next fold. For example, after evaluating 100 individuals on fold 1, we may want to only evaluate the best 50 for the remaining folds.\n", - "\n", - "The `selection_evaluation_early_stop` parameter is a list that specifies the lower and upper percentage of the population size to select each round of CV. This is used to determine which individuals to evaluate in the next generation. The `selection_evaluation_scaling` parameter is a float that controls the rate at which the selection threshold moves from the start to end percentile.\n", - "\n", - "By manipulating these parameters, we can control how the algorithm selects individuals to evaluate in the next generation and when to stop evaluating pipelines that are not performing well.\n", - "\n", - "In practice, the values of these parameters will depend on the specific problem and the available computational resources. \n", - "\n", - "In the following sections, we will show you how to set and manipulate these parameters using Python code in a Jupyter Notebook. We will also provide examples of how these parameters can affect the performance of the algorithm." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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Qz169Gi46gAAAG6hXkFm3Lhxkr5+1tLUqVOrrQsJCVH37t31H//xHw1WHAAAwK3UK8hUVVVJknr06KHc3Fx17NixUYoCAACoC78eGnnmzJmGrgMAAKDe/H76dUZGhjIyMnThwgXfkZobfv/73992YQAAAN/GryDz1FNPafny5RoyZIiioqK4OR4AALCFX0Fm06ZNSktL0+TJkxu6HgAAgDrz6z4y169f1/Dhwxu6FgAAgHrxK8g8/vjj2rZtW4MU8Pnnn+snP/mJOnTooLCwMPXv318fffSRb71lWVq6dKmioqIUFham+Ph4nTx5skF+NwAAMJtfp5auXbuml19+We+++64GDBigkJCQauvXrl1bp/18+eWXGjFihB5++GG9/fbb6tSpk06ePKl27dr5+qxZs0bPP/+8tm7dqh49emjJkiVKSEjQJ598otDQUH/KBwAAzYRfQebjjz/WwIEDJUlHjx6ttq4+F/6uXr1aMTExSk1N9bX9452BLcvSc889p8WLF2vs2LGSpFdeeUURERHavXu3JkyY4E/5gKSv//u6WnHzozZghivXmTsAfgaZffv2Ncgv//Of/6yEhAT9+Mc/VmZmpu644w796le/0s9//nNJX9+vpqioSPHx8b5t3G63hg4dqqysrBqDTHl5ucrLy32vvV5vg9SK5sWyLP1oU5byzn5pdykAgNvg1zUyN5w6dUrp6em6evWqpK8/HOrjr3/9qzZu3KjY2Filp6friSee0KxZs7R161ZJUlFRkSQpIiKi2nYRERG+dd+UkpIit9vtW2JiYuo7LPwTuFpRSYhpJoZ0a6ewkGC7ywBgE7+OyFy8eFGPPvqo9u3bJ4fDoZMnT6pnz56aPn262rVrV+fnLVVVVWnIkCF65plnJEn33nuvjh49qk2bNt30LKe6WrRokZKTk32vvV4vYQa39NHieLV08kFoqrCQYO5lBfwT8yvIzJ07VyEhISooKFDfvn197Y899piSk5PrHGSioqLUr1+/am19+/bVzp07JUmRkZGSpOLiYkVFRfn6FBcX+67R+SaXyyWXy1Wf4eCfXEtnsFo6/b7JNQDARn6dWnrnnXe0evVqdenSpVp7bGyszp49W+f9jBgxQidOnKjW9umnn6pbt26Svr7wNzIyUhkZGb71Xq9XOTk58ng8/pQOAACaEb/+DC0rK1PLli1vav/iiy/qdTRk7ty5Gj58uJ555hk9+uij+vDDD/Xyyy/r5ZdflvT1N6DmzJmjlStXKjY21vf16+joaI0bN86f0gEAQDPi1xGZBx98UK+88orvtcPhUFVVldasWaOHH364zvu57777tGvXLv3hD3/Q3XffrRUrVui5557TpEmTfH0WLFigmTNnasaMGbrvvvt0+fJl7dmzh3vIAAAAOaz6ftVIX987ZtSoURo0aJDee+89/eAHP9CxY8f0xRdf6ODBg+rVq1dj1OoXr9crt9ut0tJShYeH210OAsSV61+p39J0SdInyxO4RgYAAkxdP7/9OiJz991369NPP9UDDzygsWPHqqysTOPHj9fhw4cDKsQAAIDmze8/Q91ut37zm980ZC0AAAD14tcRmdTUVO3YseOm9h07dvhuZgcAANDY/AoyKSkp6tix403tnTt39t3cDgAAoLH5FWQKCgqqPdzxhm7duqmgoOC2iwIAAKgLv4JM586d9fHHH9/U/pe//EUdOnS47aIAAADqwq8gM3HiRM2aNUv79u1TZWWlKisr9d5772n27Nk1PpEaAACgMfj1raUVK1bob3/7m0aNGqUWLb7eRVVVlaZMmcI1MgAAoMnUO8hYlqWioiKlpaVp5cqVOnLkiMLCwtS/f3/fM5IAAACagl9Bpnfv3jp27JhiY2MVGxvbGHUBAAB8q3pfIxMUFKTY2FhdvHixMeoBAACoM78u9l21apXmz5+vo0ePNnQ9AAAAdebXxb5TpkzRlStXdM8998jpdCosLKza+i+++KJBigMAALgVv4LMc88918BlAAAA1J9fQWbq1KkNXQcAAEC9+XWNjCSdPn1aixcv1sSJE3XhwgVJ0ttvv61jx441WHEAAAC34leQyczMVP/+/ZWTk6M333xTly9flvT1IwqWLVvWoAUCAADUxq8gs3DhQq1cuVJ79+6V0+n0tY8cOVLZ2dkNVhwAAMCt+BVk8vPz9cMf/vCm9s6dO+vvf//7bRcFAABQF34FmbZt2+r8+fM3tR8+fFh33HHHbRcFAABQF34FmQkTJujXv/61ioqK5HA4VFVVpYMHD2revHmaMmVKQ9cIAABQI7+CzDPPPKO4uDjFxMTo8uXL6tevnx588EENHz5cixcvbugaAQAAauTXfWScTqc2b96spUuXKj8/X2VlZbr33nvVu3fvhq4PAACgVn4F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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import matplotlib.pyplot as plt\n", - "import tpot2\n", - "\n", - "threshold_evaluation_early_stop = [30, 90]\n", - "threshold_evaluation_scaling = .5\n", - "cv = 5\n", - "\n", - "#Population and budget use stepwise\n", - "fig, ax1 = plt.subplots()\n", - "\n", - "interpolated_values = tpot2.utils.beta_interpolation(start=threshold_evaluation_early_stop[0], end=threshold_evaluation_early_stop[-1], n=cv, n_steps=cv, scale=threshold_evaluation_scaling)\n", - "ax1.step(list(range(len(interpolated_values))), interpolated_values, label=f\"threshold\")\n", - "ax1.set_xlabel(\"fold\")\n", - "ax1.set_ylabel(\"percentile\")\n", - "#ax1.legend(loc='center left', bbox_to_anchor=(1.1, 0.4))\n", - "plt.show()\n" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "total time: 23.97980833053589\n" - ] - } - ], - "source": [ - "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=cv,\n", - " \n", - " # budget_range = [.3,1],\n", - " # generations_until_end_budget=4,\n", - "\n", - " threshold_evaluation_early_stop = threshold_evaluation_early_stop,\n", - " threshold_evaluation_scaling = threshold_evaluation_scaling,\n", - " verbose=0)\n", - "\n", - "\n", - "start = time.time()\n", - "est.fit(X, y)\n", - "print(f\"total time: {time.time()-start}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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P+uTpAsXLqhNmzZKTEz82X+hhJpQ2n/Xo0ePHmrRooXuuecebdmyJdhxfHbu3DlJUrNmza46J9T3oS9rlELzdeh2u7Vq1SqVl5crNTW11jmhvP98WZ8UmvtuwoQJuv/++2vsm9oEax9Sbnx06tQpud1uxcfHVxuPj4+/6jUKJSUltuYHkz/r69y5s1asWKE//elP+o//+A95PB7169dP//M//1Mfkevc1fZfWVmZLl68GKRUgdOiRQstXbpUH3zwgT744AMlJibq7rvvVmFhYbCj/SyPx6PJkyerf//+6tat21XnhdJr8Kd8XWOovQ737Nmjm2++WS6XS08++aQ+/PBDde3atda5obj/7Kwv1PadJK1atUqFhYXKycnxaX6w9uEN96ngCJzU1NRq/yLp16+funTpojfeeEMvvfRSEJPBF507d1bnzp29X/fr10+HDh3Sq6++qt/97ndBTPbzJkyYoL179+rzzz8PdpQ64+saQ+112LlzZxUVFencuXN6//33NWbMGG3evPmqBSDU2FlfqO2748ePa9KkSdqwYUODv/CZcuOj2NhYhYeHq7S0tNp4aWmpEhISat0mISHB1vxg8md9PxUREaGePXvq4MGDdRGx3l1t/zVu3FhRUVFBSlW3+vbt2+ALw9NPP63//M//1KeffqrWrVtfc24ovQb/Pztr/KmG/jp0Op3q0KGDJKl3797asWOHFi1apDfeeKPG3FDcf3bW91MNfd8VFBTo5MmT6tWrl3fM7Xbr008/1euvv67KykqFh4dX2yZY+5DTUj5yOp3q3bu38vPzvWMej0f5+flXPZ+amppabb4kbdiw4ZrnX4PFn/X9lNvt1p49e9SiRYu6ilmvQmn/BUpRUVGD3X+WZenpp5/Whx9+qE8++URt27b92W1CbR/6s8afCrXXocfjUWVlZa3fC7X9V5trre+nGvq+GzJkiPbs2aOioiLvo0+fPnr00UdVVFRUo9hIQdyHdXq5smFWrVpluVwua+XKldb+/futJ554wmrSpIlVUlJiWZZljR492po+fbp3/pYtW6ybbrrJeuWVV6wDBw5Y2dnZVkREhLVnz55gLeGa7K7vhRdesPLy8qxDhw5ZBQUF1sMPP2xFRkZa+/btC9YSrun8+fPWrl27rF27dlmSrAULFli7du2yjh07ZlmWZU2fPt0aPXq0d/7hw4et6Oho67nnnrMOHDhgLV682AoPD7dyc3ODtYRrsru+V1991Vq7dq313//939aePXusSZMmWWFhYdbHH38crCVc01NPPWXFxMRYmzZtsk6cOOF9VFRUeOeE+mvQnzWG0utw+vTp1ubNm60jR45Yu3fvtqZPn245HA5r/fr1lmWF/v6zu75Q2ndX89N3SzWUfUi5sem1116zbrnlFsvpdFp9+/a1vvzyS+/37rrrLmvMmDHV5v/hD3+wOnXqZDmdTuu2226z1q1bV8+J7bGzvsmTJ3vnxsfHW0OHDrUKCwuDkNo3V976/NPHlTWNGTPGuuuuu2ps06NHD8vpdFrt2rWz3n777XrP7Su765s7d67Vvn17KzIy0mrWrJl19913W5988klwwvugtrVJqrZPQv016M8aQ+l1+Nhjj1lt2rSxnE6n1bx5c2vIkCHeX/yWFfr7z+76QmnfXc1Py01D2YcOy7Ksuj02BAAAUH+45gYAABiFcgMAAIxCuQEAAEah3AAAAKNQbgAAgFEoNwAAwCiUGwAAYBTKDQAAMArlBkCDZ1mWnnjiCTVr1kwOh0NFRUXXnL9p0yY5HA6dPXv2qnNWrlypJk2aBDQngIaBTwUH0ODl5uZq5cqV2rRpk9q1a6fY2NhgRwLQgFFuADR4hw4dUosWLdSvX79gRwEQAjgtBaBB++d//mc988wzKi4ulsPhUFJSkiorKzVx4kTFxcUpMjJSAwYM0I4dO675PCtXrtQtt9yi6OhoPfDAAzp9+nQ9rQBAfaPcAGjQFi1apBdffFGtW7fWiRMntGPHDk2dOlUffPCB3nnnHRUWFqpDhw5KT0/XmTNnan2Obdu2ady4cXr66adVVFSkQYMG6eWXX67nlQCoL5QbAA1aTEyMGjVqpPDwcCUkJCg6OlpLlizRr3/9a913333q2rWrli1bpqioKC1fvrzW51i0aJH+4R/+QVOnTlWnTp00ceJEpaen1/NKANQXyg2AkHLo0CFdvnxZ/fv3945FRESob9++OnDgQK3bHDhwQCkpKdXGUlNT6zQngOCh3AAAAKNQbgCElPbt28vpdGrLli3escuXL2vHjh3q2rVrrdt06dJF27Ztqzb25Zdf1mlOAMHDW8EBhJS/+7u/01NPPaXnnntOzZo10y233KJ58+apoqJC48aNq3WbiRMnqn///nrllVc0fPhw5eXlKTc3t56TA6gvHLkBEHLmzJmjf/zHf9To0aPVq1cvHTx4UHl5eWratGmt8++8804tW7ZMixYtUnJystavX6+ZM2fWc2oA9cVhWZYV7BAAAACBwpEbAABgFMoNAAAwCuUGAAAYhXIDAACMQrkBAABGodwAAACjUG4AAIBRKDcAAMAolBsAAGAUyg0AADAK5QYAABjlfwHjX0kC8BXCEQAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import matplotlib.pyplot as plt\n", - "import tpot2\n", - "\n", - "selection_evaluation_early_stop = [.1, 1]\n", - "selection_evaluation_scaling = .5\n", - "cv = 5\n", - "\n", - "#Population and budget use stepwise\n", - "fig, ax1 = plt.subplots()\n", - "\n", - "interpolated_values = tpot2.utils.beta_interpolation(start=selection_evaluation_early_stop[0], end=selection_evaluation_early_stop[-1], n=cv, n_steps=cv, scale=selection_evaluation_scaling)\n", - "ax1.step(list(range(len(interpolated_values))), interpolated_values, label=f\"threshold\")\n", - "ax1.set_xlabel(\"fold\")\n", - "ax1.set_ylabel(\"percent to select\")\n", - "#ax1.legend(loc='center left', bbox_to_anchor=(1.1, 0.4))\n", - "plt.show()\n" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "total time: 23.03678798675537\n" - ] - } - ], - "source": [ - "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=cv,\n", - "\n", - " selection_evaluation_early_stop = selection_evaluation_early_stop,\n", - " selection_evaluation_scaling = selection_evaluation_scaling,\n", - "\n", - " verbose=0)\n", - "\n", - "\n", - "start = time.time()\n", - "est.fit(X, y)\n", - "print(f\"total time: {time.time()-start}\")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "All of the above methods can be used independently or simultaneously as done below:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([1.2, 3.4, 1. ])" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import math\n", - "np.array([1.2,3.4,1])" - ] - }, - { - "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/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": [ - "total time: 36.7981653213501\n" - ] - } - ], - "source": [ - "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=cv,\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", - " threshold_evaluation_early_stop = threshold_evaluation_early_stop,\n", - " threshold_evaluation_scaling = threshold_evaluation_scaling,\n", - "\n", - " selection_evaluation_early_stop = selection_evaluation_early_stop,\n", - " selection_evaluation_scaling = selection_evaluation_scaling,\n", - "\n", - " verbose=0)\n", - "\n", - "\n", - "start = time.time()\n", - "est.fit(X, y)\n", - "print(f\"total time: {time.time()-start}\")" - ] - } - ], - "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/7_dask_parallelization.ipynb b/Tutorial/7_dask_parallelization.ipynb index cbbfc28d..0a68448f 100644 --- a/Tutorial/7_dask_parallelization.ipynb +++ b/Tutorial/7_dask_parallelization.ipynb @@ -33,14 +33,91 @@ "name": "stderr", "output_type": "stream", "text": [ - "Evaluations: : 242it [02:01, 1.99it/s]\n" + "Generation: 20%|██ | 1/5 [00:01<00:07, 1.93s/it]" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "0.9995194086144522\n" + "Generation: 1\n", + "Best roc_auc_score score: 0.9976190476190476\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Generation: 40%|████ | 2/5 [00:04<00:06, 2.12s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Generation: 2\n", + "Best roc_auc_score score: 0.9976984126984128\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Generation: 60%|██████ | 3/5 [00:09<00:07, 3.80s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Generation: 3\n", + "Best roc_auc_score score: 1.0\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Generation: 80%|████████ | 4/5 [00:15<00:04, 4.46s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Generation: 4\n", + "Best roc_auc_score score: 1.0\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Generation: 100%|██████████| 5/5 [00:24<00:00, 4.99s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Generation: 5\n", + "Best roc_auc_score score: 1.0\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\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": [ + "1.0\n" ] } ], @@ -55,9 +132,29 @@ "\n", "if __name__==\"__main__\":\n", " scorer = sklearn.metrics.get_scorer('roc_auc_ovr')\n", - " X, y = sklearn.datasets.load_digits(return_X_y=True)\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 = 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_ovr\"],\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 = 3,\n", + " )\n", + " \n", + " \n", " est.fit(X_train, y_train)\n", " print(scorer(est, X_test, y_test))" ] @@ -85,14 +182,20 @@ "name": "stderr", "output_type": "stream", "text": [ - "Evaluations: : 224it [02:00, 1.86it/s]\n" + "Generation: 100%|██████████| 5/5 [00:11<00:00, 2.24s/it]\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/decomposition/_fastica.py:595: UserWarning: n_components is too large: it will be set to 8\n", + " warnings.warn(\n", + "/home/ribeirop/miniconda3/envs/tpot2env/lib/python3.10/site-packages/sklearn/decomposition/_fastica.py:128: ConvergenceWarning: FastICA did not converge. Consider increasing tolerance or the maximum number of iterations.\n", + " warnings.warn(\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.9996005895289903\n" + "1.0\n" ] } ], @@ -102,11 +205,31 @@ "import sklearn.datasets\n", "import numpy as np\n", "scorer = sklearn.metrics.get_scorer('roc_auc_ovr')\n", - "X, y = sklearn.datasets.load_digits(return_X_y=True)\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", "\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_ovr\"],\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))" ] @@ -193,28 +316,41 @@ "name": "stderr", "output_type": "stream", "text": [ - "Evaluations: : 119it [02:01, 1.02s/it]\n" + "Generation: 100%|██████████| 5/5 [00:13<00:00, 2.62s/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.9988827327847432\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2023-08-23 13:49:06,747 - distributed.nanny - WARNING - Worker process still alive after 3.1999992370605472 seconds, killing\n", - "2023-08-23 13:49:06,748 - distributed.nanny - WARNING - Worker process still alive after 3.199999694824219 seconds, killing\n", - "2023-08-23 13:49:06,748 - distributed.nanny - WARNING - Worker process still alive after 3.199999694824219 seconds, killing\n" + "1.0\n" ] } ], "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_ovr\"],\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", @@ -237,29 +373,23 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "Evaluations: : 132it [02:00, 1.10it/s]\n" + "Generation: 100%|██████████| 5/5 [00:16<00:00, 3.33s/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.999973663151898\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2023-08-23 13:51:14,527 - distributed.nanny - WARNING - Worker process still alive after 3.199999694824219 seconds, killing\n", - "2023-08-23 13:51:14,528 - distributed.nanny - WARNING - Worker process still alive after 3.19999984741211 seconds, killing\n" + "1.0\n" ] } ], @@ -271,7 +401,7 @@ "import numpy as np\n", "\n", "scorer = sklearn.metrics.get_scorer('roc_auc_ovr')\n", - "X, y = sklearn.datasets.load_digits(return_X_y=True)\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", "\n", "\n", @@ -283,7 +413,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_ovr\"],\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))" ] @@ -307,17 +455,9 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Sun Grid Engine is not installed. This example requires Sun Grid Engine to be installed.\n" - ] - } - ], + "outputs": [], "source": [ "from dask.distributed import Client, LocalCluster\n", "import sklearn\n", @@ -349,7 +489,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", @@ -377,7 +536,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/8_Genetic_Algorithm_Overview.ipynb b/Tutorial/8_Genetic_Algorithm_Overview.ipynb deleted file mode 100644 index 3abfd1e6..00000000 --- a/Tutorial/8_Genetic_Algorithm_Overview.ipynb +++ /dev/null @@ -1,501 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Objective functions can optionally take in step, budget, and generations.\n", - "\n", - "step - The same objective function will be run for #evaluation_early_stop_steps, the current step will be passed into the function as an interger. (This is useful for getting a single fold of cross validation for example).\n", - "\n", - "budget - A parameter that varies over the course of the generations. Gets passed into the objective function as a float between 0 and 1. If the budget of the previous evaluation is less than the current budget, it will get re-evaluated. Useful for using smaller datasets earlier in training.\n", - "\n", - "generations - an int corresponding to the current generation number.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Generation: 100%|██████████| 100/100 [04:05<00:00, 2.46s/it]\n" - ] - } - ], - "source": [ - "#knapsack problem\n", - "import numpy as np\n", - "import tpot2\n", - "import random\n", - "import matplotlib.pyplot as plt\n", - "from dask.distributed import Client, LocalCluster\n", - "\n", - "class SubsetSelector(tpot2.individual_representations.BaseIndividual):\n", - " def __init__( self,\n", - " values,\n", - " initial_set = None,\n", - " k=1, #step size for shuffling\n", - " ):\n", - "\n", - " if isinstance(values, int):\n", - " self.values = set(range(0,values))\n", - " else:\n", - " self.values = set(values)\n", - "\n", - "\n", - " if initial_set is None:\n", - " self.subsets = set(random.choices(values, k=k))\n", - " else:\n", - " self.subsets = set(initial_set)\n", - "\n", - " self.k = k\n", - "\n", - " self.mutation_list = [self._mutate_add, self._mutate_remove]\n", - " self.crossover_list = [self._crossover_swap]\n", - " \n", - "\n", - " def mutate(self, rng_=None):\n", - " mutation_list_copy = self.mutation_list.copy()\n", - " random.shuffle(mutation_list_copy)\n", - " for func in mutation_list_copy:\n", - " if func():\n", - " return True\n", - " return False\n", - "\n", - " def crossover(self, ind2, rng_=None):\n", - " crossover_list_copy = self.crossover_list.copy()\n", - " random.shuffle(crossover_list_copy)\n", - " for func in crossover_list_copy:\n", - " if func(ind2):\n", - " return True\n", - " return False\n", - "\n", - " def _mutate_add(self,):\n", - " not_included = list(self.values.difference(self.subsets))\n", - " if len(not_included) > 1:\n", - " self.subsets.update(random.sample(not_included, k=min(self.k, len(not_included))))\n", - " return True\n", - " else:\n", - " return False\n", - "\n", - " def _mutate_remove(self,):\n", - " if len(self.subsets) > 1:\n", - " self.subsets = self.subsets - set(random.sample(list(self.subsets), k=min(self.k, len(self.subsets)-1) ))\n", - "\n", - " def _crossover_swap(self, ss2):\n", - " diffs = self.subsets.symmetric_difference(ss2.subsets)\n", - "\n", - " if len(diffs) == 0:\n", - " return False\n", - " for v in diffs:\n", - " self.subsets.discard(v)\n", - " ss2.subsets.discard(v)\n", - " random.choice([self.subsets, ss2.subsets]).add(v)\n", - " \n", - " return True\n", - "\n", - " def unique_id(self):\n", - " return str(tuple(sorted(self.subsets)))\n", - "\n", - "def individual_generator():\n", - " while True:\n", - " yield SubsetSelector(values=np.arange(len(values)))\n", - "\n", - "\n", - "values = np.random.randint(200,size=100)\n", - "weights = np.random.random(200)*10\n", - "max_weight = 50\n", - "\n", - "def simple_objective(ind, **kwargs):\n", - " subset = np.array(list(ind.subsets))\n", - " if len(subset) == 0:\n", - " return 0, 0\n", - "\n", - " total_weight = np.sum(weights[subset])\n", - " total_value = np.sum(values[subset])\n", - "\n", - " if total_weight > max_weight:\n", - " total_value = 0\n", - "\n", - " return total_value, total_weight\n", - "\n", - "objective_names = [\"Value\", \"Weight\"]\n", - "objective_function_weights = [1,-1]\n", - "\n", - "\n", - "\n", - "evolver = tpot2.evolvers.BaseEvolver( individual_generator=individual_generator(), \n", - " objective_functions=[simple_objective],\n", - " objective_function_weights = objective_function_weights,\n", - " bigger_is_better = True,\n", - " population_size= 100,\n", - " objective_names = objective_names,\n", - " generations= 100,\n", - " n_jobs=1,\n", - " verbose = 1,\n", - "\n", - ")\n", - "\n", - "evolver.optimize()" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "best subset {0, 65, 2, 1, 38, 71, 40, 75, 44, 15, 48, 16, 85, 59, 60, 62}\n", - "Best value 2056.0, weight 48.6142482308331\n", - "\n", - "All results\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Selected IndexValueWeightParentsVariation_FunctionIndividualGenerationSubmitted TimestampCompleted TimestampPareto_Front
0(16,)75.02.054788NaNNaN<__main__.SubsetSelector object at 0x7faf86cfc...0.01.708121e+091.708121e+09NaN
1(13,)11.04.466691NaNNaN<__main__.SubsetSelector object at 0x7faf86635...0.01.708121e+091.708121e+09NaN
2(41,)50.06.249590NaNNaN<__main__.SubsetSelector object at 0x7faf84e87...0.01.708121e+091.708121e+09NaN
3(40,)35.00.992726NaNNaN<__main__.SubsetSelector object at 0x7faf83fdf...0.01.708121e+091.708121e+09NaN
4(77,)0.01.475988NaNNaN<__main__.SubsetSelector object at 0x7faf83ff1...0.01.708121e+091.708121e+09NaN
.................................
9995(0, 1, 5, 15, 60, 62, 65, 75, 83, 85)1323.017.180098((0, 5, 15, 60, 62, 65, 75, 83, 85), (0, 5, 15...ind_mutate<__main__.SubsetSelector object at 0x7faf695e5...99.01.708121e+091.708121e+091.0
9996(0, 8, 15, 60, 62, 65, 75, 96)916.018.695221((0, 15, 39, 40, 60, 62, 65, 75, 85), (0, 15, ...ind_mutate , ind_mutate , ind_crossover<__main__.SubsetSelector object at 0x7faf69fbf...99.01.708121e+091.708121e+09NaN
9997(0, 15, 57, 62, 65, 75, 85, 86, 92)967.015.581100((0, 15, 60, 62, 65, 75, 85, 86), (0, 15, 60, ...ind_mutate<__main__.SubsetSelector object at 0x7faf6b05a...99.01.708121e+091.708121e+09NaN
9998(0, 15, 21, 65, 75, 76)878.018.495023((0, 15, 60, 65, 75), (0, 15, 60, 65, 75))ind_mutate<__main__.SubsetSelector object at 0x7faf5eec0...99.01.708121e+091.708121e+09NaN
9999(0, 15, 39, 65, 75, 83, 85, 92)1054.014.423653((0, 2, 15, 39, 60, 65, 75, 83, 85), (0, 15, 3...ind_mutate , ind_mutate , ind_crossover<__main__.SubsetSelector object at 0x7faf6b36b...99.01.708121e+091.708121e+09NaN
\n", - "

10000 rows × 10 columns

\n", - "
" - ], - "text/plain": [ - " Selected Index Value Weight \\\n", - "0 (16,) 75.0 2.054788 \n", - "1 (13,) 11.0 4.466691 \n", - "2 (41,) 50.0 6.249590 \n", - "3 (40,) 35.0 0.992726 \n", - "4 (77,) 0.0 1.475988 \n", - "... ... ... ... \n", - "9995 (0, 1, 5, 15, 60, 62, 65, 75, 83, 85) 1323.0 17.180098 \n", - "9996 (0, 8, 15, 60, 62, 65, 75, 96) 916.0 18.695221 \n", - "9997 (0, 15, 57, 62, 65, 75, 85, 86, 92) 967.0 15.581100 \n", - "9998 (0, 15, 21, 65, 75, 76) 878.0 18.495023 \n", - "9999 (0, 15, 39, 65, 75, 83, 85, 92) 1054.0 14.423653 \n", - "\n", - " Parents \\\n", - "0 NaN \n", - "1 NaN \n", - "2 NaN \n", - "3 NaN \n", - "4 NaN \n", - "... ... \n", - "9995 ((0, 5, 15, 60, 62, 65, 75, 83, 85), (0, 5, 15... \n", - "9996 ((0, 15, 39, 40, 60, 62, 65, 75, 85), (0, 15, ... \n", - "9997 ((0, 15, 60, 62, 65, 75, 85, 86), (0, 15, 60, ... \n", - "9998 ((0, 15, 60, 65, 75), (0, 15, 60, 65, 75)) \n", - "9999 ((0, 2, 15, 39, 60, 65, 75, 83, 85), (0, 15, 3... \n", - "\n", - " Variation_Function \\\n", - "0 NaN \n", - "1 NaN \n", - "2 NaN \n", - "3 NaN \n", - "4 NaN \n", - "... ... \n", - "9995 ind_mutate \n", - "9996 ind_mutate , ind_mutate , ind_crossover \n", - "9997 ind_mutate \n", - "9998 ind_mutate \n", - "9999 ind_mutate , ind_mutate , ind_crossover \n", - "\n", - " Individual Generation \\\n", - "0 <__main__.SubsetSelector object at 0x7faf86cfc... 0.0 \n", - "1 <__main__.SubsetSelector object at 0x7faf86635... 0.0 \n", - "2 <__main__.SubsetSelector object at 0x7faf84e87... 0.0 \n", - "3 <__main__.SubsetSelector object at 0x7faf83fdf... 0.0 \n", - "4 <__main__.SubsetSelector object at 0x7faf83ff1... 0.0 \n", - "... ... ... \n", - "9995 <__main__.SubsetSelector object at 0x7faf695e5... 99.0 \n", - "9996 <__main__.SubsetSelector object at 0x7faf69fbf... 99.0 \n", - "9997 <__main__.SubsetSelector object at 0x7faf6b05a... 99.0 \n", - "9998 <__main__.SubsetSelector object at 0x7faf5eec0... 99.0 \n", - "9999 <__main__.SubsetSelector object at 0x7faf6b36b... 99.0 \n", - "\n", - " Submitted Timestamp Completed Timestamp Pareto_Front \n", - "0 1.708121e+09 1.708121e+09 NaN \n", - "1 1.708121e+09 1.708121e+09 NaN \n", - "2 1.708121e+09 1.708121e+09 NaN \n", - "3 1.708121e+09 1.708121e+09 NaN \n", - "4 1.708121e+09 1.708121e+09 NaN \n", - "... ... ... ... \n", - "9995 1.708121e+09 1.708121e+09 1.0 \n", - "9996 1.708121e+09 1.708121e+09 NaN \n", - "9997 1.708121e+09 1.708121e+09 NaN \n", - "9998 1.708121e+09 1.708121e+09 NaN \n", - "9999 1.708121e+09 1.708121e+09 NaN \n", - "\n", - "[10000 rows x 10 columns]" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "final_population_results = evolver.population.evaluated_individuals\n", - "final_population_results.reset_index(inplace=True)\n", - "final_population_results = final_population_results.rename(columns = {'index':'Selected Index'})\n", - "\n", - "best_idx = final_population_results[\"Value\"].idxmax()\n", - "best_individual = final_population_results.loc[best_idx]['Individual']\n", - "print(\"best subset\", best_individual.subsets)\n", - "print(\"Best value {0}, weight {1}\".format(final_population_results.loc[best_idx, \"Value\"],final_population_results.loc[best_idx, \"Weight\"]))\n", - "print()\n", - "\n", - "print(\"All results\")\n", - "final_population_results" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "from scipy.stats import binned_statistic_2d\n", - "\n", - "y = final_population_results[\"Value\"]\n", - "x = final_population_results[\"Weight\"]\n", - "c = final_population_results[\"Generation\"]\n", - "\n", - "x_bins = np.linspace(0, 100, 100)\n", - "y_bins = np.linspace(0, 3000, 100)\n", - "\n", - "ret = binned_statistic_2d(x, y, c, statistic=np.mean, bins=[x_bins, y_bins])\n", - "\n", - "fig, ax1 = plt.subplots(1, 1, figsize=(12, 4))\n", - "\n", - "im = ax1.imshow(ret.statistic.T, origin='lower', extent=(0,100,0,3000), vmin=0, vmax=100, aspect=.03)\n", - "ax1.set_xlabel(\"Weight\")\n", - "ax1.set_ylabel(\"Value\")\n", - "ax1.set_title(\"Binned Average Generation\")\n", - "\n", - "cbar = fig.colorbar(im,)\n", - "cbar.set_label('Generation')\n", - "plt.tight_layout()" - ] - } - ], - "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.11.7" - }, - "orig_nbformat": 4, - "vscode": { - "interpreter": { - "hash": "7fe1fe9ef32cd5efd76326a08046147513534f0dd2318301a1a96ae9071c1c4e" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/Tutorial/8_SH_and_early_termination.ipynb b/Tutorial/8_SH_and_early_termination.ipynb new file mode 100644 index 00000000..26f08e49 --- /dev/null +++ b/Tutorial/8_SH_and_early_termination.ipynb @@ -0,0 +1,365 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Welcome to this Jupyter Notebook tutorial parameters relating to computational resources. In this tutorial, we will cover the following parameters:\n", + "\n", + "`population_size`\n", + "\n", + "`initial_population_size`\n", + "\n", + "`population_scaling`\n", + "\n", + "`generations_until_end_population`\n", + "\n", + "`budget_range`\n", + "\n", + "`generations_until_end_budget`\n", + "\n", + "`budget_scaling`\n", + "\n", + "`stepwise_steps`\n", + "\n", + "Population size is the number of individuals evaluated each generation. Budget refers to the proportion of data to sample. By manipulating these parameters, we can control how quickly the budget increases and how population size changes over time. Most often, this will be used to start the algorithm by evaluating a large number of pipelines on small subsets of the data to quickly narrow now best models, before later getting a better estimate with larger samples on fewer datasets. This can reduce overall computational cost by not spending as much time evaluating poor performing pipelines.\n", + "\n", + "`population_size` determines the number of individuals to evalaute each generation. Sometimes we may want to evaluate more or fewer individuals in the earlier generations. The `initial_population_size` parameter specifies the starting size of the population. The population size will gradually move from `initial_population_size` to `population_size` over the course of `generations_until_end_population` generations. `population_scaling` dictates how fast that scaling takes place. The interpolation over `generations_until_end_population` is done stepwise with the number of steps specified by `stepwise_steps`.\n", + "\n", + "The same process goes for the budget scaling. \n", + "\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The following cell illustrates how the population size and budget change over time with the given settings." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import tpot2\n", + "\n", + "population_size=60\n", + "initial_population_size=100\n", + "population_scaling = .5\n", + "generations_until_end_population = 50\n", + "\n", + "budget_range = [.3,1]\n", + "generations_until_end_budget=50\n", + "budget_scaling = .5\n", + "stepwise_steps = 5\n", + "\n", + "#Population and budget use stepwise\n", + "fig, ax1 = plt.subplots()\n", + "ax2 = ax1.twinx()\n", + "\n", + "interpolated_values_population = tpot2.utils.beta_interpolation(start=initial_population_size, end=population_size, n=generations_until_end_population, n_steps=stepwise_steps, scale=population_scaling)\n", + "interpolated_values_budget = tpot2.utils.beta_interpolation(start=budget_range[0], end=budget_range[1], n=generations_until_end_budget, n_steps=stepwise_steps, scale=budget_scaling)\n", + "ax1.step(list(range(len(interpolated_values_population))), interpolated_values_population, label=f\"population size\")\n", + "ax2.step(list(range(len(interpolated_values_budget))), interpolated_values_budget, label=f\"budget\", color='r')\n", + "ax1.set_xlabel(\"generation\")\n", + "ax1.set_ylabel(\"population size\")\n", + "ax2.set_ylabel(\"bugdet\")\n", + "\n", + "ax1.legend(loc='center left', bbox_to_anchor=(1.1, 0.4))\n", + "ax2.legend(loc='center left', bbox_to_anchor=(1.1, 0.3))\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "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 and a max depth of 6.\n", + "\n", + "import tpot2\n", + "import sklearn\n", + "import sklearn.datasets\n", + "import numpy as np\n", + "import time\n", + "import tpot2\n", + "import pandas as pd\n", + "import numpy as np\n", + "from sklearn.linear_model import LogisticRegression\n", + "import sklearn\n", + "\n", + "X, y = sklearn.datasets.load_iris(return_X_y=True)\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_ovr\"],\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 = 3,\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_jobs=30,\n", + " )\n", + "\n", + "\n", + "\n", + "start = time.time()\n", + "est.fit(X, y)\n", + "print(f\"total time: {time.time()-start}\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Tutorial on early termination of evaluating CV scores.\n", + "\n", + "We can further reduce computational load by terminating the evaluation of individual pipelines early if the first few CV scores are not promising. Note that this is different than early stopping of the full algorithm. In this section we will cover:\n", + "\n", + "`threshold_evaluation_early_stop`\n", + "\n", + "`threshold_evaluation_scaling`\n", + "\n", + "`min_history_threshold`\n", + "\n", + "`selection_evaluation_early_stop`\n", + "\n", + "`selection_evaluation_scaling`\n", + "\n", + "Threshold early stopping uses previous scores to identify and terminate the cross validation evaluation of poorly performing pipelines. We calculate the percentile scores from the previously evaluated pipelines. A pipeline must reach the given percentile each fold for the next to be evaluated, otherwise the pipeline is discarded.\n", + "\n", + "The `threshold_evaluation_early_stop` parameter is a list that specifies the starting and ending percentiles to use as a threshold for the evaluation early stopping. W The `threshold_evaluation_scaling` parameter is a float that controls the rate at which the threshold moves from the start to end percentile. The `min_history_threshold` parameter specifies the minimum number of previous scores needed before using threshold early stopping. This ensures that the algorithm has enough historical data to make an informed decision about when to stop evaluating pipelines.\n", + "\n", + "Selection early stopping uses a selection algorithm after each fold to select which algorithms will be evaluated for the next fold. For example, after evaluating 100 individuals on fold 1, we may want to only evaluate the best 50 for the remaining folds.\n", + "\n", + "The `selection_evaluation_early_stop` parameter is a list that specifies the lower and upper percentage of the population size to select each round of CV. This is used to determine which individuals to evaluate in the next generation. The `selection_evaluation_scaling` parameter is a float that controls the rate at which the selection threshold moves from the start to end percentile.\n", + "\n", + "By manipulating these parameters, we can control how the algorithm selects individuals to evaluate in the next generation and when to stop evaluating pipelines that are not performing well.\n", + "\n", + "In practice, the values of these parameters will depend on the specific problem and the available computational resources. \n", + "\n", + "In the following sections, we will show you how to set and manipulate these parameters using Python code in a Jupyter Notebook. We will also provide examples of how these parameters can affect the performance of the algorithm." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import tpot2\n", + "\n", + "threshold_evaluation_early_stop = [30, 90]\n", + "threshold_evaluation_scaling = .5\n", + "cv = 5\n", + "\n", + "#Population and budget use stepwise\n", + "fig, ax1 = plt.subplots()\n", + "\n", + "interpolated_values = tpot2.utils.beta_interpolation(start=threshold_evaluation_early_stop[0], end=threshold_evaluation_early_stop[-1], n=cv, n_steps=cv, scale=threshold_evaluation_scaling)\n", + "ax1.step(list(range(len(interpolated_values))), interpolated_values, label=f\"threshold\")\n", + "ax1.set_xlabel(\"fold\")\n", + "ax1.set_ylabel(\"percentile\")\n", + "#ax1.legend(loc='center left', bbox_to_anchor=(1.1, 0.4))\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "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", + " search_space = graph_search_space,\n", + " n_jobs=32,\n", + " cv=cv,\n", + " \n", + " # budget_range = [.3,1],\n", + " # generations_until_end_budget=4,\n", + "\n", + " threshold_evaluation_early_stop = threshold_evaluation_early_stop,\n", + " threshold_evaluation_scaling = threshold_evaluation_scaling,\n", + " verbose=0)\n", + "\n", + "\n", + "start = time.time()\n", + "est.fit(X, y)\n", + "print(f\"total time: {time.time()-start}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import tpot2\n", + "\n", + "selection_evaluation_early_stop = [.1, 1]\n", + "selection_evaluation_scaling = .5\n", + "cv = 5\n", + "\n", + "#Population and budget use stepwise\n", + "fig, ax1 = plt.subplots()\n", + "\n", + "interpolated_values = tpot2.utils.beta_interpolation(start=selection_evaluation_early_stop[0], end=selection_evaluation_early_stop[-1], n=cv, n_steps=cv, scale=selection_evaluation_scaling)\n", + "ax1.step(list(range(len(interpolated_values))), interpolated_values, label=f\"threshold\")\n", + "ax1.set_xlabel(\"fold\")\n", + "ax1.set_ylabel(\"percent to select\")\n", + "#ax1.legend(loc='center left', bbox_to_anchor=(1.1, 0.4))\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "\n", + "\n", + "est = tpot2.TPOTEstimator( \n", + " generations=5,\n", + " scorers=['roc_auc_ovr'],\n", + " scorers_weights=[1],\n", + " classification=True,\n", + " search_space = graph_search_space,\n", + " n_jobs=32,\n", + " cv=cv,\n", + "\n", + " selection_evaluation_early_stop = selection_evaluation_early_stop,\n", + " selection_evaluation_scaling = selection_evaluation_scaling,\n", + "\n", + " verbose=0)\n", + "\n", + "\n", + "start = time.time()\n", + "est.fit(X, y)\n", + "print(f\"total time: {time.time()-start}\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "All of the above methods can be used independently or simultaneously as done below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import math\n", + "np.array([1.2,3.4,1])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "est = tpot2.TPOTEstimator( \n", + " generations=5,\n", + " scorers=['roc_auc_ovr'],\n", + " scorers_weights=[1],\n", + " classification=True,\n", + " search_space = graph_search_space,\n", + " n_jobs=32,\n", + " cv=cv,\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", + " threshold_evaluation_early_stop = threshold_evaluation_early_stop,\n", + " threshold_evaluation_scaling = threshold_evaluation_scaling,\n", + "\n", + " selection_evaluation_early_stop = selection_evaluation_early_stop,\n", + " selection_evaluation_scaling = selection_evaluation_scaling,\n", + "\n", + " verbose=0)\n", + "\n", + "\n", + "start = time.time()\n", + "est.fit(X, y)\n", + "print(f\"total time: {time.time()-start}\")" + ] + } + ], + "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.14" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "7fe1fe9ef32cd5efd76326a08046147513534f0dd2318301a1a96ae9071c1c4e" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Tutorial/9_Genetic_Algorithm_Overview.ipynb b/Tutorial/9_Genetic_Algorithm_Overview.ipynb new file mode 100644 index 00000000..9c931057 --- /dev/null +++ b/Tutorial/9_Genetic_Algorithm_Overview.ipynb @@ -0,0 +1,500 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Objective functions can optionally take in step, budget, and generations.\n", + "\n", + "step - The same objective function will be run for #evaluation_early_stop_steps, the current step will be passed into the function as an interger. (This is useful for getting a single fold of cross validation for example).\n", + "\n", + "budget - A parameter that varies over the course of the generations. Gets passed into the objective function as a float between 0 and 1. If the budget of the previous evaluation is less than the current budget, it will get re-evaluated. Useful for using smaller datasets earlier in training.\n", + "\n", + "generations - an int corresponding to the current generation number.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Generation: 100%|██████████| 100/100 [03:43<00:00, 2.23s/it]\n" + ] + } + ], + "source": [ + "#knapsack problem\n", + "import numpy as np\n", + "import tpot2\n", + "import random\n", + "import matplotlib.pyplot as plt\n", + "from dask.distributed import Client, LocalCluster\n", + "\n", + "class SubsetSelector(tpot2.individual.BaseIndividual):\n", + " def __init__( self,\n", + " values,\n", + " initial_set = None,\n", + " k=1, #step size for shuffling\n", + " ):\n", + "\n", + " if isinstance(values, int):\n", + " self.values = set(range(0,values))\n", + " else:\n", + " self.values = set(values)\n", + "\n", + "\n", + " if initial_set is None:\n", + " self.subsets = set(random.choices(values, k=k))\n", + " else:\n", + " self.subsets = set(initial_set)\n", + "\n", + " self.k = k\n", + "\n", + " self.mutation_list = [self._mutate_add, self._mutate_remove]\n", + " self.crossover_list = [self._crossover_swap]\n", + " \n", + "\n", + " def mutate(self, rng=None):\n", + " mutation_list_copy = self.mutation_list.copy()\n", + " random.shuffle(mutation_list_copy)\n", + " for func in mutation_list_copy:\n", + " if func():\n", + " return True\n", + " return False\n", + "\n", + " def crossover(self, ind2, rng=None):\n", + " crossover_list_copy = self.crossover_list.copy()\n", + " random.shuffle(crossover_list_copy)\n", + " for func in crossover_list_copy:\n", + " if func(ind2):\n", + " return True\n", + " return False\n", + "\n", + " def _mutate_add(self,):\n", + " not_included = list(self.values.difference(self.subsets))\n", + " if len(not_included) > 1:\n", + " self.subsets.update(random.sample(not_included, k=min(self.k, len(not_included))))\n", + " return True\n", + " else:\n", + " return False\n", + "\n", + " def _mutate_remove(self,):\n", + " if len(self.subsets) > 1:\n", + " self.subsets = self.subsets - set(random.sample(list(self.subsets), k=min(self.k, len(self.subsets)-1) ))\n", + "\n", + " def _crossover_swap(self, ss2):\n", + " diffs = self.subsets.symmetric_difference(ss2.subsets)\n", + "\n", + " if len(diffs) == 0:\n", + " return False\n", + " for v in diffs:\n", + " self.subsets.discard(v)\n", + " ss2.subsets.discard(v)\n", + " random.choice([self.subsets, ss2.subsets]).add(v)\n", + " \n", + " return True\n", + "\n", + " def unique_id(self):\n", + " return str(tuple(sorted(self.subsets)))\n", + "\n", + "def individual_generator():\n", + " while True:\n", + " yield SubsetSelector(values=np.arange(len(values)))\n", + "\n", + "\n", + "values = np.random.randint(200,size=100)\n", + "weights = np.random.random(200)*10\n", + "max_weight = 50\n", + "\n", + "def simple_objective(ind, **kwargs):\n", + " subset = np.array(list(ind.subsets))\n", + " if len(subset) == 0:\n", + " return 0, 0\n", + "\n", + " total_weight = np.sum(weights[subset])\n", + " total_value = np.sum(values[subset])\n", + "\n", + " if total_weight > max_weight:\n", + " total_value = 0\n", + "\n", + " return total_value, total_weight\n", + "\n", + "objective_names = [\"Value\", \"Weight\"]\n", + "objective_function_weights = [1,-1]\n", + "\n", + "\n", + "\n", + "evolver = tpot2.evolvers.BaseEvolver( individual_generator=individual_generator(), \n", + " objective_functions=[simple_objective],\n", + " objective_function_weights = objective_function_weights,\n", + " bigger_is_better = True,\n", + " population_size= 100,\n", + " objective_names = objective_names,\n", + " generations= 100,\n", + " n_jobs=1,\n", + " verbose = 1,\n", + "\n", + ")\n", + "\n", + "evolver.optimize()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "best subset {6, 10, 11, 17, 24, 25, 29, 30, 32, 37, 43, 45, 49, 50, 53, 57, 59, 66, 67, 76, 82, 91, 99}\n", + "Best value 2891.0, weight 49.1782782587545\n", + "\n", + "All results\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Selected IndexValueWeightParentsVariation_FunctionIndividualGenerationSubmitted TimestampCompleted TimestampEval ErrorPareto_Front
0(24,)145.02.946778NaNNaN<__main__.SubsetSelector object at 0x735f11b77...0.01.719625e+091.719625e+09NoneNaN
1(99,)4.03.345873NaNNaN<__main__.SubsetSelector object at 0x735f11b77...0.01.719625e+091.719625e+09NoneNaN
2(28,)62.06.965614NaNNaN<__main__.SubsetSelector object at 0x735f11b77...0.01.719625e+091.719625e+09NoneNaN
3(86,)108.04.322944NaNNaN<__main__.SubsetSelector object at 0x735f12734...0.01.719625e+091.719625e+09NoneNaN
4(36,)148.01.660910NaNNaN<__main__.SubsetSelector object at 0x735f12734...0.01.719625e+091.719625e+09NoneNaN
....................................
9995(0, 10, 11, 17, 24, 30, 32, 37, 43, 45, 49, 53...0.058.255546((0, 10, 11, 17, 24, 30, 32, 37, 43, 45, 49, 5...ind_mutate<__main__.SubsetSelector object at 0x735f04c5d...99.01.719625e+091.719625e+09NoneNaN
9996(10, 29, 48, 76)516.010.002974((10, 17, 29, 76), (10, 17, 29, 76))ind_mutate<__main__.SubsetSelector object at 0x735f04c5e...99.01.719625e+091.719625e+09NoneNaN
9997(2, 10, 17, 25, 29, 43, 50, 53, 68, 76)1101.05.322915((2, 10, 17, 25, 29, 43, 50, 53, 76), (2, 10, ...ind_mutate<__main__.SubsetSelector object at 0x735f04c5e...99.01.719625e+091.719625e+09NoneNaN
9998(2, 10, 17, 20, 25, 29, 43, 76)910.011.552131((2, 10, 17, 25, 29, 43, 76), (2, 10, 17, 25, ...ind_mutate<__main__.SubsetSelector object at 0x735f04c5e...99.01.719625e+091.719625e+09NoneNaN
9999(0, 10, 11, 17, 25, 29, 43, 49, 50, 53, 57, 59...1967.019.677724((0, 10, 11, 17, 25, 29, 30, 43, 49, 50, 53, 5...ind_mutate<__main__.SubsetSelector object at 0x735f04c5e...99.01.719625e+091.719625e+09NoneNaN
\n", + "

10000 rows × 11 columns

\n", + "
" + ], + "text/plain": [ + " Selected Index Value Weight \\\n", + "0 (24,) 145.0 2.946778 \n", + "1 (99,) 4.0 3.345873 \n", + "2 (28,) 62.0 6.965614 \n", + "3 (86,) 108.0 4.322944 \n", + "4 (36,) 148.0 1.660910 \n", + "... ... ... ... \n", + "9995 (0, 10, 11, 17, 24, 30, 32, 37, 43, 45, 49, 53... 0.0 58.255546 \n", + "9996 (10, 29, 48, 76) 516.0 10.002974 \n", + "9997 (2, 10, 17, 25, 29, 43, 50, 53, 68, 76) 1101.0 5.322915 \n", + "9998 (2, 10, 17, 20, 25, 29, 43, 76) 910.0 11.552131 \n", + "9999 (0, 10, 11, 17, 25, 29, 43, 49, 50, 53, 57, 59... 1967.0 19.677724 \n", + "\n", + " Parents Variation_Function \\\n", + "0 NaN NaN \n", + "1 NaN NaN \n", + "2 NaN NaN \n", + "3 NaN NaN \n", + "4 NaN NaN \n", + "... ... ... \n", + "9995 ((0, 10, 11, 17, 24, 30, 32, 37, 43, 45, 49, 5... ind_mutate \n", + "9996 ((10, 17, 29, 76), (10, 17, 29, 76)) ind_mutate \n", + "9997 ((2, 10, 17, 25, 29, 43, 50, 53, 76), (2, 10, ... ind_mutate \n", + "9998 ((2, 10, 17, 25, 29, 43, 76), (2, 10, 17, 25, ... ind_mutate \n", + "9999 ((0, 10, 11, 17, 25, 29, 30, 43, 49, 50, 53, 5... ind_mutate \n", + "\n", + " Individual Generation \\\n", + "0 <__main__.SubsetSelector object at 0x735f11b77... 0.0 \n", + "1 <__main__.SubsetSelector object at 0x735f11b77... 0.0 \n", + "2 <__main__.SubsetSelector object at 0x735f11b77... 0.0 \n", + "3 <__main__.SubsetSelector object at 0x735f12734... 0.0 \n", + "4 <__main__.SubsetSelector object at 0x735f12734... 0.0 \n", + "... ... ... \n", + "9995 <__main__.SubsetSelector object at 0x735f04c5d... 99.0 \n", + "9996 <__main__.SubsetSelector object at 0x735f04c5e... 99.0 \n", + "9997 <__main__.SubsetSelector object at 0x735f04c5e... 99.0 \n", + "9998 <__main__.SubsetSelector object at 0x735f04c5e... 99.0 \n", + "9999 <__main__.SubsetSelector object at 0x735f04c5e... 99.0 \n", + "\n", + " Submitted Timestamp Completed Timestamp Eval Error Pareto_Front \n", + "0 1.719625e+09 1.719625e+09 None NaN \n", + "1 1.719625e+09 1.719625e+09 None NaN \n", + "2 1.719625e+09 1.719625e+09 None NaN \n", + "3 1.719625e+09 1.719625e+09 None NaN \n", + "4 1.719625e+09 1.719625e+09 None NaN \n", + "... ... ... ... ... \n", + "9995 1.719625e+09 1.719625e+09 None NaN \n", + "9996 1.719625e+09 1.719625e+09 None NaN \n", + "9997 1.719625e+09 1.719625e+09 None NaN \n", + "9998 1.719625e+09 1.719625e+09 None NaN \n", + "9999 1.719625e+09 1.719625e+09 None NaN \n", + "\n", + "[10000 rows x 11 columns]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "final_population_results = evolver.population.evaluated_individuals\n", + "final_population_results.reset_index(inplace=True)\n", + "final_population_results = final_population_results.rename(columns = {'index':'Selected Index'})\n", + "\n", + "best_idx = final_population_results[\"Value\"].idxmax()\n", + "best_individual = final_population_results.loc[best_idx]['Individual']\n", + "print(\"best subset\", best_individual.subsets)\n", + "print(\"Best value {0}, weight {1}\".format(final_population_results.loc[best_idx, \"Value\"],final_population_results.loc[best_idx, \"Weight\"]))\n", + "print()\n", + "\n", + "print(\"All results\")\n", + "final_population_results" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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Jv7+/jh//ryS999//5m6zKRi2fivTp06mD17Nl588UWcOXPG7Ot2796NDz/8EEeOHMH169fx008/oXfv3kr+wIEDsXLlSumaiIgIbN68WTlOTEzE8OHD8dtvv8HOzg59+vTBwoULpYVgjh8/jsjISBw6dAhVq1bF8OHDMW6cvH9KaVhzrpnBUQ0ldSXXSyp3NUM9blPpHynveo6aF5fhLeVNufykkg6rFKek/82Qp3rtfUKdPXL1X38pr9MS9T7VTlcXAfM8Kw9+1turzyILLlo2Skm9O1/OMQzfNPpLzTyxxnR4xnCvGwDwiFNDYrs3yD/XN46oY6NOTmlsspyhPGd7k3nmMl4kzXh2kCmbT8l78hguvOZwRw2x3X8LicjWKsIibPlatWqFvXv3on79+ujWrRveeustnDhxAuvXr0erVq3uXYGRYtuV2MHBAdeuXbt3QQNpaWlo0qQJBg8ejKeffrrQMl26dMHy5cuVY51OJ+X369cP169fR1RUFHJycjBo0CC88sorWL16NQAgJSUFnTt3RqdOnbB06VKcOHECgwcPhpeXF1555RULPyUREZGZKtCYk3nz5iE19e54xmnTpiE1NRXff/896tSpY/FMHcCKzsmvv/4qHQshcP36dSxatAitW7e2qK6uXbuia9euRZbR6XTw8/MrNO/vv//G5s2bcejQITRrdvcpxCeffIJu3brho48+QkBAAFatWoXs7Gx89dVX0Gq1aNCgAWJiYjBv3jx2ToiIiO5TXl4e/v33XzRufPfptKur631vAmxx58Qw7ALcXSG2atWq6NChA+bOnVv4Rfdh586d8PHxQaVKldChQwe89957qFy5MgAgOjoaXl5eSscEADp16gQ7OzscOHAATz31FKKjo9GmTRtpL6CIiAh88MEHuH37drGtZmeNxFw19BTilKCk517uLJVLylBDKHF35PYadrJ7VTsh5dVxUYcUpeapT5yqO9+WyvXYM1xJO2iekvIq/aMuDnfNX63jVit5wbeaa9W08UJouU6mY6guqWr9eke1vU1fk3vavs+rs40c04KkvKJCNJ+Gfauk5334hEGO6Wv2/DLWZF7Ew5PkEwYzk7Ycn2HyOsPZQeYuFAdw4TWiB5kGVoR1bNIS27K3t0fnzp3x999/w8vLq1jqtGpvnZLSpUsXPP300wgODsb58+cxceJEdO3a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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from scipy.stats import binned_statistic_2d\n", + "\n", + "y = final_population_results[\"Value\"]\n", + "x = final_population_results[\"Weight\"]\n", + "c = final_population_results[\"Generation\"]\n", + "\n", + "x_bins = np.linspace(0, 100, 100)\n", + "y_bins = np.linspace(0, 3000, 100)\n", + "\n", + "ret = binned_statistic_2d(x, y, c, statistic=np.mean, bins=[x_bins, y_bins])\n", + "\n", + "fig, ax1 = plt.subplots(1, 1, figsize=(12, 4))\n", + "\n", + "im = ax1.imshow(ret.statistic.T, origin='lower', extent=(0,100,0,3000), vmin=0, vmax=100, aspect=.03)\n", + "ax1.set_xlabel(\"Weight\")\n", + "ax1.set_ylabel(\"Value\")\n", + "ax1.set_title(\"Binned Average Generation\")\n", + "\n", + "cbar = fig.colorbar(im,)\n", + "cbar.set_label('Generation')\n", + "plt.tight_layout()" + ] + } + ], + "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.14" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "7fe1fe9ef32cd5efd76326a08046147513534f0dd2318301a1a96ae9071c1c4e" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Tutorial/Example_Search_Spaces/imputation.ipynb b/Tutorial/Example_Search_Spaces/imputation.ipynb new file mode 100644 index 00000000..b6de7ef8 --- /dev/null +++ b/Tutorial/Example_Search_Spaces/imputation.ipynb @@ -0,0 +1,540 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Configuration(values={\n", + " 'add_indicator': False,\n", + " 'strategy': 'most_frequent',\n", + "})" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from ConfigSpace import ConfigurationSpace\n", + "from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal\n", + "import tpot2\n", + "from sklearn.impute import SimpleImputer\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": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
Pipeline(steps=[('simpleimputer',\n",
+       "                 SimpleImputer(add_indicator=True, strategy='median')),\n",
+       "                ('selectpercentile',\n",
+       "                 SelectPercentile(percentile=44.546578384975824)),\n",
+       "                ('featureagglomeration',\n",
+       "                 FeatureAgglomeration(linkage='complete', metric='cosine',\n",
+       "                                      n_clusters=102,\n",
+       "                                      pooling_func=<function median at 0x711a67539830>)),\n",
+       "                ('extratreesclassifier',\n",
+       "                 ExtraTreesClassifier(bootstrap=True, class_weight='balanced',\n",
+       "                                      max_features=0.9974817877523433,\n",
+       "                                      min_samples_leaf=8, min_samples_split=20,\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=[('simpleimputer',\n", + " SimpleImputer(add_indicator=True, strategy='median')),\n", + " ('selectpercentile',\n", + " SelectPercentile(percentile=44.546578384975824)),\n", + " ('featureagglomeration',\n", + " FeatureAgglomeration(linkage='complete', metric='cosine',\n", + " n_clusters=102,\n", + " pooling_func=)),\n", + " ('extratreesclassifier',\n", + " ExtraTreesClassifier(bootstrap=True, class_weight='balanced',\n", + " max_features=0.9974817877523433,\n", + " min_samples_leaf=8, min_samples_split=20,\n", + " n_jobs=1))])" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "imputation_node =tpot2.search_spaces.nodes.EstimatorNode(\n", + " method = SimpleImputer,\n", + " space = simple_imputer,\n", + ")\n", + "\n", + "impute_classifier_space = tpot2.search_spaces.pipelines.SequentialPipeline([\n", + " imputation_node,\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", + "\n", + "impute_classifier_space.generate().export_pipeline()" + ] + } + ], + "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/setup.py b/setup.py index 27b4a474..0a404280 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ def calculate_version(): ''', zip_safe=True, - install_requires=['numpy>=1.26.4', + install_requires=['numpy==1.26.4', 'scipy>=1.3.1', 'scikit-learn>=1.3.0', 'update_checker>=0.16', diff --git a/tox.ini b/tox.ini index 4e250aef..7177d0a7 100644 --- a/tox.ini +++ b/tox.ini @@ -27,4 +27,4 @@ commands = flake8 tpot2 basepython = python3.10 deps = -r{toxinidir}/requirements_dev.txt -commands = mypy tpot2 \ No newline at end of file +commands = mypy tpot2 diff --git a/tpot2/__init__.py b/tpot2/__init__.py index ddb8357a..62290884 100644 --- a/tpot2/__init__.py +++ b/tpot2/__init__.py @@ -2,17 +2,23 @@ #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 from . import tpot_estimator - +from . import old_config_utils from .tpot_estimator import TPOTClassifier, TPOTRegressor, TPOTEstimator, TPOTEstimatorSteadyState + +from update_checker import update_check +from ._version import __version__ +update_check("tpot2",__version__) \ No newline at end of file diff --git a/tpot2/_version.py b/tpot2/_version.py index 16e56e5d..bb1511da 100644 --- a/tpot2/_version.py +++ b/tpot2/_version.py @@ -1 +1 @@ -__version__ = '0.1.6a1' +__version__ = '0.1.7a0' diff --git a/tpot2/builtin_modules/__init__.py b/tpot2/builtin_modules/__init__.py index 75419820..7f825e66 100644 --- a/tpot2/builtin_modules/__init__.py +++ b/tpot2/builtin_modules/__init__.py @@ -1,9 +1,9 @@ from .feature_set_selector import FeatureSetSelector from .zero_count import ZeroCount -from .one_hot_encoder import OneHotEncoder from .column_one_hot_encoder import ColumnOneHotEncoder from .arithmetictransformer import ArithmeticTransformer from .arithmetictransformer import AddTransformer, mul_neg_1_Transformer, MulTransformer, SafeReciprocalTransformer, EQTransformer, NETransformer, GETransformer, GTTransformer, LETransformer, LTTransformer, MinTransformer, MaxTransformer, ZeroTransformer, OneTransformer, NTransformer -from .passthrough import Passthrough +from .passthrough import Passthrough, SkipTransformer from .imputer import ColumnSimpleImputer -from .selector_wrappers import RFE_ExtraTreesClassifier, SelectFromModel_ExtraTreesClassifier, RFE_ExtraTreesRegressor, SelectFromModel_ExtraTreesRegressor \ No newline at end of file +from .estimatortransformer import EstimatorTransformer +from .passkbinsdiscretizer import PassKBinsDiscretizer \ No newline at end of file diff --git a/tpot2/builtin_modules/column_one_hot_encoder.py b/tpot2/builtin_modules/column_one_hot_encoder.py index 4f3843bf..d3472b5c 100644 --- a/tpot2/builtin_modules/column_one_hot_encoder.py +++ b/tpot2/builtin_modules/column_one_hot_encoder.py @@ -37,13 +37,14 @@ def _X_selected(X, selected): class ColumnOneHotEncoder(BaseEstimator, TransformerMixin): - def __init__(self, columns='auto', drop=None, handle_unknown='error', sparse_output=False, min_frequency=None,max_categories=None): + def __init__(self, columns='auto', drop=None, handle_unknown='infrequent_if_exist', sparse_output=False, min_frequency=None,max_categories=None): ''' Parameters ---------- columns : str, list, default='auto' + Determines which columns to onehot encode with sklearn.preprocessing.OneHotEncoder. - 'auto' : Automatically select categorical features based on columns with less than 10 unique values - 'categorical' : Automatically select categorical features - 'numeric' : Automatically select numeric features diff --git a/tpot2/builtin_modules/estimatortransformer.py b/tpot2/builtin_modules/estimatortransformer.py new file mode 100644 index 00000000..839e679c --- /dev/null +++ b/tpot2/builtin_modules/estimatortransformer.py @@ -0,0 +1,121 @@ +from numpy import ndarray +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.model_selection import cross_val_predict +from sklearn.utils.validation import check_is_fitted +from sklearn.utils.metaestimators import available_if +import numpy as np +from sklearn.utils.validation import check_is_fitted + +class EstimatorTransformer(BaseEstimator, TransformerMixin): + def __init__(self, estimator, method='auto', passthrough=False, cross_val_predict_cv=0): + self.estimator = estimator + self.method = method + self.passthrough = passthrough + self.cross_val_predict_cv = cross_val_predict_cv + + def fit(self, X, y=None): + return self.estimator.fit(X, y) + + def transform(self, X): + if self.method == 'auto': + if hasattr(self.estimator, 'predict_proba'): + method = 'predict_proba' + elif hasattr(self.estimator, 'decision_function'): + method = 'decision_function' + elif hasattr(self.estimator, 'predict'): + method = 'predict' + else: + raise ValueError('Estimator has no valid method') + else: + method = self.method + + output = getattr(self.estimator, method)(X) + output=np.array(output) + + if len(output.shape) == 1: + output = output.reshape(-1,1) + + if self.passthrough: + return np.hstack((output, X)) + else: + return output + + + + def fit_transform(self, X, y=None): + self.estimator.fit(X,y) + + if self.method == 'auto': + if hasattr(self.estimator, 'predict_proba'): + method = 'predict_proba' + elif hasattr(self.estimator, 'decision_function'): + method = 'decision_function' + elif hasattr(self.estimator, 'predict'): + method = 'predict' + else: + raise ValueError('Estimator has no valid method') + else: + method = self.method + + if self.cross_val_predict_cv > 0: + output = cross_val_predict(self.estimator, X, y=y, cv=self.cross_val_predict_cv) + + else: + output = getattr(self.estimator, method)(X) + #reshape if needed + + if len(output.shape) == 1: + output = output.reshape(-1,1) + + output=np.array(output) + if self.passthrough: + return np.hstack((output, X)) + else: + return output + + def _estimator_has(attr): + '''Check if we can delegate a method to the underlying estimator. + First, we check the first fitted final estimator if available, otherwise we + check the unfitted final estimator. + ''' + return lambda self: (self.estimator is not None and + hasattr(self.estimator, attr) + ) + + @available_if(_estimator_has('predict')) + def predict(self, X, **predict_params): + check_is_fitted(self.estimator) + #X = check_array(X) + + preds = self.estimator.predict(X,**predict_params) + return preds + + @available_if(_estimator_has('predict_proba')) + def predict_proba(self, X, **predict_params): + check_is_fitted(self.estimator) + #X = check_array(X) + return self.estimator.predict_proba(X,**predict_params) + + @available_if(_estimator_has('decision_function')) + def decision_function(self, X, **predict_params): + check_is_fitted(self.estimator) + #X = check_array(X) + return self.estimator.decision_function(X,**predict_params) + + def __sklearn_is_fitted__(self): + """ + Check fitted status and return a Boolean value. + """ + return check_is_fitted(self.estimator) + + + # @property + # def _estimator_type(self): + # return self.estimator._estimator_type + + + + @property + def classes_(self): + """The classes labels. Only exist if the last step is a classifier.""" + return self.estimator._classes \ No newline at end of file diff --git a/tpot2/builtin_modules/one_hot_encoder.py b/tpot2/builtin_modules/one_hot_encoder.py deleted file mode 100644 index 565247fc..00000000 --- a/tpot2/builtin_modules/one_hot_encoder.py +++ /dev/null @@ -1,501 +0,0 @@ -# -*- coding: utf-8 -*- - -"""Copyright (c) 2015 The auto-sklearn developers. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - a. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - b. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - c. Neither the name of the auto-sklearn Developers nor the names of - its contributors may be used to endorse or promote products - derived from this software without specific prior written - permission. - - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH -DAMAGE. -""" - -import numpy as np -from scipy import sparse - -from sklearn.base import BaseEstimator, TransformerMixin -from sklearn.utils import check_array - - -SPARSE_ENCODINGS = { - 'OTHER': 1, - 'NAN': 2, -} - - -def auto_select_categorical_features(X, threshold=10): - """Make a feature mask of categorical features in X. - - Features with less than 10 unique values are considered categorical. - - Parameters - ---------- - X : array-like or sparse matrix, shape=(n_samples, n_features) - Dense array or sparse matrix. - - threshold : int - Maximum number of unique values per feature to consider the feature - to be categorical. - - Returns - ------- - feature_mask : array of booleans of size {n_features, } - """ - feature_mask = [] - - for column in range(X.shape[1]): - if sparse.issparse(X): - indptr_start = X.indptr[column] - indptr_end = X.indptr[column + 1] - unique = np.unique(X.data[indptr_start:indptr_end]) - else: - unique = np.unique(X[:, column]) - - feature_mask.append(len(unique) <= threshold) - - return feature_mask - - -def _X_selected(X, selected): - """Split X into selected features and other features""" - n_features = X.shape[1] - ind = np.arange(n_features) - sel = np.zeros(n_features, dtype=bool) - sel[np.asarray(selected)] = True - non_sel = np.logical_not(sel) - n_selected = np.sum(sel) - X_sel = X[:, ind[sel]] - X_not_sel = X[:, ind[non_sel]] - return X_sel, X_not_sel, n_selected, n_features - - -def _transform_selected(X, transform, selected, copy=True): - """Apply a transform function to portion of selected features. - - Parameters - ---------- - X : array-like or sparse matrix, shape=(n_samples, n_features) - Dense array or sparse matrix. - - transform : callable - A callable transform(X) -> X_transformed - - copy : boolean, optional - Copy X even if it could be avoided. - - selected: "all", "auto" or array of indices or mask - Specify which features to apply the transform to. - - Returns - ------- - X : array or sparse matrix, shape=(n_samples, n_features_new) - """ - if selected == "all": - return transform(X) - if len(selected) == 0: - return X - - X = check_array(X, accept_sparse='csc', force_all_finite=False) - - X_sel, X_not_sel, n_selected, n_features = _X_selected(X, selected) - - if n_selected == 0: - # No features selected. - return X - elif n_selected == n_features: - # All features selected. - return transform(X) - else: - X_sel = transform(X_sel) - - if sparse.issparse(X_sel) or sparse.issparse(X_not_sel): - return sparse.hstack((X_sel, X_not_sel), format='csr') - else: - return np.hstack((X_sel, X_not_sel)) - - -class OneHotEncoder(BaseEstimator, TransformerMixin): - """Encode categorical integer features using a one-hot aka one-of-K scheme. - - The input to this transformer should be a matrix of integers, denoting - the values taken on by categorical (discrete) features. The output will be - a sparse matrix were each column corresponds to one possible value of one - feature. It is assumed that input features take on values in the range - [0, n_values). - - This encoding is needed for feeding categorical data to many scikit-learn - estimators, notably linear models and SVMs with the standard kernels. - - Parameters - ---------- - - categorical_features: "all" or array of indices or mask - Specify what features are treated as categorical. - - - 'all': All features are treated as categorical. - - 'auto' (default): Select only features that have less than 10 unique values. - - array of indices: Array of categorical feature indices. - - mask: Array of length n_features and with dtype=bool. - - Non-categorical features are always stacked to the right of the matrix. - - dtype : number type, default=np.float - Desired dtype of output. - - sparse : boolean, default=True - Will return sparse matrix if set True else will return an array. - - threshold : int, default=10 - Maximum number of unique values per feature to consider the feature - to be categorical when categorical_features is 'auto'. - - minimum_fraction : float, default=None - Minimum fraction of unique values in a feature to consider the feature - to be categorical. - - Attributes - ---------- - `active_features_` : array - Indices for active features, meaning values that actually occur - in the training set. Only available when n_values is ``'auto'``. - - `feature_indices_` : array of shape (n_features,) - Indices to feature ranges. - Feature ``i`` in the original data is mapped to features - from ``feature_indices_[i]`` to ``feature_indices_[i+1]`` - (and then potentially masked by `active_features_` afterwards) - - `n_values_` : array of shape (n_features,) - Maximum number of values per feature. - - Examples - -------- - Given a dataset with three features and two samples, we let the encoder - find the maximum value per feature and transform the data to a binary - one-hot encoding. - - >>> from sklearn.preprocessing import OneHotEncoder - >>> enc = OneHotEncoder() - >>> enc.fit([[0, 0, 3], [1, 1, 0], [0, 2, 1], [1, 0, 2]]) # doctest: +ELLIPSIS - OneHotEncoder(categorical_features='all', dtype=<... 'float'>, - sparse=True, minimum_fraction=None) - >>> enc.n_values_ - array([2, 3, 4]) - >>> enc.feature_indices_ - array([0, 2, 5, 9]) - >>> enc.transform([[0, 1, 1]]).toarray() - array([[ 1., 0., 0., 1., 0., 0., 1., 0., 0.]]) - - See also - -------- - sklearn.feature_extraction.DictVectorizer : performs a one-hot encoding of - dictionary items (also handles string-valued features). - sklearn.feature_extraction.FeatureHasher : performs an approximate one-hot - encoding of dictionary items or strings. - """ - - def __init__(self, categorical_features='auto', dtype=np.float64, - sparse=True, minimum_fraction=None, threshold=10): - self.categorical_features = categorical_features - self.dtype = dtype - self.sparse = sparse - self.minimum_fraction = minimum_fraction - self.threshold = threshold - - def fit(self, X, y=None): - """Fit OneHotEncoder to X. - - Parameters - ---------- - X : array-like, shape=(n_samples, n_feature) - Input array of type int. - - Returns - ------- - self - """ - self.fit_transform(X) - return self - - def _matrix_adjust(self, X): - """Adjust all values in X to encode for NaNs and infinities in the data. - - Parameters - ---------- - X : array-like, shape=(n_samples, n_feature) - Input array of type int. - - Returns - ------- - X : array-like, shape=(n_samples, n_feature) - Input array without any NaNs or infinities. - """ - data_matrix = X.data if sparse.issparse(X) else X - - # Shift all values to specially encode for NAN/infinity/OTHER and 0 - # Old value New Value - # --------- --------- - # N (0..int_max) N + 3 - # np.NaN 2 - # infinity 2 - # *other* 1 - # - # A value of 0 is reserved, as that is specially handled in sparse - # matrices. - data_matrix += len(SPARSE_ENCODINGS) + 1 - data_matrix[~np.isfinite(data_matrix)] = SPARSE_ENCODINGS['NAN'] - - return X - - def _fit_transform(self, X): - """Assume X contains only categorical features. - - Parameters - ---------- - X : array-like or sparse matrix, shape=(n_samples, n_features) - Dense array or sparse matrix. - """ - X = self._matrix_adjust(X) - - X = check_array( - X, - accept_sparse='csc', - force_all_finite=False, - dtype=int - ) - - if X.min() < 0: - raise ValueError("X needs to contain only non-negative integers.") - - n_samples, n_features = X.shape - - # Remember which values should not be replaced by the value 'other' - if self.minimum_fraction is not None: - do_not_replace_by_other = [] - for column in range(X.shape[1]): - do_not_replace_by_other.append(list()) - - if sparse.issparse(X): - indptr_start = X.indptr[column] - indptr_end = X.indptr[column + 1] - unique = np.unique(X.data[indptr_start:indptr_end]) - colsize = indptr_end - indptr_start - else: - unique = np.unique(X[:, column]) - colsize = X.shape[0] - - for unique_value in unique: - if np.isfinite(unique_value): - if sparse.issparse(X): - indptr_start = X.indptr[column] - indptr_end = X.indptr[column + 1] - count = np.nansum(unique_value == - X.data[indptr_start:indptr_end]) - else: - count = np.nansum(unique_value == X[:, column]) - else: - if sparse.issparse(X): - indptr_start = X.indptr[column] - indptr_end = X.indptr[column + 1] - count = np.nansum(~np.isfinite( - X.data[indptr_start:indptr_end])) - else: - count = np.nansum(~np.isfinite(X[:, column])) - - fraction = float(count) / colsize - if fraction >= self.minimum_fraction: - do_not_replace_by_other[-1].append(unique_value) - - for unique_value in unique: - if unique_value not in do_not_replace_by_other[-1]: - if sparse.issparse(X): - indptr_start = X.indptr[column] - indptr_end = X.indptr[column + 1] - X.data[indptr_start:indptr_end][ - X.data[indptr_start:indptr_end] == - unique_value] = SPARSE_ENCODINGS['OTHER'] - else: - X[:, column][X[:, column] == unique_value] = SPARSE_ENCODINGS['OTHER'] - - self.do_not_replace_by_other_ = do_not_replace_by_other - - if sparse.issparse(X): - n_values = X.max(axis=0).toarray().flatten() + len(SPARSE_ENCODINGS) - else: - n_values = np.max(X, axis=0) + len(SPARSE_ENCODINGS) - - self.n_values_ = n_values - n_values = np.hstack([[0], n_values]) - indices = np.cumsum(n_values) - self.feature_indices_ = indices - - if sparse.issparse(X): - row_indices = X.indices - column_indices = [] - for i in range(len(X.indptr) - 1): - nbr = X.indptr[i+1] - X.indptr[i] - column_indices_ = [indices[i]] * nbr - column_indices_ += X.data[X.indptr[i]:X.indptr[i+1]] - column_indices.extend(column_indices_) - data = np.ones(X.data.size) - else: - column_indices = (X + indices[:-1]).ravel() - row_indices = np.repeat(np.arange(n_samples, dtype=np.int32), - n_features) - data = np.ones(n_samples * n_features) - - out = sparse.coo_matrix((data, (row_indices, column_indices)), - shape=(n_samples, indices[-1]), - dtype=self.dtype).tocsc() - - mask = np.array(out.sum(axis=0)).ravel() != 0 - active_features = np.where(mask)[0] - out = out[:, active_features] - self.active_features_ = active_features - return out.tocsr() if self.sparse else out.toarray() - - def fit_transform(self, X, y=None): - """Fit OneHotEncoder to X, then transform X. - - Equivalent to self.fit(X).transform(X), but more convenient and more - efficient. See fit for the parameters, transform for the return value. - - Parameters - ---------- - X : array-like or sparse matrix, shape=(n_samples, n_features) - Dense array or sparse matrix. - y: array-like {n_samples,} (Optional, ignored) - Feature labels - """ - - if self.categorical_features == "auto": - self.categorical_features_ = auto_select_categorical_features(X, threshold=self.threshold) - else: - self.categorical_features_ = self.categorical_features - - return _transform_selected( - X, - self._fit_transform, - self.categorical_features_, - copy=True - ) - - def _transform(self, X): - """Asssume X contains only categorical features. - - Parameters - ---------- - X : array-like or sparse matrix, shape=(n_samples, n_features) - Dense array or sparse matrix. - """ - X = self._matrix_adjust(X) - - X = check_array(X, accept_sparse='csc', force_all_finite=False, - dtype=int) - if X.min() < 0: - raise ValueError("X needs to contain only non-negative integers.") - n_samples, n_features = X.shape - - indices = self.feature_indices_ - if n_features != indices.shape[0] - 1: - raise ValueError("X has different shape than during fitting." - " Expected %d, got %d." - % (indices.shape[0] - 1, n_features)) - - # Replace all indicators which were below `minimum_fraction` in the - # training set by 'other' - if self.minimum_fraction is not None: - for column in range(X.shape[1]): - if sparse.issparse(X): - indptr_start = X.indptr[column] - indptr_end = X.indptr[column + 1] - unique = np.unique(X.data[indptr_start:indptr_end]) - else: - unique = np.unique(X[:, column]) - - for unique_value in unique: - if unique_value not in self.do_not_replace_by_other_[column]: - if sparse.issparse(X): - indptr_start = X.indptr[column] - indptr_end = X.indptr[column + 1] - X.data[indptr_start:indptr_end][ - X.data[indptr_start:indptr_end] == - unique_value] = SPARSE_ENCODINGS['OTHER'] - else: - X[:, column][X[:, column] == unique_value] = SPARSE_ENCODINGS['OTHER'] - - if sparse.issparse(X): - n_values_check = X.max(axis=0).toarray().flatten() + 1 - else: - n_values_check = np.max(X, axis=0) + 1 - - # Replace all indicators which are out of bounds by 'other' (index 0) - if (n_values_check > self.n_values_).any(): - # raise ValueError("Feature out of bounds. Try setting n_values.") - for i, n_value_check in enumerate(n_values_check): - if (n_value_check - 1) >= self.n_values_[i]: - if sparse.issparse(X): - indptr_start = X.indptr[i] - indptr_end = X.indptr[i+1] - X.data[indptr_start:indptr_end][X.data[indptr_start:indptr_end] >= self.n_values_[i]] = 0 - else: - X[:, i][X[:, i] >= self.n_values_[i]] = 0 - - if sparse.issparse(X): - row_indices = X.indices - column_indices = [] - for i in range(len(X.indptr) - 1): - nbr = X.indptr[i + 1] - X.indptr[i] - column_indices_ = [indices[i]] * nbr - column_indices_ += X.data[X.indptr[i]:X.indptr[i + 1]] - column_indices.extend(column_indices_) - data = np.ones(X.data.size) - else: - column_indices = (X + indices[:-1]).ravel() - row_indices = np.repeat(np.arange(n_samples, dtype=np.int32), - n_features) - data = np.ones(n_samples * n_features) - out = sparse.coo_matrix((data, (row_indices, column_indices)), - shape=(n_samples, indices[-1]), - dtype=self.dtype).tocsc() - - out = out[:, self.active_features_] - return out.tocsr() if self.sparse else out.toarray() - - def transform(self, X): - """Transform X using one-hot encoding. - - Parameters - ---------- - X : array-like or sparse matrix, shape=(n_samples, n_features) - Dense array or sparse matrix. - - Returns - ------- - X_out : sparse matrix if sparse=True else a 2-d array, dtype=int - Transformed input. - """ - return _transform_selected( - X, self._transform, - self.categorical_features_, - copy=True - ) diff --git a/tpot2/builtin_modules/passkbinsdiscretizer.py b/tpot2/builtin_modules/passkbinsdiscretizer.py new file mode 100644 index 00000000..6ca5a9b5 --- /dev/null +++ b/tpot2/builtin_modules/passkbinsdiscretizer.py @@ -0,0 +1,43 @@ +import pandas as pd +from sklearn.base import BaseEstimator, TransformerMixin +from sklearn.compose import ColumnTransformer +from sklearn.preprocessing import KBinsDiscretizer +import numpy as np + +def select_features(X, min_unique=10,): + + if isinstance(X, pd.DataFrame): + return [col for col in X.columns if len(X[col].unique()) > min_unique] + else: + return [i for i in range(X.shape[1]) if len(np.unique(X[:, i])) > min_unique] + +class PassKBinsDiscretizer(BaseEstimator, TransformerMixin): + """ + Same as sklearn.preprocessing.KBinsDiscretizer, but passes through columns that are not discretized due to having fewer than n_bins unique values instead of ignoring them. + """ + def __init__(self, n_bins=5, encode='onehot-dense', strategy='quantile', subsample='warn', random_state=None): + self.n_bins = n_bins + self.encode = encode + self.strategy = strategy + self.subsample = subsample + self.random_state = random_state + + def fit(self, X, y=None): + # Identify columns with more than n unique values + # Create a ColumnTransformer to select and discretize the chosen columns + self.selected_columns_ = select_features(X, min_unique=10) + if isinstance(X, pd.DataFrame): + self.not_selected_columns_ = [col for col in X.columns if col not in self.selected_columns_] + else: + self.not_selected_columns_ = [i for i in range(X.shape[1]) if i not in self.selected_columns_] + + enc = KBinsDiscretizer(n_bins=self.n_bins, encode=self.encode, strategy=self.strategy, subsample=self.subsample, random_state=self.random_state) + self.transformer = ColumnTransformer([ + ('discretizer', enc, self.selected_columns_), + ('passthrough', 'passthrough', self.not_selected_columns_) + ]) + self.transformer.fit(X) + return self + + def transform(self, X): + return self.transformer.transform(X) \ No newline at end of file diff --git a/tpot2/builtin_modules/passthrough.py b/tpot2/builtin_modules/passthrough.py index 3d82215f..654c632f 100644 --- a/tpot2/builtin_modules/passthrough.py +++ b/tpot2/builtin_modules/passthrough.py @@ -1,4 +1,5 @@ from sklearn.base import BaseEstimator, TransformerMixin +import numpy as np class Passthrough(TransformerMixin,BaseEstimator): @@ -7,3 +8,14 @@ def fit(self, X=None, y=None): def transform(self, X): return X + + +class SkipTransformer(TransformerMixin,BaseEstimator): + + def fit(self, X=None, y=None): + return self + + def transform(self, X): + #empty array of same shape as X + return np.array([]).reshape(X.shape[0],0) + diff --git a/tpot2/builtin_modules/selector_wrappers.py b/tpot2/builtin_modules/selector_wrappers.py deleted file mode 100644 index dd5c8718..00000000 --- a/tpot2/builtin_modules/selector_wrappers.py +++ /dev/null @@ -1,109 +0,0 @@ -import sklearn -from sklearn.ensemble import ExtraTreesClassifier, ExtraTreesRegressor -from sklearn.feature_selection import SelectFromModel -from sklearn.feature_selection import RFE - -class RFE_ExtraTreesClassifier(RFE): - def __init__(self, n_features_to_select=None, step=1, verbose=0, importance_getter='auto', n_estimators=100, *, criterion='gini', max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features='sqrt', max_leaf_nodes=None, min_impurity_decrease=0.0, bootstrap=False, oob_score=False, n_jobs=None, random_state=None, warm_start=False, class_weight=None, ccp_alpha=0.0, max_samples=None): - - self.n_estimators = n_estimators - self.criterion = criterion - self.max_depth = max_depth - self.min_samples_split = min_samples_split - self.min_samples_leaf = min_samples_leaf - self.min_weight_fraction_leaf = min_weight_fraction_leaf - self.max_features = max_features - self.max_leaf_nodes = max_leaf_nodes - self.min_impurity_decrease = min_impurity_decrease - self.bootstrap = bootstrap - self.oob_score = oob_score - self.n_jobs = n_jobs - self.random_state = random_state - self.verbose = verbose - self.warm_start = warm_start - self.class_weight = class_weight - self.ccp_alpha = ccp_alpha - self.max_samples = max_samples - - - estimator = ExtraTreesClassifier(n_estimators=n_estimators, criterion=criterion, max_depth=max_depth, min_samples_split=min_samples_split, min_samples_leaf=min_samples_leaf, min_weight_fraction_leaf=min_weight_fraction_leaf, max_features=max_features, max_leaf_nodes=max_leaf_nodes, min_impurity_decrease=min_impurity_decrease, bootstrap=bootstrap, oob_score=oob_score, n_jobs=n_jobs, random_state=random_state, verbose=verbose, warm_start=warm_start, class_weight=class_weight, ccp_alpha=ccp_alpha, max_samples=max_samples) - super().__init__(estimator=estimator, n_features_to_select=n_features_to_select, step=step, verbose=verbose, importance_getter=importance_getter) - - -class SelectFromModel_ExtraTreesClassifier(SelectFromModel): - def __init__(self, threshold=None, prefit=False, norm_order=1, SFM_max_features=None, importance_getter='auto', n_estimators=100, *, criterion='gini', max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features='sqrt', max_leaf_nodes=None, min_impurity_decrease=0.0, bootstrap=False, oob_score=False, n_jobs=None, random_state=None, verbose=0, warm_start=False, class_weight=None, ccp_alpha=0.0, max_samples=None): - - self.SFM_max_features = SFM_max_features - self.n_estimators = n_estimators - self.criterion = criterion - self.max_depth = max_depth - self.min_samples_split = min_samples_split - self.min_samples_leaf = min_samples_leaf - self.min_weight_fraction_leaf = min_weight_fraction_leaf - self.max_features = max_features - self.max_leaf_nodes = max_leaf_nodes - self.min_impurity_decrease = min_impurity_decrease - self.bootstrap = bootstrap - self.oob_score = oob_score - self.n_jobs = n_jobs - self.random_state = random_state - self.verbose = verbose - self.warm_start = warm_start - self.class_weight = class_weight - self.ccp_alpha = ccp_alpha - self.max_samples = max_samples - - estimator = ExtraTreesClassifier(n_estimators=n_estimators, criterion=criterion, max_depth=max_depth, min_samples_split=min_samples_split, min_samples_leaf=min_samples_leaf, min_weight_fraction_leaf=min_weight_fraction_leaf, max_features=max_features, max_leaf_nodes=max_leaf_nodes, min_impurity_decrease=min_impurity_decrease, bootstrap=bootstrap, oob_score=oob_score, n_jobs=n_jobs, random_state=random_state, verbose=verbose, warm_start=warm_start, class_weight=class_weight, ccp_alpha=ccp_alpha, max_samples=max_samples) - super().__init__(estimator=estimator, threshold=threshold, prefit=prefit, norm_order=norm_order, max_features=SFM_max_features, importance_getter=importance_getter) - - -class RFE_ExtraTreesRegressor(RFE): - def __init__(self, n_features_to_select=None, step=1, verbose=0, importance_getter='auto',n_estimators=100, *, criterion='squared_error', max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features=1.0, max_leaf_nodes=None, min_impurity_decrease=0.0, bootstrap=False, oob_score=False, n_jobs=None, random_state=None, warm_start=False, ccp_alpha=0.0, max_samples=None ) -> None: - - self.n_estimators = n_estimators - self.criterion = criterion - self.max_depth = max_depth - self.min_samples_split = min_samples_split - self.min_samples_leaf = min_samples_leaf - self.min_weight_fraction_leaf = min_weight_fraction_leaf - self.max_features = max_features - self.max_leaf_nodes = max_leaf_nodes - self.min_impurity_decrease = min_impurity_decrease - self.bootstrap = bootstrap - self.oob_score = oob_score - self.n_jobs = n_jobs - self.random_state = random_state - self.verbose = verbose - self.warm_start = warm_start - self.ccp_alpha = ccp_alpha - self.max_samples = max_samples - - - - estimator = ExtraTreesRegressor(n_estimators=n_estimators, criterion=criterion, max_depth=max_depth, min_samples_split=min_samples_split, min_samples_leaf=min_samples_leaf, min_weight_fraction_leaf=min_weight_fraction_leaf, max_features=max_features, max_leaf_nodes=max_leaf_nodes, min_impurity_decrease=min_impurity_decrease, bootstrap=bootstrap, oob_score=oob_score, n_jobs=n_jobs, random_state=random_state, verbose=verbose, warm_start=warm_start, ccp_alpha=ccp_alpha, max_samples=max_samples) - super().__init__(estimator=estimator, n_features_to_select=n_features_to_select, step=step, verbose=verbose, importance_getter=importance_getter) - -class SelectFromModel_ExtraTreesRegressor(SelectFromModel): - def __init__(self, threshold=None, prefit=False, norm_order=1, SFM_max_features=None, importance_getter='auto', n_estimators=100, *, criterion='squared_error', max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features=1.0, max_leaf_nodes=None, min_impurity_decrease=0.0, bootstrap=False, oob_score=False, n_jobs=None, random_state=None, verbose=0, warm_start=False, ccp_alpha=0.0, max_samples=None ): - - self.SFM_max_features = SFM_max_features - self.n_estimators = n_estimators - self.criterion = criterion - self.max_depth = max_depth - self.min_samples_split = min_samples_split - self.min_samples_leaf = min_samples_leaf - self.min_weight_fraction_leaf = min_weight_fraction_leaf - self.max_features = max_features - self.max_leaf_nodes = max_leaf_nodes - self.min_impurity_decrease = min_impurity_decrease - self.bootstrap = bootstrap - self.oob_score = oob_score - self.n_jobs = n_jobs - self.random_state = random_state - self.verbose = verbose - self.warm_start = warm_start - self.ccp_alpha = ccp_alpha - self.max_samples = max_samples - - estimator = ExtraTreesRegressor(n_estimators=n_estimators, criterion=criterion, max_depth=max_depth, min_samples_split=min_samples_split, min_samples_leaf=min_samples_leaf, min_weight_fraction_leaf=min_weight_fraction_leaf, max_features=max_features, max_leaf_nodes=max_leaf_nodes, min_impurity_decrease=min_impurity_decrease, bootstrap=bootstrap, oob_score=oob_score, n_jobs=n_jobs, random_state=random_state, verbose=verbose, warm_start=warm_start, ccp_alpha=ccp_alpha, max_samples=max_samples) - super().__init__(estimator=estimator, threshold=threshold, prefit=prefit, norm_order=norm_order, max_features=SFM_max_features, importance_getter=importance_getter) 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/autoqtl_builtins.py b/tpot2/config/autoqtl_builtins.py index d3cc8dfc..d649bacd 100644 --- a/tpot2/config/autoqtl_builtins.py +++ b/tpot2/config/autoqtl_builtins.py @@ -3,22 +3,19 @@ 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 +FeatureEncodingFrequencySelector_ConfigurationSpace = ConfigurationSpace( + space = { + 'threshold': Float("threshold", bounds=(0, .35)) + } +) -def make_FeatureEncodingFrequencySelector_config_dictionary(): - return {feature_encoding_frequency_selector.FeatureEncodingFrequencySelector: params_FeatureEncodingFrequencySelector} +# genetic_encoders.DominantEncoder : {}, +# genetic_encoders.RecessiveEncoder : {}, +# genetic_encoders.HeterosisEncoder : {}, +# genetic_encoders.UnderDominanceEncoder : {}, +# genetic_encoders.OverDominanceEncoder : {}, -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 06ed2507..49b714ac 100644 --- a/tpot2/config/classifiers.py +++ b/tpot2/config/classifiers.py @@ -1,270 +1,564 @@ -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 - +from ConfigSpace import ConfigurationSpace +from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal +from ConfigSpace import EqualsCondition, OrConjunction, NotEqualsCondition, InCondition +from ..search_spaces.nodes.estimator_node import NONE_SPECIAL_STRING, TRUE_SPECIAL_STRING, FALSE_SPECIAL_STRING import numpy as np +import sklearn + + +def get_LogisticRegression_ConfigurationSpace(random_state): + + dual = FALSE_SPECIAL_STRING + + space = {"solver":"saga", + "max_iter":1000, + "n_jobs":1, + "dual":dual, + } + + penalty = Categorical('penalty', ['l1', 'l2',"elasticnet"], default='l2') + C = Float('C', (0.01, 1e5), log=True) + l1_ratio = Float('l1_ratio', (0.0, 1.0)) + class_weight = Categorical('class_weight', [NONE_SPECIAL_STRING, 'balanced']) + + l1_ratio_condition = EqualsCondition(l1_ratio, penalty, 'elasticnet') + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + + cs = ConfigurationSpace(space) + cs.add_hyperparameters([penalty, C, l1_ratio, class_weight]) + cs.add_conditions([l1_ratio_condition]) + + return cs + + +def get_KNeighborsClassifier_ConfigurationSpace(n_samples): + return ConfigurationSpace( + + space = { + + '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']), + 'n_jobs': 1, + } + ) + +def get_BaggingClassifier_ConfigurationSpace(random_state): + space = { + 'n_estimators': Integer("n_estimators", bounds=(3, 100)), + 'max_samples': Float("max_samples", bounds=(0.1, 1.0)), + 'max_features': Float("max_features", bounds=(0.1, 1.0)), + + 'bootstrap_features': Categorical("bootstrap_features", [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 + bootstrap = Categorical("bootstrap", [True, False]) + oob_score = Categorical("oob_score", [True, False]) + oob_condition = EqualsCondition(oob_score, bootstrap, True) -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, - } + cs = ConfigurationSpace( + space = space + ) + + cs.add_hyperparameters([bootstrap, oob_score]) + cs.add_conditions([oob_condition]) + + return cs +def get_DecisionTreeClassifier_ConfigurationSpace(n_featues, random_state): -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), + space = { + 'criterion': Categorical("criterion", ['gini', 'entropy']), + 'max_depth': Integer("max_depth", bounds=(1, min(20,2*n_featues))), #max of 20? log scale? + 'min_samples_split': Integer("min_samples_split", bounds=(1, 20)), + 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 20)), + 'max_features': Categorical("max_features", [NONE_SPECIAL_STRING, 'sqrt', 'log2']), '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 + 'class_weight' : Categorical('class_weight', [NONE_SPECIAL_STRING, '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 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 - } +#TODO Does not support predict_proba +def get_LinearSVC_ConfigurationSpace(random_state): + space = {"dual":"auto"} + + penalty = Categorical('penalty', ['l1', 'l2']) + C = Float('C', (0.01, 1e5), log=True) + loss = Categorical('loss', ['hinge', 'squared_hinge']) + loss_condition = EqualsCondition(loss, penalty, 'l2') + 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_LinearSVC(trial, random_state=None, name=None): - 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']) + cs = ConfigurationSpace(space) + cs.add_hyperparameters([penalty, C, loss]) + cs.add_conditions([loss_condition]) - 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 - } + return cs -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 get_SVC_ConfigurationSpace(random_state): + space = { + 'max_iter': 3000, + 'probability':TRUE_SPECIAL_STRING} + + kernel = Categorical("kernel", ['poly', 'rbf', 'sigmoid', 'linear']) + C = Float('C', (0.01, 1e5), log=True) + degree = Integer("degree", bounds=(1, 5)) + gamma = Float("gamma", bounds=(1e-5, 8), log=True) + shrinking = Categorical("shrinking", [True, False]) + coef0 = Float("coef0", bounds=(-1, 1)) + class_weight = Categorical('class_weight', [NONE_SPECIAL_STRING, 'balanced']) -def params_GradientBoostingClassifier(trial, random_state=None, n_classes=None, name=None): + degree_condition = EqualsCondition(degree, kernel, 'poly') + gamma_condition = InCondition(gamma, kernel, ['rbf', 'poly']) + coef0_condition = InCondition(coef0, kernel, ['poly', 'sigmoid']) - 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), + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + + cs = ConfigurationSpace(space) + cs.add_hyperparameters([kernel, C, coef0, degree, gamma, shrinking, class_weight]) + cs.add_conditions([degree_condition, gamma_condition, coef0_condition]) + + return cs + + +def get_RandomForestClassifier_ConfigurationSpace( random_state): + space = { + 'n_estimators': 128, #as recommended by Oshiro et al. (2012 + 'max_features': Float("max_features", bounds=(0.01,1)), #log scale like autosklearn? + '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]), + 'class_weight': Categorical("class_weight", [NONE_SPECIAL_STRING, '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_XGBClassifier_ConfigurationSpace(random_state,): + + space = { + 'n_estimators': 100, + 'learning_rate': Float("learning_rate", bounds=(1e-3, 1), log=True), + 'subsample': Float("subsample", bounds=(0.5, 1.0)), + 'min_child_weight': Integer("min_child_weight", bounds=(1, 21)), + 'gamma': Float("gamma", bounds=(1e-4, 20), log=True), + 'max_depth': Integer("max_depth", bounds=(3, 18)), + 'reg_alpha': Float("reg_alpha", bounds=(1e-4, 100), log=True), + 'reg_lambda': Float("reg_lambda", bounds=(1e-4, 1), log=True), + 'n_jobs': 1, + 'nthread': 1, + 'verbosity': 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_LGBMClassifier_ConfigurationSpace(random_state,): + + space = { + '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)), + 'class_weight': Categorical("class_weight", [NONE_SPECIAL_STRING, 'balanced']), + 'verbose':-1, + '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_ExtraTreesClassifier_ConfigurationSpace(random_state): + space = { + 'n_estimators': 100, + 'criterion': Categorical("criterion", ["gini", "entropy"]), + 'max_features': Float("max_features", bounds=(0.01, 1.00)), + '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]), + 'class_weight': Categorical("class_weight", [NONE_SPECIAL_STRING, 'balanced']), + '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): + + space = { + 'loss': Categorical("loss", ['modified_huber']), #don't include hinge because we have LinearSVC, don't include log because we have LogisticRegression. TODO 'squared_hinge'? doesn't support predict proba + 'penalty': 'elasticnet', + 'alpha': Float("alpha", bounds=(1e-5, 0.01), log=True), + 'l1_ratio': Float("l1_ratio", bounds=(0.0, 1.0)), + 'eta0': Float("eta0", bounds=(0.01, 1.0)), + 'n_jobs': 1, + 'fit_intercept': Categorical("fit_intercept", [True]), + 'class_weight': Categorical("class_weight", [NONE_SPECIAL_STRING, 'balanced']), + } + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + power_t = Float("power_t", bounds=(1e-5, 100.0), log=True) + learning_rate = Categorical("learning_rate", ['invscaling', 'constant', "optimal"]) + powertcond = EqualsCondition(power_t, learning_rate, 'invscaling') + + + cs = ConfigurationSpace( + space = space + ) + + cs.add_hyperparameters([power_t, learning_rate]) + cs.add_conditions([powertcond]) + + return cs + + +GaussianNB_ConfigurationSpace = {} + +def get_BernoulliNB_ConfigurationSpace(): + return ConfigurationSpace( + space = { + 'alpha': Float("alpha", bounds=(1e-2, 100), log=True), + 'fit_prior': Categorical("fit_prior", [True, False]), + } + ) + + +def get_MultinomialNB_ConfigurationSpace(): + return ConfigurationSpace( + space = { + 'alpha': Float("alpha", bounds=(1e-3, 100), log=True), + 'fit_prior': Categorical("fit_prior", [True, False]), + } + ) + + + +def get_AdaBoostClassifier_ConfigurationSpace(random_state): + 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']), + } + + 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_QuadraticDiscriminantAnalysis_ConfigurationSpace(): + return ConfigurationSpace( + space = { + 'reg_param': Float("reg_param", bounds=(0, 1)), + } + ) + +def get_PassiveAggressiveClassifier_ConfigurationSpace(random_state): + space = { + 'C': Float("C", bounds=(1e-5, 10), log=True), + 'loss': Categorical("loss", ['hinge', 'squared_hinge']), + 'average': Categorical("average", [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 + ) +#TODO support auto shrinkage when solver is svd. may require custom node +def get_LinearDiscriminantAnalysis_ConfigurationSpace(): + + solver = Categorical("solver", ['svd', 'lsqr', 'eigen']) + shrinkage = Float("shrinkage", bounds=(0, 1)) + + shrinkcond = NotEqualsCondition(shrinkage, solver, 'svd') + + cs = ConfigurationSpace() + cs.add_hyperparameters([solver, shrinkage]) + cs.add_conditions([shrinkcond]) + + return cs + + + +#### Gradient Boosting Classifiers + +def get_GradientBoostingClassifier_ConfigurationSpace(n_classes, random_state): + early_stop = Categorical("early_stop", ["off", "valid", "train"]) + n_iter_no_change = Integer("n_iter_no_change",bounds=(1,20)) + validation_fraction = Float("validation_fraction", bounds=(0.01, 0.4)) + + n_iter_no_change_cond = InCondition(n_iter_no_change, early_stop, ["valid", "train"] ) + validation_fraction_cond = EqualsCondition(validation_fraction, early_stop, "valid") + + space = { + 'learning_rate': Float("learning_rate", bounds=(1e-3, 1), log=True), + '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.01, 1.00)), + 'max_leaf_nodes': Integer("max_leaf_nodes", bounds=(3, 2047)), + 'max_depth':NONE_SPECIAL_STRING, # 'max_depth': Integer("max_depth", bounds=(1, 2*n_features)), '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 + + if n_classes == 2: + space['loss']= Categorical("loss", ['log_loss', 'exponential']) + else: + space['loss'] = "log_loss" + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + cs = ConfigurationSpace( + space = space + ) + cs.add_hyperparameters([n_iter_no_change, validation_fraction, early_stop ]) + cs.add_conditions([validation_fraction_cond, n_iter_no_change_cond]) + return cs + +def GradientBoostingClassifier_hyperparameter_parser(params): + + final_params = { + 'loss': params['loss'], + 'learning_rate': params['learning_rate'], + 'min_samples_leaf': params['min_samples_leaf'], + 'min_samples_split': params['min_samples_split'], + 'max_features': params['max_features'], + 'max_leaf_nodes': params['max_leaf_nodes'], + 'max_depth': params['max_depth'], + 'tol': params['tol'], + 'subsample': params['subsample'] } + if 'random_state' in params: + final_params['random_state'] = params['random_state'] -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 + if params['early_stop'] == 'off': + final_params['n_iter_no_change'] = None + final_params['validation_fraction'] = None + elif params['early_stop'] == 'valid': + #this is required because in crossover, its possible that n_iter_no_change is not in the params + if 'n_iter_no_change' not in params: + final_params['n_iter_no_change'] = 10 + else: + final_params['n_iter_no_change'] = params['n_iter_no_change'] + if 'validation_fraction' not in params: + final_params['validation_fraction'] = 0.1 + else: + final_params['validation_fraction'] = params['validation_fraction'] + elif params['early_stop'] == 'train': + if 'n_iter_no_change' not in params: + final_params['n_iter_no_change'] = 10 + final_params['validation_fraction'] = None - } - 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 - } - return params + return final_params + -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 + +#only difference is l2_regularization +def get_HistGradientBoostingClassifier_ConfigurationSpace(random_state): + early_stop = Categorical("early_stop", ["off", "valid", "train"]) + n_iter_no_change = Integer("n_iter_no_change",bounds=(1,20)) + validation_fraction = Float("validation_fraction", bounds=(0.01, 0.4)) + + n_iter_no_change_cond = InCondition(n_iter_no_change, early_stop, ["valid", "train"] ) + validation_fraction_cond = EqualsCondition(validation_fraction, early_stop, "valid") + + space = { + 'learning_rate': Float("learning_rate", bounds=(1e-3, 1), log=True), + 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 200)), + 'max_features': Float("max_features", bounds=(0.1,1.0)), + 'max_leaf_nodes': Integer("max_leaf_nodes", bounds=(3, 2047)), + 'max_depth':NONE_SPECIAL_STRING, # 'max_depth': Integer("max_depth", bounds=(1, 2*n_features)), + 'l2_regularization': Float("l2_regularization", bounds=(1e-10, 1), log=True), + 'tol': 1e-4, } - 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 + + cs = ConfigurationSpace( + space = space + ) + cs.add_hyperparameters([n_iter_no_change, validation_fraction, early_stop ]) + cs.add_conditions([validation_fraction_cond, n_iter_no_change_cond]) -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)) + return cs - 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 + + +def HistGradientBoostingClassifier_hyperparameter_parser(params): + + final_params = { + 'learning_rate': params['learning_rate'], + 'min_samples_leaf': params['min_samples_leaf'], + 'max_features': params['max_features'], + 'max_leaf_nodes': params['max_leaf_nodes'], + 'max_depth': params['max_depth'], + 'tol': params['tol'], + 'l2_regularization': params['l2_regularization'] } - return params + if 'random_state' in params: + final_params['random_state'] = params['random_state'] + + + + if params['early_stop'] == 'off': + # final_params['n_iter_no_change'] = 0 + final_params['validation_fraction'] = None + final_params['early_stopping'] = False + elif params['early_stop'] == 'valid': + + #this is required because in crossover, its possible that n_iter_no_change is not in the params + if 'n_iter_no_change' not in params: + final_params['n_iter_no_change'] = 10 + else: + final_params['n_iter_no_change'] = params['n_iter_no_change'] + if 'validation_fraction' not in params: + final_params['validation_fraction'] = 0.1 + else: + final_params['validation_fraction'] = params['validation_fraction'] + + final_params['early_stopping'] = True + elif params['early_stop'] == 'train': + + if 'n_iter_no_change' not in params: + final_params['n_iter_no_change'] = 10 + else: + final_params['n_iter_no_change'] = params['n_iter_no_change'] + + + final_params['validation_fraction'] = None + final_params['early_stopping'] = True + + + return final_params + + +### + +def get_MLPClassifier_ConfigurationSpace(random_state): + space = {"n_iter_no_change":32} + 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_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]), + cs = ConfigurationSpace( + space = space + ) + + n_hidden_layers = Integer("n_hidden_layers", bounds=(1, 3)) + n_nodes_per_layer = Integer("n_nodes_per_layer", bounds=(16, 512)) + activation = Categorical("activation", ["identity", "logistic",'tanh', 'relu']) + alpha = Float("alpha", bounds=(1e-4, 1e-1), log=True) + early_stopping = Categorical("early_stopping", [True,False]) + + learning_rate_init = Float("learning_rate_init", bounds=(1e-4, 1e-1), log=True) + learning_rate = Categorical("learning_rate", ['constant', 'invscaling', 'adaptive']) + + cs.add_hyperparameters([n_hidden_layers, n_nodes_per_layer, activation, alpha, learning_rate, early_stopping, learning_rate_init]) + + return cs + +def MLPClassifier_hyperparameter_parser(params): + hyperparameters = { + 'n_iter_no_change': params['n_iter_no_change'], + 'hidden_layer_sizes' : [params['n_nodes_per_layer']]*params['n_hidden_layers'], + 'activation': params['activation'], + 'alpha': params['alpha'], + 'early_stopping': params['early_stopping'], + + 'learning_rate_init': params['learning_rate_init'], + 'learning_rate': params['learning_rate'], } - return params + if 'random_state' in params: + hyperparameters['random_state'] = params['random_state'] + return hyperparameters -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]), +### + + +### + +def get_GaussianProcessClassifier_ConfigurationSpace(n_features, random_state): + space = { + 'n_features': n_features, + 'alpha': Float("alpha", bounds=(1e-14, 1.0), log=True), + 'thetaL': Float("thetaL", bounds=(1e-10, 1e-3), log=True), + 'thetaU': Float("thetaU", bounds=(1.0, 100000), log=True), } - 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), - } + + 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 GaussianProcessClassifier_hyperparameter_parser(params): + kernel = sklearn.gaussian_process.kernels.RBF( + length_scale = [1.0]*params['n_features'], + length_scale_bounds=[(params['thetaL'], params['thetaU'])] * params['n_features'], + ) + final_params = {"kernel": kernel, + "n_restarts_optimizer": 10, + "optimizer": "fmin_l_bfgs_b", + "copy_X_train": True, + } + + if "random_state" in params: + final_params['random_state'] = params['random_state'] + + return final_params \ No newline at end of file diff --git a/tpot2/config/classifiers_sklearnex.py b/tpot2/config/classifiers_sklearnex.py index 16983332..e16d2c03 100644 --- a/tpot2/config/classifiers_sklearnex.py +++ b/tpot2/config/classifiers_sklearnex.py @@ -1,80 +1,82 @@ -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 +from ConfigSpace import ConfigurationSpace +from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal +from ..search_spaces.nodes.estimator_node import NONE_SPECIAL_STRING, TRUE_SPECIAL_STRING, FALSE_SPECIAL_STRING -import numpy as np +def get_RandomForestClassifier_ConfigurationSpace(random_state): + 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 + ) -from functools import partial +def get_KNeighborsClassifier_ConfigurationSpace(n_samples): + return ConfigurationSpace( + space = { + 'n_neighbors': Integer("n_neighbors", bounds=(1, max(n_samples, 100)), log=True), + 'weights': Categorical("weights", ['uniform', 'distance']), + } + ) -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 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 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): + 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): + 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): + 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)), + 'class_weight': Categorical("class_weight", [NONE_SPECIAL_STRING, '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/get_configspace.py b/tpot2/config/get_configspace.py new file mode 100644 index 00000000..4a5cc997 --- /dev/null +++ b/tpot2/config/get_configspace.py @@ -0,0 +1,485 @@ +import importlib.util +import sys +import numpy as np +import warnings +import importlib.util + +from ..search_spaces.nodes import EstimatorNode +from ..search_spaces.pipelines import ChoicePipeline, WrapperPipeline + +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 + +from . import classifiers_sklearnex +from . import regressors_sklearnex + +from ConfigSpace import ConfigurationSpace +from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal + +#autoqtl_builtins +from tpot2.builtin_modules import genetic_encoders, feature_encoding_frequency_selector +from tpot2.builtin_modules import AddTransformer, mul_neg_1_Transformer, MulTransformer, SafeReciprocalTransformer, EQTransformer, NETransformer, GETransformer, GTTransformer, LETransformer, LTTransformer, MinTransformer, MaxTransformer, ZeroTransformer, OneTransformer, NTransformer +from tpot2.builtin_modules.genetic_encoders import DominantEncoder, RecessiveEncoder, HeterosisEncoder, UnderDominanceEncoder, OverDominanceEncoder +from tpot2.builtin_modules import ZeroCount, ColumnOneHotEncoder, PassKBinsDiscretizer +from tpot2.builtin_modules import Passthrough, SkipTransformer +from sklearn.linear_model import SGDClassifier, LogisticRegression, SGDRegressor, Ridge, Lasso, ElasticNet, Lars, LassoLars, LassoLarsCV, RidgeCV, ElasticNetCV, PassiveAggressiveClassifier, ARDRegression +from sklearn.ensemble import BaggingClassifier, RandomForestClassifier, ExtraTreesClassifier, GradientBoostingClassifier, ExtraTreesRegressor, ExtraTreesClassifier, AdaBoostRegressor, AdaBoostClassifier, GradientBoostingRegressor,RandomForestRegressor, BaggingRegressor, ExtraTreesRegressor, HistGradientBoostingClassifier, HistGradientBoostingRegressor +from sklearn.neural_network import MLPClassifier, MLPRegressor +from sklearn.tree import DecisionTreeClassifier, DecisionTreeRegressor +from xgboost import XGBClassifier, XGBRegressor +from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor +from sklearn.svm import SVC, SVR, LinearSVR, LinearSVC +from lightgbm import LGBMClassifier, LGBMRegressor +from sklearn.naive_bayes import GaussianNB, BernoulliNB, MultinomialNB +from sklearn.decomposition import FastICA, PCA +from sklearn.cluster import FeatureAgglomeration +from sklearn.kernel_approximation import Nystroem, RBFSampler +from sklearn.preprocessing import StandardScaler, PowerTransformer, QuantileTransformer, RobustScaler, PolynomialFeatures, Normalizer, MinMaxScaler, MaxAbsScaler, Binarizer +from sklearn.feature_selection import SelectFwe, SelectPercentile, VarianceThreshold, RFE, SelectFromModel +from sklearn.feature_selection import f_classif, f_regression #TODO create a selectomixin using these? +from sklearn.discriminant_analysis import LinearDiscriminantAnalysis, QuadraticDiscriminantAnalysis +from sklearn.gaussian_process import GaussianProcessRegressor, GaussianProcessClassifier +from sklearn.impute import SimpleImputer + +all_methods = [SGDClassifier, RandomForestClassifier, ExtraTreesClassifier, GradientBoostingClassifier, MLPClassifier, DecisionTreeClassifier, XGBClassifier, KNeighborsClassifier, SVC, LogisticRegression, LGBMClassifier, LinearSVC, GaussianNB, BernoulliNB, MultinomialNB, ExtraTreesRegressor, RandomForestRegressor, GradientBoostingRegressor, BaggingRegressor, DecisionTreeRegressor, KNeighborsRegressor, XGBRegressor, ZeroCount, ColumnOneHotEncoder, Binarizer, FastICA, FeatureAgglomeration, MaxAbsScaler, MinMaxScaler, Normalizer, Nystroem, PCA, PolynomialFeatures, RBFSampler, RobustScaler, StandardScaler, SelectFwe, SelectPercentile, VarianceThreshold, SGDRegressor, Ridge, Lasso, ElasticNet, Lars, LassoLars, LassoLarsCV, RidgeCV, SVR, LinearSVR, AdaBoostRegressor, GradientBoostingRegressor, RandomForestRegressor, BaggingRegressor, ExtraTreesRegressor, DecisionTreeRegressor, KNeighborsRegressor, ElasticNetCV, + AdaBoostClassifier,MLPRegressor, + GaussianProcessRegressor, HistGradientBoostingClassifier, HistGradientBoostingRegressor, + AddTransformer, mul_neg_1_Transformer, MulTransformer, SafeReciprocalTransformer, EQTransformer, NETransformer, GETransformer, GTTransformer, LETransformer, LTTransformer, MinTransformer, MaxTransformer, ZeroTransformer, OneTransformer, NTransformer, + PowerTransformer, QuantileTransformer,ARDRegression, QuadraticDiscriminantAnalysis, PassiveAggressiveClassifier, LinearDiscriminantAnalysis, + DominantEncoder, RecessiveEncoder, HeterosisEncoder, UnderDominanceEncoder, OverDominanceEncoder, + GaussianProcessClassifier, BaggingClassifier,LGBMRegressor, + Passthrough,SkipTransformer, + PassKBinsDiscretizer, + SimpleImputer, + ] + + +#if mdr is installed +if importlib.util.find_spec('mdr') is not None: + from mdr import MDR, ContinuousMDR + all_methods.append(MDR) + all_methods.append(ContinuousMDR) + +if importlib.util.find_spec('skrebate') is not None: + from skrebate import ReliefF, SURF, SURFstar, MultiSURF + all_methods.append(ReliefF) + all_methods.append(SURF) + all_methods.append(SURFstar) + all_methods.append(MultiSURF) + +STRING_TO_CLASS = { + t.__name__: t for t in all_methods +} + +if importlib.util.find_spec('sklearnex') is not None: + import sklearnex + import sklearnex.linear_model + import sklearnex.svm + import sklearnex.ensemble + import sklearnex.neighbors + + + sklearnex_methods = [] + + sklearnex_methods.append(sklearnex.linear_model.LinearRegression) + sklearnex_methods.append(sklearnex.linear_model.Ridge) + sklearnex_methods.append(sklearnex.linear_model.Lasso) + sklearnex_methods.append(sklearnex.linear_model.ElasticNet) + sklearnex_methods.append(sklearnex.svm.SVR) + sklearnex_methods.append(sklearnex.svm.NuSVR) + sklearnex_methods.append(sklearnex.ensemble.RandomForestRegressor) + sklearnex_methods.append(sklearnex.neighbors.KNeighborsRegressor) + sklearnex_methods.append(sklearnex.ensemble.RandomForestClassifier) + sklearnex_methods.append(sklearnex.neighbors.KNeighborsClassifier) + sklearnex_methods.append(sklearnex.svm.SVC) + sklearnex_methods.append(sklearnex.svm.NuSVC) + sklearnex_methods.append(sklearnex.linear_model.LogisticRegression) + + STRING_TO_CLASS.update({f"{t.__name__}_sklearnex": t for t in sklearnex_methods}) + + + + + +# not including "PassiveAggressiveClassifier" in classifiers since it is mainly for larger than memory datasets/online use cases + +# TODO need to subclass "GaussianProcessClassifier" and 'GaussianProcessRegressor'. These require n_features as a parameter for the kernel, but n_features may be different depending on selection functions or transformations previously in the pipeline. + +GROUPNAMES = { + "selectors": ["SelectFwe", "SelectPercentile", "VarianceThreshold",], + "selectors_classification": ["SelectFwe", "SelectPercentile", "VarianceThreshold", "RFE_classification", "SelectFromModel_classification"], + "selectors_regression": ["SelectFwe", "SelectPercentile", "VarianceThreshold", "RFE_regression", "SelectFromModel_regression"], + "classifiers" : ["LGBMClassifier", "BaggingClassifier", 'AdaBoostClassifier', 'BernoulliNB', 'DecisionTreeClassifier', 'ExtraTreesClassifier', 'GaussianNB', 'HistGradientBoostingClassifier', 'KNeighborsClassifier','LinearDiscriminantAnalysis', 'LogisticRegression', "LinearSVC", "SVC", 'MLPClassifier', 'MultinomialNB', "QuadraticDiscriminantAnalysis", 'RandomForestClassifier', 'SGDClassifier', 'XGBClassifier'], + "regressors" : ["LGBMRegressor", 'AdaBoostRegressor', "ARDRegression", 'DecisionTreeRegressor', 'ExtraTreesRegressor', 'HistGradientBoostingRegressor', 'KNeighborsRegressor', 'LinearSVR', "MLPRegressor", 'RandomForestRegressor', 'SGDRegressor', 'SVR', 'XGBRegressor'], + + + "transformers": ["PassKBinsDiscretizer", "Binarizer", "PCA", "ZeroCount", "ColumnOneHotEncoder", "FastICA", "FeatureAgglomeration", "Nystroem", "RBFSampler", "QuantileTransformer", "PowerTransformer"], + "scalers": ["MinMaxScaler", "RobustScaler", "StandardScaler", "MaxAbsScaler", "Normalizer", ], + "all_transformers" : ["transformers", "scalers"], + + "arithmatic": ["AddTransformer", "mul_neg_1_Transformer", "MulTransformer", "SafeReciprocalTransformer", "EQTransformer", "NETransformer", "GETransformer", "GTTransformer", "LETransformer", "LTTransformer", "MinTransformer", "MaxTransformer"], + "imputers": ["SimpleImputer"], + "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=1000, n_features=100, random_state=None): + match name: + case "SimpleImputer": + return imputers.simple_imputer_cs + + #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 {} + + case "Passthrough": + return {} + case "SkipTransformer": + return {} + + #classifiers.py + case "LinearDiscriminantAnalysis": + return classifiers.get_LinearDiscriminantAnalysis_ConfigurationSpace() + case "AdaBoostClassifier": + return classifiers.get_AdaBoostClassifier_ConfigurationSpace(random_state=random_state) + case "LogisticRegression": + 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(n_featues=n_features, random_state=random_state) + case "SVC": + return classifiers.get_SVC_ConfigurationSpace(random_state=random_state) + case "LinearSVC": + return classifiers.get_LinearSVC_ConfigurationSpace(random_state=random_state) + case "RandomForestClassifier": + return classifiers.get_RandomForestClassifier_ConfigurationSpace(random_state=random_state) + case "GradientBoostingClassifier": + return classifiers.get_GradientBoostingClassifier_ConfigurationSpace(n_classes=n_classes, random_state=random_state) + case "HistGradientBoostingClassifier": + return classifiers.get_HistGradientBoostingClassifier_ConfigurationSpace(random_state=random_state) + case "XGBClassifier": + return classifiers.get_XGBClassifier_ConfigurationSpace(random_state=random_state) + case "LGBMClassifier": + return classifiers.get_LGBMClassifier_ConfigurationSpace(random_state=random_state) + case "ExtraTreesClassifier": + return classifiers.get_ExtraTreesClassifier_ConfigurationSpace(random_state=random_state) + case "SGDClassifier": + return classifiers.get_SGDClassifier_ConfigurationSpace(random_state=random_state) + case "MLPClassifier": + return classifiers.get_MLPClassifier_ConfigurationSpace(random_state=random_state) + case "BernoulliNB": + return classifiers.get_BernoulliNB_ConfigurationSpace() + case "MultinomialNB": + return classifiers.get_MultinomialNB_ConfigurationSpace() + case "GaussianNB": + return {} + case "LassoLarsCV": + return {} + case "ElasticNetCV": + return regressors.ElasticNetCV_configspace + case "RidgeCV": + return {} + case "PassiveAggressiveClassifier": + return classifiers.get_PassiveAggressiveClassifier_ConfigurationSpace(random_state=random_state) + case "QuadraticDiscriminantAnalysis": + return classifiers.get_QuadraticDiscriminantAnalysis_ConfigurationSpace() + case "GaussianProcessClassifier": + return classifiers.get_GaussianProcessClassifier_ConfigurationSpace(n_features=n_features, random_state=random_state) + case "BaggingClassifier": + return classifiers.get_BaggingClassifier_ConfigurationSpace(random_state=random_state) + + #regressors.py + case "RandomForestRegressor": + return regressors.get_RandomForestRegressor_ConfigurationSpace(random_state=random_state) + case "SGDRegressor": + return regressors.get_SGDRegressor_ConfigurationSpace(random_state=random_state) + case "Ridge": + return regressors.get_Ridge_ConfigurationSpace(random_state=random_state) + case "Lasso": + return regressors.get_Lasso_ConfigurationSpace(random_state=random_state) + case "ElasticNet": + return regressors.get_ElasticNet_ConfigurationSpace(random_state=random_state) + case "Lars": + return regressors.get_Lars_ConfigurationSpace(random_state=random_state) + case "OthogonalMatchingPursuit": + return regressors.get_OthogonalMatchingPursuit_ConfigurationSpace() + case "BayesianRidge": + return regressors.get_BayesianRidge_ConfigurationSpace() + case "LassoLars": + return regressors.get_LassoLars_ConfigurationSpace(random_state=random_state) + case "BaggingRegressor": + return regressors.get_BaggingRegressor_ConfigurationSpace(random_state=random_state) + case "ARDRegression": + return regressors.get_ARDRegression_ConfigurationSpace() + case "TheilSenRegressor": + return regressors.get_TheilSenRegressor_ConfigurationSpace(random_state=random_state) + case "Perceptron": + return regressors.get_Perceptron_ConfigurationSpace(random_state=random_state) + case "DecisionTreeRegressor": + return regressors.get_DecisionTreeRegressor_ConfigurationSpace(random_state=random_state) + case "LinearSVR": + return regressors.get_LinearSVR_ConfigurationSpace(random_state=random_state) + case "SVR": + return regressors.get_SVR_ConfigurationSpace() + case "XGBRegressor": + return regressors.get_XGBRegressor_ConfigurationSpace(random_state=random_state) + case "AdaBoostRegressor": + return regressors.get_AdaBoostRegressor_ConfigurationSpace(random_state=random_state) + case "ExtraTreesRegressor": + return regressors.get_ExtraTreesRegressor_ConfigurationSpace(random_state=random_state) + case "GradientBoostingRegressor": + return regressors.get_GradientBoostingRegressor_ConfigurationSpace(random_state=random_state) + case "HistGradientBoostingRegressor": + return regressors.get_HistGradientBoostingRegressor_ConfigurationSpace(random_state=random_state) + case "MLPRegressor": + return regressors.get_MLPRegressor_ConfigurationSpace(random_state=random_state) + case "KNeighborsRegressor": + return regressors.get_KNeighborsRegressor_ConfigurationSpace(n_samples=n_samples) + case "GaussianProcessRegressor": + return regressors.get_GaussianProcessRegressor_ConfigurationSpace(n_features=n_features, random_state=random_state) + case "LGBMRegressor": + return regressors.get_LGBMRegressor_ConfigurationSpace(random_state=random_state) + case "BaggingRegressor": + return regressors.get_BaggingRegressor_ConfigurationSpace(random_state=random_state) + + #transformers.py + case "Binarizer": + return transformers.Binarizer_configspace + case "Normalizer": + return transformers.Normalizer_configspace + case "PCA": + return transformers.PCA_configspace + case "ZeroCount": + return transformers.ZeroCount_configspace + case "FastICA": + return transformers.get_FastICA_configspace(n_features=n_features, random_state=random_state) + case "FeatureAgglomeration": + return transformers.get_FeatureAgglomeration_configspace(n_samples=n_samples) + case "Nystroem": + return transformers.get_Nystroem_configspace(n_features=n_features, random_state=random_state) + case "RBFSampler": + return transformers.get_RBFSampler_configspace(n_features=n_features, random_state=random_state) + case "MinMaxScaler": + return {} + case "PowerTransformer": + return {} + case "QuantileTransformer": + return transformers.get_QuantileTransformer_configspace(random_state=random_state) + case "RobustScaler": + return transformers.RobustScaler_configspace + case "ColumnOneHotEncoder": + return {} + case "MaxAbsScaler": + return {} + case "PolynomialFeatures": + return transformers.PolynomialFeatures_configspace + case "StandardScaler": + return {} + case "PassKBinsDiscretizer": + return transformers.get_passkbinsdiscretizer_configspace(random_state=random_state) + + #selectors.py + case "SelectFwe": + return selectors.SelectFwe_configspace + case "SelectPercentile": + return selectors.SelectPercentile_configspace + case "VarianceThreshold": + return selectors.VarianceThreshold_configspace + case "RFE": + return selectors.RFE_configspace_part + case "SelectFromModel": + return selectors.SelectFromModel_configspace_part + + + #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 ConfigurationSpace( + + space = { + + 'n': Float("n", bounds=(-1e2, 1e2)), + } + ) + + #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) + + #raise error + raise ValueError(f"Could not find configspace for {name}") + + +def get_search_space(name, n_classes=3, n_samples=100, n_features=100, random_state=None, return_choice_pipeline=True): + + + #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, n_features=n_features, random_state=random_state, return_choice_pipeline=False) for n in name] + #remove Nones + search_spaces = [s for s in search_spaces if s is not None] + + if return_choice_pipeline: + return ChoicePipeline(search_spaces=np.hstack(search_spaces)) + else: + return np.hstack(search_spaces) + + 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, return_choice_pipeline=return_choice_pipeline) + + 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, n_features=100, random_state=None): + + #these are wrappers that take in another estimator as a parameter + # TODO Add AdaBoostRegressor, AdaBoostClassifier as wrappers? wrap a decision tree with different params? + # TODO add other meta-estimators? + if name == "RFE_classification": + rfe_sp = get_configspace(name="RFE", 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(estimator_search_space=ext, method=RFE, space=rfe_sp) + if name == "RFE_regression": + rfe_sp = get_configspace(name="RFE", 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(estimator_search_space=ext, method=RFE, space=rfe_sp) + if name == "SelectFromModel_classification": + sfm_sp = get_configspace(name="SelectFromModel", 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(estimator_search_space=ext, method=SelectFromModel, space=sfm_sp) + if name == "SelectFromModel_regression": + sfm_sp = get_configspace(name="SelectFromModel", 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(estimator_search_space=ext, method=SelectFromModel, space=sfm_sp) + + #these are nodes that have special search spaces which require custom parsing of the hyperparameters + if name == "RobustScaler": + configspace = get_configspace(name, n_classes=n_classes, n_samples=n_samples, random_state=random_state) + return EstimatorNode(STRING_TO_CLASS[name], configspace, hyperparameter_parser=transformers.robust_scaler_hyperparameter_parser) + if name == "GradientBoostingClassifier": + configspace = get_configspace(name, n_classes=n_classes, n_samples=n_samples, random_state=random_state) + return EstimatorNode(STRING_TO_CLASS[name], configspace, hyperparameter_parser=classifiers.GradientBoostingClassifier_hyperparameter_parser) + if name == "HistGradientBoostingClassifier": + configspace = get_configspace(name, n_classes=n_classes, n_samples=n_samples, random_state=random_state) + return EstimatorNode(STRING_TO_CLASS[name], configspace, hyperparameter_parser=classifiers.HistGradientBoostingClassifier_hyperparameter_parser) + if name == "GradientBoostingRegressor": + configspace = get_configspace(name, n_classes=n_classes, n_samples=n_samples, random_state=random_state) + return EstimatorNode(STRING_TO_CLASS[name], configspace, hyperparameter_parser=regressors.GradientBoostingRegressor_hyperparameter_parser) + if name == "HistGradientBoostingRegressor": + configspace = get_configspace(name, n_classes=n_classes, n_samples=n_samples, random_state=random_state) + return EstimatorNode(STRING_TO_CLASS[name], configspace, hyperparameter_parser=regressors.HistGradientBoostingRegressor_hyperparameter_parser) + if name == "MLPClassifier": + configspace = get_configspace(name, n_classes=n_classes, n_samples=n_samples, random_state=random_state) + return EstimatorNode(STRING_TO_CLASS[name], configspace, hyperparameter_parser=classifiers.MLPClassifier_hyperparameter_parser) + if name == "MLPRegressor": + configspace = get_configspace(name, n_classes=n_classes, n_samples=n_samples, random_state=random_state) + return EstimatorNode(STRING_TO_CLASS[name], configspace, hyperparameter_parser=regressors.MLPRegressor_hyperparameter_parser) + if name == "GaussianProcessRegressor": + configspace = get_configspace(name, n_classes=n_classes, n_samples=n_samples, random_state=random_state) + return EstimatorNode(STRING_TO_CLASS[name], configspace, hyperparameter_parser=regressors.GaussianProcessRegressor_hyperparameter_parser) + if name == "GaussianProcessClassifier": + configspace = get_configspace(name, n_classes=n_classes, n_samples=n_samples, random_state=random_state) + return EstimatorNode(STRING_TO_CLASS[name], configspace, hyperparameter_parser=classifiers.GaussianProcessClassifier_hyperparameter_parser) + if name == "FeatureAgglomeration": + configspace = get_configspace(name, n_features=n_features) + return EstimatorNode(STRING_TO_CLASS[name], configspace, hyperparameter_parser=transformers.FeatureAgglomeration_hyperparameter_parser) + + 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) \ No newline at end of file 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..3499c0aa --- /dev/null +++ b/tpot2/config/imputers.py @@ -0,0 +1,9 @@ +from ConfigSpace import ConfigurationSpace +from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal + +simple_imputer_cs = 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 1fe7cc7a..df92cd17 100644 --- a/tpot2/config/mdr_configs.py +++ b/tpot2/config/mdr_configs.py @@ -1,60 +1,45 @@ -from mdr import MDR, ContinuousMDR -from skrebate import ReliefF, SURF, SURFstar, MultiSURF -from functools import partial - -#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]), - } +from ConfigSpace import ConfigurationSpace +from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal -#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), - } -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 +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), - } +def get_skrebate_ReliefF_config_space(n_features): + 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_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 get_skrebate_SURF_config_space(n_features): + return ConfigurationSpace( + space = { + 'n_features_to_select': Integer('n_features_to_select', bounds=(1, n_features), log=True), + } +) -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 get_skrebate_SURFstar_config_space(n_features): + return ConfigurationSpace( + space = { + 'n_features_to_select': Integer('n_features_to_select', bounds=(1, n_features), log=True), + } +) +def get_skrebate_MultiSURF_config_space(n_features): + return ConfigurationSpace( + space = { + 'n_features_to_select': Integer('n_features_to_select', bounds=(1, n_features), log=True), + } +) diff --git a/tpot2/config/regressors.py b/tpot2/config/regressors.py index ad7aa182..d1b9343d 100644 --- a/tpot2/config/regressors.py +++ b/tpot2/config/regressors.py @@ -1,396 +1,618 @@ -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 +import sklearn +from ConfigSpace import ConfigurationSpace +from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal +from ConfigSpace import EqualsCondition, OrConjunction, NotEqualsCondition, InCondition +from ..search_spaces.nodes.estimator_node import NONE_SPECIAL_STRING, TRUE_SPECIAL_STRING, FALSE_SPECIAL_STRING +import numpy as np +#TODO: fill in remaining +#TODO check for places were we could use log scaling -from sklearn.svm import SVR -from sklearn.svm import LinearSVR +ElasticNetCV_configspace = { + "l1_ratio" : np.arange(0.0, 1.01, 0.05), +} -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 +def get_RandomForestRegressor_ConfigurationSpace(random_state): + space = { + 'n_estimators': 100, + 'criterion': Categorical("criterion", ['mse', 'mae', "friedman_mse"]), + '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): + space = { + 'alpha': Float("alpha", bounds=(1e-7, 1e-1), log=True), + 'average': Categorical("average", [True, False]), + '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 + + cs = ConfigurationSpace( + space = space + ) + + l1_ratio = Float("l1_ratio", bounds=(1e-7, 1.0), log=True) + penalty = Categorical("penalty", ["l1", "l2", "elasticnet"]) + epsilon = Float("epsilon", bounds=(1e-5, 1e-1), log=True) + loss = Categorical("loss", ['epsilon_insensitive', 'squared_epsilon_insensitive', 'huber', 'squared_error']) + eta0 = Float("eta0", bounds=(1e-7, 1e-1), log=True) + learning_rate = Categorical("learning_rate", ['optimal', 'invscaling', 'constant']) + power_t = Float("power_t", bounds=(1e-5, 1.0), log=True) + + elasticnet = EqualsCondition(l1_ratio, penalty, "elasticnet") + epsilon_condition = InCondition( + epsilon, + loss, + ["huber", "epsilon_insensitive", "squared_epsilon_insensitive"], + ) + + eta0_in_inv_con = InCondition(eta0, learning_rate, ["invscaling", "constant"]) + power_t_condition = EqualsCondition(power_t, learning_rate, "invscaling") + + cs.add_hyperparameters( + [l1_ratio, penalty, epsilon, loss, eta0, learning_rate, power_t] + ) + cs.add_conditions( + [elasticnet, epsilon_condition, power_t_condition, eta0_in_inv_con] + ) + + return cs + + +def get_Ridge_ConfigurationSpace(random_state): + 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']), + } + + 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): + space = { + 'alpha': Float("alpha", bounds=(0.0, 1.0)), + 'fit_intercept': Categorical("fit_intercept", [True]), + 'tol': 0.0001, + } + + 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): + 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 + ) -from xgboost import XGBRegressor -from functools import partial +def get_Lars_ConfigurationSpace(random_state): + space = { + } + + 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_OthogonalMatchingPursuit_ConfigurationSpace(): + return ConfigurationSpace( + space = { + } + ) + +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), + } + ) -#TODO: fill in remaining -#TODO check for places were we could use log scaling -def params_RandomForestRegressor(trial, random_state=None, name=None): - return { - '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 +def get_LassoLars_ConfigurationSpace(random_state): + space = { + 'alpha': Float("alpha", bounds=(0.0, 1.0)), + 'eps': Float("eps", bounds=(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 -# 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 - - } - 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, + return ConfigurationSpace( + space = space + ) - #'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 +def get_BaggingRegressor_ConfigurationSpace(random_state): + 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 + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state -# 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']), + return ConfigurationSpace( + space = space + ) - #'max_iter': trial.suggest_int(f'max_iter_{name}', 100, 1000), - 'tol': trial.suggest_float(f'tol_{name}', 1e-5, 1e-1, log=True), +def get_ARDRegression_ConfigurationSpace(): + return ConfigurationSpace( + space = { + + 'alpha_1': Float("alpha_1", bounds=(1e-10, 1e-3), log=True), + 'alpha_2': Float("alpha_2", bounds=(1e-10, 1e-3), log=True), + 'lambda_1': Float("lambda_1", bounds=(1e-10, 1e-3), log=True), + 'lambda_2': Float("lambda_2", bounds=(1e-10, 1e-3), log=True), + 'threshold_lambda': Integer("threshold_lambda", bounds=(1e3, 1e5)), - '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 } - 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 + ) -# 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 +def get_TheilSenRegressor_ConfigurationSpace(random_state): + space = { + 'n_subsamples': Integer("n_subsamples", bounds=(10, 10000)), + 'max_subpopulation': Integer("max_subpopulation", bounds=(10, 1000)), } - 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]), - } - 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 -# 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 + return ConfigurationSpace( + space = space + ) -# 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 - } - 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 +def get_Perceptron_ConfigurationSpace(random_state): + space = { + 'penalty': Categorical("penalty", [NONE_SPECIAL_STRING, '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 - + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state -#GradientBoostingRegressor parameters -def params_GradientBoostingRegressor(trial, random_state=None, name=None): - loss = trial.suggest_categorical(f'loss_{name}', ['ls', 'lad', 'huber', 'quantile']) + return ConfigurationSpace( + space = space + ) - 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_DecisionTreeRegressor_ConfigurationSpace(random_state): + space = { + 'criterion': Categorical("criterion", ['squared_error', 'friedman_mse', 'mae']), + # 'max_depth': Integer("max_depth", bounds=(1, n_features*2)), + 'min_samples_split': Integer("min_samples_split", bounds=(2, 21)), + 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 21)), } - 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_KNeighborsRegressor_ConfigurationSpace(n_samples): + return ConfigurationSpace( + space = { + 'n_neighbors': Integer("n_neighbors", bounds=(1, min(100,n_samples))), + 'weights': Categorical("weights", ['uniform', 'distance']), + 'p': Integer("p", bounds=(1, 3)), + 'metric': Categorical("metric", ['minkowski', 'euclidean', 'manhattan']), + } + ) -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_LinearSVR_ConfigurationSpace(random_state): + space = { + 'epsilon': Float("epsilon", bounds=(1e-4, 1.0), log=True), + 'C': Float('C', (0.01, 1e5), log=True), + 'dual': "auto", + 'loss': Categorical("loss", ['epsilon_insensitive', 'squared_epsilon_insensitive']), } - 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']), + 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 + ) + +#add coef0? +def get_SVR_ConfigurationSpace(): + space = { + 'epislon': Float("epsilon", bounds=(1e-4, 1.0), log=True), + 'shrinking': Categorical("shrinking", [True, False]), + 'C': Float('C', (0.01, 1e5), log=True), + 'max_iter': 3000, + 'tol': 0.005, } - return params + + cs = ConfigurationSpace( + space = space + ) -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 + kernel = Categorical("kernel", ['poly', 'rbf', 'linear', 'sigmoid']) + degree = Integer("degree", bounds=(1, 5)) + gamma = Float("gamma", bounds=(1e-5, 10.0), log=True) + coef0 = Float("coef0", bounds=(-1, 1)) + + + degree_condition = EqualsCondition(degree, kernel, 'poly') + gamma_condition = InCondition(gamma, kernel, ['poly', 'rbf',]) + coef0_condition = InCondition(coef0, kernel, ['poly', 'sigmoid']) + + cs.add_hyperparameters([kernel, degree, gamma, coef0]) + cs.add_conditions([degree_condition,gamma_condition]) + + return cs - } - return params -# 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']), + +def get_XGBRegressor_ConfigurationSpace(random_state): + space = { 'n_estimators': 100, - 'max_depth': trial.suggest_int(f'max_depth_{name}', 1, 11), + 'learning_rate': Float("learning_rate", bounds=(1e-3, 1), log=True), + 'subsample': Float("subsample", bounds=(0.5, 1.0)), + 'min_child_weight': Integer("min_child_weight", bounds=(1, 21)), + 'gamma': Float("gamma", bounds=(1e-4, 20), log=True), + 'max_depth': Integer("max_depth", bounds=(3, 18)), + 'reg_alpha': Float("reg_alpha", bounds=(1e-4, 100), log=True), + 'reg_lambda': Float("reg_lambda", bounds=(1e-4, 1), log=True), + 'n_jobs': 1, 'nthread': 1, 'verbosity': 0, 'objective': 'reg:squarederror', - '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 -def params_AdaBoostRegressor(trial, random_state=None, name=None): - params = { - '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 + return ConfigurationSpace( + space = space + ) + + +def get_AdaBoostRegressor_ConfigurationSpace(random_state): + space = { + 'n_estimators': Integer("n_estimators", bounds=(50, 500)), + 'learning_rate': Float("learning_rate", bounds=(1e-3, 2.0), log=True), + 'loss': Categorical("loss", ['linear', 'square', 'exponential']), } - 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]), - #'criterion': trial.suggest_categorical(f'criterion_{name}', ['squared_error', 'poisson', 'absolute_error', 'friedman_mse']), + 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 + ) - #'max_depth': trial.suggest_int(f'max_depth_{name}', 1, 10), +def get_ExtraTreesRegressor_ConfigurationSpace(random_state): + space = { + 'n_estimators': 100, + 'criterion': Categorical("criterion", ["squared_error", "friedman_mse", "mae"]), + '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]), + } - #'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 + + return ConfigurationSpace( + space = space + ) +### + +def get_GaussianProcessRegressor_ConfigurationSpace(n_features, random_state): + space = { + 'n_features': n_features, + 'alpha': Float("alpha", bounds=(1e-14, 1.0), log=True), + 'thetaL': Float("thetaL", bounds=(1e-10, 1e-3), log=True), + 'thetaU': Float("thetaU", bounds=(1.0, 100000), log=True), + } - #if bootstrap is True - #'oob_score': trial.suggest_categorical(f'oob_score_{name}', [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 GaussianProcessRegressor_hyperparameter_parser(params): + kernel = sklearn.gaussian_process.kernels.RBF( + length_scale = [1.0]*params['n_features'], + length_scale_bounds=[(params['thetaL'], params['thetaU'])] * params['n_features'], + ) + final_params = {"kernel": kernel, + "alpha": params['alpha'], + "n_restarts_optimizer": 10, + "optimizer": "fmin_l_bfgs_b", + "normalize_y": True, + "copy_X_train": True, + } + + if "random_state" in params: + final_params['random_state'] = params['random_state'] + + return final_params + +### +def get_GradientBoostingRegressor_ConfigurationSpace(random_state): + early_stop = Categorical("early_stop", ["off", "valid", "train"]) + n_iter_no_change = Integer("n_iter_no_change",bounds=(1,20)) + validation_fraction = Float("validation_fraction", bounds=(0.01, 0.4)) + + n_iter_no_change_cond = InCondition(n_iter_no_change, early_stop, ["valid", "train"] ) + validation_fraction_cond = EqualsCondition(validation_fraction, early_stop, "valid") + + space = { + 'loss': Categorical("loss", ['log_loss', 'exponential']), + 'learning_rate': Float("learning_rate", bounds=(1e-3, 1), log=True), + '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.01, 1.00)), + 'max_leaf_nodes': Integer("max_leaf_nodes", bounds=(3, 2047)), + 'max_depth':NONE_SPECIAL_STRING, #'max_depth': Integer("max_depth", bounds=(1, 2*n_features)), + 'tol': 1e-4, + } - #'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), + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + cs = ConfigurationSpace( + space = space + ) + cs.add_hyperparameters([n_iter_no_change, validation_fraction, early_stop ]) + cs.add_conditions([validation_fraction_cond, n_iter_no_change_cond]) + return cs + +def GradientBoostingRegressor_hyperparameter_parser(params): + + final_params = { + 'loss': params['loss'], + 'learning_rate': params['learning_rate'], + 'min_samples_leaf': params['min_samples_leaf'], + 'min_samples_split': params['min_samples_split'], + 'max_features': params['max_features'], + 'max_leaf_nodes': params['max_leaf_nodes'], + 'max_depth': params['max_depth'], + 'tol': params['tol'], + 'subsample': params['subsample'] + } - 'random_state': random_state + if 'random_state' in params: + final_params['random_state'] = params['random_state'] + + if params['early_stop'] == 'off': + final_params['n_iter_no_change'] = None + final_params['validation_fraction'] = None + elif params['early_stop'] == 'valid': + #this is required because in crossover, its possible that n_iter_no_change is not in the params + if 'n_iter_no_change' not in params: + final_params['n_iter_no_change'] = 10 + else: + final_params['n_iter_no_change'] = params['n_iter_no_change'] + if 'validation_fraction' not in params: + final_params['validation_fraction'] = 0.1 + else: + final_params['validation_fraction'] = params['validation_fraction'] + elif params['early_stop'] == 'train': + if 'n_iter_no_change' not in params: + final_params['n_iter_no_change'] = 10 + else: + final_params['n_iter_no_change'] = params['n_iter_no_change'] + final_params['validation_fraction'] = None + + + return final_params + +#only difference is l2_regularization +def get_HistGradientBoostingRegressor_ConfigurationSpace(random_state): + early_stop = Categorical("early_stop", ["off", "valid", "train"]) + n_iter_no_change = Integer("n_iter_no_change",bounds=(1,20)) + validation_fraction = Float("validation_fraction", bounds=(0.01, 0.4)) + + n_iter_no_change_cond = InCondition(n_iter_no_change, early_stop, ["valid", "train"] ) + validation_fraction_cond = EqualsCondition(validation_fraction, early_stop, "valid") + + space = { + 'loss': Categorical("loss", ['log_loss', 'exponential']), + 'learning_rate': Float("learning_rate", bounds=(1e-3, 1), log=True), + 'min_samples_leaf': Integer("min_samples_leaf", bounds=(1, 200)), + 'max_features': Float("max_features", bounds=(0.1,1.0)), + 'max_leaf_nodes': Integer("max_leaf_nodes", bounds=(3, 2047)), + 'max_depth':NONE_SPECIAL_STRING, #'max_depth': Integer("max_depth", bounds=(1, 2*n_features)), + 'l2_regularization': Float("l2_regularization", bounds=(1e-10, 1), log=True), + 'tol': 1e-4, } - 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 + cs = ConfigurationSpace( + space = space + ) + cs.add_hyperparameters([n_iter_no_change, validation_fraction, early_stop ]) + cs.add_conditions([validation_fraction_cond, n_iter_no_change_cond]) -def make_regressor_config_dictionary(random_state=None, cv=None, n_samples=10): - n_samples = min(n_samples,100) #TODO optimize this + return cs - 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), +def HistGradientBoostingRegressor_hyperparameter_parser(params): + final_params = { + 'learning_rate': params['learning_rate'], + 'min_samples_leaf': params['min_samples_leaf'], + 'max_features': params['max_features'], + 'max_leaf_nodes': params['max_leaf_nodes'], + 'max_depth': params['max_depth'], + 'tol': params['tol'], + 'l2_regularization': params['l2_regularization'] } - return regressor_config_dictionary \ No newline at end of file + if 'random_state' in params: + final_params['random_state'] = params['random_state'] + + + if params['early_stop'] == 'off': + # final_params['n_iter_no_change'] = 0 + # final_params['validation_fraction'] = None + final_params['early_stopping'] = False + elif params['early_stop'] == 'valid': + if 'n_iter_no_change' not in params: + final_params['n_iter_no_change'] = 10 + else: + final_params['n_iter_no_change'] = params['n_iter_no_change'] + if 'validation_fraction' not in params: + final_params['validation_fraction'] = 0.1 + else: + final_params['validation_fraction'] = params['validation_fraction'] + final_params['early_stopping'] = True + elif params['early_stop'] == 'train': + if 'n_iter_no_change' not in params: + final_params['n_iter_no_change'] = 10 + else: + final_params['n_iter_no_change'] = params['n_iter_no_change'] + final_params['validation_fraction'] = None + final_params['early_stopping'] = True + + + return final_params + + +### + +def get_MLPRegressor_ConfigurationSpace(random_state): + space = {"n_iter_no_change":32} + + if random_state is not None: #This is required because configspace doesn't allow None as a value + space['random_state'] = random_state + + cs = ConfigurationSpace( + space = space + ) + + n_hidden_layers = Integer("n_hidden_layers", bounds=(1, 3)) + n_nodes_per_layer = Integer("n_nodes_per_layer", bounds=(16, 512)) + activation = Categorical("activation", ['tanh', 'relu']) + alpha = Float("alpha", bounds=(1e-7, 1e-1), log=True) + learning_rate = Float("learning_rate", bounds=(1e-4, 1e-1), log=True) + early_stopping = Categorical("early_stopping", [True,False]) + + learning_rate_init = Float("learning_rate_init", bounds=(1e-4, 1e-1), log=True) + learning_rate = Categorical("learning_rate", ['constant', 'invscaling', 'adaptive']) + + cs.add_hyperparameters([n_hidden_layers, n_nodes_per_layer, activation, alpha, learning_rate, early_stopping, learning_rate_init]) + + return cs + +def MLPRegressor_hyperparameter_parser(params): + hyperparameters = { + 'n_iter_no_change': params['n_iter_no_change'], + 'hidden_layer_sizes' : [params['n_nodes_per_layer']]*params['n_hidden_layers'], + 'activation': params['activation'], + 'alpha': params['alpha'], + 'early_stopping': params['early_stopping'], + 'learning_rate_init': params['learning_rate_init'], + 'learning_rate': params['learning_rate'], + } + + if 'random_state' in params: + hyperparameters['random_state'] = params['random_state'] + + return hyperparameters + + +def get_BaggingRegressor_ConfigurationSpace(random_state): + space = { + 'n_estimators': Integer("n_estimators", bounds=(3, 100)), + 'max_samples': Float("max_samples", bounds=(0.1, 1.0)), + 'max_features': Float("max_features", bounds=(0.1, 1.0)), + + 'bootstrap_features': Categorical("bootstrap_features", [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 + + bootstrap = Categorical("bootstrap", [True, False]) + oob_score = Categorical("oob_score", [True, False]) + + oob_condition = EqualsCondition(oob_score, bootstrap, True) + + cs = ConfigurationSpace( + space = space + ) + + cs.add_hyperparameters([bootstrap, oob_score]) + cs.add_conditions([oob_condition]) + + return cs + +def get_LGBMRegressor_ConfigurationSpace(random_state,): + + space = { + '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)), + 'verbose':-1, + '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 + ) diff --git a/tpot2/config/regressors_sklearnex.py b/tpot2/config/regressors_sklearnex.py index 279d2dba..7346a7c3 100644 --- a/tpot2/config/regressors_sklearnex.py +++ b/tpot2/config/regressors_sklearnex.py @@ -1,94 +1,108 @@ -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 ConfigSpace import ConfigurationSpace +from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal -from sklearnex.svm import SVR -from sklearnex.svm import NuSVR -from sklearnex.ensemble import RandomForestRegressor -from sklearnex.neighbors import KNeighborsRegressor -import numpy as np +def get_RandomForestRegressor_ConfigurationSpace(random_state): + space = { + 'n_estimators': 100, + '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 + ) -from functools import partial +def get_KNeighborsRegressor_ConfigurationSpace(n_samples): + return ConfigurationSpace( + space = { + 'n_neighbors': Integer("n_neighbors", bounds=(1, max(n_samples, 100))), + 'weights': Categorical("weights", ['uniform', 'distance']), + } + ) -def params_RandomForestRegressor(trial, random_state=None, name=None): - return { - '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 + +def get_Ridge_ConfigurationSpace(random_state): + 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_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']), - } + 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_LinearRegression(trial, name=None): - return {} + return ConfigurationSpace( + space = space + ) -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_Lasso_ConfigurationSpace(random_state): + 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_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_ElasticNet_ConfigurationSpace(random_state): + space = { + 'alpha': Float("alpha", bounds=(0.0, 1.0)), + 'l1_ratio': Float("l1_ratio", bounds=(0.0, 1.0)), } -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_SVR_ConfigurationSpace(random_state): + 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 - -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), + + 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_NuSVR_ConfigurationSpace(random_state): + 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, } -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, - } + 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/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/special_configs.py b/tpot2/config/special_configs.py index a6745b6f..5d22dfad 100644 --- a/tpot2/config/special_configs.py +++ b/tpot2/config/special_configs.py @@ -4,105 +4,29 @@ 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: {}, - } - - - - - -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. - """ +from ConfigSpace import ConfigurationSpace +from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal - subset_name = trial.suggest_categorical(f'subset_name_{name}', names_list) - - params = {'name': subset_name, - 'sel_subset': subset_dict[subset_name], +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"]), } - - return params - -def make_FSS_config_dictionary(subsets=None, n_features=None, feature_names=None): - """Create the search space of parameters for FeatureSetSelector. - - Parameters - ---------- - subsets: Sets the subsets to select from. - - str : If a string, it is assumed to be a path to a csv file with the subsets. - The first column is assumed to be the name of the subset and the remaining columns are the features in the subset. - - list or np.ndarray : If a list or np.ndarray, it is assumed to be a list of subsets. - - n_features: int the number of features in the dataset. - If subsets is None, each column will be treated as a subset. One column will be selected per subset. - """ - - #require at least of of the parameters - if subsets is None and n_features is None: - raise ValueError('At least one of the parameters must be provided') - - 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) - subset_dict = {} - for row in df.index: - subset_dict[row] = df.loc[row]['features'] - elif isinstance(subsets, dict): - subset_dict = subsets - elif isinstance(subsets, list) or isinstance(subsets, np.ndarray): - subset_dict = {str(i):subsets[i] for i in range(len(subsets))} - else: - if feature_names is None: - subset_dict = {str(i):i for i in range(n_features)} - else: - subset_dict = {str(i):feature_names[i] for i in range(len(feature_names))} - - 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 {} +# AddTransformer: {} +# mul_neg_1_Transformer: {} +# MulTransformer: {} +# SafeReciprocalTransformer: {} +# EQTransformer: {} +# NETransformer: {} +# GETransformer: {} +# GTTransformer: {} +# LETransformer: {} +# LTTransformer: {} +# MinTransformer: {} +# MaxTransformer: {} -def make_passthrough_config_dictionary(): - return {Passthrough: params_passthrough} \ No newline at end of file diff --git a/tpot2/individual_representations/graph_pipeline_individual/test/__init__.py b/tpot2/config/templates/__init__.py similarity index 100% rename from tpot2/individual_representations/graph_pipeline_individual/test/__init__.py rename to tpot2/config/templates/__init__.py diff --git a/tpot2/config/all_single_modules.py b/tpot2/config/templates/autoqtl.py similarity index 100% rename from tpot2/config/all_single_modules.py rename to tpot2/config/templates/autoqtl.py diff --git a/tpot2/tests/test_full_runs.py b/tpot2/config/templates/stc.py similarity index 100% rename from tpot2/tests/test_full_runs.py rename to tpot2/config/templates/stc.py diff --git a/tpot2/config/tests/__init__.py b/tpot2/config/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tpot2/config/tests/test_get_configspace.py b/tpot2/config/tests/test_get_configspace.py new file mode 100644 index 00000000..ae9af09a --- /dev/null +++ b/tpot2/config/tests/test_get_configspace.py @@ -0,0 +1,42 @@ +import pytest +import tpot2 +from sklearn.datasets import load_iris +import random +import sklearn + +import tpot2.config + +from ..get_configspace import STRING_TO_CLASS, GROUPNAMES + +def test_loop_through_all_hyperparameters(): + + n_classes=3 + n_samples=100 + n_features=100 + random_state=None + + for class_name, _ in STRING_TO_CLASS.items(): + print(class_name) + estnode_gen = tpot2.config.get_search_space(class_name, n_classes=n_classes, n_samples=n_samples, n_features=n_features, random_state=random_state) + + #generate 100 random hyperparameters and make sure they are all valid + for i in range(25): + estnode = estnode_gen.generate() + est = estnode.export_pipeline() + +def test_loop_through_groupnames(): + + n_classes=3 + n_samples=100 + n_features=100 + random_state=None + + for groupname, group in GROUPNAMES.items(): + for class_name in group: + print(class_name) + estnode_gen = tpot2.config.get_search_space(class_name, n_classes=n_classes, n_samples=n_samples, n_features=n_features, random_state=random_state) + + #generate 10 random hyperparameters and make sure they are all valid + for i in range(25): + estnode = estnode_gen.generate() + est = estnode.export_pipeline() \ No newline at end of file diff --git a/tpot2/config/transformers.py b/tpot2/config/transformers.py index fe869411..6d393460 100644 --- a/tpot2/config/transformers.py +++ b/tpot2/config/transformers.py @@ -1,103 +1,155 @@ -from functools import partial +from ConfigSpace import ConfigurationSpace +from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal +from ConfigSpace import EqualsCondition, OrConjunction, NotEqualsCondition, InCondition 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), +Binarizer_configspace = ConfigurationSpace( + space = { + 'threshold': Float('threshold', bounds=(0.0, 1.0)), } +) -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']), - 'whiten':'unit-variance', - 'random_state': random_state +Normalizer_configspace = ConfigurationSpace( + space={'norm': Categorical('norm', ['l1', 'l2', 'max'])} +) + +PCA_configspace = ConfigurationSpace( + space={'n_components': Float('n_components', bounds=(0.5, 0.999))} +) + +ZeroCount_configspace = {} + +PolynomialFeatures_configspace = ConfigurationSpace( + space = { + 'degree': Integer('degree', bounds=(2, 3)), + 'interaction_only': Categorical('interaction_only', [True, False]), } +) + +OneHotEncoder_configspace = {} #TODO include the parameter for max unique values -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 +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', } + + 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_samples): + + linkage = Categorical('linkage', ['ward', 'complete', 'average']) + metric = Categorical('metric', ['euclidean', 'l1', 'l2', 'manhattan', 'cosine']) + n_clusters = Integer('n_clusters', bounds=(2, min(n_samples,400))) + pooling_func = Categorical('pooling_func', ['mean', 'median', 'max']) -def params_sklearn_preprocessing_Normalizer(trial, name=None): - return { - 'norm': trial.suggest_categorical(f'norm_{name}', ['l1', 'l2', 'max']), + metric_condition = NotEqualsCondition(metric, linkage, 'ward') + + cs = ConfigurationSpace() + cs.add_hyperparameters([linkage, metric, n_clusters, pooling_func]) + cs.add_condition(metric_condition) + + return cs + + +def FeatureAgglomeration_hyperparameter_parser(params): + new_params = params.copy() + if "pooling_func" in new_params: + if new_params["pooling_func"] == "mean": + new_params["pooling_func"] = np.mean + elif new_params["pooling_func"] == "median": + new_params["pooling_func"] = np.median + elif new_params["pooling_func"] == "max": + new_params["pooling_func"] = np.max + elif new_params["pooling_func"] == "min": + new_params["pooling_func"] = np.min + + return new_params + + +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_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 get_RBFSampler_configspace(n_features=100, random_state=None): + + space = { + 'gamma': Float('gamma', bounds=(0.0, 1.0)), + 'n_components': Integer('n_components', bounds=(1, n_features)), } -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 + 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 + + ) + - return { - 'n_components': variance_explained, - 'random_state': random_state +def get_QuantileTransformer_configspace(random_state=None): + + space = { + 'n_quantiles': Integer('n_quantiles', bounds=(10, 2000)), + 'output_distribution': Categorical('output_distribution', ['uniform', 'normal']), } -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 + 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_passkbinsdiscretizer_configspace(random_state=None): + space = { + 'n_bins': Integer('n_bins', bounds=(3, 100)), + 'encode': 'onehot-dense', + 'strategy': Categorical('strategy', ['uniform', 'quantile', 'kmeans']), + # 'subsample': Categorical('subsample', ['auto', 'warn', 'ignore']), } -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 + + ) + + +### ROBUST SCALER + +RobustScaler_configspace = ConfigurationSpace({ + "q_min": Float("q_min", bounds=(0.001, 0.3)), + "q_max": Float("q_max", bounds=(0.7, 0.999)), + }) + +def robust_scaler_hyperparameter_parser(params): + return {"quantile_range": (params["q_min"], params["q_max"])} + + + diff --git a/tpot2/evolvers/base_evolver.py b/tpot2/evolvers/base_evolver.py index 83623754..756a4cde 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: @@ -498,7 +498,7 @@ def optimize(self, generations=None): self._client.close() self._cluster.close() - tpot2.utils.get_pareto_frontier(self.population.evaluated_individuals, column_names=self.objective_names, weights=self.objective_function_weights, invalid_values=["TIMEOUT","INVALID"]) + tpot2.utils.get_pareto_frontier(self.population.evaluated_individuals, column_names=self.objective_names, weights=self.objective_function_weights) def step(self,): if self.population_size_list is not None: @@ -522,7 +522,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() @@ -530,7 +530,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) @@ -539,7 +539,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, ) @@ -624,7 +624,7 @@ def evaluate_population_full(self, budget=None): parallel_timeout = 10 #scores = tpot2.utils.eval_utils.parallel_eval_objective_list(individuals_to_evaluate, self.objective_functions, self.n_jobs, verbose=self.verbose, timeout=self.max_eval_time_seconds, budget=budget, n_expected_columns=len(self.objective_names), client=self._client, parallel_timeout=parallel_timeout, **self.objective_kwargs) - scores, start_times, end_times, eval_errors = tpot2.utils.eval_utils.parallel_eval_objective_list2(individuals_to_evaluate, self.objective_functions, verbose=self.verbose, max_eval_time_seconds=self.max_eval_time_seconds, budget=budget, n_expected_columns=len(self.objective_names), client=self._client, **self.objective_kwargs) + scores, start_times, end_times, eval_errors = tpot2.utils.eval_utils.parallel_eval_objective_list2(individuals_to_evaluate, self.objective_functions, verbose=self.verbose, max_eval_time_seconds=self.max_eval_time_seconds, budget=budget, n_expected_columns=len(self.objective_names), client=self._client, scheduled_timeout_time=self.scheduled_timeout_time, **self.objective_kwargs) self.population.update_column(individuals_to_evaluate, column_names=self.objective_names, data=scores) if budget is not None: @@ -705,6 +705,7 @@ def evaluate_population_selection_early_stop(self,survival_counts, thresholds=No generation = self.generation, n_expected_columns=len(self.objective_names), client=self._client, + scheduled_timeout_time=self.scheduled_timeout_time, **self.objective_kwargs, ) diff --git a/tpot2/evolvers/steady_state_evolver.py b/tpot2/evolvers/steady_state_evolver.py index 952efa82..e0b9b593 100644 --- a/tpot2/evolvers/steady_state_evolver.py +++ b/tpot2/evolvers/steady_state_evolver.py @@ -1,17 +1,11 @@ #All abstract methods in the Evolutionary_Optimization module - -from abc import abstractmethod import tpot2 import typing import tqdm -from tpot2.individual_representations import BaseIndividual import time import numpy as np -import copy -import scipy import os import pickle -import statistics from tqdm.dask import TqdmCallback import distributed from dask.distributed import Client @@ -23,13 +17,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, @@ -76,10 +70,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 @@ -185,7 +179,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): @@ -305,17 +299,18 @@ def optimize(self): eval_error = "INVALID" else: #if future is not done - #check if the future has been running for too long, cancel the future - if time.time() - submitted_futures[completed_future]["time"] > self.max_eval_time_seconds*1.25: - completed_future.cancel() + if self.max_eval_time_seconds is not None: + #check if the future has been running for too long, cancel the future + if time.time() - submitted_futures[completed_future]["time"] > self.max_eval_time_seconds*1.25: + completed_future.cancel() - if self.verbose >= 4: - print(f'WARNING AN INDIVIDUAL TIMED OUT (Fallback): \n {submitted_futures[completed_future]} \n') + if self.verbose >= 4: + print(f'WARNING AN INDIVIDUAL TIMED OUT (Fallback): \n {submitted_futures[completed_future]} \n') - scores = [np.nan for _ in range(len(self.objective_names))] - eval_error = "TIMEOUT" - else: - continue #otherwise, continue to next future + scores = [np.nan for _ in range(len(self.objective_names))] + eval_error = "TIMEOUT" + else: + continue #otherwise, continue to next future @@ -348,7 +343,7 @@ def optimize(self): ############################### if self.verbose >= 3: sign = np.sign(self.objective_function_weights) - valid_df = self.population.evaluated_individuals[~self.population.evaluated_individuals[self.objective_names].isin(["TIMEOUT","INVALID"]).any(axis=1)][self.objective_names]*sign + valid_df = self.population.evaluated_individuals[~self.population.evaluated_individuals[["Eval Error"]].isin(["TIMEOUT","INVALID"]).any(axis=1)][self.objective_names]*sign cur_best_scores = valid_df.max(axis=0)*sign cur_best_scores = cur_best_scores.to_numpy() for i, obj in enumerate(self.objective_names): @@ -359,7 +354,7 @@ def optimize(self): #get sign of objective_function_weights sign = np.sign(self.objective_function_weights) #get best score for each objective - valid_df = self.population.evaluated_individuals[~self.population.evaluated_individuals[self.objective_names].isin(["TIMEOUT","INVALID"]).any(axis=1)][self.objective_names]*sign + valid_df = self.population.evaluated_individuals[~self.population.evaluated_individuals[["Eval Error"]].isin(["TIMEOUT","INVALID"]).any(axis=1)][self.objective_names]*sign cur_best_scores = valid_df.max(axis=0) cur_best_scores = cur_best_scores.to_numpy() #cur_best_scores = self.population.get_column(self.population.population, column_names=self.objective_names).max(axis=0)*sign #TODO this assumes the current population is the best @@ -428,13 +423,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) @@ -480,16 +475,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,14 +493,14 @@ 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 elif len(submitted_futures) < self.max_queue_size: initial_population = self.population.evaluated_individuals.iloc[:self.initial_population_size*3] - invalid_initial_population = initial_population[initial_population[self.objective_names].isin(["TIMEOUT","INVALID"]).any(axis=1)] + invalid_initial_population = initial_population[initial_population[["Eval Error"]].isin(["TIMEOUT","INVALID"]).any(axis=1)] if len(invalid_initial_population) >= self.initial_population_size*3: #if all individuals in the 3*initial population are invalid raise Exception("No individuals could be evaluated in the initial population. This may indicate a bug in the configuration, included models, or objective functions. Set verbose>=4 to see the errors that caused individuals to fail.") @@ -546,8 +541,8 @@ def optimize(self): # Step 7: Cleanup ############################### - self.population.remove_invalid_from_population(column_names=self.objective_names, invalid_value="INVALID") - self.population.remove_invalid_from_population(column_names=self.objective_names, invalid_value="TIMEOUT") + self.population.remove_invalid_from_population(column_names="Eval Error", invalid_value="INVALID") + self.population.remove_invalid_from_population(column_names="Eval Error", invalid_value="TIMEOUT") #done, cleanup futures @@ -562,7 +557,7 @@ def optimize(self): self._client.close() self._cluster.close() - tpot2.utils.get_pareto_frontier(self.population.evaluated_individuals, column_names=self.objective_names, weights=self.objective_function_weights, invalid_values=["TIMEOUT","INVALID"]) + tpot2.utils.get_pareto_frontier(self.population.evaluated_individuals, column_names=self.objective_names, weights=self.objective_function_weights) diff --git a/tpot2/graphsklearn.py b/tpot2/graphsklearn.py index 113c70aa..5f2f3b81 100644 --- a/tpot2/graphsklearn.py +++ b/tpot2/graphsklearn.py @@ -11,6 +11,8 @@ from sklearn.utils.validation import check_memory from sklearn.preprocessing import LabelEncoder +from sklearn.base import is_classifier, is_regressor + #labels - str #attributes - "instance" -> instance of the type @@ -62,47 +64,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 +99,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 +125,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) + if is_classifier(instance) or is_regressor(instance): + 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 @@ -189,7 +170,7 @@ def transform_sklearn_digraph(graph: nx.DiGraph, else: this_X = np.hstack([transformed_steps[child] for child in get_ordered_successors(graph, node)]) - if issubclass(type(instance), sklearn.base.RegressorMixin) or issubclass(type(instance), sklearn.base.ClassifierMixin): + if is_classifier(instance) or is_regressor(instance): this_method = _method_name(instance.__class__.__name__, instance, method) transformed = getattr(instance, this_method)(this_X) else: @@ -252,9 +233,7 @@ def __init__( graph, 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, + memory=None, use_label_encoder=False, **kwargs, ): @@ -275,14 +254,7 @@ def __init__( The prediction method to use for the inner classifiers or regressors. If 'auto', it will try to use predict_proba, decision_function, or predict in that order. 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. + Used to cache the input and outputs of nodes to prevent refitting or computationally heavy transformations. By default, no caching is performed. If a string is given, it is the path to the caching directory. 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. @@ -296,8 +268,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 +297,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 +306,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 +315,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 +336,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 +354,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 +366,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 +377,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/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 9ca1a9da..00000000 --- a/tpot2/individual_representations/graph_pipeline_individual/individual.py +++ /dev/null @@ -1,1225 +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): - try: - return f"" - - 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/objectives/complexity.py b/tpot2/objectives/complexity.py index b9167fa5..f4d5112d 100644 --- a/tpot2/objectives/complexity.py +++ b/tpot2/objectives/complexity.py @@ -1,78 +1,30 @@ from tpot2 import GraphPipeline - -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 - +import numpy as np +import sklearn +import warnings +from functools import reduce # Valid in Python 2.6+, required in Python 3 +import operator + +from sklearn.multiclass import OneVsOneClassifier, OneVsRestClassifier + +from sklearn.linear_model import SGDClassifier, LogisticRegression, SGDRegressor, Ridge, Lasso, ElasticNet, Lars, LassoLars, LassoLarsCV, RidgeCV, ElasticNetCV, PassiveAggressiveClassifier, ARDRegression +from sklearn.ensemble import BaggingClassifier, RandomForestClassifier, ExtraTreesClassifier, GradientBoostingClassifier, ExtraTreesRegressor, ExtraTreesClassifier, AdaBoostRegressor, AdaBoostClassifier, GradientBoostingRegressor,RandomForestRegressor, BaggingRegressor, ExtraTreesRegressor, HistGradientBoostingClassifier, HistGradientBoostingRegressor +from sklearn.neural_network import MLPClassifier, MLPRegressor +from sklearn.tree import DecisionTreeClassifier, DecisionTreeRegressor +from xgboost import XGBClassifier, XGBRegressor +from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor +from sklearn.svm import SVC, SVR, LinearSVR, LinearSVC +from lightgbm import LGBMClassifier, LGBMRegressor from sklearn.naive_bayes import GaussianNB, BernoulliNB, MultinomialNB -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 sklearn.ensemble import StackingClassifier, StackingRegressor, VotingClassifier, VotingRegressor - - -from xgboost import XGBRegressor -from functools import partial - - -#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 - - -from functools import partial -from tpot2.builtin_modules import ZeroCount, OneHotEncoder -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.discriminant_analysis import LinearDiscriminantAnalysis, QuadraticDiscriminantAnalysis +from sklearn.gaussian_process import GaussianProcessRegressor, GaussianProcessClassifier # MultinomialNB: params_MultinomialNB, - +from sklearn.base import is_classifier, is_regressor #https://scikit-learn.org/stable/auto_examples/applications/plot_model_complexity_influence.html def _count_nonzero_coefficients_and_intercept(est): @@ -93,12 +45,24 @@ def forest_complexity(forest): all_trees = all_trees.ravel() return sum(tree_complexity(tree) for tree in all_trees) + +def histgradientboosting_complexity(forest): + all_trees = np.array(forest._predictors) + if len(all_trees.shape)>1: + all_trees = all_trees.ravel() + return sum(len(tree.nodes)*5 for tree in all_trees) + def knn_complexity(knn): return knn.n_neighbors def support_vector_machine_complexity(svm): - return sum(svm.n_support_) + count = 0 + count += sum(svm.n_support_) + if svm.kernel == 'linear': + count += np.count_nonzero(svm.coef_) + + return count def sklearn_MLP_complexity(mlp): n_layers = len(mlp.coefs_) @@ -107,10 +71,18 @@ def sklearn_MLP_complexity(mlp): n_params += len(mlp.coefs_[i]) + len(mlp.intercepts_[i]) return n_params -# TODO use the complexity defined by XGBoost? -def calculate_xgb_model_complexity(model): - num_nodes = len(model.get_booster().trees_to_dataframe()) - return num_nodes*5 +def calculate_xgb_model_complexity(est): + df = est.get_booster().trees_to_dataframe() + cols_to_remove = ['Tree','Node', 'ID', 'count', 'Gain', 'Cover'] + #keeps ['Feature', 'Split', 'Yes', 'No', 'Missing', 'Category'] + #category is the specific category for a given feature. takes the place of split for categorical features + + for col in cols_to_remove: + if col in df.columns: + df = df.drop(col, axis=1) + + df = ~df.isna() + return df.sum().sum() def BernoulliNB_Complexity(model): num_coefficients = len(model.class_log_prior_) + len(model.feature_log_prob_) @@ -124,29 +96,129 @@ def MultinomialNB_Complexity(model): num_coefficients = len(model.class_log_prior_) + len(model.feature_log_prob_) return num_coefficients +def BaggingComplexity(est): + return sum([calculate_model_complexity(bagged) for bagged in est.estimators_]) + +def lightgbm_complexity(est): + df = est.booster_.trees_to_dataframe() + #remove tree_index and node_depth + cols_to_remove = ['node_index','tree_index', 'node_depth', 'count', 'parent_index'] + + for col in cols_to_remove: + if col in df.columns: + df = df.drop(col, axis=1) + + s = df.shape + return s[0] * s[1] + +def QuadraticDiscriminantAnalysis_complexity(est): + count = reduce(operator.mul,np.array(est.rotations_).shape) + reduce(operator.mul,np.array(est.scalings_).shape) + reduce(operator.mul,np.array(est.means_).shape) + reduce(operator.mul,np.array(est.priors_).shape) + return count + +#TODO consider the complexity of the kernel? +def gaussian_process_classifier_complexity(est): + if isinstance(est.base_estimator_, OneVsOneClassifier) or isinstance(est.base_estimator_, OneVsRestClassifier): + count = 0 + for clf in est.base_estimator_.estimators_: + count += len(clf.pi_) + return count + return len(est.base_estimator_.pi_) + +#TODO consider the complexity of the kernel? +def gaussian_process_regressor_complexity(est): + return len(est.alpha_) + +def adaboost_complexity(est): + return len(est.estimator_weights_) + sum(calculate_model_complexity(bagged) for bagged in est.estimators_) + +def ensemble_complexity(est): + return sum(calculate_model_complexity(bagged) for bagged in est.estimators_) + + complexity_objective_per_estimator = { LogisticRegression: _count_nonzero_coefficients_and_intercept, - RandomForestClassifier : forest_complexity, SGDClassifier: _count_nonzero_coefficients_and_intercept, + LinearSVC : _count_nonzero_coefficients_and_intercept, + LinearSVR : _count_nonzero_coefficients_and_intercept, + ARDRegression: _count_nonzero_coefficients_and_intercept, #When predicting mean, only coef and intercept used. Though there are more params for the variance/covariance matrix + LinearDiscriminantAnalysis: _count_nonzero_coefficients_and_intercept, + QuadraticDiscriminantAnalysis: QuadraticDiscriminantAnalysis_complexity, + + SGDRegressor: _count_nonzero_coefficients_and_intercept, + Ridge: _count_nonzero_coefficients_and_intercept, + Lasso: _count_nonzero_coefficients_and_intercept, + ElasticNet: _count_nonzero_coefficients_and_intercept, + Lars: _count_nonzero_coefficients_and_intercept, + LassoLars: _count_nonzero_coefficients_and_intercept, + LassoLarsCV: _count_nonzero_coefficients_and_intercept, + RidgeCV: _count_nonzero_coefficients_and_intercept, + ElasticNetCV: _count_nonzero_coefficients_and_intercept, + PassiveAggressiveClassifier: _count_nonzero_coefficients_and_intercept, + KNeighborsClassifier: knn_complexity, + KNeighborsRegressor: knn_complexity, + DecisionTreeClassifier: tree_complexity, + DecisionTreeRegressor: tree_complexity, + + GradientBoostingRegressor: forest_complexity, GradientBoostingClassifier: forest_complexity, + RandomForestClassifier : forest_complexity, + RandomForestRegressor: forest_complexity, + + HistGradientBoostingClassifier: histgradientboosting_complexity, + HistGradientBoostingRegressor: histgradientboosting_complexity, + + ExtraTreesRegressor: forest_complexity, ExtraTreesClassifier: forest_complexity, + XGBClassifier: calculate_xgb_model_complexity, + XGBRegressor: calculate_xgb_model_complexity, + SVC : support_vector_machine_complexity, - LinearSVC : _count_nonzero_coefficients_and_intercept, + SVR : support_vector_machine_complexity, + MLPClassifier: sklearn_MLP_complexity, + MLPRegressor: sklearn_MLP_complexity, + + BaggingRegressor: BaggingComplexity, + BaggingClassifier: BaggingComplexity, + BernoulliNB: BernoulliNB_Complexity, GaussianNB: GaussianNB_Complexity, + MultinomialNB: MultinomialNB_Complexity, + + LGBMClassifier: lightgbm_complexity, + LGBMRegressor: lightgbm_complexity, + + GaussianProcessClassifier: gaussian_process_classifier_complexity, + GaussianProcessRegressor: gaussian_process_regressor_complexity, + + AdaBoostClassifier: adaboost_complexity, + AdaBoostRegressor: adaboost_complexity, + + # StackingClassifier: ensemble_complexity, + # StackingRegressor: ensemble_complexity, + # VotingClassifier: ensemble_complexity, + # VotingRegressor: ensemble_complexity + } def calculate_model_complexity(est): - if isinstance(est, sklearn.pipeline.Pipeline) or isinstance(est, sklearn.pipeline.FeatureUnion): + if isinstance(est, sklearn.pipeline.Pipeline): return sum(calculate_model_complexity(estimator) for _,estimator in est.steps) + if isinstance(est, sklearn.pipeline.FeatureUnion): + return sum(calculate_model_complexity(estimator) for _,estimator in est.transformer_list) if isinstance(est, GraphPipeline): return sum(calculate_model_complexity(est.graph.nodes[node]['instance']) for node in est.graph.nodes) model_type = type(est) + + if is_classifier(est) or is_regressor(est): + if model_type not in complexity_objective_per_estimator: + warnings.warn(f"Complexity objective not defined for this classifier/regressor: {model_type}") + + if model_type in complexity_objective_per_estimator: return complexity_objective_per_estimator[model_type](est) #else, if is subclass of sklearn selector diff --git a/tpot2/objectives/number_of_nodes.py b/tpot2/objectives/number_of_nodes.py index 68d87437..a56368e2 100644 --- a/tpot2/objectives/number_of_nodes.py +++ b/tpot2/objectives/number_of_nodes.py @@ -1,2 +1,13 @@ -def number_of_nodes_objective(graph_pipeline): - return graph_pipeline.graph.number_of_nodes() \ No newline at end of file +from ..graphsklearn import GraphPipeline +from sklearn.pipeline import Pipeline +import sklearn + +def number_of_nodes_objective(est): + if isinstance(est, GraphPipeline): + return sum(number_of_nodes_objective(est.graph.nodes[node]["instance"]) for node in est.graph.nodes) + if isinstance(est, Pipeline): + return sum(number_of_nodes_objective(estimator) for _,estimator in est.steps) + if isinstance(est, sklearn.pipeline.FeatureUnion): + return sum(number_of_nodes_objective(estimator) for _,estimator in est.transformer_list) + + return 1 \ No newline at end of file diff --git a/tpot2/objectives/tests/test_complexity_objective.py b/tpot2/objectives/tests/test_complexity_objective.py new file mode 100644 index 00000000..e69de29b diff --git a/tpot2/objectives/tests/test_number_of_nodes.py b/tpot2/objectives/tests/test_number_of_nodes.py new file mode 100644 index 00000000..cfb94726 --- /dev/null +++ b/tpot2/objectives/tests/test_number_of_nodes.py @@ -0,0 +1,115 @@ +import pytest +import tpot2 +from sklearn.datasets import load_iris +import random +import sklearn + +from sklearn.svm import SVC +from sklearn.preprocessing import StandardScaler +from sklearn.linear_model import LogisticRegression +from sklearn.datasets import make_classification +from sklearn.model_selection import train_test_split +from sklearn.pipeline import Pipeline +import networkx as nx +import tpot2 +from tpot2 import GraphPipeline +import sklearn.metrics + +def test_number_of_nodes_objective_Graphpipeline(): + g = nx.DiGraph() + + g.add_node("scaler", instance=StandardScaler()) + g.add_node("svc", instance=SVC()) + g.add_node("LogisticRegression", instance=LogisticRegression()) + g.add_node("LogisticRegression2", instance=LogisticRegression()) + + g.add_edge("svc","scaler") + g.add_edge("LogisticRegression", "scaler") + g.add_edge("LogisticRegression2", "LogisticRegression") + g.add_edge("LogisticRegression2", "svc") + + est = GraphPipeline(g) + + assert tpot2.objectives.number_of_nodes.number_of_nodes_objective(est) == 4 + +def test_number_of_nodes_objective_Pipeline(): + pipe = Pipeline([("scaler", StandardScaler()), ("svc", SVC())]) + + assert tpot2.objectives.number_of_nodes.number_of_nodes_objective(pipe) == 2 + +def test_number_of_nodes_objective_not_pipeline_or_graphpipeline(): + assert tpot2.objectives.number_of_nodes.number_of_nodes_objective(SVC()) == 1 + assert tpot2.objectives.number_of_nodes.number_of_nodes_objective(StandardScaler()) == 1 + assert tpot2.objectives.number_of_nodes.number_of_nodes_objective(LogisticRegression()) == 1 + +def test_number_of_nodes_objective_pipeline_in_graphpipeline(): + g = nx.DiGraph() + + g.add_node("scaler", instance=StandardScaler()) + g.add_node("pipe", instance=Pipeline([("scaler", StandardScaler()), ("svc", SVC())])) + + g.add_edge("pipe","scaler") + + est = GraphPipeline(g) + + assert tpot2.objectives.number_of_nodes.number_of_nodes_objective(est) == 3 + +def test_number_of_nodes_objective_graphpipeline_in_pipeline(): + pipe = Pipeline([("scaler", StandardScaler()), ("svc", SVC())]) + + g = nx.DiGraph() + + g.add_node("scaler", instance=StandardScaler()) + g.add_node("svc", instance=SVC()) + g.add_node("LogisticRegression", instance=LogisticRegression()) + g.add_node("LogisticRegression2", instance=LogisticRegression()) + + g.add_edge("svc","scaler") + g.add_edge("LogisticRegression", "scaler") + g.add_edge("LogisticRegression2", "LogisticRegression") + g.add_edge("LogisticRegression2", "svc") + + est = GraphPipeline(g) + + pipe.steps.append(("graphpipe", est)) + + assert tpot2.objectives.number_of_nodes.number_of_nodes_objective(pipe) == 6 + + +def test_number_of_nodes_objective_graphpipeline_in_graphpipeline(): + g = nx.DiGraph() + + g.add_node("scaler", instance=StandardScaler()) + g.add_node("svc", instance=SVC()) + g.add_node("LogisticRegression", instance=LogisticRegression()) + g.add_node("LogisticRegression2", instance=LogisticRegression()) + + g.add_edge("svc","scaler") + g.add_edge("LogisticRegression", "scaler") + g.add_edge("LogisticRegression2", "LogisticRegression") + g.add_edge("LogisticRegression2", "svc") + + est = GraphPipeline(g) + + g2 = nx.DiGraph() + + g2.add_node("g1", instance=est) + g2.add_node("svc", instance=SVC()) + g2.add_node("LogisticRegression", instance=LogisticRegression()) + g2.add_node("LogisticRegression2", instance=LogisticRegression()) + + g2.add_edge("svc","g1") + g2.add_edge("LogisticRegression", "g1") + g2.add_edge("LogisticRegression2", "LogisticRegression") + g2.add_edge("LogisticRegression2", "svc") + + est2 = GraphPipeline(g2) + + assert tpot2.objectives.number_of_nodes.number_of_nodes_objective(est2) == 7 + +def test_number_of_nodes_objective_pipeline_in_pipeline(): + pipe = Pipeline([("scaler", StandardScaler()), ("svc", SVC())]) + + pipe2 = Pipeline([("pipe", pipe), ("svc", SVC())]) + + assert tpot2.objectives.number_of_nodes.number_of_nodes_objective(pipe2) == 3 diff --git a/tpot2/old_config_utils/__init__.py b/tpot2/old_config_utils/__init__.py new file mode 100644 index 00000000..e61b81fc --- /dev/null +++ b/tpot2/old_config_utils/__init__.py @@ -0,0 +1 @@ +from .old_config_utils import convert_config_dict_to_list, convert_config_dict_to_choicepipeline, convert_config_dict_to_graphpipeline \ No newline at end of file diff --git a/tpot2/old_config_utils/old_config_utils.py b/tpot2/old_config_utils/old_config_utils.py new file mode 100644 index 00000000..82892758 --- /dev/null +++ b/tpot2/old_config_utils/old_config_utils.py @@ -0,0 +1,140 @@ +from ConfigSpace import ConfigurationSpace +from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal +from ConfigSpace import EqualsCondition, OrConjunction, NotEqualsCondition, InCondition +from ..search_spaces.nodes.estimator_node import NONE_SPECIAL_STRING, TRUE_SPECIAL_STRING, FALSE_SPECIAL_STRING +from ..search_spaces.nodes import EstimatorNode +from ..search_spaces.pipelines import WrapperPipeline, ChoicePipeline, GraphPipeline +import ConfigSpace +import sklearn +from functools import partial +import inspect +import numpy as np + +def load_get_module_from_string(module_string): + module_name, class_name = module_string.rsplit('.', 1) + module = __import__(module_name, fromlist=[class_name]) + return getattr(module, class_name) + + +def hyperparameter_parser(hdict, function_params_conversion_dict): + d = hdict.copy() + d.update(function_params_conversion_dict) + return d + + + +def get_node_space(module_string, params): + method = load_get_module_from_string(module_string) + config_space = ConfigurationSpace() + sub_space = None + sub_space_name = None + + function_params_conversion_dict = {} + + if params is None: + return EstimatorNode(method=method, space=config_space) + + for param_name, param in params.items(): + if param is None: + config_space.add_hyperparameter(Categorical(param_name, [NONE_SPECIAL_STRING])) + + if isinstance(param, range): + param = list(param) + + if isinstance(param, list) or isinstance(param, np.ndarray): + if len(param) == 0: + p = param[0] + if p is None: + p = NONE_SPECIAL_STRING + elif type(p) == bool: + p = TRUE_SPECIAL_STRING if p else FALSE_SPECIAL_STRING + + config_space.add_hyperparameter(ConfigSpace.hyperparameters.Constant(param_name, p)) + else: + config_space.add_hyperparameter(Categorical(param_name, param)) + # if all(isinstance(i, int) for i in param): + # config_space.add_hyperparameter(Integer(param_name, (min(param), max(param)))) + # elif all(isinstance(i, float) for i in param): + # config_space.add_hyperparameter(Float(param_name, (min(param), max(param)))) + # else: + # config_space.add_hyperparameter(Categorical(param_name, param)) + elif isinstance(param, dict): #TPOT1 config dicts have dictionaries for values of hyperparameters that are either a function or an estimator + if len(param) > 1: + raise ValueError(f"Multiple items in dictionary entry for {param_name}") + + key = list(param.keys())[0] + + innermethod = load_get_module_from_string(key) + + if inspect.isclass(innermethod) and issubclass(innermethod, sklearn.base.BaseEstimator): #is an estimator + if sub_space is None: + sub_space_name = param_name + sub_space = get_node_space(key, param[key]) + else: + raise ValueError("Only multiple hyperparameters are estimators. Only one parameter ") + + else: #assume the key is a function and ignore the value + function_params_conversion_dict[param_name] = innermethod + + else: + # config_space.add_hyperparameter(Categorical(param_name, param)) + config_space.add_hyperparameter(ConfigSpace.hyperparameters.Constant(param_name, param)) + + parser=None + if len(function_params_conversion_dict) > 0: + parser = partial(hyperparameter_parser, function_params_conversion_dict) + + + if sub_space is None: + + if parser is not None: + return EstimatorNode(method=method, space=config_space, hyperparameter_parser=parser) + else: + return EstimatorNode(method=method, space=config_space) + + + else: + if parser is not None: + return WrapperPipeline(method=method, space=config_space, estimator_search_space=sub_space, wrapped_param_name=sub_space_name, hyperparameter_parser=parser) + else: + return WrapperPipeline(method=method, space=config_space, estimator_search_space=sub_space, wrapped_param_name=sub_space_name) + + +def convert_config_dict_to_list(config_dict): + search_spaces = [] + for key, value in config_dict.items(): + search_spaces.append(get_node_space(key, value)) + return search_spaces + + +def convert_config_dict_to_choicepipeline(config_dict): + search_spaces = [] + for key, value in config_dict.items(): + search_spaces.append(get_node_space(key, value)) + return ChoicePipeline(search_spaces) + +#Note doesn't convert estimators so they passthrough inputs like in TPOT1 +def convert_config_dict_to_graphpipeline(config_dict): + root_search_spaces = [] + inner_search_spaces = [] + + for key, value in config_dict.items(): + #if root + if issubclass(load_get_module_from_string(key), sklearn.base.ClassifierMixin) or issubclass(load_get_module_from_string(key), sklearn.base.RegressorMixin): + root_search_spaces.append(get_node_space(key, value)) + else: + inner_search_spaces.append(get_node_space(key, value)) + + if len(root_search_spaces) == 0: + Warning("No classifiers or regressors found, allowing any estimator to be the root node") + root_search_spaces = inner_search_spaces + + #merge inner and root search spaces + + inner_space = np.concatenate([root_search_spaces,inner_search_spaces]) + + root_space = ChoicePipeline(root_search_spaces) + inner_space = ChoicePipeline(inner_search_spaces) + + final_space = GraphPipeline(root_search_space=root_space, inner_search_space=inner_space) + return final_space \ No newline at end of file diff --git a/tpot2/population.py b/tpot2/population.py index 4e842eb9..b73606f5 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] @@ -91,19 +91,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] @@ -322,9 +322,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 = [] @@ -334,29 +334,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__}") @@ -372,7 +372,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] @@ -405,56 +405,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..306db6c2 --- /dev/null +++ b/tpot2/search_spaces/__init__.py @@ -0,0 +1,3 @@ +from .base import * +from . import nodes +from . import pipelines \ 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..3133057e --- /dev/null +++ b/tpot2/search_spaces/base.py @@ -0,0 +1,170 @@ +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 +import sklearn +import networkx as nx +from . import graph_utils +from typing import final +from abc import ABC, abstractmethod + + + + + +class SklearnIndividual(tpot2.BaseIndividual): + + def __init_subclass__(cls): + cls.crossover = cls.validate_same_type(cls.crossover) + + + def __init__(self,) -> None: + super().__init__() + + def mutate(self, rng=None): + return + + def crossover(self, other, rng=None, **kwargs): + return + + @final + def validate_same_type(func): + + def wrapper(self, other, rng=None, **kwargs): + if not isinstance(other, type(self)): + return False + return func(self, other, rng=None, **kwargs) + + return wrapper + + def export_pipeline(self) -> BaseEstimator: + return + + def unique_id(self): + """ + Returns a unique identifier for the individual. Used for preventing duplicate individuals from being evaluated. + """ + return self + + #TODO currently TPOT2 population class manually uses the unique_id to generate the index for the population data frame. + #alternatively, the index could be the individual itself, with the __eq__ and __hash__ methods implemented. + + # Though this breaks the graphpipeline. When a mutation is called, it changes the __eq__ and __hash__ outputs. + # Since networkx uses the hash and eq to determine if a node is already in the graph, this causes the graph thing that + # This is a new node not in the graph. But this could be changed if when the graphpipeline mutates nodes, + # it "replaces" the existing node with the mutated node. This would require a change in the graphpipeline class. + + # def __eq__(self, other): + # return self.unique_id() == other.unique_id() + + # def __hash__(self): + # return hash(self.unique_id()) + + #number of components in the pipeline + def get_size(self): + return 1 + + @final + def export_flattened_graphpipeline(self, **graphpipeline_kwargs) -> tpot2.GraphPipeline: + return flatten_to_graphpipeline(self.export_pipeline(), **graphpipeline_kwargs) + +class SklearnIndividualGenerator(): + def __init__(self,): + pass + + def generate(self, rng=None) -> SklearnIndividual: + pass + + +def flatten_graphpipeline(est): + flattened_full_graph = est.graph.copy() + + #put ests into the node label from the attributes + + flattened_full_graph = nx.relabel_nodes(flattened_full_graph, {n: flattened_full_graph.nodes[n]['instance'] for n in flattened_full_graph.nodes}) + + + remove_list = [] + for node in flattened_full_graph.nodes: + if isinstance(node, nx.DiGraph): + flattened = flatten_any(node) + + 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]) + + for node in remove_list: + flattened_full_graph.remove_node(node) + + return flattened_full_graph + +def flatten_pipeline(est): + graph = nx.DiGraph() + steps = [flatten_any(s[1]) for s in est.steps] + + #add steps to graph and connect them + for s in steps: + graph = nx.compose(graph, s) + + #connect leaves of each step to the roots of the next step + for i in range(len(steps)-1): + roots = graph_utils.get_roots(steps[i]) + leaves = graph_utils.get_leaves(steps[i+1]) + graph.add_edges_from([ (l,r) for l in leaves for r in roots]) + + + return graph + + + +def flatten_estimator(est): + graph = nx.DiGraph() + graph.add_node(est) + return graph + +def flatten_any(est): + if isinstance(est, tpot2.GraphPipeline): + return flatten_graphpipeline(est) + elif isinstance(est, sklearn.pipeline.Pipeline): + return flatten_pipeline(est) + else: + return flatten_estimator(est) + + +def flatten_to_graphpipeline(est, **graphpipeline_kwargs): + #rename nodes to string representation of the instance and put the instance in the node attributes + flattened_full_graph = flatten_any(est) + + instance_to_label = {} + label_to_instance = {} + for node in flattened_full_graph.nodes: + found_unique_label = False + i=1 + while not found_unique_label: + new_label = f"{node.__class__.__name__}_{i}" + if new_label not in label_to_instance: + found_unique_label = True + i+=1 + label_to_instance[new_label] = node + instance_to_label[node] = new_label + + flattened_full_graph = nx.relabel_nodes(flattened_full_graph, instance_to_label) + + for label, instance in label_to_instance.items(): + flattened_full_graph.nodes[label]["instance"] = instance + + return tpot2.GraphPipeline(flattened_full_graph, **graphpipeline_kwargs) \ No newline at end of file diff --git a/tpot2/individual_representations/graph_pipeline_individual/graph_utils/graph_utils.py b/tpot2/search_spaces/graph_utils.py similarity index 92% rename from tpot2/individual_representations/graph_pipeline_individual/graph_utils/graph_utils.py rename to tpot2/search_spaces/graph_utils.py index 1956d49d..cb85a571 100644 --- a/tpot2/individual_representations/graph_pipeline_individual/graph_utils/graph_utils.py +++ b/tpot2/search_spaces/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) @@ -96,4 +96,6 @@ def select_nodes_randomly(g1, g2, rng_=None): rng.shuffle(sorted_other_nodes_list) for node1 in sorted_self_nodes_list: for node2 in sorted_other_nodes_list: + if node1 is node2: + continue yield node1, node2 \ 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..4026d02c --- /dev/null +++ b/tpot2/search_spaces/nodes/__init__.py @@ -0,0 +1,3 @@ +from .estimator_node import * +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 new file mode 100644 index 00000000..50d698f3 --- /dev/null +++ b/tpot2/search_spaces/nodes/estimator_node.py @@ -0,0 +1,113 @@ +# try https://automl.github.io/ConfigSpace/main/api/hyperparameters.html + +import numpy as np +from ..base import SklearnIndividual, SklearnIndividualGenerator +from ConfigSpace import ConfigurationSpace +from typing import final + +NONE_SPECIAL_STRING = "" +TRUE_SPECIAL_STRING = "" +FALSE_SPECIAL_STRING = "" + + +def default_hyperparameter_parser(params:dict) -> dict: + return params + + +class EstimatorNodeIndividual(SklearnIndividual): + """ + Note that ConfigurationSpace does not support None as a parameter. Instead, use the special string "". TPOT will automatically replace instances of this string with the Python None. + + Parameters + ---------- + method : type + The class of the estimator to be used + + space : ConfigurationSpace|dict + The hyperparameter space to be used. If a dict is passed, hyperparameters are fixed and not learned. + + """ + def __init__(self, method: type, + 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? + hyperparameter_parser: callable = None, + rng=None) -> None: + super().__init__() + self.method = method + self.space = space + + if hyperparameter_parser is None: + self.hyperparameter_parser = default_hyperparameter_parser + else: + self.hyperparameter_parser = hyperparameter_parser + + if isinstance(space, dict): + self.hyperparameters = space + else: + rng = np.random.default_rng(rng) + self.space.seed(rng.integers(0, 2**32)) + self.hyperparameters = dict(self.space.sample_configuration()) + + self.check_hyperparameters_for_None() + + 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 = dict(self.space.sample_configuration()) + + self.check_hyperparameters_for_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 + + #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] + + self.check_hyperparameters_for_None() + + return True + + def check_hyperparameters_for_None(self): + for key, value in self.hyperparameters.items(): + #if string + if isinstance(value, str): + if value == NONE_SPECIAL_STRING: + self.hyperparameters[key] = None + elif value == TRUE_SPECIAL_STRING: + self.hyperparameters[key] = True + elif value == FALSE_SPECIAL_STRING: + self.hyperparameters[key] = False + + @final #this method should not be overridden, instead override hyperparameter_parser + def export_pipeline(self, **kwargs): + return self.method(**self.hyperparameter_parser(self.hyperparameters)) + + def unique_id(self): + #return a dictionary of the method and the hyperparameters + method_str = self.method.__name__ + params = list(self.hyperparameters.keys()) + params = sorted(params) + + id_str = f"{method_str}({', '.join([f'{param}={self.hyperparameters[param]}' for param in params])})" + + return id_str + +class EstimatorNode(SklearnIndividualGenerator): + def __init__(self, method, space, hyperparameter_parser=default_hyperparameter_parser): + self.method = method + self.space = space + self.hyperparameter_parser = hyperparameter_parser + + def generate(self, rng=None): + return EstimatorNodeIndividual(self.method, self.space, hyperparameter_parser=self.hyperparameter_parser, rng=rng) \ No newline at end of file diff --git a/tpot2/search_spaces/nodes/fss_node.py b/tpot2/search_spaces/nodes/fss_node.py new file mode 100644 index 00000000..4dda0d92 --- /dev/null +++ b/tpot2/search_spaces/nodes/fss_node.py @@ -0,0 +1,80 @@ +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): + id_str = "FeatureSetSelector({0})".format(self.selected_subset_name) + return id_str + + +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 new file mode 100644 index 00000000..f9c4892a --- /dev/null +++ b/tpot2/search_spaces/nodes/genetic_feature_selection.py @@ -0,0 +1,185 @@ +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, + rng=None, + ): + + self.start_p = start_p + self.mutation_rate = mutation_rate + self.crossover_rate = crossover_rate + self.mutation_rate_rate = 0 + self.crossover_rate_rate = 0 + + + + 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 + + # check if there are no features selected, if so select one + if sum(self.mask) == 0: + index = rng.choice(len(self.mask)) + self.mask[index] = True + + 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): + mask_idexes = np.where(self.mask)[0] + id_str = ','.join([str(i) for i in mask_idexes]) + return id_str + + +class GeneticFeatureSelectorNode(SklearnIndividualGenerator): + def __init__(self, + n_features, + start_p=0.2, + mutation_rate = 0.5, + crossover_rate = 0.5, + mutation_rate_rate = 0, + crossover_rate_rate = 0, + ): + + self.n_features = n_features + 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 + + + def generate(self, rng=None) -> SklearnIndividual: + return GeneticFeatureSelectorIndividual( mask=self.n_features, + 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=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..2c17a950 --- /dev/null +++ b/tpot2/search_spaces/pipelines/__init__.py @@ -0,0 +1,8 @@ +from .choice import * +from .dynamic_linear import * +from .sequential import * +from .graph import * +from .tree import * +from .wrapper import * +from .dynamicunion import * +from .union 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..25051aa0 --- /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, search_spaces : List[SklearnIndividualGenerator], rng=None) -> None: + super().__init__() + + self.search_spaces = search_spaces + self.node = np.random.default_rng(rng).choice(self.search_spaces).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.search_spaces).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, search_spaces : List[SklearnIndividualGenerator] ) -> None: + self.search_spaces = search_spaces + + """ + Takes in a list of search spaces. Will select one node from the search space. + + """ + + def generate(self, rng=None): + return ChoicePipelineIndividual(self.search_spaces, rng=rng) \ 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..528ec7c4 --- /dev/null +++ b/tpot2/search_spaces/pipelines/dynamic_linear.py @@ -0,0 +1,151 @@ +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 +from ..tuple_index import TupleIndex + +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, max_length: int , rng=None) -> None: + super().__init__() + + rng = np.random.default_rng(rng) + + self.search_space = search_space + self.min_length = 1 + self.max_length = max_length + + self.pipeline = self._generate_pipeline(rng) + + def _generate_pipeline(self, rng=None): + rng = np.random.default_rng(rng) + pipeline = [] + length = rng.integers(self.min_length, self.max_length) + length = min(length, 3) + + for _ in range(length): + pipeline.append(self.search_space.generate(rng)) + return pipeline + + + def mutate(self, rng=None): + rng = np.random.default_rng(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(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(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(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 + + rng = np.random.default_rng(rng) + cx_funcs = [self._crossover_swap_multiple_nodes, self._crossover_node] + + rng.shuffle(cx_funcs) + for cx_func in cx_funcs: + if cx_func(other, rng): + return True + + return False + + def _crossover_swap_multiple_nodes(self, other, rng): + rng = np.random.default_rng(rng) + + max_steps = int(min(len(self.pipeline), len(other.pipeline))/2) + max_steps = max(max_steps, 1) + + if max_steps == 1: + n_steps_to_swap = 1 + else: + n_steps_to_swap = rng.integers(1, max_steps) + + other_indexes_to_take = rng.choice(len(other.pipeline), n_steps_to_swap, replace=False) + self_indexes_to_replace = rng.choice(len(self.pipeline), n_steps_to_swap, replace=False) + + # self.pipeline[self_indexes_to_replace], other.pipeline[other_indexes_to_take] = other.pipeline[other_indexes_to_take], self.pipeline[self_indexes_to_replace] + + for self_idx, other_idx in zip(self_indexes_to_replace, other_indexes_to_take): + self.pipeline[self_idx], other.pipeline[other_idx] = other.pipeline[other_idx], self.pipeline[self_idx] + + return True + + def _crossover_swap_node(self, other, rng): + if len(self.pipeline) != len(other.pipeline): + return False + + if len(self.pipeline) < 2: + return False + + rng = np.random.default_rng(rng) + idx = rng.integers(1,len(self.pipeline)) + + self.pipeline[idx], other.pipeline[idx] = other.pipeline[idx], self.pipeline[idx] + return True + + def _crossover_node(self, other, rng): + rng = np.random.default_rng(rng) + + pipeline1_indexes= list(range(len(self.pipeline))) + pipeline2_indexes= list(range(len(other.pipeline))) + + rng.shuffle(pipeline1_indexes) + rng.shuffle(pipeline2_indexes) + + crossover_success = False + for idx1, idx2 in zip(pipeline1_indexes, pipeline2_indexes): + if self.pipeline[idx1].crossover(other.pipeline[idx2], rng): + crossover_success = True + return crossover_success + + def export_pipeline(self): + return sklearn.pipeline.make_pipeline(*[step.export_pipeline() for step in self.pipeline]) + + def unique_id(self): + l = [step.unique_id() for step in self.pipeline] + l = ["DynamicLinearPipeline"] + l + return TupleIndex(tuple(l)) + + +class DynamicLinearPipeline(SklearnIndividualGenerator): + def __init__(self, search_space : SklearnIndividualGenerator, max_length: int ) -> None: + self.search_space = search_space + 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.max_length, rng=rng) \ No newline at end of file diff --git a/tpot2/search_spaces/pipelines/dynamicunion.py b/tpot2/search_spaces/pipelines/dynamicunion.py new file mode 100644 index 00000000..8d8772eb --- /dev/null +++ b/tpot2/search_spaces/pipelines/dynamicunion.py @@ -0,0 +1,163 @@ +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 ..tuple_index import TupleIndex + +class DynamicUnionPipelineIndividual(SklearnIndividual): + """ + Takes in one search space. + Will produce a FeatureUnion of up to max_estimators number of steps. + The output of the FeatureUnion will the all of the steps concatenated together. + + """ + + def __init__(self, search_space : SklearnIndividualGenerator, max_estimators=None, allow_repeats=False, rng=None) -> None: + super().__init__() + self.search_space = search_space + + if max_estimators is None: + self.max_estimators = np.inf + else: + self.max_estimators = max_estimators + + self.allow_repeats = allow_repeats + + self.union_dict = {} + + if self.max_estimators == np.inf: + init_max = 3 + else: + init_max = self.max_estimators + + rng = np.random.default_rng(rng) + + for _ in range(rng.integers(1, init_max)): + self._mutate_add_step(rng) + + + def mutate(self, rng=None): + rng = np.random.default_rng(rng) + mutation_funcs = [self._mutate_add_step, self._mutate_remove_step, self._mutate_replace_step, self._mutate_note] + rng.shuffle(mutation_funcs) + for mutation_func in mutation_funcs: + if mutation_func(rng): + return True + + def _mutate_add_step(self, rng): + rng = np.random.default_rng(rng) + max_attempts = 10 + if len(self.union_dict) < self.max_estimators: + for _ in range(max_attempts): + new_step = self.search_space.generate(rng) + if new_step.unique_id() not in self.union_dict: + self.union_dict[new_step.unique_id()] = new_step + return True + return False + + def _mutate_remove_step(self, rng): + rng = np.random.default_rng(rng) + if len(self.union_dict) > 1: + self.union_dict.pop( rng.choice(list(self.union_dict.keys()))) + return True + return False + + def _mutate_replace_step(self, rng): + rng = np.random.default_rng(rng) + changed = self._mutate_remove_step(rng) or self._mutate_add_step(rng) + return changed + + #TODO mutate one step or multiple? + def _mutate_note(self, rng): + rng = np.random.default_rng(rng) + changed = False + values = list(self.union_dict.values()) + for step in values: + if rng.random() < 0.5: + changed = step.mutate(rng) or changed + + self.union_dict = {step.unique_id(): step for step in values} + + return changed + + + def crossover(self, other, rng=None): + rng = np.random.default_rng(rng) + + cx_funcs = [self._crossover_swap_multiple_nodes, self._crossover_node] + rng.shuffle(cx_funcs) + for cx_func in cx_funcs: + if cx_func(other, rng): + return True + + return False + + + def _crossover_swap_multiple_nodes(self, other, rng): + rng = np.random.default_rng(rng) + self_values = list(self.union_dict.values()) + other_values = list(other.union_dict.values()) + + rng.shuffle(self_values) + rng.shuffle(other_values) + + self_idx = rng.integers(0,len(self_values)) + other_idx = rng.integers(0,len(other_values)) + + #Note that this is not one-point-crossover since the sequence doesn't matter. this is just a quick way to swap multiple random items + self_values[:self_idx], other_values[:other_idx] = other_values[:other_idx], self_values[:self_idx] + + self.union_dict = {step.unique_id(): step for step in self_values} + other.union_dict = {step.unique_id(): step for step in other_values} + + return True + + + def _crossover_node(self, other, rng): + rng = np.random.default_rng(rng) + + changed = False + self_values = list(self.union_dict.values()) + other_values = list(other.union_dict.values()) + + rng.shuffle(self_values) + rng.shuffle(other_values) + + for self_step, other_step in zip(self_values, other_values): + if rng.random() < 0.5: + changed = self_step.crossover(other_step, rng) or changed + + self.union_dict = {step.unique_id(): step for step in self_values} + other.union_dict = {step.unique_id(): step for step in other_values} + + return changed + + def export_pipeline(self): + values = list(self.union_dict.values()) + return sklearn.pipeline.make_union(*[step.export_pipeline() for step in values]) + + def unique_id(self): + values = list(self.union_dict.values()) + l = [step.unique_id() for step in values] + # if all items are strings, then sort them + if all([isinstance(x, str) for x in l]): + l.sort() + l = ["FeatureUnion"] + l + return TupleIndex(frozenset(l)) + +class DynamicUnionPipeline(SklearnIndividualGenerator): + def __init__(self, search_spaces : List[SklearnIndividualGenerator],max_estimators=None, allow_repeats=False ) -> 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 + self.max_estimators = max_estimators + self.allow_repeats = allow_repeats + + def generate(self, rng=None): + return DynamicUnionPipelineIndividual(self.search_spaces, max_estimators=self.max_estimators, allow_repeats=self.allow_repeats, rng=rng) \ 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..fc769b1c --- /dev/null +++ b/tpot2/search_spaces/pipelines/graph.py @@ -0,0 +1,819 @@ +import tpot2 +import numpy as np +from typing import Generator, List, Tuple, Union +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 +from typing import Union, Callable +import sklearn +from functools import partial +import random + +class GraphPipelineIndividual(SklearnIndividual): + """ + Defines a search space of pipelines in the shape of a Directed Acyclic Graphs. The search spaces for root, leaf, and inner nodes can be defined separately if desired. + Each graph will have a single root serving as the final estimator which is drawn from the `root_search_space`. If the `leaf_search_space` is defined, all leaves + in the pipeline will be drawn from that search space. If the `leaf_search_space` is not defined, all leaves will be drawn from the `inner_search_space`. + Nodes that are not leaves or roots will be drawn from the `inner_search_space`. If the `inner_search_space` is not defined, there will be no inner nodes. + + `cross_val_predict_cv`, `method`, `memory`, and `use_label_encoder` are passed to the GraphPipeline object when the pipeline is exported and not directly used in the search space. + + Exports to a GraphPipeline object. + + Parameters + ---------- + + root_search_space: SklearnIndividualGenerator + The search space for the root node of the graph. This node will be the final estimator in the pipeline. + + inner_search_space: SklearnIndividualGenerator, optional + The search space for the inner nodes of the graph. If not defined, there will be no inner nodes. + + leaf_search_space: SklearnIndividualGenerator, optional + The search space for the leaf nodes of the graph. If not defined, the leaf nodes will be drawn from the inner_search_space. + + crossover_same_depth: bool, optional + If True, crossover will only occur between nodes at the same depth in the graph. If False, crossover will occur between nodes at any depth. + + cross_val_predict_cv: int, cross-validation generator or an iterable, optional + Determines the cross-validation splitting strategy used in inner classifiers or regressors + + method: str, optional + The prediction method to use for the inner classifiers or regressors. If 'auto', it will try to use predict_proba, decision_function, or predict in that order. + + memory: str or object with the joblib.Memory interface, optional + Used to cache the input and outputs of nodes to prevent refitting or computationally heavy transformations. By default, no caching is performed. If a string is given, it is the path to the caching directory. + + 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. + Can also be a sklearn.preprocessing.LabelEncoder object. If so, that label encoder is used. + + rng: int, RandomState instance or None, optional + Seed for sampling the first graph instance. + + """ + + def __init__( + self, + root_search_space: SklearnIndividualGenerator, + leaf_search_space: SklearnIndividualGenerator = None, + inner_search_space: SklearnIndividualGenerator = None, + max_size: int = np.inf, + crossover_same_depth: bool = False, + cross_val_predict_cv: Union[int, Callable] = 0, #signature function(estimator, X, y=none) + method: str = 'auto', + memory=None, + use_label_encoder: bool = False, + rng=None): + + 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.cross_val_predict_cv = cross_val_predict_cv + self.method = method + self.memory = memory + self.use_label_encoder = use_label_encoder + + 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) + + if self.inner_search_space is None and self.leaf_search_space is None: + self.mutate_methods_list = [self._mutate_node] + self.crossover_methods_list = [self._crossover_swap_branch,]#[self._crossover_swap_branch, self._crossover_swap_node, self._crossover_take_branch] #TODO self._crossover_nodes, + + else: + self.mutate_methods_list = [self._mutate_insert_leaf, self._mutate_insert_inner_node, self._mutate_remove_node, self._mutate_node, self._mutate_insert_bypass_node] + self.crossover_methods_list = [self._crossover_swap_branch, self._crossover_nodes, self._crossover_take_branch ]#[self._crossover_swap_branch, self._crossover_swap_node, self._crossover_take_branch] #TODO self._crossover_nodes, + + self.merge_duplicated_nodes_toggle = True + + self.graphkey = None + + 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): + + 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 + + self.graphkey = None + + 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): + """ + Finds an edge in the graph and inserts a new node between the two nodes. Removes the edge between the two nodes. + """ + 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): + """ + Pick two nodes (doesn't necessarily need to be connected). Create a new node. connect one node to the new node and the new node to the other node. + Does not remove any edges. + """ + 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 + + if finished: + self.graphkey = None + + 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_search_space, 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): + if not node1_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_search_space, 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_search_space 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 randomly 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: + if node1.crossover(node2): + return True + + + #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)))>0: + 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 + + + + #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. + ''' + 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: + continue + + #If nodes are same class/hyperparameters + if node.unique_id() == other_node.unique_id(): + 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): + 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, memory=self.memory, use_label_encoder=self.use_label_encoder, method=self.method, cross_val_predict_cv=self.cross_val_predict_cv) + + + 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): + if self.graphkey is None: + #copy self.graph + new_graph = self.graph.copy() + for n in new_graph.nodes: + new_graph.nodes[n]['label'] = n.unique_id() + + new_graph = nx.convert_node_labels_to_integers(new_graph) + self.graphkey = GraphKey(new_graph) + + return self.graphkey + + +class GraphPipeline(SklearnIndividualGenerator): + def __init__(self, + root_search_space: SklearnIndividualGenerator, + leaf_search_space: SklearnIndividualGenerator = None, + inner_search_space: SklearnIndividualGenerator = None, + max_size: int = np.inf, + crossover_same_depth: bool = False, + cross_val_predict_cv: Union[int, Callable] = 0, #signature function(estimator, X, y=none) + method: str = 'auto', + memory=None, + use_label_encoder: bool = False): + + """ + Defines a search space of pipelines in the shape of a Directed Acyclic Graphs. The search spaces for root, leaf, and inner nodes can be defined separately if desired. + Each graph will have a single root serving as the final estimator which is drawn from the `root_search_space`. If the `leaf_search_space` is defined, all leaves + in the pipeline will be drawn from that search space. If the `leaf_search_space` is not defined, all leaves will be drawn from the `inner_search_space`. + Nodes that are not leaves or roots will be drawn from the `inner_search_space`. If the `inner_search_space` is not defined, there will be no inner nodes. + + `cross_val_predict_cv`, `method`, `memory`, and `use_label_encoder` are passed to the GraphPipeline object when the pipeline is exported and not directly used in the search space. + + Exports to a GraphPipeline object. + + Parameters + ---------- + + root_search_space: SklearnIndividualGenerator + The search space for the root node of the graph. This node will be the final estimator in the pipeline. + + inner_search_space: SklearnIndividualGenerator, optional + The search space for the inner nodes of the graph. If not defined, there will be no inner nodes. + + leaf_search_space: SklearnIndividualGenerator, optional + The search space for the leaf nodes of the graph. If not defined, the leaf nodes will be drawn from the inner_search_space. + + crossover_same_depth: bool, optional + If True, crossover will only occur between nodes at the same depth in the graph. If False, crossover will occur between nodes at any depth. + + cross_val_predict_cv: int, cross-validation generator or an iterable, optional + Determines the cross-validation splitting strategy used in inner classifiers or regressors + + method: str, optional + The prediction method to use for the inner classifiers or regressors. If 'auto', it will try to use predict_proba, decision_function, or predict in that order. + + memory: str or object with the joblib.Memory interface, optional + Used to cache the input and outputs of nodes to prevent refitting or computationally heavy transformations. By default, no caching is performed. If a string is given, it is the path to the caching directory. + + 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. + Can also be a sklearn.preprocessing.LabelEncoder object. If so, that label encoder is used. + + """ + + + 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.cross_val_predict_cv = cross_val_predict_cv + self.method = method + self.memory = memory + self.use_label_encoder = use_label_encoder + + def generate(self, rng=None): + rng = np.random.default_rng(rng) + ind = GraphPipelineIndividual(self.root_search_space, self.leaf_search_space, self.inner_search_space, self.max_size, self.crossover_same_depth, + self.cross_val_predict_cv, self.method, self.memory, self.use_label_encoder, rng=rng) + # if user specified limit, grab a random number between that limit + + if self.max_size is None or self.max_size == np.inf: + n_nodes = rng.integers(1, 5) + else: + n_nodes = min(rng.integers(1, self.max_size), 5) + + starting_ops = [] + if self.inner_search_space is not None: + starting_ops.append(ind._mutate_insert_inner_node) + if self.leaf_search_space is not None or self.inner_search_space 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) + + ind._merge_duplicated_nodes() + + return ind + + + + + +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]) + diff --git a/tpot2/search_spaces/pipelines/sequential.py b/tpot2/search_spaces/pipelines/sequential.py new file mode 100644 index 00000000..75bad8d2 --- /dev/null +++ b/tpot2/search_spaces/pipelines/sequential.py @@ -0,0 +1,152 @@ +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 ..tuple_index import TupleIndex + +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], memory=None, rng=None) -> None: + super().__init__() + self.search_spaces = search_spaces + self.memory = memory + self.pipeline = [] + + for space in self.search_spaces: + self.pipeline.append(space.generate(rng)) + + self.pipeline = np.array(self.pipeline) + + #TODO, mutate all steps or just one? + def mutate(self, rng=None): + rng = np.random.default_rng(rng) + + # mutated = False + # for step in self.pipeline: + # if rng.random() < 0.5: + # if step.mutate(rng): + # mutated = True + # return mutated + + 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 + + rng = np.random.default_rng(rng) + cx_funcs = [self._crossover_swap_multiple_nodes, self._crossover_swap_segment, self._crossover_node] + + rng.shuffle(cx_funcs) + for cx_func in cx_funcs: + if cx_func(other, rng): + return True + + return False + + def _crossover_swap_node(self, other, rng): + if len(self.pipeline) != len(other.pipeline): + return False + + + rng = np.random.default_rng(rng) + idx = rng.integers(1,len(self.pipeline)) + + self.pipeline[idx], other.pipeline[idx] = other.pipeline[idx], self.pipeline[idx] + return True + + def _crossover_swap_multiple_nodes(self, other, rng): + + if len(self.pipeline) != len(other.pipeline): + return False + + if len(self.pipeline) < 2: + return False + + rng = np.random.default_rng(rng) + + max_steps = int(min(len(self.pipeline), len(other.pipeline))/2) + max_steps = max(max_steps, 1) + + if max_steps == 1: + n_steps_to_swap = 1 + else: + n_steps_to_swap = rng.integers(1, max_steps) + + indexes_to_swap = rng.choice(len(other.pipeline), n_steps_to_swap, replace=False) + + for idx in indexes_to_swap: + self.pipeline[idx], other.pipeline[idx] = other.pipeline[idx], self.pipeline[idx] + + + return True + + def _crossover_swap_segment(self, other, rng): + if len(self.pipeline) != len(other.pipeline): + return False + + if len(self.pipeline) < 2: + return False + + rng = np.random.default_rng(rng) + idx = rng.integers(1,len(self.pipeline)) + + left = rng.choice([True, False]) + if left: + self.pipeline[:idx], other.pipeline[:idx] = other.pipeline[:idx], self.pipeline[:idx] + else: + self.pipeline[idx:], other.pipeline[idx:] = other.pipeline[idx:], self.pipeline[idx:] + + return True + + def _crossover_node(self, other, rng): + rng = np.random.default_rng(rng) + + # crossover_success = False + # for idx in range(len(self.pipeline)): + # if rng.random() < 0.5: + # if self.pipeline[idx].crossover(other.pipeline[idx], rng): + # crossover_success = True + + # return crossover_success + + + crossover_success = False + for idx in range(len(self.pipeline)): + if rng.random() < 0.5: + if self.pipeline[idx].crossover(other.pipeline[idx], rng): + crossover_success = True + + return crossover_success + + def export_pipeline(self): + return sklearn.pipeline.make_pipeline(*[step.export_pipeline() for step in self.pipeline], memory=self.memory) + + def unique_id(self): + l = [step.unique_id() for step in self.pipeline] + l = ["SequentialPipeline"] + l + return TupleIndex(tuple(l)) + + + + +class SequentialPipeline(SklearnIndividualGenerator): + def __init__(self, search_spaces : List[SklearnIndividualGenerator], memory=None ) -> 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 + self.memory = memory + + def generate(self, rng=None): + return SequentialPipelineIndividual(self.search_spaces, memory=self.memory, rng=rng) \ No newline at end of file diff --git a/tpot2/search_spaces/pipelines/tests/test_graphspace.py b/tpot2/search_spaces/pipelines/tests/test_graphspace.py new file mode 100644 index 00000000..b90ff2fe --- /dev/null +++ b/tpot2/search_spaces/pipelines/tests/test_graphspace.py @@ -0,0 +1,46 @@ +# Test all nodes have all dictionaries +import pytest +import tpot2 + +import tpot2 +from ConfigSpace import ConfigurationSpace +from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal +from sklearn.neighbors import KNeighborsClassifier +from sklearn.linear_model import LogisticRegression +from sklearn.tree import DecisionTreeClassifier +from sklearn.preprocessing import StandardScaler + + +def test_merge_duplicate_nodes(): + knn_configspace = {} + standard_scaler_configspace = {} + + knn_node = tpot2.search_spaces.nodes.EstimatorNode( + method = KNeighborsClassifier, + space = knn_configspace, + ) + + scaler_node = tpot2.search_spaces.nodes.EstimatorNode( + method = StandardScaler, + space = standard_scaler_configspace, + ) + + + graph_search_space = tpot2.search_spaces.pipelines.GraphPipeline( + root_search_space= knn_node, + leaf_search_space = scaler_node, + inner_search_space = None, + max_size = 10, + ) + + ind = graph_search_space.generate() + + # all of these leaves should be identical + ind._mutate_insert_leaf() + ind._mutate_insert_leaf() + ind._mutate_insert_leaf() + ind._mutate_insert_leaf() + + ind._merge_duplicated_nodes() + + assert len(ind.graph.nodes) == 2 \ 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..b2e642e7 --- /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] + self.merge_duplicated_nodes_toggle = False + + + +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) -> 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/union.py b/tpot2/search_spaces/pipelines/union.py new file mode 100644 index 00000000..811ef38b --- /dev/null +++ b/tpot2/search_spaces/pipelines/union.py @@ -0,0 +1,81 @@ +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 ..tuple_index import TupleIndex + +class UnionPipelineIndividual(SklearnIndividual): + """ + Takes in a list of search spaces. each space is a list of SklearnIndividualGenerators. + Will produce a FeatureUnion pipeline. Each step in the pipeline will correspond to the the search space provided in the same index. + The resulting pipeline will be a FeatureUnion of the steps in the pipeline. + + """ + + def __init__(self, search_spaces : List[SklearnIndividualGenerator], rng=None) -> None: + super().__init__() + self.search_spaces = search_spaces + + self.pipeline = [] + for space in self.search_spaces: + self.pipeline.append(space.generate(rng)) + + def mutate(self, rng=None): + rng = np.random.default_rng(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 + rng = np.random.default_rng(rng) + + cx_funcs = [self._crossover_node, self._crossover_swap_node] + rng.shuffle(cx_funcs) + for cx_func in cx_funcs: + if cx_func(other, rng): + return True + + return False + + def _crossover_swap_node(self, other, rng): + rng = np.random.default_rng(rng) + idx = rng.integers(1,len(self.pipeline)) + + self.pipeline[idx], other.pipeline[idx] = other.pipeline[idx], self.pipeline[idx] + return True + + def _crossover_node(self, other, rng): + rng = np.random.default_rng(rng) + + crossover_success = False + for idx in range(len(self.pipeline)): + if rng.random() < 0.5: + if self.pipeline[idx].crossover(other.pipeline[idx], rng): + crossover_success = True + + return crossover_success + + def export_pipeline(self): + return sklearn.pipeline.make_union(*[step.export_pipeline() for step in self.pipeline]) + + def unique_id(self): + l = [step.unique_id() for step in self.pipeline] + l = ["FeatureUnion"] + l + return TupleIndex(tuple(l)) + + +class UnionPipeline(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 UnionPipelineIndividual(self.search_spaces, 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..d61bc5f3 --- /dev/null +++ b/tpot2/search_spaces/pipelines/wrapper.py @@ -0,0 +1,151 @@ + +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 +from ..tuple_index import TupleIndex + +NONE_SPECIAL_STRING = "" +TRUE_SPECIAL_STRING = "" +FALSE_SPECIAL_STRING = "" + + +class WrapperPipelineIndividual(SklearnIndividual): + def __init__( + self, + method: type, + space: ConfigurationSpace, + estimator_search_space: SklearnIndividualGenerator, + hyperparameter_parser: callable = None, + wrapped_param_name: str = None, + rng=None) -> None: + super().__init__() + + self.method = method + self.space = space + self.estimator_search_space = estimator_search_space + self.hyperparameters_parser = hyperparameter_parser + self.wrapped_param_name = wrapped_param_name + + rng = np.random.default_rng(rng) + self.node = self.estimator_search_space.generate(rng) + + if isinstance(space, dict): + self.hyperparameters = space + else: + rng = np.random.default_rng(rng) + self.space.seed(rng.integers(0, 2**32)) + self.hyperparameters = dict(self.space.sample_configuration()) + + self.check_hyperparameters_for_None() + + 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): + if isinstance(self.space, dict): + return False + rng = np.random.default_rng(rng) + self.space.seed(rng.integers(0, 2**32)) + self.hyperparameters = dict(self.space.sample_configuration()) + self.check_hyperparameters_for_None() + return True + + def _mutate_node(self, rng=None): + return self.node.mutate(rng) + + def crossover(self, other, rng=None): + rng = np.random.default_rng(rng) + if rng.choice([True, False]): + return self._crossover_hyperparameters(other, rng) + else: + self.node.crossover(other.estimator_search_space, rng) + + + def _crossover_hyperparameters(self, other, rng=None): + if isinstance(self.space, dict): + return False + + 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] + + self.check_hyperparameters_for_None() + + return True + + def check_hyperparameters_for_None(self): + for key, value in self.hyperparameters.items(): + #if string + if isinstance(value, str): + if value == NONE_SPECIAL_STRING: + self.hyperparameters[key] = None + elif value == TRUE_SPECIAL_STRING: + self.hyperparameters[key] = True + elif value == FALSE_SPECIAL_STRING: + self.hyperparameters[key] = False + + + def export_pipeline(self): + + if self.hyperparameters_parser is not None: + final_params = self.hyperparameters_parser(self.hyperparameters) + else: + final_params = self.hyperparameters + + est = self.node.export_pipeline() + wrapped_est = self.method(est, **final_params) + return wrapped_est + + + + def unique_id(self): + #return a dictionary of the method and the hyperparameters + method_str = self.method.__name__ + params = list(self.hyperparameters.keys()) + params = sorted(params) + + id_str = f"{method_str}({', '.join([f'{param}={self.hyperparameters[param]}' for param in params])})" + + return TupleIndex(("WrapperPipeline", id_str, self.node.unique_id())) + + +class WrapperPipeline(SklearnIndividualGenerator): + def __init__( + self, + method: type, + space: ConfigurationSpace, + estimator_search_space: SklearnIndividualGenerator, + hyperparameter_parser: callable = None, + wrapped_param_name: str = None + ) -> 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.estimator_search_space = estimator_search_space + self.method = method + self.space = space + self.hyperparameter_parser=hyperparameter_parser + self.wrapped_param_name = wrapped_param_name + + def generate(self, rng=None): + return WrapperPipelineIndividual(method=self.method, space=self.space, estimator_search_space=self.estimator_search_space, hyperparameter_parser=self.hyperparameter_parser, wrapped_param_name=self.wrapped_param_name, rng=rng) \ No newline at end of file diff --git a/tpot2/search_spaces/tests/test_search_spaces.py b/tpot2/search_spaces/tests/test_search_spaces.py new file mode 100644 index 00000000..6baf3ce3 --- /dev/null +++ b/tpot2/search_spaces/tests/test_search_spaces.py @@ -0,0 +1,44 @@ +# Test all nodes have all dictionaries +import pytest +import tpot2 + +import tpot2 +from ConfigSpace import ConfigurationSpace +from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal +from sklearn.neighbors import KNeighborsClassifier +from sklearn.linear_model import LogisticRegression +from sklearn.tree import DecisionTreeClassifier +from sklearn.preprocessing import StandardScaler + + +def test_EstimatorNodeCrossover(): + knn_configspace = {} + standard_scaler_configspace = {} + + knn_node = tpot2.search_spaces.nodes.EstimatorNode( + method = KNeighborsClassifier, + space = knn_configspace, + ) + + knnind1 = knn_node.generate() + knnind2 = knn_node.generate() + + for i in range(0,10): + knnind1.mutate() + knnind2.mutate() + knnind1.crossover(knnind2) + + +def test_ValueError_different_types(): + knn_node = tpot2.config.get_search_space(["KNeighborsClassifier"]) + sfm_wrapper_node = tpot2.config.get_search_space(["SelectFromModel_classification"]) + + for i in range(10): + ind1 = knn_node.generate() + ind2 = sfm_wrapper_node.generate() + assert not ind1.crossover(ind2) + assert not ind2.crossover(ind1) + +if __name__ == "__main__": + test_EstimatorNodeCrossover() + test_ValueError_different_types() \ No newline at end of file diff --git a/tpot2/search_spaces/tuple_index.py b/tpot2/search_spaces/tuple_index.py new file mode 100644 index 00000000..adfbbb2c --- /dev/null +++ b/tpot2/search_spaces/tuple_index.py @@ -0,0 +1,25 @@ +import numpy as np + +class TupleIndex(): + """ + TPOT2 uses tuples to create a unique id for some pipeline search spaces. However, tuples sometimes don't interact correctly with pandas indexes. + This class is a wrapper around a tuple that allows it to be used as a key in a dictionary, without it being an itereable. + + An alternative could be to make unique id return a string, but this would not work with graphpipelines, which require a special object. + This class allows linear pipelines to contain graph pipelines while still being able to be used as a key in a dictionary. + + """ + def __init__(self, tup): + self.tup = tup + + def __eq__(self,other) -> bool: + return self.tup == other + + def __hash__(self) -> int: + return self.tup.__hash__() + + def __str__(self) -> str: + return self.tup.__str__() + + def __repr__(self) -> str: + return self.tup.__repr__() \ No newline at end of file diff --git a/tpot2/selectors/__init__.py b/tpot2/selectors/__init__.py index a6cdfd27..ab3764c3 100644 --- a/tpot2/selectors/__init__.py +++ b/tpot2/selectors/__init__.py @@ -4,6 +4,7 @@ from .tournament_selection import tournament_selection from .tournament_selection_dominated import tournament_selection_dominated from .nsgaii import nondominated_sorting, crowding_distance, dominates, survival_select_NSGA2 +from .map_elites_selection import map_elites_survival_selector, map_elites_parent_selector SELECTORS = {"lexicase":lexicase_selection, @@ -12,4 +13,6 @@ "tournament":tournament_selection, "tournament_dominated":tournament_selection_dominated, "nsgaii":survival_select_NSGA2, + "map_elites_survival":map_elites_survival_selector, + "map_elites_parent":map_elites_parent_selector, } \ No newline at end of file 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/map_elites_selection.py b/tpot2/selectors/map_elites_selection.py new file mode 100644 index 00000000..27ac6156 --- /dev/null +++ b/tpot2/selectors/map_elites_selection.py @@ -0,0 +1,103 @@ +import numpy as np +#TODO make these functions take in a predetermined set of bins rather than calculating a new set each time + +def create_nd_matrix(matrix, k): + # Extract scores and features + scores = [row[0] for row in matrix] + features = [row[1:] for row in matrix] + + # Determine the min and max of each feature + min_vals = np.min(features, axis=0) + max_vals = np.max(features, axis=0) + + # Create bins for each feature + bins = [np.linspace(min_vals[i], max_vals[i], k) for i in range(len(min_vals))] + + # Initialize n-dimensional matrix with negative infinity + nd_matrix = np.full([k-1]*len(min_vals), {"score": -np.inf, "idx": None}) + + # Fill in each cell with the highest score for that cell + for idx, (score, feature) in enumerate(zip(scores, features)): + indices = [np.digitize(f, bin)-1 for f, bin in zip(feature, bins)] + + indices = [min(i, k-2) for i in indices] #the last bin is inclusive + + cur_score = nd_matrix[tuple(indices)]["score"] + if score > cur_score: + nd_matrix[tuple(indices)] = {"score": score, "idx": idx} + + + return nd_matrix + +def manhattan(a, b): + return sum(abs(val1-val2) for val1, val2 in zip(a,b)) + + +def map_elites_survival_selector(scores, k, rng=None, grid_steps= 10): + rng = np.random.default_rng(rng) + scores = np.array(scores) + #create grid + + matrix = create_nd_matrix(scores, grid_steps) + matrix = matrix.flatten() + + indexes = [cell["idx"] for cell in matrix if cell["idx"] is not None] + + return np.unique(indexes) + +def map_elites_parent_selector(scores, k, rng=None, grid_steps= 10, manhattan_distance = 2, n_parents=1,): + rng = np.random.default_rng(rng) + scores = np.array(scores) + #create grid + + matrix = create_nd_matrix(scores, grid_steps) + + #return true if cell is not empty + f = np.vectorize(lambda x: x["idx"] is not None) + valid_coordinates = np.array(np.where(f(matrix))).T + + idx_to_coordinates = {matrix[tuple(coordinates)]["idx"]: coordinates for coordinates in valid_coordinates} + + idxes = [idx for idx in idx_to_coordinates.keys()] #all the indexes of best score per cell + + + + distance_matrix = np.zeros((len(idxes), len(idxes))) + + for i, idx1 in enumerate(idxes): + for j, idx2 in enumerate(idxes): + distance_matrix[i][j] = manhattan(idx_to_coordinates[idx1], idx_to_coordinates[idx2]) + + + parents = [] + + for i in range(k): + #randomly select a cell + idx = rng.choice(idxes) #select random parent + + #get the distance from this parent to all other parents + dm_idx = idxes.index(idx) + row = distance_matrix[dm_idx] + + #get all second parents that are within manhattan distance. if none are found increase the distance + candidates = [] + while len(candidates) == 0: + candidates = np.where(row <= manhattan_distance)[0] + #remove self from candidates + candidates = candidates[candidates != dm_idx] + manhattan_distance += 1 + + if manhattan_distance > grid_steps*scores.shape[1]: + break + + if len(candidates) == 0: + parents.append([idx]) + + this_parents = [idx] + for p in range(n_parents-1): + idx2_cords = rng.choice(candidates) + this_parents.append(idxes[idx2_cords]) + + parents.append(this_parents) + + return np.array(parents) \ No newline at end of file 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/tests/test_estimators.py b/tpot2/tests/test_estimators.py index 29dfa8f8..cf6b7f21 100644 --- a/tpot2/tests/test_estimators.py +++ b/tpot2/tests/test_estimators.py @@ -4,10 +4,29 @@ import random import sklearn +@pytest.fixture +def sample_dataset(): + X_train, y_train = load_iris(return_X_y=True) + return X_train, y_train + #standard test @pytest.fixture def tpot_estimator(): - return tpot2.TPOTEstimator( population_size=50, + + n_classes=3 + n_samples=100 + n_features=100 + + search_space = tpot2.search_spaces.pipelines.GraphPipeline( + root_search_space= tpot2.config.get_search_space("classifiers", n_samples=n_samples, n_features=n_features, n_classes=n_classes), + leaf_search_space = None, + inner_search_space = tpot2.config.get_search_space(["selectors","transformers"],n_samples=n_samples, n_features=n_features, n_classes=n_classes), + max_size = 10, + ) + return tpot2.TPOTEstimator( + search_space=search_space, + population_size=10, + generations=2, scorers=['roc_auc_ovr'], scorers_weights=[1], classification=True, @@ -15,20 +34,17 @@ def tpot_estimator(): early_stop=5, other_objective_functions= [], other_objective_functions_weights=[], - max_time_seconds=300, + max_time_seconds=10, verbose=3) @pytest.fixture -def sample_dataset(): - X_train, y_train = load_iris(return_X_y=True) - return X_train, y_train +def tpot_classifier(): + return tpot2.tpot_estimator.templates.TPOTClassifier(max_time_seconds=10,verbose=0) + +@pytest.fixture +def tpot_regressor(): + return tpot2.tpot_estimator.templates.TPOTRegressor(max_time_seconds=10,verbose=0) -def test_tpot_estimator_fit(tpot_estimator,sample_dataset): - #load iris dataset - X_train = sample_dataset[0] - y_train = sample_dataset[1] - tpot_estimator.fit(X_train, y_train) - assert tpot_estimator.fitted_pipeline_ is not None @pytest.fixture def tpot_estimator_with_pipeline(tpot_estimator,sample_dataset): @@ -40,15 +56,7 @@ def test_tpot_estimator_predict(tpot_estimator_with_pipeline,sample_dataset): X_test = sample_dataset[0] y_pred = tpot_estimator_with_pipeline.predict(X_test) assert len(y_pred) == len(X_test) - -@pytest.mark.skip(reason="not an informative test. X_test is a list instead of a numpy array or pandas dataframe.") -def test_tpot_estimator_score(tpot_estimator_with_pipeline,sample_dataset): - random.seed(42) - #random sample 10% of the dataset - X_test = random.sample(list(sample_dataset[0]), int(len(sample_dataset[0])*0.1)) - y_test = random.sample(list(sample_dataset[1]), int(len(sample_dataset[1])*0.1)) - scorer = sklearn.metrics.get_scorer('roc_auc_ovo') - assert isinstance(scorer(tpot_estimator_with_pipeline, X_test, y_test), float) + assert tpot_estimator_with_pipeline.fitted_pipeline_ is not None def test_tpot_estimator_generations_type(): with pytest.raises(TypeError): @@ -80,13 +88,7 @@ def test_tpot_estimator_config_dict_type(): -@pytest.fixture -def tpot_classifier(): - return tpot2.tpot_estimator.templates.TPOTClassifier(max_time_seconds=300,verbose=3) -@pytest.fixture -def tpot_regressor(): - return tpot2.tpot_estimator.templates.TPOTRegressor(max_time_seconds=300,verbose=3) def test_tpot_classifier_fit(tpot_classifier,sample_dataset): #load iris dataset @@ -99,7 +101,7 @@ def test_tpot_regressor_fit(tpot_regressor): scorer = sklearn.metrics.get_scorer('neg_mean_squared_error') X, y = sklearn.datasets.load_diabetes(return_X_y=True) - X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.75, test_size=0.25) + X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, train_size=0.05, test_size=0.95) tpot_regressor.fit(X_train, y_train) assert tpot_regressor.fitted_pipeline_ is not None diff --git a/tpot2/tests/test_hello_world.py b/tpot2/tests/test_hello_world.py index 15f444ce..d4d369a2 100644 --- a/tpot2/tests/test_hello_world.py +++ b/tpot2/tests/test_hello_world.py @@ -17,10 +17,6 @@ def test_hello_world(test_input, expected): assert test_input is expected -@pytest.mark.xfail(reason="Not yet implemented") -def test_divide_by_zero(): - assert 1 / 0 == 1 - def test_print(capture_stdout): print("Hello World") diff --git a/tpot2/tests/test_nodes.py b/tpot2/tests/test_nodes.py deleted file mode 100644 index d73bfcc2..00000000 --- a/tpot2/tests/test_nodes.py +++ /dev/null @@ -1,134 +0,0 @@ -# Test all nodes have all dictionaries -import pytest -import tpot2.graphsklearn as GraphSklearn - - -@pytest.mark.skip(reason="Not yet implemented") -def test_BaseNode_static_methods(): - n1 = GraphSklearn.BaseNode() - n2 = GraphSklearn.BaseNode() - n3 = GraphSklearn.BaseNode() - n4 = GraphSklearn.BaseNode() - n5 = GraphSklearn.BaseNode() - n6 = GraphSklearn.BaseNode() - n7 = GraphSklearn.BaseNode() - - GraphSklearn.BaseNode._connect_nodes(child=n2,parent=n1) - GraphSklearn.BaseNode._connect_nodes(child=n3,parent=n1) - GraphSklearn.BaseNode._connect_nodes(child=n4,parent=n1) - - GraphSklearn.BaseNode._connect_nodes(child=n5,parent=n2) - - GraphSklearn.BaseNode._connect_nodes(child=n5,parent=n3) - GraphSklearn.BaseNode._connect_nodes(child=n6,parent=n3) - - GraphSklearn.BaseNode._connect_nodes(child=n6,parent=n4) - GraphSklearn.BaseNode._connect_nodes(child=n3,parent=n4) - - GraphSklearn.BaseNode._connect_nodes(child=n4,parent=n7) #This is a second root, allowed by basenode - - assert n2 in n1.children - assert n3 in n1.children - assert n4 in n1.children - assert n5 in n2.children - assert n5 in n3.children - assert n6 in n3.children - assert n6 in n4.children - assert n3 in n4.children - assert n4 in n7.children - - assert n1 in n2.parents - assert n1 in n3.parents - assert n1 in n4.parents - assert n2 in n5.parents - assert n3 in n5.parents - assert n3 in n6.parents - assert n4 in n6.parents - assert n4 in n3.parents - assert n7 in n4.parents - - assert n2 not in n1.parents - assert n3 not in n1.parents - assert n4 not in n1.parents - assert n6 not in n2.parents - assert n5 not in n3.parents - assert n6 not in n3.parents - assert n6 not in n4.parents - - #Make sure every node knows about every other node in the graph. - for this_node in [n1,n2,n3,n4,n5,n6, n7]: - for other_node in [n1,n2,n3,n4,n5,n6, n7]: - assert this_node in other_node.node_set - - - GraphSklearn.BaseNode._disconnect_nodes(child=n3, parent=n4) - assert n4 not in n3.parents - assert n3 not in n4.children - - n8 = GraphSklearn.BaseNode() - GraphSklearn.BaseNode._insert_inner_node(child=n3,new_node=n8,parent=n4) - assert n8 in n3.parents - assert n4 in n8.parents - - assert n3 in n8.children - assert n8 in n4.children - - n9 = GraphSklearn.BaseNode() - - GraphSklearn.BaseNode._remove_node(node=n8, replacement=n9) - assert n8 not in n3.parents - assert n4 not in n8.parents - - assert n3 not in n8.children - assert n8 not in n4.children - assert n8 not in n1.node_set - - assert n9 in n3.parents - assert n4 in n9.parents - - assert n3 in n9.children - assert n9 in n4.children - - for this_node in [n1,n2,n3,n4,n5,n6, n7, n9]: - for other_node in [n1,n2,n3,n4,n5,n6, n7, n9]: - assert this_node in other_node.node_set - - -@pytest.mark.skip(reason="Not yet implemented") -def test_BaseNode_static_crossover_methods(): - n1 = GraphSklearn.BaseNode() - n2 = GraphSklearn.BaseNode() - n3 = GraphSklearn.BaseNode() - n4 = GraphSklearn.BaseNode() - n5 = GraphSklearn.BaseNode() - n6 = GraphSklearn.BaseNode() - n7 = GraphSklearn.BaseNode() - n8 = GraphSklearn.BaseNode() - - GraphSklearn.BaseNode._connect_nodes(child=n2,parent=n1) - GraphSklearn.BaseNode._connect_nodes(child=n3,parent=n1) - GraphSklearn.BaseNode._connect_nodes(child=n4,parent=n1) - - GraphSklearn.BaseNode._connect_nodes(child=n5,parent=n2) - - GraphSklearn.BaseNode._connect_nodes(child=n5,parent=n3) - GraphSklearn.BaseNode._connect_nodes(child=n6,parent=n3) - - GraphSklearn.BaseNode._connect_nodes(child=n6,parent=n4) - GraphSklearn.BaseNode._connect_nodes(child=n3,parent=n4) - - GraphSklearn.BaseNode._connect_nodes(child=n4,parent=n7) #This is a second root, allowed by basenode - - GraphSklearn.BaseNode._connect_nodes(child=n8,parent=n5) - GraphSklearn.BaseNode._connect_nodes(child=n8,parent=n1) - - depth_dict = n1._get_depth_dictionary() - - assert depth_dict[n1] == 0 - assert depth_dict[n2] == 1 - assert depth_dict[n3] == 2 - assert depth_dict[n4] == 1 - assert depth_dict[n5] == 3 - assert depth_dict[n6] == 3 - assert depth_dict[n8] == 4 - assert n7 not in depth_dict \ No newline at end of file diff --git a/tpot2/tpot_estimator/default_search_spaces.py b/tpot2/tpot_estimator/default_search_spaces.py new file mode 100644 index 00000000..6546ac86 --- /dev/null +++ b/tpot2/tpot_estimator/default_search_spaces.py @@ -0,0 +1,97 @@ +import tpot2 +from tpot2.search_spaces.pipelines import * +from tpot2.search_spaces.nodes import * +from tpot2.config import get_search_space + + + +def get_linear_search_space(classification=True, inner_predictors=True, **get_search_space_params ): + + if classification: + selectors = get_search_space(["selectors","selectors_classification", "Passthrough"], **get_search_space_params) + estimators = get_search_space(["classifiers"], **get_search_space_params) + else: + selectors = get_search_space(["selectors","selectors_regression", "Passthrough"], **get_search_space_params) + estimators = get_search_space(["regressors"], **get_search_space_params) + + # this allows us to wrap the classifiers in the EstimatorTransformer + # this is necessary so that classifiers can be used inside of sklearn pipelines + wrapped_estimators = WrapperPipeline(tpot2.builtin_modules.EstimatorTransformer, {}, estimators) + + scalers = get_search_space(["scalers","Passthrough"], **get_search_space_params) + + transformers_layer =UnionPipeline([ + ChoicePipeline([ + DynamicUnionPipeline(get_search_space(["transformers"],**get_search_space_params)), + get_search_space("SkipTransformer", **get_search_space_params), + ]), + get_search_space("Passthrough", **get_search_space_params) + ] + ) + + inner_estimators_layer = UnionPipeline([ + ChoicePipeline([ + DynamicUnionPipeline(wrapped_estimators), + get_search_space("SkipTransformer", **get_search_space_params), + ]), + get_search_space("Passthrough", **get_search_space_params)] + ) + + if inner_predictors: + search_space = SequentialPipeline(search_spaces=[ + scalers, + selectors, + transformers_layer, + inner_estimators_layer, + estimators, + ]) + else: + search_space = SequentialPipeline(search_spaces=[ + scalers, + selectors, + transformers_layer, + estimators, + ]) + + return search_space + + +def get_graph_search_space(classification=True, inner_predictors=True, **get_search_space_params ): + + if classification: + root_search_space = get_search_space(["classifiers"], **get_search_space_params) + inner_search_space = tpot2.config.get_search_space(["selectors","transformers","scalers","selectors_classification"],**get_search_space_params) + else: + root_search_space = get_search_space(["regressors"], **get_search_space_params) + + + if classification: + if inner_predictors: + inner_search_space = tpot2.config.get_search_space(["classifiers", "selectors","transformers","scalers","selectors_regression"],**get_search_space_params) + else: + inner_search_space = tpot2.config.get_search_space(["selectors","transformers","scalers","selectors_regression"],**get_search_space_params) + else: + if inner_predictors: + inner_search_space = tpot2.config.get_search_space(["regressors", "selectors","transformers","scalers","selectors_regression"],**get_search_space_params) + else: + inner_search_space = tpot2.config.get_search_space(["selectors","transformers","scalers","selectors_regression"],**get_search_space_params) + + + search_space = tpot2.search_spaces.pipelines.GraphPipeline( + root_search_space= root_search_space, + leaf_search_space = None, + inner_search_space = inner_search_space, + ) + + return search_space + +def get_default_search_space(default_search_space, classification=True, inner_predictors=True, **get_search_space_params): + if isinstance(default_search_space, str): + if default_search_space == "linear": + return get_linear_search_space(classification, inner_predictors, **get_search_space_params) + elif default_search_space == "graph": + return get_graph_search_space(classification, inner_predictors, **get_search_space_params) + else: + raise ValueError("Invalid search space") + else: + return default_search_space \ No newline at end of file diff --git a/tpot2/tpot_estimator/estimator.py b/tpot2/tpot_estimator/estimator.py index 060539c7..bb1f3b3f 100644 --- a/tpot2/tpot_estimator/estimator.py +++ b/tpot2/tpot_estimator/estimator.py @@ -18,7 +18,9 @@ from .estimator_utils import * from dask import config as cfg +from sklearn.experimental import enable_iterative_imputer +from .default_search_spaces import get_default_search_space def set_dask_settings(): cfg.set({'distributed.scheduler.worker-ttl': None}) @@ -29,7 +31,9 @@ def set_dask_settings(): #TODO inherit from _BaseComposition? class TPOTEstimator(BaseEstimator): - def __init__(self, scorers, + def __init__(self, + search_space, + scorers, scorers_weights, classification, cv = 5, @@ -38,18 +42,12 @@ 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, + export_graphpipeline = False, cross_val_predict_cv = 0, + memory = None, + categorical_features = None, subsets = None, - memory = None, preprocessing = False, population_size = 50, initial_population_size = None, @@ -88,20 +86,16 @@ 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, - memory_limit = "4GB", + memory_limit = None, client = None, processes = True, #debugging and logging parameters warm_start = False, - subset_column = None, periodic_checkpoint_folder = None, callback = None, @@ -118,7 +112,12 @@ def __init__(self, scorers, Parameters ---------- - + search_space : (String, tpot2.search_spaces.SklearnIndividualGenerator) + - String : The default search space to use for the optimization. This can be either "linear" or "graph". If "linear", will use the default linear pipeline search space. If "graph", will use the default graph pipeline search space. + - SklearnIndividualGenerator : The search space to use for the optimization. This should be an instance of a SklearnIndividualGenerator. + The search space to use for the optimization. This should be an instance of a SklearnIndividualGenerator. + TPOT2 has groups of search spaces found in the following folders, tpot2.search_spaces.nodes for the nodes in the pipeline and tpot2.search_spaces.pipelines for the pipeline structure. + scorers : (list, scorer) A scorer or list of scorers to be used in the cross-validation process. see https://scikit-learn.org/stable/modules/model_evaluation.html @@ -146,71 +145,7 @@ def __init__(self, scorers, bigger_is_better : bool, default=True 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. @@ -218,20 +153,6 @@ def __init__(self, scorers, - >=2 : When fitting pipelines with inner classifiers or regressors, they will still be fit on the full dataset. However, the output to the next node will come from cross_val_predict with the specified number of folds. - categorical_features: list or None - Categorical columns to inpute and/or one hot encode during the preprocessing step. Used only if preprocessing is not False. - - None : If None, TPOT2 will automatically use object columns in pandas dataframes as objects for one hot encoding in preprocessing. - - List of categorical features. If X is a dataframe, this should be a list of column names. If X is a numpy array, this should be a list of column indices - - subsets : str or list, default=None - Sets the subsets that the FeatureSetSeletor will select from if set as an option in one of the configuration dictionaries. - - str : If a string, it is assumed to be a path to a csv file with the subsets. - The first column is assumed to be the name of the subset and the remaining columns are the features in the subset. - - list or np.ndarray : If a list or np.ndarray, it is assumed to be a list of subsets. - - None : If None, each column will be treated as a subset. One column will be selected per subset. - If subsets is None, each column will be treated as a subset. One column will be selected per subset. - - memory: Memory object or string, default=None If supplied, pipeline will cache each transformer after calling fit. This feature is used to avoid computing the fit transformers within a pipeline if the parameters @@ -246,12 +167,25 @@ def __init__(self, scorers, TPOT uses the instance of joblib.Memory for memory caching, and TPOT does NOT clean the caching directory up upon shutdown. - None: - TPOT does not use memory caching. + TPOT does not use memory caching. + + categorical_features: list or None + Categorical columns to inpute and/or one hot encode during the preprocessing step. Used only if preprocessing is not False. + - None : If None, TPOT2 will automatically use object columns in pandas dataframes as objects for one hot encoding in preprocessing. + - List of categorical features. If X is a dataframe, this should be a list of column names. If X is a numpy array, this should be a list of column indices + + subsets : str or list, default=None + Sets the subsets that the FeatureSetSeletor will select from if set as an option in one of the configuration dictionaries. + - str : If a string, it is assumed to be a path to a csv file with the subsets. + The first column is assumed to be the name of the subset and the remaining columns are the features in the subset. + - list or np.ndarray : If a list or np.ndarray, it is assumed to be a list of subsets. + - None : If None, each column will be treated as a subset. One column will be selected per subset. + If subsets is None, each column will be treated as a subset. One column will be selected per subset. preprocessing : bool or BaseEstimator/Pipeline, EXPERIMENTAL - A pipeline that will be used to preprocess the data before CV. - - bool : If True, will use a default preprocessing pipeline. + A pipeline that will be used to preprocess the data before CV. Note that the parameters for these steps are not optimized. Add them to the search space to be optimized. + - bool : If True, will use a default preprocessing pipeline which includes imputation followed by one hot encoding. - Pipeline : If an instance of a pipeline is given, will use that pipeline as the preprocessing pipeline. population_size : int, default=50 @@ -378,9 +312,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. @@ -398,6 +329,9 @@ def __init__(self, scorers, >=5. full warnings trace 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.) + scatter : bool, default=True + If True, will scatter the data to the dask workers. If False, will not scatter the data. This can be useful for debugging. + random_state : int, None, default=None A seed 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 @@ -441,18 +375,20 @@ 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.export_graphpipeline = export_graphpipeline self.cross_val_predict_cv = cross_val_predict_cv + self.memory = memory + + if self.cross_val_predict_cv !=0 or self.memory is not None: + if not self.export_graphpipeline: + raise ValueError("cross_val_predict_cv and memory parameters are parameters for GraphPipeline. To enable these options export_graphpipeline to be True. Otherwise these can be passed into the relevant Search spaces as parameters.") + self.categorical_features = categorical_features self.subsets = subsets - self.memory = memory + self.preprocessing = preprocessing self.validation_strategy = validation_strategy self.validation_fraction = validation_fraction @@ -487,7 +423,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 @@ -496,12 +431,9 @@ 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 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 @@ -620,7 +552,7 @@ def fit(self, X, y): #X = pd.DataFrame(X) if not isinstance(self.preprocessing, bool) and isinstance(self.preprocessing, sklearn.base.BaseEstimator): - self._preprocessing_pipeline = self.preprocessing + self._preprocessing_pipeline = sklearn.base.clone(self.preprocessing) #TODO: check if there are missing values in X before imputation. If not, don't include imputation in pipeline. Check if there are categorical columns. If not, don't include one hot encoding in pipeline else: #if self.preprocessing is True or not a sklearn estimator @@ -630,7 +562,7 @@ def fit(self, X, y): if self.categorical_features is not None: #if categorical features are specified, use those pipeline_steps.append(("impute_categorical", tpot2.builtin_modules.ColumnSimpleImputer(self.categorical_features, strategy='most_frequent'))) pipeline_steps.append(("impute_numeric", tpot2.builtin_modules.ColumnSimpleImputer("numeric", strategy='mean'))) - pipeline_steps.append(("impute_categorical", tpot2.builtin_modules.ColumnOneHotEncoder(self.categorical_features, strategy='most_frequent'))) + pipeline_steps.append(("ColumnOneHotEncoder", tpot2.builtin_modules.ColumnOneHotEncoder(self.categorical_features, strategy='most_frequent'))) else: if isinstance(X, pd.DataFrame): @@ -638,7 +570,7 @@ def fit(self, X, y): if len(categorical_columns) > 0: pipeline_steps.append(("impute_categorical", tpot2.builtin_modules.ColumnSimpleImputer("categorical", strategy='most_frequent'))) pipeline_steps.append(("impute_numeric", tpot2.builtin_modules.ColumnSimpleImputer("numeric", strategy='mean'))) - pipeline_steps.append(("impute_categorical", tpot2.builtin_modules.ColumnOneHotEncoder("categorical", strategy='most_frequent'))) + pipeline_steps.append(("ColumnOneHotEncoder", tpot2.builtin_modules.ColumnOneHotEncoder("categorical", strategy='most_frequent'))) else: pipeline_steps.append(("impute_numeric", tpot2.builtin_modules.ColumnSimpleImputer("all", strategy='mean'))) else: @@ -675,17 +607,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, @@ -695,9 +616,9 @@ def objective_function(pipeline_individual, scorers= self._scorers, cv=self.cv_gen, other_objective_functions=self.other_objective_functions, + export_graphpipeline=self.export_graphpipeline, memory=self.memory, cross_val_predict_cv=self.cross_val_predict_cv, - subset_column=self.subset_column, **kwargs): return objective_function_generator( pipeline_individual, @@ -707,23 +628,13 @@ def objective_function(pipeline_individual, scorers= scorers, cv=cv, other_objective_functions=other_objective_functions, + export_graphpipeline=export_graphpipeline, 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 +648,56 @@ def objective_function(pipeline_individual, X_future = X y_future = y + if self.classification: + n_classes = len(np.unique(y)) + else: + n_classes = None + + get_search_space_params = {"n_classes": n_classes, + "n_samples":len(y), + "n_features":X.shape[1], + "random_state":self.random_state} + + self._search_space = get_default_search_space(self.search_space, classification=True, inner_predictors=True, **get_search_space_params) + + + # TODO : Add check for empty values in X and if so, add imputation to the search space + # make this depend on self.preprocessing + # if check_empty_values(X): + # from sklearn.experimental import enable_iterative_imputer + + # from ConfigSpace import ConfigurationSpace + # from ConfigSpace import ConfigurationSpace, Integer, Float, Categorical, Normal + # iterative_imputer_cs = ConfigurationSpace( + # space = { + # 'n_nearest_features' : Categorical('n_nearest_features', [100]), + # 'initial_strategy' : Categorical('initial_strategy', ['mean','median', 'most_frequent', ]), + # 'add_indicator' : Categorical('add_indicator', [True, False]), + # } + # ) + + # imputation_search = tpot2.search_spaces.pipelines.ChoicePipeline([ + # tpot2.config.get_search_space("SimpleImputer"), + # tpot2.search_spaces.nodes.EstimatorNode(sklearn.impute.IterativeImputer, iterative_imputer_cs) + # ]) + + + + + # self.search_space_final = tpot2.search_spaces.pipelines.SequentialPipeline(search_spaces=[ imputation_search, self._search_space], memory="sklearn_pipeline_memory") + # else: + # self.search_space_final = self._search_space + + self.search_space_final = self._search_space + + def ind_generator(rng): + rng = np.random.default_rng(rng) + while True: + yield self.search_space_final.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 +739,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, ) @@ -790,21 +748,9 @@ def objective_function(pipeline_individual, 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"]) + tpot2.utils.get_pareto_frontier(self.evaluated_individuals, column_names=self.objective_names, weights=self.objective_function_weights) if validation_strategy == 'reshuffled': best_pareto_front_idx = list(self.pareto_front.index) @@ -827,9 +773,10 @@ def objective_function(pipeline_individual, scorers= self._scorers, cv=self.cv_gen, other_objective_functions=self.other_objective_functions, + export_graphpipeline=self.export_graphpipeline, memory=self.memory, cross_val_predict_cv=self.cross_val_predict_cv, - subset_column=self.subset_column, + **kwargs: objective_function_generator( ind, X, @@ -838,22 +785,29 @@ def objective_function(pipeline_individual, scorers= scorers, cv=cv, other_objective_functions=other_objective_functions, + export_graphpipeline=export_graphpipeline, memory=memory, cross_val_predict_cv=cross_val_predict_cv, - subset_column=subset_column, **kwargs, )] objective_kwargs = {"X": X_future, "y": y_future} - val_scores = tpot2.utils.eval_utils.parallel_eval_objective_list( - best_pareto_front, - val_objective_function_list, n_jobs=self.n_jobs, verbose=self.verbose, timeout=self.max_eval_time_seconds,n_expected_columns=len(self.objective_names), client=_client, **objective_kwargs) + # val_scores = tpot2.utils.eval_utils.parallel_eval_objective_list( + # best_pareto_front, + # val_objective_function_list, n_jobs=self.n_jobs, verbose=self.verbose, timeout=self.max_eval_time_seconds,n_expected_columns=len(self.objective_names), client=_client, **objective_kwargs) + val_scores, start_times, end_times, eval_errors = tpot2.utils.eval_utils.parallel_eval_objective_list2(best_pareto_front, val_objective_function_list, verbose=self.verbose, max_eval_time_seconds=self.max_eval_time_seconds, n_expected_columns=len(self.objective_names), client=_client, **objective_kwargs) + + val_objective_names = ['validation_'+name for name in self.objective_names] self.objective_names_for_selection = val_objective_names self.evaluated_individuals.loc[best_pareto_front_idx,val_objective_names] = val_scores + self.evaluated_individuals.loc[best_pareto_front_idx,'validation_start_times'] = start_times + self.evaluated_individuals.loc[best_pareto_front_idx,'validation_end_times'] = end_times + self.evaluated_individuals.loc[best_pareto_front_idx,'validation_eval_errors'] = eval_errors + + self.evaluated_individuals["Validation_Pareto_Front"] = tpot2.utils.get_pareto_frontier(self.evaluated_individuals, column_names=val_objective_names, weights=self.objective_function_weights) - self.evaluated_individuals["Validation_Pareto_Front"] = tpot2.utils.get_pareto_front(self.evaluated_individuals, val_objective_names, self.objective_function_weights, invalid_values=["TIMEOUT","INVALID"]) elif validation_strategy == 'split': @@ -880,9 +834,9 @@ def objective_function(pipeline_individual, y_val, scorers= self._scorers, other_objective_functions=self.other_objective_functions, + export_graphpipeline=self.export_graphpipeline, memory=self.memory, cross_val_predict_cv=self.cross_val_predict_cv, - subset_column=self.subset_column, **kwargs: val_objective_function_generator( ind, X, @@ -891,24 +845,29 @@ def objective_function(pipeline_individual, y_val, scorers= scorers, other_objective_functions=other_objective_functions, + export_graphpipeline=export_graphpipeline, memory=memory, cross_val_predict_cv=cross_val_predict_cv, - subset_column=subset_column, **kwargs, )] - val_scores = tpot2.utils.eval_utils.parallel_eval_objective_list( - best_pareto_front, - val_objective_function_list, n_jobs=self.n_jobs, verbose=self.verbose, timeout=self.max_eval_time_seconds,n_expected_columns=len(self.objective_names),client=_client, **objective_kwargs) + val_scores, start_times, end_times, eval_errors = tpot2.utils.eval_utils.parallel_eval_objective_list2(best_pareto_front, val_objective_function_list, verbose=self.verbose, max_eval_time_seconds=self.max_eval_time_seconds, n_expected_columns=len(self.objective_names), client=_client, **objective_kwargs) + + val_objective_names = ['validation_'+name for name in self.objective_names] self.objective_names_for_selection = val_objective_names self.evaluated_individuals.loc[best_pareto_front_idx,val_objective_names] = val_scores - self.evaluated_individuals["Validation_Pareto_Front"] = tpot2.utils.get_pareto_front(self.evaluated_individuals, val_objective_names, self.objective_function_weights, invalid_values=["TIMEOUT","INVALID"]) + self.evaluated_individuals.loc[best_pareto_front_idx,'validation_start_times'] = start_times + self.evaluated_individuals.loc[best_pareto_front_idx,'validation_end_times'] = end_times + self.evaluated_individuals.loc[best_pareto_front_idx,'validation_eval_errors'] = eval_errors + + self.evaluated_individuals["Validation_Pareto_Front"] = tpot2.utils.get_pareto_frontier(self.evaluated_individuals, column_names=val_objective_names, weights=self.objective_function_weights) + else: self.objective_names_for_selection = self.objective_names - - val_scores = self.evaluated_individuals[~self.evaluated_individuals[self.objective_names_for_selection].isin(["TIMEOUT","INVALID"]).any(axis=1)][self.objective_names_for_selection].astype(float) + + val_scores = self.evaluated_individuals[~self.evaluated_individuals[self.objective_names_for_selection].isna().all(1)][self.objective_names_for_selection] weighted_scores = val_scores*self.objective_function_weights if self.bigger_is_better: @@ -920,7 +879,12 @@ 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) + #TODO + #best_individual_pipeline = best_individual.export_pipeline(memory=self.memory, cross_val_predict_cv=self.cross_val_predict_cv) + if self.export_graphpipeline: + best_individual_pipeline = best_individual.export_flattened_graphpipeline(memory=self.memory, cross_val_predict_cv=self.cross_val_predict_cv) + else: + 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 ) @@ -932,8 +896,15 @@ def objective_function(pipeline_individual, if self.client is None: #no client was passed in #close cluster and client - _client.close() - cluster.close() + # _client.close() + # cluster.close() + try: + _client.shutdown() + cluster.close() + #catch exception + except Exception as e: + print("Error shutting down client and cluster") + Warning(e) return self @@ -1003,7 +974,7 @@ def make_evaluated_individuals(self): self.evaluated_individuals = self.evaluated_individuals.set_index(self.evaluated_individuals.index.map(object_to_int)) self.evaluated_individuals['Parents'] = self.evaluated_individuals['Parents'].apply(lambda row: convert_parents_tuples_to_integers(row, object_to_int)) - self.evaluated_individuals["Instance"] = self.evaluated_individuals["Individual"].apply(lambda ind: apply_make_pipeline(ind, preprocessing_pipeline=self._preprocessing_pipeline)) + self.evaluated_individuals["Instance"] = self.evaluated_individuals["Individual"].apply(lambda ind: apply_make_pipeline(ind, preprocessing_pipeline=self._preprocessing_pipeline, export_graphpipeline=self.export_graphpipeline, memory=self.memory, cross_val_predict_cv=self.cross_val_predict_cv)) return self.evaluated_individuals @@ -1017,3 +988,21 @@ def pareto_front(self): return self.evaluated_individuals else: return self.evaluated_individuals[self.evaluated_individuals["Pareto_Front"]==1] + + +def check_empty_values(data): + """ + Checks for empty values in a dataset. + + Args: + data (numpy.ndarray or pandas.DataFrame): The dataset to check. + + Returns: + bool: True if the dataset contains empty values, False otherwise. + """ + if isinstance(data, pd.DataFrame): + return data.isnull().values.any() + elif isinstance(data, np.ndarray): + return np.isnan(data).any() + else: + raise ValueError("Unsupported data type") \ No newline at end of file diff --git a/tpot2/tpot_estimator/estimator_utils.py b/tpot2/tpot_estimator/estimator_utils.py index 47f31450..7be96e26 100644 --- a/tpot2/tpot_estimator/estimator_utils.py +++ b/tpot2/tpot_estimator/estimator_utils.py @@ -12,96 +12,34 @@ def convert_parents_tuples_to_integers(row, object_to_int): else: return np.nan -def apply_make_pipeline(graphindividual, preprocessing_pipeline=None): +#TODO add kwargs +def apply_make_pipeline(graphindividual, preprocessing_pipeline=None, export_graphpipeline=False, **pipeline_kwargs): try: - if preprocessing_pipeline is None: - return graphindividual.export_pipeline() - else: - return sklearn.pipeline.make_pipeline(sklearn.base.clone(preprocessing_pipeline), graphindividual.export_pipeline()) - except: - return None - -def get_configuration_dictionary(options, n_samples, n_features, classification, random_state=None, cv=None, subsets=None, feature_names=None, n_classes=None): - if options is None: - return options - - if isinstance(options, dict): - return recursive_with_defaults(options, n_samples, n_features, classification, random_state=None, cv=None, subsets=subsets, feature_names=feature_names, n_classes=n_classes) - - if not isinstance(options, list): - options = [options] - - config_dict = {} - - for option in options: - - if option == "selectors": - config_dict.update(tpot2.config.make_selector_config_dictionary(random_state=random_state, classifier=classification)) - - elif option == "classifiers": - config_dict.update(tpot2.config.make_classifier_config_dictionary(random_state=random_state, n_samples=n_samples, n_classes=n_classes)) - - elif option == "classifiers_sklearnex": - config_dict.update(tpot2.config.make_sklearnex_classifier_config_dictionary(random_state=random_state, n_samples=n_samples, n_classes=n_classes)) - - elif option == "regressors": - config_dict.update(tpot2.config.make_regressor_config_dictionary(random_state=random_state, cv=cv, n_samples=n_samples)) - - elif option == "regressors_sklearnex": - config_dict.update(tpot2.config.make_sklearnex_regressor_config_dictionary(random_state=random_state, n_samples=n_samples)) - - elif option == "transformers": - config_dict.update(tpot2.config.make_transformer_config_dictionary(random_state=random_state, n_features=n_features)) - - elif option == "arithmetic_transformer": - config_dict.update(tpot2.config.make_arithmetic_transformer_config_dictionary()) - - elif option == "feature_set_selector": - config_dict.update(tpot2.config.make_FSS_config_dictionary(subsets, n_features, feature_names=feature_names)) - - elif option == "skrebate": - config_dict.update(tpot2.config.make_skrebate_config_dictionary(n_features=n_features)) - - elif option == "MDR": - config_dict.update(tpot2.config.make_MDR_config_dictionary()) - - elif option == "continuousMDR": - config_dict.update(tpot2.config.make_ContinuousMDR_config_dictionary()) - - elif option == "FeatureEncodingFrequencySelector": - config_dict.update(tpot2.config.make_FeatureEncodingFrequencySelector_config_dictionary()) - - elif option == "genetic encoders": - config_dict.update(tpot2.config.make_genetic_encoders_config_dictionary()) - - elif option == "passthrough": - config_dict.update(tpot2.config.make_passthrough_config_dictionary()) - + if export_graphpipeline: + est = graphindividual.export_flattened_graphpipeline(**pipeline_kwargs) else: - config_dict.update(recursive_with_defaults(option, n_samples, n_features, classification, random_state, cv, subsets=subsets, feature_names=feature_names, n_classes=n_classes)) + est = graphindividual.export_pipeline() - if len(config_dict) == 0: - raise ValueError("No valid configuration options were provided. Please check the options you provided and try again.") - return config_dict + if preprocessing_pipeline is None: + return est + else: + return sklearn.pipeline.make_pipeline(sklearn.base.clone(preprocessing_pipeline), est) + except: + return None -def recursive_with_defaults(config_dict, n_samples, n_features, classification, random_state=None, cv=None, subsets=None, feature_names=None, n_classes=None): - for key in 'leaf_config_dict', 'root_config_dict', 'inner_config_dict', 'Recursive': - if key in config_dict: - value = config_dict[key] - if key=="Resursive": - config_dict[key] = recursive_with_defaults(value, n_samples, n_features, classification, random_state, cv, subsets=None, feature_names=None, n_classes=None) - else: - config_dict[key] = get_configuration_dictionary(value, n_samples, n_features, classification, random_state, cv, subsets, feature_names, n_classes) - return config_dict +def objective_function_generator(pipeline, x,y, scorers, cv, other_objective_functions, step=None, budget=None, generation=1, is_classification=True, export_graphpipeline=False, **pipeline_kwargs): + #pipeline = pipeline.export_pipeline(**pipeline_kwargs) + if export_graphpipeline: + pipeline = pipeline.export_flattened_graphpipeline(**pipeline_kwargs) + else: + pipeline = pipeline.export_pipeline() -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) 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 +65,13 @@ 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, export_graphpipeline=False, **pipeline_kwargs): #subsample the data - pipeline = pipeline.export_pipeline(memory=memory, cross_val_predict_cv=cross_val_predict_cv, subset_column=subset_column) + if export_graphpipeline: + pipeline = pipeline.export_flattened_graphpipeline(**pipeline_kwargs) + else: + pipeline = pipeline.export_pipeline() + fitted_pipeline = sklearn.base.clone(pipeline) fitted_pipeline.fit(X_train, y_train) diff --git a/tpot2/tpot_estimator/steady_state_estimator.py b/tpot2/tpot_estimator/steady_state_estimator.py index 240b3a86..4de12c47 100644 --- a/tpot2/tpot_estimator/steady_state_estimator.py +++ b/tpot2/tpot_estimator/steady_state_estimator.py @@ -27,7 +27,9 @@ def set_dask_settings(): #TODO inherit from _BaseComposition? class TPOTEstimatorSteadyState(BaseEstimator): - def __init__(self, scorers= [], + def __init__(self, + search_space, + scorers= [], scorers_weights = [], classification = False, cv = 5, @@ -35,15 +37,14 @@ def __init__(self, scorers= [], 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, + + + export_graphpipeline = False, cross_val_predict_cv = 0, + memory = None, + categorical_features = None, subsets = None, - memory = None, preprocessing = False, validation_strategy = "none", validation_fraction = .2, @@ -77,7 +78,6 @@ def __init__(self, scorers= [], stepwise_steps = 5, warm_start = False, - subset_column = None, verbose = 0, periodic_checkpoint_folder = None, @@ -364,8 +364,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. verbose : int, default=1 How much information to print during the optimization process. Higher values include the information from lower values. @@ -422,6 +420,7 @@ def __init__(self, scorers= [], # sklearn BaseEstimator must have a corresponding attribute for each parameter. # These should not be modified once set. + self.search_space = search_space self.scorers = scorers self.scorers_weights = scorers_weights self.classification = classification @@ -430,15 +429,18 @@ 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.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.export_graphpipeline = export_graphpipeline self.cross_val_predict_cv = cross_val_predict_cv + self.memory = memory + + if self.cross_val_predict_cv !=0 or self.memory is not None: + if not self.export_graphpipeline: + raise ValueError("cross_val_predict_cv and memory parameters are parameters for GraphPipeline. To enable these options export_graphpipeline to be True. Otherwise these can be passed into the relevant Search spaces as parameters.") + + self.categorical_features = categorical_features self.subsets = subsets - self.memory = memory self.preprocessing = preprocessing self.validation_strategy = validation_strategy self.validation_fraction = validation_fraction @@ -468,7 +470,6 @@ def __init__(self, scorers= [], self.stepwise_steps = stepwise_steps self.warm_start = warm_start - self.subset_column = subset_column self.verbose = verbose self.periodic_checkpoint_folder = periodic_checkpoint_folder @@ -483,7 +484,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 @@ -660,17 +661,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) @@ -681,9 +671,9 @@ def objective_function(pipeline_individual, scorers= self._scorers, cv=self.cv_gen, other_objective_functions=self.other_objective_functions, + export_graphpipeline=self.export_graphpipeline, memory=self.memory, cross_val_predict_cv=self.cross_val_predict_cv, - subset_column=self.subset_column, **kwargs): return objective_function_generator( pipeline_individual, @@ -693,19 +683,16 @@ def objective_function(pipeline_individual, scorers= scorers, cv=cv, other_objective_functions=other_objective_functions, + export_graphpipeline=export_graphpipeline, 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, - ) + def ind_generator(rng): + rng = np.random.default_rng(rng) + while True: + yield self.search_space.generate(rng) @@ -718,7 +705,7 @@ def objective_function(pipeline_individual, #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, @@ -759,7 +746,7 @@ def objective_function(pipeline_individual, max_evaluated_individuals = self.max_evaluated_individuals, - rng_=self.rng, + rng=self.rng, ) @@ -782,7 +769,7 @@ def objective_function(pipeline_individual, 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"]) + tpot2.utils.get_pareto_frontier(self.evaluated_individuals, column_names=self.objective_names, weights=self.objective_function_weights) if validation_strategy == 'reshuffled': best_pareto_front_idx = list(self.pareto_front.index) @@ -805,9 +792,10 @@ def objective_function(pipeline_individual, scorers= self._scorers, cv=self.cv_gen, other_objective_functions=self.other_objective_functions, + export_graphpipeline=self.export_graphpipeline, memory=self.memory, cross_val_predict_cv=self.cross_val_predict_cv, - subset_column=self.subset_column, + **kwargs: objective_function_generator( ind, X, @@ -816,23 +804,23 @@ def objective_function(pipeline_individual, scorers= scorers, cv=cv, other_objective_functions=other_objective_functions, + export_graphpipeline=export_graphpipeline, memory=memory, cross_val_predict_cv=cross_val_predict_cv, - subset_column=subset_column, **kwargs, )] objective_kwargs = {"X": X_future, "y": y_future} - val_scores = tpot2.utils.eval_utils.parallel_eval_objective_list( - best_pareto_front, - val_objective_function_list, n_jobs=self.n_jobs, verbose=self.verbose, timeout=self.max_eval_time_seconds,n_expected_columns=len(self.objective_names), client=_client, **objective_kwargs) + val_scores, start_times, end_times, eval_errors = tpot2.utils.eval_utils.parallel_eval_objective_list2(best_pareto_front, val_objective_function_list, verbose=self.verbose, max_eval_time_seconds=self.max_eval_time_seconds, n_expected_columns=len(self.objective_names), client=_client, **objective_kwargs) val_objective_names = ['validation_'+name for name in self.objective_names] self.objective_names_for_selection = val_objective_names self.evaluated_individuals.loc[best_pareto_front_idx,val_objective_names] = val_scores + self.evaluated_individuals.loc[best_pareto_front_idx,'validation_start_times'] = start_times + self.evaluated_individuals.loc[best_pareto_front_idx,'validation_end_times'] = end_times + self.evaluated_individuals.loc[best_pareto_front_idx,'validation_eval_errors'] = eval_errors - self.evaluated_individuals["Validation_Pareto_Front"] = tpot2.utils.get_pareto_front(self.evaluated_individuals, val_objective_names, self.objective_function_weights, invalid_values=["TIMEOUT","INVALID"]) - + self.evaluated_individuals["Validation_Pareto_Front"] = tpot2.utils.get_pareto_frontier(self.evaluated_individuals, column_names=val_objective_names, weights=self.objective_function_weights) elif validation_strategy == 'split': @@ -858,9 +846,9 @@ def objective_function(pipeline_individual, y_val, scorers= self._scorers, other_objective_functions=self.other_objective_functions, + export_graphpipeline=self.export_graphpipeline, memory=self.memory, cross_val_predict_cv=self.cross_val_predict_cv, - subset_column=self.subset_column, **kwargs: val_objective_function_generator( ind, X, @@ -869,20 +857,24 @@ def objective_function(pipeline_individual, y_val, scorers= scorers, other_objective_functions=other_objective_functions, + export_graphpipeline=export_graphpipeline, memory=memory, cross_val_predict_cv=cross_val_predict_cv, - subset_column=subset_column, **kwargs, )] - val_scores = tpot2.utils.eval_utils.parallel_eval_objective_list( - best_pareto_front, - val_objective_function_list, n_jobs=self.n_jobs, verbose=self.verbose, timeout=self.max_eval_time_seconds,n_expected_columns=len(self.objective_names),client=_client, **objective_kwargs) + val_scores, start_times, end_times, eval_errors = tpot2.utils.eval_utils.parallel_eval_objective_list2(best_pareto_front, val_objective_function_list, verbose=self.verbose, max_eval_time_seconds=self.max_eval_time_seconds, n_expected_columns=len(self.objective_names), client=_client, **objective_kwargs) + + val_objective_names = ['validation_'+name for name in self.objective_names] self.objective_names_for_selection = val_objective_names self.evaluated_individuals.loc[best_pareto_front_idx,val_objective_names] = val_scores - self.evaluated_individuals["Validation_Pareto_Front"] = tpot2.utils.get_pareto_front(self.evaluated_individuals, val_objective_names, self.objective_function_weights, invalid_values=["TIMEOUT","INVALID"]) + self.evaluated_individuals.loc[best_pareto_front_idx,'validation_start_times'] = start_times + self.evaluated_individuals.loc[best_pareto_front_idx,'validation_end_times'] = end_times + self.evaluated_individuals.loc[best_pareto_front_idx,'validation_eval_errors'] = eval_errors + + self.evaluated_individuals["Validation_Pareto_Front"] = tpot2.utils.get_pareto_frontier(self.evaluated_individuals, column_names=val_objective_names, weights=self.objective_function_weights) else: self.objective_names_for_selection = self.objective_names @@ -898,7 +890,10 @@ 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) + if self.export_graphpipeline: + best_individual_pipeline = best_individual.export_flattened_graphpipeline(memory=self.memory, cross_val_predict_cv=self.cross_val_predict_cv) + else: + 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 ) @@ -910,8 +905,15 @@ def objective_function(pipeline_individual, if self.client is None: #no client was passed in #close cluster and client - _client.close() - cluster.close() + # _client.close() + # cluster.close() + try: + _client.shutdown() + cluster.close() + #catch exception + except Exception as e: + print("Error shutting down client and cluster") + Warning(e) return self @@ -979,7 +981,7 @@ def make_evaluated_individuals(self): self.evaluated_individuals = self.evaluated_individuals.set_index(self.evaluated_individuals.index.map(object_to_int)) self.evaluated_individuals['Parents'] = self.evaluated_individuals['Parents'].apply(lambda row: convert_parents_tuples_to_integers(row, object_to_int)) - self.evaluated_individuals["Instance"] = self.evaluated_individuals["Individual"].apply(lambda ind: apply_make_pipeline(ind, preprocessing_pipeline=self._preprocessing_pipeline)) + self.evaluated_individuals["Instance"] = self.evaluated_individuals["Individual"].apply(lambda ind: apply_make_pipeline(ind, preprocessing_pipeline=self._preprocessing_pipeline, export_graphpipeline=self.export_graphpipeline, memory=self.memory, cross_val_predict_cv=self.cross_val_predict_cv)) return self.evaluated_individuals 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 diff --git a/tpot2/tpot_estimator/templates/tpottemplates.py b/tpot2/tpot_estimator/templates/tpottemplates.py index 6da52dad..3871e6e1 100644 --- a/tpot2/tpot_estimator/templates/tpottemplates.py +++ b/tpot2/tpot_estimator/templates/tpottemplates.py @@ -1,26 +1,24 @@ import tpot2 import numpy as np +import pandas as pd from ..estimator import TPOTEstimator from sklearn.utils.validation import check_X_y, check_array, check_is_fitted from tpot2.selectors import survival_select_NSGA2, tournament_selection_dominated #TODO These do not follow sklearn conventions of __init__ +from ..default_search_spaces import get_default_search_space + class TPOTRegressor(TPOTEstimator): def __init__( self, + search_space = "linear", scorers=['neg_mean_squared_error'], scorers_weights=[1], + cv = 10, #remove this and use a value based on dataset size? other_objective_functions=[], #tpot2.objectives.estimator_objective_functions.number_of_nodes_objective], 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, @@ -33,60 +31,247 @@ def __init__( self, periodic_checkpoint_folder = None, verbose = 0, memory_limit = "4GB", - client = None + client = None, + random_state=None, + allow_inner_regressors=True, + **tpotestimator_kwargs, ): - """ - See TPOTEstimator for documentation - """ - super(TPOTRegressor,self).__init__( - scorers=scorers, - scorers_weights=scorers_weights, - cv=5, - other_objective_functions=other_objective_functions, #tpot2.objectives.estimator_objective_functions.number_of_nodes_objective], - 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, - categorical_features = categorical_features, - subsets = subsets, - memory = memory, - preprocessing = preprocessing, - max_time_seconds=max_time_seconds, - max_eval_time_seconds=max_eval_time_seconds, - n_jobs=n_jobs, - validation_strategy = validation_strategy, - validation_fraction = validation_fraction, - early_stop = early_stop, - warm_start = warm_start, - periodic_checkpoint_folder = periodic_checkpoint_folder, - verbose = verbose, - classification=False, - memory_limit = memory_limit, - client = client -) + ''' + An sklearn baseestimator that uses genetic programming to optimize a regression pipeline. + For more parameters, see the TPOTEstimator class. + + Parameters + ---------- + + search_space : (String, tpot2.search_spaces.SklearnIndividualGenerator) + - String : The default search space to use for the optimization. This can be either "linear" or "graph". If "linear", will use the default linear pipeline search space. If "graph", will use the default graph pipeline search space. + - SklearnIndividualGenerator : The search space to use for the optimization. This should be an instance of a SklearnIndividualGenerator. + The search space to use for the optimization. This should be an instance of a SklearnIndividualGenerator. + TPOT2 has groups of search spaces found in the following folders, tpot2.search_spaces.nodes for the nodes in the pipeline and tpot2.search_spaces.pipelines for the pipeline structure. + + scorers : (list, scorer) + A scorer or list of scorers to be used in the cross-validation process. + see https://scikit-learn.org/stable/modules/model_evaluation.html + + scorers_weights : list + A list of weights to be applied to the scorers during the optimization process. + + classification : bool + If True, the problem is treated as a classification problem. If False, the problem is treated as a regression problem. + Used to determine the CV strategy. + + cv : int, cross-validator + - (int): Number of folds to use in the cross-validation process. By uses the sklearn.model_selection.KFold cross-validator for regression and StratifiedKFold for classification. In both cases, shuffled is set to True. + - (sklearn.model_selection.BaseCrossValidator): A cross-validator to use in the cross-validation process. + - max_depth (int): The maximum depth from any node to the root of the pipelines to be generated. + + other_objective_functions : list, default=[] + A list of other objective functions to apply to the pipeline. The function takes a single parameter for the graphpipeline estimator and returns either a single score or a list of scores. + + other_objective_functions_weights : list, default=[] + A list of weights to be applied to the other objective functions. + + objective_function_names : list, default=None + A list of names to be applied to the objective functions. If None, will use the names of the objective functions. + + bigger_is_better : bool, default=True + If True, the objective function is maximized. If False, the objective function is minimized. Use negative weights to reverse the direction. + + categorical_features : list or None + Categorical columns to inpute and/or one hot encode during the preprocessing step. Used only if preprocessing is not False. + + categorical_features: list or None + Categorical columns to inpute and/or one hot encode during the preprocessing step. Used only if preprocessing is not False. + - None : If None, TPOT2 will automatically use object columns in pandas dataframes as objects for one hot encoding in preprocessing. + - List of categorical features. If X is a dataframe, this should be a list of column names. If X is a numpy array, this should be a list of column indices + + + memory: Memory object or string, default=None + If supplied, pipeline will cache each transformer after calling fit. This feature + is used to avoid computing the fit transformers within a pipeline if the parameters + and input data are identical with another fitted pipeline during optimization process. + - String 'auto': + TPOT uses memory caching with a temporary directory and cleans it up upon shutdown. + - String path of a caching directory + TPOT uses memory caching with the provided directory and TPOT does NOT clean + the caching directory up upon shutdown. If the directory does not exist, TPOT will + create it. + - Memory object: + TPOT uses the instance of joblib.Memory for memory caching, + and TPOT does NOT clean the caching directory up upon shutdown. + - None: + TPOT does not use memory caching. + + preprocessing : bool or BaseEstimator/Pipeline, + EXPERIMENTAL + A pipeline that will be used to preprocess the data before CV. Note that the parameters for these steps are not optimized. Add them to the search space to be optimized. + - bool : If True, will use a default preprocessing pipeline which includes imputation followed by one hot encoding. + - Pipeline : If an instance of a pipeline is given, will use that pipeline as the preprocessing pipeline. + + max_time_seconds : float, default=float("inf") + Maximum time to run the optimization. If none or inf, will run until the end of the generations. + + max_eval_time_seconds : float, default=60*5 + Maximum time to evaluate a single individual. If none or inf, there will be no time limit per evaluation. + + + n_jobs : int, default=1 + Number of processes to run in parallel. + + validation_strategy : str, default='none' + EXPERIMENTAL The validation strategy to use for selecting the final pipeline from the population. TPOT2 may overfit the cross validation score. A second validation set can be used to select the final pipeline. + - 'auto' : Automatically determine the validation strategy based on the dataset shape. + - 'reshuffled' : Use the same data for cross validation and final validation, but with different splits for the folds. This is the default for small datasets. + - 'split' : Use a separate validation set for final validation. Data will be split according to validation_fraction. This is the default for medium datasets. + - 'none' : Do not use a separate validation set for final validation. Select based on the original cross-validation score. This is the default for large datasets. + + validation_fraction : float, default=0.2 + EXPERIMENTAL The fraction of the dataset to use for the validation set when validation_strategy is 'split'. Must be between 0 and 1. + + early_stop : int, default=None + Number of generations without improvement before early stopping. All objectives must have converged within the tolerance for this to be triggered. + + warm_start : bool, default=False + If True, will use the continue the evolutionary algorithm from the last generation of the previous run. + + 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. + + + verbose : int, default=1 + How much information to print during the optimization process. Higher values include the information from lower values. + 0. nothing + 1. progress bar + + 3. best individual + 4. warnings + >=5. full warnings trace + 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.) + + + memory_limit : str, default="4GB" + Memory limit for each job. See Dask [LocalCluster documentation](https://distributed.dask.org/en/stable/api.html#distributed.Client) for more information. + + client : dask.distributed.Client, default=None + A dask client to use for parallelization. If not None, this will override the n_jobs and memory_limit parameters. If None, will create a new client with num_workers=n_jobs and memory_limit=memory_limit. + + random_state : int, None, default=None + A seed 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 + + - int + Will be used to create and lock in Generator instance with 'numpy.random.default_rng()' + - None + Will be used to create Generator for 'numpy.random.default_rng()' where a fresh, unpredictable entropy will be pulled from the OS + + allow_inner_regressors : bool, default=True + If True, the search space will include ensembled regressors. + + Attributes + ---------- + + fitted_pipeline_ : GraphPipeline + A fitted instance of the GraphPipeline that inherits from sklearn BaseEstimator. This is fitted on the full X, y passed to fit. + + evaluated_individuals : A pandas data frame containing data for all evaluated individuals in the run. + Columns: + - *objective functions : The first few columns correspond to the passed in scorers and objective functions + - Parents : A tuple containing the indexes of the pipelines used to generate the pipeline of that row. If NaN, this pipeline was generated randomly in the initial population. + - Variation_Function : Which variation function was used to mutate or crossover the parents. If NaN, this pipeline was generated randomly in the initial population. + - Individual : The internal representation of the individual that is used during the evolutionary algorithm. This is not an sklearn BaseEstimator. + - Generation : The generation the pipeline first appeared. + - Pareto_Front : The nondominated front that this pipeline belongs to. 0 means that its scores is not strictly dominated by any other individual. + To save on computational time, the best frontier is updated iteratively each generation. + The pipelines with the 0th pareto front do represent the exact best frontier. However, the pipelines with pareto front >= 1 are only in reference to the other pipelines in the final population. + All other pipelines are set to NaN. + - Instance : The unfitted GraphPipeline BaseEstimator. + - *validation objective functions : Objective function scores evaluated on the validation set. + - Validation_Pareto_Front : The full pareto front calculated on the validation set. This is calculated for all pipelines with Pareto_Front equal to 0. Unlike the Pareto_Front which only calculates the frontier and the final population, the Validation Pareto Front is calculated for all pipelines tested on the validation set. + + pareto_front : The same pandas dataframe as evaluated individuals, but containing only the frontier pareto front pipelines. + ''' + + self.search_space = search_space + self.scorers = scorers + self.scorers_weights = scorers_weights + self.cv = cv + self.other_objective_functions = other_objective_functions + self.other_objective_functions_weights = other_objective_functions_weights + self.objective_function_names = objective_function_names + self.bigger_is_better = bigger_is_better + self.categorical_features = categorical_features + self.memory = memory + self.preprocessing = preprocessing + self.max_time_seconds = max_time_seconds + self.max_eval_time_seconds = max_eval_time_seconds + self.n_jobs = n_jobs + self.validation_strategy = validation_strategy + self.validation_fraction = validation_fraction + self.early_stop = early_stop + self.warm_start = warm_start + self.periodic_checkpoint_folder = periodic_checkpoint_folder + self.verbose = verbose + self.memory_limit = memory_limit + self.client = client + self.random_state = random_state + self.allow_inner_regressors = allow_inner_regressors + self.tpotestimator_kwargs = tpotestimator_kwargs + + self.initialized = False + + + def fit(self, X, y): + + if not self.initialized: + get_search_space_params = {"n_classes": None, + "n_samples":len(y), + "n_features":X.shape[1], + "random_state":self.random_state} + + search_space = get_default_search_space(self.search_space, classification=True, inner_predictors=self.allow_inner_regressors, **get_search_space_params) + + super(TPOTRegressor,self).__init__( + search_space=search_space, + scorers=self.scorers, + scorers_weights=self.scorers_weights, + cv=self.cv, + other_objective_functions=self.other_objective_functions, #tpot2.objectives.estimator_objective_functions.number_of_nodes_objective], + other_objective_functions_weights = self.other_objective_functions_weights, + objective_function_names = self.objective_function_names, + bigger_is_better = self.bigger_is_better, + categorical_features = self.categorical_features, + memory = self.memory, + preprocessing = self.preprocessing, + max_time_seconds=self.max_time_seconds, + max_eval_time_seconds=self.max_eval_time_seconds, + n_jobs=self.n_jobs, + validation_strategy = self.validation_strategy, + validation_fraction = self.validation_fraction, + early_stop = self.early_stop, + warm_start = self.warm_start, + periodic_checkpoint_folder = self.periodic_checkpoint_folder, + verbose = self.verbose, + classification=False, + memory_limit = self.memory_limit, + client = self.client, + random_state=self.random_state, + **self.tpotestimator_kwargs) + self.initialized = True + + return super().fit(X,y) class TPOTClassifier(TPOTEstimator): def __init__( self, + search_space = "linear", scorers=['roc_auc_ovr'], scorers_weights=[1], + cv = 10, other_objective_functions=[], #tpot2.objectives.estimator_objective_functions.number_of_nodes_objective], 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, @@ -99,43 +284,235 @@ def __init__( self, periodic_checkpoint_folder = None, verbose = 0, memory_limit = "4GB", - client = None + client = None, + random_state=None, + allow_inner_classifiers=True, + **tpotestimator_kwargs, ): """ - See TPOTEstimator for documentation + An sklearn baseestimator that uses genetic programming to optimize a classification pipeline. + For more parameters, see the TPOTEstimator class. + + Parameters + ---------- + + search_space : (String, tpot2.search_spaces.SklearnIndividualGenerator) + - String : The default search space to use for the optimization. This can be either "linear" or "graph". If "linear", will use the default linear pipeline search space. If "graph", will use the default graph pipeline search space. + - SklearnIndividualGenerator : The search space to use for the optimization. This should be an instance of a SklearnIndividualGenerator. + The search space to use for the optimization. This should be an instance of a SklearnIndividualGenerator. + TPOT2 has groups of search spaces found in the following folders, tpot2.search_spaces.nodes for the nodes in the pipeline and tpot2.search_spaces.pipelines for the pipeline structure. + + scorers : (list, scorer) + A scorer or list of scorers to be used in the cross-validation process. + see https://scikit-learn.org/stable/modules/model_evaluation.html + + scorers_weights : list + A list of weights to be applied to the scorers during the optimization process. + + classification : bool + If True, the problem is treated as a classification problem. If False, the problem is treated as a regression problem. + Used to determine the CV strategy. + + cv : int, cross-validator + - (int): Number of folds to use in the cross-validation process. By uses the sklearn.model_selection.KFold cross-validator for regression and StratifiedKFold for classification. In both cases, shuffled is set to True. + - (sklearn.model_selection.BaseCrossValidator): A cross-validator to use in the cross-validation process. + - max_depth (int): The maximum depth from any node to the root of the pipelines to be generated. + + other_objective_functions : list, default=[] + A list of other objective functions to apply to the pipeline. The function takes a single parameter for the graphpipeline estimator and returns either a single score or a list of scores. + + other_objective_functions_weights : list, default=[] + A list of weights to be applied to the other objective functions. + + objective_function_names : list, default=None + A list of names to be applied to the objective functions. If None, will use the names of the objective functions. + + bigger_is_better : bool, default=True + If True, the objective function is maximized. If False, the objective function is minimized. Use negative weights to reverse the direction. + + categorical_features : list or None + Categorical columns to inpute and/or one hot encode during the preprocessing step. Used only if preprocessing is not False. + + categorical_features: list or None + Categorical columns to inpute and/or one hot encode during the preprocessing step. Used only if preprocessing is not False. + - None : If None, TPOT2 will automatically use object columns in pandas dataframes as objects for one hot encoding in preprocessing. + - List of categorical features. If X is a dataframe, this should be a list of column names. If X is a numpy array, this should be a list of column indices + + + memory: Memory object or string, default=None + If supplied, pipeline will cache each transformer after calling fit. This feature + is used to avoid computing the fit transformers within a pipeline if the parameters + and input data are identical with another fitted pipeline during optimization process. + - String 'auto': + TPOT uses memory caching with a temporary directory and cleans it up upon shutdown. + - String path of a caching directory + TPOT uses memory caching with the provided directory and TPOT does NOT clean + the caching directory up upon shutdown. If the directory does not exist, TPOT will + create it. + - Memory object: + TPOT uses the instance of joblib.Memory for memory caching, + and TPOT does NOT clean the caching directory up upon shutdown. + - None: + TPOT does not use memory caching. + + preprocessing : bool or BaseEstimator/Pipeline, + EXPERIMENTAL + A pipeline that will be used to preprocess the data before CV. Note that the parameters for these steps are not optimized. Add them to the search space to be optimized. + - bool : If True, will use a default preprocessing pipeline which includes imputation followed by one hot encoding. + - Pipeline : If an instance of a pipeline is given, will use that pipeline as the preprocessing pipeline. + + max_time_seconds : float, default=float("inf") + Maximum time to run the optimization. If none or inf, will run until the end of the generations. + + max_eval_time_seconds : float, default=60*5 + Maximum time to evaluate a single individual. If none or inf, there will be no time limit per evaluation. + + + n_jobs : int, default=1 + Number of processes to run in parallel. + + validation_strategy : str, default='none' + EXPERIMENTAL The validation strategy to use for selecting the final pipeline from the population. TPOT2 may overfit the cross validation score. A second validation set can be used to select the final pipeline. + - 'auto' : Automatically determine the validation strategy based on the dataset shape. + - 'reshuffled' : Use the same data for cross validation and final validation, but with different splits for the folds. This is the default for small datasets. + - 'split' : Use a separate validation set for final validation. Data will be split according to validation_fraction. This is the default for medium datasets. + - 'none' : Do not use a separate validation set for final validation. Select based on the original cross-validation score. This is the default for large datasets. + + validation_fraction : float, default=0.2 + EXPERIMENTAL The fraction of the dataset to use for the validation set when validation_strategy is 'split'. Must be between 0 and 1. + + early_stop : int, default=None + Number of generations without improvement before early stopping. All objectives must have converged within the tolerance for this to be triggered. + + warm_start : bool, default=False + If True, will use the continue the evolutionary algorithm from the last generation of the previous run. + + 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. + + + verbose : int, default=1 + How much information to print during the optimization process. Higher values include the information from lower values. + 0. nothing + 1. progress bar + + 3. best individual + 4. warnings + >=5. full warnings trace + 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.) + + + memory_limit : str, default="4GB" + Memory limit for each job. See Dask [LocalCluster documentation](https://distributed.dask.org/en/stable/api.html#distributed.Client) for more information. + + client : dask.distributed.Client, default=None + A dask client to use for parallelization. If not None, this will override the n_jobs and memory_limit parameters. If None, will create a new client with num_workers=n_jobs and memory_limit=memory_limit. + + random_state : int, None, default=None + A seed 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 + + - int + Will be used to create and lock in Generator instance with 'numpy.random.default_rng()' + - None + Will be used to create Generator for 'numpy.random.default_rng()' where a fresh, unpredictable entropy will be pulled from the OS + + allow_inner_classifiers : bool, default=True + If True, the search space will include ensembled classifiers. + + Attributes + ---------- + + fitted_pipeline_ : GraphPipeline + A fitted instance of the GraphPipeline that inherits from sklearn BaseEstimator. This is fitted on the full X, y passed to fit. + + evaluated_individuals : A pandas data frame containing data for all evaluated individuals in the run. + Columns: + - *objective functions : The first few columns correspond to the passed in scorers and objective functions + - Parents : A tuple containing the indexes of the pipelines used to generate the pipeline of that row. If NaN, this pipeline was generated randomly in the initial population. + - Variation_Function : Which variation function was used to mutate or crossover the parents. If NaN, this pipeline was generated randomly in the initial population. + - Individual : The internal representation of the individual that is used during the evolutionary algorithm. This is not an sklearn BaseEstimator. + - Generation : The generation the pipeline first appeared. + - Pareto_Front : The nondominated front that this pipeline belongs to. 0 means that its scores is not strictly dominated by any other individual. + To save on computational time, the best frontier is updated iteratively each generation. + The pipelines with the 0th pareto front do represent the exact best frontier. However, the pipelines with pareto front >= 1 are only in reference to the other pipelines in the final population. + All other pipelines are set to NaN. + - Instance : The unfitted GraphPipeline BaseEstimator. + - *validation objective functions : Objective function scores evaluated on the validation set. + - Validation_Pareto_Front : The full pareto front calculated on the validation set. This is calculated for all pipelines with Pareto_Front equal to 0. Unlike the Pareto_Front which only calculates the frontier and the final population, the Validation Pareto Front is calculated for all pipelines tested on the validation set. + + pareto_front : The same pandas dataframe as evaluated individuals, but containing only the frontier pareto front pipelines. """ - super(TPOTClassifier,self).__init__( - scorers=scorers, - scorers_weights=scorers_weights, - cv = 5, - other_objective_functions=other_objective_functions, #tpot2.objectives.estimator_objective_functions.number_of_nodes_objective], - 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, - categorical_features = categorical_features, - subsets = subsets, - memory = memory, - preprocessing = preprocessing, - max_time_seconds=max_time_seconds, - max_eval_time_seconds=max_eval_time_seconds, - n_jobs=n_jobs, - validation_strategy = validation_strategy, - validation_fraction = validation_fraction, - early_stop = early_stop, - warm_start = warm_start, - periodic_checkpoint_folder = periodic_checkpoint_folder, - verbose = verbose, - classification=True, - memory_limit = memory_limit, - client = client - ) + self.search_space = search_space + self.scorers = scorers + self.scorers_weights = scorers_weights + self.cv = cv + self.other_objective_functions = other_objective_functions + self.other_objective_functions_weights = other_objective_functions_weights + self.objective_function_names = objective_function_names + self.bigger_is_better = bigger_is_better + self.categorical_features = categorical_features + self.memory = memory + self.preprocessing = preprocessing + self.max_time_seconds = max_time_seconds + self.max_eval_time_seconds = max_eval_time_seconds + self.n_jobs = n_jobs + self.validation_strategy = validation_strategy + self.validation_fraction = validation_fraction + self.early_stop = early_stop + self.warm_start = warm_start + self.periodic_checkpoint_folder = periodic_checkpoint_folder + self.verbose = verbose + self.memory_limit = memory_limit + self.client = client + self.random_state = random_state + self.tpotestimator_kwargs = tpotestimator_kwargs + self.allow_inner_classifiers = allow_inner_classifiers + + self.initialized = False + + def fit(self, X, y): + + if not self.initialized: + + get_search_space_params = {"n_classes": len(np.unique(y)), + "n_samples":len(y), + "n_features":X.shape[1], + "random_state":self.random_state} + + search_space = get_default_search_space(self.search_space, classification=True, inner_predictors=self.allow_inner_classifiers, **get_search_space_params) + + + super(TPOTClassifier,self).__init__( + search_space=search_space, + scorers=self.scorers, + scorers_weights=self.scorers_weights, + cv = self.cv, + other_objective_functions=self.other_objective_functions, #tpot2.objectives.estimator_objective_functions.number_of_nodes_objective], + other_objective_functions_weights = self.other_objective_functions_weights, + objective_function_names = self.objective_function_names, + bigger_is_better = self.bigger_is_better, + categorical_features = self.categorical_features, + memory = self.memory, + preprocessing = self.preprocessing, + max_time_seconds=self.max_time_seconds, + max_eval_time_seconds=self.max_eval_time_seconds, + n_jobs=self.n_jobs, + validation_strategy = self.validation_strategy, + validation_fraction = self.validation_fraction, + early_stop = self.early_stop, + warm_start = self.warm_start, + periodic_checkpoint_folder = self.periodic_checkpoint_folder, + verbose = self.verbose, + classification=True, + memory_limit = self.memory_limit, + client = self.client, + random_state=self.random_state, + **self.tpotestimator_kwargs) + self.initialized = True + + return super().fit(X,y) def predict(self, X, **predict_params): diff --git a/tpot2/tpot_estimator/tests/__init__.py b/tpot2/tpot_estimator/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tpot2/utils/eval_utils.py b/tpot2/utils/eval_utils.py index ccc847f3..f8d4bd7f 100644 --- a/tpot2/utils/eval_utils.py +++ b/tpot2/utils/eval_utils.py @@ -145,6 +145,7 @@ def parallel_eval_objective_list2(individual_list, max_eval_time_seconds=None, n_expected_columns=None, client=None, + scheduled_timeout_time=None, **objective_kwargs): individual_stack = list(individual_list) @@ -152,6 +153,7 @@ def parallel_eval_objective_list2(individual_list, submitted_futures = {} scores_dict = {} submitted_inds = set() + global_timeout_triggered = False while len(submitted_futures) < max_queue_size and len(individual_stack)>0: individual = individual_stack.pop() future = client.submit(eval_objective_list, individual, objective_list, verbose=verbose, timeout=max_eval_time_seconds,**objective_kwargs) @@ -172,6 +174,8 @@ def parallel_eval_objective_list2(individual_list, except dask.distributed.CancelledError: pass + global_timeout_triggered = scheduled_timeout_time is not None and time.time() > scheduled_timeout_time + #Loop through all futures, collect completed and timeout futures. for completed_future in list(submitted_futures.keys()): #get scores and update @@ -211,8 +215,10 @@ def parallel_eval_objective_list2(individual_list, eval_error = "INVALID" else: #if future is not done + + #check if the future has been running for too long, cancel the future - if time.time() - submitted_futures[completed_future]["time"] > max_eval_time_seconds*1.25: + if max_eval_time_seconds is not None and time.time() - submitted_futures[completed_future]["time"] > max_eval_time_seconds*1.25: completed_future.cancel() if verbose >= 4: @@ -220,6 +226,15 @@ def parallel_eval_objective_list2(individual_list, scores = [np.nan for _ in range(n_expected_columns)] eval_error = "TIMEOUT" + elif global_timeout_triggered: + completed_future.cancel() + + if verbose >= 4: + print(f'WARNING AN INDIVIDUAL TIMED OUT (max_time_seconds): \n {submitted_futures[completed_future]} \n') + + scores = [np.nan for _ in range(n_expected_columns)] + eval_error = None + else: continue #otherwise, continue to next future @@ -235,6 +250,17 @@ def parallel_eval_objective_list2(individual_list, #update submitted futures submitted_futures.pop(completed_future) + #break if timeout + if global_timeout_triggered: + while len(individual_stack) > 0: + individual = individual_stack.pop() + scores_dict[individual] = {"scores": [np.nan for _ in range(n_expected_columns)], + "start_time": time.time(), + "end_time": time.time(), + "eval_error": None, + } + break + #submit new futures while len(submitted_futures) < max_queue_size and len(individual_stack)>0: individual = individual_stack.pop() @@ -246,6 +272,9 @@ def parallel_eval_objective_list2(individual_list, submitted_inds.add(individual.unique_id()) + #collect remaining futures + + final_scores = [scores_dict[individual]["scores"] for individual in individual_list] final_start_times = [scores_dict[individual]["start_time"] for individual in individual_list] final_end_times = [scores_dict[individual]["end_time"] for individual in individual_list] diff --git a/tpot2/utils/utils.py b/tpot2/utils/utils.py index fe165993..21ec0662 100644 --- a/tpot2/utils/utils.py +++ b/tpot2/utils/utils.py @@ -85,8 +85,9 @@ def is_pareto_efficient(scores, return_mask = True): else: return is_efficient -def get_pareto_frontier(df, column_names, weights, invalid_values=["TIMEOUT","INVALID"]): - dftmp = df[~df[column_names].isin(invalid_values).any(axis=1)] +def get_pareto_frontier(df, column_names, weights): + # dftmp = df[~df[column_names].isin(invalid_values).any(axis=1)] + dftmp = df[df[column_names].notnull().all(axis=1)] if "Budget" in dftmp.columns: #get rows with the max budget @@ -101,8 +102,8 @@ def get_pareto_frontier(df, column_names, weights, invalid_values=["TIMEOUT","IN -def get_pareto_front(df, column_names, weights, invalid_values=["TIMEOUT","INVALID"]): - dftmp = df[~df[column_names].isin(invalid_values).any(axis=1)] +def get_pareto_front(df, column_names, weights): + dftmp = df[df[column_names].notnull().all(axis=1)] if "Budget" in dftmp.columns: #get rows with the max budget