Size input image in CNN

Hello,

I have a trained CNN for segmentation with a certain input image size and now I want to use it and predict some output for test image. As you might know for segmentation, it is not required to use the same image size and so I wanted to do some experiment and try different size (I have some other problematic to justify this). But the thing is when doing

proba = model.forward(new_image)

I get the following error :

RuntimeError: Sizes of tensors must match except in dimension 1. Got 64 and 63 in dimension 2 (The offending index is 1)

And it kind of appear that my input size can’t be other that a multiple of 32 … I wanted to know if someone has some info about this topic, is it normal ? Does it depend from my network architecture (from what operation) ? Or does it depend from my hardware architecture (I am on CPU now) ?

Thank you

U can see this as matrix multiplication.
Assuming u have a matrix called input that is being multiplied by a matrix called weight to give another called output such that
input x weight = output.

If input shape = (1, 5)
and output shape = (1, 10)
It means that the shape of ur weights = (5, 10)

Now that the network has been trained on such inputs and outputs, the weights will have a fixed shape. This means that any input that is not of shape (1, 5) will be incompatible with the weight and will hence throw an error.

So yes it is normal to get these kinda errors if u try to run inference on a differently sized image with a network that was trained on a specific image size.

So what I suggest is to create and algorithms the scales down the input the the required size.

I really Hope this was helpful.:kissing:

No, for segmentation task it works for different size because you are doing only convolution operation

What’s the shape of ur testing input ?
Like: (N, C, H, W)

Have u checked if maybe u transposed the input matrix wrongly too?

:thinking:

As I said, I can confirm that I don’t have any error if the input image size is a multiple of 32 … The error appears only for specific input image size … So it can’t be a problem of transposed matrix

Ok fair point
Can I see the code of ur network conv layers

OK … Here it is :


> Unet(
>   (encoder): MobileNetV2Encoder(
>     (features): Sequential(
>       (0): ConvBNReLU(
>         (0): Conv2d(3, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
>         (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>         (2): ReLU6(inplace=True)
>       )
>       (1): InvertedResidual(
>         (conv): Sequential(
>           (0): ConvBNReLU(
>             (0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=32, bias=False)
>             (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (1): Conv2d(32, 16, kernel_size=(1, 1), stride=(1, 1), bias=False)
>           (2): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>         )
>       )
>       (2): InvertedResidual(
>         (conv): Sequential(
>           (0): ConvBNReLU(
>             (0): Conv2d(16, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)
>             (1): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (1): ConvBNReLU(
>             (0): Conv2d(96, 96, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=96, bias=False)
>             (1): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (2): Conv2d(96, 24, kernel_size=(1, 1), stride=(1, 1), bias=False)
>           (3): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>         )
>       )
>       (3): InvertedResidual(
>         (conv): Sequential(
>           (0): ConvBNReLU(
>             (0): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1), bias=False)
>             (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (1): ConvBNReLU(
>             (0): Conv2d(144, 144, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=144, bias=False)
>             (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (2): Conv2d(144, 24, kernel_size=(1, 1), stride=(1, 1), bias=False)
>           (3): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>         )
>       )
>       (4): InvertedResidual(
>         (conv): Sequential(
>           (0): ConvBNReLU(
>             (0): Conv2d(24, 144, kernel_size=(1, 1), stride=(1, 1), bias=False)
>             (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (1): ConvBNReLU(
>             (0): Conv2d(144, 144, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=144, bias=False)
>             (1): BatchNorm2d(144, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (2): Conv2d(144, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)
>           (3): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>         )
>       )
>       (5): InvertedResidual(
>         (conv): Sequential(
>           (0): ConvBNReLU(
>             (0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)
>             (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (1): ConvBNReLU(
>             (0): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192, bias=False)
>             (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (2): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)
>           (3): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>         )
>       )
>       (6): InvertedResidual(
>         (conv): Sequential(
>           (0): ConvBNReLU(
>             (0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)
>             (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (1): ConvBNReLU(
>             (0): Conv2d(192, 192, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=192, bias=False)
>             (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (2): Conv2d(192, 32, kernel_size=(1, 1), stride=(1, 1), bias=False)
>           (3): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>         )
>       )
>       (7): InvertedResidual(
>         (conv): Sequential(
>           (0): ConvBNReLU(
>             (0): Conv2d(32, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)
>             (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (1): ConvBNReLU(
>             (0): Conv2d(192, 192, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=192, bias=False)
>             (1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (2): Conv2d(192, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
>           (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>         )
>       )
>       (8): InvertedResidual(
>         (conv): Sequential(
>           (0): ConvBNReLU(
>             (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)
>             (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (1): ConvBNReLU(
>             (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)
>             (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (2): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
>           (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>         )
>       )
>       (9): InvertedResidual(
>         (conv): Sequential(
>           (0): ConvBNReLU(
>             (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)
>             (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (1): ConvBNReLU(
>             (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)
>             (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (2): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
>           (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>         )
>       )
>       (10): InvertedResidual(
>         (conv): Sequential(
>           (0): ConvBNReLU(
>             (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)
>             (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (1): ConvBNReLU(
>             (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)
>             (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (2): Conv2d(384, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
>           (3): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>         )
>       )
>       (11): InvertedResidual(
>         (conv): Sequential(
>           (0): ConvBNReLU(
>             (0): Conv2d(64, 384, kernel_size=(1, 1), stride=(1, 1), bias=False)
>             (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (1): ConvBNReLU(
>             (0): Conv2d(384, 384, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=384, bias=False)
>             (1): BatchNorm2d(384, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (2): Conv2d(384, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)
>           (3): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>         )
>       )
>       (12): InvertedResidual(
>         (conv): Sequential(
>           (0): ConvBNReLU(
>             (0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)
>             (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (1): ConvBNReLU(
>             (0): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576, bias=False)
>             (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (2): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)
>           (3): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>         )
>       )
>       (13): InvertedResidual(
>         (conv): Sequential(
>           (0): ConvBNReLU(
>             (0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)
>             (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (1): ConvBNReLU(
>             (0): Conv2d(576, 576, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=576, bias=False)
>             (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (2): Conv2d(576, 96, kernel_size=(1, 1), stride=(1, 1), bias=False)
>           (3): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>         )
>       )
>       (14): InvertedResidual(
>         (conv): Sequential(
>           (0): ConvBNReLU(
>             (0): Conv2d(96, 576, kernel_size=(1, 1), stride=(1, 1), bias=False)
>             (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (1): ConvBNReLU(
>             (0): Conv2d(576, 576, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), groups=576, bias=False)
>             (1): BatchNorm2d(576, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (2): Conv2d(576, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)
>           (3): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>         )
>       )
>       (15): InvertedResidual(
>         (conv): Sequential(
>           (0): ConvBNReLU(
>             (0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)
>             (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (1): ConvBNReLU(
>             (0): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)
>             (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (2): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)
>           (3): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>         )
>       )
>       (16): InvertedResidual(
>         (conv): Sequential(
>           (0): ConvBNReLU(
>             (0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)
>             (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (1): ConvBNReLU(
>             (0): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)
>             (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (2): Conv2d(960, 160, kernel_size=(1, 1), stride=(1, 1), bias=False)
>           (3): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>         )
>       )
>       (17): InvertedResidual(
>         (conv): Sequential(
>           (0): ConvBNReLU(
>             (0): Conv2d(160, 960, kernel_size=(1, 1), stride=(1, 1), bias=False)
>             (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (1): ConvBNReLU(
>             (0): Conv2d(960, 960, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), groups=960, bias=False)
>             (1): BatchNorm2d(960, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>             (2): ReLU6(inplace=True)
>           )
>           (2): Conv2d(960, 320, kernel_size=(1, 1), stride=(1, 1), bias=False)
>           (3): BatchNorm2d(320, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>         )
>       )
>       (18): ConvBNReLU(
>         (0): Conv2d(320, 1280, kernel_size=(1, 1), stride=(1, 1), bias=False)
>         (1): BatchNorm2d(1280, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>         (2): ReLU6(inplace=True)
>       )
>     )
>   )
>   (decoder): UnetDecoder(
>     (center): Identity()
>     (blocks): ModuleList(
>       (0): DecoderBlock(
>         (conv1): Conv2dReLU(
>           (0): Conv2d(1376, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
>           (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>           (2): ReLU(inplace=True)
>         )
>         (attention1): Attention(
>           (attention): Identity()
>         )
>         (conv2): Conv2dReLU(
>           (0): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
>           (1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>           (2): ReLU(inplace=True)
>         )
>         (attention2): Attention(
>           (attention): Identity()
>         )
>       )
>       (1): DecoderBlock(
>         (conv1): Conv2dReLU(
>           (0): Conv2d(288, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
>           (1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>           (2): ReLU(inplace=True)
>         )
>         (attention1): Attention(
>           (attention): Identity()
>         )
>         (conv2): Conv2dReLU(
>           (0): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
>           (1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>           (2): ReLU(inplace=True)
>         )
>         (attention2): Attention(
>           (attention): Identity()
>         )
>       )
>       (2): DecoderBlock(
>         (conv1): Conv2dReLU(
>           (0): Conv2d(152, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
>           (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>           (2): ReLU(inplace=True)
>         )
>         (attention1): Attention(
>           (attention): Identity()
>         )
>         (conv2): Conv2dReLU(
>           (0): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
>           (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>           (2): ReLU(inplace=True)
>         )
>         (attention2): Attention(
>           (attention): Identity()
>         )
>       )
>       (3): DecoderBlock(
>         (conv1): Conv2dReLU(
>           (0): Conv2d(80, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
>           (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>           (2): ReLU(inplace=True)
>         )
>         (attention1): Attention(
>           (attention): Identity()
>         )
>         (conv2): Conv2dReLU(
>           (0): Conv2d(32, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
>           (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>           (2): ReLU(inplace=True)
>         )
>         (attention2): Attention(
>           (attention): Identity()
>         )
>       )
>       (4): DecoderBlock(
>         (conv1): Conv2dReLU(
>           (0): Conv2d(32, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
>           (1): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>           (2): ReLU(inplace=True)
>         )
>         (attention1): Attention(
>           (attention): Identity()
>         )
>         (conv2): Conv2dReLU(
>           (0): Conv2d(16, 16, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
>           (1): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
>           (2): ReLU(inplace=True)
>         )
>         (attention2): Attention(
>           (attention): Identity()
>         )
>       )
>     )
>   )
>   (segmentation_head): SegmentationHead(
>     (0): Conv2d(16, 1, kernel_size=(1, 1), stride=(1, 1), bias=False)
>     (1): Identity()
>     (2): Activation(
>       (activation): Sigmoid()
>     )
>   )
> )

So I’ve seen the structure of ur network.
U said ur network only accept inputs with size of multiple of 32 so I was just thinking what r the exact multiples of 32 u tested it with (eg: 64, 128, etc)
Have u tried and image of size exactly equal to 32?

Also I’ve not completely studied ur architecture but from what I’m seeing, ur network seems to be downsampling the input with it’s kernals and strides without upsampling back and from what I know about segmentation, the output is usually the same size as it’s image but with annotated pixels :man_shrugging:
So what if u feed in an image of 1 x 3 x 64 x 64 what do u get as it’s output.

U said ur network only accept inputs with size of multiple of 32 so I was just thinking what r the exact multiples of 32 u tested it with (eg: 64, 128, etc)
Have u tried and image of size exactly equal to 32?

Yes I have tried 32,64,224,448 (and some others…)

ur network seems to be downsampling the input with it’s kernals and strides without upsampling back and from what I know about segmentation, the output is usually the same size as it’s image but with annotated pixels :man_shrugging:
So what if u feed in an image of 1 x 3 x 64 x 64 what do u get as it’s output.

It might not appear in the lines I have copy/paste above but I can confirm that I have an encoder/decoder architecture (with upsampling in the decoder part). I have actually used a Unet model with the package smp. And indeed my output is a mask with exactly the same size as the input (for height and width) …

Well the only way to actually help u now is to reimplement ur network architecture
And see things for myself coz currently I’m not able to spot any thing that looks wrong here.
I’ll probably get back to u tomorrow.

I suggest you to install the package segmentation-models-pytorch then to create the same model as me with :

model = smp.Unet(encoder_name="mobilenet_v2",  encoder_weights='imagenet', activation=activation)
model.segmentation_head[0] = nn.Conv2d(16, num_classes, kernel_size=(1, 1), stride=(1, 1), bias=False) # to have one class in output

The solution of the question is about the architecture. For segmentation task, I was using an encoder/decoder architecture (Unet). And indeed the input size had to be a multiple of 32 due to multiple undersampling, oversampling and copy feature. It might be possible to solve this problem by adapting or changing the architecture