Gaussian HMM
Hidden Markov model with Gaussian emissions, fitted by expectation-maximisation.
Description
Each hidden state \(k\) emits a Gaussian \(\mathcal{N}(\mu_k, \operatorname{diag}(\sigma_k^2))\), and the states follow a first-order Markov chain with initial probabilities \(\pi\) and transition matrix \(A\). All parameters are estimated by maximum likelihood with the Baum-Welch algorithm, an expectation-maximisation procedure whose E-step is the forward-backward algorithm (Rabiner, 1989). Each time step then receives the state of the most probable path (Viterbi).
Initialisation: k-means on the (z-normalised) observations gives the means and variances; \(\pi\) and \(A\) start uniform.
n_initinitialisations with different seeds are run and the one with the highest log-likelihood is kept.Regularisation:
reg_covar(positive) is added to every variance at each M-step, which limits the collapse of a state onto a few points. On integer or coarsely quantised data a state can still collapse onto a single value; raisereg_covarthen.Number of states: given by
n_states, or, withn_states=None, chosen by the Bayesian information criterion among1, ..., max_states. The fitted model has \((K-1) + K(K-1) + 2Kd\) free parameters.
The emissions are independent given the state, so the model has no notion of
duration or of temporal shape within a state. It suits regimes that differ in
level or spread, not regimes that differ only in frequency or waveform: on
oscillating signals the states follow the oscillation and the labels change
within a regime. For the same reason BIC often selects max_states on long
autocorrelated series (a warning says so), and the number of states is then
better given.
n_states)tsseg[accelerators]) speeds up the recursions considerablyParameters
Name |
Type |
Default |
Description |
|---|---|---|---|
|
int / None |
|
Number of hidden states; |
|
int |
|
Largest number of states tried by BIC. |
|
int |
|
Maximum number of EM iterations per initialisation. |
|
float |
|
EM stops when the log-likelihood per time step improves by less. |
|
int |
|
Number of k-means initialisations. |
|
float |
|
Added to the variances at each M-step (positive; units of the normalised data). |
|
bool |
|
Z-normalise each channel before fitting. |
|
int / None |
|
Seed of the k-means initialisations. |
|
int |
|
Time axis. |
Usage
from tsseg.algorithms import GaussianHMMDetector
# number of states chosen by BIC
labels = GaussianHMMDetector(random_state=0).fit_predict(X)
# known number of states
labels = GaussianHMMDetector(n_states=4, random_state=0).fit_predict(X)
Implementation: written for tsseg in NumPy, with numba for the
forward-backward and Viterbi recursions. With the same starting point, the
parameters and log-likelihood after each EM iteration match those of
hmmlearn (GaussianHMM with
covariance_type="diag" and covars_prior=0) to \(10^{-10}\) or
better.
It replaces HMM, which only decoded with user-supplied parameters.
Reference: Rabiner (1989), A tutorial on hidden Markov models and selected applications in speech recognition, Proceedings of the IEEE 77(2); Schwarz (1978), Estimating the dimension of a model, The Annals of Statistics 6(2).
API reference
Gaussian hidden Markov model fitted by Baum-Welch (EM) and decoded by Viterbi.
- class tsseg.algorithms.gaussian_hmm.detector.GaussianHMMDetector(*, n_states=None, max_states=8, n_iter=100, tol=0.0001, n_init=3, reg_covar=0.001, normalize=True, random_state=None, axis=0)[source]
Bases:
BaseSegmenterState detection with a Gaussian hidden Markov model.
Each hidden state emits a Gaussian with diagonal covariance. The parameters (initial and transition probabilities, means, variances) are estimated by maximum likelihood with the Baum-Welch algorithm, an expectation-maximisation procedure whose E-step is the forward-backward algorithm [1]. Each time step then gets the state of the most probable path (Viterbi).
With
n_states=Nonethe number of states is chosen by the Bayesian information criterion [2] among1, ..., max_states.- Parameters:
n_states (
int|None) – Number of hidden states.Noneselects it by BIC.max_states (
int) – Largest number of states tried whenn_statesisNone.n_iter (
int) – Maximum number of EM iterations per initialisation.tol (
float) – EM stops when the log-likelihood per time step improves by less thantol.n_init (
int) – Number of initialisations (k-means with different seeds); the fit with the highest log-likelihood is kept.reg_covar (
float) – Added to every variance at each M-step (must be positive), which limits the collapse of a state onto a few points. Expressed in units of the (normalised) data.normalize (
bool) – Z-normalise each channel before fitting.random_state (
int|None) – Seed of the k-means initialisations. Pass an int for reproducible results.axis (
int) – Time axis.
- startprob_
Initial state probabilities.
- Type:
ndarray of shape (K,)
- transmat_
transmat_[i, j]is the probability of moving from stateitoj.- Type:
ndarray of shape (K, K)
- means_
Emission means.
- Type:
ndarray of shape (K, n_channels)
- variances_
Emission variances.
- Type:
ndarray of shape (K, n_channels)
- ``means_``, ``variances_``, ``loglik_`` and ``bic_`` refer to the normalised
- data when ``normalize=True``. The labels returned by ``predict`` are numbered
- by order of first appearance, so label ``k`` is not necessarily row ``k`` of
- these arrays.
Notes
One EM iteration costs \(O(nK^2 + nKd)\) for \(n\) time steps, \(K\) states and \(d\) channels. Selecting \(K\) by BIC multiplies this by the number of candidates. The forward-backward and Viterbi recursions use numba when it is installed (
tsseg[accelerators]) and run, much slower, in pure Python otherwise.The emissions are i.i.d. given the state, so this model has no notion of duration or of temporal shape within a state: it suits regimes that differ in level or spread, not regimes that differ only in frequency or waveform. On oscillating signals, the states follow the oscillation and the labels change within a regime. For the same reason, BIC often selects
max_stateson long autocorrelated series; a warning says so.On integer or coarsely quantised data, a state can collapse onto a single value, with a variance of
reg_covar; raisereg_covarin that case.The initial probabilities are estimated from one series and are often close to one-hot: on another series that starts in a different regime, the first labels can be wrong. A state that receives no posterior mass during EM stays empty, so fewer than
n_stateslabels may appear.reg_covaris added to the variances rather than used as a floor, so EM is not strictly monotone; a decrease of the log-likelihood stops it.References
Examples
>>> import numpy as np >>> from tsseg.algorithms import GaussianHMMDetector >>> rng = np.random.default_rng(0) >>> X = np.concatenate([rng.normal(0, 1, 200), rng.normal(5, 1, 200)]) >>> labels = GaussianHMMDetector(n_states=2, random_state=0).fit_predict(X) >>> int(labels[:200].mean() != labels[200:].mean()) 1
- set_fit_request(*, axis: bool | None | str = '$UNCHANGED$') GaussianHMMDetector
Configure whether metadata should be requested to be passed to the
fitmethod.Note that this method is only relevant when this estimator is used as a sub-estimator within a meta-estimator and metadata routing is enabled with
enable_metadata_routing=True(seesklearn.set_config()). Please check the User Guide on how the routing mechanism works.The options for each parameter are:
True: metadata is requested, and passed tofitif provided. The request is ignored if metadata is not provided.False: metadata is not requested and the meta-estimator will not pass it tofit.None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.str: metadata should be passed to the meta-estimator with this given alias instead of the original name.
The default (
sklearn.utils.metadata_routing.UNCHANGED) retains the existing request. This allows you to change the request for some parameters and not others.Added in version 1.3.
- set_predict_request(*, axis: bool | None | str = '$UNCHANGED$') GaussianHMMDetector
Configure whether metadata should be requested to be passed to the
predictmethod.Note that this method is only relevant when this estimator is used as a sub-estimator within a meta-estimator and metadata routing is enabled with
enable_metadata_routing=True(seesklearn.set_config()). Please check the User Guide on how the routing mechanism works.The options for each parameter are:
True: metadata is requested, and passed topredictif provided. The request is ignored if metadata is not provided.False: metadata is not requested and the meta-estimator will not pass it topredict.None: metadata is not requested, and the meta-estimator will raise an error if the user provides it.str: metadata should be passed to the meta-estimator with this given alias instead of the original name.
The default (
sklearn.utils.metadata_routing.UNCHANGED) retains the existing request. This allows you to change the request for some parameters and not others.Added in version 1.3.