org.apache.mahout.classifier.sequencelearning.hmm.HmmEvaluator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mahout-mr Show documentation
Show all versions of mahout-mr Show documentation
Scalable machine learning libraries
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.mahout.classifier.sequencelearning.hmm;
import java.util.Random;
import org.apache.mahout.common.RandomUtils;
import org.apache.mahout.math.Matrix;
import org.apache.mahout.math.Vector;
/**
* The HMMEvaluator class offers several methods to evaluate an HMM Model. The
* following use-cases are covered: 1) Generate a sequence of output states from
* a given model (prediction). 2) Compute the likelihood that a given model
* generated a given sequence of output states (model likelihood). 3) Compute
* the most likely hidden sequence for a given model and a given observed
* sequence (decoding).
*/
public final class HmmEvaluator {
/**
* No constructor for utility classes.
*/
private HmmEvaluator() {}
/**
* Predict a sequence of steps output states for the given HMM model
*
* @param model The Hidden Markov model used to generate the output sequence
* @param steps Size of the generated output sequence
* @return integer array containing a sequence of steps output state IDs,
* generated by the specified model
*/
public static int[] predict(HmmModel model, int steps) {
return predict(model, steps, RandomUtils.getRandom());
}
/**
* Predict a sequence of steps output states for the given HMM model
*
* @param model The Hidden Markov model used to generate the output sequence
* @param steps Size of the generated output sequence
* @param seed seed to use for the RNG
* @return integer array containing a sequence of steps output state IDs,
* generated by the specified model
*/
public static int[] predict(HmmModel model, int steps, long seed) {
return predict(model, steps, RandomUtils.getRandom(seed));
}
/**
* Predict a sequence of steps output states for the given HMM model using the
* given seed for probabilistic experiments
*
* @param model The Hidden Markov model used to generate the output sequence
* @param steps Size of the generated output sequence
* @param rand RNG to use
* @return integer array containing a sequence of steps output state IDs,
* generated by the specified model
*/
private static int[] predict(HmmModel model, int steps, Random rand) {
// fetch the cumulative distributions
Vector cip = HmmUtils.getCumulativeInitialProbabilities(model);
Matrix ctm = HmmUtils.getCumulativeTransitionMatrix(model);
Matrix com = HmmUtils.getCumulativeOutputMatrix(model);
// allocate the result IntArrayList
int[] result = new int[steps];
// choose the initial state
int hiddenState = 0;
double randnr = rand.nextDouble();
while (cip.get(hiddenState) < randnr) {
hiddenState++;
}
// now draw steps output states according to the cumulative
// distributions
for (int step = 0; step < steps; ++step) {
// choose output state to given hidden state
randnr = rand.nextDouble();
int outputState = 0;
while (com.get(hiddenState, outputState) < randnr) {
outputState++;
}
result[step] = outputState;
// choose the next hidden state
randnr = rand.nextDouble();
int nextHiddenState = 0;
while (ctm.get(hiddenState, nextHiddenState) < randnr) {
nextHiddenState++;
}
hiddenState = nextHiddenState;
}
return result;
}
/**
* Returns the likelihood that a given output sequence was produced by the
* given model. Internally, this function calls the forward algorithm to
* compute the alpha values and then uses the overloaded function to compute
* the actual model likelihood.
*
* @param model Model to base the likelihood on.
* @param outputSequence Sequence to compute likelihood for.
* @param scaled Use log-scaled parameters for computation. This is computationally
* more expensive, but offers better numerically stability in case of
* long output sequences
* @return Likelihood that the given model produced the given sequence
*/
public static double modelLikelihood(HmmModel model, int[] outputSequence, boolean scaled) {
return modelLikelihood(HmmAlgorithms.forwardAlgorithm(model, outputSequence, scaled), scaled);
}
/**
* Computes the likelihood that a given output sequence was computed by a
* given model using the alpha values computed by the forward algorithm.
* // TODO I am a bit confused here - where is the output sequence referenced in the comment above in the code?
* @param alpha Matrix of alpha values
* @param scaled Set to true if the alpha values are log-scaled.
* @return model likelihood.
*/
public static double modelLikelihood(Matrix alpha, boolean scaled) {
double likelihood = 0;
if (scaled) {
for (int i = 0; i < alpha.numCols(); ++i) {
likelihood += Math.exp(alpha.getQuick(alpha.numRows() - 1, i));
}
} else {
for (int i = 0; i < alpha.numCols(); ++i) {
likelihood += alpha.getQuick(alpha.numRows() - 1, i);
}
}
return likelihood;
}
/**
* Computes the likelihood that a given output sequence was computed by a
* given model.
*
* @param model model to compute sequence likelihood for.
* @param outputSequence sequence to base computation on.
* @param beta beta parameters.
* @param scaled set to true if betas are log-scaled.
* @return likelihood of the outputSequence given the model.
*/
public static double modelLikelihood(HmmModel model, int[] outputSequence, Matrix beta, boolean scaled) {
double likelihood = 0;
// fetch the emission probabilities
Matrix e = model.getEmissionMatrix();
Vector pi = model.getInitialProbabilities();
int firstOutput = outputSequence[0];
if (scaled) {
for (int i = 0; i < model.getNrOfHiddenStates(); ++i) {
likelihood += pi.getQuick(i) * Math.exp(beta.getQuick(0, i)) * e.getQuick(i, firstOutput);
}
} else {
for (int i = 0; i < model.getNrOfHiddenStates(); ++i) {
likelihood += pi.getQuick(i) * beta.getQuick(0, i) * e.getQuick(i, firstOutput);
}
}
return likelihood;
}
/**
* Returns the most likely sequence of hidden states for the given model and
* observation
*
* @param model model to use for decoding.
* @param observations integer Array containing a sequence of observed state IDs
* @param scaled Use log-scaled computations, this requires higher computational
* effort but is numerically more stable for large observation
* sequences
* @return integer array containing the most likely sequence of hidden state
* IDs
*/
public static int[] decode(HmmModel model, int[] observations, boolean scaled) {
return HmmAlgorithms.viterbiAlgorithm(model, observations, scaled);
}
}