First Optimization

Quadratic Function Example

Usually, Optuna is used to optimize hyper-parameters, but as an example, let us directly optimize a quadratic function in an IPython shell.

import optuna

The objective function is what will be optimized.

def objective(trial):
    x = trial.suggest_uniform('x', -10, 10)
    return (x - 2) ** 2

This function returns the value of \((x - 2)^2\). Our goal is to find the value of x that minimizes the output of the objective function. This is the “optimization.” During the optimization, Optuna repeatedly calls and evaluates the objective function with different values of x.

A Trial object corresponds to a single execution of the objective function and is internally instantiated upon each invocation of the function.

The suggest APIs (for example, suggest_uniform()) are called inside the objective function to obtain parameters for a trial. suggest_uniform() selects parameters uniformly within the range provided. In our example, from -10 to 10.

To start the optimization, we create a study object and pass the objective function to method optimize() as follows.

study = optuna.create_study()
study.optimize(objective, n_trials=100)

Out:

[I 2020-04-08 10:42:09,028] Finished trial#0 with value: 25.77382032395108 with parameters: {'x': 7.076792326257898}. Best is trial#0 with value: 25.77382032395108.
[I 2020-04-08 10:42:09,064] Finished trial#1 with value: 1.5189812248635903 with parameters: {'x': 0.7675304365366298}. Best is trial#1 with value: 1.5189812248635903.
[I 2020-04-08 10:42:09,106] Finished trial#2 with value: 34.4074691838153 with parameters: {'x': -3.865788027521562}. Best is trial#1 with value: 1.5189812248635903.
[I 2020-04-08 10:42:09,145] Finished trial#3 with value: 3.3601305753722657 with parameters: {'x': 3.8330658949891205}. Best is trial#1 with value: 1.5189812248635903.
[I 2020-04-08 10:42:09,185] Finished trial#4 with value: 61.16797535698886 with parameters: {'x': -5.820995803412048}. Best is trial#1 with value: 1.5189812248635903.
[I 2020-04-08 10:42:09,228] Finished trial#5 with value: 90.08665552769618 with parameters: {'x': -7.491399028999686}. Best is trial#1 with value: 1.5189812248635903.
[I 2020-04-08 10:42:09,274] Finished trial#6 with value: 25.254236332163032 with parameters: {'x': 7.025359323686519}. Best is trial#1 with value: 1.5189812248635903.
...
[I 2020-04-08 10:42:14,237] Finished trial#99 with value: 0.5227007740782738 with parameters: {'x': 2.7229804797352926}. Best is trial#67 with value: 2.916284393762304e-06.

You can get the best parameter as follows.

study.best_params

Out:

{'x': 2.001707713205946}

We can see that Optuna found the best x value 2.001707713205946, which is close to the optimal value of 2.

Note

When used to search for hyper-parameters in machine learning, usually the objective function would return the loss or accuracy of the model.

Study Object

Let us clarify the terminology in Optuna as follows:

  • Trial: A single call of the objective function

  • Study: An optimization session, which is a set of trials

  • Parameter: A variable whose value is to be optimized, such as x in the above example

In Optuna, we use the study object to manage optimization. Method create_study() returns a study object. A study object has useful properties for analyzing the optimization outcome.

To get the best parameter:

study.best_params

Out:

{'x': 2.001707713205946}

To get the best value:

study.best_value

Out:

2.916284393762304e-06

To get the best trial:

study.best_trial

Out:

FrozenTrial(number=67, value=2.916284393762304e-06, datetime_start=datetime.datetime(2020, 4, 8, 10, 42, 12, 595884), datetime_complete=datetime.datetime(2020, 4, 8, 10, 42, 12, 639969), params={'x': 2.001707713205946}, distributions={'x': UniformDistribution(high=10, low=-10)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=67, state=TrialState.COMPLETE)

To get all trials:

study.trials

Out:

[FrozenTrial(number=0, value=25.77382032395108, datetime_start=datetime.datetime(2020, 4, 8, 10, 42, 8, 987277), datetime_complete=datetime.datetime(2020, 4, 8, 10, 42, 9, 27959), params={'x': 7.076792326257898}, distributions={'x': UniformDistribution(high=10, low=-10)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=0, state=TrialState.COMPLETE),
 ...
 user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=99, state=TrialState.COMPLETE)]

To get the number of trials:

len(study.trials)

Out:

100

By executing optimize() again, we can continue the optimization.

study.optimize(objective, n_trials=100)

To get the updated number of trials:

len(study.trials)

Out:

200