[Python] SVM(Support Vector Machine)구현 실습

2020. 7. 16. 16:40ML in Python/Python

 

 SVM은 다른 분류모형처럼 Decision Boundary를 형성하는 것에 집중한다. 

 그리고 boundary의 결정은 위의 그림과 같이, 각 분포로부터 margin을 최대화하는 것을 목표로 결정하게 된다. 하지만 이러면 분산대비 평균의 차이를 극대화시키는 LDA와는 크게 다른 점이 없어 보인다.  

 그렇다면 아래와 같은 경우는 어떻게 되는가? 

 위의 그림을 보면 margin사이에 다른 데이터가 분포하고, 게다가 boundary를 넘어서 다른 집단의 데이터가 분포한다. 이러한 경우는 일반적인 데이터에 매우 많이 존재한다. 오히려 깔끔하게 boudary에 따라 분리되는 데이터를 찾는 경우가 더 힘들수도 있다. 

 SVM은 이러한 데이터의 경우를 생각하여, 적당한 error를 허용하며, 이를 최소화하는 boundary를 결정하는 것이다. 

 그리고 범주형 변수일 경우 Support Vector Classifier(SVC), 연속형 변수일 경우 Support Vector Regression(SVR)로 SVM의 사용법이 달라진다.  

상세한 원리와 수학적 이해가 필요하다면 아래 링크를 통해서 학습하길 바란다. 

https://todayisbetterthanyesterday.tistory.com/33

 

[Data Analysis 개념] (kernel)SVM - Support Vector Machine의 직관적 이해와 수학적 개념

*아래 학습은 Fastcampus의 "머신러닝 A-Z까지"라는 인터넷 강의에서 실습한 내용을 복습하며 학습과정을 공유하고자 복기한 내용입니다. 1. SVM의 직관적 이해 1) SVM의 직관적 이해 SVM은 다른 분류모�

todayisbetterthanyesterday.tistory.com

 

 이번 게시글에서는 Python을 통해 직접 구현해보는 시간을 갖도록 하겠다. 과정은

1. 기본적인 SVM 적합

2. 기본적인 Kernel SVM 적합 및 비교

3. 시각화를 통한 Kernel SVM 비교

순으로 진행된다.

 


 

1. 기본적인 SVM 적합

 

# 필요 라이브러리 import
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm, dataset

필요한 라이브러리들을 import한다.

datasets에서 iris 데이터를 이용하여 실습을 진행할 것이다. 그리고 svm 라이브러리에 svc/svr 모두 존재한다.

# 기본적인 SVM 모델적합
# iris 학습용 데이터 사용

iris = datasets.load_iris()
X = iris.data[:, :2]
y = iris.target

C = 1                         # SVM의 regularization parameter
clf. svm.SVC(kernel = "linear", C=C)
clf.fit(X,y)

# confusion matrix를 통한 정확도 확인

from sklearn.metrics import confusion_matrix    # confusion_matrix라이브러리
y_pred = clf.predict(X)                         # 학습데이터 분류예측
confusion_matrix(y, y_pred)                     # 정확성검정

confusion matrix 결과

이상 가장 기본적인 SVM(SVC) 분류모형을 확인해보았다. 이는 boundary가 선형으로 이루어진 SVM이다. 

kernel의 형태를 조정하여 2차원,3차원 혹은 다른 곡선형태의 boundary를 생성하는 SVM을 만들어낼 수 있다. 

다음 과정을 살펴보자 

 

2. 기본적인 Kernel SVM 적합 및 비교

1) Linear SVC

 위에서 SVC에서 kernel을  "linear"로 설정하여 Linear SVC 모델을 확인하였다. 하지만, sklearn의 SVM 모듈안에는 LinearSVC이라는 함수가 따로 존재한다. 그리고 결과 또한 차이가 발생한다.

 

# LinearSVM 활용
clf = svm.LinearSVC(C=C, max_iter = 10000)              # 학습 반복횟수 10000
clf.fit(X,y)
y_pred = clf.predict(X)
confusion_matrix(y, y_pred)

confusion matrix 결과

"1. 기본적인 SVM 적합"에서 확인한 confusion matrix와 결과가 다르다. 사실 둘다 Linear모델이고 SVC모델인데 결과가 다른 이유가 없지않나? 분명 차이가 있기에 결과가 다르게 나타나는 것이다. 

그 차이는 바로 사용하는 Loss fuction이 다르고 내부적으로 구현된 알고리즘에서 차이가 존재한다. 그렇기에 결과에서 또한 차이가 발생하는 것이다.

2) radial basis function SVC

# rbf 활용

clf = svm.SVC(kernel = 'rbf', gamma = 0.7, C=C, max_iter = 10000)    
#gamma는 sigma^2에 해당하는 scale parameter
#학습 반복횟수 10000

clf.fit(X,y)
y_pred = clf.predict(X)
confusion_matrix(y, y_pred)

2) polynomial SVC

# polynomial 활용

clf = svm.SVC(kernel = 'poly', degree = 3, gamma = 'auto', C=C, max_iter = 10000)    
#3차항으로 설정, degree = 3
#gamma는 sigma^2에 해당하는 scale parameter
#학습 반복횟수 10000

clf.fit(X,y)
y_pred = clf.predict(X)
confusion_matrix(y, y_pred)

 위와같이 rbf또는 polynomial의 우는 곡선이기에 0과 1사이의 scale parameter가 추가된다. 이는 auto로 설정하여 자동으로 찾게 할 수도 있고, 경험적으로 찾을 수도 있다. 게다가 polynomial의 경우 몇차항까지 고려할건지에 따른 degree설정 또한 필요하다. 

 이제 이 SVC의 형태를 시각적으로 확인해보자.

3. 시각화를 통한 Kernel SVM 비교

# 함수 정의
def make_meshgrid(x, y, h=.02):
    x_min, x_max = x.min() - 1, x.max() + 1
    y_min, y_max = y.min() - 1, y.max() + 1
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                         np.arange(y_min, y_max, h))
    return xx, yy


def plot_contours(ax, clf, xx, yy, **params):
    Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    out = ax.contourf(xx, yy, Z, **params)
    return out
 

위의 함수는 meshgrid를 통해 contour plot을 생성하는 함수이다. 

# 데이터 로드하기
iris = datasets.load_iris()

X = iris.data[:, :2]
y = iris.target

위와 동일한 iris데이터를 활용한다.

# 모델 정의&피팅
C = 1.0 #regularization parameter
models = (svm.SVC(kernel='linear', C=C),
          svm.LinearSVC(C=C, max_iter=10000),
          svm.SVC(kernel='rbf', gamma=0.7, C=C),
          svm.SVC(kernel='poly', degree=3, gamma='auto', C=C))
models = (clf.fit(X, y) for clf in models)

앞에서 했던 예제 모델을 다시 정리한 것이다. 

# plot title 형성
titles = ('SVC with linear kernel',
          'LinearSVC (linear kernel)',
          'SVC with RBF kernel',
          'SVC with polynomial (degree 3) kernel')

이는 plot에 들어갈 title을 붙여주기 위한 문자열 튜플이다. 

# plot 그리기

fig, sub = plt.subplots(2, 2)
plt.subplots_adjust(wspace=0.4, hspace=0.4)

X0, X1 = X[:, 0], X[:, 1]
xx, yy = make_meshgrid(X0, X1)

for clf, title, ax in zip(models, titles, sub.flatten()):
    plot_contours(ax, clf, xx, yy,
                  cmap=plt.cm.coolwarm, alpha=0.8)
    ax.scatter(X0, X1, c=y, cmap=plt.cm.coolwarm, s=20, edgecolors='k')
    ax.set_xlim(xx.min(), xx.max())
    ax.set_ylim(yy.min(), yy.max())
    ax.set_xlabel('Sepal length')
    ax.set_ylabel('Sepal width')
    ax.set_xticks(())
    ax.set_yticks(())
    ax.set_title(title)

plt.show()

위의 각 axis에 존재하는 plot들은 모형이 모두 다르다. 

1번째와 2번째 plot의 경우 둘다 선형이고 다른 함수를 썼기에 parameter를 잘 조정해주면 다른 방법으로 동일한 결과를 만들어 줄 수 있다.

3번째의 경우 radial basis function을 활용한 kernel SVC인데  이 경우는 방사형의 SVC boundary가 형성된 것을 볼 수 있다. 

4번째의 경우는 polynomial kernel SVC이다. 하지만 경계면이 굳이 저렇게 3차원의 형태일 필요는 없어보이고, 오히려 적절하지 않아 보이기에 데이터 분포에 맞지않는 kernel로 확인된다. 

이처럼 SVM은 다양한 kernel과 함께 다양한 경우에 구현을 달리할 수 있다.