Why conv2d computation results are all different even if their data types are all integer?

Cheul: <p>I’ve tried and compared three different methods for convolution computation with a custom kernel in Pytorch. Their results are different but I don’t understand why that is.</p>
<p>Setup code:</p>
<pre><code class="lang-auto">import torch
import torch.nn.functional as F

inp = torch.arange(3*500*700).reshape(1,3,500,700).to(dtype=torch.float32)
wgt = torch.ones((1,3,3,3)).to(dtype=torch.float32)
stride = 1
padding = 0
h = inp.shape[2] - wgt.shape[2] + 1
w = inp.shape[3] - wgt.shape[3] + 1
</code></pre>
<p>Method 1</p>
<pre><code class="lang-auto">out1 = torch.zeros((1,h,w)).to(dtype=torch.float32)
for o in range(1):
    for i in range(3):
        for j in range(h):
            for k in range(w):
                out1[o,j,k] = out1[o,j,k] + (inp[0, i, j*stride:j*stride+3, k*stride:k*stride+3] * wgt[0,i]).sum()
out1 = out1.to(dtype=torch.int)
</code></pre>
<p>Method 2</p>
<pre><code class="lang-auto">inp_unf = F.unfold(inp, (3,3))
out_unf = inp_unf.transpose(1,2).matmul(wgt.view(1,-1).t()).transpose(1,2)
out2 = F.fold(out_unf, (h,w), (1,1))
out2 = out2.to(dtype=torch.int)
</code></pre>
<p>Method 3</p>
<pre><code class="lang-auto">out3 = F.conv2d(inp, wgt, bias=None, stride=1, padding=0)
out3 = out3.to(dtype=torch.int)
</code></pre>
<p>And here are the results comparison:</p>
<pre><code class="lang-auto">&gt;&gt;&gt; h*w
347604

&gt;&gt;&gt; (out1==out2).sum().item()
327338
&gt;&gt;&gt; (out2 == out3).sum().item()
344026
&gt;&gt;&gt; (out1 == out3).sum().item()
330797

&gt;&gt;&gt; out1.shape
(1, 498, 698)
&gt;&gt;&gt; out2.shape 
(1, 1, 498, 698)
&gt;&gt;&gt; out3.shape
(1, 1, 498, 698)
</code></pre>
<p>Their data types are all <code>int</code> so floating point won’t the result. When I use a squared input format such as <code>h=500</code> and <code>w=500</code> , all three results are all matching. But not for non-squared inputs, such as the one above with <code>h=500</code> and <code>w=700</code> . Any insight?</p>

ptrblck: <p>It’s not completely true that you are using <code>int32</code> <code>dtypes</code> as the actual convolutions are performed in <code>float32</code>.<br>
If you check the errors you would see that you start to diverge at the higher range and in particular you are running into the expected rounding in <code>float32</code>.<br>
<a href="https://en.wikipedia.org/wiki/Single-precision_floating-point_format">This Wikipedia article on Single-precision floats</a> explains when <code>float32</code> starts to round on integer values.</p>
<p>Interesting for you would be the range in:</p>
<blockquote>
<p>Integers between <code>2**24=16777216</code> and <code>2**25=33554432</code> round to a multiple of 2 (even number)</p>
</blockquote>
<p>If you check the max. values you would see that the output fits into this range:</p>
<pre data-code-wrap="python"><code class="lang-python">print(out1[-1, -1, -1] &gt; 2**24)
# tensor(True)

print(out1[-1, -1, -1].float())
# tensor(18881046.)
print(out1[-1, -1, -1].float() + 1)
# tensor(18881048.)
</code></pre>
<p>If you use <code>float64</code> your results would match as you wouldn’t round the integer values in the output range.</p>

Cheul: <p>Thank you very much for your answer, ptrblck. This question has been bugging me for awhile haha. My suffering and pain’s gone now. Thank you again.</p>