Skip to content

statspai.deepiv

deepiv

DeepIV: Deep Learning Instrumental Variables (Hartford et al. 2017).

Uses a two-stage neural network approach: Stage 1: Mixture Density Network estimates P(T | Z, X) Stage 2: Response network minimises counterfactual loss using Monte-Carlo samples from the learned treatment distribution.

References

Hartford, J., Lewis, G., Leyton-Brown, K., & Taddy, M. (2017). "Deep IV: A Flexible Approach for Counterfactual Prediction." Proceedings of the 34th International Conference on Machine Learning (ICML). [@hartford2017deep]

DeepIV

Deep Instrumental Variables estimator (Hartford et al. 2017).

The default Stage-2 loss is the unbiased paired-sample one (n_gradient_samples=1). See the module docstring for the single-sample alternative and when to prefer modern estimators.

Parameters:

Name Type Description Default
data DataFrame
required
y str

Outcome variable.

required
treat str

Endogenous treatment variable (continuous).

required
instruments list of str

Excluded instruments.

required
covariates list of str

Exogenous controls.

required
n_components int

Gaussian mixture components in the MDN.

10
hidden_layers tuple of int

Hidden layer sizes.

(128, 64)
first_stage_epochs int

Stage 1 training epochs.

100
second_stage_epochs int

Stage 2 training epochs.

100
n_samples int

MC samples per observation used to form the Stage-2 residual.

1
n_gradient_samples int

Independent additional samples for the gradient path. >= 1 is Hartford et al.'s paired-sample unbiased gradient estimator; 0 is the single-sample upper-bound loss, which is attenuated.

1
batch_size int
256
learning_rate float
0.001
alpha float

Significance level.

0.05
random_state int
42
verbose bool
False

Examples:

>>> import numpy as np
>>> import pandas as pd
>>> import statspai as sp
>>> rng = np.random.default_rng(0)
>>> n = 150
>>> z = rng.normal(size=n)
>>> x = rng.normal(size=n)
>>> t = 0.8 * z + 0.5 * x + rng.normal(size=n)
>>> y = 1.5 * t + x + rng.normal(size=n)
>>> df = pd.DataFrame({"y": y, "t": t, "z": z, "x": x})
>>> est = sp.DeepIV(df, y="y", treat="t", instruments=["z"],
...                 covariates=["x"], first_stage_epochs=5,
...                 second_stage_epochs=5, hidden_layers=(16,),
...                 batch_size=64,
...                 random_state=0)
>>> res = est.fit()
>>> isinstance(res, sp.CausalResult)
True
References

[@hartford2017deep]

fit

fit() -> CausalResult

Fit the DeepIV model and return causal effect estimates.

Returns:

Type Description
CausalResult

effect

effect(t0: float, t1: float, X: Optional[ndarray] = None) -> ndarray

Estimate E[Y(t1) - Y(t0) | X] for given treatment levels.

Parameters:

Name Type Description Default
t0 float

Baseline treatment value (original scale).

required
t1 float

Counterfactual treatment value (original scale).

required
X ndarray

Covariates. If None, uses training data.

None

Returns:

Type Description
ndarray

Individual-level treatment effects.

deepiv

deepiv(data: DataFrame, y: str, treat: str, instruments: List[str], covariates: List[str], n_components: int = 10, hidden_layers: Tuple[int, ...] = (128, 64), first_stage_epochs: int = 100, second_stage_epochs: int = 100, n_samples: int = 1, n_gradient_samples: int = 1, batch_size: int = 256, learning_rate: float = 0.001, alpha: float = 0.05, random_state: int = 42, verbose: bool = False, refit_bootstrap: int = 0) -> CausalResult

Estimate causal effects using Deep Instrumental Variables.

Parameters:

Name Type Description Default
data DataFrame

Input data.

required
y str

Outcome variable name.

required
treat str

Endogenous treatment variable name (continuous).

required
instruments list of str

Excluded instrument variable names.

required
covariates list of str

Exogenous control variable names.

required
n_components int

Number of Gaussian mixture components in the MDN (stage 1).

10
hidden_layers tuple of int

Hidden layer sizes for both networks.

(128, 64)
first_stage_epochs int

Training epochs for the treatment model (MDN).

100
second_stage_epochs int

Training epochs for the response model.

100
n_samples int

Number of MC samples per observation used to form the Stage-2 residual (y - h(t, x)).

1
n_gradient_samples int

Number of independent additional samples used for the gradient path. When > 0 (default), two independent sample sets are drawn and the unbiased paired-sample estimator mean((y - h(p, x)) * (y - h(p', x))) from Hartford et al. Section 3.2 is used. 0 uses a single set of samples: an upper bound on the loss whose minimiser is attenuated toward zero (see Notes), kept to reproduce results from 1.34.2 and earlier.

1
batch_size int

Mini-batch size.

256
learning_rate float

Adam learning rate.

1e-3
alpha float

Significance level for confidence interval.

0.05
random_state int

Random seed for reproducibility.

42
verbose bool

Print training progress.

False
refit_bootstrap int

Number of bootstrap refits for a standard error that carries the sampling variability of both networks. Each refit trains both stages on a resample of the rows from its own initialisation, so the run takes refit_bootstrap + 1 times as long. With it, se, ci and pvalue come from the spread of the refits, model_info['se_valid_for_ate'] is True and the fixed-network number moves to model_info['se_plugin']. Must be 0 or at least 5. In simulation it is conservative: about 1.45 times the sampling standard deviation, where the fixed-network number is about 0.04 times.

0

Returns:

Type Description
CausalResult
Notes

The estimate is the average effect of raising the treatment by one standard deviation from its mean (model_info['treatment_shift']), not a per-unit slope.

n_gradient_samples=0 is not a faster version of the same estimator. On y = d + 0.5 x + u with a first stage that explains 56% of the variance of d, it returned 0.50 to 0.71 per unit of d across seeds (the errors-in-variables limit is 0.56), against 1.05 to 1.12 for the paired-sample default, and a larger n_samples did not help. It warns when selected.

Without refit_bootstrap, se holds the networks fixed and is far too small as a standard error for the effect; model_info['se_valid_for_ate'] is False.

For high-dimensional covariates or weak instruments, consider DeepGMM / DFIV / DualIV instead — see the module docstring for references.

Examples:

Requires the optional deepiv extra (PyTorch); the calls below are illustrative and are skipped by the doctest runner.

>>> import statspai as sp
>>> result = sp.deepiv(
...     df, y='lwage', treat='educ',
...     instruments=['nearc4'],
...     covariates=['exper', 'expersq'],
... )
>>> print(result.summary())

Custom architecture with the unbiased paired-sample gradient:

>>> result = sp.deepiv(
...     df, y='sales', treat='price',
...     instruments=['cost_shifter'],
...     covariates=['demand_controls'],
...     n_components=20,
...     hidden_layers=(256, 128, 64),
...     first_stage_epochs=200,
...     n_gradient_samples=1,   # paired-sample unbiased gradient
... )