[핸즈온 머신러닝] 5강 SVM

2018. 7. 20. 19:06Python-이론/python-인공지능2

SVM(서포트 벡터 머신)


5.1 선형 SVM 분류 


svm의 기본 아이디어는 그림을 통해 설명하겠습니다. 


분꽃의 데이터를 활용해서 위와 같은데 그래프를 만들어 보았습니다. 왼쪽 사진의 점선은 두개의 데이터를 적절히 나누지 못했고 나머지 두 직선은 구분은 했지만 너무 가까워서 새로운 데이터가 들어갔을 때 알맞게 구분하기 힘들 것 입니다. 


오른쪽 그래프를 봅시다. 큰직선은 일정한 거리를 두고 두개의 데이터를 잘 구분하고 있습니다. 

여기서 일정한 거리를 마진 혹은 도로라고 하겠습니다. 오른쪽 그래프와 같이 마진에 데이터가 없고 실선으로 부터 최대한 멀리 떨어지게 만드는 것을 라지 마진 분류라고 합니다. 


그리고 오른쪽 점선에 딱 걸치고 있는 데이터 두개를 기준으로 새로운 데이터가 들어왔을 때 구분시켜 줍니다. 이러한 기준이 되는 데이터를 서포트 벡터라고 합니다. 


팁 서포터 백터 머신은 데이터들의 스케일을 맞추어 주는게 중요합니다. 그렇지 않으면 그래프가 이상하게 나와요. 


5.1.1 소프트 마진 분류 


마진(도로) 바깥쪽으로 올바르게 분류하는 것을 하드 마진 분류라고 합니다.  하드마진 분류는 두가지 문제점이 있는데 


1. 선형적으로 구분되어야하고 

2. 이상치에 민감합니다. 



왼쪽 그래프에서는 하드마진 분류를 사용할 수 없고 오른쪽 그래프는 위의 사진과 결정경계가 다르고 일반화를 잘 하지 못한 것 같습니다.  


이런 문제를 피하기 위해서는 조금 더 유연한 모델이 필요합니다. 이를 소프트 마진 분류라고 합니다. 


1. 최대한 마진거리를 유지 

2. 어느 정도 마진 오류를 인정


마진오류: 도로 한중간에 데이터가 있거나 반대편에 데이터가 존재하는 경우 


사이킷 런에서는 C라는 하이퍼 파라미터를 사용해서 균형을 맞추어 줄 수 있습니다. 


C가 크다면 마진의 크기가 작아지고 마진오류도 작아진다. 


C가 작다면 마진의 크기가 커지고 마진오류도 커집니다. 



만약 svm 모델이 과대적합이면 c를 감소시켜 조금 더 유연하게 만들어 줄 수 있고 과소 적합이라면 c를 증가시켜 타이트하게 만들어 줄 수 있습니다.



그럼 이제 코드에서 SVM을 사용해보겠습니다. 


1. SVC(kernel="linear", C=1)과 같이 SVC모델을 사용할 수 있습니다. 하지만 큰 데이터에서는 속도가 느려권장하지 않습니다. 


2. SGDClassifier(loss="hinge", alpha=1/(m*c))와 같이 SGDClassifier 모델을 사용하는 것 입니다. 속도가 많이 빠르진 않지만 데이터가 크고 특성이 많아서 메모리에 적재시켜줄 수 없을 때 이용하면 좋습니다. 


3. LinearSVC(C=1 ,loss="hinge")


등을 이용하여 코드로 표현할 수 있습니다. 



5.2 비선형 SVM 분류 


선형 SVM 분류기가 효율적이고 많은 경우에 아주 잘작동하지만 모든 데이터 셋이 선형적이지 않습니다. 비선형 데이터를 선형적으로 다루기 위해서는 특성 교차를 통해 다항특성을 더 추가하면 됩니다. 이를 구현하려면 PolynomialFeatures 변환기와 StandardScaler, LinearSVC를 연결하여 Pipeline을 만들면 좋습니다. 


코드 


1. 훈련


from sklearn.datasets import make_moons
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
from sklearn.svm import LinearSVC

X, Y = make_moons(n_samples=100, noise=0.15, random_state=42)
polynomial_svm_clf = Pipeline([
("poly_features", PolynomialFeatures(degree=3)), #3차식까지 자동으로 조합해줌
("scaler", StandardScaler()), #스케일러 맞춰줌
("svm_Clf", LinearSVC(C=10, loss="hinge"))
])

polynomial_svm_clf.fit(X, Y)


2. 그래프로 그려보기


import numpy as np
from matplotlib import pyplot as plt

def plot_dataset(X, y, axes):
plt.plot(X[:, 0][y==0], X[:, 1][y==0], "bs")
plt.plot(X[:, 0][y==1], X[:, 1][y==1], "g^")
plt.axis(axes)
plt.grid(True, which='both')
plt.xlabel(r"$x_1$", fontsize=20)
plt.ylabel(r"$x_2$", fontsize=20, rotation=0)

def plot_predictions(clf, axes):
x0s = np.linspace(axes[0], axes[1], 100)#axes[0]부터 axes[1]까지 100개로 이루어진 숫자들
x1s = np.linspace(axes[2], axes[3], 100)
x0, x1 = np.meshgrid(x0s, x1s) #표현할 수 있는 모든 배열조합
X = np.c_[x0.ravel(), x1.ravel()]# ravel 1차원 배열로 핀다.
y_pred = clf.predict(X).reshape(x0.shape)
y_decision = clf.decision_function(X).reshape(x0.shape)
plt.contourf(x0, x1, y_pred, cmap=plt.cm.brg, alpha=0.2)
plt.contourf(x0, x1, y_decision, cmap=plt.cm.brg, alpha=0.1)

plot_predictions(polynomial_svm_clf, [-1.5, 2.5, -1, 1.5])
plot_dataset(X, Y, [-1.5, 2.5, -1, 1.5])

plt.show()





5.2.1 다항식 커널 


다항식 특성을 추가하는 것은 간편하고 모든 비선형 데이터에 알맞게 사용할 수 있습니다. 하지만 다항식 특성이 간단하다면 복잡한 데이터 셋을 잘 표현하지 못하고 높은 차수의 다항식은 굉장히 많은 특성을 추가하므로 모델을 느리게 만듭니다. 


다행히도 SVM을 사용할 땐 커널 트릭이라는 수학적 기교를 사용하면 위와 같은 문제를 해결할 수 있습니다. 커널 트릭이란 실제로 특성을 추가하지 않고도 특성을 많이 추가한 것 같은 효과를 내는 것 입니다. 


코드

from sklearn.svm import SVC
poly_kernel_svm_clf = Pipeline([
("scaler", StandardScaler()),
("svm_clf", SVC(kernel="poly", degree=3, coef0=1, C=5))

#coef0는 모델이 높은 차수와 낮은 차수에 얼마나 영향을 끼치는지 정할 수 있다.
])

poly_kernel_svm_clf.fit(X, Y)


그래프 그리기


plt.figure(figsize=(11, 4)) plt.subplot(121) plot_predictions(poly_kernel_svm_clf, [-1.5, 2.5, -1, 1.5]) plot_dataset(X, Y, [-1.5, 2.5, -1, 1.5]) plt.title(r"$d=3, r=1, C=5$", fontsize=18)

plt.subplot(122) plot_predictions(poly_kernel_svm100_clf, [-1.5, 2.5, -1, 1.5]) plot_dataset(X, Y, [-1.5, 2.5, -1, 1.5]) plt.title(r"$d=10, r=100, C=5$", fontsize=18) plt.show()





5.2.2 유사도 특성 추가하기 


비선형 특성을 다루는 또 다른 기법은 각 샘플이 특정 랜드마크와 얼마나 닮았는지 측정하는 유사도 함수로 계산한 특성을 추가하는 것입니다. 그리고 가우시안 방사 기저 함수를 유사도 함수로 정의하면 된다. 


수식 


그래프


랜드마크를 설정하는 방법은 데이터 셋에 있는 모든 샘플위치에 랜드마크를 설정하는 것 입니다. 이렇게 하면 차원이 매우 커지고 따라서 변환된 훈련세트가 선형적으로 구분될 확률이 높습니다. 


단점은 훈련세트에 있는 n개의 특성을 가진 m개의 샘플이 m개의 특성을 가진 m개의 샘플이 된다는 것 입니다. 훈련 세트가 매우 클 경우 동일한 크기의 매우 많은 특성이 만들어집니다. 


5.2.3 가우시안 RBF 커널 


커널 기법이란 데이터를 고차원적으로 바꿔서 사상해주는 것이다(필요한 성질 이외는 버리다). rbf 뿐만 아니라 sigmoid, Polynomial 등등의 커널이 있지만 가우시안 RBF 커널이 제일 좋다. RBF 커널은 gamma라는 하이퍼파라미터가 있는데 각각의 샘플데이터의 영향력을 뜻한다. rbf는 종 모양으로 그래프가 만들어지는데 gamma가 작아지면 영향력이 커지고 커지면 작아지는 반비례 관계이다.  



rbf_kernel = Pipeline([
("scaler", StandardScaler()),
("svm_clf", SVC(kernel="rbf", gamma=5, C=0.001))
])
rbf_kernel.fit(X, Y)

  

그래프 그리기

from sklearn.svm import SVC

gamma1, gamma2 = 0.1, 5
C1, C2 = 0.001, 1000
hyperparams = (gamma1, C1), (gamma1, C2), (gamma2, C1), (gamma2, C2)

svm_clfs = []
for gamma, C in hyperparams:
rbf_kernel_svm_clf = Pipeline([
("scaler", StandardScaler()),
("svm_clf", SVC(kernel="rbf", gamma=gamma, C=C))
])
rbf_kernel_svm_clf.fit(X, Y)
svm_clfs.append(rbf_kernel_svm_clf)

plt.figure(figsize=(11, 7))

for i, svm_clf in enumerate(svm_clfs):
plt.subplot(221 + i)
plot_predictions(svm_clf, [-1.5, 2.5, -1, 1.5])
plot_dataset(X, Y, [-1.5, 2.5, -1, 1.5])
gamma, C = hyperparams[i]
plt.title(r"$\gamma = {}, C = {}$".format(gamma, C), fontsize=16)


plt.show()



5.3 SVM 회귀 


지금까지 이야기 한 것과 같이 svm은 회귀, 선형 데이터, 비선형 데이터 분류등 다양한 곳에서 사용할 수 있습니다. 그럼 이제 부터 회귀에 적용시켜 보겠습니다. 회귀에 적용하는 방법은 목표를 반대로 하는 것입니다. 


마진을 최대한 크게하고 도로안으로 도로 밖 데이터를 최대한 많이 들어가도록 학습하는 것 입니다. 

도르의 폭은 epilson 이라는 하이퍼파라미터를 사용하여 조절합니다. epilson은 마진과 비례합니다. 

from sklearn.svm import SVR #회귀는 SVR
svm_poly_reg = SVR(kernel="poly", degree=2, C=100, epsilon=0.1)
svm_poly_reg.fit(X,Y)