Skip to content

Regional Effects (unknown black-box function)

  • Author: givasile
  • Runtime: ~55 s
  • Description: The realistic follow-up to tutorial 03: a neural network is fitted on the data and explained with regional PDP, RHALE and SHAP-DP, under uncorrelated and correlated features.
  • πŸ“„ The whole notebook in one page: PDP report

This tutorial use the same dataset with the previous tutorial, but instead of explaining the known (synthetic) predictive function, we fit a neural network on the data and explain the neural network. This is a more realistic scenario, since in real-world applications we do not know the underlying function and we only have access to the data. We advise the reader to first read the previous tutorial.

import numpy as np
import effector
import keras
import tensorflow as tf

np.random.seed(12345)
tf.random.set_seed(12345)
WARNING: All log messages before absl::InitializeLog() is called are written to STDERR
I0000 00:00:1784066339.959660   16943 cpu_feature_guard.cc:227] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
To enable the following instructions: AVX2 FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.

Simulation example

Data Generating Distribution

We will generate \(N=1000\) examples with \(D=3\) features, which are in the uncorrelated setting all uniformly distributed as follows:

Feature Description Distribution
\(x_1\) Uniformly distributed between \(-1\) and \(1\) \(x_1 \sim \mathcal{U}(-1,1)\)
\(x_2\) Uniformly distributed between \(-1\) and \(1\) \(x_2 \sim \mathcal{U}(-1,1)\)
\(x_3\) Uniformly distributed between \(-1\) and \(1\) \(x_3 \sim \mathcal{U}(-1,1)\)

For the correlated setting, we keep the distributional assumptions for \(x_2\) and \(x_3\), but define \(x_1\) such that it is highly correlated with \(x_3\) by:
\(x_1 = x_3 + \delta\) with \(\delta \sim \mathcal{N}(0,0.0625)\).

def generate_dataset_uncorrelated(N):
    x1 = np.random.uniform(-1, 1, size=N)
    x2 = np.random.uniform(-1, 1, size=N)
    x3 = np.random.uniform(-1, 1, size=N)
    return np.stack((x1, x2, x3), axis=-1)

def generate_dataset_correlated(N):
    x3 = np.random.uniform(-1, 1, size=N)
    x2 = np.random.uniform(-1, 1, size=N)
    x1 = x3 + np.random.normal(loc = np.zeros_like(x3), scale = 0.25)
    return np.stack((x1, x2, x3), axis=-1)

# generate the dataset for the uncorrelated and correlated setting
N = 1000
X_uncor_train = generate_dataset_uncorrelated(N)
X_uncor_test = generate_dataset_uncorrelated(10000)
X_cor_train = generate_dataset_correlated(N)
X_cor_test = generate_dataset_correlated(10000)

Black-box function

We will use the following linear model with a subgroup-specific interaction term: $$ y = 3x_1I_{x_3>0} - 3x_1I_{x_3\leq0} + x_3$$

On a global level, there is a high heterogeneity for the features \(x_1\) and \(x_3\) due to their interaction with each other. However, this heterogeneity vanishes to 0 if the feature space is separated into subregions:

| Feature | Region | Average Effect | Heterogeneity | |---------|-------------|----------------|---------------| | $x_1$ | $x_3>0$ | $3x_1$ | 0 | | $x_1$ | $x_3\leq 0$ | $-3x_1$ | 0 | | $x_2$ | all | 0 | 0 | | $x_3$ | $x_3>0$ | $x_3$ | 0 | | $x_3$ | $x_3\leq 0$ | $x_3$ | 0 |
def generate_target(X):
    f = np.where(X[:,2] > 0, 3*X[:,0] + X[:,2], -3*X[:,0] + X[:,2])
    epsilon = np.random.normal(loc = np.zeros_like(X[:,0]), scale = 0.1)
    Y = f + epsilon
    return(Y)

# generate target for uncorrelated and correlated setting
Y_uncor_train = generate_target(X_uncor_train)
Y_uncor_test = generate_target(X_uncor_test)
Y_cor_train = generate_target(X_cor_train)
Y_cor_test = generate_target(X_cor_test)      

Fit a Neural Network

We create a two-layer feedforward Neural Network, a weight decay of 0.01 for 100 epochs. We train two instances of this NN, one on the uncorrelated and one on the correlated setting. In both cases, the NN achieves a Mean Squared Error of about \(0.17\) units.

# Train - Evaluate - Explain a neural network
model_uncor = keras.Sequential([
    keras.layers.Dense(10, activation="relu", input_shape=(3,)),
    keras.layers.Dense(10, activation="relu", input_shape=(3,)),
    keras.layers.Dense(1)
])

optimizer = keras.optimizers.Adam(learning_rate=0.01)
model_uncor.compile(optimizer=optimizer, loss="mse")
model_uncor.fit(X_uncor_train, Y_uncor_train, epochs=100)
model_uncor.evaluate(X_uncor_test, Y_uncor_test)
Epoch 1/100


/home/givasile/github/packages/effector/.venv/lib/python3.10/site-packages/keras/src/layers/core/dense.py:95: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.
  super().__init__(activity_regularizer=activity_regularizer, **kwargs)

 1/32 ━━━━━━━━━━━━━━━━━━━━ 17s 553ms/step - loss: 3.5923



32/32 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 2.4581

Epoch 2/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 1.4139



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.9727

Epoch 3/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.6694



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.4579

Epoch 4/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.3966



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.2979

Epoch 5/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.2973



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.2391

Epoch 6/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 18ms/step - loss: 0.2410



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.2042

Epoch 7/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.2032



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.1818

Epoch 8/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.1761



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.1649

Epoch 9/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 15ms/step - loss: 0.1517



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.1493

Epoch 10/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.1284



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.1368

Epoch 11/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 19ms/step - loss: 0.1124



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.1255

Epoch 12/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0953



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.1174

Epoch 13/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0830



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.1100

Epoch 14/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 15ms/step - loss: 0.0735



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.1049

Epoch 15/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0656



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0998

Epoch 16/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0597



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0954

Epoch 17/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 15ms/step - loss: 0.0552



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0910

Epoch 18/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0525



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 0.0866

Epoch 19/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0492



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0829

Epoch 20/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0457



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0797

Epoch 21/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0436



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0771

Epoch 22/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0400



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0746

Epoch 23/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0401



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0728

Epoch 24/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0399



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0712

Epoch 25/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0413



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0700

Epoch 26/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0419



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0697

Epoch 27/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0406



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0699

Epoch 28/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 18ms/step - loss: 0.0392



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0698

Epoch 29/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 21ms/step - loss: 0.0403



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0701

Epoch 30/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0388



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0686

Epoch 31/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0395



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0679

Epoch 32/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0399



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0670

Epoch 33/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 18ms/step - loss: 0.0393



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0664

Epoch 34/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0398



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0662

Epoch 35/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0396



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0654

Epoch 36/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0393



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0646

Epoch 37/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 18ms/step - loss: 0.0395



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0643

Epoch 38/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0388



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0635

Epoch 39/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0390



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 0.0647

Epoch 40/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0372



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0639

Epoch 41/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0366



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0638

Epoch 42/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0369



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0632

Epoch 43/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 19ms/step - loss: 0.0354



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0621

Epoch 44/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0370



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0612

Epoch 45/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0338



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0610

Epoch 46/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0367



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0614

Epoch 47/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0340



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0603

Epoch 48/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 19ms/step - loss: 0.0343



25/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0492



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0602

Epoch 49/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0350



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0606

Epoch 50/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0335



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0595

Epoch 51/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 15ms/step - loss: 0.0321



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0592

Epoch 52/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0355



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0590

Epoch 53/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 18ms/step - loss: 0.0321



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0579

Epoch 54/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0319



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 0.0582

Epoch 55/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0315



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0581

Epoch 56/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0305



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0572

Epoch 57/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0316



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0563

Epoch 58/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0302



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0561

Epoch 59/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0287



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0554

Epoch 60/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 15ms/step - loss: 0.0309



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0550

Epoch 61/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0295



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0548

Epoch 62/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0290



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0549

Epoch 63/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0284



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0542

Epoch 64/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0273



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0564

Epoch 65/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0268



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0560

Epoch 66/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0274



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0564

Epoch 67/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0276



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0571

Epoch 68/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 15ms/step - loss: 0.0266



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0569

Epoch 69/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0261



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0547

Epoch 70/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0245



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 0.0534

Epoch 71/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0238



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0537

Epoch 72/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 18ms/step - loss: 0.0236



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0537

Epoch 73/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 15ms/step - loss: 0.0263



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0529

Epoch 74/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0275



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0518

Epoch 75/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0268



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0528

Epoch 76/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0251



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0523

Epoch 77/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0247



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0519

Epoch 78/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 18ms/step - loss: 0.0242



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0530

Epoch 79/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0258



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0519

Epoch 80/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0247



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0514

Epoch 81/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 15ms/step - loss: 0.0239



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0513

Epoch 82/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0234



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0507

Epoch 83/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 15ms/step - loss: 0.0244



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0514

Epoch 84/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0243



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0506

Epoch 85/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0233



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 0.0501

Epoch 86/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0234



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0501

Epoch 87/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 18ms/step - loss: 0.0236



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 0.0498

Epoch 88/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0232



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0509

Epoch 89/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 15ms/step - loss: 0.0239



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0509

Epoch 90/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0239



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0500

Epoch 91/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0240



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0508

Epoch 92/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0234



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0490

Epoch 93/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0234



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0480

Epoch 94/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0227



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 0.0472

Epoch 95/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0231



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 0.0477

Epoch 96/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0231



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0476

Epoch 97/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 18ms/step - loss: 0.0236



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0479

Epoch 98/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0235



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0482

Epoch 99/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 15ms/step - loss: 0.0241



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0483

Epoch 100/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0236



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0476

 1/313 ━━━━━━━━━━━━━━━━━━━━ 19s 63ms/step - loss: 0.0280



 53/313 ━━━━━━━━━━━━━━━━━━━━ 0s 972us/step - loss: 0.0534



111/313 ━━━━━━━━━━━━━━━━━━━━ 0s 920us/step - loss: 0.0633



169/313 ━━━━━━━━━━━━━━━━━━━━ 0s 904us/step - loss: 0.0672



227/313 ━━━━━━━━━━━━━━━━━━━━ 0s 894us/step - loss: 0.0693



285/313 ━━━━━━━━━━━━━━━━━━━━ 0s 889us/step - loss: 0.0697



313/313 ━━━━━━━━━━━━━━━━━━━━ 0s 919us/step - loss: 0.0690

0.06895451992750168
model_cor = keras.Sequential([
    keras.layers.Dense(10, activation="relu", input_shape=(3,)),
    keras.layers.Dense(10, activation="relu", input_shape=(3,)),
    keras.layers.Dense(1)
])

optimizer = keras.optimizers.Adam(learning_rate=0.01)
model_cor.compile(optimizer=optimizer, loss="mse")
model_cor.fit(X_cor_train, Y_cor_train, epochs=100)
model_cor.evaluate(X_cor_test, Y_cor_test)
Epoch 1/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 15s 505ms/step - loss: 3.8755



32/32 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 1.9269

Epoch 2/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.8581



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.4580

Epoch 3/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.3018



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.1648

Epoch 4/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.1879



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.1121

Epoch 5/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.1917



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0988

Epoch 6/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.1987



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0901

Epoch 7/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.1949



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0842

Epoch 8/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.1894



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0793

Epoch 9/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.1834



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0754

Epoch 10/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.1780



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0720

Epoch 11/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.1725



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0688

Epoch 12/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 18ms/step - loss: 0.1668



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 0.0659

Epoch 13/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.1619



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0634

Epoch 14/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.1578



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0609

Epoch 15/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.1534



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0584

Epoch 16/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.1497



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0562

Epoch 17/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.1462



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0541

Epoch 18/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.1420



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 0.0524

Epoch 19/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.1390



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0506

Epoch 20/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.1365



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0492

Epoch 21/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.1332



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0479

Epoch 22/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.1312



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0467

Epoch 23/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.1290



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0457

Epoch 24/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.1267



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0449

Epoch 25/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.1239



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0442

Epoch 26/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.1214



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0436

Epoch 27/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.1200



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0429

Epoch 28/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.1176



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0421

Epoch 29/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.1166



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0413

Epoch 30/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.1125



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0409

Epoch 31/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0998



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0398

Epoch 32/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0929



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0391

Epoch 33/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0877



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0381

Epoch 34/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0844



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0372

Epoch 35/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0812



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0362

Epoch 36/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0797



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0352

Epoch 37/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0760



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0344

Epoch 38/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0725



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0342

Epoch 39/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 18ms/step - loss: 0.0516



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0325

Epoch 40/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0537



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0324

Epoch 41/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0534



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0318

Epoch 42/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0503



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0312

Epoch 43/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0488



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0308

Epoch 44/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0513



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0303

Epoch 45/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0467



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0301

Epoch 46/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0443



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0295

Epoch 47/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0430



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0290

Epoch 48/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 15ms/step - loss: 0.0410



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0287

Epoch 49/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0396



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0283

Epoch 50/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0386



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0282

Epoch 51/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 2s 91ms/step - loss: 0.0371



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0277

Epoch 52/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0374



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0273

Epoch 53/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0351



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0271

Epoch 54/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0328



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0266

Epoch 55/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0317



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0262

Epoch 56/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0321



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0258

Epoch 57/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0276



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0250

Epoch 58/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0276



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0244

Epoch 59/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0267



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0239

Epoch 60/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0244



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0238

Epoch 61/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0262



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0232

Epoch 62/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0228



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0236

Epoch 63/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0224



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0228

Epoch 64/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0186



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0226

Epoch 65/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0182



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0222

Epoch 66/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 18ms/step - loss: 0.0194



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0221

Epoch 67/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 18ms/step - loss: 0.0182



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 0.0222

Epoch 68/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0179



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0219

Epoch 69/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0156



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0216

Epoch 70/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0159



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0214

Epoch 71/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0149



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0211

Epoch 72/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0159



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0211

Epoch 73/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0146



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0208

Epoch 74/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 15ms/step - loss: 0.0147



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0207

Epoch 75/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0153



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0207

Epoch 76/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0140



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0206

Epoch 77/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step - loss: 0.0143



30/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0171



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0202

Epoch 78/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 18ms/step - loss: 0.0148



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0205

Epoch 79/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0142



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0202

Epoch 80/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0146



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0201

Epoch 81/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0145



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0202

Epoch 82/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0142



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0200

Epoch 83/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 18ms/step - loss: 0.0143



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 0.0198

Epoch 84/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0143



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0201

Epoch 85/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0138



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0198

Epoch 86/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 19ms/step - loss: 0.0147



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0198

Epoch 87/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0147



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 0.0200

Epoch 88/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0139



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0199

Epoch 89/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0144



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0199

Epoch 90/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0142



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0196

Epoch 91/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0140



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0196

Epoch 92/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0136



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0195

Epoch 93/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 18ms/step - loss: 0.0145



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0189

Epoch 94/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0138



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0192

Epoch 95/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 18ms/step - loss: 0.0143



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0187

Epoch 96/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0148



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0193

Epoch 97/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0148



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0186

Epoch 98/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 16ms/step - loss: 0.0129



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0192

Epoch 99/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 18ms/step - loss: 0.0159



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0181

Epoch 100/100

 1/32 ━━━━━━━━━━━━━━━━━━━━ 0s 17ms/step - loss: 0.0137



32/32 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0195

 1/313 ━━━━━━━━━━━━━━━━━━━━ 19s 63ms/step - loss: 0.0147



 50/313 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 0.0341



101/313 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 0.0303



148/313 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 0.0287



169/313 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 0.0283



188/313 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 0.0280



212/313 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step - loss: 0.0276



232/313 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0273



259/313 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0272



282/313 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0270



302/313 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step - loss: 0.0269



313/313 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0254

0.025420546531677246

PDP

Uncorrelated setting

Global PDP

pdp = effector.PDP(data=X_uncor_train, model=model_uncor, schema={"feature_names": ['x1','x2','x3'], "target_name": "Y"})
pdp.plot(feature=0, centering=True, show_avg_output=False, heterogeneity="ice", y_limits=[-5, 5])
pdp.plot(feature=1, centering=True, show_avg_output=False, heterogeneity="ice", y_limits=[-5, 5])
pdp.plot(feature=2, centering=True, show_avg_output=False, heterogeneity="ice", y_limits=[-5, 5])

png

png

png

Feature importance and one-click explanation

Beyond the plots, every global effect exposes importances() (the dispersion of the mean effect, the \(\mu\)-twin of heterogeneity), and effector.explain(...) produces a self-contained Report in one call.

# per-feature importance = dispersion of the mean effect (mu-twin of heterogeneity)
print("importances:", np.round(pdp.importances(), 3))

# one-click auto-explanation -> Report (serializable; self-contained HTML)
report = effector.explain(
    X_uncor_train, model_uncor, method="pdp",
    schema={"feature_names": ['x1', 'x2', 'x3'], "target_name": "Y"},
    nof_instances="all",
)
report.show()

# the whole notebook, in one page: the report published with this example
from pathlib import Path
_out = Path("reports") / "04_regional_effects_real_f"
_out.mkdir(parents=True, exist_ok=True)
report.to_html(_out / "report_pdp.html")
importances: [0.024 0.002 0.585]
[effector] global effects   (GAM)  -> 11.0% of the model's variance
           regional effects (CALM) -> 99.3%

  ════════════════════════════════════════════════════════════════════════
  PDP report  Β·  target: Y
  ════════════════════════════════════════════════════════════════════════

  DATA & MODEL
  ────────────────────────────────────────────────────────────────────────
    instances     1,000
    features      3  Β·  3 continuous
    model output  mean 0.0708 Β· std 1.79 Β· range [-3.65, 3.92]

  EXPLAINED VARIANCE
  ────────────────────────────────────────────────────────────────────────
    step         split on                 solo     Ξ”RΒ²      RΒ²       heter
    ──────────────────────────────────────────────────────────────────────
    GAM          (all features global)       β€”       β€”   11.0%           β€”
  + x1           x3                     +88.3%  +88.3%   99.3% 1.69 β†’ 0.08
    ──────────────────────────────────────────────────────────────────────
    FINAL                                                99.3%

  REJECTED SPLITS                                            min gain 1.0%
  ────────────────────────────────────────────────────────────────────────
    feature      split on                 solo     Ξ”RΒ²    reason
    ──────────────────────────────────────────────────────────────────────
  βœ— x3           x1                     +83.2%  -80.1%    redundant

    βœ— redundant: it would explain variance on its own (see solo),
      but the accepted splits already account for it.

  FEATURES                                ranked, in the selected snapshot
  ────────────────────────────────────────────────────────────────────────
    feature        importance                          heter      #regions
    ──────────────────────────────────────────────────────────────────────
    x1                 1.6690  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ     0.0781             4
    x3                 0.5849  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ                 1.6876             1
    ──────────────────────────────────────────────────────────────────────
    the features above carry 100% of the total importance mass



Feature 0 - Full partition tree:
🌳 Full Tree Structure:
───────────────────────
x1 πŸ”Ή [id: 0 | heter: 1.69 | inst: 1000 | w: 1.00]
    x3 < -0.00 πŸ”Ή [id: 1 | heter: 0.19 | inst: 498 | w: 0.50]
        x3 < -0.10 πŸ”Ή [id: 2 | heter: 0.02 | inst: 450 | w: 0.45]
        -0.10 ≀ x3 < -0.00 πŸ”Ή [id: 3 | heter: 0.53 | inst: 48 | w: 0.05]
    x3 β‰₯ -0.00 πŸ”Ή [id: 4 | heter: 0.24 | inst: 502 | w: 0.50]
        -0.00 ≀ x3 < 0.10 πŸ”Ή [id: 5 | heter: 0.63 | inst: 50 | w: 0.05]
        x3 β‰₯ 0.10 πŸ”Ή [id: 6 | heter: 0.03 | inst: 452 | w: 0.45]
--------------------------------------------------
Feature 0 - Statistics per tree level:
🌳 Tree Summary:
─────────────────
Level 0πŸ”Ήheter: 1.69
    Level 1πŸ”Ήheter: 0.21 | πŸ”»1.47 (87.42%)
        Level 2πŸ”Ήheter: 0.08 | πŸ”»0.13 (63.16%)




/home/givasile/github/packages/effector/effector/report.py:606: UserWarning: This figure includes Axes that are not compatible with tight_layout, so results might be incorrect.
  fig.tight_layout()

Feature triage

Survey all features at once: effector.plot_triage β€” importance right, heterogeneity up; the top-right corner is the to-do list for find_regions.

effector.plot_triage(pdp)

png

Regional PDP

pdp = effector.PDP(
    data=X_uncor_train, model=model_uncor,
    schema={"feature_names": ['x1', 'x2', 'x3']},
    axis_limits=np.array([[-1, 1], [-1, 1], [-1, 1]]).T,
    nof_instances="all",
)
pdp.fit("all", centering=True)
finder = effector.space_partitioning.Best(min_heterogeneity_decrease_pcg=0.3, numerical_features_grid_size=10)
# plural form: one search per feature in a single call -> {feature_name: Partition}
parts = pdp.find_regions(features="all", finder=finder)
partitions = {feat: parts[name] for feat, name in enumerate(['x1', 'x2', 'x3'])}
partitions[0].show()
Feature 0 - Full partition tree:
🌳 Full Tree Structure:
───────────────────────
x1 πŸ”Ή [id: 0 | heter: 1.69 | inst: 1000 | w: 1.00]
    x3 < 0.00 πŸ”Ή [id: 1 | heter: 0.22 | inst: 500 | w: 0.50]
        x3 < -0.20 πŸ”Ή [id: 2 | heter: 0.01 | inst: 405 | w: 0.41]
        -0.20 ≀ x3 < 0.00 πŸ”Ή [id: 3 | heter: 0.46 | inst: 95 | w: 0.10]
    x3 β‰₯ 0.00 πŸ”Ή [id: 4 | heter: 0.21 | inst: 500 | w: 0.50]
        0.00 ≀ x3 < 0.20 πŸ”Ή [id: 5 | heter: 0.43 | inst: 106 | w: 0.11]
        x3 β‰₯ 0.20 πŸ”Ή [id: 6 | heter: 0.02 | inst: 394 | w: 0.39]
--------------------------------------------------
Feature 0 - Statistics per tree level:
🌳 Tree Summary:
─────────────────
Level 0πŸ”Ήheter: 1.69
    Level 1πŸ”Ήheter: 0.21 | πŸ”»1.47 (87.30%)
        Level 2πŸ”Ήheter: 0.10 | πŸ”»0.11 (51.26%)
# plot the two level-1 subregions (region idx == old node_idx)
part = partitions[0]
for r in part:
    if r.level == 1:
        part.plot(r.idx, heterogeneity="ice", y_limits=[-5, 5])

png

png

partitions[1].show()
Feature 1 - Full partition tree:
No splits found for feature 1
--------------------------------------------------
Feature 1 - Statistics per tree level:
No splits found for feature 1
partitions[2].show()
Feature 2 - Full partition tree:
🌳 Full Tree Structure:
───────────────────────
x3 πŸ”Ή [id: 0 | heter: 1.69 | inst: 1000 | w: 1.00]
    x1 < 0.00 πŸ”Ή [id: 1 | heter: 0.84 | inst: 494 | w: 0.49]
        x1 < -0.40 πŸ”Ή [id: 2 | heter: 0.52 | inst: 301 | w: 0.30]
        -0.40 ≀ x1 < 0.00 πŸ”Ή [id: 3 | heter: 0.34 | inst: 193 | w: 0.19]
    x1 β‰₯ 0.00 πŸ”Ή [id: 4 | heter: 0.83 | inst: 506 | w: 0.51]
        0.00 ≀ x1 < 0.40 πŸ”Ή [id: 5 | heter: 0.35 | inst: 199 | w: 0.20]
        x1 β‰₯ 0.40 πŸ”Ή [id: 6 | heter: 0.50 | inst: 307 | w: 0.31]
--------------------------------------------------
Feature 2 - Statistics per tree level:
🌳 Tree Summary:
─────────────────
Level 0πŸ”Ήheter: 1.69
    Level 1πŸ”Ήheter: 0.83 | πŸ”»0.85 (50.66%)
        Level 2πŸ”Ήheter: 0.44 | πŸ”»0.39 (46.70%)
part = partitions[2]
for r in part:
    if r.level == 1:
        part.plot(r.idx, heterogeneity="ice", centering=True, y_limits=[-5, 5])

png

png

Back on the triage plane, partitions= (the dict returned by the plural find_regions) turns it into a before/after story: an arrow runs from each feature's global point to each of its leaf points. Leaves of a good partition land right and down β€” more decisive, less heterogeneous.

effector.plot_triage(pdp, partitions={"x1": parts["x1"], "x3": parts["x3"]})

png

Conclusion

For the Global PDP:

  • the average effect of \(x_1\) is \(0\) with some heterogeneity implied by the interaction with \(x_1\). The heterogeneity is expressed with two opposite lines; \(-3x_1\) when \(x_1 \leq 0\) and \(3x_1\) when \(x_1 >0\)
  • the average effect of \(x_2\) to be \(0\) without heterogeneity
  • the average effect of \(x_3\) to be \(x_3\) with some heterogeneity due to the interaction with \(x_1\). The heterogeneity is expressed with a discontinuity around \(x_3=0\), with either a positive or a negative offset depending on the value of \(x_1^i\)

For the Regional PDP:

  • For \(x_1\), the algorithm finds two regions, one for \(x_3 \leq 0\) and one for \(x_3 > 0\)
  • when \(x_3>0\) the effect is \(3x_1\)
  • when \(x_3 \leq 0\), the effect is \(-3x_1\)
  • For \(x_2\) the algorithm does not find any subregion
  • For \(x_3\), there is a change in the offset:
  • when \(x_1>0\) the line is \(x_3 - 3x_1^i\) in the first half and \(x_3 + 3x_1^i\) later
  • when \(x_1<0\) the line is \(x_3 + 3x_1^i\) in the first half and \(x_3 - 3x_1^i\) later

Correlated setting

Global PDP

pdp = effector.PDP(data=X_cor_train, model=model_cor, schema={"feature_names": ['x1','x2','x3'], "target_name": "Y"})
pdp.plot(feature=0, centering=True, show_avg_output=False, heterogeneity="ice", y_limits=[-5, 5])
pdp.plot(feature=1, centering=True, show_avg_output=False, heterogeneity="ice", y_limits=[-5, 5])
pdp.plot(feature=2, centering=True, show_avg_output=False, heterogeneity="ice", y_limits=[-5, 5])

png

png

png

Regional-PDP

pdp = effector.PDP(
    data=X_cor_train, model=model_cor,
    schema={"feature_names": ['x1', 'x2', 'x3']},
    axis_limits=np.array([[-1, 1], [-1, 1], [-1, 1]]).T,
    nof_instances="all",
)
pdp.fit("all", centering=True)
finder = effector.space_partitioning.Best(min_heterogeneity_decrease_pcg=0.4, numerical_features_grid_size=10)
partitions = {feat: pdp.find_regions(feat, finder=finder) for feat in range(3)}
partitions[0].show()
Feature 0 - Full partition tree:
🌳 Full Tree Structure:
───────────────────────
x1 πŸ”Ή [id: 0 | heter: 1.46 | inst: 900 | w: 1.00]
    x3 < 0.00 πŸ”Ή [id: 1 | heter: 0.21 | inst: 436 | w: 0.48]
    x3 β‰₯ 0.00 πŸ”Ή [id: 2 | heter: 0.27 | inst: 464 | w: 0.52]
        0.00 ≀ x3 < 0.20 πŸ”Ή [id: 3 | heter: 0.28 | inst: 115 | w: 0.13]
        x3 β‰₯ 0.20 πŸ”Ή [id: 4 | heter: 0.02 | inst: 349 | w: 0.39]
--------------------------------------------------
Feature 0 - Statistics per tree level:
🌳 Tree Summary:
─────────────────
Level 0πŸ”Ήheter: 1.46
    Level 1πŸ”Ήheter: 0.24 | πŸ”»1.22 (83.76%)
        Level 2πŸ”Ήheter: 0.05 | πŸ”»0.19 (80.94%)
part = partitions[0]
for r in part:
    if r.level == 1:
        part.plot(r.idx, heterogeneity="ice", centering=True, y_limits=[-5, 5])

png

png

partitions[1].show()
Feature 1 - Full partition tree:
No splits found for feature 1
--------------------------------------------------
Feature 1 - Statistics per tree level:
No splits found for feature 1
partitions[2].show()
Feature 2 - Full partition tree:
🌳 Full Tree Structure:
───────────────────────
x3 πŸ”Ή [id: 0 | heter: 1.46 | inst: 900 | w: 1.00]
    x1 < 0.00 πŸ”Ή [id: 1 | heter: 0.72 | inst: 463 | w: 0.51]
        x1 < -0.40 πŸ”Ή [id: 2 | heter: 0.39 | inst: 246 | w: 0.27]
        -0.40 ≀ x1 < 0.00 πŸ”Ή [id: 3 | heter: 0.33 | inst: 217 | w: 0.24]
    x1 β‰₯ 0.00 πŸ”Ή [id: 4 | heter: 0.75 | inst: 437 | w: 0.49]
        0.00 ≀ x1 < 0.40 πŸ”Ή [id: 5 | heter: 0.32 | inst: 189 | w: 0.21]
        x1 β‰₯ 0.40 πŸ”Ή [id: 6 | heter: 0.48 | inst: 248 | w: 0.28]
--------------------------------------------------
Feature 2 - Statistics per tree level:
🌳 Tree Summary:
─────────────────
Level 0πŸ”Ήheter: 1.46
    Level 1πŸ”Ήheter: 0.74 | πŸ”»0.72 (49.52%)
        Level 2πŸ”Ήheter: 0.39 | πŸ”»0.35 (47.69%)
part = partitions[2]
for r in part:
    if r.level == 1:
        part.plot(r.idx, heterogeneity="ice", centering=True, y_limits=[-5, 5])

png

png

Conclusion

(RH)ALE

def model_uncor_jac(x):
    x_tensor = tf.convert_to_tensor(x, dtype=tf.float32)
    with tf.GradientTape() as t:
        t.watch(x_tensor)
        pred = model_uncor(x_tensor)
        grads = t.gradient(pred, x_tensor)
    return grads.numpy()

def model_cor_jac(x):
    x_tensor = tf.convert_to_tensor(x, dtype=tf.float32)
    with tf.GradientTape() as t:
        t.watch(x_tensor)
        pred = model_cor(x_tensor)
        grads = t.gradient(pred, x_tensor)
    return grads.numpy()

Uncorrelated setting

Global RHALE

rhale = effector.RHALE(data=X_uncor_train, model=model_uncor, model_jac=model_uncor_jac, schema={"feature_names": ['x1','x2','x3'], "target_name": "Y"})

binning_method = effector.axis_partitioning.Fixed(10, min_points_per_bin=0)
rhale.fit(features="all", binning_method=binning_method, centering=True)

rhale.plot(feature=0, centering=True, heterogeneity="std", show_avg_output=False, y_limits=[-5, 5], dy_limits=[-5, 5])
rhale.plot(feature=1, centering=True, heterogeneity="std", show_avg_output=False, y_limits=[-5, 5], dy_limits=[-5, 5])
rhale.plot(feature=2, centering=True, heterogeneity="std", show_avg_output=False, y_limits=[-5, 5], dy_limits=[-5, 5])

png

png

png

Regional RHALE

rhale = effector.RHALE(
    data=X_uncor_train, model=model_uncor, model_jac=model_uncor_jac,
    schema={"feature_names": ['x1', 'x2', 'x3']},
    axis_limits=np.array([[-1, 1], [-1, 1], [-1, 1]]).T,
    nof_instances="all",
)
binning_method = effector.axis_partitioning.Fixed(11, min_points_per_bin=0)
rhale.fit("all", binning_method=binning_method, centering=True)
finder = effector.space_partitioning.Best(min_heterogeneity_decrease_pcg=0.6, numerical_features_grid_size=10)
partitions = {feat: rhale.find_regions(feat, finder=finder) for feat in range(3)}
partitions[0].show()
Feature 0 - Full partition tree:
🌳 Full Tree Structure:
───────────────────────
x1 πŸ”Ή [id: 0 | heter: 1.68 | inst: 1000 | w: 1.00]
    x3 < 0.00 πŸ”Ή [id: 1 | heter: 0.30 | inst: 500 | w: 0.50]
    x3 β‰₯ 0.00 πŸ”Ή [id: 2 | heter: 0.35 | inst: 500 | w: 0.50]
--------------------------------------------------
Feature 0 - Statistics per tree level:
🌳 Tree Summary:
─────────────────
Level 0πŸ”Ήheter: 1.68
    Level 1πŸ”Ήheter: 0.33 | πŸ”»1.35 (80.56%)
part = partitions[0]
for r in part:
    if r.level == 1:
        part.plot(r.idx, heterogeneity="std", centering=True, y_limits=[-5, 5])

png

png

partitions[1].show()
Feature 1 - Full partition tree:
No splits found for feature 1
--------------------------------------------------
Feature 1 - Statistics per tree level:
No splits found for feature 1
partitions[2].show()
Feature 2 - Full partition tree:
No splits found for feature 2
--------------------------------------------------
Feature 2 - Statistics per tree level:
No splits found for feature 2

Conclusion

Correlated setting

Global RHALE

rhale = effector.RHALE(data=X_cor_train, model=model_cor, model_jac=model_cor_jac, schema={"feature_names": ['x1','x2','x3'], "target_name": "Y"})

binning_method = effector.axis_partitioning.Fixed(10, min_points_per_bin=0)
rhale.fit(features="all", binning_method=binning_method, centering=True)
rhale.plot(feature=0, centering=True, heterogeneity="std", show_avg_output=False, y_limits=[-5, 5], dy_limits=[-5, 5])
rhale.plot(feature=1, centering=True, heterogeneity="std", show_avg_output=False, y_limits=[-5, 5], dy_limits=[-5, 5])
rhale.plot(feature=2, centering=True, heterogeneity="std", show_avg_output=False, y_limits=[-5, 5], dy_limits=[-5, 5])

png

png

png

Regional RHALE

rhale = effector.RHALE(
    data=X_cor_train, model=model_cor, model_jac=model_cor_jac,
    schema={"feature_names": ['x1', 'x2', 'x3']},
    axis_limits=np.array([[-1, 1], [-1, 1], [-1, 1]]).T,
    nof_instances="all",
)
binning_method = effector.axis_partitioning.Fixed(11, min_points_per_bin=0)
rhale.fit("all", binning_method=binning_method, centering=True)
finder = effector.space_partitioning.Best(min_heterogeneity_decrease_pcg=0.6, numerical_features_grid_size=10)
partitions = {feat: rhale.find_regions(feat, finder=finder) for feat in range(3)}
partitions[0].show()
Feature 0 - Full partition tree:
🌳 Full Tree Structure:
───────────────────────
x1 πŸ”Ή [id: 0 | heter: 0.86 | inst: 900 | w: 1.00]
    x3 < 0.00 πŸ”Ή [id: 1 | heter: 0.25 | inst: 436 | w: 0.48]
    x3 β‰₯ 0.00 πŸ”Ή [id: 2 | heter: 0.30 | inst: 464 | w: 0.52]
--------------------------------------------------
Feature 0 - Statistics per tree level:
🌳 Tree Summary:
─────────────────
Level 0πŸ”Ήheter: 0.86
    Level 1πŸ”Ήheter: 0.28 | πŸ”»0.58 (67.75%)
partitions[1].show()
Feature 1 - Full partition tree:
No splits found for feature 1
--------------------------------------------------
Feature 1 - Statistics per tree level:
No splits found for feature 1
partitions[2].show()
Feature 2 - Full partition tree:
No splits found for feature 2
--------------------------------------------------
Feature 2 - Statistics per tree level:
No splits found for feature 2

Conclusion

SHAP DP

Uncorrelated setting

Global SHAP DP

shap = effector.ShapDP(data=X_uncor_train, model=model_uncor, schema={"feature_names": ['x1', 'x2', 'x3'], "target_name": "Y"})
binning_method = effector.axis_partitioning.Fixed(nof_bins=5, min_points_per_bin=0)
shap.fit(features="all", binning_method=binning_method, centering=True)
shap.plot(feature=0, centering=True, heterogeneity="shap_values", show_avg_output=False, y_limits=[-3, 3])
shap.plot(feature=1, centering=True, heterogeneity="shap_values", show_avg_output=False, y_limits=[-3, 3])
shap.plot(feature=2, centering=True, heterogeneity="shap_values", show_avg_output=False, y_limits=[-3, 3])

png

png

png

Regional SHAP-DP

shap = effector.ShapDP(
    data=X_uncor_train, model=model_uncor,
    schema={"feature_names": ['x1', 'x2', 'x3']},
    axis_limits=np.array([[-1, 1], [-1, 1], [-1, 1]]).T,
    nof_instances="all",
)
binning_method = effector.axis_partitioning.Fixed(nof_bins=5, min_points_per_bin=0)
shap.fit("all", binning_method=binning_method, centering=True)
finder = effector.space_partitioning.Best(min_heterogeneity_decrease_pcg=0.6, numerical_features_grid_size=10)
partitions = {feat: shap.find_regions(feat, finder=finder) for feat in range(3)}
partitions[0].show()
Feature 0 - Full partition tree:
🌳 Full Tree Structure:
───────────────────────
x1 πŸ”Ή [id: 0 | heter: 0.88 | inst: 1000 | w: 1.00]
    x3 < 0.00 πŸ”Ή [id: 1 | heter: 0.20 | inst: 500 | w: 0.50]
    x3 β‰₯ 0.00 πŸ”Ή [id: 2 | heter: 0.17 | inst: 500 | w: 0.50]
--------------------------------------------------
Feature 0 - Statistics per tree level:
🌳 Tree Summary:
─────────────────
Level 0πŸ”Ήheter: 0.88
    Level 1πŸ”Ήheter: 0.19 | πŸ”»0.69 (78.68%)
part = partitions[0]
for r in part:
    if r.level == 1:
        part.plot(r.idx, heterogeneity="std", centering=True, y_limits=[-5, 5])

png

png

partitions[1].show()
Feature 1 - Full partition tree:
No splits found for feature 1
--------------------------------------------------
Feature 1 - Statistics per tree level:
No splits found for feature 1
partitions[2].show()
Feature 2 - Full partition tree:
No splits found for feature 2
--------------------------------------------------
Feature 2 - Statistics per tree level:
No splits found for feature 2

Conclusion

Correlated setting

Global SHAP-DP

shap = effector.ShapDP(data=X_cor_train, model=model_cor, schema={"feature_names": ['x1', 'x2', 'x3'], "target_name": "Y"})
binning_method = effector.axis_partitioning.Fixed(nof_bins=5, min_points_per_bin=0)
shap.fit(features="all", binning_method=binning_method, centering=True)
shap.plot(feature=0, centering=True, heterogeneity="shap_values", show_avg_output=False, y_limits=[-3, 3])
shap.plot(feature=1, centering=True, heterogeneity="shap_values", show_avg_output=False, y_limits=[-3, 3])
shap.plot(feature=2, centering=True, heterogeneity="shap_values", show_avg_output=False, y_limits=[-3, 3])

png

png

png

Regional SHAP

shap = effector.ShapDP(
    data=X_cor_train, model=model_cor,
    schema={"feature_names": ['x1', 'x2', 'x3']},
    axis_limits=np.array([[-1, 1], [-1, 1], [-1, 1]]).T,
    nof_instances="all",
)
binning_method = effector.axis_partitioning.Fixed(nof_bins=5, min_points_per_bin=0)
shap.fit("all", binning_method=binning_method, centering=True)
finder = effector.space_partitioning.Best(min_heterogeneity_decrease_pcg=0.6, numerical_features_grid_size=10)
partitions = {feat: shap.find_regions(feat, finder=finder) for feat in range(3)}
partitions[0].show()
partitions[1].show()
partitions[2].show()
Feature 0 - Full partition tree:
No splits found for feature 0
--------------------------------------------------
Feature 0 - Statistics per tree level:
No splits found for feature 0




Feature 1 - Full partition tree:
No splits found for feature 1
--------------------------------------------------
Feature 1 - Statistics per tree level:
No splits found for feature 1




Feature 2 - Full partition tree:
No splits found for feature 2
--------------------------------------------------
Feature 2 - Statistics per tree level:
No splits found for feature 2

Conclusion

Cross-method sanity check

The one-liner effector.explain with every engine this notebook's model supports. Everything must run end to end; the closing table puts the reads side by side. Where methods disagree β€” ranking, accepted splits, RΒ² β€” that is a property of the data/model worth a closer look, not an error.

from pathlib import Path
_out = Path("reports") / "04_regional_effects_real_f"
_out.mkdir(parents=True, exist_ok=True)

# === cross-method sweep: effector.explain on every applicable engine ======
sweep_reports = {}
for _m in ["pdp", "ale", "rhale", "shapdp"]:
    _kw = {"nof_instances": 300} if _m == "shapdp" else {}
    print(f"--- {_m} " + "-" * 50)
    sweep_reports[_m] = effector.explain(
        X_uncor_train, model_uncor, None, method=_m, schema={"feature_names": ['x1','x2','x3'], "target_name": "Y"}, **_kw
    )
    if _m != "pdp":  # the published report is the narrated one above
        sweep_reports[_m].to_html(_out / f"report_{_m}.html")

print()
print(f"{'method':<8} {'ranking (plotted)':<44} {'GAM R2':>8} {'final R2':>9}  splits")
for _m, _r in sweep_reports.items():
    _rank = " > ".join(fr.name for fr in _r.features)
    _ev = _r.explained_variance
    if _ev:
        _sp = "; ".join(f"{s['name']} on {s['on']}" for s in _ev["stages"]) or "none"
        print(f"{_m:<8} {_rank:<44} {_ev['gam_r2']:>7.1%} {_ev['regional_r2']:>8.1%}  {_sp}")
    else:
        print(f"{_m:<8} {_rank:<44} {'-':>7} {'-':>8}  (derivative scale: no variance ledger)")

print(f"\nreports stored in {_out}/")
--- pdp --------------------------------------------------
[effector] global effects   (GAM)  -> 11.0% of the model's variance
           regional effects (CALM) -> 99.3%
--- ale --------------------------------------------------


[effector] global effects   (GAM)  -> 10.5% of the model's variance
           regional effects (CALM) -> 99.3%


/home/givasile/github/packages/effector/effector/report.py:606: UserWarning: This figure includes Axes that are not compatible with tight_layout, so results might be incorrect.
  fig.tight_layout()


--- rhale --------------------------------------------------


[effector] global effects   (GAM)  -> 10.8% of the model's variance
           regional effects (CALM) -> 99.3%


/home/givasile/github/packages/effector/effector/report.py:606: UserWarning: This figure includes Axes that are not compatible with tight_layout, so results might be incorrect.
  fig.tight_layout()


--- shapdp --------------------------------------------------


[effector] global effects   (GAM)  -> 9.1% of the model's variance
           regional effects (CALM) -> 98.6%


/home/givasile/github/packages/effector/effector/report.py:606: UserWarning: This figure includes Axes that are not compatible with tight_layout, so results might be incorrect.
  fig.tight_layout()



method   ranking (plotted)                              GAM R2  final R2  splits
pdp      x1 > x3                                        11.0%    99.3%  x1 on x3
ale      x1 > x3                                        10.5%    99.3%  x1 on x3
rhale    x1 > x3                                        10.8%    99.3%  x1 on x3
shapdp   x3 > x1                                         9.1%    98.6%  x1 on x3; x3 on x1

reports stored in reports/04_regional_effects_real_f/