OPTUNA

Optuna: 一个超参数优化框架

Optuna 是一个特别为机器学习设计的自动超参数优化软件框架.它具有命令式的, define-by-run 风格的 API.由于这种 API 的存在, 用 Optuna 编写的代码模块化程度很高, Optuna 的用户因此也可以动态地构造超参数的搜索空间.

主要特点

Optuna 有如下现代化的功能:

基本概念

我们以如下方式使用 studytrial 这两个术语:

  • Study: 基于目标函数的优化过程

  • Trial: 目标函数的单次执行过程

请参考下面的示例代码.一个 study 的目的是通过多次 trial (例如 n_trials=100 ) 来找出最佳的超参数值集(比如选择 classifier 还是 svm_c).而 Optuna 旨在加速和自动化此类 study 优化过程.

Open in Colab

import ...

# Define an objective function to be minimized.
def objective(trial):

    # Invoke suggest methods of a Trial object to generate hyperparameters.
    regressor_name = trial.suggest_categorical('classifier', ['SVR', 'RandomForest'])
    if regressor_name == 'SVR':
        svr_c = trial.suggest_float('svr_c', 1e-10, 1e10, log=True)
        regressor_obj = sklearn.svm.SVR(C=svr_c)
    else:
        rf_max_depth = trial.suggest_int('rf_max_depth', 2, 32)
        regressor_obj = sklearn.ensemble.RandomForestRegressor(max_depth=rf_max_depth)

    X, y = sklearn.datasets.load_boston(return_X_y=True)
    X_train, X_val, y_train, y_val = sklearn.model_selection.train_test_split(X, y, random_state=0)

    regressor_obj.fit(X_train, y_train)
    y_pred = regressor_obj.predict(X_val)

    error = sklearn.metrics.mean_squared_error(y_val, y_pred)

    return error  # An objective value linked with the Trial object.

study = optuna.create_study()  # Create a new study.
study.optimize(objective, n_trials=100)  # Invoke optimization of the objective function.

Communication

贡献

欢迎大家对Optuna的一切贡献!但是在发送 pull request 时请遵从 contribution guide 的规范.

License

MIT License (see LICENSE).

Reference

Takuya Akiba, Shotaro Sano, Toshihiko Yanase, Takeru Ohta, and Masanori Koyama. 2019. Optuna: A Next-generation Hyperparameter Optimization Framework. In KDD (arXiv).

安装

Optuna 支持 3.6 或者更新版本的 Python.

我们推荐使用 pip 来安装 Optuna:

$ pip install optuna

你也可以在 Git 仓库的 master 分支下用开发模式安装Optuna:

$ pip install git+https://github.com/optuna/optuna.git

你也可以通过 conda 来安装 Optuna:

$ conda install -c conda-forge optuna

教程

如果你不熟悉Optuna或者需要一个一般的介绍,我们强烈推荐下面这个视频。




关键特性

展现 Optuna 的关键特性

轻量级、多功能和跨平台架构

Optuna 完全由 Python 写成,并且只有少量依赖。这意味着一旦你开始对 Optuna 感兴趣,我们可以快速上手真实的例子。

二次函数的例子

通常,Optuna是用于优化超参数的,但是作为例子,我们这里来优化一个简单的而此函数:\((x - 2)^2\).

首先,导入 optuna.

import optuna

在 Optuna 中,待优化函数一般被命名为 objective.

def objective(trial):
    x = trial.suggest_float("x", -10, 10)
    return (x - 2) ** 2

该函数的返回值是 \((x - 2)^2\). 我们的目标是找到一个 x,使 objective 函数的输出最小。这被称为 “optimization” (优化)。 在优化过程中,Optuna 反复调用目标函数,在不同的 x 下对其进行求值。

一个 Trial 对应着目标函数的单次执行。在每次调用该函数的时候,它都被内部实例化一次。

suggest API (例如 suggest_uniform()) 在目标函数内部被调用。它被用于获取单个 trial 的参数。在上面的例子中,suggest_uniform() 在给定的范围(-1010)内均匀地选择参数。

为了开始优化过程,我们将创建一个 study 对象,并将目标函数传递给它的一个方法 optimize()

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

You can get the best parameter as follows.

best_params = study.best_params
found_x = best_params["x"]
print("Found x: {}, (x - 2)^2: {}".format(found_x, (found_x - 2) ** 2))

Out:

Found x: 1.9918889682901693, (x - 2)^2: 6.578883539787978e-05

最佳参数可以通过如下方式获得:

备注

当 Optuna 被用于机器学习时,目标函数通常返回模型的损失或者准确度。

Study 对象

下面是几个常用术语:

  • Trial: 目标函数的单次调用

  • Study: 一次优化过程,包含一系列的 trials.

  • Parameter: 待优化的参数,比如上面例子中的 x.

在 Optuna 中,我们用 study 对象来管理优化过程。 create_study() 方法会返回一个 study 对象。该对象包含若干有用的属性,可以用于分析优化结果。

获得参数名和参数值的字典:

study.best_params

Out:

{'x': 1.9918889682901693}

获得最佳目标函数值:

study.best_value

Out:

6.578883539787978e-05

获得最佳 trial:

study.best_trial

Out:

FrozenTrial(number=51, values=[6.578883539787978e-05], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 137432), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 140603), params={'x': 1.9918889682901693}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=51, state=TrialState.COMPLETE, value=None)

获得所有 trials:

study.trials

Out:

[FrozenTrial(number=0, values=[45.598730566901985], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 34, 982358), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 34, 982755), params={'x': -4.752683212390611}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=0, state=TrialState.COMPLETE, value=None), FrozenTrial(number=1, values=[119.81387381698536], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 34, 983204), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 34, 983517), params={'x': -8.945952394240775}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=1, state=TrialState.COMPLETE, value=None), FrozenTrial(number=2, values=[111.46058967810127], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 34, 983932), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 34, 984232), params={'x': -8.557489743215537}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=2, state=TrialState.COMPLETE, value=None), FrozenTrial(number=3, values=[35.57587195942296], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 34, 984630), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 34, 984929), params={'x': -3.964551278966672}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=3, state=TrialState.COMPLETE, value=None), FrozenTrial(number=4, values=[46.670857770478044], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 34, 985275), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 34, 985560), params={'x': -4.831607261141264}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=4, state=TrialState.COMPLETE, value=None), FrozenTrial(number=5, values=[116.5910798805615], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 34, 985975), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 34, 986280), params={'x': -8.797734942133072}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=5, state=TrialState.COMPLETE, value=None), FrozenTrial(number=6, values=[0.002680014768041531], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 34, 986676), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 34, 986987), params={'x': 1.9482311409432125}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=6, state=TrialState.COMPLETE, value=None), FrozenTrial(number=7, values=[16.26188576510393], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 34, 987389), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 34, 987700), params={'x': 6.032602852389996}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=7, state=TrialState.COMPLETE, value=None), FrozenTrial(number=8, values=[0.9418909496063309], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 34, 988098), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 34, 988415), params={'x': 1.029489335655537}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=8, state=TrialState.COMPLETE, value=None), FrozenTrial(number=9, values=[0.12147959765973194], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 34, 988811), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 34, 989130), params={'x': 2.3485392340321702}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=9, state=TrialState.COMPLETE, value=None), FrozenTrial(number=10, values=[61.08771684444376], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 34, 989547), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 34, 993102), params={'x': 9.815863154152826}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=10, state=TrialState.COMPLETE, value=None), FrozenTrial(number=11, values=[1.2436603312778658], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 34, 993578), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 34, 996629), params={'x': 3.1151951987333275}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=11, state=TrialState.COMPLETE, value=None), FrozenTrial(number=12, values=[12.68371355828232], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 34, 997088), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 91), params={'x': 5.561420160312782}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=12, state=TrialState.COMPLETE, value=None), FrozenTrial(number=13, values=[8.143803878359984], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 596), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 3816), params={'x': -0.853735075013093}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=13, state=TrialState.COMPLETE, value=None), FrozenTrial(number=14, values=[4.41513157112696], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 4275), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 7459), params={'x': 4.101221447426939}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=14, state=TrialState.COMPLETE, value=None), FrozenTrial(number=15, values=[50.302494837603305], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 7914), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 10932), params={'x': 9.092425173211439}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=15, state=TrialState.COMPLETE, value=None), FrozenTrial(number=16, values=[7.789307273481423], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 11390), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 14398), params={'x': -0.7909330471155023}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=16, state=TrialState.COMPLETE, value=None), FrozenTrial(number=17, values=[0.16065150690336386], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 14858), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 17861), params={'x': 1.5991864437130852}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=17, state=TrialState.COMPLETE, value=None), FrozenTrial(number=18, values=[24.460101814001746], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 18339), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 21313), params={'x': 6.9457155007139}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=18, state=TrialState.COMPLETE, value=None), FrozenTrial(number=19, values=[17.28917473460124], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 21786), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 24796), params={'x': -2.1580253407839205}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=19, state=TrialState.COMPLETE, value=None), FrozenTrial(number=20, values=[0.42630874522106565], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 25254), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 28259), params={'x': 2.6529232307255315}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=20, state=TrialState.COMPLETE, value=None), FrozenTrial(number=21, values=[2.2654123767813035], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 28717), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 31753), params={'x': 0.494871308897042}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=21, state=TrialState.COMPLETE, value=None), FrozenTrial(number=22, values=[0.028489729853048023], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 32212), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 35259), params={'x': 1.8312109901295466}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=22, state=TrialState.COMPLETE, value=None), FrozenTrial(number=23, values=[5.553487889919093], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 35719), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 38768), params={'x': 4.356583945018529}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=23, state=TrialState.COMPLETE, value=None), FrozenTrial(number=24, values=[36.33353081420967], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 39228), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 42508), params={'x': 8.027730154395572}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=24, state=TrialState.COMPLETE, value=None), FrozenTrial(number=25, values=[21.133677498261164], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 42972), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 46011), params={'x': -2.597137968155966}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=25, state=TrialState.COMPLETE, value=None), FrozenTrial(number=26, values=[0.002053670170683944], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 46474), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 49526), params={'x': 1.954682562178738}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=26, state=TrialState.COMPLETE, value=None), FrozenTrial(number=27, values=[5.019689327006978], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 50019), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 53036), params={'x': 4.240466319096758}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=27, state=TrialState.COMPLETE, value=None), FrozenTrial(number=28, values=[6.74588475839922], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 53511), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 56606), params={'x': -0.5972841119906811}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=28, state=TrialState.COMPLETE, value=None), FrozenTrial(number=29, values=[18.662711672171046], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 57119), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 60337), params={'x': -2.320036073017336}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=29, state=TrialState.COMPLETE, value=None), FrozenTrial(number=30, values=[78.07265834734876], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 60835), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 63950), params={'x': -6.835873377734018}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=30, state=TrialState.COMPLETE, value=None), FrozenTrial(number=31, values=[0.018922953862383386], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 64415), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 67573), params={'x': 2.1375607279072897}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=31, state=TrialState.COMPLETE, value=None), FrozenTrial(number=32, values=[0.25271475323906456], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 68039), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 71176), params={'x': 1.4972925769007737}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=32, state=TrialState.COMPLETE, value=None), FrozenTrial(number=33, values=[2.90454345753206], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 71645), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 74790), params={'x': 0.29572788043339165}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=33, state=TrialState.COMPLETE, value=None), FrozenTrial(number=34, values=[1.4316740805442856], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 75256), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 78388), params={'x': 3.196525837808898}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=34, state=TrialState.COMPLETE, value=None), FrozenTrial(number=35, values=[9.575720708802683], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 78862), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 82154), params={'x': 5.094466142778538}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=35, state=TrialState.COMPLETE, value=None), FrozenTrial(number=36, values=[0.026192367072935412], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 82626), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 85833), params={'x': 2.161840560654415}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=36, state=TrialState.COMPLETE, value=None), FrozenTrial(number=37, values=[2.4026175282558926], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 86328), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 89452), params={'x': 3.550037911876962}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=37, state=TrialState.COMPLETE, value=None), FrozenTrial(number=38, values=[13.131194732326328], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 89949), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 93077), params={'x': -1.6236990399764615}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=38, state=TrialState.COMPLETE, value=None), FrozenTrial(number=39, values=[2.3930013098831866], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 93560), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 96686), params={'x': 0.45306712819101724}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=39, state=TrialState.COMPLETE, value=None), FrozenTrial(number=40, values=[36.202864472441256], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 97154), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 100313), params={'x': -4.016881623602151}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=40, state=TrialState.COMPLETE, value=None), FrozenTrial(number=41, values=[0.0011346872942620569], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 100785), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 103953), params={'x': 1.9663148802249115}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=41, state=TrialState.COMPLETE, value=None), FrozenTrial(number=42, values=[0.03845837782035236], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 104443), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 107637), params={'x': 2.196108076887089}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=42, state=TrialState.COMPLETE, value=None), FrozenTrial(number=43, values=[0.6366861793940127], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 108106), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 111294), params={'x': 1.2020738258497765}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=43, state=TrialState.COMPLETE, value=None), FrozenTrial(number=44, values=[0.3263843119440706], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 111767), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 114965), params={'x': 2.5713005443232753}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=44, state=TrialState.COMPLETE, value=None), FrozenTrial(number=45, values=[21.76545647561303], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 115440), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 118652), params={'x': 6.665346340370994}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=45, state=TrialState.COMPLETE, value=None), FrozenTrial(number=46, values=[8.533233409436638], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 119126), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 122313), params={'x': 4.921169870006987}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=46, state=TrialState.COMPLETE, value=None), FrozenTrial(number=47, values=[5.14897391162914], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 122786), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 125967), params={'x': -0.2691350580406491}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=47, state=TrialState.COMPLETE, value=None), FrozenTrial(number=48, values=[1.653464156778069], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 126443), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 129619), params={'x': 3.2858709720567103}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=48, state=TrialState.COMPLETE, value=None), FrozenTrial(number=49, values=[0.774963234553805], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 130116), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 133267), params={'x': 1.1196800385349626}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=49, state=TrialState.COMPLETE, value=None), FrozenTrial(number=50, values=[12.423387236916076], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 133754), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 136944), params={'x': -1.5246825725043776}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=50, state=TrialState.COMPLETE, value=None), FrozenTrial(number=51, values=[6.578883539787978e-05], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 137432), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 140603), params={'x': 1.9918889682901693}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=51, state=TrialState.COMPLETE, value=None), FrozenTrial(number=52, values=[0.37078488737261833], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 141087), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 144333), params={'x': 2.608921084683901}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=52, state=TrialState.COMPLETE, value=None), FrozenTrial(number=53, values=[2.7370864030548474], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 144817), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 148017), params={'x': 3.654414217496588}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=53, state=TrialState.COMPLETE, value=None), FrozenTrial(number=54, values=[1.8823808574989804], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 148503), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 151745), params={'x': 0.6280011452267971}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=54, state=TrialState.COMPLETE, value=None), FrozenTrial(number=55, values=[0.0024882580008509583], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 152226), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 155476), params={'x': 1.9501175581907726}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=55, state=TrialState.COMPLETE, value=None), FrozenTrial(number=56, values=[14.270754611645605], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 155961), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 159209), params={'x': 5.777665232871437}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=56, state=TrialState.COMPLETE, value=None), FrozenTrial(number=57, values=[4.4484765527905346], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 159704), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 162983), params={'x': -0.10914118844389709}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=57, state=TrialState.COMPLETE, value=None), FrozenTrial(number=58, values=[0.15611335614357152], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 163478), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 166724), params={'x': 1.6048881726098654}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=58, state=TrialState.COMPLETE, value=None), FrozenTrial(number=59, values=[5.660936130375584], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 167210), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 170497), params={'x': 4.3792721850128}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=59, state=TrialState.COMPLETE, value=None), FrozenTrial(number=60, values=[9.389214737016154], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 170989), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 174428), params={'x': -1.0641825560850895}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=60, state=TrialState.COMPLETE, value=None), FrozenTrial(number=61, values=[0.003492796125829687], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 174917), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 178188), params={'x': 2.059099882621116}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=61, state=TrialState.COMPLETE, value=None), FrozenTrial(number=62, values=[0.0012471767447324205], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 178676), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 181936), params={'x': 1.964684610369806}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=62, state=TrialState.COMPLETE, value=None), FrozenTrial(number=63, values=[1.0307101000778789], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 182426), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 185671), params={'x': 0.9847610625680875}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=63, state=TrialState.COMPLETE, value=None), FrozenTrial(number=64, values=[0.6149542517901266], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 186179), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 189453), params={'x': 2.784190188532174}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=64, state=TrialState.COMPLETE, value=None), FrozenTrial(number=65, values=[3.1371839247236366], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 189961), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 193186), params={'x': 3.7712097348207063}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=65, state=TrialState.COMPLETE, value=None), FrozenTrial(number=66, values=[0.0016153179389367636], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 193692), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 196941), params={'x': 1.9598089818624016}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=66, state=TrialState.COMPLETE, value=None), FrozenTrial(number=67, values=[1.0072461981116687], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 197446), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 200701), params={'x': 3.00361655930523}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=67, state=TrialState.COMPLETE, value=None), FrozenTrial(number=68, values=[8.678111597333611], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 201192), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 204482), params={'x': 4.9458634722834}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=68, state=TrialState.COMPLETE, value=None), FrozenTrial(number=69, values=[1.441692566557962], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 204972), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 208262), params={'x': 0.799294971044944}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=69, state=TrialState.COMPLETE, value=None), FrozenTrial(number=70, values=[0.22823123525119846], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 208753), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 212061), params={'x': 1.5222644714371785}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=70, state=TrialState.COMPLETE, value=None), FrozenTrial(number=71, values=[0.0007039887398216746], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 212552), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 215858), params={'x': 2.0265327861300255}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=71, state=TrialState.COMPLETE, value=None), FrozenTrial(number=72, values=[0.05844718777874724], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 216348), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 219662), params={'x': 1.7582414680331897}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=72, state=TrialState.COMPLETE, value=None), FrozenTrial(number=73, values=[5.457920955711181], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 220156), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 223463), params={'x': -0.3362193723430982}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=73, state=TrialState.COMPLETE, value=None), FrozenTrial(number=74, values=[2.861402012358332], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 223956), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 227779), params={'x': 0.30843208461547955}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=74, state=TrialState.COMPLETE, value=None), FrozenTrial(number=75, values=[0.9381394370991073], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 228274), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 231595), params={'x': 2.9685759841639205}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=75, state=TrialState.COMPLETE, value=None), FrozenTrial(number=76, values=[4.233303245084991], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 232091), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 235441), params={'x': 4.05749926976536}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=76, state=TrialState.COMPLETE, value=None), FrozenTrial(number=77, values=[0.07593521259492125], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 235934), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 239271), params={'x': 2.275563445679795}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=77, state=TrialState.COMPLETE, value=None), FrozenTrial(number=78, values=[0.6550732477970714], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 239767), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 243108), params={'x': 1.1906340458129763}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=78, state=TrialState.COMPLETE, value=None), FrozenTrial(number=79, values=[3.4974294862610504], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 243603), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 246942), params={'x': 0.12985843149213694}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=79, state=TrialState.COMPLETE, value=None), FrozenTrial(number=80, values=[1.693369499713808], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 247440), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 250832), params={'x': 3.301295316103846}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=80, state=TrialState.COMPLETE, value=None), FrozenTrial(number=81, values=[0.02517321608536449], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 251332), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 254667), params={'x': 1.8413393051655058}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=81, state=TrialState.COMPLETE, value=None), FrozenTrial(number=82, values=[0.05629492177101777], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 255168), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 258517), params={'x': 2.2372655090210496}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=82, state=TrialState.COMPLETE, value=None), FrozenTrial(number=83, values=[0.018596035504924155], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 259016), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 262363), params={'x': 1.8636327183488497}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=83, state=TrialState.COMPLETE, value=None), FrozenTrial(number=84, values=[140.50094286625492], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 262863), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 266223), params={'x': -9.853309363475455}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=84, state=TrialState.COMPLETE, value=None), FrozenTrial(number=85, values=[0.44831182340893005], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 266719), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 270152), params={'x': 2.6695609183703377}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=85, state=TrialState.COMPLETE, value=None), FrozenTrial(number=86, values=[1.3494200850396654], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 270656), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 274021), params={'x': 0.8383545786085733}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=86, state=TrialState.COMPLETE, value=None), FrozenTrial(number=87, values=[6.278033096153004], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 274531), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 277864), params={'x': 4.505600346454519}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=87, state=TrialState.COMPLETE, value=None), FrozenTrial(number=88, values=[0.5041874760940434], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 278395), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 281722), params={'x': 1.2899383997891145}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=88, state=TrialState.COMPLETE, value=None), FrozenTrial(number=89, values=[3.509453291730217], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 282245), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 285604), params={'x': 3.8733534881944243}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=89, state=TrialState.COMPLETE, value=None), FrozenTrial(number=90, values=[0.11414550534938579], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 286132), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 289478), params={'x': 2.3378542664365596}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=90, state=TrialState.COMPLETE, value=None), FrozenTrial(number=91, values=[0.07683673953870722], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 290006), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 293345), params={'x': 1.722805592519064}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=91, state=TrialState.COMPLETE, value=None), FrozenTrial(number=92, values=[0.003223689053182925], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 293851), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 297254), params={'x': 1.9432224599583345}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=92, state=TrialState.COMPLETE, value=None), FrozenTrial(number=93, values=[1.9465317407544378], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 297774), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 301141), params={'x': 3.3951816156882364}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=93, state=TrialState.COMPLETE, value=None), FrozenTrial(number=94, values=[2.148169010607775], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 301665), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 305023), params={'x': 0.5343366653259778}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=94, state=TrialState.COMPLETE, value=None), FrozenTrial(number=95, values=[0.32294965225967065], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 305544), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 308918), params={'x': 2.568286593418911}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=95, state=TrialState.COMPLETE, value=None), FrozenTrial(number=96, values=[7.586587619520327], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 309438), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 312809), params={'x': -0.7543760853449784}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=96, state=TrialState.COMPLETE, value=None), FrozenTrial(number=97, values=[0.8921391843286989], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 313326), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 316717), params={'x': 2.944531198176481}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=97, state=TrialState.COMPLETE, value=None), FrozenTrial(number=98, values=[0.7684811256944052], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 317224), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 320619), params={'x': 1.1233694474327254}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=98, state=TrialState.COMPLETE, value=None), FrozenTrial(number=99, values=[0.0020308765557163935], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 35, 321182), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 35, 324773), params={'x': 2.0450652477605127}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=99, state=TrialState.COMPLETE, value=None)]

获得 trial 的数目:

len(study.trials)

Out:

100

再次执行 optimize(),我们可以继续优化过程。

study.optimize(objective, n_trials=100)

获得更新后的的 trial 数量:

len(study.trials)

Out:

200

由于此目标函数非常简单,所以后面的 100 个trial 并没有提升结果。然而,我们可以再一次检查该结果:

best_params = study.best_params
found_x = best_params["x"]
print("Found x: {}, (x - 2)^2: {}".format(found_x, (found_x - 2) ** 2))

Out:

Found x: 1.9980396014917614, (x - 2)^2: 3.843162311104098e-06

Total running time of the script: ( 0 minutes 0.786 seconds)

Gallery generated by Sphinx-Gallery

Python 式的搜索空间

对于超参数采样,Optuna 提供了以下特性:

通过可选的 steplog 参数,我们可以对整形或者浮点型参数进行离散化或者取对数操作。

import optuna


def objective(trial):
    # Categorical parameter
    optimizer = trial.suggest_categorical("optimizer", ["MomentumSGD", "Adam"])

    # Integer parameter
    num_layers = trial.suggest_int("num_layers", 1, 3)

    # Integer parameter (log)
    num_channels = trial.suggest_int("num_channels", 32, 512, log=True)

    # Integer parameter (discretized)
    num_units = trial.suggest_int("num_units", 10, 100, step=5)

    # Floating point parameter
    dropout_rate = trial.suggest_float("dropout_rate", 0.0, 1.0)

    # Floating point parameter (log)
    learning_rate = trial.suggest_float("learning_rate", 1e-5, 1e-2, log=True)

    # Floating point parameter (discretized)
    drop_path_rate = trial.suggest_float("drop_path_rate", 0.0, 1.0, step=0.1)
定义参数空间

在 Optuna 中,我们使用和 Python 语法类似的方式来定义搜索空间,其中包含条件和循环语句。

类似地,你也可以根据参数值采用分支或者循环。

更多用法见 examples.

  • 分支:

import sklearn.ensemble
import sklearn.svm


def objective(trial):
    classifier_name = trial.suggest_categorical("classifier", ["SVC", "RandomForest"])
    if classifier_name == "SVC":
        svc_c = trial.suggest_float("svc_c", 1e-10, 1e10, log=True)
        classifier_obj = sklearn.svm.SVC(C=svc_c)
    else:
        rf_max_depth = trial.suggest_int("rf_max_depth", 2, 32, log=True)
        classifier_obj = sklearn.ensemble.RandomForestClassifier(max_depth=rf_max_depth)
  • 循环:

import torch
import torch.nn as nn


def create_model(trial, in_size):
    n_layers = trial.suggest_int("n_layers", 1, 3)

    layers = []
    for i in range(n_layers):
        n_units = trial.suggest_int("n_units_l{}".format(i), 4, 128, log=True)
        layers.append(nn.Linear(in_size, n_units))
        layers.append(nn.ReLU())
        in_size = n_units
    layers.append(nn.Linear(in_size, 10))

    return nn.Sequential(*layers)
关于参数个数的注意事项

随着参数个数的增长,优化的难度约呈指数增长。也就是说,当你增加参数的个数的时候,优化所需要的 trial 个数会呈指数增长。因此我们不推荐增加不必要的参数。

Total running time of the script: ( 0 minutes 0.001 seconds)

Gallery generated by Sphinx-Gallery

高效的优化算法

通过采用最先进的超参数采样算法和对无望 trial 的剪枝, Optuna使得高效的超参数优化成为可能。

采样算法

利用 suggested 参数值和评估的目标值的记录,采样器基本上不断缩小搜索空间,直到找到一个最佳的搜索空间,其产生的参数会带来 更好的目标函数值。关于采样器如何 suggest 参数的更详细的解释见 optuna.samplers.BaseSampler.

Optuna 提供了下列采样算法:

默认的采样器是 optuna.samplers.TPESampler.

切换采样器
import optuna

默认情况下, Optuna 这样使用 TPESampler.

study = optuna.create_study()
print(f"Sampler is {study.sampler.__class__.__name__}")

Out:

Sampler is TPESampler

如果你希望使用其他采样器,比如 RandomSamplerCmaEsSampler,

study = optuna.create_study(sampler=optuna.samplers.RandomSampler())
print(f"Sampler is {study.sampler.__class__.__name__}")

study = optuna.create_study(sampler=optuna.samplers.CmaEsSampler())
print(f"Sampler is {study.sampler.__class__.__name__}")

Out:

Sampler is RandomSampler
Sampler is CmaEsSampler
剪枝算法

Pruners 自动在训练的早期(也就是自动化的 early-stopping)终止无望的 trial.

Optuna 提供以下剪枝算法:

在大多数例子中我们采用的 optuna.pruners.MedianPruner, 尽管其性能基本上会被 optuna.pruners.SuccessiveHalvingPruneroptuna.pruners.HyperbandPruner 超过,就像在 这个基准测试结果 中那样.

激活 Pruner

要打开剪枝特性的话,你需要在迭代式训练的每一步后调用 report()should_prune(). report() 定期监控目标函数的中间值. should_prune() 确定终结那些没有达到预先设定条件的 trial.

我们推荐在主流机器学习框架中使用集成模块,全部的模块列表在 optuna.integration 里,用例见 optuna/examples.

import logging
import sys

import sklearn.datasets
import sklearn.linear_model
import sklearn.model_selection


def objective(trial):
    iris = sklearn.datasets.load_iris()
    classes = list(set(iris.target))
    train_x, valid_x, train_y, valid_y = sklearn.model_selection.train_test_split(
        iris.data, iris.target, test_size=0.25, random_state=0
    )

    alpha = trial.suggest_float("alpha", 1e-5, 1e-1, log=True)
    clf = sklearn.linear_model.SGDClassifier(alpha=alpha)

    for step in range(100):
        clf.partial_fit(train_x, train_y, classes=classes)

        # Report intermediate objective value.
        intermediate_value = 1.0 - clf.score(valid_x, valid_y)
        trial.report(intermediate_value, step)

        # Handle pruning based on the intermediate value.
        if trial.should_prune():
            raise optuna.TrialPruned()

    return 1.0 - clf.score(valid_x, valid_y)

将中位数终止规则设置为剪枝条件。

# Add stream handler of stdout to show the messages
optuna.logging.get_logger("optuna").addHandler(logging.StreamHandler(sys.stdout))
study = optuna.create_study(pruner=optuna.pruners.MedianPruner())
study.optimize(objective, n_trials=20)

Out:

A new study created in memory with name: no-name-089fdc83-be6a-4ddf-8353-935bb86404d3
Trial 0 finished with value: 0.1842105263157895 and parameters: {'alpha': 0.011833923547648995}. Best is trial 0 with value: 0.1842105263157895.
Trial 1 finished with value: 0.42105263157894735 and parameters: {'alpha': 8.73199227486913e-05}. Best is trial 0 with value: 0.1842105263157895.
Trial 2 finished with value: 0.052631578947368474 and parameters: {'alpha': 0.00043882602909121307}. Best is trial 2 with value: 0.052631578947368474.
Trial 3 finished with value: 0.13157894736842102 and parameters: {'alpha': 0.00494812069913214}. Best is trial 2 with value: 0.052631578947368474.
Trial 4 finished with value: 0.23684210526315785 and parameters: {'alpha': 0.0003437532386692776}. Best is trial 2 with value: 0.052631578947368474.
Trial 5 pruned.
Trial 6 pruned.
Trial 7 pruned.
Trial 8 pruned.
Trial 9 finished with value: 0.23684210526315785 and parameters: {'alpha': 0.0050598766818539965}. Best is trial 2 with value: 0.052631578947368474.
Trial 10 pruned.
Trial 11 pruned.
Trial 12 pruned.
Trial 13 pruned.
Trial 14 pruned.
Trial 15 pruned.
Trial 16 pruned.
Trial 17 pruned.
Trial 18 pruned.
Trial 19 pruned.

如你所见,有几个 trial 在其迭代完成之前被剪枝(终止)了。消息格式是 "Trial <Trial Number> pruned.".

应该使用哪个 pruner 呢?

对于非深度学习来说,根据 optuna/optuna - wiki “Benchmarks with Kurobako” 里的基准测试结果,我们推荐

不过,注意这个基准测试不是深度学习。对于深度学习而言,请参考 Ozaki et al, Hyperparameter Optimization Methods: Overview and Characteristics, in IEICE Trans, Vol.J103-D No.9 pp.615-631, 2020 里的这张表格。

Parallel Compute Resource

Categorical/Conditional Hyperparameters

Recommended Algorithms

Limited

No

TPE. GP-EI if search space is low-dimensional and continuous.

Yes

TPE. GP-EI if search space is low-dimensional and continuous

Sufficient

No

CMA-ES, Random Search

Yes

Random Search or Genetic Algorithm

用于剪枝的集成模块

为了用最简单的形式实现剪枝算法,Optuna 为以下库提供了集成模块。

关于 Optuna 集成模块的完整列表,参见 optuna.integration.

For example, XGBoostPruningCallback introduces pruning without directly changing the logic of training iteration. (See also example for the entire script.)

pruning_callback = optuna.integration.XGBoostPruningCallback(trial, 'validation-error')
bst = xgb.train(param, dtrain, evals=[(dvalid, 'validation')], callbacks=[pruning_callback])

Total running time of the script: ( 0 minutes 1.655 seconds)

Gallery generated by Sphinx-Gallery

简单的并行化

并行化非常直接 optuna.study.Study.optimize().

如果你想要手动执行 Optuna 的优化:

  1. 启动一个 RDB 服务器(在本例中我们用 MySQL)

  2. 通过 –storage 参数创建 study

  3. 在多个节点和进程之间共享 study

当然,你可以像 the kubernetes examples 里一样使用 Kubernetes.

要看并行优化怎么进行的化,请查看下面这个视频。

创建 study

你可以通过 optuna create-study 来创建 study.或者在 Python 脚本中通过 optuna.create_study() 也行。

$ mysql -u root -e "CREATE DATABASE IF NOT EXISTS example"
$ optuna create-study --study-name "distributed-example" --storage "mysql://root@localhost/example"
[I 2020-07-21 13:43:39,642] A new study created with name: distributed-example

然后写一个优化脚本。假设 foo.py 包含了以下代码。

import optuna


def objective(trial):
    x = trial.suggest_float("x", -10, 10)
    return (x - 2) ** 2


if __name__ == "__main__":
    study = optuna.load_study(
        study_name="distributed-example", storage="mysql://root@localhost/example"
    )
    study.optimize(objective, n_trials=100)
在多个节点和进程之间分享该 study

最后,在多个进程中运行这个共享的 study. 比如,在一个终端中运行 Process 1 而在另一个终端中运行 Process 2. 它们基于共享参数的历史记录来获取参数 suggestion.

Process 1:

$ python foo.py
[I 2020-07-21 13:45:02,973] Trial 0 finished with value: 45.35553104173011 and parameters: {'x': 8.73465151598285}. Best is trial 0 with value: 45.35553104173011.
[I 2020-07-21 13:45:04,013] Trial 2 finished with value: 4.6002397305938905 and parameters: {'x': 4.144816945707463}. Best is trial 1 with value: 0.028194513284051464.
...

Process 2 (the same command as process 1):

$ python foo.py
[I 2020-07-21 13:45:03,748] Trial 1 finished with value: 0.028194513284051464 and parameters: {'x': 1.8320877810162361}. Best is trial 1 with value: 0.028194513284051464.
[I 2020-07-21 13:45:05,783] Trial 3 finished with value: 24.45966755098074 and parameters: {'x': 6.945671597566982}. Best is trial 1 with value: 0.028194513284051464.
...

备注

我们不建议在大规模的分布式优化中采用 SQLite 因为其可能造成死锁和烟zhngde性能问题。请考虑其他数据库引擎,比如 PostgreSQL 或者 MySQL.

备注

在分布式优化里,请不要将 SQLite 数据库放在网络文件系统上。参见 https://www.sqlite.org/faq.html#q5

Total running time of the script: ( 0 minutes 0.000 seconds)

Gallery generated by Sphinx-Gallery

用于超参数优化分析的快速可视化

Optuna 在 optuna.visualization 里提供了各种可视化特性用于分析优化结果。

通过可视化乳腺癌数据集 上的 lightgbm 历史记录,本教程将带你体验此模块。

import lightgbm as lgb
import numpy as np
import sklearn.datasets
import sklearn.metrics
from sklearn.model_selection import train_test_split

import optuna
from optuna.visualization import plot_contour
from optuna.visualization import plot_edf
from optuna.visualization import plot_intermediate_values
from optuna.visualization import plot_optimization_history
from optuna.visualization import plot_parallel_coordinate
from optuna.visualization import plot_param_importances
from optuna.visualization import plot_slice

SEED = 42

np.random.seed(SEED)

定义目标函数

def objective(trial):
    data, target = sklearn.datasets.load_breast_cancer(return_X_y=True)
    train_x, valid_x, train_y, valid_y = train_test_split(data, target, test_size=0.25)
    dtrain = lgb.Dataset(train_x, label=train_y)
    dvalid = lgb.Dataset(valid_x, label=valid_y)

    param = {
        "objective": "binary",
        "metric": "auc",
        "verbosity": -1,
        "boosting_type": "gbdt",
        "bagging_fraction": trial.suggest_float("bagging_fraction", 0.4, 1.0),
        "bagging_freq": trial.suggest_int("bagging_freq", 1, 7),
        "min_child_samples": trial.suggest_int("min_child_samples", 5, 100),
    }

    # Add a callback for pruning.
    pruning_callback = optuna.integration.LightGBMPruningCallback(trial, "auc")
    gbm = lgb.train(
        param, dtrain, valid_sets=[dvalid], verbose_eval=False, callbacks=[pruning_callback]
    )

    preds = gbm.predict(valid_x)
    pred_labels = np.rint(preds)
    accuracy = sklearn.metrics.accuracy_score(valid_y, pred_labels)
    return accuracy
study = optuna.create_study(
    direction="maximize",
    sampler=optuna.samplers.TPESampler(seed=SEED),
    pruner=optuna.pruners.MedianPruner(n_warmup_steps=10),
)
study.optimize(objective, n_trials=100, timeout=600)

Out:

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.
绘图函数

对优化历史进行可视化。细节见 plot_optimization_history().

plot_optimization_history(study)


绘制各 trial 的学习曲线。细节见 plot_intermediate_values().

plot_intermediate_values(study)


绘制高维参数关系。细节见 plot_parallel_coordinate().

plot_parallel_coordinate(study)


选择要绘制的参数

plot_parallel_coordinate(study, params=["bagging_freq", "bagging_fraction"])


绘制超参数关系。细节见 plot_contour().

plot_contour(study)


选择要绘制的参数

plot_contour(study, params=["bagging_freq", "bagging_fraction"])


绘制各超参数的切片图。细节见 plot_slice().

plot_slice(study)


选择要绘制的参数

plot_slice(study, params=["bagging_freq", "bagging_fraction"])


绘制超参数重要性。细节见 plot_param_importances().

plot_param_importances(study)


绘制经验分布函数。 plot_edf().

plot_edf(study)


Total running time of the script: ( 0 minutes 4.492 seconds)

Gallery generated by Sphinx-Gallery

用法

展现可能对你轻松使用 Optuna 更有帮助的一些用法

用 RDB 后端保存/恢复 Study

RDB后端可以实现持久化实验(即保存和恢复 study)以及访问 study 的历史记录。此外,我们还可以利用这个特点来进行分布式优化。具体描述见 简单的并行化.

在本部分中,我们将尝试一个在本地环境下运行SQLite DB的简单例子。

备注

通过设置 DB 的 storage URL 参数,你也可以使用其他的 RDB 后端,比如 PostgreSQL 或者 MySQL. 设置 URL 的方式参见 SQLAlchemy 的文档.

新建 Study

通过调用函数 create_study(),我们可以创建一个持久化的 study. 创建新 study 会自动初始化一个 SQLite 文件 example.db.

import logging
import sys

import optuna

# Add stream handler of stdout to show the messages
optuna.logging.get_logger("optuna").addHandler(logging.StreamHandler(sys.stdout))
study_name = "example-study"  # Unique identifier of the study.
storage_name = "sqlite:///{}.db".format(study_name)
study = optuna.create_study(study_name=study_name, storage=storage_name)

Out:

A new study created in RDB with name: example-study

为了运行一个 study, 我们需要将目标函数传入 optimize() 方法并调用它。

def objective(trial):
    x = trial.suggest_float("x", -10, 10)
    return (x - 2) ** 2


study.optimize(objective, n_trials=3)

Out:

Trial 0 finished with value: 26.616254945485093 and parameters: {'x': 7.1590943919921735}. Best is trial 0 with value: 26.616254945485093.
Trial 1 finished with value: 136.0228813400637 and parameters: {'x': -9.662884777792486}. Best is trial 0 with value: 26.616254945485093.
Trial 2 finished with value: 6.92296253664903 and parameters: {'x': 4.631152321065626}. Best is trial 2 with value: 6.92296253664903.
恢复 Study

为了恢复 study, 首先需要初始化一个 Study 对象, 并将该study 的名字 example-study 和 DB URL参数 sqlite:///example.db 传入其中。

study = optuna.create_study(study_name=study_name, storage=storage_name, load_if_exists=True)
study.optimize(objective, n_trials=3)

Out:

Using an existing study with name 'example-study' instead of creating a new one.
Trial 3 finished with value: 88.36747504461586 and parameters: {'x': -7.400397600347331}. Best is trial 2 with value: 6.92296253664903.
Trial 4 finished with value: 9.371463685595762 and parameters: {'x': 5.061284646287529}. Best is trial 2 with value: 6.92296253664903.
Trial 5 finished with value: 11.281489026771837 and parameters: {'x': 5.358792793068938}. Best is trial 2 with value: 6.92296253664903.
实验历史记录

我们可以通过 Study 类来获得 study 和对应 trials的历史记录。比如,下面的语句可以获取 example-study 的所有 trials.

study = optuna.create_study(study_name=study_name, storage=storage_name, load_if_exists=True)
df = study.trials_dataframe(attrs=("number", "value", "params", "state"))

Out:

Using an existing study with name 'example-study' instead of creating a new one.

trials_dataframe() 方法会返回一个如下的 pandas dataframe:

print(df)

Out:

   number       value  params_x     state
0       0   26.616255  7.159094  COMPLETE
1       1  136.022881 -9.662885  COMPLETE
2       2    6.922963  4.631152  COMPLETE
3       3   88.367475 -7.400398  COMPLETE
4       4    9.371464  5.061285  COMPLETE
5       5   11.281489  5.358793  COMPLETE

Study 对象也有一些其他属性,比如 trials, best_valuebest_params (见 轻量级、多功能和跨平台架构).

print("Best params: ", study.best_params)
print("Best value: ", study.best_value)
print("Best Trial: ", study.best_trial)
print("Trials: ", study.trials)

Out:

Best params:  {'x': 4.631152321065626}
Best value:  6.92296253664903
Best Trial:  FrozenTrial(number=2, values=[6.92296253664903], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 42, 730964), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 42, 746113), params={'x': 4.631152321065626}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=3, state=TrialState.COMPLETE, value=None)
Trials:  [FrozenTrial(number=0, values=[26.616254945485093], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 42, 635850), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 42, 655944), params={'x': 7.1590943919921735}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=1, state=TrialState.COMPLETE, value=None), FrozenTrial(number=1, values=[136.0228813400637], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 42, 691172), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 42, 707448), params={'x': -9.662884777792486}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=2, state=TrialState.COMPLETE, value=None), FrozenTrial(number=2, values=[6.92296253664903], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 42, 730964), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 42, 746113), params={'x': 4.631152321065626}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=3, state=TrialState.COMPLETE, value=None), FrozenTrial(number=3, values=[88.36747504461586], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 42, 812319), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 42, 833198), params={'x': -7.400397600347331}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=4, state=TrialState.COMPLETE, value=None), FrozenTrial(number=4, values=[9.371463685595762], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 42, 862712), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 42, 878204), params={'x': 5.061284646287529}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=5, state=TrialState.COMPLETE, value=None), FrozenTrial(number=5, values=[11.281489026771837], datetime_start=datetime.datetime(2022, 5, 26, 12, 5, 42, 901207), datetime_complete=datetime.datetime(2022, 5, 26, 12, 5, 42, 916775), params={'x': 5.358792793068938}, distributions={'x': UniformDistribution(high=10.0, low=-10.0)}, user_attrs={}, system_attrs={}, intermediate_values={}, trial_id=6, state=TrialState.COMPLETE, value=None)]

Total running time of the script: ( 0 minutes 0.626 seconds)

Gallery generated by Sphinx-Gallery

用 Optuna 进行多目标优化

This tutorial showcases Optuna’s multi-objective optimization feature by optimizing the validation accuracy of Fashion MNIST dataset and the FLOPS of the model implemented in PyTorch.

我们采用 thop 来测量 FLOPS.

import thop
import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision

import optuna


DEVICE = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
DIR = ".."
BATCHSIZE = 128
N_TRAIN_EXAMPLES = BATCHSIZE * 30
N_VALID_EXAMPLES = BATCHSIZE * 10


def define_model(trial):
    n_layers = trial.suggest_int("n_layers", 1, 3)
    layers = []

    in_features = 28 * 28
    for i in range(n_layers):
        out_features = trial.suggest_int("n_units_l{}".format(i), 4, 128)
        layers.append(nn.Linear(in_features, out_features))
        layers.append(nn.ReLU())
        p = trial.suggest_float("dropout_{}".format(i), 0.2, 0.5)
        layers.append(nn.Dropout(p))

        in_features = out_features

    layers.append(nn.Linear(in_features, 10))
    layers.append(nn.LogSoftmax(dim=1))

    return nn.Sequential(*layers)


# Defines training and evaluation.
def train_model(model, optimizer, train_loader):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.view(-1, 28 * 28).to(DEVICE), target.to(DEVICE)
        optimizer.zero_grad()
        F.nll_loss(model(data), target).backward()
        optimizer.step()


def eval_model(model, valid_loader):
    model.eval()
    correct = 0
    with torch.no_grad():
        for batch_idx, (data, target) in enumerate(valid_loader):
            data, target = data.view(-1, 28 * 28).to(DEVICE), target.to(DEVICE)
            pred = model(data).argmax(dim=1, keepdim=True)
            correct += pred.eq(target.view_as(pred)).sum().item()

    accuracy = correct / N_VALID_EXAMPLES

    flops, _ = thop.profile(model, inputs=(torch.randn(1, 28 * 28).to(DEVICE),), verbose=False)
    return flops, accuracy

Out:

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/torch/cuda/__init__.py:52: UserWarning:

CUDA initialization: Found no NVIDIA driver on your system. Please check that you have an NVIDIA GPU and installed a driver from http://www.nvidia.com/Download/index.aspx (Triggered internally at  /pytorch/c10/cuda/CUDAFunctions.cpp:100.)

定义多目标函数. 其目标为 FLOPS 和 准确度.

def objective(trial):
    train_dataset = torchvision.datasets.FashionMNIST(
        DIR, train=True, download=True, transform=torchvision.transforms.ToTensor()
    )
    train_loader = torch.utils.data.DataLoader(
        torch.utils.data.Subset(train_dataset, list(range(N_TRAIN_EXAMPLES))),
        batch_size=BATCHSIZE,
        shuffle=True,
    )

    val_dataset = torchvision.datasets.FashionMNIST(
        DIR, train=False, transform=torchvision.transforms.ToTensor()
    )
    val_loader = torch.utils.data.DataLoader(
        torch.utils.data.Subset(val_dataset, list(range(N_VALID_EXAMPLES))),
        batch_size=BATCHSIZE,
        shuffle=True,
    )
    model = define_model(trial).to(DEVICE)

    optimizer = torch.optim.Adam(
        model.parameters(), trial.suggest_float("lr", 1e-5, 1e-1, log=True)
    )

    for epoch in range(10):
        train_model(model, optimizer, train_loader)
    flops, accuracy = eval_model(model, val_loader)
    return flops, accuracy
运行多目标优化.

如果你的优化问题是多目标的, Optuna 预设你会为每一个目标指定优化方向. 具体在本例中, 我们希望最小化 FLOPS (我们想要更快的模型) 和最大化准确度. 所以我们将 directions 设置为 ["minimize", "maximize"].

study = optuna.create_study(directions=["minimize", "maximize"])
study.optimize(objective, n_trials=30, timeout=300)

print("Number of finished trials: ", len(study.trials))

Out:

Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gz to ../FashionMNIST/raw/train-images-idx3-ubyte.gz

0it [00:00, ?it/s]
  0%|          | 0/26421880 [00:00<?, ?it/s]
  0%|          | 49152/26421880 [00:00<01:46, 248014.92it/s]
  1%|          | 139264/26421880 [00:00<01:02, 421743.45it/s]
  2%|1         | 442368/26421880 [00:00<00:20, 1272550.89it/s]
  4%|3         | 942080/26421880 [00:00<00:11, 2176696.28it/s]
  7%|7         | 1974272/26421880 [00:00<00:05, 4511643.66it/s]
 14%|#3        | 3678208/26421880 [00:01<00:02, 8152329.01it/s]
 21%|##        | 5545984/26421880 [00:01<00:02, 10092032.65it/s]
 27%|##6       | 7127040/26421880 [00:01<00:01, 11192705.86it/s]
 33%|###3      | 8830976/26421880 [00:01<00:01, 12787248.30it/s]
 40%|###9      | 10518528/26421880 [00:01<00:01, 13926096.75it/s]
 46%|####6     | 12197888/26421880 [00:01<00:00, 14735962.39it/s]
 53%|#####2    | 13959168/26421880 [00:01<00:00, 15569539.36it/s]
 61%|######1   | 16179200/26421880 [00:01<00:00, 15751040.63it/s]
 67%|######7   | 17833984/26421880 [00:01<00:00, 15966606.44it/s]
 74%|#######3  | 19513344/26421880 [00:02<00:00, 16195251.49it/s]
 80%|########  | 21233664/26421880 [00:02<00:00, 16483372.21it/s]
 87%|########7 | 23019520/26421880 [00:02<00:00, 16880922.11it/s]
 94%|#########3| 24748032/26421880 [00:02<00:00, 15316962.81it/s]
26427392it [00:02, 10739958.83it/s]
Extracting ../FashionMNIST/raw/train-images-idx3-ubyte.gz to ../FashionMNIST/raw
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-labels-idx1-ubyte.gz to ../FashionMNIST/raw/train-labels-idx1-ubyte.gz

0it [00:00, ?it/s]
  0%|          | 0/29515 [00:00<?, ?it/s]
32768it [00:00, 99803.39it/s]
Extracting ../FashionMNIST/raw/train-labels-idx1-ubyte.gz to ../FashionMNIST/raw
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-images-idx3-ubyte.gz to ../FashionMNIST/raw/t10k-images-idx3-ubyte.gz

0it [00:00, ?it/s]
  0%|          | 0/4422102 [00:00<?, ?it/s]
  1%|1         | 49152/4422102 [00:00<00:17, 247986.57it/s]
  3%|2         | 114688/4422102 [00:00<00:10, 418145.41it/s]
  6%|5         | 253952/4422102 [00:00<00:05, 782465.83it/s]
 11%|#1        | 507904/4422102 [00:00<00:02, 1394129.49it/s]
 25%|##4       | 1089536/4422102 [00:00<00:01, 2862981.83it/s]
 50%|####9     | 2203648/4422102 [00:00<00:00, 5526569.71it/s]
 89%|########9 | 3956736/4422102 [00:01<00:00, 9307042.78it/s]
4423680it [00:01, 4222097.03it/s]
Extracting ../FashionMNIST/raw/t10k-images-idx3-ubyte.gz to ../FashionMNIST/raw
Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-labels-idx1-ubyte.gz to ../FashionMNIST/raw/t10k-labels-idx1-ubyte.gz

0it [00:00, ?it/s]
  0%|          | 0/5148 [00:00<?, ?it/s]
8192it [00:00, 30002.98it/s]
Extracting ../FashionMNIST/raw/t10k-labels-idx1-ubyte.gz to ../FashionMNIST/raw
Processing...
/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/torchvision/datasets/mnist.py:480: UserWarning:

The given NumPy array is not writeable, and PyTorch does not support non-writeable tensors. This means you can write to the underlying (supposedly non-writeable) NumPy array using the tensor. You may want to copy the array to protect its data or make it writeable before converting it to a tensor. This type of warning will be suppressed for the rest of this program. (Triggered internally at  /pytorch/torch/csrc/utils/tensor_numpy.cpp:141.)

Done!
Number of finished trials:  30

可视化地检查位于帕累托前沿上的 trials

optuna.visualization.plot_pareto_front(study, target_names=["FLOPS", "accuracy"])

Out:

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/checkouts/latest/tutorial/20_recipes/002_multi_objective.py:123: ExperimentalWarning:

plot_pareto_front is experimental (supported from v2.4.0). The interface can change in the future.


Total running time of the script: ( 1 minutes 48.248 seconds)

Gallery generated by Sphinx-Gallery

用户定义属性

利用用户自定义属性,这个功能可以给实验做注解。

将用户定义属性添加到 Study

Study 对象提供了一个将键-值对设置为用户自定义属性的方法: set_user_attr(). 这里的键应该属于 str 类型, 而值可以是任何能用 json.dumps 来序列化的对象。

import sklearn.datasets
import sklearn.model_selection
import sklearn.svm

import optuna


study = optuna.create_study(storage="sqlite:///example.db")
study.set_user_attr("contributors", ["Akiba", "Sano"])
study.set_user_attr("dataset", "MNIST")

我们可以利用 user_attr 属性来获取所有定义过的属性。

study.user_attrs  # {'contributors': ['Akiba', 'Sano'], 'dataset': 'MNIST'}

Out:

{'contributors': ['Akiba', 'Sano'], 'dataset': 'MNIST'}

StudySummary 对象中也包含了用户的自定义属性。我们可以从 get_all_study_summaries() 中获取它。

study_summaries = optuna.get_all_study_summaries("sqlite:///example.db")
study_summaries[0].user_attrs  # {"contributors": ["Akiba", "Sano"], "dataset": "MNIST"}

Out:

{'contributors': ['Akiba', 'Sano'], 'dataset': 'MNIST'}

参见

在命令行界面里,optuna study set-user-attr 可用于设置用户定义属性。

将用户属性添加到 Trial 中

Study 类似,Trial 对象也提供了一个设置属性的方法 set_user_attr() 方法。这些属性是在目标函数内部设置的。

def objective(trial):
    iris = sklearn.datasets.load_iris()
    x, y = iris.data, iris.target

    svc_c = trial.suggest_float("svc_c", 1e-10, 1e10, log=True)
    clf = sklearn.svm.SVC(C=svc_c)
    accuracy = sklearn.model_selection.cross_val_score(clf, x, y).mean()

    trial.set_user_attr("accuracy", accuracy)

    return 1.0 - accuracy  # return error for minimization


study.optimize(objective, n_trials=1)

可以用如下方式获取这些注解的属性:

study.trials[0].user_attrs

Out:

{'accuracy': 0.9266666666666667}

注意,在本例中,属性不是被注解到 Study 的,它属于一个单独的 Trial.

Total running time of the script: ( 0 minutes 0.284 seconds)

Gallery generated by Sphinx-Gallery

命令行界面

Command

Description

create-study

创建一个新的 study.

delete-study

删除指定的 study.

dashboard

启动 web 面板 (beta).

storage upgrade

升级数据库 schema.

studies

输出 study 列表

study optimize

针对一个 study 开始优化过程。

study set-user-attr

设置 study 的用户属性。

Optuna 提供的命令行界面包括上表中的所有命令。

如果你不使用 IPython shell, 而是写 Python 脚本文件的话,编写像下面这样的脚本是完全可以的:

import optuna


def objective(trial):
    x = trial.suggest_float("x", -10, 10)
    return (x - 2) ** 2


if __name__ == "__main__":
    study = optuna.create_study()
    study.optimize(objective, n_trials=100)
    print("Best value: {} (params: {})\n".format(study.best_value, study.best_params))

Out:

Best value: 6.857169057397077e-05 (params: {'x': 2.0082808025319996})

然而,通过 optuna 命令,我们可以少写一些上面这样的模版代码。假设有一个 foo.py 文件,它只包含如下代码:

def objective(trial):
    x = trial.suggest_float("x", -10, 10)
    return (x - 2) ** 2

即使在这种情况下,我们也可以利用如下命令启动优化过程(请暂时忽略 --storage sqlite:///example.db 的作用, 关于它的具体描述见 用 RDB 后端保存/恢复 Study)。

$ cat foo.py
def objective(trial):
    x = trial.suggest_float('x', -10, 10)
    return (x - 2) ** 2

$ STUDY_NAME=`optuna create-study --storage sqlite:///example.db`
$ optuna study optimize foo.py objective --n-trials=100 --storage sqlite:///example.db --study-name $STUDY_NAME
[I 2018-05-09 10:40:25,196] Finished a trial resulted in value: 54.353767789264026. Current best value is 54.353767789264026 with parameters: {'x': -5.372500782588228}.
[I 2018-05-09 10:40:25,197] Finished a trial resulted in value: 15.784266965526376. Current best value is 15.784266965526376 with parameters: {'x': 5.972941852774387}.
...
[I 2018-05-09 10:40:26,204] Finished a trial resulted in value: 14.704254135013741. Current best value is 2.280758099793617e-06 with parameters: {'x': 1.9984897821018828}.

请注意,foo.py 中只包含目标函数的定义。通过向 optuna study optimize 命令传递该文件名和对应目标函数的方法名,我们就可以启动优化过程。

Total running time of the script: ( 0 minutes 0.355 seconds)

Gallery generated by Sphinx-Gallery

用户定义的采样器 (Sampler)

你可以用用户定义的 sampler 来实现:

  • 试验你自己的采样算法,

  • 实现具体任务对应的算法来改进优化性能,或者

  • 将其他的优化框架包装起来,整合进 Optuna 的流水线中 (比如 SkoptSampler ).

本节将介绍 sampler 类的内部行为,并展示一个实现用户自定义 sampler 的例子。

Sampler 概述

A sampler has the responsibility to determine the parameter values to be evaluated in a trial. When a suggest API (e.g., suggest_float()) is called inside an objective function, the corresponding distribution object (e.g., UniformDistribution) is created internally. A sampler samples a parameter value from the distribution. The sampled value is returned to the caller of the suggest API and evaluated in the objective function.

为了创建一个新的 sampler, 你所定义的类需继承 BaseSampler.该基类提供三个抽象方法:infer_relative_search_space(), sample_relative()sample_independent().

从这些方法名可以看出,Optuna 支持两种类型的采样过程:一种是 relative sampling, 它考虑了单个 trial 内参数之间的相关性, 另一种是 independent sampling, 它对各个参数的采样是彼此独立的。

在一个 trial 刚开始时,infer_relative_search_space() 会被调用,它向该 trial 提供一个相对搜索空间。之后, sample_relative() 会被触发,它从该搜索空间中对相对参数进行采样。在目标函数的执行过程中,sample_independent() 用于对不属于该相对搜索空间的参数进行采样。

备注

更多细节参见 BaseSampler 的文档。

案例: 实现模拟退火 Sampler (SimulatedAnnealingSampler)

下面的代码根据 Simulated Annealing (SA) 定义类一个 sampler:

import numpy as np
import optuna


class SimulatedAnnealingSampler(optuna.samplers.BaseSampler):
    def __init__(self, temperature=100):
        self._rng = np.random.RandomState()
        self._temperature = temperature  # Current temperature.
        self._current_trial = None  # Current state.

    def sample_relative(self, study, trial, search_space):
        if search_space == {}:
            return {}

        # Simulated Annealing algorithm.
        # 1. Calculate transition probability.
        prev_trial = study.trials[-2]
        if self._current_trial is None or prev_trial.value <= self._current_trial.value:
            probability = 1.0
        else:
            probability = np.exp(
                (self._current_trial.value - prev_trial.value) / self._temperature
            )
        self._temperature *= 0.9  # Decrease temperature.

        # 2. Transit the current state if the previous result is accepted.
        if self._rng.uniform(0, 1) < probability:
            self._current_trial = prev_trial

        # 3. Sample parameters from the neighborhood of the current point.
        # The sampled parameters will be used during the next execution of
        # the objective function passed to the study.
        params = {}
        for param_name, param_distribution in search_space.items():
            if not isinstance(param_distribution, optuna.distributions.UniformDistribution):
                raise NotImplementedError("Only suggest_float() is supported")

            current_value = self._current_trial.params[param_name]
            width = (param_distribution.high - param_distribution.low) * 0.1
            neighbor_low = max(current_value - width, param_distribution.low)
            neighbor_high = min(current_value + width, param_distribution.high)
            params[param_name] = self._rng.uniform(neighbor_low, neighbor_high)

        return params

    # The rest are unrelated to SA algorithm: boilerplate
    def infer_relative_search_space(self, study, trial):
        return optuna.samplers.intersection_search_space(study)

    def sample_independent(self, study, trial, param_name, param_distribution):
        independent_sampler = optuna.samplers.RandomSampler()
        return independent_sampler.sample_independent(study, trial, param_name, param_distribution)

备注

为了代码的简洁性,上面的实现没有支持一些特性 (比如 maximization). 如果你对如何实现这些特性感兴趣,请看 examples/samplers/simulated_annealing.py.

你可以像使用内置的 sampler 一样使用 SimulatedAnnealingSampler:

def objective(trial):
    x = trial.suggest_float("x", -10, 10)
    y = trial.suggest_float("y", -5, 5)
    return x ** 2 + y


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

best_trial = study.best_trial
print("Best value: ", best_trial.value)
print("Parameters that achieve the best value: ", best_trial.params)

Out:

Best value:  -4.823887940146913
Parameters that achieve the best value:  {'x': -0.16867044642607176, 'y': -4.852337659644483}

在上面这个优化过程中,参数 xy 的值都是由 SimulatedAnnealingSampler.sample_relative 方法采样得出的。

备注

严格意义上说,在第一个 trial 中,SimulatedAnnealingSampler.sample_independent 用于采样参数值。因为,如果没有已经完成的 trial 的话, SimulatedAnnealingSampler.infer_relative_search_space 中的 intersection_search_space() 是无法对搜索空间进行推断的。

Total running time of the script: ( 0 minutes 0.373 seconds)

Gallery generated by Sphinx-Gallery

用户定义的 Pruner

optuna.pruners 中, 我们描述了一个目标函数如何可选地包含一些 pruning 函数调用, 这些函数允许 Optuna 去终结中间结果无望的 tiral. 在本文档中, 我们描述了如何实现一个你自己的 pruner, 也即一个自定义 trial 终止条件的策略.

Pruning 界面概述

The create_study() constructor takes, as an optional argument, a pruner inheriting from BasePruner. The pruner should implement the abstract method prune(), which takes arguments for the associated Study and Trial and returns a boolean value: True if the trial should be pruned and False otherwise. Using the Study and Trial objects, you can access all other trials through the get_trial() method and, and from a trial, its reported intermediate values through the intermediate_values() (a dictionary which maps an integer step to a float value).

你可以参考 Optuna 内置的 pruner 的源代码作为构建你自己的 pruner 的模板. 在本文档中, 我们描述了一个简单 (但是却激进) 的 pruner 实现过程, 它会对那些处于同步骤 trial 中最后一名的 trial 进行剪枝.

备注

Please refer to the documentation of BasePruner or, for example, ThresholdPruner or PercentilePruner for more robust examples of pruner implementation, including error checking and complex pruner-internal logic.

例子: 实现 LastPlacePruner

We aim to optimize the loss and alpha hyperparameters for a stochastic gradient descent classifier (SGDClassifier) run on the sklearn iris dataset. We implement a pruner which terminates a trial at a certain step if it is in last place compared to completed trials at the same step. We begin considering pruning after a “warmup” of 1 training step and 5 completed trials. For demonstration purposes, we print() a diagnostic message from prune when it is about to return True (indicating pruning).

It may be important to note that the SGDClassifier score, as it is evaluated on a holdout set, decreases with enough training steps due to overfitting. This means that a trial could be pruned even if it had a favorable (high) value on a previous training set. After pruning, Optuna will take the intermediate value last reported as the value of the trial.

import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.linear_model import SGDClassifier

import optuna
from optuna.pruners import BasePruner
from optuna.trial._state import TrialState


class LastPlacePruner(BasePruner):
    def __init__(self, warmup_steps, warmup_trials):
        self._warmup_steps = warmup_steps
        self._warmup_trials = warmup_trials

    def prune(self, study: "optuna.study.Study", trial: "optuna.trial.FrozenTrial") -> bool:
        # Get the latest score reported from this trial
        step = trial.last_step

        if step:  # trial.last_step == None when no scores have been reported yet
            this_score = trial.intermediate_values[step]

            # Get scores from other trials in the study reported at the same step
            completed_trials = study.get_trials(deepcopy=False, states=(TrialState.COMPLETE,))
            other_scores = [
                t.intermediate_values[step]
                for t in completed_trials
                if step in t.intermediate_values
            ]
            other_scores = sorted(other_scores)

            # Prune if this trial at this step has a lower value than all completed trials
            # at the same step. Note that steps will begin numbering at 0 in the objective
            # function definition below.
            if step >= self._warmup_steps and len(other_scores) > self._warmup_trials:
                if this_score < other_scores[0]:
                    print(f"prune() True: Trial {trial.number}, Step {step}, Score {this_score}")
                    return True

        return False

最后, 让我们通过一个简单的超参数优化来确认该实现是正确的.

def objective(trial):
    iris = load_iris()
    classes = np.unique(iris.target)
    X_train, X_valid, y_train, y_valid = train_test_split(
        iris.data, iris.target, train_size=100, test_size=50, random_state=0
    )

    loss = trial.suggest_categorical("loss", ["hinge", "log", "perceptron"])
    alpha = trial.suggest_float("alpha", 0.00001, 0.001, log=True)
    clf = SGDClassifier(loss=loss, alpha=alpha, random_state=0)
    score = 0

    for step in range(0, 5):
        clf.partial_fit(X_train, y_train, classes=classes)
        score = clf.score(X_valid, y_valid)

        trial.report(score, step)

        if trial.should_prune():
            raise optuna.TrialPruned()

    return score


pruner = LastPlacePruner(warmup_steps=1, warmup_trials=5)
study = optuna.create_study(direction="maximize", pruner=pruner)
study.optimize(objective, n_trials=50)

Out:

prune() True: Trial 6, Step 1, Score 0.72
prune() True: Trial 12, Step 1, Score 0.62
prune() True: Trial 13, Step 1, Score 0.66
prune() True: Trial 14, Step 1, Score 0.7
prune() True: Trial 16, Step 1, Score 0.5
prune() True: Trial 17, Step 3, Score 0.5
prune() True: Trial 19, Step 4, Score 0.7
prune() True: Trial 30, Step 1, Score 0.8
prune() True: Trial 43, Step 4, Score 0.64
prune() True: Trial 49, Step 4, Score 0.64

Total running time of the script: ( 0 minutes 0.703 seconds)

Gallery generated by Sphinx-Gallery

Study.optimize 的回调

本教程展示了如何使用和实现一个 用于 optimize() 的Optuna Callback .

Callback 会在 objective 每次求值以后被调用一次, 它接受 StudyFrozenTrial 作为参数并进行处理.

MLflowCallback 是个好例子.

在特定数量的 trial 被剪枝后终止优化

本例实现了一个有状态的回调函数. 如果特定数目的 trial 被剪枝了, 它将终止优化. 被剪枝的 trial 数是通过 threshold 来指定的.

import optuna


class StopWhenTrialKeepBeingPrunedCallback:
    def __init__(self, threshold: int):
        self.threshold = threshold
        self._consequtive_pruned_count = 0

    def __call__(self, study: optuna.study.Study, trial: optuna.trial.FrozenTrial) -> None:
        if trial.state == optuna.trial.TrialState.PRUNED:
            self._consequtive_pruned_count += 1
        else:
            self._consequtive_pruned_count = 0

        if self._consequtive_pruned_count >= self.threshold:
            study.stop()

该目标函数会对除了前五个 trial 之外的所有trial 进行剪枝 (trial.number 从 0 开始计数).

def objective(trial):
    if trial.number > 4:
        raise optuna.TrialPruned

    return trial.suggest_float("x", 0, 1)

在这里, 我们将阈值设置为 2: 优化过程会在一旦两个 trial 被剪枝后发生. 因此, 我们预期该 study 会在 7 个 trial 后停止.

import logging
import sys

# Add stream handler of stdout to show the messages
optuna.logging.get_logger("optuna").addHandler(logging.StreamHandler(sys.stdout))

study_stop_cb = StopWhenTrialKeepBeingPrunedCallback(2)
study = optuna.create_study()
study.optimize(objective, n_trials=10, callbacks=[study_stop_cb])

Out:

A new study created in memory with name: no-name-be0f83c6-46e8-42fd-9d9e-f6ebd9175ba1
Trial 0 finished with value: 0.03979847340429532 and parameters: {'x': 0.03979847340429532}. Best is trial 0 with value: 0.03979847340429532.
Trial 1 finished with value: 0.32272885613475355 and parameters: {'x': 0.32272885613475355}. Best is trial 0 with value: 0.03979847340429532.
Trial 2 finished with value: 0.04095547461074589 and parameters: {'x': 0.04095547461074589}. Best is trial 0 with value: 0.03979847340429532.
Trial 3 finished with value: 0.36590369991767835 and parameters: {'x': 0.36590369991767835}. Best is trial 0 with value: 0.03979847340429532.
Trial 4 finished with value: 0.23228097273032844 and parameters: {'x': 0.23228097273032844}. Best is trial 0 with value: 0.03979847340429532.
Trial 5 pruned.
Trial 6 pruned.

从上面的日志中可以看出, study 如期在 7 个trial 之后停止了.

Total running time of the script: ( 0 minutes 0.008 seconds)

Gallery generated by Sphinx-Gallery

手动指定超参数

你自然有一些特定的超参数集要先尝试, 比如初始学习率和叶子数量. 另外, 也有可能在让 Optuna 找到更好的超参数集之前,你已经尝试过一些集合.

Optuna 提供 两个API 以应对这种场景:

  1. 将这些超参数集合传递过去并让 Optuna 对其求值 - enqueue_trial()

  2. 将这些集合的结果标记为已完成的 Trials - add_trial()

第一个场景: 让 Optuna 对你的超参数求值

在这个场景中, 我们假设你已经有了一些开箱即用的超参数但是还没有对他们进行过求值, 你决定用 Optuna 来找到更好的超参数.

Optuna 有一个 API optuna.study.Study.enqueue_trial() , 它允许你将这些超参数传入 Optuna, Optuna 会对他们求值.

这部分将结合 LightGBM 向你介绍如何使用这些API.

import lightgbm as lgb
import numpy as np
import sklearn.datasets
import sklearn.metrics
from sklearn.model_selection import train_test_split

import optuna

定义目标函数.

def objective(trial):
    data, target = sklearn.datasets.load_breast_cancer(return_X_y=True)
    train_x, valid_x, train_y, valid_y = train_test_split(data, target, test_size=0.25)
    dtrain = lgb.Dataset(train_x, label=train_y)
    dvalid = lgb.Dataset(valid_x, label=valid_y)

    param = {
        "objective": "binary",
        "metric": "auc",
        "verbosity": -1,
        "boosting_type": "gbdt",
        "bagging_fraction": min(trial.suggest_float("bagging_fraction", 0.4, 1.0 + 1e-12), 1),
        "bagging_freq": trial.suggest_int("bagging_freq", 0, 7),
        "min_child_samples": trial.suggest_int("min_child_samples", 5, 100),
    }

    # Add a callback for pruning.
    pruning_callback = optuna.integration.LightGBMPruningCallback(trial, "auc")
    gbm = lgb.train(
        param, dtrain, valid_sets=[dvalid], verbose_eval=False, callbacks=[pruning_callback]
    )

    preds = gbm.predict(valid_x)
    pred_labels = np.rint(preds)
    accuracy = sklearn.metrics.accuracy_score(valid_y, pred_labels)
    return accuracy

然后构建 Study 用于超参数优化.

study = optuna.create_study(direction="maximize", pruner=optuna.pruners.MedianPruner())

在这里, 我们让 Optuna 对一些带有更大的 "bagging_fraq" 和默认值的集合求值.

study.enqueue_trial(
    {
        "bagging_fraction": 1.0,
        "bagging_freq": 0,
        "min_child_samples": 20,
    }
)

study.enqueue_trial(
    {
        "bagging_fraction": 0.75,
        "bagging_freq": 5,
        "min_child_samples": 20,
    }
)

import logging
import sys

# Add stream handler of stdout to show the messages to see Optuna works expectedly.
optuna.logging.get_logger("optuna").addHandler(logging.StreamHandler(sys.stdout))
study.optimize(objective, n_trials=100, timeout=600)

Out:

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/checkouts/latest/tutorial/20_recipes/008_specify_params.py:81: ExperimentalWarning:

enqueue_trial is experimental (supported from v1.2.0). The interface can change in the future.

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/optuna/study.py:857: ExperimentalWarning:

create_trial is experimental (supported from v2.0.0). The interface can change in the future.

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/optuna/study.py:857: ExperimentalWarning:

add_trial is experimental (supported from v2.0.0). The interface can change in the future.

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/checkouts/latest/tutorial/20_recipes/008_specify_params.py:89: ExperimentalWarning:

enqueue_trial is experimental (supported from v1.2.0). The interface can change in the future.

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

Trial 0 finished with value: 0.972027972027972 and parameters: {'bagging_fraction': 1.0, 'bagging_freq': 0, 'min_child_samples': 20}. Best is trial 0 with value: 0.972027972027972.
/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

Trial 1 finished with value: 0.965034965034965 and parameters: {'bagging_fraction': 0.75, 'bagging_freq': 5, 'min_child_samples': 20}. Best is trial 0 with value: 0.972027972027972.
/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

Trial 2 finished with value: 0.965034965034965 and parameters: {'bagging_fraction': 0.9245043426687198, 'bagging_freq': 7, 'min_child_samples': 32}. Best is trial 0 with value: 0.972027972027972.
/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

Trial 3 finished with value: 0.993006993006993 and parameters: {'bagging_fraction': 0.8187425833714853, 'bagging_freq': 4, 'min_child_samples': 86}. Best is trial 3 with value: 0.993006993006993.
/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

Trial 4 finished with value: 0.9370629370629371 and parameters: {'bagging_fraction': 0.5343516754966955, 'bagging_freq': 1, 'min_child_samples': 68}. Best is trial 3 with value: 0.993006993006993.
/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

Trial 5 pruned. Trial was pruned at iteration 0.
Trial 6 pruned. Trial was pruned at iteration 1.
Trial 7 pruned. Trial was pruned at iteration 1.
Trial 8 pruned. Trial was pruned at iteration 1.
Trial 9 pruned. Trial was pruned at iteration 0.
Trial 10 pruned. Trial was pruned at iteration 0.
Trial 11 pruned. Trial was pruned at iteration 0.
Trial 12 pruned. Trial was pruned at iteration 1.
Trial 13 pruned. Trial was pruned at iteration 50.
Trial 14 pruned. Trial was pruned at iteration 0.
Trial 15 pruned. Trial was pruned at iteration 50.
Trial 16 pruned. Trial was pruned at iteration 5.
Trial 17 pruned. Trial was pruned at iteration 0.
Trial 18 pruned. Trial was pruned at iteration 1.
Trial 19 pruned. Trial was pruned at iteration 0.
Trial 20 pruned. Trial was pruned at iteration 0.
Trial 21 pruned. Trial was pruned at iteration 2.
Trial 22 pruned. Trial was pruned at iteration 16.
Trial 23 pruned. Trial was pruned at iteration 9.
Trial 24 pruned. Trial was pruned at iteration 16.
Trial 25 pruned. Trial was pruned at iteration 1.
Trial 26 pruned. Trial was pruned at iteration 0.
Trial 27 pruned. Trial was pruned at iteration 1.
Trial 28 pruned. Trial was pruned at iteration 9.
Trial 29 pruned. Trial was pruned at iteration 3.
Trial 30 pruned. Trial was pruned at iteration 11.
Trial 31 pruned. Trial was pruned at iteration 2.
Trial 32 pruned. Trial was pruned at iteration 5.
Trial 33 pruned. Trial was pruned at iteration 0.
Trial 34 pruned. Trial was pruned at iteration 0.
Trial 35 pruned. Trial was pruned at iteration 0.
Trial 36 pruned. Trial was pruned at iteration 5.
Trial 37 finished with value: 0.958041958041958 and parameters: {'bagging_fraction': 0.7739890653990704, 'bagging_freq': 2, 'min_child_samples': 31}. Best is trial 3 with value: 0.993006993006993.
/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

Trial 38 pruned. Trial was pruned at iteration 0.
Trial 39 pruned. Trial was pruned at iteration 2.
Trial 40 pruned. Trial was pruned at iteration 0.
Trial 41 pruned. Trial was pruned at iteration 1.
Trial 42 pruned. Trial was pruned at iteration 0.
Trial 43 pruned. Trial was pruned at iteration 6.
Trial 44 pruned. Trial was pruned at iteration 0.
Trial 45 pruned. Trial was pruned at iteration 0.
Trial 46 pruned. Trial was pruned at iteration 14.
Trial 47 finished with value: 0.972027972027972 and parameters: {'bagging_fraction': 0.7858482082974627, 'bagging_freq': 2, 'min_child_samples': 36}. Best is trial 3 with value: 0.993006993006993.
/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

Trial 48 pruned. Trial was pruned at iteration 0.
Trial 49 pruned. Trial was pruned at iteration 17.
Trial 50 pruned. Trial was pruned at iteration 0.
Trial 51 pruned. Trial was pruned at iteration 0.
Trial 52 pruned. Trial was pruned at iteration 0.
Trial 53 pruned. Trial was pruned at iteration 0.
Trial 54 pruned. Trial was pruned at iteration 0.
Trial 55 finished with value: 0.9790209790209791 and parameters: {'bagging_fraction': 0.793864144255535, 'bagging_freq': 4, 'min_child_samples': 32}. Best is trial 3 with value: 0.993006993006993.
/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

Trial 56 pruned. Trial was pruned at iteration 0.
Trial 57 pruned. Trial was pruned at iteration 0.
Trial 58 finished with value: 0.9790209790209791 and parameters: {'bagging_fraction': 0.9730199146950024, 'bagging_freq': 5, 'min_child_samples': 17}. Best is trial 3 with value: 0.993006993006993.
/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

Trial 59 pruned. Trial was pruned at iteration 0.
Trial 60 pruned. Trial was pruned at iteration 0.
Trial 61 pruned. Trial was pruned at iteration 0.
Trial 62 pruned. Trial was pruned at iteration 0.
Trial 63 pruned. Trial was pruned at iteration 0.
Trial 64 pruned. Trial was pruned at iteration 0.
Trial 65 pruned. Trial was pruned at iteration 0.
Trial 66 pruned. Trial was pruned at iteration 0.
Trial 67 pruned. Trial was pruned at iteration 0.
Trial 68 pruned. Trial was pruned at iteration 0.
Trial 69 pruned. Trial was pruned at iteration 0.
Trial 70 pruned. Trial was pruned at iteration 0.
Trial 71 finished with value: 0.9790209790209791 and parameters: {'bagging_fraction': 0.7761725416286711, 'bagging_freq': 3, 'min_child_samples': 32}. Best is trial 3 with value: 0.993006993006993.
/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

Trial 72 pruned. Trial was pruned at iteration 0.
Trial 73 pruned. Trial was pruned at iteration 2.
Trial 74 pruned. Trial was pruned at iteration 0.
Trial 75 pruned. Trial was pruned at iteration 0.
Trial 76 pruned. Trial was pruned at iteration 0.
Trial 77 pruned. Trial was pruned at iteration 0.
Trial 78 pruned. Trial was pruned at iteration 0.
Trial 79 pruned. Trial was pruned at iteration 0.
Trial 80 pruned. Trial was pruned at iteration 0.
Trial 81 pruned. Trial was pruned at iteration 0.
Trial 82 pruned. Trial was pruned at iteration 0.
Trial 83 pruned. Trial was pruned at iteration 0.
Trial 84 pruned. Trial was pruned at iteration 0.
Trial 85 pruned. Trial was pruned at iteration 0.
Trial 86 pruned. Trial was pruned at iteration 0.
Trial 87 pruned. Trial was pruned at iteration 0.
Trial 88 pruned. Trial was pruned at iteration 0.
Trial 89 pruned. Trial was pruned at iteration 0.
Trial 90 pruned. Trial was pruned at iteration 0.
Trial 91 pruned. Trial was pruned at iteration 0.
Trial 92 pruned. Trial was pruned at iteration 0.
Trial 93 pruned. Trial was pruned at iteration 0.
Trial 94 pruned. Trial was pruned at iteration 0.
Trial 95 pruned. Trial was pruned at iteration 0.
Trial 96 pruned. Trial was pruned at iteration 0.
Trial 97 pruned. Trial was pruned at iteration 0.
Trial 98 pruned. Trial was pruned at iteration 6.
Trial 99 pruned. Trial was pruned at iteration 0.
第二个场景: 让 Optuna 利用已经求值过的超参数.

在这个场景中, 我们假设你已经有了一些开箱即用的超参数, 而且你已经对他们进行求值过, 但是其结果并不让人满意. 因此你考虑使用 Optuna 来找到更好的超参数.

Optuna 有一个 API optuna.study.Study.add_trial() , 它让你向Optuna 注册这些结果, 之后 Optuna 会在进行超参数采样的时候将它们考虑进去.

在本部分中, objective 和第一个场景中一样.

study = optuna.create_study(direction="maximize", pruner=optuna.pruners.MedianPruner())
study.add_trial(
    optuna.trial.create_trial(
        params={
            "bagging_fraction": 1.0,
            "bagging_freq": 0,
            "min_child_samples": 20,
        },
        distributions={
            "bagging_fraction": optuna.distributions.UniformDistribution(0.4, 1.0 + 1e-12),
            "bagging_freq": optuna.distributions.IntUniformDistribution(0, 7),
            "min_child_samples": optuna.distributions.IntUniformDistribution(5, 100),
        },
        value=0.94,
    )
)
study.add_trial(
    optuna.trial.create_trial(
        params={
            "bagging_fraction": 0.75,
            "bagging_freq": 5,
            "min_child_samples": 20,
        },
        distributions={
            "bagging_fraction": optuna.distributions.UniformDistribution(0.4, 1.0 + 1e-12),
            "bagging_freq": optuna.distributions.IntUniformDistribution(0, 7),
            "min_child_samples": optuna.distributions.IntUniformDistribution(5, 100),
        },
        value=0.95,
    )
)
study.optimize(objective, n_trials=100, timeout=600)

Out:

A new study created in memory with name: no-name-cc69cd99-8453-4d99-8141-15cb8d5c5dda
/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/checkouts/latest/tutorial/20_recipes/008_specify_params.py:126: ExperimentalWarning:

create_trial is experimental (supported from v2.0.0). The interface can change in the future.

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/checkouts/latest/tutorial/20_recipes/008_specify_params.py:126: ExperimentalWarning:

add_trial is experimental (supported from v2.0.0). The interface can change in the future.

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/checkouts/latest/tutorial/20_recipes/008_specify_params.py:141: ExperimentalWarning:

create_trial is experimental (supported from v2.0.0). The interface can change in the future.

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/checkouts/latest/tutorial/20_recipes/008_specify_params.py:141: ExperimentalWarning:

add_trial is experimental (supported from v2.0.0). The interface can change in the future.

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

Trial 2 finished with value: 0.972027972027972 and parameters: {'bagging_fraction': 0.4790265485370792, 'bagging_freq': 0, 'min_child_samples': 52}. Best is trial 2 with value: 0.972027972027972.
/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

Trial 3 finished with value: 0.986013986013986 and parameters: {'bagging_fraction': 0.871999548285771, 'bagging_freq': 6, 'min_child_samples': 79}. Best is trial 3 with value: 0.986013986013986.
/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

Trial 4 finished with value: 0.972027972027972 and parameters: {'bagging_fraction': 0.6430934932000298, 'bagging_freq': 4, 'min_child_samples': 69}. Best is trial 3 with value: 0.986013986013986.
/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

Trial 5 pruned. Trial was pruned at iteration 0.
Trial 6 pruned. Trial was pruned at iteration 0.
Trial 7 pruned. Trial was pruned at iteration 8.
Trial 8 pruned. Trial was pruned at iteration 3.
Trial 9 pruned. Trial was pruned at iteration 0.
Trial 10 pruned. Trial was pruned at iteration 0.
Trial 11 pruned. Trial was pruned at iteration 0.
Trial 12 pruned. Trial was pruned at iteration 1.
Trial 13 pruned. Trial was pruned at iteration 0.
Trial 14 pruned. Trial was pruned at iteration 0.
Trial 15 finished with value: 0.965034965034965 and parameters: {'bagging_fraction': 0.7055398871356235, 'bagging_freq': 3, 'min_child_samples': 6}. Best is trial 3 with value: 0.986013986013986.
/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

Trial 16 pruned. Trial was pruned at iteration 0.
Trial 17 pruned. Trial was pruned at iteration 0.
Trial 18 pruned. Trial was pruned at iteration 4.
Trial 19 pruned. Trial was pruned at iteration 1.
Trial 20 pruned. Trial was pruned at iteration 0.
Trial 21 pruned. Trial was pruned at iteration 0.
Trial 22 pruned. Trial was pruned at iteration 0.
Trial 23 pruned. Trial was pruned at iteration 0.
Trial 24 pruned. Trial was pruned at iteration 0.
Trial 25 pruned. Trial was pruned at iteration 0.
Trial 26 pruned. Trial was pruned at iteration 0.
Trial 27 pruned. Trial was pruned at iteration 0.
Trial 28 pruned. Trial was pruned at iteration 0.
Trial 29 pruned. Trial was pruned at iteration 0.
Trial 30 pruned. Trial was pruned at iteration 3.
Trial 31 pruned. Trial was pruned at iteration 0.
Trial 32 pruned. Trial was pruned at iteration 3.
Trial 33 pruned. Trial was pruned at iteration 1.
Trial 34 pruned. Trial was pruned at iteration 0.
Trial 35 pruned. Trial was pruned at iteration 0.
Trial 36 pruned. Trial was pruned at iteration 0.
Trial 37 pruned. Trial was pruned at iteration 3.
Trial 38 pruned. Trial was pruned at iteration 0.
Trial 39 pruned. Trial was pruned at iteration 0.
Trial 40 pruned. Trial was pruned at iteration 0.
Trial 41 pruned. Trial was pruned at iteration 1.
Trial 42 pruned. Trial was pruned at iteration 12.
Trial 43 pruned. Trial was pruned at iteration 0.
Trial 44 finished with value: 0.9790209790209791 and parameters: {'bagging_fraction': 0.6761135150854422, 'bagging_freq': 5, 'min_child_samples': 11}. Best is trial 3 with value: 0.986013986013986.
/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

Trial 45 pruned. Trial was pruned at iteration 0.
Trial 46 pruned. Trial was pruned at iteration 3.
Trial 47 pruned. Trial was pruned at iteration 0.
Trial 48 pruned. Trial was pruned at iteration 0.
Trial 49 pruned. Trial was pruned at iteration 0.
Trial 50 pruned. Trial was pruned at iteration 0.
Trial 51 pruned. Trial was pruned at iteration 0.
Trial 52 pruned. Trial was pruned at iteration 1.
Trial 53 pruned. Trial was pruned at iteration 3.
Trial 54 pruned. Trial was pruned at iteration 1.
Trial 55 pruned. Trial was pruned at iteration 3.
Trial 56 pruned. Trial was pruned at iteration 0.
Trial 57 pruned. Trial was pruned at iteration 1.
Trial 58 pruned. Trial was pruned at iteration 0.
Trial 59 pruned. Trial was pruned at iteration 1.
Trial 60 pruned. Trial was pruned at iteration 0.
Trial 61 pruned. Trial was pruned at iteration 0.
Trial 62 pruned. Trial was pruned at iteration 1.
Trial 63 pruned. Trial was pruned at iteration 0.
Trial 64 pruned. Trial was pruned at iteration 0.
Trial 65 pruned. Trial was pruned at iteration 0.
Trial 66 pruned. Trial was pruned at iteration 0.
Trial 67 pruned. Trial was pruned at iteration 0.
Trial 68 pruned. Trial was pruned at iteration 7.
Trial 69 pruned. Trial was pruned at iteration 0.
Trial 70 pruned. Trial was pruned at iteration 0.
Trial 71 finished with value: 0.972027972027972 and parameters: {'bagging_fraction': 0.8551267429082452, 'bagging_freq': 6, 'min_child_samples': 15}. Best is trial 3 with value: 0.986013986013986.
/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

Trial 72 pruned. Trial was pruned at iteration 0.
Trial 73 pruned. Trial was pruned at iteration 1.
Trial 74 finished with value: 0.986013986013986 and parameters: {'bagging_fraction': 0.8767555146302938, 'bagging_freq': 7, 'min_child_samples': 18}. Best is trial 3 with value: 0.986013986013986.
/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/lightgbm/engine.py:239: UserWarning:

'verbose_eval' argument is deprecated and will be removed in a future release of LightGBM. Pass 'log_evaluation()' callback via 'callbacks' argument instead.

Trial 75 pruned. Trial was pruned at iteration 0.
Trial 76 pruned. Trial was pruned at iteration 0.
Trial 77 pruned. Trial was pruned at iteration 1.
Trial 78 pruned. Trial was pruned at iteration 0.
Trial 79 pruned. Trial was pruned at iteration 0.
Trial 80 pruned. Trial was pruned at iteration 0.
Trial 81 pruned. Trial was pruned at iteration 0.
Trial 82 pruned. Trial was pruned at iteration 0.
Trial 83 pruned. Trial was pruned at iteration 5.
Trial 84 pruned. Trial was pruned at iteration 0.
Trial 85 pruned. Trial was pruned at iteration 0.
Trial 86 pruned. Trial was pruned at iteration 0.
Trial 87 pruned. Trial was pruned at iteration 0.
Trial 88 pruned. Trial was pruned at iteration 0.
Trial 89 pruned. Trial was pruned at iteration 0.
Trial 90 pruned. Trial was pruned at iteration 0.
Trial 91 pruned. Trial was pruned at iteration 0.
Trial 92 pruned. Trial was pruned at iteration 0.
Trial 93 pruned. Trial was pruned at iteration 3.
Trial 94 pruned. Trial was pruned at iteration 0.
Trial 95 pruned. Trial was pruned at iteration 0.
Trial 96 pruned. Trial was pruned at iteration 1.
Trial 97 pruned. Trial was pruned at iteration 0.
Trial 98 pruned. Trial was pruned at iteration 3.
Trial 99 pruned. Trial was pruned at iteration 0.
Trial 100 pruned. Trial was pruned at iteration 4.
Trial 101 pruned. Trial was pruned at iteration 0.

Total running time of the script: ( 0 minutes 6.277 seconds)

Gallery generated by Sphinx-Gallery

Ask-and-Tell 接口

Optuna 带有 Ask-and-Tell 接口, 它为超参数优化提供了一个更灵活的接口.本教程将展示三种可以用到ask-and-tell接口的情况.

只进行最小改动, 就可将 Optuna 应用到一个现存的优化问题上

考虑一个传统的有监督分类问题; 你想最大化验证准确率. 所以, 你训练了一个 LogisticRegression 作为简单模型.

import numpy as np
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

import optuna


X, y = make_classification(n_features=10)
X_train, X_test, y_train, y_test = train_test_split(X, y)

C = 0.01
clf = LogisticRegression(C=C)
clf.fit(X_train, y_train)
val_accuracy = clf.score(X_test, y_test)  # the objective

然后你试图通过 Optuna 来优化超参数 Csolver.当你简单地引入 Optuna 后, 你定义了一个 objective 函数, 它接受 trial 并且调用 trialsuggest_* 方法 来采样超参数:

def objective(trial):
    X, y = make_classification(n_features=10)
    X_train, X_test, y_train, y_test = train_test_split(X, y)

    C = trial.suggest_loguniform("C", 1e-7, 10.0)
    solver = trial.suggest_categorical("solver", ("lbfgs", "saga"))

    clf = LogisticRegression(C=C, solver=solver)
    clf.fit(X_train, y_train)
    val_accuracy = clf.score(X_test, y_test)

    return val_accuracy


study = optuna.create_study(direction="maximize")
study.optimize(objective, n_trials=10)

这个接口并不灵活. 比如, 如果 objective 需要不同于 trial 的额外参数, 你就需要顶一个类, 就像 How to define objective functions that have own arguments? 里做的那样. 而 ask-and-tell 接口提供了更加灵活的语法来优化超参数. 下面的例子等同于上面的代码块.

study = optuna.create_study(direction="maximize")

n_trials = 10
for _ in range(n_trials):
    trial = study.ask()  # `trial` is a `Trial` and not a `FrozenTrial`.

    C = trial.suggest_loguniform("C", 1e-7, 10.0)
    solver = trial.suggest_categorical("solver", ("lbfgs", "saga"))

    clf = LogisticRegression(C=C, solver=solver)
    clf.fit(X_train, y_train)
    val_accuracy = clf.score(X_test, y_test)

    study.tell(trial, val_accuracy)  # tell the pair of trial and objective value

Out:

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/sklearn/linear_model/_sag.py:354: ConvergenceWarning:

The max_iter was reached which means the coef_ did not converge

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/sklearn/linear_model/_sag.py:354: ConvergenceWarning:

The max_iter was reached which means the coef_ did not converge

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/sklearn/linear_model/_sag.py:354: ConvergenceWarning:

The max_iter was reached which means the coef_ did not converge

主要区别是这里用了两个方法 optuna.study.Study.ask()optuna.study.Study.tell(). optuna.study.Study.ask() 创建了一个 trial, 它可以采样超参数, 而 optuna.study.Study.tell() 通过传递 trial 和一个目标函数值完成这个 trial. 你可以在没有 objective 函数的情况下对你的原始代码应用 Optuna 的超参数优化.

如果你想用 pruner 让你的优化变得更快, 你需要显式地将 trial 的状态传入到 optuna.study.Study.tell() 的参数中, 就像下面这样:

import numpy as np
from sklearn.datasets import load_iris
from sklearn.linear_model import SGDClassifier
from sklearn.model_selection import train_test_split

import optuna


X, y = load_iris(return_X_y=True)
X_train, X_valid, y_train, y_valid = train_test_split(X, y)
classes = np.unique(y)
n_train_iter = 100

# define study with hyperband pruner.
study = optuna.create_study(
    direction="maximize",
    pruner=optuna.pruners.HyperbandPruner(
        min_resource=1, max_resource=n_train_iter, reduction_factor=3
    ),
)

for _ in range(20):
    trial = study.ask()

    alpha = trial.suggest_uniform("alpha", 0.0, 1.0)

    clf = SGDClassifier(alpha=alpha)
    pruned_trial = False

    for step in range(n_train_iter):
        clf.partial_fit(X_train, y_train, classes=classes)

        intermediate_value = clf.score(X_valid, y_valid)
        trial.report(intermediate_value, step)

        if trial.should_prune():
            pruned_trial = True
            break

if pruned_trial:
    study.tell(trial, state=optuna.trial.TrialState.PRUNED)  # tell the pruned state
else:
    score = clf.score(X_valid, y_valid)
    study.tell(trial, score)  # tell objective value

备注

optuna.study.Study.tell() 可以接受一个 trial number 而不是 trial 对象本身. study.tell(trial.number, y) 等价于 study.tell(trial, y).

定义-运行 (Define-and-Run)

ask-and-tell 接口同时支持 define-by-rundefine-and-run API. 在上面 define-by-run的例子之外, 本部分展示 define-and-run 的 API.

在调用 optuna.study.Study.ask() 方法之前为 define-and-run API 定义超参数分布. 例如,

distributions = {
    "C": optuna.distributions.LogUniformDistribution(1e-7, 10.0),
    "solver": optuna.distributions.CategoricalDistribution(("lbfgs", "saga")),
}

在每次调用时, 将 distributions 传递给 optuna.study.Study.ask() 方法.返回的 trial 里将包含建议的超参数.

study = optuna.create_study(direction="maximize")
n_trials = 10
for _ in range(n_trials):
    trial = study.ask(distributions)  # pass the pre-defined distributions.

    # two hyperparameters are already sampled from the pre-defined distributions
    C = trial.params["C"]
    solver = trial.params["solver"]

    clf = LogisticRegression(C=C, solver=solver)
    clf.fit(X_train, y_train)
    val_accuracy = clf.score(X_test, y_test)

    study.tell(trial, val_accuracy)

Out:

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/sklearn/linear_model/_sag.py:354: ConvergenceWarning:

The max_iter was reached which means the coef_ did not converge

/home/docs/checkouts/readthedocs.org/user_builds/optuna-zh-cn/envs/latest/lib/python3.7/site-packages/sklearn/linear_model/_sag.py:354: ConvergenceWarning:

The max_iter was reached which means the coef_ did not converge
批优化

为了更快地完成优化, ask-and-tell 接口使我们可以优化一个批目标函数. 比如, 并行求值, 向量操作等.

下面这个目标函数接受批超参数 xs 而不是一个单独的超参数, 并对整个向量计算目标函数.

def batched_objective(xs: np.ndarray):
    return xs ** 2 + 1

在下面的例子中, 一批中包含的超参数个数是 \(10\), 而 batched_objective 进行了三次求值.因此, trial的个数是 \(30\). 注意, 在 批求值以后, 你需要存储 trial_ids 或者 trial 才能调用 optuna.study.Study.tell() 方法

batch_size = 10
study = optuna.create_study()

for _ in range(3):

    # create batch
    trial_ids = []
    samples = []
    for _ in range(batch_size):
        trial = study.ask()
        trial_ids.append(trial.number)
        x = trial.suggest_int("x", -10, 10)
        samples.append(x)

    # evaluate batched objective
    samples = np.array(samples)
    objectives = batched_objective(samples)

    # finish all trials in the batch
    for trial_id, objective in zip(trial_ids, objectives):
        study.tell(trial_id, objective)

Total running time of the script: ( 0 minutes 0.162 seconds)

Gallery generated by Sphinx-Gallery

重新使用最佳值

有时候, 在完成超参数优化以后, 你可能要用最佳的超参数对目标函数重新求值.

比如,

  • 你已经用 Optuna 发现了好的超参数, 并且想用已经发现的最佳超参数来运行一个类似的 objective 函数以进一步分析结果, 或者

  • 为节省时间, 你已经用 Optuna 来优化了一个部分数据集. 在超参数调整以后, 你想在整个数据集上用你找到的最佳超参数来训练模型.

best_trial 提供了一个接口, 用当前的最佳超参数值对目标函数进行重新求值.

像上面的第一个例子一样, 本教程展示了利用当前最佳值来重新运行一个不同的 objective 函数的例子.

进一步研究最佳模型

考虑一个如下的采用了 Optuna 的经典有监督分类问题

from sklearn import metrics
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split


import optuna


def objective(trial):
    X, y = make_classification(n_features=10, random_state=1)
    X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1)

    C = trial.suggest_loguniform("C", 1e-7, 10.0)

    clf = LogisticRegression(C=C)
    clf.fit(X_train, y_train)

    return clf.score(X_test, y_test)


study = optuna.create_study(direction="maximize")
study.optimize(objective, n_trials=10)

print(study.best_trial.value)  # Show the best value.

Out:

0.92

假设在超参数优化之后, 你想计算出同一个数据集上的其他的测度, 比如召回率, 精确度和 f1-score. 你可以定义另一个目标函数, 令其和 objective 高度相似, 以用最佳超参数来重现模型.

def detailed_objective(trial):
    # Use same code objective to reproduce the best model
    X, y = make_classification(n_features=10, random_state=1)
    X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1)

    C = trial.suggest_loguniform("C", 1e-7, 10.0)

    clf = LogisticRegression(C=C)
    clf.fit(X_train, y_train)

    # calculate more evaluation metrics
    pred = clf.predict(X_test)

    acc = metrics.accuracy_score(pred, y_test)
    recall = metrics.recall_score(pred, y_test)
    precision = metrics.precision_score(pred, y_test)
    f1 = metrics.f1_score(pred, y_test)

    return acc, f1, recall, precision

study.best_trial 传递给 detailed_objective 做参数.

detailed_objective(study.best_trial)  # calculate acc, f1, recall, and precision

Out:

(0.92, 0.9285714285714286, 0.9285714285714286, 0.9285714285714286)
best_trial 和普通的 trials 的不同之处

这里使用了 best_trial, 它返回一个 best_trial, 属于 FrozenTrial 类型. FrozenTrial 与活跃的 trial 不同, 而且在某些情况下它的行为和 Trial 也不一样. 比如, 剪枝没法使用, 因为 should_prune 总是返回 False.

Total running time of the script: ( 0 minutes 0.062 seconds)

Gallery generated by Sphinx-Gallery

Gallery generated by Sphinx-Gallery

API Reference

optuna

在 optuna 被用在其他模块中时,optuna 模块主要作为 Optuna 的基础功能的别名来使用。目前有两个模块有别名:(1) optuna.study,包含和 Study 生命周期有关的函数,(2) optuna.exceptions,是当trial 被剪枝时抛出的TrialPruned异常。

optuna.create_study

创建新 Study.

optuna.load_study

加载指定名字的 Study

optuna.delete_study

删除 Study 对象。

optuna.get_all_study_summaries

获取指定存储内所有 study的历史记录。

optuna.TrialPruned

被剪枝的 trial 异常。

optuna.cli

cli 模块使用 cliff framework 框架实现了Optuna的命令行功能。

optuna
    [--version]
    [-v | -q]
    [--log-file LOG_FILE]
    [--debug]
    [--storage STORAGE]
--version

显示应用的版本号并退出

-v, --verbose

打印冗余内容。可重复。

-q, --quiet

禁止打印除了警告和错误之外的输出。

--log-file <LOG_FILE>

确定用于记录日志的文件名。该选项默认情况下是禁用的。

--debug

显示异常处理和堆栈跟踪信息。

--storage <STORAGE>

数据库 URL. (比如 sqlite:///example.db )

create-study

创建新的 study

optuna create-study
    [--study-name STUDY_NAME]
    [--direction {minimize,maximize}]
    [--skip-if-exists]
--study-name <STUDY_NAME>

设定具有可读性的 study 名,以方便和其他的 study 区分。

--direction <DIRECTION>

设定新 study 的优化方向。如果方向是最小化的话就设成 ‘minimize’, 如果是最大化就设置成 ‘maximize’.

--skip-if-exists

如果开启该选项, 当存在一个同名的 study 时,optuna 在优化开始时将跳过创建 study 的过程,并且不报错。

该命令由 optuna 插件提供。

dashboard

启动 web dashboard (beta).

This feature is deprecated since version 2.7.0. Please use optuna-dashboard instead.

optuna dashboard
    [--study STUDY]
    [--study-name STUDY_NAME]
    [--out OUT]
    [--allow-websocket-origin BOKEH_ALLOW_WEBSOCKET_ORIGINS]
--study <STUDY>

该参数已弃用。作为替代,请使用 –study-name.

--study-name <STUDY_NAME>

The name of the study to show on the dashboard.

--out <OUT>, -o <OUT>

HTML 文件的输出路径。如果该参数没有设定的话,Optuna 将会启动一个 HTTP server 并host 一个dashboard

--allow-websocket-origin <BOKEH_ALLOW_WEBSOCKET_ORIGINS>

允许从制定的 host 发起的的 websocket 请求。该选项用法和 bokeh 的 –allow-websocket-origin 一样。更多细节见 https://docs.bokeh.org/en/latest/docs/reference/command/subcommands/serve.html.设定具有可读性的 study 名,以方便和其他的 study 区分。

该命令由 optuna 插件提供。

delete-study

删除特定的 study。

optuna delete-study [--study-name STUDY_NAME]
--study-name <STUDY_NAME>

待删除的 study 名。

该命令由 optuna 插件提供。

storage upgrade

升级数据库架构。

optuna storage upgrade

该命令由 optuna 插件提供。

studies

显示 study 列表。

optuna studies
    [-f {csv,json,table,value,yaml}]
    [-c COLUMN]
    [--quote {all,minimal,none,nonnumeric}]
    [--noindent]
    [--max-width <integer>]
    [--fit-width]
    [--print-empty]
    [--sort-column SORT_COLUMN]
    [--sort-ascending | --sort-descending]
-f <FORMATTER>, --format <FORMATTER>

输出格式,默认情况下是表格。

-c COLUMN, --column COLUMN

用于设定要展示的(表格)列。可设置多个 column 参数以同时显示多个column.

--quote <QUOTE_MODE>

when to include quotes, defaults to nonnumeric

--noindent

设定是否在 json输出格式中禁用缩进。

--max-width <integer>

最大展示宽度。如果设置成小于 1 的数值,就代表禁用最大宽度限制。你也可以通过设置 CLIFF_MAX_TERM_WIDTH `` 环境变量来限制最大宽度,但是 ``--max-width 参数的优先级比环境变量高。

--fit-width

将输出表格设置成与显示屏同宽。如果 -max-width > 1 的话,该选项会自动启用。设定是否在 json 中禁用缩进。也可以通过设置 CLIFF_FIT_WIDTH 环境变量的值为 1 来启用该选项。

--print-empty

在无数据的情况下也输出空表格

--sort-column SORT_COLUMN

按照指定列来对表格的行进行排序。越靠前指定的列在排序中的优先级越高。没有被指定的列不参与排序。

--sort-ascending

sort the column(s) in ascending order

--sort-descending

sort the column(s) in descending order

该命令由 optuna 插件提供。

study optimize

针对特定 study 开始优化过程。自 2.0.0 版本后弃用。

optuna study optimize
    [--n-trials N_TRIALS]
    [--timeout TIMEOUT]
    [--n-jobs N_JOBS]
    [--study STUDY]
    [--study-name STUDY_NAME]
    file
    method
--n-trials <N_TRIALS>

优化过程将运行的总 trial 数目。如果不设定该数目的话,优化过程会一直持续下去。

--timeout <TIMEOUT>

到达指定时间(按秒计)以后终止 study. 如果不设定该数目的话,优化过程也会一直持续下去。

--n-jobs <N_JOBS>

并行运行的任务个数。如果该参数设置为 -1, 则任务的实际数目将等于 CPU 的核心数。

--study <STUDY>

该参数已弃用。作为替代,请使用 –study-name.

--study-name <STUDY_NAME>

被优化的 study 名。

file

用于指定定义了目标函数的 Python 脚本文件名。

method

目标函数的方法名。

该命令由 optuna 插件提供。

study set-user-attr

针对特定 study 设定用户属性 (user attribute).

optuna study set-user-attr
    [--study STUDY]
    [--study-name STUDY_NAME]
    --key KEY
    --value VALUE
--study <STUDY>

该参数已弃用。作为替代,请使用 –study-name.

--study-name <STUDY_NAME>

用于设置用户属性的 study 名。

--key <KEY>, -k <KEY>

设定用户属性中的键 (key).

--value <VALUE>, -v <VALUE>

设定用户属性中的值 (value).

该命令由 optuna 插件提供。

optuna.distributions

distributions 模块定义了代表各种概率分布的类,主要用于优化 trial 时进行初始超参数建议。这些分布类继承自库内部的 BaseDistribution, 并且以特定的参数被初始化,比如对 UniformDistribution 来说是 lowhigh 端点。

Optuna用户不应该直接使用分布类,而应该使用 Trial 提供的实用函数,如 suggest_int()

optuna.distributions.UniformDistribution

线性域的均匀分布。

optuna.distributions.LogUniformDistribution

Log 均匀分布。

optuna.distributions.DiscreteUniformDistribution

线性离散均匀分布。

optuna.distributions.IntUniformDistribution

整数上的均匀分布。

optuna.distributions.IntLogUniformDistribution

Log 操作后的整数均匀分布。

optuna.distributions.CategoricalDistribution

分类分布。

optuna.distributions.distribution_to_json

将分布序列化成json格式。

optuna.distributions.json_to_distribution

将 JSON 格式的分布反序列化。

optuna.distributions.check_distribution_compatibility

检查两分布的兼容性。

optuna.exceptions

exceptions 模块定义了Optuna特有的异常,它派生自一个基本的 OptunaError 类。对于库用户来说,特别重要的是 TrialPruned 异常。此异常在 optuna.trial.Trial.should_prune() 对一个应被剪枝的trial返回 True 时被抛出。

optuna.exceptions.OptunaError

Optuna 异常基类。

optuna.exceptions.TrialPruned

被剪枝 trial 异常。

optuna.exceptions.CLIUsageError

命令行界面异常。

optuna.exceptions.StorageInternalError

存储异常。

optuna.exceptions.DuplicatedStudyError

重复 study 名导致的异常。

optuna.importance

importance 模块提供了根据给定 study 中已完成的trials评估超参数重要性的功能。实用函数 get_param_importances() 将一个 Study 和可选的评价器作为它的两个输入。 评价器必须从 BaseImportanceEvaluator 派生,当没有传入时,默认初始化为 FanovaImportanceEvaluator。实现自定义评估器的用户应该参考 FanovaImportanceEvaluatorMeanDecreaseImpurityImportanceEvaluator 作为指导,请格外注意 Evaluator的 evaluate() 函数的返回值的格式。

optuna.importance.get_param_importances

在给定的 study 中根据已完成的 trial 来评估参数重要性。

optuna.importance.FanovaImportanceEvaluator

fANOVA 重要性求解器。

optuna.importance.MeanDecreaseImpurityImportanceEvaluator

Mean Decrease Impurity (MDI) 参数重要性求解器。

optuna.integration

integration 模块包含用于将Optuna与外部机器学习框架集成的类。

对于Optuna支持的大多数ML框架来说,相应的Optuna集成类的作用只是实现一个回调对象和函数,符合框架特定的回调API,在模型训练的每一个中间步骤中都会被调用。这些回调在不同ML框架中实现的功能包括:

  1. 使用 optuna.trial.Trial.should_prune() 向Optuna trial报告模型中间分数,

  2. 根据 optuna.trial.Trial.should_prune() 的结果,通过抛出 optuna.TrialPruned(),对当前模型进行剪枝,并且

  3. 将当前的 trial number 等中间Optuna数据汇报给框架,功能类似 MLflowCallback

针对scikit-learn,我们提供了一个集成的 OptunaSearchCV 估计器,它结合了scikit-learn BaseEstimator功能和对类级 Study 对象的访问。

AllenNLP

optuna.integration.AllenNLPExecutor

为了让 Jsonnet 配置文件在 Optuna 中可用的 AllenNLP 扩展。

optuna.integration.allennlp.dump_best_config

保存 study 中最佳 trial 的参数更新到 JSON 配置文件。

optuna.integration.AllenNLPPruningCallback

用于清除无望 trial 的 AllenNLP 回调函数。

BoTorch

optuna.integration.BoTorchSampler

采用 BoTorch 的采样器。BoTorch 是一个建立在 PyTorch 上的贝叶斯优化库。

optuna.integration.botorch.qei_candidates_func

Quasi MC-based batch Expected Improvement (qEI).

optuna.integration.botorch.qehvi_candidates_func

Quasi MC-based batch Expected Hypervolume Improvement (qEHVI).

optuna.integration.botorch.qparego_candidates_func

Quasi MC-based extended ParEGO (qParEGO) for constrained multi-objective optimization.

Catalyst

optuna.integration.CatalystPruningCallback

用于清除无望 trial 的 Catalyst 回调。

Chainer

optuna.integration.ChainerPruningExtension

用于清除无望 trial 的 Chainer 扩展。

optuna.integration.ChainerMNStudy

一个将 Optuna 并入 ChainerMN 的 Study wrapper.

fast.ai

optuna.integration.FastAIV1PruningCallback

用于清除 FastAI 中无望 trial 的回调函数。

optuna.integration.FastAIV2PruningCallback

用于清除 FastAI 中无望 trial 的回调函数。

optuna.integration.FastAIPruningCallback

:py:class:`optuna.integration.fastaiv2.FastAIV2PruningCallback`的别名

Keras

optuna.integration.KerasPruningCallback

用于清除无望 trial 的 Keras 回调函数。

LightGBM

optuna.integration.LightGBMPruningCallback

用于清除无望 trial 的 LightGBM 回调函数。

optuna.integration.lightgbm.train

用于超参数调参的 LightGBM training API 的 wrapper.

optuna.integration.lightgbm.LightGBMTuner

用于 LightGBM 的超参数调参器。

optuna.integration.lightgbm.LightGBMTunerCV

带有交叉验证的、用于 LightGBM 的超参数调参器。

MLflow

optuna.integration.MLflowCallback

用 MLflow 来追踪 Optuna trial 的回调函数。

MXNet

optuna.integration.MXNetPruningCallback

用于清除无望 trial 的 MXNet 回调函数。

pycma

optuna.integration.PyCmaSampler

使用 cma 库作为后端的采样器。

optuna.integration.CmaEsSampler

用于向后兼容的 PyCmaSampler wrapper 类。

PyTorch

optuna.integration.PyTorchIgnitePruningHandler

用于清除无望 trial 的 PyTorch Ignite handler。

optuna.integration.PyTorchLightningPruningCallback

用于清除无望 trial 的 PyTorch Lighting 回调函数。

optuna.integration.TorchDistributedTrial

A wrapper of Trial to incorporate Optuna with PyTorch distributed.

scikit-learn

optuna.integration.OptunaSearchCV

带有交叉验证的超参数搜索。

scikit-optimize

optuna.integration.SkoptSampler

以 Scikit-Optimize 为后端的 sampler。

skorch

optuna.integration.SkorchPruningCallback

用于清除无望 trial 的 Skorch 回调函数。

TensorFlow

optuna.integration.TensorBoardCallback

使用 TensorBoard 追踪 Optuna trial 的回调函数。

optuna.integration.TensorFlowPruningHook

用于清除无望 trial 的 TensorFlow SessionRunHook 钩子函数。

optuna.integration.TFKerasPruningCallback

用于清除无望 trial 的 tf.keras 回调函数。

XGBoost

optuna.integration.XGBoostPruningCallback

用于清除无望 trial 的 XGBoost 回调函数。

optuna.logging

logging 模块使用Python logging 包来实现日志记录。库用户可使用 set_verbosity() 设置verbosity级别为 optuna.logging.CRITICAL (又名 optuna.logging.FATAL)、 optuna.logging.ERRORoptuna.logging.WARNING (又名 optuna.logging.WARN)、 optuna.logging.INFOoptuna.logging.DEBUG

optuna.logging.get_verbosity

返回目前 Optuna 的 root logger 的 logging 层级。

optuna.logging.set_verbosity

设置 Optuna 的 root logger 的 logging 层级。

optuna.logging.disable_default_handler

禁用 Optuna 的 root logger 的默认句柄。

optuna.logging.enable_default_handler

启用 Optuna 的 root logger 的默认句柄。

optuna.logging.disable_propagation

禁用库 log 输出的传递。

optuna.logging.enable_propagation

启用库 log 输出的传递。

optuna.multi_objective

该模块已被弃用,其功能已经被迁移到 optuna.samplers, optuna.study, optuna.trialoptuna.visualization 中。

multi-objective sampler 类

optuna.multi_objective.samplers.BaseMultiObjectiveSampler

multi-objective sampler 基类。

optuna.multi_objective.samplers.NSGAIIMultiObjectiveSampler

使用 NSGA-II 算法的 Multi-objective sampler。

optuna.multi_objective.samplers.RandomMultiObjectiveSampler

Multi-objective sampler using random sampling.

optuna.multi_objective.samplers.MOTPEMultiObjectiveSampler

采用 MOTPE 算法的多目标sampler。

optuna.multi_objective.study

optuna.multi_objective.study.MultiObjectiveStudy

A study corresponds to a multi-objective optimization task, i.e., a set of trials.

optuna.multi_objective.study.create_study

Create a new MultiObjectiveStudy.

optuna.multi_objective.study.load_study

Load the existing MultiObjectiveStudy that has the specified name.

optuna.multi_objective.trial

optuna.multi_objective.trial.MultiObjectiveTrial

A trial is a process of evaluating an objective function.

optuna.multi_objective.trial.FrozenMultiObjectiveTrial

Status and results of a MultiObjectiveTrial.

optuna.multi_objective.visualization

备注

optuna.multi_objective.visualization module uses plotly to create figures, but JupyterLab cannot render them by default. Please follow this installation guide to show figures in JupyterLab.

optuna.multi_objective.visualization.plot_pareto_front

Plot the pareto front of a study.

optuna.pruners

pruners 模块定义了一个 BasePruner 类,其特点是有一个抽象的 prune() 方法。该方法对一个给定的trial及其相关的 study,返回一个代表该trial是否应该被剪枝的布尔值。该值由存储的目标函数的中间值确定,这些中间值由前述 optuna.trial.Trial.report() 传入。本模块中其余的类代表子类,继承自 BasePruner,它们实现了不同的剪枝策略。

optuna.pruners.BasePruner

Pruner 基类

optuna.pruners.MedianPruner

使用中值停止规则 的 pruner.

optuna.pruners.NopPruner

不剪枝 trial 的 pruner.

optuna.pruners.PercentilePruner

保留到指定百分位 trial 的 pruner.

optuna.pruners.SuccessiveHalvingPruner

使用异步连续减半算法的 pruner.

optuna.pruners.HyperbandPruner

使用 hyperband 的 pruner.

optuna.pruners.ThresholdPruner

用于检测 trial 的无关度量的 pruner.

optuna.samplers

samplers 模块定义了一个参数采样的基类,如 BaseSampler 中广泛描述的那样。本模块中其余的类代表从 BaseSampler 派生出来的子类,它们实现了不同的采样策略。

optuna.samplers.BaseSampler

Samplers 基类

optuna.samplers.GridSampler

使用网格搜索的 sampler.

optuna.samplers.RandomSampler

使用随机 sampling 的sampler.

optuna.samplers.TPESampler

使用 TPE (Tree-structured Parzen Estimator) 算法的 sampler.

optuna.samplers.CmaEsSampler

A sampler using cmaes as the backend.

optuna.samplers.PartialFixedSampler

部分参数固定的 sampler.

optuna.samplers.NSGAIISampler

使用 NSGA-II 算法的多目标 sampler.

optuna.samplers.MOTPESampler

使用 MOTPE 算法的多目标 sampler.

optuna.samplers.IntersectionSearchSpace

用于计算 BaseStudy 的搜索空间的交集的类。

optuna.samplers.intersection_search_space

返回 BaseStudy 的搜索空间交集。

optuna.storages

The storages module defines a BaseStorage class which abstracts a backend database and provides library-internal interfaces to the read/write histories of the studies and trials. Library users who wish to use storage solutions other than the default in-memory storage should use one of the child classes of BaseStorage documented below.

optuna.storages.RDBStorage

RDB 后端的存储类。

optuna.storages.RedisStorage

Redis 后端的存储类。

optuna.structs

该模块已被弃用,其以前的功能移至 optuna.trialoptuna.study

class optuna.structs.TrialState(value)[源代码]

Trial 的状态

RUNNING

运行中的 Trial.

COMPLETE

已完成且未触发错误的 Trial.

PRUNED

已经被 TrialPruned 剪枝的 Trial.

FAIL

由于未捕获的错误,该 Trial 已经失败。

1.4.0 版后已移除: 该类已废弃。请改用 TrialState.

class optuna.structs.StudyDirection(value)[源代码]

Study 的方向。

NOT_SET

方向未设置。

MINIMIZE

Study 将最小化目标函数

MAXIMIZE

Study 将最大化目标函数。

1.4.0 版后已移除: 该类已废弃,请改用 StudyDirection.

class optuna.structs.FrozenTrial(number, state, value, datetime_start, datetime_complete, params, distributions, user_attrs, system_attrs, intermediate_values, trial_id, *, values=None)[源代码]

警告

在 v1.4.0 中被弃用。该特性将在未来被移除。目前我们计划在 v3.0.0 中移除它,但也可能会改变。参见 https://github.com/optuna/optuna/releases/tag/v1.4.0.

该类已经迁移至 trial, 请改用 FrozenTrial.

参数
返回类型

None

property distributions: Dict[str, optuna.distributions.BaseDistribution]

包含了 params 分布的字典。

property duration: Optional[datetime.timedelta]

返回完成一个 trial 所消耗的时间。

返回

消耗的时间。

property last_step: Optional[int]

返回 trial 中 intermediate_values 的最大步数。

返回

intermediate_values 的最大步数。

report(value, step)[源代码]

report 函数接口。

由于 FrozenTrial 未被剪枝,该函数将不做任何处理。

参见

Please refer to should_prune().

参数
  • value (float) – 目标函数返回的值。

  • step (int) – Trial 的步骤(比如,神经网络训练中的 epoch 数)。注意,pruners 假定 step 从零开始计算。比如 MedianPruner 将仅检查 step 是否小于 n_warmup_steps 作为热身机制。

返回类型

None

should_prune()[源代码]

建议该 trial 是否应该被剪枝。

无论剪枝算法是什么,建议值永远是 False.

备注

FrozenTrial 只对参数组合进行一次采样。

返回

False.

返回类型

bool

class optuna.structs.StudySummary(study_name, direction, best_trial, user_attrs, system_attrs, n_trials, datetime_start, study_id, *, directions=None)[源代码]

警告

在 v1.4.0 中被弃用。该特性将在未来被移除。目前我们计划在 v3.0.0 中移除它,但也可能会改变。参见 https://github.com/optuna/optuna/releases/tag/v1.4.0.

该类已经迁移到 study, 请改用 StudySummary.

参数

optuna.study

study 模块实现了 Study 对象和相关函数。Study 类有一个公共构造函数,但不建议直接使用这个构造函数。相反,要创建或加载一个 Study 时, 库用户应该分别使用 create_studyload_study()

optuna.study.Study

一个 study 对应于一个优化任务,即一组 trials.

optuna.study.create_study

创建新的 Study.

optuna.study.load_study

加载一个所给名字的已经存在的 Study.

optuna.study.delete_study

删除一个 Study 对象。

optuna.study.get_all_study_summaries

返回指定存储中 所有 study 的历史记录。

optuna.study.StudyDirection

Study 的方向。

optuna.study.StudySummary

一个 Study 的基本属性和总汇结果。

optuna.trial

trial 模块包含与 Trial 相关的类和功能。

一个 Trial 实例代表了一个评估目标函数的过程。该实例被传递给目标函数来提供获取参数建议、管理trial状态、设置/获取用户定义的trial属性等接口,Optuna用户可以通过接口定义自定义目标函数。基本上,Optuna用户只在自己的自定义目标函数中使用它。

optuna.trial.Trial

一个 trial 是一个评估目标函数的过程。

optuna.trial.FixedTrial

一个始终为每个参数提供固定 suggestion 值的 trial 类。

optuna.trial.FrozenTrial

一个 Trial 的状态和结果。

optuna.trial.TrialState

Trial 的状态。

optuna.trial.create_trial

创建一个新的 FrozenTrial

optuna.visualization

The visualization module provides utility functions for plotting the optimization process using plotly and matplotlib. Plotting functions generally take a Study object and optional parameters are passed as a list to the params argument.

备注

optuna.visualization 模块中,以下函数使用 plotly 来绘制图像,但 JupyterLab 默认情况下不能渲染它们。请按照这个 installation guideJupyterLab 中显示图像。

optuna.visualization.plot_contour

将 study 中的参数关系绘制成等高线图。

optuna.visualization.plot_edf

绘制study的目标值EDF(经验分布函数)。

optuna.visualization.plot_intermediate_values

绘制一个 study 中所有 trial 的中间值。

optuna.visualization.plot_optimization_history

绘制一个 study 中所有 trial 的优化历史记录。

optuna.visualization.plot_parallel_coordinate

Plot the high-dimensional parameter relationships in a study.

optuna.visualization.plot_param_importances

绘制超参数重要性。

optuna.visualization.plot_pareto_front

绘制 study 的帕累托前沿面。

optuna.visualization.plot_slice

将study中的参数关系绘制成切片图。

optuna.visualization.is_available

返回是否可以使用 plotly 进行可视化。

备注

optuna.visualization.matplotlib 模块的后端是 Matplotlib。

optuna.visualization.matplotlib

备注

The following functions use Matplotlib as a backend.

optuna.visualization.matplotlib.plot_contour

使用 Matplotlib 将 study 中参数关系画成等高线图。

optuna.visualization.matplotlib.plot_edf

使用 Matplotlib 绘制 study 中目标函数值的 EDF (经验分布函数)。

optuna.visualization.matplotlib.plot_intermediate_values

使用 Matplotlib 绘制 study 中所有 trial 的中间值。

optuna.visualization.matplotlib.plot_optimization_history

使用 Matplotlib 绘制 study 中所有 trial 的优化历史。

optuna.visualization.matplotlib.plot_parallel_coordinate

Plot the high-dimensional parameter relationships in a study with Matplotlib.

optuna.visualization.matplotlib.plot_param_importances

使用 Matplotlib 绘制超参数重要性。

optuna.visualization.matplotlib.plot_slice

使用 Matplotlib 将 study 中的参数关系绘制成切片图。

optuna.visualization.matplotlib.is_available

返回 Matplotlib 可视化是否可用。

常见问题

某某库可以和 Optuna 配合使用吗?(某某是你常用的机器学习库)

Optuna 和绝大多数机器学习库兼容,并且很容易同他们配合使用。参见 examples.

如何定义带有额外参数的目标函数?

有两种方法可以实现这类函数。

首先,如下例所示,可调用的 objective 类具有这个功能:

import optuna


class Objective(object):
    def __init__(self, min_x, max_x):
        # Hold this implementation specific arguments as the fields of the class.
        self.min_x = min_x
        self.max_x = max_x

    def __call__(self, trial):
        # Calculate an objective value by using the extra arguments.
        x = trial.suggest_float("x", self.min_x, self.max_x)
        return (x - 2) ** 2


# Execute an optimization by using an `Objective` instance.
study = optuna.create_study()
study.optimize(Objective(-100, 100), n_trials=100)

其次,你可以用 lambda 或者 functools.partial 来创建带有额外参数的函数(闭包)。 下面是一个使用了 lambda 的例子:

import optuna

# Objective function that takes three arguments.
def objective(trial, min_x, max_x):
    x = trial.suggest_float("x", min_x, max_x)
    return (x - 2) ** 2


# Extra arguments.
min_x = -100
max_x = 100

# Execute an optimization by using the above objective function wrapped by `lambda`.
study = optuna.create_study()
study.optimize(lambda trial: objective(trial, min_x, max_x), n_trials=100)

Please also refer to sklearn_addtitional_args.py example, which reuses the dataset instead of loading it in each trial execution.

没有远程 RDB 的情况下可以使用 Optuna 吗?

可以。

在最简单的情况下,Optuna 使用内存 (in-memory) 存储:

study = optuna.create_study()
study.optimize(objective)

如果想保存和恢复 study 的话,你可以轻松地将 SQLite 用作本地存储。

study = optuna.create_study(study_name="foo_study", storage="sqlite:///example.db")
study.optimize(objective)  # The state of `study` will be persisted to the local SQLite file.

更多细节请参考 用 RDB 后端保存/恢复 Study .

如何保存和恢复 study?

有两种方法可以将 study 持久化。具体采用哪种取决于你是使用内存存储 (in-memory) 还是远程数据库存储 (RDB). 通过 pickle 或者 joblib, 采用了内存存储的 study 可以和普通的 Python 对象一样被存储和加载。比如用 joblib 的话:

study = optuna.create_study()
joblib.dump(study, "study.pkl")

恢复 study:

study = joblib.load("study.pkl")
print("Best trial until now:")
print(" Value: ", study.best_trial.value)
print(" Params: ")
for key, value in study.best_trial.params.items():
    print(f"    {key}: {value}")

如果你用的是 RDB, 具体细节请参考 用 RDB 后端保存/恢复 Study.

如何禁用 Optuna 的日志信息?

默认情况下,Optuna 打印处于 optuna.logging.INFO 层级的日志信息。通过设置 optuna.logging.set_verbosity(), 你可以改变这个层级。

比如,下面的代码可以终止打印每一个trial的结果:

optuna.logging.set_verbosity(optuna.logging.WARNING)

study = optuna.create_study()
study.optimize(objective)
# Logs like '[I 2020-07-21 13:41:45,627] Trial 0 finished with value:...' are disabled.

更多的细节请参考 optuna.logging.

如何在目标函数中保存训练好的机器学习模型?

Optuna 会保存超参数和对应的目标函数值,但是它不会存储诸如机器学习模型或者网络权重这样的中间数据。要保存模型或者权重的话,请利用你正在使用的机器学习库提供的对应功能。

在保存模型的时候,我们推荐将 optuna.trial.Trial.number 一同存储。这样易于之后确认对应的 trial.比如,你可以用以下方式在目标函数中保存训练好的 SVM 模型:

def objective(trial):
    svc_c = trial.suggest_float("svc_c", 1e-10, 1e10, log=True)
    clf = sklearn.svm.SVC(C=svc_c)
    clf.fit(X_train, y_train)

    # Save a trained model to a file.
    with open("{}.pickle".format(trial.number), "wb") as fout:
        pickle.dump(clf, fout)
    return 1.0 - accuracy_score(y_valid, clf.predict(X_valid))


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

# Load the best model.
with open("{}.pickle".format(study.best_trial.number), "rb") as fin:
    best_clf = pickle.load(fin)
print(accuracy_score(y_valid, best_clf.predict(X_valid)))

如何获得可复现的优化结果?

要让 Optuna 生成的参数可复现的话,你可以通过设置 RandomSampler 或者 TPESampler 中的参数 seed 来指定一个固定的随机数种子:

sampler = TPESampler(seed=10)  # Make the sampler behave in a deterministic way.
study = optuna.create_study(sampler=sampler)
study.optimize(objective)

但是这么做的需要注意以下两点。

首先,如果一个 study 的优化过程本身是分布式的或者并行的,那么这个过程中存在着固有的不确定性。因此,在这种情况下我们很难复现出同样的结果。如果你想复现结果的话,我们建议用顺序执行的方式来优化你的 study.

其次,如果你的目标函数的行为本身就是不确定的(也就是说,即使送入同样的参数,其返回值也不是唯一的),那么你就无法复现这个优化过程。要解决这个问题的话,请设置一个选项(比如随机数种子)来让你的优化目标的行为变成确定性的,前提是你用的机器学习库支持这一功能。

Trial 是如何处理抛出异常的?

那些抛出异常却没有对应的捕获机制的 trial 会被视作失败的 trial, 也就是处于 FAIL 状态的 trial.

在默认情况下,除了目标函数中抛出的 TrialPruned, 其他所有异常都会被传回给调用函数 optimize().换句话说,当此类异常被抛出时,对应的 study 就会被终止。但有时候我们希望能用剩余的 trial 将该 study 继续下去。要这么做的话,你得通过 optimize() 函数中的 catch 参数来指定要捕获的异常类型。这样,此类异常就会在 study 内部被捕获,而不会继续向外层传递。

你可以在日志信息里找到失败的 trial.

[W 2018-12-07 16:38:36,889] Setting status of trial#0 as TrialState.FAIL because of \
the following error: ValueError('A sample error in objective.')

你也可以通过查看 trial 的状态来找到它们:

study.trials_dataframe()

number

state

value

params

system_attrs

0

TrialState.FAIL

0

Setting status of trial#0 as TrialState.FAIL because of the following error: ValueError(‘A test error in objective.’)

1

TrialState.COMPLETE

1269

1

参见

The catch argument in optimize().

Trial 返回的 NaN 是如何处理的?

返回 NaN 的 trial 被视为失败的 trial, 但是它们并不会导致 study 被终止。

这些返回 NaN 的 trial 在日志里长这样:

[W 2018-12-07 16:41:59,000] Setting status of trial#2 as TrialState.FAIL because the \
objective function returned nan.

动态地改变搜索空间会导致怎样的结果?

Since parameters search spaces are specified in each call to the suggestion API, e.g. suggest_float() and suggest_int(), it is possible to, in a single study, alter the range by sampling parameters from different search spaces in different trials. The behavior when altered is defined by each sampler individually.

备注

关于 TPE sampler 的 讨论:https://github.com/optuna/optuna/issues/822

如何在两块 GPU 上同时跑两个 trial?

如果你的优化目标支持 GPU (CUDA) 加速,你又想指定优化所用的 GPU 的话,设置 CUDA_VISIBLE_DEVICES 环境变量可能是实现这一目标最轻松的方式了:

# On a terminal.
#
# Specify to use the first GPU, and run an optimization.
$ export CUDA_VISIBLE_DEVICES=0
$ optuna study optimize foo.py objective --study-name foo --storage sqlite:///example.db

# On another terminal.
#
# Specify to use the second GPU, and run another optimization.
$ export CUDA_VISIBLE_DEVICES=1
$ optuna study optimize bar.py objective --study-name bar --storage sqlite:///example.db

更多细节见 CUDA C Programming Guide.

如何对目标函数进行测试?

在对目标函数的测试中,我们总倾向于使用固定的,而不是随机采样的参数。这时,你可以选择用 FixedTrial 作为目标函数的输入参数。它会从一个给定的参数字典中送入固定的参数值。比如,针对函数 \(x + y\), 你可以用如下方式送入两个任意的 \(x\)\(y\):

def objective(trial):
    x = trial.suggest_float("x", -1.0, 1.0)
    y = trial.suggest_int("y", -5, 5)
    return x + y


objective(FixedTrial({"x": 1.0, "y": -1}))  # 0.0
objective(FixedTrial({"x": -1.0, "y": -4}))  # -5.0

如果使用 FixedTrial 的话,你也可以用如下方式写单元测试:

# A test function of pytest
def test_objective():
    assert 1.0 == objective(FixedTrial({"x": 1.0, "y": 0}))
    assert -1.0 == objective(FixedTrial({"x": 0.0, "y": -1}))
    assert 0.0 == objective(FixedTrial({"x": -1.0, "y": 1}))

在优化时, 我该如何避免耗尽内存 (running out of memory, OOM)?

如果内存使用量随着你运行更多的 trial 而增长,请尝试定期运行垃圾回收器。可在调用 optimize() 时指定 gc_after_trialTrue 或在回调函数中调用 gc.collect().

def objective(trial):
    x = trial.suggest_float("x", -1.0, 1.0)
    y = trial.suggest_int("y", -5, 5)
    return x + y


study = optuna.create_study()
study.optimize(objective, n_trials=10, gc_after_trial=True)

# `gc_after_trial=True` is more or less identical to the following.
study.optimize(objective, n_trials=10, callbacks=[lambda study, trial: gc.collect()])

运行垃圾回收器是有性能损失的,取决于你的目标函数运行的速度,这种损失可能是不能忽略的。因此,gc_after_trial 在默认情况下是 False. 注意,上面这个例子类似于在目标函数内运行垃圾回收器,不同之处在于 gc.collect() 哪怕在出现包括 TrialPruned 这样的错误时也会被调用。

备注

ChainerMNStudy 目前并不提供 gc_after_trial 和用于 optimize() 的回调接口。在使用该类时,你只能在目标函数内部调用垃圾回收器。

Indices and tables