xref: /aosp_15_r20/external/tensorflow/tensorflow/core/kernels/conv_grad_shape_utils.cc (revision b6fb3261f9314811a0f4371741dbb8839866f948)
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