MatrixDouble MatrixDouble::multiple(const MatrixDouble &b){ const unsigned int M = rows; const unsigned int N = cols; const unsigned int K = (unsigned int)b.getNumRows(); const unsigned int L = (unsigned int)b.getNumCols(); if( N != K ) { warningLog << "multiple(MatrixDouble b) - The number of rows in b (" << b.getNumRows() << ") does not match the number of columns in this matrix (" << N << ")" << std::endl; return MatrixDouble(); } MatrixDouble c(M,L); for(unsigned int i=0; i<M; i++){ for(unsigned int j=0; j<L; j++){ c[i][j] = 0; for(unsigned int k=0; k<K; k++){ c[i][j] += dataPtr[i][k] * b[k][j]; } } } return c; }
void DTW::znormData(MatrixDouble &data,MatrixDouble &normData){ const UINT R = data.getNumRows(); const UINT C = data.getNumCols(); if( normData.getNumRows() != R || normData.getNumCols() != C ){ normData.resize(R,C); } for(UINT j=0; j<C; j++){ double mean = 0.0; double stdDev = 0.0; //Calculate Mean for(UINT i=0; i<R; i++) mean += data[i][j]; mean /= double(R); //Calculate Std Dev for(UINT i=0; i<R; i++) stdDev += SQR(data[i][j]-mean); stdDev = sqrt( stdDev / (R - 1.0) ); if(constrainZNorm && stdDev < 0.01){ //Normalize the data to 0 mean for(UINT i=0; i<R; i++) normData[i][j] = (data[i][j] - mean); }else{ //Normalize the data to 0 mean and standard deviation of 1 for(UINT i=0; i<R; i++) normData[i][j] = (data[i][j] - mean) / stdDev; } } }
bool MatrixDouble::subtract(const MatrixDouble &b){ if( b.getNumRows() != rows ){ errorLog << "subtract(const MatrixDouble &b) - Failed to add matrix! The rows do not match!" << endl; errorLog << " rows: " << rows << " b rows: " << b.getNumRows() << endl; return false; } if( b.getNumCols() != cols ){ errorLog << "subtract(const MatrixDouble &b) - Failed to add matrix! The rows do not match!" << endl; errorLog << " cols: " << cols << " b cols: " << b.getNumCols() << endl; return false; } unsigned int i,j; //Using direct pointers really helps speed up the computation time double **pb = b.getDataPointer(); for(i=0; i<rows; i++){ for(j=0; j<cols; j++){ dataPtr[i*cols+j] -= pb[i][j]; } } return true; }
bool MatrixDouble::subtract(const MatrixDouble &a,const MatrixDouble &b){ const unsigned int M = a.getNumRows(); const unsigned int N = a.getNumCols(); if( M != b.getNumRows() ){ errorLog << "subtract(const MatrixDouble &a,const MatrixDouble &b) - Failed to add matrix! The rows do not match!"; errorLog << " a rows: " << M << " b rows: " << b.getNumRows() << endl; return false; } if( N != b.getNumCols() ){ errorLog << "subtract(const MatrixDouble &a,const MatrixDouble &b) - Failed to add matrix! The columns do not match!"; errorLog << " a cols: " << N << " b cols: " << b.getNumCols() << endl; return false; } resize( M, N ); UINT i,j; //Using direct pointers really helps speed up the computation time double **pa = a.getDataPointer(); double **pb = b.getDataPointer(); for(i=0; i<M; i++){ for(j=0; j<N; j++){ dataPtr[i*cols+j] = pa[i][j] - pb[i][j]; } } return true; }
bool MatrixDouble::multiple(const MatrixDouble &a,const MatrixDouble &b,const bool aTranspose){ const unsigned int M = !aTranspose ? a.getNumRows() : a.getNumCols(); const unsigned int N = !aTranspose ? a.getNumCols() : a.getNumRows(); const unsigned int K = b.getNumRows(); const unsigned int L = b.getNumCols(); if( N != K ) { errorLog << "multiple(const MatrixDouble &a,const MatrixDouble &b,const bool aTranspose) - The number of rows in a (" << K << ") does not match the number of columns in matrix b (" << N << ")" << std::endl; return false; } if( !resize( M, L ) ){ errorLog << "multiple(const MatrixDouble &b,const MatrixDouble &c,const bool bTranspose) - Failed to resize matrix!" << endl; return false; } unsigned int i, j, k = 0; //Using direct pointers really helps speed up the computation time double **pa = a.getDataPointer(); double **pb = b.getDataPointer(); if( aTranspose ){ for(j=0; j<L; j++){ for(i=0; i<M; i++){ dataPtr[i*cols+j] = 0; for(k=0; k<K; k++){ dataPtr[i*cols+j] += pa[k][i] * pb[k][j]; } } } }else{ for(j=0; j<L; j++){ for(i=0; i<M; i++){ dataPtr[i*cols+j] = 0; for(k=0; k<K; k++){ dataPtr[i*cols+j] += pa[i][k] * pb[k][j]; } } } } return true; }
void DTW::scaleData(MatrixDouble &data,MatrixDouble &scaledData){ const UINT R = data.getNumRows(); const UINT C = data.getNumCols(); if( scaledData.getNumRows() != R || scaledData.getNumCols() != C ){ scaledData.resize(R, C); } //Scale the data using the min and max values for(UINT i=0; i<R; i++) for(UINT j=0; j<C; j++) scaledData[i][j] = scale(data[i][j],rangesBuffer[j].minValue,rangesBuffer[j].maxValue,0.0,1.0); }
bool RBMQuantizer::train_(MatrixDouble &trainingData){ //Clear any previous model clear(); if( trainingData.getNumRows() == 0 ){ errorLog << "train_(MatrixDouble &trainingData) - Failed to train quantizer, the training data is empty!" << endl; return false; } //Train the RBM model rbm.setNumHiddenUnits( numClusters ); rbm.setLearningRate( learningRate ); rbm.setMinNumEpochs( minNumEpochs ); rbm.setMaxNumEpochs( maxNumEpochs ); rbm.setMinChange( minChange ); if( !rbm.train_( trainingData ) ){ errorLog << "train_(MatrixDouble &trainingData) - Failed to train quantizer!" << endl; return false; } //Flag that the feature vector is now initalized initialized = true; trained = true; numInputDimensions = trainingData.getNumCols(); numOutputDimensions = 1; //This is always 1 for the quantizer featureVector.resize(numOutputDimensions,0); quantizationDistances.resize(numClusters,0); return true; }
bool KMeans::setClusters(const MatrixDouble &clusters){ clear(); numClusters = clusters.getNumRows(); numInputDimensions = clusters.getNumCols(); this->clusters = clusters; return true; }
MatrixDouble MatrixDouble::multiple(const MatrixDouble &b) const{ const unsigned int M = rows; const unsigned int N = cols; const unsigned int K = b.getNumRows(); const unsigned int L = b.getNumCols(); if( N != K ) { errorLog << "multiple(MatrixDouble b) - The number of rows in b (" << K << ") does not match the number of columns in this matrix (" << N << ")" << std::endl; return MatrixDouble(); } MatrixDouble c(M,L); double **pb = b.getDataPointer(); double **pc = c.getDataPointer(); unsigned int i,j,k = 0; for(i=0; i<M; i++){ for(j=0; j<L; j++){ pc[i][j] = 0; for(k=0; k<K; k++){ pc[i][j] += dataPtr[i*cols+k] * pb[k][j]; } } } return c; }
void DTW::offsetTimeseries(MatrixDouble ×eries){ VectorDouble firstRow = timeseries.getRowVector(0); for(UINT i=0; i<timeseries.getNumRows(); i++){ for(UINT j=0; j<timeseries.getNumCols(); j++){ timeseries[i][j] -= firstRow[j]; } } }
int main (int argc, const char * argv[]) { //Create some input data for the PCA algorithm - this data comes from the Matlab PCA example MatrixDouble data(13,4); data[0][0] = 7; data[0][1] = 26; data[0][2] = 6; data[0][3] = 60; data[1][0] = 1; data[1][1] = 29; data[1][2] = 15; data[1][3] = 52; data[2][0] = 11; data[2][1] = 56; data[2][2] = 8; data[2][3] = 20; data[3][0] = 11; data[3][1] = 31; data[3][2] = 8; data[3][3] = 47; data[4][0] = 7; data[4][1] = 52; data[4][2] = 6; data[4][3] = 33; data[5][0] = 11; data[5][1] = 55; data[5][2] = 9; data[5][3] = 22; data[6][0] = 3; data[6][1] = 71; data[6][2] = 17; data[6][3] = 6; data[7][0] = 1; data[7][1] = 31; data[7][2] = 22; data[7][3] = 44; data[8][0] = 2; data[8][1] = 54; data[8][2] = 18; data[8][3] = 22; data[9][0] = 21; data[9][1] = 47; data[9][2] = 4; data[9][3] = 26; data[10][0] = 1; data[10][1] = 40; data[10][2] = 23; data[10][3] = 34; data[11][0] = 11; data[11][1] = 66; data[11][2] = 9; data[11][3] = 12; data[12][0] = 10; data[12][1] = 68; data[12][2] = 8; data[12][3] = 12; //Print the input data data.print("Input Data:"); //Create a new principal component analysis instance PrincipalComponentAnalysis pca; //Run pca on the input data, setting the maximum variance value to 95% of the variance if( !pca.computeFeatureVector( data, 0.95 ) ){ cout << "ERROR: Failed to compute feature vector!\n"; return EXIT_FAILURE; } //Get the number of principal components UINT numPrincipalComponents = pca.getNumPrincipalComponents(); cout << "Number of Principal Components: " << numPrincipalComponents << endl; //Project the original data onto the principal subspace MatrixDouble prjData; if( !pca.project( data, prjData ) ){ cout << "ERROR: Failed to project data!\n"; return EXIT_FAILURE; } //Print out the pca info //Print our pca.print("PCA Info:"); //Print the projected data cout << "ProjectedData:\n"; for(UINT i=0; i<prjData.getNumRows(); i++){ for(UINT j=0; j<prjData.getNumCols(); j++){ cout << prjData[i][j] << "\t"; }cout << endl; } return EXIT_SUCCESS; }
//Init the model with a pre-trained a, b, and pi matrices DiscreteHiddenMarkovModel::DiscreteHiddenMarkovModel(const MatrixDouble &a,const MatrixDouble &b,const VectorDouble &pi,const UINT modelType,const UINT delta){ numStates = 0; numSymbols = 0; numRandomTrainingIterations = 5; maxNumEpochs = 100; cThreshold = -1000; logLikelihood = 0.0; minChange = 1.0e-5; debugLog.setProceedingText("[DEBUG DiscreteHiddenMarkovModel]"); errorLog.setProceedingText("[ERROR DiscreteHiddenMarkovModel]"); warningLog.setProceedingText("[WARNING DiscreteHiddenMarkovModel]"); trainingLog.setProceedingText("[TRAINING DiscreteHiddenMarkovModel]"); if( a.getNumRows() == a.getNumRows() && a.getNumRows() == b.getNumRows() && a.getNumRows() == pi.size() ){ this->a = a; this->b = b; this->pi = pi; this->modelType = modelType; this->delta = delta; numStates = b.getNumRows(); numSymbols = b.getNumCols(); trained = true; }else{ errorLog << "DiscreteHiddenMarkovModel(...) - The a,b,pi sizes are invalid!" << endl; } }
bool PrincipalComponentAnalysis::project(const MatrixDouble &data,MatrixDouble &prjData) { if( !trained ) { warningLog << "project(const MatrixDouble &data,MatrixDouble &prjData) - The PrincipalComponentAnalysis module has not been trained!" << endl; return false; } if( data.getNumCols() != numInputDimensions ) { warningLog << "project(const MatrixDouble &data,MatrixDouble &prjData) - The number of columns in the input vector (" << data.getNumCols() << ") does not match the number of input dimensions (" << numInputDimensions << ")!" << endl; return false; } MatrixDouble msData( data ); prjData.resize(data.getNumRows(),numPrincipalComponents); if( normData ) { //Mean subtract the data for(UINT i=0; i<data.getNumRows(); i++) for(UINT j=0; j<numInputDimensions; j++) msData[i][j] = (msData[i][j]-mean[j])/stdDev[j]; } else { //Mean subtract the data for(UINT i=0; i<data.getNumRows(); i++) for(UINT j=0; j<numInputDimensions; j++) msData[i][j] -= mean[j]; } //Projected Data for(UINT row=0; row<msData.getNumRows(); row++) { //For each row in the final data for(UINT i=0; i<numPrincipalComponents; i++) { //For each PC prjData[row][i]=0; for(UINT j=0; j<data.getNumCols(); j++)//For each feature prjData[row][i] += msData[row][j] * eigenvectors[j][sortedEigenvalues[i].index]; } } return true; }
bool KMeans::train_(MatrixDouble &data){ trained = false; if( numClusters == 0 ){ errorLog << "train_(MatrixDouble &data) - Failed to train model. NumClusters is zero!" << endl; return false; } if( data.getNumRows() == 0 || data.getNumCols() == 0 ){ errorLog << "train_(MatrixDouble &data) - The number of rows or columns in the data is zero!" << endl; return false; } numTrainingSamples = data.getNumRows(); numInputDimensions = data.getNumCols(); clusters.resize(numClusters,numInputDimensions); assign.resize(numTrainingSamples); count.resize(numClusters); //Randomly pick k data points as the starting clusters Random random; vector< UINT > randIndexs(numTrainingSamples); for(UINT i=0; i<numTrainingSamples; i++) randIndexs[i] = i; std::random_shuffle(randIndexs.begin(), randIndexs.end()); //Copy the clusters for(UINT k=0; k<numClusters; k++){ for(UINT j=0; j<numInputDimensions; j++){ clusters[k][j] = data[ randIndexs[k] ][j]; } } return trainModel( data ); }
bool saveResults( const GestureRecognitionPipeline &pipeline, const string &filename ){ infoLog << "Saving results to file: " << filename << endl; fstream file( filename.c_str(), fstream::out ); if( !file.is_open() ){ errorLog << "Failed to open results file: " << filename << endl; return false; } file << pipeline.getTestAccuracy() << endl; vector< UINT > classLabels = pipeline.getClassLabels(); for(UINT k=0; k<pipeline.getNumClassesInModel(); k++){ file << pipeline.getTestPrecision( classLabels[k] ); if( k+1 < pipeline.getNumClassesInModel() ) file << "\t"; else file << endl; } for(UINT k=0; k<pipeline.getNumClassesInModel(); k++){ file << pipeline.getTestRecall( classLabels[k] ); if( k+1 < pipeline.getNumClassesInModel() ) file << "\t"; else file << endl; } for(UINT k=0; k<pipeline.getNumClassesInModel(); k++){ file << pipeline.getTestFMeasure( classLabels[k] ); if( k+1 < pipeline.getNumClassesInModel() ) file << "\t"; else file << endl; } MatrixDouble confusionMatrix = pipeline.getTestConfusionMatrix(); for(UINT i=0; i<confusionMatrix.getNumRows(); i++){ for(UINT j=0; j<confusionMatrix.getNumCols(); j++){ file << confusionMatrix[i][j]; if( j+1 < confusionMatrix.getNumCols() ) file << "\t"; }file << endl; } file.close(); infoLog << "Results saved." << endl; return true; }
void DTW::smoothData(MatrixDouble &data,UINT smoothFactor,MatrixDouble &resultsData){ const UINT M = data.getNumRows(); const UINT C = data.getNumCols(); const UINT N = (UINT) floor(double(M)/double(smoothFactor)); resultsData.resize(N,C); if(smoothFactor==1 || M<smoothFactor){ resultsData = data; return; } for(UINT i=0; i<N; i++){ for(UINT j=0; j<C; j++){ double mean = 0.0; int index = i*smoothFactor; for(UINT x=0; x<smoothFactor; x++){ mean += data[index+x][j]; } resultsData[i][j] = mean/smoothFactor; } } //Add on the data that does not fit into the window if(M%smoothFactor!=0.0){ VectorDouble mean(C,0.0); for(UINT j=0; j<C; j++){ for(UINT i=N*smoothFactor; i<M; i++) mean[j] += data[i][j]; mean[j]/=M-(N*smoothFactor); } //Add one row to the end of the Matrix MatrixDouble tempMatrix(N+1,C); for(UINT i=0; i<N; i++) for(UINT j=0; j<C; j++) tempMatrix[i][j] = resultsData[i][j]; for(UINT j=0; j<C; j++) tempMatrix[N][j] = mean[j]; resultsData = tempMatrix; } }
ClassificationData TimeSeriesClassificationDataStream::getClassificationData( const bool includeNullGestures ) const { ClassificationData classificationData; classificationData.setNumDimensions( getNumDimensions() ); classificationData.setAllowNullGestureClass( includeNullGestures ); bool addSample = false; for(UINT i=0; i<timeSeriesPositionTracker.size(); i++){ addSample = includeNullGestures ? true : timeSeriesPositionTracker[i].getClassLabel() != GRT_DEFAULT_NULL_CLASS_LABEL; if( addSample ){ MatrixDouble dataSegment = getTimeSeriesData( timeSeriesPositionTracker[i] ); for(UINT j=0; j<dataSegment.getNumRows(); j++){ classificationData.addSample(timeSeriesPositionTracker[i].getClassLabel(), dataSegment.getRowVector(j) ); } } } return classificationData; }
bool MatrixDouble::add(const MatrixDouble &b){ if( b.getNumRows() != rows ){ errorLog << "add(const MatrixDouble &b) - Failed to add matrix! The rows do not match!" << endl; return false; } if( b.getNumCols() != cols ){ errorLog << "add(const MatrixDouble &b) - Failed to add matrix! The rows do not match!" << endl; return false; } unsigned int i = 0; //Using direct pointers really helps speed up the computation time const double *p_b = &(b[0][0]); for(i=0; i<rows*cols; i++){ dataPtr[i] += p_b[i]; } return true; }
bool HMM::predict_continuous(MatrixDouble ×eries){ if( !trained ){ errorLog << "predict_continuous(MatrixDouble ×eries) - The HMM classifier has not been trained!" << endl; return false; } if( timeseries.getNumCols() != numInputDimensions ){ errorLog << "predict_continuous(MatrixDouble ×eries) - The number of columns in the input matrix (" << timeseries.getNumCols() << ") does not match the num features in the model (" << numInputDimensions << endl; return false; } //Scale the input vector if needed if( useScaling ){ const UINT timeseriesLength = timeseries.getNumRows(); for(UINT j=0; j<numInputDimensions; j++){ for(UINT i=0; i<timeseriesLength; i++){ timeseries[i][j] = scale(timeseries[i][j], ranges[j].minValue, ranges[j].maxValue, 0, 1); } } } if( classLikelihoods.size() != numClasses ) classLikelihoods.resize(numClasses,0); if( classDistances.size() != numClasses ) classDistances.resize(numClasses,0); std::fill(classLikelihoods.begin(),classLikelihoods.end(),0); std::fill(classDistances.begin(),classDistances.end(),0); bestDistance = -1000; UINT bestIndex = 0; double minValue = -1000; const UINT numModels = (UINT)continuousModels.size(); vector< IndexedDouble > results(numModels); for(UINT i=0; i<numModels; i++){ //Run the prediction for this model if( continuousModels[i].predict_( timeseries ) ){ results[i].value = continuousModels[i].getLoglikelihood(); results[i].index = continuousModels[i].getClassLabel(); }else{ errorLog << "predict_(VectorDouble &inputVector) - Prediction failed for model: " << i << endl; return false; } if( results[i].value < minValue ){ minValue = results[i].value; } if( results[i].value > bestDistance ){ bestDistance = results[i].value; bestIndex = i; } } //Store the phase from the best model phase = continuousModels[ bestIndex ].getPhase(); //Sort the results std::sort(results.begin(),results.end(),IndexedDouble::sortIndexedDoubleByValueDescending); //Run the majority vote const double committeeWeight = 1.0 / committeeSize; for(UINT i=0; i<committeeSize; i++){ classDistances[ getClassLabelIndexValue( results[i].index ) ] += Util::scale(results[i].value, -1000, 0, 0, committeeWeight, true); } //Turn the class distances into likelihoods double sum = Util::sum(classDistances); if( sum > 0 ){ for(UINT k=0; k<numClasses; k++){ classLikelihoods[k] = classDistances[k] / sum; } //Find the maximum label for(UINT k=0; k<numClasses; k++){ if( classDistances[k] > bestDistance ){ bestDistance = classDistances[k]; bestIndex = k; } } maxLikelihood = classLikelihoods[ bestIndex ]; predictedClassLabel = classLabels[ bestIndex ]; }else{ //If the sum is not greater than 1, then no class is close to any model maxLikelihood = 0; predictedClassLabel = 0; } return true; }
bool ANBC_Model::train(UINT classLabel,MatrixDouble &trainingData,VectorDouble &weightsVector){ //Check to make sure the column sizes match if( trainingData.getNumCols() != weightsVector.size() ){ N = 0; return false; } UINT M = trainingData.getNumRows(); N = trainingData.getNumCols(); this->classLabel = classLabel; //Update the weights buffer weights = weightsVector; //Resize the buffers mu.resize( N ); sigma.resize( N ); //Calculate the mean for each dimension for(UINT j=0; j<N; j++){ mu[j] = 0.0; for(UINT i=0; i<M; i++){ mu[j] += trainingData[i][j]; } mu[j] /= double(M); if( mu[j] == 0 ){ return false; } } //Calculate the sample standard deviation for(UINT j=0; j<N; j++){ sigma[j] = 0.0; for(UINT i=0; i<M; i++){ sigma[j] += SQR( trainingData[i][j]-mu[j] ); } sigma[j] = sqrt( sigma[j]/double(M-1) ); if( sigma[j] == 0 ){ return false; } } //Now compute the threshold double meanPrediction = 0.0; VectorDouble predictions(M); for(UINT i=0; i<M; i++){ //Test the ith training example vector<double> testData(N); for(UINT j=0; j<N; j++) { testData[j] = trainingData[i][j]; } predictions[i] = predict(testData); meanPrediction += predictions[i]; } //Calculate the mean prediction value meanPrediction /= double(M); //Calculate the standard deviation double stdDev = 0.0; for(UINT i=0; i<M; i++) { stdDev += SQR( predictions[i]-meanPrediction ); } stdDev = sqrt( stdDev / (double(M)-1.0) ); threshold = meanPrediction-(stdDev*gamma); //Update the training mu and sigma values so the threshold value can be dynamically computed at a later stage trainingMu = meanPrediction; trainingSigma = stdDev; return true; }
bool PrincipalComponentAnalysis::computeFeatureVector_(const MatrixDouble &data,const UINT analysisMode) { trained = false; const UINT M = data.getNumRows(); const UINT N = data.getNumCols(); this->numInputDimensions = N; MatrixDouble msData( M, N ); //Compute the mean and standard deviation of the input data mean = data.getMean(); stdDev = data.getStdDev(); if( normData ) { //Normalize the data for(UINT i=0; i<M; i++) for(UINT j=0; j<N; j++) msData[i][j] = (data[i][j]-mean[j]) / stdDev[j]; } else { //Mean Subtract Data for(UINT i=0; i<M; i++) for(UINT j=0; j<N; j++) msData[i][j] = data[i][j] - mean[j]; } //Get the covariance matrix MatrixDouble cov = msData.getCovarianceMatrix(); //Use Eigen Value Decomposition to find eigenvectors of the covariance matrix EigenvalueDecomposition eig; if( !eig.decompose( cov ) ) { mean.clear(); stdDev.clear(); componentWeights.clear(); sortedEigenvalues.clear(); eigenvectors.clear(); errorLog << "computeFeatureVector(const MatrixDouble &data,UINT analysisMode) - Failed to decompose input matrix!" << endl; return false; } //Get the eigenvectors and eigenvalues eigenvectors = eig.getEigenvectors(); VectorDouble eigenvalues = eig.getRealEigenvalues(); //Any eigenvalues less than 0 are not worth anything so set to 0 for(UINT i=0; i<eigenvalues.size(); i++) { if( eigenvalues[i] < 0 ) eigenvalues[i] = 0; } //Sort the eigenvalues and compute the component weights double sum = 0; UINT componentIndex = 0; sortedEigenvalues.clear(); componentWeights.resize(N,0); while( true ) { double maxValue = 0; UINT index = 0; for(UINT i=0; i<eigenvalues.size(); i++) { if( eigenvalues[i] > maxValue ) { maxValue = eigenvalues[i]; index = i; } } if( maxValue == 0 || componentIndex >= eigenvalues.size() ) { break; } sortedEigenvalues.push_back( IndexedDouble(index,maxValue) ); componentWeights[ componentIndex++ ] = eigenvalues[ index ]; sum += eigenvalues[ index ]; eigenvalues[ index ] = 0; //Set the maxValue to zero so it won't be used again } double cumulativeVariance = 0; switch( analysisMode ) { case MAX_VARIANCE: //Normalize the component weights and workout how many components we need to use to reach the maxVariance numPrincipalComponents = 0; for(UINT k=0; k<N; k++) { componentWeights[k] /= sum; cumulativeVariance += componentWeights[k]; if( cumulativeVariance >= maxVariance && numPrincipalComponents==0 ) { numPrincipalComponents = k+1; } } break; case MAX_NUM_PCS: //Normalize the component weights and compute the maxVariance maxVariance = 0; for(UINT k=0; k<N; k++) { componentWeights[k] /= sum; if( k < numPrincipalComponents ) { maxVariance += componentWeights[k]; } } break; default: errorLog << "computeFeatureVector(const MatrixDouble &data,UINT analysisMode) - Unknown analysis mode!" << endl; break; } //Flag that the features have been computed trained = true; return true; }
void metrics_subset_data(){ ANBC anbc; anbc.enableScaling(true); anbc.enableNullRejection(true); MinDist minDist; minDist.setNumClusters(4); minDist.enableScaling(true); minDist.enableNullRejection(true); // ofstream opRecall("anbc-recall-nr-0-10.csv"); // opRecall <<"nrCoeff,class0,class1,class2,class3,class4,class5\n"; // // ofstream opInstanceRes("anbc-prediction-nr-2.csv"); // opInstanceRes <<"actualClass,predictedClass,maximumLikelihood,lZ,lY,lZ,rZ,rY,rZ\n"; // // ofstream opMetrics("anbc-precision-recall-fmeasure-nr-2.csv"); // opMetrics <<"class1,class2,class3,class4,class5\n"; // // ofstream opConfusion("anbc-confusion-nr-2.csv"); // opConfusion <<"class0,class1,class2,class3,class4,class5\n"; ofstream opRecall("mindist-recall-nr-0-10.csv"); opRecall <<"nrCoeff,class0,class1,class2,class3,class4,class5\n"; ofstream opInstanceRes("mindist-prediction-nr-2.csv"); opInstanceRes <<"actualClass,predictedClass,maximumLikelihood,lZ,lY,lZ,rZ,rY,rZ\n"; ofstream opMetrics("mindist-precision-recall-fmeasure-nr-2.csv"); opMetrics <<"class1,class2,class3,class4,class5\n"; ofstream opConfusion("mindist-confusion-nr-2.csv"); opConfusion <<"class0,class1,class2,class3,class4,class5\n"; // Training and test data ClassificationData trainingData; ClassificationData testData; ClassificationData nullGestureData; string file_path = "../../../data/"; if( !trainingData.loadDatasetFromFile(file_path + "train/grt/hri-training-dataset.txt") ){ std::cout <<"Failed to load training data!\n"; } if( !nullGestureData.loadDatasetFromFile(file_path + "test/grt/0.txt") ){ std::cout <<"Failed to load null gesture data!\n"; } testData = trainingData.partition(90); testData.sortClassLabels(); // testData.saveDatasetToFile("anbc-validation-subset.txt"); testData.saveDatasetToFile("mindist-validation-subset.txt"); for(double nullRejectionCoeff = 0; nullRejectionCoeff <= 10; nullRejectionCoeff=nullRejectionCoeff+0.2){ // anbc.setNullRejectionCoeff(nullRejectionCoeff); // GestureRecognitionPipeline pipeline; // pipeline.setClassifier(anbc); minDist.setNullRejectionCoeff(nullRejectionCoeff); GestureRecognitionPipeline pipeline; pipeline.setClassifier(minDist); pipeline.train(trainingData); pipeline.test(testData); TestResult testRes = pipeline.getTestResults(); opRecall << nullRejectionCoeff << ","; //null rejection prediction double accuracy = 0; for(UINT i=0; i<nullGestureData.getNumSamples(); i++){ vector< double > inputVector = nullGestureData[i].getSample(); if( !pipeline.predict( inputVector )){ std::cout << "Failed to perform prediction for test sampel: " << i <<"\n"; } UINT predictedClassLabel = pipeline.getPredictedClassLabel(); if(predictedClassLabel == 0 ) accuracy++; } opRecall << accuracy/double(nullGestureData.getNumSamples()) << ","; // other classes prediction for(int cl = 0; cl < testRes.recall.size(); cl++ ){ opRecall << testRes.recall[cl]; if(cl < testRes.recall.size() - 1){ opRecall << ","; } } opRecall<< endl; // Calculate instance prediction, precision, recall, fmeasure and confusion matrix for nullRejection 2.0 if(AreDoubleSame(nullRejectionCoeff, 2.0)) { //instance prediction for(UINT i=0; i<testData.getNumSamples(); i++){ UINT actualClassLabel = testData[i].getClassLabel(); vector< double > inputVector = testData[i].getSample(); if( !pipeline.predict( inputVector )){ std::cout << "Failed to perform prediction for test sampel: " << i <<"\n"; } UINT predictedClassLabel = pipeline.getPredictedClassLabel(); double maximumLikelihood = pipeline.getMaximumLikelihood(); opInstanceRes << actualClassLabel << "," << predictedClassLabel << "," << maximumLikelihood << "," << inputVector[0] << "," << inputVector[1] << "," << inputVector[2] << "," << inputVector[3] << "," << inputVector[4] << "," << inputVector[5] << "\n"; } //precision, recall, fmeasure for(int cl = 0; cl < testRes.precision.size(); cl++ ){ opMetrics << testRes.precision[cl]; if(cl < testRes.precision.size() - 1){ opMetrics << ","; } } opMetrics<< endl; for(int cl = 0; cl < testRes.recall.size(); cl++ ){ opMetrics << testRes.recall[cl]; if(cl < testRes.recall.size() - 1){ opMetrics << ","; } } opMetrics<< endl; for(int cl = 0; cl < testRes.fMeasure.size(); cl++ ){ opMetrics << testRes.fMeasure[cl]; if(cl < testRes.fMeasure.size() - 1){ opMetrics << ","; } } opMetrics<< endl; //confusion matrix MatrixDouble matrix = testRes.confusionMatrix; for(UINT i=0; i<matrix.getNumRows(); i++){ for(UINT j=0; j<matrix.getNumCols(); j++){ opConfusion << matrix[i][j]; if(j < matrix.getNumCols() - 1){ opConfusion << ","; } } opConfusion << endl; } opConfusion << endl; } } cout << "Done\n"; }
int main (int argc, const char * argv[]) { //Create an empty matrix double MatrixDouble matrix; //Resize the matrix matrix.resize( 100, 2 ); //Set all the values in the matrix to zero matrix.setAllValues( 0 ); //Loop over the data and set the values to random values UINT counter = 0; for(UINT i=0; i<matrix.getNumRows(); i++){ for(UINT j=0; j<matrix.getNumCols(); j++){ matrix[i][j] = counter++; } } //Add a new row at the very end of the matrix VectorDouble newRow(2); newRow[0] = 1000; newRow[1] = 2000; matrix.push_back( newRow ); //Print the values cout << "Matrix Data: \n"; for(UINT i=0; i<matrix.getNumRows(); i++){ for(UINT j=0; j<matrix.getNumCols(); j++){ cout << matrix[i][j] << "\t"; } cout << endl; } cout << endl; //Get the second row as a vector VectorDouble rowVector = matrix.getRowVector( 1 ); cout << "Row Vector Data: \n"; for(UINT i=0; i<rowVector.size(); i++){ cout << rowVector[i] << "\t"; } cout << endl; //Get the second column as a vector VectorDouble colVector = matrix.getColVector( 1 ); cout << "Column Vector Data: \n"; for(UINT i=0; i<colVector.size(); i++){ cout << colVector[i] << "\n"; } cout << endl; //Get the mean of each column VectorDouble mean = matrix.getMean(); cout << "Mean: \n"; for(UINT i=0; i<mean.size(); i++){ cout << mean[i] << "\n"; } cout << endl; //Get the Standard Deviation of each column VectorDouble stdDev = matrix.getStdDev(); cout << "StdDev: \n"; for(UINT i=0; i<stdDev.size(); i++){ cout << stdDev[i] << "\n"; } cout << endl; //Get the covariance matrix MatrixDouble cov = matrix.getCovarianceMatrix(); cout << "Covariance Matrix: \n"; for(UINT i=0; i<cov.getNumRows(); i++){ for(UINT j=0; j<cov.getNumCols(); j++){ cout << cov[i][j] << "\t"; } cout << endl; } cout << endl; vector< MinMax > ranges = matrix.getRanges(); cout << "Ranges: \n"; for(UINT i=0; i<ranges.size(); i++){ cout << "i: " << i << "\tMinValue: " << ranges[i].minValue << "\tMaxValue:" << ranges[i].maxValue << "\n"; } cout << endl; //Save the matrix data to a csv file matrix.save( "data.csv" ); //load the matrix data from a csv file matrix.load( "data.csv" ); return EXIT_SUCCESS; }
bool DTW::train_NDDTW(LabelledTimeSeriesClassificationData &trainingData,DTWTemplate &dtwTemplate,UINT &bestIndex){ UINT numExamples = trainingData.getNumSamples(); VectorDouble results(numExamples,0.0); MatrixDouble distanceResults(numExamples,numExamples); dtwTemplate.averageTemplateLength = 0; for(UINT m=0; m<numExamples; m++){ MatrixDouble templateA; //The m'th template MatrixDouble templateB; //The n'th template dtwTemplate.averageTemplateLength += trainingData[m].getLength(); //Smooth the data if required if( useSmoothing ) smoothData(trainingData[m].getData(),smoothingFactor,templateA); else templateA = trainingData[m].getData(); if( offsetUsingFirstSample ){ offsetTimeseries(templateA); } for(UINT n=0; n<numExamples; n++){ if(m!=n){ //Smooth the data if required if( useSmoothing ) smoothData(trainingData[n].getData(),smoothingFactor,templateB); else templateB = trainingData[n].getData(); if( offsetUsingFirstSample ){ offsetTimeseries(templateB); } //Compute the distance between the two time series MatrixDouble distanceMatrix(templateA.getNumRows(),templateB.getNumRows()); vector< IndexDist > warpPath; double dist = computeDistance(templateA,templateB,distanceMatrix,warpPath); trainingLog << "Template: " << m << " Timeseries: " << n << " Dist: " << dist << endl; //Update the results values distanceResults[m][n] = dist; results[m] += dist; }else distanceResults[m][n] = 0; //The distance is zero because the two timeseries are the same } } for(UINT m=0; m<numExamples; m++) results[m]/=(numExamples-1); //Find the best average result, this is the result with the minimum value bestIndex = 0; double bestAverage = results[0]; for(UINT m=1; m<numExamples; m++){ if( results[m] < bestAverage ){ bestAverage = results[m]; bestIndex = m; } } if( numExamples > 2 ){ //Work out the threshold value for the best template dtwTemplate.trainingMu = results[bestIndex]; dtwTemplate.trainingSigma = 0.0; for(UINT n=0; n<numExamples; n++){ if(n!=bestIndex){ dtwTemplate.trainingSigma += SQR( distanceResults[ bestIndex ][n] - dtwTemplate.trainingMu ); } } dtwTemplate.trainingSigma = sqrt( dtwTemplate.trainingSigma / double(numExamples-2) ); }else{ warningLog << "_train_NDDTW(LabelledTimeSeriesClassificationData &trainingData,DTWTemplate &dtwTemplate,UINT &bestIndex - There are not enough examples to compute the trainingMu and trainingSigma for the template for class " << dtwTemplate.classLabel << endl; dtwTemplate.trainingMu = 0.0; dtwTemplate.trainingSigma = 0.0; } //Set the average length of the training examples dtwTemplate.averageTemplateLength = (UINT) (dtwTemplate.averageTemplateLength/double(numExamples)); trainingLog << "AverageTemplateLength: " << dtwTemplate.averageTemplateLength << endl; //Flag that the training was successfull return true; }
double DTW::computeDistance(MatrixDouble &timeSeriesA,MatrixDouble &timeSeriesB,MatrixDouble &distanceMatrix,vector< IndexDist > &warpPath){ const int M = timeSeriesA.getNumRows(); const int N = timeSeriesB.getNumRows(); const int C = timeSeriesA.getNumCols(); int i,j,k,index = 0; double totalDist,v,normFactor = 0.; warpPath.clear(); if( int(distanceMatrix.getNumRows()) != M || int(distanceMatrix.getNumCols()) != N ){ distanceMatrix.resize(M, N); } switch (distanceMethod) { case (ABSOLUTE_DIST): for(i=0; i<M; i++){ for(j=0; j<N; j++){ distanceMatrix[i][j] = 0.0; for(k=0; k< C; k++){ distanceMatrix[i][j] += fabs(timeSeriesA[i][k]-timeSeriesB[j][k]); } } } break; case (EUCLIDEAN_DIST): //Calculate Euclidean Distance for all possible values for(i=0; i<M; i++){ for(j=0; j<N; j++){ distanceMatrix[i][j] = 0.0; for(k=0; k< C; k++){ distanceMatrix[i][j] += SQR( timeSeriesA[i][k]-timeSeriesB[j][k] ); } distanceMatrix[i][j] = sqrt( distanceMatrix[i][j] ); } } break; case (NORM_ABSOLUTE_DIST): for(i=0; i<M; i++){ for(j=0; j<N; j++){ distanceMatrix[i][j] = 0.0; for(k=0; k< C; k++){ distanceMatrix[i][j] += fabs(timeSeriesA[i][k]-timeSeriesB[j][k]); } distanceMatrix[i][j]/=N; } } break; default: errorLog<<"ERROR: Unknown distance method: "<<distanceMethod<<endl; return -1; break; } //Run the recursive search function to build the cost matrix double distance = sqrt( d(M-1,N-1,distanceMatrix,M,N) ); if( isinf(distance) || isnan(distance) ){ warningLog << "DTW computeDistance(...) - Distance Matrix Values are INF!" << endl; return INFINITY; } //cout << "DIST: " << distance << endl; //The distMatrix values are negative so make them positive for(i=0; i<M; i++){ for(j=0; j<N; j++){ distanceMatrix[i][j] = fabs( distanceMatrix[i][j] ); } } //Now Create the Warp Path through the cost matrix, starting at the end i=M-1; j=N-1; totalDist = distanceMatrix[i][j]; warpPath.push_back( IndexDist(i,j,distanceMatrix[i][j]) ); //Use dynamic programming to navigate through the cost matrix until [0][0] has been reached normFactor = 1; while( true ) { if( i==0 && j==0 ) break; if( i==0 ){ j--; } else{ if( j==0 ) i--; else{ //Find the minimum cell to move to v = numeric_limits<double>::max(); index = 0; if( distanceMatrix[i-1][j] < v ){ v = distanceMatrix[i-1][j]; index = 1; } if( distanceMatrix[i][j-1] < v ){ v = distanceMatrix[i][j-1]; index = 2; } if( distanceMatrix[i-1][j-1] <= v ){ index = 3; } switch(index){ case(1): i--; break; case(2): j--; break; case(3): i--; j--; break; default: warningLog << "DTW computeDistance(...) - Could not compute a warping path for the input matrix! Dist: " << distanceMatrix[i-1][j] << " i: " << i << " j: " << j << endl; return INFINITY; break; } } } normFactor++; totalDist += distanceMatrix[i][j]; warpPath.push_back( IndexDist(i,j,distanceMatrix[i][j]) ); } return totalDist/normFactor; }
bool HMM::predict_discrete(MatrixDouble ×eries){ if( !trained ){ errorLog << "predict_continuous(MatrixDouble ×eries) - The HMM classifier has not been trained!" << endl; return false; } if( timeseries.getNumCols() != 1 ){ errorLog << "predict_discrete(MatrixDouble ×eries) The number of columns in the input matrix must be 1. It is: " << timeseries.getNumCols() << endl; return false; } //Covert the matrix double to observations const UINT M = timeseries.getNumRows(); vector<UINT> observationSequence( M ); for(UINT i=0; i<M; i++){ observationSequence[i] = (UINT)timeseries[i][0]; if( observationSequence[i] >= numSymbols ){ errorLog << "predict_discrete(VectorDouble &inputVector) - The new observation is not a valid symbol! It should be in the range [0 numSymbols-1]" << endl; return false; } } if( classLikelihoods.size() != numClasses ) classLikelihoods.resize(numClasses,0); if( classDistances.size() != numClasses ) classDistances.resize(numClasses,0); bestDistance = -99e+99; UINT bestIndex = 0; double sum = 0; for(UINT k=0; k<numClasses; k++){ classDistances[k] = discreteModels[k].predict( observationSequence ); //Set the class likelihood as the antilog of the class distances classLikelihoods[k] = antilog( classDistances[k] ); //The loglikelihood values are negative so we want the values closest to 0 if( classDistances[k] > bestDistance ){ bestDistance = classDistances[k]; bestIndex = k; } sum += classLikelihoods[k]; } //Turn the class distances into proper likelihoods for(UINT k=0; k<numClasses; k++){ classLikelihoods[k] /= sum; } maxLikelihood = classLikelihoods[ bestIndex ]; predictedClassLabel = classLabels[ bestIndex ]; if( useNullRejection ){ if( maxLikelihood > nullRejectionThresholds[ bestIndex ] ){ predictedClassLabel = classLabels[ bestIndex ]; }else predictedClassLabel = GRT_DEFAULT_NULL_CLASS_LABEL; } return true; }
bool KMeansFeatures::train_(MatrixDouble &trainingData){ if( !initialized ){ errorLog << "train_(MatrixDouble &trainingData) - The quantizer has not been initialized!" << endl; return false; } //Reset any previous model featureDataReady = false; const UINT M = trainingData.getNumRows(); const UINT N = trainingData.getNumCols(); numInputDimensions = N; numOutputDimensions = numClustersPerLayer[ numClustersPerLayer.size()-1 ]; //Scale the input data if needed ranges = trainingData.getRanges(); if( useScaling ){ for(UINT i=0; i<M; i++){ for(UINT j=0; j<N; j++){ trainingData[i][j] = scale(trainingData[i][j],ranges[j].minValue,ranges[j].maxValue,0,1.0); } } } //Train the KMeans model at each layer const UINT K = (UINT)numClustersPerLayer.size(); for(UINT k=0; k<K; k++){ KMeans kmeans; kmeans.setNumClusters( numClustersPerLayer[k] ); kmeans.setComputeTheta( true ); kmeans.setMinChange( minChange ); kmeans.setMinNumEpochs( minNumEpochs ); kmeans.setMaxNumEpochs( maxNumEpochs ); trainingLog << "Layer " << k+1 << "/" << K << " NumClusters: " << numClustersPerLayer[k] << endl; if( !kmeans.train_( trainingData ) ){ errorLog << "train_(MatrixDouble &trainingData) - Failed to train kmeans model at layer: " << k << endl; return false; } //Save the clusters clusters.push_back( kmeans.getClusters() ); //Project the data through the current layer to use as training data for the next layer if( k+1 != K ){ MatrixDouble data( M, numClustersPerLayer[k] ); VectorDouble input( trainingData.getNumCols() ); VectorDouble output( data.getNumCols() ); for(UINT i=0; i<M; i++){ //Copy the data into the sample for(UINT j=0; j<input.size(); j++){ input[j] = trainingData[i][j]; } //Project the sample through the current layer if( !projectDataThroughLayer( input, output, k ) ){ errorLog << "train_(MatrixDouble &trainingData) - Failed to project sample through layer: " << k << endl; return false; } //Copy the result into the training data for the next layer for(UINT j=0; j<output.size(); j++){ data[i][j] = output[j]; } } //Swap the data for the next layer trainingData = data; } } //Flag that the kmeans model has been trained trained = true; featureVector.resize( numOutputDimensions, 0 ); return true; }
int main (int argc, const char * argv[]) { //Load some training data from a file ClassificationData trainingData; if( !trainingData.loadDatasetFromFile("HelloWorldTrainingData.grt") ){ cout << "ERROR: Failed to load training data from file\n"; return EXIT_FAILURE; } cout << "Data Loaded\n"; //Print out some stats about the training data trainingData.printStats(); //Partition the training data into a training dataset and a test dataset. 80 means that 80% //of the data will be used for the training data and 20% will be returned as the test dataset ClassificationData testData = trainingData.partition(80); //Create a new Gesture Recognition Pipeline using an Adaptive Naive Bayes Classifier GestureRecognitionPipeline pipeline; pipeline.setClassifier( ANBC() ); //Train the pipeline using the training data if( !pipeline.train( trainingData ) ){ cout << "ERROR: Failed to train the pipeline!\n"; return EXIT_FAILURE; } //Save the pipeline to a file if( !pipeline.savePipelineToFile( "HelloWorldPipeline.grt" ) ){ cout << "ERROR: Failed to save the pipeline!\n"; return EXIT_FAILURE; } //Load the pipeline from a file if( !pipeline.loadPipelineFromFile( "HelloWorldPipeline.grt" ) ){ cout << "ERROR: Failed to load the pipeline!\n"; return EXIT_FAILURE; } //Test the pipeline using the test data if( !pipeline.test( testData ) ){ cout << "ERROR: Failed to test the pipeline!\n"; return EXIT_FAILURE; } //Print some stats about the testing cout << "Test Accuracy: " << pipeline.getTestAccuracy() << endl; cout << "Precision: "; for(UINT k=0; k<pipeline.getNumClassesInModel(); k++){ UINT classLabel = pipeline.getClassLabels()[k]; cout << "\t" << pipeline.getTestPrecision(classLabel); }cout << endl; cout << "Recall: "; for(UINT k=0; k<pipeline.getNumClassesInModel(); k++){ UINT classLabel = pipeline.getClassLabels()[k]; cout << "\t" << pipeline.getTestRecall(classLabel); }cout << endl; cout << "FMeasure: "; for(UINT k=0; k<pipeline.getNumClassesInModel(); k++){ UINT classLabel = pipeline.getClassLabels()[k]; cout << "\t" << pipeline.getTestFMeasure(classLabel); }cout << endl; MatrixDouble confusionMatrix = pipeline.getTestConfusionMatrix(); cout << "ConfusionMatrix: \n"; for(UINT i=0; i<confusionMatrix.getNumRows(); i++){ for(UINT j=0; j<confusionMatrix.getNumCols(); j++){ cout << confusionMatrix[i][j] << "\t"; }cout << endl; } return EXIT_SUCCESS; }
bool GaussianMixtureModels::train(const MatrixDouble &data,const UINT K){ modelTrained = false; failed = false; //Clear any previous training results det.clear(); invSigma.clear(); if( data.getNumRows() == 0 ){ errorLog << "train(const MatrixDouble &trainingData,const unsigned int K) - Training Failed! Training data is empty!" << endl; return false; } //Resize the variables M = data.getNumRows(); N = data.getNumCols(); this->K = K; //Resize mu and resp mu.resize(K,N); resp.resize(M,K); //Resize sigma sigma.resize(K); for(UINT k=0; k<K; k++){ sigma[k].resize(N,N); } //Resize frac and lndets frac.resize(K); lndets.resize(K); //Pick K random starting points for the inital guesses of Mu Random random; vector< UINT > randomIndexs(M); for(UINT i=0; i<M; i++) randomIndexs[i] = i; for(UINT i=0; i<M; i++){ SWAP(randomIndexs[ random.getRandomNumberInt(0,M) ],randomIndexs[ random.getRandomNumberInt(0,M) ]); } for(UINT k=0; k<K; k++){ for(UINT n=0; n<N; n++){ mu[k][n] = data[ randomIndexs[k] ][n]; } } //Setup sigma and the uniform prior on P(k) for(UINT k=0; k<K; k++){ frac[k] = 1.0/double(K); for(UINT i=0; i<N; i++){ for(UINT j=0; j<N; j++) sigma[k][i][j] = 0; sigma[k][i][i] = 1.0e-10; //Set the diagonal to a small number } } loglike = 0; UINT iterCounter = 0; bool keepGoing = true; double change = 99.9e99; while( keepGoing ){ change = estep( data ); mstep( data ); if( fabs( change ) < minChange ) keepGoing = false; if( ++iterCounter >= maxIter ) keepGoing = false; if( failed ) keepGoing = false; } if( failed ){ errorLog << "train(UnlabelledClassificationData &trainingData,unsigned int K) - Training failed!" << endl; return modelTrained; } //Compute the inverse of sigma and the determinants for prediction if( !computeInvAndDet() ){ det.clear(); invSigma.clear(); errorLog << "train(UnlabelledClassificationData &trainingData,unsigned int K) - Failed to compute inverse and determinat!" << endl; return false; } //Flag that the model was trained modelTrained = true; return true; }
bool GaussianMixtureModels::train_(MatrixDouble &data){ trained = false; //Clear any previous training results det.clear(); invSigma.clear(); numTrainingIterationsToConverge = 0; if( data.getNumRows() == 0 ){ errorLog << "train_(MatrixDouble &data) - Training Failed! Training data is empty!" << endl; return false; } //Resize the variables numTrainingSamples = data.getNumRows(); numInputDimensions = data.getNumCols(); //Resize mu and resp mu.resize(numClusters,numInputDimensions); resp.resize(numTrainingSamples,numClusters); //Resize sigma sigma.resize(numClusters); for(UINT k=0; k<numClusters; k++){ sigma[k].resize(numInputDimensions,numInputDimensions); } //Resize frac and lndets frac.resize(numClusters); lndets.resize(numClusters); //Scale the data if needed ranges = data.getRanges(); if( useScaling ){ for(UINT i=0; i<numTrainingSamples; i++){ for(UINT j=0; j<numInputDimensions; j++){ data[i][j] = scale(data[i][j],ranges[j].minValue,ranges[j].maxValue,0,1); } } } //Pick K random starting points for the inital guesses of Mu Random random; vector< UINT > randomIndexs(numTrainingSamples); for(UINT i=0; i<numTrainingSamples; i++) randomIndexs[i] = i; for(UINT i=0; i<numClusters; i++){ SWAP(randomIndexs[ i ],randomIndexs[ random.getRandomNumberInt(0,numTrainingSamples) ]); } for(UINT k=0; k<numClusters; k++){ for(UINT n=0; n<numInputDimensions; n++){ mu[k][n] = data[ randomIndexs[k] ][n]; } } //Setup sigma and the uniform prior on P(k) for(UINT k=0; k<numClusters; k++){ frac[k] = 1.0/double(numClusters); for(UINT i=0; i<numInputDimensions; i++){ for(UINT j=0; j<numInputDimensions; j++) sigma[k][i][j] = 0; sigma[k][i][i] = 1.0e-2; //Set the diagonal to a small number } } loglike = 0; bool keepGoing = true; double change = 99.9e99; UINT numIterationsNoChange = 0; VectorDouble u(numInputDimensions); VectorDouble v(numInputDimensions); while( keepGoing ){ //Run the estep if( estep( data, u, v, change ) ){ //Run the mstep mstep( data ); //Check for convergance if( fabs( change ) < minChange ){ if( ++numIterationsNoChange >= minNumEpochs ){ keepGoing = false; } }else numIterationsNoChange = 0; if( ++numTrainingIterationsToConverge >= maxNumEpochs ) keepGoing = false; }else{ errorLog << "train_(MatrixDouble &data) - Estep failed at iteration " << numTrainingIterationsToConverge << endl; return false; } } //Compute the inverse of sigma and the determinants for prediction if( !computeInvAndDet() ){ det.clear(); invSigma.clear(); errorLog << "train_(MatrixDouble &data) - Failed to compute inverse and determinat!" << endl; return false; } //Flag that the model was trained trained = true; return true; }