✅ 오늘 한 것
머신러닝 정리, 태블로를 활용한 데이터 시각화
✏️ 오늘 배운 점
머신러닝
1) Feature Engineering (특성 엔지니어링)
원본 데이터를 머신러닝 모델이 더 잘 이해하도록 재구성하는 과정
# 날짜 데이터 분해
df['year'] = df['date'].dt.year
df['month'] = df['date'].dt.month
df['day'] = df['date'].dt.day
df['weekday'] = df['date'].dt.weekday
# train/test split
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 범주형 인코딩
1. One-Hot
df = pd.get_dummies(df, columns=['category'], drop_first=True)
2. Label Encoding
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
df['cat_encoded'] = le.fit_transform(df['category'])
# 스케일링
1. StandardScaler
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
2. MinMaxScaler
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# 결측치 처리
1. KNN
from sklearn.impute import KNNImputer
imputer = KNNImputer(n_neighbors=5)
df_imputed = pd.DataFrame(imputer.fit_transform(df), columns=df.columns)
2. MICE
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
imputer = IterativeImputer()
df_mice = pd.DataFrame(imputer.fit_transform(df), columns=df.columns)
# 과적합 방지
1. VIF기반 변수 제거
from statsmodels.stats.outliers_influence import variance_inflation_factor
def calculate_vif(X):
vif_data = pd.DataFrame()
vif_data["Variable"] = X.columns
vif_data["VIF"] = [variance_inflation_factor(X.values, i)
for i in range(X.shape[1])]
return vif_data.sort_values('VIF', ascending=False)
vif_result = calculate_vif(df)
print(vif_result)
2. 높은 상관관계를 가진 변수 제거
corr_matrix = df.corr().abs()
threshold = 0.9
high_corr_vars = set()
for i in range(len(corr_matrix.columns)):
for j in range(i):
if corr_matrix.iloc[i, j] > threshold:
colname = corr_matrix.columns[i]
high_corr_vars.add(colname)
df_corr_reduced = df_numeric.drop(columns=high_corr_vars)
print("제거된 변수들:", high_corr_vars)
3. PCA 분석
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
df_scaled = scaler.fit_transform(df)
pca = PCA(n_components=0.95)
df_pca = pca.fit_transform(df_scaled[['a','b']]) # 바꾸고자 하는 두 변수
print(f"PCA 적용 후 차원 수: {df_pca.shape[1]}")
# SMOTE
sm = SMOTE()
X_train_res, y_train_res = sm.fit_resample(X_train_scaled, y_train)
train/test split을 진행한 이후 Scaling, Encoding 등 사용해야 함.
📌 Scaling과 Encoding은 반드시 Train 데이터로만 fit하고 Test 데이터에는 transform만 적용해야 한다.
One-Hot Encoding: 순서가 없는 범주형 변수를 숫자로 변환하는 과정
Label Encoding: 순서가 있는 범주형 변수를 숫자로 변환하는 과정
StandardScaler: 각 값을 평균 0, 표준편차 1로 변환 (데이터가 정규분포에 가깝거나 대칭적일 때)
MinMaxScaler: 각 값을 0~1 사이로 변환 (데이터 분포가 비정규형일 때)
VIF 값이 10 이상일 때 다중공선성이 높다고 판단하여 제거
상관계수가 0.9 이상인 변수 쌍 중 하나를 제거
SMOTE에는 Train에만 수행
2) 모델 선택
회귀분석
1. LightGBM
from lightgbm import LGBMRegressor
model = LGBMRegressor(
n_estimators=500,
learning_rate=0.05,
max_depth=-1,
num_leaves=31
)
model.fit(X_train, y_train)
pred = model.predict(X_test)
2. CatBoost (범주형 처리)
from catboost import CatBoostRegressor
model = CatBoostRegressor(
iterations=500,
learning_rate=0.05,
depth=6,
verbose=0
)
model.fit(X_train, y_train)
pred = model.predict(X_test)
3. XGBoost
from xgboost import XGBRegressor
model = XGBRegressor(
n_estimators=500,
learning_rate=0.05,
max_depth=6,
subsample=0.8,
colsample_bytree=0.8
)
model.fit(X_train, y_train)
pred = model.predict(X_test)
4. RandomForest
from sklearn.ensemble import RandomForestRegressor
model = RandomForestRegressor(
n_estimators=300,
max_depth=None,
random_state=42
)
model.fit(X_train, y_train)
pred = model.predict(X_test)
from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error
import numpy as np
r2 = r2_score(y_test, pred)
rmse = mean_squared_error(y_test, pred, squared=False)
mae = mean_absolute_error(y_test, pred)
mape = np.mean(np.abs((y_test - pred) / y_test)) * 100
def adjusted_r2(r2, n, p): # n=샘플 수, p=독립변수 개수
return 1 - (1 - r2) * (n - 1) / (n - p - 1)
adj_r2 = adjusted_r2(r2, len(y_test), X_test.shape[1])
R2 (결정계수): 0 ~ 1 (높을수록 좋음)
RMSE(평균 오차 크기): 작을수록 좋음
MAE(평균 절대 오차): 작을수록 좋음
MAPE(예측 대비 실제값 오차 비율): 10% 이하일 때 좋은 성능
Adjusted R2 (수정된 결정계수): 변수 수에 따라 보정된 R2
| 지표 | 목적 | 언제 사용하는가? |
| R² | 모델 설명력 | 기본 필수 |
| Adjusted R² | 변수 많을 때 | 다중회귀, 고차원 데이터 |
| RMSE | 큰 오차에 민감 | 실무 기본 평가 |
| MAE | 평균 오차 | 직관적 판단 |
| MAPE | 예측 오차 비율 | 시계열, 매출, 수요 |
| MSE | 비교용 | RMSE의 기반 |
분류분석
1. LightGBM
from lightgbm import LGBMClassifier
model = LGBMClassifier(
n_estimators=500,
learning_rate=0.05,
max_depth=-1,
num_leaves=31
)
model.fit(X_train, y_train)
pred = model.predict(X_test)
2. CatBoost (범주형 처리)
from catboost import CatBoostClassifier
model = CatBoostClassifier(
iterations=500,
learning_rate=0.05,
depth=6,
verbose=0
)
model.fit(X_train, y_train)
pred = model.predict(X_test)
3. XGBoost
from xgboost import XGBClassifier
model = XGBClassifier(
n_estimators=500,
learning_rate=0.05,
max_depth=6,
subsample=0.8,
colsample_bytree=0.8,
eval_metric='logloss'
)
model.fit(X_train, y_train)
pred = model.predict(X_test)
4. RandomForest
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier(
n_estimators=300,
max_depth=None,
random_state=42
)
model.fit(X_train, y_train)
pred = model.predict(X_test)
from sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score, roc_auc_score
acc = accuracy_score(y_test, pred)
f1 = f1_score(y_test, pred, average='binary'(이진분류) or 'macro'(다중분류))
prec = precision_score(y_test, pred)
rec = recall_score(y_test, pred)
proba = model.predict_proba(X_test)[:, 1]
roc = roc_auc_score(y_test, proba)
print("Accuracy:", acc)
print("F1:", f1)
print("Precision:", prec)
print("Recall:", rec)
print("ROC-AUC:", roc)
Accuracy(정확도): 전체 예측 중 맞춘 비율
Precision(정밀도): 양성으로 예측한 것 중 실제로 양성이 얼마나 있는가?
Recall(민감도, 재현율): 실제 양성을 얼마나 잘 찾았는가?
F1 Score: Precision과 Recall의 조화평균
ROC-AUC: 분류 Threshold(임계값)를 바꾸며 모델이 양성과 음성을 얼마나 잘 구분하는가?
| 지표 | 해석 | 값 범위 | 언제 중요한가 | 장점 | 단점 |
| Accuracy | 전체 정확도 | 0~1 | 데이터 균형일 때 | 직관적 | 불균형 데이터에서 무의미 |
| Precision | 양성 예측 중 얼마나 맞았는가 | 0~1 | FP가 위험한 상황 | FP 감소에 유리 | FN 무시 |
| Recall | 실제 양성 중 얼마나 맞았나 | 0~1 | FN이 위험한 상황 | FN 감소에 유리 | FP 증가 가능 |
| F1 Score | Precision+Recall 균형 | 0~1 | 불균형 데이터 | 두 지표의 균형 | 해석이 직관적이지 않음 |
| ROC-AUC | 전체 분류 능력 | 0.5~1.0 | 전반적 모델 평가 | threshold 영향 없음 | 확률 모델 필요 |
시계열 모델링
1. ARIMA/SARIMA
import statsmodels.api as sm
model = sm.tsa.statespace.SARIMAX(
y_train,
order=(1, 1, 1),
seasonal_order=(1, 1, 1, 12)
)
result = model.fit()
pred = result.predict(start=len(y_train), end=len(y_train)+len(y_test)-1)
print(result.summary())
2. Prophet
from prophet import Prophet
import pandas as pd
df_train = pd.DataFrame({'ds': X_train['date'], 'y': y_train})
model = Prophet()
model.fit(df_train)
future = pd.DataFrame({'ds': X_test['date']})
forecast = model.predict(future)
pred = forecast['yhat']
3. LightGBM
from lightgbm import LGBMRegressor
# Lag 변수 생성 예시
df['lag_1'] = df['y'].shift(1)
df['lag_7'] = df['y'].shift(7)
df['lag_30'] = df['y'].shift(30)
df = df.dropna()
X = df[['lag_1', 'lag_7', 'lag_30']]
y = df['y']
split_point = int(len(df) * 0.8)
X_train, X_test = X[:split_point], X[split_point:]
y_train, y_test = y[:split_point], y[split_point:]
model = LGBMRegressor(
n_estimators=500,
learning_rate=0.05,
num_leaves=31
)
model.fit(X_train, y_train)
pred = model.predict(X_test)
4. LSTM
import numpy as np
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense
def create_dataset(series, timesteps=10):
X, y = [], []
for i in range(len(series) - timesteps):
X.append(series[i:i+timesteps])
y.append(series[i+timesteps])
return np.array(X), np.array(y)
timesteps = 10
X, y = create_dataset(df['y'].values, timesteps=timesteps)
split = int(len(X) * 0.8)
X_train, X_test = X[:split], X[split:]
y_train, y_test = y[:split], y[split:]
model = Sequential()
model.add(LSTM(64, activation='tanh', input_shape=(timesteps, 1)))
model.add(Dense(1))
model.compile(optimizer='adam', loss='mse')
model.fit(X_train, y_train, epochs=20, batch_size=32)
pred = model.predict(X_test)
| 모델 | 장점 | 단점 | 적합 데이터 |
| ARIMA/SARIMA | 통계 기반, 해석력 좋음 | 피처 사용 어려움 | 선형 + 계절성 구조 |
| Prophet | 자동으로 계절성/추세 처리 | 튜닝 어려울 때 있음 | 비즈니스 주기 데이터 |
| LightGBM 회귀 | 피처 활용 최고, 정확도 최고 | 시계열 지식 필요 | 대규모/복잡 데이터 |
| LSTM | 비선형·복잡 패턴 강함 | 데이터 많이 필요 | 센서, 수요, 신호 예측 |
from sklearn.metrics import mean_squared_error, mean_absolute_error
import numpy as np
rmse = mean_squared_error(y_test, pred, squared=False)
mae = mean_absolute_error(y_test, pred)
mape = np.mean(np.abs((y_test - pred) / y_test)) * 100
smape = np.mean(2 * np.abs(y_test - pred) / (np.abs(y_test) + np.abs(pred))) * 100
def mase(y_test, pred, y_train):
naive = np.mean(np.abs(np.diff(y_train)))
return np.mean(np.abs(y_test - pred)) / naive
mase_score = mase(y_test, pred, y_train)
| 지표 | 해석 | 값 범위 | 언제 중요한가 | 장점 | 단점 |
| RMSE (Root Mean Squared Error) | 예측 오차의 크기(큰 오차에 특히 민감) | 0~∞ (작을수록 좋음) | 급등·급락이 큰 시계열(매출·수요 예측) | 큰 오차를 강하게 반영해 모델 차이가 잘 드러남 | 이상치에 매우 민감 |
| MAE (Mean Absolute Error) | 평균 절대 오차(실제값과 예측값의 직관적 거리) | 0~∞ (작을수록 좋음) | 모델의 전반적 평균 오차를 보고 싶을 때 | 해석이 쉽고 직관적 | 큰 오차를 과소평가할 수 있음 |
| MAPE (Mean Absolute Percentage Error) | 예측 오차를 %로 표현한 지표 | 0%~∞ | 매출/수요 등 "예측 대비 오차 비율"이 중요할 때 | 단위의 영향을 받지 않음 → 비교 용이 | 실제값이 0에 가까우면 수식이 폭발함 |
| sMAPE (Symmetric MAPE) | MAPE 개선판, 실제값=0 문제 해결 | 0%~200% | 값이 0에 가까운 시계열(재고·센서 데이터) | 0 문제 해결, 실제/예측에 대해 대칭적 | 해석이 MAPE보다 직관적이지 않음 |
3) 모델 학습 + Cross Validation
비시계열 데이터
# K-fold (회귀 모델)
from sklearn.model_selection import KFold, cross_val_score
from sklearn.metrics import mean_squared_error, make_scorer
import numpy as np
rmse_scorer = make_scorer(mean_squared_error, greater_is_better=False, squared=False)
kf = KFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(model, X_train, y_train, cv=kf, scoring=rmse_scorer)
print("개별 Fold RMSE:", -scores)
print("평균 RMSE:", -scores.mean())
# Stratified K-fold (분류 모델)
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.metrics import f1_score, make_scorer
f1_scorer = make_scorer(f1_score, average='binary') # 다중분류면 average='macro'
skf = StratifiedKFold(n_splits=5, shuffle=True)
scores = cross_val_score(model, X_train, y_train, cv=skf, scoring=f1_scorer)
print("개별 Fold F1:", scores)
print("평균 F1:", scores.mean())
시계열 데이터
# TimeSeriesSplit (시계열 전용 CV)
from sklearn.model_selection import TimeSeriesSplit
from sklearn.metrics import mean_absolute_error
tscv = TimeSeriesSplit(n_splits=5)
fold_scores = []
for train_idx, valid_idx in tscv.split(X):
X_tr, X_val = X.iloc[train_idx], X.iloc[valid_idx]
y_tr, y_val = y.iloc[train_idx], y.iloc[valid_idx]
model.fit(X_tr, y_tr)
pred = model.predict(X_val)
fold_scores.append(mean_absolute_error(y_val, pred))
print("개별 Fold MAE:", fold_scores)
print("평균 MAE:", np.mean(fold_scores))
4) 하이퍼파라미터 튜닝
GridSearchCV
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestRegressor
param_grid = {
'n_estimators': [100, 300, 500],
'max_depth': [None, 5, 10, 15],
'min_samples_split': [2, 5, 10]
}
model = RandomForestRegressor()
grid = GridSearchCV(
estimator=model,
param_grid=param_grid,
cv=5,
scoring='neg_root_mean_squared_error',
n_jobs=-1,
verbose=1
)
grid.fit(X_train, y_train)
print("Best Params:", grid.best_params_)
print("Best Score (RMSE):", -grid.best_score_)
RandomizedSearchCV
from sklearn.model_selection import RandomizedSearchCV
from sklearn.ensemble import RandomForestRegressor
from scipy.stats import randint
param_dist = {
'n_estimators': randint(100, 600),
'max_depth': randint(3, 20),
'min_samples_split': randint(2, 15)
}
model = RandomForestRegressor()
random_search = RandomizedSearchCV(
estimator=model,
param_distributions=param_dist,
n_iter=30,
cv=5,
scoring='neg_root_mean_squared_error',
n_jobs=-1,
verbose=1,
random_state=42
)
random_search.fit(X_train, y_train)
print("Best Params:", random_search.best_params_)
print("Best Score (RMSE):", -random_search.best_score_)
Optuna
import optuna
from lightgbm import LGBMRegressor
from sklearn.metrics import mean_squared_error
def objective(trial):
params = {
'n_estimators': trial.suggest_int('n_estimators', 200, 800),
'learning_rate': trial.suggest_float('learning_rate', 0.01, 0.2),
'num_leaves': trial.suggest_int('num_leaves', 20, 80),
'max_depth': trial.suggest_int('max_depth', -1, 15),
'subsample': trial.suggest_float('subsample', 0.5, 1.0),
'colsample_bytree': trial.suggest_float('colsample_bytree', 0.5, 1.0)
}
model = LGBMRegressor(**params)
model.fit(X_train, y_train)
pred = model.predict(X_test)
rmse = mean_squared_error(y_test, pred, squared=False)
return rmse
study = optuna.create_study(direction='minimize')
study.optimize(objective, n_trials=40)
print("Best Params:", study.best_params)
print("Best RMSE:", study.best_value)
| 방법 | 탐색 방식 | 속도 | 성능 | 언제 사용하는가? |
| GridSearchCV | 모든 조합 탐색 | 느림 | 보통 | 소규모 파라미터 |
| RandomizedSearchCV | 랜덤 샘플링 | 빠름 | 좋음 | 중간 규모 파라미터 |
| Optuna | Bayesian Optimization | 매우 빠름 | 최고 | 대규모·고성능 모델 |
5) 모델 해석
1. Feature Importance (특성 중요도)
Permutation Importance → 특징을 임의로 섞었을 때 성능 얼마나 떨어지는지
from sklearn.inspection import permutation_importance
result = permutation_importance(
model, X_test, y_test, n_repeats=20, random_state=42
)
importances = result.importances_mean
sorted_idx = importances.argsort()
print("Permutation Importance:")
for i in sorted_idx[::-1]:
print(f"{X_test.columns[i]}: {importances[i]:.4f}")
2. SHAP (최강의 모델 해석 도구)
import shap
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X_test)
shap.summary_plot(shap_values, X_test)
shap.summary_plot(shap_values, X_test, plot_type="bar")
shap.force_plot(
explainer.expected_value,
shap_values[0],
X_test.iloc[0]
)
3. Partial Dependence Plot (PDP)
from sklearn.inspection import PartialDependenceDisplay
PartialDependenceDisplay.from_estimator(
model,
X_test,
features=['temperature'], # 또는 [0] 인덱스
kind='average'
)
4. ICE Plot (Individual Conditional Expectation)
PartialDependenceDisplay.from_estimator(
model,
X_test,
features=['humidity'],
kind='individual'
)
| 기법 | 목적 | 장점 | 단점 | 언제 사용하는가? |
| Permutation Importance | 변수 중요도 | 해석 간단 | 상호작용 고려 어려움 | 모델 전체 방향성 파악 |
| SHAP Summary Plot | 전체 영향도 + 국소 영향도 | 가장 정교함, 논문 수준 | 계산 비쌈(비트리 모델) | 실무/논문/설명 책임 필요 |
| SHAP Force Plot | 개별 예측 근거 설명 | Explainable AI에서 필수 | 많은 샘플에 사용 어려움 | 고객 맞춤형 설명, 의사결정 |
| PDP(Partial Dependence) | 단일 변수 영향 | 직관적 | 이질성 반영 어려움 | 전체 패턴 확인 |
| ICE Plot | 개별 변화 패턴 | 개별 경향 파악 | 해석 난이도 있음 | 비선형 데이터 |
📌추가로 해야할 점
태블로를 활용한 데이터 시각화, 라이브 세션
'품질관리(QAQC) 데이터 부트캠프(본캠프)' 카테고리의 다른 글
| 본캠프_10주차(목)_TIL(ADF, ACF, ARIMA 정리) (0) | 2025.11.20 |
|---|---|
| 본캠프_10주차(수)_TIL(심화 프로젝트 기반 피드백) (0) | 2025.11.19 |
| 본캠프_10주차(월)_TIL(데이터 준비 & 통계적 검정 정리) (0) | 2025.11.17 |
| 본캠프_9주차(금)_TIL(심화 프로젝트 기간) (0) | 2025.11.14 |
| 본캠프_9주차(목)_TIL(심화 프로젝트 기간) (0) | 2025.11.13 |