[CS300 #265] 로지스틱 회귀 — 확률을 내놓는 분류기
컴퓨터공학 300 주제 시리즈의 265번째 글이다. 전체 지도는 여기.
한 줄 요약
로지스틱 회귀는 선형 결합 z = wᵀx 를 시그모이드에 통과시켜 0~1 사이의 확률로 바꾸고, 로그 손실(교차 엔트로피)을 최소화해 학습하는 분류 모델이다. 이름에 “회귀”가 붙었지만 분류기다.
왜 필요한가
분류 문제에 선형 회귀를 그대로 쓰면 곤란하다. 예측값이 -0.3 이나 1.7 처럼 확률로 읽을 수 없는 값이 나오고, 결정 경계에서 먼 점 몇 개가 직선을 끌고 가 경계가 엉뚱하게 움직인다.
로지스틱 회귀는 이 문제를 풀면서도 선형 모델의 장점(빠름, 해석 가능)을 지킨다. 신용 평가, 클릭 예측, 의료 위험도처럼 “확률”이 필요한 곳에서 지금도 기준 모델이다. 그리고 신경망 분류기의 마지막 층(시그모이드·소프트맥스 + 교차 엔트로피)이 바로 로지스틱 회귀다. 여기서 이해한 것이 딥러닝 분류 전체로 이어진다.
핵심 개념
시그모이드
σ(z) = 1 / (1 + e^(−z))
z: −∞ ... −2 0 2 ... +∞
σ(z): 0 ... 0.12 0.5 0.88 ... 1
| 어떤 실수든 (0, 1) 로 눌러 담는다. 모델은 P(y=1 | x) = σ(wᵀx) 다. |
오즈와 로짓
σ 의 역함수를 풀면
log( p / (1 − p) ) = wᵀx
p/(1−p) 는 오즈, 그 로그를 로짓이라 한다. 즉 로지스틱 회귀는 로그 오즈를 선형으로 모델링한다. 해석도 여기서 나온다. 가중치 wj 가 0.7 이면, 특징 xj 가 1 늘 때 오즈가 e^0.7 ≈ 2 배가 된다.
결정 경계
p ≥ 0.5 이면 1 로 분류한다고 하면, σ(z) = 0.5 인 z = 0 이 경계다. 즉 wᵀx = 0 인 초평면. 그래서 로지스틱 회귀는 선형 분류기다. 원 모양 경계가 필요하면 x², x·y 같은 특징을 직접 만들어 넣어야 한다.
손실 — 로그 손실
정답 y ∈ {0, 1}, 예측 확률 p 에 대해
L = −[ y·log p + (1 − y)·log(1 − p) ]
정답이 1 인데 p = 0.01 이라고 자신 있게 틀리면 −log 0.01 ≈ 4.6 으로 크게 벌받는다. MSE 대신 이 손실을 쓰는 이유는 두 가지다. 확률 모델의 최대우도 추정과 정확히 같고, 시그모이드와 결합했을 때 손실이 w 에 대해 볼록해서 지역 최솟값이 없다.
기울기는 놀랄 만큼 깔끔하다.
∂L/∂w = (p − y) · x
선형 회귀의 (ŷ − y)·x 와 같은 모양이다. “예측과 정답의 차이 × 입력”이라는 이 형태는 신경망의 출력층에서도 그대로 나온다.
다중 클래스 — 소프트맥스
클래스가 K 개면 클래스마다 점수 zk 를 만들고
P(y = k | x) = exp(zk) / Σj exp(zj)
로 정규화한다. 이것이 소프트맥스 회귀(다항 로지스틱 회귀)다.
정규화와 분리 가능한 데이터
데이터가 완벽하게 선형 분리되면 로그 손실은 w 를 무한히 키울수록 계속 줄어든다. 가중치가 발산한다. 그래서 실무 구현은 기본으로 L2 정규화를 건다. scikit-learn 의 LogisticRegression 도 기본 벌점이 L2 이고, 강도는 C(정규화 세기의 역수)로 조절한다.
직접 해 보기
공부 시간으로 합격 여부를 예측하는 1차원 예제다. 넘파이로 경사 하강법을 직접 돌린다.
import numpy as np
rng = np.random.default_rng(1)
# 공부 시간(x) -> 합격(1)/불합격(0)
x = np.r_[rng.normal(2, 1, 30), rng.normal(5, 1, 30)]
y = np.r_[np.zeros(30), np.ones(30)]
X = np.column_stack([np.ones_like(x), x])
sig = lambda z: 1 / (1 + np.exp(-z))
w = np.zeros(2)
for step in range(3000):
p = sig(X @ w)
w -= 0.1 * X.T @ (p - y) / len(y) # 로그 손실의 기울기
p = sig(X @ w)
eps = 1e-12
loss = -np.mean(y*np.log(p+eps) + (1-y)*np.log(1-p+eps))
acc = np.mean((p >= 0.5) == y)
print("가중치 [b, w]:", w.round(3), " 결정 경계 x =", round(-w[0]/w[1], 3))
print(f"로그 손실={loss:.3f} 정확도={acc:.3f}")
for h in (2, 3.5, 5):
print(f"x={h}: P(합격)={sig(w[0]+w[1]*h):.3f}")
실행 결과:
가중치 [b, w]: [-8.939 2.715] 결정 경계 x = 3.293
로그 손실=0.078 정확도=0.983
x=2: P(합격)=0.029
x=3.5: P(합격)=0.637
x=5: P(합격)=0.990
결정 경계는 공부 시간 약 3.3 시간이다. 두 무리의 평균(2, 5) 사이다. 중요한 것은 출력이 확률이라는 점이다. 3.5 시간은 경계를 겨우 넘어 63.7% 로, 5 시간은 99% 로 판단한다. 같은 “합격” 예측이라도 확신의 정도가 다르고, 이 숫자로 임계값을 업무에 맞게 조정할 수 있다.
코드의 갱신식 X.T @ (p - y) 가 위에서 본 기울기 (p − y)·x 그대로다.
현업에서는
- 확률이 곧 의사결정 입력이다. 대출 심사는 “부도 확률 3%” 를 받아 금리를 정한다. 이때는 확률이 실제 빈도와 맞는지(보정, calibration)가 정확도만큼 중요하다.
- 임계값은 0.5 가 아니어도 된다. 암 검진처럼 놓치는 비용이 크면 임계값을 낮춘다. 다음 글들의 평가 지표와 연결된다.
- 해석이 필요한 규제 영역에서 여전히 선호된다. 계수를 오즈비로 설명할 수 있기 때문이다.
- 피처 엔지니어링이 성능을 좌우한다. 선형 경계밖에 못 그리므로, 비선형 관계는 구간화나 상호작용 특징으로 넣어 줘야 한다.
확인 문제
- 로지스틱 회귀의 결정 경계(임계값 0.5)는 어떤 모양인가?
- 가중치 w = 0.7 의 의미를 오즈로 설명하라.
- 분류에 MSE 대신 로그 손실을 쓰는 이유 두 가지는?
- 학습 데이터가 완벽하게 선형 분리될 때 정규화 없이 학습하면 어떻게 되는가?
- 예제에서 x = 3.5 의 예측은 1 인데, 이것을 “확실히 합격” 이라고 보고하면 왜 문제인가?
풀이
- wᵀx = 0 인 초평면(1차원에서는 점, 2차원에서는 직선).
- 해당 특징이 1 증가하면 오즈가 e^0.7 ≈ 2.01 배가 된다.
- 최대우도 추정과 같아 확률적 해석이 명확하고, 시그모이드와 결합해 볼록 함수가 되어 최적화가 안정적이다.
- 손실을 계속 줄이려고 가중치 크기가 무한히 커진다. 확률이 0 과 1 로 극단화되고 수치적으로 불안정해진다.
- 확률이 63.7% 에 불과하다. 경계 근처라 틀릴 가능성이 상당하므로 확률을 함께 보고해야 한다.
더 읽을거리 (References)
- scikit-learn User Guide, Linear Models — Logistic regression
- G. James 외, An Introduction to Statistical Learning, 2nd ed., 4장 “Classification”. 책 사이트
- I. Goodfellow, Y. Bengio, A. Courville, Deep Learning, MIT Press, 6.2절(출력 단위와 교차 엔트로피). 온라인판