| 분류 | 포함 모델 | 설명 |
|---|---|---|
| 회귀 모델 | Linear, Ridge, Lasso, SVR, XGBoostRegressor 등 | 숫자 예측 목적 (ex. 집값) |
| 분류 모델 | Logistic, RandomForest, CatBoost, SVM, KNN 등 | 카테고리 분류 목적 (ex. 스팸 여부) |
| 비지도 학습 | KMeans, DBSCAN, PCA, TSNE 등 | 정답 없이 구조/군집/차원 탐색 |
| 앙상블 | RandomForest, GradientBoosting, Voting, Stacking 등 | 여러 모델 결합으로 성능 향상 |
| 신경망 기반 | MLPClassifier/Regressor | 기초적인 다층 퍼셉트론 모델 |
| 기타 도구 | DummyClassifier, GridSearchCV 등 | 비교, 평가, 튜닝용 |
| 타입 | 예시 | 이유 |
|---|---|---|
| 딥러닝 전용 | CNN, RNN, BERT | 일반 머신러닝보다는 딥러닝 범주 |
| 규칙 기반 시스템 | if-else, 룰 엔진 | 학습이 아닌 직접 규칙 지정 |
| 통계모델 | ARIMA, OLS | 통계적 추정 기반, 머신러닝과 다소 다름 |
| 모델명 | 설명 |
|---|---|
| Ridge Regression | 선형 회귀 + L2 정규화 (과적합 방지) |
| Lasso Regression | 선형 회귀 + L1 정규화 (불필요한 변수 제거) |
| ElasticNet | Ridge + Lasso 혼합 |
| SVR | SVM 기반 회귀 (마진 기반 회귀) |
| GradientBoostingRegressor | 부스팅 기반 회귀 (예측 성능 높음) |
| XGBoostRegressor / LightGBMRegressor | 고성능 부스팅 회귀 |
| KNeighborsRegressor | KNN의 회귀 버전 |
개념
특징
언제 사용하나?
사용 코드
from sklearn.linear_model import Ridge
model = Ridge(alpha=1.0)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
결과
[1.53, 2.15, 2.88, 3.02, 2.61]
개념
특징
언제 사용하나?
사용 코드
from sklearn.linear_model import Lasso
model = Lasso(alpha=0.1)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
결과
[1.47, 2.12, 2.76, 3.00, 2.54]
개념
특징
언제 사용하나?
사용 코드
from sklearn.linear_model import ElasticNet
model = ElasticNet(alpha=0.1, l1_ratio=0.5)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
결과
[1.50, 2.10, 2.80, 2.95, 2.57]
개념
특징
언제 사용하나?
X = [[-2], [-1], [0], [1], [2]]
y = [4, 1, 0, 1, 4] # 즉, y = x² 형태 이 데이터를 선형 회귀로는 맞출 수 없음 하지만 SVR(kernel='rbf') 을 사용하면 내부적으로 고차원 공간으로 옮겨서 다음처럼 동작X는 그대로지만, 내부적으로 변환한 후 선형 SVR 수행
결과적으로 비선형 회귀처럼 작동
| 커널 이름 | 설명 | 사용 예 |
|---|---|---|
'linear' | 선형 회귀 (직선) | 단순한 관계 |
'poly' | 다항 회귀 (곡선) | 곡선 형태 데이터 |
'rbf' | 가우시안 커널 (비선형 대응 가장 강력) | 대부분 비선형 상황에서 기본 선택 |
'sigmoid' | 신경망 유사 커널 | 실험적 사용 |
import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVR
# 비선형 데이터
X = np.linspace(-5, 5, 100).reshape(-1, 1)
y = np.sin(X).ravel()
# SVR with RBF kernel
svr_rbf = SVR(kernel='rbf', C=100, epsilon=0.1)
svr_rbf.fit(X, y)
y_pred = svr_rbf.predict(X)
# 시각화
plt.plot(X, y, label='True Function (sin)', color='gray')
plt.plot(X, y_pred, label='SVR with RBF Kernel', color='red')
plt.legend()
plt.title("비선형 회귀 with 커널 트릭")
plt.show()
C 파라미터의 뜻C는 오차에 대한 패널티의 크기(엄격함)를 결정하는 값
C 값이 클수록:
→ ε 범위를 벗어난 오차를 엄하게 처벌함
→ 오차를 줄이려는 경향이 강함 → 훈련 데이터에 더 민감함 → 과적합(overfitting) 위험 증가
C 값이 작을수록:
→ ε 범위를 벗어난 오차를 **관대하게 허용**함
→ **일반화 성능**이 좋아짐 (훈련 데이터에 덜 민감함) → **과소적합(underfitting) 가능성**
epsilon: 오차 무시 범위 (ε-튜브의 폭)예측값이 실제값과 ε 이내로 차이가 나면, 그 오차는 벌점 없이 무시
즉, SVR은 모든 오차를 줄이는 게 아니라, "ε만큼은 무시하고 넘어가자"는 방식
SVR(epsilon=0.1) # 0.1 만큼의 오차는 그냥 무시
ε=0.1 → 실제값과 예측값이 0.1보다 작게 차이 나면 "잘 맞춘 거"로 간주
ε가 작을수록 → 더 민감하게 맞추려 함
ε가 클수록 → 더 많은 오차를 무시 → 더 부드러운 모델
| epsilon 값 | 설명 | 예측 결과 |
|---|---|---|
| ε = 0.01 | 거의 모든 데이터 포인트를 감싸려 함 | 복잡하고 민감한 회귀선 |
| ε = 0.5 | 많은 오차를 무시하고, 부드럽고 단순한 회귀선 | 일반화 ↑ |
gamma: RBF 커널에서 거리의 영향력커널 함수(RBF 등)에서 하나의 데이터 포인트가 영향을 미치는 범위를 결정
RBF 커널 함수 정의:
→ gamma는 데이터 간의 거리 차이를 얼마나 민감하게 볼 것인가를 결정
| gamma 값 | 설명 | 결과 |
|---|---|---|
| 작은 값 (e.g. 0.01) | 넓은 범위의 데이터를 참고함 | 더 부드럽고 단순 |
| 큰 값 (e.g. 10) | 가까운 데이터에만 반응 | 복잡하고 민감 (과적합 가능) |
from sklearn.svm import SVR
SVR(kernel='rbf', gamma=0.1) # RBF 커널의 반응 범위 설정
gamma=0.1 → 곡선이 부드럽고 천천히 변화
gamma=100 → 아주 민감하게 휘어짐, 노이즈에 민감 → 과적합 가능
| 하이퍼파라미터 | 역할 | 영향 |
|---|---|---|
| C | ε를 벗어난 오차에 대한 패널티 강도 | ↑: 과적합 가능 / ↓: 과소적합 가능 |
| epsilon | 오차를 무시할 수 있는 범위 (ε-튜브 폭) | ↑: 더 많은 오차 무시 / ↓: 더 민감 |
| gamma | RBF 커널에서 영향 범위 (곡선의 유연성) | ↑: 복잡, 민감 / ↓: 단순, 부드러움 |
개념
특징
언제 사용하나?
사용 코드
from sklearn.ensemble import GradientBoostingRegressor
model = GradientBoostingRegressor(n_estimators=100)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
결과 예시
[1.55, 2.18, 2.89, 3.01, 2.65]
즉, n_estimators는 앙상블을 구성하는 기본 모델(보통 트리)의 개수를 의미“모델 안에 몇 개의 결정 트리(decision tree)를 만들 건지”를 뜻
| n_estimators 값 | 의미 | 장단점 |
|---|---|---|
| 작은 값 (e.g. 10~50) | 빠름, 계산 적음 | 예측 불안정, 성능 낮을 수 있음 |
| 기본값 (100) | 적절한 타협 | 성능/속도 균형 |
| 큰 값 (500~1000) | 더 안정적, 과적합 방지 가능 | 속도 느림, 메모리 사용 ↑ |
주의: 너무 많이 늘려도 성능은 크게 좋아지지 않고 시간만 오래 걸릴 수 있음
n_estimators가 쓰이는 대표 앙상블 모델들| 모델명 | 라이브러리 | 모델 종류 | n_estimators 의미 |
|---|---|---|---|
| RandomForestClassifier / Regressor | sklearn.ensemble | 배깅 (Bagging) | 생성할 결정 트리 수 |
| BaggingClassifier / Regressor | sklearn.ensemble | 배깅 | Base 모델 복사 수 |
| ExtraTreesClassifier / Regressor | sklearn.ensemble | 배깅 + 완전 랜덤 분할 | 매우 빠른 랜덤 포레스트 |
| GradientBoostingClassifier / Regressor | sklearn.ensemble | 부스팅 | 순차적 학습 단계 수 |
| AdaBoostClassifier / Regressor | sklearn.ensemble | 부스팅 | 약한 학습기의 수 |
| XGBClassifier / XGBRegressor | xgboost | 부스팅 | 트리 개수 |
| LGBMClassifier / LGBMRegressor | lightgbm | 부스팅 | 트리 개수 |
| CatBoostClassifier / Regressor | catboost | 부스팅 | 트리 개수 |
| HistGradientBoostingClassifier / Regressor | sklearn.ensemble | 고속 히스토그램 기반 부스팅 | 트리 개수 |
개념
특징
언제 사용하나?
사용 코드
from xgboost import XGBRegressor
model = XGBRegressor(n_estimators=100)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
결과 예시
[1.57, 2.21, 2.91, 3.03, 2.66]
개념
특징
언제 사용하나?
사용 코드
from lightgbm import LGBMRegressor
model = LGBMRegressor(n_estimators=100)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
결과
[1.56, 2.20, 2.88, 3.02, 2.64]
개념
특징
언제 사용하나?
사용 코드
from sklearn.neighbors import KNeighborsRegressor
model = KNeighborsRegressor(n_neighbors=5)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
결과
[1.49, 2.16, 2.77, 2.95, 2.60]
Random Forest는 여러 개의 결정 트리를 만들고 그 결과를 앙상블하여 예측하는 지도학습 모델
Bagging 기반 앙상블 기법이며, 과적합을 줄이고 예측 성능을 높이는 데 매우 강력.
| 용어 | 의미 |
|---|---|
| Tree | 결정 트리 (Decision Tree) 하나 |
| Forest | 여러 트리를 모은 숲 → 여러 모델의 앙상블 |
| Random | 트리를 만들 때 데이터와 피처를 랜덤하게 샘플링해서 다양성을 확보 |
원본 데이터에서 중복 허용 샘플링 (bootstrap)
각 샘플로 결정 트리 학습
예측 시:
- 분류: 트리들의 다수결
- 회귀: 트리들의 평균값
각 노드 분할 시 사용할 피처도 랜덤하게 선택 → 트리 간 상관성↓, 분산↓
이것이 "Random" Forest의 핵심 차별점
| 항목 | 분류 (RandomForestClassifier) | 회귀 (RandomForestRegressor) |
|---|---|---|
| 예측값 | 클래스 (0, 1, 2...) | 연속적인 수치 (실수) |
| 결합 방식 | 다수결 (majority vote) | 평균 (mean) |
| 출력 함수 | predict_proba(), predict() | predict() |
| 장점 | 단점 |
|---|---|
| 과적합에 강함 (트리 평균 덕분) | 모델 크기 큼 (트리 많음) |
| 특성 중요도 파악 가능 | 예측 해석 어려움 (블랙박스) |
| 높은 정확도 | 느릴 수 있음 (트리 많으면) |
| 결측치·스케일링 영향 적음 | 고차원 데이터선 느림 |
회귀일 때:
분류일 때:
여기서 Ti(x)T_i(x)Ti(x)는 i번째 결정 트리의 예측 결과
| 파라미터 | 설명 |
|---|---|
n_estimators | 트리의 개수 (많을수록 성능↑, 속도↓) |
max_depth | 각 트리의 최대 깊이 |
max_features | 노드 분할 시 고려할 최대 피처 수 |
bootstrap | 부트스트랩 샘플링 여부 (True 권장) |
random_state | 랜덤 시드 고정 |
n_jobs | 병렬 처리 사용 개수 (-1이면 모든 코어 사용) |
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
print("예측 결과:", model.predict(X_test))
print("정확도:", model.score(X_test, y_test))
from sklearn.ensemble import RandomForestRegressor
from sklearn.datasets import make_regression
X, y = make_regression(n_samples=1000, n_features=10, noise=10)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
model = RandomForestRegressor(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
from sklearn.metrics import r2_score
y_pred = model.predict(X_test)
print("R2 Score:", r2_score(y_test, y_pred))
import matplotlib.pyplot as plt
importances = model.feature_importances_
plt.bar(range(len(importances)), importances)
plt.title("Feature Importances")
plt.show()
→ 어떤 피처가 예측에 영향을 많이 줬는지 시각적으로 파악 가능
| 상황 | 이유 |
|---|---|
| 과적합 위험이 있는 트리 모델 | 여러 트리 평균으로 완화 |
| 해석보단 성능이 중요한 경우 | 앙상블이 정확도 높음 |
| 피처 수 많거나 이상치 있음 | 트리는 자동 처리에 강함 |
| 기본 베이스라인 모델로 | 튜닝 없이도 잘 동작함 |
| 항목 | 내용 |
|---|---|
| 분류 | RandomForestClassifier |
| 회귀 | RandomForestRegressor |
| 핵심 기법 | Bagging + Random Subspace |
| 결합 방식 | 다수결 or 평균 |
| 트리 수 | n_estimators로 설정 |
| 장점 | 과적합 방지, 성능 높음, 중요도 출력 |
| 단점 | 느림, 블랙박스 해석 어려움 |
Gradient Boosting은 약한 모델(보통 얕은 트리)을 순차적으로 학습시켜 오차를 줄여가는 방식의 앙상블 학습법
- Boosting: 모델을 순차적으로 쌓으며 보완
- Gradient: 오차(loss)의 기울기(gradient)를 따라 보완 방향을 결정
| 분류 기준 | 포함 여부 | 설명 |
|---|---|---|
| 지도학습 | ✅ | X → y 예측 |
| 앙상블 학습 | ✅ | 여러 모델을 결합 |
| Boosting 방식 | ✅ | 순차적으로 모델을 학습시킴 |
여러 약한 모델(weak learner)을 순차적으로 학습
각 모델은 이전 모델의 오차를 보완
최종 예측은 모든 모델의 예측을 가중 합
처음에는 단순한 예측 (예: 평균값)
오차(잔차 residual)를 계산
그 오차를 잘 예측하는 결정 트리 모델을 하나 학습
새 예측 = 기존 예측 + 학습된 트리의 출력
다시 오차 계산 → 또 트리 학습 → 반복…
: m번째 약한 모델 (보통 결정 트리)
: 학습률(learning rate)
: M번째 단계까지 누적된 최종 예측값
: i번째 데이터 샘플에 대한 손실 함수의 기울기 (잔차)
: 손실 함수 (예: MSE, 로그손실 등)
: 이전 단계까지의 누적 예측
→ 이 잔차를 예측하도록 (새로운 트리) 를 학습
→ 그래서 이름이 Gradient Boosting (기울기를 따라가며 개선)
| 장점 | 단점 |
|---|---|
| 높은 예측 성능 | 학습 시간 오래 걸림 |
| 다양한 손실 함수 사용 가능 | 과적합 가능성 있음 (너무 깊거나 많으면) |
| 특성 중요도 파악 가능 | 직관적 해석 어려움 |
| 회귀, 분류 모두 가능 | 하이퍼파라미터 튜닝 필요 |
| 파라미터 | 설명 |
|---|---|
n_estimators | 학습할 트리 개수 |
learning_rate | 학습률 (각 트리 기여 정도) |
max_depth | 트리의 최대 깊이 |
loss | 손실 함수 (회귀: MSE, 분류: log-loss 등) |
subsample | 샘플 일부만 학습 (과적합 방지용) |
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score
# 데이터 생성
X, y = make_regression(n_samples=1000, n_features=10, noise=15)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 모델 학습
model = GradientBoostingRegressor(n_estimators=100, learning_rate=0.1, max_depth=3)
model.fit(X_train, y_train)
# 예측 및 평가
y_pred = model.predict(X_test)
print("R2 Score:", r2_score(y_test, y_pred))
| 모델 | 특징 | 라이브러리 |
|---|---|---|
| Gradient Boosting (GBM) | 기본적인 부스팅 모델 | sklearn |
| XGBoost | 속도 향상 + 정규화 추가 | xgboost |
| LightGBM | 대용량 데이터에 특화, 속도 매우 빠름 | lightgbm |
| CatBoost | 범주형 자동 처리 | catboost |
| 상황 | 추천 설정 |
|---|---|
| 과적합 우려 | learning_rate ↓, n_estimators ↑ |
| 훈련 속도 느릴 때 | subsample < 1.0, max_depth ↓ |
| 변수 중요도 분석 | feature_importances_ 사용 |
| 파라미터 튜닝 | GridSearchCV or RandomizedSearchCV |
| 항목 | 설명 |
|---|---|
| 학습 방식 | 지도학습 + Boosting 앙상블 |
| 구성 모델 | 결정 트리(보통 작은 트리) |
| 결합 방식 | 잔차 보완식 순차 학습 |
| 최종 예측 | 모든 트리의 출력값 가중합 |
| 대표 구현 | GradientBoostingRegressor, GradientBoostingClassifier |
| 대표 라이브러리 | sklearn, xgboost, lightgbm, catboost |
XGBoost는 여러 약한 결정 트리를 순차적으로 학습하고, 잔차를 줄이도록 가중 합산하는
Boosting 기반의 고성능 앙상블 모델
Gradient Boosting에 정규화 + 병렬화 + 가지치기 + 결측처리를 더해 성능을 극대화한 형태
| 용어 | 의미 |
|---|---|
| Boost | 이전 모델이 틀린 걸 고쳐나가는 방식 (잔차 보정) |
| Gradient | 손실 함수의 기울기를 이용해 최적화 |
| eXtreme | 속도/성능 모두 향상 (병렬화 + 최적화) |
| Tree | 약한 학습기로 CART 트리 사용 |
각 트리는 이전까지의 예측값을 보정함
트리마다 분할 기준은 손실 감소 + 복잡도 규제를 같이 고려
학습은 다음과 같은 방식으로 이뤄짐:
| 항목 | 분류 (XGBClassifier) | 회귀 (XGBRegressor) |
|---|---|---|
| 예측값 | 클래스 | 연속 수치 |
| 손실 함수 | logloss / softmax | MSE / MAE |
| 결합 방식 | 클래스 확률 평균 후 최빈값 | 예측값 평균 |
| 출력 함수 | predict_proba() / predict() | predict() |
| 장점 | 단점 |
|---|---|
| 높은 정확도 | 구조 복잡함 |
| 과적합 방지 정규화 내장 | 해석 어려움 |
| 결측치 자동 처리 | 학습 시간 긴 경우 있음 |
| 다양한 손실 함수 지원 | 파라미터 많아 튜닝 필요 |
| 빠른 학습 (C++ 백엔드) | 기울기/헤시안 계산 필요 |
| 사용 조건 | 이유 |
|---|---|
| 예측 정확도가 매우 중요 | 캐글 1위 모델 다수 |
| 과적합 걱정되는 복잡한 데이터 | 정규화 + 가지치기 내장 |
| 대용량 데이터 | 병렬 처리, GPU 지원 |
| 결측값 존재 | 자동 처리 지원 |
| 분류, 회귀, 랭킹 문제 | 전용 손실 함수 지원 |
기본 구조는 Gradient Boosting과 동일:
→ 오차를 줄이도록 새로운 트리를 순차적으로 추가
하지만 XGBoost는 다음을 추가함:
- 정규화 (L1/L2)
- 가지치기 (Pruning)
- 병렬 최적화
- 2차 테일러 근사
| 파라미터 | 의미 | 설명 |
|---|---|---|
n_estimators | 트리 수 | 많을수록 복잡도 ↑ |
learning_rate | 학습률 | 작을수록 안정적 |
max_depth | 트리 깊이 | 깊을수록 복잡 |
subsample | 샘플 비율 | 과적합 방지 |
colsample_bytree | 피처 비율 | 다양성 증가 |
gamma | 최소 분할 손실 | 가지치기 기준 |
reg_alpha, reg_lambda | L1/L2 정규화 | 과적합 방지 |
python
복사편집
from xgboost import XGBRegressor
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score
X, y = make_regression(n_samples=1000, n_features=10, noise=15)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
model = XGBRegressor(
n_estimators=100,
learning_rate=0.1,
max_depth=4,
random_state=42
)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print("R2 Score:", r2_score(y_test, y_pred))
python
복사편집
from xgboost import XGBClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
clf = XGBClassifier(
n_estimators=100,
max_depth=3,
learning_rate=0.1,
use_label_encoder=False,
eval_metric='logloss'
)
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
print("Accuracy:", accuracy_score(y_test, y_pred))
from xgboost import plot_importance
import matplotlib.pyplot as plt
plot_importance(model)
plt.title("Feature Importance")
plt.show()
from xgboost import plot_importance
plot_importance(model)
plt.show()
| 모델 | 주요 평가 지표 | 의미 |
|---|---|---|
| XGBRegressor | R² Score | 1에 가까울수록 예측 정확 |
| XGBClassifier | Accuracy, F1 Score | 높을수록 분류 잘함 |
| 항목 | 회귀 | 분류 |
|---|---|---|
| 클래스 | XGBRegressor | XGBClassifier |
| 손실 함수 | MSE, MAE 등 | logloss, softmax |
| 출력 | 실수 값 | 클래스 확률 또는 라벨 |
| 예측 함수 | predict() | predict(), predict_proba() |
| 평가지표 | R², RMSE | Accuracy, F1, AUC |
| 항목 | 설명 |
|---|---|
| 핵심 모델 | XGBRegressor, XGBClassifier |
| 구조 | Gradient Boosting + 정규화 + 최적화 |
| 특징 | 정확도 높음, 과적합 억제, 속도 빠름 |
| 입력 | X, y → 지도학습 기반 |
| 핵심 수식 | 기울기 + 헤시안 기반 손실 최소화 |
| 활용 | 분류/회귀/랭킹 전반에서 성능 최강 |
"Light" = 빠르고 가볍다
"GBM" = Gradient Boosting Machine
Leaf-wise 성장 방식: 가장 손실이 큰 리프 노드만 분기 → 더 정확
Histogram 기반 학습: 연산량 줄이기 → 더 빠름
GOSS: 중요한 데이터 위주로 학습
EFB: 희소 feature들을 묶어 처리
| 조건 | 설명 |
|---|---|
| 대규모 데이터셋 | 수십~수백만 행 이상의 테이블 |
| 고차원 데이터 | 수천 개 이상의 컬럼 |
| 희소 데이터 | 원-핫 인코딩된 데이터, 결측치가 많은 데이터 |
| 속도가 중요한 경우 | 실시간 추천 시스템, 대용량 서비스 등 |
이진 분류: 예) 스팸 이메일 분류, 암/정상 진단
다중 분류: 예) 손글씨 숫자 분류
회귀: 예) 집값 예측, 수요 예측
랭킹: 예) 검색 결과 정렬, 추천 시스템
이상치 탐지
LightGBM은 트리를 키울 때 전체 트리의 depth를 일정하게 늘리는 것이 아니라,
가장 손실(loss)을 많이 줄일 수 있는 리프 노드 하나만 골라 분기합니다.
이 방식의 장점은 더 정밀한 학습이 가능하다는 것이고,
단점은 트리가 불균형하게 커져서 과적합 가능성이 높아진다는 것입니다.
LightGBM은 feature 값을 일정 구간(bin)으로 나눠서 히스토그램을 만들고,
이 히스토그램을 기반으로 분할 기준을 정합니다.
예를 들어, 연속적인 수치형 feature를 256개 구간으로 나누면,
원래 값들이 정수든 실수든 구간(bin) 번호로 대체하여 처리하므로 훨씬 빠릅니다.
Gradient 값이 큰 샘플은 모델이 틀린 예측을 한 것이므로 중요도가 높습니다.
LightGBM은 Gradient 값이 큰 샘플은 모두 포함하고, 작은 값의 샘플은 일부만 샘플링해서 학습에 사용합니다.
즉, 중요한 정보는 유지하면서 학습 속도를 크게 줄이는 전략입니다.
많은 feature들이 희소한 경우, 예를 들어 one-hot encoding된 경우,
같은 row에서 둘 다 1이 될 일이 없는 feature들끼리 묶어서 하나처럼 처리합니다.
예) [0, 1, 0], [0, 0, 1] 같은 feature들 → 하나의 컬럼으로 압축
| 파라미터 | 설명 | 예시 |
|---|---|---|
num_leaves | 하나의 트리에서 사용할 최대 리프 노드 수 | 과적합 방지: 보통 31~100 |
max_depth | 트리의 최대 깊이 | 너무 깊으면 과적합 |
learning_rate | 학습률 | 작게 잡을수록 더 정밀 |
n_estimators | 트리의 개수 | 많을수록 성능↑ (시간↑) |
min_data_in_leaf | 리프 노드가 가져야 할 최소 데이터 수 | 과적합 방지 |
feature_fraction | 학습에 사용할 feature 비율 | 0.8이면 무작위 80% 사용 |
bagging_fraction | 학습에 사용할 샘플 비율 | 랜덤샘플링 효과 |
boosting_type | 부스팅 알고리즘 종류 | 기본값: ‘gbdt’ |
lambda_l1, lambda_l2 | 정규화 계수 (L1/L2) | 과적합 방지용 |
pip install lightgbm
import lightgbm as lgb
또는 GPU 지원 버전:
pip install lightgbm --install-option=--gpu
import lightgbm as lgb
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# 데이터 로드
X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 모델 정의
model = lgb.LGBMClassifier(
n_estimators=100,
num_leaves=31,
learning_rate=0.1,
max_depth=-1
)
# 학습
model.fit(X_train, y_train)
# 예측
y_pred = model.predict(X_test)
# 평가
print("정확도:", accuracy_score(y_test, y_pred))
정확도: 0.9649122807017544
→ 각 회귀 모델의 예측값을 새로운 입력 특성(feature)으로 간주하고, → 그것들을 바탕으로 최종 모델이 다시 예측하는 방식StackingRegressor는 여러 개의 다른 회귀 모델(base learners)을 조합하고,
그 결과를 다시 최종 메타 회귀 모델(meta regressor)이 학습하여 더 정확한 예측을 만드는 앙상블 회귀 기법
원본 입력 (X)
↓
┌─────────────┐
│ Base 모델 1 │──▶ 예측값1 ─┐
├─────────────┤ │
│ Base 모델 2 │──▶ 예측값2 ─┼─▶ [예측값1, 예측값2, ...] → 메타 모델 → 최종 예측값
├─────────────┤ │
│ Base 모델 3 │──▶ 예측값3 ─┘
└─────────────┘
Base 모델: 서로 다른 종류의 회귀 모델 (예: 결정 트리, KNN, SVR 등)
Meta 모델: Base 모델들의 예측값을 받아 다시 예측하는 회귀 모델 (보통 선형 모델이 좋음)
| 문제 유형 | 메타 모델 (보통 쓰는 것) |
|---|---|
| 분류 문제 (classification) | ✅ 로지스틱 회귀 (LogisticRegression) |
| 회귀 문제 (regression) | ✅ 릿지 회귀 / 선형 회귀 (RidgeCV / LinearRegression) |
| 상황 | 설명 |
|---|---|
| 다양한 회귀 모델의 예측을 조합하고 싶을 때 | 서로 다른 특성을 가진 모델이 잘 보완해줌 |
| 하나의 모델이 완벽하지 않을 때 | 서로 다른 편향을 가진 모델을 합치면 시너지 가능 |
| 캐글(Kaggle) 등 대회에서 성능 극대화 | 실제 우승 솔루션에서 자주 등장 |
from sklearn.ensemble import StackingRegressor
from sklearn.linear_model import RidgeCV
from sklearn.tree import DecisionTreeRegressor
from sklearn.neighbors import KNeighborsRegressor
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
# 1. 데이터 생성
X, y = make_regression(n_samples=1000, n_features=20, noise=0.1, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 2. Base 모델 정의
base_models = [
('tree', DecisionTreeRegressor(max_depth=5)),
('knn', KNeighborsRegressor(n_neighbors=10))
]
# 3. 메타 모델 정의 (보통 선형 회귀나 릿지 회귀 사용)
meta_model = RidgeCV()
# 4. StackingRegressor 정의
stacking = StackingRegressor(
estimators=base_models,
final_estimator=meta_model,
cv=5 # base 모델 훈련 시 교차검증용
)
# 5. 학습 및 예측
stacking.fit(X_train, y_train)
y_pred = stacking.predict(X_test)
# 6. 결과 확인
from sklearn.metrics import r2_score
print("R2 Score:", r2_score(y_test, y_pred))
| 파라미터 | 설명 |
|---|---|
estimators | 리스트 형태로 base 모델들을 정의 ((이름, 모델) 튜플) |
final_estimator | 메타 모델 (보통 간단한 회귀 모델 사용) |
cv | base 모델 학습 시 내부에서 사용할 교차검증 폴드 수 |
n_jobs | 병렬 처리용 (CPU 코어 병렬 실행) |
passthrough=True | 메타 모델에게 원본 X도 함께 전달 (성능 향상 가능) |
| 주의사항 | 설명 |
|---|---|
| Meta 모델은 너무 복잡하지 않게! | 보통 LinearRegression, RidgeCV 등 추천 |
| Base 모델은 서로 다른 성격의 모델로 구성 | 서로 다른 성능, 편향을 가진 모델을 조합해야 효과적 |
cv는 꼭 지정할 것 | 메타 모델이 과적합되지 않게 하기 위해 중요 |
원본 X도 함께 주고 싶다면 passthrough=True 사용 | 예측 성능이 더 좋아질 수 있음 |
StackingRegressor 안에 넣는 base 모델은 반드시 회귀 모델이어야 함.
최종 meta 모델도 회귀여야 함.
예시:
from sklearn.linear_model import Lasso
from sklearn.svm import SVR
from sklearn.ensemble import GradientBoostingRegressor
base = [
('lasso', Lasso(alpha=0.01)),
('svr', SVR(C=10))
]
meta = GradientBoostingRegressor(n_estimators=100)
model = StackingRegressor(estimators=base, final_estimator=meta, cv=5)
| 항목 | 내용 |
|---|---|
| 정의 | 여러 회귀 모델의 예측을 조합해 최종 회귀 예측을 수행하는 앙상블 모델 |
| 사용 라이브러리 | sklearn.ensemble.StackingRegressor |
| 장점 | 다양한 모델의 장점을 종합해 성능 극대화 |
| 구성 요소 | Base 모델들 + 메타 회귀 모델 |
| 추천 구조 | 서로 다른 성격의 base 모델 + 간단한 메타 모델 |
| 적합한 문제 | 일반적인 회귀, 캐글 대회, 성능이 중요한 프로젝트 |
개념Bagging = Bootstrap Aggregating
→ 여러 개의 회귀 모델을 무작위로 다르게 학습시키고,
그 결과를 평균해서 더 안정적이고 일반화된 예측을 만드는 앙상블 회귀 모델
훈련 데이터를 중복 허용 샘플링 (bootstrap) 으로 여러 개 생성
각 샘플에 별도의 회귀 모델 학습
예측값은 → 평균(회귀) 으로 결합
| 사용 시점 | 이유 |
|---|---|
| 단일 회귀 모델이 과적합되는 경우 | Bagging은 분산을 줄여 과적합을 완화함 |
| 예측값의 안정성이 중요한 경우 | 평균값을 사용해 노이즈에 덜 민감 |
| 비선형적이거나 복잡한 관계가 있는 데이터 | 결정 트리 기반 회귀 모델이 잘 작동함 |
| 모델 간 병렬 처리가 가능해야 할 때 | Base 모델이 독립적이므로 병렬화 쉬움 |
훈련 데이터 X
├─ bootstrap 샘플링 → 모델1 학습 → 예측1
├─ bootstrap 샘플링 → 모델2 학습 → 예측2
├─ bootstrap 샘플링 → 모델3 학습 → 예측3
...
└─ 예측 결과 평균 → 최종 예측값
DecisionTreeRegressor + BaggingRegressorfrom sklearn.ensemble import BaggingRegressor
from sklearn.tree import DecisionTreeRegressor
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score
# 1. 데이터 생성
X, y = make_regression(n_samples=1000, n_features=10, noise=20, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 2. 모델 정의
base_model = DecisionTreeRegressor()
bagging = BaggingRegressor(
base_estimator=base_model,
n_estimators=100, # 몇 개의 모델을 만들 것인지
bootstrap=True, # bootstrap 샘플링 여부
max_samples=1.0, # 각 모델에 사용할 샘플 비율
max_features=1.0, # 각 모델에 사용할 특성 비율
random_state=42,
n_jobs=-1 # 병렬 처리
)
# 3. 학습 및 평가
bagging.fit(X_train, y_train)
y_pred = bagging.predict(X_test)
print("R² Score:", r2_score(y_test, y_pred))
| 파라미터 | 설명 | 기본값 |
|---|---|---|
base_estimator | 사용할 회귀 모델 (예: DecisionTreeRegressor) | None → DecisionTree |
n_estimators | 앙상블할 모델 수 | 10 |
bootstrap | 샘플링 시 중복 허용 여부 | True |
max_samples | 각 base 모델에 줄 샘플 수 (비율 또는 정수) | 1.0 |
max_features | 각 모델에 사용할 특성 수 | 1.0 |
oob_score | OOB 평가 여부 (훈련에 쓰이지 않은 샘플로 검증) | False |
n_jobs | 병렬 처리할 코어 수 (-1: 전부 사용) | None |
random_state | 재현 가능성 위한 시드 | None |
| 항목 | 단일 모델 | BaggingRegressor |
|---|---|---|
| 예측 성능 | 불안정하거나 과적합 가능 | 더 안정적, 분산 감소 |
| 과적합 | 발생 가능 | 완화됨 |
| 노이즈에 민감 | 높음 | 낮음 |
| 병렬 처리 | ❌ | 가능 |
| 속도 | 빠름 | 느릴 수 있음 (n_estimators ↑) |
from sklearn.svm import SVR
from sklearn.linear_model import Ridge
from sklearn.neighbors import KNeighborsRegressor
BaggingRegressor(base_estimator=SVR())
BaggingRegressor(base_estimator=Ridge())
BaggingRegressor(base_estimator=KNeighborsRegressor())
단, 각 base 모델은 회귀 모델이어야 함!
bagging = BaggingRegressor(oob_score=True)
bagging.fit(X_train, y_train)
print("OOB R² Score:", bagging.oob_score_)
Bootstrap에서 빠진 샘플로 내부 검증
추가적인 검증 세트 없이도 성능을 간접 평가 가능
| 모델 | R² Score |
|---|---|
| DecisionTreeRegressor | 0.74 |
| BaggingRegressor | 0.86 (훨씬 향상) |
| 항목 | 설명 |
|---|---|
| 정의 | 여러 회귀 모델을 샘플링된 데이터로 학습시켜 평균하는 앙상블 |
| 대표 클래스 | sklearn.ensemble.BaggingRegressor |
| 기본 모델 | DecisionTreeRegressor (기본값) |
| 장점 | 과적합 완화, 일반화 성능 ↑, 병렬화 가능 |
| 사용 시점 | 단일 모델이 불안정하거나, 고차원 / 복잡한 데이터 |
여러 개의 서로 다른 회귀 모델들의 예측값을 평균해서
최종 예측값을 계산하는 앙상블 모델
- 분류에서는
VotingClassifier가 다수결(Majority voting)을 사용- 회귀에서는
VotingRegressor가 예측값의 평균(Averaging) 을 사용
X 입력
↓
┌──────────────┐
│ 모델1 예측 │ → 예측값1
│ 모델2 예측 │ → 예측값2
│ 모델3 예측 │ → 예측값3
└──────────────┘
↓
최종 예측 = (예측1 + 예측2 + 예측3) / 3| 사용 조건 | 이유 |
|---|---|
| 다양한 회귀 모델을 조합하고 싶을 때 | 서로 다른 모델 특성이 시너지를 낼 수 있음 |
| 단순하면서 안정적인 앙상블을 원할 때 | 평균만 사용하므로 과적합 위험 낮음 |
| Boosting처럼 순차 학습이 필요 없는 구조 | 병렬화 가능하고 빠름 |
from sklearn.ensemble import VotingRegressor
from sklearn.linear_model import Ridge
from sklearn.tree import DecisionTreeRegressor
from sklearn.svm import SVR
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
# 데이터 생성
X, y = make_regression(n_samples=1000, n_features=20, noise=15, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 모델 정의
model1 = Ridge(alpha=1.0)
model2 = DecisionTreeRegressor(max_depth=5)
model3 = SVR()
# VotingRegressor 정의
voting_model = VotingRegressor(estimators=[
('ridge', model1),
('tree', model2),
('svr', model3)
])
# 학습 및 예측
voting_model.fit(X_train, y_train)
y_pred = voting_model.predict(X_test)
| 파라미터 | 설명 |
|---|---|
estimators | 사용할 회귀 모델 리스트 (이름, 모델) 튜플 |
n_jobs | 병렬 처리 (기본: None, 전체 CPU: -1) |
weights | 각 모델의 가중치 지정 가능 |
VotingRegressor(
estimators=[('ridge', model1), ('tree', model2), ('svr', model3)],
weights=[1, 2, 1] # tree 모델에 더 높은 비중
)비중 조정해서 더 신뢰하는 모델에 가중치 부여 가능
| 항목 | VotingRegressor | BaggingRegressor | StackingRegressor |
|---|---|---|---|
| 예측 방식 | 평균 | 각 모델 독립 학습 후 평균 | 메타 모델이 다시 예측 |
| 모델 다양성 | 높음 (여러 모델 가능) | 보통 같은 모델 | 가장 다양함 |
| 학습 방식 | 병렬, 단순 | 병렬, 부트스트랩 | 병렬 + 메타 학습 |
| 해석력 | 높음 | 중간 | 낮음 |
| 성능 | 보통 | 안정적 | 가장 높을 수 있음 |
| 항목 | 내용 |
|---|---|
| 용도 | 여러 회귀 모델의 예측 평균값을 사용해 안정적 예측 |
| 구조 | 병렬 예측 + 단순 평균 |
| 라이브러리 | sklearn.ensemble.VotingRegressor |
| 추천 사용 상황 | 단순하고 빠른 앙상블, baseline 비교할 때 유용 |
| 모델명 | 설명 |
|---|---|
| RidgeClassifier | 선형 분류 + L2 정규화 |
| GradientBoostingClassifier | 순차적 학습으로 성능 향상 |
| XGBoostClassifier | Kaggle 1등 단골 모델, 빠르고 강력 |
| LightGBMClassifier | 대용량 데이터에 매우 빠름 |
| CatBoostClassifier | 범주형 데이터 자동 처리 가능 |
| BaggingClassifier | 트리 기반 Bagging 앙상블 |
| VotingClassifier | 여러 모델의 결과를 투표로 결정 |
| StackingClassifier | 여러 모델을 층으로 쌓아 결합 |
개념
특징
언제 사용하나?
사용 코드
from sklearn.linear_model import RidgeClassifier
model = RidgeClassifier()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
결과
[0, 1, 2, 2, 1]
개념
특징
과적합에 비교적 강함
성능이 우수하지만 학습 속도는 느린 편
하이퍼파라미터가 많아 튜닝 필요
언제 사용하나?
복잡한 분류 문제
과적합 방지 + 높은 예측력이 필요한 경우
LogisticRegression, 트리 기반 모델보다 더 강력한 모델이 필요할 때
사용 코드
from sklearn.ensemble import GradientBoostingClassifier
model = GradientBoostingClassifier(n_estimators=100, learning_rate=0.1)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
🔹 결과 예시
[1, 0, 0, 2, 2]
개념
특징
gamma, lambda, eta)언제 사용하나?
사용 코드
from xgboost import XGBClassifier
model = XGBClassifier(n_estimators=100, use_label_encoder=False, eval_metric='mlogloss')
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
🔹 결과 예시
[1, 0, 2, 2, 1]
개념
특징
언제 사용하나?
사용 코드
from lightgbm import LGBMClassifier
model = LGBMClassifier(n_estimators=100)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
🔹 결과 예시
[0, 1, 1, 2, 0]
개념
특징
언제 사용하나?
사용 코드
from catboost import CatBoostClassifier
model = CatBoostClassifier(verbose=0)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
🔹 결과 예시
[1, 0, 1, 1, 2]
개념
특징
언제 사용하나?
사용 코드
from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier
base_model = DecisionTreeClassifier()
model = BaggingClassifier(base_estimator=base_model, n_estimators=10)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
결과 예시
[0, 1, 1, 2, 1]
개념
특징
Logistic + Tree + SVM 조합 추천hard vs soft voting 선택 가능언제 사용?
사용 코드
from sklearn.ensemble import VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC
model1 = LogisticRegression()
model2 = DecisionTreeClassifier()
model3 = SVC(probability=True)
ensemble = VotingClassifier(
estimators=[('lr', model1), ('dt', model2), ('svc', model3)],
voting='soft'
)
ensemble.fit(X_train, y_train)
y_pred = ensemble.predict(X_test)
🔹 결과 예시
[1, 0, 2, 2, 1]
개념
특징
언제 사용?
사용 코드
from sklearn.ensemble import StackingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC
base_models = [
('dt', DecisionTreeClassifier()),
('svc', SVC(probability=True))
]
meta_model = LogisticRegression()
stack = StackingClassifier(estimators=base_models, final_estimator=meta_model)
stack.fit(X_train, y_train)
y_pred = stack.predict(X_test)
결과 예시
[0, 1, 1, 2, 2]
전체적인 코드 순서
base_models →
meta_model →
StackingClassifier(estimators=base_models, final_estimator=meta_model)
| 파라미터 이름 | 하는 일 | 예시 |
|---|---|---|
estimators | 기본 모델들(base learners) 정의 | 여러 모델들의 이름 + 인스턴스 리스트 |
final_estimator | 메타 모델 정의 | base 모델의 예측 결과를 받아 최종 판단하는 모델 |
estimators : 여러 개의 모델(로지스틱, 트리, SVM 등)이 원래 입력 데이터를 보고 각각 예측하는 역할
final_estimator : 각 모델이 낸 예측값을 모아서 최종적으로 “이게 정답이다!” 라고 판단하는 역할
| 문제 유형 | 메타 모델 (보통 쓰는 것) |
|---|---|
| 분류 문제 (classification) | ✅ 로지스틱 회귀 (LogisticRegression) |
| 회귀 문제 (regression) | ✅ 릿지 회귀 / 선형 회귀 (RidgeCV / LinearRegression) |
| 모델명 | 설명 |
|---|---|
| KMeans | 대표적인 군집화 알고리즘 |
| DBSCAN | 밀도 기반 클러스터링 |
| PCA | 차원 축소 (주성분 분석) |
| TSNE, UMAP | 시각화용 고급 차원 축소 |
| IsolationForest | 이상치 탐지 (비지도) |
KMeans는 데이터를 K개의 군집으로 분할하는 알고리즘
각 군집은 하나의 중심점(centroid)을 가지고, 데이터는 이 중심점에 가장 가까운 군집에 속함
반복적으로 중심점과 군집을 재조정하면서 최적화
임의로 K개의 중심점(centroids)을 선택
각 데이터를 가장 가까운 중심점에 할당
각 군집의 평균값으로 중심점 업데이트
중심점 변화가 거의 없거나 최대 반복 횟수 도달 시 종료
데이터를 명확히 K개의 그룹으로 나눌 수 있다고 가정할 때
데이터의 중심이 존재하는 군집이 있을 때
고객 세분화, 문서 분류, 이미지 압축 등에서 자주 사용
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
# 예시 데이터 생성
X, _ = make_blobs(n_samples=300, centers=4, cluster_std=0.6, random_state=42)
# KMeans 모델 생성 및 학습
kmeans = KMeans(n_clusters=4, random_state=42)
kmeans.fit(X)
# 예측 결과
y_pred = kmeans.predict(X)
# 군집 중심점
centroids = kmeans.cluster_centers_
# 시각화
plt.scatter(X[:, 0], X[:, 1], c=y_pred, cmap='viridis', alpha=0.6)
plt.scatter(centroids[:, 0], centroids[:, 1], s=200, c='red', marker='X', label='Centroids')
plt.title('KMeans Clustering')
plt.legend()
plt.show()
산점도 위에 각 데이터 포인트는 색깔로 군집이 구분됨
빨간 X는 각 군집의 중심 (centroid)
군집이 잘 나눠진 형태일 경우, 비슷한 성향의 데이터를 같은 군집으로 묶음
| 항목 | 설명 |
|---|---|
| 장점 | 단순, 빠름, 대규모 데이터에 잘 작동 |
| 단점 | K를 사전에 알아야 함, 이상치에 민감 |
| 가정 | 각 군집이 원형이고, 같은 분산을 가짐 |
inertias = []
for k in range(1, 10):
km = KMeans(n_clusters=k, random_state=42)
km.fit(X)
inertias.append(km.inertia_) # 군집 내 거리합 (작을수록 군집 잘 됨)
plt.plot(range(1, 10), inertias, marker='o')
plt.title('Elbow Method')
plt.xlabel('K')
plt.ylabel('Inertia')
plt.show()
그래프에서 급격히 감소하다가 완만해지는 지점이 최적의 K
DBSCAN은 밀도 기반 군집화 알고리즘
밀도가 높은 지역은 군집으로 묶고, 밀도가 낮은 지역은 이상치(노이즈)로 처리
어떤 점의 주변 반경(ε, 입실론) 안에 최소 n개의 이웃(min_samples)이 있으면, 이 점은 코어 포인트(Core Point) 라고 부름.
코어 포인트 주변에 연결된 점들을 같은 군집으로 묶음.
밀도가 낮은 지역에 있는 점들은 노이즈 (Noise) 로 간주됨.
| 사용 시점 | 이유 |
|---|---|
| 클러스터 개수를 모르거나 정하기 어렵다 | DBSCAN은 군집 수를 자동으로 결정해줘 |
| 복잡한 모양의 군집이 있을 때 | KMeans는 원형 군집만 잘 나누지만, DBSCAN은 자유로운 모양 가능 |
| 이상치를 잘 처리하고 싶을 때 | 노이즈 포인트를 따로 분류해줌 |
| 파라미터 | 설명 |
|---|---|
eps | 점 주변의 반경 (이 거리 안에 몇 개의 점이 있는지 봄) |
min_samples | eps 거리 내에 있어야 할 최소 이웃 수 (이 수 이상이면 Core Point) |
from sklearn.datasets import make_moons
from sklearn.cluster import DBSCAN
import matplotlib.pyplot as plt
import numpy as np
# 데이터 생성 (복잡한 모양)
X, _ = make_moons(n_samples=300, noise=0.05, random_state=0)
# DBSCAN 모델 생성 및 학습
db = DBSCAN(eps=0.2, min_samples=5)
y_db = db.fit_predict(X)
# 시각화
plt.scatter(X[:, 0], X[:, 1], c=y_db, cmap='plasma', s=50)
plt.title("DBSCAN Clustering Result")
plt.xlabel("X")
plt.ylabel("Y")
plt.show()
같은 색깔 → 같은 군집
1로 라벨링된 점들 → 노이즈로 간주된 점들
DBSCAN은 fit_predict()로 바로 군집 번호를 예측
1은 이상치(노이즈)로 판별된 데이터 포인트를 의미
array([0, 1, 1, 0, 0, 0, 1, 1, -1, 1, 1, 0, 0, ...])
0, 1 → 군집 번호
1 → 노이즈
| 장점 | 단점 |
|---|---|
| 군집 수 자동 결정 (K 설정 X) | eps와 min_samples 설정이 어렵고 민감함 |
| 이상치 탐지 기능 포함 | 고차원에서는 성능 저하 가능 |
| 비원형, 복잡한 모양도 잘 클러스터링 | 밀도가 균일하지 않으면 잘 안 나뉨 |
from sklearn.neighbors import NearestNeighbors
neigh = NearestNeighbors(n_neighbors=5)
nbrs = neigh.fit(X)
distances, indices = nbrs.kneighbors(X)
# 거리 정렬 후 시각화
distances = np.sort(distances[:, 4])
plt.plot(distances)
plt.title("K-distance Graph (5-NN)")
plt.xlabel("Points")
plt.ylabel("5th Nearest Distance")
plt.show()
꺾이는 지점(엘보우)을 eps 값으로 사용하면 좋음.
| 항목 | 내용 |
|---|---|
| 군집 수 지정? | 불필요 |
| 이상치 감지 | 가능 |
| 자유로운 군집 모양 | 가능 |
| 복잡한 데이터 | 매우 적합 |
| 대표 활용 | 지리정보(위치기반), 이상탐지, 이미지 분석 |
PCA는 고차원 데이터를 저차원으로 줄이되, 데이터의 분산(정보)을 최대한 보존하는 방식의 차원 축소 기법
- PCA는 데이터를 회전시켜 가장 잘 퍼져 있는 방향(=주성분)을 찾아
- 원래 데이터의 축 대신 새 축(주성분 축)으로 데이터를 표현
x축 →
⬛ ⬛
⬛ ⬛
⬛
⬛ ⬛
⬛ ⬛
↖ 주성분 방향 (제일 넓게 퍼진 방향)
→ PCA는 이 대각선 방향을 새로운 x축으로 잡고, 이 위에 데이터를 투영해서 1차원으로 줄이는 거예요.| 상황 | 설명 |
|---|---|
| 피처 수가 너무 많아서 연산이 느릴 때 | → 불필요한 정보를 줄이고 빠르게 처리 가능 |
| 데이터 시각화가 필요할 때 | → 2D, 3D로 축소하여 시각화 |
| 머신러닝 전처리로 과적합 줄이고 싶을 때 | → 불필요한 노이즈 제거 |
| 피처 간 상관관계가 높을 때 | → 새로운 축은 서로 직교함 (상관 X) |
데이터 정규화 (평균 0, 분산 1)
→ StandardScaler 등으로 먼저 스케일 조정
공분산 행렬 계산Cov(X)=n−11XTX
Cov(X)=1n−1XTX\text{Cov}(X) = \frac{1}{n-1} X^T X
→ 어떤 방향으로 데이터가 가장 잘 퍼져 있는지를 확인
고유값 분해 (Eigen Decomposition)
→ 공분산 행렬을 분해해서 고유값 + 고유벡터를 얻음
→ 고유벡터가 주성분 방향, 고유값은 분산의 크기
고유값이 큰 순으로 주성분 선택
→ 정보를 많이 담은 축부터 순서대로 사용
원 데이터를 주성분 축으로 투영 (변환)
→ 차원 축소된 새로운 데이터 완성
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
# 1. 데이터 로딩
data = load_iris()
X = data.data
y = data.target
# 2. 스케일링 (PCA 전에 꼭 필요!)
X_scaled = StandardScaler().fit_transform(X)
# 3. PCA 적용 (2차원으로 축소)
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
# 4. 시각화
plt.figure(figsize=(8,6))
plt.scatter(X_pca[:, 0], X_pca[:, 1], c=y, cmap='viridis')
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.title('PCA - Iris Dataset')
plt.colorbar(label='Target')
plt.show()
X_pca.shape → (150, 2) ← 원래는 4차원 → 2차원으로 줄어듦
시각화: 군집별로 잘 분리된 결과 확인 가능 (꽃 종류에 따라 나뉨)
| 속성 / 메서드 | 설명 |
|---|---|
pca.components_ | 주성분 축 (고유벡터들) |
pca.explained_variance_ | 각 주성분의 고유값 (분산 크기) |
pca.explained_variance_ratio_ | 각 주성분이 설명하는 전체 분산의 비율 |
pca.singular_values_ | 특이값 분해 시 사용하는 값 |
fit() | 주성분 방향 계산만 함 |
transform() | 이미 계산된 축으로 데이터 변환 |
fit_transform() | 위 둘을 한 번에 실행 |
explained_variance_ratio_로 몇 개의 차원만 쓸지 결정 가능!print(pca.explained_variance_ratio_)
print(sum(pca.explained_variance_ratio_)) # 총 분산 설명량
예: [0.72, 0.23] → 총 95% 설명 → 2개만 써도 충분!
??????????????
> pca.explained_variance_ratio_는
>
>
> 각 **주성분 축(PC1, PC2, …)** 이 **원본 데이터의 분산을 얼마나 설명하는지를 비율로** 나타낸 거야.
>
---
## 예시
원래 데이터가 **4차원**이었다고 해보자.
그럼 PCA는 이렇게 물어봐요:
> "이 4개의 축(컬럼)을 새로운 축으로 바꿔서, **정보(분산)**를 압축하고 싶은데...
>
>
> 그럼 새로 만든 축(PCA 축)들이 원래 정보를 **얼마나 잘 담고 있는지** 확인해보자!"
>
---
## 실제 출력
```python
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_iris
data = load_iris()
X = data.data
# 표준화 → PCA 필수!
X_scaled = StandardScaler().fit_transform(X)
# PCA 전체 축 계산 (4개 원래 차원 전부)
pca = PCA()
X_pca = pca.fit_transform(X_scaled)
print(pca.explained_variance_ratio_)
print(sum(pca.explained_variance_ratio_))
```
출력 예:
```
[0.72962445 0.22850762 0.03668922 0.00517871]
0.9999999999999999
```
---
### 이게 무슨 뜻이야?
| 주성분 축 (PC) | 설명 비율 (%) |
| --- | --- |
| PC1 (1번째 축) | **72.96%** |
| PC2 (2번째 축) | **22.85%** |
| PC3 | 3.66% |
| PC4 | 0.52% |
| **총합** | 100% (모든 정보 포함) |
---
## 핵심 포인트! 왜 중요한가?
> 주성분들은 정보를 많이 담은 순서대로 정렬돼 있어요.
>
>
> 즉,
>
- **PC1이 가장 중요**
- **PC2는 그 다음**
- …
- **PC4는 거의 쓸모 없음**
---
## 그래서 `n_components`를 어떻게 결정해?
예를 들어:
```python
pca = PCA(n_components=2)
```
이렇게 하면:
→ **PC1 + PC2만 사용**
→ 위 예에서 72.96% + 22.85% = **약 95.8%** 정보 유지됨
즉! **2개 축만 사용해도 거의 모든 정보(95%)가 보존되는 것!**
그래서:
```python
print(pca.explained_variance_ratio_)
print(sum(pca.explained_variance_ratio_)) # 총 분산 설명량
```
이걸 출력해서 "**몇 개의 차원을 쓰면 충분한가?**" 판단하는 데 쓰는 거야!
---
## 시각적으로 보면?
| PC1 | PC2 | PC3 | PC4 |
| --- | --- | --- | --- |
| ▓▓▓▓▓▓▓▓▓▓▓ | ▓▓▓▓▓▓▓▓ | ▓ | ░ |
| 72.9% | 22.8% | 3.7% | 0.5% |
✔️ → 그럼 우리는 "PC1 + PC2만 쓰면 되겠다!" 하고 `n_components=2`로 줄일 수 있어.
---
## 만약 자동으로 차원 수를 정하고 싶다면?
```python
pca = PCA(n_components=0.95)
```
이렇게 쓰면:
→ **자동으로 95% 이상 분산을 설명하는 최소한의 차원 수만 유지!**
→ 사람이 직접 2, 3 이렇게 안 정해도 됨
---
## 정리 요약
| 용어 | 의미 |
| --- | --- |
| `explained_variance_ratio_` | 각 PCA 축이 **전체 정보(분산)** 중 얼마나 설명하는지 비율 |
| `sum(...)` | 총 몇 %의 정보를 보존하는지 확인 |
| `n_components=2` | 앞에서 중요한 두 개의 축만 쓰겠다 |
| `n_components=0.95` | 전체 분산의 95% 이상 설명하는 축을 자동으로 선택 |
| 사용 상황 | 설명 |
|---|---|
| 시각화 | 고차원 데이터를 2D로 축소해서 plot |
| 전처리 | 차원을 줄이고 모델 성능 향상 |
| 노이즈 제거 | 주성분만 남기고 나머지는 버려서 잡음 제거 |
| 얼굴 인식 | Eigenface 기법도 PCA 기반 |
| 주의 사항 | 이유 |
|---|---|
꼭 StandardScaler 등으로 정규화 먼저 | 변수 스케일이 다르면 PCA가 왜곡됨 |
| 너무 많은 차원을 줄이면 정보 손실 | explained_variance_ratio_로 얼마나 설명하는지 확인해야 함 |
| 해석이 어려움 | 주성분 축은 기존 피처와 다르기 때문에 의미 해석 어려움 |
| 항목 | 설명 |
|---|---|
| 이름 | PCA (Principal Component Analysis) |
| 목적 | 차원 축소 + 정보 최대 보존 |
| 내부 동작 | 공분산 → 고유값 분해 → 투영 |
| 핵심 개념 | 분산을 많이 가진 방향으로 축을 새로 정함 |
| 언제 사용 | 고차원 데이터, 시각화, 과적합 방지 |
| 필수 전처리 | StandardScaler 등으로 정규화 |
| 결과 해석 | explained_variance_ratio_로 정보 보존량 확인 |
| 알고리즘 | 설명 | 특징 |
|---|---|---|
| KMeans | 중심점 기반 군집화 | 단순, 빠름. 군집 수(k) 미리 정해야 함 |
| DBSCAN | 밀도 기반 클러스터링 | 이상치 탐지 포함, 클러스터 수 미정 |
| MeanShift | 밀도 기반 클러스터링 | 자동으로 클러스터 수 찾음 |
| Agglomerative Clustering | 계층적 군집화 | 덴드로그램 시각화 가능 |
| Spectral Clustering | 그래프 기반 클러스터링 | 복잡한 경계도 잘 나눔 |
| 알고리즘 | 군집 수 자동 | 이상치 감지 | 비선형 구조 | 빠름 | 복잡도 |
|---|---|---|---|---|---|
| KMeans | ❌ 필요 | ❌ 없음 | ❌ 약함 | ✅ 빠름 | 낮음 |
| DBSCAN | ✅ 자동 | ✅ 강함 | ✅ 강함 | ✅ 보통 | 중간 |
| MeanShift | ✅ 자동 | ❌ 약함 | ✅ 강함 | ❌ 느림 | 중간 |
| Agglomerative | ❌ (덴드로그램으로 조절) | ❌ 없음 | ✅ 강함 | ❌ 느림 | 중간 |
| Spectral | ❌ 필요 | ❌ 없음 | ✅ 매우 강함 | ❌ 느림 | 높음 |
KMeans
KMeans는 데이터를 K개의 군집으로 분할하는 알고리즘
각 군집은 하나의 중심점(centroid)을 가지고, 데이터는 이 중심점에 가장 가까운 군집에 속함
반복적으로 중심점과 군집을 재조정하면서 최적화
임의로 K개의 중심점(centroids)을 선택
각 데이터를 가장 가까운 중심점에 할당
각 군집의 평균값으로 중심점 업데이트
중심점 변화가 거의 없거나 최대 반복 횟수 도달 시 종료
데이터를 명확히 K개의 그룹으로 나눌 수 있다고 가정할 때
데이터의 중심이 존재하는 군집이 있을 때
고객 세분화, 문서 분류, 이미지 압축 등에서 자주 사용
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
# 예시 데이터 생성
X, _ = make_blobs(n_samples=300, centers=4, cluster_std=0.6, random_state=42)
# KMeans 모델 생성 및 학습
kmeans = KMeans(n_clusters=4, random_state=42)
kmeans.fit(X)
# 예측 결과
y_pred = kmeans.predict(X)
# 군집 중심점
centroids = kmeans.cluster_centers_
# 시각화
plt.scatter(X[:, 0], X[:, 1], c=y_pred, cmap='viridis', alpha=0.6)
plt.scatter(centroids[:, 0], centroids[:, 1], s=200, c='red', marker='X', label='Centroids')
plt.title('KMeans Clustering')
plt.legend()
plt.show()
산점도 위에 각 데이터 포인트는 색깔로 군집이 구분됨
빨간 X는 각 군집의 중심 (centroid)
군집이 잘 나눠진 형태일 경우, 비슷한 성향의 데이터를 같은 군집으로 묶음
| 항목 | 설명 |
|---|---|
| 장점 | 단순, 빠름, 대규모 데이터에 잘 작동 |
| 단점 | K를 사전에 알아야 함, 이상치에 민감 |
| 가정 | 각 군집이 원형이고, 같은 분산을 가짐 |
inertias = []
for k in range(1, 10):
km = KMeans(n_clusters=k, random_state=42)
km.fit(X)
inertias.append(km.inertia_) # 군집 내 거리합 (작을수록 군집 잘 됨)
plt.plot(range(1, 10), inertias, marker='o')
plt.title('Elbow Method')
plt.xlabel('K')
plt.ylabel('Inertia')
plt.show()
그래프에서 급격히 감소하다가 완만해지는 지점이 최적의 K
DBSCAN
DBSCAN은 밀도 기반 군집화 알고리즘
밀도가 높은 지역은 군집으로 묶고, 밀도가 낮은 지역은 이상치(노이즈)로 처리
어떤 점의 주변 반경(ε, 입실론) 안에 최소 n개의 이웃(min_samples)이 있으면, 이 점은 코어 포인트(Core Point) 라고 부름.
코어 포인트 주변에 연결된 점들을 같은 군집으로 묶음.
밀도가 낮은 지역에 있는 점들은 노이즈 (Noise) 로 간주됨.
| 사용 시점 | 이유 |
|---|---|
| 클러스터 개수를 모르거나 정하기 어렵다 | DBSCAN은 군집 수를 자동으로 결정해줘 |
| 복잡한 모양의 군집이 있을 때 | KMeans는 원형 군집만 잘 나누지만, DBSCAN은 자유로운 모양 가능 |
| 이상치를 잘 처리하고 싶을 때 | 노이즈 포인트를 따로 분류해줌 |
| 파라미터 | 설명 |
|---|---|
eps | 점 주변의 반경 (이 거리 안에 몇 개의 점이 있는지 봄) |
min_samples | eps 거리 내에 있어야 할 최소 이웃 수 (이 수 이상이면 Core Point) |
from sklearn.datasets import make_moons
from sklearn.cluster import DBSCAN
import matplotlib.pyplot as plt
import numpy as np
# 데이터 생성 (복잡한 모양)
X, _ = make_moons(n_samples=300, noise=0.05, random_state=0)
# DBSCAN 모델 생성 및 학습
db = DBSCAN(eps=0.2, min_samples=5)
y_db = db.fit_predict(X)
# 시각화
plt.scatter(X[:, 0], X[:, 1], c=y_db, cmap='plasma', s=50)
plt.title("DBSCAN Clustering Result")
plt.xlabel("X")
plt.ylabel("Y")
plt.show()
같은 색깔 → 같은 군집
1로 라벨링된 점들 → 노이즈로 간주된 점들
DBSCAN은 fit_predict()로 바로 군집 번호를 예측
1은 이상치(노이즈)로 판별된 데이터 포인트를 의미
array([0, 1, 1, 0, 0, 0, 1, 1, -1, 1, 1, 0, 0, ...])
0, 1 → 군집 번호
1 → 노이즈
| 장점 | 단점 |
|---|---|
| 군집 수 자동 결정 (K 설정 X) | eps와 min_samples 설정이 어렵고 민감함 |
| 이상치 탐지 기능 포함 | 고차원에서는 성능 저하 가능 |
| 비원형, 복잡한 모양도 잘 클러스터링 | 밀도가 균일하지 않으면 잘 안 나뉨 |
from sklearn.neighbors import NearestNeighbors
neigh = NearestNeighbors(n_neighbors=5)
nbrs = neigh.fit(X)
distances, indices = nbrs.kneighbors(X)
# 거리 정렬 후 시각화
distances = np.sort(distances[:, 4])
plt.plot(distances)
plt.title("K-distance Graph (5-NN)")
plt.xlabel("Points")
plt.ylabel("5th Nearest Distance")
plt.show()
꺾이는 지점(엘보우)을 eps 값으로 사용하면 좋음.
| 항목 | 내용 |
|---|---|
| 군집 수 지정? | 불필요 |
| 이상치 감지 | 가능 |
| 자유로운 군집 모양 | 가능 |
| 복잡한 데이터 | 매우 적합 |
| 대표 활용 | 지리정보(위치기반), 이상탐지, 이미지 분석 |
MeanShift
MeanShift는 데이터의 밀도(density)가 높은 방향으로 중심점을 이동시켜서 군집을 찾는 알고리즘
이름 그대로, “평균(Mean)” 방향으로 이동(Shift)” 한다는 의미!
각 데이터 포인트에서 주변 데이터를 살펴보고, 그들의 평균으로 계속 이동한다.
이 과정을 반복하면서 밀도가 높은 지점으로 중심이 이동 → 결국 데이터 밀집된 지점들이 군집의 중심점이 됨.
| 사용 상황 | 이유 |
|---|---|
| 군집 수를 사전에 정하기 어려운 경우 | MeanShift는 자동으로 군집 수를 결정함 |
| 데이터 밀집 정도가 중요할 때 | 중심이 자동으로 조정되어 밀도가 높은 지점을 중심으로 클러스터 형성 |
| 군집 모양이 복잡하거나 비선형적인 경우 | KMeans와 달리 원형이 아니어도 잘 작동함 |
| 파라미터 | 설명 |
|---|---|
bandwidth | 중심점이 이웃을 인식하는 반경 (kernel window size) |
| → 클수록 더 큰 지역 평균, 작을수록 세밀한 군집화 | |
bin_seeding | 빠른 계산을 위해 bin 기반 시드 사용 여부 (True로 하면 속도 ↑) |
bandwidth는 전체 밀도에 영향을 주는 핵심 하이퍼파라미터.
너무 크면 군집 수 적고, 너무 작으면 과도한 군집이 나옴.
from sklearn.cluster import MeanShift, estimate_bandwidth
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
# 예시 데이터 생성
X, _ = make_blobs(n_samples=300, centers=3, cluster_std=0.7, random_state=42)
# bandwidth 자동 추정
bandwidth = estimate_bandwidth(X, quantile=0.2)
# MeanShift 모델 정의
ms = MeanShift(bandwidth=bandwidth)
ms.fit(X)
# 결과
labels = ms.labels_
cluster_centers = ms.cluster_centers_
# 시각화
plt.scatter(X[:, 0], X[:, 1], c=labels, cmap='rainbow', s=50)
plt.scatter(cluster_centers[:, 0], cluster_centers[:, 1], c='black', s=200, marker='X', label='Centers')
plt.title('MeanShift Clustering')
plt.legend()
plt.show()
데이터 밀집도가 높은 지역 중심으로 자동 군집화
X 마커가 군집의 중심점 (centroid)
군집 수는 따로 지정하지 않아도 bandwidth에 따라 자동 결정됨
print("군집 수:", len(np.unique(labels)))
print("레이블:", labels[:10])
예시 출력:
군집 수: 3
레이블: [2 1 1 2 0 2 1 1 1 0]
| 장점 | 단점 |
|---|---|
| 군집 수 자동 결정 | 느림 (특히 데이터 많을 때) |
| 비원형, 복잡한 군집도 탐지 가능 | bandwidth 설정에 민감 |
| 이상치에 어느 정도 강함 | 고차원에서는 성능 저하 |
| 항목 | 설명 |
|---|---|
| 군집 수 자동 결정 | O |
| 이상치 감지 | 직접적으로는 안됨 |
| 군집 모양 | 자유로움 (비선형 가능) |
| 중심 계산 방식 | 주변 평균으로 이동 (Kernel 밀도 추정 기반) |
| 추천 용도 | GPS 기반 클러스터링, 이미지 분할, 복잡한 군집 탐색 |
Agglomerative Clustering(병합 계층적 군집화)
Agglomerative Clustering은 계층적 군집화(Hierarchical Clustering) 방식 중 하향식(Bottom-up) 접근법
처음에는 모든 데이터를 하나씩 따로 군집으로 시작하고, 점차 서로 가까운 군집끼리 병합해서 하나의 트리 구조(덴드로그램)를 형성
데이터들 간의 유사도(거리)를 기준으로 군집을 병합해 나가는 방식.
최종적으로 사용자가 자르는 지점에 따라 군집 수를 정할 수 있음.
모든 데이터 포인트를 자기 자신만 포함하는 군집으로 시작
가장 가까운 두 군집을 찾아 병합
이 과정을 하나의 군집이 남을 때까지 반복
군집 수는 최종 덴드로그램을 어디서 자르냐에 따라 정해짐
| 상황 | 이유 |
|---|---|
| 군집 수를 직접 조절하거나 시각적으로 판단하고 싶을 때 | 덴드로그램으로 군집 수를 결정 가능 |
| 군집 간의 관계나 계층적 구조가 있는 데이터 | 예: 문서 분류, 생물 분류, 유전자 분석 |
| KMeans처럼 중심점 가정이 어색한 경우 | 중심점 기반이 아님, 거리 기반이라 자유로움 |
| 파라미터 | 설명 |
|---|---|
n_clusters | 최종적으로 남길 군집 수 (덴드로그램 자르는 위치) |
linkage | 군집 간 거리 측정 방법 (ward, average, complete, single) |
affinity | 거리 계산 방식 (euclidean, manhattan 등) |
| linkage 종류 | 설명 |
|---|---|
ward | 군집 내 분산을 최소화 (기본값) |
average | 군집 간 모든 점의 평균 거리 |
complete | 군집 간 가장 먼 점끼리 거리 |
single | 군집 간 가장 가까운 점끼리 거리 (연결만 잘 돼도 묶임) |
from sklearn.datasets import make_blobs
from sklearn.cluster import AgglomerativeClustering
import matplotlib.pyplot as plt
import scipy.cluster.hierarchy as sch
# 예시 데이터 생성
X, _ = make_blobs(n_samples=200, centers=4, random_state=42)
# 덴드로그램 그리기
plt.figure(figsize=(10, 5))
dendro = sch.dendrogram(sch.linkage(X, method='ward'))
plt.title("Dendrogram")
plt.xlabel("Data Index")
plt.ylabel("Distance")
plt.show()
위 그래프에서 수직선이 길게 그려진 곳을 수평으로 자르면 군집 수가 정해짐. 예: 4개의 긴 가지 전에서 자르면 4개 군집.
# 군집 수 4개로 클러스터링
cluster = AgglomerativeClustering(n_clusters=4, affinity='euclidean', linkage='ward')
y_pred = cluster.fit_predict(X)
# 시각화
plt.scatter(X[:, 0], X[:, 1], c=y_pred, cmap='rainbow')
plt.title("Agglomerative Clustering Result")
plt.show()
각 군집은 색상으로 구분됨
덴드로그램으로 얼마나 유사한 군집끼리 먼저 병합됐는지 시각적으로 확인 가능
ward linkage는 KMeans와 비슷하게 원형 구조에 강함, complete, average는 다양한 모양 처리 가능
import numpy as np
print("군집 레이블:", np.unique(y_pred)) # → [0 1 2 3]
print("예측값 일부:", y_pred[:10])
| 장점 | 단점 |
|---|---|
| 덴드로그램으로 군집 수 조절 쉬움 | 큰 데이터셋엔 느림 (거리 계산이 많음) |
| 다양한 linkage 전략 사용 가능 | 거리 기반이라 고차원엔 어려움 |
| 비선형, 다양한 군집 가능 | 이상치에 다소 민감 |
| 항목 | 설명 |
|---|---|
| 군집 수 자동 결정 | 없음. 덴드로그램에서 수동 조정 필요 |
| 이상치 감지 | 직접 탐지 기능 없음 |
| 군집 모양 | 자유롭고 계층적 구조 탐색 가능 |
| 중심점 사용 | 없음 (거리 기반 병합) |
| 추천 용도 | 문서 분류, 유전자 분석, 소셜 네트워크 분석, 텍스트 마이닝 |
Spectral Clustering
## 개념
- 스펙트럴(Spectral)이란 말 그대로 “스펙트럼 = 고유값(eigenvalue)” 기반의 클러스터링 기법
- 데이터 간 유사도(similarity)로 만든 **그래프 구조**를 기반으로
→ 그래프의 라플라시안 행렬(Laplacian Matrix)을 분해한 뒤,
→ 얻은 고유벡터들을 새로운 축으로 사용해서 군집화
### 한마디로 요약
> 복잡한 구조의 데이터라도, 데이터 간 유사도만 정의할 수 있다면 그래프 형태로 표현해서, **비선형 구조**도 잘 클러스터링할 수 있음
>
---
## 작동 원리 요약
1. **유사도 행렬(Similarity Matrix)** 생성 (ex. 가우시안 커널, 거리 기반)
2. 이걸로 **그래프 라플라시안 행렬**을 만든 뒤,
3. **고유값 분해 (Eigen Decomposition)** 수행
4. 주요 고유벡터들을 새로운 좌표(저차원 특징 공간)로 사용
5. 이 특징 공간에서 **KMeans** 같은 방법으로 클러스터링 수행
---
## 언제 사용?
| 사용 상황 | 이유 |
| --- | --- |
| 데이터의 구조가 **비선형 경계**일 때 | KMeans처럼 단순한 원형 분리가 불가능한 경우 |
| **정확한 군집 수(K)를 알고 있고**, 복잡한 구조일 때 | 복잡한 경계의 데이터를 선형으로 잘 나눌 수 있음 |
| **이미 유사도나 거리 정보가 있는 경우** | 유사도 기반이므로 다양한 입력 가능 |
---
## 장점 & 단점
| 장점 | 단점 |
| --- | --- |
| 비선형 경계도 잘 나눔 | 계산 복잡도 높음 (대규모 데이터엔 느림) |
| 유사도 정의만 가능하면 매우 유연 | 클러스터 수 K 필요 |
| 라플라시안 그래프 기반 | 수학적으로 직관 어렵고 구현 비용 ↑ |
---
## Scikit-learn 사용 코드
```python
from sklearn.datasets import make_circles
from sklearn.cluster import SpectralClustering
import matplotlib.pyplot as plt
# 데이터 생성 (KMeans로는 분리 어려운 원형 구조)
X, _ = make_circles(n_samples=300, factor=0.5, noise=0.05, random_state=42)
# 스펙트럴 클러스터링 적용
sc = SpectralClustering(n_clusters=2, affinity='rbf', random_state=42)
y_sc = sc.fit_predict(X)
# 시각화
plt.scatter(X[:, 0], X[:, 1], c=y_sc, cmap='plasma', s=50)
plt.title("Spectral Clustering (비선형 구조 분리)")
plt.xlabel("X1")
plt.ylabel("X2")
plt.show()
```
---
## 예시 결과 해석
- **내부 원과 외부 원**이 잘 분리됨 → 일반적인 KMeans로는 불가능한 분리 구조
- Spectral은 데이터 간 **유사도 기반으로 변형된 공간에서 클러스터링**하기 때문에 이런 것도 가능
---
## 주요 파라미터 설명
| 파라미터 | 설명 |
| --- | --- |
| `n_clusters` | 군집 수 K |
| `affinity` | 유사도 방식 (`rbf`, `nearest_neighbors`, `precomputed`) |
| `assign_labels` | 클러스터링 방법 (`kmeans`, `discretize`) |
| `gamma` | RBF 커널 계수 (affinity='rbf'일 때 사용됨) |
---
## affinity 종류
| 방식 | 설명 |
| --- | --- |
| `rbf` | 가우시안 커널 기반 거리 유사도 (기본값) |
| `nearest_neighbors` | K-최근접 이웃 기반 그래프 생성 |
| `precomputed` | 유사도 행렬 직접 전달 |
---
## 예시 출력
```python
import numpy as np
print("클러스터 레이블:", np.unique(y_sc)) # → [0 1]
print("예측값 일부:", y_sc[:10])
```
---
## Spectral Clustering 요약
| 항목 | 내용 |
| --- | --- |
| 군집 수 자동 설정 | 안됨. K 필요 |
| 이상치 감지 | 지원 X |
| 장점 | 복잡한 형태 분리 가능 (비선형, 복잡한 구조에 매우 강함) |
| 핵심 기술 | 그래프 라플라시안 + 고유값 분해 + KMeans |
| 추천 사용 | 이미지 분할, 원형·비선형 데이터, 정형화 어려운 문제 구조 |
---
고차원 데이터를 저차원으로 압축하면서 핵심 정보 유지
| 알고리즘 | 설명 | 사용 용도 |
|---|---|---|
| PCA (주성분 분석) | 선형 차원 축소 | 설명력 유지, 속도 빠름 |
| t-SNE | 비선형 축소, 시각화에 특화 | 데이터 구조 시각화 |
| UMAP | 시각화 + 군집 유지 | t-SNE보다 빠름, 군집형태 보존 |
| TruncatedSVD | 희소 행렬용 PCA | LSA에 사용 (텍스트) |
| AutoEncoder | 신경망 기반 차원 축소 | 비선형 축소, 재구성 가능 |
PCA:
t-SNE:
UMAP:
TruncatedSVD:
CountVectorizer, TF-IDF 결과에 사용AutoEncoder:
- 비선형 구조를 잘 잡아내지만, 신경망 설계 필요
- 실험적 분석이나 이상치 탐지, 이미지/신호 복원에 적합
| 알고리즘 | 실제 사용 빈도 | 많이 쓰는 목적 |
|---|---|---|
| PCA | 매우 높음 | 데이터 전처리, 속도 개선, 노이즈 제거 |
| t-SNE | 중간 | 고차원 데이터 시각화 (특히 논문/보고서용) |
| UMAP | 증가 추세 | 시각화 + 군집 형태 분석 (t-SNE 대체) |
| TruncatedSVD | 높음 (텍스트) | 희소 텍스트 데이터의 차원 축소 (LSA) |
| AutoEncoder | 실험적/깊이 있는 분석 | 비선형 차원 축소, 재구성/이상탐지 |
일반적인 데이터 전처리: PCA
텍스트 (희소 행렬): TruncatedSVD
시각화: t-SNE, UMAP
딥러닝 기반 실험/연구: AutoEncoder
| 항목 | PCA (Principal Component Analysis) | TruncatedSVD (Truncated Singular Value Decomposition) |
|---|---|---|
| 목적 | 분산(설명력)이 큰 축을 기준으로 차원 축소 | SVD를 통해 주요 축만 남겨 차원 축소 |
| 입력 데이터 | Dense (밀집) 행렬에 적합 | Sparse (희소) 행렬도 처리 가능 |
| 특징 | 데이터를 평균 중심화(mean centering)함 | 평균 중심화하지 않음 (빠름, sparse 유지) |
| 주요 사용 | 수치 데이터 일반 차원 축소 | 텍스트 데이터(TF-IDF 등) 차원 축소 |
| 결과 해석 | 분산 보존 기준 주성분 생성 | SVD 기반 주성분, 직관적 해석 어려움 |
입력 행렬 XXX에서 평균 제거 (centering):Xcentered=X−Xˉ
Xcentered=X−XˉX_{\text{centered}} = X - \bar{X}
공분산 행렬 계산:Σ=n1X⊤X
Σ=1nX⊤X\Sigma = \frac{1}{n} X^\top X
고유값 분해 (Eigen Decomposition):Σ=VΛV⊤
Σ=VΛV⊤\Sigma = V \Lambda V^\top
고유값이 큰 순으로 일부 주성분 선택 → 저차원 투영
입력 행렬 XXX (희소 가능)에 대해 SVD 수행:X≈UkΣkVk⊤
X≈UkΣkVk⊤X \approx U_k \Sigma_k V_k^\top
단, 전체 SVD가 아닌 앞부분 kkk개만 자름 (truncated)
결과로 저차원 잠재 요인(latent semantics) 공간 생성
mean-centering을 하지 않으므로, 텍스트처럼 0이 많은 희소 행렬의 구조 유지됨
| 구분 | PCA | TruncatedSVD |
|---|---|---|
| Mean-centering | 수행함 | 수행 안 함 |
| 희소행렬 처리 | 어렵거나 비효율적 | 효율적 (희소성 유지) |
| 속도 | 느릴 수 있음 | 상대적으로 빠름 |
| 적용 예시 | 수치 데이터 | 텍스트 벡터 (TF-IDF, CountVectorizer) |
| 수학적 기반 | 공분산 행렬 고유값 분해 | SVD 분해 (특이값 분해) |
| 라이브러리 | sklearn.decomposition.PCA | sklearn.decomposition.TruncatedSVD |
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
import pandas as pd
# 수치 데이터 예시
df = pd.DataFrame({
'x1': [1, 2, 3],
'x2': [4, 5, 6],
'x3': [7, 8, 9]
})
scaler = StandardScaler()
X_scaled = scaler.fit_transform(df)
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
from sklearn.decomposition import TruncatedSVD
from sklearn.feature_extraction.text import TfidfVectorizer
corpus = [
"dog cat mouse",
"dog dog tiger",
"cat tiger lion"
]
vectorizer = TfidfVectorizer()
X_tfidf = vectorizer.fit_transform(corpus)
svd = TruncatedSVD(n_components=2)
X_svd = svd.fit_transform(X_tfidf)
| 목적 | 추천 방법 |
|---|---|
| 일반 수치 데이터의 차원 축소 | PCA |
| 텍스트 벡터 (희소행렬)의 차원 축소 | TruncatedSVD |
| 속도/희소성 유지가 중요할 때 | TruncatedSVD |
| 데이터 분산 기반 축소, 해석이 중요한 경우 | PCA |
다수와 다른 데이터 탐지 → 비정상 여부 판단
| 알고리즘 | 설명 | 장점 |
|---|---|---|
| Isolation Forest | 트리 구조 기반, 이상치 격리 | 고차원 데이터에 강함 |
| One-Class SVM | SVM으로 경계 생성 | 정규 데이터만 학습 |
| Elliptic Envelope | 가우시안 분포 가정 | 통계 기반 탐지 |
| LOF (Local Outlier Factor) | 주변 밀도 비교 | 지역적 이상치 탐지 |
물건 A를 사면 B도 살 확률은? → 장바구니 분석
| 알고리즘 | 설명 | 대표 사용처 |
|---|---|---|
| Apriori | 항목 집합의 지지도 계산 | 마트, 추천 시스템 |
| FP-Growth | 빈발 패턴 트리 기반 | 대규모 데이터에 적합 |
추천 시스템의 핵심! → 사용자-아이템 관계 예측
| 알고리즘 | 설명 | 용도 |
|---|---|---|
| SVD (Singular Value Decomposition) | 사용자-아이템 행렬 분해 | 추천 시스템 |
| NMF (Non-negative Matrix Factorization) | 음수가 없는 행렬 분해 | 해석 쉬움, 추천에 활용 |
| 구성 요소 | 설명 |
|---|---|
| State (s) | 현재 환경의 상태 (예: 로봇의 위치) |
| Action (a) | 에이전트가 선택할 수 있는 행동 |
| Reward (r) | 행동의 결과로 받는 수치형 보상 |
| Policy (π) | 상태에서 어떤 행동을 할지 결정하는 전략 |
| Value Function (V(s), Q(s,a)) | 특정 상태(또는 상태-행동)에 대한 기대 보상 |
| Environment | 상태-보상-상태 전이 구조를 가진 외부 시스템 |
1. 환경 정의 (OpenAI Gym 등)
2. 에이전트 클래스 정의 (정책, 가치 함수, Q-table, 신경망 등)
3. 행동 선택 (탐험/이용)
4. 학습(업데이트) 로직
5. 학습 루프 (에피소드 반복)
6. 성능 평가 및 시각화
| 알고리즘 | 설명 |
|---|---|
| Q-Learning | 테이블 방식 Q(s, a) 업데이트 |
| SARSA | 행동 후 보상 기반 Q 업데이트 (on-policy) |
| DQN (Deep Q-Network) | Q-Learning + 딥러닝 |
| Policy Gradient | 정책 함수를 직접 학습 |
| REINFORCE | 가장 기본적인 정책 경사법 |
| Actor-Critic | 정책(Actor) + 가치(Critic)를 분리 |
| PPO (Proximal Policy Optimization) | 안정적이고 최근 가장 널리 사용 |
| DDPG / TD3 / SAC | 연속적인 행동 공간 지원 (로봇 제어 등) |
에이전트는 행동을 탐험(랜덤)으로 선택하더라도,
학습할 때는 항상 가장 좋은 행동을 했다고 가정해서 학습합니다.
즉, 현재 정책이 아닌 이상적인 정책으로 학습 → Off-policy
Q-table로 Q(s,a)Q(s,a)Q(s,a) 값을 업데이트
업데이트 공식:
탐험 vs 이용: ε-greedy 전략 사용
상태 s에서 시작
행동 a선택 (탐험/이용 → ε-greedy)
보상 r, 다음 상태 s′받기
Q-값 업데이트
상태를 s′s's′로 옮기고 반복
예시
# 환경 준비
import gym #gym: 강화학습 환경을 제공하는 라이브러리
import numpy as np
env = gym.make("FrozenLake-v1", is_slippery=False)
# is_slippery=False -> 내가 선택한 행동이 정확하게 실행
# is_slippery=True (기본값) -> 행동이 확률적으로 빗나감(실행하려던 방향이 아닐 수 있음)
q_table = np.zeros((env.observation_space.n, env.action_space.n))
# 학습 파라미터
alpha = 0.1 # 학습률 : 새 정보 반영 비율 (0: 안 바꿈, 1: 완전 반영)
gamma = 0.99 # 할인률 : 미래 보상의 중요도 (0: 현재만, 1: 미래도 중시)
epsilon = 0.1 # 탐험 확률 : 무작위 행동을 선택할 확률 (0: 탐험X, 1: 항상 탐험)
# 10% 확률로 탐험 (random action)
# 90% 확률로 이용 (가장 Q값이 큰 action)
# 학습 루프
for episode in range(1000):
state = env.reset()
done = False
while not done:
# ε-greedy 행동 선택
if np.random.rand() < epsilon:
action = env.action_space.sample() # 탐험
else:
action = np.argmax(q_table[state])
next_state, reward, done, _ = env.step(action) # 이용
# Q값 업데이트
q_table[state, action] += alpha * (reward + gamma * np.max(q_table[next_state]) - q_table[state, action])
state = next_state
is_slippery
is_slippery 옵션의 의미| 설정 | 의미 | 행동 결과 |
|---|---|---|
is_slippery=False | 미끄럽지 않음 | 내가 선택한 행동이 정확하게 실행됨 |
is_slippery=True (기본값) | 미끄러움 | 행동이 확률적으로 빗나감 (실행하려던 방향이 아닐 수도 있음) |
is_slippery=False내가 "→"(오른쪽)으로 이동하려고 하면,
→ **정확히 오른쪽**으로 이동함
✔️ 초보자 실습이나 알고리즘 디버깅에 유리
✔️ 학습이 빠르고 안정적
is_slippery=True (기본값)"→"로 이동하려고 해도,
- 80% 확률: 오른쪽
- 10% 확률: 위
- 10% 확률: 아래
→ 즉, **원하는 방향으로 이동할 확률이 낮음**
더 현실적인 환경 (로봇 제어, 자율주행처럼 제어가 완벽하지 않음)
정책 학습이 더 어려워지고, Q값이 덜 안정적
| 행동 시도 | is_slippery=False | is_slippery=True |
|---|---|---|
"→" 선택 | 무조건 오른쪽으로 감 | 대부분 오른쪽, 가끔 위/아래 |
| 상황 | 추천 설정 |
|---|---|
| 입문, 구조 이해, Q-table 디버깅 | is_slippery=False |
| 현실성 있는 학습, 일반 RL 연구 | is_slippery=True |
epsilon
에이전트가 행동을 선택할 때 다음 두 가지 중 하나:
| 선택 방식 | 설명 | 발생 확률 |
|---|---|---|
| 탐험 (exploration) | 무작위로 행동 선택 (새로운 정보 탐색) | ε (epsilon) |
| 이용 (exploitation) | 현재 Q-table에서 가장 Q값이 큰 행동 선택 | 1 − ε |
즉,
epsilon = 0.1
10% 확률로 → 탐험 (random action)
90% 확률로 → 이용 (가장 Q값이 큰 action)
| ε 값 | 의미 | 행동 방식 |
|---|---|---|
0.0 | 탐험 X → 항상 최선 행동만 | 빠르지만 과적합 위험, 최적 정책 못 찾을 수도 |
1.0 | 항상 무작위 행동 | 계속 탐험만 → 학습이 안 됨 |
0.1 | 10% 확률로 랜덤 | 일반적으로 많이 사용되는 시작 값 |
보통 학습이 진행되면, ε 값을 다음처럼 점점 줄여서:
epsilon = max(0.01, epsilon * 0.995)
처음엔 많이 탐험
나중엔 이용 중심 학습
| ε 값 | 행동 경향 | 설명 |
|---|---|---|
0 | 완전한 이용 | 탐험 전혀 안 함, 빠르지만 불완전 |
1 | 완전한 탐험 | 학습 안 됨 |
0.1 | 탐험 10%, 이용 90% | 일반적인 설정 |
| 점점 감소 | 초기 탐험 → 점차 최적화 | 현실적이고 효과적 |
env.step(action)
env.step(action)의 반환값 구조:next_state, reward, done, info = env.step(action)
| 반환값 | 의미 | 설명 |
|---|---|---|
next_state | 다음 상태 | 에이전트가 행동한 후, 바뀐 상태 (예: 카트 위치 등) |
reward | 보상 | 현재 행동 결과로 얻은 즉각적인 점수 |
done | 종료 여부 | 게임이 끝났는지 여부 (True면 종료) |
info (_) | 디버깅 정보 | 추가 정보(예: 이유, 시간 등). 보통 안 쓰므로 _로 무시 |
next_state상태는 환경에 따라 다르지만:
FrozenLake: 정수 번호 (0 ~ 15)CartPole: 연속형 벡터 (예: [0.01, 0.04, 0.03, -0.02])이 상태는 다음 행동을 결정하는 데 사용됨
reward행동의 즉각적인 보상값
예시:
- FrozenLake: 0 (실패), 1 (성공)
- CartPole: 막대가 안 쓰러지면 계속 1, 쓰러지면 종료
done에피소드가 끝났는지를 알려줌:
- True: 목표에 도달하거나 실패해서 게임 종료
- False: 아직 게임 진행 중
→ 이걸 while not done: 같은 조건에서 사용해요
info (또는 _)보통 디버깅 정보
예:
대부분 학습에는 필요 없어서 _로 무시합니다.
| 이름 | 타입 | 설명 |
|---|---|---|
next_state | 상태 (정수 또는 벡터) | 다음 상태 |
reward | 숫자 (int or float) | 행동의 즉각적인 보상 |
done | bool | 게임 종료 여부 |
info | dict | 추가 정보 (보통 무시 가능) |
next_state, reward, done, info = env.step(2)
print(next_state) # 4
print(reward) # 0
print(done) # False
print(info) # {'prob': 1.0}
4x4 얼음판에서 스타트 → 목표지점까지 이동하는 환경
물에 빠지면 보상 0, 도착하면 보상 1
Q-Learning은 최적 경로를 찾는 법을 학습함
| 장점 | 단점 |
|---|---|
| 구현이 매우 간단함 | Q-table은 상태가 많아지면 메모리 낭비 심함 |
| 최적 정책으로 수렴함 (이론적으로) | 연속 공간에서는 사용 불가능 |
| Off-policy라 정책 변경과 분리 가능 | 고차원 문제에는 신경망 필요 (→ DQN) |
Q-Learning은 테이블 기반의 가치 학습 알고리즘
목표는 각 상태-행동 쌍의 Q값을 학습해서 최적의 행동 정책을 찾는 것
간단하지만 강화학습의 기본 중 기본
이름은 S-A-R-S-A의 약자:
즉, 현재 상태-행동 → 보상 → 다음 상태-행동까지 보고 Q값을 업데이트
현재 정책을 따르는 행동 후의 Q값으로 업데이트
Q-Learning과 다른 점: Q-Learning은 다음 상태에서 최고 Q값(max)을 사용, SARSA는 실제 선택된 행동 Q값을 사용
SARSA는 자신이 실제로 사용하는 정책에 따라 학습
학습과 행동이 같은 정책을 따르므로 안정적이지만 탐험에 민감
import numpy as np
import gym
env = gym.make("FrozenLake-v1", is_slippery=False)
q_table = np.zeros((env.observation_space.n, env.action_space.n))
alpha = 0.1
gamma = 0.99
epsilon = 0.1
for episode in range(1000):
state = env.reset()
# 현재 상태에서 행동 선택
action = env.action_space.sample() if np.random.rand() < epsilon else np.argmax(q_table[state])
done = False
while not done:
next_state, reward, done, _ = env.step(action)
# 다음 행동도 ε-greedy로 선택
next_action = env.action_space.sample() if np.random.rand() < epsilon else np.argmax(q_table[next_state])
# SARSA 업데이트
q_table[state, action] += alpha * (
reward + gamma * q_table[next_state, next_action] - q_table[state, action]
)
state, action = next_state, next_action
(s, a) → (s', a') → (s'', a'') ...
↑
실제 행동
Q-Learning과 비교하면:
| 알고리즘 | 다음 행동 기준 | On/Off-policy |
|---|---|---|
| Q-Learning | 최대 Q값 사용 (max Q(s', a')) | Off-policy |
| SARSA | 실제 선택한 행동 사용 (Q(s', a')) | On-policy |
SARSA는 현실적인 환경에서,
- 탐험도 반영하면서 학습해야 할 때
- 에이전트가 실수를 줄이며 보수적으로 배워야 할 때
유리합니다.
SARSA는 실제 행동에 기반한 Q값 업데이트를 수행하는 On-policy 알고리즘
Q-Learning보다 조금 더 보수적이고 안전한 학습 방식
간단한 환경에서도 효과적이며, 이해하기 좋은 입문 알고리즘
| 항목 | Q-Learning | SARSA |
|---|---|---|
| 정책 종류 | Off-policy | On-policy |
| 업데이트 기준 | 최대 Q값 사용 (maxQ(s′,a′)\max Q(s', a')maxQ(s′,a′)) | 실제 선택한 Q(s′,a′)Q(s', a')Q(s′,a′) |
| 탐험 고려 | 안 함 (가정만 함) | 함 (실제 행동 기반) |
| 수렴 속도 | 빠를 수 있음 | 더 안정적 |
| 위험성 | 과감한 선택 가능 | 보수적 학습 경향 |
기존 Q-Learning은 Q-table을 사용 (상태/행동이 적을 때 OK)
DQN은 Q값을 예측하는 신경망을 사용 (복잡한 환경도 가능)
즉, Q(s, a)를 일일이 저장하지 않고, 신경망이 직접 계산하는 구조.
주요 기법:
- Experience Replay: 샘플 저장 → 랜덤 학습
- Target Network: 안정성 향상
| 기존 Q-Learning 문제 | DQN 해결 방법 |
|---|---|
| Q-table은 상태/행동이 많으면 메모리 폭발 | NN으로 Q값 근사 |
| 연속 상태, 이미지 상태는 Q-table 불가 | CNN/MLP 등으로 상태 처리 가능 |
| 학습 불안정, 진동 많음 | 경험 재플레이, 타겟 네트워크 등 도입 |
| 구성 요소 | 설명 |
|---|---|
| Q-Network | 상태를 받아 행동별 Q값을 출력하는 NN |
| Experience Replay | 과거 경험을 버퍼에 저장 & 무작위 샘플링 |
| Target Network | 일정 주기로 Q-Network의 가중치를 복사해 학습 안정화 |
| ε-greedy | 행동 선택 시 탐험 vs 이용 균형 |
상태 s를 관찰
NN으로 Q(s, a) 계산 → 행동 선택 (ε-greedy)
행동 수행 → 보상 r, 다음 상태 s′
경험 (s, a, r, s', done)을 버퍼에 저장
샘플을 꺼내서 손실 계산:
손실로 NN 학습 (역전파)
일정 주기로 타겟 네트워크 업데이트
# Q-Network
class DQN(nn.Module):
def __init__(self, state_dim, action_dim):
super().__init__()
self.fc = nn.Sequential(
nn.Linear(state_dim, 128), nn.ReLU(),
nn.Linear(128, action_dim)
)
def forward(self, x):
return self.fc(x)
| 요소 | 설명 |
|---|---|
nn.Module | PyTorch 신경망 클래스 |
state_dim | 상태 벡터의 차원 수 (입력 크기) |
action_dim | 행동의 개수 (출력 노드 수) |
self.fc | 2층 fully connected 신경망 |
| 구조 | 상태 → 128노드 은닉층(ReLU) → 행동 수만큼 Q값 출력 |
하나의 상태 벡터를 받아, 각 행동의 Q값을 출력
# 메인 루프
for each episode:
state = env.reset()
while not done:
# 행동 선택
if random < epsilon:
action = random_action()
else:
action = argmax(Q(state))
# 행동수행
next_state, reward, done = env.step(action)
# 경험저장
replay_buffer.append((state, action, reward, next_state, done))
# 학습
batch = sample(replay_buffer)
# → 버퍼에서 미니배치로 (s, a, r, s', done) 데이터를 무작위로 추출
loss = compute_dqn_loss(batch)
# → DQN 손실 함수 계산:
optimizer.step()
# → 손실을 줄이기 위해 신경망 파라미터를 업데이트 (역전파)
# 타겟 네트워크 업데이트
if step % C == 0:
target_net.load_state_dict(q_net.state_dict())
일정 스텝마다 q_net의 가중치를 타겟 네트워크에 복사
이유: max Q(s', a')를 계산할 때 안정된 값을 사용하기 위함
타겟 네트워크는 학습하지 않고 기준 역할만 수행함
| 단계 | 내용 |
|---|---|
| Q-Network 정의 | 상태 → Q값 예측 |
| ε-greedy 정책 | 탐험과 이용의 균형 |
| 경험 저장 | (s, a, r, s', done) 저장 |
| 배치 학습 | 손실 계산 + 파라미터 업데이트 |
| 타겟 네트워크 | 안정된 학습 보장 |
| 개념 | 설명 |
|---|---|
| Q-Network | Q값을 예측하는 신경망 |
| Experience Replay | 샘플을 무작위로 뽑아 학습 안정화 |
| Target Network | 기준이 되는 별도 Q값 네트워크 |
| ε-greedy | 탐험과 이용 균형 조절 |
| 손실함수 | TD오차: (Q−Target)2(Q - \text{Target})^2(Q−Target)2 |
import numpy as np
import random
import gym
import torch
import torch.nn as nn
import torch.optim as optim
from collections import deque
# Q-Network 정의
class DQN(nn.Module):
def __init__(self, state_dim, action_dim):
super().__init__()
self.fc = nn.Sequential(
nn.Linear(state_dim, 128),
nn.ReLU(),
nn.Linear(128, action_dim)
)
def forward(self, x):
return self.fc(x)
# 손실 함수 계산
def compute_loss(batch, q_net, target_net, gamma):
states, actions, rewards, next_states, dones = batch
states = torch.FloatTensor(states)
actions = torch.LongTensor(actions).unsqueeze(1)
rewards = torch.FloatTensor(rewards).unsqueeze(1)
next_states = torch.FloatTensor(next_states)
dones = torch.FloatTensor(dones).unsqueeze(1)
q_values = q_net(states).gather(1, actions)
next_q = target_net(next_states).max(1)[0].detach().unsqueeze(1)
target = rewards + gamma * next_q * (1 - dones)
return nn.MSELoss()(q_values, target)
# 환경 설정
env = gym.make("CartPole-v1")
state_dim = env.observation_space.shape[0]
action_dim = env.action_space.n
q_net = DQN(state_dim, action_dim)
target_net = DQN(state_dim, action_dim)
target_net.load_state_dict(q_net.state_dict())
optimizer = optim.Adam(q_net.parameters(), lr=0.001)
replay_buffer = deque(maxlen=10000)
# 하이퍼파라미터
episodes = 500
batch_size = 32
gamma = 0.99
epsilon = 0.1
target_update_freq = 10
# 학습 루프
for episode in range(episodes):
state = env.reset()
done = False
total_reward = 0
while not done:
if random.random() < epsilon:
action = env.action_space.sample()
else:
with torch.no_grad():
action = q_net(torch.FloatTensor(state).unsqueeze(0)).argmax().item()
next_state, reward, done, _ = env.step(action)
replay_buffer.append((state, action, reward, next_state, done))
state = next_state
total_reward += reward
if len(replay_buffer) >= batch_size:
batch = random.sample(replay_buffer, batch_size)
batch = list(zip(*batch)) # (states, actions, rewards, next_states, dones)
loss = compute_loss(batch, q_net, target_net, gamma)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if episode % target_update_freq == 0:
target_net.load_state_dict(q_net.state_dict())
print(f"Episode {episode}, Total Reward: {total_reward}")
env.close()
| 장점 | 단점 |
|---|---|
| 고차원 상태 입력 처리 가능 (ex. 이미지) | 연속 행동공간에는 부적합 (→ DDPG, SAC) |
| Q-table 없이 근사 가능 | overestimation 문제 있음 (→ Double DQN) |
| 다양한 환경에 적용 가능 | 학습 안정화 필요 (버퍼, 타겟넷 등 중요) |
이미지 기반 환경 (ex. Atari Pong)
상태공간이 큰 문제 (CartPole, LunarLander 등)
discrete action space (행동이 정수형)
| 알고리즘 | 설명 |
|---|---|
| Double DQN | Q값 과대평가 완화 |
| Dueling DQN | 상태의 가치와 행동의 Advantage 분리 |
| Rainbow DQN | 다양한 기법을 결합 (지금까지 최고 성능 중 하나) |
| PER | Prioritized Experience Replay (더 중요한 경험 우선 학습) |
가장 기본적인 정책 경사법 (에피소드 단위)
수렴 느리지만 개념적으로 중요
행동 확률의 로그에 보상을 곱해서 정책을 개선
에피소드 전체 수집 (s, a, r, s', a', ...)
에피소드 종료 후 총 보상 계산
log(π(a|s)) * R로 정책 업데이트
---
## 간단한 PyTorch 스타일 예시
```python
# 정책 신경망
class PolicyNet(nn.Module):
def __init__(self, state_dim, action_dim):
super().__init__()
self.fc = nn.Sequential(
nn.Linear(state_dim, 128),
nn.ReLU(),
nn.Linear(128, action_dim),
nn.Softmax(dim=-1)
)
def forward(self, x):
return self.fc(x)
# 학습 루프 (REINFORCE)
log_probs = []
rewards = []
state = env.reset()
done = False
while not done:
probs = policy(torch.tensor(state).float())
dist = Categorical(probs)
action = dist.sample()
log_probs.append(dist.log_prob(action))
state, reward, done, _ = env.step(action.item())
rewards.append(reward)
# 전체 리턴 계산 후 역전파
G = sum(rewards)
loss = -sum([log_prob * G for log_prob in log_probs])
loss.backward()
정책 함수 π(a∣s)\pi(a|s)π(a∣s) 직접 학습
목표: 파라미터 θ\thetaθ에 대해 기대 보상 최대화
Actor: 정책 학습
Critic: 상태의 가치 V(s) 또는 Q(s, a) 평가
Advantage function 사용:
최근 가장 널리 쓰이는 안정적인 정책 경사법
큰 업데이트를 방지하는 클리핑 함수 사용
| 계열 | 알고리즘 | 특징 |
|---|---|---|
| 가치 기반 | Q-Learning, SARSA, DQN | Q값 기반, 정책은 간접적으로 |
| 정책 기반 | REINFORCE, Policy Gradient | 확률적 정책을 직접 업데이트 |
| 정책 + 가치 (Actor-Critic) | Actor-Critic, A2C, PPO | Actor: 정책 / Critic: 가치 평가 |
| 연속 제어 + Actor-critic | DDPG, TD3, SAC | 연속적인 행동 공간에 사용 가능 |
| 알고리즘 | 구현 구조 | 핵심 구현 포인트 |
|---|---|---|
| Q-Learning | 테이블 기반 | Q-table 업데이트, ε-greedy |
| SARSA | 테이블 기반 | 실제 행동으로 Q 업데이트 (on-policy) |
| REINFORCE | 신경망 기반 (정책만) | 에피소드 단위로 로그 확률 × 보상, high variance |
| Policy Gradient | 신경망 기반 | REINFORCE 수식 사용, 정책 확률 업데이트 |
| DQN | 신경망 기반 (Q함수) | Q-Network + 경험 리플레이 + 타겟 네트워크 |
| Actor-Critic | 2개 신경망 | Actor(정책), Critic(V or Q), Advantage 사용 |
| PPO | 신경망 기반 + 안정화 | Clip objective, mini-batch 업데이트 |
| DDPG / TD3 / SAC | 연속 공간 + NN | Actor-Critic + noise 처리 + 경험 버퍼 (연속 제어에 적합) |
| 분야 | 활용 예 |
|---|---|
| 게임 | AlphaGo, Dota2 AI, Atari 게임 |
| 로보틱스 | 팔 움직임, 드론 제어 |
| 금융 | 포트폴리오 최적화 |
| 자율주행 | 행동 선택, 조향 각도 제어 |
| 제조 | 최적화된 공정 자동화 |
1. 상태 관측 → 2. 행동 선택 → 3. 보상 + 상태 전이 → 4. 정책/가치 업데이트 → 반복
| 모델명 | 설명 |
|---|---|
| DummyClassifier | 성능 비교용 더미 모델 |
| MLPClassifier / Regressor | 신경망 기반 분류/회귀 (다층 퍼셉트론) |
| HistGradientBoosting | LightGBM 비슷한 scikit-learn 내장 부스팅 |
RandomForestClassifierXGBoostClassifierLightGBMClassifierLogisticRegressionGradientBoostingClassifier\hat{y}_i^{(t)} = \hat{y}_i^{(t-1)} + \eta \cdot f_t(x_i)
| 사용 조건 | 이유 |
|---|---|
| 예측 정확도가 매우 중요 | 캐글 1위 모델 다수 |
| 과적합 걱정되는 복잡한 데이터 | 정규화 + 가지치기 내장 |
| 대용량 데이터 | 병렬 처리, GPU 지원 |
| 결측값 존재 | 자동 처리 지원 |
| 분류, 회귀, 랭킹 문제 | 전용 손실 함수 지원 |