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_ops.h"
22
23 #include <algorithm>
24 #include <vector>
25
26 #include "tensorflow/core/framework/common_shape_fns.h"
27 #include "tensorflow/core/framework/numeric_op.h"
28 #include "tensorflow/core/framework/op_kernel.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/framework/tensor_slice.h"
33 #include "tensorflow/core/kernels/conv_2d.h"
34 #include "tensorflow/core/kernels/ops_util.h"
35 #include "tensorflow/core/lib/core/errors.h"
36 #include "tensorflow/core/platform/logging.h"
37 #include "tensorflow/core/platform/macros.h"
38 #include "tensorflow/core/util/padding.h"
39 #include "tensorflow/core/util/tensor_format.h"
40 #include "tensorflow/core/util/use_cudnn.h"
41
42 namespace tensorflow {
43
44 // Compute padding for the given spatial dimension.
SpatialPadding(const Padding & padding,int dim) const45 int ConvBackpropDimensions::SpatialPadding(const Padding& padding,
46 int dim) const {
47 return (padding == VALID)
48 ? 0
49 : std::max<int>(
50 0, static_cast<int>((output_size(dim) - 1) * stride(dim) +
51 (filter_size(dim) - 1) * dilation(dim) +
52 1 - input_size(dim)));
53 }
54
55 namespace {
56
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)57 Status ConvBackpropExtractAndVerifyDimension(
58 StringPiece label, const TensorShape& input_shape,
59 const TensorShape& filter_shape, const TensorShape& output_shape,
60 const gtl::ArraySlice<int32>& dilations, const std::vector<int32>& strides,
61 Padding padding, int64 padding_before, int64 padding_after, int spatial_dim,
62 int filter_spatial_dim, ConvBackpropSpatialDimension* dim) {
63 dim->input_size = input_shape.dim_size(spatial_dim);
64 dim->filter_size = filter_shape.dim_size(filter_spatial_dim);
65 dim->output_size = output_shape.dim_size(spatial_dim);
66 dim->stride = strides[spatial_dim];
67 dim->dilation = dilations[spatial_dim];
68 int64 out_size = 0;
69 TF_RETURN_IF_ERROR(GetWindowedOutputSizeVerboseV2(
70 dim->input_size, dim->filter_size, dim->dilation, dim->stride, padding,
71 &out_size, &padding_before, &padding_after));
72 if (dim->output_size != out_size) {
73 return errors::InvalidArgument(
74 label, ": Size of out_backprop doesn't match computed: ", "actual = ",
75 dim->output_size, ", computed = ", out_size,
76 " spatial_dim: ", spatial_dim, " input: ", dim->input_size,
77 " filter: ", dim->filter_size, " output: ", dim->output_size,
78 " stride: ", dim->stride, " dilation: ", dim->dilation);
79 }
80
81 int64 effective_filter_size = (dim->filter_size - 1) * dim->dilation + 1;
82 dim->expanded_output_size = (dim->output_size - 1) * dim->stride + 1;
83 const auto padded_out_size = dim->input_size + effective_filter_size - 1;
84 dim->pad_before = effective_filter_size - 1 - padding_before;
85 dim->pad_after =
86 padded_out_size - dim->expanded_output_size - dim->pad_before;
87 VLOG(2) << label << ": expanded_out = " << dim->expanded_output_size
88 << ", effective_filter_size = " << effective_filter_size
89 << ", padded_out = " << padded_out_size
90 << ", pad_before = " << dim->pad_before
91 << ", pad_after = " << dim->pad_after
92 << ", dilation = " << dim->dilation << ", strides = " << dim->stride;
93 return Status::OK();
94 }
95
96 } // namespace
97
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)98 Status ConvBackpropComputeDimensionsV2(
99 StringPiece label, int num_spatial_dims, const TensorShape& input_shape,
100 const TensorShape& filter_shape, const TensorShape& out_backprop_shape,
101 const gtl::ArraySlice<int32>& dilations, const std::vector<int32>& strides,
102 Padding padding, absl::Span<const int64> explicit_paddings,
103 TensorFormat data_format, ConvBackpropDimensions* dims) {
104 // The + 2 in the following line is for the batch and feature dimensions.
105 const int num_dims = num_spatial_dims + 2;
106 if (input_shape.dims() != num_dims) {
107 return errors::InvalidArgument(label, ": input must be ", num_dims,
108 "-dimensional");
109 }
110 if (filter_shape.dims() != num_dims) {
111 return errors::InvalidArgument(label, ": filter must be ", num_dims,
112 "-dimensional");
113 }
114 if (out_backprop_shape.dims() != num_dims) {
115 return errors::InvalidArgument(label, ": out_backprop must be ", num_dims,
116 "-dimensional");
117 }
118 int batch_dim = GetTensorBatchDimIndex(num_dims, data_format);
119 dims->batch_size = input_shape.dim_size(batch_dim);
120 if (dims->batch_size != out_backprop_shape.dim_size(batch_dim)) {
121 return errors::InvalidArgument(
122 label, ": input and out_backprop must have the same batch size",
123 "input batch: ", dims->batch_size,
124 "outbackprop batch: ", out_backprop_shape.dim_size(batch_dim),
125 " batch_dim: ", batch_dim);
126 }
127
128 int feature_dim = GetTensorFeatureDimIndex(num_dims, data_format);
129 dims->in_depth = input_shape.dim_size(feature_dim);
130 // The input and output feature dimensions are the second last and last
131 // dimensions of the filter Tensor.
132 VLOG(2) << "input vs filter_in depth " << dims->in_depth << " "
133 << filter_shape.dim_size(num_dims - 2);
134 if (dims->in_depth % filter_shape.dim_size(num_dims - 2)) {
135 return errors::InvalidArgument(
136 label, ": input depth must be evenly divisible by filter depth");
137 }
138 dims->out_depth = filter_shape.dim_size(num_dims - 1);
139 if (dims->out_depth != out_backprop_shape.dim_size(feature_dim)) {
140 return errors::InvalidArgument(
141 label, ": filter and out_backprop must have the same out_depth");
142 }
143 dims->spatial_dims.resize(num_spatial_dims);
144 for (int i = 0; i < num_spatial_dims; ++i) {
145 int image_dim = GetTensorSpatialDimIndex(num_dims, data_format, i);
146 int64 padding_before = -1, padding_after = -1;
147 if (padding == EXPLICIT) {
148 padding_before = explicit_paddings[2 * image_dim];
149 padding_after = explicit_paddings[2 * image_dim + 1];
150 }
151 TF_RETURN_IF_ERROR(ConvBackpropExtractAndVerifyDimension(
152 label, input_shape, filter_shape, out_backprop_shape, dilations,
153 strides, padding, padding_before, padding_after, image_dim, i,
154 &dims->spatial_dims[i]));
155 }
156 return Status::OK();
157 }
158
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)159 Status ConvBackpropComputeDimensions(StringPiece label, int num_spatial_dims,
160 const TensorShape& input_shape,
161 const TensorShape& filter_shape,
162 const TensorShape& out_backprop_shape,
163 const std::vector<int32>& strides,
164 Padding padding, TensorFormat data_format,
165 ConvBackpropDimensions* dims) {
166 static constexpr std::array<int32, 5> one_dilations = {{1, 1, 1, 1, 1}};
167 return ConvBackpropComputeDimensionsV2(
168 label, num_spatial_dims, input_shape, filter_shape, out_backprop_shape,
169 one_dilations, strides, padding, /*explicit_paddings=*/{}, data_format,
170 dims);
171 }
172
173 } // namespace tensorflow
174