scikit-learn 決定木によるsin関数回帰サンプル

# scikit-learn備忘録
pythonの機械学習ライブラリ、scikit-learnの学習を開始したので、備忘録として記事を残します。
## 決定木による回帰サンプル
決定木に関する学習を開始するために、まずは回帰のサンプルを学習します。

環境はAnaconda3 python3.8で専用の環境を構築して行いました。
あらかじめscikit-learn関係のライブラリとして

+ scikit-image
+ scikit-learn
+ scikit-rf

をインストールしておきます。

実行環境はjupyterです。

これはsin波に決定木で回帰をするサンプルコードです。
[Decision Tree Regression](https://scikit-learn.org/stable/auto_examples/tree/plot_tree_regression.html#decision-tree-regression)

```python
print(__doc__)

# Import the necessary modules and libraries
import numpy as np
from sklearn.tree import DecisionTreeRegressor
import matplotlib.pyplot as plt

# Create a random dataset
rng = np.random.RandomState(1)
X = np.sort(5 * rng.rand(80, 1), axis=0)
y = np.sin(X).ravel()
y[::5] += 3 * (0.5 - rng.rand(16))

# Fit regression model
regr_1 = DecisionTreeRegressor(max_depth=2)
regr_2 = DecisionTreeRegressor(max_depth=5)
regr_1.fit(X, y)
regr_2.fit(X, y)

# Predict
X_test = np.arange(0.0, 5.0, 0.01)[:, np.newaxis]
y_1 = regr_1.predict(X_test)
y_2 = regr_2.predict(X_test)

# Plot the results
plt.figure()
plt.scatter(X, y, s=20, edgecolor="black",
            c="darkorange", label="data")
plt.plot(X_test, y_1, color="cornflowerblue",
         label="max_depth=2", linewidth=2)
plt.plot(X_test, y_2, color="yellowgreen", label="max_depth=5", linewidth=2)
plt.xlabel("data")
plt.ylabel("target")
plt.title("Decision Tree Regression")
plt.legend()
plt.show()
```

このコードを読んでみることにします。

## 環境の情報
```python
print(__doc__)
```
これは**ドキュメントスリング**と呼ばれるもので、モジュールやメソッドのドキュメント、要するに説明を参照できるコードですが、内容は全開もやったので省略します。

## 必要なライブラリとモジュールのインポート

```python
# Import the necessary modules and libraries
import numpy as np
from sklearn.tree import DecisionTreeRegressor
import matplotlib.pyplot as plt
```
まず数値計算ライブラリの[numpy](https://numpy.org/)、

[決定木](https://scikit-learn.org/stable/modules/classes.html#module-sklearn.tree)の決定木回帰分析[sklearn.tree.DecisionTreeRegressor](https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeRegressor.html#sklearn.tree.DecisionTreeRegressor)、

pythonでグラフを表示する際に使う[matplotlib.pyplot](https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.html)をimportしています。

## 誤差を含めたデータセットの生成
```python
# Create a random dataset
rng = np.random.RandomState(1)
X = np.sort(5 * rng.rand(80, 1), axis=0)
y = np.sin(X).ravel()
y[::5] += 3 * (0.5 - rng.rand(16))
```
まず[class numpy.random.RandomState(seed=None)](https://numpy.org/doc/stable/reference/random/legacy.html?highlight=random%20randomstate#numpy.random.RandomState)で疑似乱数（メルセンヌツイスター）のシードを1から生成します。疑似乱数というのがポイントで、これは正確な乱数ではなく、計算すると乱数のような値を出す計算式が内部で走っています。本物の乱数ではなく、疑似乱数を利用するのは、再現性の問題です。本物の乱数を使うと、実行の度に結果が異なるようになってしまい、アルゴリズムの問題等がわかりずらくなってしまうので、シードが同じなら常に同じ反応が返ってくる疑似乱数を使います。

次にシードからデータXを生成します。[numpy.random.rand]
(https://numpy.org/doc/stable/reference/random/generated/numpy.random.rand.html?highlight=rand#numpy.random.rand)では与えた引数の次元の配列が生成されるので、この場合は80行1列の配列が生成され、次にようになります。

![80行乱数.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216739/3a2ae2c7-cf3e-6f51-9e35-7970036e9f12.png)


この乱数の配列を5倍して、numpyのソートをかけます。[numpy.sort](https://numpy.org/doc/stable/reference/generated/numpy.sort.html?highlight=numpy%20sort#numpy.sort)
ソートの対象は乱数配列,ソートの軸は配列インデックスの0。これにより昇順にソートされます。
![npsort.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216739/b39d385d-aac8-b9c4-3e5f-6281e40c13ee.png)
次はyをXからsin関数で作ります。[numpy.sin](https://numpy.org/doc/stable/reference/generated/numpy.sin.html?highlight=numpy%20sin#numpy.sin)で乱数からsin関数を作った後、[numpy.ravel](https://numpy.org/doc/stable/reference/generated/numpy.ravel.html)で80行1列の2次元配列を80要素の1次元配列にします。

```python
y[::5]
```
これはpythonの配列のスライスで[組み込み型](https://docs.python.org/ja/3/library/stdtypes.html#list)の

>s[i:j:k] s の i から j まで、 k 毎のスライス

のiとjを省略したので、配列の初めから終わりまで、ステップ5のインデックスということになります。つまり、5刻みのインデックスで16個の乱数を0.5から引いて3倍したものを加えているということになります。

これでデータセットの作成が完了しました。

## 学習モデルの作成
```python
# Fit regression model
regr_1 = DecisionTreeRegressor(max_depth=2)
regr_2 = DecisionTreeRegressor(max_depth=5)
regr_1.fit(X, y)
regr_2.fit(X, y)
```
[sklearn.tree.DecisionTreeRegressor](https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeRegressor.html#sklearn.tree.DecisionTreeRegressor)を木の深さ、max_depthの違いで2つ作成して、fitメソッドで学習させています。

## 学習済みモデルによる推測
```ptyhon
# Predict
X_test = np.arange(0.0, 5.0, 0.01)[:, np.newaxis]
y_1 = regr_1.predict(X_test)
y_2 = regr_2.predict(X_test)
```
[numpy.arange](https://numpy.org/doc/stable/reference/generated/numpy.arange.html?highlight=arange#numpy.arange)により0.0から5.0まで0.01刻みの配列を作成します。[numpy.newaxis](https://numpy.org/doc/stable/reference/constants.html?highlight=newaxis#numpy.newaxis)で80行1列の配列にしています。

作成した配列を学習済みモデルにpredictメソッドで読み込ませることで、推測させます。

## グラフの表示
```python
# Plot the results
plt.figure()
plt.scatter(X, y, s=20, edgecolor="black",
            c="darkorange", label="data")
plt.plot(X_test, y_1, color="cornflowerblue",
         label="max_depth=2", linewidth=2)
plt.plot(X_test, y_2, color="yellowgreen", label="max_depth=5", linewidth=2)
plt.xlabel("data")
plt.ylabel("target")
plt.title("Decision Tree Regression")
plt.legend()
plt.show()
```
まずグラフオブジェクトを作成します。

```python
plt.figure()
```
[matplotlib.pyplot.figure](https://matplotlib.org/3.3.3/api/_as_gen/matplotlib.pyplot.figure.html#matplotlib.pyplot.figure) 


そしてXとyの散布図をデータセットから描画します。

```python
plt.scatter(X, y, s=20, edgecolor="black",c="darkorange", label="data")
```
[matplotlib.pyplot.scatter](https://matplotlib.org/3.3.3/api/_as_gen/matplotlib.pyplot.scatter.html#matplotlib.pyplot.scatter)　マーカーサイズはs=20、マーカーのエッジの色は黒、マーカーの色はダークオレンジ、 凡例はdataにします。

これにより、5つ毎にランダムなデータの入ったsin波がダークオレンジの点で描写されます（内部で描写されただけで表示されていません）。

深さ2の決定木による推測データを描画します。

```python
plt.plot(X_test, y_1, color="cornflowerblue",
         label="max_depth=2", linewidth=2)
```
[matplotlib.pyplot.plot](https://matplotlib.org/3.3.3/api/_as_gen/matplotlib.pyplot.plot.html#matplotlib.pyplot.plot)　色は青、ラベルはmax_depth=2、線の幅は2にします。

同様に深さ5の決定木による推測データを描画します。

```python
plt.plot(X_test, y_2, color="yellowgreen", label="max_depth=5", linewidth=2)
```
こちらは色は黄緑、ラベルはmax_depth=5、線の幅は2にします。

グラフ自体のラベル等を設定します

```python
plt.xlabel("data")
plt.ylabel("target")
plt.title("Decision Tree Regression")
```

[matplotlib.pyplot.xlabel](https://matplotlib.org/3.3.3/api/_as_gen/matplotlib.pyplot.xlabel.html#matplotlib.pyplot.xlabel)
[matplotlib.pyplot.ylabel](https://matplotlib.org/3.3.3/api/_as_gen/matplotlib.pyplot.ylabel.html#matplotlib.pyplot.ylabel)
[matplotlib.pyplot.title](https://matplotlib.org/3.3.3/api/_as_gen/matplotlib.pyplot.title.html#matplotlib-pyplot-title)
グラフのxラベルはdata,yラベルはtarget,グラフのタイトルはDecision Tree Regressionに設定します。


```python
plt.legend()
plt.show()
```
[matplotlib.pyplot.legend](https://matplotlib.org/3.3.3/api/_as_gen/matplotlib.pyplot.legend.html#matplotlib.pyplot.legend)　グラフに凡例を描画します。
[matplotlib.pyplot.show](https://matplotlib.org/3.3.3/api/_as_gen/matplotlib.pyplot.show.html#matplotlib.pyplot.show)　最後に内部で描画されていたグラフを表示します。

これで以下のような画像が表示されました。

![決定木.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216739/db94c5aa-792e-f8ac-597e-14a242a3d80d.png)


## 考察

+ 決定木の深さを深くすると外れ値を拾うようになり、オーバーフィットの傾向が出てくる

+ 決定木でsin関数の近似は難しい

決定木は連続したデータには向いておらず、非連続なデータに適していることが見て取れました。
離散的なデータに使用することで、閾値で分類することを容易に行えると思われます。