Convolve function channel wise over tensor

Etienne_Guichard: <p>Is there any way to convolve a function channel-wise over a tensor?<br>
I have a tensor of size <code>u = torch.size([8,16,32,32])</code> = (N,C,H,W)<br>
and trainable parameters:</p>
<pre><code class="lang-auto">mu = torch.Size([16])
sigma = torch.Size([16])

</code></pre>
<p>Batch-wise, to every channel in the tensor I want to apply the function:</p>
<pre><code class="lang-auto">    def growth_func(self, u, mu, sigma):
        return 2 * torch.exp(-(u - mu) ** 2 / (2 * sigma ** 2)) - 1
</code></pre>
<p>where every channel gets its own mu and sigma, and then convolve it over the 32x32 image size.<br>
Would I have to expand the dimensions of mu and sigma?</p>

KFrank: <p>Hi Etienne!</p>
<aside class="quote no-group" data-username="Etienne_Guichard" data-post="1" data-topic="196012" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/etienne_guichard/48/67220_2.png" class="avatar"> Etienne_Guichard:</div>
<blockquote>
<p>Is there any way to convolve a function channel-wise over a tensor?</p>
</blockquote>
</aside>
<p>I’m not sure what you mean by "“convolve.”</p>
<p>I think you are asking how to apply your function element-wise to your<br>
tensor <code>u</code>, taking into account that each of the <code>16</code> channels has its own<br>
value of <code>mu</code> and <code>sigma</code>.</p>
<p>(To me, “convolve” implies that you have a sliding window that mixes<br>
neighboring values in the tensor <code>u</code> together.)</p>
<aside class="quote no-group">
<blockquote>
<p>Batch-wise, to every channel in the tensor I want to apply the function:</p>
<pre><code class="lang-auto">    def growth_func(self, u, mu, sigma):
        return 2 * torch.exp(-(u - mu) ** 2 / (2 * sigma ** 2)) - 1
</code></pre>
<p>…<br>
Would I have to expand the dimensions of mu and sigma?</p>
</blockquote>
</aside>
<p>If I understand properly what you want to do, you do not need to<br>
<code>expand()</code> any dimensions of <code>mu</code> and <code>sigma</code>.  Instead you only need<br>
to add <em>singleton</em> dimensions (“trivial” dimensions of length one that<br>
don’t require any additional storage) to <code>mu</code> and <code>sigma</code> and let pytorch<br>
<em>broadcast</em> them over the non-channel dimensions of <code>u</code>.</p>
<p>Consider:</p>
<pre><code class="lang-plaintext">&gt;&gt;&gt; import torch
&gt;&gt;&gt; print (torch.__version__)
2.1.2
&gt;&gt;&gt;
&gt;&gt;&gt; u = torch.ones (8, 16, 32, 32)
&gt;&gt;&gt; mu = torch.arange (1.0, 17.0) / 16.0
&gt;&gt;&gt; sigma = torch.arange (1.0, 17.0) / 8.0
&gt;&gt;&gt;
&gt;&gt;&gt; mu.shape
torch.Size([16])
&gt;&gt;&gt; muB = mu[None, :, None, None]                        # add singleton dimensions for non-channel dimensions
&gt;&gt;&gt; muB.shape
torch.Size([1, 16, 1, 1])
&gt;&gt;&gt;
&gt;&gt;&gt; sigmaB = sigma[None, :, None, None]                  # add singleton dimensions for non-channel dimensions
&gt;&gt;&gt;
&gt;&gt;&gt; result = 2 * torch.exp(-(u - muB) ** 2 / (2 * sigmaB ** 2)) - 1   # broadcast along non-channel dimensions
&gt;&gt;&gt;
&gt;&gt;&gt; result.shape
torch.Size([8, 16, 32, 32])
&gt;&gt;&gt; result[0, :, 0, 0]
tensor([-1.0000, -0.9956, -0.8087, -0.3507,  0.0921,  0.4133,  0.6266,  0.7650,
         0.8543,  0.9120,  0.9490,  0.9724,  0.9867,  0.9949,  0.9989,  1.0000])
</code></pre>
<p>Best.</p>
<p>K. Frank</p>

Etienne_Guichard: <p>Thank you!<br>
This is indeed what I meant, I thought you might need to do a convolution of kernel size 1 but this seems much simpler.</p>
<p>Should I Change the title of my question to help others with the same issue, or leave it as is?</p>