vlmprobe 0.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vlm_probing/__init__.py +17 -0
- vlm_probing/_vendor/__init__.py +1 -0
- vlm_probing/_vendor/chefer2021/LICENSE +21 -0
- vlm_probing/_vendor/chefer2021/README.md +22 -0
- vlm_probing/_vendor/chefer2021/__init__.py +1 -0
- vlm_probing/_vendor/chefer2021/layer_helpers.py +21 -0
- vlm_probing/_vendor/chefer2021/layers.py +280 -0
- vlm_probing/_vendor/chefer2021/vit.py +372 -0
- vlm_probing/_vendor/chefer2021/weight_init.py +60 -0
- vlm_probing/adapters/__init__.py +7 -0
- vlm_probing/adapters/auto.py +35 -0
- vlm_probing/adapters/base.py +15 -0
- vlm_probing/adapters/hf_attention.py +70 -0
- vlm_probing/adapters/huggingface.py +184 -0
- vlm_probing/adapters/spec.py +198 -0
- vlm_probing/adapters/torch.py +201 -0
- vlm_probing/adapters/vit.py +103 -0
- vlm_probing/api/__init__.py +1 -0
- vlm_probing/api/attention.py +214 -0
- vlm_probing/api/attention_maps.py +146 -0
- vlm_probing/api/causal.py +371 -0
- vlm_probing/api/common.py +145 -0
- vlm_probing/api/lenses.py +248 -0
- vlm_probing/attention/__init__.py +22 -0
- vlm_probing/attention/_attribution_utils.py +45 -0
- vlm_probing/attention/attribution.py +46 -0
- vlm_probing/attention/base.py +68 -0
- vlm_probing/attention/beyond_intuition.py +103 -0
- vlm_probing/attention/chefer.py +53 -0
- vlm_probing/attention/grad_cam.py +55 -0
- vlm_probing/attention/head_logit_attribution.py +54 -0
- vlm_probing/attention/lrp.py +195 -0
- vlm_probing/attention/profile.py +51 -0
- vlm_probing/attention/relevance.py +47 -0
- vlm_probing/attention/reweight.py +49 -0
- vlm_probing/attention/rollout.py +54 -0
- vlm_probing/attention/tam.py +58 -0
- vlm_probing/attention/temperature.py +46 -0
- vlm_probing/causal/__init__.py +15 -0
- vlm_probing/causal/activation_patching.py +29 -0
- vlm_probing/causal/attention_knockout.py +47 -0
- vlm_probing/causal/attribution_patching.py +47 -0
- vlm_probing/causal/base.py +94 -0
- vlm_probing/causal/eap_ig.py +81 -0
- vlm_probing/causal/path_patching.py +185 -0
- vlm_probing/causal/steering.py +63 -0
- vlm_probing/causal/transformer_eap_ig.py +397 -0
- vlm_probing/causal/visual_steering.py +99 -0
- vlm_probing/core/__init__.py +4 -0
- vlm_probing/core/base.py +16 -0
- vlm_probing/core/types.py +41 -0
- vlm_probing/lenses/__init__.py +10 -0
- vlm_probing/lenses/attention.py +116 -0
- vlm_probing/lenses/base.py +144 -0
- vlm_probing/lenses/embed.py +74 -0
- vlm_probing/lenses/jacobian.py +127 -0
- vlm_probing/lenses/logit.py +28 -0
- vlm_probing/lenses/patchscope.py +65 -0
- vlm_probing/lenses/tuned.py +76 -0
- vlm_probing/metrics.py +91 -0
- vlm_probing/prober.py +129 -0
- vlm_probing/training/__init__.py +8 -0
- vlm_probing/training/cache.py +49 -0
- vlm_probing/training/distribution.py +187 -0
- vlm_probing/training/jacobian.py +139 -0
- vlm_probing/training/model_bound.py +83 -0
- vlm_probing/training/tuned_import.py +98 -0
- vlm_probing/visualization/__init__.py +22 -0
- vlm_probing/visualization/_common.py +112 -0
- vlm_probing/visualization/attention.py +238 -0
- vlm_probing/visualization/causal.py +222 -0
- vlm_probing/visualization/lenses.py +84 -0
- vlm_probing/visualization/path_graphs.py +254 -0
- vlmprobe/__init__.py +38 -0
- vlmprobe-0.9.0.dist-info/METADATA +257 -0
- vlmprobe-0.9.0.dist-info/RECORD +78 -0
- vlmprobe-0.9.0.dist-info/WHEEL +5 -0
- vlmprobe-0.9.0.dist-info/top_level.txt +2 -0
vlm_probing/__init__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Three method families: causal interventions, attention analysis, and lenses."""
|
|
2
|
+
from .core import BaseMethod, ForwardTrace, ProbeResult
|
|
3
|
+
from .adapters import BaseModelAdapter, HookPoint, ModelReadout, TorchModelAdapter
|
|
4
|
+
from .adapters import CapabilityError, HeadSite, ModelSpec, ProbeInputs, TokenLayout, register_adapter
|
|
5
|
+
from .prober import Prober
|
|
6
|
+
from .api.lenses import LensMethods
|
|
7
|
+
from .api.attention import AttentionMethods
|
|
8
|
+
from .api.causal import CausalMethods
|
|
9
|
+
from .metrics import TokenMargin, SequenceLogProb, ClassScore
|
|
10
|
+
from . import visualization
|
|
11
|
+
|
|
12
|
+
__version__ = "0.9.0"
|
|
13
|
+
__all__ = ["BaseMethod", "ProbeResult", "ForwardTrace", "BaseModelAdapter",
|
|
14
|
+
"HookPoint", "ModelReadout", "TorchModelAdapter", "Prober", "ModelSpec",
|
|
15
|
+
"ProbeInputs", "TokenLayout", "HeadSite", "CapabilityError", "register_adapter",
|
|
16
|
+
"LensMethods", "AttentionMethods", "CausalMethods", "TokenMargin", "SequenceLogProb", "ClassScore",
|
|
17
|
+
"visualization"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Pinned third-party research kernels, imported only by explicit backends."""
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 Hila Chefer
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Pinned Chefer CVPR 2021 ViT relevance implementation
|
|
2
|
+
|
|
3
|
+
Source: [hila-chefer/Transformer-Explainability](https://github.com/hila-chefer/Transformer-Explainability),
|
|
4
|
+
commit [`c3e578f76b954e8528afeaaee26de3f07e3fe559`](https://github.com/hila-chefer/Transformer-Explainability/tree/c3e578f76b954e8528afeaaee26de3f07e3fe559).
|
|
5
|
+
The source is MIT licensed; the complete license is retained in [LICENSE](LICENSE).
|
|
6
|
+
|
|
7
|
+
| Local file | Upstream file | Changes |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| `layers.py` | `modules/layers_ours.py` | Byte-identical. |
|
|
10
|
+
| `vit.py` | `baselines/ViT/ViT_LRP.py` | Relative imports; equivalent native PyTorch reshapes replace `einops.rearrange`; remove model URL tables, pretrained factories and conversion helpers. All forward and relevance rules remain intact. |
|
|
11
|
+
| `weight_init.py` | `baselines/ViT/weight_init.py` | Byte-identical. |
|
|
12
|
+
| `layer_helpers.py` | `baselines/ViT/layer_helpers.py` | Byte-identical. |
|
|
13
|
+
|
|
14
|
+
The public wrapper does not implicitly download weights. It can copy a native
|
|
15
|
+
Hugging Face ViT classifier into this architecture with strict state-dict loading.
|
|
16
|
+
The wrapper evaluates one image per relevance propagation, replaces hard-coded
|
|
17
|
+
CUDA class seeds with device-aware tensors, leaves parameter gradient buffers
|
|
18
|
+
unchanged, and restores the caller's training flags.
|
|
19
|
+
|
|
20
|
+
The separate upstream `ViT_orig_LRP.py` / `layers_lrp.py` implementation is not
|
|
21
|
+
included. Do not describe the optional `full` or `last_layer` modes of this
|
|
22
|
+
Chefer-rule model as that separate conventional LRP baseline.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Chefer et al. CVPR 2021 source; see README.md and LICENSE."""
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
""" Layer/Module Helpers
|
|
2
|
+
Hacked together by / Copyright 2020 Ross Wightman
|
|
3
|
+
"""
|
|
4
|
+
from itertools import repeat
|
|
5
|
+
import collections.abc
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# From PyTorch internals
|
|
9
|
+
def _ntuple(n):
|
|
10
|
+
def parse(x):
|
|
11
|
+
if isinstance(x, collections.abc.Iterable):
|
|
12
|
+
return x
|
|
13
|
+
return tuple(repeat(x, n))
|
|
14
|
+
return parse
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
to_1tuple = _ntuple(1)
|
|
18
|
+
to_2tuple = _ntuple(2)
|
|
19
|
+
to_3tuple = _ntuple(3)
|
|
20
|
+
to_4tuple = _ntuple(4)
|
|
21
|
+
to_ntuple = _ntuple
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import torch
|
|
2
|
+
import torch.nn as nn
|
|
3
|
+
import torch.nn.functional as F
|
|
4
|
+
|
|
5
|
+
__all__ = ['forward_hook', 'Clone', 'Add', 'Cat', 'ReLU', 'GELU', 'Dropout', 'BatchNorm2d', 'Linear', 'MaxPool2d',
|
|
6
|
+
'AdaptiveAvgPool2d', 'AvgPool2d', 'Conv2d', 'Sequential', 'safe_divide', 'einsum', 'Softmax', 'IndexSelect',
|
|
7
|
+
'LayerNorm', 'AddEye']
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def safe_divide(a, b):
|
|
11
|
+
den = b.clamp(min=1e-9) + b.clamp(max=1e-9)
|
|
12
|
+
den = den + den.eq(0).type(den.type()) * 1e-9
|
|
13
|
+
return a / den * b.ne(0).type(b.type())
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def forward_hook(self, input, output):
|
|
17
|
+
if type(input[0]) in (list, tuple):
|
|
18
|
+
self.X = []
|
|
19
|
+
for i in input[0]:
|
|
20
|
+
x = i.detach()
|
|
21
|
+
x.requires_grad = True
|
|
22
|
+
self.X.append(x)
|
|
23
|
+
else:
|
|
24
|
+
self.X = input[0].detach()
|
|
25
|
+
self.X.requires_grad = True
|
|
26
|
+
|
|
27
|
+
self.Y = output
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def backward_hook(self, grad_input, grad_output):
|
|
31
|
+
self.grad_input = grad_input
|
|
32
|
+
self.grad_output = grad_output
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class RelProp(nn.Module):
|
|
36
|
+
def __init__(self):
|
|
37
|
+
super(RelProp, self).__init__()
|
|
38
|
+
# if not self.training:
|
|
39
|
+
self.register_forward_hook(forward_hook)
|
|
40
|
+
|
|
41
|
+
def gradprop(self, Z, X, S):
|
|
42
|
+
C = torch.autograd.grad(Z, X, S, retain_graph=True)
|
|
43
|
+
return C
|
|
44
|
+
|
|
45
|
+
def relprop(self, R, alpha):
|
|
46
|
+
return R
|
|
47
|
+
|
|
48
|
+
class RelPropSimple(RelProp):
|
|
49
|
+
def relprop(self, R, alpha):
|
|
50
|
+
Z = self.forward(self.X)
|
|
51
|
+
S = safe_divide(R, Z)
|
|
52
|
+
C = self.gradprop(Z, self.X, S)
|
|
53
|
+
|
|
54
|
+
if torch.is_tensor(self.X) == False:
|
|
55
|
+
outputs = []
|
|
56
|
+
outputs.append(self.X[0] * C[0])
|
|
57
|
+
outputs.append(self.X[1] * C[1])
|
|
58
|
+
else:
|
|
59
|
+
outputs = self.X * (C[0])
|
|
60
|
+
return outputs
|
|
61
|
+
|
|
62
|
+
class AddEye(RelPropSimple):
|
|
63
|
+
# input of shape B, C, seq_len, seq_len
|
|
64
|
+
def forward(self, input):
|
|
65
|
+
return input + torch.eye(input.shape[2]).expand_as(input).to(input.device)
|
|
66
|
+
|
|
67
|
+
class ReLU(nn.ReLU, RelProp):
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
class GELU(nn.GELU, RelProp):
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
class Softmax(nn.Softmax, RelProp):
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
class LayerNorm(nn.LayerNorm, RelProp):
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
class Dropout(nn.Dropout, RelProp):
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class MaxPool2d(nn.MaxPool2d, RelPropSimple):
|
|
84
|
+
pass
|
|
85
|
+
|
|
86
|
+
class LayerNorm(nn.LayerNorm, RelProp):
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
class AdaptiveAvgPool2d(nn.AdaptiveAvgPool2d, RelPropSimple):
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class AvgPool2d(nn.AvgPool2d, RelPropSimple):
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class Add(RelPropSimple):
|
|
98
|
+
def forward(self, inputs):
|
|
99
|
+
return torch.add(*inputs)
|
|
100
|
+
|
|
101
|
+
def relprop(self, R, alpha):
|
|
102
|
+
Z = self.forward(self.X)
|
|
103
|
+
S = safe_divide(R, Z)
|
|
104
|
+
C = self.gradprop(Z, self.X, S)
|
|
105
|
+
|
|
106
|
+
a = self.X[0] * C[0]
|
|
107
|
+
b = self.X[1] * C[1]
|
|
108
|
+
|
|
109
|
+
a_sum = a.sum()
|
|
110
|
+
b_sum = b.sum()
|
|
111
|
+
|
|
112
|
+
a_fact = safe_divide(a_sum.abs(), a_sum.abs() + b_sum.abs()) * R.sum()
|
|
113
|
+
b_fact = safe_divide(b_sum.abs(), a_sum.abs() + b_sum.abs()) * R.sum()
|
|
114
|
+
|
|
115
|
+
a = a * safe_divide(a_fact, a.sum())
|
|
116
|
+
b = b * safe_divide(b_fact, b.sum())
|
|
117
|
+
|
|
118
|
+
outputs = [a, b]
|
|
119
|
+
|
|
120
|
+
return outputs
|
|
121
|
+
|
|
122
|
+
class einsum(RelPropSimple):
|
|
123
|
+
def __init__(self, equation):
|
|
124
|
+
super().__init__()
|
|
125
|
+
self.equation = equation
|
|
126
|
+
def forward(self, *operands):
|
|
127
|
+
return torch.einsum(self.equation, *operands)
|
|
128
|
+
|
|
129
|
+
class IndexSelect(RelProp):
|
|
130
|
+
def forward(self, inputs, dim, indices):
|
|
131
|
+
self.__setattr__('dim', dim)
|
|
132
|
+
self.__setattr__('indices', indices)
|
|
133
|
+
|
|
134
|
+
return torch.index_select(inputs, dim, indices)
|
|
135
|
+
|
|
136
|
+
def relprop(self, R, alpha):
|
|
137
|
+
Z = self.forward(self.X, self.dim, self.indices)
|
|
138
|
+
S = safe_divide(R, Z)
|
|
139
|
+
C = self.gradprop(Z, self.X, S)
|
|
140
|
+
|
|
141
|
+
if torch.is_tensor(self.X) == False:
|
|
142
|
+
outputs = []
|
|
143
|
+
outputs.append(self.X[0] * C[0])
|
|
144
|
+
outputs.append(self.X[1] * C[1])
|
|
145
|
+
else:
|
|
146
|
+
outputs = self.X * (C[0])
|
|
147
|
+
return outputs
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class Clone(RelProp):
|
|
152
|
+
def forward(self, input, num):
|
|
153
|
+
self.__setattr__('num', num)
|
|
154
|
+
outputs = []
|
|
155
|
+
for _ in range(num):
|
|
156
|
+
outputs.append(input)
|
|
157
|
+
|
|
158
|
+
return outputs
|
|
159
|
+
|
|
160
|
+
def relprop(self, R, alpha):
|
|
161
|
+
Z = []
|
|
162
|
+
for _ in range(self.num):
|
|
163
|
+
Z.append(self.X)
|
|
164
|
+
S = [safe_divide(r, z) for r, z in zip(R, Z)]
|
|
165
|
+
C = self.gradprop(Z, self.X, S)[0]
|
|
166
|
+
|
|
167
|
+
R = self.X * C
|
|
168
|
+
|
|
169
|
+
return R
|
|
170
|
+
|
|
171
|
+
class Cat(RelProp):
|
|
172
|
+
def forward(self, inputs, dim):
|
|
173
|
+
self.__setattr__('dim', dim)
|
|
174
|
+
return torch.cat(inputs, dim)
|
|
175
|
+
|
|
176
|
+
def relprop(self, R, alpha):
|
|
177
|
+
Z = self.forward(self.X, self.dim)
|
|
178
|
+
S = safe_divide(R, Z)
|
|
179
|
+
C = self.gradprop(Z, self.X, S)
|
|
180
|
+
|
|
181
|
+
outputs = []
|
|
182
|
+
for x, c in zip(self.X, C):
|
|
183
|
+
outputs.append(x * c)
|
|
184
|
+
|
|
185
|
+
return outputs
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class Sequential(nn.Sequential):
|
|
189
|
+
def relprop(self, R, alpha):
|
|
190
|
+
for m in reversed(self._modules.values()):
|
|
191
|
+
R = m.relprop(R, alpha)
|
|
192
|
+
return R
|
|
193
|
+
|
|
194
|
+
class BatchNorm2d(nn.BatchNorm2d, RelProp):
|
|
195
|
+
def relprop(self, R, alpha):
|
|
196
|
+
X = self.X
|
|
197
|
+
beta = 1 - alpha
|
|
198
|
+
weight = self.weight.unsqueeze(0).unsqueeze(2).unsqueeze(3) / (
|
|
199
|
+
(self.running_var.unsqueeze(0).unsqueeze(2).unsqueeze(3).pow(2) + self.eps).pow(0.5))
|
|
200
|
+
Z = X * weight + 1e-9
|
|
201
|
+
S = R / Z
|
|
202
|
+
Ca = S * weight
|
|
203
|
+
R = self.X * (Ca)
|
|
204
|
+
return R
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class Linear(nn.Linear, RelProp):
|
|
208
|
+
def relprop(self, R, alpha):
|
|
209
|
+
beta = alpha - 1
|
|
210
|
+
pw = torch.clamp(self.weight, min=0)
|
|
211
|
+
nw = torch.clamp(self.weight, max=0)
|
|
212
|
+
px = torch.clamp(self.X, min=0)
|
|
213
|
+
nx = torch.clamp(self.X, max=0)
|
|
214
|
+
|
|
215
|
+
def f(w1, w2, x1, x2):
|
|
216
|
+
Z1 = F.linear(x1, w1)
|
|
217
|
+
Z2 = F.linear(x2, w2)
|
|
218
|
+
S1 = safe_divide(R, Z1 + Z2)
|
|
219
|
+
S2 = safe_divide(R, Z1 + Z2)
|
|
220
|
+
C1 = x1 * torch.autograd.grad(Z1, x1, S1)[0]
|
|
221
|
+
C2 = x2 * torch.autograd.grad(Z2, x2, S2)[0]
|
|
222
|
+
|
|
223
|
+
return C1 + C2
|
|
224
|
+
|
|
225
|
+
activator_relevances = f(pw, nw, px, nx)
|
|
226
|
+
inhibitor_relevances = f(nw, pw, px, nx)
|
|
227
|
+
|
|
228
|
+
R = alpha * activator_relevances - beta * inhibitor_relevances
|
|
229
|
+
|
|
230
|
+
return R
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class Conv2d(nn.Conv2d, RelProp):
|
|
234
|
+
def gradprop2(self, DY, weight):
|
|
235
|
+
Z = self.forward(self.X)
|
|
236
|
+
|
|
237
|
+
output_padding = self.X.size()[2] - (
|
|
238
|
+
(Z.size()[2] - 1) * self.stride[0] - 2 * self.padding[0] + self.kernel_size[0])
|
|
239
|
+
|
|
240
|
+
return F.conv_transpose2d(DY, weight, stride=self.stride, padding=self.padding, output_padding=output_padding)
|
|
241
|
+
|
|
242
|
+
def relprop(self, R, alpha):
|
|
243
|
+
if self.X.shape[1] == 3:
|
|
244
|
+
pw = torch.clamp(self.weight, min=0)
|
|
245
|
+
nw = torch.clamp(self.weight, max=0)
|
|
246
|
+
X = self.X
|
|
247
|
+
L = self.X * 0 + \
|
|
248
|
+
torch.min(torch.min(torch.min(self.X, dim=1, keepdim=True)[0], dim=2, keepdim=True)[0], dim=3,
|
|
249
|
+
keepdim=True)[0]
|
|
250
|
+
H = self.X * 0 + \
|
|
251
|
+
torch.max(torch.max(torch.max(self.X, dim=1, keepdim=True)[0], dim=2, keepdim=True)[0], dim=3,
|
|
252
|
+
keepdim=True)[0]
|
|
253
|
+
Za = torch.conv2d(X, self.weight, bias=None, stride=self.stride, padding=self.padding) - \
|
|
254
|
+
torch.conv2d(L, pw, bias=None, stride=self.stride, padding=self.padding) - \
|
|
255
|
+
torch.conv2d(H, nw, bias=None, stride=self.stride, padding=self.padding) + 1e-9
|
|
256
|
+
|
|
257
|
+
S = R / Za
|
|
258
|
+
C = X * self.gradprop2(S, self.weight) - L * self.gradprop2(S, pw) - H * self.gradprop2(S, nw)
|
|
259
|
+
R = C
|
|
260
|
+
else:
|
|
261
|
+
beta = alpha - 1
|
|
262
|
+
pw = torch.clamp(self.weight, min=0)
|
|
263
|
+
nw = torch.clamp(self.weight, max=0)
|
|
264
|
+
px = torch.clamp(self.X, min=0)
|
|
265
|
+
nx = torch.clamp(self.X, max=0)
|
|
266
|
+
|
|
267
|
+
def f(w1, w2, x1, x2):
|
|
268
|
+
Z1 = F.conv2d(x1, w1, bias=None, stride=self.stride, padding=self.padding)
|
|
269
|
+
Z2 = F.conv2d(x2, w2, bias=None, stride=self.stride, padding=self.padding)
|
|
270
|
+
S1 = safe_divide(R, Z1)
|
|
271
|
+
S2 = safe_divide(R, Z2)
|
|
272
|
+
C1 = x1 * self.gradprop(Z1, x1, S1)[0]
|
|
273
|
+
C2 = x2 * self.gradprop(Z2, x2, S2)[0]
|
|
274
|
+
return C1 + C2
|
|
275
|
+
|
|
276
|
+
activator_relevances = f(pw, nw, px, nx)
|
|
277
|
+
inhibitor_relevances = f(nw, pw, px, nx)
|
|
278
|
+
|
|
279
|
+
R = alpha * activator_relevances - beta * inhibitor_relevances
|
|
280
|
+
return R
|