Convolutional Autoencoder in Pytorch for Dummies
I am here to ask some more general questions about Pytorch and Convolutional Autoencoders. 


If I only use Convolutional Layers (FCN), do I even have to care about the input shape? And then how do I choose the number of featuremaps best?
Does a ConvTranspose2d Layer automatically unpool?
Can you spot any errors or unconventional code in my example?


By the way, I want to make a symmetrical Convolutional Autoencoder to colorize black and white images with different image sizes.

    self.encoder = nn.Sequential (
        # conv 1
        nn.Conv2d(in_channels=3, out_channels=512, kernel_size=3, stride=1, padding=1),
        nn.ReLU,
        nn.MaxPool2d(kernel_size=2, stride=2),  # 1/2
        nn.BatchNorm2d(512),

        # conv 2
        nn.Conv2d(in_channels=512, out_channels=256, kernel_size=3, stride=1, padding=1),
        nn.ReLU,
        nn.MaxPool2d(kernel_size=2, stride=2),  # 1/4
        nn.BatchNorm2d(256),

        # conv 3
        nn.Conv2d(in_channels=256, out_channels=128, kernel_size=3, stride=1, padding=1),
        nn.ReLU,
        nn.MaxPool2d(kernel_size=2, stride=2),  # 1/8
        nn.BatchNorm2d(128),

        # conv 4
        nn.Conv2d(in_channels=128, out_channels=64, kernel_size=3, stride=1, padding=1),
        nn.ReLU,
        nn.MaxPool2d(kernel_size=2, stride=2),  #1/16
        nn.BatchNorm2d(64)
    )


    self.encoder = nn.Sequential (
        # conv 5
        nn.ConvTranspose2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1),
        nn.ReLU,
        nn.BatchNorm2d(128),

        # conv 6
        nn.ConvTranspose2d(in_channels=128, out_channels=256, kernel_size=3, stride=1, padding=1),
        nn.ReLU,
        nn.BatchNorm2d(256),

        # conv 7
        nn.ConvTranspose2d(in_channels=256, out_channels=512, kernel_size=3, stride=1, padding=1),
        nn.ReLU,
        nn.BatchNorm2d(512),

        # conv 8
        nn.ConvTranspose2d(in_channels=512, out_channels=512, kernel_size=3, stride=1, padding=1),
        nn.Softmax()
    )

    def forward(self, x):
        h = x
        h = self.encoder(h)
        h = self.decoder(h)
        return h



No, you don't need to care about input width and height with a fully convolutional model. But should probably ensure that each downsampling operation in the encoder is matched by a corresponding upsampling operation in the decoder.
I'm not sure what you mean by unpooling. If you mean upsampling (increasing spatial dimensions), then this is what the stride parameter is for. In PyTorch, a transpose convolution with stride=2 will upsample twice. Note, however, that instead of a transpose convolution, many practitioners prefer to use bilinear upsampling followed by a regular convolution. This is one reason why.


If, on the other hand, you mean actual unpooling, then you should look at the documentation of torch.MaxUnpool2d. You need to collect maximal value indices from the MaxPool2d operation and feed them into MaxUnpool2d.

The general consensus seems to be that you should increase the number of feature maps as you downsample. Your code appears to do the reverse. Consecutive powers of 2 seem like a good place to start. It's hard to suggest a better rule of thumb. You probably need to experiment a little.

In other notes, I'm not sure why you apply softmax to the encoder output.
