Caffe2 - C++ API
A deep learning, cross platform ML framework
margin_ranking_criterion_op.cc
1 
17 #include "caffe2/operators/margin_ranking_criterion_op.h"
18 
19 #include <algorithm>
20 
21 #include "caffe2/utils/math.h"
22 
23 namespace caffe2 {
24 
25 template <>
26 bool MarginRankingCriterionOp<CPUContext>::RunOnDevice() {
27  auto& X1 = Input(0);
28  auto& X2 = Input(1);
29  auto& Y = Input(2);
30  auto* loss = Output(0);
31  CAFFE_ENFORCE(
32  X1.size() == X2.size(),
33  "The two inputs for computing ranking loss should have the same size.");
34  CAFFE_ENFORCE(
35  X1.size() == Y.size(), "The input and label should have the same size.");
36  loss->ResizeLike(X1);
37 
38  const float* X1data = X1.data<float>();
39  const float* X2data = X2.data<float>();
40  const int* Ydata = Y.data<int>();
41  float* output = loss->mutable_data<float>();
42  for (int i = 0; i < X1.size(); ++i) {
43  output[i] = std::max(-Ydata[i] * (X1data[i] - X2data[i]) + margin_, 0.f);
44  }
45  return true;
46 }
47 
48 template <>
49 bool MarginRankingCriterionGradientOp<CPUContext>::RunOnDevice() {
50  auto& X1 = Input(0);
51  auto& X2 = Input(1);
52  auto& Y = Input(2);
53  auto& dLoss = Input(3);
54  auto* dX1 = Output(0);
55  auto* dX2 = Output(1);
56 
57  dX1->ResizeLike(X1);
58  dX2->ResizeLike(X2);
59 
60  const float* X1data = X1.data<float>();
61  const float* X2data = X2.data<float>();
62  const int* Ydata = Y.data<int>();
63  const float* dLoss_data = dLoss.data<float>();
64 
65  float* dX1_data = dX1->mutable_data<float>();
66  float* dX2_data = dX2->mutable_data<float>();
67  for (int i = 0; i < X1.size(); ++i) {
68  auto dist = -Ydata[i] * (X1data[i] - X2data[i]) + margin_;
69  if (dist < 0.f) {
70  dX1_data[i] = dX2_data[i] = 0.f;
71  } else {
72  dX1_data[i] = -Ydata[i] * dLoss_data[i];
73  dX2_data[i] = Ydata[i] * dLoss_data[i];
74  }
75  }
76  return true;
77 }
78 
79 REGISTER_CPU_OPERATOR(
80  MarginRankingCriterion,
81  MarginRankingCriterionOp<CPUContext>);
82 REGISTER_CPU_OPERATOR(
83  MarginRankingCriterionGradient,
84  MarginRankingCriterionGradientOp<CPUContext>);
85 
86 OPERATOR_SCHEMA(MarginRankingCriterion)
87  .NumInputs(3)
88  .NumOutputs(1)
89  .SetDoc(R"DOC(
90 MarginRankingCriterion takes two input data X1 (Tensor<float>),
91 X2 (Tensor<float>), and label Y (Tensor<int>) to produce the
92 loss (Tensor<float>) where the loss function,
93 loss(X1, X2, Y) = max(0, -Y * (X1 - X2) + margin), is applied to
94 the tensor elementwise.
95 
96 If y == 1 then it assumed the first input should be ranked higher
97 (have a larger value) than the second input, and vice-versa for
98 y == -1.
99 )DOC")
100  .Input(0, "X1", "The left input vector as a 1-dim TensorCPU.")
101  .Input(1, "X2", "The right input vector as a 1-dim TensorCPU.")
102  .Input(2, "Y", "The label as a 1-dim TensorCPU with int value of 1 or -1.")
103  .Output(0, "loss", "The output loss with the same dimensionality as X1.");
104 
105 OPERATOR_SCHEMA(MarginRankingCriterionGradient)
106  .NumInputs(4)
107  .NumOutputs(2)
108  .SetDoc(R"DOC(
109 MarginRankingCriterionGradient takes both X1, X2, Y and dY and
110 uses them to update dX1, and dX2 according to the chain rule
111 and derivatives of the loss function.
112 )DOC");
113 
114 class GetMarginRankingCriterionGradient : public GradientMakerBase {
115  using GradientMakerBase::GradientMakerBase;
116  vector<OperatorDef> GetGradientDefs() override {
117  return SingleGradientDef(
118  "MarginRankingCriterionGradient",
119  "",
120  vector<string>{I(0), I(1), I(2), GO(0)},
121  vector<string>{GI(0), GI(1)});
122  }
123 };
124 REGISTER_GRADIENT(MarginRankingCriterion, GetMarginRankingCriterionGradient);
125 
126 } // namespace caffe2
Copyright (c) 2016-present, Facebook, Inc.