em.patch

patch to fix problems in CvEM::predict and CvEM::calcLikelihood - Alexander Alekseychuk, 2011-09-10 02:21 am

Download (7.2 kB)

 
src/em.cpp 2011-09-09 23:00:53.128977442 +0000
482 482

  
483 483
    // probability = (2*pi)^(-dims/2)*exp( -0.5 * cur )
484 484
    cvConvertScale( &expo, &expo, -0.5 );
485
    double factor = -double(dims)/2.0 * log(2.0*CV_PI);
486
    cvAndS( &expo, cvScalar(factor), &expo );
485
    const double factor = -0.5*double(dims)*CV_LOG2PI;
486
    cvAddS( &expo, cvScalar(factor), &expo );
487 487

  
488 488
    // Calculate the log-likelihood of the given sample -
489 489
    // see Alex Smola's blog http://blog.smola.org/page/2 for
490 490
    // details on the log-sum-exp trick
491
    double mini,maxi,retval;
491
    double mini, maxi;
492 492
    cvMinMaxLoc( &expo, &mini, &maxi, 0, 0 );
493
    CvMat *flp = cvCloneMat(&expo);
494
    cvSubS( &expo, cvScalar(maxi), flp);
495
    cvExp( flp, flp );
496
    CvScalar ss = cvSum( flp );
497
    retval = log(ss.val[0]) + maxi;
498
    cvReleaseMat(&flp);
493
    cvSubS( &expo, cvScalar(maxi), &expo);
494
    cvExp( &expo, &expo );
495
    const CvScalar ss = cvSum( &expo );
499 496

  
500 497
    if( sample_data != _sample->data.fl )
501 498
        cvFree( &sample_data );
502 499

  
503
    return retval;
500
    return log(ss.val[0]) + maxi;
504 501
}
505 502

  
506 503
/****************************************************************************************/
......
561 558
        }
562 559
    }
563 560

  
564
    // probability = (2*pi)^(-dims/2)*exp( -0.5 * cur )
565
    cvConvertScale( &expo, &expo, -0.5 );
566
    double factor = -double(dims)/2.0 * log(2.0*CV_PI);
567
    cvAndS( &expo, cvScalar(factor), &expo );
568

  
569 561
    // Calculate the posterior probability of each component
570 562
    // given the sample data.
571 563
    if( _probs )
572 564
    {
573
        cvExp( &expo, &expo );
574
        if( _probs->cols == 1 )
575
            cvReshape( &expo, &expo, 0, nclusters );
576
        cvConvertScale( &expo, _probs, 1./cvSum( &expo ).val[0] );
565
      cvScale( &expo, &expo, -0.5 );
566

  
567
      // Use the same log-sum-exp trick like in calcLikelihood,
568
      // but neither max_i(log(p_i)) must be added back (because of 
569
      // normalization) nor taking of log of sum is necessary
570
      double mini, maxi;
571
      cvMinMaxLoc( &expo, &mini, &maxi, 0, 0 );
572
      cvSubS( &expo, cvScalar(maxi), &expo);
573
      cvExp( &expo, &expo );
574
      if( _probs->cols == 1 )
575
        cvReshape( &expo, &expo, 0, nclusters );
576
      cvConvertScale( &expo, _probs, 1./cvSum( &expo ).val[0] );
577 577
    }
578 578

  
579 579
    if( sample_data != _sample->data.fl )
......
814 814
        {
815 815
            class_ranges->data.i[0] = 0;
816 816
            class_ranges->data.i[1] = nsamples;
817
            nclusters_found = 1;
817 818
        }
818 819

  
819 820
        for( i = 0; i < nclusters_found; i++ )
......
1081 1082

  
1082 1083
    int nsamples = train_data.count, dims = train_data.dims, nclusters = params.nclusters;
1083 1084
    double min_variation = FLT_EPSILON;
1084
    double min_det_value = MAX( DBL_MIN, pow( min_variation, dims ));
1085
    //double min_det_value = MAX( DBL_MIN, pow( min_variation, dims ));
1086
    double min_log_det_value = dims*std::log(min_variation);
1085 1087
    double _log_likelihood = -DBL_MAX;
1086 1088
    int start_step = params.start_step;
1087 1089
    double sum_max_val;
......
1095 1097

  
1096 1098
    if( nclusters == 1 )
1097 1099
    {
1098
        double log_weight;
1099 1100
        CV_CALL( cvSet( probs, cvScalar(1.)) );
1100 1101

  
1101 1102
        if( params.cov_mat_type == COV_MAT_SPHERICAL )
......
1103 1104
            d = cvTrace(*covs).val[0]/dims;
1104 1105
            d = MAX( d, FLT_EPSILON );
1105 1106
            inv_eigen_values->data.db[0] = 1./d;
1106
            log_weight = pow( d, dims*0.5 );
1107
            det = dims * std::log(d);
1107 1108
        }
1108 1109
        else
1109 1110
        {
......
1115 1116
                cvTranspose( cvGetDiag(*covs, &diag), inv_eigen_values );
1116 1117

  
1117 1118
            cvMaxS( inv_eigen_values, FLT_EPSILON, inv_eigen_values );
1118
            for( j = 0, det = 1.; j < dims; j++ )
1119
                det *= w_data[j];
1120
            log_weight = sqrt(det);
1119
            for( j = 0, det = 0.; j < dims; j++ )
1120
	      det += std::log( w_data[j] );
1121 1121
            cvDiv( 0, inv_eigen_values, inv_eigen_values );
1122 1122
        }
1123 1123

  
1124
        log_weight_div_det->data.db[0] = -2*log(weights->data.db[0]/log_weight);
1124
        log_weight_div_det->data.db[0] = -2*std::log(weights->data.db[0]) + det;
1125 1125
        log_likelihood = DBL_MAX/1000.;
1126 1126
        EXIT;
1127 1127
    }
......
1170 1170
                w_data = w->data.db;
1171 1171
                for( j = 0, det = 0.; j < dims; j++ )
1172 1172
                    det += std::log(w_data[j]);
1173
                if( det < std::log(min_det_value) )
1174
                {
1175
                    if( start_step == START_AUTO_STEP )
1176
                        det = std::log(min_det_value);
1177
                    else
1178
                        EXIT;
1179
                }
1173
                if( det < min_log_det_value )
1174
                // {
1175
                //     if( start_step == START_AUTO_STEP )
1176
		         det = min_log_det_value;
1177
                //     else
1178
                //         EXIT;  
1179
                //   early exit at this point _silently_ leaves inv_eigen_values 
1180
                //   uninitialized and log_weight_div_det with wrong 
1181
                //   values; it seems to be a better solution to just let 
1182
                //   it running, i.e. use pseudoinverce on the first step
1183
                //   and recompute covariances (which hopefully will be
1184
                //   nonsingular) and then proceed as usual
1185
                // }
1180 1186
                log_det->data.db[k] = det;
1181 1187
            }
1182 1188
            else // spherical
1183 1189
            {
1184 1190
                d = cvTrace(covs[k]).val[0]/(double)dims;
1185 1191
                if( d < min_variation )
1186
                {
1187
                    if( start_step == START_AUTO_STEP )
1192
                // {
1193
                //     if( start_step == START_AUTO_STEP )
1188 1194
                        d = min_variation;
1189
                    else
1190
                        EXIT;
1191
                }
1195
                //     else
1196
                //         EXIT;
1197
                // }
1192 1198
                cov_eigen_values->data.db[k] = d;
1193
                log_det->data.db[k] = d;
1199
                log_det->data.db[k] = dims * std::log(d); // = d;
1194 1200
            }
1195 1201
        }
1196 1202

  
1197
        if( is_spherical )
1198
        {
1199
            cvLog( log_det, log_det );
1200
            cvScale( log_det, log_det, dims );
1201
        }
1203
        // if( is_spherical )
1204
        // {
1205
        //     cvLog( log_det, log_det );
1206
        //     cvScale( log_det, log_det, dims );
1207
        // }
1202 1208
    }
1203 1209

  
1204 1210
    for( n = 0; n < params.term_crit.max_iter; n++ )
......
1323 1329
                d = w_data[0]/(double)dims;
1324 1330
                d = MAX( d, min_variation );
1325 1331
                w->data.db[0] = d;
1326
                log_det->data.db[k] = d;
1332
                log_det->data.db[k] = dims * std::log(d); // = d;
1327 1333
            }
1328 1334
            else
1329 1335
            {
......
1340 1346
        cvConvertScale( weights, weights, 1./(double)nsamples, 0 );
1341 1347
        cvMaxS( weights, DBL_MIN, weights );
1342 1348

  
1343
        if( is_spherical )
1344
        {
1345
            cvLog( log_det, log_det );
1346
            cvScale( log_det, log_det, dims );
1347
        }
1349
        // if( is_spherical )
1350
        // {
1351
        //     cvLog( log_det, log_det );
1352
        //     cvScale( log_det, log_det, dims );
1353
        // }
1348 1354
    } // end of iteration process
1349 1355

  
1350 1356
    //log_weight_div_det[k] = -2*log(weights_k/det(Sigma_k))^0.5) = -2*log(weights_k) + log(det(Sigma_k)))