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. |
41 | 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 | |
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 | |
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 | |
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 | |
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 | 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 | |