주요 알고리즘 — 선형 회귀, 랜덤 포레스트, k-NN
머신러닝의 핵심 알고리즘을 원리부터 실전 코드까지 비교합니다.
선형 회귀 (Linear Regression)
from sklearn.linear_model import (
LinearRegression,
Ridge, # L2 규제
Lasso, # L1 규제 (특성 선택 효과)
ElasticNet, # L1 + L2 결합
)
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import numpy as np
# 데이터 준비
housing = fetch_california_housing()
X, y = housing.data, housing.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
scaler = StandardScaler()
X_train_s = scaler.fit_transform(X_train)
X_test_s = scaler.transform(X_test)
# 기본 선형 회귀
lr = LinearRegression()
lr.fit(X_train_s, y_train)
print(f"계수: {lr.coef_}")
print(f"절편: {lr.intercept_:.4f}")
print(f"R² (학습): {lr.score(X_train_s, y_train):.4f}")
print(f"R² (테스트): {lr.score(X_test_s, y_test):.4f}")
# Ridge (L2 규제 — 계수 크기 제한)
ridge = Ridge(alpha=1.0)
ridge.fit(X_train_s, y_train)
print(f"Ridge R²: {ridge.score(X_test_s, y_test):.4f}")
# Lasso (L1 규제 — 불필요한 특성 계수 → 0)
lasso = Lasso(alpha=0.1)
lasso.fit(X_train_s, y_train)
nonzero = np.sum(lasso.coef_ != 0)
print(f"Lasso 0이 아닌 계수 수: {nonzero}/{len(lasso.coef_)}")
로지스틱 회귀 (분류)
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_breast_cancer
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, stratify=y, random_state=42)
scaler = StandardScaler()
X_train_s = scaler.fit_transform(X_train)
X_test_s = scaler.transform(X_test)
lr_clf = LogisticRegression(C=1.0, max_iter=1000, random_state=42)
lr_clf.fit(X_train_s, y_train)
# 클래스 확률
probs = lr_clf.predict_proba(X_test_s) # [[P(0), P(1)], ...]
print(f"첫 샘플 확률: 음성={probs[0,0]:.3f}, 양성={probs[0,1]:.3f}")
결정 트리 (Decision Tree)
from sklearn.tree import DecisionTreeClassifier, export_text, plot_tree
import matplotlib.pyplot as plt
dt = DecisionTreeClassifier(
max_depth=4, # 과적합 방지
min_samples_leaf=5, # 리프 노드 최소 샘플
random_state=42,
)
dt.fit(X_train, y_train)
# 트리 시각화
plt.figure(figsize=(20, 8))
plot_tree(dt, feature_names=feature_names, class_names=["악성", "양성"],
filled=True, fontsize=8)
plt.show()
# 텍스트로 출력
print(export_text(dt, feature_names=feature_names))
랜덤 포레스트 (Random Forest)
from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor
# 분류
rf = RandomForestClassifier(
n_estimators=200, # 트리 수
max_depth=10, # 최대 깊이
min_samples_split=5,
max_features="sqrt", # 각 분기에서 고려할 특성 수
n_jobs=-1, # 병렬 처리
random_state=42,
)
rf.fit(X_train, y_train)
print(f"RF 정확도: {rf.score(X_test, y_test):.4f}")
# OOB (Out-of-Bag) 점수 — 추가 검증 없이 모델 평가
rf_oob = RandomForestClassifier(n_estimators=100, oob_score=True, random_state=42)
rf_oob.fit(X_train, y_train)
print(f"OOB 점수: {rf_oob.oob_score_:.4f}")
# 특성 중요도 시각화
import pandas as pd
feat_imp = pd.Series(rf.feature_importances_, index=feature_names).sort_values(ascending=False)
print(feat_imp.head(10))
k-NN (k-최근접 이웃)
from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor
# k값에 따른 성능 비교
from sklearn.model_selection import cross_val_score
k_values = range(1, 31)
cv_scores = []
for k in k_values:
knn = KNeighborsClassifier(n_neighbors=k, metric="euclidean")
scores = cross_val_score(knn, X_train_s, y_train, cv=5, scoring="accuracy")
cv_scores.append(scores.mean())
best_k = k_values[np.argmax(cv_scores)]
print(f"최적 k: {best_k}, 점수: {max(cv_scores):.4f}")
# 최적 k로 학습
knn = KNeighborsClassifier(n_neighbors=best_k)
knn.fit(X_train_s, y_train)
print(f"테스트 정확도: {knn.score(X_test_s, y_test):.4f}")
SVM (서포트 벡터 머신)
from sklearn.svm import SVC, SVR
# 분류
svm = SVC(
C=1.0, # 마진-오분류 균형 (클수록 오분류 적음)
kernel="rbf", # 'linear', 'poly', 'rbf', 'sigmoid'
gamma="scale",
probability=True, # predict_proba 사용 시 필요
random_state=42,
)
svm.fit(X_train_s, y_train)
print(f"SVM 정확도: {svm.score(X_test_s, y_test):.4f}")
알고리즘 비교
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
models = {
"Logistic Regression": LogisticRegression(max_iter=1000, random_state=42),
"Decision Tree": DecisionTreeClassifier(max_depth=5, random_state=42),
"Random Forest": RandomForestClassifier(n_estimators=100, random_state=42),
"Gradient Boosting": GradientBoostingClassifier(n_estimators=100, random_state=42),
"k-NN (k=5)": KNeighborsClassifier(n_neighbors=5),
"SVM": SVC(random_state=42),
}
results = {}
for name, model in models.items():
scores = cross_val_score(model, X_train_s, y_train, cv=5, scoring="accuracy")
results[name] = {"mean": scores.mean(), "std": scores.std()}
print(f"{name:25s}: {scores.mean():.4f} ± {scores.std():.4f}")
알고리즘 선택 가이드
데이터 크기와 특성에 따라 알고리즘 선택:
소규모 데이터 (< 10K):
분류 → SVM, Logistic Regression, k-NN
회귀 → Ridge, Lasso
중간 규모 (10K ~ 100K):
분류/회귀 → Random Forest, Gradient Boosting
대규모 (> 100K):
분류/회귀 → SGDClassifier/Regressor, XGBoost, LightGBM
해석 필요:
→ Logistic Regression, Decision Tree
비선형 복잡 패턴:
→ Random Forest, Gradient Boosting, SVM(RBF)
정리
| 알고리즘 | 유형 | 강점 | 약점 |
|---|---|---|---|
| 선형 회귀 | 회귀 | 해석 용이, 빠름 | 비선형 관계 취약 |
| 로지스틱 회귀 | 분류 | 확률 출력, 해석 용이 | 비선형 패턴 취약 |
| 결정 트리 | 분류/회귀 | 해석 쉬움 | 과적합 위험 |
| 랜덤 포레스트 | 분류/회귀 | 강건, 특성 중요도 | 느림, 메모리 |
| k-NN | 분류/회귀 | 간단, 직관적 | 예측 느림, 스케일 민감 |
| SVM | 분류/회귀 | 고차원 효과적 | 대규모 데이터 느림 |
실무에서는 Random Forest → Gradient Boosting (XGBoost/LightGBM) 순서로 시도하는 것이 일반적입니다.