2022.03.23 수
Kernelized support vector machines
SVM
: 입력 데이터에서 단순한 초평면으로 정의되지 않는 더 복잡한 모델을 만들 수 있도록 확장한 것
회귀 & 분류 모두 사용 (여기서는 분류만 다룰 예정)
선형 모델 유연하게 만드는 방법 → 특성끼리 곱 or 특성끼리 거듭제곱 하여 새로운 특성 추가
from sklearn.datasets import make_blobs
X,y = make_blobs(centers=4, random_state=8)
y = y % 2
mglearn.discrete_scatter(X[:,0],X[:,1],y)
plt.xlabel("특성 0")
plt.ylabel("특성 1")
from sklearn.svm import LinearSVC
# SVC 적용
linear_svm = LinearSVC(max_iter=5000, tol=1e-3).fit(X,y)
# 2차원 데이터셋 분할 평면 그리기 (분류기, X)
mglearn.plots.plot_2d_separator(linear_svm, X)
# 산점도
mglearn.discrete_scatter(X[:,0],X[:,1],y)
plt.xlabel("특성 0")
plt.ylabel("특성 1")
비선형 특성 두 번째 특성^2 → 새로운 특성으로 추가
# (두 번째 특성^2) 추가
X_new = np.hstack([X, X[:,1:] ** 2])
from mpl_toolkits.mplot3d import Axes3D, axes3d
figure = plt.figure()
# 3차원 그래프
ax = Axes3D(figure, elev = -152, azim = -26)
# y == 0 인 포인트 그리고 -> y == 1인 포인트 그리기
mask = y == 0
ax.scatter(X_new[mask,0], X_new[mask,1], X_new[mask,2], c = 'b',
cmap = mglearn.cm2, s = 60, edgecolor='k')
ax.scatter(X_new[~mask,0], X_new[~mask,1], X_new[~mask,2], c = 'r',
marker = '^', cmap=mglearn.cm2, s=60, edgecolor='k')
ax.set_xlabel("특성 0")
ax.set_ylabel("특성 1")
ax.set_zlabel("특성 1 ** 2")
# SVC 적용
linear_svm_3d = LinearSVC(max_iter=5000).fit(X_new,y)
coef, intercept = linear_svm_3d.coef_.ravel(), linear_svm_3d.intercept_
# 선형 결정 경계 그리기
figure = plt.figure()
ax = Axes3D(figure, elev=-152, azim= -26)
xx = np.linspace(X_new[:,0].min() - 2, X_new[:,0].max() + 2, 50)
yy = np.linspace(X_new[:,1].min() - 2, X_new[:,1].max() + 2, 50)
XX, YY = np.meshgrid(xx, yy)
# 결정 경계
ZZ = (coef[0] * XX +
coef[1] * YY +
intercept)
/ -coef[2]
ax.plot_surface(XX, YY, ZZ, rstride=8, cstride=8, alpha=0.3)
ax.scatter(X_new[mask,0], X_new[mask,1], X_new[mask,2], c='b',
cmap=mglearn.cm2, s=60, edgecolor='k')
ax.scatter(X_new[~mask,0], X_new[~mask,1], X_new[~mask,2], c = 'r',
marker='^', cmap=mglearn.cm2, s = 60, edgecolor='k')
ax.set_xlabel("특성0")
ax.set_ylabel("특성1")
ax.set_zlabel("특성1**2")
w
b
ravel()
linspace()
meshgrid()
ZZ = (coef[0] * XX + coef[1] * YY + intercept) / -coef[2] 이유
# ZZ = YY의 제곱
ZZ = YY ** 2
dec = linear_svm_3d.decision_function(np.c_[XX.ravel(), YY.ravel(), ZZ.ravel()])
plt.contourf(XX, YY, dec.reshape(XX.shape), levels=[dec.min(), 0, dec.max()], cmap=mglearn.cm2, alpha=0.5)
mglearn.discrete_scatter(X[:,0], X[:,1], y)
plt.xlabel("특성 0")
plt.ylabel("특성 1")
커널 기법 특성 많이 추가 → 연산 비용 ⬆️ ⇒ 새로운 특성 많이 만들지 않고 고차원에서 분류기 학습 시킬 수 있게 하자! ⇒ 커널 기법
SVM 이해하기
각 훈련 데이터 포인트가 두 클래스 사이의 결정 경계 구분하는데 중요!
→ 훈련 데이터 중 일부만 영향을 줌
서포트 벡터 (Support Vector) 두 클래스 사이의 경계에 위치한 데이터 포인트
예측 각 서포트 벡터와의 거리 측정 → 가우시안 커널에 의해 계산됨 $k_{rbf}(x_1, x_2) = exp(-\gamma \|x_1 - x_2 \|)^2$
$exp$
$\gamma$
$\|x_1 - x_2 \|$
분류 결정 - 서포트 벡터까지의 거리
서포트 벡터 중요도 - 훈련 과정에서 학습 (SVC의 dual_coef_ 속성에 저장)
from sklearn.svm import SVC
# forge 데이터셋 사용
X, y = mglearn.tools.make_handcrafted_dataset()
# 서포트 벡터 머신
svm = SVC(kernel = 'rbf', C=10, gamma=0.1).fit(X,y)
mglearn.plots.plot_2d_separator(svm, X, eps=.5)
# 데이터 포인트 그리기
mglearn.discrete_scatter(X[:,0], X[:,1], y)
# 서포트 벡터
sv = svm.support_vectors_
# dual_coef_의 부호에 의해 서포트 벡터 클래스 레이블 결정됨
sv_labels = svm.dual_coef_.ravel() > 0
# 서포트 벡터 표시
mglearn.discrete_scatter(sv[:,0], sv[:,1], sv_labels, s=15, markeredgewidth=3)
plt.xlabel("특성0")
plt.ylabel("특성1")
매개변수 튜닝
svm = SVC(kernel = 'rbf', **C**=10, **gamma**=0.1).fit(X,y)
gamma
훈련 샘플이 미치는 영향의 범위 정함
= 가우시안 커널의 폭 커질 수록 → 영향 범위 커짐 ($\gamma$ 작음)
C 규제 매개변수 : 각 포인트의 중요도 제한 = dual_coef_ 값 제한
선형 모델에서와 비슷
fig, axes = plt.subplots(3,3,figsize=(15,10))
for ax, C in zip(axes, [-1, 0, 3]):
for a, gamma in zip(ax, range(-1,2)):
mglearn.plots.plot_svm(log_C=C, log_gamma=gamma, ax=a)
axes[0,0].legend(["클래스 0", "클래스 1", "클래스 0 서포트 벡터", "클래스 1 서포트벡터"], ncol=4, loc=(.9,1.2))
🔼작음 영향력작음 제약 큼 규제 🔽큼 영향력큼
◀️감소 범위 넓음 gamma ▶️증가 범위 제한
# 유방암 데이터셋 - 기본값
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer
cancer = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(
cancer.data, cancer.target, random_state=0)
svc = SVC()
svc.fit(X_train,y_train)
print("훈련 세트 정확도: {:.2f}".format(svc.score(X_train, y_train)))
print("테스트 세트 정확도: {:.2f}".format(svc.score(X_test, y_test)))
훈련 세트 정확도: 1.00 테스트 세트 정확도: 0.63
→ 과대적합
SVM ← 매개변수 & 데이터 스케일에 매우 민감
입력 특성 범위 비슷해야함
plt.boxplot(X_train, manage_ticks=False)
plt.yscale("symlog")
plt.xlabel("특성 목록")
plt.ylabel("특성 크기")
→ 입력 특성의 크기가 많이 차이남 → 커널 SVC 영향 많이 줌
해결 방법 ⇒ 데이터 전처리
데이터 전처리 특성값의 범위가 비슷해지도록 조정 MinMaxScaler or StandardScaler
# 훈련 세트에서 특성별 최소값 계산
min_on_training = X_train.min(axis=0)
# 훈련 세트에서 특성별 (최대-최소) 범위 계산
range_on_training = (X_train - min_on_training).max(axis=0)
# (훈련데이터 - 최소) / 범위 <최소:0 최대:1>
X_train_scaled = (X_train - min_on_training) / range_on_training
# 테스트에도 동일하게
X_test_scaled = (X_test - min_on_training) / range_on_training
svc = SVC()
svc.fit(X_train_scaled, y_train)
print("훈련 세트 정확도: {:.3f}".format(svc.score(X_train_scaled, y_train)))
print("테스트 세트 정확도: {:.3f}".format(svc.score(X_test_scaled, y_test)))
훈련 세트 정확도: 0.948 테스트 세트 정확도: 0.951
→ 둘이 너무 비슷
⇒ 과소적합
svc = SVC(C=1000)
svc.fit(X_train_scaled, y_train)
print("훈련 세트 정확도: {:.3f}".format(svc.score(X_train_scaled, y_train)))
print("테스트 세트 정확도: {:.3f}".format(svc.score(X_test_scaled, y_test)))
훈련 세트 정확도: 0.988 테스트 세트 정확도: 0.972
→ C값 증가
⇒ 성능 향상
장단점
샘플이 많을 때 ㅂㄹ
데이터 전처리와 매개변수 설정 신중히
→ 대부분 트리 기반 모델(랜덤 포레스트, 그레디언트 부스팅) 사용
⇒ 그래도 특성 비슷한 단위, 스케일이 비슷하다면 커널 서포트 벡터 머신 시도
모델 분석 어려움
매개변수