[Python] ML-SVM(Support Vector Machine)
1. 왜 등장했는가
로지스틱 회귀는 클래스를 나누는 결정 경계를 찾지만, 어떤 경계가 “가장 좋은 경계”인지 기준이 없습니다.
SVM은 두 클래스 사이의 여백(Margin)을 최대화하는 경계를 찾아 일반화 성능을 높입니다.
또한 커널 함수로 선형 분리가 불가능한 데이터도 처리할 수 있습니다. (Vapnik, 1995)
“무한히 많은 결정 경계 중 가장 안전한 것을 고른다” — 이것이 SVM의 핵심 철학입니다.
두 클래스에서 최대한 멀리 떨어진 경계일수록 새 데이터에서도 오분류 확률이 낮아집니다.
2. 핵심 아이디어 — 두 클래스에서 가장 멀리
SVM은 본질적으로 두 클래스에서 최대한 멀리 떨어진 결정 경계를 찾습니다.
이 최대 여백을 만드는 데이터 포인트를 Support Vector라고 합니다.
SVM은 전체 데이터가 아닌 경계에 가장 가까운 소수의 포인트(Support Vector)만 을 사용합니다.
3. 실제 예시로 보기 (분류 / 회귀)
예시 1 — 타이타닉 생존 예측 (분류)
1
2
3
4
5
6
7
8
9
10
11
12
13
2D로 단순화 (나이, 운임):
운임
│ ○ ○ ○ (생존)
│ ○ ○ ○
│ ───────────────── ← 결정 경계 (Margin 최대)
│ ━━━━━━━━━━━━━━━━ ← Margin 경계
│ ━━━━━━━━━━━━━━━━ ← Margin 경계
│ ● ● ● (사망)
└──────────────── 나이
Support Vector: 경계 가장 가까이 있는 ○, ● 포인트들
Margin: 두 Support Vector 경계 사이의 거리 → 최대화하려는 것
예시 2 — 비선형 분류 (커널 트릭)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
원본 2D 데이터 (선형으로 분리 불가):
○ ○ ○
○ ● ● ● ○
○ ● ● ● ○
○ ○ ○
RBF 커널로 3D 변환:
내부 ● → 높은 z값
외부 ○ → 낮은 z값
3D에서:
────────────── ← 3D 평면으로 분리 가능
→ 3D 평면 경계를 2D로 투영하면 원형 경계가 됨
4. 알고리즘 구성 요소
SVM이 데이터를 받아 결정 경계를 만들기까지 5단계 전체 흐름
읽는 법 (가운데 순서도를 따라 위→아래로):
① 특성 스케일링 ← SVM에 필수 (거리 기반)
왼쪽 위 그래프: -5~+5 범위의 원본 데이터가 -1~+1로 표준화됩니다.
SVM은 거리로 Margin을 계산하므로 스케일이 다른 특성이 있으면
결과가 왜곡됩니다.StandardScaler가 반드시 필요한 이유입니다.② 커널 함수로 고차원 변환 ← 선형/RBF/Poly/Sigmoid
오른쪽 커널 모양 그림: 각 커널이 만드는 결정 경계 형태가 다릅니다.
왼쪽 중간 3D 그래프: 2D에서 선형 분리가 불가능한 데이터를
고차원으로 올려 평면으로 분리할 수 있게 만드는 것이 커널 트릭입니다.③ Margin 최대화 최적화 ← QP (이차 계획법) 풀기
왼쪽 중간 포물선 그래프:min ½||w||²를 최소화하는 것이 곧
Margin을 최대화하는 것과 동일합니다.
이를 이차 계획법(Quadratic Programming)으로 수학적으로 풉니다.④ Support Vector 선택 ← 경계 가장 가까운 포인트들
왼쪽 아래 그래프: 큰 원으로 강조된 점들이 Support Vector입니다.
오른쪽 아래 그래프: 이 포인트들만으로 경계가 완전히 결정됩니다.
나머지 데이터를 모두 제거해도 Support Vector가 남으면 경계는 바뀌지 않습니다.⑤ 결정 경계 확정 및 예측
오른쪽 아래: 최종 결정 경계(실선)와 Margin 경계(점선) 사이로
새 데이터 포인트가 들어오면 어느 쪽에 속하는지 예측합니다.
| 구성 요소 | 설명 | 비유 |
|---|---|---|
| Support Vector | 결정 경계에 가장 가까운 샘플들 | 경기 판가름하는 핵심 선수 |
| Margin | Support Vector 간의 거리 | 두 팀 사이 중립 지대 |
| 커널 함수 | 데이터를 고차원으로 변환 | 2D를 3D로 올려서 보기 |
| C (슬랙 변수) | 오분류 허용 정도 | 엄격함 vs 유연함 |
Margin의 개념과 C 파라미터의 영향을 아래 그래프에서 확인할 수 있습니다.
읽는 법:
왼쪽(C가 작을 때): Margin이 넓고, 일부 오분류(margin 내 샘플)를 허용합니다. 과적합 위험이 낮습니다.
오른쪽(C가 클 때): 오분류를 최대한 줄이려 Margin이 좁아집니다. 경계가 복잡해져 과적합 위험이 높아집니다.
실무에서는 GridSearchCV로 최적 C를 탐색합니다.
5. 어떻게 Margin을 최대화하는가
5-1. Soft Margin (실전)
\[\text{최소화: } \frac{1}{2}\|\mathbf{w}\|^2 + C\sum_i \xi_i\]1
2
3
4
5
C가 클수록 → 오분류에 엄격 → 복잡한 경계 → 과적합 위험
C가 작을수록 → 오분류 허용 → 단순한 경계 → 과소적합 위험
C=0.01: ○ ○ ─────── ● ● (넓은 margin, 일부 오분류 허용)
C=100: ○ ○ ─ ○ ─── ● ● (좁은 margin, 오분류 최소화)
5-2. 커널 함수
1
2
3
4
5
6
7
8
선형 커널: K(x,z) = x^T z
→ 선형 분리 가능한 데이터, 고차원·희소 데이터 (텍스트)
RBF/가우시안 커널: K(x,z) = exp(-γ||x-z||²)
→ 원형 경계, 실무 기본값, 대부분의 경우 좋은 성능
다항식 커널: K(x,z) = (x^T z + c)^d
→ d차 다항식 경계
각 커널이 만들어내는 결정 경계를 아래 그래프에서 비교할 수 있습니다.
읽는 법:
왼쪽(선형 커널): 직선 결정 경계. 선형 분리 가능한 데이터나 텍스트 분류에 적합합니다.
가운데(RBF 커널): 원형 또는 곡선 경계. 비선형 데이터에 가장 범용적으로 사용됩니다.
오른쪽(다항식 커널): 다항식 형태의 경계. RBF와 비슷하지만 특정 데이터에 더 적합할 수 있습니다.
데이터가 선형 분리 불가능할 때는 RBF를 먼저 시도하는 것이 일반적입니다.
6. SVM 장・단점
6-1. ✅ SVM 장점
1
2
3
4
5
6
7
8
9
10
11
1. 고차원 데이터에 강함
→ 특성 수 > 샘플 수 상황에서도 효과적
2. 커널 트릭
→ 비선형 경계도 효율적으로 표현
3. 과적합 저항
→ Margin 최대화 → 일반화 성능 우수
4. 메모리 효율
→ Support Vector만 사용 → 예측 시 전체 데이터 불필요
6-2. ❌ SVM이 약한 상황
1
2
3
4
5
6
7
8
9
10
11
1. 대용량 데이터
→ 훈련 시간 O(N²~N³) → 수십만 샘플 이상 매우 느림
2. 하이퍼파라미터 동시 조정 필요
→ C, γ 조합을 GridSearchCV로 탐색해야 함
3. 확률 출력 불가 (기본)
→ predict_proba()는 추가 계산 필요 (probability=True)
4. 스케일링 필수
→ 거리 기반 → 반드시 전처리
6-2-1. 스케일링 필수
SVM의 가장 중요한 전처리 요구사항입니다.
나이(0~80) vs 연봉(0~100,000,000)이 같이 있을 때:
1
2
3
4
5
6
스케일링 없이:
거리 계산이 연봉 차이에 의해 지배됨
→ Margin 계산 왜곡 → 의미 없는 경계
스케일링 후:
두 특성이 동등하게 Margin에 기여
해결책 :
1
2
3
4
5
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train) # 훈련 데이터로 fit + transform
X_test = scaler.transform(X_test) # 테스트 데이터는 transform만
7. 한눈에 요약
| 항목 | 내용 |
|---|---|
| 알고리즘 유형 | 지도학습 / 분류 & 회귀 모두 가능 |
| 핵심 아이디어 | Margin 최대화 + 커널 트릭으로 비선형 처리 |
| 결정 경계 | 선형 또는 비선형 (커널에 따라) |
| 스케일링 필요? | ✅ 필수 (거리 기반) |
| 핵심 파라미터 | C, kernel, gamma |
| 실전 사용 | 중소규모 데이터, 고차원 데이터, 텍스트 분류 |
8. 다른 알고리즘과 무엇이 다른가
Logistic Regression vs SVM
1
2
3
4
5
Logistic Regression: SVM:
모든 포인트의 손실 합산 Support Vector만 고려
확률 출력 (기본) 확률 출력 별도 계산 필요
선형 경계 선형 + 비선형 (커널)
빠름 느림 (N 클 때)
| 항목 | Logistic Regression | SVM |
|---|---|---|
| 결정 기준 | 전체 손실 최소화 | Margin 최대화 |
| 비선형 | 다항 특성 추가 필요 | 커널 트릭 |
| 확률 출력 | ✅ 기본 | ⚠️ probability=True |
| 속도 | 빠름 | 느림 (N 클 때) |
9. 코드로 보기 — 타이타닉 생존 예측
1
2
3
4
5
6
7
8
from sklearn.svm import SVC
svm = SVC(
C = 1.0, # 오분류 패널티 (클수록 엄격)
kernel = 'rbf', # 커널: 'linear', 'rbf', 'poly', 'sigmoid'
gamma = 'scale', # RBF 커널의 γ: 'scale', 'auto' 또는 수치
probability = True, # predict_proba() 사용 위해 필요 (속도 다소 느려짐)
random_state = 0
)
| Parameter | Default | 역할 | 과적합 방향 |
|---|---|---|---|
C | 1.0 | 오분류 허용 정도 | 클수록 과적합 ↑ |
kernel | 'rbf' | 커널 함수 종류 | - |
gamma | 'scale' | RBF 커널 폭 | 클수록 과적합 ↑ |
probability | False | 확률 출력 여부 | - |
C: 오분류 페널티- 값 변화별 효과
- 클수록 → 모든 훈련 샘플 정확히 분류 시도 → 좁은 Margin → 과적합
- 작을수록 → 일부 오분류 허용 → 넓은 Margin → 일반화 강함
- 값 변화별 효과
gamma: RBF 커널의 영향 범위- 값 변화별 효과
- 클수록 → 가까운 샘플만 영향 → 복잡한 경계 → 과적합
- 작을수록 → 멀리까지 영향 → 단순한 경계
- Options
'scale': 1 / (n_features × X.var()) — 기본값'auto': 1 / n_featuresfloat: 직접 지정
- 값 변화별 효과
kernel: 커널 함수 종류'rbf': 기본값, 대부분의 경우 우수'linear': 선형 분리 가능 데이터, 고차원·희소 데이터'poly': 다항식 경계
9-1. 전처리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
titanic = pd.read_csv('./Data/Titanic.csv')
titanic['FamSize'] = titanic['SibSp'] + titanic['Parch']
use_cols = ['Survived', 'Pclass', 'Sex', 'Age', 'FamSize', 'Fare', 'Embarked']
titanic = titanic[use_cols].dropna(subset=['Age'])
titanic['Age'] = titanic['Age'].astype(int)
titanic = pd.get_dummies(titanic, columns=['Pclass', 'Sex', 'Embarked'], drop_first=True)
y = titanic['Survived']
X = titanic.drop('Survived', axis=1)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=0)
# ✅ SVM은 거리 기반 → 스케일링 필수
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
Note: SVM은 거리 기반 알고리즘이므로
StandardScaler가 반드시 필요합니다.
9-2. 모델 학습
1
2
3
4
5
6
7
8
svm = SVC(
C = 1.0, # 오분류 패널티 (클수록 엄격)
kernel = 'rbf', # 커널: 'linear', 'rbf', 'poly', 'sigmoid'
gamma = 'scale', # RBF 커널의 γ: 'scale', 'auto' 또는 수치
probability = True, # predict_proba() 사용 위해 필요 (속도 다소 느려짐)
random_state = 0
)
svm.fit(X_train, y_train)
9-2-1. 파라미터 탐색
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from sklearn.model_selection import GridSearchCV
param_grid = {
'C': [0.01, 0.1, 1, 10, 100],
'gamma': ['scale', 0.001, 0.01, 0.1]
}
svm_cv = GridSearchCV(
SVC(kernel='rbf', probability=True, random_state=0),
param_grid,
cv=5,
scoring='roc_auc'
)
svm_cv.fit(X_train, y_train)
print(f"Best params : {svm_cv.best_params_}")
print(f"Best AUC : {svm_cv.best_score_:.4f}")
9-3. 평가
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from sklearn.metrics import (
accuracy_score, confusion_matrix,
classification_report, roc_auc_score
)
pred = svm.predict(X_test)
pred_prob = svm.predict_proba(X_test)[:, 1]
cfx = confusion_matrix(y_test, pred)
sensitivity = cfx[1, 1] / (cfx[1, 0] + cfx[1, 1])
specificity = cfx[0, 0] / (cfx[0, 0] + cfx[0, 1])
roc_auc = roc_auc_score(y_test, pred_prob)
print(f"Accuracy : {accuracy_score(y_test, pred) * 100:.2f}%")
print(f"Sensitivity : {sensitivity * 100:.2f}%")
print(f"Specificity : {specificity * 100:.2f}%")
print(f"ROC AUC : {roc_auc:.4f}")
print()
print(classification_report(y_test, pred, target_names=['Died (0)', 'Survived (1)']))



