diff --git a/docs/wiki/4_SurrogateModel.md b/docs/wiki/4_SurrogateModel.md index d0f37d0..4ae7359 100644 --- a/docs/wiki/4_SurrogateModel.md +++ b/docs/wiki/4_SurrogateModel.md @@ -4,62 +4,103 @@ The `obsidian.surrogates` submodule is a key component of the Obsidian Bayesian optimization library. It provides a collection of surrogate models used to approximate the objective function in the optimization process. These surrogate models are essential for efficient exploration of the parameter space and for making informed decisions about which points to evaluate next. -## 2. Available Surrogate Models +## 2. How to Use Surrogate Models - Basic Syntax -The `obsidian.surrogates` submodule offers several types of surrogate models: +To use a surrogate model in your optimization process, you typically don't need to interact with it directly. The `obsidian.optimizer` submodule will handle the creation and management of the surrogate model. However, if you need to create a surrogate model manually, you can do so using the `SurrogateBoTorch` class. -1. **Gaussian Process (GP)**: The default surrogate model, suitable for most optimization tasks. -2. **Mixed Gaussian Process (MixedGP)**: A GP model that can handle mixed continuous and categorical input spaces. -3. **Deep Kernel Learning GP (DKL)**: A GP model with a neural network feature extractor. -4. **Flat GP**: A GP model with non-informative or no prior distributions. -5. **Prior GP**: A GP model with custom prior distributions. -6. **Multi-Task GP (MTGP)**: A GP model for multi-output optimization. -7. **Deep Neural Network (DNN)**: A dropout neural network model. +Below is a simple example using the default standard GP surrogate: -## 3. How to Use Surrogate Models - -To use a surrogate model in your optimization process, you typically don't need to interact with it directly. The Obsidian optimizer will handle the creation and management of the surrogate model. However, if you need to create a surrogate model manually, you can do so using the `SurrogateBoTorch` class: +### Define the parameter space: +```python +from obsidian.parameters import ParamSpace, Param_Continuous +params = ParamSpace([Param_Continuous('X1', 0, 1),Param_Continuous('X2', 0, 1)]) +X_space = ParamSpace(params) +``` +### Simulate training data: ```python -from obsidian.surrogates import SurrogateBoTorch -from obsidian.parameters import ParamSpace, Target +from obsidian.experiment import ExpDesigner +designer = ExpDesigner(X_space, seed = 789) +X_train = designer.initialize(m_initial = 10, method='Sobol') -# Define your parameter space -param_space = ParamSpace([...]) # Define your parameters here +from obsidian.parameters import Target +target = Target(name = 'Y', f_transform = 'Standard', aim='max') -# Create a surrogate model (default is GP) -surrogate = SurrogateBoTorch(model_type='GP') +from obsidian.experiment import Simulator +from obsidian.experiment.benchmark import paraboloid +simulator = Simulator(X_space, paraboloid, name='Y') +y_train = simulator.simulate(X_train) +y_train_transformed = target.transform_f(y_train, fit = True) -# Fit the model to your data -surrogate.fit(X, y) +import pandas as pd +print(pd.concat([X_train, y_train_transformed], axis=1).to_markdown()) +``` -# Make predictions +| | X1 | X2 | Y Trans | +|---:|---------:|----------:|----------:| +| 0 | 0.709874 | 0.891838 | -0.692069 | +| 1 | 0.316783 | 0.0374523 | -1.283 | +| 2 | 0.102254 | 0.517022 | -0.229447 | +| 3 | 0.99438 | 0.412149 | -1.33756 | +| 4 | 0.80163 | 0.663557 | 0.252902 | +| 5 | 0.162435 | 0.265744 | -0.351742 | +| 6 | 0.385264 | 0.788242 | 0.507141 | +| 7 | 0.523473 | 0.140918 | 0.113744 | +| 8 | 0.588516 | 0.604981 | 1.423 | +| 9 | 0.445465 | 0.465715 | 1.59703 | + + +### Create a surrogate model (using the default 'GP' model), and fit the model to the training data: +```python +from obsidian.surrogates import SurrogateBoTorch +surrogate = SurrogateBoTorch(model_type='GP', seed = 123) +surrogate.fit(X_train, y_train_transformed,cat_dims=[],task_feature=None) +``` + +### Generate new input experimental conditions and make predictions: +```python +X_new = designer.initialize(m_initial = 3, method='Sobol') mean, std = surrogate.predict(X_new) + +df = X_new.assign(pred_mean=mean,pred_std=std) +print(df.to_markdown()) ``` -## 4. Customization Options +| | X1 | X2 | pred_mean | pred_std | +|---:|---------:|----------:|------------:|-----------:| +| 0 | 0.709874 | 0.891838 | -0.669406 | 0.120078 | +| 1 | 0.316783 | 0.0374523 | -1.25515 | 0.119587 | +| 2 | 0.102254 | 0.517022 | -0.217739 | 0.12007 | + -### 4.1 Model Selection +## 3. Customization Options -You can choose different surrogate models by specifying the `model_type` parameter when creating a `SurrogateBoTorch` instance. Available options are: +### 3.1 Available Surrogate Models -- `'GP'`: Standard Gaussian Process -- `'MixedGP'`: Mixed input Gaussian Process -- `'DKL'`: Deep Kernel Learning GP -- `'GPflat'`: Flat (non-informative prior) GP -- `'GPprior'`: Custom prior GP -- `'MTGP'`: Multi-Task GP -- `'DNN'`: Dropout Neural Network +The `obsidian.surrogates` submodule offers several types of surrogate models. +You can choose different surrogate models by specifying the `model_type` parameter when creating a `SurrogateBoTorch` instance. +Available options are: -### 4.2 Hyperparameters +- `'GP'`: Standard Gaussian Process, which is the default surrogate model suitable for most optimization tasks. +- `'MixedGP'`: Mixed input Gaussian Process, which is a GP model that can handle mixed continuous and categorical input spaces. +- `'DKL'`: Deep Kernel Learning GP, which is a GP model with a neural network feature extractor. +- `'GPflat'`: A GP model with non-informative or no prior distributions. +- `'GPprior'`: A GP model with custom prior distributions. +- `'MTGP'`: Multi-Task GP, which is a GP model for multi-output optimization. +- `'DNN'`: Dropout Neural Network model. -You can pass custom hyperparameters to the surrogate model using the `hps` parameter: +### 3.2 Hyperparameters +You can pass custom hyperparameters to the surrogate model using the `hps` argument as: +> surrogate = SurrogateBoTorch(model_type='GP', hps={'custom_param_1': value_1, 'custom_param_2': value_2, ...}) + +Some specific examples: ```python -surrogate = SurrogateBoTorch(model_type='GP', hps={'your_custom_param': value}) +surrogate = SurrogateBoTorch(model_type='FlatGP', hps={'nu': 1.5}) +surrogate = SurrogateBoTorch(model_type='DNN', hps={'p_dropout': 0.1, 'h_width': 15, 'h_layers': 4, 'num_outputs': 2}) ``` -### 4.3 Custom GP Models +### 3.3 Custom GP Models The submodule provides several custom GP implementations: @@ -67,70 +108,60 @@ The submodule provides several custom GP implementations: - `FlatGP`: A GP with non-informative or no prior distributions - `DKLGP`: A GP with a neural network feature extractor -### 4.4 Custom Neural Network Model - -The `DNN` class provides a customizable dropout neural network model. You can adjust parameters such as dropout probability, hidden layer width, and number of hidden layers. - -## 5. Examples - -### 5.1 Using a standard GP surrogate - -```python -from obsidian.surrogates import SurrogateBoTorch -from obsidian.parameters import ParamSpace, Target -import pandas as pd - -# Define your parameter space -param_space = ParamSpace([...]) # Define your parameters here +### 3.4 Custom Neural Network Model -# Assume X and y are your input features and target variables -X = pd.DataFrame(...) -y = pd.Series(...) +The `DNN` class provides a customizable dropout neural network model. +The 'hps' parameter allows you to customize the DNN architecture. +You can adjust multiple DNN hyperparameters such as dropout probability, hidden layer width, and number of hidden layers. -surrogate = SurrogateBoTorch(model_type='GP') -surrogate.fit(X, y) +## 4. Additional Examples -# Make predictions -X_new = pd.DataFrame(...) -mean, std = surrogate.predict(X_new) -``` -### 5.2 Using a Mixed GP for categorical and continuous variables +### 4.1 Using a Mixed GP for categorical and continuous variables ```python +# DO NOT RUN surrogate = SurrogateBoTorch(model_type='MixedGP') # cat_dims should be a list of indices for categorical variables in your input space surrogate.fit(X, y, cat_dims=[0, 2]) # Assuming columns 0 and 2 are categorical ``` -### 5.3 Using a DNN surrogate +### 4.2 Using a DNN surrogate ```python -# The 'hps' parameter allows you to customize the DNN architecture surrogate = SurrogateBoTorch(model_type='DNN', hps={'p_dropout': 0.1, 'h_width': 32, 'h_layers': 3}) -surrogate.fit(X, y) +surrogate.fit(X_train, y_train_transformed,cat_dims=[],task_feature=None) ``` -## 6. Advanced Usage +## 5. Advanced Usage -### 6.1 Saving and Loading Models +### 5.1 Saving and Loading Models -You can save and load surrogate models using the `save_state()` and `load_state()` methods: +You can save and load surrogate models to/from dictionary objects using the `save_state()` and `load_state()` methods, +which enable saving the trained model to json files and reload for future usage: ```python -# Save model state -state = surrogate.save_state() +import json + +# Save model state as dictionary to json file +with open('surrogate.json', 'w') as f: + surrogate_dict = surrogate.save_state() + json.dump(surrogate_dict, f) -# Load model state -loaded_surrogate = SurrogateBoTorch.load_state(state) +# Load model state dictionary from json file +with open('surrogate.json', 'r') as f: + surrogate_dict = json.load(f) + surrogate_reload = SurrogateBoTorch.load_state(surrogate_dict) ``` -### 6.2 Model Evaluation +### 5.2 Model Evaluation You can evaluate the performance of a surrogate model using the `score()` method: ```python -loss, r2_score = surrogate.score(X_test, y_test) +y_new = simulator.simulate(X_new) +y_new_transformed = target.transform_f(y_new, fit = False) +loss, r2_score = surrogate.score(X_new, y_new_transformed) ``` This concludes the user guide for the `obsidian.surrogates` submodule. For more detailed information, please refer to the source code and docstrings in the individual files. \ No newline at end of file diff --git a/obsidian/surrogates/botorch.py b/obsidian/surrogates/botorch.py index 9ddfaa0..c1e57ea 100644 --- a/obsidian/surrogates/botorch.py +++ b/obsidian/surrogates/botorch.py @@ -206,7 +206,7 @@ def score(self, # Calculate a final loss and R2 train score loss = self.loss = self.loss_fcn(self.torch_model(X_p), y_p).sum().detach().cpu().data.numpy().tolist() - score = self.score = corr_matrix[0][1]**2 + score = self.r2_score = corr_matrix[0][1]**2 return loss, score