ハイパーパラメータ自動最適化ツール「Optuna」公開

秋葉 拓哉
リサーチャー

2018-12-03 13:45:42

ハイパーパラメータ自動最適化フレームワーク「Optuna」のベータ版を OSS として公開しました。この記事では、Optuna の開発に至った動機や特徴を紹介します。

 

 

ハイパーパラメータとは?

ハイパーパラメータとは、機械学習アルゴリズムの挙動を制御するパラメータのことです。特に深層学習では勾配法によって最適化できない・しないパラメータに相当します。例えば、学習率やバッチサイズ、学習イテレーション数といったようなものがハイパーパラメータとなります。また、ニューラルネットワークの層数やチャンネル数といったようなものもハイパーパラメータです。更に、そのような数値だけでなく、学習に Momentum SGD を用いるかそれとも Adam を用いるか、といったような選択もハイパーパラメータと言えます。

ハイパーパラメータの調整は機械学習アルゴリズムが力を発揮するためにほぼ不可欠と言えます。特に、深層学習はハイパーパラメータの数が多い傾向がある上に、その調整が性能を大きく左右すると言われています。深層学習を用いる多くの研究者・エンジニアは、ハイパーパラメータの調整を手動で行っており、ハイパーパラメータの調整にかなりの時間が費やされてしまっています。

Optuna とは?

Optuna はハイパーパラメータの最適化を自動化するためのソフトウェアフレームワークです。ハイパーパラメータの値に関する試行錯誤を自動的に行いながら、優れた性能を発揮するハイパーパラメータの値を自動的に発見します。現在は Python で利用できます。

Optuna は次の試行で試すべきハイパーパラメータの値を決めるために、完了している試行の履歴を用いています。そこまでで完了している試行の履歴に基づき、有望そうな領域を推定し、その領域の値を実際に試すということを繰り返します。そして、新たに得られた結果に基づき、更に有望そうな領域を推定します。具体的には、Tree-structured Parzen Estimator というベイズ最適化アルゴリズムの一種を用いています。

Chainer との関係は?

Optuna は Chainer を含む様々な機械学習ソフトウェアと一緒に使うことができます。

Chainer は深層学習フレームワークであり、Optuna はハイパーパラメータの自動最適化フレームワークです。例えば、Chainer を用いたニューラルネットの学習に関するハイパーパラメータを最適化する場合、Chainer を用いるユーザーコードの一部に Optuna からハイパーパラメータを受け取るコードを書くことになります。それを Optuna に渡すことによって、Optuna が自動的に何度もそのユーザーコードを呼び出し、異なるハイパーパラメータによりニューラルネットの学習が何度も行われ、優れたハイパーパラメータが自動的に発見されます。

社内では Chainer と共に用いられているユースケースがほとんどですが、Optuna と Chainer は密結合しているわけではなく、Chainer の以外の機械学習ソフトウェアとも一緒に使うことができます。サンプルとして、Chainer の他に scikit-learn, XGBoost, LightGBM を用いたものを用意しています。また、実際には機械学習に限らず、高速化など、ハイパーパラメータを受け取って評価値を返すようなインターフェースを用意できる幅広いユースケースで利用可能です。

なぜ Optuna を開発したのか?

ハイパーパラメータの自動最適化フレームワークとして、Hyperopt, Spearmint, SMAC といった有名なソフトウェアが既に存在しています。そんな中でなぜ Optuna を開発したのでしょうか?

複数の理由やきっかけがありますが、一言で言うと、我々の要求を満たすフレームワークが存在せず、そして既存のものよりも優れたものを作るアイディアがあったからです。また、実際には、機能面だけではなく品質面でも、既存のフレームワークにはレガシーなものが多く、不安定であったり環境によって動作しなかったり修正が必要だったりという状況でした。

Optuna の特徴

Define-by-Run スタイルの API

Optuna は Define-by-Run スタイルの API を提供しており、既存のフレームワークと比較し、対象のユーザーコードが複雑であっても高いモジュール性を保ったまま最適化を行うことを可能とし、またこれまでのフレームワークでは表現出来なかったような複雑な空間の中でハイパーパラメータを最適化することもできます。

深層学習フレームワークには Define-and-Run と Define-by-Run という 2 つのパラダイムが存在します。黎明期は Caffe など Define-and-Run のフレームワークが中心でしたが、PFN の開発した Chainer は Define-by-Run のパラダイムを提唱し先駆けとなり、その後 PyTorch が公開され、TensorFlow も 2.0 では eager mode がデフォルトになるなど、今では Define-by-Run のパラダイムは非常に高く評価されており、標準的にすらなろうとする勢いです。

Define-by-Run のパラダイムの有用性は、深層学習フレームワークの世界に限られたものなのでしょうか?我々は、ハイパーパラメータ自動最適化フレームワークの世界でも同様の考え方を適用できることに気づきました。この考え方の下では、全ての既存のハイパーパラメータ自動最適化フレームワークは Define-and-Run に分類されます。そして Optuna は Define-by-Run の考え方に基づき、既存のフレームワークと大きく異なるスタイルの API をユーザに提供しています。これにより、ユーザプログラムに高いモジュール性を持たせたり複雑なハイパーパラメータ空間を表現したりといったことが可能になりました。

学習曲線を用いた試行の枝刈り

深層学習や勾配ブースティングなど、反復アルゴリズムが学習に用いられる場合、学習曲線から、最終的な結果がどのぐらいうまくいきそうかを大まかに予測することができます。この予測を用いて、良い結果を残すことが見込まれない試行は、最後まで行うことなく早期に終了させてしまうことができます。これが、Optuna のもつ枝刈りの機能になります。

Hyperopt, Spearmint, SMAC 等のレガシーなフレームワークはこの機能を持ちません。学習曲線を用いた枝刈りは、近年の研究で、非常に効果的であることが分かっています。下図はある深層学習タスクでの例です。最適化エンジン自体は Optuna も Hyperopt も TPE を用いており同一であるものの、枝刈りの機能の貢献により、Optuna の方が最適化が効率的になっています。

並列分散最適化

深層学習は計算量が大きく一度の学習に時間がかかるため、実用的なユースケースでのハイパーパラメータの自動最適化のためには、性能が高く安定した並列分散処理を簡単に使えることが必要不可欠です。Optuna は複数ワーカーを用いて複数の試行を同時に行う非同期分散最適化をサポートします。下図のように、並列化を用いることで最適化は更に加速します。下図はワーカー数を 1, 2, 4, 8 と変化させた場合の例ですが、並列化により最適化がさらに高速化されていることが確認できます。

また、Chainer の分散並列化拡張である ChainerMN との連携を容易にする機能も用意されており、最適化対象の学習自体が分散処理を用いるような場合にも Optuna を簡単に使うことができます。これらの組み合わせにより、分散処理が含まれた目的関数を並列に分散実行するようなこともできます。

ダッシュボードによる可視化(実装中)

最適化の過程を見たり、実験結果から有用な知見を得たりするために、ダッシュボードを用意しています。1 コマンドで HTTP サーバが立ち上がり、そこにブラウザで接続することで見ることができます。また、最適化過程を pandas の dataframe 等で export する機能もあり、それらを用いてユーザがシステマチックに解析を行うこともできます。

終わりに

Optuna は既に複数の社内プロジェクトで活用されています。例えば、今夏準優勝を果たした Open Images Challenge 2018 でも用いられました。今後も活発に開発は続けられ、完成度の向上と先進的な機能の試作・実装の両方を精力的に進めていきます。現段階でも他のフレームワークと比較し Optuna を利用する理由は十分存在すると我々は考えています。お試し頂きお気づきの点があれば忌憚のないフィードバックを頂ければ幸いです。

先日開催された第 21 回情報論的学習理論ワークショップ (IBIS’18) では、弊社でのインターンシップにおける成果であるハイパーパラメータ自動最適化に関する研究を 2 件発表しました。これらは Optuna を実際に利用している中で出てきた問題意識に基づいており、成果はいち早く Optuna に組み込むことを目指して取り組んでいます。こういった技術により Optuna を更に優れたものとしていければと考えています。

我々の目標は、深層学習関連の研究開発をできるだけ加速することです。ハイパーパラメータの自動最適化はそのための重要なステップとして取り組んでいますが、他にも既にニューラルアーキテクチャー探索や特徴量の自動抽出といった技術に関しても取り組みを開始しています。PFN では、こういった領域や活動に興味を持ち一緒に取り組んでくれるメンバーをフルタイム・インターンで募集しています

深層強化学習ライブラリChainerRL

Yasuhiro Fujita

2017-02-16 19:10:47

Chainerを使った深層強化学習ライブラリChainerRLを公開しました. https://github.com/pfnet/chainerrl

PFNエンジニアの藤田です.社内でChainerを使って実装していた深層強化学習アルゴリズムを”ChainerRL”というライブラリとしてまとめて公開しました.RLはReinforcement Learning(強化学習)の略です.以下のような最近の深層強化学習アルゴリズムを共通のインタフェースで使えるよう実装してまとめています.

A3CでAtari 2600のゲームをプレイするexampleや,

DDPGでヒューマノイドロボットの制御を学習するexampleなどがあります.

以下では簡単にChainerRLの使い方を説明します.

まず,強化学習を使って問題を解くには,解きたい問題(”環境”と呼びます)をしっかり定義する必要があります.環境の定義の仕方は,OpenAIが公開している強化学習ベンチマーク環境のGym(https://github.com/openai/gym)のインタフェースに従っています.Gymの環境で動かすこともできますし,インタフェースを揃えればオリジナルな環境で動かすこともできます.基本的にはresetとstepという2つのメソッドが実装されていれば十分です.

env = YourEnv()
# reset は環境をリセットして現在の観測を返す
obs = env.reset()
action = 0
# step は環境にアクションを送り,4つの値(次の観測,報酬,エピソード終端かどうか,追加情報)を返す
obs, r, done, info = env.step(action)

深層強化学習では,状態から行動を決める方策(Policy)や,状態や行動の価値を予測する価値関数(V-function,Q-function)をニューラルネットで表現し,そのパラメータを学習します.ChainerRLでは,これらは単に__call__を実装したChainerのLinkとして表現されます.

class CustomDiscreteQFunction(chainer.Chain):
    def __init__(self):
        super().__init__(l1=L.Linear(100, 50)
                         l2=L.Linear(50, 4))
    def __call__(self, x, test=False):
        h = F.relu(self.l1(x))
        h = self.l2(h)
        return chainerrl.action_value.DiscreteActionValue(h)

class CustomGaussianPolicy(chainer.Chain):
    def __init__(self):
        super().__init__(l1=L.Linear(100, 50)
                         mean=L.Linear(50, 4),
                         var=L.Linear(50, 4))
    def __call__(self, x, test=False):
        h = F.relu(self.l1(x))
        mean = self.mean(h)
        var = self.var(h)
        return chainerrl.distribution.GaussianDistribution(mean, var)

このように作ったモデルやChainerのOptimizer,アルゴリズムごとに必要な引数を渡して”エージェント”を作ります.エージェントは環境とのインタラクションを通じてデータを集めながらモデルの学習を行います.

q_func = CustomDiscreteQFunction()
optimizer = chainer.Adam()
optimizer.setup(q_func)
agent = chainerrl.agents.DQN(q_func, optimizer, ...)  # 残りの引数は省略

エージェントを作ったら,自分で学習ループを書いて動かすか,

# Training
obs = env.reset()
r = 0
done = False
for _ in range(10000):
    while not done:
        action = agent.act_and_train(obs, r)
        obs, r, done, info = env.step(action)
    agent.stop_episode_and_train(obs, r, done)
    obs = env.reset()
    r = 0
    done = False
agent.save('final_agent')

あるいはあらかじめ用意されている学習用関数に渡せば学習が行なえます.

chainerrl.experiments.train_agent_with_evaluation(
    agent, env, steps=100000, eval_frequency=10000, eval_n_runs=10,
    outdir='results')

とりあえず動かしてみるためのクイックスタートガイドを用意しました. https://github.com/pfnet/chainerrl/blob/master/examples/quickstart/quickstart.ipynb

ChainerRLはまだベータ版ですが,強化学習に興味がある方はぜひ試してもらってフィードバックをいただけるとありがたいです.ライブラリとしての使いやすさや,新しいアルゴリズムの追加など,今後も改善を続けていこうと思います.

実験ビルドシステムmafのv0.2をリリースしました

beam2d
リサーチャー

2014-08-04 14:12:31

こんにちは、得居です。先週末からインターンシップの3名を迎え、これからの二ヶ月間が楽しみです。

さて、昨年末に公開した実験用環境のmaf (Github)ですが、先週こっそりと v0.2 をリリースいたしました。今日は何が変わったのかをお伝えしたいと思います。

その前に、まずmafについて紹介します。mafは主に機械学習を用いた実験を書くための環境で、アルバイトの能地さん @nozyh と私の2人で開発しています。ビルドツールのwafを拡張する形で書かれていて、データセットから実験結果をビルドする過程を記述することができます。基本的な紹介は昨年末のブログ記事をご参照ください。特徴としては、学習や評価などの処理に付随するハイパーパラメータを管理する仕組みがあることです。詳細はmafのドキュメントをご参照ください。

それでは、v0.2で入った主な変更を紹介していきます。

続きを読む »

データ解析作業の救世主! 超絶☆実験ビルドシステムmafをOSS公開しました

beam2d
リサーチャー

2013-12-25 13:39:24

Photo by midiman. Used following a Creative Commons License. Taken from https://www.flickr.com/photos/midiman/90232391/
Photo by midiman under Creative Commons License (original)

メリークリスマフ!

得居です。今日はクリスマスですね。皆様昨日はいかがお過ごしでしたでしょうか?

クリスマスということで、今日は私たちから皆様に、特にデータ解析や論文執筆、手法の比較検証のために計算機上で様々な実験をしている方々に、プレゼントがあります!

Github – pfi/maf

今日、実験結果を「ビルドする」ためのツールmafを公開しました!

mafは、PFIでもよく使われているPythonベースのビルドツールwafを実験に使うための拡張です。大まかな使い方を学ぶために、ドキュメントとサンプルも公開しています。

maf — maf 0.1 documentation
サンプル

実験手順をビルドだと思って宣言的に書くこと自体はwaf等既存のビルドツールで可能です。mafはこの手順のうち、パラメータだけが違うという部分をまとめて書くための仕組みや、実験特有の手順(プロットなど)をサポートするようなライブラリを提供しています。

例えば5-foldの交差検証を行う例は以下のような雰囲気で書けます。なんとなく何をやる実験なのかわかるでしょうか? 正確な書き方はドキュメントやサンプルを参照してください。

...  # import等

def experiment(exp):
    # 5-foldの交差検証のためにデータセットを5通りに分割
    NUM_FOLD = 5
    exp(source='dataset',
        target='train test',
        # parametersを指定することで、パラメータ付けられたタスクや
        # パラメータ付けられた出力ファイルを作ることができる
        parameters=[{'fold': i} for i in range(NUM_FOLD)}],
        # 1行1データの形式のデータセットを5通りのtrain testに分割する
        # 出力されるtrain, testは'fold'パラメータでパラメータ付けられる
        rule=maflib.rules.segment_by_line(NUM_FOLD, 'fold'))

    # 分割した各foldに対して実験
    exp(source='train',
        target='model',
        parameters=maflib.util.product({  # 全組合せを実行
            'param1': [1, 2, 3, 4],
            'param2': ['a', 'b', 'c']
        }),  # 'fold'はもう指定しなくて良い (trainに紐付いている)
        rule='my-train ${SRC} ${TGT}')

    # ここにはもうfold, param1, param2などのパラメータを指定する必要はない
    # (modelとtestに紐付いているので)
    exp(source='model test',
        target='result',
        rule='my-eval ${SRC} ${TGT}')

    # 各foldにおける実験結果における'accuracy'値の最大値を取る
    exp(source='result',
        target='max_accuracy_result',
        aggregated_by('fold'),  # foldパラメータを「潰す」
        rule=maflib.rules.max('accuracy'))

    # 結果を可視化
    exp(source='max_accuracy_result',
        target='result.png',
        rule=my_plot_fun)

# 結果をプロットする方法はmatplotlibを使って書ける
@maflib.plot.plot_by
def my_plot_fun(figure, plotdata, parameter):
    ...

my-trainとmy-evalという学習・評価コマンドさえあれば、データサイエンティストや研究者の方なら誰でもforループを使って書いたことのある面倒くさい、そして意外とややこしい交差検証が実質3行で書けます。my-trainやmy-evalのコマンド部分はpython関数で書いて指定することもできます。

maf開発の背景は、実験の複雑さです。データを解析したりアルゴリズムの比較を行う際に、実験手順をスクリプト(shell, python, ruby…)でよく書くと思います。最初は単一のデータに単一のアルゴリズムを一回適用して結果を見るだけだったりするので、直接スクリプトを書くので十分なのですが、他のデータに適用したり、他のアルゴリズムや異なる設定(パラメータ)と比べたりし始めると、実験手順と実験結果の管理に割くコストが上がっていきます。機械学習の実験だとさらにデータを複数通りに分割する必要もあったりして、データの管理も必要になります。これを最初のスクリプトの延長で書き続けると書いた本人にしか読み解けない複雑な実験スクリプトができあがったり、実験に必要な手操作が本人にしかわからなくなったりします。メンテナンス性を上げるために実験のためのドキュメントを書き始めると、管理コストはさらに上がります。

様々な設定での実験手順と実験結果、およびそれらの集約と可視化、これらを宣言的に記述して途中で生成されるファイル群の管理をうまく隠蔽することができれば、実験とそのメンテナンスにかかるコストは大幅に下がると期待できます。mafはこれらを実現することを目指して開発されました。

mafは辛い実験生活をサポートしてくれる縁の下の力持ち的な存在です。ぜひ一度試してみてください。フィードバックやPull Reqなどもお待ちしております!

今年のSIGKDDベストペーパーを実装・公開してみました

hido
Chief Research Officer

2013-08-16 18:23:11

毎日暑いですね。比戸です。

ちょうど今週シカゴで開かれていたSIGKDD2013でBest research paperに選ばれたEdo Liberty氏 (Yahoo! Haifa Labs)の”Simple and Deterministic Matrix Sketching”のアルゴリズムを実装して公開してみました。

元論文PDFは著者サイトから、私が書いたPythonコードはGithubからそれぞれ入手できます。

続きを読む »

Compressed Permuterm Index: キーワード辞書検索のための多機能&省メモリなデータ構造

maruyama
リサーチャー

2012-11-06 14:00:23

はじめましてこんにちわ。
4月からPFIで働いているまるまる(丸山)です。最近のマイブームはスダチです。
リサーチブログの更新が再開されたので、私も流れに乗って初ブログを書いてみようと思います。

今回は社内の情報検索輪講で少し話題にあがったCompressed Permuterm Indexを紹介したいと思います。

続きを読む »

Interaction Design向けのC++ライブラリ "pocode"

祢次金 佑
エンジニア

2012-02-28 10:21:16

祢次金です。

今回はC++で書かれたオープンソースなライブラリ、pocodeを簡単にご紹介します。
pocodeはPotion社によって設計された、主にインタラクションデザインのためのライブラリであり、プラットフォームとしてはWindows、MacOS(Lion)、iOSに対応しています。オープンソースとして公開されたのは最近ですが、既にいくつかのプロジェクトで利用実績があるようです。

続きを読む »

EaselJSでインタラクティブなグラフを描こう

祢次金 佑
エンジニア

2011-12-04 23:35:36

先日、paper.jsによるグラフ描画について触れましたが、今回はflashのようにcanvasを使うことのできるJavascriptライブラリ「EaselJS」を使ったグラフ描画について少しご紹介したいと思います。

EaselJSでは、ShapeやBitmap、TextといったDisplayObjectをStageにaddChildしてディスプレイリストを作ることで、Stageに紐付けられたcanvasにその内容が描画されます。 ActionScriptに慣れている開発者には馴染みやすいやり方です。onClickなどのイベントハンドラもDisplayObjectごとに設定することができ、アニメーションも勿論可能。

<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript" src="easel.js"></script>
    <script type="text/javascript">
      window.onload = function() {
        var canvas = document.getElementById("myCanvas");
        var stage = new Stage(canvas);

        var shape = new Shape();
        shape.graphics.beginFill('#f00').drawCircle(100, 100, 100);
        stage.addChild(shape);

        var label = new Text("hoge", "30px Arial", "#000");
        label.x = 70;
        label.y = 110;
        stage.addChild(label);

        stage.update();
      };
    </script>
  </head>
  <body>
    <canvas id="myCanvas" width="300" height="300"></canvas>
  </body>
</html>

先日の記事と同様、クリックに反応する棒グラフを実装してみました。見た目にはほとんど変わりませんが、EaselJSで実装しています。

bar graph using EaselJS – jsdo.it – share JavaScript, HTML5 and CSS

paper.jsと同じく、プロジェクトサイトにはデモやAPIドキュメントが揃っています。(冒頭の画像はEaselJSによるgameデモのスクリーンショットです。)

最速の疎ベクトルはどれだ

海野 裕也
リサーチャー

2011-11-22 15:57:49

海野です。
自然言語処理などで機械学習を行おうとすると、非常に疎なベクトル表現を使いたくなります。疎、というのはほとんどの要素が0である、という意味です。前々から疎ベクトルライブラリのパフォーマンスに関して気になっていたので、幾つか調べてみました。

続きを読む »

モダン並列・並行プログラミング ~ Concurrent Revisions による実装と現実 ~

preferred

2011-10-20 14:43:49

本日社内向けのTechTalkにて、並列・並行プログラミングに関する話を行いました。

昨今、プログラムの並列化はなくてはならないものとなっています。しかし、そのプログラミング環境は依然としてロックを用いたものが主流です。今回の発表の主張を端的に申し上げますと、

続きを読む »