/** * @file MutualInformation.cpp * @brief Part Selection with Mutual Information * @author Erik Rodner * @date 02/20/2008 */ #include #include "MutualInformation.h" #include "vislearning/baselib/Gnuplot.h" using namespace OBJREC; using namespace std; using namespace NICE; MutualInformation::MutualInformation( bool verbose ) { this->verbose = verbose; } MutualInformation::~MutualInformation() { } void MutualInformation::addStatistics ( const vector & v, size_t d, double threshold, size_t & ones ) const { for ( vector::const_iterator j = v.begin() ; j != v.end(); j++ ) if ( (*(*j))[d] > threshold ) ones++; } double MutualInformation::entropy ( size_t n1, size_t n2 ) const { // - p_1 log p_1 - p_2 log p_2 // aber log im bereich [0,1] numerisch instabil // daher // p_1 log 1/p_1 + p_2 log 1/p_2 double sum = n1 + n2; double log1 = n1 > 0 ? log( (double)n1) : 0; double log2 = n2 > 0 ? log( (double)n2) : 0; double logsum = (sum > 0) ? log(sum) : 0; return - (n1/sum*log1 + n2/sum*log2) + logsum; } double MutualInformation::mutualInformationOverall ( const LabeledSetVector & ls, size_t dimension, double threshold ) const { double entropy_conditional = 0.0; for ( LabeledSetVector::const_iterator i = ls.begin(); i != ls.end(); i++ ) { size_t ones = 0; addStatistics ( i->second, dimension, threshold, ones ); double entropy_conditional_class = entropy ( ones, i->second.size() - ones ); entropy_conditional += entropy_conditional_class; } entropy_conditional /= ls.size(); double entropy_joint = 0.0; size_t ones = 0; size_t count = 0; for ( LabeledSetVector::const_iterator i = ls.begin(); i != ls.end(); i++ ) { addStatistics ( i->second, dimension, threshold, ones ); count += i->second.size(); } entropy_joint = entropy ( ones, count - ones ); return entropy_joint - entropy_conditional; } double MutualInformation::mutualInformationClass ( const LabeledSetVector & ls, size_t classno, size_t dimension, double threshold ) const { size_t ones_p = 0; LabeledSetVector::const_iterator iclassno = ls.find(classno); if ( iclassno == ls.end() ) { fprintf (stderr, "MutualInformation::mutualInformationClass: classno %u not found\n", classno ); exit(-1); } size_t count_p = iclassno->second.size(); addStatistics ( iclassno->second, dimension, threshold, ones_p ); double entropy_conditional_p = entropy ( ones_p, count_p - ones_p ); size_t ones_n = 0; size_t count_n = 0; for ( LabeledSetVector::const_iterator i = ls.begin(); i != ls.end(); i++ ) { if ( i->first != (int)classno ); addStatistics ( i->second, dimension, threshold, ones_n ); count_n += i->second.size(); fprintf(stderr,"This exception means: review code and check the statement 'if ( i->first != (int)classno );' in line 108 / file MutualInformation.cpp. It contains an empty control statement and might be an error. If nonetheless desired behavoir, delete this exception throw."); } double entropy_conditional_n = entropy ( ones_n, count_n - ones_n ); double entropy_conditional = 0.5 * ( entropy_conditional_n + entropy_conditional_p ); double entropy_joint = entropy ( ones_p + ones_n, count_p + count_n - ones_p - ones_n ); return entropy_joint - entropy_conditional; } double MutualInformation::computeThresholdClass ( const LabeledSetVector & ls, size_t classno, size_t dimension, double & opt_threshold ) const { vector thresholds; LOOP_ALL(ls) { EACH(classno, v); double val = v[dimension]; thresholds.push_back ( val ); } sort ( thresholds.begin(), thresholds.end() ); thresholds.erase( std::unique( thresholds.begin(), thresholds.end()), thresholds.end()); opt_threshold = 0.0; double opt_mi = 0.0; for ( vector::const_iterator i = thresholds.begin(); i != thresholds.end(); i++ ) { vector::const_iterator j = i + 1; if ( j == thresholds.end() ) break; double threshold = 0.5 * ((*i) + (*j)); double mi = mutualInformationClass ( ls, classno, dimension, threshold ); if ( mi > opt_mi ) { opt_mi = mi; opt_threshold = threshold; } } return opt_mi; } double MutualInformation::computeThresholdOverall ( const LabeledSetVector & ls, size_t dimension, double & opt_threshold ) const { vector thresholds; vector y; LOOP_ALL(ls) { EACH(classno, v); double val = v[dimension]; thresholds.push_back ( val ); y.push_back(classno); } sort ( thresholds.begin(), thresholds.end() ); thresholds.erase( std::unique( thresholds.begin(), thresholds.end()), thresholds.end()); opt_threshold = 0.0; double opt_mi = 0.0; uint ind = 0; for ( vector::const_iterator i = thresholds.begin(); i != thresholds.end(); i++, ind++ ) { vector::const_iterator j = i + 1; if ( j == thresholds.end() ) break; // the optimimum can not be found at non-class borders if ( y[ind] == y[ind+1] ) continue; double threshold = 0.5 * ((*i) + (*j)); // FIXME: This call is pretty inefficient!! // We can directly count the features here...might be 100times faster :) double mi = mutualInformationOverall ( ls, dimension, threshold ); if ( mi > opt_mi ) { opt_mi = mi; opt_threshold = threshold; } } return opt_mi; } void MutualInformation::computeThresholdsClass ( const LabeledSetVector & ls, size_t classno, NICE::Vector & thresholds, NICE::Vector & mis ) const { size_t max_dimension = ls.dimension(); thresholds.resize(max_dimension); mis.resize(max_dimension); for ( size_t k = 0 ; k < max_dimension ; k++ ) { double t, mi; mi = computeThresholdClass ( ls, classno, k, t ); mis[k] = mi; thresholds[k] = t; } } void MutualInformation::computeThresholdsOverall ( const LabeledSetVector & ls, NICE::Vector & thresholds, NICE::Vector & mis ) const { size_t max_dimension = ls.dimension(); thresholds.resize(max_dimension); mis.resize(max_dimension); for ( size_t k = 0 ; k < max_dimension ; k++ ) { if ( verbose ) cerr << "MutualInformation: Optimizing threshold for feature " << k << " / " << max_dimension << endl; double t, mi; mi = computeThresholdOverall ( ls, k, t ); mis[k] = mi; thresholds[k] = t; } }