Feat C++ API
A feature engineering automation tool
metrics.cc
Go to the documentation of this file.
1 /* FEAT
2 copyright 2017 William La Cava
3 license: GNU/GPL v3
4 */
5 
6 #include "metrics.h"
7 
8 namespace FT
9 {
10 
11  using namespace Util;
12 
13  namespace Eval
14  {
15  /* Scoring functions */
16 
17  // Squared difference
18  VectorXf squared_difference(const VectorXf& y, const VectorXf& yhat)
19  {
20  return (yhat - y).array().pow(2);
21  }
22 
23  VectorXf squared_difference(const VectorXf& y,
24  shared_ptr<CLabels>& labels,
25  const vector<float>& weights)
26  {
27  SGVector<double> _tmp =
28  dynamic_pointer_cast<sh::CRegressionLabels>(labels)->get_labels();
29  SGVector<float> tmp(_tmp.begin(), _tmp.end());
30 
31  Map<VectorXf> yhat(tmp.data(),tmp.size());
32  return squared_difference(y,yhat);
33  /* if (weights.empty()) */
34  /* return (yhat - y).array().pow(2); */
35  /* else */
36  /* { */
37  /* assert(weights.size() == yhat.size()); */
38  /* ArrayXf w = ArrayXf::Map(weights.data(),weights.size()); */
39  /* return (yhat - y).array().pow(2) * w.cast<float>() ; */
40  /* } */
41  }
42 
43  // Derivative of squared difference with respec to yhat
44  VectorXf d_squared_difference(const VectorXf& y, const VectorXf& yhat)
45  {
46  return 2 * (yhat - y);
47  }
48 
49  // Derivative of squared difference with respec to yhat
50  VectorXf d_squared_difference(const VectorXf& y,
51  shared_ptr<CLabels>& labels, const vector<float>& weights)
52  {
53  SGVector<double> _tmp =
54  dynamic_pointer_cast<sh::CRegressionLabels>(labels)->get_labels();
55  SGVector<float> tmp(_tmp.begin(), _tmp.end());
56  Map<VectorXf> yhat(tmp.data(),tmp.size());
57 
58  return d_squared_difference(y,yhat);
59  /* if (weights.empty()) */
60  /* return 2 * (yhat - y); */
61  /* else */
62  /* { */
63  /* assert(weights.size() == yhat.size()); */
64  /* ArrayXf w = ArrayXf::Map(weights.data(),weights.size()); */
65  /* return 2 * (yhat - y).array() * w.cast<float>() ; */
66  /* } */
67  }
68 
70  float mse(const VectorXf& y, const VectorXf& yhat, VectorXf& loss,
71  const vector<float>& weights)
72  {
73  loss = (yhat - y).array().pow(2);
74  return loss.mean();
75  }
76 
77  float mse_label(const VectorXf& y,
78  const shared_ptr<CLabels>& labels, VectorXf& loss,
79  const vector<float>& weights)
80  {
81  SGVector<double> _tmp =
82  dynamic_pointer_cast<sh::CRegressionLabels>(labels)->get_labels();
83  SGVector<float> tmp(_tmp.begin(), _tmp.end());
84  Map<VectorXf> yhat(tmp.data(),tmp.size());
85  return mse(y,yhat,loss,weights);
86  }
87 
88  VectorXf log_loss(const VectorXf& y, const VectorXf& yhat,
89  const vector<float>& class_weights)
90  {
91  float eps = pow(10,-10);
92 
93  VectorXf loss;
94 
95  float sum_weights = 0;
96  loss.resize(y.rows());
97  for (unsigned i = 0; i < y.rows(); ++i)
98  {
99  if (yhat(i) < eps || 1 - yhat(i) < eps)
100  // clip probabilities since log loss is undefined for yhat=0 or yhat=1
101  loss(i) = -(y(i)*log(eps) + (1-y(i))*log(1-eps));
102  else
103  loss(i) = -(y(i)*log(yhat(i)) + (1-y(i))*log(1-yhat(i)));
104  if (loss(i)<0)
105  THROW_RUNTIME_ERROR("loss(i)= " + to_string(loss(i))
106  + ". y = " + to_string(y(i)) + ", yhat(i) = "
107  + to_string(yhat(i)));
108 
109  if (!class_weights.empty())
110  {
111  loss(i) = loss(i) * class_weights.at(y(i));
112  sum_weights += class_weights.at(y(i));
113  }
114  }
115 
116  if (sum_weights > 0)
117  loss = loss.array() / sum_weights * y.size(); // normalize weight contributions
118 
119  return loss;
120  }
121 
122  VectorXf log_loss(const VectorXf& y, shared_ptr<CLabels>& labels,
123  const vector<float>& class_weights)
124  {
125  SGVector<double> _tmp =
126  dynamic_pointer_cast<sh::CBinaryLabels>(labels)->get_values();
127  SGVector<float> tmp(_tmp.begin(), _tmp.end());
128 
129  Map<VectorXf> yhat(tmp.data(),tmp.size());
130 
131  VectorXf loss = log_loss(y,yhat,class_weights);
132  return loss;
133  }
134 
136  float mean_log_loss(const VectorXf& y,
137  const VectorXf& yhat, VectorXf& loss,
138  const vector<float>& class_weights)
139  {
140 
141  /* std::cout << "loss: " << loss.transpose() << "\n"; */
142  loss = log_loss(y,yhat,class_weights);
143  return loss.mean();
144  }
145 
147  float log_loss_label(const VectorXf& y,
148  const shared_ptr<CLabels>& labels, VectorXf& loss,
149  const vector<float>& class_weights)
150  {
151  SGVector<double> _tmp =
152  dynamic_pointer_cast<sh::CBinaryLabels>(labels)->get_values();
153  SGVector<float> tmp(_tmp.begin(), _tmp.end());
154  Map<VectorXf> yhat(tmp.data(),tmp.size());
155  /* cout << "log loss yhat: " << yhat.transpose() << "\n"; */
156  return mean_log_loss(y,yhat,loss,class_weights);
157  }
158 
159  VectorXf d_log_loss(const VectorXf& y, const VectorXf& yhat,
160  const vector<float>& class_weights)
161  {
162  VectorXf dll(y.size());
163  for (int i = 0; i < y.size(); ++i)
164  {
165  /* dll(i) = (-(yhat(i) - y(i)) / ( (yhat(i)-1)*yhat(i) ) ); */
166  // note this derivative assumes a logistic form for yhat, i.e. yhat = 1/(1+exp(-o))
167  dll(i) = (yhat(i) - y(i));
168  if (!class_weights.empty())
169  dll(i) = dll(i) * class_weights.at(y(i));
170  }
171  return dll;
172  }
173 
174  VectorXf d_log_loss(const VectorXf& y, shared_ptr<CLabels>& labels,
175  const vector<float>& class_weights)
176  {
177  SGVector<double> _tmp =
178  dynamic_pointer_cast<sh::CBinaryLabels>(labels)->get_values();
179  SGVector<float> tmp(_tmp.begin(), _tmp.end());
180  Map<VectorXf> yhat(tmp.data(),tmp.size());
181 
182  return d_log_loss(y,yhat,class_weights);
183  }
184 
185  /* float weighted_average(const VectorXf& y, const VectorXf& w) */
186  /* { */
187  /* w = w.array() / w.sum(); */
188  /* return (y.array() * w.array()).mean(); */
189  /* } */
191  VectorXf multi_log_loss(const VectorXf& y, const ArrayXXf& confidences,
192  const vector<float>& class_weights)
193  {
194  VectorXf loss = VectorXf::Zero(y.rows());
195 
196  // get class labels
197  vector<float> uc = unique(y);
198 
199  float eps = pow(10,-10);
200  float sum_weights = 0;
201  for (unsigned i = 0; i < y.rows(); ++i)
202  {
203  for (const auto& c : uc)
204  {
205  ArrayXf yhat = confidences.col(int(c));
206  /* std::cout << "class " << c << "\n"; */
207 
208  /* float yi = y(i) == c ? 1.0 : 0.0 ; */
209  /* std::cout << "yi: " << yi << ", yhat(" << i << "): " << yhat(i) ; */
210  if (y(i) == c)
211  {
212  if (yhat(i) < eps || 1 - yhat(i) < eps)
213  {
214  // clip probabilities since log loss is undefined for yhat=0 or yhat=1
215  loss(i) += -log(eps);
216  }
217  else
218  {
219  loss(i) += -log(yhat(i));
220  }
221  /* std::cout << ", loss(" << i << ") = " << loss(i); */
222  }
223  /* std::cout << "\n"; */
224  }
225  if (!class_weights.empty()){
226  /* std::cout << "weights.at(y(" << i << ")): " << class_weights.at(y(i)) << "\n"; */
227  loss(i) = loss(i)*class_weights.at(y(i));
228  sum_weights += class_weights.at(y(i));
229  }
230  }
231  if (sum_weights > 0)
232  loss = loss.array() / sum_weights * y.size();
233  /* cout << "loss.mean(): " << loss.mean() << "\n"; */
234  /* cout << "loss.sum(): " << loss.sum() << "\n"; */
235  return loss;
236  }
237 
238  float mean_multi_log_loss(const VectorXf& y,
239  const ArrayXXf& confidences, VectorXf& loss,
240  const vector<float>& class_weights)
241  {
242  loss = multi_log_loss(y, confidences, class_weights);
243 
244  /* std::cout << "loss: " << loss.transpose() << "\n"; */
245  /* std::cout << "mean loss: " << loss.mean() << "\n"; */
246  return loss.mean();
247  }
248 
250  float multi_log_loss_label(const VectorXf& y,
251  const shared_ptr<CLabels>& labels, VectorXf& loss,
252  const vector<float>& class_weights)
253  {
254  ArrayXXf confidences(y.size(),unique(y).size());
255  /* std::cout << "confidences:\n"; */
256  for (unsigned i =0; i<y.size(); ++i)
257  {
258  SGVector<double> _tmp =
259  dynamic_pointer_cast<sh::CMulticlassLabels>(labels)->
260  get_multiclass_confidences(int(i));
261  SGVector<float> tmp(_tmp.begin(), _tmp.end());
262  if (confidences.cols() != tmp.size())
263  {
264  /* WARN("mismatch in confidences: expected " */
265  /* + to_string(confidences.cols()) */
266  /* + " values, got " */
267  /* + to_string(tmp.size()) */
268  /* + " from labels"); */
269  confidences.row(i) = 0;
270  }
271  else
272  confidences.row(i) = Map<ArrayXf>(tmp.data(),tmp.size());
273  /* std::cout << confidences.row(i) << "\n"; */
274  }
275  /* for (auto c : uc) */
276  /* std::cout << "class " << int(c) << ": " << confidences.col(int(c)).transpose() << "\n"; */
277  /* std::cout << "in log loss\n"; */
278 
279  return mean_multi_log_loss(y, confidences, loss, class_weights);
280 
281  }
282 
283  VectorXf multi_log_loss(const VectorXf& y, shared_ptr<CLabels>& labels,
284  const vector<float>& class_weights)
285  {
286  ArrayXXf confidences(y.size(),unique(y).size());
287  /* std::cout << "confidences:\n"; */
288  for (unsigned i =0; i<y.size(); ++i)
289  {
290  SGVector<double> _tmp =
291  dynamic_pointer_cast<sh::CMulticlassLabels>(labels)->
292  get_multiclass_confidences(int(i));
293 
294  SGVector<float> tmp(_tmp.begin(), _tmp.end());
295  confidences.row(i) = Map<ArrayXf>(tmp.data(),tmp.size());
296  /* std::cout << confidences.row(i) << "\n"; */
297  }
298 
299  VectorXf loss = multi_log_loss(y, confidences, class_weights);
300  return loss;
301  }
303  VectorXf d_multi_log_loss(const VectorXf& y,
304  shared_ptr<CLabels>& labels,
305  const vector<float>& class_weights)
306  {
307  ArrayXXf confidences(y.size(),unique(y).size());
308  /* std::cout << "confidences:\n"; */
309  for (unsigned i =0; i<y.size(); ++i)
310  {
311  SGVector<double> _tmp =
312  dynamic_pointer_cast<sh::CMulticlassLabels>(labels)->
313  get_multiclass_confidences(int(i));
314  SGVector<float> tmp(_tmp.begin(), _tmp.end());
315  confidences.row(i) = Map<ArrayXf>(tmp.data(),tmp.size());
316  /* std::cout << confidences.row(i) << "\n"; */
317  }
318 
319  VectorXf loss = VectorXf::Zero(y.rows());
320 
321  // get class labels
322  vector<float> uc = unique(y);
323 
324  float eps = pow(10,-10);
325  float sum_weights = 0;
326  for (unsigned i = 0; i < y.rows(); ++i)
327  {
328  for (const auto& c : uc)
329  {
330  ArrayXf yhat = confidences.col(int(c));
331  /* std::cout << "class " << c << "\n"; */
332 
333  /* float yi = y(i) == c ? 1.0 : 0.0 ; */
334  /* std::cout << "yi: " << yi << ", yhat(" << i << "): " << yhat(i) ; */
335  if (y(i) == c)
336  {
337  if (yhat(i) < eps)
338  {
339  // clip probabilities since log loss is undefined for yhat=0 or yhat=1
340  loss(i) += -1/eps;
341  /* std::cout << ", loss(" << i << ") += " << -yi*log(eps); */
342  }
343  else
344  {
345  loss(i) += -1/yhat(i);
346  /* std::cout << ", loss(" << i << ") += " << -yi*log(yhat(i)); */
347  }
348  }
349  /* std::cout << "\n"; */
350  }
351  if (!class_weights.empty())
352  {
353  /* std::cout << "w.at(y(" << i << ")): " << w.at(y(i)) << "\n"; */
354  loss(i) = loss(i)*class_weights.at(y(i));
355  sum_weights += class_weights.at(y(i));
356  /* std::cout << "w.at(y(" << i << ")): " << w.at(y(i)) << "\n"; */
357  /* loss(i) = loss(i)*w.at(i); */
358  }
359  }
360  if (sum_weights > 0)
361  loss = loss.array() / sum_weights * y.size(); // normalize weight contributions
362 
363  return loss;
364  }
366  float bal_zero_one_loss(const VectorXf& y, const VectorXf& yhat,
367  VectorXf& loss, const vector<float>& class_weights)
368  {
369  vector<float> uc = unique(y);
370  vector<int> c;
371  for (const auto& i : uc)
372  c.push_back(int(i));
373 
374  // sensitivity (TP) and specificity (TN)
375  vector<float> TP(c.size(),0.0), TN(c.size(), 0.0), P(c.size(),0.0), N(c.size(),0.0);
376  ArrayXf class_accuracies(c.size());
377 
378  // get class counts
379 
380  for (unsigned i=0; i< c.size(); ++i)
381  {
382  P.at(i) = (y.array().cast<int>() == c.at(i)).count(); // total positives for this class
383  N.at(i) = (y.array().cast<int>() != c.at(i)).count(); // total negatives for this class
384  }
385 
386 
387  for (unsigned i = 0; i < y.rows(); ++i)
388  {
389  if (yhat(i) == y(i)) // true positive
390  ++TP.at(y(i) == -1 ? 0 : y(i)); // if-then ? accounts for -1 class encoding
391 
392  for (unsigned j = 0; j < c.size(); ++j)
393  if ( y(i) !=c.at(j) && yhat(i) != c.at(j) ) // true negative
394  ++TN.at(j);
395 
396  }
397 
398  // class-wise accuracy = 1/2 ( true positive rate + true negative rate)
399  for (unsigned i=0; i< c.size(); ++i){
400  class_accuracies(i) = (TP.at(i)/P.at(i) + TN.at(i)/N.at(i))/2;
401  //std::cout << "TP(" << i << "): " << TP.at(i) << ", P[" << i << "]: " << P.at(i) << "\n";
402  //std::cout << "TN(" << i << "): " << TN.at(i) << ", N[" << i << "]: " << N.at(i) << "\n";
403  //std::cout << "class accuracy(" << i << "): " << class_accuracies(i) << "\n";
404  }
405 
406  // set loss vectors if third argument supplied
407  loss = (yhat.cast<int>().array() != y.cast<int>().array()).cast<float>();
408 
409  return 1.0 - class_accuracies.mean();
410  }
411 
412  float bal_zero_one_loss_label(const VectorXf& y,
413  const shared_ptr<CLabels>& labels,
414  VectorXf& loss, const vector<float>& class_weights)
415  {
416 
417  SGVector<double> _tmp;
418 
419  auto ptrmulticast = dynamic_pointer_cast<sh::CMulticlassLabels>(labels);
420 
421  if(ptrmulticast == NULL)
422  {
423  auto ptrbinary = dynamic_pointer_cast<sh::CBinaryLabels>(labels);
424  _tmp = ptrbinary->get_labels();
425  }
426  else
427  _tmp = ptrmulticast->get_labels();
428 
429  SGVector<float> tmp(_tmp.begin(), _tmp.end());
430  Map<VectorXf> yhat(tmp.data(),tmp.size());
431 
432  return bal_zero_one_loss(y, yhat, loss, class_weights);
433  }
434 
435  float zero_one_loss(const VectorXf& y, const VectorXf& yhat, VectorXf& loss,
436  const vector<float>& class_weights)
437  {
438  loss = (yhat.cast<int>().array() != y.cast<int>().array()).cast<float>();
439  //TODO: weight loss by sample weights
440  return loss.mean();
441  }
442 
444  float zero_one_loss_label(const VectorXf& y,
445  const shared_ptr<CLabels>& labels, VectorXf& loss,
446  const vector<float>& class_weights)
447  {
448  SGVector<double> _tmp =
449  dynamic_pointer_cast<sh::CBinaryLabels>(labels)->get_labels();
450  SGVector<float> tmp(_tmp.begin(), _tmp.end());
451  Map<VectorXf> yhat(tmp.data(),tmp.size());
452 
453  return zero_one_loss(y,yhat,loss,class_weights);
454  }
455  // make a false positive rate loss
456  //
457  float false_positive_loss(const VectorXf& y,
458  const VectorXf& yhat, VectorXf& loss,
459  const vector<float>& class_weights)
460  {
461  ArrayXb ybool = y.cast<bool>();
462  /* ArrayXb noty = !ybool; */
463  loss = (yhat.cast<bool>().select(
464  !ybool, false)).cast<float>();
465  return loss.sum()/float((y.size() - ybool.count()));
466  }
468  float false_positive_loss_label(const VectorXf& y,
469  const shared_ptr<CLabels>& labels, VectorXf& loss,
470  const vector<float>& class_weights)
471  {
472  SGVector<double> _tmp =
473  dynamic_pointer_cast<sh::CBinaryLabels>(labels)->get_labels();
474  SGVector<float> tmp(_tmp.begin(), _tmp.end());
475  Map<VectorXf> yhat(tmp.data(),tmp.size());
476 
477  return false_positive_loss(y,yhat,loss,class_weights);
478  }
479  } // metrics
480 } // FT
481 
482 
Eigen::Array< bool, Eigen::Dynamic, 1 > ArrayXb
Definition: data.h:21
#define THROW_RUNTIME_ERROR(err)
Definition: error.h:30
VectorXf multi_log_loss(const VectorXf &y, shared_ptr< CLabels > &labels, const vector< float > &class_weights)
Definition: metrics.cc:283
float bal_zero_one_loss_label(const VectorXf &y, const shared_ptr< CLabels > &labels, VectorXf &loss, const vector< float > &class_weights)
Definition: metrics.cc:412
VectorXf log_loss(const VectorXf &y, shared_ptr< CLabels > &labels, const vector< float > &class_weights)
Definition: metrics.cc:122
float false_positive_loss(const VectorXf &y, const VectorXf &yhat, VectorXf &loss, const vector< float > &class_weights)
Definition: metrics.cc:457
VectorXf squared_difference(const VectorXf &y, shared_ptr< CLabels > &labels, const vector< float > &weights)
Definition: metrics.cc:23
float zero_one_loss_label(const VectorXf &y, const shared_ptr< CLabels > &labels, VectorXf &loss, const vector< float > &class_weights)
1 - accuracy
Definition: metrics.cc:444
VectorXf d_log_loss(const VectorXf &y, shared_ptr< CLabels > &labels, const vector< float > &class_weights)
Definition: metrics.cc:174
float mse_label(const VectorXf &y, const shared_ptr< CLabels > &labels, VectorXf &loss, const vector< float > &weights)
Definition: metrics.cc:77
float multi_log_loss_label(const VectorXf &y, const shared_ptr< CLabels > &labels, VectorXf &loss, const vector< float > &class_weights)
multinomial log loss
Definition: metrics.cc:250
VectorXf d_squared_difference(const VectorXf &y, shared_ptr< CLabels > &labels, const vector< float > &weights)
Definition: metrics.cc:50
float mean_multi_log_loss(const VectorXf &y, const ArrayXXf &confidences, VectorXf &loss, const vector< float > &class_weights)
Definition: metrics.cc:238
float bal_zero_one_loss(const VectorXf &y, const VectorXf &yhat, VectorXf &loss, const vector< float > &class_weights)
1 - balanced accuracy
Definition: metrics.cc:366
float mean_log_loss(const VectorXf &y, const VectorXf &yhat, VectorXf &loss, const vector< float > &class_weights)
log loss
Definition: metrics.cc:136
float zero_one_loss(const VectorXf &y, const VectorXf &yhat, VectorXf &loss, const vector< float > &class_weights)
Definition: metrics.cc:435
float false_positive_loss_label(const VectorXf &y, const shared_ptr< CLabels > &labels, VectorXf &loss, const vector< float > &class_weights)
false positive rate
Definition: metrics.cc:468
float mse(const VectorXf &y, const VectorXf &yhat, VectorXf &loss, const vector< float > &weights)
mean squared error
Definition: metrics.cc:70
float log_loss_label(const VectorXf &y, const shared_ptr< CLabels > &labels, VectorXf &loss, const vector< float > &class_weights)
log loss
Definition: metrics.cc:147
VectorXf d_multi_log_loss(const VectorXf &y, shared_ptr< CLabels > &labels, const vector< float > &class_weights)
derivative of multinomial log loss
Definition: metrics.cc:303
vector< T > unique(vector< T > w)
returns unique elements in vector
Definition: utils.h:336
std::string to_string(const T &value)
template function to convert objects to string for logging
Definition: utils.h:422
main Feat namespace
Definition: data.cc:13
int i
Definition: params.cc:552