-
핸즈온 머신러닝[5] 서포트 벡터 머신(3)핸즈온머신러닝 2022. 7. 5. 18:24
https://www.youtube.com/watch?v=pIq527ZHiAE&list=PLJN246lAkhQjX3LOdLVnfdFaCbGouEBeb&index=18
SVM 이론
결정 함수와 예측
w는 가중치 벡터로, w^Tx + b = w1x1+w2x2+...+wnxn+b 이다.
음수일때 양성 클래스인지 양수일때 양성 클래스인지는 라이브러리마다 차이가 있다.
iris = datasets.load_iris() X = iris["data"][:, (2, 3)] # 꽃잎 길이, 꽃잎 너비 y = (iris["target"] == 2).astype(np.float64) # Iris virginica
특성은 꽃잎 길이, 꽃잎 너비 두가지를 사용하고 타깃은 이진 분류기로 Iris virginica인지 아닌지로 분류한다.
# 힌지 손실 함수 max(0, 1-t*y) from mpl_toolkits.mplot3d import Axes3D def plot_3D_decision_function(ax, w, b, x1_lim=[4, 6], x2_lim=[0.8, 2.8]): x1_in_bounds = (X[:, 0] > x1_lim[0]) & (X[:, 0] < x1_lim[1]) X_crop = X[x1_in_bounds] y_crop = y[x1_in_bounds] x1s = np.linspace(x1_lim[0], x1_lim[1], 20) x2s = np.linspace(x2_lim[0], x2_lim[1], 20) x1, x2 = np.meshgrid(x1s, x2s) xs = np.c_[x1.ravel(), x2.ravel()] df = (xs.dot(w) + b).reshape(x1.shape) m = 1 / np.linalg.norm(w) # 결정경계를 그리는 부분 y = xls * w[0] + x2s*w[1] + b boundary_x2s = -x1s*(w[0]/w[1])-b/w[1] margin_x2s_1 = -x1s*(w[0]/w[1])-(b-1)/w[1] margin_x2s_2 = -x1s*(w[0]/w[1])-(b+1)/w[1] ax.plot_surface(x1s, x2, np.zeros_like(x1), color="b", alpha=0.2, cstride=100, rstride=100) ax.plot(x1s, boundary_x2s, 0, "k-", linewidth=2, label=r"$h=0$") ax.plot(x1s, margin_x2s_1, 0, "k--", linewidth=2, label=r"$h=\pm 1$") ax.plot(x1s, margin_x2s_2, 0, "k--", linewidth=2) ax.plot(X_crop[:, 0][y_crop==1], X_crop[:, 1][y_crop==1], 0, "g^") ax.plot_wireframe(x1, x2, df, alpha=0.3, color="k") ax.plot(X_crop[:, 0][y_crop==0], X_crop[:, 1][y_crop==0], 0, "bs") ax.axis(x1_lim + x2_lim) ax.text(4.5, 2.5, 3.8, "Decision function $h$", fontsize=16) ax.set_xlabel(r"Petal length", fontsize=16, labelpad=10) ax.set_ylabel(r"Petal width", fontsize=16, labelpad=10) ax.set_zlabel(r"$h = \mathbf{w}^T \mathbf{x} + b$", fontsize=18, labelpad=5) ax.legend(loc="upper left", fontsize=16) fig = plt.figure(figsize=(11, 6)) ax1 = fig.add_subplot(111, projection='3d') plot_3D_decision_function(ax1, w=svm_clf2.coef_[0], b=svm_clf2.intercept_[0]) save_fig("iris_3D_plot") plt.show()
결정 경계는 결정 함수의 값이 0인 점들로 이루어져 있으며 두 평면의 교차점으로 직선이다.
def plot_2D_decision_function(w, b, ylabel=True, x1_lim=[-3, 3]): x1 = np.linspace(x1_lim[0], x1_lim[1], 200) y = w * x1 + b m = 1 / w plt.plot(x1, y) plt.plot(x1_lim, [1, 1], "k:") plt.plot(x1_lim, [-1, -1], "k:") plt.axhline(y=0, color='k') plt.axvline(x=0, color='k') plt.plot([m, m], [0, 1], "k--") plt.plot([-m, -m], [0, -1], "k--") plt.plot([-m, m], [0, 0], "k-o", linewidth=3) plt.axis(x1_lim + [-2, 2]) plt.xlabel(r"$x_1$", fontsize=16) if ylabel: plt.ylabel(r"$w_1 x_1$ ", rotation=0, fontsize=16) plt.title(r"$w_1 = {}$".format(w), fontsize=16) fig, axes = plt.subplots(ncols=2, figsize=(9, 3.2), sharey=True) plt.sca(axes[0]) plot_2D_decision_function(1, 0) plt.sca(axes[1]) plot_2D_decision_function(0.5, 0, ylabel=False) save_fig("small_w_large_margin_plot") plt.show()
작은 가중치 값이 큰 마진 값을 갖게끔 한다.
from sklearn.svm import SVC from sklearn import datasets iris = datasets.load_iris() X = iris["data"][:, (2, 3)] # 꽃잎 길이, 꽃잎 너비 y = (iris["target"] == 2).astype(np.float64) # Iris virginica svm_clf = SVC(kernel="linear", C=1) svm_clf.fit(X, y) svm_clf.predict([[5.3, 1.3]]) # array([1.])
사이킷런 svm 클래스로 예측한 결과
l2 norm 값은 sqrt(w1^2 + w2^2)인데 루트가 들어가므로 미분을 할 수 없다. 따라서 l2 norm을 제곱한 값을 사용하고 미분했을 때 상수항이 소거되도록 앞에 1/2를 곱해준다.
C가 작아지면 모델이 단순해지고 마진 오류는 늘어나는 경향이 있다.
힌지 손실 함수
t = np.linspace(-2, 4, 200) h = np.where(1 - t < 0, 0, 1 - t) # max(0, 1-t) plt.figure(figsize=(5,2.8)) plt.plot(t, h, "b-", linewidth=2, label="$max(0, 1 - t)$") plt.grid(True, which='both') plt.axhline(y=0, color='k') plt.axvline(x=0, color='k') plt.yticks(np.arange(-1, 2.5, 1)) plt.xlabel("$t$", fontsize=16) plt.axis([-2, 4, -1, 2.5]) plt.legend(loc="upper right", fontsize=16) save_fig("hinge_plot") plt.show()
힌지 손실 함수는 t 값이 1이상일때는 0이 되고, 1 이하일때는 t가 작아짐에 따라 선형적으로 증가하는 함수 모양이다.
따라서 마진 밖에 있어야 손실을 최소화한다는 svm의 목적을 달성할 수 있다.
훈련 시간
X, y = make_moons(n_samples=1000, noise=0.4, random_state=42) plt.plot(X[:, 0][y==0], X[:, 1][y==0], "bs") plt.plot(X[:, 0][y==1], X[:, 1][y==1], "g^")
import time tol = 0.1 tols = [] times = [] for i in range(10): svm_clf = SVC(kernel="poly", gamma=3, C=10, tol=tol, verbose=1) t1 = time.time() svm_clf.fit(X, y) t2 = time.time() times.append(t2-t1) tols.append(tol) print(i, tol, t2-t1) tol /= 10 plt.semilogx(tols, times, "bo-") plt.xlabel("Tolerance", fontsize=16) plt.ylabel("Time (seconds)", fontsize=16) plt.grid(True) plt.show()
Tolerence 값이 너무 작으면 알고리즘이 너무 불안정하다는 것을 보여주고 있다.
배치 경사 하강법을 사용한 선형 SVM 분류기 구현
# 훈련 세트 X = iris["data"][:, (2, 3)] # # 꽃잎 길이, 꽃잎 너비 y = (iris["target"] == 2).astype(np.float64).reshape(-1, 1) # Iris virginica
from sklearn.base import BaseEstimator class MyLinearSVC(BaseEstimator): def __init__(self, C=1, eta0=1, eta_d=10000, n_epochs=1000, random_state=None): self.C = C self.eta0 = eta0 self.n_epochs = n_epochs self.random_state = random_state self.eta_d = eta_d def eta(self, epoch): return self.eta0 / (epoch + self.eta_d) def fit(self, X, y): # Random initialization if self.random_state: np.random.seed(self.random_state) w = np.random.randn(X.shape[1], 1) # n feature weights b = 0 m = len(X) t = y * 2 - 1 # -1 if y==0, +1 if y==1 X_t = X * t self.Js=[] # Training for epoch in range(self.n_epochs): support_vectors_idx = (X_t.dot(w) + t * b < 1).ravel() X_t_sv = X_t[support_vectors_idx] t_sv = t[support_vectors_idx] J = 1/2 * np.sum(w * w) + self.C * (np.sum(1 - X_t_sv.dot(w)) - b * np.sum(t_sv)) self.Js.append(J) w_gradient_vector = w - self.C * np.sum(X_t_sv, axis=0).reshape(-1, 1) b_derivative = -self.C * np.sum(t_sv) w = w - self.eta(epoch) * w_gradient_vector b = b - self.eta(epoch) * b_derivative self.intercept_ = np.array([b]) self.coef_ = np.array([w]) support_vectors_idx = (X_t.dot(w) + t * b < 1).ravel() self.support_vectors_ = X[support_vectors_idx] return self def decision_function(self, X): return X.dot(self.coef_[0]) + self.intercept_[0] def predict(self, X): return (self.decision_function(X) >= 0).astype(np.float64) C=2 svm_clf = MyLinearSVC(C=C, eta0 = 10, eta_d = 1000, n_epochs=60000, random_state=2) svm_clf.fit(X, y) svm_clf.predict(np.array([[5, 2], [4, 1]]))
plt.plot(range(svm_clf.n_epochs), svm_clf.Js) plt.axis([0, svm_clf.n_epochs, 0, 100])
print(svm_clf.intercept_, svm_clf.coef_) #[-15.56761653] [[[2.28120287] # [2.71621742]]]
svm_clf2 = SVC(kernel="linear", C=C) svm_clf2.fit(X, y.ravel()) print(svm_clf2.intercept_, svm_clf2.coef_) # [-15.51721253] [[2.27128546 2.71287145]]
yr = y.ravel() fig, axes = plt.subplots(ncols=2, figsize=(11, 3.2), sharey=True) plt.sca(axes[0]) plt.plot(X[:, 0][yr==1], X[:, 1][yr==1], "g^", label="Iris virginica") plt.plot(X[:, 0][yr==0], X[:, 1][yr==0], "bs", label="Not Iris virginica") plot_svc_decision_boundary(svm_clf, 4, 6) plt.xlabel("Petal length", fontsize=14) plt.ylabel("Petal width", fontsize=14) plt.title("MyLinearSVC", fontsize=14) plt.axis([4, 6, 0.8, 2.8]) plt.legend(loc="upper left") plt.sca(axes[1]) plt.plot(X[:, 0][yr==1], X[:, 1][yr==1], "g^") plt.plot(X[:, 0][yr==0], X[:, 1][yr==0], "bs") plot_svc_decision_boundary(svm_clf2, 4, 6) plt.xlabel("Petal length", fontsize=14) plt.title("SVC", fontsize=14) plt.axis([4, 6, 0.8, 2.8])
from sklearn.linear_model import SGDClassifier sgd_clf = SGDClassifier(loss="hinge", alpha=0.017, max_iter=1000, tol=1e-3, random_state=42) sgd_clf.fit(X, y.ravel()) m = len(X) t = y * 2 - 1 # y==0이면 -1, y==1이면 +1 X_b = np.c_[np.ones((m, 1)), X] # 편향 x0=1을 추가합니다 X_b_t = X_b * t sgd_theta = np.r_[sgd_clf.intercept_[0], sgd_clf.coef_[0]] print(sgd_theta) support_vectors_idx = (X_b_t.dot(sgd_theta) < 1).ravel() sgd_clf.support_vectors_ = X[support_vectors_idx] sgd_clf.C = C plt.figure(figsize=(5.5,3.2)) plt.plot(X[:, 0][yr==1], X[:, 1][yr==1], "g^") plt.plot(X[:, 0][yr==0], X[:, 1][yr==0], "bs") plot_svc_decision_boundary(sgd_clf, 4, 6) plt.xlabel("Petal length", fontsize=14) plt.ylabel("Petal width", fontsize=14) plt.title("SGDClassifier", fontsize=14) plt.axis([4, 6, 0.8, 2.8])
문제
1. 원 문제(primal problem) 쌍대 문제(dual problem)이 각각 무엇이고 어떻게 다른지 말하시오
2. 선형적인 제약 조건이 있는 볼록 함수의 이차 최적화 문제를 OOOO 프로그래밍이라 한다.
3. 온라인 SVM이 무엇인지 그리고 온라인 SVM을 구현하는 한가지 방법을 말하시오
'핸즈온머신러닝' 카테고리의 다른 글
핸즈온 머신러닝[6] 결정 트리 (0) 2022.07.06 핸즈온 머신러닝[5] 서포트 벡터 머신(2) (0) 2022.07.03 핸즈온 머신러닝[5] 서포트 벡터 머신(1) (0) 2022.06.22 핸즈온 머신러닝[4] 모델 훈련(3) (0) 2022.06.14 핸즈온 머신러닝[4] 모델 훈련(2) (0) 2022.06.13