Hi @ptrblck 
I am getting same problem, and  parameters aren’t updating in my custom_loss function after running .backward() found that problem was occuring afteroptimizer.step() I tried to see model before and after and it gave same results,
MODEL.train()

for i in range(EPOCHS):
    before = list(MODEL.parameters())[0].clone()
    for i in tqdm(valid_loader):
        q1 = i[0].to(device)
        q2 = i[1].to(device)
        q1_vec, q2_vec = MODEL(q2, q1)

        loss = CRITERION(q2_vec, q1_vec, MARGIN)

        loss.backward()
        OPTIMIZER.step()
        OPTIMIZER.zero_grad()
    after = list(MODEL.parameters())[0].clone()
    print(before == after)    

equaling before and after tensors results same after a epoch
tensor([[True, True, True,  ..., True, True, True],
        [True, True, True,  ..., True, True, True],
        [True, True, True,  ..., True, True, True],
        ...,
        [True, True, True,  ..., True, True, True],
        [True, True, True,  ..., True, True, True],
        [True, True, True,  ..., True, True, True]])

however removing initialize weights on siamese model  had updated parameter but only on some side others
100%
250/250 [00:19<00:00, 12.53it/s]


tensor([[ True,  True,  True,  ...,  True,  True,  True],
        [False, False, False,  ...,  True, False, False],
        [ True,  True,  True,  ...,  True,  True,  True],
        ...,
        [ True,  True,  True,  ...,  True,  True,  True],
        [ True,  True,  True,  ...,  True,  True,  True],
        [ True,  True,  True,  ...,  True,  True,  True]])

100%
250/250 [00:18<00:00, 13.54it/s]


tensor([[ True,  True,  True,  ...,  True,  True,  True],
        [False, False, False,  ..., False, False, False],
        [False, False, False,  ..., False, False, False],
        ...,
        [ True,  True,  True,  ...,  True,  True,  True],
        [ True,  True,  True,  ...,  True,  True,  True],
        [ True,  True,  True,  ...,  True,  True,  True]])

100%
250/250 [00:18<00:00, 13.67it/s]


tensor([[ True,  True,  True,  ...,  True,  True,  True],
        [False, False, False,  ..., False, False, False],
        [False, False, False,  ..., False, False, False],
        ...,
        [ True,  True,  True,  ...,  True,  True,  True],
        [ True,  True,  True,  ...,  True,  True,  True],
        [ True,  True,  True,  ...,  True,  True,  True]])

100%
250/250 [00:19<00:00, 12.67it/s]


I had defined optimizers as
CRITERION = HardTripletLoss(device)
MODEL = Siamese(len(vocab),128, 128, bidirectional=False).to(device)
OPTIMIZER = torch.optim.Adam(MODEL.parameters(),lr = 0.0001)

I tried different learning rates , model architecture  and init weights commented below giving same type of problem only updating some parts even for longer epochs some time and sometimes not updating .


model
class Siamese(nn.Module):
    def __init__(self, vocab_size, embed_dim, hidden_dim, bidirectional=False, em_weight=None):
        """
        :param vocab_size: defaultdict containing word to index
        :param embed_dim: embedding dim
        :param hidden_dim: hidden dim
        :param bidirectional: bool if True sets LSTM layer to bidirectional
        :param em_weight: embedding weight initialization see https://pytorch.org/docs/stable/nn.init.html
        """
        super(Siamese, self).__init__()
        self.vocab_size = vocab_size
        self.embed_dim = embed_dim
        self.hidden_dim = hidden_dim
        self.bidirectional = bidirectional
        self.emb_out_dim = self.hidden_dim if not self.bidirectional else self.hidden_dim * 2
        self.em = nn.Sequential(
            nn.Embedding(self.vocab_size, self.embed_dim),
            nn.LSTM(self.embed_dim, self.hidden_dim, batch_first=True, bidirectional=self.bidirectional)
        )
        self.fc = nn.Sequential(
            nn.Linear(self.emb_out_dim, self.hidden_dim)
        )
#         self.init_hidden(em_weight)

    def forward(self, x1, x2):
        # n1
        lstm_out_1, _ = self.em(x1)
        mean_layer1 = lstm_out_1[:, -1, :]
#         fc1 = self.fc(lstm_out_1[:, -1, :])

        normalize1 = F.normalize(mean_layer1)
        
        lstm_out_2, _ = self.em(x2)
        mean_layer2 = lstm_out_2[:, -1, :] # .mean(0, keepdims=True)
        normalize2 = F.normalize(mean_layer2)
#         fc2 = self.fc(lstm_out_2[:, -1, :])
        
        return normalize1, normalize2


#     def init_hidden(self, em_weight):
#         for m in self.modules():
#             if isinstance(m, nn.Embedding):
#                 if em_weight is None:
#                     nn.init.normal_(m.weight)
#                 else:
#                     em_weight(m.weight)
#             elif isinstance(m, nn.Linear):
#                 nn.init.normal_(m.weight)


I  have defined my custom loss function as


loss
class HardTripletLoss(nn.Module):
    def __init__(self, device='cpu'):
        """
        Custom Hard triplet loss
        :param device: device type used
        """
        super(HardTripletLoss, self).__init__()
        self.device = device

    def forward(self, v1, v2, margin):
        scores = v1 @ v2.T
        batch_size = len(scores)
        positive = torch.diag(scores)
        negative_without_positive = scores - 2.0 * torch.eye(batch_size).to(self.device)
        closest_negative = negative_without_positive.max(axis=1)[0]
        negative_zero_on_duplicate = scores * (1.0 - torch.eye(batch_size).to(self.device))
        mean_negative = torch.sum(negative_zero_on_duplicate, 1) / (batch_size - 1)
        triplet_loss1 = torch.maximum(margin - positive + mean_negative, torch.tensor(0).to(self.device))
        triplet_loss2 = torch.maximum(margin - positive + closest_negative, torch.tensor(0).to(self.device))
        triplet_loss = torch.mean(triplet_loss2 + triplet_loss1)
        return triplet_loss



I  am confused where might be my mistake model or custom loss.
I had tested custom loss which had given right results on manual putting vectors.
What might be doing wrong here , Help!! ?