<jupyter_start><jupyter_text>Basics of ObjectiveLearner  What is ObjectiveLearner**ObjectiveLearner** defines the python module `objlearner` that provides functionality to run [Supervised Machine Learning](https://en.wikipedia.org/wiki/Machine_learningSupervised_learning) (linear regression) and [Sensitivity Analysis](https://en.wikipedia.org/wiki/Sensitivity_analysisRegression_analysis) on the objective function versus parameter relationship using the thousands (to millions) of (sometimes expensive) objective function evaluations performed during model calibration with packages like [PyDREAM](https://github.com/LoLab-VU/PyDREAM), [simplePSO](https://github.com/LoLab-VU/ParticleSwarmOptimization), [Gleipnir](https://github.com/LoLab-VU/Gleipnir), and [GAlibrate](https://github.com/blakeaw/GAlibrate).ObjectiveLearner provides easy to use objective function decorators which allow users to save data from the objective function evaluations performed during model calibration, and thereby provides them a way to utilize what would typically be lost data (i.e., not saved by the calibrator) and learn even more about the objective function and its relationship to model parameters, as well as learn more about the underlying model and assumptions the objective function represents.ObjectiveLearner installs as the `objlearner` package which defines the three decorator classes:   * ObjectiveCounter - counts the number of objective function calls.   * ObjectiveSaver - saves the input-output pairs fo the objective function calls.   * ObjectiveLearner - provides machine learning (linear regression) and sensitivity analysis.In the following sections we'll cover each class and it's use.   The Example ModelFor the purposes of this overview we will use the [GAlibrate](https://github.com/blakeaw/GAlibrate) package which defines a genetic algorithm optimizer: ```pip install galibrate```or ```conda install -c blakeaw galibrate```We will estimate the model parameters for a linear fit to three data points with uncertainty, an example adapted from the Nestle 'Getting started' section at: http://kylebarbary.com/nestle/ Here are the imports we need from NumPy and GAlibrate:<jupyter_code>import numpy as np
import warnings
warnings.filterwarnings('ignore')
from galibrate.sampled_parameter import SampledParameter
from galibrate import GAO<jupyter_output><empty_output><jupyter_text>And this is the data that we are going calibrate against:<jupyter_code># Setup the data points that are being fitted.
data_x = np.array([1., 2., 3.])
data_y = np.array([1.4, 1.7, 4.1])
data_yerr = np.array([0.2, 0.15, 0.2])<jupyter_output><empty_output><jupyter_text>This is the objective function for this problem, a genetic algorithm fitness function which will be maximized by the GAO:<jupyter_code>def fitness(chromosome):
    y = chromosome[1] * data_x + chromosome[0]
    chisq = np.sum(((data_y - y) / data_yerr)**2)
    if np.isnan(chisq):
        return -np.inf
    return -chisq / 2.<jupyter_output><empty_output><jupyter_text>ObjectiveCounterThe ObjectiveCounter simply keeps count of the number of calls to the objective function. It's imported from `objlearner`:<jupyter_code>from objlearner import ObjectiveCounter<jupyter_output><empty_output><jupyter_text>Then we use it as a decorator for the objective function:<jupyter_code>@ObjectiveCounter
def fitness(chromosome):
    y = chromosome[1] * data_x + chromosome[0]
    chisq = np.sum(((data_y - y) / data_yerr)**2)
    if np.isnan(chisq):
        return -np.inf
    return -chisq / 2.<jupyter_output><empty_output><jupyter_text>And then we setup and run the calibration run as normal:<jupyter_code># Set up the list of sampled parameters
parm_names = list(['m', 'b'])
sampled_parameters = [SampledParameter(name=p, loc=-5.0, width=10.0) for p in parm_names]

# Set the active point population size
population_size = 200
n_params = len(sampled_parameters)
print("Sampling a total of {} parameters".format(n_params))
print("Will use GA population size of {}".format(population_size))
# Construct the GAO
gao = GAO(sampled_parameters,
         fitness,
         population_size,
         generations = 100,
         mutation_rate = 0.1)
# run it
best, best_f = gao.run()
print(best, best_f)<jupyter_output>Sampling a total of 2 parameters
Will use GA population size of 200
[-0.56705695  1.36729111] -13.346725455987585<jupyter_text>Then we can access the information on the number of objective function evaluations with `count` member variable:<jupyter_code>print("Number of fitness evaluations: ",fitness.count)<jupyter_output>Number of fitness evaluations:  10300<jupyter_text>ObjectiveSaverIn addition to keeping count of the number of calls to the objective function, ObjectiveSaver also saves the input parameter vectors and the corresponding objective function evaluations. It's imported from `objlearner`:<jupyter_code>from objlearner import ObjectiveSaver<jupyter_output><empty_output><jupyter_text>Then we use it as a decorator for the objective function:<jupyter_code>@ObjectiveSaver
def fitness(chromosome):
    y = chromosome[1] * data_x + chromosome[0]
    chisq = np.sum(((data_y - y) / data_yerr)**2)
    if np.isnan(chisq):
        return -np.inf
    return -chisq / 2.<jupyter_output><empty_output><jupyter_text>And then we setup and run the calibration run as normal:<jupyter_code># Set up the list of sampled parameters
parm_names = list(['m', 'b'])
sampled_parameters = [SampledParameter(name=p, loc=-5.0, width=10.0) for p in parm_names]

# Set the active point population size
population_size = 200
n_params = len(sampled_parameters)
print("Sampling a total of {} parameters".format(n_params))
print("Will use GA population size of {}".format(population_size))
# Construct the GAO
gao = GAO(sampled_parameters,
         fitness,
         population_size,
         generations = 100,
         mutation_rate = 0.1)
# run it
best, best_f = gao.run()
print(best, best_f)<jupyter_output>Sampling a total of 2 parameters
Will use GA population size of 200
[-0.79918611  1.5037125 ] -13.66846625270572<jupyter_text>Then the principal information for the objective function evaluations is accessed with `objective_theta` member variable:<jupyter_code>print(fitness.objective_theta)<jupyter_output>objective         0         1
0      -582.077383  2.496212 -1.509628
1     -2040.860470 -4.143326 -0.037828
2     -1154.504034  2.668814  2.230891
3      -295.493719  0.838757 -0.347136
4     -1175.177549 -3.536096  0.437793
...            ...       ...       ...
10295   -75.299264 -3.106083  2.195946
10296  -121.823400 -3.106083  1.954393
10297  -547.583096 -1.569299  0.276223
10298 -3243.127509  3.189460  3.587610
10299 -1283.108397  0.597351  3.321245

[10300 rows x 3 columns]<jupyter_text>which returns a pandas DataFrame.Additionally, the ObjectiveSaver defines the following member variable and functions:  * count - the number of objective function evaluations  * write_csv(prefix='filename_prefix') - write out the objective function evaluation data as a csv file  * write_npy(prefix='filename_prefix') - write out the objective function evaluation data as a NumPy npy file   ObjectiveLearnerThe ObjectiveLearner is the principal (and name) tool from the **ObjectiveLearner** package, and it provides functionanlity to analyze objective function evaluation data with machine learning (linear regression) and sensitivity analysis. It's imported from `objlearner`:<jupyter_code>from objlearner import ObjectiveLearner<jupyter_output><empty_output><jupyter_text>Then we use it as a decorator for the objective function:<jupyter_code>@ObjectiveLearner
def fitness(chromosome):
    y = chromosome[1] * data_x + chromosome[0]
    chisq = np.sum(((data_y - y) / data_yerr)**2)
    if np.isnan(chisq):
        return -np.inf
    return -chisq / 2.<jupyter_output><empty_output><jupyter_text>And then we setup and run the calibration run as normal:<jupyter_code># Set up the list of sampled parameters
parm_names = list(['m', 'b'])
sampled_parameters = [SampledParameter(name=p, loc=-5.0, width=10.0) for p in parm_names]

# Set the active point population size
population_size = 200
n_params = len(sampled_parameters)
print("Sampling a total of {} parameters".format(n_params))
print("Will use GA population size of {}".format(population_size))
# Construct the GAO
gao = GAO(sampled_parameters,
         fitness,
         population_size,
         generations = 100,
         mutation_rate = 0.1)
# run it
best, best_f = gao.run()
print(best, best_f)<jupyter_output>Sampling a total of 2 parameters
Will use GA population size of 200
[0.31559723 1.16998722] -21.32452627782136<jupyter_text>Machine Learning (Linear Regression)The CostLearner decorator provides several functions to compute the coefficients and explained variance score between the objective function and input parameter vectors using linear regression methods (form [scikit-learn](https://scikit-learn.org/stable/index.html)):  * least_squares() - Least squares linear regression of the objective function against the parameters.  * ridge() - Ridge regression of the objective function against the parameters.  * lasso() - Lasso regression of the objective function against the parameters.  * linear_svr() - Linear Support Vector Regression (SVR) of the objective function against the parameters.  For example:<jupyter_code>coefs, ev_score = fitness.least_squares()
print("Least-squares linear regression coefficients and explained variance score:")
print(coefs, ev_score)<jupyter_output>Least-squares linear regression coefficients and explained variance score:
[266.20168399 530.35933768] 0.394795416957206<jupyter_text>For more information about machine learning, the following are a good place to start:  * https://scikit-learn.org/stable/tutorial/basic/tutorial.html  * https://en.wikipedia.org/wiki/Machine_learning  * https://towardsdatascience.com/machine-learning-for-beginners-d247a9420dab  Sensitivity AnalysisThe CostLearner decorator also provides several functions to compute sensitivity metrics between the objective function and input parameter vectors using sensitivity analyses (from [SALib](https://salib.readthedocs.io/en/latest/index.html)):  * **sobol()** - Sobol sensitivity of the objective function. Returns the SALib.analyze.sobol.analyze output dictionary; see https://salib.readthedocs.io/en/latest/api.htmlsobol-sensitivity-analysis for more information.  * **morris()** - Morris Method sensitivity of the objective function. Returns the SALib.analyze.morris.analyze output dictionary; see https://salib.readthedocs.io/en/latest/api.htmlmethod-of-morris for more information.  * **delta()** - Delta Moment-Independent Measure sensitivity of the objective function. Returns the SALib.analyze.delta.analyze output dictionary; see https://salib.readthedocs.io/en/latest/api.htmldelta-moment-independent-measure for more information.  * **fast()** - FAST sensitivity analysis of the objective function. Returns the SALib.analyze.fast.analyze output dictionary; see https://salib.readthedocs.io/en/latest/api.htmlfast-fourier-amplitude-sensitivity-test for more information.  * **rbd_fast()** - RBD-FAST sensitivity analysis of the objective function. Returns the SALib.analyze.rbd_fast.analyze output dictionary; see https://salib.readthedocs.io/en/latest/api.htmlfast-fourier-amplitude-sensitivity-test for more information.  For example:<jupyter_code>Si = fitness.sobol()
print(Si)<jupyter_output>{'S1': array([0.06311959, 0.61061728]), 'S1_conf': array([0.03795813, 0.06443074]), 'ST': array([0.3950405 , 0.93424471]), 'ST_conf': array([0.0388747 , 0.06694005]), 'S2': array([[       nan, 0.33133916],
       [       nan,        nan]]), 'S2_conf': array([[       nan, 0.08332596],
       [       nan,        nan]])}
{'fraction_non_alphanumeric': 0.0936943053551727, 'fraction_numerical': 0.05083595009759823, 'mean_word_length': 4.990849008642603, 'pattern_counts': {'":': 0, '<': 58, '<?xml version=': 0, '>': 58, 'https://': 17, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '16', 'dir': 'jupyter-structured-clean-dedup', 'id': '438243', 'license': 'MIT', 'n_tokens_mistral': 3979, 'n_tokens_neox': 3509, 'n_words': 1123, 'path': 'jupyter_notebooks/Basics_of_ObjectiveLearner.ipynb', 'repo_name': 'blakeaw/CostLearner', 'size': '11783'}