Lambda Strategies#

The lambda_ module handles estimation of Λ(x) = E[∂²ℓ/∂θ² | X=x], the conditional Hessian of the loss function. This is critical for valid influence function inference.

Overview#

The package supports three regimes based on your data and model:

Regime

Condition

Lambda Method

Cross-Fitting

A

RCT with known F_T

Compute via MC

2-way

B

Linear model

Analytic closed-form

2-way

C

Observational + nonlinear

Estimate via neural net

3-way


Regime Detection#

The package automatically detects the appropriate regime:

from deep_inference.lambda_ import detect_regime, Regime

# Returns Regime.A, Regime.B, or Regime.C
regime = detect_regime(
    model=my_model,
    is_randomized=True,
    has_treatment_dist=True
)

Decision Logic#

if is_randomized AND treatment_dist provided:
    → Regime A (ComputeLambda)
elif model is linear:
    → Regime B (AnalyticLambda)
else:
    → Regime C (EstimateLambda)

Lambda Strategies#

ComputeLambda (Regime A)#

For randomized experiments where T is independent of X and has known distribution F_T.

from deep_inference import inference
from deep_inference.lambda_.compute import Normal, Bernoulli, Uniform

# Normal treatment: T ~ N(μ, σ²)
result = inference(
    Y, T, X,
    model='logit',
    is_randomized=True,
    treatment_dist=Normal(mean=0.0, std=1.0)
)

# Binary treatment: T ~ Bernoulli(p)
result = inference(
    Y, T, X,
    model='logit',
    is_randomized=True,
    treatment_dist=Bernoulli(p=0.5)
)

# Uniform treatment: T ~ Uniform(a, b)
result = inference(
    Y, T, X,
    model='logit',
    is_randomized=True,
    treatment_dist=Uniform(low=-1.0, high=1.0)
)

How it works:

Λ(x, θ) = E_T[∂²ℓ/∂θ² | θ]
        ≈ (1/M) Σ ∂²ℓ(y, t_m, θ)/∂θ²

Where t_m ~ F_T are Monte Carlo samples.

Advantages:

  • No neural network for Λ needed

  • 2-way cross-fitting (faster)

  • More stable

AnalyticLambda (Regime B)#

For linear models, Λ has a closed-form solution.

# Automatically selected for linear models
result = inference(Y, T, X, model='linear')
# Uses AnalyticLambda internally

Formula (linear):

Λ = E[[1, T]ᵀ[1, T]] = [[1, E[T]], [E[T], E[T²]]]

EstimateLambda (Regime C)#

For observational data with nonlinear models, Λ(x) must be estimated.

# Default for observational logit
result = inference(Y, T, X, model='logit')
# Uses EstimateLambda with method='ridge' (default)

Available Methods:

Method

Correlation

Coverage

Default

Notes

ridge

0.508

96%

Yes

Recommended - validated coverage

aggregate

0.000

95%

No

Stable when Hessian doesn’t depend on X

lgbm

0.978

96%

No

High accuracy alternative

mlp

0.997

67%

No

AVOID - invalid SEs

Warning: Using lambda_method='mlp' will emit a warning because it produces invalid standard errors despite high correlation with oracle Lambda.

How it works:

  1. Compute sample Hessians ∂²ℓ/∂θ² for training data

  2. Fit regression model to predict Λ̂(x) from covariates

  3. Predict Λ̂ for evaluation fold

Cross-fitting: 3-way split is required to avoid bias:

  • Fold A: Train θ̂(x)

  • Fold B: Train Λ̂(x) using θ̂ from Fold A

  • Fold C: Evaluate ψ using both


Treatment Distributions#

Available Distributions#

from deep_inference.lambda_.compute import Normal, Bernoulli, Uniform

# Continuous
Normal(mean=0.0, std=1.0)      # Gaussian
Uniform(low=-1.0, high=1.0)    # Uniform

# Discrete
Bernoulli(p=0.5)               # Binary

Custom Distributions#

from deep_inference.lambda_.compute import TreatmentDistribution
import torch

class MyDistribution(TreatmentDistribution):
    def sample(self, n: int) -> torch.Tensor:
        # Return n samples from your distribution
        return torch.randn(n) * 2 + 1  # Example: N(1, 4)

Strategy Selection API#

Automatic Selection#

from deep_inference.lambda_ import select_lambda_strategy

strategy = select_lambda_strategy(
    model=my_model,
    is_randomized=True,
    treatment_dist=Normal(0, 1),
    lambda_method=None  # Auto-detect
)

Manual Override#

result = inference(
    Y, T, X,
    model='logit',
    lambda_method='estimate'  # Force estimation even if RCT
)

Lambda Strategy Protocol#

All strategies implement:

class LambdaStrategy(Protocol):
    requires_theta: bool        # Does fit() need θ̂?
    requires_separate_fold: bool  # 3-way cross-fitting?

    def fit(self, X, T, Y, theta_hat, model) -> None:
        """Fit the strategy (if needed)."""
        ...

    def predict(self, X, theta_hat) -> Tensor:
        """Return Λ̂(x) matrices of shape (n, d_θ, d_θ)."""
        ...

Diagnostics#

Check which regime was used:

result = inference(Y, T, X, model='logit', verbose=True)

print(result.diagnostics['regime'])        # 'A', 'B', or 'C'
print(result.diagnostics['lambda_method']) # 'ComputeLambda', etc.

When to Use Each Regime#

Scenario

Regime

Example

A/B test with known assignment

A

Marketing experiment with 50/50 split

Survey experiment with normal dosage

A

Price sensitivity study with T ~ N(0,1)

Linear regression

B

Simple OLS with heterogeneous coefficients

Observational logit

C

Health outcomes from medical records

Complex nonlinear model

C

Custom loss with unknown treatment selection