Backpropagation through multiple forward-propagations

rawdev: <p>Hello everyone.</p>
<p>In studying <strong>Unsupervised-Classification</strong> , An incomprehensible logic was found.<br>
<a href="https://github.com/wvangansbeke/Unsupervised-Classification" rel="noopener nofollow ugc">Here</a> is refer git-hub.</p>
<p>A function <strong>scan_train</strong> in train_utils.py</p>
<pre><code class="lang-auto">
def scan_train(train_loader, model, criterion, optimizer, epoch, update_cluster_head_only=False):
    """ 
    Train w/ SCAN-Loss
    """
    total_losses = AverageMeter('Total Loss', ':.4e')
    consistency_losses = AverageMeter('Consistency Loss', ':.4e')
    entropy_losses = AverageMeter('Entropy', ':.4e')
    progress = ProgressMeter(len(train_loader),
        [total_losses, consistency_losses, entropy_losses],
        prefix="Epoch: [{}]".format(epoch))

    if update_cluster_head_only:
        model.eval() # No need to update BN
    else:
        model.train() # Update BN

    for i, batch in enumerate(train_loader):
        # Forward pass
        anchors = batch['anchor'].cuda(non_blocking=True)
        neighbors = batch['neighbor'].cuda(non_blocking=True)
       
        if update_cluster_head_only: # Only calculate gradient for backprop of linear layer
            with torch.no_grad():
                anchors_features = model(anchors, forward_pass='backbone')
                neighbors_features = model(neighbors, forward_pass='backbone')
            anchors_output = model(anchors_features, forward_pass='head')
            neighbors_output = model(neighbors_features, forward_pass='head')

        else: # Calculate gradient for backprop of complete network
            anchors_output = model(anchors)
            neighbors_output = model(neighbors)     

        # Loss for every head
        total_loss, consistency_loss, entropy_loss = [], [], []
        for anchors_output_subhead, neighbors_output_subhead in zip(anchors_output, neighbors_output):
            total_loss_, consistency_loss_, entropy_loss_ = criterion(anchors_output_subhead,
                                                                         neighbors_output_subhead)
            total_loss.append(total_loss_)
            consistency_loss.append(consistency_loss_)
            entropy_loss.append(entropy_loss_)

        # Register the mean loss and backprop the total loss to cover all subheads
        total_losses.update(np.mean([v.item() for v in total_loss]))
        consistency_losses.update(np.mean([v.item() for v in consistency_loss]))
        entropy_losses.update(np.mean([v.item() for v in entropy_loss]))

        total_loss = torch.sum(torch.stack(total_loss, dim=0))

        optimizer.zero_grad()
        total_loss.backward()
        optimizer.step()

        if i % 25 == 0:
            progress.display(i)
</code></pre>
<p>A model is used to calculate two outputs</p>
<pre><code>        anchors_output = model(anchors)
        neighbors_output = model(neighbors) 
</code></pre>
<p>and two output is used to get a loss.</p>
<pre><code>        total_loss_, consistency_loss_, entropy_loss_ = criterion(anchors_output_subhead,
                                                                     neighbors_output_subhead)
</code></pre>
<p>and back propagation</p>
<pre><code>    optimizer.zero_grad()
    total_loss.backward()
    optimizer.step()
</code></pre>
<p>As far as I know, An output value of a node is need to backpropagate gradients through the node.<br>
(e.g matmul gradient)</p>
<p>but in the upper case, the output value is not one.</p>
<p>Do I misunderstood the backpropagation?</p>

ptrblck: <aside class="quote no-group" data-username="rawdev" data-post="1" data-topic="137877">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/rawdev/48/38369_2.png" class="avatar"> rawdev:</div>
<blockquote>
<p>As far as I know, An output value of a node is need to backpropagate gradients through the node.<br>
(e.g matmul gradient)<br>
but in the upper case, the output value is not one.</p>
</blockquote>
</aside>
<p>I’m not sure what exactly this is referring to. In your code snippet you are reducing the losses by applying <code>torch.sum</code> on them and are calling <code>total_loss.backward()</code> later.<br>
If you are concerned about the <code>backward</code> call on non-scalar values: by default PyTorch will pass a <code>1.</code> gradient to <code>backward()</code>, if this operation is called on a scalar. If you are working with multiple values, you would need to specify the input gradient manually to the <code>backward</code> operation.</p>

rawdev: <p>Thanks for your replay and sorry to late check.</p>
<p>My question is about a calculating partial derivatives.<br>
Here is an example.</p>
<p><div class="lightbox-wrapper"><a class="lightbox" href="https://discuss.pytorch.org/uploads/default/original/3X/d/4/d4a45304423cc1a2128f793dc347853c23695bdf.png" data-download-href="https://discuss.pytorch.org/uploads/default/d4a45304423cc1a2128f793dc347853c23695bdf" title="캡처"><img src="https://discuss.pytorch.org/uploads/default/optimized/3X/d/4/d4a45304423cc1a2128f793dc347853c23695bdf_2_537x500.png" alt="캡처" data-base62-sha1="ul7hMNMah9p8nJCYXoWq5yJjHkz" width="537" height="500" srcset="https://discuss.pytorch.org/uploads/default/optimized/3X/d/4/d4a45304423cc1a2128f793dc347853c23695bdf_2_537x500.png, https://discuss.pytorch.org/uploads/default/original/3X/d/4/d4a45304423cc1a2128f793dc347853c23695bdf.png 1.5x, https://discuss.pytorch.org/uploads/default/original/3X/d/4/d4a45304423cc1a2128f793dc347853c23695bdf.png 2x" data-dominant-color="F4F4F5"><div class="meta"><svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true"><use href="#far-image"></use></svg><span class="filename">캡처</span><span class="informations">685×637 63.8 KB</span><svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true"><use href="#discourse-expand"></use></svg></div></a></div></p>
<p>To find the slope of the network, the output values are required.<br>
a1, a2, a3 are output values  of training.</p>
<p>This is my question.<br>
The output value is used to obtain the slop.<br>
but the evaluation of the model is calculated twice in the code above.</p>
<pre><code>    anchors_output = model(anchors)
    neighbors_output = model(neighbors) 
</code></pre>
<p>What evaluation values ​​are used to find the slope?</p>
<p>Thanks.</p>