Support Vector Regression (SVR)¶
논문 정보¶
| 항목 | 내용 |
|---|---|
| 제목 | Support Vector Regression Machines |
| 저자 | Harris Drucker, Christopher J.C. Burges, Linda Kaufman, Alex Smola, Vladimir Vapnik |
| 학회/저널 | Advances in Neural Information Processing Systems (NIPS) |
| 연도 | 1996 |
| 링크 | https://proceedings.neurips.cc/paper/1996/hash/d38901788c533e8286cb6400b40b386d-Abstract.html |
개요¶
문제 정의¶
회귀 문제에 Support Vector Machine의 개념을 적용:
- 일반 회귀: 예측값과 실제값 차이를 최소화
- SVR: epsilon-tube 내의 오차는 무시하고, 밖의 오차만 최소화
- 목표: 모든 학습 데이터가 epsilon 범위 내에 있으면서 가장 평평한(flat) 함수 찾기
핵심 아이디어¶
epsilon-insensitive loss function:
- epsilon 이내의 오차: 손실 0 (무시)
- epsilon 초과 오차: 선형적으로 페널티
- 이 손실 함수로 희소한(sparse) 해를 얻음
Structural Risk Minimization: - 경험적 위험 + 모델 복잡도 동시 최소화 - 과적합 방지 및 일반화 성능 향상
알고리즘/방법론¶
수식¶
Primal Formulation:
minimize: (1/2)||w||^2 + C * sum_i (xi_i + xi_i*)
subject to: y_i - (w . x_i + b) <= epsilon + xi_i
(w . x_i + b) - y_i <= epsilon + xi_i*
xi_i, xi_i* >= 0
여기서: - w: 가중치 벡터 - b: 편향 - C: 정규화 파라미터 (오차 허용도) - epsilon: 튜브 폭 - xi, xi*: slack 변수 (epsilon 초과 오차)
Dual Formulation:
maximize: -1/2 * sum_{i,j} (alpha_i - alpha_i*)(alpha_j - alpha_j*) K(x_i, x_j)
- epsilon * sum_i (alpha_i + alpha_i*)
+ sum_i y_i (alpha_i - alpha_i*)
subject to: sum_i (alpha_i - alpha_i*) = 0
0 <= alpha_i, alpha_i* <= C
예측 함수:
커널 함수¶
| 커널 | 수식 | 특징 |
|---|---|---|
| Linear | K(x, x') = x . x' | 선형 관계 |
| RBF (Gaussian) | K(x, x') = exp(-gamma ||x-x'||^2) | 비선형, 범용 |
| Polynomial | K(x, x') = (gamma * x . x' + r)^d | 다항 관계 |
| Sigmoid | K(x, x') = tanh(gamma * x . x' + r) | 신경망 유사 |
단계별 설명¶
Algorithm: SVR Training
Input: 학습 데이터 {(x_i, y_i)}, 파라미터 C, epsilon, kernel
Output: 예측 함수 f(x)
1. Construct kernel matrix
K_ij = K(x_i, x_j) for all i, j
2. Solve dual QP problem
- Quadratic programming으로 alpha, alpha* 최적화
- SMO (Sequential Minimal Optimization) 알고리즘 사용
3. Identify support vectors
- alpha_i > 0 또는 alpha_i* > 0인 샘플
- epsilon-tube 경계 또는 바깥에 위치
4. Compute bias term b
- KKT 조건 활용
- 0 < alpha_i < C인 서포트 벡터로 계산
5. Prediction
f(x) = sum_{SV} (alpha_i - alpha_i*) K(x_i, x) + b
시간 복잡도¶
| 단계 | 복잡도 |
|---|---|
| 커널 행렬 계산 | O(n^2 * d) |
| QP 최적화 (SMO) | O(n^2) ~ O(n^3) |
| 예측 (단일 샘플) | O(n_sv * d) |
주요 결과¶
논문의 실험 결과¶
Boston Housing 데이터셋:
| 방법 | Mean Squared Error |
|---|---|
| Ridge Regression | 24.3 |
| Bagging Trees | 12.1 |
| SVR (RBF) | 8.4 |
Sinc 함수 근사:
| 방법 | Support Vectors | MSE |
|---|---|---|
| SVR epsilon=0.1 | 45 | 0.0012 |
| SVR epsilon=0.01 | 112 | 0.0003 |
이론적 특성¶
- Sparsity: 학습 데이터 중 일부만 support vector로 사용
- Generalization: VC 차원 기반 일반화 한계 존재
- Global Optimum: Convex 최적화로 전역 최적해 보장
실전 적용 가이드¶
언제 사용하나¶
- 중소규모 데이터셋: n < 10,000에서 효과적
- 비선형 관계: RBF 커널로 복잡한 패턴 학습
- 이상치 존재: epsilon-insensitive loss로 로버스트
- 고차원 데이터: 커널 트릭으로 효율적 처리
하이퍼파라미터¶
| 파라미터 | 설명 | 권장 범위 | 튜닝 방법 |
|---|---|---|---|
| C | 정규화 강도 (높을수록 오차에 민감) | 0.1 ~ 1000 | Grid search, log scale |
| epsilon | 튜브 폭 (높을수록 관대) | 0.01 ~ 1.0 | 타겟 노이즈 수준 참고 |
| gamma (RBF) | 커널 폭 (높을수록 복잡) | 1/n_features ~ 10 | Grid search |
| degree (Poly) | 다항식 차수 | 2 ~ 5 | 도메인 지식 |
파라미터 선택 가이드라인¶
C 튜닝:
- C 낮음: 과소적합, 단순한 모델
- C 높음: 과적합, 모든 점 맞추려 함
epsilon 튜닝:
- epsilon 낮음: 정밀한 예측, support vector 많음
- epsilon 높음: 관대한 예측, support vector 적음
gamma (RBF) 튜닝:
- gamma 낮음: 부드러운 결정 경계
- gamma 높음: 복잡한 결정 경계, 과적합 위험
주의사항¶
-
스케일링 필수: 특성 스케일에 민감. StandardScaler 또는 MinMaxScaler 적용 필수.
-
대규모 데이터: n > 10,000이면 계산 비용 급증. LinearSVR 또는 다른 알고리즘 고려.
-
커널 선택:
- 먼저 RBF 시도
- 선형 관계 의심시 Linear 커널
-
도메인 지식 있으면 Polynomial
-
메모리: 커널 행렬이 O(n^2) 메모리 사용.
Python 코드 예시¶
import numpy as np
from sklearn.svm import SVR
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
from sklearn.datasets import fetch_california_housing
import matplotlib.pyplot as plt
# 데이터 로드
data = fetch_california_housing()
X, y = data.data[:2000], data.target[:2000] # 계산 시간을 위해 서브샘플
# Train/test split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# 스케일링 (필수)
scaler_X = StandardScaler()
scaler_y = StandardScaler()
X_train_scaled = scaler_X.fit_transform(X_train)
X_test_scaled = scaler_X.transform(X_test)
y_train_scaled = scaler_y.fit_transform(y_train.reshape(-1, 1)).ravel()
# SVR 모델 학습
svr = SVR(
kernel='rbf',
C=100,
epsilon=0.1,
gamma='scale'
)
svr.fit(X_train_scaled, y_train_scaled)
# 예측
y_pred_scaled = svr.predict(X_test_scaled)
y_pred = scaler_y.inverse_transform(y_pred_scaled.reshape(-1, 1)).ravel()
# 평가
print("=== SVR Performance ===")
print(f"R2 Score: {r2_score(y_test, y_pred):.4f}")
print(f"MSE: {mean_squared_error(y_test, y_pred):.4f}")
print(f"RMSE: {np.sqrt(mean_squared_error(y_test, y_pred)):.4f}")
print(f"MAE: {mean_absolute_error(y_test, y_pred):.4f}")
print(f"Number of support vectors: {svr.n_support_.sum()}")
print(f"Support vector ratio: {svr.n_support_.sum() / len(X_train):.2%}")
# 시각화
plt.figure(figsize=(10, 4))
plt.subplot(1, 2, 1)
plt.scatter(y_test, y_pred, alpha=0.5)
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--')
plt.xlabel('Actual')
plt.ylabel('Predicted')
plt.title('SVR: Actual vs Predicted')
plt.subplot(1, 2, 2)
residuals = y_test - y_pred
plt.hist(residuals, bins=30, edgecolor='black')
plt.xlabel('Residual')
plt.ylabel('Frequency')
plt.title('Residual Distribution')
plt.tight_layout()
plt.savefig('svr_result.png', dpi=150)
plt.show()
하이퍼파라미터 튜닝¶
from sklearn.model_selection import GridSearchCV
# 파라미터 그리드
param_grid = {
'C': [0.1, 1, 10, 100],
'epsilon': [0.01, 0.1, 0.5],
'gamma': ['scale', 'auto', 0.01, 0.1]
}
# Grid Search
svr = SVR(kernel='rbf')
grid_search = GridSearchCV(
svr,
param_grid,
cv=5,
scoring='neg_mean_squared_error',
n_jobs=-1,
verbose=1
)
grid_search.fit(X_train_scaled, y_train_scaled)
print("\n=== Grid Search Results ===")
print(f"Best parameters: {grid_search.best_params_}")
print(f"Best CV score (neg MSE): {grid_search.best_score_:.4f}")
# 최적 모델로 예측
best_svr = grid_search.best_estimator_
y_pred_best = scaler_y.inverse_transform(
best_svr.predict(X_test_scaled).reshape(-1, 1)
).ravel()
print(f"\nTest R2 with best model: {r2_score(y_test, y_pred_best):.4f}")
커널 비교¶
# 여러 커널 비교
kernels = ['linear', 'rbf', 'poly']
results = {}
for kernel in kernels:
svr = SVR(kernel=kernel, C=10, epsilon=0.1)
svr.fit(X_train_scaled, y_train_scaled)
y_pred = scaler_y.inverse_transform(
svr.predict(X_test_scaled).reshape(-1, 1)
).ravel()
results[kernel] = {
'r2': r2_score(y_test, y_pred),
'rmse': np.sqrt(mean_squared_error(y_test, y_pred)),
'n_sv': svr.n_support_.sum()
}
print("\n=== Kernel Comparison ===")
print(f"{'Kernel':<10} {'R2':>8} {'RMSE':>8} {'#SV':>8}")
print("-" * 36)
for kernel, metrics in results.items():
print(f"{kernel:<10} {metrics['r2']:>8.4f} {metrics['rmse']:>8.4f} {metrics['n_sv']:>8}")
대규모 데이터용 LinearSVR¶
from sklearn.svm import LinearSVR
# 대규모 데이터에서 빠른 학습
linear_svr = LinearSVR(
epsilon=0.1,
C=1.0,
max_iter=10000,
dual=True
)
linear_svr.fit(X_train_scaled, y_train_scaled)
y_pred_linear = scaler_y.inverse_transform(
linear_svr.predict(X_test_scaled).reshape(-1, 1)
).ravel()
print(f"\nLinearSVR R2: {r2_score(y_test, y_pred_linear):.4f}")
관련 기법¶
비교¶
| 기법 | 장점 | 단점 | 적합 상황 |
|---|---|---|---|
| SVR | 비선형, 로버스트 | 느림, 파라미터 민감 | 중소규모, 비선형 |
| Linear Regression | 빠름, 해석 가능 | 선형만 | 선형 관계 |
| Random Forest | 빠름, 로버스트 | 외삽 불가 | 범용 |
| Gradient Boosting | 높은 성능 | 과적합 위험 | 대회, 복잡한 패턴 |
| Neural Network | 매우 유연 | 데이터/계산 필요 | 대규모 데이터 |
대안 기법¶
- Kernel Ridge Regression
- SVR과 유사하나 모든 데이터 사용
- Closed-form solution 존재
-
Sparsity 없음
-
Gaussian Process Regression
- 불확실성 추정 가능
- 자동 하이퍼파라미터 최적화 (MLE)
-
계산 비용 O(n^3)
-
nu-SVR
- epsilon 대신 nu 파라미터 사용
- nu: support vector 비율의 하한
-
epsilon 자동 결정
-
Relevance Vector Machine (RVM)
- Bayesian SVR 변형
- 더 희소한 해
- 불확실성 추정 가능
참고 문헌¶
- Smola, A.J., & Scholkopf, B. (2004). "A tutorial on support vector regression". Statistics and Computing.
- Vapnik, V. (1995). "The Nature of Statistical Learning Theory". Springer.
- Chang, C.C., & Lin, C.J. (2011). "LIBSVM: A library for support vector machines". ACM TIST.