研究プロジェクトにおける再現性を考慮したディレクトリ構成

はじめに

研究を行う上で研究結果の再現性は非常に重要です.例えば,論文を書いて査読をしてもらって,査読コメントを元に修正をするときに,結果を再現できないと非常に困りますし,論文の内容の正しさを示すという点でも再現性は担保するべきでしょう.

一方で,我々ソフトウェア工学分野においてはこの再現性の担保に利点があります.なぜなら,全ての実験をスクリプトとして記述して保存をしておけるためです.また,GitHubなどを利用すれば,簡単に再現用のスクリプトを全世界に公開することもできます.

ですので理想としては

  • 仮説を立てる
  • 仮説を検証するための実験計画を作成する
  • 実験計画を元に実験を行うためのスクリプトを作成する
  • スクリプトを実行して結果をまとめて論文にする
  • スクリプトをまとめて公開する

このような手順を踏んでいきたいです.

ただ,そうは言っても現実はなかなかうまくいきません.

  • ある実験に使ったスクリプトがどこにいったかわからなくなったり,
  • 追加の実験のしすぎでどのスクリプトがどの結果に対応しているのかわからなくなったり,
  • スクリプトと結果はあるけど,どの条件での実験なのかがスクリプトを一から全部読まないとわからなかったり

様々な問題が発生します. これらの問題が発生するのは,様々な仮説のもと実験を行ったり,実験結果を説明するための追加の実験を行ったり,少しづつ条件を変えた比較実験をしたりと研究の特性上避けられない部分に起因していると思っています.

この記事では,このような問題をできる限り軽減するべく行っている私のスクリプトの管理方法を紹介します.

ディレクトリ構成

以下でディレクトリ構成について説明します.私の場合ある研究プロジェクトResearch Aがあった場合,ResearchA/というディレクトリをプロジェクトのルートとして,その直下に以下のようなディレクトリ郡を作成します.

  • exp*: 実験のスクリプトが入っているディレクトリです.各番号ごとに異なる実験が行われています (exp1やexp2といった形).
  • docs: 各実験の説明が書かれたドキュメントが入っています.具体的にはexp1.mdというようなファイルを生成して,その中に,exp1でどのような実験が行われているのか,どんなスクリプトがあるのか,得られた結果は何かなどをメモしています.
  • data: 実験に必要なデータを保存しておくためのディレクトリです.基本的には素の状態のデータを保存しておきます.ここではdataとなっていますが,取得したデータによってディレクトリ名を色々と変えています.
  • repository: 私の場合はソースコードリポジトリを使用する場合も多いのですが,リポジトリはdataと分けて管理をしています.
  • common: よく利用するスクリプトがまとまったディレクトリになります.例えば,Gitリポジトリからコミットハッシュの一覧を取り出すスクリプトなどです.パッケージ化する前のものが入っています
  • slides: 進捗報告などのために作成した資料が入っています
  • tables: 適当なtexのテンプレートが入っています.何をするかというと,各実験で作成された表のデータ(tex形式で出力しています)を読み込んでpdf形式で出力するというものです.
  • papers: 関連研究の論文を入れています.分野ごとに分けることが多いです.

ドキュメントの構成

docs/の中のドキュメントには以下のようなことを最低限書き込んでいます.

  • start date: 実験開始日
  • Abstract: 実験の仮説,どうしてその仮説を考えたか,予想される結果,何を明らかにしたいかなどを書きます
  • Scripts: どのようなスクリプトを作ったかをスクリプトごとにまとめて書いておきます.箇条書きで,何をするスクリプトか,どのような条件を設定しているのか,出力は何でどこに出力されるのかくらいを書いておきます
  • How to get this result?: スクリプトの実行順序を書いておきます.データの準備が必要な場合などに忘れないようにするためです.
  • Results: 結果がどうであったのかを書き込みます.また,私の場合その結果に対する思考も書いておきます.

なお,これらはAbstract以外は順番に書く必要はなく,行ったりきたりしながら書いています.特に,実験をしてResultsが出た後に,その結果を元にもっと調べたいことがないのかを考え,それを追加するということはよく行います.

運用方法

基本的には以下のような手順で運用しています.

  • docsにexpN.mdというファイルを作成し,実験計画や,実験の概要や背景を記載する.
  • expNというディレクトリを作成する.
  • docsのexpN.mdを更新しつつ,expNに必要となるスクリプトを作成していく.expN.mdには各スクリプトの説明も書き込む
  • 実行して,結果をexpN/の中に出力させる(plot,data,tablesなどのディレクトリ)
  • tablesに表のデータが出力されたら,それをtable/でtexのテンプレートに流し込んで表をpdfで作成する
  • docs/expN.mdに結果を記載する(もしくは,重要な点を記入する.)

このような手順となっています.実験ごとにディレクトリを分け,それぞれのディレクトリにドキュメントを付けているため,後からどのディレクトリでどのような実験を行ったかを見失いません.また,各スクリプトに対する説明も書いてあるため,どのスクリプトがどの条件なのかなども見失いません.

実験をどの程度の粒度で分けるかですが,私の場合は,「AメトリクスとBメトリクスの相関はどうなっているのか?」くらいの粒度では分けずに,「あるプロジェクトにおけるメトリクスの特性は何か?」くらいの粒度で分けています.この辺りは好みかと思いますが,あまり小さすぎるとexpNが乱立しすぎてしんどくなりました.

似通ったスクリプトについては適宜該当コードを抽出してcommonにまとめます.複数の研究プロジェクトで共通して使うようなものについては,パッケージ化も検討しましょう.

論文を書く段階

論文を書く段階になって忘れがちなのが,論文に載せている結果がどの実験スクリプトと対応しているのかをメモしておくことです.これを忘れると,論文を読んで,その論文内容と合致するスクリプトを探しに行く必要があって結構しんどいです.個人的におすすめなのは,全ての表,図,文章内の実験結果からの記述に対して以下のような情報を書き込んでおくことです

%% ============================================================================
%% PLEASE DON'T REMOVE THIS COMMENT
%% ============================================================================
%% exp40/tables/corr.csv
%% exp40/compute_correlation.py
%% exp40/make_corr_table.py
%% ============================================================================
%% ============================================================================ 

こうしておけば,あとで論文を見返したときに,「ああ,この結果は,exp40/tables/corr.csvなんだな.そして,この結果は,compute_correlation.pyとmake_corr_table.pyから生成されるんだな」ということがすぐにわかります.

Q&A

  • 問. このやり方では大量の似通ったソースコードが生成されるのでは?
  • 答. commonに適宜抽出はしますが,確かに似通ったコードが大量にできます.ただ,再現性を担保したいという狙いがあり多少はいいかなと思っています.

  • 問. Gitのコミットハッシュを使えばわざわざexpなどで分けなくても済むのでは?

  • 答. Gitのコミットハッシュを使ってバージョンで管理するということも検討しました.一方で,その場合にドキュメントの書き方が難しかったり,少し戻ったり大きく戻ったりをして違う実験をするためにブランチを作ってみたいなことをしていると,Gitの歴史がすごいことになってしまうため,このような形で落ち着きました.Git自体は使って,バックアップをしたり,diffを管理したりしています.

おわりに

すぐに思いつく内容を思うままに書いてみました.上の方法で嬉しいのは

  • ドキュメントを全部読み返せば,全てのスクリプトの意味がわかること
  • expごとに分けているので,あとですぐに再実験をしたり,結果を確認したり,条件を確認できたりすること
  • 論文に載せた結果がどこからきているのかがすぐにわかること

などです.

もし使ってみての感想などがあれば教えてもらえると嬉しいです.