sklearn을 사용한 SVM(Support Vector Machine) 분류
- IT/머신러닝
- 2020. 2. 17. 03:54
파이썬 코드로 쉽게 배우는 머신러닝 시리즈 (3)
SVM(Support Vector Machine)
sklearn을 사용한 SVM 모델 학습으로 주어진 데이터셋을 분류하고 시각화해보려 한다. 참고로, 본 포스팅에서는 수학적 지식은 다루지 않고 sklearn의 SVM을 사용하기 위한 최소한의 개념을 다루며 '코드 구현과 활용'의 측면에 초점을 맞춘다. 또한 sklearn svc 함수의 매개변수 kernel, C에 따른 변화도 살펴본다.
SVM(Support Vector Machine) 이란?
SVM(Support Vector Machine)은 주어진 데이터가 어느 그룹에 속하는지 분류하는 분류 모델이다. 아직 포스팅하진 않은 신경망을 포함해 기존의 분류 모델들은 분류 오류를 최소화하려는 목적으로 설계되었지만 SVM은 두 부류 사이에 존재하는 여백을 의미하는 마진(margin)을 최대화하여 일반화 능력을 극대화하는 모델이다.
여기서 마진(margin)이란, 아래 이미지에서 보이는 바와 같이 SVM에서 데이터를 분류하기 위해 사용한 일종의 분류선과 가장 가까운 데이터들과의 거리를 의미한다. 여기서 분류선을 Decision Boundary라 부르고 이 선과 가장 가까운 데이터를 서포트 벡터(Support Vector)라고 부른다. 오차를 최소화하는게 아니라 마진을 극대화시키는 선을 찾기 때문에 확실히 다른 분류 모델과는 극명한 차이가 있는 모델이다.
학습/테스트(Train/Test) 데이터셋 설명
SVM 모델을 사용해 분류를 수행할 학습/데이터셋은 앞서 포스팅한 선형 회귀분석과 로지스틱 회귀분석에서 사용한 데이터셋과 같이 x,y 값으로 이루어진 간단한 1차원 데이터이다. 각 데이터를 plot한 결과는 아래와 같다.
sklearn을 사용한 SVM 분류 코드
파이썬 sklearn svm의 svc를 사용하였고 svc를 사용하면 fit(x data, label data)을 사용해 선언해준 모델을 학습시킬 수 있다. svc에 지정해줄 수 있는 인자가 굉장히 많은데 여기서는 svc의 인자로 kernel과 C를 사용할 것이다. kernel은 쉽게 데이터셋을 분류할 커널 방식을 지정해준다고 생각하면 되고 C는 마진과 관계가 있다.
마진값이 작더라도 데이터셋을 정확히 분류하길 원하면 C값을 크게주면 되고, 반대로 데이터를 일부 오분류하더라도 최대한 마진을 크게 잡고싶다면 C값을 작게주면 된다. kernel과 C 매개변수에 대한 차이가 어떻게 발생하는지도 살펴볼 예정이다.
# train the model with X_tr and Y_tr
sv=svm.SVC(kernel='linear',C=1000)
result=sv.fit(self.X_tr,self.Y_tr)
그리고 학습된 svm 모델에 predict을 사용하면 테스트 데이터에 대한 분류 결과를 리스트 형태로 받을 수 있다. 아래 코드가 predict을 사용하여 분류된 라벨을 사용해 테스트 데이터를 색으로 분류하여 plot하는 내용이다.
#train the model with X_tr and Y_tr
sv=svm.SVC(kernel='linear',C=10)
result=sv.fit(self.X_tr,self.Y_tr)
#train data classification plot(with dicision surface,support vector)
plt.figure(3)
plt.title('SVM-train data Classification')
predict1=sv.predict(self.X_tr)
predict1=predict1.astype(float)
for i in range(100):
if predict1[i]==0:
plt.scatter(self.X_tr[:,0][i],self.X_tr[:,1][i],color='blue')
if predict1[i]==1:
plt.scatter(self.X_tr[:,0][i],self.X_tr[:,1][i],color='red')
svm의 decision boundary를 그리기 위해 x,y를 데이터가 존재하는 구간 사이의 100개의 데이터로 선언해준다. 그리고 2차원 좌표에 대응되도록 x,y 두개의 1차원 배열을 2차원 배열 두개로 변경하기 위해 numpy의 meshgrid를 사용한다.
그리고 각 2차원 배열을 다시 1차로 합친 후에 x,y 배열을 vstack을 사용해 수직으로 합쳐준다. 이렇게 나온 xy를 decision_function에 넣으면 데이터셋을 분류하기 위한 분류선 decision boundary인 Z를 얻을 수 있다.
#decision surface,support vector plot
x=np.linspace(-3,2,100)
y=np.linspace(-2,5,100)
X,Y=np.meshgrid(x,y)
xy=np.vstack([X.ravel(),Y.ravel()]).T
Z=sv.decision_function(xy).reshape(X.shape)
# plot 3 lines
plt.contour(X,Y,Z,colors='k',levels=[-1,0,1],alpha=0.5,linestypes=['--','-','--'])
아무래도 시각화를 하려다보니, 분류선 Z를 그리기 위한 decision_function 함수 입력값에 맞는 형태의 배열을 형성하기 위함이다. 아래 파이썬 인터프리터를 이용해 한줄 한줄 출력해본 결과를 확인해보면 이해하기 쉬울 것이다.
마지막으로 서포트 벡터(support vector)를 시각화해주기 위해 학습된 sv 객체의 결과로 나오는 support_vectors_를 사용한다.
# plot support vectors
plt.scatter(sv.support_vectors_[:,0],sv.support_vectors_[:,1],facecolors='yellow',edgecolor='k')
sklearn을 사용한 SVM 분류 결과
위에서 분류 목적의 데이터셋과 시각화를 위한 총 3개의 선(X,Y,Z) 그리고 서포트 벡터를 plot해주었다. 그 결과는 아래와 같은데 여기서 노란색으로 표시된 데이터가 서포트 벡터이다.
위 분류 결과는 svc의 매개변수 C를 10으로 크게 준 결과인데 학습 데이터에서 100% 정확도로 학습을 했고 테스트 데이터에서의 정확도도 100%로 측정됐다(정확도 측정 방법은 아래 전체 코드를 참고하길 바란다).
svc의 C 매개변수 값을 1을 준 결과도 봐보자.
학습 정확도를 100%로 마쳤지만 테스트 데이터의 정확도는 92.5%에 그쳤다. 위 학습 결과만 보아도 SVM에서 마진이 어떤 역할을 하는 것인지 이해할 수 있을 것이다. 마진을 크게 주면, 학습 데이터에 과학습되어 다른 테스트 데이터에 대해 일반성이 떨어지는 것을 막아주는 것 같다. 결국 어느 것에 중점을 두느냐에 따라 C값을 크게 줄지, 적게 줄지 결정하면 된다.
이번엔 커널 방식에 따른 차이를 봐보자. 위 결과는 kernel값을 선형으로 분류하기 위한 'linear' 값으로 준 결과인데 아래는 선형으로 분리하기 힘든 데이터셋에 대해 커널 방식을 'rbf'로 지정해준 결과이다(눈에도 보이지만 다른 데이터셋이다).
sklearn을 사용한 SVM 전체 코드
sklearn을 사용한 SVM 분류 전체 코드는 아래와 같다.
import matplotlib.pyplot as plt
import numpy as np
from sklearn import svm
class support_vector_machine:
def __init__(self):
self.X_tr=[]
self.Y_tr=[]
self.X_te=[]
self.Y_te=[]
def get_data(self):
with open('datasets/dataset3_1/tr_x.txt','r') as f:
tmp_list=f.read().split('\n')
tmp_list.pop()
self.X_tr=np.zeros((len(tmp_list),2))
for i in range(len(tmp_list)):
tmp=tmp_list[i].split(' ')
self.X_tr[i][0]=float(tmp[0])
self.X_tr[i][1]=float(tmp[1])
with open('datasets/dataset3_1/tr_t.txt','r') as f:
self.Y_tr=f.read().split('\n')
self.Y_tr.pop()
self.Y_tr=np.array(self.Y_tr)
self.Y_tr=self.Y_tr.astype(np.float).T
with open('datasets/dataset3_1/te_x.txt','r') as f:
tmp_list=f.read().split('\n')
tmp_list.pop()
self.X_te=np.zeros((len(tmp_list),2))
for i in range(len(tmp_list)):
tmp=tmp_list[i].split(' ')
self.X_te[i][0]=float(tmp[0])
self.X_te[i][1]=float(tmp[1])
with open('datasets/dataset3_1/te_t.txt','r') as f:
self.Y_te=f.read().split('\n')
self.Y_te.pop()
self.Y_te=np.array(self.Y_te)
self.Y_te=self.Y_te.astype(np.float).T
#train data plot
plt.figure(1)
plt.title('SVM-train data')
for i in range(len(self.Y_tr)):
if self.Y_tr[i]==0:
plt.scatter(self.X_tr[i][0],self.X_tr[i][1],color='blue')
if self.Y_tr[i]==1:
plt.scatter(self.X_tr[i][0],self.X_tr[i][1],color='red')
#test data plot
plt.figure(2)
plt.title('SVM-test data')
for i in range(len(self.Y_te)):
if self.Y_te[i]==0:
plt.scatter(self.X_te[i][0],self.X_te[i][1],color='blue')
if self.Y_te[i]==1:
plt.scatter(self.X_te[i][0],self.X_te[i][1],color='red')
def SVM(self):
sv=svm.SVC(kernel='rbf',C=1)
result=sv.fit(self.X_tr,self.Y_tr)
#train data classification plot(with dicision surface,support vector)
plt.figure(3)
plt.title('SVM-train data Classification')
predict1=sv.predict(self.X_tr)
predict1=predict1.astype(float)
for i in range(100):
if predict1[i]==0:
plt.scatter(self.X_tr[:,0][i],self.X_tr[:,1][i],color='blue')
if predict1[i]==1:
plt.scatter(self.X_tr[:,0][i],self.X_tr[:,1][i],color='red')
#decision surface,support vector plot
x=np.linspace(-3,2,100)
y=np.linspace(-2,5,100)
X,Y=np.meshgrid(x,y)
xy=np.vstack([X.ravel(),Y.ravel()]).T
Z=sv.decision_function(xy).reshape(X.shape)
plt.contour(X,Y,Z,colors='k',levels=[-1,0,1],alpha=0.5,linestypes=['--','-','--'])
plt.scatter(sv.support_vectors_[:,0],sv.support_vectors_[:,1],facecolors='yellow',edgecolor='k')
#test data classification plot(with dicision surface,support vector)
plt.figure(4)
plt.title('SVM-test data Classification')
predict2=sv.predict(self.X_te)
predict2=predict2.astype(float)
for i in range(len(predict2)):
if predict2[i]==0:
plt.scatter(self.X_te[:,0][i],self.X_te[:,1][i],color='blue')
if predict2[i]==1:
plt.scatter(self.X_te[:,0][i],self.X_te[:,1][i],color='red')
#decision surface,support vector plot
plt.contour(X,Y,Z,colors='k',levels=[-1,0,1],alpha=0.5,linestypes=['--','-','--'])
plt.scatter(sv.support_vectors_[:,0],sv.support_vectors_[:,1],facecolors='yellow',edgecolor='k')
plt.show()
#accuracy,true positive,false positive,false negative,true negative of train data
tp=0; fp=0; tn=0; fn=0
for i in range(len(predict1)):
if predict1[i]==self.Y_tr[i]:
if predict1[i]==1:
tp+=1
if predict1[i]==0:
tn+=1
if predict1[i]!=self.Y_tr[i]:
if predict1[i]==1:
fn+=1
if predict1[i]==0:
fp+=1
print('\n[*] train data accuracy')
print('accuracy:',(float(tp+tn)/len(predict1))*100,'%')
print('true positive:',(float(tp)/len(predict1))*100,'%')
print('false positive:',(float(fp)/len(predict1))*100,'%')
print('true negative:',(float(tn)/len(predict1))*100,'%')
print('false negative:',(float(fn)/len(predict1))*100,'%')
#accuracy,true positive,false positive,false negative,true negative of test data
tp=0; fp=0; tn=0; fn=0
for i in range(len(predict2)):
if predict2[i]==self.Y_te[i]:
if predict2[i]==1:
tp+=1
if predict2[i]==0:
tn+=1
if predict2[i]!=self.Y_te[i]:
if predict2[i]==1:
fn+=1
if predict2[i]==0:
fp+=1
print('\n[*] train data accuracy')
print('accuracy:',(float(tp+tn)/len(predict2))*100,'%')
print('true positive:',(float(tp)/len(predict2))*100,'%')
print('false positive:',(float(fp)/len(predict2))*100,'%')
print('true negative:',(float(tn)/len(predict2))*100,'%')
print('false negative:',(float(fn)/len(predict2))*100,'%')
def main():
s=support_vector_machine()
s.get_data()
s.SVM()
if __name__=="__main__":
main()
'IT > 머신러닝' 카테고리의 다른 글
아나콘다(Anaconda)에 tensorflow-cpu 설치하기 (0) | 2020.03.04 |
---|---|
신경망의 지도학습, 델타규칙과 경사하강법 (0) | 2020.02.18 |
퍼셉트론의 기본 개념과 학습 규칙, 한계점 (1) | 2020.02.17 |
sklearn을 사용한 로지스틱 회귀분석(Logistic Regression) (4) | 2020.02.10 |
sklearn을 사용한 선형 회귀분석(Linear Regression) (5) | 2020.02.05 |