본문 바로가기

AI/기계학습(Machine Learning)

[기계학습/ML]16. SVM(Support Vector Machine) 서포트 벡터 머신

728x90
반응형

SVM(Support Vector Machine)이란?

SVM은 선형이나 비선형 분류, 회귀, 이상치 탐색 등에 사용 가능한 다목적 머신러닝 모델이다.

  • 분류에 적용
    • 라지 마진 분류(Large Margin Classification)에 해당한다.
      • 클래스를 구분 짓는 가장 폭이 넓은 도로 찾기
      • 서포트 벡터(Support Vector) : 도로 경계(결정 경계)에 위치한 샘플
      • ex. 붓꽃 데이터의 분류

  • 특성 스케일에 민감하다.
    • 특성 스케일링을 통해 더 나은 결정 경계(SV) 생성이 가능하다.
    • ex. 스케일링에 따른 결정 경계 차이

비선형 SVM 분류 - 하드 마진 분류

모든 샘플이 경계(Support Vector) 밖에 분류 되어야 한다.

  • 마진 오류 = 0

이상치에 민감하다.

  • 서포트 벡터를 찾지 못할 수도 있다.

ex. 붓꽃 데이터에 이상치 값을 추가한 경우

 

비선형 SVM 분류 - 소프트 마진 분류

일부 샘플이 경계(Support Vector) 안쪽이나 반대쪽에 있는 것을 허용한다.

  • 마진 오류 > 0

마진 오류가 있지만, 더 일반화되는 효과가 있다.

사이킷런의 SVM 모델의 하이퍼파라미터 "C"의 값을 통해 마진 오류 정도를 조절 가능하다.

ex. 붓꽃 데이터의 C 조정

 

SVM 분류 모델 예시 : LinearSVC

import numpy as np
from sklearn import datasets
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import LinearSVC

iris = datasets.load_iris()
X = iris['data'][:,(2,3)] # 꽃잎의 길이, 꽃잎 너비
y = (iris['target'] == 2).astype(np.float64) # iris virginica

# 머신러닝 모델 학습을 위한 파이프라인 작성
# 데이터를 특성 스케일링 후 LinearSVC에 전달한다.
# LinearSVC는 손실함수 "힌지손실함수", 마진 오류 1
svm_clf = Pipeline([
    ("scler",StandardScaler()),
    ("linear_svc",LinearSVC(C=1, loss="hinge",random_state=42)),
])

svm_clf.fit(X,y)

 

비선형 SVM 분류 - 다항 특성 추가

기존 특성으로 선형 분류가 불가능하다면, 다항 특성을 추가하여 분류한다.

예시 - 그림

예시 - moons 데이터셋의 비선형 분류

import matplotlib.pyplot as plt
from sklearn.datasets import make_moons
X,y = make_moons(n_samples=100,noise=0.15,random_state=42)

def plot_dataset(X,y,axes):
    plt.plot(X[:, 0][y==0], X[:, 1][y==0],'bs')
    plt.plot(X[:, 0][y==1], X[:, 1][y==1],'g^')
    plt.axis(axes)
    plt.grid(True,which='both')
    plt.xlabel(r'X1',fontsize=20)
    plt.ylabel(r'X2',fontsize=20,rotation=0)

plot_dataset(X,y,[-1.5,2.5,-1,1.5])
plt.show()

import numpy as np
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
from sklearn.svm import LinearSVC

polynomial_svm_clf = Pipeline([
    ("poly_features",PolynomialFeatures(degree=3)),
    ("scaler",StandardScaler()),
    ("svm_clf",LinearSVC(C=10,loss="hinge",random_state=42)),
])

polynomial_svm_clf.fit(X,y)

def plot_predict(clf,axes):
    x0s = np.linspace(axes[0],axes[1],100)
    x1s = np.linspace(axes[0],axes[3],100)
    x0,x1 = np.meshgrid(x0s,x1s)
    X = np.c_[x0.ravel(),x1.ravel()]
    y_pred = clf.predict(X).reshape(x0.shape)
    y_decision = clf.decision_function(X).reshape(x0.shape) 
    plt.contour(x0,x1,y_pred,cmap=plt.cm.brg,alpha=0.2)
    plt.contour(x0,x1,y_decision,cmap=plt.cm.brg,alpha=0.1)

plot_predict(polynomial_svm_clf,[-1.5,2.5,-1,1.5])
plot_dataset(X,y,[-1.5,2.5,-1,1.5])
plt.show()

 

비선형 SVM 분류 - 다항식 커널

다항식 특성 추가의 적절한 차수 찾기

  • 낮은 차수 - 복잡한 데이터셋을 잘 표현하지 못한다.
  • 높은 차수 - 굉장히 많은 특성이 추가돼 모델의 속도를 저하시킨다.

다항식 커널 트릭(kernel trick)

  • 실제로 특성을 추가하지 않으면서, 다항식 특성 추가와 같은 결과를 만드는 수학적 기교이다.
  • 사이킷런의 SVC 모델에서의 하이퍼파라미터 kernel='poly',degree 제공(LinearSVC는 커널 트릭 미제공)
    • d : degree(최고차 항)
    • r : 차수에 영향을 받는 정도
    • C : 마진 오류 정도
from sklearn.svm import SVC
poly_kernel_svm_clf = Pipeline([
    ('scaler',StandardScaler()),
    ('svm_clf',SVC(kernel='poly',degree=3,coef0=1,C=5))
])

poly_kernel_svm_clf.fit(X,y)

poly100_kernel_svm_clf = Pipeline([
    ('scaler',StandardScaler()),
    ('svm_clf',SVC(kernel='poly',degree=3,coef0=100,C=5))
])

poly100_kernel_svm_clf.fit(X,y)


plt.figure(figsize=(11,4))
plt.subplot(121)
# 위에서 정의했던 plot 그리는 함수
plot_predict(poly_kernel_svm_clf,[-1.5,2.5,-1,1.5])
plot_dataset(X,y,[-1.5,2.5,-1,1.5])
plt.title(r'd=3,r=1,C=5',fontsize=18)

plt.subplot(122)
# 위에서 정의했던 plot 그리는 함수
plot_predict(poly100_kernel_svm_clf,[-1.5,2.5,-1,1.5])
plot_dataset(X,y,[-1.5,2.5,-1,1.5])
plt.title(r'd=10,r=100,C=5',fontsize=18)
plt.show()

비선형 SVM 분류 - 유사도 특성 추가

  • 유사도 특성 추가
    • 기존 특성들을 가지고 유사도 특성을 추가한다.
    • 유사도 특성 : 각 샘플이 특정 랜드마크(landmark)와 얼마나 닮았는지 측정한 값이다.
      • 랜드마크 설정 : 모든 샘플 위치에 랜드마크 설정, 즉 m개 샘플이면 m x m 특성 생성
    • 유사도 함수(similarity function) : 유사도를 계산하는 함수이다.
      • 유사도 함수 예 : 가우시안 RBF(방사 기저 함수, radial basis function)

  • 예시, 유사도 특성 x2(랜드마크-2), x3(랜드마크 1)을 이용한 SVM 분류

비선형 SVM 분류 - 가우시안 RBF 커널

실제로 특성을 추가하지 않으면서, 유사도 특성 추가와 같은 결과를 만드는 수학적 기교이다.

사이킷런의 SVC 모델에서의 하이퍼파라미터 kernel='rbf', gamma 제공

gamma1,gamma2 = 0.1,5
C1,C2 = 0.001,1000
hyperparams = (gamma1,C1),(gamma1,C2),(gamma2,C1),(gamma2,C2)

svm_clfs = []
for gamma,C in hyperparams:
    rbf_kernel_svm_clf = Pipeline([
        ('scaler',StandardScaler()),
        ('svm_clf',SVC(kernel='rbf',gamma=gamma,C=C))
    ])
    rbf_kernel_svm_clf.fit(X,y)
    svm_clfs.append(rbf_kernel_svm_clf)

plt.figure(figsize=(11,7))

for i,svm_clf in enumerate(svm_clfs):
    plt.subplots_adjust(left=0.125, bottom=0.1, right=0.9, top=0.9, wspace=0.2, hspace=0.35)
    plt.subplot(221+i)
    plot_predict(svm_clf,[-1.5,2.5,-1,1.5])
    plot_dataset(X,y,[-1.5,2.5,-1,1.5])
    gamma,C = hyperparams[i]
    plt.title(r'γ = {0},C = {1}'.format(gamma,C),fontsize=16)

plt.show()

 

SVM 회귀

SVM을 분류가 아니라 회귀에 적용하는 방법이다.

목표를 반대로 하는 것 - 서포트 벡터 안쪽에 가능한 많은 샘플이 들어가도록 하는 것 이다.

SVM 회귀 - 선형 SVM 회귀

서포트 벡터의 폭은 하이퍼파라미터 ε(epsilon)으로 조절한다.

사이킷런의 선형 SVM 회귀 모델 : LinearSVR

np.random.seed(42)
m = 50
X = 2 * np.random.rand(m,1)
y = (4+3*X+np.random.randn(m,1)).ravel()

from sklearn.svm import LinearSVR

svm_reg1 = LinearSVR(epsilon=1.5,random_state=42)
svm_reg1.fit(X,y)
svm_reg2 = LinearSVR(epsilon=0.5,random_state=42)
svm_reg2.fit(X,y)

def find_SV(svm_reg,X,y):
    y_pred = svm_reg.predict(X)
    off_margin = (np.abs(y-y_pred)>= svm_reg.epsilon)
    return np.argwhere(off_margin)

svm_reg1.support_ = find_SV(svm_reg1,X,y)
svm_reg2.support_ = find_SV(svm_reg2,X,y)

eps_x1 = 1
eps_y_pred = svm_reg1.predict([[eps_x1]])

def plot_svm_regression(svm_reg,X,y,axes):
    x1s = np.linspace(axes[0],axes[1],100).reshape(100,1)
    y_pred = svm_reg.predict(x1s)
    plt.plot(x1s,y_pred,'k-',linewidth=2,label=r'y^')
    plt.plot(x1s,y_pred + svm_reg.epsilon,'k--')
    plt.plot(x1s,y_pred - svm_reg.epsilon,'k--')
    plt.scatter(X[svm_reg.support_],y[svm_reg.support_],s=180,facecolors='#FFAAAA')
    plt.plot(X,y,'bo')
    plt.xlabel(r'X1',fontsize=18)
    plt.legend(loc='upper left',fontsize=18)
    plt.axis(axes)

plt.figure(figsize=(9,4))
plt.subplot(121)
plot_svm_regression(svm_reg1,X,y,[0,2,3,11])
plt.title(r'epsilon = {0}'.format(svm_reg1.epsilon),fontsize=18)
plt.ylabel(r'y',fontsize=18,rotation=0)

plt.annotate(
    '',xy= (eps_x1,eps_y_pred),xycoords='data',
    xytext = (eps_x1,eps_y_pred-svm_reg1.epsilon),
    textcoords= 'data',arrowprops={'arrowstyle':'<->','linewidth':1.5}
)
plt.text(0.91,5.6,r'epsilon',fontsize=20)
plt.subplot(122)
plot_svm_regression(svm_reg2,X,y,[0,2,3,11])
plt.title(r"epsilon = {0}".format(svm_reg2.epsilon),fontsize=18)

plt.show()

SVM 회귀 - 비선형 SVM 회귀

다항 특성을 추가하거나 커널 트릭을 이용한다.사이킷런의 비선형 SVM 회귀 모델 : SVR

np.random.seed(42)
m = 100
X = 2*np.random.rand(m,1) - 1
y = (0.2 + 0.1 * X + 0.5 * X**2 + np.random.randn(m,1)/10).ravel()

from sklearn.svm import SVR

svm_poly_reg1 = SVR(kernel='poly',gamma='auto',degree=2,C=100,epsilon=0.1)
svm_poly_reg2 = SVR(kernel='poly',gamma='auto',degree=2,C=0.01,epsilon=0.1)
svm_poly_reg1.fit(X,y)
svm_poly_reg2.fit(X,y)

plt.figure(figsize=(9,4))
plt.subplot(121)
plot_svm_regression(svm_poly_reg1,X,y,[-1,1,0,1])
plt.title(r'degree = {0},C = {1}, epsilon = {2}'.format(svm_poly_reg1.degree,svm_poly_reg1.C,svm_poly_reg1.epsilon))
plt.ylabel(r'y',fontsize=18,rotation=0)
plt.subplot(122)
plot_svm_regression(svm_poly_reg2,X,y,[-1,1,0,1])
plt.title(r'degree = {0},C = {1}, epsilon = {2}'.format(svm_poly_reg2.degree,svm_poly_reg2.C,svm_poly_reg2.epsilon))
plt.ylabel(r'y',fontsize=18,rotation=0)

plt.show()

728x90
반응형