MiniDNN
BinaryClassEntropy.h
1 #ifndef OUTPUT_BINARYCLASSENTROPY_H_
2 #define OUTPUT_BINARYCLASSENTROPY_H_
3 
4 #include <Eigen/Core>
5 #include <stdexcept>
6 #include "../Config.h"
7 
8 namespace MiniDNN {
9 
10 
17 {
18 private:
19  Matrix m_din; // Derivative of the input of this layer.
20  // Note that input of this layer is also the output of previous layer
21 
22 public:
23  void check_target_data(const Matrix& target)
24  {
25  // Each element should be either 0 or 1
26  const int nelem = target.size();
27  const Scalar* target_data = target.data();
28  for(int i = 0; i < nelem; i++)
29  {
30  if((target_data[i] != Scalar(0)) && (target_data[i] != Scalar(1)))
31  throw std::invalid_argument("[class BinaryClassEntropy]: Target data should only contain zero or one");
32  }
33  }
34 
35  void check_target_data(const IntegerVector& target)
36  {
37  // Each element should be either 0 or 1
38  const int nobs = target.size();
39  for(int i = 0; i < nobs; i++)
40  {
41  if((target[i] != 0) && (target[i] != 1))
42  throw std::invalid_argument("[class BinaryClassEntropy]: Target data should only contain zero or one");
43  }
44  }
45 
46  void evaluate(const Matrix& prev_layer_data, const Matrix& target)
47  {
48  // Check dimension
49  const int nobs = prev_layer_data.cols();
50  const int nvar = prev_layer_data.rows();
51  if((target.cols() != nobs) || (target.rows() != nvar))
52  throw std::invalid_argument("[class BinaryClassEntropy]: Target data have incorrect dimension");
53 
54  // Compute the derivative of the input of this layer
55  // L = -y * log(phat) - (1 - y) * log(1 - phat)
56  // in = phat
57  // d(L) / d(in) = -y / phat + (1 - y) / (1 - phat), y is either 0 or 1
58  m_din.resize(nvar, nobs);
59  m_din.array() = (target.array() < Scalar(0.5)).select((Scalar(1) - prev_layer_data.array()).cwiseInverse(),
60  -prev_layer_data.cwiseInverse());
61  }
62 
63  void evaluate(const Matrix& prev_layer_data, const IntegerVector& target)
64  {
65  // Only when the last hidden layer has only one unit can we use this version
66  const int nvar = prev_layer_data.rows();
67  if(nvar != 1)
68  throw std::invalid_argument("[class BinaryClassEntropy]: Only one response variable is allowed when class labels are used as target data");
69 
70  // Check dimension
71  const int nobs = prev_layer_data.cols();
72  if(target.size() != nobs)
73  throw std::invalid_argument("[class BinaryClassEntropy]: Target data have incorrect dimension");
74 
75  // Same as above
76  m_din.resize(1, nobs);
77  m_din.array() = (target.array() == 0).select((Scalar(1) - prev_layer_data.array()).cwiseInverse(),
78  -prev_layer_data.cwiseInverse());
79  }
80 
81  const Matrix& backprop_data() const
82  {
83  return m_din;
84  }
85 
86  Scalar loss() const
87  {
88  // L = -y * log(phat) - (1 - y) * log(1 - phat)
89  // y = 0 => L = -log(1 - phat)
90  // y = 1 => L = -log(phat)
91  // m_din contains 1/(1 - phat) if y = 0, and -1/phat if y = 1, so
92  // L = log(abs(m_din)).sum()
93  return m_din.array().abs().log().sum() / m_din.cols();
94  }
95 };
96 
97 
98 } // namespace MiniDNN
99 
100 
101 #endif /* OUTPUT_BINARYCLASSENTROPY_H_ */