Support Vector Machine(SVM)

SVM은 범주형 결과를 위한 분류 문제나 수치형 결과를 위한 예측문제에 모두 적용 가능한 방법이다.
SVM은 우수한 분류 성능을 갖고 있으며, 분류를 위한 기준선을 정의하여 분류를 수행한다.

1. 개념

데이터 내 그룹에서 레코드 간 거리를 측정하여 중심을 구한 후에 그 가운데서 최적의 초평면(Hyper Plane)을 구하고 분류한다.
SVM은 분리 초평명은 이용한 분류에서 시작해 현재 커널 함수를 이용한 비선형 문제와 종속 변수가 다항인 경우 문제를 해결할 수 있도록 발전되어왔다.

1.1 주요 용어

  • 초평면 : p차원 공간에서 차원이 p-1인 평평한 아핀 부분공간이다.
    예를 들어, 2차원에서 1차원의 아핀 부분공간은 선이고, 3차원에서 초평명은 2차원의 부분공간인 평면을 의미한다.
  • 결정경계 : 분류를 위한 기준선. feature가 2개인 데이터는 결정경계가 선이고 3개라면 초평면이 된다.
  • Convex Hull : 초평면으로 구분되는 각 클래스에서 벡터들의 연결선으로 이루어진 다각형
    클래스를 분류하는데 가장 큰 영향을 미치는 벡터는 convex hull 바깥에 위치한 벡터임
  • 최대초평면 : convex hull에 속한 벡터들 중 초평면과 거리가 멀수록 클래스를 효과적으로 분리할 것이다.
  • Support vector : 최대초평면에 가장 가까운 벡터들 즉, 최대 마진 초평면 결정에 영향을 주는 관측치
  • margin : 결정경계와 서포트 벡터 사이의 거리를 의미함
  • 커널 트릭 : 비선형 분류를 효율적으로 실행하기 위해 레코드들을 고차원 특징 공간으로 투영하는 작업

1.2 분류 방법론 발전과 특징

  1. 분리 초평면(Seperating hyperplane)
    • 두 클래스를 분리할 수 있는 분리 초평면이 존재하면 무한개의 초평면이 존재한다.
  2. 최대 마진 분류기(Maximal Margin Classifier)
    • 분리 초평면이 존재하는 경우 무한개의 초평면이 생기는데 이때, 최대 마진을 갖는 초평면을 선택하는 방법이다.
      다만, 분리 초평면이 존재하지 않으면 적합시킬 수 없다.
  3. 서포트 벡터 분류기(Support Vector Classifier)
    • 분리 초평면이 존재하지 않는 경우를 적합시킬 수 없는 문제를 해결하기 위해 일부 위반이 허용되는 소프트 마진을 적용한 방법이다.
      다만, 데이터가 비선형으로 분류되는 경우 초평면을 찾을 수 없다.
  4. 서포트 벡터 머신(Support Vector Machine)
    • 비선형 문제를 해결하기 위해 커널 함수를 이용해 한 차원 높은 공간에서 초평면을 찾는 방법이다.

1.3 SVM

초평면은 수학적으로 아래와 같이 정의할 수 있다.

$ WX+b=0 $

여기서 W는 $W={W_1,W_2,…,W_n}$ 가중치 벡터이다. n은 feature의 수이고, b는 스칼라 값으로 편의(bias)를 의미한다.
만약 속성이 n개라고 한다면 아래와 같이 나타낼 수 있다.

$ W_0+W_1X_1+...+W_nX_n = 0 $


이때 분리 초평면 위에 있는 모든 점들은 아래의 식을 만족하고,

$H_1 : W_0+W_1X_1+...+W_nX_n > 0 $


분리 초명편 아래에 있는 점들은 다음의 식을 만족한다.

$H_2 : W_0+W_1X_1+...+W_nX_n < 0 $


비선형 커널을 이용해 한 차원 높은 공간에서의 초평면은 내적으로 표현할 수 있는데 $W=(W_1,W_2,…W_p)$ 와 $X_i=(X_1,X_2,…,X_n)$의 내적으로 나타내면 다음과 같고, 초평면 $H_1, H_2$에 속하는 관측치는 모두 아래의 식을 만족한다.

$f(x) = a_i(W_0+W_1x_{i1}+...+W_nx_{ip}) = W_0 + \sum^n_{i=1} a_i<W,x_i)$


위 초평면으로부터 $H_1$ 위의 점까지의 거리는 $1 \over \parallel w \parallel$ 이고, $\parallel w \parallel=\sqrt{ww}$이며, $W$의 유클리드 norm이다. 이는 의사결정선의 기울기와 동일하다. $H_2$의 거리 또한 동일하며, 따라서 최대 마진은 $2\over\parallel w \parallel$이다.
만약 가중치 벡터의 norm값 $\parallel w \parallel$을 2로 나누면 마진이 2배 커지는 반비례관계를 형성하고, 마진 값이 최대가 되는 경우는 $\parallel w \parallel$이 최소가 되는 경우와 같다. 즉 다음과 같은 목적함수를 최소화하면 되는 것이다.

$minimize {1\over2}{\parallel w \parallel}= {1\over2}{w^{T}w}$


임의의 두 벡터 $x_1=(x_11,…,x_1k)과 x_2=(x_21,…,x_2k)$의 내적은 다음과 같다.

$<x_1, x_2>=\sum^k_{i=1} x_{1i}x_{2i}$


여기에 차수가 d인 비선형 커널로 확장한 다항식 커널 함수를 적용하면 다음과 같다.

$K(x_i,x_j') = (1+\sum^p_{j=1} x_{ij}x_{i'j})^d$


d>1인 커널을 서포트 벡터 분류기 알고리즘에 적용하면 훨씬 유연한 결정경계가 만들어지고,
원래 변수 공간이 아닌 차원이 d인 더 높은 차원 공간에서 적합시키는 방법을 SVM이라고 하는 것이다.

다양한 커널이 있으며, 추가로 방사형 커널(RBF or Gaussian)의 수식은 아래와 같다.

$K(x_i,x_j') = exp(-\gamma\sum^p_{j=1} (x_{ij}-x_{i'j})^2)$


이외에도 시그모이드 커널등 다양한 커널이 있다.

예제 : Titanic

로지스틱 회귀분석에서 사용했던 변수 사용

import numpy as np
import pandas as pd

from sklearn import svm
from sklearn.model_selection import GridSearchCV

from matplotlib import pyplot as plt
from mlxtend.plotting import plot_decision_regions


%matplotlib inline

import warnings
warnings.filterwarnings('ignore')
df_svm = pd.read_csv('C:/Users/opqrs/Desktop/블로그용 쥬피터/Python_ML/titanic.csv').drop('Unnamed: 0', axis=1)

df_svm.head(5)
survived age sibsp parch fare pclass_2 pclass_3 sex_1
0 0 22.0 1 0 2.110213 0.0 1.0 1.0
1 1 38.0 1 0 4.280593 0.0 0.0 0.0
2 1 26.0 0 0 2.188856 0.0 1.0 0.0
3 1 35.0 1 0 3.990834 0.0 0.0 0.0
4 0 35.0 0 0 2.202765 0.0 1.0 1.0

선형커널(Linear SVC)

X = np.array(df_svm[['fare','age']])
y = np.array(df_svm['survived']) # 꼭 1D array 

clf = svm.SVC(kernel='linear', C=0.01)
clf.fit(X,y)
SVC(C=0.01, kernel='linear')
def svm_visualization(X, y, clf):
    plt.scatter(X[:,0], X[:,1], c=y, s=30, cmap=plt.cm.Paired)

    # 결정함수
    ax = plt.gca()
    xlim = ax.get_xlim()
    ylim = ax.get_ylim()

    # 모델 평가를 위한 그리드 생성
    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
    z = clf.decision_function(xy).reshape(XX.shape)

    # 결정경계와 마진
    ax.contour(XX, YY, z, colors='k', levels=[-1,0,1], alpha=0.5,
              linstyle=['--','-','--'])

    # 서포트 벡터
    ax.scatter(clf.support_vectors_[:,0], clf.support_vectors_[:,1], s=50,
              linewidths=1, facecolors='yellow', edgecolors='k')

    # 축 라벨링
    plt.xlabel('Fare')
    plt.ylabel('Age')

    plot_decision_regions(X, y, clf, legend = 2)

    plt.title('SVM-Linear', size=14)
    L = plt.legend()
    L.get_texts()[0].set_text('Survived=0')
    L.get_texts()[1].set_text('Survived=1')

svm_visualization(X, y, clf)
clf.score(X,y)

정의

네모모양은 사망 세모모양은 생존을 의미한다. 나이가 어리면서 요금이 높은 경우 생존 확률이 높다.
학습한 모델의 스코어는 0.6545이다.

GridSearchCV 활용 최적의 파라미터 탐색

SVM의 주요 파라미터는 다음과 같다.

  • C : cost, 마진 너비 조절 변수값이 클수록 마진 너비가 좁아짐
  • gamma : 작을수록 데이터 포인트의 영향이 커 경계가 완만해진다.
  • kernel : linear, rbf, poly, sigmoid, precomputed의 커널 방식

GridSearchCV의 주요 파라미터는 다음과 같다.

  • estimator : regressor, classifier
  • param_grid : dict 형태, 파라미터 튜닝을 위한 여러 파라미터 명과 값을 지정
  • n_jobs : CPU 코어 사용 개수(-1은 모든 코어 사용)
  • refit : 최적의 하이퍼 파라미터를 찾은 뒤 그 파라미터를 입력한 estimator로 재학습
  • cv : 교차검증을 위해 분할되는 세트의 수
# 파라미터 튜닝을 위한 파라미터 세팅
param_grid = {'C':[0.1, 1, 10, 100, 1000],
             'gamma':[1, 0.1, 0.01, 0.001, 0.0001],
             'kernel':['linear']}

grid = GridSearchCV(estimator=svm.SVC(), param_grid=param_grid, refit=True, scoring='accuracy', n_jobs=-1)
grid.fit(X,y)
GridSearchCV(estimator=SVC(), n_jobs=-1,
             param_grid={'C': [0.1, 1, 10, 100, 1000],
                         'gamma': [1, 0.1, 0.01, 0.001, 0.0001],
                         'kernel': ['linear']},
             scoring='accuracy')
df_svm_grid = pd.DataFrame(grid.cv_results_)
df_svm_grid.head()
df_svm_grid[['params','mean_test_score','rank_test_score']].sort_values(['rank_test_score']).head()
params mean_test_score rank_test_score
24 {'C': 1000, 'gamma': 0.0001, 'kernel': 'linear'} 0.681247 1
22 {'C': 1000, 'gamma': 0.01, 'kernel': 'linear'} 0.681247 1
21 {'C': 1000, 'gamma': 0.1, 'kernel': 'linear'} 0.681247 1
20 {'C': 1000, 'gamma': 1, 'kernel': 'linear'} 0.681247 1
23 {'C': 1000, 'gamma': 0.001, 'kernel': 'linear'} 0.681247 1

파라미터 최적화 이후 0.6545에서 0.6812까지 꽤나 스코어가 상승했다.

# 최적의 파라미터로 학습된 estimator를 clf로 입력
clf = grid.best_estimator_
svm_visualization(X, y, clf)
clf.score(X,y)
0.6825842696629213

정의

다항식 커널 적용

clf = svm.SVC(kernel='poly',degree=3, C=0.1, gamma='auto')
clf.fit(X,y)
svm_visualization(X, y, clf)

정의

clf.score(X,y)
0.6797752808988764

방사 기저 함수

clf = svm.SVC(kernel='rbf',C=10, gamma=0.1)
clf.fit(X,y)
svm_visualization(X, y, clf)
clf.score(X,y)
0.7359550561797753

정의

결과적으로 Linear < Linear(GridSearchCv) < poly < rbf 순으로 높은 성능을 보였다.

데이터 분할 및 정규화

feature_columns = df_svm.columns.difference(['survived'])

X = df_svm[feature_columns]
y = df_svm['survived']

# 정규화 
from sklearn import preprocessing
X = preprocessing.StandardScaler().fit(X).transform(X)

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.3, random_state=7)

모형 학습 및 검증

# 모형 객체 생성
svm_model = svm.SVC(kernel='rbf',C=10, gamma=0.1)

# 학습데이터로 학습
svm_model.fit(X_train, y_train)

# test 데이터로 y 예측
y_hat = svm_model.predict(X_test)

# 모형 성능 평가
svm_report = metrics.classification_report(y_test, y_hat)
print(svm_report)
              precision    recall  f1-score   support

           0       0.81      0.88      0.85       129
           1       0.80      0.69      0.74        85

    accuracy                           0.81       214
   macro avg       0.81      0.79      0.79       214
weighted avg       0.81      0.81      0.81       214

f1-score를 보면 dead(0)의 예측 정확도가 0.85 alive(1)의 예측 정확도가 0.74이다.
k-NN과 비교하여 살짝 성능이 좋아졌다. ※ 링크 최하단 참고

Leave a comment