import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings(action="ignore")
import matplotlib
from IPython.display import set_matplotlib_formats
matplotlib.rc('font', family='AppleGothic')
matplotlib.rc('axes', unicode_minus=False)
set_matplotlib_formats('retina')
pd.options.display.max_rows=100
pd.options.display.max_columns=100
pd.set_option('display.float_format', '{:.2f}'.format)
5. 서포트 벡터 머신¶
서포트 벡터 머신(support vector machine = SVM)은 매우 강력하고 선형이나 비선형 분류, 회귀, 이상치 탐색에도 사용할 수 있는 다목적 머신러닝 모델입니다. 머신러닝에서 가장 인기 있는 모델에 속하고 머신러닝에 관심 있는 사람이라면 반드시 알고 있어야 하는 모델입니다. SVM은 특히 복잡한 분류 문제에 잘 들어맞으며 작거나 중간 크기의 데이터셋에 적합합니다.
5.1 선형 SVM 분류¶
- 폭이 넓은 도로를 찾아 분류한다 = 라지 마진 분류 (large margin classification)
- 도로 바깥쪽에 훈련 샘플을 더 추가해도 결정 경계에는 전혀 영향을 미치지 않는다. 도로 경계에 위치한 샘플에 의해 전적으로 결정된다. 이런 샘플을 서포트 벡터라고 한다.
- SVM은 특성의 스케일에 민감하다. 특성 스케일을 조정하면(예>사이킷런의 StandardScaler) 결정 경계가 훨씬 좋아진다.
5.1.1 소프트 마진 분류¶
모든 샘플이 도로 바깥쪽에 올바르게 분류되어 있다면 이를 하드 마진 분류(hard margin classification)라고 한다. 하드 마진 분류에는 두 가지 문제점이 있다.
- 데이터가 선형적으로 구분될 수 있어야 제대로 작동하며,
- 이상치에 민감하다.
이런 문제를 피하려면 좀 더 유연한 모델이 필요하다. 도로의 폭을 가능한 한 넓게 유지하는 것과 마진 오류(margin violation)(즉, 샘플이 도로 중간이나 심지어 반대쪽에 있는 경우)사이에 적절한 균형을 잡아야 한다. 이를 소프트 마진 분류(soft margin classification)라고 한다.
사이킷런의 SVM모델에서는 C 하이퍼파라미터를 사용해 이 균형을 조절할 수 있다. C 값을 줄이면 도로의 폭이 넓어지지만 마진 오류도 커진다. C 값을 줄였을 때 마진이 넓어지지만 많은 샘플이 도로 안에 포함되게 된다. 일반화가 더 잘된 것으로 보인다. 대부분의 마진 오류는 결정 경계를 기준으로 올바른 클래스로 분류되기 때문에 이 훈련 세트에서 예측 에러는 마진 오류보다 작다.
- SVM 모델이 과대적합이라면 C를 감소시켜 모델을 규제할 수 있다.
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
svm_clf = Pipeline([
("scaler", StandardScaler()),
("linear_svc", LinearSVC(C=1, loss="hinge")),
])
svm_clf.fit(X, y)
Pipeline(steps=[('scaler', StandardScaler()), ('linear_svc', LinearSVC(C=1, loss='hinge'))])
svm_clf.predict([[5.5, 1.7]])
array([1.])
- SVM 분류기는 로지스틱 회귀 분류기와는 다르게 클래스에 대한 확률을 제공하지 않습니다. 사이킷런의 LinearSVC는 predict_proba() 메서드를 제공하지 않지만, SVC 모델은 probability=True로 매개변수를 지정하면 predict_proba() 메서드를 제공한다. SVC의 probability 매개변수 기본값은 False이다.
LinearSVC 외 다른 방법으로
SVC(kernel="linear", C=1)과 같이 SVC 모델을 사용할 수 있다. 하지만 큰 훈련 세트에서는 속도가 매우 느리므로 권장하지 않는다.
SGDClassifier(loss="hinge", alpha=1/(m*C))와 같이 SGDClassifier 모델을 사용할 수 있다. 이는 선형 SVM 분류기를 훈련시키기 위해 일반적인 확률적 경사 하강법을 적용한다. LinearSVC만큼 빠르게 수렴하지는 않지만 데이터셋이 아주 커서 메모리에 적재할 수 없거나(외부 메모리 훈련), 온라인 학습으로 분류 문제를 다룰 때는 유용하다.
- LinearSVC는 규제에 편향을 포함시킵니다. 그래서 훈련 세트에서 평균을 빼서 중앙에 맞춰야 합니다. StandardScaler를 사용하여 데이터 스케일을 맞추면 자동으로 이렇게 됩니다. 그리고 loss 매개변수를 "hinge"로 지정해야 합니다. 마지막으로 훈련 샘플보다 특성이 많지 않다면 성능을 높이기 위해 dual 매개변수를 False로 지정해야 합니다. = 이 내용은 SVC(kernel="linear")에 상응하는 LinearSVC의 설정을 설명하는 것입니다. LinearSVC는 보통의 SVM구현과 달리 규제에 편향을 포함하고 있어서 데이터의 스케일을 맞추지 않고 SVC 모델과 비교하면 큰 차이가 납니다. LinearSVC의 loss 매개변수의 기본값은 "squared_hinge"입니다.
5.2 비선형 SVM 분류¶
선형 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, random_state=42)
polynomial_svm_clf = Pipeline([
("poly_features", PolynomialFeatures(degree=3)),
("scaler", StandardScaler()),
("svm_clf", LinearSVC(C=10, loss="hinge")),
])
polynomial_svm_clf.fit(X, y)
Pipeline(steps=[('poly_features', PolynomialFeatures(degree=3)), ('scaler', StandardScaler()), ('svm_clf', LinearSVC(C=10, loss='hinge'))])
5.2.1 다항식 커널¶
낮은 차수의 다항식은 매우 복잡한 데이터셋을 잘 표현하지 못하고 높은 차수의 다항식은 굉장히 많은 특성을 추가하므로 모델을 느리게 만든다.
이때 커널 트릭(kernel trick)을 사용한다.
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)
Pipeline(steps=[('scaler', StandardScaler()), ('svm_clf', SVC(C=5, coef0=1, kernel='poly'))])
3차 다항식 커널을 사용해 SVM 분류기를 훈련시킨 것이다. 모델이 과대적합이라면 다항식의 차수를 줄여야 한다. 반대로 과소적합이라면 다항식의 차수를 늘려야 한다. 매개변수 coef0는 모델이 높은 차수와 낮은 차수에 얼마나 영향을 받을지 조절하는 것이다.
- 적절한 하이퍼파라미터를 찾는 일반적인 방법은 그리드 탐색을 사용하는 것이다. 처음에는 그리드의 폭을 크게 하여 빠르게 검색하고, 그다음에는 최적의 값을 찾기 위해 그리드를 세밀하게 검색한다.
- coef0 매개변수는 다항식 커널에 있는 상수항 r이다. coef0을 적절한 값으로 지정하면 고차항의 영향을 줄일 수 있다.
5.2.2 유사도 특성 추가¶
비선형 특성을 다루는 또 다른 기법은 각 샘플이 특정 랜드마크와 얼마나 닮았는지 측정하는 유사도 함수(similarity function)로 계산한 특성을 추가하는 것이다.
가우시안 방사 기저 함수(Radial Basis Function = RBF)를 유사도 함수로 정의할 수 있다.
랜드마크를 선택하는 간단한 방법은 데이터셋에 있는 모든 샘플 위치에 랜드마크를 설정하는 것이다. 훈련 세트가 매우 클 경우 동일한 크기의 아주 많은 특성이 만들어지게 된다.
5.2.3 가우시안 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)
Pipeline(steps=[('scaler', StandardScaler()), ('svm_clf', SVC(C=0.001, gamma=5))])
하이퍼파라미터 gamma(γ)와 C를 바꾸어서 훈련시킨 모델이다. gamma를 증가시키면 종 모양 그래프가 좁아져서 각 샘플의 영향 범위가 작아진다. 반대로 작은 gamma값은 넓은 종 모양 그래프를 만들며 샘플이 넓은 범위에 걸쳐 영향을 주므로 결정 경계가 더 부드러워진다. 결국 하이퍼파라미터 γ가 규제의 역할을 한다. 모델이 과대적합일 경우엔 감소시켜야하고 과소적합일 경우엔 증가시켜야 한다.
- 다른 커널도 있지만 거의 사용되지 않는다.
- 문자열 커널(string kernel)은 가끔 텍스트 문서나 DNA서열을 분류할 때 사용된다.
- 예) 문자열 서브시퀀스 커널(string subsequence kernel), 레벤슈타인 거리(Levenshtein distance)기반의 커널
- 여러 가지 커널 중 선형 커널을 가장 먼저 시도해보는 것이 좋다.
- LinearSVC가 SVC(kernel="linear")보다 훨씬 빠르다는 것을 기억해두자.
- 특히 훈련 세트가 아주 크거나 특성 수가 많을 경우에 그렇다.
- 훈련 세트가 너무 크지 않다면 가우시안 RBF 커널을 시도해보면 좋다.
- 대부분의 경우 이 커널이 잘 들어맞는다.
- 시간과 컴퓨팅 성능이 충분하다면(특히 훈련 데이터의 구조에 특화된 커널이 있는 경우) 교차 검증과 그리드 탐색을 사용해 다른 커널을 좀 더 시도해볼 수 있다.
5.2.4 계산 복잡도¶
5.3 SVM 회귀¶
SVM 알고리즘은 다목적으로 사용할 수 있습니다. 선형, 비선형 분류뿐만 아니라 선형, 비선형 회귀에도 사용할 수 있습니다. 회귀에 적용하는 방법은 목표를 반대로 하는 것입니다. 일정한 마진 오류 안에서 두 클래스 간의 도로 폭이 가능한 최대가 되도록 하는 대신, SVM회귀는 제한된 마진 오류(즉, 도로 밖의 샘플) 안에서 도로 안에 가능한 한 많은 샘플이 들어가도록 학습합니다. 도로의 폭은 하이퍼파라미터 ε으로 조절합니다.
마진 안에서는 훈련 샘플이 추가되어도 모델의 예측에는 영향이 없습니다. 그래서 이 모델을 ε에 민감하지 않다고 말합니다.
from sklearn.svm import LinearSVR
svm_reg = LinearSVR(epsilon=1.5)
svm_reg.fit(X, y)
LinearSVR(epsilon=1.5)
SVR은 SVC의 회귀 버전이고, LinearSVR은 LinearSVC의 회귀 버전이다.
- LinearSVR은 (LinearSVC처럼) 필요한 시간이 훈련 세트의 크기에 비례해서 선형적으로 늘어난다.
- 하지만 SVR은 (SVC처럼) 훈련 세트가 커지면 훨씬 느려진다.
from sklearn.svm import SVR
svm_poly_reg = SVR(kernel="poly", degree=2, C=100, epsilon=0.1)
svm_poly_reg.fit(X, y)
SVR(C=100, degree=2, kernel='poly')
SVM은 이상치 탐지에도 사용할 수 있다.
5.4 SVM 이론¶
5.4.1 결정 함수와 예측¶
5.4.2 목적 함수¶
5.4.3 콰드라틱 프로그래밍¶
5.4.4 쌍대 문제¶
5.4.5 커널 SVM¶
5.4.6 온라인 SVM¶
5.5 연습문제¶
'Data Analysis > Hands On ML' 카테고리의 다른 글
[Hands On ML] 7. 앙상블 학습과 랜덤 포레스트 (0) | 2021.11.04 |
---|---|
[Hands On ML] 6. 결정 트리 (0) | 2021.11.04 |
[Hands On ML] 4. 모델 훈련 (0) | 2021.11.04 |
[Hands On ML] 3. 분류 (0) | 2021.11.04 |
[Hands On ML] 2. 머신러닝 프로젝트 처음부터 끝까지 (0) | 2021.11.04 |