Weighted loss function, with learnable weights

Mj1111: <p>Dear all,<br>
I want to ask you for some help. I am training a dual-path CNN, where one path processes the image in a holistic manner, where the other path processes the same image but patch-wise, which means I decompose N_patches from the same image, and feed all patches in a second CNN, where each single patch goes in the same CNN (sharing weights).</p>
<p>My idea is to make a combined loss function, where in the local processing path (from the two path model), the local losses are calculated, which means each patch corresponds to its own loss. Now my question is, how to make weighting between the local losses, where the weights are learnable during the training.<br>
In the final stage I combine the global loss and the local loss (the local loss is then averaged).</p>
<p>I already trained the combined Cnn model, where in the final stage i combined the global image features and the local image features, but now i want to try to learn local losses from the local processing path.</p>
<p>I did something like this:</p>
<pre><code class="lang-auto">class PatchRelevantLoss(nn.Module):
   def __init__(self, n_patches):
        super(PatchRelevantLoss, self).__init__()
        self.ce = nn.CrossEntropyLoss(reduction = 'none')
        self.n_patches = n_patches
        self.weights = .... nn.Parameter or something (dim: n_patches)
    
    def forward(self, feature_matrix, labels):
        loss = ()
        for features, weight in zip(feature_matrix, self.weights):
             loss += ((self.ce(features, labels)*weight ).mean(0), )
        loss = torch.stack(loss, 0)
        return loss
</code></pre>
<p>Shapes: feature_matrix has (n_patches, batch_size, feature_dimension)<br>
Now the idea is to make the wieghts learnable.</p>
<p>Here is the basic idea on the picture.</p>
<p>I am new to Pytorch and i’ll be thankful for some help.<br>
Cheers.<div class="lightbox-wrapper"><a class="lightbox" href="https://discuss.pytorch.org/uploads/default/original/3X/5/7/5795a39b3736fdcb8f900741bd7af3e3dcc5f272.png" data-download-href="https://discuss.pytorch.org/uploads/default/5795a39b3736fdcb8f900741bd7af3e3dcc5f272" title="img"><img src="https://discuss.pytorch.org/uploads/default/original/3X/5/7/5795a39b3736fdcb8f900741bd7af3e3dcc5f272.png" alt="img" data-base62-sha1="cuO8GUk0zs1lcbwI1bpE0k0Mf98" width="690" height="258" data-dominant-color="F9F9F9"><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">img</span><span class="informations">1185×444 11.2 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>

googlebot: <p>IIUC, with your architecture, these weights are hyperparameters (equivalent to learning rates) and hence only externally tunable</p>

Mj1111: <p>Thanks for the answer, I need more clarification unfortunately. Does that mean I need to define them in the CNN architecture and to make an additional output from the network which will serve later on as an input to the custom loss function?<br>
Now I see there are some implementation on how to make trainable weights in multi-task loss, but I am not exactly sure about this specific problem.</p>

googlebot: <p>Loss terms are not trainable, because loss*w is minimized by w=0 (or other degenerate results with negative numbers). Even constraints like sum(w)=1 don’t have sensible interpretations - normally you would combine predictions instead.</p>

Mj1111: <p>Thanks for the guidance. That means I can optimize upon W based on the prediction outputs, and then to take the average of them? For example to define the learnable parameters in the CNN model and as an output to take the weighted average while W are learnable?</p>

googlebot: <p>Generally, you would want to have autonomous submodels before creating an “ensemble” - you can then take the weighted average of predictions with softmax-ed trained combination weights.</p>
<p>Without this - i.e. if you train everything at once with one objective(loss) across all parts, you’ll just get a higher capacity model where parts (and combination weights) train to mutually compensate output errors from other parts.</p>

Mj1111: <p>Thank you for the insights, learned a lot from you. Yes, the idea is to make the local processing path to compensate the missing information from the global processing path (where the image is processed in a holistic manner), while the trained submodels in the local processing path  to be trained with sofmax-ed trained combination weights.</p>
<p>So basically, as you said, I can use only one loss function across all parts (bigger capacity model) or either several losses where each of them corresponds to a single patch?</p>
<p>What’s more meaningful to you, which way to go, many outputs from the local branch, where each output is multiplied by a learnable weight while optimizing with several losses (each patch one loss), or weighted average ensemble trained with a single loss function?</p>

googlebot: <p>For corrections, perhaps enforce an order:<br>
y0 = globalCnn(x; pars)<br>
y1 = f(y0, localCnn(x_patch[0]; lpars)) + y0<br>
y2 = f(y1, localCnn(x_patch[1]; lpars)) + y1<br>
…<br>
loss = criterion(yN, target_y)</p>
<p>there are many possibilities for f(), the key is that this computation is no longer permutation invariant</p>
<p>more generally, patch specific losses don’t feel right to me, idk…</p>

Mj1111: <p>Thanks, I only have one more question. Since there are trainable parameters W, what is the most common way to initialize for this specific task?</p>
<p>Is this a good practice :</p>
<pre><code class="lang-auto">self.weights = nn.Parameter(torch.FloatTensor(self.n_patches)).unsqueeze(1).cuda()
nn.init.uniform_(self.weights)
</code></pre>
<p>And then to normalize so that their sum(w) = 1? Or its enough to initialize with uniform without normalization? Maybe other initialization procedure will be more suitable in this case?</p>

googlebot: <p>if you need vector(s) with sum(w)=1:<br>
self.w = nn.Parameter(torch.zeros(n))<br>
then in forward:<br>
w = self.w.softmax(-1)</p>
<p>you only need random / asymmetric init if components are non-identifiable (swappable)</p>

Mj1111: <p>Thank you mate for the help. This is part of my research project, ill inform how it goes. Will see if this approach give a performance boost.<br>
Have a great day.</p>