Masaponto's Blog

お勉強メモ

機械学習のための実験プログラムについて

Aizu advent calendar 2017 8日目の記事である。

まえがき

本稿では機械学習(または数値解析)における「実験を行うプログラムの作成」から「実験結果資料の作成」までの流れとプログラムの構成を、僕がどのようにやっているかを紹介していく。 僕はPython3を用いて研究を進めているので、いくつかPythonライブラリの紹介もする。 書いているうちに去年書いた記事と似ていて新規性に欠ける気がしてきてけど、今回は過去記事の詳細版ということで。

本題

機械学習アルゴリズムの精度確認のための実験を行う際には、基本的に下記の4つのプログラムに分けて書くようにしている。

  1. 学習アルゴリズムのプログラム
  2. 入力として学習データ及びパラメータを受けとり、それを元に1.のプログラムを動かし、その結果を出力するプログラム
  3. 2.を行うために共通な処理を行うためのプログラム
  4. 入力として2.の結果を受け取り、グラフ画像や表を出力するプログラム

つまり、下の図になる。

f:id:masaponto:20171215095412j:plain
実験用プラグラムの処理の流れ

また、この図を元にしたディレクトリ構成は下記のようになる。

├── docs
│   ├── experiment_01.md
│   └── experiment_02.md
├── experiment_01
│   ├── algorithm.py
│   ├── experiment.py
│   ├── graph_generator.py
│   ├── utils.py
│   ├── graphs/
│   │   ├── {dataset_name}_{prameters}.png
│   │   └── {dataset_name}_{prameters}.eps
│   └── results
│       ├── {dataset_name}_{prameters}.csv
│       └── summary.md
└─── experiment_02
     ├── hogehoge.py
     ├── fugafuga.py
     .......

このようにプログラムを分割しておくと、追加実験による仕様変更に比較的少ないプログラム変更で対応できるかなと思う。
登場するものは、以下の6つである。

  1. 入力するデータセット
  2. 入力するパラメータ
  3. 実験用のプログラム
  4. 実験を助けるユーティリティ関数のプログラム
  5. アルゴリズムのプログラム
  6. グラフ生成用プログラム
  7. 実験資料

これら6つについてそれぞれ書いていく。

入力データセット

僕の研究では、公開データセットを使っている。 基本的には、UCI machine learning repositoryや、LIBSVM Dataで公開されているものである。 データのフォーマットがcsvlibSVM形式などの一般的な形式だと、大抵読み込むライブラリ(scikit-learnなど)が存在している。 しかし、たまに独自フォーマット(僕が知らないだけかもしれないが)のデータががあったりする。(あるいは2クラス分類問題の教師信号が{-1, 1}でない場合がある)その場合は、それ用のプログラムを書かねばならないので、非常にめんどうだったりする。これに限っては頑張るしかないかなと思っている。

Pythonでscikit-learnを使用している場合は、sklearn.datasets.fetch_mldata()という便利な関数がある。 fetch_mldata これはデータセットの名前の文字列を引数に与えるとmldata.orgから、ダウンロードしてきて、データセットのオブジェクト(Bunch object, sklearn.datasets.base.Bunch)で返してくれる。デフォルトでは、自身のホームディレクトリに~/scikit_learn_dataというディレクトリが生成され、その中にデータセットのファイルが入る。

まとめると、とりあえずmldata.orgを探して、あったらfetch_mldata()、なかったらUCILIBSVMから持ってきて、読み込んでBunch objectを返すって流れだと良いと思う。 scikit-learnを使わない場合も同様にデータセットを置いとくディレクトリを作っといて、データセット名を指定すると、データを特定の形式で持ってくるというプログラムを作ればよい。

入力パラメータ

パラメータの入力方法は実験プログラムを実行する際のコマンドライン引数にしている。 僕の場合は標準ライブラリのargparseを使っている。他にもpython-fireClickがあるが僕はまだためしていない。

複数の入力パラメータを用いて、それぞれで実験を試すことはよくあるので、使用したパラメータを実験結果の出力ファイル名にしておくと、わかりやすくて良いと思う。

実験プログラム

上記で紹介したデータセットの読み込み処理と、コマンドライン引数を用いたパラメータ指定は、この実験プログラムに記述する。 つまり、実験を行う際にはこのプログラムを実行し開始することになる。 実験プログラムは、入力にデータセットとパラメータを受けとり、アルゴリズムのプログラムやユーティリティプログラムを適宜呼ぶような構成になる。(アルゴリズムは複数の場合もある)

ユーティリティプログラム

Datasetの読み込みや交差検定、データの前処理など、プログラム中でよく使う関数はutilityとしてまとめたり、ライブラリ化すると便利である。

アルゴリズムのプラグラム

このプログラムは前節の実験プログラムから呼ばれることになる。 scikit-learnに準拠させる場合はBaseEstimatorClassifierMixinを継承させて、学習用の関数fit()と推論用の関数predict()を実装させればよい。 プログラムにおいて(これに限らず)、必要に応じてテストコードを書くとより良い。doctestを用いると関数定義の下に簡単にテストを書くことができるので、おすすめである。 また、ここもアルゴリズムのプラグラムとして独立させておき、ライブラリ化すると便利である。

参考
sklearn準拠モデルの作り方 - Qiita
GitHub - masaponto/Python-ELM
GitHub - masaponto/Python-DBM

実験結果

実験結果はcsvファイル、またはmarkdowntex形式の表にして実験プログラムから出力するようにしている。これは、後のグラフ生成や資料作成を楽にするためである。また、実験結果は、指定ディレクトリに保存すると同時に、ぼっちslackに送るようにしている。簡易的なバックアップやログにもなるし、計算用のサーバで実験を行った最には結果を通知させる役割にもなり非常に便利である。slackに送るにはslackteeを用いると良い。slackteeは標準出力した文字列を任意のslackに投稿する機能を提供する。実験プログラム実行の際に、$ python experiment.py | slacktee.sh -c <chanel-name>とすれば良い。これがなかなか便利で、過去に僕はkamebotを作ったりしたが、全然使わなくなってしまった。ちなみに、kamebotはPythonコードの中の関数にデコレータをつけることで、その関数内の標準出力を指定slackに送るというものである。

一方、表の生成にはpython-tabulateが便利である。markdowntexなどの表形式に対応しているので、何も考えずにそのまま研究資料に貼り付けるだけで済む。

参考: ぼっちsalck

グラフ生成

結果のcsvファイルの読み込みには、Numpynumpy.load_txt()という関数が便利である。ファイルの文字列を引数にとり、Numpy array形式にして返してくれる。また、pandaspandas.read_csv()で読み込むと、csvにヘッダとして文字列が入っていても問題なく読み込むことができる。 グラフの生成にはmatplotlibを使う。この際、epsファイル(などのベクター画像)とpngファイルを生成するようにしている。epsファイルは論文用、pngファイルはmarkdownドキュメントとスライド用である。

グラフを生成する際に注意することは2つある。グラフの軸のラベルを必ずつけることと、グラフのタイトルはつけないことである。 前者、後者ともに読者にわかりやすくするためである。後者の方は、グラフ画像にタイトルをつけると、論文に画像として読み込んだ際に、latexのfigureのcaptionとでグラフの名前の記述が重複してしまい読みにくくなるからである。

実験資料

実験結果として他人に伝えるためには、得られたものを資料にまとめなければならない。 僕はmarkdwonで資料を書いて、pandocを使って、pdfを生成するようにしている。 pandocとはドキュメント向けのファイルを様々な形式で変換できるツールである。また、pdfへの変換にはlatexを経由するので、数式も対応する。(latexの形式で数式を記述する) 適当な資料なら$ pandoc document.md -o document.pdfでpdfが生成されてOKである。 しかし、表紙のついた洒落た資料にしたいときもある。 その場合は、下記のテンプレートと使うと良い。

github.com

これを使ってpandoc on Dockerで資料を作るためのキットを作ってみた。

github.com

Docker環境さえあればどこでも$ makeで動く。

おわりに

3年生のときに研究をはじめて、実験プログラムを作りはじめたとき、よく考えずにファイルを作っていたため、ディレクトリの構成がごちゃごちゃになっていた。 そういう過去があったので、本稿のような記事があれば少し役立つかなと思い、書くことにした。 今でも、追加実験が複数続いたりすると、構成がわかりにくくなってきてしまうことがあり、難しいなと感じている。 Pythonのようにゴリゴリ書けてしまう言語だと余計そうなりやすいのかもしれない。 大切なのは、できるだけ共通部分とそうでない部分に分割し、前者をライブラリ化していくことだと思う。

本稿が機械学習や数値解析を用いた研究を進めていく際の参考の一つになるとよいと思う。

そんな感じで修論がんばるぞい!

おまけに修論用Dockerを置いておく。
修論用 · GitHub