Why is my ResNet18 model with BCEWithLogitsLoss predicting only one class?

252 Views Asked by At

I am building a deep learning model with Sentinel-2 satellite imagery as the input dataset. The dataset can be labeled as being either natural (1) or anthropogenic (0). I am using PyTorch, with a ResNet18 model, and BCEWithLogitsLoss as a loss function.

I have tried following several solutions found online, including feeding the model with a dataset of only natural (label = 1) images, but every time the model will only predict the label 0.

The same code, with loss = CrossEntropyLoss() and output classes = 2, will predict more correct results, up to 99% accuracy.

Where am I going wrong?

I am new here, so apologies in advance if I made any mistakes! Please, let me know if I need to provide additional code.

Thank you :)

Dataset class:

class CustomDataset(Dataset):
  def __init__(self, csv_file, Data_Folder, transform = None, datasplitcat = None, bands = None):
    self.data = pd.read_csv(csv_file, sep = ",", skiprows=None)    
    if datasplitcat:
      self.data = self.data[self.data['datasplit'] == datasplitcat]

    self.data = self.data.reset_index(drop=True, inplace=False)
    self.image_paths = self.data['file']
    self.image_folder = Data_Folder
    self.target_values = self.data['label']
    self.transform = transform
    self.bands = bands
  
  def __len__(self):
    return len(self.image_paths)
  
  def __getitem__(self, idx):

    # IMAGES # 

    image_name = self.image_paths[idx]
    image_path = os.path.join(self.image_folder, image_name)
    image = tiff.imread(image_path)

    # Choose Number of Bands
    image = image[:, :, self.bands]
    
    # Float transformation
    image = image.astype('float32')    
    
    # Normalization
    image = image[:,:,:]/8000
    
    # Transformation
    if self.transform:
       image = self.transform(image)
    
    # LABELS #
    label = int(self.target_values[idx])
    label = np.array(label).astype('int64')
    label = torch.tensor(label, dtype=torch.long)
    label = label.view(-1)
    
    return image, label

Loss implementation:

for batch in train_loader:
        optimizer.zero_grad() 
        inputs, targets = batch
        inputs, targets = inputs.to(device), targets.to(device)
        
        # Forward pass
        outputs = model(inputs)

        targets = targets.view(-1, 1)
        targets = torch.tensor(targets, dtype=torch.float32).to(device)
        
        # Compute loss
        loss = loss_fn(outputs, targets)

        total_loss += loss.item()
        
        # Backward pass and optimization
        loss.backward()
        optimizer.step()

Model code:

class ResNet18(nn.Module):
    def __init__(self, num_classes, band, pt_value):
        super(ResNet18, self).__init__()

        resnet = resnet18(pretrained = pt_value)  # Use pre-trained ResNet50

        # Modify the first convolutional layer to accept variable input channels
        if band != 3:
            resnet.conv1 = nn.Conv2d(band, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)

        self.features = nn.Sequential(*list(resnet.children())[:-2])

        self.pool = nn.AdaptiveAvgPool2d(1)
        
        self.fc1 = nn.Linear(512, 64)
        self.fc2 = nn.Linear(64, 1)

    def forward(self, x):
        x = self.features(x)
        x = self.pool(x)
        x = x.view(x.size(0), -1)
        x = relu(self.fc1(x))
        x = self.fc2(x)
        return x
0

There are 0 best solutions below