Search code examples
pytorchhuggingface-transformers

How to obtain sequence of submodules from a pytorch module?


For a pytorch module, I suppose I could use .named_children, .named_modules, etc. to obtain a list of the submodules. However, I suppose the list is not given in order, right? An example:

In [19]: import transformers

In [20]: model = transformers.DistilBertForSequenceClassification.from_pretrained('distilb
    ...: ert-base-cased')

In [21]: [name for name, _ in model.named_children()]
Out[21]: ['distilbert', 'pre_classifier', 'classifier', 'dropout']

The order of .named_children() in the above model is given as distilbert, pre_classifier, classifier, and dropout. However, if you examine the code, it is evident that dropout happens before classifier. So how do I get the order of these submodules?


Solution

  • In Pytorch, the results of print(model) or .named_children(), etc are listed based on the order they are declared in __init__ of the model's class e.g.

    Case 1

    class Model(nn.Module):
        def __init__(self):
            super().__init__()
            self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
            self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
            self.fc1 = nn.Linear(320, 50)
            self.fc2 = nn.Linear(50, 10)
            self.conv2_drop = nn.Dropout2d()
    
        def forward(self, x):
            x = F.relu(F.max_pool2d(self.conv1(x), 2))
            x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
            x = x.view(-1, 320)
            x = F.relu(self.fc1(x))
            x = F.dropout(x, p=0.6)
            x = self.fc2(x)
            return F.log_softmax(x, dim=1)
    
    model = Model()
    print(model)
    [name for name, _ in model.named_children()]
    # output
    ['conv1', 'conv2', 'fc1', 'fc2', 'conv2_drop']
    

    Case 2

    Changed order of fc1 and fc2 layers in constructor.

    class Model(nn.Module):
        def __init__(self):
            super().__init__()
            self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
            self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
            self.fc2 = nn.Linear(50, 10)
            self.fc1 = nn.Linear(320, 50)
            self.conv2_drop = nn.Dropout2d()
    
        def forward(self, x):
            x = F.relu(F.max_pool2d(self.conv1(x), 2))
            x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
            x = x.view(-1, 320)
            x = F.relu(self.fc1(x))
            x = F.dropout(x, p=0.6)
            x = self.fc2(x)
            return F.log_softmax(x, dim=1)
    
    model = Model()
    print(model)
    [name for name, _ in model.named_children()]
    # output
    ['conv1', 'conv2', 'fc2', 'fc1', 'conv2_drop']
    

    That's why classifier is printed before dropout as it's declared so in constructor:

    class DistilBertForSequenceClassification(DistilBertPreTrainedModel):
            ...
            self.distilbert = DistilBertModel(config)
            self.pre_classifier = nn.Linear(config.dim, config.dim)
            self.classifier = nn.Linear(config.dim, config.num_labels)
            self.dropout = nn.Dropout(config.seq_classif_dropout)
    

    Nevertheless, you can play with model's submodules using .modules(), etc. but they'll be listed only in the order they are declared in __init__. If you only want to print structure based on forward method, you may try using pytorch-summary.