はじめに
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次元のデータでいろいろ突っ込んでみると面白いと思います。実際には全体の数割くらいのデータしか使わないで学習している様子がわかります。