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

공부/머신러닝, 딥러닝|2018. 12. 19. 15:36

지난 포스팅에서 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을 활용하는 방법에 대해 알아봤다. 누군가에게 조금이나마 도움이 되길 기원하며 글을 매조짓는다. 



<참고자료>


  1. gyuho 2019.07.05 13:54 댓글주소  수정/삭제  댓글쓰기

    명령창에 make 입력시 파일들이 생성되지 않고
    gcc: error: \-fexceptions: No such file or directory
    라며 에러가 발생합니다.

    어떻게 해결할 수 있을 까요?

  2. 뚱박 2019.07.22 03:09 댓글주소  수정/삭제  댓글쓰기

    안녕하세요. 저는 libsvm을 통해 svdd(support vector data description)을 이용하는 학생입니다. 다름이 아니라 궁금한 점이 있어 질문 드립니다.

    svdd 같은 경우에는 모델 학습을 통해 구의 반지름와 중심값을 구하고 해당 중심과 반지름을 이용해서 test data를 이용하여 결과값을 출력하게 됩니다.

    이때, 모델에 test데이터를 입력하게 되면 값이 산출되고, 해당 값을 이용하여 레이블링이 이뤄진다고 생각하는데 어떤 기준으로 레이블링이 되는지 궁금합니다.

  3. 뚱박 2019.07.26 00:09 댓글주소  수정/삭제  댓글쓰기

    안녕하세요. 밑에 글 남겼는데 추가로 궁금한 점이 있어 또 질문 남기게 됐습니다.

    혹시 libsvm이용해서 svm의 kernel에 따른 하이퍼파라미터 튜닝을 하고 싶은데 방법이 있을까요?

    또 svdd에 적용할 수 있을까요?

    참고로 svdd는 libsvm 3.22에 업그레이드 되어 있습니다.

  4. Jmk 2020.03.17 15:02 댓글주소  수정/삭제  댓글쓰기

    예제에 나온 매틀랩 코드 똑깥이 써서 했는데 왜 안되는거죠??

    'svmtrain323'은(는) 정의되지 않은 함수 또는 변수입니다.

    오류 발생: Untitled (line 105)
    svm_model = svmtrain323(train_label, train_feat, libsvm_options);

    >>

  5. Jmk 2020.03.17 15:18 댓글주소  수정/삭제  댓글쓰기

    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 = svmtrain(train_label, train_feat, libsvm_options);
    [predicted_label, accuracy, decision_value] = svmpredict(test_label, test_feat, svm_model);


    빼고 했는데 오류 발생하서 댓글남깁니다.

    다음 사용 중 오류가 발생함: svmtrain (line 234)
    Y must be a vector or a character array.

    오류 발생: Untitled (line 88)
    svm_model = svmtrain(train_label, train_feat, libsvm_options);

    • BlogIcon 비스카이비전 2020.03.17 15:29 신고 댓글주소  수정/삭제

      svmtrain.mexw64와 svmpredict.mexw64를 svmtrain323.mexw64와 svmpredict323.mexw64으로 각각 바꿔주셨나요? 바꾸셨다면 포스팅의 코드대로 실행해주셔야합니다. 그리고 해당 코드를 담은 .m 파일이랑 svmtrain323.mexw64, svmpredict323.mexw64은 같은 디렉토리 안에 있어야 합니다.

    • Jmk 2020.03.18 10:56 댓글주소  수정/삭제

      처음에 make 쳤을 때 "지원되는 컴파일러 또는 SDK를 찾을 수 없습니다" 라고 떠서 MINGW MATLAB 에서 C/C++ 컴파일러 TDM-GCC 설치 등 하고 make 입력했더니

      'MinGW64 Compiler (C)'(으)로 빌드됩니다.
      Error: C:\Users\user\Desktop\연습\make.m failed (line 13)
      gcc: error: \-fexceptions: No such file or directory

      라고 뜨네요. 어떻게 해야하나요??

    • BlogIcon 비스카이비전 2020.03.18 10:57 신고 댓글주소  수정/삭제

      이멜 주소 알려주시면 제가 빌드된 것과 실행되는 예제를 보내드리겠습니다~

    • 2020.03.18 11:13 댓글주소  수정/삭제

      비밀댓글입니다

    • Jmk 2020.03.18 11:18 댓글주소  수정/삭제

      매트랩에서 해결할수있는 방법은 없을까요?

    • BlogIcon 비스카이비전 2020.03.18 12:08 신고 댓글주소  수정/삭제

      이멜로 연락주세요. ㅎㅎ

    • Jmk 2020.03.18 14:58 댓글주소  수정/삭제

      >> make
      'MinGW64 Compiler (C)'(으)로 빌드됩니다.
      Error: C:\Users\user\Desktop\libsvm-3.24\libsvm-3.24\matlab\make.m failed (line 13)
      gcc: error: \-fexceptions: No such file or directory

      => Please check README for detailed instructions.


      계속 이렇게 뜨는데 매트랩으로 구현할 수 없는건가요?

    • BlogIcon 비스카이비전 2020.03.18 15:03 신고 댓글주소  수정/삭제

      일단 빌드가 되야 matlab에서 사용할수가 있어서요^^;

    • Jmk 2020.03.18 15:39 댓글주소  수정/삭제

      도와주셔서 감사합니다

  6. student 2020.05.14 03:25 댓글주소  수정/삭제  댓글쓰기

    LIBSVM은 데이터의 차원을 축소하는 것은 아닌가요??

  7. student 2020.05.14 19:21 댓글주소  수정/삭제  댓글쓰기

    1. 커널을 만드는건 새로운 공간의 차원을 만드는 것으로 봐야되니 차원 축소는 아닌거겠죠?
    2. 매트랩에서 사용하는 Multisvm(https://kr.mathworks.com/help/stats/fitcecoc.html)이랑 LIBSVM랑 무슨 차이가 있는건가요?? 비슷해 보여서요.

    • BlogIcon 비스카이비전 2020.05.14 19:40 신고 댓글주소  수정/삭제

      1. SVM에서 커널을 사용하는 주된 이유는 초평면으로 데이터를 분리하기 어려운 경우에 커널을 통해 분리가능하게 만듭니다.
      2. 매트랩에서 사용하는 multisvm이나 libsvm이나 결국 SVM을 사용하기 위한 라이브러리라고 보시면 됩니다. 사용방법에 있어서는 차이가 있지만 결과적으로 같은 거라고 보셔도 좋을 것 같습니다.

    • student 2020.05.14 20:07 댓글주소  수정/삭제

      감사합니다

    • BlogIcon 비스카이비전 2020.05.14 21:20 신고 댓글주소  수정/삭제

      질문감사드립니다^^

  8. student 2020.05.15 14:09 댓글주소  수정/삭제  댓글쓰기

    공부하다 한가지 더 궁금한게 생겨서요.

    레이블값을 더 추가하면 multiclass svm이 가능하잖아요?
    그럼 one vs the Rest 방법을 사용해서 분류를 하는건가요?
    (각 class 별로 해당 class vs 나머지 class로 binary decision을 만들어서 분류를 하는 방법을 말하는 겁니다!)

    • BlogIcon 비스카이비전 2020.05.15 14:36 신고 댓글주소  수정/삭제

      안녕하세요ㅎㅎ LIBSVM 만드신 분들이 쓴 글에 의하면 multiclass SVM은 one vs one 방식, one vs rest 방식, DAG 방식 등을 이용해서 실현가능한데 LIBSVM에서 실제로 multiclass SVM을 어떻게 구현했는지는 잘 모르겠네요.^^; 아래 글을 참고해보세요.
      https://www.csie.ntu.edu.tw/~cjlin/papers/multisvm.pdf

  9. SHJ4770 2020.05.29 13:43 댓글주소  수정/삭제  댓글쓰기

    RBF가 polynimial 커널보다 범용적으로 쓰이는 이유가 RBF는 무한대의 차원으로 이동할 수 있어서 그런것으로 알고 있습니다. 실제로 Grid search를 해보고 RBF와 polynomial 둘 다 써봤는데 RBF가 분류를 더 잘하더라구요. 그런데 이유에 대해서 잘 모르겠어서 질문 남깁니다.

    • BlogIcon 비스카이비전 2020.05.29 13:57 신고 댓글주소  수정/삭제

      저도 경험적으로 RBF가 많은 경우에 더 잘 작동한다고 판단하고 있습니다. RBF 커널을 사용할 때 좀 더 안정된 성능을 보입니다. Polynomial 커널이 RBF보다 과적합(overfitting)시키는 경향이 더 크다고 합니다. 그런데 이론적인 원인을 정확하게 꼬집어 말하기는 어려운 것 같습니다. 다들 경험적으로 느낀것이라..

  10. ㅈㄴㄱ 2020.06.05 09:12 댓글주소  수정/삭제  댓글쓰기

    cost와 gamma는 노가다로 직접 구하는 방법밖에 없는건가요~?