#ifdef NICE_USELIB_OPENMP #include #endif #include #include "GMM.h" #include #include "vislearning/math/cluster/KMeans.h" // #define DEBUGGMM using namespace OBJREC; using namespace NICE; using namespace std; GMM::GMM() { gaussians = 3; maxiter = 200; featsperclass = 0; tau = 10.0; srand ( time ( NULL ) ); comp = false; } GMM::GMM ( int _gaussians ) : gaussians ( _gaussians ) { maxiter = 200; featsperclass = 0; tau = 0.0; srand ( time ( NULL ) ); pyramid = false; comp = false; } GMM::GMM ( const Config *conf, int _gaussians ) : gaussians ( _gaussians ) { this->conf = conf; if ( gaussians < 2 ) gaussians = conf->gI ( "GMM", "gaussians", 2 ); maxiter = conf->gI ( "GMM", "maxiter", 200 ); featsperclass = conf->gI ( "GMM", "featsperclass", 0 ); tau = conf->gD ( "GMM", "tau", 100.0 ); pyramid = conf->gB ( "GMM", "pyramid", false ); comp = false; srand ( time ( NULL ) ); } void GMM::computeMixture ( Examples examples ) { int fsize = ( int ) examples.size(); assert ( fsize >= gaussians ); dim = examples[0].second.vec->size(); int samples = fsize; if ( featsperclass > 0 ) { samples = featsperclass * gaussians; samples = std::min ( samples, fsize ); } // Copy data in Matrix VVector dataset; cout << "reduced training data for GMM from " << fsize << " features to " << samples << " features."; for ( int i = 0; i < samples; i++ ) { int k = rand() % fsize; NICE::Vector *vec = examples[k].second.vec; dataset.push_back ( *vec ); } computeMixture ( dataset ); } void GMM::computeMixture ( const VVector &DataSet ) { // Learn the GMM model assert ( DataSet.size() >= ( uint ) gaussians ); //initEMkMeans(DataSet); // initialize the model srand ( time ( NULL ) ); bool poweroftwo = false; int power = 1; while ( power <= gaussians ) { if ( gaussians == power ) poweroftwo = true; power *= 2; } if ( poweroftwo && pyramid ) { initEM ( DataSet ); // initialize the model int g = 1; while ( g <= gaussians ) { cout << "g = " << g << endl; doEM ( DataSet, g ); if ( 2*g <= gaussians ) { for ( int i = g; i < g*2; i++ ) { mu[i] = mu[i-g]; sparse_sigma[i] = sparse_sigma[i-g]; sparse_inv_sigma[i] = sparse_inv_sigma[i-g]; log_det_sigma[i] = log_det_sigma[i-g]; priors[i] = priors[i-g]; double interval = 1.0; for ( int k = 0; k < ( int ) mu[i].size(); k++ ) { interval = mu[i][k]; interval = std::max ( interval, 0.1 ); double r = ( interval * ( ( double ) rand() / ( double ) RAND_MAX ) ) - interval / 2.0; mu[i][k] += r; } } } g *= 2; } } else { initEMkMeans ( DataSet ); // initialize the model doEM ( DataSet, gaussians ); } // performs EM } inline double diagDeterminant ( const NICE::Vector &sparse_mat ) { double det = 1.0; for ( int i = 0; i < ( int ) sparse_mat.size(); i++ ) { det *= sparse_mat[i]; } return det; } inline double logdiagDeterminant ( const NICE::Vector &sparse_mat ) { double det = 0.0; for ( int i = 0; i < ( int ) sparse_mat.size(); i++ ) { det += log ( sparse_mat[i] ); } return det; } inline NICE::Vector diagInverse ( const NICE::Vector &sparse_mat ) { NICE::Vector inv ( sparse_mat.size() ); for ( int i = 0; i < ( int ) sparse_mat.size(); i++ ) { inv[i] = 1.0 / sparse_mat[i]; } return inv; } void GMM::initEMkMeans ( const VVector &DataSet ) { /*init GMM with k-Means*/ KMeans k ( gaussians ); VVector means; vector weights; vector assignment; k.cluster ( DataSet, means, weights, assignment ); int nData = DataSet.size(); this->dim = DataSet[0].size(); cdimval = dim * log ( 2 * 3.14159 ); vector pop ( gaussians, 0 ); priors.resize ( gaussians ); mu = VVector ( gaussians, dim ); log_det_sigma.clear(); vector allk; NICE::Vector globmean ( dim ); globmean.set ( 0.0 ); for ( int n = 0; n < ( int ) DataSet.size(); n++ ) /* getting the max value for time */ { globmean = globmean + DataSet[n]; } globmean *= ( 1.0 / nData ); NICE::Vector sparse_globsigma; sparse_globsigma.resize ( dim ); sparse_globsigma.set ( 0.0 ); for ( int i = 0; i < ( int ) DataSet.size(); i++ ) // Covariances updates { // nur diagonal Elemente berechnen for ( int d = 0; d < dim; d++ ) { double diff = ( DataSet[i][d] - globmean[d] ); sparse_globsigma[d] += diff * diff; } } sparse_globsigma *= ( 1.0 / DataSet.size() ); for ( int i = 0; i < gaussians; i++ ) { NICE::Vector _inv_sigma = diagInverse ( sparse_globsigma ); sparse_sigma.push_back ( sparse_globsigma ); sparse_inv_sigma.push_back ( _inv_sigma ); log_det_sigma.push_back ( logdiagDeterminant ( sparse_globsigma ) ); mu[i] = means[i]; //priors[i]=1.0/(double)gaussians; // set equi-probables states priors[i] = weights[i]; // set kMeans weights } } void GMM::initEM ( const VVector &DataSet ) { /* init the GaussianMixture by using randomized meanvectors */ int nData = DataSet.size(); this->dim = DataSet[0].size(); cdimval = dim * log ( 2 * 3.14159 ); vector pop ( gaussians, 0 ); priors.resize ( gaussians ); mu = VVector ( gaussians, dim ); log_det_sigma.clear(); vector allk; NICE::Vector globmean ( dim ); globmean.set ( 0.0 ); for ( int n = 0; n < ( int ) DataSet.size(); n++ ) /* getting the max value for time */ { globmean = globmean + DataSet[n]; } globmean *= ( 1.0 / nData ); NICE::Vector sparse_globsigma; sparse_globsigma.resize ( dim ); sparse_globsigma.set ( 0.0 ); for ( int i = 0; i < ( int ) DataSet.size(); i++ ) // Covariances updates { // nur diagonal Elemente berechnen for ( int d = 0; d < dim; d++ ) { double diff = ( DataSet[i][d] - globmean[d] ); sparse_globsigma[d] += diff * diff; } } sparse_globsigma *= ( 1.0 / DataSet.size() ); for ( int i = 0; i < gaussians; i++ ) { priors[i] = 1.0 / ( double ) gaussians; // set equi-probables states NICE::Vector _inv_sigma = diagInverse ( sparse_globsigma ); sparse_sigma.push_back ( sparse_globsigma ); sparse_inv_sigma.push_back ( _inv_sigma ); log_det_sigma.push_back ( logdiagDeterminant ( sparse_globsigma ) ); bool newv = false; int k; while ( !newv ) { newv = true; k = rand() % nData; for ( int nk = 0; nk < ( int ) allk.size(); nk++ ) if ( allk[nk] == k ) { newv = false; } if ( newv ) allk.push_back ( k ); } mu[i] = DataSet[k]; } } inline void sumRow ( NICE::Matrix mat, NICE::Vector &res ) { int row = mat.rows(); int column = mat.cols(); res = NICE::Vector ( column ); res.set ( 1e-5f ); //#pragma omp parallel for for ( int i = 0; i < column; i++ ) { for ( int j = 0; j < row; j++ ) { res[i] += mat ( j, i ); } } } double GMM::logpdfState ( const NICE::Vector &Vin, int state ) { /* get the probability density for a given state and a given vector */ double p; NICE::Vector dif; dif = Vin; dif -= mu[state]; p = 0.0; for ( int i = 0; i < ( int ) dif.size(); i++ ) { p += dif[i] * dif[i] * sparse_inv_sigma[state][i]; } p = -0.5 * ( p + cdimval + log_det_sigma[state] ); return p; } int GMM::doEM ( const VVector &DataSet, int nbgaussians ) { /* perform Expectation/Maximization on the given Dataset : Matrix DataSet(nSamples,Dimensions). The GaussianMixture Object must be initialised before (see initEM or initEMkMeans methods ) */ #ifdef DEBUG cerr << "GMM::start EM" << endl; #endif int nData = DataSet.size(); int iter = 0; double log_lik; double log_lik_threshold = 1e-6f; double log_lik_old = -1e10f; NICE::Matrix unity ( dim, dim, 0.0 ); for ( int k = 0; k < dim; k++ ) unity ( k, k ) = 1.0; //EM loop while ( true ) { #ifdef DEBUGGMM cerr << "GMM::EM: iteration: " << iter << endl; #endif vector sum_p; sum_p.resize ( nData ); for ( int i = 0; i < nData; i++ ) { sum_p[i] = 0.0; } NICE::Matrix pxi ( nData, gaussians ); pxi.set ( 0.0 ); NICE::Matrix pix ( nData, gaussians ); pix.set ( 0.0 ); NICE::Vector E; iter++; if ( iter > maxiter ) { cerr << "EM stops here. Max number of iterations (" << maxiter << ") has been reached." << endl; return iter; } double sum_log = 0.0; // computing expectation double maxp = -numeric_limits::max(); vector maxptmp ( nData, -numeric_limits::max() ); #pragma omp parallel for for ( int i = 0; i < nData; i++ ) { for ( int j = 0; j < nbgaussians; j++ ) { double p = logpdfState ( DataSet[i], j ); // log(P(x|i)) maxptmp[i] = std::max ( maxptmp[i], p ); pxi ( i, j ) = p; } } for ( int i = 0; i < nData; i++ ) { maxp = std::max ( maxp, maxptmp[i] ); } #pragma omp parallel for for ( int i = 0; i < nData; i++ ) { sum_p[i] = 0.0; for ( int j = 0; j < nbgaussians; j++ ) { double p = pxi ( i, j ) - maxp; // log(P(x|i)) p = exp ( p ); // P(x|i) if ( p < 1e-40 ) p = 1e-40; pxi ( i, j ) = p; sum_p[i] += p * priors[j]; } } for ( int i = 0; i < nData; i++ ) { sum_log += log ( sum_p[i] ); } #pragma omp parallel for for ( int j = 0; j < nbgaussians; j++ ) { for ( int i = 0; i < nData; i++ ) { pix ( i, j ) = ( pxi ( i, j ) * priors[j] ) / sum_p[i]; // then P(i|x) } } // here we compute the log likehood log_lik = sum_log / nData; #ifdef DEBUGGMM cout << "diff: " << fabs ( ( log_lik / log_lik_old ) - 1 ) << " thresh: " << log_lik_threshold << " sum: " << sum_log << endl; //cout << "logold: " << log_lik_old << " lognew: " << log_lik << endl; #endif if ( fabs ( ( log_lik / log_lik_old ) - 1 ) < log_lik_threshold ) { //if log likehood hasn't move enough, the algorithm has converged, exiting the loop return iter; } log_lik_old = log_lik; // Update Step sumRow ( pix, E ); #pragma omp parallel for for ( int j = 0; j < nbgaussians; j++ ) { priors[j] = ( E[j] + tau ) / ( nData + tau * nbgaussians ); // new priors NICE::Vector tmu ( dim ); tmu.set ( 0.0 ); NICE::Vector sparse_tmsigma ( dim ); sparse_tmsigma.set ( 0.0 ); for ( int i = 0; i < nData; i++ ) // Means update loop { tmu = tmu + ( DataSet[i] * pix ( i, j ) ); } NICE::Vector tmu2 = mu[j]; mu[j] = tmu + tau * tmu2; mu[j] = mu[j] * ( 1.0 / ( E[j] + tau ) ); for ( int i = 0; i < nData; i++ ) // Covariances updates { // nur diagonal Elemente berechnen for ( int d = 0; d < dim; d++ ) { sparse_tmsigma[d] += DataSet[i][d] * DataSet[i][d] * pix ( i, j ); } } NICE::Vector sparse_tmsigma2 ( dim ); for ( int d = 0; d < dim; d++ ) { sparse_tmsigma[d] += 1e-5f; sparse_tmsigma2[d] = sparse_sigma[j][d] + tmu2[d] * tmu2[d]; sparse_sigma[j][d] = ( sparse_tmsigma[d] + tau * sparse_tmsigma2[d] ) / ( E[j] + tau ) - ( mu[j][d] * mu[j][d] ); } sparse_inv_sigma[j] = diagInverse ( sparse_sigma[j] ); log_det_sigma[j] = logdiagDeterminant ( sparse_sigma[j] ); } if ( comp ) { double dist = compare(); cout << "dist for iteration " << iter << endl; cout << "d: " << dist << endl; } } #ifdef DEBUG cerr << "GMM::finished EM after " << iter << " iterations" << endl; #endif return iter; } int GMM::getBestClass ( const NICE::Vector &v, double *bprob ) { int bestclass = 0; double maxprob = logpdfState ( v, 0 ) + log ( priors[0] ); // log(P(x|i)) for ( int i = 1; i < gaussians; i++ ) { double prob = logpdfState ( v, i ) + log ( priors[i] ); // log(P(x|i)) if ( prob > maxprob ) { maxprob = prob; bestclass = i; } } if ( bprob != NULL ) *bprob = maxprob; return bestclass; } void GMM::getProbs ( const NICE::Vector &vin, SparseVector &outprobs ) { outprobs.clear(); outprobs.setDim ( gaussians ); Vector probs; getProbs ( vin, probs ); for ( int i = 0; i < gaussians; i++ ) { if ( probs[i] > 1e-7f ) outprobs[i] = probs[i]; } } void GMM::getProbs ( const NICE::Vector &vin, Vector &outprobs ) { Vector probs; probs.resize ( gaussians ); probs.set ( 0.0 ); double probsum = 0.0; double maxp = -numeric_limits::max(); for ( int i = 0; i < gaussians; i++ ) { probs[i] = logpdfState ( vin, i ) + log ( priors[i] ); // log(P(x|i)) maxp = std::max ( maxp, probs[i] ); } for ( int i = 0; i < gaussians; i++ ) { probs[i] -= maxp; probs[i] = exp ( probs[i] ); probsum += probs[i]; } // normalize probs #pragma omp parallel for for ( int i = 0; i < gaussians; i++ ) { probs[i] /= probsum; } outprobs = probs; } void GMM::getFisher ( const NICE::Vector &vin, SparseVector &outprobs ) { int size = gaussians * 2 * dim; outprobs.clear(); outprobs.setDim ( size ); int counter = 0; NICE::Vector classprobs; classprobs.resize ( gaussians ); classprobs.set ( 0.0 ); double maxp = -numeric_limits::max(); for ( int i = 0; i < gaussians; i++ ) { classprobs[i] = logpdfState ( vin, i ) + log ( priors[i] ); // log(P(x|i)) maxp = std::max ( maxp, classprobs[i] ); } for ( int i = 0; i < gaussians; i++ ) { double p = classprobs[i] - maxp; p = exp ( p ); for ( int d = 0; d < dim; d++ ) { double diff = vin[d] - mu[i][d]; double sigma2 = sparse_sigma[i][d] * sparse_sigma[i][d]; double sigma3 = sigma2 * sparse_sigma[i][d]; double normmu = sqrt ( priors[i] / sigma2 ); double normsig = sqrt ( ( 2.0 * priors[i] ) / sigma2 ); double gradmu = ( p * ( diff / sigma2 ) ) / normmu; double gradsig = ( p * ( ( diff * diff ) / sigma3 - 1.0 / sparse_sigma[i][d] ) ) / normsig; if ( fabs ( gradmu ) > 1e-7f ) outprobs[counter] = gradmu; counter++; if ( fabs ( gradsig ) > 1e-7f ) outprobs[counter] = gradsig; counter++; } } } void GMM::cluster ( const VVector & features, VVector & prototypes, vector & weights, vector & assignment ) { computeMixture ( features ); prototypes.clear(); weights.clear(); assignment.clear(); for ( int i = 0; i < ( int ) features.size(); i++ ) { int c = getBestClass ( features[i] ); assignment.push_back ( c ); weights.push_back ( priors[c] ); } for ( int c = 0; c < gaussians; c++ ) prototypes.push_back ( mu[c] ); cout << "tau: " << tau << endl; } void GMM::saveData ( const std::string filename ) { ofstream fout ( filename.c_str() ); fout << gaussians << " " << dim << endl; mu >> fout; fout << endl; for ( int n = 0; n < gaussians; n++ ) { fout << sparse_sigma[n] << endl; } for ( int n = 0; n < gaussians; n++ ) { fout << priors[n] << " "; } fout.close(); } bool GMM::loadData ( const std::string filename ) { cerr << "read GMM Data" << endl; ifstream fin ( filename.c_str() ); if ( fin.fail() ) { cerr << "GMM: Datei " << filename << " nicht gefunden!" << endl; return false; } fin >> gaussians; fin >> dim; cdimval = log ( pow ( 2 * 3.14159, dim ) ); mu.clear(); for ( int i = 0; i < gaussians; i++ ) { NICE::Vector tmp; fin >> tmp; mu.push_back ( tmp ); } log_det_sigma.clear(); for ( int n = 0; n < gaussians; n++ ) { NICE::Matrix _sigma; NICE::Vector _sparse_sigma; _sparse_sigma = NICE::Vector ( dim ); fin >> _sparse_sigma; sparse_sigma.push_back ( _sparse_sigma ); sparse_inv_sigma.push_back ( diagInverse ( sparse_sigma[n] ) ); log_det_sigma.push_back ( logdiagDeterminant ( sparse_sigma[n] ) ); } for ( int n = 0; n < gaussians; n++ ) { double tmpd; fin >> tmpd; priors.push_back ( tmpd ); } fin.close(); cerr << "reading GMM Data finished" << endl; return true; } void GMM::getParams ( VVector &mean, VVector &sSigma, vector &p ) { mean = mu; sSigma.resize ( gaussians ); p.clear(); for ( int i = 0; i < gaussians; i++ ) { sSigma[i] = sparse_sigma[i]; p.push_back ( priors[i] ); } } void GMM::setCompareGM ( VVector mean, VVector sSigma, vector p ) { mu2 = mean; sparse_sigma2 = sSigma; priors2 = vector ( p ); } double GMM::kPPK ( NICE::Vector sigma1, NICE::Vector sigma2, NICE::Vector mu1, NICE::Vector mu2, double p ) { double d = mu1.size(); double det1 = 1.0; double det2 = 1.0; double det3 = 1.0; double eval = 0.0; for ( int i = 0; i < d; i++ ) { det1 *= sigma1[i]; det2 *= sigma2[i]; double sigma = 1.0 / ( ( 1.0 / sigma1[i] + 1.0 / sigma2[i] ) * p ); det3 *= sigma; double mu = p * ( mu1[i] * sigma1[i] + mu2[i] * sigma2[i] ); eval += 0.5 * mu * sigma * mu - ( p * mu1[i] * mu1[i] ) / ( 2.0 * sigma1[i] ) - ( p * mu2[i] * mu2[i] ) / ( 2.0 * sigma2[i] ); } return ( pow ( 2.0*M_PI, ( ( 1.0 - 2.0*p ) *d ) / 2.0 ) * pow ( det1, -p / 2.0 ) * pow ( det2, -p / 2.0 ) * pow ( det3, 0.5 ) * exp ( eval ) ); } double GMM::compare() { double distkij = 0.0; double distkjj = 0.0; double distkii = 0.0; for ( int i = 0; i < gaussians; i++ ) { for ( int j = 0; j < gaussians; j++ ) { double kij = kPPK ( sparse_sigma[i], sparse_sigma2[j], mu[i], mu2[j], 0.5 ); double kii = kPPK ( sparse_sigma[i], sparse_sigma[j], mu[i], mu[j], 0.5 ); double kjj = kPPK ( sparse_sigma2[i], sparse_sigma2[j], mu2[i], mu2[j], 0.5 ); kij = priors[i] * priors2[j] * kij; kii = priors[i] * priors[j] * kii; kjj = priors2[i] * priors2[j] * kjj; distkij += kij; distkii += kii; distkjj += kjj; } } return ( distkij / ( sqrt ( distkii ) *sqrt ( distkjj ) ) ); } void GMM::comparing ( bool c ) { comp = c; }