SVM(サポートベクターマシーン)とは
SVMは「マージン最大化」の考えに基づいて分類する手法である。
n次元空間に超平面(線形識別関数、二次元なら直線)で分類境界を作ることでベクトルを分離する。
(マージンが大きいほど、未知データでの識別誤りが起こりにくいだろうという理屈でマージン最大化する)。
手法としては、線形分離可能を仮定する「ハードマージン法」と任意の識別誤りを許容する「ソフトマージン法」がある。
さらに、非線形問題に対しても、高次元特徴空間を作り「カーネルトリック」を使って分類する方法がある。
前者を線形SVM、後者を非線形SVMと呼ぶ。
線形SVM(ハードマージン法とソフトマージン法)
「ハードマージン法」は、訓練データ(学習するデータ)を超平面で完全に分離できると仮定した問題である。
ただし、現実的には線形分離可能な問題は珍しく、ある程度の識別誤りを許容する「ソフトマージン法」が用いられる。識別誤りのパラメータとしてスラック変数 ξ を導入し、ハイパーパラメータCでその許容を調整する。
主問題と双対問題
SVMでは「主問題」を直接解くのではなく、計算コストの良い「双対問題」に帰着して解く方法が実装されている。双対問題を解く際、後に記載する「カーネルトリック」も扱える。
双対問題を解くにあたっては、主問題を不等式制約のラグランジュ未定乗数法を用いて双対問題へ置き換える(計算の詳細は割愛)。
以下はハードマージン法とソフトマージン法の式を横に並べたもの。赤字部分が異なる箇所。
これは主変数にとっての最小値が、双対変数にとっての最大値になる「鞍点」(あんてん)を探している(鞍点が最適解ということ。鞍点はKKT条件を満たす)。鞍点は字のごとく、馬の鞍(くら)の形。
※双対問題・鞍点の概念は、以下リンクのスライド22枚目が分かりやすかった(他サイト様のもの)。
はじめてのパターン認識 第8章 サポートベクトルマシン
<再掲>
Pythonによる実行
sklearn の svm.LinearSVC
で線形SVMが実行できる。
ソフトマージン法のハイパーパラメータCはデフォルトで C=1.0
である。
Pythonコード
from sklearn.svm import LinearSVC mod1 = LinearSVC(C=C, random_state=1) mod1.fit(X_train, y_train) y_pred1 = mod1.predict(X_test) print(confusion_matrix(y_test, y_pred1)) print(classification_report(y_test, y_pred1)) print()
なお、線形SVMでもL1正則化とL2正則化を指定できる。デフォルトは penalty="l2"
(L2正則化)。
正則化については次の記事を参照。
【機械学習_Python】Ridge回帰、Lasso回帰、Elastic Net回帰 - こちにぃるの日記
非線形SVM(カーネルトリック)
ソフトマージン法によって線形分離不可能なデータセットも扱えるようになったが、現実的には非線形の分類境界を作りたい場面が多い。
このような場面に対して、入力空間を高次元特徴空間に変換(特徴量を非線形に写像)して分類可能とする方法がある。
この方法は、次元を高めるほど計算コストが爆発的に高まり実装しづらい欠点があったが、カーネル関数を導入した「カーネルトリック」と呼ばれる方法によって計算コスト面を解消することができる。
カーネル関数
Pythonによる実行
sklearn の svm.SVC
で非線形SVMが実行できる。
ソフトマージン法と同じくハイパーパラメータ:Cを設定可能。デフォルトは C=1.0
。
カーネルの指定は、linear
、poly
、rbf
、sigmoid
、precomputed
があり、デフォルトは kernel='rbf'
。poly
が多項式カーネル、rbf
がGaussカーネル、sigmoid
がシグモイドカーネルで、linear
は線形SVM(sklearn.svm.LinearSVC
と少し異なるらしい)。
多クラス分類においては、decision_function_shapeにおいて、ovo
(one vs one、1対1)とovr
(one vs rest、1対残り)を指定できる。ovo
はクラス同士のペアを総当たりするため計算量が多い。デフォルトは decision_function_shape='ovr'
Pythonコード
from sklearn.svm import SVC mod1 = SVC(C=C, kernel='rbf', random_state=1) mod1.fit(X_train, y_train) y_pred1 = mod1.predict(X_test) print(confusion_matrix(y_test, y_pred1)) print(classification_report(y_test, y_pred1)) print()
Wineデータで実行
Wineデータから線形分離が難しそうなデータの組を適当に見つけて、線形SVMと非線形SVMの識別性能を比較してみる。
import seaborn as sns import pandas as pd import numpy as np from sklearn import datasets, model_selection from sklearn.svm import LinearSVC, SVC from sklearn.metrics import classification_report, confusion_matrix import matplotlib.pyplot as plt ROW = datasets.load_wine() ADS = pd.DataFrame(ROW.data, columns=ROW.feature_names).assign(target=np.array(ROW.target)) sns.pairplot(ADS[["ash","proline","target"]], hue="target")
特徴量 "ash", "proline" の target 1 vs. 2 が良い感じに混ざっていたので、今回これらを用いる。
ADS12 = ADS[ADS["target"]!=0] ADS12["target"] = ADS["target"] - 1 X_train, X_test, y_train, y_test = model_selection.train_test_split( ADS12[["ash","proline"]], ADS12["target"], test_size=0.3, random_state=999) #-- 線形SVM mod1 = LinearSVC(C=1, random_state=999) mod1.fit(X_train, y_train) y_pred1 = mod1.predict(X_test) print('線形SVM') print(confusion_matrix(y_test, y_pred1)) print(mod1.score(X_test, y_test)) #print(classification_report(y_test, y_pred1)) print() #-- 非線形SVM klist=['poly','rbf','sigmoid'] for k in klist: mod1 = SVC(C=1, kernel=k, random_state=999) mod1.fit(X_train, y_train) y_pred1 = mod1.predict(X_test) print('非線形SVM ', k) print(confusion_matrix(y_test, y_pred1)) print(mod1.score(X_test, y_test)) #print(classification_report(y_test, y_pred1)) print()
線形SVMと比べ、テストデータに対する accuracy は、非線形SVMの多項式カーネル(poly)とGaussカーネル(rbf)が良さそうだった。
参考
https://home.hiroshima-u.ac.jp/tkurita/lecture/svm.pdf
サポートベクターマシン(Support Vector Machine, SVM)~優秀な(非線形)判別関数~
サポートベクターマシンの詳しい理論的な解説について【線形分離可能な場合】
はじめてのパターン認識 第8章 サポートベクトルマシン