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  int N = X.numel();
33  CAFFE_ENFORCE_EQ(T.numel(), N);
34  auto* L = Output(0, X.sizes(), at::dtype<float>()); // JSD loss output
35  auto* x_data = X.data<float>();
36  auto* t_data = T.data<float>();
37  auto* l_data = L->template mutable_data<float>();
38  for (int i = 0; i < N; i++) {
39  auto p_mdl = x_data[i];
40  auto p_emp = t_data[i];
41  auto p_avg = (p_mdl + p_emp) / 2.;
42  auto jsd = entropy(p_avg) - (entropy(p_mdl) + entropy(p_emp)) / 2.;
43  l_data[i] = jsd;
44  }
45  return true;
46 }
47 
48 template <>
49 bool BernoulliJSDGradientOp<float, CPUContext>::RunOnDevice() {
50  auto& go = Input(0);
51  auto& X = Input(1);
52  auto& T = Input(2);
53 
54  int N = X.numel();
55  auto* gi = Output(0, X.sizes(), at::dtype<float>());
56  auto* go_data = go.data<float>();
57  auto* x_data = X.data<float>();
58  auto* t_data = T.data<float>();
59  auto* gi_data = gi->template mutable_data<float>();
60  for (int i = 0; i < N; i++) {
61  auto p_mdl = x_data[i];
62  auto p_emp = t_data[i];
63  auto p_avg = (p_mdl + p_emp) / 2.;
64  auto g_jsd = (logit(p_mdl) - logit(p_avg)) / 2.;
65  gi_data[i] = go_data[i] * g_jsd;
66  }
67  return true;
68 }
69 REGISTER_CPU_OPERATOR(BernoulliJSD, BernoulliJSDOp<float, CPUContext>);
70 REGISTER_CPU_OPERATOR(
71  BernoulliJSDGradient,
72  BernoulliJSDGradientOp<float, CPUContext>);
73 OPERATOR_SCHEMA(BernoulliJSD)
74  .NumInputs(2)
75  .NumOutputs(1)
76  .SetDoc(R"DOC(
77 Computes the Jensen-Shannon divergence (JSD) between two Bernoulli distributions
78 where each is parametrized by a single probability.
79 )DOC")
80  .Input(0, "X", "array of probabilities for prediction")
81  .Input(0, "T", "array of probabilities for target")
82  .Output(0, "L", "array of JSD losses");
83 OPERATOR_SCHEMA(BernoulliJSDGradient).NumInputs(3).NumOutputs(1);
84 
86  using GradientMakerBase::GradientMakerBase;
87  vector<OperatorDef> GetGradientDefs() override {
88  return SingleGradientDef(
89  "BernoulliJSDGradient",
90  "",
91  vector<string>{GO(0), I(0), I(1)},
92  vector<string>{GI(0)});
93  }
94 };
95 REGISTER_GRADIENT(BernoulliJSD, GetBernoulliJSDGradient);
96 
97 } // namespace caffe2
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...
Definition: blob.h:13
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 ...