티스토리 뷰

2장_09_SVM

커널 서포트벡터 머신

  • 유방암 데이터에 커널 SVM 을 적용해 보겠습니다.
In [2]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer
from sklearn.svm import SVC

cancer = load_breast_cancer()
In [3]:
malignant = cancer.data[cancer.target==0] # 37%
benign = cancer.data[cancer.target==1]    # 63%

malignant.shape, benign.shape
Out[3]:
((212, 30), (357, 30))
In [4]:
X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target)

model = SVC()
model.fit(X_train, y_train)

train_score = model.score(X_train, y_train)
test_score = model.score(X_test, y_test)
display(train_score, test_score)
1.0
0.6713286713286714
In [5]:
pred_y = model.predict(X_test)
pred_y
Out[5]:
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
  • 위의 결과를 보면, 테스트세트에 대한 점수가 60% 를 잘 넘지 못합니다.
  • 모든 데이터를 양성이라고 판단하더라도 최소한 63% 는 맞출수 있습니다.
  • 문제의 원인은 유방암 데이터의 스케일이 아주 차이가 크기 때문입니다.
In [6]:
fig=plt.figure(figsize=[10,8])
plt.title('Cancer - boxplot for features',fontsize=15)
plt.boxplot(cancer.data)
plt.xticks(np.arange(30)+1,cancer.feature_names,rotation=90)
plt.xlabel('features')
plt.ylabel('scale')
print('')

  • 커널 SVM 은 스케일에 아주 민감합니다. 많은 차원과 큰 차원의 다항식, 복잡한 곡선함수를 사용하기 때문에 조금만 스케일이 차이가 나도 훈련데이터에 과대적합 되어 버립니다.
  • 커널 SVM 을 적용할 때는 반드시 **정규화(normalization)**을 해 주어야 합니다.
  • 머신러닝 알고리즘 중에서 거리(distance) 를 사용하는 함수는 반드시 정규화를 해 주어야 한다고 알아두면 좋습니다.
    1. k-NN 은 점들 사이의 거리에 기반하므로 정규화가 반드시 필요합니다.
    2. 선형회귀와 로지스틱회귀는 높이값을 비교하므로 정규화가 꼭 필요한 것은 아니지만, 적용하면 좋습니다.
    3. 선형 SVM 도 직선과 점들 사이의 거리를 사용하기 때문에 정규화가 꼭 필요합니다.
  • 정규화의 방법은 대표적으로 2가지 방법을 사용합니다.
    1. 0과 1사이의 값으로 균등하게 비율을 조정하는 방법
    2. 평균 0, 표준편차 1의 정규분포로 변환하는 방법
  • 데이터의 특성에 맞게 적당한 변환 방법을 선택합니다.
  • 훈련세트와 테스트세트로 분리되었을 때에는, 훈련세트의 값을 기준으로 테스트세트를 변환해야 합니다.
In [7]:
# 균등 비율
X_max = X_train.max(axis=0)
X_min = X_train.min(axis=0)
X_train_uni = (X_train - X_min) / (X_max - X_min)
X_test_uni = (X_test - X_min) / (X_max - X_min) # 학습데이터 값을 기준으로 변환

plt.boxplot(X_train_uni)
print('')

In [8]:
# 정규분포
X_mean = X_train.mean(axis=0)
X_std = X_train.std(axis=0)
X_train_norm = (X_train - X_mean) / X_std
X_test_norm = (X_test - X_mean) / X_std # 학습데이터 값을 기준으로 변환

plt.boxplot(X_train_norm)
print('')

In [9]:
model = SVC()
model.fit(X_train_uni, y_train)

train_score = model.score(X_train_uni, y_train)
test_score = model.score(X_test_uni, y_test)
display(train_score, test_score)
0.9460093896713615
0.972027972027972
In [10]:
model = SVC()
model.fit(X_train_norm, y_train)

train_score = model.score(X_train_norm, y_train)
test_score = model.score(X_test_norm, y_test)
display(train_score, test_score)
0.9859154929577465
0.9790209790209791
In [13]:
pred_y=model.predict(X_test_norm)
In [15]:
np.where(y_test!=pred_y)
Out[15]:
(array([ 48,  99, 141], dtype=int64),)
In [16]:
X_test[np.where(y_test!=pred_y)[0]] # 잘못된 값 판단
Out[16]:
array([[9.029e+00, 1.733e+01, 5.879e+01, 2.505e+02, 1.066e-01, 1.413e-01,
        3.130e-01, 4.375e-02, 2.111e-01, 8.046e-02, 3.274e-01, 1.194e+00,
        1.885e+00, 1.767e+01, 9.549e-03, 8.606e-02, 3.038e-01, 3.322e-02,
        4.197e-02, 9.559e-03, 1.031e+01, 2.265e+01, 6.550e+01, 3.247e+02,
        1.482e-01, 4.365e-01, 1.252e+00, 1.750e-01, 4.228e-01, 1.175e-01],
       [1.277e+01, 2.247e+01, 8.172e+01, 5.063e+02, 9.055e-02, 5.761e-02,
        4.711e-02, 2.704e-02, 1.585e-01, 6.065e-02, 2.367e-01, 1.380e+00,
        1.457e+00, 1.987e+01, 7.499e-03, 1.202e-02, 2.332e-02, 8.920e-03,
        1.647e-02, 2.629e-03, 1.449e+01, 3.337e+01, 9.204e+01, 6.536e+02,
        1.419e-01, 1.523e-01, 2.177e-01, 9.331e-02, 2.829e-01, 8.067e-02],
       [1.346e+01, 1.875e+01, 8.744e+01, 5.511e+02, 1.075e-01, 1.138e-01,
        4.201e-02, 3.152e-02, 1.723e-01, 6.317e-02, 1.998e-01, 6.068e-01,
        1.443e+00, 1.607e+01, 4.413e-03, 1.443e-02, 1.509e-02, 7.369e-03,
        1.354e-02, 1.787e-03, 1.535e+01, 2.516e+01, 1.019e+02, 7.198e+02,
        1.624e-01, 3.124e-01, 2.654e-01, 1.427e-01, 3.518e-01, 8.665e-02]])
In [26]:
X_train, X_test, y_train, y_test, i_train, i_test = train_test_split(cancer.data, cancer.target, range(569))

X_mean = X_train.mean(axis=0)
X_std = X_train.std(axis=0)
X_train_norm = (X_train - X_mean) / X_std
X_test_norm = (X_test - X_mean) / X_std 

model = SVC()
model.fit(X_train_norm, y_train)

train_score = model.score(X_train_norm, y_train)
test_score = model.score(X_test_norm, y_test)
display(train_score, test_score)
0.9882629107981221
0.972027972027972
In [27]:
pred_y=model.predict(X_test_norm)
In [28]:
np.where(y_test!=pred_y)
Out[28]:
(array([ 45,  55, 132, 138], dtype=int64),)
In [30]:
np.array(i_test)[[45,  55, 132, 138]]
Out[30]:
array([541, 413,  73, 135])
  • 이제 SVC 의 중요 옵션인 C 와 gamma 값을 바꿔가며 적용해 보자.
  • C 와 gamma 는 값이 커질수록 훈련데이터에 맞추는 경향이 있다.
In [35]:
C=[0.1,1,10,100]
gamma=[0.001,0.01,0.1,1,10]
s_train=[]
s_test=[]

for c in C:
    s1=[]
    s2=[]
    
    for g in gamma:
        model=SVC(C=c,gamma=g)
        model.fit(X_train_norm,y_train)

        pred_y=model.predict(X_test_norm)
        s1.append(model.score(X_train_norm,y_train))
        s2.append(model.score(X_test_norm,y_test))
        
    s_train.append(s1)
    s_test.append(s2)
    
fig=plt.figure(figsize=[12,8])
for i in range(len(C)):
    plt.subplot(1,len(C),i+1)
    plt.plot(s_train[i],'gs--',label='train')
    plt.plot(s_test[i],'ro-',label='test')
    plt.title('C= %f' % (C[i]))
    plt.xticks(range(len(gamma)),gamma)
    plt.ylim(0,1)
    plt.xlabel('gamma')
    plt.ylabel('score')
    plt.legend(loc='lower right')
  • 마지막으로, 시각화를 위해 속성 2개를 선택해 SVC 를 적용해 보겠습니다.
In [34]:
col1 = 0 #20
col2 = 1 #27
# 컬럼은 하나하나 상관관계 등 여러가지를 고려하여 선택을 해야한다.


X = cancer.data[:,[col1,col2]]
y = cancer.target

X_train,X_test,y_train,y_test = train_test_split(X, y)

X_mean = X_train.mean(axis=0)
X_std = X_train.std(axis=0)

X_train_norm = (X_train-X_mean)/X_std
X_test_norm = (X_test-X_mean)/X_std

model = SVC(C=1, gamma=0.1, probability=True) # 확률 점수를 얻기 위해 probability=True
model.fit(X_train_norm, y_train)

# 점수를 출력
train_score = model.score(X_train_norm, y_train)
test_score = model.score(X_test_norm, y_test)
display(train_score, test_score)

# fit() 결과를 등고선으로 나타낸다
xmax = X_train_norm[:,0].max()+1
xmin = X_train_norm[:,0].min()-1
ymax = X_train_norm[:,1].max()+1
ymin = X_train_norm[:,1].min()-1

xx=np.linspace(xmin,xmax,200)
yy=np.linspace(ymin,ymax,200)
data1, data2 = np.meshgrid(xx,yy)
X_grid = np.c_[data1.ravel(), data2.ravel()]
decision_values = model.predict_proba(X_grid)[:,0] # 등고선을 위해 확률점수를 구함

sv=model.support_vectors_

fig=plt.figure(figsize=[14,12])

# show probability countour
CS=plt.contour(data1,data2,decision_values.reshape(data1.shape),levels=[0.01, 0.1, 0.5, 0.9, 0.99])#contourf는 색칠도 해준다.
plt.clabel(CS, inline=2, fontsize=10)

# show support vectors
plt.scatter(sv[:,0], sv[:,1], marker='s', c= 'k', s=100) # k는 검은색

# show train samples
plt.scatter(X_train_norm[:,0][y_train==0],X_train_norm[:,1][y_train==0],marker='o',c='r',label='malignant')
plt.scatter(X_train_norm[:,0][y_train==1],X_train_norm[:,1][y_train==1],marker='^',c='y',label='benign')

plt.legend()
plt.colorbar(CS,shrink=0.5)
plt.xlabel(cancer.feature_names[col1])
plt.ylabel(cancer.feature_names[col2])
plt.title('SVM - decision bounds',fontsize=20)
0.9107981220657277
0.8811188811188811
Out[34]:
Text(0.5,1,'SVM - decision bounds')

까만 점. 50%를 기준으로 상대방쪽으로 넘어간 점들은 sv(서포트벡터)로 판단. 경계선하고 가까운 애들도.

In [13]:
display(sv.shape, sv[:10])
(135, 2)
array([[ 0.49684469, -0.32077007],
       [-0.90639651,  0.49230345],
       [-0.13359701,  1.34532566],
       [ 0.4736026 ,  0.24791141],
       [ 0.11044494,  0.2267621 ],
       [-0.19460749,  0.2972598 ],
       [ 0.63048671, -0.1139768 ],
       [ 0.30509745, -0.60511081],
       [ 0.35158163,  0.74139533],
       [ 0.04362393,  0.9082399 ]])
In [14]:
model.predict_proba([[0,0]])
Out[14]:
array([[0.33490442, 0.66509558]])


'beginner > 파이썬 머신러닝 기초' 카테고리의 다른 글

지도학습 - 랜덤포레스트  (0) 2019.03.17
지도학습 - 결정트리  (0) 2019.03.12
지도학습 - kernel SVM  (0) 2019.03.07
유방암 데이터 분석  (3) 2019.03.05
지도학습 - 로지스틱회귀  (0) 2019.03.05
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함