The code looks generally great! 
Some minor issues:

Based on the architecture it seems you are working on a multi-class classification use case. If that’s the case, you should remove the nn.Softmax as the last activation function and use nn.CrossEntropyLoss, which will internally apply F.log_softmax and nn.NLLLoss.




 CSstudent:

Each convolution layer is followed by a ReLU and a max-pool operation with kernel size 2x2.



This statement is currently not true, as you are using reusing your conv layers:

        x = self.conv1(x)
        x = self.relu(self.conv1(x))

This code will use self.conv1 twice. I’m not sure, if that’s your use case, but it doesn’t match your explanation.