[Python] Problem implementing a 1D CNNs Autoencoder

Discussão em 'Python' iniciado por Stack, Março 31, 2025.

  1. Stack

    Stack Membro Participativo

    I'm having an extremely hard time trying to implement a simple Autoencoder where the encoder and the decoder are 1D CNNs:

    import torch
    import torch.nn as nn
    import torch.nn.functional as F
    import numpy as np

    class IrAutoencoder(nn.Module):
    def __init__(self, input_dim=1800, latent_dim=128, channels=[1, 16, 32, 64], pool_kernel=2):
    super().__init__()

    self.dims = [input_dim]

    # Encoder
    self.conv1 = nn.Conv1d(channels[0], channels[1], kernel_size=10, stride=2, padding=4)
    self.dims.append(self._conv_out_dim(input_dim, kernel_size=10, stride=2, padding=4, dilation=1))

    self.norm1 = nn.BatchNorm1d(channels[1])
    self.pool1 = nn.AvgPool1d(kernel_size=pool_kernel, stride=pool_kernel)
    self.dims.append(self._conv_out_dim(self.dims[-1], pool_kernel, pool_kernel, 0, 0))

    self.conv2 = nn.Conv1d(channels[1], channels[2], kernel_size=5, stride=2, padding=2)
    self.dims.append(self._conv_out_dim(self.dims[-1], kernel_size=5, stride=2, padding=2, dilation=1))

    self.norm2 = nn.BatchNorm1d(channels[2])
    self.pool2 = nn.AvgPool1d(kernel_size=pool_kernel, stride=pool_kernel)
    self.dims.append(self._conv_out_dim(self.dims[-1], pool_kernel, pool_kernel, 0, 0))

    self.conv3 = nn.Conv1d(channels[2], channels[3], kernel_size=2, stride=1, padding=1)
    self.dims.append(self._conv_out_dim(self.dims[-1], kernel_size=2, stride=1, padding=1, dilation=1))

    self.norm3 = nn.BatchNorm1d(channels[3])
    self.pool3 = nn.AvgPool1d(kernel_size=pool_kernel, stride=pool_kernel)
    self.dims.append(self._conv_out_dim(self.dims[-1], pool_kernel, pool_kernel, 0, 0))

    # Bottleneck
    self.bottleneck = nn.Sequential(
    nn.Linear(channels[3] * self.dims[-1], latent_dim),
    nn.BatchNorm1d(latent_dim),
    nn.ReLU()
    )

    # Decoder
    ...

    @staticmethod
    def _conv_out_dim(dim_in, kernel_size, stride, padding, dilation):
    return (dim_in + 2 * padding - dilation * (kernel_size - 1) - 1) // stride + 1

    def forward(self, x):
    # Encoder
    x = self.pool1(F.relu(self.norm1(self.conv1(x))))
    x = self.pool2(F.relu(self.norm2(self.conv2(x))))
    x = self.pool3(F.relu(self.norm3(self.conv3(x))))

    x = x.view(x.size(0), -1) # Flatten before bottleneck
    x = self.bottleneck(x)

    # decoder ...

    if __name__ == '__main__': #Double checking dimensions
    input_tensor = torch.randn(10, 1, 1800)

    # Initialize and run the autoencoder
    autoencoder = IrAutoencoder(input_dim=1800, latent_dim=128)
    output = autoencoder(input_tensor)

    print("Input shape:", input_tensor.shape) # Expected: [10, 1, 1800]
    print("Output shape:", output.shape) # Expected: [10, 1, 1800]


    First of all I'm not too sure about this but there might be problems with self.bottleneck. To be sure, I compute the dimension at every nn.Conv1d and nn.AvgPool1D layer in order to extract the signal dimension and I obtain

    self.dims
    [1800, 900, 450, 225, 113, 114, 57]


    but then when it gets to self.bottleneck x has shape [10,64,56]. I guess this might be related to a problem with the other parameters I'm currently using, i.e. kernel_size, stride and so on? Additionally, I find it very difficult now to implement the decoder and I don't know how should I select the same parameters in order to obtain a signal with the input dimension (i.e. 1800). In general I'm finding quite difficult to implement this type of model and it seems you just have to rely on tweaking all these parameters until getting the right shape. Could you please help me with this, and eventually also suggesting best practices to follow when implementing this kind of architectures? Thanks.

    Continue reading...

Compartilhe esta Página