목차
👀, 🤷♀️ , 📜
이 아이콘들을 누르시면 코드, 개념 부가 설명을 보실 수 있습니다:)
SVM이란?
서포트 벡터 머신 SVM(support vector machine)
- 매우 강력함: 머신러닝 모델 중 가장 인기있는 모델 중 하나임
- 다목적 머신러닝 모델: 선형 or 비선형 분류 회귀, 이상치 탐색에 사용가능
- 사용 용도
- 특히 복잡한 분류 문제에 적합
- 작거나 중간 크기의 데이터셋에 적합
선형 SVM 분류
- 직선: (선형적으로 구분) 두 클래스가 으로 잘 나뉘어짐
- 점선: 클래스를 적절하게 분류하지 못함
기존 선형 분류기
이 분류기의 직선은 훈련 세트에 대해 완벽하게 작용한다.
🚫 하지만, 결정 경계가 샘플에 너무 가까워 새로운 샘플에 대해서는 잘 작동하지 못할 가능성이 큼
👀코드 보기
x0 = np.linspace(0, 5.5, 200)
pred_1 = 5*x0 - 20
pred_2 = x0 - 1.8
pred_3 = 0.1 * x0 + 0.5
def plot_svc_decision_boundary(svm_clf, xmin, xmax):
w = svm_clf.coef_[0]
b = svm_clf.intercept_[0]
# 결정 경계에서 w0*x0 + w1*x1 + b = 0 이므로
# => x1 = -w0/w1 * x0 - b/w1
x0 = np.linspace(xmin, xmax, 200)
decision_boundary = -w[0]/w[1] * x0 - b/w[1]
margin = 1/w[1]
gutter_up = decision_boundary + margin
gutter_down = decision_boundary - margin
svs = svm_clf.support_vectors_
plt.scatter(svs[:, 0], svs[:, 1], s=180, facecolors='#FFAAAA')
plt.plot(x0, decision_boundary, "k-", linewidth=2)
plt.plot(x0, gutter_up, "k--", linewidth=2)
plt.plot(x0, gutter_down, "k--", linewidth=2)
라지 마진 분류 large margin classification
이 분류기의 직선은 두 개의 클래스를 나누고 있을 뿐만 아니라,
제일 가까운 훈련 샘플 부터 가능한 한 멀리 떨어짐
➡️ 폭이 넓은 도로를 찾는 것으로 생각가능
👀 라지 마진 분류 코드 보기
먼저 실행 전 필요한 것을 실행시켜보자
# 파이썬 ≥3.5 필수
import sys
assert sys.version_info >= (3, 5)
# 사이킷런 ≥0.20 필수
import sklearn
assert sklearn.__version__ >= "0.20"
# 공통 모듈 임포트
import numpy as np
import os
# 노트북 실행 결과를 동일하게 유지하기 위해
np.random.seed(42)
# 깔끔한 그래프 출력을 위해
%matplotlib inline
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.rc('axes', labelsize=14)
mpl.rc('xtick', labelsize=12)
mpl.rc('ytick', labelsize=12)
# 그림을 저장할 위치
PROJECT_ROOT_DIR = "."
CHAPTER_ID = "svm"
IMAGES_PATH = os.path.join(PROJECT_ROOT_DIR, "images", CHAPTER_ID)
os.makedirs(IMAGES_PATH, exist_ok=True)
def save_fig(fig_id, tight_layout=True, fig_extension="png", resolution=300):
path = os.path.join(IMAGES_PATH, fig_id + "." + fig_extension)
print("그림 저장:", fig_id)
if tight_layout:
plt.tight_layout()
plt.savefig(path, format=fig_extension, dpi=resolution)
from sklearn.svm import SVC
from sklearn import datasets
iris = datasets.load_iris()
X = iris["data"][:, (2, 3)] # 꽃잎 길이, 꽃잎 너비
y = iris["target"]
setosa_or_versicolor = (y == 0) | (y == 1)
X = X[setosa_or_versicolor]
y = y[setosa_or_versicolor]
# SVM 분류 모델
svm_clf = SVC(kernel="linear", C=float("inf"))
svm_clf.fit(X, y)
👀두 분류기 시각화 코드 보기
fig, axes = plt.subplots(ncols=2, figsize=(10,2.7), sharey=True)
plt.sca(axes[0])
plt.plot(x0, pred_1, "g--", linewidth=2)
plt.plot(x0, pred_2, "m-", linewidth=2)
plt.plot(x0, pred_3, "r-", linewidth=2)
plt.plot(X[:, 0][y==1], X[:, 1][y==1], "bs", label="Iris versicolor")
plt.plot(X[:, 0][y==0], X[:, 1][y==0], "yo", label="Iris setosa")
plt.xlabel("Petal length", fontsize=14)
plt.ylabel("Petal width", fontsize=14)
plt.legend(loc="upper left", fontsize=14)
plt.axis([0, 5.5, 0, 2])
plt.sca(axes[1])
plot_svc_decision_boundary(svm_clf, 0, 5.5)
plt.plot(X[:, 0][y==1], X[:, 1][y==1], "bs")
plt.plot(X[:, 0][y==0], X[:, 1][y==0], "yo")
plt.xlabel("Petal length", fontsize=14)
plt.axis([0, 5.5, 0, 2])
save_fig("large_margin_classification_plot")
plt.show()
[서포트 벡터 support vector]
도로 바깥쪽에 훈련 샘플을 더 추가해도 결정경계에는 전혀 영향을 미치지않는다.
왜냐하면 이 분류기는 도로 경계에 위치한 샘플에 완전히 결정되기 때문이다.
이렇게 결정엥 영향을 미치는 도로경계에 위치한 벡터를 서포트 벡터라고 한다.
[스케일 조정]
SVM 은 데이터 특성의 스케일에 민감하다.
예를들어, 아래의 그림을 보면 수직축의 스케일이 수평축의 스케일보다 훨씬 크다.
➡️ \(X_1\)은 한 칸이 25 단위인데 ##X_0$$은 1단위이다. 즉 두 축의 스케일(단위)가 다르다는 것이다.
그래서 가장 넓은 도로가 거의 수평에 가깝게 된다.
이렇게 되면 상대적인 비율을 반영하지 못해 좋지 않은 분류기가 나온다.
즉 이를 조정할 필요가 있다.
➡️ 특성의 스케일을 조정하면 예를 결정 경계가 훨씬 좋아짐
사이킷런의 StandardScaler
을 이용하면 스케일 조정이 가능하다.
👀스케일에 따른 민감성 코드 보기
Xs = np.array([[1, 50], [5, 20], [3, 80], [5, 60]]).astype(np.float64)
ys = np.array([0, 0, 1, 1])
svm_clf = SVC(kernel="linear", C=100)
svm_clf.fit(Xs, ys)
plt.figure(figsize=(9,2.7))
plt.subplot(121)
plt.plot(Xs[:, 0][ys==1], Xs[:, 1][ys==1], "bo")
plt.plot(Xs[:, 0][ys==0], Xs[:, 1][ys==0], "ms")
plot_svc_decision_boundary(svm_clf, 0, 6)
plt.xlabel("$x_0$", fontsize=20)
plt.ylabel("$x_1$ ", fontsize=20, rotation=0)
plt.title("Unscaled", fontsize=16)
plt.axis([0, 6, 0, 90])
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(Xs)
svm_clf.fit(X_scaled, ys)
plt.subplot(122)
plt.plot(X_scaled[:, 0][ys==1], X_scaled[:, 1][ys==1], "bo")
plt.plot(X_scaled[:, 0][ys==0], X_scaled[:, 1][ys==0], "ms")
plot_svc_decision_boundary(svm_clf, -2, 2)
plt.xlabel("$x'_0$", fontsize=20)
plt.ylabel("$x'_1$ ", fontsize=20, rotation=0)
plt.title("Scaled", fontsize=16)
plt.axis([-2, 2, -2, 2])
소프트 마진 분류 soft margin classification
이 개념을 알아보기 전에 하드마진 분류의 문제점부터 알아보자.
📜 하드 마진 분류 hard margin classification
모든 샘플이 도로 바깥쪽에 올바르게 분류되어 있음.
[문제점]
- 데이터가 선형적으로 구분될 수 있어야 제대로 작동함
- 이상치에 민감함
이상치 하나 때문에 과하게 분류가 될 가능성이 높음
👀하드 코드 보기
X_outliers = np.array([[3.4, 1.3], [3.2, 0.8]])
y_outliers = np.array([0, 0])
Xo1 = np.concatenate([X, X_outliers[:1]], axis=0)
yo1 = np.concatenate([y, y_outliers[:1]], axis=0)
Xo2 = np.concatenate([X, X_outliers[1:]], axis=0)
yo2 = np.concatenate([y, y_outliers[1:]], axis=0)
svm_clf2 = SVC(kernel="linear", C=10**9)
svm_clf2.fit(Xo2, yo2)
fig, axes = plt.subplots(ncols=2, figsize=(10,2.7), sharey=True)
plt.sca(axes[0])
plt.plot(Xo1[:, 0][yo1==1], Xo1[:, 1][yo1==1], "bs")
plt.plot(Xo1[:, 0][yo1==0], Xo1[:, 1][yo1==0], "yo")
plt.text(0.3, 1.0, "Impossible!", fontsize=24, color="red")
plt.xlabel("Petal length", fontsize=14)
plt.ylabel("Petal width", fontsize=14)
plt.annotate("Outlier",
xy=(X_outliers[0][0], X_outliers[0][1]),
xytext=(2.5, 1.7),
ha="center",
arrowprops=dict(facecolor='black', shrink=0.1),
fontsize=16,
)
plt.axis([0, 5.5, 0, 2])
plt.sca(axes[1])
plt.plot(Xo2[:, 0][yo2==1], Xo2[:, 1][yo2==1], "bs")
plt.plot(Xo2[:, 0][yo2==0], Xo2[:, 1][yo2==0], "yo")
plot_svc_decision_boundary(svm_clf2, 0, 5.5)
plt.xlabel("Petal length", fontsize=14)
plt.annotate("Outlier",
xy=(X_outliers[1][0], X_outliers[1][1]),
xytext=(3.2, 0.08),
ha="center",
arrowprops=dict(facecolor='black', shrink=0.1),
fontsize=16,
)
plt.axis([0, 5.5, 0, 2])
save_fig("sensitivity_to_outliers_plot")
plt.show()
위의 하드 마진 모델의 문제를 피하려면 좀 더 유연한 모델이 필요하다.
이러한 문제점을 보완한 것이 소프트 마진 분류이다.
[소프트 마진 분류의 특징]
- 도로의 폭을 기능한 넓게 유지함
- 마진 오류 margin violation (샘플이 도로 중간이나 심지어 반대쪽에 있는 경우) 사이에 적절한 균형 잡음.
[C]
- 사이킷런의 SVM 모델을 만들 때 여러 하이퍼파라미터를 지정 가능.
- C는 이런 하이퍼파라미터 중에 하나임.
- C를 낮게 설정
이상치 허용을 많이 함
- C를 높게 설정
이상치 허용을 거의 안 함
- C를 낮게 설정
➡️ 마진 오류는 나쁘므로 일반적으로 적은 것이 좋음.
➡️ SVM 모델이 과적합이라면 를 감소시켜 모델을 규제할 수 있음
[코드]
다음 사이킷런 코드는 붓꽃 데이터셋을 적재함
특성 스케일을 변경
Iris-Virginia 품종을 감지하기 위해 선형 SVM 델을 훈련시킴
(C= 1 과 잠시 후에 설명할 힌지 손실 hinge loss함수를 적용한 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
svm_clf = Pipeline([
("scaler", StandardScaler()),
("linear_svc", LinearSVC(C=1, loss="hinge", random_state=42)),
])
svm_clf.fit(X, y)
Pipeline(steps=[('scaler', StandardScaler()),
('linear_svc', LinearSVC(C=1, loss='hinge', random_state=42))])
이 코드를 통해 이 모델이 만들어졌다.
그럼 위에 만든 모델을 사용하여 예측해보자
svm_clf.predict([[5.5, 1.7]])
array([1.])
➕ SVM 분류기는 로지스틱 회귀 분류기와는 다르게 클래스에 대한 확률을 제공하지 않음