서포트 벡터 머신(SVM)은 매우 강력하고 선형이나 비선형 분류, 회귀, 이상치 탐색에도 사용할 수 있는 다목적 머신러닝 모델으로 머신러닝에서 가장 인기 있는 모델이기도 합니다. SVM은 특히 복잡한 분류 문제에 잘 들어맞으며 작거나 중간 크기의 데이터셋에 적합합니다.
서포트 벡터 머신 = 라지 마진 분류
SVM의 기본 아이디어는 붓꽃 데이터셋으로 설명할 수 있습니다. 아래는 꽃의 종류인 Iris versicolor와 Iris setosa의 너비가 있습니다. 왼쪽에서 점선은 분류를 잘 못하고 나머지 2개는 완벽해보이지만 결정 경계가 샘플에 너무 가까워 새로운 샘플에는 잘 작동하지 못 할 것 입니다. 반면 오른쪽의 경우 2 직선은 두 개의 클래스를 잘 나누고 있을 뿐만 아니라 제일 가까운 훈련 샘플로부터 멀리 떨어져있습니다. 오른쪽과 같은 SVM 분류기를 라지 마진 분류라고 합니다. 여기서 margin이란 남는 너비를 의미합니다.
도로 바깥쪽에 훈련 샘플을 더 추가해도 결정 경계에는 전혀 영향을 미치지 않습니다. 도로 경계에 위치한 샘플에 의해 전적으로 결정됩니다. 이런 샘플을 서포트 벡터라고 합니다. (방금 언급한 샘플은 동그라미로 표시된 샘플을 의미)
특성의 스케일에 민감한 SVM
SVM은 경사하강법과 마찬가지로 특성의 스케일에 민감합니다. 왼쪽 그래프에서는 수직푹의 스케일(0~100)이 수평축의 스케일(0~6)보다 훨씬 크기 때문에 가장 넓은 도로가 거의 수평에 가깝게 됩니다. 특성의 스케일(ex. StandardScaler)을 조정하면 결정 경계가 훨씬 좋아집니다(오른쪽 그래프)
하드 마진 분류와 소프트 마진 분류
모든 샘플이 도로 바깥쪽에 올바르게 분류되어 있다면 이를 하드 마진 분류라고 하고 어느정도 유연하게 도로의 폭을 가능한 넓게 유지하는 것을 소프트 마진 분류라고 합니다.
하드 마진 분류
하드 마진 분류이란 위에서 언급했듯이 모든 샘플이 도로 바깥쪽에 옮바르게 분류되어 경계를 깔끔하게 나눌 수 있는 경우를 의미합니다. 하지만 하드 마진 분류는 2가지 문제점이 있습니다, 하나는 데이터가 선형적으로 구분될 수 있을 때만 제대로 동작하고 다른 하나는 모든 샘플이 도로 바깥쪽에 분류되어야 하기 때문에 이상치에 매우 민감합니다. 아래 첫 번째 그림과 두 번째 그림에서는 이상치가 없었다면 확보할 수 있었던 라지 마진을 이상치 때문에 확보하지 못한 케이스입니다.
소프트 마진 분류
소프트 마진 분류이란 하드 마진 분류와 다르게 좀 더 유연합니다, 즉 어느정도의 샘플들은 도로 안쪽에 들어와도 된다는 것을 의미합니다. 그렇게 하면 도로 폭을 가능한 넓게 유지할 수 있지만 동시에 마진 오류(즉, 샘플이 도로 중간이나 심지어 반대쪽에 있는 경우) 사이에 적절한 균형을 잡아야 합니다.
아래 그림을 보면 C가 1일때, C가 100일때를 보여줍니다. C는 하이퍼파라미터 중 하나이며 이를 1처럼 낮게 설정하게 되면 마진이 넓어지지만 샘플이 도로 안에 포함되는 즉 마진 오류가 커지는 모습을 보여주지만 높게 설정한 100의 경우 마진 오류는 적게 냈지만 마진은 좁아졌다는 특징이 있습니다. 왼쪽 모델이 마진 오류가 더 많지만 일반화가 더 잘 될 것 같다는 것을 예측할 수 있습니다. 즉 C는 규제의 정도 의미
선형 SVM 모델
다음은 붓꽃 데이터셋을 적재하고, 특성 스케일을 변경하고, Iris-Virginia 품종을 감지하기 위해 SVM 모델을 훈련시키는 코드입니다. C를 1로 설정하였기 때문에 마진이 넓어지지만(유해지지만) 마진 오류가 커진다는 특징을 갖고 있습니다. 그리고 loss 매개변수를 'hinge'로 지정하였는데 이는 힌지 손실을 적용한다는 의미입니다.
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)] # petal length, petal width
y = (iris["target"] == 2).astype(np.float64) # Iris virginica
svm_clf = Pipeline([
("scaler", StandardScaler()),
("linear_svc", LinearSVC(C=1, loss="hinge", random_state=42)),
])
svm_clf.fit(X, y)
아래는 위에서 훈련시킨 SVM 모델로 값을 예측하는 코드입니다.
svm_clf.predict([[5.5, 1.7]]) # array([1.])
비선형 SVM 모델
선형 SVM 분류기가 효율적이고 많은 경우에 잘 동작하지만, 선형적으로 분류할 수 없는 데이터셋이 많습니다. 그러기 위해서는 다항 특성과 같은 특성을 추가하는 방법을 사용하여 데이터셋의 차원을 고차원으로 만들어 선형적으로 구분되는 데이터셋을 생성합니다.
아래 왼쪽 그림은 하나의 특성 x1 만을 가진 간단한 1차원 데이터셋으로 선형적으로 분류할 수 없는 상태입니다. 하지만 오른쪽 그림과 같이 다항 특성인 x2 = x1^2인 새로운 특성을 하나 추가하여 2차원 데이터셋으로 만들어 선형적으로 완벽하게 분류가 가능해졌습니다.
다항 특성과 선형 SVC
아래는 두 개의 반달 모양 데이터셋 생성 함수 make_moons을 사용하여 100개의 데이터셋을 생성한 후 차수가 3인 다항 특성을 생성한 후 특성 스케일러로 스케일링을 한 이후 C하이퍼 파라미터가 10인 SVM를 갖는 파이프라인 코드입니다.
여기서 make_moons() 함수는 두 개의 반원 모양으로 데이터 포인트가 놓여 있는 이진 분류를 위한 작은 데이터셋입니다.
아래처럼 make_moons 데이터셋은 원래 특성 [x1, x2] 공간에서는 선형적으로 나눠질 수 없으나, 차수가 3인 다항 특성 공간에서는 선형으로 나눠질 수 있기 때문에 다항 특성을 3차로 만들어줍니다.
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, random_state=42)
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)
다항식 커널
낮은 차수의 다항식은 매우 복잡한 데이터셋을 잘 표현하지 못하고 다항식 특성 추가는 간단하고 모든 ML 알고리즘에서 잘 동작하지만 높은 차수의 다항식은 굉장히 많은 특성을 추가하기 때문에 모델을 느리게 만듭니다.
다항식이 특성 추가에 따른 문제점 해결
높은 차수를 쓰면서 수학적으로 간단하게 만들어서 모델의 훈련을 빠르게할 수 있게 만든 것이 커널 트릭입니다. 커널 트릭은 실제로는 특성을 추가하지 않으면서 다항식 특성에 많이 추가한 것과 같은 결과를 얻을 수 있습니다. 이는 어떤 특성도 추가하지 않았기 때문에 엄청난 수의 특성 조합이 생기지 않으며 속도도 빠릅니다.
다음은 커널 트릭을 사용하여 3차 다항식 커널을 사용한 분류기 입니다. poly는 타항특성을 3차로 사용하겠다는 것입니다.
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)
고차원의 특성 형향을 얼마나 받을지 결정하는 것이 coef0이며 coef0을 줄일수록 고차원의 영향을 줄일 수 있습니다.
모델이 오버피팅이면 다항식의 차수를 줄여야하고 언더피팅이면 차수를 늘려야합니다.
poly100_kernel_svm_clf = Pipeline([
("scaler", StandardScaler()),
("svm_clf", SVC(kernel="poly", degree=10, coef0=100, C=5))
])
poly100_kernel_svm_clf.fit(X, y)
아래 그림처럼 확인할 수 있듯이 차수 d가 3일때보다 10인 경우 더 세밀하게 만들어내는 것을 확인할 수 있습니다.
유사도 특성
비선형 특성을 다루는 또 다른 기법은 각 샘플이 특정 랜드마크와 얼마나 닮았는지 측정하는 유사도 함수로 계산한 특성을 추가하는 것입니다. 다시 말해 아래 RBF 함수에서 x와 ℓ(랜드마크)의 유사도를 나타낼 수 있습니다.
𝜸 : 종 폭과 모양 (작을 수록 퍼짐)
ℓ : 랜드마크 지점
x : 내 현재 위치
첫 번째 그림을 확인하면 현재 내 위치x를 -1로 설정되어 있고 랜드마크ℓ는 -2와 1인 비선형 특성이 있습니다, 그리고 RBF 함수를 통해서 유사도를 계산할 수 있습니다. 계산해보면 현재 x와 x2에 유사도는 0.74라는 것을 확인할 수 있고 x와 x3은 0.3이라는 것을 알 수 있습니다
두 번째 그림을 확인하면 현재 특성 x3과 x2과 x1의 유사도의 관계를 확인할 수 있습니다.
랜드마크가 만약에 -1이라고 했을 경우에는 x2과 x3처럼 종 폭을 만들어 다른 특성들과의 유사도를 오른쪽 그림처럼 다시 표현하면 됩니다.
만약 -1을 랜드마크로 추가했다면 3차원 특성이므로 오른쪽 그래프도 3차원으로 변할 것입니다.
즉 RBF을 사용하면 특성(샘플)에 개수에 따라서 랜드마크를 만들고 특정 랜드마크를 기준으로 삼아 다른 특성들과의 얼마나 가까운지를 즉 유사도를 백터로 표현합니다, 그 다음으로도 다른 랜드마크를 기준으로 삼아서 다른 특성들과의 유사도를 백터로 표현합니다.
RBF를 사용하려면 SVC을 사용하고 kernel을 'rbf'로 설정합니다, rbf로 지정하면 반드시 gamma를 설정해야합니다. gamma는 𝜸는 종 폭과 모양을 의미하며 크면 클수록 뾰족해지고 작을수록 종 폭이 넓어집니다. 만약 𝜸를 증가시키면 랜드마크 주변만 탐색하겠다는 의미가 되며 결정 경계가 울퉁불퉁하게 됩니다.
위에서 설정했던 것과 다르게 가우시안 RBF 커널은 linear과 Poly와 다르게 kernel을 rbf로 설정하고 gamma를 사용하는 특징이 있습니다. C는 이전과 마찬가지로 도로의 폭을 결정합니다. (커지면 도로가 좁아지고 작아지면 넓어집니다)
from sklearn.svm import SVC
poly_kernel_svm_clf = Pipeline([
("scaler", StandardScaler()),
("svm_clf", SVC(kernel="rbf", gamma=5, C=0.001))
])
poly_kernel_svm_clf.fit(X, y)
즉 𝜸가 5인 그림에서는 랜드마크 주변만 탐색하기 때문에 울퉁불퉁한 것을 확인할 수 있지만 0.1인 경우에는 랜드마크 주위도 탐색하는 것을 확인할 수 있습니다. 우측 상단은 𝜸가 0.1인데도 불구하고 아래쪽이 랜드마크 주변만 탐색하는데 이는 C가 높게 설정되어서 그렇습니다.
즉 넓은 종 폭을 쓰더라도 C가 커지면 도로가 좁아지기 때문에 울퉁불퉁하게 변합니다.
𝜸이 높게 설정되어 뾰족한 종 모양이 된다면 조금만 다르더라도 유사도 평가에서 완전 틀리게 나오게 됩니다.(언더피팅 가능)
𝜸이 낮게 설정되어 완만한 종 모양이 된다면 더 유연하게 유사도를 평가하여 좀 다르더라도 같다고 할 수 있습니다(오버피팅 가능)
SVM의 계산 복잡도
서포트 백터 머신(SVM)의 복잡도는 2가지로 나눠져있습니다. SVM은 하나는 커널 트릭을 사용하지 않는 lienarSVC, 다른 하나는 커널 트릭을 사용하는 SVC 이 있습니다.
여기서 커널 트릭을 사용하지 않는 LinearSVC 클래스는 선형적으로 복잡도가 증가합니다.
> 계산 복잡도는 O(m x n), m은 샘플 수, n은 특성 수 입니다.
> 정밀도를 높이면 정밀도를 맞추기 위해서 알고리즘 수행 시간이 증가하게 됩니다.
- 허용오차 하이퍼파라미터를 통해서 조절하는데 sklearn에서 매개변수 tol로 값을 지정할 수 있습니다.
다음이면 커널 트릭을 사용하는 SVC는 libsvm 라이브러리의 기반입니다.
> 훈련 시간 복잡도는 보통 O(m^2 x n)과 O(m^3 x n) 사이로 샘플의 수가 증가할수록 엄청나게 증가하게 됩니다.
- 하지만 중간 규모나 적은 수의 샘플을 사용할 경우 엄청나게 잘 동작을 합니다.
- 희소 특성인 경우에는 확장이 잘 된다는 특징이 있습니다 (희소 특성은 샘플에 0이 아닌 특성을 몇 개만 가지는 것, 즉 대부분 0)
SVM 회귀
앞에서는 SVM을 분류에 사용하였지만 회귀에도 사용할 수 있습니다. 만약 SVM의 분류가 두 클래스간의 도로폭을 최대로 가져가는게 목적이라면 회귀는 제한된 마진 오류(도로 밖) 안에서 최대한 가지고 있는 샘플들을 많이 담을 수 있는 도로를 발견해야하는 것입니다.
즉 SVM 회귀는 제한된 마진 오류(즉, 도로 밖의 샘플) 안에서 도로 안에 가능한 한 많은 샘플이 들어가도록 학습합니다. 도로의 폭은 하이퍼파라미터 𝓔으로 조절합니다.
아래를 보면 𝓔이 크면 도로의 폭이 넓어지고 작으면 좁아지는 것을 확인할 수 있습니다. 이때 sklearn에서는 tol 매개변수가 𝓔입니다.
아래 코드는 sklearn의 LinearSVR을 사용해 선형 SVM 회귀를 적용한 코드입니다. 𝓔는 sklearn에서 epsilon 매개변수로 사용되며 이는 도로의 폭을 결정합니다.
from sklearn.svm import LinearSVR
svm_reg = LinearSVR(epsilon=1.5)
svm_reg.fit(X, y)
그리고 sklearn의 SVR을 사용해 비선형 회귀 작업을 처리할 수 있습니다.
C: 클수록 모델에 fit 하고 작을수록 더 멀어지게 됩니다.
𝓔: 도로의 폭을 결정하며 클수록 도로는 넓어지고 작을수록 도로는 좁아집니다.
fromsklearn.sunimport SVR
svm_poly_reg =SVR(kernel="poly", degree=2, C=100, epsilon=0.1, gamma="auto")
sm_poly_reg.fit(X, y)
- SVR은 SVC (SVM 분류기)의 회귀 버전
- LinearSVR은 LinearSVC (선형 SVM 분류기) 회귀 버전
- LinearSVR은 (LinearSVC 처럼) 필요 시간이 훈련셋 크기에 선형 비례
- SVR은 (SVC 처럼) 훈련셋이 커지면 매우 느려짐
데이터의 수가 많은 경우 SVC를 사용하고 적으면 SVR을 사용합니다.
'AI > Machine Learning' 카테고리의 다른 글
머신러닝 - 결정 트리 (0) | 2024.08.24 |
---|---|
머신러닝 - 모델 훈련, 선형회귀, 경사하강법 (0) | 2024.08.14 |
머신러닝 - 로지스틱 회귀 (0) | 2024.08.14 |
사이킷런 scikit-learn 이란? (0) | 2024.07.09 |
여러 개의 레이블을 갖는 하나의 데이터 분류하기 - 다중 레이블 분류 (2) | 2024.07.08 |