bool ContinuousHiddenMarkovModel::train_(TimeSeriesClassificationSample &trainingData){ //Clear any previous models clear(); //The number of states is simply set as the number of samples in the training sample timeseriesLength = trainingData.getLength(); numStates = (unsigned int)floor((double)(timeseriesLength/downsampleFactor)); numInputDimensions = trainingData.getNumDimensions(); classLabel = trainingData.getClassLabel(); //a is simply set as the number of 1/numStates a.resize(numStates, numStates); for(unsigned int i=0; i<numStates; i++){ for(unsigned int j=0; j<numStates; j++){ a[i][j] = 1.0/numStates; } } //b is simply set as the downsampled training sample b.resize(numStates, numInputDimensions); unsigned int index = 0; Float norm = 0; for(unsigned int j=0; j<numInputDimensions; j++){ index = 0; for(unsigned int i=0; i<numStates; i++){ norm = 0; b[i][j] = 0; for(unsigned int k=0; k<downsampleFactor; k++){ if( index < trainingData.getLength() ){ b[i][j] += trainingData[index++][j]; norm += 1; } } if( norm > 1 ) b[i][j] /= norm; } } //Estimate pi pi.resize(numStates); switch( modelType ){ case(HMM_ERGODIC): for(UINT i=0; i<numStates; i++){ pi[i] = 1.0/numStates; } break; case(HMM_LEFTRIGHT): //Set the state transitions constraints for(UINT i=0; i<numStates; i++){ norm = 0; for(UINT j=0; j<numStates; j++){ if((j<i) || (j>i+delta)) a[i][j] = 0.0; norm += a[i][j]; } if( norm > 0 ){ for(UINT j=0; j<numStates; j++){ a[i][j] /= norm; } } } //Set pi to start in state 0 for(UINT i=0; i<numStates; i++){ pi[i] = i==0 ? 1 : 0; } break; default: throw("HMM_ERROR: Unkown model type!"); return false; break; } //Setup sigma for each state sigmaStates.resize( numStates, numInputDimensions ); if( autoEstimateSigma ){ //Estimate the standard dev for each dimension, for each state MatrixFloat meanResults( numStates, numInputDimensions ); for(unsigned int j=0; j<numInputDimensions; j++){ //Estimate the mean for each state index = 0; for(unsigned int i=0; i<numStates; i++){ norm = 0; meanResults[i][j] = 0; for(unsigned int k=0; k<downsampleFactor; k++){ if( index < trainingData.getLength() ){ meanResults[i][j] += trainingData[index++][j]; norm += 1; } } if( norm > 1 ){ meanResults[i][j] /= norm; } } //Loop back over the data again and estimate the stddev for each state index = 0; for(unsigned int i=0; i<numStates; i++){ norm = 0; sigmaStates[i][j] = 0; for(unsigned int k=0; k<downsampleFactor; k++){ if( index < trainingData.getLength() ){ sigmaStates[i][j] += SQR( trainingData[index++][j]-meanResults[i][j] ); norm += 1; } } if( norm > 1 ){ sigmaStates[i][j] = sqrt( 1.0/norm * sigmaStates[i][j] ); } if( sigmaStates[i][j] < sigma ){ sigmaStates[i][j] = sigma; } } } }else{ sigmaStates.setAllValues(sigma); } //Setup the observation buffer for prediction observationSequence.resize( timeseriesLength, VectorFloat(numInputDimensions,0) ); obsSequence.resize(timeseriesLength,numInputDimensions); estimatedStates.resize( numStates ); //Finally, flag that the model was trained trained = true; return true; }
bool TimeSeriesClassificationSampleTrimmer::trimTimeSeries(TimeSeriesClassificationSample &timeSeries) { const UINT M = timeSeries.getLength(); const UINT N = timeSeries.getNumDimensions(); if( M == 0 ) { warningLog << "trimTimeSeries(TimeSeriesClassificationSample &timeSeries) - can't trim data, the length of the input time series is 0!" << endl; return false; } if( N == 0 ) { warningLog << "trimTimeSeries(TimeSeriesClassificationSample &timeSeries) - can't trim data, the number of dimensions in the input time series is 0!" << endl; return false; } //Compute the energy of the time series double maxValue = 0; VectorDouble x(M,0); for(UINT i=1; i<M; i++) { for(UINT j=0; j<N; j++) { x[i] += fabs(timeSeries[i][j]-timeSeries[i-1][j]); } x[i] /= N; if( x[i] > maxValue ) maxValue = x[i]; } //Normalize x so that the maximum energy has a value of 1 //At the same time search for the first time x[i] passes the trim threshold UINT firstIndex = 0; for(UINT i=1; i<M; i++) { x[i] /= maxValue; if( x[i] > trimThreshold && firstIndex == 0 ) { firstIndex = i; } } //Search for the last time x[i] passes the trim threshold UINT lastIndex = 0; for(UINT i=M-1; i>firstIndex; i--) { if( x[i] > trimThreshold && lastIndex == 0 ) { lastIndex = i; break; } } if( firstIndex == 0 && lastIndex == 0 ) { warningLog << "Failed to find either the first index or the last index!"; return false; } if( firstIndex == lastIndex ) { warningLog << "The first index and last index are the same!"; return false; } if( firstIndex > lastIndex ) { warningLog << "The first index is greater than the last index!"; return false; } if( lastIndex == 0 ) { warningLog << "Failed to find the last index!"; lastIndex = M-1; } //Compute how long the new time series would be if we trimmed it UINT newM = lastIndex-firstIndex; double trimPercentage = (double(newM) / double(M)) * 100.0; if( 100 - trimPercentage <= maximumTrimPercentage ) { MatrixDouble newTimeSeries(newM,N); UINT index = 0; for(UINT i=firstIndex; i<lastIndex; i++) { for(UINT j=0; j<N; j++) { newTimeSeries[index][j] = timeSeries[i][j]; } index++; } timeSeries.setTrainingSample(timeSeries.getClassLabel(), newTimeSeries); return true; } warningLog << "Maximum Trim Percentage Excedded, Can't Trim Sample!"; warningLog << " Original Timeseries Length: " << M << " Trimmed Timeseries Length: " << newM; warningLog << " Percentage: " << (100-trimPercentage) << " MaximumTrimPercentage: " << maximumTrimPercentage << endl; return false; }