데이터 예측 및 회귀 분석 분야에서 서포트 벡터 머신(SVM)은 뛰어난 일반화 성능을 발휘하는 알고리즘입니다. 특히 SVM의 성능은 패널티 파라미터(C)와 커널 파라미터(gamma)와 같은 하이퍼파라미터 설정에 크게 의존합니다. K-Fold 교차 검정을 결합하면 데이터의 분포를 더 정확하게 반영하며 과적합을 방지하고 최적의 파라미터를 안정적으로 도출할 수 있습니다. 본 글에서는 MATLAB와 LIBSVM 라이브러리를 활용하여 K-Fold 교차 검정 기반의 SVM 회귀 예측 모델을 구현하고 하이퍼파라미터를 최적화하는 과정을 다룹니다.
전체 아키텍처 및 프로세스
구현의 핵심은 그리드 서치(Grid Search)와 K-Fold 교차 검정을 결합하여 최적의 C와 gamma 값을 탐색하는 것입니다. 전체 프로세스는 데이터를 로드하고 특성과 타겟을 분리하는 메인 스크립트와, 실제 파라미터 탐색 및 모델 검증을 수행하는 함수로 구성됩니다. 최신 MATLAB 문법에 맞춰 데이터 I/O 방식을 개선하고, LIBSVM의 회귀 옵션을 명시적으로 지정하여 코드의 안정성을 높였습니다.
메인 스크립트 구현
메인 스크립트는 외부 데이터 세트를 불러오고, 특성과 타겟 변수를 정의한 후 파라미터 최적화 함수를 호출하는 역할을 합니다. 기존에 사용되던 xlsread 대신 더 효율적이고 권장되는 readmatrix 함수를 사용합니다.
% SVM 회귀 예측을 위한 메인 실행 스크립트
clear; clc; close all;
% 데이터 로드 (최신 MATLAB 함수 사용)
dataset = readmatrix('regression_dataset.xlsx');
% 특성 변수(X)와 타겟 변수(Y) 분리
predictors = dataset(:, 1:end-1);
responses = dataset(:, end);
% K-Fold 교차 검정 폴드 수 설정
kFolds = 5;
% 최적 파라미터 탐색 및 모델 평가 함수 호출
[optC, optGamma, minMSE] = findOptimalSVMParams(predictors, responses, kFolds);
% 최적화 결과 출력
fprintf('파라미터 탐색 완료\n');
fprintf('최적 패널티 파라미터 (C) : %.4f\n', optC);
fprintf('최적 커널 파라미터 (gamma) : %.4f\n', optGamma);
fprintf('최소 평균 제곱 오차 (MSE) : %.4f\n', minMSE);
위 코드에서 readmatrix는 Excel 파일의 수치형 데이터를 매트릭스 형태로 빠르게 읽어옵니다. 데이터의 마지막 열을 종속 변수(responses)로, 나머지 열을 독립 변수(predictors)로 할당합니다. kFolds 변수를 통해 교차 검정의 분할 수를 정의하며, 일반적으로 5 또는 10으로 설정합니다.
하이퍼파라미터 최적화 및 교차 검정 함수
파라미터 탐색 함수는 로그 스케일의 검색 공간을 정의하고, 모든 파라미터 조합에 대해 K-Fold 검증을 수행하여 평균 제곱 오차(MSE)가 가장 낮은 조합을 선택합니다. LIBSVM의 svmtrain 함수에서 회귀 모델을 의미하는 -s 3 (epsilon-SVR) 옵션과 RBF 커널을 의미하는 -t 2 옵션을 명시합니다.
function [bestC, bestGamma, bestMSE] = findOptimalSVMParams(X, Y, folds)
% 하이퍼파라미터 검색 공간 정의 (로그 스케일 그리드)
cValues = logspace(-1, 3, 5);
gammaValues = logspace(-3, 1, 5);
% 파라미터 그리드 생성
[CGrid, GammaGrid] = meshgrid(cValues, gammaValues);
% 최적값 초기화
bestMSE = inf;
bestC = CGrid(1);
bestGamma = GammaGrid(1);
numSamples = size(X, 1);
% K-Fold 인덱스 사전 생성 (모든 파라미터 조합에 동일한 폴드 적용)
indices = crossvalind('Kfold', numSamples, folds);
% 그리드 서치 반복문
for idx = 1:numel(CGrid)
currentC = CGrid(idx);
currentGamma = GammaGrid(idx);
foldMSE = zeros(folds, 1);
for f = 1:folds
% 훈련 세트와 검증 세트 인덱스 분리
trainIdx = (indices ~= f);
testIdx = (indices == f);
% LIBSVM 옵션 설정 (회귀: -s 3, RBF 커널: -t 2, 조용히 실행: -q)
options = sprintf('-s 3 -t 2 -c %f -g %f -q', currentC, currentGamma);
% 모델 학습
model = svmtrain(Y(trainIdx), X(trainIdx, :), options);
% 검증 세트를 활용한 예측
[predY, ~, ~] = svmpredict(Y(testIdx), X(testIdx, :), model, '-q');
% 현재 폴드의 MSE 계산
foldMSE(f) = mean((predY - Y(testIdx)).^2);
end
% 전체 폴드의 평균 MSE 도출
avgMSE = mean(foldMSE);
% 최소 MSE 및 최적 파라미터 업데이트
if avgMSE < bestMSE
bestMSE = avgMSE;
bestC = currentC;
bestGamma = currentGamma;
end
end
end
이 함수에서는 meshgrid를 사용하여 C와 gamma의 모든 조합을 매트릭스 형태로 생성한 후, 1차원 인덱싱으로 순회합니다. 또한 crossvalind를 반복문 외부에서 한 번만 호출하여, 모든 파라미터 조합이 동일한 데이터 분할을 기반으로 평가되도록 보장합니다. 이는 평가의 일관성을 유지하는 중요한 요소입니다.
LIBSVM 호출 시 -q 플래그를 추가하여 콘솔에 출력되는 불필요한 로그를 억제함으로써 대규모 그리드 서치 시 실행 속도와 가독성을 향상시켰습니다. 최종적으로 검증된 최적의 파라미터와 해당 파라미터에서의 평균 MSE를 메인 스크립트로 반환합니다.