The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

PDL::HMM - Hidden Markov Model utilities in PDL

SYNOPSIS

 use PDL::HMM;

 ##-----------------------------------------------------
 ## Dimensions

 $N = $number_of_states;
 $M = $number_of_symbols;
 $T = $length_of_input;

 $A = $maximum_ambiguity;

 ##-----------------------------------------------------
 ## Parameters

 $af     = log(random($N,$N));
 $bf     = log(random($N,$M));
 $pif    = log(random($N));
 $omegaf = log(random($N));

 @theta = ($a,$b,$pi,$omega) = hmmmaximize($af,$bf,$pif,$omegaf);

 $o  = long(rint($M*random($T)));

 maximum_n_ind(dice_axis($a->logsumover+$pi+$b, 1,$o),
               ($oq=zeroes(long,$A,$T))); ##-- for constrained variants

 ##-----------------------------------------------------
 ## Log arithmetic

 $log0 = logzero;
 $logz = logadd(log($x),log($y));
 $logz = logdiff(log($x),log($y));
 $logz = logsumover(log($x));

 ##-----------------------------------------------------
 ## Sequence Probability

 $alpha  = hmmfw ($a,$b,$pi,    $o     ); ##-- forward (full)
 $alphaq = hmmfwq($a,$b,$pi,    $o, $oq); ##-- forward (constrained)

 $beta   = hmmbw ($a,$b,$omega,  $o    ); ##-- backward (full)
 $betaq  = hmmbwq($a,$b,$omega,  $o,$oq); ##-- backward (constrained)

 ##-----------------------------------------------------
 ## Parameter Estimation

 @expect = ($ea,$eb,$epi,$eomega) = hmmexpect0(@theta);    ##-- initialize

 hmmexpect (@theta, $o,     $alpha, $beta,  $ea,$eb,$epi); ##-- expect (full)
 hmmexpectq(@theta, $o,$oq, $alphaq,$betaq, $ea,$eb,$epi); ##-- expect (constrained)

 ($a,$b,$pi,$omega) = hmmmaximize($ea,$eb,$epi,$eomega);   ##-- maximize

 ##-----------------------------------------------------
 ## Sequence Analysis

 ($delta,$psi)   = hmmviterbi ($a,$b,$pi, $o);     ##-- trellis (full)
 ($deltaq,$psiq) = hmmviterbiq($a,$b,$pi, $o,$oq); ##-- trellis (constrained)

 $paths  = hmmpath (     $psi,  sequence($N));     ##-- backtrace (full)
 $pathsq = hmmpathq($oq, $psiq, sequence($A));     ##-- backtrace (constrained)

FUNCTIONS

Log Arithmetic

logzero

  Signature: ([o]a())

Approximates $a() = log(0), avoids nan.

logzero() handles bad values. The state of the output PDL is always good.

logadd

  Signature: (a(); b(); [o]c())

Computes $c() = log(exp($a()) + exp($b())), should be more stable.

logadd does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

logdiff

  Signature: (a(); b(); [o]c())

Computes log symmetric difference c = log(exp(max(a,b)) - exp(min(a,b))), may be more stable.

logdiff does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

logsumover

  Signature: (a(n); [o]b())

Computes $b() = log(sumover(exp($a()))), should be more stable.

logsumover does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

Sequence Probability

hmmfw

  Signature: (a(N,N); b(N,M); pi(N);  o(T);  [o]alpha(N,T))

Compute forward probability (alpha) matrix for input $o given model parameters @theta = ($a, $b, $pi, $omega).

Output (pseudocode) for all 0<=i<N, 0<=t<T:

 $alpha(i,t) = log P( $o(0:t), q(t)==i | @theta )

Note that the final-state probability vector $omega() is neither passed to this function nor used in the computation, but can be used to compute the final sequence probability for $o as:

  log P( $o | @theta ) = logsumover( $omega() + $alpha(:,t-1) )

hmmfw does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

hmmfwq

  Signature: (a(N,N); b(N,M); pi(N);  o(T); oq(Q,T);  [o]alphaq(Q,T))

Compute constrained forward probability (alphaq) matrix for input $o given model parameters @theta = ($a, $b, $pi, $omega), considering only the initial non-negative state indices in $oq(:,t) for each observation $o(t).

Output (pseudocode) for all 0<=qi<Q, 0<=t<T:

 $alphaq(qi,t) = log P( $o(0:t), q(t)==$oq(qi,t) | @theta )

Note that the final-state probability vector $omega() is neither passed to this function nor used in the computation, but can be used to compute the final sequence probability for $o as:

  log P( $o | @theta ) = logsumover( $alphaq(:,t-1) + $omega($oqTi) )

where:

  $oqTi = $oq(:,t-1)->where($oq(:,t-1)>=0)

hmmfwq does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

hmmbw

  Signature: (a(N,N); b(N,M); omega(N); o(T); [o]beta(N,T))

Compute backward probability (beta) matrix for input $o given model parameters @theta = ($a, $b, $pi, $omega).

Output (pseudocode) for all 0<=i<N, 0<=t<T:

 $beta(i,t) = log P( $o(t+1:T-1) | q(t)==i, @theta )

Note that the initial-state probability vector $pi() is neither passed to this function nor used in the computation, but can be used to compute the final sequence probability for $o as:

  log P( $o | @theta ) = logsumover( $pi() + $b(:,$o(0)) + $beta(:,0) )

hmmbw does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

hmmbwq

  Signature: (a(N,N); b(N,M); omega(N); o(T); oq(Q,T); [o]betaq(Q,T))

Compute constrained backward probability (betaq) matrix for input $o given model parameters @theta = ($a, $b, $pi, $omega), considering only the initial non-negative state indices in $oq(:,t) for each observation $o(t).

Output (pseudocode) for all 0<=qi<Q, 0<=t<T:

 $betaq(qi,t) = log P( $o(t+1:T-1) | q(t)==$oq(qi,t), @theta )

Note that the initial-state probability vector $pi() is neither passed to this function nor used in the computation, but can be used to compute the final sequence probability for $o as:

  log P( $o | @theta ) = logsumover( $betaq(:,0) + $pi($oq0i) + $b($oq0i,$o(0)) )

where:

  $oq0i = $oq(:,0)->where( $oq(:,0) >= 0 );

hmmbwq does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

Parameter Estimation

hmmexpect0

  Signature: (a(N,N); b(N,M); pi(N); omega(N); [o]ea(N,N); [o]eb(N,M); [o]epi(N); [o]eomega(N))

Initializes expectation matrices $ea(), $eb() and $epi() to logzero(). For use with hmmexpect().

hmmexpect

  Signature: (a(N,N); b(N,M); pi(N); omega(N); o(T); alpha(N,T); beta(N,T); [o]ea(N,N); [o]eb(N,M); [o]epi(N); [o]eomega(N))

Compute partial Baum-Welch re-estimation of the model @theta = ($a, $b, $pi, $omega) for the observation sequence $o() with forward- and backward-probability matrices $alpha(), $beta(). Result is recorded as log pseudo-frequencies in the expectation matrices $ea(), $eb(), $epi(), and $eomega(), which are required parameters, and should have been initialized (e.g. by "hmmexpect0"()) before calling this function.

Can safely be called sequentially for incremental reestimation.

hmmexpect does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

hmmexpectq

  Signature: (a(N,N); b(N,M); pi(N); omega(N); o(T); oq(Q,T); alphaq(Q,T); betaq(Q,T); [o]ea(N,N); [o]eb(N,M); [o]epi(N); [o]eomega(N))

Compute constrained partial Baum-Welch re-estimation of the model @theta = ($a, $b, $pi, $omega) for the observation sequence $o(), with constrained forward- and backward-probability matrices $alphaq(), $betaq(), considering only the initial non-negative state indices in $oq(:,t) for observation $o(t). Result is recorded as log pseudo-frequencies in the expectation matrices $ea(), $eb(), $epi(), and $eomega(), which are required parameters, and should have been initialized (e.g. by "hmmexpect0"()) before calling this function.

Can safely be called sequentially for incremental reestimation.

hmmexpectq does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

hmmmaximize

  Signature: (Ea(N,N); Eb(N,M); Epi(N); Eomega(N); [o]ahat(N,N); [o]bhat(N,M); [o]pihat(N); [o]omegahat(N));

Maximizes expectation values from $Ea(), $Eb(), $Epi(), and $Eomega() to log-probability matrices $ahat(), $bhat(), $pihat(), and $omegahat(). Can also be used to compile a maximum-likelihood model from log-frequency matrices.

Sequence Analysis

hmmviterbi

  Signature: (a(N,N); b(N,M); pi(N); o(T); [o]delta(N,T); int [o]psi(N,T))

Computes Viterbi algorithm trellises $delta() and $psi() for the observation sequence $o() given the model parameters @theta = ($a,$b,$pi,$omega).

Outputs:

Probability matrix $delta(): log probability of best path to state $j at time $t:

 $delta(j,t) = max_{q(0:t)} log P( $o(0:t), q(0:t-1), $q(t)==j | @theta )

Path backtrace matrix $psi(): best predecessor for state $j at time $t:

 $psi(j,t) = arg_{q(t-1)} max_{q(0:t)} P( $o(0:t), q(0:t-1), $q(t)==j | @theta )

Note that if you are using termination probabilities $omega(), then in order to find the most likely final state, you need to compute the contribution of $omega() yourself, which is easy to do:

 $best_final_q = maximum_ind($delta->slice(",-1") + $omega);

hmmviterbi does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

hmmviterbiq

  Signature: (a(N,N); b(N,M); pi(N); o(T); oq(Q,T); [o]deltaq(Q,T); indx [o]psiq(Q,T))

Computes constrained Viterbi algorithm trellises $deltaq() and $psiq() for the observation sequence $o() given the model parameters @theta = ($a,$b,$pi,$omega), considering only the initial non-negative state indices $oq(:,t) for each observarion $o(t).

Outputs:

Constrained probability matrix $deltaq(): log probability of best path to state $oq(j,t) at time $t:

 $deltaq(j,t) = max_{j(0:t)} log P( $o(0:t), q(0:t-1)==$oq(:,j(0:t-1)), q(t)==$oq(j,t) | @theta )

Constrained path backtrace matrix $psiq(): best predecessor index for state $oq(j,t) at time $t:

 $psiq(j,t) = arg_{j(t-1)} max_{j(0:t)} P( $o(0:t), q(0:t-1)=$oq(:,j(0:t-1)), q(t)==$oq(j,t) | @theta )

Note that if you are using termination probabilities $omega(), then in order to find the most likely final state, you need to compute the contribution of $omega() yourself, which is quite easy to do:

 $best_final_j = maximum_ind($deltaq->slice(",-1") + $omega->index($oq->slice(",(-1)")))

hmmviterbiq does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

hmmpath

  Signature: (psi(N,T); indx qfinal(); indx [o]path(T))

Computes best-path backtrace $path() for the final state $qfinal() from completed Viterbi trellis $psi().

Outputs:

Path backtrace $path(): state (in best sequence) at time $t:

 $path(t) = arg_{q(t)} max_{q(0:T-1)} log P( $o(), q(0:T-2), $q(T-1)==$qfinal() | @theta )

This even threads over multiple final states, if specified, so you can align paths to their final states just by calling:

 $bestpaths = hmmpath($psi, sequence($N));

Note that $path(T-1) == $qfinal(): yes, this is redundant, but also tends to be quite convenient.

hmmpath does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

hmmpathq

  Signature: (indx oq(Q,T); psiq(Q,T); indx qfinalq(); indx [o]path(T))

Computes constrained best-path backtrace $path() for the final state index $qfinalq() from completed constrained Viterbi trellis $psiq().

Outputs:

Path backtrace $path(): state (in best sequence) at time $t:

 $path(t) = arg_{q(t)} max_{q(0:T-1)} log P( $o(), q(0:T-2), $q(T-1)==$oq($qfinalq(),T-1) | @theta )

This is really just a convenience method for dealing with constrained lookup -- the same thing can be accomplished using hmmpath() and some PDL index magic.

hmmpathq does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

COMMON PARAMETERS

HMMs are specified by parameters $a(N,N), $b(N,M), $pi(N), and $omega(N); input sequences are represented as vectors $o(T) of integer values in the range [0..M-1], where the following notational conventions are used:

States:

The model has $N states, denoted $q, 0 <= $q < $N.

Alphabet:

The input- (aka "observation-") alphabet of the model has $M elements, denoted $o(t), 0 <= $o(t) < $M.

Time indices:

Time indices are denoted $t, 1 <= $t < $T.

Input Sequences:

Input- (aka "observation-") sequences are represented as vectors of of length $T whose component values are in the range [0..M-1], i.e. alphabet indices.

Initial Probabilities:

The vector $pi(N) gives the (log) initial state probability distribution:

 $pi(i) = log P( $q(0)==i )
Final Probabilities:

The vector $omega(N) gives the (log) final state probability distribution:

 $omega(i) = log P( $q($T)==i )

This parameter is a nonstandard extension. You can simulate the behavior of more traditional definitions (such as that given in Rabiner (1989)) by setting:

 $omega = zeroes($N);

wherever it is required.

Arc Probabilities:

The matrix $a(N,N) gives the (log) conditional state-transition probability distribution:

 $a(i,j) = log P( $q(t+1)==j | $q(t)==i )
Emission Probabilities:

The matrix $b(N,M) gives the (log) conditional symbol emission probability:

 $b(j,o) = log P( $o(t)==o | $q(t)==j )

ACKNOWLEDGEMENTS

Perl by Larry Wall.

PDL by Karl Glazebrook, Tuomas J. Lukka, Christian Soeller, and others.

Implementation based largely on the formulae in: L. E. Rabiner, "A tutorial on Hidden Markov Models and selected applications in speech recognition," Proceedings of the IEEE 77:2, Februrary, 1989, pages 257--286.

KNOWN BUGS

Probably many.

AUTHOR

Bryan Jurish <moocow@cpan.org>

Copyright (C) 2005-2023 by Bryan Jurish. All rights reserved.

This package is free software, and entirely without warranty. You may redistribute it and/or modify it under the same terms as Perl itself.

SEE ALSO

perl(1), PDL(3perl).