scikit-learnのAPIデザインの一貫性

# はじめに
scikit-learnはPythonの便利な機械学習ライブラリで、Numpy・Scipy・Matplotlibと一緒に気軽に使うことができます。
scikit-learnは、APIのデザインパターンを知ると飛躍的に使いやすくなります。

今回はscikit-learnの著者による[論文](https://arxiv.org/pdf/1309.0238.pdf)をもとに、scikit-learnの魅力を解説します。

# 基本デザイン
scikit-learnのオブジェクトはAPIの一貫性を保つため、いくつかのパターンに従って設計されています。
**このパターンを理解することでどのオブジェクトでも不自由なく使うことが出来ます。**

## Estimator
scikit-learnではEstimatorというインターフェースが基本となっています。
**Estimatorは、データに基づき何らかのモデル（のパラメータ）を学習させます。**
必ず`fit`というメソッドを持っていて、`fit`の引数にデータを流すことで学習を行います。
また、学習に必要なハイパーパラメータを（コンストラクタまたは`set_params`メソッドで）設定することができます。

ロジスティック回帰を行う`LogisticRegression`というクラスもEstimatorの一つです。

```python
from sklearn.linear_model import LogisticRegession
clf = LogisticRegression(penalty="l1") # ハイパーパラメータの設定
clf.fit(X_train, y_train) # 訓練データに基づきモデルを学習
```

## Predictor
Estimatorの多くは同時にPredictorのインターフェスを導入しています。
**Predictorは`fit`で学習したモデルをもとに予想（出力）を行います。**
`predict`のメソッドにデータを引数として渡すと、予想が返されます。
また、`score`というメソッドを持っており、データセットとラベルを渡すことでモデルを評価できます。

例えば、`LogisticRegression`はPredictorなので、`predict`、`score`のメソッドを問題なく使えます。

```python
clf.predict(X_test) # テストデータについて予測
```

```python
clf.score(X_test, y_test) # テストデータについての予想と実際の答えを比較
```

## Transformer
Predictorの他に、Transformerというインターフェスを導入するクラスもあります。
名前にある通り、**Transformerはデータを変形することができます。**
機械学習モデルよりはデータ処理のAPIにおいて多用されます。
`transform`というメソッドを使って変形されたデータを返します。
また、`fit_transform`というメソッドを使うと、学習と変形を同時に行うことが出来るような設計です。

下の例では、データセットの標準化を行う`StandardScaler`による変形を実装します。
`StandardScaler`の場合、複雑なモデルではなく各特微量の平均と分散を学習します。

```python
from sklearn.preprocessing import StandardScaler 
scaler = StandScaler()
X_train = scaler.fit_transform(X_train) # 訓練データを学習・変形
X_test = scaler.transform(X_test) # テストデータは学習せず変形（訓練データの平均・分散を用いる）
```

また、PredictorとTransformerのインターフェスは同時に導入することができます。

## パラメータ・ハイパーパラメータの取得
自分が設定したハイパーパラメータ、学習されたパラメータはオブジェクトの中に保存されています。（学習されたパラメータの名前はアンダーバーで終わります）
パラメータ・ハイパーパラメータのアクセス方法については、各オブジェクトのドキュメントで「Attributes」を参照してください。

例：`StandardScaler`が学習した平均、分散を取得する

```python
# 前から続く
mean = scaler.mean_
variance = scaler.var_
```

よって、どのEstimatorでも
1. インスタンスを作りハイパーパラメータを設定する
2. `fit`で学習する
3. `predict`、`score`、`transform`などで目的を達成する、学習されたパラメータを確認する
という手順に従うことで、簡単にワークフローを構築することができます。
データ全処理からモデル学習・評価までの実装が全てEstimatorを使って行えます。

# 応用デザイン
## Estimatorの合成
全てのEstimatorが同じメソッドを持っているため、**複数のEstimatorを簡単に合成することができます。**
並列処理の場合`Pipeline`、並行処理の場合`FeatureUnion`を使って合成します。

例えば、データを標準化しロジスティック回帰を行いたい場合、パイプラインを使うことできれいに処理を実装できます。

```python
from sklearn.pipeline import Pipeline
pipe = Pipeline([
    {'std_scaler', StandardScaler()},
    {'log_reg', LogisticRegression()} # transformerの変形したデータを受け取る
])
pipe.fit(X_train, y_train)
pipe.score(X_test, y_test)
```

## Cross ValidationもEstimator
scikit-learnでは`GridSearchCV`や`RandomSearchCV`などといったクラスを使ってハイパーパラメータの検証を行うことができます。
これらも、Estimatorのインターフェスを導入しており、`fit`を使って学習を行います。

例：ロジスティック回帰に最適なハイパーパラメータをGrid Searchを使って求める

```python 
from sklearn.model_selection import GridSearchCV
clf = GridSearchCV(
    estimator=LogisticRegression(),
    param_grid={
        'C' = [1, 3, 10, 30, 100]
    }
)
clf.fit(X_train, y_train) # param_gridにあるハイパーパラメータを一つずつ適用して複数のモデルを学習させる
best_clf = clf.best_estimator_ # 最適なEstimatorをゲット！
```

## 自前のEstimatorを作る
`fit`など、インターフェースに定義されたメソッドを持つクラスを作ることで、**簡単にパイプラインや検証に使うことができます。**
Estimatorを作る場合、`BaseEstimator`を継承し、さらにTransformerなどを作る場合は同時に適当な`Mixin`を継承します。

Transformerの例：

```python 
from sklearn.base import BaseEstimator, TransformerMixin

class MyTransformer(BaseEstimator, TransformerMixin):
  def __init__(self, param_1, param_2):
    # ハイパーパラメータの処理
    self.param_1 = param_1
    # ...

  def fit(self, X, y=None):
    # 処理
    return self 

  def transform(self, X, y=None):
     # Numpy行列の処理
     # X = ...
    return X 

  # fit_transformは自動的にTransformerMixinが実装

transformer = MyTransformer()
X_transformed = transformer.fit_transform(X_train)
```

# 結論
scikit-learnには色々な機械学習手法を実装したオブジェクトが用意されていますが、内容が分からなくてもEstimator、Predictor、Transformerのデザインパターンを理解していれば一通り使えてしまいます。
scikit-learnのAPIは一貫性が高いところが魅力的で、簡単に機械学習を進めることができます。