Caffe2 - C++ API
A deep learning, cross platform ML framework
jsd_op.cc
1 #include "caffe2/operators/jsd_op.h"
2 
3 namespace caffe2 {
4 
5 namespace {
6 
7 static constexpr float kLOG_THRESHOLD() {
8  return 1e-20;
9 }
10 
11 inline float logit(float p) {
12  // it computes log(p / (1-p))
13  // to avoid numeric issue, hard code p log(p) when p approaches 0
14  float x = std::min(std::max(p, kLOG_THRESHOLD()), 1 - kLOG_THRESHOLD());
15  return -log(1. / x - 1.);
16 }
17 
18 inline float entropy(float p) {
19  if (p < kLOG_THRESHOLD() || 1 - p < kLOG_THRESHOLD()) {
20  return 0.;
21  } else {
22  float q = 1 - p;
23  return -p * log(p) - q * log(q);
24  }
25 }
26 } // namespace
27 
28 template <>
29 bool BernoulliJSDOp<float, CPUContext>::RunOnDevice() {
30  auto& X = Input(0); // predicted probabilities
31  auto& T = Input(1); // target probabilities
32  auto* L = Output(0); // JSD loss output
33  int N = X.size();
34  CAFFE_ENFORCE_EQ(T.size(), N);
35  L->ResizeLike(X);
36  auto* x_data = X.data<float>();
37  auto* t_data = T.data<float>();
38  auto* l_data = L->mutable_data<float>();
39  for (int i = 0; i < N; i++) {
40  auto p_mdl = x_data[i];
41  auto p_emp = t_data[i];
42  auto p_avg = (p_mdl + p_emp) / 2.;
43  auto jsd = entropy(p_avg) - (entropy(p_mdl) + entropy(p_emp)) / 2.;
44  l_data[i] = jsd;
45  }
46  return true;
47 }
48 
49 template <>
50 bool BernoulliJSDGradientOp<float, CPUContext>::RunOnDevice() {
51  auto& go = Input(0);
52  auto& X = Input(1);
53  auto& T = Input(2);
54  auto* gi = Output(0);
55  int N = X.size();
56  gi->ResizeLike(X);
57  auto* go_data = go.data<float>();
58  auto* x_data = X.data<float>();
59  auto* t_data = T.data<float>();
60  auto* gi_data = gi->mutable_data<float>();
61  for (int i = 0; i < N; i++) {
62  auto p_mdl = x_data[i];
63  auto p_emp = t_data[i];
64  auto p_avg = (p_mdl + p_emp) / 2.;
65  auto g_jsd = (logit(p_mdl) - logit(p_avg)) / 2.;
66  gi_data[i] = go_data[i] * g_jsd;
67  }
68  return true;
69 }
70 REGISTER_CPU_OPERATOR(BernoulliJSD, BernoulliJSDOp<float, CPUContext>);
71 REGISTER_CPU_OPERATOR(
72  BernoulliJSDGradient,
73  BernoulliJSDGradientOp<float, CPUContext>);
74 OPERATOR_SCHEMA(BernoulliJSD)
75  .NumInputs(2)
76  .NumOutputs(1)
77  .SetDoc(R"DOC(
78 Computes the Jensen-Shannon divergence (JSD) between two Bernoulli distributions
79 where each is parametrized by a single probability.
80 )DOC")
81  .Input(0, "X", "array of probabilities for prediction")
82  .Input(0, "T", "array of probabilities for target")
83  .Output(0, "L", "array of JSD losses");
84 OPERATOR_SCHEMA(BernoulliJSDGradient).NumInputs(3).NumOutputs(1);
85 
87  using GradientMakerBase::GradientMakerBase;
88  vector<OperatorDef> GetGradientDefs() override {
89  return SingleGradientDef(
90  "BernoulliJSDGradient",
91  "",
92  vector<string>{GO(0), I(0), I(1)},
93  vector<string>{GI(0)});
94  }
95 };
96 REGISTER_GRADIENT(BernoulliJSD, GetBernoulliJSDGradient);
97 
98 } // namespace caffe2
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...
static vector< OperatorDef > SingleGradientDef(const Args &...args)
a helper function to allow one to create one single operator def, which is usually the case for many ...