Hello Mr. Robot!



 MrRobot:

I could use both nn.CrossEntropyLoss() and nn.BCEWithLogtisLoss() on MNIST and get good performance.


I’ve haven’t done the experiment, but I very much expect that if
you train a network on reasonably clean multi-class data (say,
MNIST digits) with both CrossEntropyLoss and
BCEWithLogitsLoss you will get better performance using
CrossEntropyLoss.
It’s worth saying a few words about why:
When you train with BCEWithLogitsLoss (with ten outputs
from your network) you really are training a multi-label,
multi-class classifier.  You happen to be training it with samples
that always correspond to exactly one class.  If your training
data is representative and reasonably clean, your network will
learn that for the kinds samples you give it, the right answer is
exactly one label.  Then when applied to a test sample (assuming
that your training and test data are similar in character) you
should get predictions that have a high probability for exactly
one of the classes, and low probabilities for the others.
But your network had to learn that you were giving it – at
least with the training set you were using – a single-label
(but multi-class) problem.
On the other hand, if you use CrossEntropyLoss you are,
in essence, telling your network that this is a single-label
problem, so your network doesn’t have to learn this.  You’ve
made its job easier by telling it this, so you should expect
better performance.


However, before reading your reply, I thought nn.BCEWithLogitsLoss() could only be using in the multi-label classification (you said it can be used in this setting).


Well, as a semantic quibble, you could consider ordinary binary
classification to be multi-label in the sense that you have one class
(say, “cat”) and zero (nothing, so “no cat”) or one (your only class
is “cat”, so “yes cat”) labels.  You can’t have more than one label
because you only have one class.  That is, you can have none, any,
or all of your labels (in this case only one – “cat”) turned off or on.
So this is a multi-label problem.




Focusing on multi-class classification. If using two losses can both achieve good performance, I suspect they should be similar in some sense but they actually use Softmax and multiple Sigmoid respectively, which I deem very different. For example, for Softmax, it makes sense to choose the class that has highest probability. However, for Sigmoid, it likely that two or more classes have very close output (say 0.79, 0.8, 0.81), then choosing one particular class does not seem to make sense and this is main confusion for using nn.BCEWithLogitsLoss() for multi-class classification.



I think you’ve identified the main point.
First, a technical note:  In both cases the output of your network
will be “raw scores” (logits).  They, in effect, get converted to
probabilities internally to the loss functions.  I will speak in terms
of probabilities.
CrossEntropyLoss internally uses (in effect) Softmax to convert
the output of your network in to probabilities for which single class
is being predicted.  These probabilities sum to one.
BCEWithLogitsLoss internally uses (in effect) Sigmoid to convert
the output of your network into probabilities for each of your classes
individually being active.  These probabilities individually range
from zero to one (and sum anywhere from zero to your number
of classes, in the MNIST example, ten).
So, to use your example of probabilities (0.79, 0.8, 0.81),
your network is saying that class A, B, and C all have a probability
of about 80% of being active, so you should logically predict that
your image contains an “A” and a “B” and a “C”.  (You shouldn’t
say that your image only contains a “C”, but it’s a kind of squiggly
“C” that looks rather like an “A” and a “B”.)
Of course, if your problem really is single-label (multi-class),
you can’t have “A”, “B”, and “C” at the same time.  This is why
BCEWithLogitsLoss (with its internal Sigmoid) isn’t a good
fit for the single-label problem.
Now with CrossEntropyLoss (with its internal Softmax) you
will never get two classes both with greater than 50% probability.
You could get (0.33, 0.33, 0.34, 0.0, 0.0, ...), in
which case you would predict class “C”, but with low certainty,
because it’s a squiggly “C” that looks a lot like an “A” and a “B”.




Focusing on multi-label classification. Set nn.CrossEntropyLoss() aside and we now focus on nn.BCEWithLogitsLoss().  If I am building an image recognition system that is trying to identify whether there are car, people and stop sign in the given image at the same time. Now it is clear this is a multi-label classification problem (with three labels). However, people generally set 0.5 as a decision threshold for 1/0 classification. So a output like [0.2, 0.3, 0.4] will all be regarded as [0, 0, 0] by hard thresholding.



And this is reasonable.  Your network has only a 40% probability
of there being a stop sign in the image.  Your best guess is that
there isn’t a stop sign (but you wouldn’t be the farm on it).
So perhaps your network is saying that the bicycle in the image
looks a little bit like a car (20%), the bags of garbage looks
somewhat like people (30%), and the restaurant logo looks
rather like a stop sign (40%).  But your best prediction is that
none of your classes appear in the image.


But I believe this is similar to Logistic regression and the decision threshold should be tuned and using hard thresholding does not make sense.


Well, as I argued above, the hard, a priori thresholding does make
sense.
Nonetheless, it is plausible that you could develop some tuning
scheme for your post-network thresholds that would give you
better accuracy.  But I think such tuning would likely be highly
dependent on your particular problem and network.


Now comes the main question. How do I tune three thresholds at the same time or maybe I just output probability (like many object detection algorithms do).


I don’t know of any good post-network threshold-tuning scheme,
but it’s believable that you could develop one.
Whether you output probabilities or thresholded “yes / no”
predictions really depends on your use case – on how you
use the output of your classifier.  There are certainly many
use cases where outputting probabilities is appropriate.
Best.
K. Frank