1/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2
3Licensed under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License.
5You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9Unless required by applicable law or agreed to in writing, software
10distributed under the License is distributed on an "AS IS" BASIS,
11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12See the License for the specific language governing permissions and
13limitations 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
38namespace tensorflow {
39
40// Compute padding for the given spatial dimension.
41int 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
51namespace {
52
53Status 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
95Status 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
160Status 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
174Status 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 if (output_height < 0 || output_width < 0) {
195 return errors::InvalidArgument(
196 "Conv2DBackpropInput: elements of input_sizes must be >= 0, not ",
197 output_height, "x", output_width);
198 }
199 *input_shape = ShapeFromFormat(data_format, batch_size, output_height,
200 output_width, output_depth);
201 return OkStatus();
202 }
203
204 return errors::InvalidArgument(
205 "Conv2DBackpropInput requires input_sizes to "
206 "contain 4 values or 2 values, but got: ",
207 input_sizes.dim_size(0));
208}
209
210} // namespace tensorflow
211