63 if (output.
rank() != 2) {
64 throw std::invalid_argument(
"GradCAM::explain: network output must be rank-2 (N, num_classes)");
66 if (target_index < 0 || target_index >= output.
shape().
dim(1)) {
67 throw std::invalid_argument(
"GradCAM::explain: target_index out of range");
70 int64_t num_classes = output.
shape().
dim(1);
72 std::vector<float> seed_values(
static_cast<size_t>(output.
numel()), 0.0f);
73 for (int64_t n = 0; n < N; ++n) {
74 seed_values[
static_cast<size_t>(n * num_classes + target_index)] = 1.0f;
83 if (conv_nodes.empty()) {
84 throw std::invalid_argument(
"GradCAM::explain: graph has no Conv layer");
86 NodeId target_node = conv_nodes.back();
91 int64_t channels = activation_tensor.
shape().
dim(1);
92 int64_t height = activation_tensor.
shape().
dim(2);
93 int64_t width = activation_tensor.
shape().
dim(3);
96 const std::vector<float> activation = activation_tensor.
to_host_vector();
98 auto at4 = [&](
const std::vector<float>& v, int64_t n, int64_t c, int64_t h, int64_t w) {
99 return v[
static_cast<size_t>(((n * channels + c) * height + h) * width + w)];
102 std::vector<float> cam(
static_cast<size_t>(N * height * width), 0.0f);
103 for (int64_t n = 0; n < N; ++n) {
104 std::vector<float> alpha(
static_cast<size_t>(channels), 0.0f);
105 for (int64_t c = 0; c < channels; ++c) {
107 for (int64_t h = 0; h < height; ++h) {
108 for (int64_t w = 0; w < width; ++w) {
109 sum += at4(grad, n, c, h, w);
112 alpha[
static_cast<size_t>(c)] = sum /
static_cast<float>(height * width);
115 for (int64_t h = 0; h < height; ++h) {
116 for (int64_t w = 0; w < width; ++w) {
118 for (int64_t c = 0; c < channels; ++c) {
119 value += alpha[
static_cast<size_t>(c)] * at4(activation, n, c, h, w);
121 cam[
static_cast<size_t>((n * height + h) * width + w)] = value > 0.0f ? value : 0.0f;
127 cam, activation_tensor.
device());
128 return Attribution{
"grad_cam", std::move(cam_tensor),
129 {{
"target_index", std::to_string(target_index)},
130 {
"layer_node_id", std::to_string(target_node)}}};
PULSATRIX_ASSERT – debug-only invariant check for programmer errors, distinct from throw (used for ca...
First-class explanation result type – values, method, and metadata together.
std::vector< NodeId > nodes_by_op_type(OpType op_type) const
Finds every node with the given op type.
Vendor-agnostic compute/memory backend. CPUBackend, CUDABackend (Phase 1.5), and HIPBackend (Phase 1....
Definition device_backend.hpp:219
Wraps an ordered chain of Modules, running them via Module::forward_traced to build a real Computatio...
Definition explainer_context.hpp:67
const Tensor & gradient(NodeId id) const
The cached gradient at a node, from the most recent backward_pass() call.
Definition explainer_context.hpp:294
const ComputationGraph & graph() const
The current graph (from the most recent forward_pass() call).
Definition explainer_context.hpp:272
Tensor backward_pass(const Tensor &output_grad)
Runs Autograd::backward from the most recent forward_pass()'s output node.
Definition explainer_context.hpp:207
Tensor forward_pass(const Tensor &input)
Runs the full module chain forward, building a fresh graph and caching every node's activation value ...
Definition explainer_context.hpp:95
const Tensor & activation(NodeId id) const
The cached activation value at a node, from the most recent forward_pass().
Definition explainer_context.hpp:278
L^c = ReLU(sum_k alpha^c_k * A^k), where alpha^c_k = mean_ij(d(y^c)/d(A^k_ij)) and A is the last OpTy...
Definition grad_cam.hpp:37
Attribution explain(ExplainerContext &ctx, const Tensor &input, int64_t target_index, DeviceBackend *backend) const
Computes the Grad-CAM map for one target class index.
Definition grad_cam.hpp:57
An N-dimensional shape. A plain aggregate of dimensions with no invariant beyond "non-negative dimens...
Definition shape.hpp:24
int64_t dim(size_t index) const
Size of a single dimension.
Definition shape.hpp:93
N-dimensional tensor. Owns its data buffer exclusively; a DeviceBackend* is injected (not owned) – th...
Definition tensor.hpp:29
DeviceType device() const
Which device this tensor's buffer conceptually resides on.
Definition tensor.hpp:122
int64_t rank() const
Number of dimensions – shape().rank().
Definition tensor.hpp:119
int64_t numel() const
Total element count – shape().numel().
Definition tensor.hpp:116
const Shape & shape() const
This tensor's shape.
Definition tensor.hpp:113
std::vector< float > to_host_vector() const
Copies the whole buffer to a host vector through the owning backend, on any device.
Abstract interface isolating vendor-specific memory/compute operations from Tensor/ComputationGraph.
Stable interface every explainer gets, regardless of type (charter Part 2 SS2).
DeviceBackend * backend_beside(const Tensor &like, DeviceBackend *backend)
The backend to allocate a tensor through that must live beside like.
Definition attribution.hpp:49
Definition acquisition_functions.hpp:16
size_t NodeId
Stable identifier for a Node within its owning ComputationGraph.
Definition node.hpp:18
An explanation result: the raw attribution values, the method that produced them, and any relevant me...
Definition attribution.hpp:23
N-dimensional tensor – owns a buffer via DeviceBackend*, RAII (Rule of Five).