본문 바로가기
Python (Linux)

39. Python - SVM 회귀

by #Glacier 2019. 2. 7.
반응형

오랜만에 명절이 지나서야 포스팅하네요~

오늘은 SVM 회귀에 대해 공부합니다..

앞선 포스팅이 모두 기억나진 않지만, SVM 알고리즘은 다목적으로 사용할 수 있습니다.

선형, 비선형 분류 뿐만 아니라, 선형, 비선형 회귀에도 사용할 수 있습니다.


회귀에 적용하는 방법은, 목표를 반대로 하는 것입니다.

일정한 마진 오류 안에서, 두 클래스 간의 도로 폭이 가능한 한 최대가 되도록 하는 대신,

SVM 회귀는 제한된 마진 오류(즉, 도로 밖의 샘플) 안에서 도로 안에 가능한 한 많은 샘플이 들어가도록 학습합니다.

도로의 폭은 epsilon 하이퍼파라미터로 조절합니다.


아래는 무작위로 선형 데이터셋을 생성하고, 훈련시킨 두 개의 SVM 선형 회귀 모델입니다.

하나는 마진을 크게 (epsilon = 1.5), 다른 하나는 마진을 작게 (epsilon = 0.5) 하여 만들었습니다.


#시드 고정 후 가우시안 분포를 따르는 데이터셋을 만듭니다.

np.random.seed(42)

m = 50

X = 2 * np.random.rand(m,1)

y = (4+3*X+np.random.randn(m,1)).ravel()


from sklearn.svm import LinearSVR


svm_reg =LinearSVR(epsilon=1.5, random_state=42)

svm_reg.fit(X,y)

LinearSVR(C=1.0, dual=True, epsilon=1.5, fit_intercept=True,
     intercept_scaling=1.0, loss='epsilon_insensitive', max_iter=1000,
     random_state=42, tol=0.0001, verbose=0)


# svm_reg = 기본 모형

# svm_reg1 = 마진이 큰 모형(epsilon=1.5)

# svm_reg2 = 마진이 작은 모형(epsilon=0.5)

svm_reg1 = LinearSVR(epsilon=1.5, random_state=42)

svm_reg2 = LinearSVR(epsilon=0.5, random_state=42)

svm_reg1.fit(X,y)

svm_reg2.fit(X,y)


# 서포트 벡터 정하기

#svm_reg 모델을 받고, X로 예측한 값 = y_pred

#off_margin = 실제 y값과 예측값 사이의 오차를 절대값으로 표현하되, 해당 모형의 epsilon보다 크거나 같은 값

#np.argwhere는 행렬에서 True에 해당하는 값 위치를 반환합니다.

#따라서, 오차가 epsilon보다 큰 값들의 위치 반환하며 이 것들이 곧 서포트 벡터로 활용

def find_support_vectors(svm_reg, X, y):

    y_pred = svm_reg.predict(X)

    off_margin = (np.abs(y - y_pred) >= svm_reg.epsilon)

    return np.argwhere(off_margin)


svm_reg1.support_ = find_support_vectors(svm_reg1, X, y)

svm_reg2.support_ = find_support_vectors(svm_reg2, X, y)


eps_x1 = 1

eps_y_pred = svm_reg1.predict([[eps_x1]])


#plot찍기

#np.linspace로 axes의 첫 번째 값과 두 번째 값 사이를 100개로 쪼갠 일정한 값 생성 후 100행 1열로 reshape

#위 값(x1s)으로 예측한 y = y_pred

#x, y를 plot하고, y_pred에서 epsilon을 빼고 더한 값도 plot

#아까 구했던 서포트 벡터(지지도 벡터)를 scatter 찍기

def plot_svm_regression(svm_reg, X, y, axes) :

    x1s = np.linspace(axes[0], axes[1], 100).reshape(100,1)

    y_pred = svm_reg.predict(x1s)

    plt.plot(x1s, y_pred, 'k-', linewidth=2, label = r'$\hat{y}$')

    plt.plot(x1s, y_pred + svm_reg.epsilon, 'k--')

    plt.plot(x1s, y_pred - svm_reg.epsilon, 'k--')

    plt.scatter(X[svm_reg.support_], y[svm_reg.support_], s=180, facecolors='#FFAAAA')

    plt.plot(X, y, 'bo')

    plt.xlabel(r'$x_1$', fontsize=18)

    plt.legend(loc='upper left', fontsize=18)

    plt.axis(axes)

    

plt.figure(figsize=(9, 4))

plt.subplot(121)

plot_svm_regression(svm_reg1, X, y, [0,2,3,11])

plt.title(r'$\epsilon={}$'.format(svm_reg1.epsilon), fontsize=18)

plt.ylabel(r'$y$', fontsize=18, rotation=0)

#plt.plot([eps_x1, eps_x1], [eps_y_pred ,eps_y_pred - svm_reg1.epsilon], 'k-', linewidth=2)


#위에서 eps_x1 = 1 로 두고, 예측한 값 = eps_y_pred

#화살표와 함께 text를 넘기는 annotate

#따라서 도로의 폭을 나타냅니다. (epsilon)

#xy 는 주석을 달 위치입니다. ( eps_x1, eps_y_pred )

#xytext 는 xy위치에 넣을 text

#textcoords 는 지금 넣은 'data'가 default값이며, 주석을 달 객체의 좌표값 사용을 뜻합니다.

#arrowprops는 화살표설정


plt.annotate(

    ' ', xy = (eps_x1, eps_y_pred), xycoords='data', 

    xytext = (eps_x1, eps_y_pred-svm_reg1.epsilon),

    textcoords='data', arrowprops={'arrowstyle': '<->', 'linewidth' : 1.5}

    )


plt.text(0.91, 5.6, r'$\epsilon$', fontsize=20)

plt.subplot(122)

plot_svm_regression(svm_reg2, X, y, [0,2,3,11])

plt.title(r'$\epsilon={}$'.format(svm_reg2.epsilon), fontsize=18)

plt.show()



위의 그래프를 보면, 마진 안에서는 훈련 샘플이 추가되어도 모델의 예측에는 영향이 없는 것을 알 수 있습니다.

따라서 이 모델을, epsilon에 민감하지 않다()고 말합니다.


** 허용오차를 설명할 때 나온 하이퍼파라미터 ()와 혼동하지 마세요. 

** SVM 회귀 모델인 SVR와 LinearSVR에서 허용오차는 tol 매개변수, 도로의 폭은 epsilon 매개변수로 지정합니다.

** SVR과 LinearSVR의 tol 매개변수의 기본값은 SVC, LinearSVC와 마찬가지로 각각 0.001, 0.0001 입니다.



이제 비선형 회귀 작업을 처리해봅니다.

비선형 회귀 작업을 처리하기 위해 커널 SVM 모델을 사용합니다. 

임의의 2차방정식 형태의 훈련 세트에 2차 다항 커널을 사용한 SVM 회귀를 보겠습니다.

아래의 그래프중 왼쪽 의 그래프는 규제가 거의 없고(즉, 아주 큰 C), 오른쪽 그래프는 규제가 훨씬 많습니다. (작은 C)


#numpy.random.rand는 uniform distribution (균일 분포)를 따르며

#numpy.random.randn은 standard normal distribution (정규 분포, 가우시안 분포)를 따릅니다. 


np.random.seed(42)

m = 100

X = 2* np.random.rand(m,1) -1

y = (0.2 + 0.1 * X + 0.5 * X**2 + np.random.randn(m,1) / 10).ravel()


# 사이킷런 0.20버전에서 SVC, SVR 클래스의 gamma 매개변수 옵션에 auto 외에 scale이 추가되었습니다.

# auto는  1/n_features 즉, 특성 개수의 역수이며 scale은 1 / (n_features*X.std())로 스케일 조정이 되지 않은 특성에 더 좋은 결과!

# 사이킷런 0.22버전부터는 gamma 매개변수의 기본값이 auto에서 scale로 변경됩니다.

# 서포트 벡터 머신을 사용하기 전에 특성을 표준화 전처리하면 scale과 auto는 차이가 없으며 

# 현재는 경고를 피하기 위해 명시적으로 auto 옵션을 지정합니다.


from sklearn.svm import SVR


svm_poly_reg = SVR(kernel='poly', gamma='auto', degree=2, C=10, epsilon=0.1)

svm_poly_reg.fit(X,y)

SVR(C=10, cache_size=200, coef0=0.0, degree=2, epsilon=0.1, gamma='auto',
  kernel='poly', max_iter=-1, shrinking=True, tol=0.001, verbose=False)

svm_poly_reg1 = SVR(kernel='poly', gamma='auto', degree=2, C=100, epsilon=0.1)

svm_poly_reg2 = SVR(kernel='poly', gamma='auto', degree=2, C=0.01, epsilon=0.1)

svm_poly_reg1.fit(X,y)

svm_poly_reg2.fit(X,y)

SVR(C=0.01, cache_size=200, coef0=0.0, degree=2, epsilon=0.1, gamma='auto',
  kernel='poly', max_iter=-1, shrinking=True, tol=0.001, verbose=False)

plt.figure(figsize=(9,4))

plt.subplot(121)

plot_svm_regression(svm_poly_reg1, X, y, [-1, 1, 0, 1])

plt.title(r'$degree={}, C={}, \epsilon = {}$'.format(svm_poly_reg1.degree, svm_poly_reg1.C, svm_poly_reg1.epsilon), fontsize=18)

plt.ylabel(r'$y$', fontsize=18, rotation=0)

plt.subplot(122)

plot_svm_regression(svm_poly_reg2, X, y, [-1, 1, 0, 1])

plt.title(r'$degree={}, C={}, \epsilon = {}$'.format(svm_poly_reg2.degree, svm_poly_reg2.C, svm_poly_reg2.epsilon), fontsize=18)

plt.show()



SVR은 SVC의 회귀 버전이고, LinearSVR은 LinearSVC의 회귀버전입니다. 

필요한 시간이 훈련 세트의 크기에 비례해서 선형적으로 늘어나는 LinearSVR에 반해, SVR은 훈련 세트가 커지면 훨씬 느려집니다. 따라서 LinearSVR 사용!


위의 그래프를 보면, 규제가 클 수록 2차 형태의 선이 선형에 가까워짐을 알 수 있습니다.

또한, 규제가 클 수록, 서포트 벡터로 사용하는 도로 밖 샘플들이 많아지지만 도로 안의 샘플은 적어집니다.


이렇게 SVM 회귀도 알아보았는데요.

다음 시간엔 SVM회귀의 이론에 대해 알아보겠습니다.


 블로그 

출처


이 글의 상당 부분은  [핸즈온 머신러닝, 한빛미디어/오렐리앙 제롱/박해선] 서적을 참고하였습니다.


나머지는 부수적인 함수나 메서드에 대해 부족한 설명을 적어두었습니다.

학습용으로 포스팅 하는 것이기 때문에 복제보다는 머신러닝에 관심이 있다면 구매해보시길 추천합니다.


도움이 되셨다면 로그인 없이 가능한

아래 하트♥공감 버튼을 꾹 눌러주세요! 



반응형