pytorch-summary icon indicating copy to clipboard operation
pytorch-summary copied to clipboard

Prints twice all layers

Open mangotree3 opened this issue 5 years ago • 8 comments

Hi,

The summary module seems to behave weirdly on my model. The function outputs all my layers twice in duplicate fashion.

The code is the following:

import torch
import torch.nn as nn
from torchsummary import summary


class Model(nn.Module):

    def __init__(self):

        super(Model,self).__init__()

        K = 69
        L = 1000

        self.group1 = nn.Sequential(
            nn.Conv3d(3, K, kernel_size=3,padding=(1,1,1)),
            nn.LeakyReLU()
        )

        self.group2 = nn.Sequential(
            nn.Conv3d(K, L, kernel_size=4, padding=(1,1,1)),
            nn.MaxPool3d(2, stride=2,ceil_mode=True),
            nn.ReLU()
        )

        self._features = nn.Sequential(
            self.group1,
            self.group2
        )

    def forward(self,x):
        out = self._features(x)
        return out

if '__main__' == __name__:

    model = Model()
    inp = (3, 30, 30, 30)

    summary(model, inp, device='cpu')

The output is the following:

----------------------------------------------------------------
        Layer (type)               Output Shape         Param #
================================================================
            Conv3d-1       [-1, 69, 30, 30, 30]           5,658
            Conv3d-2       [-1, 69, 30, 30, 30]           5,658
         LeakyReLU-3       [-1, 69, 30, 30, 30]               0
         LeakyReLU-4       [-1, 69, 30, 30, 30]               0
            Conv3d-5     [-1, 1000, 29, 29, 29]       4,417,000
            Conv3d-6     [-1, 1000, 29, 29, 29]       4,417,000
         MaxPool3d-7     [-1, 1000, 15, 15, 15]               0
         MaxPool3d-8     [-1, 1000, 15, 15, 15]               0
              ReLU-9     [-1, 1000, 15, 15, 15]               0
             ReLU-10     [-1, 1000, 15, 15, 15]               0
================================================================
Total params: 8,845,316
Trainable params: 8,845,316
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.31
Forward/backward pass size (MB): 532.00
Params size (MB): 33.74
Estimated Total Size (MB): 566.05
----------------------------------------------------------------

As you can see in the self.group1() I have one nn.Conv3d(). However, in the output I get Conv3d-1 and Conv3d-2 for that layer.

When model.apply(register_hook) runs it seems to already append twice the number of module.register_forward_hook(hook) expected in the hooks list.

Any clues about what is happening?

Thanks, Matthias

mangotree3 avatar Nov 02 '19 00:11 mangotree3

@sksq96 Any clues? Thanks

mangotree3 avatar Nov 08 '19 19:11 mangotree3

I'm not entirely sure what that is happening, but you can get around the issue with this model structure instead. Notice that you no longer use the attributes group1 and group2. It does mean its not as neatly structured, but I wonder if its something wrong with what is being returned by register_forward_hook(hook)) . Not entirely sure though.

         self._features = nn.Sequential(
            nn.Sequential(
                nn.Conv3d(3, K, kernel_size=3,padding=(1,1,1)),
                nn.LeakyReLU()
            ), 
            nn.Sequential(
                nn.Conv3d(K, L, kernel_size=4, padding=(1,1,1)),
                nn.MaxPool3d(2, stride=2,ceil_mode=True),
                nn.ReLU()
            )
        )

Naireen avatar Dec 28 '19 04:12 Naireen

@mangotree3

This is not a pytorch-sumamry's bug. This is due to the implementation of PyTorch, and your unintended results are that self.group1 and self.group2 are declared as instance variables of Model. Actually, when I change self.group1 and self.group2 to group1 and group2 and execute, I get the intended results:

----------------------------------------------------------------
        Layer (type)               Output Shape         Param #
================================================================
            Conv3d-1       [-1, 69, 30, 30, 30]           5,658
         LeakyReLU-2       [-1, 69, 30, 30, 30]               0
            Conv3d-3     [-1, 1000, 29, 29, 29]       4,417,000
         MaxPool3d-4     [-1, 1000, 15, 15, 15]               0
              ReLU-5     [-1, 1000, 15, 15, 15]               0
================================================================
Total params: 4,422,658
Trainable params: 4,422,658
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.31
Forward/backward pass size (MB): 266.00
Params size (MB): 16.87
Estimated Total Size (MB): 283.18
----------------------------------------------------------------

5n7-sk avatar Feb 02 '20 10:02 5n7-sk

I got the same issue, not a big problem, but definitely a bug. I have a model from Colab which I am not running on Google Cloud, changed noting on the notebook, but also got it to print twice.

luisdiaz1997 avatar Apr 18 '20 07:04 luisdiaz1997

@luisdiaz1997

Hmmm, can you share the code?

5n7-sk avatar Apr 18 '20 11:04 5n7-sk

https://github.com/luisdiaz1997/DataScienceCourse/blob/master/Week_10_solutions.ipynb In Colab it only shows once, but in my cloud it shows twice, is worth to note that in Colab I did not install it from here, in my cloud I had to. It doesn't show up twice if I do

some_var = summary(model, (some_shape) ) if I save it to a variable it only prints once. I assume there is a print() inside summary()

luisdiaz1997 avatar Apr 21 '20 03:04 luisdiaz1997

I assume there is a print() inside summary()

It is true that there is a print() in summary(), but since summary() has no return value, I cannot understand its behavior:/

5n7-sk avatar Apr 21 '20 12:04 5n7-sk

Thanks everyone for your comments and answers. I agree with @skmatz there not much one can do other than adapt the model's code. The package is just built on top of pytorch and one has to work with the tools they give you... I confirm that the problem effectively lies in having a double self assignment of the model's parts in the __init__().

As mentioned by @Naireen, this works:

class Model(nn.Module):
    def __init__(self):
        super(Model,self).__init__()

        K = 69
        L = 1000
        self._features = nn.Sequential(
            nn.Sequential(
                nn.Conv3d(3, K, kernel_size=3,padding=(1,1,1)),
                nn.LeakyReLU()
            )
            nn.Sequential(
                nn.Conv3d(K, L, kernel_size=4, padding=(1,1,1)),
                nn.MaxPool3d(2, stride=2,ceil_mode=True),
                nn.ReLU()
            )
        )

    def forward(self,x):
        out = self._features(x)
        return out

Alternatively this also works:

class Model(nn.Module):

    def __init__(self):

        super(Model,self).__init__()

        K = 69
        L = 1000

        self.group1 = nn.Sequential(
            nn.Conv3d(3, K, kernel_size=3,padding=(1,1,1)),
            nn.LeakyReLU()
        )

        self.group2 = nn.Sequential(
            nn.Conv3d(K, L, kernel_size=4, padding=(1,1,1)),
            nn.MaxPool3d(2, stride=2,ceil_mode=True),
            nn.ReLU()
        )

    def forward(self,x):
        out = self.group1(x)
        out = self.group2(out)
        return out

However, this doesn't work (prints double) because you have the two groups of elements (the "groups" and the "features") in the __init__ with one nested into the other and they seem to both call the hook at initialization:

class Model(nn.Module):

    def __init__(self):

        super(Model,self).__init__()

        K = 69
        L = 1000

        self.group1 = nn.Sequential(
            nn.Conv3d(3, K, kernel_size=3,padding=(1,1,1)),
            nn.LeakyReLU()
        )

        self.group2 = nn.Sequential(
            nn.Conv3d(K, L, kernel_size=4, padding=(1,1,1)),
            nn.MaxPool3d(2, stride=2,ceil_mode=True),
            nn.ReLU()
        )

        self._features = nn.Sequential(
            self.group1,
            self.group2
        )

    def forward(self,x):
        out = self._features(x)
        return out

mangotree3 avatar Jun 11 '20 14:06 mangotree3