OpenML https://www.openml.org/

たとえば Scikit-learn ライブラリでは、sklearn.datasets.fetch_openml という関数で簡単に OpenML.org というサイトから様々なデータをダウンロード出来るようになっている。有名どころとしては mnist という手書き文字 や iris という機械学習定番のアヤメの花びらデータなどがある。大変有り難いサイトなのだが、実態を知らず使うのは気持ち悪いので調べてみた。

https://www.openml.org/contact によると、OpenML.org の設立者は Joaquin Vanschoren さん。https://joaquinvanschoren.github.io/home/ 詳しい情報がある。1981 年生まれのベルギー出身で、現在はオランダのアイントホーフェン工科大学の助教授だ。AutoML という、機械学習の設定自体の自動化が専門だ。

彼の 2014 年の論文 OpenML: networked science in machine learning https://arxiv.org/abs/1407.7722 に OpenML.org 開発の動機などが熱く語られているので、ここから OpenML.org の概要をまとめる。

## OpenML の背景

OpenML とは、機械学習の研究者が研究で使うデータやアルゴリズムを公開して共同作業出来るようにしたオンラインツールだ。17 世紀に Royal Society が研究成果を公開する事で名誉や資金を得やすくなる事に気づいてから、科学成果は論文誌で公開される事になった。しかし機械学習の研究成果の共有には論文だけでなく、実験に使われたデータや手法を機械読み取り可能な形式で共有したほうが良い。OpenML はそのようなコラボレーションの場として作られた。

ここで Joaquin はいくつかの例を挙げて、ネットワークで協力し合う使った科学「ネットワーク科学」がどのように働くか説明する。どれも大変おもしろい。

### Reinventing discovery: the new era of networked science by M. Nielsen https://www.amazon.co.jp/dp/B005OQGZ54/

まず『Reinventing discovery』という「ネットワーク科学」について詳しく語られた本を紹介している。この本によると、成功には次の秘訣がある。

* 少しの貢献を推奨する。
* 複雑な問題を小さな問題に分ける。
* 元になった知識を簡単に参照したり、新しく知識を付け加えやすくする。
* 気が散る情報を簡単に隠せるように。
* 揉め事を解決する手段をはっきりさせる。

### Polymath http://michaelnielsen.org/polymath1/

フィールド賞数学者 Tim Gowers は、2009 年に大勢の数学者たちが難しい問題に対してオンラインで議論しながら共同で取り組む試み Polymath プロジェクトを始めた。何十年も未解決だった数学的問題をブログや Wiki に掲載して多くの数学者に解決のための議論を促したのだ。様々な専門分野を持つ数学者たちがそれぞれの角度から問題に取り組む事で、多くの問題が次々と解決したらしい。これは専門家によるオンライン共同研究の好例だ。

### Sloan Digital Sky Survey (SDSS) https://www.sdss.org/

こちらは天文学の例。SDSS は天文学者たちが撮影した望遠鏡の撮影データを共有するオンラインツールだ。SDSS は一定の期間メンバー限定で公開したあと、世界中に解放する。撮影データは世界中の天文学者の研究に役立つだけでなく、どのような論文の引用されたてどのような功績を果たしたのかが分かるようになっている。

### Galaxy Zoo https://www.zooniverse.org/

「ネットワーク科学」によってプロの専門家だけでなく、アマチュア科学者たちの意欲を上手く盛り上げる事が出来る。当初 Galaxy Zoo は SDSS によって得られた天体映像を観察し、銀河の分類を行うツールとして開発された。私も知らなかったが、天文学の世界ではアマチュアによる貢献が広く行われていて、銀河の分類は特に面白いテーマらしい。

Galaxy Zoo はその後天文学だけでなく、耐性菌や結核との戦いや、シェイクスピア時代の手書き原稿の電子化など、様々な分野でアマチュア科学者に面白いテーマを提供する Zoo Universe として発展を続けている。

## OpenML のサービス

### 用語

OpenML の鍵となる以下の用語についてまとめる。

* Data:
    * 機械学習に使うデータ。表の形をしている。
    * データをアップロードすると OpenML はデータの特徴を計測してウェブページ上に表示する。
    * 一つの列を target (教師データ) として指定出来る。例えばアヤメの例だと花の種類を示す文字列が target になる。
    * 例: アヤメ https://www.openml.org/d/61
* Tasks
    * データに割り当てられた課題と評価方法。
    * 例: アヤメの分類 / 10-fold cross-validation を使った評価 https://www.openml.org/t/59
* Flows
    * 機械学習アルゴリズムの実装。
    * 例: weka.RandomForest https://www.openml.org/f/65
* Runs
    * Task を Flow で実行した結果。
    * 例: アヤメの分類の Task を weka.RandomForest の Flow で実行した例: https://www.openml.org/r/6466

ややこしい。。。図示するとこんな感じかと思います。

![openml.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35350/9ebb712a-a52a-4413-8f23-fd4aeb487dc6.png)


### 動かしてみる

実は OpenML.org のドキュメントの構成が悪く動かないコードが混ざっているので苦労した。。。意味はさっぱりわからないがとりあえず https://docs.openml.org/Python-examples/ にあるコードは動いたので紹介しておく。このコードを実行するには `pip install sklearn openml` などでライブラリをインストールしておく必要がある。

```python
# License: BSD 3-Clause

import openml
from sklearn import neighbors

# テストサーバを指定する。これで openml.org のアカウントを持っていなくても実行出来る。
openml.config.start_using_configuration_for_example()

task = openml.tasks.get_task(403)
data = openml.datasets.get_dataset(task.dataset_id)
clf = neighbors.KNeighborsClassifier(n_neighbors=5)
run = openml.runs.run_model_on_task(clf, task, avoid_duplicate_runs=False)

# テストサーバに結果を保存する。
myrun = run.publish()
print(f"kNN on {data.name}: http://test.openml.org/r/{myrun.run_id}")

# テストサーバ指定の解除
openml.config.stop_using_configuration_for_example()
```

では、アカウント無しでも出来る範囲でもうちょっと詳しくやってみる。

### Data

以下にアヤメのデータを取得するコードを示す。これは簡単。

```python
import openml

# ダウンロード 61 はアヤメの番号 (https://www.openml.org/d/61)
dataset = openml.datasets.get_dataset(61)

# 中から特徴量(X), 教師データ(y), カテゴリ量のフラグ(categorical_indicator), 特徴名(attribute_names) を取得する。
X, y, categorical_indicator, attribute_names = dataset.get_data(
    dataset_format='dataframe',
    target=dataset.default_target_attribute
)
```

データは `~/.openml` にキャッシュされます。

詳しくは https://openml.github.io/openml-python/develop/examples/20_basic/simple_datasets_tutorial.html を見て下さい。

### Task

実は Data は Task 定義に含まれるので、Task さえ取得すれば上のようにあえて get_dataset() する必要は無い。
アヤメの分類問題 (https://www.openml.org/t/59) は次のように取得する。

```python
task = openml.tasks.get_task(59)
task
```

```text
OpenML Classification Task
==========================
Task Type Description: https://www.openml.org/tt/1
Task ID..............: 59
Task URL.............: https://www.openml.org/t/59
Estimation Procedure.: crossvalidation
Evaluation Measure...: predictive_accuracy
Target Feature.......: class
# of Classes.........: 3
Cost Matrix..........: Available
```

### Run

LogisticRegression でこの問題を試してみる。

```python
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

clf = LogisticRegression(random_state=0, max_iter=200)
run = openml.runs.run_model_on_task(clf, task, avoid_duplicate_runs=False)
```

この `avoid_duplicate_runs` というのは、同じ Task と Flow (この場合 LogisticRegression) の組み合わせを避ける仕組みで、アカウントが必要なので False にした。

これで Task 定義どおり 10-fold cross-validation を行った事になっているらしい。実際に accuracy_score がどうなってるか確かめると

```python
run.get_metric_fn(accuracy_score)
```

```text
array([0.93333333, 1.        , 1.        , 0.93333333, 1.        ,
       0.93333333, 1.        , 0.93333333, 0.93333333, 1.        ])
```

それっぽい値が返る。という事で、分かったような気がする。