1 /* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15
16 // See docs in ../ops/nn_ops.cc.
17
18 #define USE_EIGEN_TENSOR
19 #define EIGEN_USE_THREADS
20
21 #include "tensorflow/core/kernels/conv_grad_shape_utils.h"
22
23 #include <algorithm>
24 #include <vector>
25
26 #include "tensorflow/core/framework/common_shape_fns.h"
27 #include "tensorflow/core/framework/kernel_shape_util.h"
28 #include "tensorflow/core/framework/numeric_op.h"
29 #include "tensorflow/core/framework/register_types.h"
30 #include "tensorflow/core/framework/tensor.h"
31 #include "tensorflow/core/framework/tensor_shape.h"
32 #include "tensorflow/core/lib/core/errors.h"
33 #include "tensorflow/core/platform/logging.h"
34 #include "tensorflow/core/platform/macros.h"
35 #include "tensorflow/core/util/padding.h"
36 #include "tensorflow/core/util/tensor_format.h"
37
38 namespace tensorflow {
39
40 // Compute padding for the given spatial dimension.
SpatialPadding(const Padding & padding,int dim) const41 int ConvBackpropDimensions::SpatialPadding(const Padding& padding,
42 int dim) const {
43 return (padding == VALID)
44 ? 0
45 : std::max<int>(
46 0, static_cast<int>((output_size(dim) - 1) * stride(dim) +
47 (filter_size(dim) - 1) * dilation(dim) +
48 1 - input_size(dim)));
49 }
50
51 namespace {
52
ConvBackpropExtractAndVerifyDimension(StringPiece label,const TensorShape & input_shape,const TensorShape & filter_shape,const TensorShape & output_shape,const gtl::ArraySlice<int32> & dilations,const std::vector<int32> & strides,Padding padding,int64 padding_before,int64 padding_after,int spatial_dim,int filter_spatial_dim,ConvBackpropSpatialDimension * dim)53 Status ConvBackpropExtractAndVerifyDimension(
54 StringPiece label, const TensorShape& input_shape,
55 const TensorShape& filter_shape, const TensorShape& output_shape,
56 const gtl::ArraySlice<int32>& dilations, const std::vector<int32>& strides,
57 Padding padding, int64 padding_before, int64 padding_after, int spatial_dim,
58 int filter_spatial_dim, ConvBackpropSpatialDimension* dim) {
59 dim->input_size = input_shape.dim_size(spatial_dim);
60 dim->filter_size = filter_shape.dim_size(filter_spatial_dim);
61 dim->output_size = output_shape.dim_size(spatial_dim);
62 dim->stride = strides[spatial_dim];
63 dim->dilation = dilations[spatial_dim];
64 int64 out_size = 0;
65 TF_RETURN_IF_ERROR(GetWindowedOutputSizeVerboseV2(
66 dim->input_size, dim->filter_size, dim->dilation, dim->stride, padding,
67 &out_size, &padding_before, &padding_after));
68 if (dim->output_size != out_size) {
69 return errors::InvalidArgument(
70 label, ": Size of out_backprop doesn't match computed: ", "actual = ",
71 dim->output_size, ", computed = ", out_size,
72 " spatial_dim: ", spatial_dim, " input: ", dim->input_size,
73 " filter: ", dim->filter_size, " output: ", dim->output_size,
74 " stride: ", dim->stride, " dilation: ", dim->dilation);
75 }
76
77 int64 effective_filter_size = (dim->filter_size - 1) * dim->dilation + 1;
78 dim->expanded_output_size = (dim->output_size - 1) * dim->stride + 1;
79 const auto padded_out_size = dim->input_size + effective_filter_size - 1;
80 dim->pad_before = effective_filter_size - 1 - padding_before;
81 dim->pad_after =
82 padded_out_size - dim->expanded_output_size - dim->pad_before;
83 VLOG(2) << label << ": expanded_out = " << dim->expanded_output_size
84 << ", effective_filter_size = " << effective_filter_size
85 << ", padded_out = " << padded_out_size
86 << ", pad_before = " << dim->pad_before
87 << ", pad_after = " << dim->pad_after
88 << ", dilation = " << dim->dilation << ", strides = " << dim->stride;
89 return Status::OK();
90 }
91
92 } // namespace
93
ConvBackpropComputeDimensionsV2(StringPiece label,int num_spatial_dims,const TensorShape & input_shape,const TensorShape & filter_shape,const TensorShape & out_backprop_shape,const gtl::ArraySlice<int32> & dilations,const std::vector<int32> & strides,Padding padding,absl::Span<const int64> explicit_paddings,TensorFormat data_format,ConvBackpropDimensions * dims)94 Status ConvBackpropComputeDimensionsV2(
95 StringPiece label, int num_spatial_dims, const TensorShape& input_shape,
96 const TensorShape& filter_shape, const TensorShape& out_backprop_shape,
97 const gtl::ArraySlice<int32>& dilations, const std::vector<int32>& strides,
98 Padding padding, absl::Span<const int64> explicit_paddings,
99 TensorFormat data_format, ConvBackpropDimensions* dims) {
100 // The + 2 in the following line is for the batch and feature dimensions.
101 const int num_dims = num_spatial_dims + 2;
102 if (input_shape.dims() != num_dims) {
103 return errors::InvalidArgument(label, ": input must be ", num_dims,
104 "-dimensional");
105 }
106 if (filter_shape.dims() != num_dims) {
107 return errors::InvalidArgument(label, ": filter must be ", num_dims,
108 "-dimensional");
109 }
110 if (out_backprop_shape.dims() != num_dims) {
111 return errors::InvalidArgument(label, ": out_backprop must be ", num_dims,
112 "-dimensional");
113 }
114 int batch_dim = GetTensorBatchDimIndex(num_dims, data_format);
115 dims->batch_size = input_shape.dim_size(batch_dim);
116 if (dims->batch_size != out_backprop_shape.dim_size(batch_dim)) {
117 return errors::InvalidArgument(
118 label, ": input and out_backprop must have the same batch size.",
119 " Input batch: ", dims->batch_size,
120 ", outbackprop batch: ", out_backprop_shape.dim_size(batch_dim),
121 ", batch_dim: ", batch_dim);
122 }
123
124 int feature_dim = GetTensorFeatureDimIndex(num_dims, data_format);
125 dims->in_depth = input_shape.dim_size(feature_dim);
126 // The input and output feature dimensions are the second last and last
127 // dimensions of the filter Tensor.
128 VLOG(2) << "input vs filter_in depth " << dims->in_depth << " "
129 << filter_shape.dim_size(num_dims - 2);
130 if (dims->in_depth % filter_shape.dim_size(num_dims - 2)) {
131 return errors::InvalidArgument(
132 label, ": input depth must be evenly divisible by filter depth");
133 }
134 dims->out_depth = filter_shape.dim_size(num_dims - 1);
135 if (dims->out_depth != out_backprop_shape.dim_size(feature_dim)) {
136 return errors::InvalidArgument(
137 label, ": filter and out_backprop must have the same out_depth");
138 }
139 dims->spatial_dims.resize(num_spatial_dims);
140 for (int i = 0; i < num_spatial_dims; ++i) {
141 int image_dim = GetTensorSpatialDimIndex(num_dims, data_format, i);
142 int64 padding_before = -1, padding_after = -1;
143 if (padding == EXPLICIT) {
144 padding_before = explicit_paddings[2 * image_dim];
145 padding_after = explicit_paddings[2 * image_dim + 1];
146 }
147 TF_RETURN_IF_ERROR(ConvBackpropExtractAndVerifyDimension(
148 label, input_shape, filter_shape, out_backprop_shape, dilations,
149 strides, padding, padding_before, padding_after, image_dim, i,
150 &dims->spatial_dims[i]));
151 }
152 return Status::OK();
153 }
154
ConvBackpropComputeDimensions(StringPiece label,int num_spatial_dims,const TensorShape & input_shape,const TensorShape & filter_shape,const TensorShape & out_backprop_shape,const std::vector<int32> & strides,Padding padding,TensorFormat data_format,ConvBackpropDimensions * dims)155 Status ConvBackpropComputeDimensions(StringPiece label, int num_spatial_dims,
156 const TensorShape& input_shape,
157 const TensorShape& filter_shape,
158 const TensorShape& out_backprop_shape,
159 const std::vector<int32>& strides,
160 Padding padding, TensorFormat data_format,
161 ConvBackpropDimensions* dims) {
162 static constexpr std::array<int32, 5> one_dilations = {{1, 1, 1, 1, 1}};
163 return ConvBackpropComputeDimensionsV2(
164 label, num_spatial_dims, input_shape, filter_shape, out_backprop_shape,
165 one_dilations, strides, padding, /*explicit_paddings=*/{}, data_format,
166 dims);
167 }
168
Conv2DBackpropComputeInputShape(const Tensor & input_sizes,const TensorShape & filter_shape,const TensorShape & out_backprop_shape,const TensorFormat & data_format,TensorShape * input_shape)169 Status Conv2DBackpropComputeInputShape(const Tensor& input_sizes,
170 const TensorShape& filter_shape,
171 const TensorShape& out_backprop_shape,
172 const TensorFormat& data_format,
173 TensorShape* input_shape) {
174 if (!TensorShapeUtils::IsVector(input_sizes.shape())) {
175 return errors::InvalidArgument(
176 "Conv2DBackpropInput: input_sizes input must be 1-dim, not ",
177 input_sizes.dims());
178 }
179
180 if (input_sizes.dim_size(0) == 4) {
181 return TensorShapeUtils::MakeShape(input_sizes.vec<int32>(), input_shape);
182 }
183
184 if (input_sizes.dim_size(0) == 2) {
185 const int batch_size = GetTensorDim(out_backprop_shape, data_format, 'N');
186 const int output_height = input_sizes.vec<int32>()(0);
187 const int output_width = input_sizes.vec<int32>()(1);
188 const int output_depth = filter_shape.dim_size(2);
189 *input_shape = ShapeFromFormat(data_format, batch_size, output_height,
190 output_width, output_depth);
191 return Status::OK();
192 }
193
194 return errors::InvalidArgument(
195 "Conv2DBackpropInput requires input_sizes to "
196 "contain 4 values or 2 values, but got: ",
197 input_sizes.dim_size(0));
198 }
199
200 } // namespace tensorflow
201