SVM (Support Vecter Machine)
SVM과 커널, SVM을 이용한 분류 모델링, 커스텀으로 커널 만들기
- 회귀 : 연속 변수를 예측. 데이터를 지나는 추세선을 찾는다.
- 분류 : 이산 변수, 범주형 변수를 예측. 데이터를 나누는 경계선을 찾는다.
- 회귀와 분류는 깊게 들어가면 비슷한 부분이 있다. (로지스틱 회귀분석 - 분류에 사용)
SVM
- 참고자료 링크
- 참고 블로그
- 선형 모델 : 오차를 줄이는 데 관심.
- 정규화된 선형 모델 : 오차를 줄이고 좋은 형태도 찾음.
- SVM : 좋은 형태를 찾고 더불어 오차도 줄이자. = SVM은 Ridge 선형 모델과 수학적으로 비슷하다.
문제의 비선형성
- 현실의 많은 문제들은 비선형적(non-linearity)이다. (예, XOR 문제)
- 비선형 문제는 비선형 모델로 풀 수 있지만 적합(fitting) 시키기가 매우 어렵다.
- 해결책 : 비선형 모델을 만드는 것이 아니라 데이터를 비선형으로 변환하여 “선형 분리”가 가능하게 만든다.
Kernel Trick
- 실제로는 비선형 변환도 쉽지 않기 때문에 비선형 변환을 한 것처럼 트릭을 사용한다.
- 데이터 상호간 거리를 재정의(비슷한 것은 가까이 있다)
- 데이터가 흔히 보는 자료 형태가 아닐 때 (예, 집합) 커널을 이용해서 일반적인 데이터인 것처럼 다룰 수 있다.
커널의 종류
- 선형 커널(linear kernel)
- RBF 커널(radio basis function)
- 다항 커널(polynomial kernel)
- 시그모이드 커널(sigmoid kernel)
- 커널을 결합해도 커널이 된다.
SVM을 사용하여 자동차의 특성에 따라 door 수를 예측
import pandas
from sklearn.svm import SVC
from sklearn import metrics
# 데이터 준비
cars = pandas.read_csv('data/automobile.csv')
variables = ['bore', 'city_mpg', 'compression_ratio', 'curb_weight', 'engine_size', 'horsepower', 'peak_rpm', 'city_mpg', 'price']
X = cars[variables] # 모델에 사용할 feature 정의.
y = cars['doors']
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4)
Linear SVM
svc = SVC(kernel='linear')
svc.fit(X_train, y_train)
SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
decision_function_shape=None, degree=3, gamma='auto', kernel='linear',
max_iter=-1, probability=False, random_state=None, shrinking=True,
tol=0.001, verbose=False)
# 예측
y_svc = svc.predict(X_test)
# 모델 평가
metrics.confusion_matrix(y_test, y_svc)
array([[29, 10],
[12, 13]])
metrics.accuracy_score(y_test, y_svc)
0.65625
metrics.precision_score(y_test, y_svc, pos_label='four')
0.70731707317073167
metrics.recall_score(y_test, y_svc, pos_label='four')
0.74358974358974361
metrics.f1_score(y_test, y_svc, pos_label='four')
0.72499999999999998
오차에 대한 패널티 C 조정
svc2 = SVC(kernel='linear', C=0.1)
svc2.fit(X_train, y_train)
SVC(C=0.1, cache_size=200, class_weight=None, coef0=0.0,
decision_function_shape=None, degree=3, gamma='auto', kernel='linear',
max_iter=-1, probability=False, random_state=None, shrinking=True,
tol=0.001, verbose=False)
y_svc2 = svc2.predict(X_test)
metrics.confusion_matrix(y_test, y_svc2)
array([[27, 12],
[ 9, 16]])
metrics.accuracy_score(y_test, y_svc2)
0.671875
metrics.precision_score(y_test, y_svc2, pos_label='four')
0.75
metrics.recall_score(y_test, y_svc2, pos_label='four')
0.69230769230769229
metrics.f1_score(y_test, y_svc2, pos_label='four')
0.71999999999999986
모델을 만들고 평가하는 전체 과정을 함수로 생성
def run_svm_model(kernel, penalty):
model = SVC(kernel=kernel, C=penalty)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print('confusion matrix')
print(metrics.confusion_matrix(y_test, y_pred))
print('accuracy : {}'.format(metrics.accuracy_score(y_test, y_pred)))
print('precision : {}'.format(metrics.precision_score(y_test, y_pred, pos_label='four')))
print('recall : {}'.format(metrics.recall_score(y_test, y_pred, pos_label='four')))
print('F1 : {}'.format(metrics.f1_score(y_test, y_pred, pos_label='four')))
return model
svc3 = run_svm_model('linear', 10)
confusion matrix
[[28 11]
[11 14]]
accuracy : 0.65625
precision : 0.717948717948718
recall : 0.717948717948718
F1 : 0.717948717948718
svc4 = run_svm_model('linear', 0.01)
confusion matrix
[[25 14]
[10 15]]
accuracy : 0.625
precision : 0.7142857142857143
recall : 0.6410256410256411
F1 : 0.6756756756756757
SVM + RBF kernel
def run_svc_model(kernel, penalty, gamma='auto'):
model = SVC(kernel=kernel, C=penalty, gamma=gamma)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print('confusion matrix')
print(metrics.confusion_matrix(y_test, y_pred))
print('accuracy : {}'.format(metrics.accuracy_score(y_test, y_pred)))
print('precision : {}'.format(metrics.precision_score(y_test, y_pred, pos_label='four')))
print('recall : {}'.format(metrics.recall_score(y_test, y_pred, pos_label='four')))
print('F1 : {}'.format(metrics.f1_score(y_test, y_pred, pos_label='four')))
return model
- gamma : how far the influence of a single training example reaches, with low values meaning ‘far’ and high values meaning ‘close’.
- gamma가 너무 작으면 모델이 너무 제한적이 되어서 데이터의 복잡성이나 모양을 잘 캡쳐할 수 없다.
svc_rbf1 = run_svc_model('rbf', 1)
confusion matrix
[[39 0]
[25 0]]
accuracy : 0.609375
precision : 0.609375
recall : 1.0
F1 : 0.7572815533980582
svc_rbf2 = run_svc_model('rbf', 0.01, gamma=0.001)
confusion matrix
[[39 0]
[25 0]]
accuracy : 0.609375
precision : 0.609375
recall : 1.0
F1 : 0.7572815533980582
Custom Kernel
# 독립변수 중에서 범주형 변수만 골라낸다.
cate_var = cars.columns[cars.dtypes == 'object']
cate_var
Index(['maker', 'fuel', 'aspiration', 'doors', 'body', 'wheels',
'engine_location', 'engine_type', 'cylinders', 'fuel_system'],
dtype='object')
data = cars[cate_var]
data.head()
maker | fuel | aspiration | doors | body | wheels | engine_location | engine_type | cylinders | fuel_system | |
---|---|---|---|---|---|---|---|---|---|---|
0 | audi | gas | std | four | sedan | fwd | front | ohc | four | mpfi |
1 | audi | gas | std | four | sedan | 4wd | front | ohc | five | mpfi |
2 | audi | gas | std | four | sedan | fwd | front | ohc | five | mpfi |
3 | audi | gas | turbo | four | sedan | fwd | front | ohc | five | mpfi |
4 | bmw | gas | std | two | sedan | rwd | front | ohc | four | mpfi |
X = cars[cate_var.difference(['doors'])] # doors 변수를 제외한 나머지를 feature로 사용
y = cars['doors']
# 데이터 분할
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4)
row_train, _ = X_train.shape # 훈련 데이터 갯수
row_train
95
row_test, _ = X_test.shape
row_test
64
# custom kernel 만들기
X_train.iloc[0:2,:]
aspiration | body | cylinders | engine_location | engine_type | fuel | fuel_system | maker | wheels | |
---|---|---|---|---|---|---|---|---|---|
140 | std | sedan | four | front | ohc | diesel | idi | volkswagen | fwd |
33 | std | hatchback | four | front | ohc | gas | 2bbl | mazda | fwd |
'''2개의 데이터 포인트에서 9가지 변수 중에 같은 변수의 갯수를 센다'''
same = 0
for a, b in zip(X_train.iloc[0,:], X_train.iloc[1,:]):
if a == b:
same = same + 1
same
5
def hand_made_kernel(d1, d2):
same = 0
for a, b in zip(d1, d2):
if a == b:
same = same + 1
return same
# custom kernel 적용
import numpy
P_train = numpy.zeros((row_train, row_train)) # array 생성 및 초기화
P_train
array([[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.],
...,
[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.],
[ 0., 0., 0., ..., 0., 0., 0.]])
for i in range(row_train):
for j in range(row_train):
P_train[i, j] = hand_made_kernel(X_train.iloc[i,:], X_train.iloc[j,:])
P_train
array([[ 9., 5., 4., ..., 4., 6., 5.],
[ 5., 9., 6., ..., 6., 7., 7.],
[ 4., 6., 9., ..., 7., 5., 5.],
...,
[ 4., 6., 7., ..., 9., 5., 5.],
[ 6., 7., 5., ..., 5., 9., 8.],
[ 5., 7., 5., ..., 5., 8., 9.]])
# modeling & training
model = SVC(kernel='precomputed', C=1)
model.fit(P_train, y_train)
SVC(C=1, cache_size=200, class_weight=None, coef0=0.0,
decision_function_shape=None, degree=3, gamma='auto',
kernel='precomputed', max_iter=-1, probability=False, random_state=None,
shrinking=True, tol=0.001, verbose=False)
# prediction
P_test = numpy.zeros((row_test, row_train))
for i in range(row_test):
for j in range(row_train):
P_test[i, j] = hand_made_kernel(X_test.iloc[i,:], X_train.iloc[j,:])
P_test
array([[ 3., 5., 5., ..., 4., 4., 4.],
[ 5., 6., 4., ..., 4., 7., 6.],
[ 6., 6., 5., ..., 5., 7., 6.],
...,
[ 4., 7., 5., ..., 5., 6., 6.],
[ 4., 5., 5., ..., 4., 5., 6.],
[ 5., 7., 5., ..., 5., 8., 8.]])
y_c_pred = model.predict(P_test)
print('confusion matrix')
print(metrics.confusion_matrix(y_test, y_c_pred))
print('accuracy : {}'.format(metrics.accuracy_score(y_test, y_c_pred)))
print('precision : {}'.format(metrics.precision_score(y_test, y_c_pred, pos_label='four')))
print('recall : {}'.format(metrics.recall_score(y_test, y_c_pred, pos_label='four')))
print('F1 : {}'.format(metrics.f1_score(y_test, y_c_pred, pos_label='four')))
# 정확도가 80 % 이상으로 높아졌다.
confusion matrix
[[42 4]
[ 5 13]]
accuracy : 0.859375
precision : 0.8936170212765957
recall : 0.9130434782608695
F1 : 0.9032258064516129