Skip to content

phelrine/pyhsmm

 
 

Repository files navigation

Sampling Inference for Bayesian HSMMs and HMMs

pyhsmm is a Python library for approximate unsupervised sampling inference in Bayesian Hidden Markov Models (HMMs) and explicit-duration Hidden semi-Markov Models (HSMMs), focusing on the Bayesian Nonparametric extensions, the HDP-HMM and HDP-HSMM, via the weak-limit approximation.

Installing

You can clone this library with the usual command:

git clone git://github.com/mattjj/pyhsmm.git

The library depends on numpy, scipy, and, for visualization, matplotlib.

There is an optional dependency on the Eigen C++ Template Library installed in the usual location of /usr/local/include (its default install location), which can make the inference run much faster in many cases (and only a bit faster in others). If you install Eigen, you can enable its use by calling pyhsmm.use_eigen() after importing pyhsmm.

A Simple Demonstration

Here's how to draw from the HDP-HSMM posterior over HSMMs given a sequence of observations. (The same example, along with the code to generate the synthetic data loaded in this example, can be found in examples/basic.py.)

Let's say we have some 2D data in a data.txt file:

$ head -n 5 data.txt
1.954325679401778038e+00 -2.556951835061773703e+00
5.112035450336103182e+00 -1.140375513299447086e+01
3.746172989570439871e-01 1.437014805219438696e+00
2.665840201248884433e+00 -6.032284411998907636e+00
1.434245756651063797e+00 -3.241457044646912422e+00

In Python, we can plot the data in a 2D plot, collapsing out the time dimension:

import numpy as np
from matplotlib import pyplot as plt

data = np.loadtxt('data.txt')
plt.plot(data[:,0],data[:,1],'kx')

2D data

We can also make a plot of time versus the first principal component:

from pyhsmm.util.plot import pca_project_data
plt.plot(pca_project_data(data,1))

Data first principal component vs time

To learn an HSMM, we'll use pyhsmm to create an hsmm object using some reasonable hyperparameters. We'll ask this model to infer the number of states as well (since an HDP-HSMM is instantiated by default), so we'll give it an Nmax parameter:

import pyhsmm

obs_dim = 2
Nmax = 10

obs_hypparams = {'mu_0':np.zeros(obs_dim),
                'lmbda_0':np.eye(obs_dim),
                'kappa_0':0.2,
                'nu_0':obs_dim+2}
dur_hypparams = {'k':8,
                'theta':5}

obs_distns = [pyhsmm.observations.gaussian(**obs_hypparams) for state in xrange(Nmax)]
dur_distns = [pyhsmm.durations.poisson(**dur_hypparams) for state in xrange(Nmax)]

posteriormodel = pyhsmm.hsmm(6.,6.,obs_distns,dur_distns,trunc=75)

(The first two arguments set the "new-table" proportionality constant for the meta-Chinese Restaurant Process and the other CRPs, respectively, in the HDP prior on transition matrices. For this example, they really don't matter at all: pretty much any values will work; those parameters only matter in very-low-evidence settings.)

The trunc parameter is an optional argument that can speed up inference: it sets a truncation limit on the maximum duration for any state. If you don't pass in the trunc argument, no truncation is used and all possible state duration lengths are considered.

Then, we add the data we want to condition on:

posteriormodel.add_data(data)

(If we had multiple observation sequences to learn from, we could add them to the model just by calling add_data() for each observation sequence.)

Now we run a resampling loop. For each iteration of the loop, all the latent variables of the model will be resampled by Gibbs sampling steps, including the transition matrix, the observation means and covariances, the duration parameters, and the hidden state sequence. We'll plot the samples every few iterations.

plot_every = 10
for idx in progprint_xrange(101):
    if (idx % plot_every) == 0:
        posteriormodel.plot()
        plt.gcf().suptitle('inferred HSMM after %d iterations (arbitrary colors)' % idx)

    posteriormodel.resample()

Sampled models

I generated these data from an HSMM that looked like this:

Randomly-generated model and data

So the posterior samples look pretty good!

In fact, if you'd like to visualize why this example data isn't so tough for an HSMM to decode, you can explore the 3D plot and see the strong time regularity with

from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111,projection='3d')
ax.scatter(np.arange(len(data)),data[:,0],data[:,1])
plt.show()

Speed

HSMMs constitute a much more powerful model class than plain-old HMMs, and that enhanced power comes with a computational price: each sampling iteration for an HSMM is much slower than that of an HMM. But that price is often worthwhile if you want to place priors on state durations or have the model learn duration structure present in the data. (In the example, strong duration structure is what made the inference algorithm latch onto the correct explanation so easily.) In addition, the increased cost of each iteration often pays for itself, since HSMM samplers empirically seem to take fewer iterations to converge than comparable HMM samplers.

Using my nothing-special i7-920 desktop machine and a NumPy/SciPy built against Intel's MKL BLAS (which generally outperforms ATLAS for vectorized operations) along with pyhsmm.use_eigen(), here's how long the demo iterations took:

$ python -m pyhsmm.examples.basic
.........................  [  25/101,    0.05sec avg,    3.95sec ETA ]
.........................  [  50/101,    0.05sec avg,    2.64sec ETA ]
.........................  [  75/101,    0.05sec avg,    1.34sec ETA ]
.........................  [ 100/101,    0.05sec avg,    0.05sec ETA ]
.
   0.05sec avg,    5.21sec total

Extending the Code

To add your own observation or duration distributions, implement the interfaces defined in abstractions.py.

Contributors

Contributions by Chia-ying Lee.

References

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Python 94.1%
  • C++ 5.9%