Since my problem is resolved, all thanks to PyTorch’s amazing discussion community… For anyone who happens to read this post in future for the same problem, here’s my final working model class and the training code:
in_features = 228 #in_features needed for Flattening(linear/fully connected) layer

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 6, 2, stride=1, padding=1)
        self.bn1 = nn.BatchNorm2d(num_features=6)
        self.conv2 = nn.Conv2d(6, 8, 2, stride=1, padding=1)
        self.bn2 = nn.BatchNorm2d(num_features=8)
        self.conv3 = nn.Conv2d(8, 12, 2, stride=1, padding=1)
        self.bn3 = nn.BatchNorm2d(num_features=12)

        self.fc1   = nn.Linear(in_features, 36)
        self.fc2   = nn.Linear(36, 3)
        
    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = F.max_pool2d(out, 2)
        
        out = F.relu(self.bn2(self.conv2(out)))
        out = F.max_pool2d(out, 2)
        
        out = F.relu(self.bn3(self.conv3(out)))
        out = F.max_pool2d(out, 2)
        
        out = out.view(out.size(0), -1)
        print (out.size())  # helps in showing the size needed
        out = F.relu(self.fc1(out))
        out = self.fc2(out)
        return out

pytorch_model = Net()

Where the in_features can be calculated by following the code (e.g. in my case starting from input size > conv1 (bn1, relu) > max_pool2d > conv2 (bn2, relu) etc…). and using the following formula to find output size for each layer and finally the output size that nn.Linear layer needs (which led me to 228 (12*19)):

O=( (W−K+2P) /S ) + 1
where
W=Input height/width
K=Filter size
P=Padding
S=Stride
O=Output size

The criterion and optimizer:
weights = torch.tensor([0.05, 0.15, 0.8]).double() # assigning weights to classes
criterion = nn.CrossEntropyLoss(weight=weights)
optimizer = optim.Adam(pytorch_model.parameters())

And finally to train the model:
batch_size = 100
epochs = 10

# Uncomment the line below if data sets are type double. 
# Or can conversely convert data sets to type float and leave this commented

# pytorch_model = pytorch_model.double()

for epoch in tqdm(range(epochs)):
    for i in range(0, len(X_TRAIN), batch_size):
        batch_x = X_TRAIN[i:i+batch_size]
        batch_y = Y_TRAIN[i:i+batch_size]
        
        optimizer.zero_grad() # clears all the gradients
        
        outputs = pytorch_model(batch_x.double())
        loss = criterion(outputs, batch_y.long())
        loss.backward()
        optimizer.step()
    print(loss)

Making sure to follow the suggestions from @ptrblck and @Eta_C, I managed to run the training successfully.
Happy Learning!