Session 02 · Part 2 · 13:00 – 16:00

California Housing
S3 파이프라인 + EBM + DoWhy

소득 수준이 집값에 미치는 인과적 효과는 얼마인가?

🏠 180분 · 전체 3/5 20,640 주택 블록 · 회귀 문제

Part 2 — California Housing S3 + EBM + DoWhy

S3 파이프라인으로 실험을 버전 관리하고, EBM 회귀 + DoWhy 인과추론으로 집값의 원인을 찾습니다.

2-1 · S3 파이프라인 연동
13:00 – 13:45 · 45분 · conf / data / output 3-버킷 체계화

이 세션의 목적

오전 Wine 실습 결과물이 S3 output에 잘 저장됐는지 확인하고, Housing 데이터를 S3 data 버킷에 올립니다. conf yml 3종(env / meta / model)을 직접 작성해 실험 설정을 코드와 분리합니다. 이 패턴을 익히면 팀원 간 실험 재현이 가능해지고, 편의점 데이터 스프린트에서 동일한 구조를 그대로 쓸 수 있습니다.

conf yml 3종 작성

파일역할주요 내용
env.yml환경 설정region, env, S3 버킷명, user_id, log_level
meta.yml실험 메타데이터project명, experiment명, 설명, author, tags
model.yml모델 설정모델 타입, 하이퍼파라미터, 피처 목록, treatment 변수

📋 env.yml

region: ap-northeast-2
env: dev
conf_bucket: gs-retail-awesome-conf-{region}
data_bucket: gs-retail-awesome-data-{region}
output_bucket: gs-retail-awesome-output-{region}
user_id: your_id   # ← 변경
log_level: INFO

📋 model.yml

model_type: EBM_Regressor
framework: interpretML
hyperparameters:
  max_bins: 256
  interactions: 5
  outer_bags: 8
  random_state: 42
features:
  - MedInc
  - HouseAge
  - AveRooms
  - AveBedrms
  - Population
  - AveOccup
  - Latitude
  - Longitude
target: MedHouseVal
treatment: MedInc  # DoWhy용
1 conf yml 업로드 & S3 연동 04_s3_pipeline.ipynb

📋 복사해서 붙여넣기

import yaml, boto3, s3fs

with open("conf/env.yml")   as f: ENV   = yaml.safe_load(f)
with open("conf/meta.yml")  as f: META  = yaml.safe_load(f)
with open("conf/model.yml") as f: MODEL = yaml.safe_load(f)

USER_ID    = ENV['user_id']
PROJECT    = META['project']
EXPERIMENT = META['experiment']
REGION     = ENV['region']

CONF_PATH   = f"s3://gs-retail-awesome-conf-{REGION}/dev/{USER_ID}/{PROJECT}/{EXPERIMENT}"
DATA_PATH   = f"s3://gs-retail-awesome-data-{REGION}/dev/{USER_ID}/{PROJECT}/v1/data"
OUTPUT_PATH = f"s3://gs-retail-awesome-output-{REGION}/dev/{USER_ID}/{PROJECT}/{EXPERIMENT}"

s3 = boto3.client('s3')
CONF_BUCKET = f"gs-retail-awesome-conf-{REGION}"
for fname in ["env.yml", "meta.yml", "model.yml"]:
    s3.upload_file(f"conf/{fname}", CONF_BUCKET,
        f"dev/{USER_ID}/{PROJECT}/{EXPERIMENT}/{fname}")
    print(f"  ✓ {fname} → conf 버킷")

2-2 · Housing EBM 실습
13:45 – 14:30 · 45분 · 회귀 EBM & shape function 해석

회귀 EBM — 분류와의 차이

Wine에서는 ExplainableBoostingClassifier(분류)를 썼습니다. Housing에서는 ExplainableBoostingRegressor(회귀)를 씁니다. Shape function의 y축이 "클래스 확률 기여도"에서 "$단위 기여도"로 바뀝니다. "이 피처가 1단위 증가할 때 집값이 평균 얼마나 변하는가"를 구간별로 읽을 수 있습니다.

2 Housing 데이터 로드 & EBM 회귀 학습 05_housing_ebm.ipynb

📋 복사해서 붙여넣기

from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from interpret.glassbox import ExplainableBoostingRegressor
from sklearn.metrics import mean_squared_error, r2_score
import numpy as np

# ── 데이터 로드
housing = fetch_california_housing()
X = pd.DataFrame(housing.data, columns=housing.feature_names)
y = pd.Series(housing.target, name="MedHouseVal")

# ── 70/15/15 분리
X_train, X_tmp, y_train, y_tmp = train_test_split(X, y, test_size=0.3, random_state=42)
X_val, X_test, y_val, y_test   = train_test_split(X_tmp, y_tmp, test_size=0.5, random_state=42)

# ── EBM 회귀 학습
ebm_reg = ExplainableBoostingRegressor(
    max_bins=256, interactions=5, outer_bags=8, random_state=42)
ebm_reg.fit(X_train, y_train)

# ── 성능 평가
val_pred = ebm_reg.predict(X_val)
val_rmse = np.sqrt(mean_squared_error(y_val, val_pred))
val_r2   = r2_score(y_val, val_pred)
print(f"Val RMSE: {val_rmse:.4f} (~${val_rmse*100:.0f}K)")
print(f"Val R²  : {val_r2:.4f}")
ℹ️ 예상 성능: Val RMSE ≈ 0.46 (~$46K), R² ≈ 0.85. 해석 가능 모델 기준으로 높은 수치입니다. Latitude, Longitude가 feature importance 1, 2위로 나타납니다. 그러나 이는 Confounder(지리적 교란 변수)입니다 — DoWhy에서 통제해야 합니다.
FeatureEBM Importance역할 태그DoWhy 처리
Latitude~0.95 (1위)Confounder교란 변수로 통제
Longitude~0.83 (2위)Confounder교란 변수로 통제
MedInc~0.45 (3위)Treatment (처치)인과 효과 추정 대상
AveOccup~0.23Confounder교란 변수로 통제
AveRooms~0.10Secondary보조 맥락 변수

2-3 · DoWhy 인과추론
14:30 – 16:00 · 90분 · DAG → identify → estimate → refute

왜 DoWhy가 필요한가?

EBM에서 Latitude(위도)가 1위, MedInc(소득)이 3위로 나왔습니다. 그렇다고 "위도가 집값의 주요 원인"이라고 말할 수 없습니다. 위도는 LA권/SF권처럼 지역 클러스터를 나타내는 교란 변수입니다. DoWhy는 이 교란 변수를 명시적으로 통제하고, "소득 수준만 바꾸면 집값이 실제로 얼마나 달라지는가"를 추정합니다.

3 인과 DAG 정의 & DoWhy 모델 생성 06_dowhy.ipynb

📋 복사해서 붙여넣기

import dowhy
from dowhy import CausalModel

# ── 인과 DAG 정의 (GML 형식)
# MedInc(처치) → MedHouseVal(결과)
# Latitude, Longitude, AveRooms, AveOccup → MedInc AND MedHouseVal (교란)
causal_graph = """
digraph {
    MedInc -> MedHouseVal;
    Latitude -> MedInc; Latitude -> MedHouseVal;
    Longitude -> MedInc; Longitude -> MedHouseVal;
    AveRooms -> MedInc; AveRooms -> MedHouseVal;
    AveOccup -> MedInc; AveOccup -> MedHouseVal;
    HouseAge -> MedHouseVal;
}
"""

model = CausalModel(
    data=pd.concat([X_train, y_train], axis=1),
    treatment="MedInc",
    outcome="MedHouseVal",
    graph=causal_graph
)
print("✓ 인과 모델 생성 완료")
4 Identify → Estimate → Refute 06_dowhy.ipynb

📋 복사해서 붙여넣기

# ── STEP 1: Identify (인과 효과 식별)
identified_estimand = model.identify_effect(proceed_when_unidentifiable=True)
print(identified_estimand)

# ── STEP 2: Estimate (ATE 추정)
estimate = model.estimate_effect(
    identified_estimand,
    method_name="backdoor.linear_regression",
    target_units="ate"
)
print(f"\nATE (MedInc → MedHouseVal): {estimate.value:.4f}")
print(f"해석: MedInc $10K 증가 시 집값 평균 ${estimate.value * 100000:.0f} 변화")

# ── STEP 3: Refute (인과 효과 검증)
refute_random = model.refute_estimate(
    identified_estimand, estimate,
    method_name="random_common_cause"
)
print(refute_random)

refute_placebo = model.refute_estimate(
    identified_estimand, estimate,
    method_name="placebo_treatment_refuter",
    placebo_type="permute"
)
print(refute_placebo)
ℹ️ Refutation 읽는 법: Random common cause: 랜덤 교란 변수를 추가해도 ATE가 크게 변하지 않으면 robust합니다. Placebo treatment: 처치 변수를 랜덤 치환했을 때 ATE가 0에 가까워야 합니다. 두 검증이 통과하면 인과 추론이 신뢰할 만합니다.

Primary / Secondary Index 선정

DoWhy 결과를 바탕으로 편의점 데이터 스프린트에서 쓸 index 체계를 정의합니다.

구분피처근거편의점 대응
Primary IndexMedIncDoWhy ATE +$35,390 — 인과 효과 확인area_income_level, foot_traffic
Secondary IndexHouseAgeATE +$550 — 미미하지만 양(+). 감가 없음 확인store_age, location_type
ConfounderLatitude, LongitudeEBM importance 높지만 인과 경로 아님. 통제 필요입지 유형, 경쟁점 수
ConfounderAveOccup과밀 거주 효과. 처치·결과 양쪽에 영향요일, 공휴일
핵심 인사이트: EBM이 Latitude를 1위로 꼽았지만, DoWhy가 이를 교란 변수로 통제한 후 MedInc의 인과 효과(+$35,390)를 확인했습니다. "위치가 중요하다"는 EBM 결과와 "소득이 집값의 실제 원인"이라는 DoWhy 결과가 서로 다른 것을 체감했습니까? 이 차이가 오늘 워크샵의 핵심입니다.
⚠️ HouseAge ATE +$550의 해석: 연식이 1년 늘어도 집값이 오릅니다. 캘리포니아 도심 오래된 주택은 희소성·역사성으로 프리미엄을 받는 것이 통계적으로 확인됩니다. "오래된 집이라 싸다"는 통념을 데이터가 반박합니다.