2018-12-19 15:36:00
지난 포스팅에서 SVM의 원리와 설정해줘야하는 대표적인 두 파라미터(C, gamma)의 의미에 대해서 알아보았다. 간단히 요약하면 SVM은 데이터를 클래스별로 분류할 때 정중간에 위치하는 선 또는 면(결정 경계)을 찾아내는 것이다. 파라미터 C는 이상치 또는 오류를 얼마나 허용하는 가를 정해주고, gamma는 결정 경계의 곡률을 결정해준다. 

오늘은 support vector machine (SVM)의 코드를 어디서 다운받고 어떻게 사용하는지에 대해서 살펴보려고 한다. 

Chih-Chung Chang과 Chih-Jen Lin은 SVM 라이브러리(LIBSVM)를 아래 사이트에서 제공하고 있다.

이 라이브러리가 좋은 점은 성능은 물론이거니와 계속해서 업데이트시켜가고 있다는 것이다. 나도 여기서 제공하는 matlab 코드를 활용하고 있다. matlab 말고도 Java, R, Python 등 다양한 언어로 작성된 SVM 코드를 제공한다. 이 글에서는 matlab 코드를 어떻게 실행하는지에 대해 중점적으로 설명하겠다. 


▶ 다운로드 및 설치

우선 최신 버전을 다운로드 받는다. 현재 (20181212) 기준 version 3.23을 다운로드 받을 수 있다. 


여기서 zip.file을 클릭해서 다운로드한 다음에 압축을 푼다. 압축을 풀고나면 java, matlab, python 등의 폴더를 확인할 수 있다. 




우리는 이중에서 matlab 폴더를 연다. 




m파일이 하나 밖에 보이지 않아서 살짝 당황스러웠다. 하지만 README를 열어 읽어보니 자세히 설명이 되어 있었다. 결과적으로 우리는 단순히 매트랩 명령창에 make만 입력하면 된다. 


>> make 


그러면 libsvmread.mexw64, libsvmwrite.mexw64, svmtrain.mexw64, svmpredict.mexw64가 빌드된다. 이 중에서 우리가 사용할 것은 svmtrain.mexw64와 svmpredict.mexw64이다. 





(2020.3.19 추가) make를 입력했는데 빌드가 안되시는 분들은 mex -setup을 명령창에 입력해서 mex를 위해 적절한 컴파일러를 선택해주신 다음에 make를 해주세요. 그래도 안되시는 분들은 make.m 파일 내의 CFLAGS를 모두 COMFLAGS로 바꾼 다음에 다시 make를 입력해보시기 바랍니다[1].^^ 



▶ 사용법

SVM은 지도학습이기 때문에 일반적으로 label 값이 있는 데이터에 사용할 수 있다. 보통 여러 개의 샘플들에서 도출한 특성들과 그에 대응하는 라벨값을 가지고 SVM 모델을 훈련한 후, 훈련이 완료되면 테스트를 진행한다. 
 
우선 SVM 모델을 훈련시키는 방법부터 알아보자.  

>> model = svmtrain(training_label_vector, training_instance_matrix, ['libsvm_options'])

여기서 training_label_vector 자리에는 훈련 데이터셋의 라벨값 벡터가 들어가야 하는데, m x 1 벡터(m: 훈련 데이터셋의 개수)의 형태가 되어야 하고, 타입은 double이어야 한다. 또한 training_instance_matrix 자리에는 훈련 데이터셋의 특성값 행렬이 들어가야 한다. m x n 행렬(n: 샘플 당 특성 개수)이어야 하고, 타입은 역시 double이어야 한다. 그리고 libsvm_options은 SVM 타입 및 파라미터 설정을 위한 공간이다. 이에 대해서는 아래에서 설명하겠다. 

위와 같은 방식으로 SVM 모델이 훈련되었다면, 이제 테스트 셋의 특성을 이용해서 테스트 셋의 라벨값을 예측할 수 있다. 

>> [predicted_label, accuracy, decision_values/prob_estimates] = svmpredict(testing_label_vector, testing_instance_matrix, model);

여기서 testing_label_vector 자리에는 테스트 데이터셋의 라벨값 벡터가 들어가야 하고, testing_instance_matrix 자리에는 테스트 데이터셋의 특성값 행렬이 들어가야 한다. 그리고 model에는 훈련된 SVM 모델을 넣어주면 된다. testing_label_vector를 넣어주는 것이 다소 의아할 수 있는데, 이는 예측한 결과와 비교를 위한 것일 뿐 예측할 때는 테스트 데이터셋의 라벨값을 전혀 참고하지 않는다는 사실을 기억하자. 

svmpredict의 출력값들을 살펴보자. 첫번째 predicted_label은 테스트셋의 예측된 라벨값들을 담고있는 벡터이다. 두번째 accuracy는 분류 문제의 경우 정확도를 담은 벡터이고, 회귀 문제의 경우 평균제곱오차(mean squared error, MSE)와 결정계수(squared correlation coefficient)를 담은 벡터이다. 참고로 결정계수가 0.65이상이면 의미 있는 회귀식으로 본다. 세번째 decision_values/prob_estimates는 결정값 또는 확률 측정값을 담고 있는 행렬이다. 


▶ SVM 타입 및 파라미터 설정 

사용할 때 먼저 SVM의 타입을 선택해야 하는데 선택할 수 있는 것들은 5개가 있다: C-SVC, nu-SVM, one-class SVM, epsilon-SVR, nu-SVR. 

각각 어떤 것을 의미하는지 알아보자.

-s svm_type
0 -> C-SVC: regularized support vector classification
1 -> nu-SVC: automatically regularized support vector classification
2 -> one-class SVM: select a hyper-sphere to maximize density
3 -> epsilon-SVR: support vector regression robust to small epsilon errors
4 -> nu-SVR: support vector regression automatically minimize epsilon

그리고 커널의 타입도 선택할 수 있다. 

-t kernel-type
0 -> linear
1 -> polynomial
2 -> radial basis
3 -> sigmoid

SVM의 중요한 파라미터들인 C와 gamma도 설정해줄 수 있다. 

-c cost
-g gamma

또한 커널 함수의 degree도 설정할 수 있다.

-d degree 

이뿐만 아니라 여러 파라미터들이 더 있지만 디폴트값으로 그냥 설정해주는 것을 추천한다. 파라미터가 너무 많으면 그만큼 조합의 개수가 많아지기 때문에 최적의 조합을 찾기가 쉽지 않기 때문이다. 

만약 회귀문제를 풀기 위해 radial basis 커널 epsilon-SVR을 선택하고 C = 10, gamma = 1로 설정하고 싶다면 아래와 같이 입력해주면 된다.

'-s 3 -t 2 -c 10 -g 1' 


▶ 예제

그렇다면 하나의 예제를 풀어보자. 윈스콘신 유방암 데이터셋을 활용해서 세포의 주인이 유방암인지 아닌지를 분류(classification)해내는 문제를 풀어보자. 


683개의 샘플이 있고, 각 샘플당 9개의 특성이 도출되어 있다. 물론 라벨값들이 있다. 나는 이중에서 80%에 해당하는 샘플을 이용해서 SVM 모델을 훈련시켰고, 나머지 20%의 샘플을 이용해서 예측 성능을 평가했다. 

분류모델을 훈련해야하기 때문에 -s 는 0으로 셋팅했고, 커널함수는 radial basis function을 선택했다. C와 gamma는 몇 번의 시행착오를 통해 좋은 성능을 내는 값으로 설정했다. SVM의 버전을 명시하기 위해 svmtrain.mexw64, svmpredict.mexw64을 svmtrain323.mexw64, svmpredict323.mexw64로 각각 이름을 바꿔줬다.

구현된 matlab 코드는 다음과 같다. 

clc, clear, close all

data = load('breast_cancer1.txt');
feature = data(:, 2:10); % 특성값
label = data(:, 11);  % 라벨값

feature = zscore(feature); % 특성값이 -1에서 1사이, 평균 0이 되도록 표준화.  

train_feat = feature(1:round(end*0.8), :); % 80%는 훈련용
test_feat = feature(round(end*0.8)+1:end, :); % 20%는 테스트용

train_label = label(1:round(end*0.8)); % 80%는 훈련용
test_label = label(round(end*0.8)+1:end); % 20%는 테스트용


libsvm_options = '-s 0 -t 3 -c 10 -g 2';

svm_model = svmtrain323(train_label, train_feat, libsvm_options);
[predicted_label, accuracy, decision_value] = svmpredict323(test_label, test_feat, svm_model);


위 코드를 작성하고 실행해보자. 매틀랩 명령창에 뜬 분류 정확도를 캡쳐했다. 

예측 정확도.



98.5401%의 정확도를 보였다. 137개의 테스트 데이터 중에 135개의 클래스를 정확히 맞춘 것이니 정확도가 꽤높다. 여기서 사용된 9개의 특성은 유방암 환자인지 여부를 알아내는데 상당히 의미있는 특성들인 것이다. 아무리 좋은 머신러닝, 딥러닝 알고리즘을 사용하더라도 특성이 별로라면 좋은 성능을 보이기 어렵다는 것을 기억하자. 이때는 알고리즘을 바꾸는 것이 아니라, 특성을 바꿔야 한다. 


참고로 더 섬세하게 grid search를 진행하면, 100% 정확한 결과를 산출할 수 있을 지도 모르겠다. 하지만 그것이 이 포스팅의 목적은 아니니, 나는 괜찮은 결과를 보이는 파라미터 값들을 몇 번의 시행을 통해 찾았다.



▶ 정리

LIBSVM 라이브러리를 이용해서 matlab에서 SVM을 활용하는 방법에 대해 알아봤다. 누군가에게 조금이나마 도움이 되길 기원하며 글을 매조짓는다. 



<참고자료>