빅데이터 분석기사 실기 2유형 (2) — 회귀 모델링 완전정리
2유형 회귀 문제 풀이 흐름. 분류와의 차이, RMSE/R² 평가, 회귀 전용 모델까지 시험장 템플릿으로 정리.
분류와 회귀의 코드 뼈대는 거의 같다. 바뀌는 건 모델 이름과 평가지표뿐. 흐름이 이미 머리에 들어와 있다면 회귀는 보너스 점수다.
분류 vs 회귀 — 한 줄로
| 구분 | 예측하는 것 | 평가지표 | 대표 모델 |
|---|---|---|---|
| 분류 | 클래스(0/1, A/B/C) | AUC, Accuracy, F1 | RandomForestClassifier |
| 회귀 | 연속 수치값 | RMSE, MAE, R² | RandomForestRegressor |
모델명 끝이 Classifier ↔ Regressor, 그 외 거의 동일하다.
회귀 풀이 7단계 (분류와 동일)
1. 데이터 불러오기
2. 데이터 탐색
3. 전처리 (결측치, 인코딩, 스케일링)
4. train / validation 분리
5. 모델 학습
6. 검증 성능 확인 (RMSE 등)
7. test 예측 → submission.csv
차이가 생기는 곳은 4번의 stratify, 5번의 모델, 6번의 지표 정도다.
시험장 회귀 템플릿
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
# 1. 로드
X_train = pd.read_csv("X_train.csv")
y_train = pd.read_csv("y_train.csv")
X_test = pd.read_csv("X_test.csv")
test_id = X_test["ID"]
X_train = X_train.drop(columns=["ID"])
X_test = X_test.drop(columns=["ID"])
y_train = y_train["target"]
# 2. 결측치/인코딩 (분류 때와 동일)
num_cols = X_train.select_dtypes(include="number").columns
cat_cols = X_train.select_dtypes(include="object").columns
for c in num_cols:
m = X_train[c].median()
X_train[c] = X_train[c].fillna(m)
X_test[c] = X_test[c].fillna(m)
for c in cat_cols:
m = X_train[c].mode()[0]
X_train[c] = X_train[c].fillna(m)
X_test[c] = X_test[c].fillna(m)
all_df = pd.concat([X_train, X_test])
all_df = pd.get_dummies(all_df, columns=cat_cols)
X_train, X_test = all_df.iloc[:len(X_train)], all_df.iloc[len(X_train):]
회귀에서 달라지는 포인트
1. train_test_split에서 stratify 빼기
분류에서는 라벨 비율 유지를 위해 stratify=y_train를 줬다.
회귀에서는 안 쓴다. 연속값은 stratify 대상이 아니다.
X_tr, X_val, y_tr, y_val = train_test_split(
X_train, y_train, test_size=0.2, random_state=42,
# stratify=y_train ← 회귀에서는 제거
)
2. 모델 이름이 Regressor
from sklearn.ensemble import RandomForestRegressor
model = RandomForestRegressor(
n_estimators=300,
max_depth=10,
random_state=42,
n_jobs=-1,
)
model.fit(X_tr, y_tr)
pred = model.predict(X_val) # predict_proba 없음. 그냥 predict
3. 평가지표 (RMSE / MAE / R²)
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
rmse = mean_squared_error(y_val, pred, squared=False) # squared=False가 RMSE
mae = mean_absolute_error(y_val, pred)
r2 = r2_score(y_val, pred)
print(f"RMSE: {rmse:.4f}")
print(f"MAE : {mae:.4f}")
print(f"R² : {r2:.4f}")
sklearn 일부 버전에서
squared=False가 제거되고root_mean_squared_error함수로 빠진다. 시험 환경이 구버전이면np.sqrt(mean_squared_error(...))로 우회.
rmse = np.sqrt(mean_squared_error(y_val, pred)) # 모든 버전에서 통함
평가지표 한 줄 정의 (외워두면 든든)
MSE = 평균( (실제 - 예측)² ) 작을수록 좋음
RMSE = sqrt(MSE) 단위가 원래 값과 같아 해석 쉬움
MAE = 평균( |실제 - 예측| ) 이상치에 덜 민감
R² = 1 - SS_res/SS_tot 1에 가까울수록 좋음, 음수면 평균 예측만도 못함
시험에서 "RMSE를 구하라" 가 가장 자주 나온다. MAE, R²는 보조로 본다.
추천 모델 — 회귀도 트리 계열 우선
| 모델 | 클래스 | 메모 |
|---|---|---|
| RandomForestRegressor | sklearn | 1순위, 안정적 |
| XGBRegressor | xgboost | 보통 RMSE 최소 |
| LGBMRegressor | lightgbm | 빠름 |
| LinearRegression | sklearn | 베이스라인 |
| Ridge / Lasso | sklearn | 다중공선성·과적합 방지 |
XGBoost 회귀
from xgboost import XGBRegressor
model = XGBRegressor(
n_estimators=500,
learning_rate=0.05,
max_depth=6,
random_state=42,
)
model.fit(X_tr, y_tr)
pred = model.predict(X_val)
print("RMSE:", np.sqrt(mean_squared_error(y_val, pred)))
선형회귀 (간단한 데이터일 때)
from sklearn.linear_model import LinearRegression, Ridge, Lasso
model = LinearRegression().fit(X_tr, y_tr)
model = Ridge(alpha=1.0).fit(X_tr, y_tr) # L2 정규화
model = Lasso(alpha=0.1).fit(X_tr, y_tr) # L1 정규화
선형 계열을 쓸 때는 스케일링 필수. 트리 계열을 쓸 때는 스케일링 생략 가능.
타깃 변환 — 점수가 안 나올 때 마지막 카드
타깃 값의 분포가 한쪽으로 심하게 치우쳐 있으면 (예: 집값, 매출),
로그 변환 후 학습 → 예측값을 다시 exp로 되돌리는 트릭이 자주 먹힌다.
import numpy as np
# 학습 시 log 변환
y_log = np.log1p(y_tr)
model.fit(X_tr, y_log)
# 예측은 다시 exp로 되돌리기
pred_log = model.predict(X_val)
pred = np.expm1(pred_log)
print("RMSE:", np.sqrt(mean_squared_error(y_val, pred)))
log1p(x) = log(1+x), expm1(x) = exp(x)-1. 0이 섞여 있어도 안전하다.
단, 평가가 log scale로 채점되는 경우도 있다. 문제에서 명시한 단위를 그대로 따라야 한다.
제출 CSV (회귀)
pred_test = model.predict(X_test)
submission = pd.DataFrame({
"ID": test_id,
"pred": pred_test,
})
submission.to_csv("result.csv", index=False)
- 분류와 형식은 같다.
index=False잊지 말기. - 음수가 나오면 안 되는 문제(예: 가격 예측)는
np.clip(pred_test, 0, None)로 한 번 더 잘라준다.
pred_test = np.clip(pred_test, 0, None) # 음수 제거
분류·회귀 통합 체크리스트
| 단계 | 분류 | 회귀 |
|---|---|---|
| 모델 클래스 | ~~Classifier | ~~Regressor |
train_test_split |
stratify=y |
stratify 없음 |
| 예측 | predict 또는 predict_proba |
predict만 |
| 평가 | AUC / ACC / F1 | RMSE / MAE / R² |
| 제출값 | 클래스 또는 확률 | 연속 수치 |
| 음수 처리 | 해당 없음 | 필요시 np.clip |
정리
- 회귀는 분류와 풀이 흐름이 동일하다. 모델·지표만 바꾸면 끝.
- RandomForestRegressor → XGBRegressor 순서로 시도하면 안정적이다.
- RMSE가 회귀의 디폴트 평가지표.
squared=False또는np.sqrt(MSE). - 타깃 분포가 치우치면
log1p→ 학습 →expm1트릭. - 제출은 컬럼명 정확히,
index=False필수.
2유형은 흐름만 외우면 사실상 보너스 점수다. 다음 글부터는 통계 검정이 본격적으로 들어오는 3유형 으로 넘어간다.