Activation Maximization for TCAV in Pytorch

username: Roos_Kraaijveld
cooked: <p>I am trying to calculate the TCAV vectors for my model, for which I need to do the following thing:</p>
<p>def compute_tcav();<br>
losses = [<br>
(ActivationMaximization(model.layers[layer_idx], filter_indices), -1)<br>
]<br>
opt = Optimizer(input_tensor, losses, wrt_tensor=wrt_tensor, norm_grads=False)<br>
grads = opt.minimize(seed_input=seed_input, max_iter=1, grad_modifier=grad_modifier, verbose=False)[1]</p>
<p>return utils.normalize(grads)[0]</p>
<p>I am using the amu package to compute activation maximization (<a href="https://github.com/Nguyen-Hoa/Activation-Maximization" class="inline-onebox" rel="noopener nofollow ugc">GitHub - Nguyen-Hoa/Activation-Maximization: Python implementation of activation maximization with PyTorch.</a>)</p>
<p>In this code, I need to get the gradient of the activation array for a particular layer wrt the target using the code below:</p>
<p>input.retain_grad() # non-leaf tensor<br>
# network.zero_grad()</p>
<pre><code>    # Propogate image through network,
    # then access activation of target layer
    network(input)
    layer_out = layer_activation[layer_name]

    # compute gradients w.r.t. target unit,
    # then access the gradient of input (image) w.r.t. target unit (neuron) 
    layer_out[0][unit].backward(retain_graph=True)
    img_grad = input.grad
</code></pre>
<p>However, when I get my layer_out, it has a size of: torch.Size([1, 2, 97, 97, 97]) and therefore I cannot compute the layer_out[0][unit].backward(retain_graph=True).</p>
<p>How can I solve this?</p>

username: ptrblck
cooked: <p><code>tensor.backward()</code> will populate the gradient with a scalar <code>1</code> value, if <code>tensor</code> is also a scalar tensor.<br>
If <code>tensor</code> contains more than a single element, you would either need to pass the gradient explicitly to <code>backward</code> (e.g. via <code>tensor.backward(gradient=torch.ones_like(tensor))</code>) or you could reduce the tensor first e.g. via <code>tensor.mean().backward()</code>.</p>

username: Roos_Kraaijveld
cooked: <p>Okay! That makes sense! I have now changed the code to the following:</p>
<pre><code class="lang-auto">tcav = {}
    for ind, (img, label) in enumerate(loader):
        img.requires_grad=True
        img = img.to(device, dtype=torch.float)
        
        output = model(img)

        layer_activation =  activation[layer_names[0]].cpu()
        #layer_activation.retain_grad()
        
        loss = torch.mean(layer_activation)
        loss.backward(retain_graph=True)
        grads = torch.nn.functional.normalize(img.grad)
        
        tcav[ind] = {}
        tcav[ind][layer_names[0]] = grads
</code></pre>
<p>This, I believe, still follows the activation maximisation code (<a href="https://github.com/Nguyen-Hoa/Activation-Maximization" rel="noopener nofollow ugc">GitHub - Nguyen-Hoa/Activation-Maximization: Python implementation of activation maximization with PyTorch.</a></p>
<p>However, when I run this, img.grad is None. I understand that it’s not a leaf tensor, but I’m not quite sure how to then compute the image gradient.</p>

username: ptrblck
cooked: <p>The <code>to()</code> operation is differentiable and thus creates a non-leaf tensor:</p>
<pre><code class="lang-python">img = torch.randn(1, 1)
print(img.is_leaf)
# True

img.requires_grad=True
print(img.is_leaf)
# True

img = img.to('cuda', dtype=torch.float)
print(img.is_leaf)
# False
print(img.grad_fn)
# &lt;ToCopyBackward0 object at 0x7f6d48d5d1c0&gt;
</code></pre>
<p>Move the tensor to the <code>device</code> and <code>dtype</code> first before setting the tensor’s <code>.requires_grad</code> attribute to <code>True</code>:</p>
<pre><code class="lang-python">img = torch.randn(1, 1)
img = img.to('cuda', dtype=torch.float)
img.requires_grad_(True)

print(img.is_leaf)
# True
</code></pre>