77 lines
2.8 KiB
Python
Executable File
77 lines
2.8 KiB
Python
Executable File
import numpy as np
|
|
from prml.rv import MultivariateGaussian
|
|
from .hmm import HiddenMarkovModel
|
|
|
|
|
|
class GaussianHMM(HiddenMarkovModel):
|
|
"""
|
|
Hidden Markov Model with Gaussian emission model
|
|
"""
|
|
|
|
def __init__(self, initial_proba, transition_proba, means, covs):
|
|
"""
|
|
construct hidden markov model with Gaussian emission model
|
|
|
|
Parameters
|
|
----------
|
|
initial_proba : (n_hidden,) np.ndarray or None
|
|
probability of initial states
|
|
transition_proba : (n_hidden, n_hidden) np.ndarray or None
|
|
transition probability matrix
|
|
(i, j) component denotes the transition probability from i-th to j-th hidden state
|
|
means : (n_hidden, ndim) np.ndarray
|
|
mean of each gaussian component
|
|
covs : (n_hidden, ndim, ndim) np.ndarray
|
|
covariance matrix of each gaussian component
|
|
|
|
Attributes
|
|
----------
|
|
ndim : int
|
|
dimensionality of observation space
|
|
n_hidden : int
|
|
number of hidden states
|
|
"""
|
|
assert initial_proba.size == transition_proba.shape[0] == transition_proba.shape[1] == means.shape[0] == covs.shape[0]
|
|
assert means.shape[1] == covs.shape[1] == covs.shape[2]
|
|
super().__init__(initial_proba, transition_proba)
|
|
self.ndim = means.shape[1]
|
|
self.means = means
|
|
self.covs = covs
|
|
self.precisions = np.linalg.inv(self.covs)
|
|
self.gaussians = [MultivariateGaussian(m, cov) for m, cov in zip(means, covs)]
|
|
|
|
def draw(self, n=100):
|
|
"""
|
|
draw random sequence from this model
|
|
|
|
Parameters
|
|
----------
|
|
n : int
|
|
length of the random sequence
|
|
|
|
Returns
|
|
-------
|
|
seq : (n, ndim) np.ndarray
|
|
generated random sequence
|
|
"""
|
|
hidden_state = np.random.choice(self.n_hidden, p=self.initial_proba)
|
|
seq = []
|
|
while len(seq) < n:
|
|
seq.extend(self.gaussians[hidden_state].draw())
|
|
hidden_state = np.random.choice(self.n_hidden, p=self.transition_proba[hidden_state])
|
|
return np.asarray(seq)
|
|
|
|
def likelihood(self, X):
|
|
diff = X[:, None, :] - self.means
|
|
exponents = np.sum(
|
|
np.einsum('nki,kij->nkj', diff, self.precisions) * diff, axis=-1)
|
|
return np.exp(-0.5 * exponents) / np.sqrt(np.linalg.det(self.covs) * (2 * np.pi) ** self.ndim)
|
|
|
|
def maximize(self, seq, p_hidden, p_transition):
|
|
self.initial_proba = p_hidden[0] / np.sum(p_hidden[0])
|
|
self.transition_proba = np.sum(p_transition, axis=0) / np.sum(p_transition, axis=(0, 2))
|
|
Nk = np.sum(p_hidden, axis=0)
|
|
self.means = (seq.T @ p_hidden / Nk).T
|
|
diffs = seq[:, None, :] - self.means
|
|
self.covs = np.einsum('nki,nkj->kij', diffs, diffs * p_hidden[:, :, None]) / Nk[:, None, None]
|