scikit-learn で最低限の自作推定器(Estimator)を実装する

# やりたいこと
scikit-learn はPythonのほぼデファクトの機械学習ライブラリです．scikit-learnの利点としては多くのアルゴリズムが実装されていることもそうですが，一貫した形で設計されており様々なアルゴリズムを共通したかたちで扱えることです．scikit-learnにないアルゴリズムを新たに実装したり，他のライブラリを使用するときにsciki-learnの他の推定器と同様に扱えるよう実装すれば，もともと実装されている推定器同様にクロスバリデーションで性能を評価したりグリッドサーチでパラメータを最適化したりできます．ここでは最低限の推定器の実装を示します．ここでは識別器または回帰器をターゲットとして考えます(クラスタリングとか教師なし学習とかは考えない)．

# べたな実装

```py3
from sklearn.base import BaseEstimator

class MyEstimator(BaseEstimator):
    def __init__(self, param1, param2):
        self.param1 = param1
        self.param2 = param2
    
    def fit(self, x, y):
        return self 
    
    def predict(self, x):
        return [1.0]*len(x) 
    
    def score(self, x, y):
        return 1
    
    def get_params(self, deep=True):
        return {'param1': self.param1, 'param2': self.param2}

    def set_params(self, **parameters):
        for parameter, value in parameters.items():
            setattr(self,parameter, value)
        return self
```

`sklearn.base.BaseEstimator`を継承して推定器クラスを定義します．メソッドの中身は適宜書き換えて下さい．
##実行例
Cross validation:

```py

x = [[2,3],[4,5],[6,1],[2,0]] 
y = [0.0,9.4,2.1,0.9]

estimator = MyEstimator()
cross_validation.cross_val_score(estimator,x,y,cv=3)
```

Result:

    array([ 1.,  1.,  1.])

Grid search:

```py
gs = grid_search.GridSearchCV(estimator, {'param1': [0,10], 'param2': (1, 1e-1, 1e-2)})
gs.fit(x,y)
gs.best_estimator_, gs.best_params_, gs.best_score_
```

Result:

    (MyEstimator(), {'param1': 0, 'param2': 1}, 1.0)

## cross_validation
cross_validationを行なう為には訓練データを学習する`fit`メソッドとテストデータを入力しそこから推定した値と正解の値を比較してスコアを出力する`score`メソッドが必要です．
### `fit(self, x, y)`
入力`x`に対して出力が`y`となるように学習する関数です．
### `predict(self, x)`
入力`x`に対して出力が`y_pred`を返す関数です．`cross_validation`をするだけなら`predict`は必要ありませんが，多くの場合`score`内部で`predict`を呼ぶでしょう．`sklearnbase.ClassifierMixin`や`scikit-learn.base.RegressionMixin`を多重継承することで`predict`のみを実装することで，実装済みの`score`関数が使えます．
### `score(self, x, y)`
入力`x`に対して出力`y_pred`を推定し,`y_pred`と正解`y`とを比較してスコア(誤差とかラベルが一致しているかなど)を返す関数です．
## grid_search
`grid_search`を行う為には，上で定義したように学習してスコアを計算する他にパラメータを操作する必要があります．データに依存しないパラメータを取得するメソッド`get_params`とパラメータをセットするメソッド`set_params`を実装します.
### `get_params(self, deep=True)`
`get_params`メソッドはパラメータのkeyがattribute名．valueが値であるような辞書を返すようにします．
### `set_params(self, **parameters)`
パラメータのセッタです．`get_params`同様に辞書で渡します．
## `Mixin`について
識別モデルの場合`sklearn.base.ClassifierMixin`，回帰モデルの場合`sklearn.base.RegressorMixin`を多重継承することで実装済のメソッドが使える.
これらを継承すると

* `sklearn.base.ClassifierMixin`
 * attribute`_estimator_type`に`classifier`または`regressor`をセット
 * 定義済み`score`メソッドが使える．`score`メソッド内で`predict`メソッドを呼ぶので`predict`メソッドは実装する必要がる．
 * スコアはaccuracy
* `sklearn.base.RegressorMixin`
 * attribute`_estimator_type`に`regressor`をセット
 * 定義済み`score`メソッドが使える．`score`メソッド内で`predict`メソッドを呼ぶので`predict`メソッドは実装する必要がる．
 * スコアは決定係数 $R^2$

## releaseにむけて
`sklearn.utils.estimator_checks.check_estimator`で自前のestimatorがsklearnに適合するかどうかがチェックできます．ちなみにこの記事で示したサンプルだと入力のvalidationができていないというエラーをはきます．自分が使うぶんには問題はないでしょうけど．
# まとめ
- 自前のestimatorクラスは`sklearn.base.BaseEstimator`を継承してつくる
- `cross_validation`するためには`fit`,`score`メソッドが必要
- `grid_search`するためには，さらに`get_params`,`set_params`メソッドが必要
- `ClassifierMixin`または`RegressorMixin`を定義すると自分で実装した`predict`を使ってスコアを計算する，`score`メソッドが使える．

# 参考
ここに書いたものはほぼ
[sklearn.baseモジュールのAPI Reference](http://scikit-learn.org/stable/modules/classes.html#module-sklearn.base)
[公式サイトの開発者向け情報](http://scikit-learn.org/stable/developers/contributing.html#rolling-your-own-estimator)
を参考にしています.