torch.nn.MaxPool2d does not need "input" argument run but quantized function torch.nn.quantized.functional.max_pool2d must have it

parth15041995: <p>I am trying to quantize a salient object detection model.<br>
Originally, my ResNet class would look like:</p>
<pre><code class="lang-auto">import torch.nn as nn
import torch.nn.quantized as nnq
import torch.nn.functional as F
import torch.nn.quantized.functional as qF
</code></pre>
<pre><code class="lang-auto">class ResNet(nn.Module):
    def __init__(self, block, layers):
        self.inplanes = 64
        super(ResNet, self).__init__()
        self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False)
        self.bn1 = nn.BatchNorm2d(64,affine = affine_par)
        for i in self.bn1.parameters():
            i.requires_grad = False
        self.relu = nn.ReLU(inplace=True)
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1, ceil_mode=True) # changed to Quanti

        self.layer1 = self._make_layer(block, 64, layers[0])
        self.layer2 = self._make_layer(block, 128, layers[1], stride=2)
        self.layer3 = self._make_layer(block, 256, layers[2], stride=2)
        self.layer4 = self._make_layer(block, 512, layers[3], stride=1, dilation__ = 2)
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
                m.weight.data.normal_(0, 0.01)
            elif isinstance(m, nn.BatchNorm2d):
                m.weight.data.fill_(1)
                m.bias.data.zero_()
    def _make_layer(self, block, planes, blocks, stride=1,dilation__ = 1):
        downsample = None
        if stride != 1 or self.inplanes != planes * block.expansion or dilation__ == 2 or dilation__ == 4:
            downsample = nn.Sequential(
                nn.Conv2d(self.inplanes, planes * block.expansion,
                          kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(planes * block.expansion,affine = affine_par),

            )
        for i in downsample._modules['1'].parameters():
            i.requires_grad = False
        layers = []
        layers.append(block(self.inplanes, planes, stride,dilation_=dilation__, downsample = downsample ))
        self.inplanes = planes * block.expansion
        for i in range(1, blocks):
            layers.append(block(self.inplanes, planes,dilation_=dilation__))
        return nn.Sequential(*layers)
    def forward(self, x):
        tmp_x = []
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        tmp_x.append(x)
        x = self.maxpool(x)
        x = self.layer1(x)
        tmp_x.append(x)
        x = self.layer2(x)
        tmp_x.append(x)
        x = self.layer3(x)
        tmp_x.append(x)
        x = self.layer4(x)
        tmp_x.append(x)
        return tmp_x
</code></pre>
<p>And it works just fine. But if I replace everything with quantized functions like:</p>
<pre><code class="lang-auto">
class ResNet(nn.Module):
    def __init__(self, block, layers):
        self.inplanes = 64
        super(ResNet, self).__init__()
        self.conv1 = nnq.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False)
        self.bn1 = nnq.BatchNorm2d(64) #,affine = affine_par
        for i in self.bn1.parameters():
            i.requires_grad = False
        self.relu = nnq.ReLU(inplace=False)

        #self.maxpool = F.nn.MaxPool2d(kernel_size=3, stride=2, padding=1, ceil_mode=True) # change 

        self.maxpool = qF.max_pool2d(x = ??? ,kernel_size=3, stride=2, padding=1, ceil_mode=True)
        self.layer1 = self._make_layer(block, 64, layers[0])
        self.layer2 = self._make_layer(block, 128, layers[1], stride=2)
        self.layer3 = self._make_layer(block, 256, layers[2], stride=2)
        self.layer4 = self._make_layer(block, 512, layers[3], stride=1, dilation__ = 2)

        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
                m.weight.data.normal_(0, 0.01)
            elif isinstance(m, nn.BatchNorm2d):
                m.weight.data.fill_(1)
                m.bias.data.zero_()

    def _make_layer(self, block, planes, blocks, stride=1,dilation__ = 1):

        downsample = None
        if stride != 1 or self.inplanes != planes * block.expansion or dilation__ == 2 or dilation__ == 4:
            downsample = nn.Sequential(
                nnq.Conv2d(self.inplanes, planes * block.expansion,
                          kernel_size=1, stride=stride, bias=False),
                nnq.BatchNorm2d(planes * block.expansion), #,affine = affine_par
            )

        for i in downsample._modules['1'].parameters():
            i.requires_grad = False
        layers = []
        layers.append(block(self.inplanes, planes, stride,dilation_=dilation__, downsample = downsample ))
        self.inplanes = planes * block.expansion
        for i in range(1, blocks):
            layers.append(block(self.inplanes, planes,dilation_=dilation__))
        return nn.Sequential(*layers)
    def forward(self, x):
        tmp_x = []
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        tmp_x.append(x)

        x = qF.max_pool2d(x,kernel_size=3, stride=2, padding=1, ceil_mode=True)  #this certainly will not create a MaxPool layer.

        #x = self.maxpool(x)

        x = self.layer1(x)
        tmp_x.append(x)
        x = self.layer2(x)
        tmp_x.append(x)
        x = self.layer3(x)
        tmp_x.append(x)
        x = self.layer4(x)
        tmp_x.append(x)
        return tmp_x
</code></pre>
<p>Error:<br>
<code>TypeError: max_pool2d() missing 1 required positional argument: 'input'</code></p>
<p>So, I think the problem is in torch.nn.MaxPool2d does not need any input argument but torch.nn.quantized.functional.max_pool2d needs an input argument. DOES ANYONE KNOW ANY WAY ROUND? How can I successfully quantize like this one other custom classes?</p>

parth15041995: <p><a href="https://discuss.pytorch.org/t/quantized-maxpool2d-and-adaptiveavgpool2d/83314">quantized maxpool2d and adaptiveavgpool2d</a>  should not be defined in the quantizable version.</p>

Zafar: <p>I don’t think there is such thing as <code>F.nn.MaxPool2d</code> – <code>F</code>, which is an alias to <code>functional</code> in your case does not have stateful layers. However, in your case you are treating it as if it did. In both models you need to replace the max pooling definition to <code>nn.MaxPool2d</code>.</p>
<p>Also, in the second case, you cannot call <code>qF.max_pool2d</code> in the constructor. As mentioned before, the <code>F</code> and <code>qF</code> namespaces are stateless, meaning they operate directly on the input, and don’t require instance construction. The way you use it is:</p>
<ol>
<li>Remove <code>self.max_pool = F.max_pool2d(...)</code> and <code>self.max_pool = qF.max_pool2d(...)</code> from the constructors.</li>
<li>Add them in the <code>forward</code> method, but without the <code>self.... =</code> part. Like this: <code>max_pool_output = qF.max_pool2d(some_input, *some_arguments)</code>
</li>
</ol>