Caffe2 - C++ API
A deep learning, cross platform ML framework
distance_op.h
1 
17 #ifndef CAFFE2_OPERATORS_DISTANCE_OP_H_
18 #define CAFFE2_OPERATORS_DISTANCE_OP_H_
19 
20 #include "caffe2/core/context.h"
21 #include "caffe2/core/operator.h"
22 #include "caffe2/utils/math.h"
23 
24 namespace caffe2 {
25 
26 template <typename T, class Context>
27 class SquaredL2DistanceOp : public Operator<Context> {
28  public:
29  SquaredL2DistanceOp(const OperatorDef& def, Workspace* ws)
30  : Operator<Context>(def, ws) {}
31  USE_OPERATOR_CONTEXT_FUNCTIONS;
32 
33  bool RunOnDevice() override;
34 
35  protected:
36  // Input: X, Y; Output: Distance
37 };
38 
39 template <typename T, class Context>
40 class SquaredL2DistanceGradientOp final : public Operator<Context> {
41  public:
42  SquaredL2DistanceGradientOp(const OperatorDef& def, Workspace* ws)
43  : Operator<Context>(def, ws) {}
44  USE_OPERATOR_CONTEXT_FUNCTIONS;
45 
46  bool RunOnDevice() override {
47  auto& X = Input(0);
48  auto& Y = Input(1);
49  auto& dDistance = Input(2);
50  auto* dX = Output(0);
51  auto* dY = Output(1);
52  int N = X.ndim() > 0 ? X.dim32(0) : 1;
53  int D = N > 0 ? X.size() / N : 0;
54  CAFFE_ENFORCE(X.ndim() == Y.ndim());
55  for (int i = 0; i < X.ndim(); ++i) {
56  CAFFE_ENFORCE(X.dim32(i) == Y.dim32(i));
57  }
58  CAFFE_ENFORCE(dDistance.ndim() == 1);
59  CAFFE_ENFORCE(dDistance.dim32(0) == N);
60  dX->ResizeLike(X);
61  dY->ResizeLike(Y);
62  math::Sub<T, Context>(
63  X.size(),
64  X.template data<T>(),
65  Y.template data<T>(),
66  dX->template mutable_data<T>(),
67  &context_);
68  for (int i = 0; i < N; ++i) {
69  math::Scale<T, Context>(
70  D,
71  dDistance.template data<T>() + i,
72  dX->template data<T>() + i * D,
73  dX->template mutable_data<T>() + i * D,
74  &context_);
75  }
76  // The gradient of the other side is basically the negative.
77  math::Scale<T, Context>(
78  X.size(),
79  -1,
80  dX->template data<T>(),
81  dY->template mutable_data<T>(),
82  &context_);
83  return true;
84  }
85 
86  protected:
87  // Input: X, Y, dDistance; Output: dX, dY
88 };
89 
90 template <typename T, class Context>
91 class L1DistanceOp : public Operator<Context> {
92  public:
93  L1DistanceOp(const OperatorDef& def, Workspace* ws)
94  : Operator<Context>(def, ws) {}
95  USE_OPERATOR_CONTEXT_FUNCTIONS;
96 
97  bool RunOnDevice() override;
98 
99  protected:
100  // Input: X, Y; Output: Distance
101 };
102 
103 template <typename T, class Context>
104 class L1DistanceGradientOp : public Operator<Context> {
105  public:
106  L1DistanceGradientOp(const OperatorDef& def, Workspace* ws)
107  : Operator<Context>(def, ws) {}
108  USE_OPERATOR_CONTEXT_FUNCTIONS;
109 
110  bool RunOnDevice() override;
111 
112  protected:
113  // Input: X, Y, dDistance; Output: dX, dY
114 };
115 
116 template <typename T, class Context>
117 class DotProductOp : public Operator<Context> {
118  public:
119  DotProductOp(const OperatorDef& def, Workspace* ws)
120  : Operator<Context>(def, ws) {}
121  USE_OPERATOR_CONTEXT_FUNCTIONS;
122 
123  bool RunOnDevice() override;
124 
125  protected:
126  INPUT_TAGS(X_IN, Y_IN);
127  OUTPUT_TAGS(DOT_OUT);
128 };
129 
130 template <typename T, class Context>
131 class DotProductGradientOp final : public Operator<Context> {
132  public:
133  DotProductGradientOp(const OperatorDef& def, Workspace* ws)
134  : Operator<Context>(def, ws) {}
135  USE_OPERATOR_CONTEXT_FUNCTIONS;
136 
137  bool RunOnDevice() override;
138 
139  protected:
140  INPUT_TAGS(X_IN, Y_IN, DER_DOT_IN);
141  OUTPUT_TAGS(DER_X_OUT, DER_Y_OUT);
142 };
143 
144 template <typename T, class Context>
145 class DotProductWithPaddingOp : public Operator<Context> {
146  public:
147  DotProductWithPaddingOp(const OperatorDef& def, Workspace* ws)
148  : Operator<Context>(def, ws),
149  pad_value_(OperatorBase::GetSingleArgument<float>("pad_value", 0.0)),
150  replicate_(OperatorBase::GetSingleArgument<bool>("replicate", false)) {}
151  USE_OPERATOR_CONTEXT_FUNCTIONS;
152 
153  bool RunOnDevice() override;
154 
155  protected:
156  float pad_value_;
157  bool replicate_;
158  INPUT_TAGS(X_IN, Y_IN);
159  OUTPUT_TAGS(DOT_OUT);
160 };
161 
162 template <typename T, class Context>
163 class CosineSimilarityOp : public Operator<Context> {
164  public:
165  CosineSimilarityOp(const OperatorDef& def, Workspace* ws)
166  : Operator<Context>(def, ws) {}
167  USE_OPERATOR_CONTEXT_FUNCTIONS;
168 
169  bool RunOnDevice() override;
170 
171  protected:
172  INPUT_TAGS(X_IN, Y_IN);
173  OUTPUT_TAGS(COS_OUT);
174 
175  private:
176  Tensor<Context> aux_;
177 };
178 
179 template <typename T, class Context>
180 class CosineSimilarityGradientOp final : public Operator<Context> {
181  public:
182  CosineSimilarityGradientOp(const OperatorDef& def, Workspace* ws)
183  : Operator<Context>(def, ws) {}
184  USE_OPERATOR_CONTEXT_FUNCTIONS;
185 
186  bool RunOnDevice() override;
187 
188  protected:
189  INPUT_TAGS(X_IN, Y_IN, DER_COS_IN);
190  OUTPUT_TAGS(DER_X_OUT, DER_Y_OUT);
191 
192  private:
193  Tensor<Context> aux_;
194 };
195 
196 template <typename T, class Context>
197 class DotProductWithPaddingGradientOp final : public Operator<Context> {
198  public:
199  DotProductWithPaddingGradientOp(const OperatorDef& def, Workspace* ws)
200  : Operator<Context>(def, ws),
201  pad_value_(OperatorBase::GetSingleArgument<float>("pad_value", 0.0)),
202  replicate_(OperatorBase::GetSingleArgument<bool>("replicate", false)) {}
203  USE_OPERATOR_CONTEXT_FUNCTIONS;
204 
205  bool RunOnDevice() override {
206  auto& X = Input(X_IN);
207  auto& Y = Input(Y_IN);
208  auto& dDot = Input(DER_DOT_IN);
209  auto* dX = Output(DER_X_OUT);
210  auto* dY = Output(DER_Y_OUT);
211  int N, D, DX, DY, restD;
212  if (X.size() > 0) {
213  N = X.ndim() > 0 ? X.dim32(0) : 1;
214  DX = X.size() / N;
215  DY = Y.size() / N;
216  } else {
217  N = 0;
218  DX = 0;
219  DY = 0;
220  }
221  CAFFE_ENFORCE(!replicate_ || DX % DY == 0 || DY % DX == 0);
222  D = std::min(DX, DY);
223  restD = std::max(DX, DY) - D;
224  CAFFE_ENFORCE_EQ(X.ndim(), Y.ndim());
225  CAFFE_ENFORCE_EQ(X.dim32(0), Y.dim32(0));
226  CAFFE_ENFORCE_EQ(dDot.ndim(), 1);
227  CAFFE_ENFORCE_EQ(dDot.dim32(0), N);
228  dX->ResizeLike(X);
229  dY->ResizeLike(Y);
230 
231  const auto* X_data = X.template data<T>();
232  const auto* Y_data = Y.template data<T>();
233  const auto* dDot_data = dDot.template data<T>();
234  auto* dX_data = dX->template mutable_data<T>();
235  auto* dY_data = dY->template mutable_data<T>();
236  for (int i = 0; i < N; ++i) { // TODO: multithreading
237  auto offsetX = i * DX;
238  auto offsetY = i * DY;
239  if (replicate_) {
240  // L_ for longer vector and S_ for shorter vector
241  const T *L_data, *S_data;
242  T *dL_data, *dS_data;
243  int DL, DS;
244  if (DX > DY) {
245  L_data = X_data + offsetX;
246  S_data = Y_data + offsetY;
247  dL_data = dX_data + offsetX;
248  dS_data = dY_data + offsetY;
249  DL = DX;
250  DS = DY;
251  } else {
252  L_data = Y_data + offsetY;
253  S_data = X_data + offsetX;
254  dL_data = dY_data + offsetY;
255  dS_data = dX_data + offsetX;
256  DL = DY;
257  DS = DX;
258  }
259 
260  // TODO: get rid of temp memory use
261  std::vector<T> tmp_data(DS);
262  math::Set<T, Context>(DS, 0.0, dS_data, &context_);
263  for (int j = 0; j < DL / DS; j++) {
264  math::Scale<T, Context>(
265  DS, dDot_data[i], S_data, dL_data + j * DS, &context_);
266  math::Scale<T, Context>(
267  DS, dDot_data[i], L_data + j * DS, tmp_data.data(), &context_);
268  math::Axpy<T, Context>(DS, 1.0, tmp_data.data(), dS_data, &context_);
269  }
270  } else {
271  math::Scale<T, Context>(
272  D, dDot_data[i], X_data + offsetX, dY_data + offsetY, &context_);
273  math::Scale<T, Context>(
274  D, dDot_data[i], Y_data + offsetY, dX_data + offsetX, &context_);
275  }
276 
277  if (!replicate_ && DX != DY) {
278  T* rest_data;
279  if (DX > DY) {
280  rest_data = dX_data + offsetX + D;
281  } else {
282  rest_data = dY_data + offsetY + D;
283  }
284  auto pad_gradient = dDot_data[i] * pad_value_;
285  math::Set<T, Context>(restD, pad_gradient, rest_data, &context_);
286  }
287  }
288 
289  return true;
290  }
291 
292  protected:
293  float pad_value_;
294  bool replicate_;
295  INPUT_TAGS(X_IN, Y_IN, DER_DOT_IN);
296  OUTPUT_TAGS(DER_X_OUT, DER_Y_OUT);
297 };
298 
299 } // namespace caffe2
300 
301 #endif // CAFFE2_OPERATORS_DISTANCE_OP_H_
Tensor is the basic class in Caffe2 that stores a contiguous memory with its shape information...
Definition: tensor.h:109
Workspace is a class that holds all the related objects created during runtime: (1) all blobs...
Definition: workspace.h:63
Copyright (c) 2016-present, Facebook, Inc.