【scikit-learn】教師あり学習:高次元データの予測-6【Python】

本ページでは以下のページを要約するとともに、個人的な解説も記載しています。独特な解釈をしている部分があるので、誤りなどの指摘はtwitterまでお願いします。

参考ページ
Supervised learning: predicting an output variable from high-dimensional observations — scikit-learn 1.1.2 documentation
https://scikit-learn.org/stable/tutorial/statistical_inference/supervised_learning.html
閲覧日:2022年10月16日

サポートベクターマシン (SVM)

線形SVM

サポートベクターマシンは分類用のアルゴリズムです。サポートベクターマシンは、二つのクラスの間隔を最大化するように境界線を引いて分類を行うアルゴリズムです。下記の画像であれば、黒の実線を境界に、赤と青のクラスに分類をしています。赤と青の点で強調されている点がありますが、それらからの距離が最大になるように境界線を引きます。
正則化はパラメータCを用いて設定されます。Cは0以上の値で、値が小さいほど正則化の強さが大きくなります。

グラフを書くためのコードはこちら

正則化されていないグラフ(C=1)

正則化を加えたグラフ(C=0.05)

※参考にしたページには下のグラフがデフォルトと表記されていますが、SVMでハイパーパラメータを設定しないとC=1となるはずなのでデフォルトは上のグラフになると思います。謝っていたら指摘してください。

例:アヤメのデータセットをSMVを使って分類する

データはこちら

SVMは分類であればSVC、考え方を応用し回帰に使用できるようにしたSVRで使用することができます。
今回は分類のタスクですので、SVCを使用します。

from sklearn import svm
from sklearn.datasets import load_iris
'''
#データをロード済みの場合はコメントアウトしたまま。
iris_X, iris_y = datasets.load_iris(return_X_y=True)
# 分割する前に配列をランダムに並べ替えています。
np.random.seed(0)
indices = np.random.permutation(len(iris_X))
#後ろの10個のデータをテストデータとして使用
iris_X_train = iris_X[indices[:-10]]
iris_y_train = iris_y[indices[:-10]]
iris_X_test = iris_X[indices[-10:]]
iris_y_test = iris_y[indices[-10:]]
'''
svc = svm.SVC(kernel='linear')
svc.fit(iris_X_train, iris_y_train)
#SVC(kernel='linear')

注意:データの正規化 SVMを含む多くのアルゴリズムは、各特徴量を正規化、標準化しなければ適切な予測を行うことができません。

カーネルの使用

クラスは必ず直線、平面で分離できるとは限りません。SVMでは、そのような時でも分離が可能になるように既存の特徴量を計算し、新たな特徴量を増やします。特徴量を増やすことにより、例えば2次元を3次元にすることにより線形分離可能になることがあります。これを線形分離可能になるまで繰り返します。詳細は本ブログのこちらの記事でも解説しています。
どのように特徴量を増やすのかによって、境界の引き方も代わります。その特徴量の増やし方のことをカーネル関数と呼びます。カーネル関数もSVMのハイパーパラメータとして指定します。
実際にカーネルを関数を変えた時のグラフが次になります。

※グラフを書くコードはこちら

線形カーネル関数

svc = svm.SVC(kernel='linear')

多項式カーネル関数

svc = svm.SVC(kernel='poly',degree=3)
# degree: 多項式の次数

RBFカーネル関数

svc = svm.SVC(kernel='rbf')
# gamma: 簡単に言うと境界の複雑さを表すハイパーパラメータです。
# 大きいほど境界線が複雑になります。⇨過学習を起こしやすくなる。

こちらを使用すると、GUI操作でハイパーパラメータの操作を行うことができます。

練習

アヤメのデータセットの最初の2つの特徴を使用して、クラス1と2を分類してみましょう。各クラスの10%を除外しテストデータとし、テストデータでスコアを求めてみましょう。

注意:元のデータの下位10% をテストデータとすると1つのデータでしかテストできません。(クラス順にデータが並んでいるため)トレーニングデータを分割するときは注意しましょう。

参考解答例:元はこちら

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets, svm

iris = datasets.load_iris()
X = iris.data
y = iris.target

X = X[y != 0, :2]
y = y[y != 0]

n_sample = len(X)

np.random.seed(0)
order = np.random.permutation(n_sample)
X = X[order]
y = y[order].astype(float)

X_train = X[: int(0.9 * n_sample)]
y_train = y[: int(0.9 * n_sample)]
X_test = X[int(0.9 * n_sample) :]
y_test = y[int(0.9 * n_sample) :]

# fit the model
for kernel in ("linear", "rbf", "poly"):
    clf = svm.SVC(kernel=kernel, gamma=10)
    clf.fit(X_train, y_train)

    plt.figure()
    plt.clf()
    plt.scatter(
        X[:, 0], X[:, 1], c=y, zorder=10, cmap=plt.cm.Paired, edgecolor="k", s=20
    )

    # Circle out the test data
    plt.scatter(
        X_test[:, 0], X_test[:, 1], s=80, facecolors="none", zorder=10, edgecolor="k"
    )

    plt.axis("tight")
    x_min = X[:, 0].min()
    x_max = X[:, 0].max()
    y_min = X[:, 1].min()
    y_max = X[:, 1].max()

    XX, YY = np.mgrid[x_min:x_max:200j, y_min:y_max:200j]
    Z = clf.decision_function(np.c_[XX.ravel(), YY.ravel()])

    # Put the result into a color plot
    Z = Z.reshape(XX.shape)
    plt.pcolormesh(XX, YY, Z > 0, cmap=plt.cm.Paired)
    plt.contour(
        XX,
        YY,
        Z,
        colors=["k", "k", "k"],
        linestyles=["--", "-", "--"],
        levels=[-0.5, 0, 0.5],
    )
    #スコアを表示
    print(kernel,":",clf.score(X_train, y_train))
    plt.title(kernel)
plt.show()