Skip to content

Commit

Permalink
Check if stride is greater than output padding
Browse files Browse the repository at this point in the history
  • Loading branch information
davidtvs committed May 28, 2018
1 parent 8a5d4bc commit 6b99d23
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 32 deletions.
24 changes: 20 additions & 4 deletions keras/layers/convolutional.py
Expand Up @@ -651,8 +651,11 @@ class Conv2DTranspose(Conv2D):
padding: one of `"valid"` or `"same"` (case-insensitive).
output_padding: An integer or tuple/list of 2 integers,
specifying the amount of padding along the height and width
of the output tensor. Can be a single integer to specify the
same value for all spatial dimensions.
of the output tensor.
Can be a single integer to specify the same value for all
spatial dimensions.
The amount of output padding along a givern dimension must be
lower than the stride along that same dimension.
If set to `None`, defaults to 1 if padding is `"same"` and to
0 if padding is '"valid"'.
data_format: A string,
Expand Down Expand Up @@ -749,9 +752,15 @@ def __init__(self, filters,
bias_constraint=bias_constraint,
**kwargs)
self.input_spec = InputSpec(ndim=4)

self.output_padding = output_padding
if self.output_padding is not None:
self.output_padding = conv_utils.normalize_tuple(self.output_padding, 2, 'output_padding')
for stride, out_pad in zip(self.strides, self.output_padding):
if out_pad >= stride:
raise ValueError('Stride ' + str(self.strides) + ' must be ' +
'greater than output padding ' +
str(self.output_padding))

def build(self, input_shape):
if len(input_shape) != 4:
Expand Down Expand Up @@ -901,8 +910,10 @@ class Conv3DTranspose(Conv3D):
output_padding: An integer or tuple/list of 3 integers,
specifying the amount of padding along the depth, height, and
width.
Can be a single integer to specify the same value for
all spatial dimensions.
Can be a single integer to specify the same value for all
spatial dimensions.
The amount of output padding along a givern dimension must be
lower than the stride along that same dimension.
If set to `None`, defaults to 1 if padding is `"same"` and to
0 if padding is '"valid"'.
data_format: A string,
Expand Down Expand Up @@ -1001,6 +1012,11 @@ def __init__(self, filters,
self.output_padding = output_padding
if self.output_padding is not None:
self.output_padding = conv_utils.normalize_tuple(self.output_padding, 3, 'output_padding')
for stride, out_pad in zip(self.strides, self.output_padding):
if out_pad >= stride:
raise ValueError('Stride ' + str(self.strides) + ' must be ' +
'greater than output padding ' +
str(self.output_padding))

def build(self, input_shape):
if len(input_shape) != 5:
Expand Down
96 changes: 68 additions & 28 deletions tests/keras/layers/convolutional_test.py
Expand Up @@ -194,11 +194,13 @@ def test_conv2d_transpose():
num_col = 6

for padding in _convolution_paddings:
for out_padding in [None, (1, 1)]:
for out_padding in [None, (0, 0), (1, 1)]:
for strides in [(1, 1), (2, 2)]:
if padding == 'same' and strides != (1, 1):
continue
layer_test(convolutional.Deconvolution2D,
if strides == (1, 1) and out_padding == (1, 1):
continue
layer_test(convolutional.Conv2DTranspose,
kwargs={'filters': filters,
'kernel_size': 3,
'padding': padding,
Expand All @@ -208,7 +210,7 @@ def test_conv2d_transpose():
input_shape=(num_samples, num_row, num_col, stack_size),
fixed_batch_size=True)

layer_test(convolutional.Deconvolution2D,
layer_test(convolutional.Conv2DTranspose,
kwargs={'filters': filters,
'kernel_size': 3,
'padding': padding,
Expand All @@ -230,6 +232,24 @@ def test_conv2d_transpose():
padding=padding,
batch_input_shape=(None, None, 5, None))])

# Test invalid output padding for given stride. Output padding equal
# to stride
with pytest.raises(ValueError):
model = Sequential([convolutional.Conv2DTranspose(filters=filters,
kernel_size=3,
padding=padding,
output_padding=(0, 3),
strides=(1, 3),
batch_input_shape=(None, num_row, num_col, stack_size))])
# Output padding greater than stride
with pytest.raises(ValueError):
model = Sequential([convolutional.Conv2DTranspose(filters=filters,
kernel_size=3,
padding=padding,
output_padding=(2, 2),
strides=(1, 3),
batch_input_shape=(None, num_row, num_col, stack_size))])


@keras_test
def test_separable_conv_1d():
Expand Down Expand Up @@ -461,20 +481,18 @@ def test_convolution_3d():
input_len_dim3 = 8

for padding in _convolution_paddings:
for out_padding in [None, (1, 1, 1)]:
for strides in [(1, 1, 1), (2, 2, 2)]:
if padding == 'same' and strides != (1, 1, 1):
continue
for strides in [(1, 1, 1), (2, 2, 2)]:
if padding == 'same' and strides != (1, 1, 1):
continue

layer_test(convolutional.Convolution3D,
kwargs={'filters': filters,
'kernel_size': 3,
'padding': padding,
'output_padding': out_padding,
'strides': strides},
input_shape=(num_samples,
input_len_dim1, input_len_dim2, input_len_dim3,
stack_size))
layer_test(convolutional.Convolution3D,
kwargs={'filters': filters,
'kernel_size': 3,
'padding': padding,
'strides': strides},
input_shape=(num_samples,
input_len_dim1, input_len_dim2, input_len_dim3,
stack_size))

layer_test(convolutional.Convolution3D,
kwargs={'filters': filters,
Expand All @@ -501,18 +519,22 @@ def test_conv3d_transpose():
num_col = 6

for padding in _convolution_paddings:
for strides in [(1, 1, 1), (2, 2, 2)]:
for data_format in ['channels_first', 'channels_last']:
if padding == 'same' and strides != (1, 1, 1):
continue
layer_test(convolutional.Conv3DTranspose,
kwargs={'filters': filters,
'kernel_size': 3,
'padding': padding,
'strides': strides,
'data_format': data_format},
input_shape=(None, num_depth, num_row, num_col, stack_size),
fixed_batch_size=True)
for out_padding in [None, (0, 0, 0), (1, 1, 1)]:
for strides in [(1, 1, 1), (2, 2, 2)]:
for data_format in ['channels_first', 'channels_last']:
if padding == 'same' and strides != (1, 1, 1):
continue
if strides == (1, 1, 1) and out_padding == (1, 1, 1):
continue
layer_test(convolutional.Conv3DTranspose,
kwargs={'filters': filters,
'kernel_size': 3,
'padding': padding,
'output_padding': out_padding,
'strides': strides,
'data_format': data_format},
input_shape=(None, num_depth, num_row, num_col, stack_size),
fixed_batch_size=True)

layer_test(convolutional.Conv3DTranspose,
kwargs={'filters': filters,
Expand All @@ -536,6 +558,24 @@ def test_conv3d_transpose():
padding=padding,
batch_input_shape=(None, None, 5, None, None))])

# Test invalid output padding for given stride. Output padding equal
# to stride
with pytest.raises(ValueError):
model = Sequential([convolutional.Conv3DTranspose(filters=filters,
kernel_size=3,
padding=padding,
output_padding=(0, 3, 3),
strides=(1, 3, 4),
batch_input_shape=(None, num_depth, num_row, num_col, stack_size))])
# Output padding greater than stride
with pytest.raises(ValueError):
model = Sequential([convolutional.Conv3DTranspose(filters=filters,
kernel_size=3,
padding=padding,
output_padding=(2, 2, 3),
strides=(1, 3, 4),
batch_input_shape=(None, num_depth, num_row, num_col, stack_size))])


@keras_test
def test_maxpooling_3d():
Expand Down

0 comments on commit 6b99d23

Please sign in to comment.