複数の機械学習モデルを使用した教師あり学習（分類）について

**はじめに**
転職活動をするためにプログラミングの知識があると便利かと思い、Aidemyのデータ分析講座を受講することにしました。知識をつけていくことは難しかったですが、コースの中で教師あり学習（分類）の理解度が高いと思い、成果物の作成に至りました。
****
**概要**
今回使用する機械学習モデルはSVC・K近傍法・決定木であり、それぞれ
>``SVC``：データの境界線を見つけ、データの分類を行うモデルです。
>``K近傍法``：予測をするデータと類似したデータを見つけ、多数決により分類結果を決めるモデルです。
>``決定木``：データの要素内での値を境にデータを分割し、属するクラスを決定するモデルです。
>>Aidemy 教師あり学習（分類）より引用

というような違いのある機械学習モデルになっています。

教師あり学習（分類）の添削課題ではSVCモデルに固定されているため他のモデルとの比較や、データに最適なモデルを見つけることができません。また、モデルが固定されていることで異なる問題への汎用性がないことがわかりました。
ということで今回はSVC以外の機械学習モデルでサーチをして結果を検証してみたいと思います。


（言語：Python、環境：Google Colaboratory）
****
**作成したプログラム**

では、それぞれ３つの機械学習モデルを使ったコードとその結果を見てみましょう。
<details><summary>クリックしてSVCを使用したコードを表示</summary>

```ｐｙthon:qiita.rb
import scipy.stats
from sklearn.datasets import load_digits
from sklearn.svm import SVC
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import RandomizedSearchCV
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score

#手書き数字の画像データの読み込み
digits_data = load_digits()


#読み込んだ digits_data の内容確認
#print(dir(digits_data))

#読み込んだ digits_data の画像データの確認
#import matplotlib.pyplot as plt
#fig , axes = plt.subplots(2,5,figsize=(10,5),
#                         subplot_kw={'xticks':(),'yticks':()})
#for ax,img in zip(axes.ravel(),digits_data.images):
#    ax.imshow(img)
#plt.show()


# グリッドサーチ：ハイパーパラメーターの値の候補を設定
model_param_set_grid = {SVC(): {"kernel":  ["linear", "poly", "rbf", "sigmoid"],
                                "C": [0.001, 0.01, 0.1, 1, 10, 100],
                                "decision_function_shape": ["ovr", "ovo"],
                                "random_state": [42]}}


# ランダムサーチ：ハイパーパラメーターの値の候補を設定
model_param_set_random =  {SVC(): {"kernel": ["linear", "poly", "rbf", "sigmoid"],
                                   "C": scipy.stats.uniform(0.00001, 1000),
                                   "decision_function_shape": ["ovr", "ovo"],
                                   "random_state": [42]}}

#トレーニングデータ、テストデータの分離
train_X, test_X, train_y, test_y = train_test_split(digits_data.data, digits_data.target, random_state=0)

#条件設定
max_score = 0

#グリッドサーチ
for model, param in model_param_set_grid.items():
    clf = GridSearchCV(model, param)
    clf.fit(train_X, train_y)
    pred_y = clf.predict(test_X)
    score = f1_score(test_y, pred_y, average="micro")

    if max_score < score:
        max_score = score
        best_param = clf.best_params_
        best_model = model.__class__.__name__

print("グリッドサーチ")
print("ベストスコア:{}".format(max_score))
print("モデル:{}".format(best_model))
print("パラメーター:{}".format(best_param))
print()

#条件設定
max_score = 0

#ランダムサーチ
for model, param in model_param_set_random.items():
    clf =RandomizedSearchCV(model, param)
    clf.fit(train_X, train_y)
    pred_y = clf.predict(test_X)
    score = f1_score(test_y, pred_y, average="micro")

    if max_score < score:
        max_score = score
        best_param = clf.best_params_
        best_model = model.__class__.__name__

print("ランダムサーチ")
print("ベストスコア:{}".format(max_score))
print("モデル:{}".format(best_model))
print("パラメーター:{}".format(best_param))
```
</details>

<details><summary>クリックしてK近傍法を使用したコードを表示</summary>

```ｐｙthon:qiita.rb
import scipy.stats
from sklearn.datasets import load_digits
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import RandomizedSearchCV
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score

# 手書き数字の画像データの読み込み
digits_data = load_digits()

# トレーニングデータ、テストデータの分離
train_X, test_X, train_y, test_y = train_test_split(digits_data.data, digits_data.target, random_state=0)

# グリッドサーチ：ハイパーパラメーターの値の候補を設定
model_param_set_grid = {KNeighborsClassifier(): {"n_neighbors": [3, 5, 7, 9],
                                                 "weights": ["uniform", "distance"],
                                                 "algorithm": ["auto", "ball_tree", "kd_tree", "brute"]}}


# ランダムサーチ：ハイパーパラメーターの値の候補を設定
model_param_set_random = {KNeighborsClassifier(): {"n_neighbors": range(1, 21),
                                                    "weights": ["uniform", "distance"],
                                                    "algorithm": ["auto", "ball_tree", "kd_tree", "brute"]}}


# 条件設定
max_score = 0

# グリッドサーチ
for model, param in model_param_set_grid.items():
    clf = GridSearchCV(model, param)
    clf.fit(train_X, train_y)
    pred_y = clf.predict(test_X)
    score = f1_score(test_y, pred_y, average="micro")

    if max_score < score:
        max_score = score
        best_param = clf.best_params_
        best_model = model.__class__.__name__

print("グリッドサーチ")
print("ベストスコア:{}".format(max_score))
print("モデル:{}".format(best_model))
print("パラメーター:{}".format(best_param))
print()

# 条件設定
max_score = 0

# ランダムサーチ
for model, param in model_param_set_random.items():
    clf = RandomizedSearchCV(model, param, n_iter=10, random_state=42)
    clf.fit(train_X, train_y)
    pred_y = clf.predict(test_X)
    score = f1_score(test_y, pred_y, average="micro")

    if max_score < score:
        max_score = score
        best_param = clf.best_params_
        best_model = model.__class__.__name__

print("ランダムサーチ")
print("ベストスコア:{}".format(max_score))
print("モデル:{}".format(best_model))
print("パラメーター:{}".format(best_param))
```
</details>

<details><summary>クリックして決定木を使用したコードを表示</summary>

```ｐｙthon:qiita.rb
import scipy.stats
from sklearn.datasets import load_digits
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import RandomizedSearchCV
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score

# 手書き数字の画像データの読み込み
digits_data = load_digits()

# トレーニングデータ、テストデータの分離
train_X, test_X, train_y, test_y = train_test_split(digits_data.data, digits_data.target, random_state=0)

# グリッドサーチ：ハイパーパラメーターの値の候補を設定
model_param_set_grid = {DecisionTreeClassifier(): {"criterion": ["gini", "entropy"],
                                                   "max_depth": [None, 10, 20, 30, 40, 50],
                                                   "min_samples_split": [2, 5, 10],
                                                   "min_samples_leaf": [1, 2, 4]}}

# ランダムサーチ：ハイパーパラメーターの値の候補を設定
model_param_set_random = {DecisionTreeClassifier(): {"criterion": ["gini", "entropy"],
                                                      "max_depth": scipy.stats.randint(1, 51),
                                                      "min_samples_split": scipy.stats.randint(2, 11),
                                                      "min_samples_leaf": scipy.stats.randint(1, 5)}}

# 条件設定
max_score = 0

# グリッドサーチ
for model, param in model_param_set_grid.items():
    clf = GridSearchCV(model, param)
    clf.fit(train_X, train_y)
    pred_y = clf.predict(test_X)
    score = f1_score(test_y, pred_y, average="micro")

    if max_score < score:
        max_score = score
        best_param = clf.best_params_
        best_model = model.__class__.__name__

print("グリッドサーチ")
print("ベストスコア:{}".format(max_score))
print("モデル:{}".format(best_model))
print("パラメーター:{}".format(best_param))
print()

# 条件設定
max_score = 0

# ランダムサーチ
for model, param in model_param_set_random.items():
    clf = RandomizedSearchCV(model, param, n_iter=10, random_state=42)
    clf.fit(train_X, train_y)
    pred_y = clf.predict(test_X)
    score = f1_score(test_y, pred_y, average="micro")

    if max_score < score:
        max_score = score
        best_param = clf.best_params_
        best_model = model.__class__.__name__

print("ランダムサーチ")
print("ベストスコア:{}".format(max_score))
print("モデル:{}".format(best_model))
print("パラメーター:{}".format(best_param))
```
</details>

**SVCの結果：**
```ｐｙthon:qiita.rb
グリッドサーチ
ベストスコア:0.9911111111111112
モデル:SVC
パラメーター:{'C': 10, 'decision_function_shape': 'ovr', 'kernel': 'rbf', 'random_state': 42}

ランダムサーチ
ベストスコア:0.9911111111111112
モデル:SVC
パラメーター:{'C': 37.81929074256014, 'decision_function_shape': 'ovo', 'kernel': 'rbf', 'random_state': 42}
```
**K近傍法の結果：**
```ｐｙthon:qiita.rb
グリッドサーチ
ベストスコア:0.9888888888888889
モデル:KNeighborsClassifier
パラメーター:{'algorithm': 'auto', 'n_neighbors': 3, 'weights': 'distance'}

ランダムサーチ
ベストスコア:0.98
モデル:KNeighborsClassifier
パラメーター:{'weights': 'distance', 'n_neighbors': 8, 'algorithm': 'ball_tree'}
```
**決定木の結果：**
```ｐｙthon:qiita.rb
グリッドサーチ
ベストスコア:0.8511111111111112
モデル:DecisionTreeClassifier
パラメーター:{'criterion': 'gini', 'max_depth': 50, 'min_samples_leaf': 1, 'min_samples_split': 2}

ランダムサーチ
ベストスコア:0.8511111111111112
モデル:DecisionTreeClassifier
パラメーター:{'criterion': 'gini', 'max_depth': 44, 'min_samples_leaf': 1, 'min_samples_split': 2}
```
***
**検証結果**

結果を見ると、SVCとK近傍法が高い精度を出していることがわかります。また、他に比べると決定木は精度が低く、過剰適合の傾向があることが考えられます。今回の手書きの数字のデータセットではSVCを使用するのがベストだとわかりました！
しかし、特徴量の多いデータセットなどによっては最適なモデルが変わってくることが考えられるため、一つの機械学習モデルだけではなく複数の機械学習モデルを使用することが重要だと感じました。

```mermaid
graph TD;
    データセット-->SVC;
    データセット-->K近傍法;
　　　　　　　　データセット-->決定木;    
    SVC-->最適なモデル;
    K近傍法-->最適なモデル;
　　　　　　　　決定木-->最適なモデル;   
```
:::note info
*複数の機械学習モデルを使用する利点とは*

1. **多様性**: 各モデルはデータを異なる方法で理解するため、さまざまなデータパターンに適合できます。

2. **信頼性の向上**: 1つのモデルが失敗しても、他のモデルがカバーできるため、予測の信頼性が向上します。

3. **選択の柔軟性**: どのモデルが最適か分からない場合、複数のモデルを試すことで、最適なモデルを見つけやすくなります。


複数のモデルを組み合わせることで、より正確な予測を行うことができるし、安定性も向上します。また、一つのモデルだけでは難しい問題に対処するために有効です。
:::
****

**今後の活用**
今回行った複数の異なる機械学習モデルでの分類は手書き数字データセット以外のデータセットへの適用や回帰問題やクラスタリング問題にも適用できます。
また、機械学習の教育やトレーニングの目的にも使用できるため、機械学習の概念を理解し、ハイパーパラメーターの調整の重要性を学ぶために活用していきたいと思います。
****

**おわりに**
私がこの講座を受講し始めた頃、ChatGPTが公開され、半年ほどでプログラミングやAIを使ったものが目まぐるしく進化を遂げました。私自身、進化していく技術についていくことはまだまだできていませんが、これからの時代でプログラミングを理解している、理解していないでは大きな差が付くと感じました。AIが発展していき、今ある仕事などが奪われる場合、AIを使える知識を有していることで新たな需要が生まれ、キャリアの築き方が変わっていくのではないでしょうか？
今回学んだことを活かし、また、更に勉強を続け、時代に取り残されないように、良いキャリアを築けるように頑張って行きたいと思います。