導入
先日scikit-learn を使って分類問題を解いた際、
勾配ブースティング系のモデルである LightGBM よりも
Support Vector Machine(以下、SVM)の方がうまく分類できるケースに遭遇しました。
この記事では
どのような状況で SVM > LightGBM になったのか
を可視化して確認します。
データ生成
かなり恣意的ですが、乱数で2次元データを生成して2次曲線で境界を設定します。
簡単のため2クラス分類を行います。
import numpy as np
import matplotlib.pyplot as plt
plt.style.use("ggplot")
# 乱数で2次元データ生成
np.random.seed(42)
n= 1000
x = (np.random.rand(n,2)-0.5)*2
y = (2*x[:,0] **2 -0.7 > x[:,1]).astype(int)
# プロット
xx, yy = np.meshgrid(
np.linspace(-1,1,200),
np.linspace(-1,1,200)
)
grid = np.c_[xx.ravel(), yy.ravel()]
Z = (2*grid[:,0] **2 -0.7 > grid[:,1]).astype(int)
Z = Z.reshape(xx.shape)
plt.contourf(xx, yy, Z, alpha=0.3)
plt.scatter(x[:,0], x[:,1], c=y, cmap='viridis', s=30)
plt.xlabel('x1')
plt.ylabel('x2')
plt.title('generated Data')
plt.show()
分類モデル
生成したデータのすべてを使って学習を行い、分類境界を図示します。
本記事では分類性能評価目的ではないので、訓練データとテストデータの分割は行いません。
SVM
まずはSVMでカーネルに2次多項式を指定して学習を行います。
Cやcoef0の値はグラフ見ながら、ざっと合わせています。
from sklearn.svm import SVC
svm_model = SVC(
kernel = "poly",
degree = 2,
C = 100,
gamma = "scale",
coef0 = 3
)
svm_model.fit(x, y)
LightGBM
LightGBMの学習を行います。
ハイパーパラメータの設定は省略しました。
import lightgbm as lgb
lgb_model = lgb.LGBMClassifier()
lgb_model.fit(x, y)
分類結果
可視化
このコードで分類曲線を可視化しました。
決定境界を可視化するために、グリッド上で予測しています。
Z = model.predict(grid)
Z = Z.reshape(xx.shape)
plt.contourf(xx, yy, Z , alpha=0.3)
plt.scatter(x[:,0], x[:,1], c=y, cmap='viridis', s=30)
plt.xlabel('x1')
plt.ylabel('x2')
SVMの分類境界
意図的に問題設定したこともありますが、綺麗に分類出来ています。
分類曲線が滑らかに変化します。
Cやcoef0の値を変えながら、分類領域を見ると理解が深まります。

LightGBMの分類境界
LightGBMの分類境界を示します。
よく分類出来ていますが境界がジグザグになっていて、境界に近い所で分類しきれないことが分かります。

考察
今回設定した分類境界は
$$
x_2 = 2x_1^2 - 0.7
$$
という2次曲線です。
SVMの多項式カーネル(degree=2)は
このような2次多項式境界を自然に表現できるため、
今回の問題設定と相性が良かったと考えられます。
一方、LightGBM は決定木ベースのアルゴリズムであり、
内部的には 2値分割を繰り返して境界を作ります。
そのため、分類境界はどうしても 矩形の組み合わせのような形になりやすく、
今回のような滑らかな境界を表現するには不利だったと考えられます。
もちろん理論的には、十分に多くの木とデータを使えば複雑な境界も近似可能です。そのためデータ数を増やしていけば、どこかでLightGBMの性能が上回る可能性もあると考えられます。
雑感
この話だとSVMがいいと聞こえるかもしれないですが、そうでもありません。
LightGBMは何も調整せずともそれなりに分類できましたが、SVMだとCやgammaのハイパーパラメータ類の調整、など気に掛ける項目が多くて手間がかかる印象を受けました。
実際は準備出来るデータの量や特性を踏まえてアルゴリズムを選択する必要があります。
コード
今回の実験コードはこちらにまとめています:
https://github.com/opticnomad/SVM-LightGRM-comparison.git
