2.3.7 커널 서포트 벡터 머신

2.3.6 결정 트리의 앙상블 | 목차 | 2.3.8 신경망(딥러닝)

다음으로 다룰 지도 학습 모델은 커널 서포트 벡터 머신kernelized support vector machines입니다. 86페이지 “분류용 선형 모델”에서 선형 서포트 벡터 머신을 사용해 분류 문제를 풀어보았습니다. 커널 서포트 벡터 머신(보통 그냥 SVM으로 부릅니다)은 입력 데이터에서 단순한 초평면hyperplane으로 정의되지 않는 더 복잡한 모델을 만들 수 있도록 확장한 것입니다. 서포트 벡터 머신을 분류와 회귀에 모두 사용할 수 있지만 여기서는 SVC를 사용하는 분류 문제만을 다루겠습니다. SVR를 사용하는 회귀 문제에도 같은 개념을 적용할 수 있습니다.

커널 서포트 벡터 머신의 수학적 정의는 조금 복잡해서 이 책의 범위를 넘어섭니다. 하지만 헤이스티, 팁시라니, 프리드먼의 『The Elements of Statistical Learning』(http://statweb.stanford.edu/~tibs/ElemStatLearn/) 12장에서 자세한 내용을 볼 수 있습니다. 여기서는 이 알고리즘의 이면에 있는 아이디어 정도만 조금 살펴보겠습니다.

선형 모델과 비선형 특성

[그림 2-15]에서 보았듯이 직선과 초평면은 유연하지 못하여 저차원 데이터셋에서는 선형 모델이 매우 제한적입니다. 선형 모델을 유연하게 만드는 한 가지 방법은 특성끼리 곱하거나 특성을 거듭제곱하는 식으로 새로운 특성을 추가하는 것입니다.

109페이지의 “트리의 특성 중요도” 절에서 사용한 인위적 데이터셋을 다시 보겠습니다.

In[77]:

X, y = make_blobs(centers=4, random_state=8)
y = y % 2

mglearn.discrete_scatter(X[:, 0], X[:, 1], y)
plt.xlabel("특성 0")
plt.ylabel("특성 1")

2-36

그림 2-36 선형적으로 구분되지 않는 클래스를 가진 이진 분류 데이터셋

분류를 위한 선형 모델은 직선으로만 데이터 포인트를 나눌 수 있어서 이런 데이터셋에는 잘 들어 맞지 않습니다(그림 2-37).

In[78]:

from sklearn.svm import LinearSVC
linear_svm = LinearSVC().fit(X, y)

mglearn.plots.plot_2d_separator(linear_svm, X)
mglearn.discrete_scatter(X[:, 0], X[:, 1], y)
plt.xlabel("특성 0")
plt.ylabel("특성 1")

2-37

그림 2-37 선형 SVM으로 만들어진 결정 경계

두 번째 특성을 제곱한 특성1 ** 2를 새로운 특성으로 추가해 입력 특성을 확장해보겠습니다. 이제 (특성0, 특성1) 2차원 데이터 포인트가 아니라 (특성0, 특성1, 특성1 ** 2)의 1 3차원 데이터 포인트로 표현됩니다. 이 데이터셋을 [그림 2-38]에서 3차원 산점도로 나타냈습니다.

In[79]:

# 두 번째 특성을 제곱하여 추가합니다.
X_new = np.hstack([X, X[:, 1:] ** 2])

from mpl_toolkits.mplot3d import Axes3D, axes3d
figure = plt.figure()
# 3차원 그래프
ax = Axes3D(figure, elev=-152, azim=-26)
# y == 0인 포인트를 먼저 그리고 그다음 y == 1인 포인트를 그립니다.
mask = y == 0
ax.scatter(X_new[mask, 0], X_new[mask, 1], X_new[mask, 2], c='b',
           cmap=mglearn.cm2, s=60, edgecolor='k')
ax.scatter(X_new[~mask, 0], X_new[~mask, 1], X_new[~mask, 2], c='r', marker='^',
           cmap=mglearn.cm2, s=60, edgecolor='k')
ax.set_xlabel("특성0")
ax.set_ylabel("특성1")
ax.set_zlabel("특성1 ** 2")

2-38

그림 2-38 특성 1에서 유용한 세 번째 특성을 추가하여 [그림 2-37]에서 확장한 데이터셋

새로운 데이터셋에서는 선형 모델과 3차원 공간의 평면을 사용해 두 클래스를 구분할 수 있습니다. 확장된 데이터셋에서 선형 모델을 만들어 확인해보겠습니다(그림 2-39).

In[80]:

linear_svm_3d = LinearSVC().fit(X_new, y)
coef, intercept = linear_svm_3d.coef_.ravel(), linear_svm_3d.intercept_

# 선형 결정 경계 그리기
figure = plt.figure()
ax = Axes3D(figure, elev=-152, azim=-26)
xx = np.linspace(X_new[:, 0].min() - 2, X_new[:, 0].max() + 2, 50)
yy = np.linspace(X_new[:, 1].min() - 2, X_new[:, 1].max() + 2, 50)

XX, YY = np.meshgrid(xx, yy)
ZZ = (coef[0] * XX + coef[1] * YY + intercept) / -coef[2]
ax.plot_surface(XX, YY, ZZ, rstride=8, cstride=8, alpha=0.3)
ax.scatter(X_new[mask, 0], X_new[mask, 1], X_new[mask, 2], c='b',
           cmap=mglearn.cm2, s=60, edgecolor='k')
ax.scatter(X_new[~mask, 0], X_new[~mask, 1], X_new[~mask, 2], c='r', marker='^',
           cmap=mglearn.cm2, s=60, edgecolor='k')

ax.set_xlabel("특성0")
ax.set_ylabel("특성1")
ax.set_zlabel("특성1 ** 2")

2-39

그림 2-39 확장된 3차원 데이터셋에서 선형 SVM이 만든 결정 경계

원래 특성으로 투영해보면 이 선형 SVM 모델은 더 이상 선형이 아닙니다. 직선보다 타원에 가까운 모습을 [그림 2-40]에서 확인할 수 있습니다.

In[81]:

ZZ = YY ** 2
dec = linear_svm_3d.decision_function(np.c_[XX.ravel(), YY.ravel(), ZZ.ravel()])
plt.contourf(XX, YY, dec.reshape(XX.shape), levels=[dec.min(), 0, dec.max()],
             cmap=mglearn.cm2, alpha=0.5)
mglearn.discrete_scatter(X[:, 0], X[:, 1], y)
plt.xlabel("특성 0")
plt.ylabel("특성 1")

2-40

그림 2-40 원래 두 개 특성에 투영한 [그림 2-39]의 결정 경계

커널 기법

앞에서는 데이터셋에 비선형 특성을 추가하여 선형 모델을 강력하게 만들었습니다. 하지만 많은 경우 어떤 특성을 추가해야 할지 모르고 특성을 많이 추가하면 (예를 들면, 100개의 특성에서 가능한 모든 조합) 연산 비용이 커집니다. 다행히 수학적 기교를 사용해서 새로운 특성을 많이 만들지 않고서도 고차원에서 분류기를 학습시킬 수 있습니다. 이를 커널 기법kernel trick이라고 하며 실제로 데이터를 확장하지 않고 확장된 특성에 대한 데이터 포인트들의 거리(더 정확히는 스칼라 곱)를 계산합니다.

서포트 벡터 머신에서 데이터를 고차원 공간에 매핑하는 데 많이 사용하는 방법은 두 가지입니다. 원래 특성의 가능한 조합을 지정된 차수까지 모두 계산(예를 들어 특성1 ** 2 × 특성2 ** 5)하는 다항식 커널이 있고 가우시안Gaussian 커널로도 불리우는 RBFradial basis function 커널이 있습니다. 가우시안 커널은 차원이 무한한 특성 공간에 매핑하는 것으로, 설명하기가 좀 더 어렵습니다. 가우시안 커널은 모든 차수의 모든 다항식을 고려한다고 이해하면 좋습니다. 하지만 특성의 중요도는 고차항이 될수록 줄어듭니다. 2

실제로는 커널 SVM 이면의 수학적인 이론은 중요하지 않지만, RBF 커널을 사용한 SVM이 결정을 만드는 방법은 비교적 쉽게 요약할 수 있습니다. 다음 절에서 이를 살펴보겠습니다.

SVM 이해하기

학습이 진행되는 동안 SVM은 각 훈련 데이터 포인트가 두 클래스 사이의 결정 경계를 구분하는 데 얼마나 중요한지를 배우게 됩니다. 일반적으로 훈련 데이터의 일부만 결정 경계를 만드는 데 영향을 줍니다. 바로 두 클래스 사이의 경계에 위치한 데이터 포인트들입니다. 이런 데이터 포인트를 서포트 벡터support vector라 하며, 여기서 서포트 벡터 머신이란 이름이 유래했습니다.

새로운 데이터 포인트에 대해 예측하려면 각 서포트 벡터와의 거리를 측정합니다. 분류 결정은 서포트 벡터까지의 거리에 기반하며 서포트 벡터의 중요도는 훈련 과정에서 학습합니다(SVC 객체의 dual_coef_ 속성에 저장됩니다).

데이터 포인트 사이의 거리는 가우시안 커널에 의해 계산됩니다.

k_{rbf}(x_1, x_2) = exp(-\gamma||x_1 - x_2||^2)

여기에서 x_1x_2는 데이터 포인트이며 ||x_1 - x_2||는 유클리디안 거리이고 \gamma감마, gamma는 가우시안 커널의 폭을 제어하는 매개변수입니다.

[그림 2-41]은 두 개의 클래스를 가진 2차원 데이터셋에 서포트 벡터 머신을 학습시킨 결과를 보여줍니다. 결정 경계는 검은 실선으로, 서포트 벡터는 굵은 테두리로 크게 그렸습니다. 다음은 forge 데이터셋에 SVM을 학습시켜 이 그래프를 그리는 코드입니다.

In[82]:

from sklearn.svm import SVC
X, y = mglearn.tools.make_handcrafted_dataset()
svm = SVC(kernel='rbf', C=10, gamma=0.1).fit(X, y)
mglearn.plots.plot_2d_separator(svm, X, eps=.5)
mglearn.discrete_scatter(X[:, 0], X[:, 1], y)
# 서포트 벡터
sv = svm.support_vectors_
# dual_coef_의 부호에 의해 서포트 벡터의 클래스 레이블이 결정됩니다.
sv_labels = svm.dual_coef_.ravel() > 0
mglearn.discrete_scatter(sv[:, 0], sv[:, 1], sv_labels, s=15, markeredgewidth=3)
plt.xlabel("특성 0")
plt.ylabel("특성 1")

2-41

그림 2-41 RBF 커널을 사용한 SVM으로 만든 결정 경계와 서포트 벡터

이 그림에서 SVM은 매우 부드럽고 비선형(직선이 아닌) 경계를 만들었습니다. 여기서 사용한 두 매개변수 C와 gamma에 대해 자세히 살펴보겠습니다.

SVM 매개변수 튜닝

gamma 매개변수는 앞 절의 공식에 나와 있는 γ로 가우시안 커널 폭의 역수에 해당합니다. gamma 매개변수가 하나의 훈련 샘플이 미치는 영향의 범위를 결정합니다. 작은 값은 넓은 영역을 의미하며 큰 값일 경우 영향이 미치는 범위가 제한적입니다. 다른 말로 하면, 가우시안 커널의 반경이 클수록 훈련 샘플의 영향 범위도 커집니다. 3 C 매개변수는 선형 모델에서 사용한 것과 비슷한 규제 매개변수입니다. 이 매개변수는 각 포인트의 중요도(정확히는 dual_coef_ 값)를 제한합니다.

이 매개변수를 다르게 했을 때 어떻게 변경되는지 살펴보겠습니다(그림 2-42).

In[83]:

fig, axes = plt.subplots(3, 3, figsize=(15, 10))

for ax, C in zip(axes, [-1, 0, 3]):
    for a, gamma in zip(ax, range(-1, 2)):
        mglearn.plots.plot_svm(log_C=C, log_gamma=gamma, ax=a)

axes[0, 0].legend(["클래스 0", "클래스 1", "클래스 0 서포트 벡터", "클래스 1 서포트 벡터"],
                  ncol=4, loc=(.9, 1.2))

2-42

그림 2-42 C와 gamma 매개변수 설정에 따른 결정 경계와 서포트 벡터

왼쪽에서 오른쪽으로 가면서 gamma 매개변수를 0.1에서 10으로 증가시켰습니다. 작은 gamma 값은 가우시안 커널의 반경을 크게 하여 많은 포인트들이 가까이 있는 것으로 고려됩니다. 그래서 왼쪽 그림의 결정 경계는 매우 부드럽고 오른쪽으로 갈수록 결정 경계는 하나의 포인트에 더 민감해집니다. 작은 gamma 값이 결정 경계를 천천히 바뀌게 하므로 모델의 복잡도를 낮춥니다. 반면에 큰 gamma 값은 더 복잡한 모델을 만듭니다.

위에서 아래로는 C 매개변수를 0.1에서 1000으로 증가시켰습니다. 선형 모델에서처럼 작은 C는 매우 제약이 큰 모델을 만들고 각 데이터 포인트의 영향력이 작습니다. 왼쪽 위의 결정 경계는 거의 선형에 가까우며 잘못 분류된 데이터 포인트가 경계에 거의 영향을 주지 않습니다. 왼쪽 아래에서 볼 수 있듯이 C를 증가시키면 이 포인트들이 모델에 큰 영향을 주며 결정 경계를 휘어서 정확하게 분류하게 합니다.

RBF 커널 SVM을 유방암 데이터셋에 적용해보겠습니다. 기본값 C=1, gamma=1/n_features를 사용합니다. 4

In[84]:

X_train, X_test, y_train, y_test = train_test_split(
    cancer.data, cancer.target, random_state=0)

svc = SVC()
svc.fit(X_train, y_train)

print("훈련 세트 정확도: {:.2f}".format(svc.score(X_train, y_train)))
print("테스트 세트 정확도: {:.2f}".format(svc.score(X_test, y_test)))

Out[84]:

훈련 세트 정확도: 1.00
테스트 세트 정확도: 0.63

훈련 세트에는 완벽한 점수를 냈지만 테스트 세트에는 63% 정확도라서 이 모델은 상당히 과대적합되었습니다. SVM은 잘 작동하는 편이지만 매개변수 설정과 데이터 스케일에 매우 민감합니다. 특히 입력 특성의 범위가 비슷해야 합니다. 각 특성의 최솟값과 최댓값을 로그 스케일로 나타내보겠습니다(그림 2-43).

In[85]:

plt.boxplot(X_train, manage_xticks=False)
plt.yscale("symlog")
plt.xlabel("특성 목록")
plt.ylabel("특성 크기")

그래프를 보니 유방암 데이터셋의 특성은 자릿수 자체가 완전히 다릅니다. 이것이 일부 모델(선형 모델 등)에서도 어느 정도 문제가 될 수 있지만, 커널 SVM에서는 영향이 아주 큽니다. 이 문제를 해결하는 방법을 알아보겠습니다.

2-43

그림 2-43 유방암 데이터셋의 특성 값 범위(y 축은 로그 스케일)

SVM을 위한 데이터 전처리

이 문제를 해결하는 방법 하나는 특성 값의 범위가 비슷해지도록 조정하는 것입니다. 커널 SVM에서는 모든 특성 값을 0과 1 사이로 맞추는 방법을 많이 사용합니다. 3장에서 MinMaxScaler 전처리 메서드를 사용해서 이를 처리하는 방법을 자세히 다루도록 하겠습니다. 여기서는 우리가 직접 만들어보겠습니다. 5

In[86]:

# 훈련 세트에서 특성별 최솟값 계산
min_on_training = X_train.min(axis=0)
# 훈련 세트에서 특성별 (최댓값 - 최솟값) 범위 계산
range_on_training = (X_train - min_on_training).max(axis=0)

# 훈련 데이터에 최솟값을 빼고 범위로 나누면
# 각 특성에 대해 최솟값은 0, 최대값은 1입니다.
X_train_scaled = (X_train - min_on_training) / range_on_training
print("특성별 최소 값\n{}".format(X_train_scaled.min(axis=0)))
print("특성별 최대 값\n {}".format(X_train_scaled.max(axis=0)))

Out[86]:

특성별 최솟값
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
  0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 특성별 최댓값
[ 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.
  1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]

In[87]:

# 테스트 세트에도 같은 작업을 적용하지만
# 훈련 세트에서 계산한 최솟값과 범위를 사용합니다(자세한 내용은 3장에 있습니다).
X_test_scaled = (X_test - min_on_training) / range_on_training

In[88]:

svc = SVC()
svc.fit(X_train_scaled, y_train)

print("훈련 세트 정확도: {:.3f}".format(
    svc.score(X_train_scaled, y_train)))
print("테스트 세트 정확도: {:.3f}".format(svc.score(X_test_scaled, y_test)))

Out[88]:

훈련 세트 정확도: 0.948
테스트세트 정확도: 0.951

데이터의 스케일을 조정하니 결과가 크게 달라졌습니다. 훈련 세트와 테스트 세트의 정확도가 100%에서는 조금 멀어졌지만 매우 비슷해서 확실히 과소적합된 상태입니다. 여기서 C나 gamma 값을 증가시켜 좀 더 복잡한 모델을 만들 수 있습니다. 예를 들면 다음과 같습니다.

In[89]:

svc = SVC(C=1000)
svc.fit(X_train_scaled, y_train)

print("훈련 세트 정확도: {:.3f}".format(svc.score(X_train_scaled, y_train)))
print("테스트 세트 정확도: {:.3f}".format(svc.score(X_test_scaled, y_test)))

Out[89]:

훈련 세트 정확도: 0.988
테스트 세트 정확도: 0.972

C 값을 증가시켰더니 모델의 성능이 97.2%로 향상되었습니다.

장단점과 매개변수

커널 서포트 벡터 머신은 강력한 모델이며 다양한 데이터셋에서 잘 작동합니다. SVM은 데이터의 특성이 몇 개 안 되더라도 복잡한 결정 경계를 만들 수 있습니다. 저차원과 고차원의 데이터(즉 특성이 적을 때와 많을 때)에 모두 잘 작동하지만 샘플이 많을 때는 잘 맞지 않습니다. 10,000개의 샘플 정도면 SVM 모델이 잘 작동하겠지만 100,000개 이상의 데이터셋에서는 속도와 메모리 관점에서 도전적인 과제입니다.

SVM의 또 하나의 단점은 데이터 전처리와 매개변수 설정에 신경을 많이 써야 한다는 점입니다. 그래서 요즘엔 사람들 대부분이 랜덤 포레스트나 그래디언트 부스팅 같은 (전처리가 거의 또는 전혀 필요 없는) 트리 기반 모델을 애플리케이션에 많이 사용합니다. 더군다나 SVM 모델은 분석하기도 어렵습니다. 예측이 어떻게 결정되었는지 이해하기 어렵고 비전문가에게 모델을 설명하기가 난해합니다.

하지만 모든 특성이 비슷한 단위이고(예를 들면 모든 값이 픽셀의 컬러 강도) 스케일이 비슷하면 SVM을 시도해볼 만합니다.

커널 SVM에서 중요한 매개변수는 규제 매개변수 C이고 어떤 커널을 사용할지와 각 커널에 따른 매개변수입니다. 우리는 RBF 커널만 살펴봤지만 scikit-learn에는 다른 커널도 많습니다. 6 RBF 커널은 가우시안 커널 폭의 역수인 gamma 매개변수 하나를 가집니다. 7 gamma와 C 모두 모델의 복잡도를 조정하며 둘 다 큰 값이 더 복잡한 모델을 만듭니다. 그러므로 연관성이 많은 이 두 매개변수를 잘 설정하려면 C와 gamma를 함께 조정해야 합니다.


  1. 예시를 위해 하나의 특성을 임의로 선택한 것으로, 큰 의미는 없습니다.
  2. 지수 함수의 테일러 급수 전개 때문입니다.
    옮긴이_ 가우시안 커널은 지수 함수의 테일러 전개를 이용한 것으로 무한한 다항식 차원으로 매핑하는 효과를 만듭니다.
  3. 옮긴이_ gamma는 0보다 커야 하므로 가우시안 커널 함수 값의 범위는 e^0 ~ e^{-\infty}, 즉 1 ~ 0 사이입니다. 따라서 gamma 값이 작을수록 데이터 포인트의 영향 범위가 커집니다.
  4. 옮긴이_ SVC의 gamma 매개변수의 기본값은 ‘auto’이며 이 값의 의미는 특성 개수의 역수입니다. 즉 1/X_train.shape[1]이 됩니다.
  5. 옮긴이_ 여기에서 사용한 식 \frac{X-min(X)}{max(X)-min(X)}에서 최대와 최소의 차이가 분모가 되므로 결과는 항상 0과 1 사이의 값이 됩니다. 데이터셋에 따라 StandardScaler도 사용할 수 있습니다.
  6. 옮긴이_ SVC의 kernel 매개변수의 옵션은 ‘linear'(x1x2), ‘poly'(γ(x1x2)+c)d, ‘rbf’, ‘sigmoid'(tanh(γ(x1x2)+c)), ‘precomputed’가 있습니다. c는 coef0, d는 degree 매개변수에서 지정할 수 있습니다. kernel 매개변수의 기본값은 ‘rbf’이고 ‘precomputed’는 미리 계산된 거리 값을 입력 특성으로 사용하는 경우입니다.
  7. 옮긴이_ 가우시안 커널의 공식 exp(\frac{-||x_1-x_2||^2}{2\sigma^2})exp(-\gamma||x_1-x_2||^2)로 간소화해 표현합니다. 여기서 \gamma=\frac{1}{2\sigma^2}인 σ가 종종 가우시안 커널의 폭을 조절한다고 말합니다.

2.3.6 결정 트리의 앙상블 | 목차 | 2.3.8 신경망(딥러닝)

이 글은 한빛미디어에서 출간한  “파이썬 라이브러리를 활용한 머신러닝“의 1장과 2장입니다. 이 책의 저작권은 한빛미디어(주)에 있으므로 무단 복제 및 무단 전제를 금합니다.

2.3.7 커널 서포트 벡터 머신”에 대한 2개의 생각

  1. 정민혁

    안녕하세요 ㅎㅎ 좋은 글 감사합니다.

    읽다가 궁금한점이 생겼는데,

    예를 들어 현재 예측하고 하는 변수가 기온이고, 예측하는데 사용하고자 하는 변수가 풍속, 일조량, 일사량, 습도 등 대략 8개의 변수가 있다고 가정하겠습니다.

    근데 SVM 기법은 커널기법을 사용해 3차원 변환시 사용되는 변수의 최대 개수는 3개로 이해가 되는데 혹시 예측 변수가 8개인 경우에는 어떻게 예측이 가능한건가요??

    제가 이해못하고 있는 부분이 있을까요?? ㅠ

    좋아요

    응답
    1. 박해선 글의 글쓴이

      안녕하세요. 커널 svm에 사용할 수 있는 특성 개수에 제한이 있지 않습니다. 책에서는 시각화를 위해 2개 특성을 사용했을 뿐입니다.

      좋아요

      응답

댓글 남기기

이 사이트는 스팸을 줄이는 아키스밋을 사용합니다. 댓글이 어떻게 처리되는지 알아보십시오.