본문 바로가기

문돌이 존버/데이터 분석

핸즈온 머신러닝 2 복습하기(챕터 5: 서포트 벡터 머신)

반응형

CH 5. 서포트 벡터 머신

선형 SVM 분류

서포트 벡터 머신(SVM, support vector machine)은 선형이나 비선형 분류, 회귀, 이상치 탐색에 사용할 수 있는 다목적 머신러닝 모델이다. SVM 분류기를 클래스 사이에 가장 폭이 넓은 도로를 찾는 것으로 생각할 수 있는데, 따라서 라지 마진 분류(large margin classification)라고도 한다. 아래 오른쪽 그림의 실선이 마진이 가장 큰 경우이다.

<출처: 핸즈온 머신러닝 깃허브>

마진이 가장 큰 경우를 찾는 것이 중요한 이유는 새로운 샘플이 들어왔을 때 영향을 적게 받기 때문이다. 다시 말해, 위그림의 실선은 실선 주변에 위치한 샘플(=서포트 백터)에 의해 전적으로 결정된다는 의미다. 또한, SVM은 특성 스케일에도 민감하여 사이킷런의 StandardScaler 등을 사용하는 것이 좋다.

소프트 마진 분류

모든 샘플이 경계 실선 바깥에 올바르게 분류된 것을 하드 마진 분류(hard margin classification)라고 한다. 하드 마진 분류는 2가지 문제점이 있는데, 하나는 데이터가 선형적으로 구분될 수 있어야 제대로 작동한다는 점, 다른 하나는 이상치에 민감하다는 점이다.

이를 해결하기 위해선, 1) 마진을 가능한 한 넓게 유지하는 것과 2) 마진 오류(margin violation, 샘플이 경계 중간이나 반대쪽에 있는 경우) 사이에 적절한 균형을 잡아야 하며, 이를 소프트 마진 분류(soft margin classification)라고 한다. 

사이킷런의 SVM 모델을 만들 때 지정할 수 있는 하이퍼파라미터 C가 있다. C 값에 따른 차이는 아래를 참고하면 되고, 일반적으로 마진 오류는 작은 것이 좋다. C=1일 때 마진 오류가 크지만 C=100보다 마진이 넓기 때문에 일반화는 더 잘 될 수 있다. 따라서 SVM 모델이 과적합이라면 C를 감소시켜 모델을 규제할 수 있다.

<출처: 핸즈온 머신러닝 깃허브>

간단한 선형 SVM 모델을 훈련시킨 과정이다. 

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()

# feature_name: ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
X = iris["data"][:, (2, 3)] # 변수: 꽃잎 길이, 너비 
y = (iris["target"] == 2).astype(np.float64) # Virginica(target == 2)면 1, 아니면 0

svm_clf = Pipeline([
          ("scaler", StandardScaler()), # LinearSVC의 경우 규제에 편향을 포함시키므로 스케일링 필수
          ("linear_svc", LinearSVC(C=1, loss="hinge")),
])

svm_clf.fit(X, y)
svm_clf.predict([[5.5, 1.7]])

>>> array([1.])

LinearSVC 클래스 대신 선형 커널을 사용하는 SVC 클래스로 대체할 수 있는데, SVC(kernel="linear", C=1) 식으로 쓰면 된다. 또는 SGDClassifier(loss="hinge", alpha=1/(m*C)) 를 사용하면 일반적인 확률적 경사 하강법을 적용한다. LinearSVC만큼 빠르게 수렴하진 않지만 데이터셋이 아주 크거나 온라인 학습 시 유용하다. 

비선형 SVM 분류

위에서 설명한 SVM은 선형 분류기에 속한다. 현실에선 비선형적 데이터가 훨씬 많으며 SVM은 비선형 역시 처리할 수 있다. 아래는 moons 데이터셋(마주보는 두 개의 반원 모양으로 데이터 포인트가 놓여 있는 이진 분류를 위한 작은 데이터셋)에 비선형 SVM을 적용한 코드이다. 

from sklearn.datasets import make_moons
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures

X, y = make_moons(n_samples=100, noise=0.15)
polynomial_svm_clf = Pipeline([
            ("poly_features", PolynomialFeatures(degree=3)),
            ("scaler", StandardScaler()),
            ("svm_clf", LinearSVC(C=10, loss="hinge"))
])

polynomial_svm_clf.fit(X, y)

<출처: 핸즈온 머신러닝 깃허브>

1. 다항식 커널

비선형 SVM을 처리하는 방법은 다항식 커널을 사용하는 것이다. 낮은 차수의 다항식은 매우 복잡한 데이터셋을 잘 표현하지 못하고, 높은 차원의 다항식은 굉장히 많은 특성을 추가하므로 모델을 느리게 한다. SVM은 커널 트릭(kernel trick)이란 수학적 기교를 통해 실제로는 특성을 추가하지 않으면서 다항식 특성을 많이 추가한 것과 같은 결과를 얻을 수 있다. 

아래 코드는 3차 다항식 커널을 사용해 SVM 분류기를 훈련한다. 파라미터 coef0 은 모델이 높은 차수와 낮은 차수에 얼마나 영향을 받을지 조절한다. 기본값은 0으로 적절한 값으로 지정하면 고차항의 영향을 줄일 수 있다. 

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)

2. 유사도 특성

비선형 특성을 다루는 또 다른 방법은 각 샘플이 특정 랜드마크(landmark)와 얼마나 닮았는지 측정하는 유사도 함수(similarity function)로 계산한 특성을 추가하는 것이다. 유사도 함수로 가우시안 방사 기저 함수(RBF, radial basis function)을 소개하겠다. 

$\phi_r(x, l) = exp(-\gamma\lVert x-l \rVert^2)$

함수 값은 0(랜드마크에서 아주 멀리 떨어진 경우)부터 1(랜드마크와 같은 위치일 경우)까지 변화하며 종 모양으로 나타난다. l이 랜드마크 지점이며, $\gamma$는 0보다 커야 하며 값이 작을수록 폭이 넓은 종 모양이 된다. 

랜드마크를 설정하는 간단한 방법은 모든 샘플 위치에 랜드마크를 설정하는 것이다. 이렇게 하면 차원이 커지고 따라서 변환된 훈련 세트가 선형적으로 구분될 가능성이 높다. 다만 훈련 세트에 있는 n개의 특성을 가진 m개의 샘플이 m개의 특성을 가진 m개의 샘플로 변환되기 때문에 훈련 세트가 클 경우 동일한 크기의 아주 많은 특성이 만들어진다. 

가우시안 RBF 커널

다항 특성 방식과 마찬가지로 유사도 특성 방식도 커널 트릭을 사용할 수 있다. 

rbf_kernel_svm_clf = Pipeline([
    ("scaler", StandardScaler()),
    ("svm_clf", SVC(kernel="rbf", gamma=5, C=0.001))
])
rbf_kernel_svm_clf.fit(X, y)

gamma($\gamma$)와 C를 바꾸어서 훈련시킨 모델을 참고해보자. gamma를 증가시키면 종 모양 그래프가 좁아져 각 샘플의 영향 범위가 작아진다. 결정 경계가 조금 더 불규칙해지고 각 샘플을 따라 구불구불하게 휘어진다. 

gamma를 감소시키면 넓은 종 모양 그래프를 만들며 샘플이 넓은 범위에 걸쳐 영향을 주므로 결정 경계가 부드러워진다. 즉 하이퍼파라미터 $\gamma$가 규제의 역할을 하여 과대적합일 경우 감소시키고, 과소적합일 경우 증가시키면 된다.(하이퍼파라미터 C와 비슷)

<출처: 핸즈온 머신러닝 깃허브>

그렇다면 여러 가지 커널 중 어떤 것을 사용해야 할까? 가장 먼저 시도해봐야 할 것은 선형 커널이다.(LinearSVC가 SVC(kernel="linear") 보다 훨씬 빠름) 훈련 세트가 너무 크지 않다면 가우시안 RBF 커널도 괜찮다. 보통 이 2가지로 많은 문제를 해결할 수 있다. 

SVM 회귀

SVM 회귀는 제한된 마진 오류(=경계 밖의 샘플) 안에서 경계 안에 가능한 한 많은 샘플이 들어가도록 학습한다. 일정한 마진 오류 안에서 두 클래스 간의 폭이 가능한 한 최대가 되도록 하는 SVM 분류와 다른 것이다. 경계 폭은 하이퍼파라미터 $\varepsilon$ 으로 저절한다. 따라서 마진 안에서는 훈련 샘플이 추가되어도 모델 예측에는 영향이 없기 때문에 $\varepsilon$에 민감하지 않다고 말한다. 

SVR은 SVC의 회귀 버전이고 LinearSVR은 LinearSVC의 회귀 버전이다. 

from sklearn.svm import LinearSVR

svm_reg = LinearSVR(epsilon=1.5)
svm_reg.fit(X, y)

<출처: 핸즈온 머신러닝 깃허브>

비선형 회귀 작업을 처리하려면 커널 SVM 모델을 사용하면 된다.

from sklearn.svm import SVR

svm_poly_reg = SVR(kernel="poly", degree=2, C=100, epsilon=0.1)
svm_poly_reg.fit(X, y)

SVM 이론

SVM 예측이 어떻게 이뤄지는지, 훈련 알고리즘이 어떻게 작동하는지 내가 이해한 선에서 정리해본다. 

결정 함수와 예측

선형 SVM 분류기 모델은 단순히 결정 함수 $w^Tx$ + b = $w_1x_1 +... + w_nx_x + b$ 를 계산해서 새로운 샘플 x의 클래스를 예측한다. 

$\hat{y}$ = 0 ($w^Tx + b < 0$ 일 때) / 1 ($w^Tx + b \ge 0$ 일 때)

결정 경계는 결정 함수의 값이 0인 점들로 이루어져 있다. 이 선을 중심으로 결정 함수의 값이 $\pm$n 인 선들이 마진을 형성하고 있는 것이다. 선형 SVM 분류기를 훈련한다는 것은 마진 오류를 하나도 발생하지 않거나(하드 마진), 제한적인 마진 오류를 가지면서 (소프트 마진) 가능한 한 마진을 크게 하는 w와 b를 찾는 것이다. 

목적 함수

결정 함수 기울기를 생각하면 가중치 벡터의 노름 $\lVert w \rVert$와 같다. 이 기울기를 2로 나누면 마진을 형성하는 결정 함수의 값이 2배만큼 더 멀어진다. 즉 기울기를 2로 나누면 마진은 2를 곱하는 것과 같다. 가중치 벡터 w가 작을수록 마진은 커진다. 

마진 오류를 하나도 만들지 않으려면(하드 마진) 결정 함수가 모든 양성 훈련 샘플에서는 1보다 커야 하고, 음성 훈련 샘플에서는 -1보다 작아야 한다. 음성 샘플($y^{(i)}$=0)일 때 $t^{(i)}$=-1로, 양성 샘플($y^{(i)}$=1)일 때 $t^{(i)}$=1로 정의하면 제약 조건을 모든 샘플에서 $t^{(i)}(w^Tx^{(i)}+b) \ge1$로 표현할 수 있다. 

하드 마진 선형 SVM 분류기의 목적 함수를 제약이 있는 최적화 문제(constrained optimization) 문제로 표현하면 아래와 같다.

minimize(w, b) = $1\over2$ $w^Tw$ [조건] i=1, 2, ..., m일 때 $t^{(i)}(w^Tx^{(i)}+b) \ge1$

$\lVert w \rVert$ 대신 $1\over2$ $\lVert w \rVert$ 인 $1\over2$ $w^Tw$를 최소화하는 이유는 더 깔끔하고 간단하게 미분되기 때문이다. 반면, $\lVert w \rVert$ 는 w=0에서 미분되지 않아 미분할 수 있는 함수에서 잘 작동된다. 

소프트 마진 분류기의 경우, 각 샘플에 대해 슬랙 변수(slack variable) $\zeta^{(i)} \ge0$을 도입해야 한다. $\zeta^{(i)}$는 i 번째 샘플이 얼마나 마진을 위반할지 정한다. 이 문제는 두 개의 상충되는 목표를 가지고 있는데, 하나는 마진 오류를 최소화하기 위해 가능한 한 슬랙 변수 값을 작게 만드는 것, 다른 하나는 마진을 크게 하기 위해(마진 오류가 증가할 가능성) $1\over2$ $w^Tw$를가능한 한 작게 만드는 것이다.

여기서 하이퍼파라미터 C가 두 목표 사이의 트레이드오프를 정의하게 된다.

minimize(w, b) = $1\over2$ $w^Tw$ + C$\sum_{i=1}^m \zeta^{(i)}$ [조건] i=1, 2, ..., m일 때 $t^{(i)}(w^Tx^{(i)}+b) \ge1-\zeta^{(i)}$ 이고 $\zeta^{(i)} \ge0$

참고로 하드 마진과 소프트 마진 문제는 모두 선형적인 제약 조건이 있는 볼록 함수의 이차 최적화 문제로 이를 콰드라틱 프로그래밍(QP, quardratic programming) 문제라고 한다. 관련 문제 공식은 내 수준을 벗어난 것 같아 설명은 생략한다. 

온라인 SVM

마지막으로 새로운 샘플이 생겼을 때 점진적으로 학습하는 온라인 학습을 위한 SVM 분류기를 간단히 설명한다. 온라인 SVM 분류기를 구현하는 한 가지 방법은 비용 함수를 최소화하기 위한 경사 하강법을 사용하는 것이다.(예를 들어 SGDClassifier) 하지만 경사 하강법은 QP 기반의 방법보다 훨씬 느리게 수렴한다는 단점이 있다.

J(w,b)= $1\over2$ $w^Tw$ +C$\sum_{i=1}^m$ $max(0,1-t^{(i)}(w^Tx^{(i)}+b))$ 

이 비용 함수의 첫 번째 항은 모델이 작은 가중치 벡터 w를 가지도록 제약을 가해 마진을 크게 만든다. 두 번째 항은 모든 마진 오류를 계산한다. 특정 샘플이 경계를 기준으로 올바르게 분류되었다면 마진 오류는 0, 잘못 분류되었다면 마진 오류는 경계선까지의 거리에 비례한다.

$max(0,1-t)$ 함수를 힌지 손실(hinge loss)라고 부르며 아래 그래프와 같다. 이 함수는 $t \ge1$일 때 0이며 도함수(기울기)는 t < 1이면 -1, t > 1이면 0이다. t = 1에서 미분이 불가능하지만 라쏘 회귀처럼 t = 1에서 서브그레디언트를 사용해(-1과 0 사이의 값) 경사 하강법을 사용할 수 있다. 참고로 SGDClassifier의 힌지 손실 함수는 t = 1일 때, -1을 사용한다.

라쏘 회귀에서 사용한 서브그레디언트 벡터 공식은 챕터 4에서도 설명했지만 다시 소개하면 다음과 같다.

$g(\theta, J)=MSE(\theta)+\alpha(sign(\theta_i))$ , 여기서 $sign(\theta_i)=-1(\theta_i<0일 때), 0(\theta_i=0일 때), 1(\theta_i>0일 때)$

<출처: 핸즈온 머신러닝 깃허브>

728x90
반응형