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_t padding_before,int64_t 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_t padding_before, int64_t padding_after,
58 int spatial_dim, int filter_spatial_dim,
59 ConvBackpropSpatialDimension* dim) {
60 dim->input_size = input_shape.dim_size(spatial_dim);
61 dim->filter_size = filter_shape.dim_size(filter_spatial_dim);
62 dim->output_size = output_shape.dim_size(spatial_dim);
63 dim->stride = strides[spatial_dim];
64 dim->dilation = dilations[spatial_dim];
65 int64_t out_size = 0;
66 TF_RETURN_IF_ERROR(GetWindowedOutputSizeVerboseV2(
67 dim->input_size, dim->filter_size, dim->dilation, dim->stride, padding,
68 &out_size, &padding_before, &padding_after));
69 if (dim->output_size != out_size) {
70 return errors::InvalidArgument(
71 label, ": Size of out_backprop doesn't match computed: ", "actual = ",
72 dim->output_size, ", computed = ", out_size,
73 " spatial_dim: ", spatial_dim, " input: ", dim->input_size,
74 " filter: ", dim->filter_size, " output: ", dim->output_size,
75 " stride: ", dim->stride, " dilation: ", dim->dilation);
76 }
77
78 int64_t effective_filter_size = (dim->filter_size - 1) * dim->dilation + 1;
79 dim->expanded_output_size = (dim->output_size - 1) * dim->stride + 1;
80 const auto padded_out_size = dim->input_size + effective_filter_size - 1;
81 dim->pad_before = effective_filter_size - 1 - padding_before;
82 dim->pad_after =
83 padded_out_size - dim->expanded_output_size - dim->pad_before;
84 VLOG(2) << label << ": expanded_out = " << dim->expanded_output_size
85 << ", effective_filter_size = " << effective_filter_size
86 << ", padded_out = " << padded_out_size
87 << ", pad_before = " << dim->pad_before
88 << ", pad_after = " << dim->pad_after
89 << ", dilation = " << dim->dilation << ", strides = " << dim->stride;
90 return OkStatus();
91 }
92
93 } // namespace
94
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_t> explicit_paddings,TensorFormat data_format,ConvBackpropDimensions * dims)95 Status ConvBackpropComputeDimensionsV2(
96 StringPiece label, int num_spatial_dims, const TensorShape& input_shape,
97 const TensorShape& filter_shape, const TensorShape& out_backprop_shape,
98 const gtl::ArraySlice<int32>& dilations, const std::vector<int32>& strides,
99 Padding padding, absl::Span<const int64_t> explicit_paddings,
100 TensorFormat data_format, ConvBackpropDimensions* dims) {
101 // The + 2 in the following line is for the batch and feature dimensions.
102 const int num_dims = num_spatial_dims + 2;
103 if (input_shape.dims() != num_dims) {
104 return errors::InvalidArgument(label, ": input must be ", num_dims,
105 "-dimensional");
106 }
107 if (filter_shape.dims() != num_dims) {
108 return errors::InvalidArgument(label, ": filter must be ", num_dims,
109 "-dimensional");
110 }
111 if (out_backprop_shape.dims() != num_dims) {
112 return errors::InvalidArgument(label, ": out_backprop must be ", num_dims,
113 "-dimensional");
114 }
115 int batch_dim = GetTensorBatchDimIndex(num_dims, data_format);
116 dims->batch_size = input_shape.dim_size(batch_dim);
117 if (dims->batch_size != out_backprop_shape.dim_size(batch_dim)) {
118 return errors::InvalidArgument(
119 label, ": input and out_backprop must have the same batch size.",
120 " Input batch: ", dims->batch_size,
121 ", outbackprop batch: ", out_backprop_shape.dim_size(batch_dim),
122 ", batch_dim: ", batch_dim);
123 }
124
125 int feature_dim = GetTensorFeatureDimIndex(num_dims, data_format);
126 dims->in_depth = input_shape.dim_size(feature_dim);
127 // The input and output feature dimensions are the second last and last
128 // dimensions of the filter Tensor.
129 VLOG(2) << "input vs filter_in depth " << dims->in_depth << " "
130 << filter_shape.dim_size(num_dims - 2);
131 if (filter_shape.dim_size(num_dims - 2) <= 0) {
132 return errors ::InvalidArgument(
133 label, ": filter depth must be strictly greated than zero");
134 }
135 if (dims->in_depth % filter_shape.dim_size(num_dims - 2)) {
136 return errors::InvalidArgument(
137 label, ": input depth must be evenly divisible by filter depth");
138 }
139 dims->out_depth = filter_shape.dim_size(num_dims - 1);
140 if (dims->out_depth != out_backprop_shape.dim_size(feature_dim)) {
141 return errors::InvalidArgument(
142 label, ": filter and out_backprop must have the same out_depth");
143 }
144 dims->spatial_dims.resize(num_spatial_dims);
145 for (int i = 0; i < num_spatial_dims; ++i) {
146 int image_dim = GetTensorSpatialDimIndex(num_dims, data_format, i);
147 int64_t padding_before = -1, padding_after = -1;
148 if (padding == EXPLICIT) {
149 padding_before = explicit_paddings[2 * image_dim];
150 padding_after = explicit_paddings[2 * image_dim + 1];
151 }
152 TF_RETURN_IF_ERROR(ConvBackpropExtractAndVerifyDimension(
153 label, input_shape, filter_shape, out_backprop_shape, dilations,
154 strides, padding, padding_before, padding_after, image_dim, i,
155 &dims->spatial_dims[i]));
156 }
157 return OkStatus();
158 }
159
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)160 Status ConvBackpropComputeDimensions(StringPiece label, int num_spatial_dims,
161 const TensorShape& input_shape,
162 const TensorShape& filter_shape,
163 const TensorShape& out_backprop_shape,
164 const std::vector<int32>& strides,
165 Padding padding, TensorFormat data_format,
166 ConvBackpropDimensions* dims) {
167 static constexpr std::array<int32, 5> one_dilations = {{1, 1, 1, 1, 1}};
168 return ConvBackpropComputeDimensionsV2(
169 label, num_spatial_dims, input_shape, filter_shape, out_backprop_shape,
170 one_dilations, strides, padding, /*explicit_paddings=*/{}, data_format,
171 dims);
172 }
173
Conv2DBackpropComputeInputShape(const Tensor & input_sizes,const TensorShape & filter_shape,const TensorShape & out_backprop_shape,const TensorFormat & data_format,TensorShape * input_shape)174 Status Conv2DBackpropComputeInputShape(const Tensor& input_sizes,
175 const TensorShape& filter_shape,
176 const TensorShape& out_backprop_shape,
177 const TensorFormat& data_format,
178 TensorShape* input_shape) {
179 if (!TensorShapeUtils::IsVector(input_sizes.shape())) {
180 return errors::InvalidArgument(
181 "Conv2DBackpropInput: input_sizes input must be 1-dim, not ",
182 input_sizes.dims());
183 }
184
185 if (input_sizes.dim_size(0) == 4) {
186 return TensorShapeUtils::MakeShape(input_sizes.vec<int32>(), input_shape);
187 }
188
189 if (input_sizes.dim_size(0) == 2) {
190 const int batch_size = GetTensorDim(out_backprop_shape, data_format, 'N');
191 const int output_height = input_sizes.vec<int32>()(0);
192 const int output_width = input_sizes.vec<int32>()(1);
193 const int output_depth = filter_shape.dim_size(2);
194 *input_shape = ShapeFromFormat(data_format, batch_size, output_height,
195 output_width, output_depth);
196 return OkStatus();
197 }
198
199 return errors::InvalidArgument(
200 "Conv2DBackpropInput requires input_sizes to "
201 "contain 4 values or 2 values, but got: ",
202 input_sizes.dim_size(0));
203 }
204
205 } // namespace tensorflow
206