【今さら】scikit-learnのテンプレを書き留める

## はじめに
大学3年でRを使い始め早3年。私はTidyverseの沼にどっぷりと浸かって過ごしていました。Pythonにももちろん興味はありましたが、Tidyverseの底なし沼を抜けられるわけもなく・・・
しかし最近、機械学習をもっと幅広く学びたい欲が高まり、ようやくtidyverseの沼から一度出てみようと思ったわけです。

## scikit-learn
scikit-learnとは今さら私が言うまでもなく、様々な機械学習を提供しているPythonライブラリです。主な特徴としては、

**1. 利用できるアルゴリズムが豊富**
scikit-learnは一般的な教師あり学習の回帰/分類は言うまでもなく、クラスタリング/次元削減といった教師なし学習などあらゆるアルゴリズムをサポートしています。

**2. 学習だけではない、前後の処理も提供**
scikit-learnは学習前のデータ分割や学習後のモデル評価など、モデル学習に限らず幅広く処理をサポートしています。

**3. 情報量が豊富**
scikit-learnは「機械学習といえば!!」と言う立場のライブラリであり (これは個人の意見です)、また書店やネット上には関連情報が立ち並んでいて、初心者でも学びやすい環境が整っています (これは事実でしょう)。

## テンプレ
scikit-learnに限らず、機械学習の流れは以下のようになっていることが一般的です (多分)。

1. 訓練データとテストデータに分割
1. モデル式を設定
1. 学習アルゴリズムを指定
1. パラメータチューニング
1. 訓練データをもとに学習
1. テストデータをもとにモデルを評価

scikit-learnにおいてはモデル式は指定しなくても良いらしい (??)。
実際にscikit-learnでランダムフォレストを当てはめるには以下のようなコードになります。

データはirisを利用します。

---

### 訓練データとテストデータに分割
```python
# 定番irisデータの読み込み
from sklearn.datasets import load_iris
iris = load_iris()
# 訓練データとテストデータに分割するtrain_test_split
from sklearn.model_selection import train_test_split 
# x: 説明変数, y: 目的変数
train_x, test_x, train_y, test_y = 
    train_test_split(iris.data, iris.target, test_size = 0.1)
```

### 学習アルゴリズムを指定
scikit-learnの中のensembleの中にRandomForestClassifierがあります。

```python
from sklearn.ensemble import RandomForestClassifier
rforest = RandomForestClassifier()
```

### パラメータチューニング
ここではグリッドサーチを行います。
パラメータは辞書型で用意し、各々に対して配列で指定します。
ここでは、ノード分割に必要とする最小サンプル数のみチューニングを行いますが、参考までに作成する決定木の本数も書いておきます。

各パラメータのデフォルトは以下公式ドキュメントを参考にしてください。
[RandomForestClassifier](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html "RandomForestClassifier")

```python
from sklearn.model_selection import GridSearchCV
# ハイパーパラメータを辞書型で用意
params = {
    'min_samples_split': [2, 5, 10],
    # 'n_estimators': [10, 20, 30]
}
# グリッドサーチを行う
rforest = GridSearchCV(
    rforest,
    params,
    verbose = 2
)

```

### 訓練データをもとに学習
ついでに最適なモデルも選択します。

```python
rforest.fit(train_x, train_y)
# 最適なモデルを選択
best_rforest = rforest.best_estimator_
```

### テストデータをもとにモデルを評価
```python
# テストデータを使った予測
pred_y = best_rforest.predict(test_x)
# モデルの精度を計算
from sklearn.metrics import accuracy_score
accuracy = accuracy_score(test_y, pred_y)
print("モデルの精度: ", accuracy)
# モデルの精度:  0.9333333333333333
```

簡単ですね。

## Tidymodels
ちなみに私が普段利用するR言語には、**Tidymodels** という機械学習をサポートするパッケージが存在します。まだまだ発展途上のものではありますが、名前の通りTidyverseとTidymodelsの相性が最高で、Rユーザーとしては大変心地良いものです。

Tidymodelsではランダムフォレストは以下のように書きます。

```R
library(tidyverse)
library(tidymodels)

# irisデータ
data("iris")

# データの分割
data_split <- initial_split(iris, prop = 0.9)
#
train <- training(data_split)
test <- testing(data_split)

# モデル式の定義 (レシピ)
recipe <- train %>% 
  recipe(formula = Species ~ .)# %>% 
  # 特徴量エンジニアリングはここで設定できる
  # step_normalize(all_predictors())

# アルゴリズムの指定 (スペック)
rforest_spec <- 
  rand_forest(
    min_n = tune(), # ノード分割に必要とする最小サンプル数
    # trees = tune(), # 木の本数
    ) %>%
  set_engine('randomForest') %>%
  set_mode('classification')

# パラメータチューニング
rforest_params <- rforest_spec %>% 
  tune_grid(
	# レシピ
	preprocessor = recipe,
	# クロスバリデーション用のデータ
	resample = vfold_cv(train, v = 5),
	# 探索するパラメータの組み合わせ
    grid = expand.grid(
	  min_n = c(2, 5, 10)
	  # trees = c(10, 20, 30)
	)
  )

# 最適なパラメータの選択とスペックの更新
rforest_best_spec <- rforest_params %>% 
	select_best(metric = "accuracy") %>% 
	finalize_model(x = rforest_spec, parameters = .)

# ワークフローの設定
rforest_flow <- workflow() %>% 
  add_recipe(recipe) %>% 
  add_model(rforest_best_spec)

# 訓練データをもとに学習
rforest_fitted <- rforest_flow %>% fit(data_train)

# テストデータを元に予測
rforest_fit %>% 
	augment(new_data = test) %>% 
	accuracy(Species, .pred_class)
```

Tidymodelsによる学習は、scikit-learnと比較してコードこそ長くなっていますが、dplyrでのデータ操作からそのまま学習に繋げられたり、学習の結果をパイプでggplot2に渡せたりと、Tidyverseとの連携が強いという大きなメリットがあります。

## 最後に

私はこれまで **Tidy** な世界に浸っていましたが、Pythonのscikit-learnを学ぶことで、新たな視点やスキルを得ることができました。R言語も楽しい言語ですが、異なるツールに触れることで自分の可能性を広げられると実感しています。

<br>

日々勉強ですね。