17
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Optuna における最適化の流れを追ってみた

Last updated at Posted at 2019-04-12

概要

Optuna の使い方,またその最適化について実装上の流れを確認していく.
ただし,TPE のアルゴリズムの実装について触れるわけではないので注意!

また,ここでは scikt-learn に対して用いたコードを基に見ていく.

Example

以下にscikit-learm を用いた例(引用元)を示す.


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


# FYI: Objective functions can take additional arguments
# (https://optuna.readthedocs.io/en/stable/faq.html#objective-func-additional-args).
def objective(trial):
    iris = sklearn.datasets.load_iris()
    x, y = iris.data, iris.target

    classifier_name = trial.suggest_categorical('classifier', ['SVC', 'RandomForest'])
    if classifier_name == 'SVC':
        svc_c = trial.suggest_loguniform('svc_c', 1e-10, 1e10)
        classifier_obj = sklearn.svm.SVC(C=svc_c)
    else:
        rf_max_depth = int(trial.suggest_loguniform('rf_max_depth', 2, 32))
        classifier_obj = sklearn.ensemble.RandomForestClassifier(max_depth=rf_max_depth)

    score = sklearn.model_selection.cross_val_score(classifier_obj, x, y, n_jobs=-1, cv=3)
    accuracy = score.mean()
    return 1.0 - accuracy


if __name__ == '__main__':
    import optuna
    study = optuna.create_study()
    study.optimize(objective, n_trials=100)
    print(study.best_trial)

main 部について

とりあえず main をざっと見てみる.
まず,

optuna.create_study()

で最適化の大枠を担う Studyクラスのインスタンスを作る.

見ての通り,その次の工程として

study.optimize(objective, n_trials=100)

で最適化が完了する.とても簡単でシンプルである.

objective 関数

次に,目的関数の設定方法について見ていく.
ここで objective 関数が守るべき性質として, 必ず trial を引数にとり,最適化対象の値を返す.ただし, trialTraial クラスのインスタンスである.

また,Traial クラスは suggest_* 系の関数を持っていて,この関数を通して最適なハイパラの推定をしている.
このことからわかるように,objective 関数は所謂目的関数の役割だけでなく,問題設定および推定フェーズも含む物となっている.

ハイパラの設定方法

次に,最適化対象のハイパラの設定方法について細かく見ていく.
例えば,対数一様分布を用いて, svcC を最適化する場合は,以下のように,trial を用いることで,

svc_c = trial.suggest_loguniform('svc_c', 1e-10, 1e10)

となる.
次に,suggest_loguniformfloat を返しているためsvc_c という変数は単純に以下のように SVC に渡せる.

sklearn.svm.SVC(C=svc_c) 

また,以下を例に各引数について見てみると,

trial.suggest_loguniform(name, low, high)
  • name: 最適化対象の変数管理のためのタグ
  • low: 最適化対象の分布の下界
  • high: 最適化対象の分布の上界
    となっている.

NOTE
ただし,suggest_loguniform は設定した下界と上界の範囲内で設定した分布に従いサンプリングしているのではなく,この範囲内で分布に従いつつ,かつ最適であると推定した値を返していることに注意する.(suggest という名前の所以はそこなんだろう.)

最適化のターゲット設定方法

objective 関数内でメトリック等を用いて計算した得た値等を return するだけ!

最適化の流れ

もう少し詳しく実装面での最適化の流れについて確認する.
理論については,以下を参照.

以降でも以下の関数を参考に見ていく.

trial.suggest_loguniform(name, low, high)

ちなみに 解説ページ#smbo を参考にすると,この関数は

 x^{*} \leftarrow \textrm{argmin}_{x}S(x, M_{t-1})

の部分を担っていることがわかる.

関数の一つ深いところへ...

この関数の中身は docs 部分を省くと以下のようになっている.

    def suggest_loguniform(self, name, low, high):
        # type: (str, float, float) -> float
        ...
        return self._suggest(name, distributions.LogUniformDistribution(low=low, high=high))

ここで,

distributions.LogUniformDistribution(low=low, high=high)

とあるが,これはどの分布であるかを管理するだけで,本体は実質ただの NamedTuple である.
また,これは BaseDistribution クラスも継承しているが,その docs の中に,

"""Base class for distributions.

Note that distribution classes are not supposed to be called by library users.
They are used by :class:`~optuna.trial.Trial` and :class:`~optuna.samplers` internally.
"""

とあるので,分布をまさに分布として使うものではないっぽい.(このあたりは chainer が最近分布の実装がされてきているので,いつか統合されるかも?)

もう一つ深く...

さっきの関数ではなにも分からなかったので,もう少し深く進み _suggest 関数を見てみる.

def _suggest(self, name, distribution):
    # type: (str, distributions.BaseDistribution) -> Any

    param_value_in_internal_repr = self.study.sampler.sample(self.storage, self.study_id, name,
    distribution)
    ...
    return param_value

ここで初めて sample というワードが出てくることに注目する.
どうやら,study インスタンスは sampler と呼ばれる分布からサンプルするためのインスタンス変数を持っている事がわかる.
この sampler はデフォルトで TPESampler というものを使っており,名前からも予想がつくが TPE のアルゴリズムを実行するものである.
このタイミング(もとを辿れば objective 関数のなかでのパラメータの suggest 部分)で推定可能にしているキモの内容としては,関数の引数に対象とする分布だけでなく,strage とされるこれまでの推定ログが渡されていることにある.
これにより,これまでの結果に基づき,与えられた分布から最適だと思われるパラメータをサンプリングすることが可能になっている.

まとめ

Optuna の最適化におけるコアの部分は TPESampler が担っており,これが study を介して strage にアクセスすることで推定を可能にしていた.
objective 関数の役割は, Define-and-Run であると言われるだけあって,まさに雛形作りをしているというのが改めてわかった.

17
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?