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