Weights do not update

skippa: <p>Hi, I’m new to pytorch.<br>
I’m developing a weak learner for an ensable approach, I defined a weak learner as a neural network with just one neuron and one weigth:</p>
<pre><code class="lang-auto">class BaseLearnerFST(nn.Module):

    def __init__(self, leakyReluM: float = 0.02):
        super().__init__()
        # weight application
        self._weightApplier: Linear = Linear(1, 1, bias=False)
        # activation function
        self._leakyRelu: LeakyReLU = nn.LeakyReLU(negative_slope=leakyReluM)

    def forward(self, batch: Tensor) -&gt; Tensor:
        weightedInput: Tensor = self._weightApplier(batch)
        return self._leakyRelu(weightedInput)
</code></pre>
<p>Given the fact that each sample in the dataset has a specific weight I developed a custom loss to handle the error computation:</p>
<pre><code class="lang-auto">class WeightedMeanSquaredError(nn.Module):

    def __init__(self):
        super().__init__()
        self._errorMap: list[Tensor] = []

    def forward(self, yTrue: Tensor, yPred: Tensor, weights: Tensor, save: bool = False) -&gt; Tensor:
        modelLabel: Tensor = gt(yPred, 1.0).float()
        absolute_diff: Tensor = th.abs(modelLabel - yTrue)
        weighted_absolute_diff = weights * absolute_diff
        if save:
            self._errorMap.append(absolute_diff)

        return th.sum(weighted_absolute_diff, dtype=float32)

    def getErrorMap(self) -&gt; list[Tensor]:
        return self._errorMap
</code></pre>
<p>I also developed a fit() method to train a BaseLearnerFST:</p>
<pre><code class="lang-auto">    def fit(
            self, xTrain: ndarray[float], yTrain: ndarray[int],
            weights: ndarray[float], batchSize: int = 1, epochs: int = 10
    ) -&gt; Tensor:

        optimizer = SGD(self.parameters(), lr=0.05)  
        weightedLoss = WeightedMeanSquaredError()
        dataset: CustomDataset = CustomDataset(xTrain, yTrain, weights)
        batches: DataLoader = BaseLearnerFST.prepareDataset(dataset, batchSize)
        self.train()

        for epoch in range(epochs):
            for batch in batches:
                print("Weight: ", self._weightApplier.weight)
                xBatch, (yBatch, weightsBatch) = batch
                optimizer.zero_grad()  # initialize gradient to zero
                yPred: Tensor = self(xBatch)  # forward pass

                loss: Tensor = weightedLoss(
                    yBatch, yPred, weightsBatch,
                    save=True if epoch == epochs - 1 else False
                )

                loss.backward()  # compute gradient
                optimizer.step()  # backpropagation

            print(f"Epoch:{epoch} loss is {loss.item()}")

        return tensor([])  # th.cat(weightedLoss.getErrorMap())

    @staticmethod
    def prepareDataset(dataset: Dataset, batchSize: int = 1):
        return DataLoader(dataset, batchSize, shuffle=True)
</code></pre>
<p>The problem is that troughout the training process the single weight does not get updated, I know that the problem is inside the custom loss function that I made because, I tried to use a pytorch loss such as MSELoss and everything worked fine.</p>
<p>I read that if the training set tensor was not declared with the flag: requires_grad, the gradient do not get computed so here I attach also the class with which I handle the dataset before the training phase:</p>
<pre><code class="lang-auto">class CustomDataset(Dataset):
    def __init__(self, data: ndarray[float], labels: ndarray[int], weights: ndarray[float]):
        self._xTrain: Tensor = tensor(data, dtype=float32, requires_grad=True)
        self._yTrain: Tensor = tensor(labels, dtype=float32, requires_grad=True)
        self._weights: Tensor = tensor(weights, dtype=float32, requires_grad=True)

    def __len__(self) -&gt; int:
        return len(self._xTrain)

    def __getitem__(self, idx: int) -&gt; tuple[Tensor, tuple[Tensor, Tensor]]:
        return self._xTrain[idx], (self._yTrain[idx], self._weights[idx])
</code></pre>

KFrank: <p>Hi Gio!</p>
<aside class="quote no-group quote-modified" data-username="skippa" data-post="1" data-topic="185129" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/letter_avatar_proxy/v4/letter/s/f17d59/48.png" class="avatar"> skippa:</div>
<blockquote>
<pre><code class="lang-auto">    def forward(self, yTrue: Tensor, yPred: Tensor, weights: Tensor, save: bool = False) -&gt; Tensor:
        modelLabel: Tensor = gt(yPred, 1.0).float()
        absolute_diff: Tensor = th.abs(modelLabel - yTrue)
</code></pre>
<p>…</p>
<p>I know that the problem is inside the custom loss function that I made because, I tried to use a pytorch loss such as MSELoss and everything worked fine.</p>
</blockquote>
</aside>
<p>I assume that <code>gt()</code> is some sort of “greater-than” function that returns<br>
a boolean (or perhaps integer) tensor that you then convert to <code>float()</code>.<br>
Pytorch’s autograd does not backpropagate through booleans (or integers),<br>
because, being discrete, they are not usefully differentiable.</p>
<p>Do you really need to threshold <code>yPred</code>?  Could you work directly with<br>
<code>yPred - yTrue</code>?  If you need to backpropagate through a thresholding<br>
operation, you would have to use a differentiable, “soft” approximation<br>
to a hard threshold, such as <code>sigmoid()</code>.</p>
<p>Best.</p>
<p>K. Frank</p>

skippa: <p>Thank you Frank,</p>
<p>I really appriciate your support, now I know what I was doing wrong.I’m probably going to stuck to your solution of just using directly yPred with a little bit of a approximation. I’m also probably going to try changing either the acitvaction function or the net architecture. One thing which is not directly a pytorch question is: if my classifier should just understand to specifically threshold value grater than 1, I really don’t care of how much beyond 1 or below we are, I just need to know if I’m over or under that threshold. Known that I also have to say that i’m trying to solve a binary classification problem (0: non face, 1: face) . So i’m worried about being to strict with a loss function like that. For example if I have a face which is just classified by my weak learner with 1.5, (where its ground truth is 1) and let’s suppose this instance of image has a weight of 0.4 then the loss function would be: 0.4*|1.5 - 1|. In this scenario my weak classifier performed well (because the output was g.t 1) but i’m still incrementing the loss.<br>
What would you do ?</p>
<p>Hope you the best</p>
<p>skippa</p>

KFrank: <p>Hi Skippa!</p>
<aside class="quote no-group" data-username="skippa" data-post="3" data-topic="185129" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/letter_avatar_proxy/v4/letter/s/f17d59/48.png" class="avatar"> skippa:</div>
<blockquote>
<p>Known that I also have to say that i’m trying to solve a binary classification problem (0: non face, 1: face) .</p>
</blockquote>
</aside>
<p>At <em>inference</em> time – that is, when you are not training, but just making<br>
predictions – you (typically) want to come up with a yes-no answer.  So<br>
for inference, thresholding makes sense.  One measure of how well you<br>
are making predictions is the so-called accuracy, namely the percentage<br>
of the predictions that are correct.</p>
<p>However, there is no need to use accuracy or some other metric similar<br>
to accuracy as your training loss, and it is very common to use a training<br>
loss that is quite different in character from an accuracy-like metric.</p>
<p>For a normal binary-classification problem the most common, and likely<br>
the best, training loss to use is binary cross entropy.  You, however, are<br>
training very simple weak learners, so the common approach may or may<br>
not be the best choice for your use case.  Nevertheless, I would suggest<br>
that you give pytorch’s <a href="https://pytorch.org/docs/2.0/generated/torch.nn.functional.binary_cross_entropy_with_logits.html" rel="noopener nofollow ugc">binary_cross_entropy_with_logits()</a> a try.  (Note that<br>
it has a <code>weight</code> argument, so you won’t have to write your own version to<br>
apply your sample weights.)</p>
<p>To use <code>binary_cross_entropy_with_logits()</code>, you would pass it the output<br>
of your <code>BaseLearnerFST</code> with no thresholding (or other further processing)<br>
and your ground-truth <code>0.0</code> / <code>1.0</code> binary labels.</p>
<aside class="quote no-group">
<blockquote>
<p>In this scenario my weak classifier performed well (because the output was g.t 1) but i’m still incrementing the loss.</p>
</blockquote>
</aside>
<p>This is part of the reason I suggested <code>binary_cross_entropy_with_logits()</code>.<br>
This loss function takes predictions that are so-called <em>logits</em> and that range<br>
from <code>-inf</code> to <code>inf</code>.  A large negative logit corresponds to a probability of<br>
being in the “1” class that is very close to zero, while a large positive logit<br>
corresponds to a probability for the “1” class that is very close to one.  And<br>
a logit of <code>0.0</code> corresponds to a probability of one half.  (Note that this means<br>
that to produce a yes-no prediction, you would typically threshold a logit<br>
against <code>0.0</code>.)</p>
<p>Best.</p>
<p>K. Frank</p>