Causal Inference (인과 추론)¶
메타 정보¶
| 항목 | 내용 |
|---|---|
| 분류 | Statistical Inference / Treatment Effect Estimation |
| 핵심 논문 | "Estimating Causal Effects of Treatments in Randomized and Nonrandomized Studies" (Rubin, 1974), "Causal Diagrams for Empirical Research" (Pearl, 1995), "Double/Debiased Machine Learning" (Chernozhukov et al., Econometrics Journal 2018), "Metalearners for Estimating Heterogeneous Treatment Effects" (Kunzel et al., PNAS 2019) |
| 주요 저자 | Donald Rubin (Potential Outcomes), Judea Pearl (SCM/do-calculus), Victor Chernozhukov (DML), Susan Athey (Causal Forest, AAAI Squire Award), Guido Imbens (Nobel 2021) |
| 핵심 개념 | 관측 또는 실험 데이터에서 처리(treatment)가 결과(outcome)에 미치는 인과적 효과를 식별하고 추정하는 통계적 프레임워크 |
| 관련 분야 | Econometrics, Epidemiology, A/B Testing, Policy Evaluation, Uplift Modeling |
정의¶
Causal Inference는 "X가 Y를 일으키는가?"라는 질문에 데이터 기반으로 답하기 위한 방법론 체계다. 단순 상관관계(correlation)와 인과관계(causation)를 구분하고, 관측 데이터에서 교란변수(confounder)의 영향을 제거하여 처리 효과를 추정한다.
인과 추론의 근본 문제 (Fundamental Problem of Causal Inference):
단위 i에 대해:
Y_i(1) = 처리를 받았을 때의 결과 (potential outcome under treatment)
Y_i(0) = 처리를 받지 않았을 때의 결과 (potential outcome under control)
개인 인과 효과: tau_i = Y_i(1) - Y_i(0)
문제: 한 단위는 처리를 받거나 받지 않거나 둘 중 하나만 관측 가능
-> 반사실(counterfactual)은 관측 불가
관측 데이터:
단위 1: D=1, Y=Y_1(1), Y_1(0)=? <-- 반사실 결측
단위 2: D=0, Y=Y_2(0), Y_2(1)=? <-- 반사실 결측
단위 3: D=1, Y=Y_3(1), Y_3(0)=?
따라서 개인 수준 인과 효과는 추정 불가
-> 집단 수준 평균 효과(ATE, ATT 등)를 추정하는 것이 목표
핵심 프레임워크¶
1. Potential Outcomes Framework (Rubin Causal Model)¶
Rubin (1974)이 체계화한 프레임워크로, 반사실(counterfactual)에 기반한다.
핵심 가정 (Identifiability Assumptions):
1. SUTVA (Stable Unit Treatment Value Assumption)
- 한 단위의 결과는 다른 단위의 처리 상태에 영향받지 않음
- 처리의 버전이 하나만 존재
2. Ignorability (Unconfoundedness / No Unmeasured Confounders)
(Y(0), Y(1)) _||_ D | X
- 공변량 X 조건부에서 처리 배정과 잠재 결과가 독립
3. Positivity (Overlap)
0 < P(D=1|X=x) < 1, for all x
- 모든 공변량 값에서 처리/비처리 모두 가능
세 가정이 충족되면:
ATE = E[Y(1) - Y(0)]
= E_X[E[Y|D=1,X] - E[Y|D=0,X]] <-- 관측 데이터로 추정 가능
2. Structural Causal Model (Pearl)¶
Pearl의 SCM은 인과 관계를 방향성 비순환 그래프(DAG)와 구조방정식으로 표현한다.
구조 방정식:
X_j = f_j(pa(X_j), U_j) j = 1, ..., p
pa(X_j): X_j의 부모 노드 (직접 원인)
U_j: 외생 변수 (noise)
f_j: 구조 함수 (인과 메커니즘)
DAG 예시:
U_X U_D U_Y
| | |
v v v
X ---> D ---> Y
| ^
+-------------+
X: 교란변수 (confounder)
D: 처리 (treatment)
Y: 결과 (outcome)
do-calculus:
P(Y|do(D=d)) =/= P(Y|D=d) (개입 =/= 관측)
Backdoor Adjustment:
P(Y|do(D=d)) = sum_x P(Y|D=d, X=x) * P(X=x)
-> X를 조건부로 조정하면 관측 데이터에서 인과 효과 식별 가능
3. 두 프레임워크의 관계¶
| 측면 | Potential Outcomes (Rubin) | SCM (Pearl) |
|---|---|---|
| 표현 | 반사실 결과 Y(d) | do-operator, DAG |
| 식별 조건 | Ignorability 가정 | Backdoor/Frontdoor criterion |
| 강점 | 추정량 설계, 통계적 추론 | 인과 구조 시각화, 식별 전략 도출 |
| 약점 | 인과 구조 명시적 표현 어려움 | 추정 세부사항이 덜 발달 |
| 실무 사용 | A/B 테스트, 경제학, 역학 | 인과 발견, 반사실 추론 |
실무에서는 두 프레임워크를 상호보완적으로 사용: Pearl의 DAG로 인과 구조를 파악하고, Rubin의 프레임워크로 추정량을 설계한다.
주요 추정 대상 (Estimands)¶
ATE = E[Y(1) - Y(0)]
전체 모집단에 대한 평균 처리 효과
ATT = E[Y(1) - Y(0) | D=1]
실제 처리받은 집단에 대한 효과
ATC = E[Y(1) - Y(0) | D=0]
처리받지 않은 집단이 처리를 받았다면의 효과
CATE = E[Y(1) - Y(0) | X=x]
공변량 x 조건부 개인화된 효과 (Heterogeneous Treatment Effect)
LATE = E[Y(1) - Y(0) | Compliers]
도구변수 설정에서 순응자에 대한 국소 효과
전통적 추정 방법¶
1. Inverse Probability Weighting (IPW)¶
import numpy as np
from sklearn.linear_model import LogisticRegression
def ipw_ate(Y, D, X):
"""
Inverse Probability Weighting으로 ATE 추정
Parameters
----------
Y : array, shape (n,) - 결과 변수
D : array, shape (n,) - 처리 변수 (0 or 1)
X : array, shape (n, p) - 공변량
Returns
-------
ate : float - 추정된 ATE
"""
# Propensity score 추정: P(D=1|X)
ps_model = LogisticRegression(max_iter=1000)
ps_model.fit(X, D)
e_x = ps_model.predict_proba(X)[:, 1]
# Trimming: 극단적 propensity score 제거 (positivity 보강)
e_x = np.clip(e_x, 0.01, 0.99)
# Horvitz-Thompson 추정량
ate = np.mean(D * Y / e_x) - np.mean((1 - D) * Y / (1 - e_x))
return ate
2. Matching¶
from sklearn.neighbors import NearestNeighbors
def matching_ate(Y, D, X, n_neighbors=1):
"""
Nearest-Neighbor Matching으로 ATE 추정
반사실을 가장 유사한 반대 처리군 관측치로 대체
"""
treated = D == 1
control = D == 0
# 처리군 -> 대조군 매칭
nn_control = NearestNeighbors(n_neighbors=n_neighbors)
nn_control.fit(X[control])
_, idx_control = nn_control.kneighbors(X[treated])
Y0_matched = Y[control][idx_control].mean(axis=1)
# 대조군 -> 처리군 매칭
nn_treated = NearestNeighbors(n_neighbors=n_neighbors)
nn_treated.fit(X[treated])
_, idx_treated = nn_treated.kneighbors(X[control])
Y1_matched = Y[treated][idx_treated].mean(axis=1)
# ATE = 양쪽 방향 매칭의 평균
ate_treated = np.mean(Y[treated] - Y0_matched)
ate_control = np.mean(Y1_matched - Y[control])
ate = (np.sum(treated) * ate_treated + np.sum(control) * ate_control) / len(Y)
return ate
3. Doubly Robust Estimation (AIPW)¶
def aipw_ate(Y, D, X, outcome_model, ps_model):
"""
Augmented Inverse Probability Weighting (Doubly Robust)
outcome model 또는 propensity model 중 하나만 올바르면 일치 추정량
psi(1) = mu_1(X) + D*(Y - mu_1(X)) / e(X)
psi(0) = mu_0(X) + (1-D)*(Y - mu_0(X)) / (1-e(X))
ATE = E[psi(1) - psi(0)]
"""
# Propensity score
e_x = ps_model.predict_proba(X)[:, 1]
e_x = np.clip(e_x, 0.01, 0.99)
# Outcome model 예측
mu_1 = outcome_model.predict(X) # E[Y|D=1, X] 추정
mu_0 = outcome_model.predict(X) # E[Y|D=0, X] 추정
# (실제로는 별도 모델 또는 D를 feature에 포함하여 추정)
# AIPW 추정량
psi_1 = mu_1 + D * (Y - mu_1) / e_x
psi_0 = mu_0 + (1 - D) * (Y - mu_0) / (1 - e_x)
ate = np.mean(psi_1 - psi_0)
return ate
ML 기반 인과 추론 (Heterogeneous Treatment Effects)¶
Meta-Learners¶
CATE 추정을 위한 meta-learner 전략 (Kunzel et al., PNAS 2019):
S-Learner:
단일 모델로 D를 feature에 포함
tau(x) = mu_hat(x, D=1) - mu_hat(x, D=0)
장점: 단순 | 단점: 처리 효과가 작으면 모델이 D를 무시할 수 있음
T-Learner:
처리군/대조군 각각 별도 모델 학습
tau(x) = mu_1_hat(x) - mu_0_hat(x)
장점: 유연 | 단점: 데이터가 불균형이면 추정 불안정
X-Learner (Kunzel et al., 2019):
1단계: T-Learner로 mu_0, mu_1 학습
2단계: 의사 처리효과 계산
D_1^i = Y_1^i - mu_0(X_1^i) (처리군)
D_0^i = mu_1(X_0^i) - Y_0^i (대조군)
3단계: tau_1(x), tau_0(x) 각각 학습
4단계: 가중 결합
tau(x) = g(x)*tau_0(x) + (1-g(x))*tau_1(x)
장점: 처리/대조군 크기 불균형에 강건
R-Learner (Nie & Wager, 2021):
Robinson decomposition 기반
Y - m(X) = (D - e(X))*tau(X) + epsilon
tau(x) = argmin E[(Y_tilde - (D - e(X))*tau(X))^2 + Lambda*reg(tau)]
장점: DML과 이론적 연결, 정규화 편향 제거
DR-Learner (Kennedy, 2023):
Doubly-robust pseudo-outcome 사용
Gamma_i = mu_1(X_i) - mu_0(X_i) + D_i*(Y_i-mu_1(X_i))/e(X_i)
- (1-D_i)*(Y_i-mu_0(X_i))/(1-e(X_i))
tau(x)를 Gamma에 대해 회귀
장점: 이중 강건성 + CATE의 최적 수렴 속도
Causal Forest (Athey & Imbens, 2018)¶
from econml.dml import CausalForestDML
from sklearn.ensemble import GradientBoostingRegressor
# Causal Forest for CATE estimation
cf = CausalForestDML(
model_y=GradientBoostingRegressor(n_estimators=200, max_depth=5),
model_t=GradientBoostingRegressor(n_estimators=200, max_depth=5),
n_estimators=2000,
min_samples_leaf=5,
max_depth=None,
cv=5,
random_state=42
)
# X_effect: CATE가 의존하는 변수 (effect modifiers)
# X_confounders: 교란변수 (nuisance)
cf.fit(Y, T=D, X=X_effect, W=X_confounders)
# 개인별 처리 효과 추정
cate = cf.effect(X_effect_test)
cate_lower, cate_upper = cf.effect_interval(X_effect_test, alpha=0.05)
# 변수 중요도 (어떤 변수가 처리 효과 이질성을 설명하는지)
importances = cf.feature_importances_
# Sherpa plot: CATE 분포 시각화
import matplotlib.pyplot as plt
plt.hist(cate, bins=50, edgecolor='black', alpha=0.7)
plt.axvline(x=0, color='red', linestyle='--')
plt.xlabel('Estimated CATE')
plt.ylabel('Frequency')
plt.title('Distribution of Heterogeneous Treatment Effects')
plt.show()
Double Machine Learning (DML)¶
Chernozhukov et al. (2018)의 DML 프레임워크 -- 별도 상세 문서 참조: double-machine-learning.md
from doubleml import DoubleMLData, DoubleMLPLR
from lightgbm import LGBMRegressor
# 데이터 구성
dml_data = DoubleMLData.from_arrays(x=X, y=Y, d=D)
# DML with cross-fitting
dml = DoubleMLPLR(
obj_dml_data=dml_data,
ml_l=LGBMRegressor(n_estimators=300, verbose=-1),
ml_m=LGBMRegressor(n_estimators=300, verbose=-1),
n_folds=5,
score='partialling out'
)
dml.fit()
print(f"ATE: {dml.coef[0]:.4f} (SE: {dml.se[0]:.4f})")
print(f"95% CI: {dml.confint().values[0]}")
준실험적 방법 (Quasi-Experimental Methods)¶
관측 데이터에서 실험적 변이를 활용하는 방법들:
Instrumental Variables (IV)¶
적용 조건:
Z --> D --> Y (Z가 D에만 영향, Y에 직접 영향 없음)
Z: 도구변수 (instrument)
조건:
1. Relevance: Cov(Z, D) != 0
2. Exclusion: Z -> Y 경로가 D를 통해서만 존재
3. Independence: Z _||_ U (관측되지 않은 교란과 독립)
2SLS (Two-Stage Least Squares):
1단계: D_hat = alpha + beta*Z + gamma*X + v
2단계: Y = theta*D_hat + delta*X + epsilon
추정: LATE (Local Average Treatment Effect) for compliers
Difference-in-Differences (DiD)¶
설정: 처리군/대조군, 처리 전/후 두 시점
t=0 (처리 전) t=1 (처리 후)
처리군: Y_T(0) Y_T(1)
대조군: Y_C(0) Y_C(1)
DiD 추정량:
tau_DiD = [E(Y_T(1)) - E(Y_T(0))] - [E(Y_C(1)) - E(Y_C(0))]
= (처리군 변화) - (대조군 변화)
핵심 가정: Parallel Trends
처리가 없었다면 두 그룹의 결과 변화 추세가 동일
확장:
- Staggered DiD: 처리 시점이 그룹마다 다른 경우
Callaway & Sant'Anna (2021), Sun & Abraham (2021)
- Synthetic DiD: Synthetic Control + DiD 결합
Arkhangelsky et al. (AER 2021)
import statsmodels.formula.api as smf
# 기본 DiD 회귀
# treated: 처리군 여부 (0/1)
# post: 처리 후 시점 여부 (0/1)
# treated:post 상호작용항이 DiD 추정량
model = smf.ols('Y ~ treated + post + treated:post', data=df).fit()
did_estimate = model.params['treated:post']
print(f"DiD ATE: {did_estimate:.4f} (p={model.pvalues['treated:post']:.4f})")
Regression Discontinuity Design (RDD)¶
설정: running variable X가 cutoff c를 기준으로 처리 배정
D = 1 if X >= c, else 0 (Sharp RDD)
LATE at cutoff:
tau_RDD = lim_{x->c+} E[Y|X=x] - lim_{x->c-} E[Y|X=x]
핵심: cutoff 근처에서 처리 배정이 무작위에 가까움
-> 국소적 무작위 실험과 유사
Y | . .
| . . * *
| . * *
| . * *
|. | *
+-------------|-------------> X
c (cutoff)
. = 대조군 관측치
* = 처리군 관측치
수직 점프 = 인과 효과
Synthetic Control Method¶
설정: 처리를 받은 단위 1개, 대조 단위 J개, 시계열
아이디어: 대조 단위들의 가중 평균으로
처리 단위의 반사실(처리 없었을 때의 결과)을 합성
W* = argmin ||Y_1 - Y_0 * W|| subject to sum(W) = 1, W >= 0
처리 효과 = Y_1(실제) - Y_1_synthetic(반사실)
사용 예: 정책 효과 평가 (최저임금, 규제, 금연법 등)
Abadie, Diamond & Hainmueller (JASA 2010)
인과 발견 (Causal Discovery)¶
데이터에서 인과 그래프 구조를 학습하는 방법:
제약 기반 (Constraint-based):
PC Algorithm (Spirtes et al., 2000)
조건부 독립 테스트로 엣지 제거 -> 방향 결정
O(p^d) 복잡도 (d: 최대 이웃 수)
점수 기반 (Score-based):
GES (Greedy Equivalence Search)
BIC 등의 점수 최적화로 DAG 탐색
연속 최적화 (Differentiable):
NOTEARS (Zheng et al., NeurIPS 2018)
min_W F(W) subject to h(W) = tr(e^{W*W}) - d = 0
비순환성 제약을 연속 함수로 변환 -> gradient 기반 최적화
DAGMA (Bello et al., NeurIPS 2022)
log-det 기반 비순환성 제약으로 NOTEARS 개선
# causal-learn 라이브러리를 사용한 인과 발견
from causallearn.search.ConstraintBased.PC import pc
from causallearn.search.ScoreBased.GES import ges
# PC Algorithm
cg = pc(data, alpha=0.05, indep_test='fisherz')
cg.draw_pydot_graph() # DAG 시각화
# GES (score-based)
record = ges(data, score_func='local_score_BIC')
record['G'].draw_pydot_graph()
실무 파이프라인¶
인과 추론 실무 워크플로우:
1. 문제 정의
|-- 처리(D), 결과(Y), 추정 대상(ATE/ATT/CATE) 명확화
|-- 인과 질문을 수학적 estimand로 번역
|
2. 인과 구조 파악
|-- 도메인 지식으로 DAG 구성
|-- 교란변수, 매개변수, 충돌변수 식별
|-- 조정 집합(adjustment set) 결정
|
3. 식별 전략 선택
|-- Backdoor criterion 충족? -> 조건부 조정
|-- Instrument 존재? -> IV/2SLS
|-- Cutoff 존재? -> RDD
|-- 처리 전후 비교 가능? -> DiD
|
4. 추정 방법 선택
|-- ATE만 필요? -> DML, AIPW
|-- CATE 필요? -> Causal Forest, Meta-Learners
|-- 시계열 정책? -> Synthetic Control
|
5. 민감도 분석 및 검증
|-- Placebo test: 가짜 처리 적용
|-- Sensitivity analysis: 관측되지 않은 교란에 대한 강건성
|-- Refutation tests (DoWhy)
|-- Bootstrap confidence intervals
DoWhy를 활용한 전체 파이프라인¶
import dowhy
from dowhy import CausalModel
# 1. 인과 모델 정의 (DAG 명시)
model = CausalModel(
data=df,
treatment='treatment',
outcome='outcome',
common_causes=['age', 'income', 'education'],
instruments=['distance_to_clinic'],
graph="""
digraph {
age -> treatment; age -> outcome;
income -> treatment; income -> outcome;
education -> treatment; education -> outcome;
distance_to_clinic -> treatment;
treatment -> outcome;
}
"""
)
# 2. 인과 효과 식별
identified_estimand = model.identify_effect()
print(identified_estimand)
# Backdoor criterion: {age, income, education}
# 3. 추정
estimate = model.estimate_effect(
identified_estimand,
method_name="backdoor.econml.dml.DML",
method_params={
"init_params": {
"model_y": LGBMRegressor(verbose=-1),
"model_t": LGBMRegressor(verbose=-1),
"cv": 5,
},
"fit_params": {}
}
)
print(f"ATE: {estimate.value:.4f}")
# 4. 반박 테스트 (Refutation)
# 무작위 공통 원인 추가 -> 추정치 변하면 안 됨
refute_random = model.refute_estimate(
identified_estimand, estimate,
method_name="random_common_cause"
)
print(refute_random)
# Placebo 처리 -> 추정치가 0에 가까워야 함
refute_placebo = model.refute_estimate(
identified_estimand, estimate,
method_name="placebo_treatment_refuter",
placebo_type="permute"
)
print(refute_placebo)
# 데이터 부분집합 -> 추정치가 안정적이어야 함
refute_subset = model.refute_estimate(
identified_estimand, estimate,
method_name="data_subset_refuter",
subset_fraction=0.8
)
print(refute_subset)
방법론 선택 가이드¶
Q: 무작위 실험(RCT) 데이터인가?
|
+-- Yes --> 단순 평균 차이 or Regression adjustment
| CATE 필요시 -> Causal Forest
|
+-- No (관측 데이터) --> Q: 관측되지 않은 교란이 우려되는가?
|
+-- No (교란변수 충분히 관측) --> Q: ATE or CATE?
| |
| +-- ATE --> DML (Chernozhukov et al.)
| | AIPW (doubly robust)
| |
| +-- CATE --> Q: 처리/대조 비율?
| |
| +-- 균형 --> T-Learner, Causal Forest
| +-- 불균형 --> X-Learner, DR-Learner
|
+-- Yes (관측되지 않은 교란 가능) --> Q: 자연실험 요소가 있는가?
|
+-- 도구변수 존재 --> IV / 2SLS
+-- 임계값 존재 --> RDD
+-- 시점 전후 비교 --> DiD
+-- 단일 처리 단위 --> Synthetic Control
+-- 없음 --> Sensitivity Analysis 수행
Partial Identification (Manski bounds)
주요 라이브러리¶
| 라이브러리 | 주요 기능 | 언어 |
|---|---|---|
| DoWhy (Microsoft/PyWhy) | 인과 모델링, 식별, 추정, 반박 | Python |
| EconML (Microsoft) | CATE 추정 (DML, Causal Forest, Meta-Learners) | Python |
| DoubleML | DML 프레임워크 특화 | Python, R |
| causal-learn | 인과 발견 (PC, GES, NOTEARS) | Python |
| CausalML (Uber) | Uplift modeling, Meta-Learners | Python |
| grf (R) | Generalized Random Forests (Causal Forest 원저자 구현) | R |
| Synthetic Control (R) | Synthetic control method | R |
한계와 주의사항¶
| 한계 | 설명 | 완화 방법 |
|---|---|---|
| 관측되지 않은 교란 | Ignorability 가정 검증 불가 | Sensitivity analysis, IV, DiD |
| Positivity 위반 | 특정 X값에서 처리 확률이 0 또는 1에 가까움 | Trimming, overlap weighting |
| SUTVA 위반 | 네트워크 효과, spillover | Cluster randomization, interference models |
| 모델 오특정 | 구조 가정이 틀릴 수 있음 | Doubly robust 방법, 민감도 분석 |
| 외적 타당성 | 추정 결과의 다른 모집단 일반화 | Transportability (Pearl), 외부 검증 |
| 시간 변동 교란 | 시간에 따라 교란 구조가 변함 | Marginal structural models, g-estimation |
2024-2025 주요 동향¶
-
Causal Representation Learning: 고차원 관측에서 인과적으로 의미 있는 저차원 잠재 변수를 학습. Multi-environment 데이터를 활용한 식별 이론 발전 (ICLR 2024, NeurIPS 2024 다수 논문).
-
LLM과 인과 추론의 결합: LLM을 DAG 구성의 사전지식 소스로 활용하거나, 인과 추론으로 LLM의 spurious correlation 문제를 해결하는 연구 활발 (Kiciman et al., "Causal Reasoning and Large Language Models", 2023).
-
Staggered DiD 재정립: TWFE(Two-Way Fixed Effects)의 편향 문제 인식 확산. Callaway-Sant'Anna, Sun-Abraham, de Chaisemartin-D'Haultfoeuille 등의 robust DiD estimator가 표준으로 자리매김.
-
Differentiable Causal Discovery: NOTEARS 이후 DAGMA (NeurIPS 2022), DiffAN (ICML 2024) 등 연속 최적화 기반 인과 발견이 확장. 시계열, 비선형, 잠재변수 설정으로 일반화.
-
Causal Fairness: 인과 추론을 활용한 공정성 정의 및 측정. 경로별 차별(path-specific fairness) 분석이 정책 평가에 적용 (Plecko & Bareinboim, 2024).
참고 자료¶
| 자료 | 유형 | 링크 |
|---|---|---|
| "Causality" (Pearl, 2009) | 교과서 | cambridge.org/pearl-causality |
| "Causal Inference for Statistics" (Imbens & Rubin, 2015) | 교과서 | cambridge.org/imbens-rubin |
| "Causal Inference: The Mixtape" (Cunningham, 2021) | 교과서 (무료) | mixtape.scunning.com |
| "The Effect" (Huntington-Klein, 2022) | 교과서 (무료) | theeffectbook.net |
| DML 원논문 (Chernozhukov et al., 2018) | 논문 | doi.org/10.1111/ectj.12097 |
| Causal Forest (Athey & Imbens, 2018) | 논문 | arxiv.org/abs/1510.04342 |
| Meta-Learners (Kunzel et al., 2019) | PNAS | doi.org/10.1073/pnas.1804597116 |
| DoWhy 문서 | 라이브러리 | pywhy.org/dowhy |
| EconML 문서 | 라이브러리 | econml.azurewebsites.net |
| Causal Inference survey (Yao et al., 2021) | ACM Survey | doi.org/10.1145/3444944 |
| Brady Neal 강의 | 강의 (무료) | bradyneal.com/causal-inference-course |