SVM - (2) ソフトマージン SVM
ソフトマージン
ハードマージン SVM の記事では、訓練集合が線形分離可能という仮定を置いた。
しかし、訓練集合が線形分離可能でない場合も考えられる。
この場合、ハードマージンの最適化問題
を満たす解は存在しない。
この問題を解決するためにスラック変数 を導入して、最適化問題を次のように書き換える。
ただし、 とする。
制約条件に注目すると、 から に変更したことで、点が超平面 を超えることが許可される。
点 が 超平面 を超えた場合に、超えた分の距離は で表される。
できるだけ超えないようにしたいので、すべての点の超えた距離の合計 をできるだけ小さくしたい。
よって、目的関数に という形で組み込む。
は正則化定数とよばれるハイパーパラメータで超平面 を超えた場合の罰則の強さを設定する。
が小さい値の場合、超えた場合の罰則が小さくなるので、誤分類が許容される。
逆に を大きい値にすると、誤分類に対する罰則が大きくなり、ハードマージン SVM に近くなる。
コード
sklearn.svm.LinearSVC で線形 SVM による学習が行える。
import matplotlib.pyplot as plt import numpy as np from sklearn import svm from sklearn.datasets import make_blobs from sklearn.preprocessing import minmax_scale # データセットを作成する。 X, y = make_blobs(n_samples=100, centers=2, random_state=0) # データセットを描画する。 fig, ax = plt.subplots(facecolor="w") ax.scatter(X[:, 0], X[:, 1], c=y, s=20, cmap="Paired") # 学習する。 clf = svm.LinearSVC(C=1) clf.fit(X, y) # 分類平面及びマージンを描画する。 XX, YY = np.meshgrid(np.linspace(*ax.get_xlim(), 100), np.linspace(*ax.get_ylim(), 100)) xy = np.column_stack([XX.ravel(), YY.ravel()]) Z = clf.decision_function(xy).reshape(XX.shape) ax.contour( XX, YY, Z, colors="k", levels=[-1, 0, 1], alpha=0.5, linestyles=["--", "-", "--"] ) plt.show()
グリッドサーチ
正則化定数 はハイパーパラメータであり、最適な値は学習するデータによって異なる。
そのため、グリッドサーチでいくつかの値での学習を試して、最適な値を採用するとよい。
scikit-learn の sklearn.model_selection.GridSearchCV を使用すると、この探索を簡単に行える。
使い方は以下の記事を参照されたい。
import matplotlib.pyplot as plt import numpy as np import pandas as pd from sklearn.datasets import make_blobs from sklearn.model_selection import GridSearchCV, train_test_split from sklearn.preprocessing import minmax_scale from sklearn.svm import LinearSVC # データセットを作成する。 X, y = make_blobs(n_samples=100, centers=2, random_state=0) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2) # 試行するパラメータとその値 params = {"C": [0.01, 0.1, 1, 10, 100, 1000]} # グリッドサーチする。 clf = GridSearchCV(LinearSVC(), params, cv=5, return_train_score=False, iid=False) clf.fit(X_train, y_train) # 最も精度がいいモデルを取得する。 best_clf = clf.best_estimator_ best_score = best_clf.score(X_test, y_test) print(f"score: {best_score:.2%}") # score: 95.00% cv_result = pd.DataFrame(clf.cv_results_) for row in cv_result.itertuples(): print(f"C = {row.params['C']}: {row.mean_test_score:.2%}") # C = 0.01: 87.54% # C = 0.1: 92.48% # C = 1: 91.23% # C = 10: 91.30% # C = 100: 92.40% # C = 1000: 91.54%