<p>Here's another viewpoint from the "gradients" perspective, i.e. why TensorFlow documentation says <code>conv2d_transpose()</code> is "actually the transpose (<strong>gradient</strong>) of conv2d rather than an actual deconvolution". <strong>For more details on the actual computation done in <code>conv2d_transpose</code>, I would highly recommend <a href="https://arxiv.org/pdf/1603.07285.pdf" rel="noreferrer">this article</a>, starting from page 19.</strong></p>

<h3>Four Related Functions</h3>

<p>In <code>tf.nn</code>, there are 4 closely related and rather confusing functions for 2d convolution:</p>

<ul>
<li><code>tf.nn.conv2d</code></li>
<li><code>tf.nn.conv2d_backprop_filter</code></li>
<li><code>tf.nn.conv2d_backprop_input</code></li>
<li><code>tf.nn.conv2d_transpose</code></li>
</ul>

<p>One sentence summary: <strong>they are all just 2d convolutions</strong>. Their differences are in their input arguments ordering, input rotation or transpose, strides (including fractional stride size), paddings and etc. With <code>tf.nn.conv2d</code> in hand, one can implement all of the 3 other ops by transforming inputs and changing the <code>conv2d</code> arguments.</p>

<h3>Problem Settings</h3>

<ul>
<li>Forward and backward computations:</li>
</ul>



<pre class="lang-py prettyprint-override"><code># forward
out = conv2d(x, w)

# backward, given d_out
=&gt; find d_x?
=&gt; find d_w?
</code></pre>

<p>In the forward computation, we compute the convolution of input image <code>x</code> with the filter <code>w</code>, and the result is <code>out</code>.
In the backward computation, assume we're given <code>d_out</code>, which is the gradient w.r.t. <code>out</code>. Our goal is to find <code>d_x</code> and <code>d_w</code>, which are the gradient w.r.t. <code>x</code> and <code>w</code> respectively.</p>

<p>For the ease of discussion, we assume:</p>

<ul>
<li>All stride size to be <code>1</code></li>
<li>All <code>in_channels</code> and <code>out_channels</code> are <code>1</code></li>
<li>Use <code>VALID</code> padding</li>
<li>Odd number filter size, this avoids some asymmetric shape problem</li>
</ul>

<h3>Short Answer</h3>

<p>Conceptually, with the assumptions above, we have the following relations:</p>

<pre class="lang-py prettyprint-override"><code>out = conv2d(x, w, padding='VALID')
d_x = conv2d(d_out, rot180(w), padding='FULL')
d_w = conv2d(x, d_out, padding='VALID')
</code></pre>

<p>Where <code>rot180</code> is a 2d matrix rotated 180 degrees (a left-right flip and a top-down flip), <code>FULL</code> means "apply filter wherever it partly overlaps with the input" (see <a href="http://deeplearning.net/software/theano/library/tensor/nnet/conv.html#theano.tensor.nnet.conv2d" rel="noreferrer">theano docs</a>). Notes that <em>this is only valid with the above assumptions</em>, however, one can change the conv2d arguments to generalize it.</p>

<p>The key takeaways:</p>

<ul>
<li>The input gradient <code>d_x</code> is the convolution of the output gradient <code>d_out</code> and the weight <code>w</code>, with some modifications.</li>
<li>The weight gradient <code>d_w</code> is the convolution of the input <code>x</code> and the output gradient <code>d_out</code>, with some modifications.</li>
</ul>

<h3>Long Answer</h3>

<p>Now, let's give an actual working code example of how to use the 4 functions above to compute <code>d_x</code> and <code>d_w</code> given <code>d_out</code>. This shows how
<code>conv2d</code>,
<code>conv2d_backprop_filter</code>,
<code>conv2d_backprop_input</code>, and
<code>conv2d_transpose</code> are related to each other. 
<a href="https://gist.github.com/yxlao/ef50416011b9587835ac752aa3ce3530" rel="noreferrer">Please find the full scripts here</a>. </p>

<p><strong>Computing <code>d_x</code> in 4 different ways:</strong></p>

<pre class="lang-py prettyprint-override"><code># Method 1: TF's autodiff
d_x = tf.gradients(f, x)[0]

# Method 2: manually using conv2d
d_x_manual = tf.nn.conv2d(input=tf_pad_to_full_conv2d(d_out, w_size),
                          filter=tf_rot180(w),
                          strides=strides,
                          padding='VALID')

# Method 3: conv2d_backprop_input
d_x_backprop_input = tf.nn.conv2d_backprop_input(input_sizes=x_shape,
                                                 filter=w,
                                                 out_backprop=d_out,
                                                 strides=strides,
                                                 padding='VALID')

# Method 4: conv2d_transpose
d_x_transpose = tf.nn.conv2d_transpose(value=d_out,
                                       filter=w,
                                       output_shape=x_shape,
                                       strides=strides,
                                       padding='VALID')
</code></pre>

<p><strong>Computing <code>d_w</code> in 3 different ways:</strong></p>

<pre class="lang-py prettyprint-override"><code># Method 1: TF's autodiff
d_w = tf.gradients(f, w)[0]

# Method 2: manually using conv2d
d_w_manual = tf_NHWC_to_HWIO(tf.nn.conv2d(input=x,
                                          filter=tf_NHWC_to_HWIO(d_out),
                                          strides=strides,
                                          padding='VALID'))

# Method 3: conv2d_backprop_filter
d_w_backprop_filter = tf.nn.conv2d_backprop_filter(input=x,
                                                   filter_sizes=w_shape,
                                                   out_backprop=d_out,
                                                   strides=strides,
                                                   padding='VALID')
</code></pre>

<p>Please see the <a href="https://gist.github.com/yxlao/ef50416011b9587835ac752aa3ce3530" rel="noreferrer">full scripts</a> for the implementation of <code>tf_rot180</code>, <code>tf_pad_to_full_conv2d</code>, <code>tf_NHWC_to_HWIO</code>. In the scripts, we check that the final output values of different methods are the same; a numpy implementation is also available. </p>
What does TensorFlow's `conv2d_transpose()` operation do?
<p>The documentation for the <code>conv2d_transpose()</code> operation does not clearly explain what it does:</p>

<blockquote>
  <p>The transpose of conv2d.</p>
  
  <p>This operation is sometimes called "deconvolution" after
  <a href="http://www.matthewzeiler.com/pubs/cvpr2010/cvpr2010.pdf" rel="noreferrer">Deconvolutional Networks</a>, but is actually the transpose (gradient) of
  conv2d rather than an actual deconvolution.</p>
</blockquote>

<p>I went through the paper that the doc points to, but it did not help.</p>

<p>What does this operation do and what are examples of why you would want to use it?</p>