Understanding the Conv2d groups parameter

mmitjans: <p>Hi,</p>
<p>I’m trying to build a convolutional 2-D layer for 3-channel images which applies a different convolution per channel. This brought me to investigate the <code>groups</code> parameter in <code>nn.Conv2d</code>. If I’m not mistaken, to do this I should simply create a <code>Conv2d</code> layer in a manner similar than:</p>
<p><code>conv_layer = Conv2d(3,3,(1,5),groups=3)</code></p>
<p>For a <code>1x5</code> filter. I ran a small test to confirm this idea, but I obtained different results from two outputs which I was expecting to be the same:</p>
<pre><code class="lang-auto">import torch
import torch.nn as nn

I = torch.randn(10,3,4,5)

conv1 = nn.Conv2d(3,3,(1,5),groups=3)
conv2 = nn.Conv2d(1,1,(1,5),groups=1)

with torch.no_grad():
    conv2.weight.copy_(conv1.weight[0:1])

out1 = conv1(I)[0,0,...]
out2 = conv2(I[:,0:1,...])[0,0,...]

print(torch.allclose(out1,out2))
&gt; False
</code></pre>
<p>I’m coying the weights for channel 1 of the <code>conv1</code> layer into the single-channel <code>conv2</code> one, and applying both layers to the same input <code>I</code>. What I’m comparing is the 1st batch of the 1st channel of both outputs, and ideally they should be the same.</p>
<p>Could anyone help me understand where I’m mistaken?<br>
Thanks!<br>
Marc</p>

KFrank: <p>Hi Marc!</p>
<aside class="quote no-group quote-modified" data-username="mmitjans" data-post="1" data-topic="138951" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/letter_avatar_proxy/v4/letter/m/51bf81/48.png" class="avatar"> mmitjans:</div>
<blockquote>
<p><code>conv_layer = Conv2d(3,3,(1,5),groups=3)</code></p>
<pre><code class="lang-auto">print(torch.allclose(out1,out2))
&gt; False
</code></pre>
</blockquote>
</aside>
<p>Your understanding of <code>groups</code> does seem to be correct.  However,<br>
in your test you’ve overlooked <code>Conv2d</code>’s <code>bias</code>.</p>
<p>You can either turn <code>bias</code> off (<code>bias = False</code>) or copy <code>bias</code> over<br>
from <code>conv1</code> to <code>conv2</code>, along with <code>weight</code>:</p>
<pre><code class="lang-plaintext">&gt;&gt;&gt; import torch
&gt;&gt;&gt; import torch.nn as nn
&gt;&gt;&gt;
&gt;&gt;&gt; I = torch.randn(10,3,4,5)
&gt;&gt;&gt;
&gt;&gt;&gt; conv1 = nn.Conv2d(3,3,(1,5),groups=3)
&gt;&gt;&gt; conv2 = nn.Conv2d(1,1,(1,5),groups=1)
&gt;&gt;&gt;
&gt;&gt;&gt; with torch.no_grad():
...     conv2.weight.copy_(conv1.weight[0:1])
...
Parameter containing:
tensor([[[[-0.3698,  0.0243,  0.3121,  0.0644,  0.2732]]]], requires_grad=True)
&gt;&gt;&gt; out1 = conv1(I)[0,0,...]
&gt;&gt;&gt; out2 = conv2(I[:,0:1,...])[0,0,...]
&gt;&gt;&gt;
&gt;&gt;&gt; print(torch.allclose(out1,out2))
False
&gt;&gt;&gt;
&gt;&gt;&gt; with torch.no_grad():
...      conv2.bias.copy_ (conv1.bias[0])
...
Parameter containing:
tensor([0.1049], requires_grad=True)
&gt;&gt;&gt; out1 = conv1(I)[0,0,...]
&gt;&gt;&gt; out2 = conv2(I[:,0:1,...])[0,0,...]
&gt;&gt;&gt;
&gt;&gt;&gt; print(torch.allclose(out1,out2))
True
</code></pre>
<p>Best.</p>
<p>K. Frank</p>

mmitjans: <p>That makes a lot of sense! I can’t believe I overlooked the bias. I’m still a beginner in DL and Pytorch, so I guess that explains it.</p>
<p>Thanks!</p>