Caffe2 - C++ API
A deep learning, cross platform ML framework
affine_channel_op.cc
1 #include "caffe2/operators/affine_channel_op.h"
2 #include "caffe2/utils/eigen_utils.h"
3 
4 #include <vector>
5 
6 namespace caffe2 {
7 
8 namespace {
9 
10 template <typename T>
11 void AffineChannelScaleBiasBackwardNCHW(
12  const int N,
13  const int C,
14  const int HxW,
15  const T* dY,
16  const T* X,
17  T* dscale,
18  T* dbias) {
19  const T* dY_ptr = dY;
20  const T* X_ptr = X;
21  const int stride = C * HxW;
22  EigenVectorArrayMap<T> dscale_arr(dscale, C);
23  EigenVectorArrayMap<T> dbias_arr(dbias, C);
24  dscale_arr.setZero();
25  dbias_arr.setZero();
26  for (int i = 0; i < N; ++i) {
27  ConstEigenArrayMap<T> dY_arr(dY_ptr, HxW, C);
28  ConstEigenArrayMap<T> X_arr(X_ptr, HxW, C);
29  dscale_arr += (dY_arr * X_arr).colwise().sum();
30  dbias_arr += dY_arr.colwise().sum();
31  dY_ptr += stride;
32  X_ptr += stride;
33  }
34 }
35 
36 template <typename T>
37 void AffineChannelScaleBiasBackwardNHWC(
38  const int N,
39  const int C,
40  const int HxW,
41  const T* dY,
42  const T* X,
43  T* dscale,
44  T* dbias) {
45  ConstEigenArrayMap<T> dY_arr(dY, C, N * HxW);
46  ConstEigenArrayMap<T> X_arr(X, C, N * HxW);
47  EigenVectorMap<T>(dscale, C) = (dY_arr * X_arr).rowwise().sum();
48  EigenVectorMap<T>(dbias, C) = dY_arr.rowwise().sum();
49 }
50 
51 } // namespace
52 
53 template <>
54 bool AffineChannelGradientOp<float, CPUContext>::RunOnDeviceWithOrderNCHW() {
55  const auto& dY = Input(0);
56  const auto& scale = is_learnable_ ? Input(2) : Input(1);
57 
58  auto* dX = Output(0, dY.sizes(), at::dtype<float>());
59  const int N = dY.dim32(0);
60  const int C = dY.dim32(1);
61  const int HxW = dY.numel() / (N * C);
62  const float* dY_data = dY.data<float>();
63  const float* scale_data = scale.data<float>();
64  const std::array<int, 3> X_dims = {N, C, HxW};
65  const std::array<int, 3> scale_dims = {1, C, 1};
66  math::Mul<float, CPUContext>(
67  3,
68  X_dims.data(),
69  3,
70  scale_dims.data(),
71  dY_data,
72  scale_data,
73  dX->template mutable_data<float>(),
74  &context_);
75  if (is_learnable_) {
76  const auto& X = Input(1);
77  const float* X_data = X.data<float>();
78 
79  auto* dscale = Output(1, scale.sizes(), at::dtype<float>());
80  auto* dbias = Output(2, scale.sizes(), at::dtype<float>());
81  AffineChannelScaleBiasBackwardNCHW<float>(
82  N,
83  C,
84  HxW,
85  dY_data,
86  X_data,
87  dscale->template mutable_data<float>(),
88  dbias->template mutable_data<float>());
89  }
90  return true;
91 }
92 
93 template <>
94 bool AffineChannelGradientOp<float, CPUContext>::RunOnDeviceWithOrderNHWC() {
95  const auto& dY = Input(0);
96  const auto& scale = is_learnable_ ? Input(2) : Input(1);
97 
98  auto* dX = Output(0, dY.sizes(), at::dtype<float>());
99  const int ndim = dY.dim();
100  const int C = dY.dim32(ndim - 1);
101  const int rows = dY.numel() / C;
102  const int cols = C;
103  const float* dY_data = dY.data<float>();
104  const float* scale_data = scale.data<float>();
105  math::RowwiseMul<float, CPUContext>(
106  rows,
107  cols,
108  dY_data,
109  scale_data,
110  dX->template mutable_data<float>(),
111  &context_);
112  if (is_learnable_) {
113  const auto& X = Input(1);
114  const float* X_data = X.data<float>();
115  const int N = X.dim32(0);
116  const int HxW = rows / N;
117 
118  auto* dscale = Output(1, scale.sizes(), at::dtype<float>());
119  auto* dbias = Output(2, scale.sizes(), at::dtype<float>());
120  AffineChannelScaleBiasBackwardNHWC<float>(
121  N,
122  C,
123  HxW,
124  dY_data,
125  X_data,
126  dscale->template mutable_data<float>(),
127  dbias->template mutable_data<float>());
128  }
129  return true;
130 }
131 
132 REGISTER_CPU_OPERATOR(AffineChannel, AffineChannelOp<float, CPUContext>);
133 REGISTER_CPU_OPERATOR(
134  AffineChannelGradient,
135  AffineChannelGradientOp<float, CPUContext>);
136 
137 OPERATOR_SCHEMA(AffineChannel)
138  .NumInputs(3)
139  .NumOutputs(1)
140  .AllowInplace({{0, 0}})
141  .SetDoc(R"DOC(
142 Applies a separate affine transformation to each channel of the input. Useful
143 for replacing spatial batch norm with its equivalent fixed transformation.
144 )DOC")
145  .Input(0, "X", "Feature map input with order NCHW or NHWC.")
146  .Input(
147  1,
148  "scale",
149  "1D input of shape (C); the c-th element is the scale factor of the "
150  "affine transformation for the c-th channel of the input.")
151  .Input(
152  2,
153  "bias",
154  "1D input of shape (C); the c-th element is the bias of the affine "
155  "transformation for the c-th channel of the input.")
156  .Output(0, "Y", "Output with the same order of Input.");
157 
158 OPERATOR_SCHEMA(AffineChannelGradient)
159  .NumInputs({2, 3})
160  .NumOutputs({1, 3})
161  .AllowInplace({{0, 0}});
162 
163 namespace {
164 
165 class GetAffineChannelGradient : public GradientMakerBase {
166  using GradientMakerBase::GradientMakerBase;
167  std::vector<OperatorDef> GetGradientDefs() override {
168  ArgumentHelper arg_helper(def_);
169  const bool is_learnable =
170  arg_helper.GetSingleArgument("is_learnable", false);
171  if (is_learnable) {
172  return SingleGradientDef(
173  "AffineChannelGradient",
174  "",
175  std::vector<std::string>{GO(0), I(0), I(1)},
176  std::vector<std::string>{GI(0), GI(1), GI(2)});
177  } else {
178  return SingleGradientDef(
179  "AffineChannelGradient",
180  "",
181  std::vector<std::string>{GO(0), I(1)},
182  std::vector<std::string>{GI(0)});
183  }
184  }
185 };
186 
187 } // namespace
188 
189 REGISTER_GRADIENT(AffineChannel, GetAffineChannelGradient);
190 
191 } // namespace caffe2
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...
Definition: blob.h:13
Definition: static.cpp:64