Recipe: Saliency and Integrated Gradients¶
What you'll build: both gradient-based explainers run on the same small
Linear(2,4) -> ReLU -> Linear(4,1) network for one input. The recipe also checks that the
Integrated Gradients attributions sum to F(x) - F(baseline).
CMake target: saliency_and_ig_recipe
(examples/recipes/saliency_and_integrated_gradients.cpp).
Run it: ./build/saliency_and_ig_recipe (Windows: build\Release\saliency_and_ig_recipe.exe).
Code¶
ExplainerContext ctx({&linear1, &relu, &linear2});
Tensor input(Shape({1, 2}), &backend, {1.0f, 1.0f});
Tensor baseline(Shape({1, 2}), &backend); // zero-initialized
Saliency saliency;
Attribution sal = saliency.explain(ctx, input, /*target_index=*/0, &backend);
IntegratedGradients ig;
Attribution ig_attr = ig.explain(ctx, input, baseline, /*target_index=*/0, /*steps=*/200, &backend);
Full source: examples/recipes/saliency_and_integrated_gradients.cpp.
Expected output¶
Saliency and Integrated Gradients recipe -- Linear(2,4)->ReLU->Linear(4,1)
input (1, 1) -> output 0.3200
saliency: d(out)/d(in) = [0.4200, -0.1000]
integrated gradients: IG = [0.4200, -0.1000], sum=0.3200 (F(x)-F(baseline)=0.3200)
...
What's happening¶
Saliency::explain() seeds a one-hot vector at target_index and calls
ctx.backward_pass(). The gradient that comes back is the saliency map.
IntegratedGradients instead averages the gradient along the straight-line path from
baseline to input, over 200 steps. On this input the two methods happen to agree, because
the ReLU doesn't switch on or off along the path.
IG's defining property is completeness: the printed sum of its attributions equals
F(x) - F(baseline). Saliency has no such guarantee. It is the gradient at a single point, so
it can miss parts of the path where a ReLU switches on or off.
See also: Gradient-Based Explainers.