2020-07-14 09:00:33

오늘은 사이킷런에서 제공하는 SVM을 가지고 간단한 분류 문제를 해결하는 법에 대해 다루도록 하겠습니다. 저는 이에 필요한 절차를 아래와 같은 5단계로 나눠봤습니다. 

 

1. 데이터셋 불러오기

2. 데이터셋을 훈련셋과 테스트셋으로 분리

3. 특성 스케일링(feature scaling)

4. 훈련셋으로 SVM 훈련

5. 테스트셋을 가지고 성능 확인

 

자, 그럼 첫 단계부터 하나씩 시작해보겠습니다. 실습을 진행하기 위해서는 사이킷런과 넘파이 패키지가 필요하니 설치하지 않으신 분들은 설치해주시기 바랍니다. 또한 저는 구글 코랩 노트북에서 실습을 진행하는데, 다른 개발환경에서 실습을 진행하셔도 무방합니다. 

 

1. 데이터셋 불러오기

사이킷런에서는 아주 유명한 데이터셋 중에 하나인 붓꽃품종분류 데이터셋을 제공합니다. 저는 그것을 이용해서 실습을 진행하겠습니다. 먼저 데이터셋을 불러오겠습니다. 

 

1
2
3
from sklearn.datasets import load_iris
 
iris_dataset = load_iris() # 붓꽃 데이터셋을 적재합니다. 
cs

 

데이터셋이 어떻게 생겼는지 한번 구경해보겠습니다. 

 

1
print(iris_dataset)
cs

 

위 코드를 실행하시면 데이터셋에 대한 정보가 딕셔너리의 형태로 잘 담겨져 있다는 것을 확인하실 수 있습니다. 특성, 타겟(라벨), 특성 이름, 타겟 이름 등의 데이터가 담겨져 있습니다. 

 

 

2. 데이터셋을 훈련셋과 테스트셋으로 분리

이제 데이터셋을 훈련셋과 테스트셋으로 분리하도록 하겠습니다. 총 150개의 샘플이 있는데, 그중 80%인 120개를 훈련셋으로, 나머지 20% 30개를 테스트셋으로 사용하겠습니다. 

 

1
2
3
4
from sklearn.model_selection import train_test_split
 
X_train, X_test, y_train, y_test = train_test_split(iris_dataset['data'], iris_dataset['target'], test_size = 0.2)
# 데이터셋을 랜덤하게 80%의 훈련셋과 20%의 테스트셋으로 분리합니다.
cs

 

X_train에는 훈련셋의 특성들이 담겨 있고, X_test에는 테스트셋의 특성들이 담겨 있습니다. 또한 y_train에는 훈련셋의 타겟들이, y_test에는 테스트셋의 타겟들이 담겨 있습니다. X_train 안에 무엇이 들어 있는지 살펴보겠습니다. 

 

1
print(X_train)
cs

 

 

위 그림에 표시한대로 행(row)은 샘플을, 열(column)은 특성을 나타냅니다. 참고로 붓꽃품종 데이터셋에서 특성1은 꽃받침 길이(sepal length), 특성2는 꽃받침 너비(sepal width), 특성3은 꽃잎 길이(petal length), 특성4는 꽃잎 너비(petal width)입니다. 이 네가지 특성을 활용해서 붓꽃의 품종을 분류하겠다는 것이죠. 저 네가지 특성을 잘 살펴보면 붓꽃의 품종 분류가 가능한가 봅니다. 

 

이번에는 y_train 내부를 살펴보겠습니다. 

 

1
print(y_train)
cs

 

 

0, 1, 2의 숫자들로 이루어진 것을 확인할 수 있습니다. 여기서 0, 1, 2는 각각 setosa, versicolour, virginica라는 이름의 품종을 지칭합니다. 식물학자가 아닌 이상 알 필요 없으니 굳이 검색해보지 마세요.^^ 

 

3. 특성 스케일링(feature scaling)

SVM을 훈련하기 전에 특성을 전처리해줘야 합니다. 왜냐하면, 각 특성마다 값의 범위에 차이가 있기 때문입니다. 

 

1
2
3
4
print("특성1 범위: ""[", min(X_train[:, 0]), ",", max(X_train[:, 0]), "]")
print("특성2 범위: ""[", min(X_train[:, 1]), ",", max(X_train[:, 1]), "]")
print("특성3 범위: ""[", min(X_train[:, 2]), ",", max(X_train[:, 2]), "]")
print("특성4 범위: ""[", min(X_train[:, 3]), ",", max(X_train[:, 3]), "]")
cs

 

 

사실 붓꽃품종 데이터셋 내의 특성들의 경우에는 그 범위의 차이가 심하지 않지만, 일반적으로는 차이가 큽니다. 특성 간 값의 범위가 크면 머신러닝 모델을 제대로 훈련시킬 수 없습니다. 값의 범위가 큰 특성에 좌지우지될 가능성이 있기 때문입니다. 값의 범위가 크다고 더 의미있는 특성이 결코 아닙니다. 값의 범위는 작지만 그 안에 매우 의미있는 정보를 담고 있는 특성도 많습니다. 따라서 특성들을 스케일링해줘야 합니다. 특성 스케일링에 대해서는 이전 포스팅에서 자세히 다뤘으니 참고하시길 바랍니다. ☞ 정규화(normalization)와 표준화(standardization), 머신러닝 성능 향상을 위한 필수 단계

 

특성 스케일링이란 간단히 말해서 특성 값들을 비슷한 범위로 매핑해주는 것을 의미합니다. 널리 사용되는 방법에는 정규화(normalization)와 표준화(standardization)가 있는데, 여기서는 표준화 방법을 사용하겠습니다. 표준화라는 것은 각 특성이 정규분포를 따른다고 가정하고, 평균 0, 표준편차 1이 되도록 특성값들을 변환해주는 것을 의미합니다.

 

 

좀 더 구체적으로 말씀드리면, 훈련셋 특성의 평균 및 표준편차를 계산해서 그것을 가지고 훈련셋 특성과 테스트셋 특성의 값을 변환해줍니다. 다음 코드를 실행하시면 표준화된 특성을 얻을 수 있습니다. 

 

1
2
3
4
5
6
7
from sklearn.preprocessing import StandardScaler
 
sc = StandardScaler()
sc.fit(X_train)
 
X_train_std = sc.transform(X_train)
X_test_std = sc.transform(X_test)
cs

 

각 특성의 범위가 어떻게 변했는지 확인해보겠습니다. 

 

1
2
3
4
print("표준화된 특성1 범위: ""[", min(X_train_std[:, 0]), ",", max(X_train_std[:, 0]), "]")
print("표준화된 특성2 범위: ""[", min(X_train_std[:, 1]), ",", max(X_train_std[:, 1]), "]")
print("표준화된 특성3 범위: ""[", min(X_train_std[:, 2]), ",", max(X_train_std[:, 2]), "]")
print("표준화된 특성4 범위: ""[", min(X_train_std[:, 3]), ",", max(X_train_std[:, 3]), "]")
cs

 

 

특성들의 범위가 비슷해졌음을 확인하실 수 있습니다. 이제 드디어 SVM을 훈련할 준비가 되었습니다. 

 

4. 훈련셋으로 SVM 훈련

저는 훈련셋의 표준화된 특성과 그에 해당하는 라벨로 RBF 커널 SVM을 훈련시키도록 하겠습니다. 

 

1
2
3
4
5
from sklearn.svm import SVC
 
svm_model = SVC(kernel='rbf', C=8, gamma=0.1)
 
svm_model.fit(X_train_std, y_train) # SVM 분류 모델 훈련
cs

 

SVC라고 되어 있는데, 분류(classification)용 SVM을 SVC라고 부르기도 합니다. 

 

5. 테스트셋을 가지고 성능 확인

잘 훈련되었는지 테스트셋을 가지고 성능을 확인해보도록 하겠습니다. 

 

1
y_pred = svm_model.predict(X_test_std) # 테스트
cs

 

테스트셋의 라벨이 잘 예측되었는지, 예측된 라벨과 ground-truth 라벨을 각각 보여드리겠습니다. 

 

1
2
print("예측된 라벨:", y_pred)
print("ground-truth 라벨:", y_test)
cs

 

 

엇, 모든 요소가 같네요. 완벽하게 예측해냈습니다. 예측 정확도를 계산해보니 1.00, 즉 100%가 나오네요. 

 

1
print("prediction accuracy: {:.2f}".format(np.mean(y_pred == y_test))) # 예측 정확도
cs

 

 

전체 코드 정리 

 

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import numpy as np
from sklearn.datasets import load_iris
 
iris_dataset = load_iris() # 붓꽃 데이터셋을 적재합니다. 
 
print(iris_dataset)
 
from sklearn.model_selection import train_test_split
 
X_train, X_test, y_train, y_test = train_test_split(iris_dataset['data'], iris_dataset['target'], test_size = 0.2)
# 데이터셋을 랜덤하게 80%의 훈련셋과 20%의 테스트셋으로 분리합니다.
 
print(X_train)
 
print(y_train)
 
print("특성1 범위: ""[", min(X_train[:, 0]), ",", max(X_train[:, 0]), "]")
print("특성2 범위: ""[", min(X_train[:, 1]), ",", max(X_train[:, 1]), "]")
print("특성3 범위: ""[", min(X_train[:, 2]), ",", max(X_train[:, 2]), "]")
print("특성4 범위: ""[", min(X_train[:, 3]), ",", max(X_train[:, 3]), "]")
 
from sklearn.preprocessing import StandardScaler
 
sc = StandardScaler()
sc.fit(X_train)
 
X_train_std = sc.transform(X_train)
X_test_std = sc.transform(X_test)
 
print("표준화된 특성1 범위: ""[", min(X_train_std[:, 0]), ",", max(X_train_std[:, 0]), "]")
print("표준화된 특성2 범위: ""[", min(X_train_std[:, 1]), ",", max(X_train_std[:, 1]), "]")
print("표준화된 특성3 범위: ""[", min(X_train_std[:, 2]), ",", max(X_train_std[:, 2]), "]")
print("표준화된 특성4 범위: ""[", min(X_train_std[:, 3]), ",", max(X_train_std[:, 3]), "]")
 
from sklearn.svm import SVC
 
svm_model = SVC(kernel='rbf', C=8, gamma=0.1)
 
svm_model.fit(X_train_std, y_train) # SVM 분류 모델 훈련
 
y_pred = svm_model.predict(X_test_std) # 테스트
 
print("예측된 라벨:", y_pred)
print("ground-truth 라벨:", y_test)
 
print("prediction accuracy: {:.2f}".format(np.mean(y_pred == y_test))) # 예측 정확도
 
cs

 

100%의 예측 정확도와 같이 좋은 성능을 보일 수 있었던 이유는 크게 4가지로 볼 수 있습니다.

 

1) 좋은 특성 사용

4개의 특성들이 품종에 대해 변별력이 좋은 특성들인 것으로 보입니다. 특성이 좋지 않으면 아무리 좋은 머신러닝 방법을 사용하더라도 답이 없습니다. 

 

2) 특성 스케일링 시행

이 예제의 경우는 필요도가 적을 수 있으나, 대부분의 경우에는 필수입니다. 성능에 매우 큰 영향을 미치니 간과해서는 안 됩니다. 

 

3) SVM사용

어지간한 문제는 머신러닝 알고리즘 중에 SVM이 잘 해결해냅니다. CNN과 같은 딥러닝 방법보다 나을 때도 많습니다. 

 

4) SVM 매개변수 값 최적으로 설정

몇번의 시도를 통해 좋은 결과를 내는 매개변수값들을 찾아냈습니다. 매개변수에 따라 성능이 크게 달라질 수 있으니, grid search를 통해 최적의 값들을 찾아내는 것이 필요합니다. 

 

성능이 잘 안나오는 경우에는 이상의 4가지를 점검해보는 것이 좋습니다. 

 

오늘은 SVM에 대해 다뤘지만, 어떤 머신러닝, 딥러닝 모델이든 훈련하고 테스트하는데 있어 이상의 절차를 따릅니다. 디테일에서 조금씩 차이는 있을 수 있지만, 큰 틀에서는 같을 것입니다. 끝까지 읽어주셔서 감사합니다. 질문과 토론은 항상 환영이니 댓글로 남겨주세요.^^ 

 

 

관련 글

[1] 서포트 벡터 머신(SVM)의 사용자로서 꼭 알아야할 것들-매개변수 C와 gamma

[2] 정규화(normalization)와 표준화(standardization), 머신러닝 성능 향상을 위한 필수 단계  

[3] LIBSVM 활용하여 매트랩에서 SVM 사용하기