Pytorch and Cross Entropy

username: Teodora-bo
cooked: <p>Hi everyone,<br>
I want to create a view a prediction done by a training and validation Attention UNet code. I have some segmentations of femur. The segmentations contain 10 classes each, from 1 to 10. I used Cross Entropy and i preprocessed all the CT scans before everything else i. My Dices are great, all of them are up to 0.92 for training and validation, but when I try to view the prediction in 3D slicer, there are only 9 segments. I think the problem is that with the Cross Entropy we train classes from 0 to 9 and then the 0 class is forget.<br>
Here is the part of the processing function in my Attention UNet code:<br>
def prepare(self):<br>
for ct_path, (left_path, right_path) in tqdm(self.ct_paths):<br>
ct_volume = read_nii(ct_path)<br>
label_volume = read_nii(left_path)  # Already combined masks into 10 classes</p>
<pre><code>        for idx in range(ct_volume.shape[-1]):
            img = cv2.resize(ct_volume[..., idx], (self.img_size, self.img_size), interpolation=cv2.INTER_AREA).astype('float32')
            label = cv2.resize(label_volume[..., idx], (self.img_size, self.img_size), interpolation=cv2.INTER_NEAREST)

            img = (img - img.min()) / (img.max() - img.min() + 1e-5)
            label = np.clip(label, 0, NUM_CLASSES-1).astype(np.uint8) 

            self.data.append(img[..., np.newaxis])
            self.labels.append(label) 
</code></pre>
<p>And from the .pth loading:</p>
<p>def read_nii(path):<br>
return np.rot90(np.array(nib.load(path).get_fdata()))</p>
<p>def preprocess_slice(slice_2d):<br>
resized = cv2.resize(slice_2d, (IMG_SIZE, IMG_SIZE), interpolation=cv2.INTER_AREA).astype(‘float32’)<br>
normed = (resized - resized.min()) / (resized.max() - resized.min() + 1e-5)<br>
return normed</p>
<p>def predict_volume(volume):<br>
original_shape = volume.shape<br>
predictions = <span class="chcklst-box fa fa-square-o fa-fw"></span><br>
for i in tqdm(range(volume.shape[-1]), desc=“Predicting slices”):<br>
slice_2d = volume[…, i]<br>
input_tensor = preprocess_slice(slice_2d)<br>
input_tensor = torch.tensor(input_tensor).unsqueeze(0).unsqueeze(0).to(DEVICE)<br>
with torch.no_grad():<br>
output = model(input_tensor)<br>
pred = torch.argmax(torch.softmax(output, dim=1), dim=1).squeeze().cpu().numpy()<br>
pred_resized = cv2.resize(pred.astype(‘uint8’), (original_shape[0], original_shape[1]), interpolation=cv2.INTER_NEAREST)<br>
predictions.append(pred_resized)<br>
pred_volume = np.stack(predictions, axis=-1)<br>
return pred_volume</p>

username: ptrblck
cooked: <aside class="quote no-group" data-username="Teodora-bo" data-post="1" data-topic="219822">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/teodora-bo/48/76933_2.png" class="avatar"> Teodora-bo:</div>
<blockquote>
<p>I think the problem is that with the Cross Entropy we train classes from 0 to 9 and then the 0 class is forget.</p>
</blockquote>
</aside>
<p>I don’t understand this claim as the model will not “forget” class0. Using class indices in the range <code>[0, nb_classes-1]</code> is the correct approach for <code>nn.CrossEntropyLoss</code>.</p>

username: Teodora-bo
cooked: <p>When I run the script to make a prediction on a CT scan, class 0 no longer appears, while the others from 1 to 9 do. At the same time, class 1 — which is the general “femur” class — does appear, but the last class, class 10, is missing. The issue is that if I add something like <code>pred = pred + 1</code> for the prediction, 10 classes are displayed, but class 1 ends up containing the background. Here are some pictures to help understand the context. The image is without adding the line <code>pred = pred + 1</code><br>
<div class="lightbox-wrapper"><a class="lightbox" href="https://discuss.pytorch.org/uploads/default/original/3X/1/c/1c629d9327463b42a71cb540265f043e52bb66c0.jpeg" data-download-href="https://discuss.pytorch.org/uploads/default/1c629d9327463b42a71cb540265f043e52bb66c0" title="image"><img src="https://discuss.pytorch.org/uploads/default/optimized/3X/1/c/1c629d9327463b42a71cb540265f043e52bb66c0_2_690x431.jpeg" alt="image" data-base62-sha1="436Eq9gvT3CTDFxVfTaHfnjPmik" width="690" height="431" srcset="https://discuss.pytorch.org/uploads/default/optimized/3X/1/c/1c629d9327463b42a71cb540265f043e52bb66c0_2_690x431.jpeg, https://discuss.pytorch.org/uploads/default/optimized/3X/1/c/1c629d9327463b42a71cb540265f043e52bb66c0_2_1035x646.jpeg 1.5x, https://discuss.pytorch.org/uploads/default/optimized/3X/1/c/1c629d9327463b42a71cb540265f043e52bb66c0_2_1380x862.jpeg 2x" data-dominant-color="BEBED4"><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">image</span><span class="informations">1920×1200 151 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>

username: Teodora-bo
cooked: <p>If I add the ‘pred = pred + 1’ the result is this<br>
<div class="lightbox-wrapper"><a class="lightbox" href="https://discuss.pytorch.org/uploads/default/original/3X/b/2/b26d074fdbfcc47598dd9c4e9dbaea8d0070d274.jpeg" data-download-href="https://discuss.pytorch.org/uploads/default/b26d074fdbfcc47598dd9c4e9dbaea8d0070d274" title="Captură de ecran din 2025-05-07 la 09.40.24"><img src="https://discuss.pytorch.org/uploads/default/optimized/3X/b/2/b26d074fdbfcc47598dd9c4e9dbaea8d0070d274_2_690x387.jpeg" alt="Captură de ecran din 2025-05-07 la 09.40.24" data-base62-sha1="psqA6A3Xx0gWLgP0meBBXqZUIE4" width="690" height="387" srcset="https://discuss.pytorch.org/uploads/default/optimized/3X/b/2/b26d074fdbfcc47598dd9c4e9dbaea8d0070d274_2_690x387.jpeg, https://discuss.pytorch.org/uploads/default/optimized/3X/b/2/b26d074fdbfcc47598dd9c4e9dbaea8d0070d274_2_1035x580.jpeg 1.5x, https://discuss.pytorch.org/uploads/default/optimized/3X/b/2/b26d074fdbfcc47598dd9c4e9dbaea8d0070d274_2_1380x774.jpeg 2x" data-dominant-color="9AA09C"><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">Captură de ecran din 2025-05-07 la 09.40.24</span><span class="informations">1920×1077 125 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>

username: ptrblck
cooked: <aside class="quote no-group" data-username="Teodora-bo" data-post="3" data-topic="219822">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/teodora-bo/48/76933_2.png" class="avatar"> Teodora-bo:</div>
<blockquote>
<p>while the others from 1 to 9 do. At the same time, class 1 — which is the general “femur” class — does appear, but the last class, class 10, is missing</p>
</blockquote>
</aside>
<p>There cannot be a class 10 unless you are dealing with 11 classes. I.e. if you are dealing with 1 background class and 10 object classes you would need to use 11 indices in the range <code>[0, 10]</code>.</p>

username: Teodora-bo
cooked: <p>In the ground truth, there is no background class explicitly segmented — it might only appear automatically.</p>

username: ptrblck
cooked: <aside class="quote no-group" data-username="Teodora-bo" data-post="6" data-topic="219822">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/teodora-bo/48/76933_2.png" class="avatar"> Teodora-bo:</div>
<blockquote>
<p>it might only appear automatically.</p>
</blockquote>
</aside>
<p>I don’t know what this means. Assuming the target tensor has the shape of <code>[batch_size, height, width]</code> it would contain the class index at each pixel location. If the ground truth does not contain the background class which values are used in when no other class is detected (i.e. for the background pixels)? The target tensor has to store values at these positions.</p>