Caffe2 - C++ API
A deep learning, cross platform ML framework
conv_transpose_op.cc
1 #include "caffe2/operators/conv_transpose_op.h"
2 #include "caffe2/ideep/operators/conv_transpose_unpool_base_op.h"
3 #include "caffe2/ideep/operators/operator_fallback_ideep.h"
4 #include <vector>
5 
6 // TODO: The code below works around correctness issues with particular input shapes
7 // in MKL-DNN v0.17, will be removed with the fixes in MKL-DNN 0.18.
8 bool need_type_zero_pad(const mkldnn_memory_desc_t *pd) {
9  if (pd->ndims == 0) {
10  return false;
11  }
12  int p1 = 1, p2 = 1;
13  for (int i = 0; i < pd->ndims; i++) {
14  p1 *= pd->dims[i];
15  p2 *= pd->layout_desc.blocking.padding_dims[i];
16  }
17  return (p1 != p2);
18 }
19 
20 namespace caffe2 {
21 
23  public:
24  USE_IDEEP_DEF_ALIASES();
25  USE_IDEEP_CONV_TRANSPOSE_UNPOOL_BASE_FUNCTIONS();
26  // TODO: The code below works around correctness issues with particular input shapes
27  // in MKL-DNN v0.17, will be removed with the fixes in MKL-DNN 0.18.
29 
30  IDEEPConvTransposeOp(const OperatorDef& operator_def, Workspace* ws)
31  : IDEEPConvTransposeUnpoolBase(operator_def, ws),
32  training_mode_(
33  OperatorBase::GetSingleArgument<int>("training_mode", 0)),
34  fallback_(operator_def, ws) {
35  OPERATOR_NEEDS_FEATURE(
36  pad_l() == pad_r() && pad_t() == pad_b(),
37  "Uneven padding not supported.");
38  }
39  ~IDEEPConvTransposeOp() override {}
40 
41  bool RunOnDeviceWithOrderNCHW() override {
42  const auto& X = Input(INPUT);
43  const auto& filter = Input(FILTER);
44  auto* Y = Output(OUTPUT);
45  CAFFE_ENFORCE_EQ(X.ndims(), 4);
46  CAFFE_ENFORCE_EQ(filter.ndims(), 4);
47  CAFFE_ENFORCE_EQ(filter.get_dim(2), kernel_h());
48  CAFFE_ENFORCE_EQ(filter.get_dim(3), kernel_w());
49 
50  ideep::tensor::dims Y_dims;
51  const bool pre_converted = filter.get_public_format() == ideep::format::iohw;
52  if (!pre_converted) {
53  CAFFE_ENFORCE_EQ(
54  filter.get_dim(0), X.get_dim(1),
55  "filter number must be equal to input channel number");
56 
57  Y_dims = CalcOutputDims(X, filter.get_dim(1));
58 
59  ideep::tensor::dims filter_dims_mkldnn {filter.get_dim(1), filter.get_dim(0),
60  filter.get_dim(2), filter.get_dim(3)};
61  auto expected_descriptor =
62  ideep::convolution_transpose_forward::expected_weights_descriptor(
63  filter_dims_mkldnn,
64  filter.get_data_type(),
65  stride_,
66  pad_tl(),
67  pad_br());
68  const bool weights_changed =
69  (cached_weights_descriptor_ != filter.get_descriptor());
70  if (weights_changed) {
71  cached_weights_descriptor_ = filter.dup_descriptor();
72  }
73 
74  if (training_mode_ || weights_changed) {
75  auto filter_in = filter;
76  // Framework has filters in IOHW while MKL-DNN requires OIHW,
77  // we have to do explicit conversion here.
78  filter_in.set_public_format(ideep::format::iohw);
79  filter_.init(expected_descriptor);
80  ideep::reorder::compute(filter_in, filter_);
81  }
82 
83  // TODO: The code below works around correctness issues with particular input shapes
84  // in MKL-DNN v0.17, will be removed with the fixes in MKL-DNN 0.18.
85  if (need_type_zero_pad(filter_.get_mkldnn_memory_desc_t())) {
86  return fallback_.Run(0);
87  }
88  } else {
89  CAFFE_ENFORCE_EQ(
90  filter.get_dim(1), X.get_dim(1),
91  "filter number must be equal to input channel number");
92 
93  // TODO: The code below works around correctness issues with particular input shapes
94  // in MKL-DNN v0.17, will be removed with the fixes in MKL-DNN 0.18.
95  if (need_type_zero_pad(filter.get_mkldnn_memory_desc_t())) {
96  return fallback_.Run(0);
97  }
98 
99  Y_dims = CalcOutputDims(X, filter.get_dim(0));
100  }
101 
102  if (InputSize() > BIAS) {
103  const auto& bias = Input(BIAS);
104  CAFFE_ENFORCE_EQ(bias.ndims(), 1, "bias must be 1D tensor");
105  CAFFE_ENFORCE_EQ(
106  bias.get_dim(0), pre_converted ? filter.get_dim(0) : filter.get_dim(1),
107  "bias dimension must be equal to output channel number");
108 
109  ideep::convolution_transpose_forward::compute(
110  X, pre_converted ? filter : filter_, bias, Y_dims, *Y, stride_, pad_tl(), pad_br());
111  } else {
112  ideep::convolution_transpose_forward::compute(
113  X, pre_converted ? filter : filter_, Y_dims, *Y, stride_, pad_tl(), pad_br());
114  }
115  return true;
116  }
117 
118  private:
119  INPUT_TAGS(INPUT, FILTER, BIAS);
120  OUTPUT_TAGS(OUTPUT);
121 
122  const bool training_mode_;
123  ideep::tensor filter_;
124  ideep::tensor::descriptor cached_weights_descriptor_;
125  // TODO: The code below works around correctness issues with particular input shapes
126  // in MKL-DNN v0.17, will be removed with the fixes in MKL-DNN 0.18.
127  FALLBACK_OP fallback_;
128 
129 };
130 
132  public:
133  USE_IDEEP_DEF_ALIASES();
134  USE_IDEEP_CONV_TRANSPOSE_UNPOOL_BASE_FUNCTIONS();
135  // TODO: The code below works around correctness issues with particular input shapes
136  // in MKL-DNN v0.17, will be removed with the fixes in MKL-DNN 0.18.
138 
139  IDEEPConvTransposeGradientOp(const OperatorDef& operator_def, Workspace* ws)
140  : IDEEPConvTransposeUnpoolBase(operator_def, ws),
141  no_bias_(OperatorBase::GetSingleArgument<int>("no_bias", false)),
142  fallback_(operator_def, ws) {
143  OPERATOR_NEEDS_FEATURE(
144  pad_l() == pad_r() && pad_t() == pad_b(),
145  "Uneven padding not supported.");
146  CAFFE_ENFORCE(
147  !(no_bias_ && OutputSize() == 3),
148  "If bias is not present, you should not have 3 grad output.");
149  CAFFE_ENFORCE(
150  OperatorBase::GetSingleArgument<int>("training_mode", 0),
151  "In order to backward propagate weights correctly, "
152  "please set training_mode=1");
153  }
154  ~IDEEPConvTransposeGradientOp() override {}
155 
156  bool RunOnDeviceWithOrderNCHW() override {
157  const auto& X = Input(INPUT);
158  const auto& filter = Input(FILTER);
159  const auto& dY = Input(OUTPUT_GRAD);
160  auto* dfilter = Output(FILTER_GRAD);
161 
162  itensor dfilter_;
163  itensor filter_;
164  auto filter_in = filter;
165 
166  itensor::dims oihw_dims {filter.get_dim(1), filter.get_dim(0),
167  filter.get_dim(2), filter.get_dim(3)};
168  const bool pre_converted = (filter.get_public_format() == ideep::format::iohw);
169  if (!pre_converted) {
170  auto expected_descriptor =
171  ideep::convolution_transpose_forward::expected_weights_descriptor(
172  oihw_dims,
173  filter.get_data_type(),
174  stride_,
175  pad_tl(),
176  pad_br());
177  // Framework has filters in IOHW while MKL-DNN requires OIHW,
178  // we have to do explicit conversion here.
179  filter_in.set_public_format(ideep::format::iohw);
180  filter_.init(expected_descriptor);
181  ideep::reorder::compute(filter_in, filter_);
182 
183  // TODO: The code below works around correctness issues with particular input shapes
184  // in MKL-DNN v0.17, will be removed with the fixes in MKL-DNN 0.18.
185  if (((filter_in.get_dim(0) % 8 != 0) && (stride_[0] * stride_[1] != 1)) ||
186  need_type_zero_pad(filter_.get_mkldnn_memory_desc_t())) {
187  return fallback_.Run(0);
188  }
189  } else {
190  // TODO: The code below works around correctness issues with particular input shapes
191  // in MKL-DNN v0.17, will be removed with the fixes in MKL-DNN 0.18.
192  if (((filter_in.get_dim(0) % 8 != 0) && (stride_[0] * stride_[1] != 1)) ||
193  need_type_zero_pad(filter.get_mkldnn_memory_desc_t())) {
194  return fallback_.Run(0);
195  }
196  }
197 
198  if (no_bias_) {
199  ideep::convolution_transpose_backward_weights::compute(
200  X, dY, pre_converted ? filter.get_dims() : oihw_dims,
201  pre_converted ? *dfilter : dfilter_, stride_, pad_tl(), pad_br());
202  } else {
203  auto* dbias = Output(BIAS_OR_INPUT_GRAD);
204  ideep::convolution_transpose_backward_weights::compute(
205  X,
206  dY,
207  pre_converted ? filter.get_dims() : oihw_dims,
208  pre_converted ? *dfilter : dfilter_,
209  *dbias,
210  stride_,
211  pad_tl(),
212  pad_br());
213  }
214 
215  if (!pre_converted) {
216  // Framework has filters in IOHW while MKL-DNN requires OIHW,
217  // we have to do explicit conversion here.
218  dfilter_.set_public_format(ideep::format::iohw);
219  dfilter->reinit(filter.get_descriptor());
220  dfilter_.to_public(dfilter->get_data_handle());
221  } else {
222  dfilter->set_public_format(ideep::format::iohw);
223  }
224 
225  if (OutputSize() == 3 || (no_bias_ && (OutputSize() == 2))) {
226  auto* dX = Output(no_bias_ ? BIAS_OR_INPUT_GRAD : INPUT_GRAD);
227  ideep::convolution_transpose_backward_data::compute(
228  dY, pre_converted ? filter : filter_, X.get_dims(), *dX, stride_, pad_tl(), pad_br());
229  }
230 
231  return true;
232  }
233 
234  private:
235  bool no_bias_;
236  // TODO: The code below works around correctness issues with particular input shapes
237  // in MKL-DNN v0.17, will be removed with the fixes in MKL-DNN 0.18.
238  FALLBACK_OP fallback_;
239 
240  INPUT_TAGS(INPUT, FILTER, OUTPUT_GRAD);
241  OUTPUT_TAGS(FILTER_GRAD, BIAS_OR_INPUT_GRAD, INPUT_GRAD);
242 };
243 
244 REGISTER_IDEEP_OPERATOR(ConvTranspose, IDEEPConvTransposeOp);
245 REGISTER_IDEEP_OPERATOR(ConvTransposeGradient, IDEEPConvTransposeGradientOp);
246 
247 } // namespace caffe2
Workspace is a class that holds all the related objects created during runtime: (1) all blobs...
Definition: workspace.h:47
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...
Definition: blob.h:13
A templated class to allow one to wrap a CPU operator as an IDEEP operator.