Skip to content

Commit 253f618

Browse files
cjldwanghaoshuang
authored andcommitted
loosen the restriction of output_size in conv2d_transpose (#12292)
* loosen the restriction of output_size in conv2d_transpose * test and docs * fix code style * fix ci test error * bug fix * fix python3 issue
1 parent cec94ca commit 253f618

File tree

3 files changed

+70
-9
lines changed

3 files changed

+70
-9
lines changed

paddle/fluid/operators/conv_transpose_op.cc

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ void ConvTransposeOp::InferShape(framework::InferShapeContext* ctx) const {
2929

3030
auto in_dims = ctx->GetInputDim("Input");
3131
auto filter_dims = ctx->GetInputDim("Filter");
32+
std::vector<int> output_size =
33+
ctx->Attrs().Get<std::vector<int>>("output_size");
3234
std::vector<int> strides = ctx->Attrs().Get<std::vector<int>>("strides");
3335
std::vector<int> paddings = ctx->Attrs().Get<std::vector<int>>("paddings");
3436
std::vector<int> dilations = ctx->Attrs().Get<std::vector<int>>("dilations");
@@ -42,6 +44,10 @@ void ConvTransposeOp::InferShape(framework::InferShapeContext* ctx) const {
4244
PADDLE_ENFORCE(in_dims.size() - strides.size() == 2U,
4345
"ConvTransposeOp input dimension and strides dimension should "
4446
"be consistent.");
47+
if (output_size.size())
48+
PADDLE_ENFORCE_EQ(output_size.size(), strides.size(),
49+
"ConvTransposeOp output_size dimension and strides "
50+
"dimension should be the same.");
4551
PADDLE_ENFORCE_EQ(paddings.size(), strides.size(),
4652
"ConvTransposeOp paddings dimension and strides "
4753
"dimension should be the same.");
@@ -55,8 +61,17 @@ void ConvTransposeOp::InferShape(framework::InferShapeContext* ctx) const {
5561
std::vector<int64_t> output_shape({in_dims[0], filter_dims[1] * groups});
5662
for (size_t i = 0; i < strides.size(); ++i) {
5763
auto filter_extent = dilations[i] * (filter_dims[i + 2] - 1) + 1;
58-
output_shape.push_back((in_dims[i + 2] - 1) * strides[i] - 2 * paddings[i] +
59-
filter_extent);
64+
auto infer_shape =
65+
(in_dims[i + 2] - 1) * strides[i] - 2 * paddings[i] + filter_extent;
66+
if (output_size.size()) {
67+
PADDLE_ENFORCE((output_size[i] >= infer_shape &&
68+
output_size[i] < infer_shape + strides[i]),
69+
"ConvTransposeOp output_size should be "
70+
"in appropriate range.");
71+
output_shape.push_back(output_size[i]);
72+
} else {
73+
output_shape.push_back(infer_shape);
74+
}
6075
}
6176
ctx->SetOutputDim("Output", framework::make_ddim(output_shape));
6277
}
@@ -103,6 +118,10 @@ void Conv2DTransposeOpMaker::Make() {
103118
AddOutput("Output",
104119
"(Tensor) The output tensor of convolution transpose operator. "
105120
"The format of output tensor is also NCHW.");
121+
AddAttr<std::vector<int>>("output_size",
122+
"(vector<int> default: []), the "
123+
"size of the output tensor")
124+
.SetDefault({});
106125
AddAttr<int>("groups",
107126
"(int default:1), the groups number of the convolution "
108127
"transpose operator. ")
@@ -192,7 +211,10 @@ void Conv3DTransposeOpMaker::Make() {
192211
"Where N is batch size, C is "
193212
"the number of channels, D is the depth of the feature, H is the "
194213
"height of the feature, and W is the width of the feature.");
195-
214+
AddAttr<std::vector<int>>("output_size",
215+
"(vector<int> default: []), the "
216+
"size of the output tensor")
217+
.SetDefault({});
196218
AddAttr<std::vector<int>>(
197219
"dilations",
198220
"(vector<int> default:{1, 1, 1}), the "
@@ -247,7 +269,7 @@ Parameters(strides, paddings) are three elements. These three elements represent
247269
depth, height and width, respectively.
248270
The input(X) size and output(Out) size may be different.
249271
250-
Example:
272+
Example:
251273
Input:
252274
Input shape: $(N, C_{in}, D_{in}, H_{in}, W_{in})$
253275
Filter shape: $(C_{in}, C_{out}, D_f, H_f, W_f)$

python/paddle/fluid/layers/nn.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2342,16 +2342,20 @@ def conv2d_transpose(input,
23422342
23432343
.. math::
23442344
2345-
H_{out} &= (H_{in} - 1) * strides[0] - 2 * paddings[0] + dilations[0] * (H_f - 1) + 1 \\\\
2346-
W_{out} &= (W_{in} - 1) * strides[1] - 2 * paddings[1] + dilations[1] * (W_f - 1) + 1
2345+
H^\prime_{out} &= (H_{in} - 1) * strides[0] - 2 * paddings[0] + dilations[0] * (H_f - 1) + 1 \\\\
2346+
W^\prime_{out} &= (W_{in} - 1) * strides[1] - 2 * paddings[1] + dilations[1] * (W_f - 1) + 1 \\\\
2347+
H_{out} \in [ H^\prime_{out}, H^\prime_{out} + strides[0] ) \\\\
2348+
W_{out} \in [ W^\prime_{out}, W^\prime_{out} + strides[1] )
23472349
23482350
Args:
23492351
input(Variable): The input image with [N, C, H, W] format.
23502352
num_filters(int): The number of the filter. It is as same as the output
23512353
image channel.
23522354
output_size(int|tuple|None): The output image size. If output size is a
2353-
tuple, it must contain two integers, (image_H, image_W). This
2354-
parameter only works when filter_size is None.
2355+
tuple, it must contain two integers, (image_H, image_W). None if use
2356+
filter_size, padding, and stride to calculate output_size.
2357+
if output_size and filter_size are specified at the same time, They
2358+
should follow the formula above.
23552359
filter_size(int|tuple|None): The filter size. If filter_size is a tuple,
23562360
it must contain two integers, (filter_size_H, filter_size_W).
23572361
Otherwise, the filter will be a square. None if use output size to
@@ -2429,7 +2433,13 @@ def conv2d_transpose(input,
24292433
else:
24302434
filter_size = utils.convert_to_list(filter_size, 2,
24312435
'conv2d_transpose.filter_size')
2432-
2436+
if output_size is None:
2437+
output_size = []
2438+
elif isinstance(output_size, list) or isinstance(output_size, int):
2439+
output_size = utils.convert_to_list(output_size, 2, 'output_size')
2440+
else:
2441+
raise ValueError("output_size should be list or int")
2442+
padding = utils.convert_to_list(padding, 2, 'padding')
24332443
groups = 1 if groups is None else groups
24342444
filter_shape = [input_channel, num_filters // groups] + filter_size
24352445
img_filter = helper.create_parameter(
@@ -2442,6 +2452,7 @@ def conv2d_transpose(input,
24422452
'Filter': [img_filter]},
24432453
outputs={'Output': pre_bias},
24442454
attrs={
2455+
'output_size': output_size,
24452456
'strides': stride,
24462457
'paddings': padding,
24472458
'dilations': dilation,

python/paddle/fluid/tests/unittests/test_conv2d_transpose_op.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ def conv2dtranspose_forward_naive(input_, filter_, attrs):
3535
d_bolck_w = dilations[1] * (f_w - 1) + 1
3636
out_h = (in_h - 1) * stride[0] + d_bolck_h
3737
out_w = (in_w - 1) * stride[1] + d_bolck_w
38+
if 'output_size' in attrs:
39+
output_size = attrs['output_size']
40+
out_h = output_size[0] + 2 * pad[0]
41+
out_w = output_size[1] + 2 * pad[1]
3842

3943
out = np.zeros((in_n, out_c, out_h, out_w))
4044

@@ -65,6 +69,7 @@ class TestConv2dTransposeOp(OpTest):
6569
def setUp(self):
6670
# init as conv transpose
6771
self.use_cudnn = False
72+
self.output_size = None
6873
self.init_op_type()
6974
self.init_test_case()
7075

@@ -80,6 +85,8 @@ def setUp(self):
8085
'use_cudnn': self.use_cudnn,
8186
'data_format': 'AnyLayout' # TODO(dzhwinter) : should be fix latter
8287
}
88+
if self.output_size is not None:
89+
self.attrs['output_size'] = self.output_size
8390

8491
output = conv2dtranspose_forward_naive(input_, filter_,
8592
self.attrs).astype('float32')
@@ -192,6 +199,18 @@ def init_test_case(self):
192199
self.filter_size = [f_c, 6, 3, 3]
193200

194201

202+
class TestWithEvenUpsample(TestConv2dTransposeOp):
203+
def init_test_case(self):
204+
self.pad = [2, 2]
205+
self.stride = [2, 2]
206+
self.groups = 1
207+
self.dilations = [1, 1]
208+
self.output_size = [14, 14]
209+
self.input_size = [2, 3, 7, 7] # NCHW
210+
f_c = self.input_size[1]
211+
self.filter_size = [f_c, 6, 5, 5]
212+
213+
195214
# ------------ test_cudnn ------------
196215
@unittest.skipIf(not core.is_compiled_with_cuda(),
197216
"core is not compiled with CUDA")
@@ -265,6 +284,15 @@ def init_test_case(self):
265284
self.op_type = "depthwise_conv2d_transpose"
266285

267286

287+
# ------------ test_cudnn ------------
288+
@unittest.skipIf(not core.is_compiled_with_cuda(),
289+
"core is not compiled with CUDA")
290+
class TestCUDNNWithEvenUpsample(TestWithEvenUpsample):
291+
def init_op_type(self):
292+
self.use_cudnn = True
293+
self.op_type = "conv2d_transpose"
294+
295+
268296
# Please Don't remove the following code.
269297
# Currently, CI use cudnn V5.0 which not support dilation conv.
270298
# class TestCUDNNWithDilation(TestWithDilation):

0 commit comments

Comments
 (0)