Tutorial#
hmmlearn
implements the Hidden Markov Models (HMMs).
The HMM is a generative probabilistic model, in which a sequence of observable
\(\mathbf{X}\) variables is generated by a sequence of internal hidden
states \(\mathbf{Z}\). The hidden states are not observed directly.
The transitions between hidden states are assumed to have the form of a
(firstorder) Markov chain. They can be specified by the start probability
vector \(\boldsymbol{\pi}\) and a transition probability matrix
\(\mathbf{A}\). The emission probability of an observable can be any
distribution with parameters \(\boldsymbol{\theta}\) conditioned on the
current hidden state. The HMM is completely determined by
\(\boldsymbol{\pi}\), \(\mathbf{A}\) and \(\boldsymbol{\theta}\).
There are three fundamental problems for HMMs:
Given the model parameters and observed data, estimate the optimal sequence of hidden states.
Given the model parameters and observed data, calculate the model likelihood.
Given just the observed data, estimate the model parameters.
The first and the second problem can be solved by the dynamic programming algorithms known as the Viterbi algorithm and the ForwardBackward algorithm, respectively. The last one can be solved by an iterative ExpectationMaximization (EM) algorithm, known as the BaumWelch algorithm.
Available models#
Hidden Markov Model with Gaussian emissions. 

Hidden Markov Model with Gaussian mixture emissions. 

Hidden Markov Model with multinomial emissions 
Read on for details on how to implement a HMM with a custom emission probability.
Building HMM and generating samples#
You can build a HMM instance by passing the parameters described above to the
constructor. Then, you can generate samples from the HMM by calling
sample()
.
>>> import numpy as np
>>> from hmmlearn import hmm
>>> np.random.seed(42)
>>>
>>> model = hmm.GaussianHMM(n_components=3, covariance_type="full")
>>> model.startprob_ = np.array([0.6, 0.3, 0.1])
>>> model.transmat_ = np.array([[0.7, 0.2, 0.1],
... [0.3, 0.5, 0.2],
... [0.3, 0.3, 0.4]])
>>> model.means_ = np.array([[0.0, 0.0], [3.0, 3.0], [5.0, 10.0]])
>>> model.covars_ = np.tile(np.identity(2), (3, 1, 1))
>>> X, Z = model.sample(100)
The transition probability matrix need not to be ergodic. For instance, a leftright HMM can be defined as follows:
>>> lr = hmm.GaussianHMM(n_components=3, covariance_type="diag",
... init_params="cm", params="cmt")
>>> lr.startprob_ = np.array([1.0, 0.0, 0.0])
>>> lr.transmat_ = np.array([[0.5, 0.5, 0.0],
... [0.0, 0.5, 0.5],
... [0.0, 0.0, 1.0]])
If any of the required parameters are missing, sample()
will raise an exception:
>>> model = hmm.GaussianHMM(n_components=3)
>>> X, Z = model.sample(100)
Traceback (most recent call last):
...
sklearn.exceptions.NotFittedError: This GaussianHMM instance is not fitted yet. Call 'fit' with appropriate arguments before using this method.
Fixing parameters#
Each HMM parameter has a character code which can be used to customize its initialization and estimation. The EM algorithm needs a starting point to proceed, thus prior to training each parameter is assigned a value either random or computed from the data. It is possible to hook into this process and provide a starting point explicitly. To do so
ensure that the character code for the parameter is missing from
init_params
and thenset the parameter to the desired value.
For example, consider a HMM with an explicitly initialized transition probability matrix:
>>> model = hmm.GaussianHMM(n_components=3, n_iter=100, init_params="mcs")
>>> model.transmat_ = np.array([[0.7, 0.2, 0.1],
... [0.3, 0.5, 0.2],
... [0.3, 0.3, 0.4]])
A similar trick applies to parameter estimation. If you want to fix some
parameter at a specific value, remove the corresponding character from
params
and set the parameter value before training.
Monitoring convergence#
The number of EM algorithm iterations is upper bounded by the n_iter
parameter. The training proceeds until n_iter
steps were performed or the
change in score is lower than the specified threshold tol
. Note, that
depending on the data, the EM algorithm may or may not achieve convergence in the
given number of steps.
You can use the monitor_
attribute to diagnose convergence:
>>> remodel.monitor_
ConvergenceMonitor(
history=[...],
iter=15,
n_iter=100,
tol=0.01,
verbose=False,
)
>>> remodel.monitor_.converged
True
Working with multiple sequences#
All of the examples so far were using a single sequence of observations. The input format in the case of multiple sequences is a bit involved and is best understood by example.
Consider two 1D sequences:
>>> X1 = [[0.5], [1.0], [1.0], [0.42], [0.24]]
>>> X2 = [[2.4], [4.2], [0.5], [0.24]]
To pass both sequences to fit()
or
predict()
, first concatenate them into a single array and
then compute an array of sequence lengths:
>>> X = np.concatenate([X1, X2])
>>> lengths = [len(X1), len(X2)]
Finally, just call the desired method with X
and lengths
:
>>> hmm.GaussianHMM(n_components=3).fit(X, lengths)
GaussianHMM(...
Saving and loading HMM#
After training, a HMM can be easily persisted for future use with the standard
pickle
module:
>>> import pickle
>>> with open("filename.pkl", "wb") as file: pickle.dump(remodel, file)
>>> with open("filename.pkl", "rb") as file: pickle.load(file)
GaussianHMM(...
Implementing HMMs with custom emission probabilities#
If you want to implement a custom emission probability (e.g. Cauchy), you have to
subclass BaseHMM
and override the following methods
Initialize model parameters prior to fitting. 

Validate model parameters prior to fitting. 


Generate a random sample from a given component. 
Compute percomponent emission log probability under the model. 

Compute percomponent probability under the model. 

Initialize sufficient statistics required for Mstep. 

Update sufficient statistics from a given sample. 


Perform the Mstep of EM algorithm. 
Optionally, only one of _compute_likelihood
and
_compute_log_likelihood
need to be overridden, and the
base implementation will provide the other.