WARNING
🧪 Beta公测版本提示:教程主体已完成,正在优化细节,欢迎大家提Issue反馈问题或建议。
因果世界模型 — demo.py 代码详解
运行方式
cd docs/world-models/causal/ladder/code
python demo.py秒级,只依赖 NumPy。SCM 极简:
代码逐段详解
第1步:观测抽样 — 相关从哪来
def sample_observational(n=2000):
z = np.random.randn(n)
a = np.tanh(1.5 * z) + 0.1 * np.random.randn(n)
y = a + 0.15 * np.random.randn(n)
return z, a, y。 - 观测策略
:动作几乎由混淆决定。 tanh把动作压到大约,避免线性里 无限大。 - 机制
:世界只听动作。 不进 的公式。
观测数据上
第2步:干预抽样 — do(A) 切断哪条边
def sample_interventional(n=2000):
z = np.random.randn(n)
a = np.random.uniform(-1.5, 1.5, size=n) # 切断 Z→A
y = a + 0.15 * np.random.randn(n)语法 uniform(-1.5, 1.5, size=n):每个样本独立抽一个动作,形状 (n,)。
第3步:一元线性最小二乘
def fit_linear(x, y):
x1 = np.stack([x, np.ones_like(x)], axis=1)
w, *_ = np.linalg.lstsq(x1, y, rcond=None)
return w设计矩阵两列:斜率和截距。ones_like(x) 与 lstsq 解 (w, residuals, rank, s);w, *_ 只要
语法 *_:解包时忽略后面所有值。
def predict(w, x):
return w[0] * x + w[1]return float(np.mean((y_hat - y) ** 2))float(...) 把 0 维 numpy 标量变成 Python float,打印更干净。
第4步:四格实验设计
在同一套观测数据上拟合两个模型:w_z = fit(Z,Y),w_a = fit(A,Y)。然后:
| 测试分布 | 特征 | 预期 |
|---|---|---|
| 新的观测样本 | MSE 低(相关还在) | |
| 新的观测样本 | MSE 低(真机制) | |
| 干预样本 | MSE 高(边断了) | |
| 干预样本 | MSE 仍低 |
main 里 sample_observational 调用两次:一次训练、一次观测测试,避免「在训练集上报测试误差」。干预集单独抽。
左图 z_tr[::5]:切片步长 5,散点少一点,图更干净。右图 barh(..., labels[::-1]):[::-1] 倒序,条形图从上到下与阅读顺序一致。
和第5步:世界模型在听什么
若世界模型用「看起来相关的混淆」当状态,规划
源码位置
clone 后打开(相对仓库根目录):
docs/world-models/causal/ladder/code/demo.py