시작하면서
우선 교본 안에 서술된 여러 머신러닝 기법들 중에는 아달린 알고리즘(경사하강법을 이용한)이 존재하나, 이미 이전에 서술했던 내용이기에 생략하고 로지스틱 회귀분석 - 결정 트리까지 정리하겠다.
로지스틱 회귀분석
- 로지스틱 회귀분석을 통한 클래스 확률 모델링
기존 퍼셉트론을 통한 훈련은 분류 알고리즘에 적합하지만 동시에 선형적으로 구분되지 않을 때, 학습 수렴을 할 수 없는 단점이 있음(XOR 게이트), 그렇기에 간단한 이진분류를 더욱 더 강력하게 학습이 가능한 로지스틱 회귀에 대해서 정리하겠다.
1. 정의
- 이진분류 문제를 해결하기 위한 통계적 모델 중 하나
- 두 가지 범주(\(Yes\) \(Or\) \(No\)) 중 하나로 분류
- 예) : 스팸 분류, 환자 질병 예측 등에 사용
2. 로지스틱 회귀의 핵심
2-1. 선형모델
- 선형회귀와 비슷하게, 입력 특성(\(X\))과 가중치(\(w\)), 편향(\(b\))의 선형 조합을 수행함.
- 수식
- \(z\) = \(w_1 x_1\) + \(w_2 x_2 \)+ \(\cdots + w_n x_n + b\)
- \(x_i\): 입력 변수 • \(b\) : 편향(Bias)
- \(w_i\): 각 입력 변수에 대한 가중치
- \(z\) = \(w_1 x_1\) + \(w_2 x_2 \)+ \(\cdots + w_n x_n + b\)
- 수식
2-2. 로짓(Logit) 함수
- 로짓 함수는 선형 모델의 출력을 오즈비(Odds Ratio)로 변환함
- 수식
- \(\log\left(\frac{P(y=1)}{1 - P(y=1)}\right)\) = \(w_1 x_1 + w_2 x_2\) + \(\cdots + w_n x_n + b\)
- 수식
2-3. 시그모이드 함수
- 로짓 값을 확률 값으로 변환(0과 1사이의 실수 값으로 수렴한다는 것)
- 수식
- \(P(y=1|x)\) = \(\sigma(z)\) = \(\frac{1}{1 + e^{-z}}\)
$$
\hat{y} =
\begin{cases}
1, & \text{if } P(y=1|x) = \sigma(z) \geq 0.5 \\
0, & \text{그 외}
\end{cases}
$$
- 각 값 설명
- \(\hat{y}\): 예측된 클래스 레이블 (0 또는 1)
- \(P(y=1|x)\): 입력 데이터 \(x\)가 클래스 1에 속할 확률
- \(\sigma(z)\): 시그모이드 함수의 출력값 \((0 \leq \sigma(z) \leq 1)\)
- 0.5: 임계값(Threshold)
- 확률이 0.5 이상이면 1로 분류
- 확률이 0.5 미만이면 0으로 분류
여기서 궁금증
🚀 오즈비란?
3. 오즈(Odds)
- 사건이 발생할 확률과 발생하지 않을 확률의 비율(베르누이 분포와 같이 0과 1로 수렴하는 결과를 찾는 것이라고 보면 된다
홀짝..- 수식
- \(\text{Odds}\) = \(\frac{P(y=1)}{P(y=0)}\) = \(\frac{P(y=1)}{1 - P(y=1)}\)
- 예) 환자가 질병에 걸릴 확률이 0.8%라면
- 수식
- \(\text{Odds}\) = \(\frac{0.8}{0.2} = 4\)
- 식 그대로 걸릴 확률이 걸리지 않을 확률보다 4배가 높다는 이야기로 설명이 가능(특정 조건을 걸어야 더 이해가 쉽습니다.. 담배를 피는 사람들을 실험 조건으로)
3-1. 오즈비(Odds Ratio)
- 두 확률의 비율을 나타내며, 계수 w에 따라 해석됨:
\(OR = e^{w}\)
- \(OR > 1\): 해당 특성이 사건 발생 가능성을 높임
- \(OR < 1\): 해당 특성이 사건 발생 가능성을 낮춤

아달린과 로지스틱 회귀를 비교하였을 때 큰 흐름 자체는 비슷하나, 아달린 알고리즘은 0~1 사이의 실수 값, 로지스틱 회귀 자체는 확률 값으로 나뉜다는 것을 알 수 있다.
🎯 차이점을 요약하자면 ...
| 구분 | ADALINE | 로지스틱 회귀 |
|---|---|---|
| 활성화 함수 | 선형 활성화 함수 | 시그모이드 활성화 함수 |
| 출력 값 | 실수 값 (-∞ ~ +∞) | 확률 값 (0 ~ 1) |
| 임계값 | 0을 기준으로 이진 분류 | 0.5를 기준으로 이진 분류 |
| 해석 | 수치적 해석만 가능 | 확률적 해석 가능 |
| 손실 함수 | 평균 제곱 오차 (MSE) | 교차 엔트로피 손실 (CE Loss) |
| 결과 예시 | \( y = 1.5 \) → 1 | \( P(y=1) = 0.8 \) → 1 |
💻 예제 코드
import numpy as np
class LogisticRegression:
"""
로지스틱 회귀 분류기 (Logistic Regression Classifier)
Parameters:
----------
eta : float
학습률 (Learning Rate) - 각 반복에서 가중치를 얼마나 조정할지 결정합니다.
n_iter : int
반복 횟수 (Number of Iterations) - 경사 하강법을 몇 번 수행할지 설정합니다.
random_state : int
난수 시드 (Random State) - 가중치 초기화의 재현성을 보장합니다.
Attributes:
----------
w_ : 1d-array
학습된 가중치 벡터 (Weight Vector)
b_ : float
학습된 절편 (Bias)
losses_ : list
각 반복(epoch)마다의 손실 함수 값 (Loss Function Value)
"""
def __init__(self, eta=0.01, n_iter=1000, random_state=1):
self.eta = eta # 학습률 (Learning Rate)
self.n_iter = n_iter # 반복 횟수 (Epochs)
self.random_state = random_state # 난수 시드 (Random State)
self.w_ = None # 가중치 초기화
self.b_ = None # 절편 초기화
self.losses_ = [] # 손실값 기록
def sigmoid(self, z):
"""
시그모이드 함수 (Sigmoid Function)
z: 선형 결합 값 (Linear Combination)
"""
return 1 / (1 + np.exp(-z))
def loss(self, y_true, y_pred):
"""
교차 엔트로피 손실 함수 (Cross-Entropy Loss Function)
"""
return -np.mean(y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred))
def fit(self, X, y):
"""
로지스틱 회귀 모델 학습
Parameters:
----------
X : ndarray
입력 데이터 (Feature Matrix)
y : ndarray
실제 레이블 (Target Labels)
"""
np.random.seed(self.random_state)
n_samples, n_features = X.shape
# 가중치와 절편 초기화
self.w_ = np.random.randn(n_features)
self.b_ = 0.0
# 경사 하강법(Gradient Descent) 학습
for _ in range(self.n_iter):
linear_output = np.dot(X, self.w_) + self.b_
y_pred = self.sigmoid(linear_output)
# 손실 함수 계산
loss = self.loss(y, y_pred)
self.losses_.append(loss)
# 기울기 계산 (Gradient)
dw = (1 / n_samples) * np.dot(X.T, (y_pred - y))
db = (1 / n_samples) * np.sum(y_pred - y)
# 가중치와 절편 업데이트
self.w_ -= self.eta * dw
self.b_ -= self.eta * db
def predict_proba(self, X):
"""
예측 확률 반환
Parameters:
----------
X : ndarray
입력 데이터 (Feature Matrix)
Returns:
--------
ndarray
클래스 1에 대한 확률
"""
linear_output = np.dot(X, self.w_) + self.b_
return self.sigmoid(linear_output)
def predict(self, X):
"""
클래스 레이블 예측 (0 또는 1)
Parameters:
----------
X : ndarray
입력 데이터 (Feature Matrix)
Returns:
--------
ndarray
예측된 클래스 레이블 (0 또는 1)
"""
probabilities = self.predict_proba(X)
return np.where(probabilities >= 0.5, 1, 0)
다음시간에는 규제와 관련된 옵티마이제이션에 대해서 정리할 예정 ... (교본에는 너무 내용이 방대해서 일단은 분류기까지만)
서포트벡터 머신
What is SVM(Support Vector Machine)?
- 서포트벡터 머신이란?
- 분류와 회귀 두 가지 문제를 모두 해결할 수 있는 지도학습 알고리즘
- 보통 이진분류 문제에서 주로 사용하며, 클래스별 결정경계를 통해 데이터 포인트를 분리하는 것이 특징
- 결정경계?? 그게 뭔가요?
- 예를 들어서 질병의 양/음성 유무를 판단하기 위한 분류기를 만든다고 가정할 때

- 클래스 A(음성): ○
- ○ ○ ○ | × × × (결정경계: 직선)
- 클래스 B(양성): ×
- 여기서 알아둬야 할 표를 보며 이해할 내용들
- 마진 : 결정경계와 초평면 사이의 간격
- 초평면 : 클래스를 구분하는 핵심 요소 중 하나(결정 경계를 통해 나누어진 공간을 보고 클래스별~ 초평면이라고 한다
- 여기서 알아둬야 할 표를 보며 이해할 내용들
그렇게 SVM을 통해 마진을 최대화시키는 것이 목적(초평면에 가까운 훈련 샘플을 사이의 거리로 정의 → 이러한 샘플을 보고 서포트 벡터라고 한다.)
🚀 슬랙 변수를 통한 비선형 분류 문제 해결
- 실제 데이터는 노이즈 및 선형으로 분리되지 않는 경우가 발생한다(XOR게이트와 같은... 곧 XOR 게이트와 비선형에 대한 내용도 정리 예정)
- 슬랙 변수를 통해 마진을 최대화 하려함
- 슬랙 변수(ξ, Xi): 초평면으로부터 벗어난 데이터 포인트의 오차를 측정하는 값
- 수식:
- \(y_i (w^T x_i + b) \geq 1\) - \(\xi_i, \quad \xi_i \geq 0\)
- \(y_i\): 실제 레이블 (+1 또는 -1)
- \(w^T x_i + b\): 초평면과의 거리
- \(\xi_i\): 슬랙 변수 (데이터 포인트가 결정 경계를 얼마나 위반했는지 나타냄)
- \(y_i (w^T x_i + b) \geq 1\) - \(\xi_i, \quad \xi_i \geq 0\)
슬랙 변수의 역할
- 오분류 허용: 완벽한 선형 분리가 불가능한 상황에서 일부 오분류를 허용
- 하이퍼파라미터 C: 슬랙 변수를 제어하기 위해 비용(패널티) 파라미터 C가 사용
- C가 크면: 오분류를 거의 허용하지 않고 마진이 좁아짐
- C가 작으면: 오분류를 더 많이 허용하며 마진이 넓어짐
슬랙 변수를 포함한 SVM의 최적화 문제:
\[
\min_{w, b, \xi} \frac{1}{2} ||w||^2 + C \sum_{i=1}^n \xi_i
\]
- 제약 조건:
- \(y_i (w^T x_i + b) \geq 1\) - \(\xi_i, \quad \xi_i \geq 0\)
설명:
- 1차항: 마진을 최대화
- 2차항: 슬랙 변수의 합 (\xi_i)을 최소화하여 오분류를 줄임
- C: 두 항목 간의 균형을 조정
슬랙변수를 통해 나누어지는 마진을 구분하는 방법
- 하드 마진(Hard Margin): 완벽하게 선형으로 분리 가능한 경우
- 소프트 마진(Soft Margin): 선형으로 완벽하게 분리할 수 없어서 약간의 오분류를 허용하는 경우

그렇지만, 실무 데이터를 슬랙 변수를 통해 구분하는 것은 비용이 많이 발생할 우려가 있어 대책안이 필요했다
🎯 커널 (Kernel)로 비선형 문제 해결
- 슬랙 변수를 통해 해결하기 어려운(비용이 많이 발생하는 부분) 상황을 고려하여 고차원으로 매핑시킨 후 비선형 문제를 해결하기 위해 등장
- 두 개념은 서로 독립적이지 않고 함께 사용이 가능하다.

보기와 같이 비선형 구조는 아예 차원을 바꿔서 분류시킨다(고차원 공간에 투영)
주요 커널 함수
- 선형 커널 (Linear Kernel): \(K(x_i, x_j) = x_i^T x_j\)
- 다항식 커널 (Polynomial Kernel): \(K(x_i, x_j) = (x_i^T x_j + 1)^d\)
- 가우시안 RBF 커널 (RBF Kernel): \(K(x_i, x_j) = \exp(-\gamma ||x_i - x_j||^2)\)
각 함수별 기능 및 장단점
- 선형 커널 (Linear Kernel): \(K(x_i, x_j) = x_i^T x_j\)
- 데이터를 고차원으로 매핑하지 않고 그대로 사용
- 선형적으로 분리 가능한 문제에 적합
- 계산 비용이 적고, 빠르게 학습됨
- 데이터가 선형적으로 잘 분리될 때, 특성의 수가 샘플 수보다 많을 때 사용, 보통 이진 분류 문제에 사용
- 다항식 커널 (Polynomial Kernel): \(K(x_i, x_j) = (x_i^T x_j + 1)^d\)
- 비선형 경계를 다룰 수 있음
- 차수(d)가 높을수록 복잡한 패턴을 학습할 수 있지만, 과적합 가능성이 있음
- 저차원 데이터에서 효과적, 보통은 간단한 곡선 경계 분류 문제
- 가우시안 RBF 커널 (RBF Kernel): \(K(x_i, x_j) = \exp(-\gamma ||x_i - x_j||^2)\)
- 가장 많이 사용되는 커널
- 다양한 형태의 비선형 경계를 학습할 수 있음
- \(\gamma\) 값이 클수록 결정 경계가 복잡해지고, 작을수록 단순해짐
- 데이터를 무한 차원 공간으로 매핑
💻 예제 코드
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# 데이터셋 생성 (2차원 이진 분류 문제)
X, y = datasets.make_classification(n_samples=200,
n_features=2,
n_redundant=0,
n_informative=2,
random_state=42,
n_clusters_per_class=1)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 시각화를 위한 함수
def plot_decision_boundary(X, y, model, title):
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.linspace(x_min, x_max, 100),
np.linspace(y_min, y_max, 100))
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.contourf(xx, yy, Z, alpha=0.3)
plt.scatter(X[:, 0], X[:, 1], c=y, edgecolors='k')
plt.title(title)
plt.show()
# 1️⃣ C 값 비교 (선형 커널 사용)
for C_value in [0.1, 1, 10]:
model = SVC(kernel='linear', C=C_value)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
acc = accuracy_score(y_test, y_pred)
print(f"Linear Kernel with C={C_value}, Accuracy: {acc:.2f}")
plot_decision_boundary(X, y, model, f"Linear Kernel (C={C_value})")
# 2️⃣ 커널 비교 (C 값 고정)
for kernel in ['linear', 'poly', 'rbf', 'sigmoid']:
model = SVC(kernel=kernel, C=1.0)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
acc = accuracy_score(y_test, y_pred)
print(f"{kernel.capitalize()} Kernel, Accuracy: {acc:.2f}")
plot_decision_boundary(X, y, model, f"{kernel.capitalize()} Kernel (C=1.0)")
'머신러닝 교과서_파이토치편' 카테고리의 다른 글
| [머신러닝 교과서] 2. 분류를 위한 머신러닝 기법들 - 2 (트리구조, KNN) (1) | 2024.12.26 |
|---|---|
| [머신러닝 교과서] 1. 퍼셉트론과 아달린(완전 배치 경사하강법, 미니 배치 경사하강법) (2) | 2024.12.08 |