Post

[Python] ML-SVM(Support Vector Machine)

[Python] ML-SVM(Support Vector Machine)

1. 왜 등장했는가

로지스틱 회귀는 클래스를 나누는 결정 경계를 찾지만, 어떤 경계가 “가장 좋은 경계”인지 기준이 없습니다.
SVM은 두 클래스 사이의 여백(Margin)을 최대화하는 경계를 찾아 일반화 성능을 높입니다.
또한 커널 함수로 선형 분리가 불가능한 데이터도 처리할 수 있습니다. (Vapnik, 1995)

“무한히 많은 결정 경계 중 가장 안전한 것을 고른다” — 이것이 SVM의 핵심 철학입니다.
두 클래스에서 최대한 멀리 떨어진 경계일수록 새 데이터에서도 오분류 확률이 낮아집니다.


2. 핵심 아이디어 — 두 클래스에서 가장 멀리

SVM은 본질적으로 두 클래스에서 최대한 멀리 떨어진 결정 경계를 찾습니다.

svm_idea

이 최대 여백을 만드는 데이터 포인트를 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_component

읽는 법 (가운데 순서도를 따라 위→아래로):

① 특성 스케일링 ← 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결정 경계에 가장 가까운 샘플들경기 판가름하는 핵심 선수
MarginSupport Vector 간의 거리두 팀 사이 중립 지대
커널 함수데이터를 고차원으로 변환2D를 3D로 올려서 보기
C (슬랙 변수)오분류 허용 정도엄격함 vs 유연함

Margin의 개념과 C 파라미터의 영향을 아래 그래프에서 확인할 수 있습니다.

svm_margin

읽는 법:
왼쪽(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차 다항식 경계

각 커널이 만들어내는 결정 경계를 아래 그래프에서 비교할 수 있습니다.

svm_kernels

읽는 법:
왼쪽(선형 커널): 직선 결정 경계. 선형 분리 가능한 데이터나 텍스트 분류에 적합합니다.
가운데(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 RegressionSVM
결정 기준전체 손실 최소화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
)
ParameterDefault역할과적합 방향
C1.0오분류 허용 정도클수록 과적합 ↑
kernel'rbf'커널 함수 종류-
gamma'scale'RBF 커널 폭클수록 과적합 ↑
probabilityFalse확률 출력 여부-
  • C : 오분류 페널티
    • 값 변화별 효과
      • 클수록 → 모든 훈련 샘플 정확히 분류 시도 → 좁은 Margin → 과적합
      • 작을수록 → 일부 오분류 허용 → 넓은 Margin → 일반화 강함
  • gamma : RBF 커널의 영향 범위
    • 값 변화별 효과
      • 클수록 → 가까운 샘플만 영향 → 복잡한 경계 → 과적합
      • 작을수록 → 멀리까지 영향 → 단순한 경계
    • Options
      • 'scale' : 1 / (n_features × X.var()) — 기본값
      • 'auto' : 1 / n_features
      • float : 직접 지정
  • 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)']))
This post is licensed under CC BY 4.0 by the author.