I just got rid of torch.sigmoid() and used BCEWithLogitsLoss instead of BCELoss and it just worked fine as expected, there is no stuck loss problem anymore and the model converges within a couple of epochs. Thank you so much for the advice.
From PyTorch documentation:

This loss combines a Sigmoid layer and the BCELoss in one single class. This version is more numerically stable than using a plain Sigmoid followed by a BCELoss as, by combining the operations into one layer, we take advantage of the log-sum-exp trick for numerical stability.

But I’m not sure why this really solves my problem.



 KFrank:

Note, when you convert to BCEWithLogitsLoss , when calculating
your accuracy, you should threshold the output of your model at
0.0 rather than 0.5 .


I really don’t know what you mean here exactly, but the model output now is just some integers, example output:
[ 7., -3., -5., -5., -5.,  4., -5., -5., -5.,  4., -5., 13., -5., -5., -5.,
-4.,  6.,  4.,  3.,  6., -3.,  3., -5.,  3., 11., -4., -4., 11., -5., -6.,
-5.,  4.,  6.,  7.,  5.,  7., -5.,  4.,  9., -5.,  8.,  4., 5.,  9., -4., 
1., 10.,  4., 14., -4., -4., -4.,  5., -5.,  6., 15., 3., -6., -5., -5.,
-5., -5., -4.,  3.]

So I just did the following:
output = torch.round(torch.sigmoid(output))




 KFrank:

Because your accuracy gets “'stuck” at 50% , I speculate that
your dataset is exactly balanced


Yes that’s true, it’s because I constructed the dataset myself (extracted the first and last 30 samples from each turbofan engine simulation in C-MAPSS dataset respectively as negative and positive samples) so my dataset is exactly balanced but I never thought that would be the cause of the problem.



 KFrank:

To clarify, what happens when you run your keras / tensorflow
model multiple times with different random seeds. Is it robust?


Yes running the same code in Keras always yields almost the same results, pretty robust.



 KFrank:

As an aside: Your output = output.view_as(target)
looks fishy. I’m guessing that it’s unnecessary (that is, that
your output and target already have the same shape).


It’s just the way my code generates data, the model output is [64, 1] while the target shape is [64] and yes I should modify the other code instead of doing this trick.