Optuna
ハイパーパラメータ最適化フレームワーク
Preferred Networks
太田 健 (Takeru Ohta)
2019 年 9 月 27 日
PyData.Tokyo Meetup #21
● ハイパーパラメータ最適化フレームワーク
○ 2018/12にv0.4.0を公開 (現在はv0.16.0)
○ https://github.com/pfnet/optuna
● 特徴:
○ Define-by-Run
○ 枝刈り
○ 分散最適化
2
● 社内外のいろいろなプロジェクトで活用
● パブリックな事例だと、
○ Kaggle: Open Images Challenge 2018 (二位)
■ Object Detection Track
■ 物体の重複検出抑制パラメータ(NMW Threshold)を最適化
○ KDD Cup 2019: AutoML Track (五位)
■ LightGBMのハイパーパラメータを最適化
■ https://research.preferred.jp/2019/09/kddcup2019automl/
3
目次
• ハイパーパラメータ最適化
• Optuna入門
• 発展的な使い方
• 新機能: LightGBMTuner
4
ハイパーパラメータ最適化
ハイパーパラメータとは?
6
● 機械が自動で学習: パラメータ
○ 訓練データから学習
● 人手で設定が必要: ハイパーパラメータ
From: https://lightgbm.readthedocs.io/en/latest/Features.html
訓練データ
ハイパーパラメータを指定
num_leaves, max_depth, ...
パラメータ(具体的な木の形状)を学習
LightGBMモデル
ハイパーパラメータの重要性
物体検出モデルの例:
• どちらも同じ学習済みモデルを使用
• 推論時のハイパーパラメータだけが異なる
7
×不適切なハイパーパラメータ 〇適切なハイパーパラメータ
画像は PASCAL VOC2007データセット より引用
ハイパーパラメータ探索(手動)
まずは、このハイ
パーパラメータを試
そう:
learning_rate: 0.1
num_leaves: 30
… 学習完了!
AUC: 0.6
Trial 1
次は、これでどうだろ
う?
learning_rate: 0.01
num_leaves: 20
… 学習完了!
AUC: 0.5
Trial 2
なるほど、これくらい
が良いかな?
learning_rate: 0.05
num_leaves: 50
… 学習完了!
AUC: 0.8
Trial 3
8
LightGBM LightGBM LightGBM
ハイパーパラメータ探索(手動)
まずは、このハイ
パーパラメータを試
そう:
learning_rate: 0.1
num_leaves: 30
… 学習完了!
AUC: 0.6
Trial 1
次は、これでどうだろ
う?
learning_rate: 0.01
num_leaves: 20
… 学習完了!
AUC: 0.5
Trial 2
なるほど、これくらい
が良いかな?
learning_rate: 0.05
num_leaves: 50
… 学習完了!
AUC: 0.8
Trial 3
9
LightGBM LightGBM LightGBM
● 時間が掛かり面倒
● 職人芸
ハイパーパラメータ探索(自動)
まずは、このハイ
パーパラメータを試
そう:
learning_rate: 0.1
num_leaves: 30
… 学習完了!
AUC: 0.6
Trial 1
次は、これでどうだろ
う?
learning_rate: 0.01
num_leaves: 20
… 学習完了!
AUC: 0.5
Trial 2
なるほど、これくらい
が良いかな?
learning_rate: 0.05
num_leaves: 50
… 学習完了!
AUC: 0.8
Trial 3
10
LightGBM LightGBM LightGBM
Optuna入門
インストール
12
• Python 2.7, Python3.5+をサポート
• pipでインストール可能
$ pip install optuna
コードの基本的な構成
13
import optuna
def objective(trial):
return evaluation_score
study = optuna.create_study()
study.optimize(objective, n_trials= )
最適化対象のコード
トライアル数
目的関数の
定義
最適化実行
簡単な例: 3x4
- 2x3
- 4x2
+ 2 の最小化
14
3x4
- 2x3
- 4x2
+ 2 の最小化
15
3x4
- 2x3
- 4x2
+ 2 の最小化
16
trialオブジェクトを引数に取る関数を定義
3x4
- 2x3
- 4x2
+ 2 の最小化
17
次に評価するハイパーパラメータの値を取得
(Suggest API)
3x4
- 2x3
- 4x2
+ 2 の最小化
18
ハイパーパラメータの評価スコアを返す
3x4
- 2x3
- 4x2
+ 2 の最小化
19
最適化処理を管理するstudyオブジェクトを生成
3x4
- 2x3
- 4x2
+ 2 の最小化
20
目的関数と試行回数を指定して、最適化を実行
実行
21
ハイパーパラメータの値を変えつつ、目
的関数を繰り返し評価して、
最適値(最小値)を探索
ベストトライアル
22
デフォルトの最適化アルゴリズム
23
● TPE: Tree-structured Parzen Estimator
○ ベイズ最適化の一種
○ 過去の評価履歴から、次に探索すべき点を推測
探索地点が最適解付近に収
束
LightGBMの例: 二値分類のaccuracy最大化
24
最適化対応前のコード
LightGBMの例: 二値分類のaccuracy最大化
25
最適化対応前のコード
ハイパーパラメータは決め打ち
最適化対応
26
最適化対応
27
最適化対応
28
最適化対応
29
特徴: Define-by-Run
30
Define-by-Run: 探索空間を目的関数の実行時に定義
特徴: 枝刈り (Pruning)
• 見込みの薄いトライアルを自動で中断
• LightGBMPruningCallbackを追加するだけで利用可能
31
lgb.cv()にも対応済み!
Iterations
特徴: 分散最適化
• 複数のトライアルを並列・分散して実行可能
• study_nameとstorageを指定し、複数プロセスから実行
32
完成コード
33
探索空間定義 (define-by-run)
枝刈り対応
分散最適化対応
入門用ドキュメント
● 公式ドキュメント
○ チュートリアル: https://optuna.readthedocs.io/en/stable/tutorial
○ FAQ: https://optuna.readthedocs.io/en/stable/faq.html
● Colabハンズオン
○ 英語: https://bit.ly/optuna-quick-start
○ 日本語: https://bit.ly/optuna-quick-start-ja
● Examples
○ https://github.com/pfnet/optuna/tree/master/examples
34
発展的な使い方
Optunaの構成要素
36
Optunaの構成要素
37
ここまでの話ここからの話
Optunaの構成要素: Storage
38
Optunaの構成要素: Storage
39
● Study/Trialの情報の格納先
● 分散実行時はストレージを通して情報共有
● 用途に応じて切り替え可能
ストレージの種類
● InMemoryStorage
○ デフォルトで使用されるメモリ上のストレージ
○ optuna.create_study()
● RDBStorage
○ RDBをバックエンドにしたストレージ
○ SQLAlchemyがサポートするRDBが使用可能
■ E.g., MySQL, PostgreSQL, SQLite
■ SQLiteはファイルベースのRDB (標準ライブラリに組み込み)
○ optuna.create_study(storage=RDB_URL)
40
ストレージの使い分け
● とりあえずOptunaを動かしてみたい
⇒ InMemoryStorage
● 分散最適化!
⇒ SQLite以外のRDBStorage (e.g., MySQL, PostgreSQL)
※ SQLiteは、NFSと相性が悪く、スケールもしないので注意
● Studyを中断・再開したり、後から結果を分析したい
⇒ RDBStorage
「とりあえずSQLiteに保存」という習慣をつけておくと便利
41
RDBストレージの活用: Studyの中断・再開
42
RDBストレージの活用: Pandasで分析
43
RDBストレージの活用: Visualization
44
Visualization PRs:
● learning_curve: #511
● optimization_history: #513
● parallel_coordinate: #531
● contour_plot: #539
● slice_plot: #540
Optunaの構成要素: Sampler
45
Optunaの構成要素: Sampler
46
● 次の探索パラメータを決定
● ストレージから過去の探索履歴を取得
● A.k.a. Black-box最適化アルゴリズム
利用可能なSampler
● TPESampler (TPE)
● RandomSampler (ランダムサーチ)
● SkoptSampler (Gaussian Process ※ 変更可能)
○ https://github.com/scikit-optimize/scikit-optimizeのラッパー
● CmaEsSampler (CMA-ES)
○ https://github.com/CMA-ES/pycmaのラッパー
● ユーザ定義Sampler
47
なぜ色々なSamplerがあるの?
48
関数グラフはhttp://infinity77.net/global_optimization/genindex.htmlより引用
SkoptSamplerが強い CmaEsSamplerが強い TPESamplerが強い
タスクによって最適な
Samplerは変わる
Samplerの選択指針は? アルゴリズムの詳細は?
https://speakerdeck.com/nmasahiro/ji-jie-xue-xi-niokeru-haihaharametazui-shi-hua-falseli-lun-toshi-jian
49
● 時間の関係上、今回は割愛🙇
● PyConJP 2019での野村さんの発表が参考になります
Optunaの構成要素: Pruner
50
Optunaの構成要素: Pruner
51
● トライアルの各ステップで、枝刈りの必要性を判定
● ストレージから、他のトライアルの情報を取得
利用可能なPruner
• MedianPruner (デフォルト)
• PercentilePruner ※ MedianPrunerの一般化
• SuccessiveHalvingPruner
• ユーザ定義Pruner
52
ベンチマーク結果
53
The transition of average test errors of simplified AlexNet for SVHN dataset
From: https://arxiv.org/pdf/1907.10902.pdf (optuna paper)
ハイパーパラメータ探索空間
MedianPruner
● 各ステップでの評価値(ベスト)を他のトライアルと比較
○ 中央値よりも悪いなら枝刈り
○ 実行中および枝刈り済みのトライアルは、比較対象から除外
● Pros:
○ 挙動が直感的
● Cons:
○ そこそこの枝刈り効率
54
枝刈り圏内
10個のトライアルの学習曲線
SuccessiveHalvingPruner
● 「トライアル数が1/Nになるように枝刈り」⇒「生き残ったものにはN倍のリソース
を割り当て」の繰り返し (右上の図) ※ かなり簡略化した説明および図
● Pros:
○ 高い枝刈り効率
○ 分散最適化と相性が良い
■ 実行中・枝刈り済みトライアルも考慮
○ 学習曲線以外にも応用が効く
■ E.g., データサイズを変えつつ枝刈り (右下の図)
● Cons:
○ 現状ではハイパーパラメータに敏感
■ min_resourceの適切な指定が必要
■ E.g., min_resource = num_iterations/100
55
Halve!
N=2, min_resource=2
1/100のデータで評価&枝刈り
1/10のデータで...
完全なデータで評価
Tips: 枝刈り時のトライアル回数はどうすべきか
● n_trialsを変更せずに、単純に枝刈りを有効にすると、
⇒ 最適化結果は悪くなる ※ 所要時間は短縮される
● n_trialsを増やすと良い?
○ 枝刈りがあると、一回のトライアルに掛かる時間が不規則になる
⇒ 適切なn_trialsの値を推測するのが難しい
● timeout指定がお勧め
○ 時間が許す範囲内で、出来るだけ多くのトライアルを実行
○ E.g., study.optimize(objective, timeout=600) # 10分間最適化
56
Tips: 枝刈りとlearning_rate(LR)の最適化
● 枝刈りとLR最適化の組み合わせには注意が必要
○ LRが小さいものほど、初期段階で刈られやすくなる傾向がある
● LightGBM向けの現状での対応策(一案)
○ フェーズを分ける
○ 最初は、LRを固定して、
それ以外のパラメータを枝刈りありで最適化
○ 最後は、LRだけを枝刈りなしで最適化
● 将来的にはPrunerを改良予定
○ E.g., 学習曲線予測、Hyperband
57
From: https://medium.com/@tomoto/1a1c9dd870df
ここで刈られてしまう
ここまでのまとめ
58
いろいろとカスタマイズできるので、
是非試してみてください
新機能: LightGBMTuner
LightGBMTuner
60
• Optunaは便利
○ ハイパーパラメータを自動で探索してくれる
○ ただ、探索空間はユーザが指定する必要がある
⇒ 一種のハイパーパラメータ
• 理想
○ ユーザに何も意識させずに勝手に最適化してくれる
⇒ LightGBMTuner!
使い方
インストール (before v0.17.0):
$ pip install git+https://github.com/smly/optuna@lgbm-autotune
LightGBM互換インタフェース:
61
修正箇所は一行
Stepwise Tuning
● 重要なハイパーパラメータを、順々にOptunaで最適化
○ E.g., feature_fraction, num_leaves, bagging_fraction
● Stepwise Tuning:
○ 一度に全パラメータを探索すると空間が巨大になってしまう
■ 一つずつ探索することで効率化
○ 経験的に良いチューニング手法であることが知られている:
■ CPMP's talk in Kaggle Days Paris
■ Chang's comment in Kaggle forum
62
一度に探索: X*Y Stepwise: X+Y
ベンチマーク結果
63
● Kaggle: DonorsChoose.org Application Screening
● LightGBM and Tf-idf StarterカーネルのLightGBM部分をLightGBMTunerに置換
○ See: https://gist.github.com/smly/367c53e855cdaeea35736f32876b7416
● オリジナルスコア:
○ Validation: 0.77910
○ Private: 0.78470
○ Public: 0.79516
● Tunedスコア:
○ Validation: 0.77993 (0.00083 up)
○ Private: 0.78622 (0.00152 up)
○ Public: 0.79535 (0.00019 up)
Stepwise Tuningの経過 (from: https://github.com/pfnet/optuna/pull/549)
※ 赤線はオリジナルのスコア
※ チューニング中(右の図)はLRを高めに設定しているため、左のスコアの値とは若干ズレがある
LightGBMTuner独自のlgb.train()引数
● best_params:
○ ベストパラメータを格納
● tuning_history:
○ チューニング履歴を格納
● time_budget:
○ 制限時間を指定
● sample_size:
○ サンプリングを有効化
64
絶賛開発中!
● 検討中の機能:
○ 枝刈り
○ lgb.cv()対応
○ レジューム対応
● フィードバックは大歓迎!
○ https://github.com/pfnet/optuna/pull/549
65
まとめ
● Optunaでハイパーパラメータが簡単に最適化可能
● 特徴:
○ Define-by-Run
○ 枝刈り
○ 分散最適化
● Storage/Sampler/Prunerは用途に応じてカスタマイズ可能
● LightGBMTunerがもうすぐリリース
66
Need Your Feedback and Contribution!
67

PyData.Tokyo Meetup #21 講演資料「Optuna ハイパーパラメータ最適化フレームワーク」太田 健