Source code for cardio.models.hmm.hmm
""" HMModel """
import numpy as np
import dill
from ...dataset.dataset.models.base import BaseModel
def prepare_hmm_input(batch, model, features, channel_ix):
"""Concatenate selected channel ``channel_ix`` of the batch attribute
``features``.
Parameters
----------
batch : EcgBatch
Batch to concatenate.
model : BaseModel
A model to get the resulting arguments.
features : str
Specifies batch attribute that contains features for ``HMModel``.
channel_ix : int
Index of channel, which data should be used in training and
predicting.
Returns
-------
kwargs : dict
Named argments for model's train or predict method. Has the
following structure:
"X" : 2-D ndarray
Features concatenated along -1 axis and transposed.
"lengths" : list
List of lengths of individual feature arrays along -1 axis.
"""
_ = model
hmm_features = getattr(batch, features)
x = np.concatenate([features[channel_ix].T for features in hmm_features])
lengths = [features.shape[-1] for features in hmm_features]
return {"X": x, "lengths": lengths}
[docs]class HMModel(BaseModel):
"""
Hidden Markov Model.
This implementation is based on ``hmmlearn`` API. It is supposed
that estimators of ``HMModel`` are model classes of ``hmmlearn``.
"""
def __init__(self, *args, **kwargs):
self.estimator = None
super().__init__(*args, **kwargs)
def build(self, *args, **kwargs):
"""
Set up estimator as an attribute and make initial settings.
Uses estimator from model config variable as estimator.
If config contains key ``init_param``, sets up initial
values ``means_``, ``covars_``, ``transmat_`` and ``startprob_``
of the estimator as defined in ``init_params``.
"""
_ = args, kwargs
self.estimator = self.get("estimator", self.config)
init_params = self.get("init_params", self.config)
if init_params is not None:
if "m" not in self.estimator.init_params:
self.estimator.means_ = init_params["means_"]
if "c" not in self.estimator.init_params:
self.estimator.covars_ = init_params["covars_"]
if "t" not in self.estimator.init_params:
self.estimator.transmat_ = init_params["transmat_"]
if "s" not in self.estimator.init_params:
self.estimator.startprob_ = init_params["startprob_"]
[docs] def save(self, path, *args, **kwargs): # pylint: disable=arguments-differ
"""Save ``HMModel`` with ``dill``.
Parameters
----------
path : str
Path to the file to save model to.
"""
if self.estimator is not None:
with open(path, "wb") as file:
dill.dump(self.estimator, file)
else:
raise ValueError("HMM estimator does not exist. Check your cofig for 'estimator'.")
[docs] def load(self, path, *args, **kwargs): # pylint: disable=arguments-differ
"""Load ``HMModel`` from file with ``dill``.
Parameters
----------
path : str
Path to the model.
"""
with open(path, "rb") as file:
self.estimator = dill.load(file)
[docs] def train(self, X, lengths=None, *args, **kwargs):
""" Train the model using data provided.
Parameters
----------
X : array-like
A matrix of observations.
Should be of shape (n_samples, n_features).
lengths : array-like of integers optional
If present, should be of shape (n_sequences, ).
Lengths of the individual sequences in ``X``. The sum of
these should be ``n_samples``.
Notes
-----
For more details and other parameters look at the documentation for the estimator used.
"""
self.estimator.fit(X, lengths)
return list(self.estimator.monitor_.history)
[docs] def predict(self, X, lengths=None, *args, **kwargs):
""" Make prediction with the data provided.
Parameters
----------
X : array-like
A matrix of observations.
Should be of shape (n_samples, n_features).
lengths : array-like of integers optional
If present, should be of shape (n_sequences, ).
Lengths of the individual sequences in ``X``. The sum of
these should be ``n_samples``.
Returns
-------
output: array
Labels for each sample of X.
Notes
-----
For more details and other parameters look at the documentation for the estimator used.
"""
preds = self.estimator.predict(X, lengths)
if lengths:
output = np.array(np.split(preds, np.cumsum(lengths)[:-1]) + [None])[:-1]
else:
output = preds
return output