3 분 소요

Support Vector Machine (SVM) 이란?

  • 선형, 비선형 분류 모두 사용 가능

  • 복잡한 분류 문제에 적합

  • 계산량의 이점과 입력 변수 차원의 덜 영향을 받음


마진(Margin) : 결정 경계(점선)로부터 양쪽 직선(실선)까지의 거리

1. 하드 마진 분류

  • 하나의 오차도 허용하지 않는 모델

  • 모든 데이터가 서포터 벡터의 바깥에 존재하는 경우

데이터가 선형적으로 구분 될 수 있어야 함

  • 이상치에 민감함

2. 소프트 마진 분류

  • 에러를 고려하는 모델

  • 에러에 대해 강건(Robust)하다

  • C : margin과 training error에 대한 trade-off를 결정하는 하이퍼 파라미터

  • C 크면 : training error에 비중. Error를 많이 허용하지 않음 (Overfit) = 하드 마진

  • C가 작으면 : margin에 비중. Error을 많이 허용 (Underfit) = 소프트 마진

Nonlinear(비선형) SVM

다변량 데이터에서

현실에서는 결정경계(Decision Boundary)가 선형으로 분류되는 경우는 거의 없음

따라서 입력변수에 대해 비선형 관계를 갖는 선이 아니라 면 (초평면, Hyperplan)인 결정경계가 필요하게 됨

Nonlinear(비선형) SVM with Kernel(커널)

선형 초평면을 비선형 초평면으로 바꾸고 차원을 늘림에 따라 계산량이 증가하게 됨

-> 커널(k) 도입

  • 커널을 이용해서 차원을 바꾸어서 분류

  • 원래의 차원에서는 비선형 분류가 됨

 

커널

**1. Polynomial Kernel**

  • 2차원 데이터를 3차원의 공간상으로 변형

 

**2. Radial Bias Function Kernel (RBF Kernel)**

  • 2차원 데이터를 무한차원의 공간상으로 변형

  • C와 gamma 파라미터의 적절한 조절이 필요

  • 커널은 사전 지식이 없다면 기본적으로 RBF를 쓰는 것이 좋음

 

**3. Sigmoid Kernel**

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

 

데이터 준비

from sklearn.model_selection import train_test_split

def get_iris():
    df = sns.load_dataset('iris')
    
    # 라벨 인코딩
    df['species'] = df['species'].map({
        'setosa': 0,
        'versicolor': 1,
        'virginica': 2
    })

    # 특성과 라벨 분리
    X, y = df.drop('species', axis=1), df['species']
    
    return train_test_split(X, y, test_size=0.2, random_state=2022) # train, test 분리
X_train, X_test, y_train, y_test = get_iris()

 

정규화

  • 표준화 (StandardScaler)

  • 최소-최대 정규화 (MinMaxScaler)

from sklearn.preprocessing import MinMaxScaler

# 최소-최대 정규화
scaler = MinMaxScaler()
scaler.fit(X_train)
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)

 

학습 (SVM 분류모델)

from sklearn.svm import SVC

# 선형으로 분류
clf = SVC(kernel='linear', random_state=2022)
clf.fit(X_train, y_train)
SVC(kernel='linear', random_state=2022)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
clf.score(X_train, y_train), clf.score(X_test, y_test)
(0.9666666666666667, 0.9666666666666667)
# X_train의 각각의 feature당 결정경계의 기울기
clf.coef_
array([[-0.53055058,  1.33970031, -2.11749153, -1.93817589],
       [-0.4691355 ,  0.51529246, -1.58409657, -1.48656971],
       [-0.54963312,  0.75497453, -2.66627722, -3.85961393]])
# 결정경계의 y절편
clf.intercept_
array([0.76496595, 1.17046034, 4.52990526])
y_pred = clf.predict(X_test)
from sklearn.metrics import accuracy_score, precision_score, recall_score, confusion_matrix

def print_score(y_true, y_pred, average='binary'):
    acc = accuracy_score(y_true, y_pred)
    pre = precision_score(y_true, y_pred, average=average)
    rec = recall_score(y_true, y_pred, average=average)

    print('accuracy:', acc)
    print('precision:', pre)
    print('recall:', rec)

print_score(y_test, y_pred, 'macro')
accuracy: 0.9666666666666667
precision: 0.9629629629629629
recall: 0.9743589743589745
def plot_confusion_matrix(y_true, y_pred):
    cfm = confusion_matrix(y_true, y_pred)
    sns.heatmap(cfm, annot=True)
    plt.xlabel('Predicted Class')
    plt.ylabel('True Class')
    plt.show()
plot_confusion_matrix(y_test, y_pred)

# Polynomial 커널
clf = SVC(kernel='poly', random_state=2022)
clf.fit(X_train, y_train)
clf.score(X_train, y_train), clf.score(X_test, y_test)
(0.9833333333333333, 0.9)

train 데이터에 과적합이 됨

y_pred = clf.predict(X_test)
print_score(y_test, y_pred, 'macro')
accuracy: 0.9
precision: 0.9090909090909092
recall: 0.923076923076923
plot_confusion_matrix(y_test, y_pred)

# rbf 커널
clf = SVC(kernel='rbf', random_state=2022)
clf.fit(X_train, y_train)
clf.score(X_train, y_train), clf.score(X_test, y_test)
(0.9916666666666667, 0.9666666666666667)
y_pred = clf.predict(X_test)
print_score(y_test, y_pred, 'macro')
accuracy: 0.9666666666666667
precision: 0.9629629629629629
recall: 0.9743589743589745
plot_confusion_matrix(y_test, y_pred)

모델 비교

from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC, LinearSVC

names = ['knn_5', 'knn_9', 'linearSVC', 'linear', 'poly', 'rbf']

models = [
    KNeighborsClassifier(n_neighbors=5),
    KNeighborsClassifier(n_neighbors=9),
    LinearSVC(C=1, max_iter=1000),
    SVC(kernel='linear', C=1),
    SVC(kernel='poly', degree=3),
    SVC(kernel='rbf', C=1, gamma=0.7),
]

scores = {}

for name, model in zip(names, models):
    model.fit(X_train, y_train)
    s = model.score(X_train, y_train)
    print(name, s)
    scores[name] = s
knn_5 0.9583333333333334
knn_9 0.9666666666666667
linearSVC 0.95
linear 0.9666666666666667
poly 0.9833333333333333
rbf 0.9666666666666667
plt.bar(scores.keys(), scores.values())
plt.show()

 

best 파라미터 선정 (GridSearch)

param_range = [0.001, 0.01, 0.1, 1, 10, 100, 1000]

params = [
    {
        'C': param_range,
        'gamma': param_range,
        'kernel': ['rbf']
    },
    {
        'C': param_range,
        'kernel': ['linear']
    },
    {
        'C': param_range,
        'degree': [2, 3],
        'kernel': ['poly']
    }
]
from sklearn.model_selection import GridSearchCV

clf = SVC(random_state=2022)

cv = GridSearchCV(estimator=clf, 
                  param_grid=params,
                  scoring='accuracy',
                  cv=3,
                  n_jobs=-1,
                 )

cv.fit(X_train, y_train)
cv.best_score_, cv.best_params_
(0.9833333333333334, {'C': 10, 'gamma': 1, 'kernel': 'rbf'})
clf = cv.best_estimator_
clf.score(X_test, y_test)
0.9333333333333333

댓글남기기