MiniDNN
MultiClassEntropy.h
1 #ifndef OUTPUT_MULTICLASSENTROPY_H_
2 #define OUTPUT_MULTICLASSENTROPY_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  // Each column has and only has one 1
27  const int nobs = target.cols();
28  const int nclass = target.rows();
29  for(int i = 0; i < nobs; i++)
30  {
31  int one = 0;
32  for(int j = 0; j < nclass; j++)
33  {
34  if(target(j, i) == Scalar(1))
35  {
36  one++;
37  continue;
38  }
39  if(target(j, i) != Scalar(0))
40  throw std::invalid_argument("[class MultiClassEntropy]: Target data should only contain zero or one");
41  }
42  if(one != 1)
43  throw std::invalid_argument("[class MultiClassEntropy]: Each column of target data should only contain one \"1\"");
44  }
45  }
46 
47  void check_target_data(const IntegerVector& target)
48  {
49  // All elements must be non-negative
50  const int nobs = target.size();
51  for(int i = 0; i < nobs; i++)
52  {
53  if(target[i] < 0)
54  throw std::invalid_argument("[class MultiClassEntropy]: Target data must be non-negative");
55  }
56  }
57 
58  // target is a matrix with each column representing an observation
59  // Each column is a vector that has a one at some location and has zeros elsewhere
60  void evaluate(const Matrix& prev_layer_data, const Matrix& target)
61  {
62  // Check dimension
63  const int nobs = prev_layer_data.cols();
64  const int nclass = prev_layer_data.rows();
65  if((target.cols() != nobs) || (target.rows() != nclass))
66  throw std::invalid_argument("[class MultiClassEntropy]: Target data have incorrect dimension");
67 
68  // Compute the derivative of the input of this layer
69  // L = -sum(log(phat) * y)
70  // in = phat
71  // d(L) / d(in) = -y / phat
72  m_din.resize(nclass, nobs);
73  m_din.noalias() = -target.cwiseQuotient(prev_layer_data);
74  }
75 
76  // target is a vector of class labels that take values from [0, 1, ..., nclass - 1]
77  // The i-th element of target is the class label for observation i
78  void evaluate(const Matrix& prev_layer_data, const IntegerVector& target)
79  {
80  // Check dimension
81  const int nobs = prev_layer_data.cols();
82  const int nclass = prev_layer_data.rows();
83  if(target.size() != nobs)
84  throw std::invalid_argument("[class MultiClassEntropy]: Target data have incorrect dimension");
85 
86  // Compute the derivative of the input of this layer
87  // L = -log(phat[y])
88  // in = phat
89  // d(L) / d(in) = [0, 0, ..., -1/phat[y], 0, ..., 0]
90  m_din.resize(nclass, nobs);
91  m_din.setZero();
92  for(int i = 0; i < nobs; i++)
93  {
94  m_din(target[i], i) = -Scalar(1) / prev_layer_data(target[i], i);
95  }
96  }
97 
98  const Matrix& backprop_data() const
99  {
100  return m_din;
101  }
102 
103  Scalar loss() const
104  {
105  // L = -sum(log(phat) * y)
106  // in = phat
107  // d(L) / d(in) = -y / phat
108  // m_din contains 0 if y = 0, and -1/phat if y = 1
109  Scalar res = Scalar(0);
110  const int nelem = m_din.size();
111  const Scalar* din_data = m_din.data();
112  for(int i = 0; i < nelem; i++)
113  {
114  if(din_data[i] < Scalar(0))
115  res += std::log(-din_data[i]);
116  }
117 
118  return res / m_din.cols();
119  }
120 };
121 
122 
123 } // namespace MiniDNN
124 
125 
126 #endif /* OUTPUT_MULTICLASSENTROPY_H_ */