[機械学習入門]ロジスティック回帰分析でアヤメ(iris)を分類してみました

# この記事について #
機械学習の勉強を始めたので、勉強した内容を忘れないためにまとめました。この記事ではロジスティック回帰モデルを使用してirisの分類問題を解くまでの流れについてまとめてます。初心者ですので、間違いがあると思いますので、もし発見された場合にはご指摘いただけると幸いです。

詳細はGitHubに投稿していますので、[こちらのnotebookをご覧ください](https://github.com/kuboshu/ML-scikit-learn-logistic_regression/blob/master/logistic.ipynb)。

# 機械学習の流れ #

1. データの読み込み
2. データ前処理(データ分割、標準化)
3. 主成分分析による特徴量の選択
4. ロジスティック回帰モデルの学習(グリッドサーチによるハイパーパラメータの調整)
5. モデルの評価

# 1. データの読み込み #
irisデータは有名な機械学習の例題データでscikit-learnに含まれているのでそれを使用します。scikit-learnの関数でデータを呼び出した後にpandasのDataFrameに変換します。最後に読み込んだデータの先頭と最後尾の５つのデータと、データの統計量を表示しています。

```python
from sklearn.datasets import load_iris
from IPython.display import display
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import pandas as pd
import warnings 
warnings.filterwarnings("ignore")

iris_data = load_iris()
iris_df = pd.DataFrame(iris_data.data, columns=iris_data.feature_names)
iris_df["target"] = iris_data.target

#データを表示
display(iris_df.head())
display(iris_df.tail())

#データの統計量の表示
display(iris_df.describe())
```

これを実行すると、先頭と最後尾の５つ分のデータと統計量が表示されます。次の２つの表がそれぞれ先頭と最後尾の５つのデータです。

![fig1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227731/ae4f9ade-1f0c-17e6-3464-457a2acf0f43.png)
また、データの統計量は以下の様になります。
![fig2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227731/f1402cbb-8cd5-e00c-81cd-fc91156dc9ae.png)

# 2. データ前処理 #
アルゴリズムに投入するデータのスケールを合わせるためにデータの標準化を行いました。その後データをモデル学習用と検証用の２つ分けました。決定木など一部のアルゴリズムではスケールを合わせないでも機能するものがありますが、基本的にはスケールは合わせないと正しい結果を得られません。

```python
#学習用(&検証用)データとテスト用データを分ける
x_train, x_test, y_train, y_test = train_test_split(
    iris_df.iloc[:, 0:4],
    iris_df.iloc[:, 4],
    test_size = 0.3,
    random_state=1
)

#データの標準化
scl = StandardScaler()
scl.fit(x_train) #学習用データで標準化
x_train_std = scl.transform(x_train)
x_test_std = scl.transform(x_test)
```

# 3. 主成分分析による特徴量の選択 #
ここでは、精度や学習効率を向上させるために機械学習のモデルに投入する特徴量の選択を行います。特徴量の選択には様々な手法が用いられるのですが、ここでは[参考文献[1]](https://bookmeter.com/books/10885379)に記載されていた主成分分析(以下、PCA)を利用しました。PCAを利用した特徴量選択では基本的に、元の特徴量からデータの分散が最大となる特徴量軸を新たに作成し、その軸に元データを射影した結果をモデルに投入します。**ただし今回は、新しい特徴量軸に射影したデータを用いるのでは無く、PCAを利用して元の特徴量同士の類似度を調べて、類似する特徴量を除外する方法を取ってみました。**

PCAではデータの分散が最大となるように新しい特徴量を作成します。詳細については参考文献[3][4]を参照してください。では、scikit-learnを用いた主成分分析を実施してみます。まずは、新しく作成した４つの特徴量(pc1, pc2, pc3, pc4)の寄与率を計算して、使用する特徴量の数を決定します。

```python
from sklearn.decomposition import PCA
import numpy as np

pca=PCA()
pca.fit(x_train_std)
# display(pca.explained_variance_ratio_)
tot = sum(pca.explained_variance_ratio_)
print("寄与率:", str(["{:.1f}%".format(val * 100/tot)  for val in pca.explained_variance_ratio_]))
```

このコードを実行した出力は
　　寄与率: ['72.2%', '23.5%', '4.0%', '0.4%']
となります。これによって、上位２個の特徴量で95%以上の寄与率を占めていることがわかります。よって、今回は２つの特徴量を使用することにします。

この新しい２つの特徴量(pc1, pc2)にデータを射影した結果をグラフ化してみます。そのためのコードは以下の通りです。

```python
import matplotlib.pyplot as plt
pca = PCA(n_components=2)
pca.fit(x_train_std)
x_train_pca = pca.transform(x_train_std)

def show_scatter(x, y):
    markers = ["s", "x", "v"]
    for idx, label in enumerate(np.unique(y)):
        plt.scatter(
            x=x[y == label, 0], 
            y=x[y == label, 1],
            marker = markers[idx],
            label = label
        )
    plt.grid()
    plt.xlabel("pc1")
    plt.ylabel("pc2")
    plt.show()

print("データをpc1, pc2に射影した図")
show_scatter(x_train_pca, y_train)
```

結果は下図の様になります。図を見ると確かにpc1(寄与率73%)でうまく分類できそうです。そしてpc2を加えることで更に精度よく分類できそうです。
![fig3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227731/35597ae7-cd79-d881-0d6e-d3122184aaa4.png)

では、参考文献[3]を参考に各特徴量の類似度を調べてみます。本当は、各特徴量を新しい特徴量空間に射影したベクトルの内積などの定量的な類似度で議論する必要があるのでしょうが、今回は簡易的に分布の見た目から決めたいと思います。


```python
# 各特徴量を単位ベクトルで表現する
# 上から順に"sepal length", "sepal width", "petal length", "petal width"
axis = np.array(
    [[1, 0, 0, 0],
    [0, 1, 0, 0],
    [0, 0, 1, 0],
    [0, 0, 0, 1]],
    np.float
)

# 特徴量の単位ベクトルを新しい特徴量空間に射影する
axis_pca = pca.transform(axis)
markers = ["s", "v", "x", "o"]
for idx, val in enumerate(range(4)):
    plt.scatter(
        x = axis_pca[idx, 0], 
        y = axis_pca[idx, 1],
        marker = markers[idx],
        label = iris_df.columns[idx]
    )
plt.legend()
plt.xlabel("pc1")
plt.ylabel("pc2")
plt.xlim(-1, 1)
plt.ylim(-1, 1)
plt.grid()
plt.show()
```

元の特徴量を新しい特徴量軸(pc1, pc2)に射影した結果が下図になります。４つの点が元々の特徴量の単位ベクトルの射影結果で、それぞれ近いほど類似した情報です。図を見ると３つの特徴量(sepal length, petal length, petal width)が類似しているので、３つの中からpetal lengthを使用することにします。よって、使用する特徴量は**sepal width**と**petal length**の２つになりました。

![fig4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227731/41834006-f556-7836-8a62-f7db34bacb06.png)

# 4. ロジスティック回帰モデルの学習 #
ロジスティック回帰モデルに学習データを適用してモデルの学習を実行します。この際グリッドサーチを使用してハイパーパラメータの調整も行いました。今回は調整したパラメータは、正則化に関係する"C"というパラメータです。

```python
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
params = {"C": [0.001, 1, 10 , 100, 1000]}
rg = LogisticRegression()
grid = GridSearchCV(
    estimator = rg,
    param_grid = params,
    cv = 5
)
grid.fit(x_train_std[:, [1, 2]], y_train)
print("{:.2f}%".format(grid.score(x_train_std[:, [1, 2]], y_train) * 100))
```

# 5. モデルの評価 #
検証用データを使用してモデルの正解率を算出して、モデルの評価を行いました。その結果、正解率95.6%となりました。

```python
rom sklearn.metrics import accuracy_score
print( "正解率:{:.2f}%".format(accuracy_score(y_test, grid.predict(x_test_std[:, [1, 2]])) * 100 ))
```

# 6. まとめ #
今回はロジスティック回帰モデルを使用して基本的な分類問題を解く一連の流れを試しました。今後は様々なアルゴリズムを使用して精度の違いなどを調べてみたいと思います。



# 7. 参考文献 #
1. [Python機械学習プログラミング 達人データサイエンティストによる理論と実践](https://bookmeter.com/books/10885379)
2. [はじめてのパターン認識](https://bookmeter.com/books/5148345)
3. [主成分分析の考え方](https://logics-of-blue.com/principal-components-analysis/)
4. [意味がわかる主成分分析](https://qiita.com/NoriakiOshita/items/460247bb57c22973a5f0)