I am super confused here:
In all the implemented codes in the pytorch website i see we send nn.module as an input of the class:
class VGG(nn.Module):
    def __init__(self, vgg_name):
        super(VGG, self).__init__()
        # self.features = self._make_layers(cfg[vgg_name])
        self.features = self._make_layers_Alireza(cfg[vgg_name])
        self.classifier = nn.Linear(512, 10)

    def forward(self, x):
        out = self.features(x)
        out = out.view(out.size(0), -1)
        out = self.classifier(out)
        return out

    def _make_layers(self, cfg):
        layers = []
        in_channels = 3
        for x in cfg:
            if x == 'M':
                layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
            else:
                layers += [nn.Conv2d(in_channels, x, kernel_size=3, padding=1),
                           nn.BatchNorm2d(x),
                           nn.ReLU(inplace=True)]
                in_channels = x
        layers += [nn.AvgPool2d(kernel_size=1, stride=1)]
        return nn.Sequential(*layers)
              ..
              ....

Then, when we want to give the input to the class we simply do VGG(Input)…

I dont understand how this work,
what if i wanna design a network that the forward part has two inputs?
meaning:

    def forward(self, x1,x2):
        ......
        return out

how does it understand it?

how the convolution functions understand what is the input that they should use??
when we give the conv2d real input and output it makes sense, but how it works when we just give them the size??

the nn.conv2d is explained as:

torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)

but we dont give the input to it in the class, in fact we give the size of input to it…!!
like: nn.Conv2d(in_channels, x, kernel_size=3, padding=1),(HOW is that possible???)