Here is the Transpose version:
# Cell to experiment with padding on Conv2DTranspose
print("padding = 'valid' stride = 1")
playmodel = tf.keras.Sequential()
playmodel.add(Conv2DTranspose(filters = 3, kernel_size = 3, strides = 1, padding = 'valid'))
for playimage, _ in processed_image_ds.take(1):
playimage = tf.expand_dims(playimage, 0)
print(f"playimage.shape {playimage.shape}")
playout = playmodel(playimage)
print(f"playout.shape {playout.shape}")
print("padding = 'valid' stride = 2")
playmodel = tf.keras.Sequential()
playmodel.add(Conv2DTranspose(filters = 3, kernel_size = 3, strides = 2, padding = 'valid'))
for playimage, _ in processed_image_ds.take(1):
playimage = tf.expand_dims(playimage, 0)
print(f"playimage.shape {playimage.shape}")
playout = playmodel(playimage)
print(f"playout.shape {playout.shape}")
print("padding = 'same' stride = 1")
playmodel = tf.keras.Sequential()
playmodel.add(Conv2DTranspose(filters = 3, kernel_size = 3, strides = 1, padding = 'same'))
for playimage, _ in processed_image_ds.take(1):
playimage = tf.expand_dims(playimage, 0)
print(f"playimage.shape {playimage.shape}")
playout = playmodel(playimage)
print(f"playout.shape {playout.shape}")
print("padding = 'same' stride = 2")
playmodel = tf.keras.Sequential()
playmodel.add(Conv2DTranspose(filters = 3, kernel_size = 3, strides = 2, padding = 'same'))
for playimage, _ in processed_image_ds.take(1):
playimage = tf.expand_dims(playimage, 0)
print(f"playimage.shape {playimage.shape}")
playout = playmodel(playimage)
print(f"playout.shape {playout.shape}")
print("padding = 'same' stride = 3")
playmodel = tf.keras.Sequential()
playmodel.add(Conv2DTranspose(filters = 3, kernel_size = 3, strides = 3, padding = 'same'))
for playimage, _ in processed_image_ds.take(1):
playimage = tf.expand_dims(playimage, 0)
print(f"playimage.shape {playimage.shape}")
playout = playmodel(playimage)
print(f"playout.shape {playout.shape}")
And here is the output from running the above:
padding = 'valid' stride = 1
playimage.shape (1, 96, 128, 3)
playout.shape (1, 98, 130, 3)
padding = 'valid' stride = 2
playimage.shape (1, 96, 128, 3)
playout.shape (1, 193, 257, 3)
padding = 'same' stride = 1
playimage.shape (1, 96, 128, 3)
playout.shape (1, 96, 128, 3)
padding = 'same' stride = 2
playimage.shape (1, 96, 128, 3)
playout.shape (1, 192, 256, 3)
padding = 'same' stride = 3
playimage.shape (1, 96, 128, 3)
playout.shape (1, 288, 384, 3)
My current understanding is that the size formula is:
n_{out} = (n_{in} - 1) * s + f - 2p
I’m not sure I believe that based on the numbers I’m seeing above. More research required …
But the bottom line here is that it is the same story as with forward convolutions: “same” only means “same” if the stride is 1. Otherwise it is not the same.
Update: We later got an explanation for the ambuigities in the formula for computing the output size of a transpose convolution. Here’s a thread from Raymond which gives the explanation.