いまさらながらサポートベクターマシーン（SVM）ついてまとめてみた（数式なし）

n番煎じですが、機械学習における分類アルゴリズムであるサポートベクターマシーンについてまとめました。

##サポートベクターマシーンとは
サポートベクターマシーンは分類アルゴリズムの１つで、パーセプトロンの拡張版になります。

サポートベクターマシーンは分類線を決めるときに、

サポートベクターと呼ばれる全体のデータのうち「**予測に必要なデータのみを使って分類する**」アルゴリズムになります。

##サポートベクターとマージン

ここで2つの用語「サポートベクター」と「マージン」ついて説明します。

図のように2つのクラスの分類問題があった場合。

その分類線に最も近い、それぞれのクラスのデータ点のことを**サポートベクター**と呼び、これが予測に必要なデータとなります。

また、各クラスのサポートベクター間の距離のことを**マージン**と呼びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1997363/83fd9235-13fe-0f9e-ae40-f118b30342ff.png)
サポートベクターマシーンでは**このマージンが最大になるよう**分類線を学習し、２つのクラスを分類します。

##サポートベクターマシーンとパーセプトロンの違い

同じ分類アルゴリズムであるサポートベクターマシーンとパーセプトロンですが、

実際に使うときに、どのような性能の違いがあるのでしょうか？

サポートベクターマシーンの強みは、外れ値のあるデータセットに対しても上手く

分類問題を解くことができることになります。

パーセプトロンでは、分類が上手くいかなかったデータの分類線からの距離をモデルの誤差として、その誤差が最小にするように分類線が引かれました。

そのため、一つでもほかのデータから離れた外れ値があると、その外れ値による誤差を小さくしようとして全体の分類が上手くいきません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1997363/c1289fc7-defd-3127-9043-05511f5e2e44.png)




それに対して、サポートベクターマシーンでは、予測に使われるのは分類線近くのサポートベクターだけで、マージンを最大にすることを目的としているので、**分類線から遠く離れた外れ値の影響を、ほとんど受けずに分類することができます**。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1997363/cfa56a59-6f15-bbcf-4d12-407d63b1edfb.png)

##IRISデータセットによるサポートベクターマシーンの結果
実際にIris（アヤメ）の分類用データセットを使ってサポートベクターマシーンとパーセプトロンを使ってみましたので結果をみせます。

・分類するアヤメの種類は2種類（versicolor, virginica)のみに設定しました。
・もともとの特徴量は「ガクの長さ」「ガクの幅」「花弁の長さ」「花弁の幅」の4種類ですが、
　可視化するために主成分分析により特徴量を2種類に削減しました。

```py

import matplotlib.pyplot as plt 
import numpy as np
import pandas as pd 
from sklearn.linear_model import Perceptron
from sklearn.datasets import load_iris
from sklearn.decomposition import PCA
from sklearn import svm
# IRISのデータセットの読み込み
iris = load_iris()

label_names=iris.target_names
feature_names=iris.feature_names
label=iris.target
feature=iris.data

# 簡単のためSentosaクラスを削除する 
label_names=label_names[1:]

feature=feature[label!=0]
label=label[label!=0]


df= pd.concat([pd.DataFrame(label,columns=['label']),pd.DataFrame(feature, columns=feature_names)], axis=1)

# 主成分分析でIRISの特徴量を２つに減らす
pca=PCA(n_components=2)
pca.fit(feature)
feature=pca.transform(feature)

#外れ値を加える
feature=np.vstack((feature,[9.5,0.8]))
label=np.hstack((label,1))

#  SVM ,パーセプトロン分類器の作成
clf_SVM = svm.SVC(kernel='linear')
clf_SVM.fit(feature, label)

clf_per = Perceptron()
clf_per.fit(feature, label)   

clf_list=[clf_SVM,clf_per]
clf_name=['SVM', 'Perceptron']

for clf, name in zip(clf_list,clf_name):
    fig, ax = plt.subplots(figsize=(8, 6))
    ax.set_title(f'Iris data Classification using {name}')
    ax.set_xlabel('component1')
    ax.set_ylabel('component2')
    color_list=[ 'blue','red']
    marker_list=['x','o']
    for i in range(len(label_names)):
        ax.scatter(feature[label==i+1][:, 0], feature[label==i+1][:, 1], marker=marker_list[i],\
                   c=color_list[i], s=15, label=label_names[i])
    
    X, Y = np.meshgrid(np.linspace(-2.5,10, 1000),
                       np.linspace(*ax.get_ylim(), 1000))
    XY = np.column_stack([X.ravel(), Y.ravel()])
    Z =clf.predict(XY).reshape(X.shape)
    
    ax.contourf(X, Y, Z, alpha=0.4, cmap='seismic')
    ax.set_xlim(-2.5,10)
    ax.legend()
    plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1997363/377bd1be-baf6-49fa-d00d-1d3a67d71864.png)

サポートベクターマシーンは外れ値があっても上手く予測できていることがわかります。

##サポートベクターマシーンにおけるハイパーパラメータ

ここからはサポートベクターマシーンのハイパーパラメータである。
・ペナルティーパラメーター　C
・カーネル法のパラメーター　Γ（ガンマ）   について紹介していきます。

###ペナルティーパラメーター　C　

**C（ペナルティーパラメーター）**は簡単に言うと、誤分類をどれくらい許すかを決めるパラメーターになります。

サポートベクターマシーンの目的は、マージンを大きくすることでしたが。

誤分類しないようにマージンを大きくすることには限界があります。

そこで、C（ペナルティーパラメーター）を調整することで、ある程度の誤分類を許してマージンを大きくすることができるようになります。

C（ペナルティーパラメーター）が大きくなると、誤分類に厳しくマージンは小さくなり、
C（ペナルティーパラメーター）が小さくなると、誤分類に甘くマージンは大きくなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1997363/0dbfa1c5-0acf-0a87-cf03-b88539bce53f.png)

C（ペナルティーパラメーター）が小さいモデルのほうが、トレーニングのデータに分類線を完全に合わせようとしなくなるので、過学習が起こりにくくなります。

###カーネル法のパラメーター　Γ（ガンマ）
カーネル法は簡単に言うと、線形分離できないデータを、より高次元の異なる特徴空間のデータへと変換して線形分離する方法になります。詳しい話長くなるので割愛しますが、下図のようなイメージです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1997363/80d541e9-419d-c432-3996-e6fd3496147b.png)

Γ（ガンマ）が大きいほど、分離線がより非線形な表現ができるようになり、
Γ（ガンマ）が小さいほど、分離線は直線に近づきます。

Γ（ガンマ）が大きいと、データに合わせる力が強くなるので過学習しやすくなります。

##ハイパーパラメータによる分類結果への影響
IRISのデータでSVMのハイパーパラメータを変えて分類をさせてみました。

```py
import matplotlib.pyplot as plt 
import numpy as np
import pandas as pd 
from sklearn.linear_model import Perceptron
from sklearn.datasets import load_iris
from sklearn.decomposition import PCA
from sklearn import svm
import itertools

# IRISのデータセットの読み込み
iris = load_iris()

label_names=iris.target_names
feature_names=iris.feature_names
label=iris.target
feature=iris.data

# 簡単のためSentosaクラスを削除する 
label_names=label_names[1:]

feature=feature[label!=0]
label=label[label!=0]

df= pd.concat([pd.DataFrame(label,columns=['label']),pd.DataFrame(feature, columns=feature_names)], axis=1)

# 主成分分析でIRISの特徴量を２つに減らす
pca=PCA(n_components=2)
pca.fit(feature)
feature=pca.transform(feature)

c_list=[0.1,10,1000]

gamma_list=[0.1,10,1000]

fig, axs = plt.subplots(3,3, figsize=(16, 12), facecolor='w', edgecolor='k')
fig.subplots_adjust(hspace = .5, wspace=.25)

axs=axs.ravel()

for n, param in enumerate(itertools.product(c_list,gamma_list)):
    #  SVM ,パーセプトロン分類器の作成
    print(f'SVM C:{param[0]}  gamma:{param[1]}')
    clf = svm.SVC(C=param[0],gamma=param[1])
    clf.fit(feature, label)
    Y_pred =clf.predict(feature) 
    score = clf.score(feature, label)
    print('accuracy: {:.2%}'.format(score))

    ax=axs[n]
    ax.set_title(f'SVM C:{param[0]}  gamma:{param[1]}')
    ax.set_xlabel('component1')
    ax.set_ylabel('component2')
    color_list=[ 'blue','red']
    marker_list=['x','o']
    for i in range(len(label_names)):
        ax.scatter(feature[label==i+1][:, 0], feature[label==i+1][:, 1], marker=marker_list[i],\
                   c=color_list[i], s=15, label=label_names[i])
    
    X, Y = np.meshgrid(np.linspace(-2.5,10, 1000),
                       np.linspace(*ax.get_ylim(), 1000))
    XY = np.column_stack([X.ravel(), Y.ravel()])
    Z =clf.predict(XY).reshape(X.shape)
    
    # describe the contour
    ax.contourf(X, Y, Z, alpha=0.4, cmap='seismic')
    ax.set_xlim(-2.5,2.5)
    ax.legend()
    plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1997363/92ea085a-4c7d-ae86-57ca-5cb3fe7f307e.png)

ハイパーパラメータにより結果が大きく変わるのがわかるかと思います。
ガンマ1000のケースは過学習ですね。
##まとめ
分類アルゴリズムのサポートベクターマシーンについてまとめてみました。
分類器の特徴としては、外れ値に強いこと、カーネル法により非線形問題にも対応できるということだと思います。
なにかありましたらコメントいただけると幸いです。