서포트벡터머신(Support Vector Machine)

서포트벡터머신(SVM,Support Vector Machine, 이하 SVM)은 강력한 머신러닝 모델이며 또 다양한 정형 데이터셋에서 좋은 성능을 보여줍니다. SVM은 데이터의 특징이 몇개 되지 않더라도 복잡한 결정경계(Decision Boundary)를 만들어줍니다.

SVM은 저차원, 고차원의 모델에서 좋은 성능을 발휘하지만 샘플이 많을 경우 예를 들어 100,000개 이상의 데이터를 처리하는데는 성능이 좋지 않습니다. 또 데이터 전처리(MinMaxScaler와 같은 작업이 필요)와 매개변수 설정에 좀 더 고민해야 할 부분들이 많이 있고 예측이 어떤 결과를 통해서 이뤄졌는지에 대한 모델 설명이 쉽지 않은 부분이 있습니다.

그럼에도 SVM은 특징이 비슷한 단위이고 샘플 수가 많지 않다면 좋은 결과를 얻을 수 있는 모델입니다.

아래와 같은 예제를 통해서 SVM 모델을 테스트해보겠습니다.

from sklearn.datasets import make_classification
from sklearn.datasets import make_blobs
from sklearn import svm

import numpy as np

import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split, GridSearchCV

테스트 데이터는 sklearn의 make_classification() 함수를 사용해서 만들었습니다. 그렇기 때문에 이 코드를 통해서 테스트하시는 분들은 다른 형태의 그래프가 그려집니다. 데이터를 계속 새로 만들면서 결정경계가 어떻게 표시되는지 테스트해보시는 것도 좋겠습니다.

저는 2개의 feature가 있는 1,000개의 샘플 데이터를 생성했습니다. 생성한 데이터를 통해서 시각화 해보면 아래와 같은 그래프가 표시됩니다. 이 그래프는 선형으로 분류가 불가능합니다. 두개의 클래스가 겹쳐있기 때문입니다. 그렇기 때문에 차원을 높여서 비선형(Nonlinear) 방식의 분류가 필요합니다.

예제를 수행하기에 앞서 간단히 설명하면 서포트벡터머신(Support Vector Machine)은 선형 데이터 분류와 비선형 데이터 분류 모두 가능합니다. 그 방법은 데이터를 분류할 수 있는 직선을 긋고 직선을 기준으로 데이터를 분류하는 것입니다. 테스트 예제인 그림 1의 경우는 직선으로 분류할 수 없기 때문에 커널(kernel)이라는 형태의 방법이 필요하지만 그림2의 경우는 데이터가 굉장히 명확하게 구분되어 있기 때문에(몇개의 아웃라이어가 있지만…) 하나의 직선으로 두개의 데이터 그룹을 분리 할 수 있습니다. 정리하면 그림1은 비선형, 그림 2는 선형 이라고 하고 SVM은 이 두가지 모두 가능합니다.

그림2에서 좀 더 자세히 설명하면 두개의 데이터 그룹이 있고 한 그룹마다 데이터가 많이 존재하고 있지만 실제로 모든 데이터가 결정경계(Decision Boundary)를 만드는 것은 아니고 일부의 데이터만 결정경계를 만드는데 사용됩니다. 이것은 비교적 적은 연산 과정으로도 결과를 얻을 수 있다는 의미이며 또 속도가 빠르다는 의미도됩니다.
이러한 데이터들을 서포트 벡터(Support Vector)라고 표현합니다. 풀어서 말하면 결정경계를 존재하게 하는 지지 벡터라고 할 수 있습니다. 그런데 왜 점이라고 하지 않고 벡터라고 했을까를 생각해보면 어떤 포인트의 연속된 데이터 형태를 표현하기 위해서 라고 이해할 수 있습니다.

색을 표시하기 위해서 아래와 같은 컬러 코드를 사용했습니다.

red_RGB = (1,0,0)
blue_RGB = (0,0,1)
data_colors = [red_RGB, blue_RGB]

def get_colors(y):
    return [data_colors[label] for label in y]
X,y = make_classification(n_samples=1000, n_features=2, n_informative=1, n_redundant=0, n_clusters_per_class=1)
plt.scatter(X[:,0], X[:,1], c=get_colors(y), s=10)
그림 1
그림 2

이제 그림1의 데이터로 다시 돌아와서 train 데이터와 test 데이터로 나눠줍니다. 분리는 8:2의 형태로 나누겠습니다.

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state=0) # 8:2

데이터를 표시하기 위해서 시각화 함수를 정의합니다.

def plot_decision_function_helper(X, y, clf, show_only_decision_function = False):

    colors = get_colors(y)
    plt.axis('equal')
    plt.tight_layout()

    plt.scatter(X[:, 0], X[:, 1], c=colors, s=10, edgecolors=colors)
    ax = plt.gca()
    xlim = ax.get_xlim()
    ylim = ax.get_ylim()

    # Create grid to evaluate model
    xx = np.linspace(xlim[0], xlim[1], 30)
    yy = np.linspace(ylim[0], ylim[1], 30)
    YY, XX = np.meshgrid(yy, xx)
    xy = np.vstack([XX.ravel(), YY.ravel()]).T # xy.shape = (900, 2)
    Z = clf.decision_function(xy).reshape(XX.shape)

    if  show_only_decision_function:
        ax.contour(XX, YY, Z, colors='k', levels=[0], alpha=0.5,
                 linestyles=['-'])
    else :
        ax.contour(XX, YY, Z, colors='k', levels=[-1, 0, 1], alpha=0.5,
                 linestyles=['--', '-', '--'])

def plot_decision_function(X_train, y_train, clf):
    colors = get_colors(y_train)
    
    plt.figure(figsize=(8,4), dpi=150)
    plt.title('Support Vector Machine Classifier')
    plot_decision_function_helper(X_train, y_train, clf, False)

SVM은 비선형 데이터를 고차원으로 확장하여 분리할 수 있도록 rbf, linear, poly 커널을 제공합니다. 기본값은 rbf 커널입니다.

clf = svm.SVC() #default kernel ='rbf'    
print(clf)
clf.fit(X_train, y_train)

plot_decision_function(X, y, clf)
print("Accuracy: {}%".format(clf.score(X_test, y_test) * 100 ))
#Accuracy: 92.5%
clf2 = svm.SVC(kernel='linear', C=1)    
clf2.fit(X, y)
plot_decision_function(X, y, clf2)
print("Accuracy: {}%".format(clf2.score(X_test, y_test) * 100 ))
#Accuracy: 91.5%
clf4 = svm.SVC(kernel='poly', C=1)    
clf4.fit(X, y)
plot_decision_function(X, y, clf4)
print("Accuracy: {}%".format(clf4.score(X_test, y_test) * 100 ))
#Accuracy: 90.5%

참고로 결정경계를 만들때 고려해야 할 내용은 마진을 어떻게 결정하는가? 에 대한 내용입니다. 이것은 “C”를 어떻게 설정할 것인가에 따라 달라집니다. C는 기본값은 1이지만 데이터의 특징에 따라 더 많이 설정할 수도 있습니다.

만약 아웃라이어를 최대한 허용하지 않고 모든 데이터를 대상으로 까다롭게(?) 만드는 경우 이것을 하드 마진(hard margin)이라고 부릅니다. 이렇게 되면 서포트 벡터와 결정 경계 사이의 거리가 매우 좁다. 즉, 마진이 매우 작아진다. 이렇게 개별적인 학습 데이터들을 다 놓치지 않으려고 아웃라이어를 허용하지 않는 기준으로 결정 경계를 정해버리면 오버피팅(overfitting) 문제가 발생할 수 있다.

반대로 마진(margin)을 어느 정도 오류값들이 포함되도록 너그럽게 기준을 세운다면 이것을 소프트 마진(soft margin)이라고 부릅니다. 이렇게 너그럽게(?) 잡아 놓으면 서포트 벡터와 결정 경계 사이의 거리가 멀어지는 즉, 마진이 현상이 나타납니다. 그렇게되면 언더피팅(underfitting) 문제가 발생할 수 있다. 그렇기 때문에 훈련 데이터의 오류를 어느 정도는 허용할 것인가에 대한 기준을 잡고 모델을 설계하는 것이 필요합니다.

또 하나 고려해야 할 부분은 gamma 값입니다. gamma는 훈련 샘플이 미치는 영향의 범위를 의미하는 것으로 작은 값은 넓은 영역을 큰 값이라면 미치는 범위가 제한적인 특징이 있습니다.

Reference

https://www.learnopencv.com/svm-using-scikit-learn-in-python/

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 항목은 *(으)로 표시합니다