빅데이터 분석기사 실기 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

정리

  1. 회귀는 분류와 풀이 흐름이 동일하다. 모델·지표만 바꾸면 끝.
  2. RandomForestRegressor → XGBRegressor 순서로 시도하면 안정적이다.
  3. RMSE가 회귀의 디폴트 평가지표. squared=False 또는 np.sqrt(MSE).
  4. 타깃 분포가 치우치면 log1p → 학습 → expm1 트릭.
  5. 제출은 컬럼명 정확히, index=False 필수.

2유형은 흐름만 외우면 사실상 보너스 점수다. 다음 글부터는 통계 검정이 본격적으로 들어오는 3유형 으로 넘어간다.