Note
Click here to download the full example code
Minimal example for evolutionary regression using hurdlesΒΆ
Example demonstrating the use of Cartesian genetic programming for a simple regression task where we use hurdles to implement early stopping for low-performing invididuals.
Hurdles are implemented by introducing multiple objectives, here two, which are sequentially evaluated. Only those individuals with fitness in the upper 50th percentile on the first objective are evaluated on the second objective.
References:
Real, E., Liang, C., So, D., & Le, Q. (2020, November). AutoML-zero: evolving machine learning algorithms from scratch. In International Conference on Machine Learning (pp. 8007-8019). PMLR.
# The docopt str is added explicitly to ensure compatibility with
# sphinx-gallery.
docopt_str = """
Usage:
example_minimal.py [--max-generations=<N>]
Options:
-h --help
--max-generations=<N> Maximum number of generations [default: 300]
"""
import matplotlib.pyplot as plt
import numpy as np
import scipy.constants
from docopt import docopt
import cgp
args = docopt(docopt_str)
We first define a target function.
def f_target(x):
return x ** 2 + 1.0
Then we define two objective functions for the evolution. They use the mean-squared error between the output of the expression represented by a given individual and the target function evaluated on a set of random points. The first objective uses only few samples (100) to get a fast estimate how well an individual performs. The second objective uses may samples (99900) to determine the fitness precisely.
def objective_one(individual):
if not individual.fitness_is_None():
return individual
n_function_evaluations = 100000
np.random.seed(1234)
values = np.random.uniform(-4, 4, n_function_evaluations)
f = individual.to_func()
loss = 0
for x in values[:100]:
# the callable returned from `to_func` accepts and returns
# lists; accordingly we need to pack the argument and unpack
# the return value
y = f(x)
loss += (f_target(x) - y) ** 2
individual.fitness = -loss / n_function_evaluations
return individual
def objective_two(individual):
if not individual.fitness_is_None():
return individual
n_function_evaluations = 100000
np.random.seed(1234)
values = np.random.uniform(-4, 4, n_function_evaluations)
f = individual.to_func()
loss = 0
for x in values[100:]:
# the callable returned from `to_func` accepts and returns
# lists; accordingly we need to pack the argument and unpack
# the return value
y = f(x)
loss += (f_target(x) - y) ** 2
individual.fitness = -loss / n_function_evaluations
return individual
Next, we set up the evolutionary search. We first define the parameters for the population, the genome of individuals, and the evolutionary algorithm.
population_params = {"n_parents": 1, "seed": 8188211}
genome_params = {
"n_inputs": 1,
"n_outputs": 1,
"n_columns": 12,
"n_rows": 1,
"levels_back": 5,
"primitives": (cgp.Add, cgp.Sub, cgp.Mul, cgp.ConstantFloat),
}
We define the upper percentile of individuals which are evaluated on the (n+1)th objective by a list of numbers between 0 and 1.
ea_params = {
"n_offsprings": 4,
"mutation_rate": 0.03,
"n_processes": 1,
"hurdle_percentile": [0.5, 0.0],
}
evolve_params = {"max_generations": int(args["--max-generations"]), "termination_fitness": 0.0}
We create a population that will be evolved
pop = cgp.Population(**population_params, genome_params=genome_params)
and an instance of the (mu + lambda) evolutionary algorithm
ea = cgp.ea.MuPlusLambda(**ea_params)
We define a callback for recording of fitness over generations
history = {}
history["fitness_champion"] = []
def recording_callback(pop):
history["fitness_champion"].append(pop.champion.fitness)
and finally perform the evolution
cgp.evolve(
pop,
[objective_one, objective_two],
ea,
**evolve_params,
print_progress=True,
callback=recording_callback
)
Out:
[2/300] max fitness: -84.24595774757668[K
[3/300] max fitness: -62.89903747153193[K
[4/300] max fitness: -62.89903747153193[K
[5/300] max fitness: -62.89903747153193[K
[6/300] max fitness: -51.22588427381885[K
[7/300] max fitness: -51.22588427381885[K
[8/300] max fitness: -51.22588427381885[K
[9/300] max fitness: -51.22588427381885[K
[10/300] max fitness: -51.22588427381885[K
[11/300] max fitness: -51.22588427381885[K
[12/300] max fitness: -51.22588427381885[K
[13/300] max fitness: -51.22588427381885[K
[14/300] max fitness: -51.22588427381885[K
[15/300] max fitness: -51.22588427381885[K
[16/300] max fitness: -51.22588427381885[K
[17/300] max fitness: -51.22588427381885[K
[18/300] max fitness: -51.22588427381885[K
[19/300] max fitness: -51.22588427381885[K
[20/300] max fitness: -51.22588427381885[K
[21/300] max fitness: -51.22588427381885[K
[22/300] max fitness: -51.22588427381885[K
[23/300] max fitness: -51.22588427381885[K
[24/300] max fitness: -51.22588427381885[K
[25/300] max fitness: -51.22588427381885[K
[26/300] max fitness: -51.22588427381885[K
[27/300] max fitness: -51.22588427381885[K
[28/300] max fitness: -51.22588427381885[K
[29/300] max fitness: -51.22588427381885[K
[30/300] max fitness: -51.22588427381885[K
[31/300] max fitness: -51.22588427381885[K
[32/300] max fitness: -51.22588427381885[K
[33/300] max fitness: -51.22588427381885[K
[34/300] max fitness: -51.22588427381885[K
[35/300] max fitness: -51.22588427381885[K
[36/300] max fitness: -51.22588427381885[K
[37/300] max fitness: -51.22588427381885[K
[38/300] max fitness: -51.22588427381885[K
[39/300] max fitness: -51.22588427381885[K
[40/300] max fitness: -51.22588427381885[K
[41/300] max fitness: -51.22588427381885[K
[42/300] max fitness: -51.22588427381885[K
[43/300] max fitness: -51.22588427381885[K
[44/300] max fitness: -51.22588427381885[K
[45/300] max fitness: -51.22588427381885[K
[46/300] max fitness: -51.22588427381885[K
[47/300] max fitness: -51.22588427381885[K
[48/300] max fitness: -51.22588427381885[K
[49/300] max fitness: -51.22588427381885[K
[50/300] max fitness: -51.22588427381885[K
[51/300] max fitness: -51.22588427381885[K
[52/300] max fitness: -51.22588427381885[K
[53/300] max fitness: -51.22588427381885[K
[54/300] max fitness: -51.22588427381885[K
[55/300] max fitness: -51.22588427381885[K
[56/300] max fitness: -51.22588427381885[K
[57/300] max fitness: -51.22588427381885[K
[58/300] max fitness: -51.22588427381885[K
[59/300] max fitness: -51.22588427381885[K
[60/300] max fitness: -51.22588427381885[K
[61/300] max fitness: -51.22588427381885[K
[62/300] max fitness: -51.22588427381885[K
[63/300] max fitness: -51.22588427381885[K
[64/300] max fitness: -51.22588427381885[K
[65/300] max fitness: -51.22588427381885[K
[66/300] max fitness: -51.22588427381885[K
[67/300] max fitness: -51.22588427381885[K
[68/300] max fitness: -51.22588427381885[K
[69/300] max fitness: -51.22588427381885[K
[70/300] max fitness: -51.22588427381885[K
[71/300] max fitness: -51.22588427381885[K
[72/300] max fitness: -51.22588427381885[K
[73/300] max fitness: -51.22588427381885[K
[74/300] max fitness: -51.22588427381885[K
[75/300] max fitness: -51.22588427381885[K
[76/300] max fitness: -51.22588427381885[K
[77/300] max fitness: -51.22588427381885[K
[78/300] max fitness: -51.22588427381885[K
[79/300] max fitness: -51.22588427381885[K
[80/300] max fitness: -51.22588427381885[K
[81/300] max fitness: -51.22588427381885[K
[82/300] max fitness: -51.22588427381885[K
[83/300] max fitness: -51.22588427381885[K
[84/300] max fitness: -51.22588427381885[K
[85/300] max fitness: -51.22588427381885[K
[86/300] max fitness: -51.22588427381885[K
[87/300] max fitness: -41.552731076106[K
[88/300] max fitness: -1.0[K
[89/300] max fitness: -1.0[K
[90/300] max fitness: -1.0[K
[91/300] max fitness: -1.0[K
[92/300] max fitness: -1.0[K
[93/300] max fitness: -1.0[K
[94/300] max fitness: -1.0[K
[95/300] max fitness: -1.0[K
[96/300] max fitness: -1.0[K
[97/300] max fitness: -1.0[K
[98/300] max fitness: -1.0[K
[99/300] max fitness: -1.0[K
[100/300] max fitness: -1.0[K
[101/300] max fitness: -1.0[K
[102/300] max fitness: -1.0[K
[103/300] max fitness: -1.0[K
[104/300] max fitness: -1.0[K
[105/300] max fitness: -1.0[K
[106/300] max fitness: -1.0[K
[107/300] max fitness: -1.0[K
[108/300] max fitness: -1.0[K
[109/300] max fitness: -1.0[K
[110/300] max fitness: -1.0[K
[111/300] max fitness: -1.0[K
[112/300] max fitness: -1.0[K
[113/300] max fitness: -1.0[K
[114/300] max fitness: -1.0[K
[115/300] max fitness: -1.0[K
[116/300] max fitness: -1.0[K
[117/300] max fitness: -1.0[K
[118/300] max fitness: -1.0[K
[119/300] max fitness: -1.0[K
[120/300] max fitness: -1.0[K
[121/300] max fitness: -1.0[K
[122/300] max fitness: -1.0[K
[123/300] max fitness: -1.0[K
[124/300] max fitness: -1.0[K
[125/300] max fitness: -1.0[K
[126/300] max fitness: -1.0[K
[127/300] max fitness: -1.0[K
[128/300] max fitness: -1.0[K
[129/300] max fitness: -1.0[K
[130/300] max fitness: -1.0[K
[131/300] max fitness: -1.0[K
[132/300] max fitness: -1.0[K
[133/300] max fitness: -1.0[K
[134/300] max fitness: -1.0[K
[135/300] max fitness: -1.0[K
[136/300] max fitness: -1.0[K
[137/300] max fitness: -1.0[K
[138/300] max fitness: -1.0[K
[139/300] max fitness: -1.0[K
[140/300] max fitness: -1.0[K
[141/300] max fitness: -1.0[K
[142/300] max fitness: -1.0[K
[143/300] max fitness: -1.0[K
[144/300] max fitness: -1.0[K
[145/300] max fitness: -1.0[K
[146/300] max fitness: -1.0[K
[147/300] max fitness: -1.0[K
[148/300] max fitness: -1.0[K
[149/300] max fitness: -1.0[K
[150/300] max fitness: -1.0[K
[151/300] max fitness: -1.0[K
[152/300] max fitness: -1.0[K
[153/300] max fitness: -1.0[K
[154/300] max fitness: -1.0[K
[155/300] max fitness: -1.0[K
[156/300] max fitness: -1.0[K
[157/300] max fitness: -1.0[K
[158/300] max fitness: -1.0[K
[159/300] max fitness: -1.0[K
[160/300] max fitness: -1.0[K
[161/300] max fitness: -1.0[K
[162/300] max fitness: -1.0[K
[163/300] max fitness: -1.0[K
[164/300] max fitness: -1.0[K
[165/300] max fitness: -1.0[K
[166/300] max fitness: -1.0[K
[167/300] max fitness: -1.0[K
[168/300] max fitness: -1.0[K
[169/300] max fitness: -1.0[K
[170/300] max fitness: -1.0[K
[171/300] max fitness: -1.0[K
[172/300] max fitness: -1.0[K
[173/300] max fitness: -1.0[K
[174/300] max fitness: -1.0[K
[175/300] max fitness: -1.0[K
[176/300] max fitness: -1.0[K
[177/300] max fitness: -1.0[K
[178/300] max fitness: -1.0[K
[179/300] max fitness: -1.0[K
[180/300] max fitness: -1.0[K
[181/300] max fitness: -1.0[K
[182/300] max fitness: -1.0[K
[183/300] max fitness: -1.0[K
[184/300] max fitness: -1.0[K
[185/300] max fitness: -1.0[K
[186/300] max fitness: -1.0[K
[187/300] max fitness: -1.0[K
[188/300] max fitness: -1.0[K
[189/300] max fitness: -1.0[K
[190/300] max fitness: -1.0[K
[191/300] max fitness: -1.0[K
[192/300] max fitness: -1.0[K
[193/300] max fitness: -1.0[K
[194/300] max fitness: -1.0[K
[195/300] max fitness: -1.0[K
[196/300] max fitness: -1.0[K
[197/300] max fitness: -1.0[K
[198/300] max fitness: -1.0[K
[199/300] max fitness: -1.0[K
[200/300] max fitness: -1.0[K
[201/300] max fitness: -1.0[K
[202/300] max fitness: -1.0[K
[203/300] max fitness: -1.0[K
[204/300] max fitness: -1.0[K
[205/300] max fitness: -1.0[K
[206/300] max fitness: -1.0[K
[207/300] max fitness: -1.0[K
[208/300] max fitness: -1.0[K
[209/300] max fitness: -1.0[K
[210/300] max fitness: -1.0[K
[211/300] max fitness: -1.0[K
[212/300] max fitness: -1.0[K
[213/300] max fitness: -1.0[K
[214/300] max fitness: -1.0[K
[215/300] max fitness: -1.0[K
[216/300] max fitness: -1.0[K
[217/300] max fitness: -1.0[K
[218/300] max fitness: -1.0[K
[219/300] max fitness: -1.0[K
[220/300] max fitness: -1.0[K
[221/300] max fitness: -1.0[K
[222/300] max fitness: -1.0[K
[223/300] max fitness: -1.0[K
[224/300] max fitness: -1.0[K
[225/300] max fitness: -1.0[K
[226/300] max fitness: -1.0[K
[227/300] max fitness: -1.0[K
[228/300] max fitness: -1.0[K
[229/300] max fitness: -1.0[K
[230/300] max fitness: -1.0[K
[231/300] max fitness: -1.0[K
[232/300] max fitness: -1.0[K
[233/300] max fitness: -1.0[K
[234/300] max fitness: -1.1749097107135445e-33[K
After finishing the evolution, we plot the result and log the final evolved expression.
width = 9.0
fig, axes = plt.subplots(1, 2, figsize=(width, width / scipy.constants.golden))
ax_fitness, ax_function = axes[0], axes[1]
ax_fitness.set_xlabel("Generation")
ax_fitness.set_ylabel("Fitness")
ax_fitness.plot(history["fitness_champion"], label="Champion")
ax_fitness.set_yscale("symlog")
ax_fitness.set_ylim(-1.0e2, 0.1)
ax_fitness.axhline(0.0, color="0.7")
f = pop.champion.to_func()
x = np.linspace(-5.0, 5.0, 20)
y = [f(x_i) for x_i in x]
y_target = [f_target(x_i) for x_i in x]
ax_function.plot(x, y_target, lw=2, alpha=0.5, label="Target")
ax_function.plot(x, y, "x", label="Champion")
ax_function.legend()
ax_function.set_ylabel(r"$f(x)$")
ax_function.set_xlabel(r"$x$")
fig.savefig("example_minimal.pdf", dpi=300)
Total running time of the script: ( 0 minutes 10.718 seconds)