contrib/brl/bbas/bsta/algo/bsta_fit_gaussian.h
Go to the documentation of this file.
00001 // This is brl/bbas/bsta/algo/bsta_fit_gaussian.h
00002 #ifndef bsta_fit_gaussian_h_
00003 #define bsta_fit_gaussian_h_
00004 //:
00005 // \file
00006 // \brief Functions for fitting Gaussian distributions to a set of samples
00007 // \author Daniel Crispell (daniel_crispell@brown.edu)
00008 // \date February 9, 2010
00009 //
00010 // \verbatim
00011 //  Modifications
00012 //
00013 // \endverbatim
00014 
00015 #include <bsta/bsta_gaussian.h>
00016 #include <bsta/bsta_gaussian_sphere.h>
00017 #include <bsta/bsta_gaussian_indep.h>
00018 #include <bsta/bsta_gaussian_full.h>
00019 #include <bsta/bsta_mixture.h>
00020 #include <bsta/bsta_attributes.h>
00021 #include <vcl_iostream.h>
00022 
00023 //: fit a 1D Gaussian distribution to a set of weighted samples
00024 template <class T>
00025 void bsta_fit_gaussian(vcl_vector<T> const& samples, vcl_vector<T> const& sample_weights, bsta_gaussian_sphere<T,1>& gaussian)
00026 {
00027   // sanity check
00028   if (samples.size() != sample_weights.size()) {
00029     vcl_cerr << "bsta_fit_gaussian : error - samples.size == " << samples.size()
00030              << ", sample_weights.size == " << sample_weights.size() << vcl_endl;
00031     return;
00032   }
00033 
00034  const unsigned int nobs = (unsigned int)samples.size();
00035   T weight_sum = 0;
00036 
00037   // compute mean
00038   T mean = 0;
00039   for (unsigned int i=0; i<nobs; ++i) {
00040     mean += samples[i]*sample_weights[i];
00041     weight_sum += sample_weights[i];
00042   }
00043   if (weight_sum > 0) {
00044     mean /= weight_sum;
00045   }
00046   else {
00047     // error: no samples with non-zero weight!
00048     return;
00049   }
00050 
00051   // compute variance
00052   T var = 0;
00053   T weight_norm_sqrd_sum = 0;
00054 
00055   for (unsigned int i=0; i<nobs; ++i) {
00056     const T diff = samples[i] - mean;
00057     const T weight_norm = sample_weights[i] / weight_sum;
00058     var += diff*diff*weight_norm;
00059     weight_norm_sqrd_sum += weight_norm*weight_norm;
00060   }
00061   if( (T)(1-weight_norm_sqrd_sum) > (T) 0 )
00062     var /= ((T)1 - weight_norm_sqrd_sum);
00063   else
00064     var = (T) 0; 
00065 
00066   gaussian.set_mean(mean);
00067   if(var > 0)
00068     gaussian.set_covar(var);
00069 }
00070 
00071 //: fit a N-D spherical Gaussian distribution to a set of weighted samples
00072 template <class T, unsigned n>
00073 void bsta_fit_gaussian(vcl_vector<vnl_vector_fixed<T,n> > const& samples, vcl_vector<T> const& sample_weights,
00074                        bsta_gaussian_sphere<T,n>& gaussian )
00075 {
00076   // sanity check
00077   if (samples.size() != sample_weights.size()) {
00078     vcl_cerr << "bsta_fit_gaussian : error - samples.size == " << samples.size()
00079              << ", sample_weights.size == " << sample_weights.size() << vcl_endl;
00080     return;
00081   }
00082 
00083  const unsigned int nobs = samples.size();
00084   T weight_sum = 0;
00085 
00086   // compute mean
00087   vnl_vector_fixed<T,n> mean((T)0);
00088   for (unsigned int i=0; i<nobs; ++i) {
00089     mean += samples[i]*sample_weights[i];
00090     weight_sum += sample_weights[i];
00091   }
00092   if (weight_sum > 0) {
00093     mean /= weight_sum;
00094   }
00095   else {
00096     // error: no samples with non-zero weight!
00097     return;
00098   }
00099 
00100   // compute variance: single variance for all dimensions
00101   T var = 0;
00102   T weight_norm_sqrd_sum = 0;
00103 
00104   for (unsigned int i=0; i<nobs; ++i) {
00105     const vnl_vector_fixed<T,n> diff = samples[i] - mean;
00106     const T weight_norm = sample_weights[i] / weight_sum;
00107     var += dot_product(diff,diff)*weight_norm/n;
00108     weight_norm_sqrd_sum += weight_norm*weight_norm;
00109   }
00110   var /= ((T)1 - weight_norm_sqrd_sum);
00111 
00112   gaussian.set_mean(mean);
00113   gaussian.set_covar(var);
00114 }
00115 
00116 
00117 //: fit a N-D independent Gaussian distribution to a set of weighted samples
00118 template <class T, unsigned n>
00119 void bsta_fit_gaussian(vcl_vector<vnl_vector_fixed<T,n> > const& samples, vcl_vector<T> const& sample_weights,
00120                        bsta_gaussian_indep<T,n>& gaussian)
00121 {
00122   const unsigned int nobs = samples.size();
00123   // sanity check
00124   if (nobs != sample_weights.size()) {
00125     vcl_cerr << "bsta_fit_gaussian : error - samples.size == " << samples.size()
00126              << ", sample_weights.size == " << sample_weights.size() << vcl_endl;
00127     return;
00128   }
00129 
00130   // compute mean
00131   T weight_sum = 0;
00132   vnl_vector_fixed<T,n> mean((T)0);
00133   for (unsigned int i=0; i<nobs; ++i) {
00134     mean += samples[i]*sample_weights[i];
00135     weight_sum += sample_weights[i];
00136   }
00137   if (weight_sum > 0) {
00138     mean /= weight_sum;
00139   }
00140   else {
00141     // error: no samples with non-zero weight!
00142     return;
00143   }
00144 
00145   // compute variance independently for each dimension
00146   vnl_vector_fixed<T,n> diag_covar((T)0);
00147   T weight_norm_sqrd_sum = 0;
00148 
00149   for (unsigned int i=0; i<nobs; ++i) {
00150     const vnl_vector_fixed<T,n> diff = samples[i] - mean;
00151     const T weight_norm = sample_weights[i] / weight_sum;
00152     diag_covar += element_product(diff,diff)*weight_norm;
00153     weight_norm_sqrd_sum += weight_norm*weight_norm;
00154   }
00155   diag_covar /= ((T)1 - weight_norm_sqrd_sum);
00156 
00157   gaussian.set_mean(mean);
00158   gaussian.set_covar(diag_covar);
00159 }
00160 
00161 
00162 //: fit a N-D Gaussian distribution with full covariance to a set of weighted samples
00163 template <class T, unsigned n>
00164 void bsta_fit_gaussian(vcl_vector<vnl_vector_fixed<T,n> > const& samples, vcl_vector<T> const& sample_weights,
00165                        bsta_gaussian_full<T,n>& gaussian)
00166 {
00167   const unsigned int nobs = samples.size();
00168   // sanity check
00169   if (nobs != sample_weights.size()) {
00170     vcl_cerr << "bsta_fit_gaussian : error - samples.size == " << samples.size()
00171              << ", sample_weights.size == " << sample_weights.size() << vcl_endl;
00172     return;
00173   }
00174 
00175   // compute mean
00176   T weight_sum = 0;
00177   vnl_vector_fixed<T,n> mean((T)0);
00178   for (unsigned int i=0; i<nobs; ++i) {
00179     mean += samples[i]*sample_weights[i];
00180     weight_sum += sample_weights[i];
00181   }
00182   if (weight_sum > 0) {
00183     mean /= weight_sum;
00184   }
00185   else {
00186     // error: no samples with non-zero weight!
00187     return;
00188   }
00189 
00190   // compute variance independently for each dimension
00191   vnl_matrix_fixed<T,n,n> covar((T)0);
00192   T weight_norm_sqrd_sum = 0;
00193 
00194   for (unsigned int i=0; i<nobs; ++i) {
00195     const vnl_vector_fixed<T,n> diff = samples[i] - mean;
00196     const T weight_norm = sample_weights[i] / weight_sum;
00197     covar += outer_product(diff,diff)*weight_norm;
00198     weight_norm_sqrd_sum += weight_norm*weight_norm;
00199   }
00200   covar /= ((T)1 - weight_norm_sqrd_sum);
00201 
00202   gaussian.set_mean(mean);
00203   gaussian.set_covar(covar);
00204 }
00205 
00206 // Helper function for clipping covariance matrix
00207 template<class T>
00208 T clip_covar(T var, T min_var)
00209 { return vcl_max(var,min_var); }
00210 
00211 // Helper function for clipping covariance matrix
00212 template<class T, unsigned n>
00213 vnl_vector_fixed<T,n> clip_covar(vnl_vector_fixed<T,n> covar, vnl_vector_fixed<T,n> min_covar)
00214 {
00215   vnl_vector_fixed<T,n> maxval;
00216   for (unsigned int i=0; i<n; ++i) {
00217     maxval[i] = vcl_max(covar[i],min_covar[i]);
00218   }
00219   return maxval;
00220 }
00221 
00222 // Helper function for clipping covariance matrix
00223 template<class T, unsigned n>
00224 vnl_matrix_fixed<T,n,n> clip_covar(vnl_matrix_fixed<T,n,n> covar, vnl_matrix_fixed<T,n,n> min_covar)
00225 {
00226   vnl_matrix_fixed<T,n,n> maxval;
00227   for (unsigned int i=0; i<n; ++i) {
00228     for (unsigned int j=0; j<n; ++j) {
00229       maxval(i,j) = vcl_max(covar(i,j),min_covar(i,j));
00230     }
00231   }
00232   return maxval;
00233 }
00234 
00235 
00236 // Uses EM to compute mean and sigma.
00237 // min_var prevents EM from converging to degenerate distribution centered around a single sample
00238 template <class gauss_type>
00239 void bsta_fit_gaussian(vcl_vector<typename gauss_type::vector_type> const& samples,
00240                        vcl_vector<typename gauss_type::math_type> const& sample_probs,
00241                        vcl_vector<typename gauss_type::math_type> const& prob_densities_other,
00242                        gauss_type& gaussian, typename gauss_type::covar_type min_covar)
00243 {
00244   // sanity checks
00245   const unsigned int nobs = (unsigned int)samples.size();
00246   if (nobs != sample_probs.size()) {
00247     vcl_cerr << "bsta_fit_gaussian : error - samples.size == " << samples.size()
00248              << ", sample_probs.size == " << sample_probs.size() << vcl_endl;
00249     return;
00250   }
00251   if (nobs != prob_densities_other.size()) {
00252     vcl_cerr << "bsta_fit_gaussian : error - samples.size == " << samples.size()
00253              << ", prob_densities_other.size == " << prob_densities_other.size() << vcl_endl;
00254     return;
00255   }
00256   if (nobs == 0) {
00257     // zero observations. nothing to do here.
00258     return;
00259   }
00260 
00261   // initialize with estimate of gaussian parameters
00262   typedef typename gauss_type::math_type T;
00263 
00264   vcl_vector<T> sample_weights = sample_probs;
00265   bsta_fit_gaussian(samples, sample_weights, gaussian);
00266 
00267   const unsigned int max_its = 100;
00268   const T max_converged_weight_change = 1e-4f;
00269   for (unsigned int i=0; i<max_its; ++i)
00270   {
00271     T max_weight_change = 0;
00272     T sample_weight_sum = 0;
00273     // EXPECTATION
00274     for (unsigned int n=0; n<nobs; ++n) {
00275       // for each observation, assign probabilities that observation was produced by this model
00276       // P1: observation is generated by this gaussian.
00277       // P2: observation is not valid - observation is generated by the alternate model
00278       const T P1 = sample_probs[n] * gaussian.prob_density(samples[n]);
00279       const T P2 = prob_densities_other[n];
00280       const T normalizing_factor = P1 + P2;
00281       T new_sample_weight = 0;
00282       if (normalizing_factor > 1e-6) {
00283         new_sample_weight = P1 / normalizing_factor;
00284       }
00285       // compute delta weight for convergence check
00286       T weight_delta = vcl_fabs(sample_weights[n] - new_sample_weight);
00287       if (weight_delta > max_weight_change) {
00288         max_weight_change = weight_delta;
00289       }
00290       sample_weights[n] = new_sample_weight;
00291       sample_weight_sum += new_sample_weight;
00292     }
00293     // check for convergence
00294     if (max_weight_change < max_converged_weight_change) {
00295       break;
00296     }
00297     // MAXIMIZATION
00298     bsta_fit_gaussian(samples, sample_weights, gaussian);
00299     // make sure covariance does not get too "tight" to avoid degenerate solutions
00300     gaussian.set_covar(clip_covar(gaussian.covar(),min_covar));
00301   }
00302 }
00303 
00304 #endif // bsta_fit_gaussian_h_