「因果推斷」(causal inference)是基於觀察數據進行反事實估計,分析干預與結果之間的因果關係的一門科學。雖然在因果推斷領域已經有許多的框架與方法,但大部分方法缺乏穩定的實現。 DoWhy 是微軟發布的一個用於進行因果推斷的 Python 套件,其特點在於:
- 為了保證所有假設的正確性,將給定的問題轉化為一張因果圖
- 提供了一種面向多種常用因果推斷方法的統一接口,並結合了兩種主要的因果推斷框架
- 自動化測試假設的正確性
如上所述,DoWhy 基於因果推斷的兩大框架構建:「圖模型」與「潛在結果模型」。具體來說,其使用基於圖的準則與 do-積分來對假設進行建模並識別出非參數化的因果效應;而在評估階段則主要基於潛在結果框架中的方法進行估計。 DoWhy 的整個因果推斷過程可以劃分為四大步驟:
- 建模 (model):利用假設(先驗知識)對因果推斷問題建模
- 識別(identify):在假設(模型)下識別因果效應的表達式(因果估計量)
- 評估(estimate):使用統計方法對表達式進行估計
- 反駁(refute):使用各種反駁檢查來驗證評估的正確性
下圖取自於為微軟,是Dowhy的整體流程圖:
建模
Dowhy的第一步就是創建因果圖模型,來表示各種因子的先驗知識。目前Dowhy支援以下的因果假設:
- 圖 (Graph): 提供gml或dot格式的因果圖,具體可以是文件或是字串格式
- 命名變數集合 (Named variable sets): 直接提供變量的類型,包括「混雜因子」(common causes / cofounders)、「工具變量」(instrumental variables)、「結果修改變量」(effect modifiers)、「前門變量」(front-door variables)等
識別
Dowhy會根據建構出來的因果圖,識別出因果效應的表達式,支援的準則有:
- 後門準則 (Back-door criterion)
- 前門準則 (Front-door criterion)
- 工具變量 (Instrumental Variables)
- 中介-直接或間接效應識別 (Mediation-Direct and indirect effect identification)
反駁
DoWhy 支援多種反駁方法來驗證估計的正確性,具體列表如下:
- 添加隨機混雜因子:添加一個隨機變量作為混雜因子後,評估因果效應是否會改變(期望結果:不會)
- 安慰劑干預:將真實干預變量替換為獨立隨機變量後,因果效應是否會改變(期望結果:因果效應歸零)
- 虛擬結果:將真實結果變量替換為獨立隨機變量後,因果效應是否會改變(期望結果:因果效應歸零)
- 模擬結果:將數據集替換為基於接近給定數據集數據生成過程的方式模擬生成的數據集後因果效應是否會改變(期望結果:與數據生成過程的效應參數相匹配)
- 添加未觀測混雜因子:添加一個額外的與干預和結果相關的混雜因子後,因果效應的敏感性(期望結果:不過度敏感)
- 數據子集驗證:將給定數據集替換為一個隨機子集後,因果效應是否會改變(期望結果:不會)
- 自助驗證:將給定數據集替換為同一數據集的自助樣本後因果效應是否會改變(期望結果:不會)
Demo
看完上述的DoWhy四大步驟詳解,想必大家還是霧煞煞,以下就讓我用程式碼,帶各位跑完一次DoWhy
安裝套件:
Dowhy的安裝很簡單,直接pip install:
pip install dowhy
導入套件和模擬數據
import numpy as np
import pandas as pd
import dowhy
from dowhy import CausalModel
import dowhy.datasets
本例中簡單模擬不同變量之間的「線性」關係。具體程式碼如下:
data = dowhy.datasets.linear_dataset(beta=10, # beta 表示真實的因果效應
num_common_causes=5, # 混雜因子,用 W 表示,作用於干預變量和結果變量
num_instruments=2, # 工具變量,用 Z 表示,作用於干預變量(間接影響結果)
num_effect_modifiers=1, # 效果修改變量,用 X 表示,作用於結果變量
num_samples=10000, # 樣本數
treatment_is_binary=True, # 干預為二元變量,用 v 表示
num_discrete_common_causes=1)
df = data["df"] # DoWhy 使用 pandas 的 dataframe 來載入數據
print(df.head())
print(data["dot_graph"]) # 還可以輸出 gml_graph,內容一致只是表達形式不同
輸出結果為:
X0 Z0 Z1 W0 ... W3 W4 v0 y
0 1.872740 1.0 0.160074 -1.620751 ... 0.879328 3 True 13.811488
1 1.960272 1.0 0.564677 1.358228 ... -0.491948 3 True 18.908277
2 2.157464 1.0 0.591804 -1.228804 ... 0.044177 0 True 13.981751
3 0.751028 1.0 0.977419 1.651551 ... 0.046785 2 True 12.470669
4 1.269126 1.0 0.654663 1.004417 ... 0.409693 2 True 7.934809
[5 rows x 10 columns]
digraph {v0->y;W0-> v0; W1-> v0; W2-> v0; W3-> v0; W4-> v0;Z0-> v0; Z1-> v0;W0-> y; W1-> y; W2-> y; W3-> y; W4-> y;X0-> y;}
執行因果推斷
建模
現在我們以 GML 圖構建因果圖,程式碼如下:
# With graph
model = CausalModel(
data=df,
treatment=data["treatment_name"],
outcome=data["outcome_name"],
graph=data["gml_graph"]
)
# INFO:dowhy.causal_model:Model to find the causal effect of treatment ['v0'] on outcome ['y']
model.view_model()
from IPython.display import Image, display
display(Image(filename="causal_model.png"))
輸出的因果圖:
我們也可以通過指定具體變量的方式來生成圖(本例中由於數據是模擬的,所以各種方式均可以生成),程式碼如下:
# Without graph
model = CausalModel(
data=df,
treatment=data["treatment_name"],
outcome=data["outcome_name"],
instruments=data["instrument_names"],
common_causes=data["common_causes_names"],
effect_modifiers=data["effect_modifier_names"])
得到的圖與上一張是一樣:
上圖包含了數據中給定的先驗知識(變量分類),我們可以利用這張圖來識別因果效應(從因果估計量到概率表達式)並進行評估。
識別
「識別階段」可以脫離於數據,僅根據圖進行識別,其給出的結果是一個用於計算的「表達式」。具體的程式碼如下:
identified_estimand = model.identify_effect()
print(identified_estimand)
輸出結果為:
Estimand type: EstimandType.NONPARAMETRIC_ATE
### Estimand : 1
Estimand name: backdoor
Estimand expression:
d
─────(E[y|W0,W3,W4,W2,W1])
d[v₀]
Estimand assumption 1, Unconfoundedness: If U→{v0} and U→y then P(y|v0,W0,W3,W4,W2,W1,U) = P(y|v0,W0,W3,W4,W2,W1)
### Estimand : 2
Estimand name: iv
Estimand expression:
⎡ -1⎤
⎢ d ⎛ d ⎞ ⎥
E⎢─────────(y)⋅⎜─────────([v₀])⎟ ⎥
⎣d[Z₀ Z₁] ⎝d[Z₀ Z₁] ⎠ ⎦
Estimand assumption 1, As-if-random: If U→→y then ¬(U →→{Z0,Z1})
Estimand assumption 2, Exclusion: If we remove {Z0,Z1}→{v0}, then ¬({Z0,Z1}→y)
### Estimand : 3
Estimand name: frontdoor
No such variable(s) found!
Process finished with exit code 0
顯示結果認為我們的因果圖,可以使用後門準則(backdoor)和工具變量(iv)
評估
根據識別出來的結果,運算評估分數(後門準則為示範),程式碼如下:
estimate = model.estimate_effect(identified_estimand,
method_name="backdoor.propensity_score_stratification")
print(estimate)
print("Causal Estimate is " + str(estimate.value))
輸出結果為:
*** Causal Estimate ***
## Identified estimand
Estimand type: EstimandType.NONPARAMETRIC_ATE
### Estimand : 1
Estimand name: backdoor
Estimand expression:
d
─────(E[y|W0,W4,W3,W1,W2])
d[v₀]
Estimand assumption 1, Unconfoundedness: If U→{v0} and U→y then P(y|v0,W0,W4,W3,W1,W2,U) = P(y|v0,W0,W4,W3,W1,W2)
## Realized estimand
b: y~v0+W0+W4+W3+W1+W2
Target units: ate
## Estimate
Mean value: 12.20597937728158
Causal Estimate is 12.20597937728158
Process finished with exit code 0
結果顯示,後門準則的平均因果效應為12.2
另外,們可以指定target_units 參數來選擇因果效應分析的群體,如 ate(群體層面)、att(干預組)、ate(對照組)。也可以指定結果修改變量來分析不同變量對結果的影響。
同時我們也可以使用工具變量(iv)來算平均因果效應,依個人喜好!
反駁
為了增加我們的正確性,我們需要反駁,來驗證我們構建出來的因果圖是否是合理的。
DoWhy 支援多種反駁方法,我們就拿其中3個來跑看看結果:
添加一個隨機的混雜因子變量(如果假設正確,新的因果效應變化不大):
res_random = model.refute_estimate(identified_estimand, estimate, method_name="random_common_cause")
print(res_random)
Refute: Add a random common cause
Estimated effect:9.778247527168062
New effect:9.778247527168064
p value:2.0
安慰劑干預(因果效應越接近0,表示假設正確):
res_placebo = model.refute_estimate(identified_estimand, estimate,
method_name="placebo_treatment_refuter", placebo_type="permute")
print(res_placebo)
Refute: Use a Placebo Treatment
Estimated effect:13.242744523784841
New effect:-0.017628768835216576
p value:0.8999999999999999
移除數據的一個隨機子集(如果假設正確,新的因果效應變化不大):
res_subset = model.refute_estimate(identified_estimand, estimate,
method_name="data_subset_refuter", subset_fraction=0.9)
print(res_subset)
Refute: Use a subset of data
Estimated effect:11.750332640201313
New effect:11.758927888294751
p value:0.86
以上就是使用DoWhy框架,進行的模擬程式,完整版可以到我的github
結論
DoWhy 是微軟發布的一個用於進行因果推斷的 Python 套件,整個因果推斷過程可以劃分為四大步驟,建構因果圖,識別出準則,評估因果分數,反駁。
前半段簡單介紹Dowhy的各步驟內容,後半部使用模擬數據,跑一次DoWhy流程。
下一篇文章,筆者將使用實際案例,跑一次DoWhy流程,並列出遇到的問題,和解決方法。
若有任何錯誤或要討論的地方,非常歡迎與我聯繫。以下是我的LinkedIn:
https://www.linkedin.com/in/ray-huang-a01a5a171/
歡迎大家follow起來,也可以拍手給我一點鼓勵!