静かなる名辞

pythonとプログラミングのこと


SVMのsupport vectorを可視化してみた

はじめに

 SVMはヒンジ関数を使ってマージン最大化を行い、境界付近のデータに基づいて分離超平面を決定する……ということはよく言われています。でも、実際のデータで確認している図はあまり見たことがありません。

 sklearnのSVMのドキュメントを読んでいたら、属性からサポートベクトル関連の情報が取れることがわかったので、いつものようにirisで見てみます。

見方

 ここに書いてあります。
sklearn.svm.SVC — scikit-learn 0.21.3 documentation

support_ : array-like, shape = [n_SV]
  Indices of support vectors.

support_vectors_ : array-like, shape = [n_SV, n_features]
  Support vectors.

n_support_ : array-like, dtype=int32, shape = [n_class]
  Number of support vectors for each class.

 これを使うと便利です。support_とsupport_vectors_はどちらを使っても良いのですが、散布図でサポートベクトルとそれ以外の点を分けてプロットしたいという都合上、support_の方を使います。

 なお、小ネタとして、indiciesの配列がある場合、以下のようにすることでboolean maskに変換できます。今回は論理反転を使いたいので、これが使えると便利です。

>>> import numpy as np
>>> a = np.arange(10)
>>> idx = np.where(a > 4)
>>> idx
(array([5, 6, 7, 8, 9]),)
>>> mask = np.zeros(10, dtype="bool")
>>> mask[idx] = True
>>> mask
array([False, False, False, False, False,  True,  True,  True,  True,
        True])
>>> ~mask
array([ True,  True,  True,  True,  True, False, False, False, False,
       False])

 参考:python - How to invert numpy.where (np.where) function - Stack Overflow

コード

 素直にやります。以前やったRGB予測確率プロットも同時に出します。

sklearnとmatplotlibでiris(3クラス)の予測確率を可視化した話 - 静かなる名辞

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn.datasets import load_iris
from sklearn.decomposition import PCA
from sklearn.svm import SVC

def main():
    iris = load_iris()

    # とりあえず二次元に
    X = PCA(n_components=2).fit_transform(iris.data)

    # 学習
    clf = SVC(gamma="scale", probability=True)
    clf.fit(X, iris.target)
    
    # support_をboolean maskにしておく
    support = np.zeros(X.shape[0], dtype="bool")
    support[clf.support_] = True
 
    # --散布図--
    ax = plt.subplot()
    cm = ListedColormap(["b", "g", "r"])

    # サポートベクトルとそれ以外を違うmarkerで
    ax.scatter(X[~support,0], X[~support,1], marker="2",
               c=iris.target[~support], cmap=cm, alpha=0.5)
    ax.scatter(X[support,0], X[support,1], marker=".", 
               c=iris.target[support], cmap=cm)

    # 確率のプロット
    XX, YY = np.meshgrid(np.arange(-5, 5, 0.025),
                         np.arange(-2, 2, 0.025))
    Z = clf.predict_proba(np.stack([XX.ravel(), YY.ravel()], axis=1))
    ZZ = np.flip(Z.reshape(XX.shape + (3, )), axis=1)
    ax.imshow(ZZ, alpha=0.1,
              aspect="auto", extent=(-5, 5, -2, 2))

    plt.savefig("result.png")
    
if __name__ == "__main__":
    main()

結果
結果

 濃いめの点がサポートベクトル、薄い三菱マークがそれ以外です。なるほど、こうなっているのかという発見というか納得感が得られます。あと、よく見るとマージン最大化のため、緑色の領域がずいぶん青い側に寄っているみたいですね。

まとめ

 簡単に見れるので、2次元のデータでいろいろ突っ込んでみると面白いと思います。実際には全体の数割くらいのデータしか使わないで学習している様子がわかります。