Problem in adding new layer between a model

ANKUR_GUPTA1: <p>I wanted to add a new conv layer in middle of resnet50 I was able to add it and also I was able to get inference from the new model but the problem is that I initialized the weights of the new layer to ones so that there is no difference between the output of the new and the original model. Kernel size, weight every thing is 1 and bias is also 0.<br>
I checked the output of both the new and the original models before the layer I have added in the new model and both outputs are same.<br>
But when I passed that output to the new layer they became different. I am not sure why and not able to rectify the error.</p>
<p>I also created a new layer similar to the one that I added in the model with all same attributes and initializations and passed that output to this layer and then it remained same.<br>
I am not able to understand why this is not happening in the model. Outside the model it is working fine.</p>
<p>Could anyone please help me with this</p>

ptrblck: <p>Could you post the definition of the new conv layer, please?<br>
Note that even if you are setting the kernel size to 1 and fill all weights with 1s, the output might still differ, since the filter kernel will sum over the <code>in_channel</code> dimension by default (or are you using a depthwise conv?).</p>

ANKUR_GUPTA1: <p><code>nn.Conv2d(2048, 2048, kernel_size=[1, 1], stride=(1, 1), bias=False)</code><br>
I added this to the model with</p>
<pre><code class="lang-auto">with torch.no_grad():
  self.layer.weight.fill_(1.0)
</code></pre>
<p>in the <strong>init</strong> function after defining it</p>
<p>but the thing is that when I got output till the previous layer in both the original and the new network they were same<br>
but when I passed them through this layer it changed</p>

ANKUR_GUPTA1: <p>I used this</p>
<pre><code class="lang-auto">for k,v in model_dict.items():
  if(k in trained_original_state_dict):
    model_dict[k] = trained_original_state_dict[k]
</code></pre>
<p>to copy the weights from one model to another<br>
i.e from pretrained model to the new model</p>

ANKUR_GUPTA1: <p><a href="http://www.robots.ox.ac.uk/~vgg/data/vgg_face2/models/pytorch/resnet50_128_pytorch.tar.gz" class="onebox" target="_blank" rel="nofollow noopener">http://www.robots.ox.ac.uk/~vgg/data/vgg_face2/models/pytorch/resnet50_128_pytorch.tar.gz</a></p>
<p>I got the model from here</p>

ANKUR_GUPTA1: <p>I tried to recreate the same thing with a smaller model so I took up LeNet5</p>
<pre><code class="lang-auto">import torch
import torch.nn as nn
import torch.nn.functional as F
class LeNet5(nn.Module):

    def __init__(self, n_classes):
        super(LeNet5, self).__init__()
        
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, stride=1)
        self.ac1 = nn.Tanh()
        self.pool1 = nn.AvgPool2d(kernel_size=2)
        self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5, stride=1)
        self.ac2 = nn.Tanh()
        self.pool2 = nn.AvgPool2d(kernel_size=2)
        self.conv3 = nn.Conv2d(in_channels=16, out_channels=120, kernel_size=5, stride=1)
        self.ac3 = nn.Tanh()
        self.fc1 = nn.Linear(in_features=120, out_features=84)
        self.ac4 = nn.Tanh()
        self.fc2 = nn.Linear(in_features=84, out_features=n_classes)


    def forward(self, x):
        x = self.conv1(x)
        x = self.ac1(x)
        x = self.pool1(x)
        x = self.conv2(x)
        x = self.ac2(x)
        x = self.pool2(x)
        x = self.conv3(x)
        x = self.ac3(x)
        x = torch.flatten(x, 1)
        x = self.fc1(x)
        x = self.ac4(x)
        x = self.fc2(x)
        return x

my_model = LeNet5(5)
ran = torch.randn(1,1,32,32)
my_model(ran)
my_model_state_dict = my_model.state_dict()
</code></pre>
<p>In the second model I added conv4 layer</p>
<pre><code class="lang-auto">class LeNet5_new(nn.Module):

    def __init__(self, n_classes):
        super(LeNet5_new, self).__init__()
        
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, stride=1)
        self.ac1 = nn.Tanh()
        self.pool1 = nn.AvgPool2d(kernel_size=2)
        self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5, stride=1)
        self.ac2 = nn.Tanh()
        self.pool2 = nn.AvgPool2d(kernel_size=2)
        self.conv3 = nn.Conv2d(in_channels=16, out_channels=120, kernel_size=5, stride=1)
        self.ac3 = nn.Tanh()
        self.conv4 = nn.Conv2d(in_channels=120, out_channels=120, kernel_size=1, stride=1,bias=0)
        self.fc1 = nn.Linear(in_features=120, out_features=84)
        self.ac4 = nn.Tanh()
        self.fc2 = nn.Linear(in_features=84, out_features=n_classes)

        with torch.no_grad():
          self.conv4.weight.fill_(1.0)


    def forward(self, x):
        x = self.conv1(x)
        x = self.ac1(x)
        x = self.pool1(x)
        x = self.conv2(x)
        x = self.ac2(x)
        x = self.pool2(x)
        x = self.conv3(x)
        x = self.ac3(x)
        x = self.conv4(x)
        x = torch.flatten(x, 1)
        x = self.fc1(x)
        x = self.ac4(x)
        x = self.fc2(x)
        return x

my_model_new = LeNet5_new(5)

my_model_new_state_dict = my_model_new.state_dict()

# Below is the code to copy the weights from old state-dict to the new one
for k,v in my_model_new_state_dict.items():
  if(k in my_model_state_dict):
    my_model_new_state_dict[k] = my_model_state_dict[k]

# my_model_new_state_dict['conv4.weight']  # Checked weights are all 1s for that layer

# Loaded the State-dict
my_model_new.load_state_dict(my_model_new_state_dict)

# Passed through the same output as earlier model but found them to be different
my_model_new(ran)
</code></pre>

ANKUR_GUPTA1: <p>I also added hooks to see if the output till <code>ac3</code>  is same or not for both the models and it turned out to be same.<br>
but when we pass through that particular layer then the change happens.</p>
<p>I also tried to see if it happens when I create a layer seperately</p>
<pre><code class="lang-auto">md = nn.Conv2d(1, 1, kernel_size=[1, 1], stride=(1, 1), bias=False)

with torch.no_grad():
  md.weight.fill_(1.0)
</code></pre>
<p>created random input</p>
<pre><code class="lang-auto">inp = torch.randn(1, 1, 2, 2)
inp
</code></pre>
<pre><code class="lang-auto">tensor([[[[-0.5713,  0.0483],
          [ 0.3396,  0.9315]]]])
</code></pre>
<p>passed through that layer</p>
<pre><code class="lang-auto">ap = md(inp)
ap
</code></pre>
<pre><code class="lang-auto">tensor([[[[-0.5713,  0.0483],
          [ 0.3396,  0.9315]]]], grad_fn=&lt;MkldnnConvolutionBackward&gt;)
</code></pre>
<p>These were same.<br>
There was no effect on passing through that layer.</p>
<p>How can I achieve the same thing when I add it into the model</p>

ANKUR_GUPTA1: <p>Ok I get it now.<br>
I think if we have the weights as like one hot encodings then we can get the output same as input as it will give only the specific channel and we can do this for all the channels</p>

ptrblck: <p>The problem with your small example is that you are using <code>in_channels=out_channels=1</code>, which would return the same output.<br>
However, as explained in the previous post, this won’t be the case for multiple input channels, since the convolution will sum over the <code>in_channels</code> dimension:</p>
<pre><code class="lang-python">md = nn.Conv2d(3, 3, kernel_size=[1, 1], stride=(1, 1), bias=False)
with torch.no_grad():
  md.weight.fill_(1.0)

x = torch.ones(1, 3, 4, 4)
out = md(x)
print(out)
&gt; tensor([[[[3., 3., 3., 3.],
          [3., 3., 3., 3.],
          [3., 3., 3., 3.],
          [3., 3., 3., 3.]],

         [[3., 3., 3., 3.],
          [3., 3., 3., 3.],
          [3., 3., 3., 3.],
          [3., 3., 3., 3.]],

         [[3., 3., 3., 3.],
          [3., 3., 3., 3.],
          [3., 3., 3., 3.],
          [3., 3., 3., 3.]]]], grad_fn=&lt;MkldnnConvolutionBackward&gt;)
</code></pre>
<p>You could use a grouped convolution instead (but it might not fit your use case):</p>
<pre><code class="lang-python">md = nn.Conv2d(3, 3, kernel_size=[1, 1], stride=(1, 1), bias=False, groups=3)
</code></pre>