From 5657437d82d2180f71d9c4102df15a6556dd9599 Mon Sep 17 00:00:00 2001 From: Edwin Onuonga Date: Fri, 1 Jan 2021 21:53:23 +0400 Subject: [PATCH 1/7] [patch:docs] Add original_labels documentation to KNNClassifier (#133) --- lib/sequentia/classifiers/knn/knn_classifier.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/sequentia/classifiers/knn/knn_classifier.py b/lib/sequentia/classifiers/knn/knn_classifier.py index cf8c5049..8e07c6aa 100644 --- a/lib/sequentia/classifiers/knn/knn_classifier.py +++ b/lib/sequentia/classifiers/knn/knn_classifier.py @@ -147,6 +147,9 @@ def predict(self, X, verbose=True, original_labels=True, n_jobs=1): are always displayed in the console, regardless of where you are running this function from (e.g. a Jupyter notebook). + original_labels: bool + Whether to inverse-transform the labels to their original encoding. + n_jobs: int > 0 or -1 | The number of jobs to run in parallel. | Setting this to -1 will use all available CPU cores. From c65346d570981b218740c2743bbbb9ab59b15e08 Mon Sep 17 00:00:00 2001 From: Edwin Onuonga Date: Sun, 3 Jan 2021 01:43:52 +0400 Subject: [PATCH 2/7] [patch:docs] Simplify GMMHMM documentation (#134) * Fix KNNClassifier documentation typo * Simplify GMMHMM documentation --- docs/sections/classifiers/gmmhmm.rst | 21 +++++++++---------- .../classifiers/knn/knn_classifier.py | 2 +- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/docs/sections/classifiers/gmmhmm.rst b/docs/sections/classifiers/gmmhmm.rst index bb8ac95a..2a65f74f 100644 --- a/docs/sections/classifiers/gmmhmm.rst +++ b/docs/sections/classifiers/gmmhmm.rst @@ -70,17 +70,16 @@ Note that even in the case that multiple Gaussian densities are not needed, the can be adjusted so that irrelevant Gaussians are omitted and only a single Gaussian remains. However, the default setting of the :class:`~GMMHMM` class is a single Gaussian. -Then a GMM-HMM is completely determined by the learnable parameters -:math:`\lambda=(\boldsymbol{\pi}, A, B)` where :math:`B=(C,\Pi,\Psi)` and +Then a GMM-HMM is completely determined by +:math:`\lambda=(\boldsymbol{\pi}, A, B)`, where :math:`B` is a collection of +:math:`M` emission distributions (one for each state :math:`m=1,\ldots,M`), which are each +parameterized by a collection of -- :math:`C=\big(c_1^{(m)}, \ldots, c_K^{(m)}\big)_{m=1}^M` is - a collection of the mixture weights, -- :math:`\Pi=\big(\boldsymbol\mu_1^{(m)}, \ldots, \boldsymbol\mu_K^{(m)}\big)_{m=1}^M` is - a collection of the mean vectors, -- :math:`\Psi=\big(\Sigma_1^{(m)}, \ldots, \Sigma_K^{(m)}\big)_{m=1}^M` is - a collection of the covariance matrices, +- mixture weights :math:`c_1^{(m)}, \ldots, c_K^{(m)}`, +- mean vectors :math:`\boldsymbol\mu_1^{(m)}, \ldots, \boldsymbol\mu_K^{(m)}`, +- covariance matrices :math:`\Sigma_1^{(m)}, \ldots, \Sigma_K^{(m)}`, -for every mixture component of each state of the HMM. +for each of the :math:`1,\ldots,K` mixture components of each state. Usually if :math:`K` is large enough, a mixture of :math:`K` Gaussian densities can effectively model any probability density function. With large enough :math:`K`, we can also restrict the @@ -89,8 +88,8 @@ and at the same time decrease the number of parameters that need to be updated d The covariance matrix type can be specified by a string parameter ``covariance_type`` in the :class:`~GMMHMM` constructor that takes values `'spherical'`, `'diag'`, `'full'` or `'tied'`. -The various types are explained well in this `StackExchange answer `_, -and summarized in the below image (also courtesy of the same StackExchange answerer). +The various types are explained well `here `_, +and summarized in the below image (also courtesy of the author of the response in the previous link). .. image:: /_static/covariance_types.png :alt: Covariance Types diff --git a/lib/sequentia/classifiers/knn/knn_classifier.py b/lib/sequentia/classifiers/knn/knn_classifier.py index 8e07c6aa..cac511c0 100644 --- a/lib/sequentia/classifiers/knn/knn_classifier.py +++ b/lib/sequentia/classifiers/knn/knn_classifier.py @@ -20,7 +20,7 @@ class KNNClassifier: k: int > 0 Number of neighbors. - classes: array-liike of str/numeric + classes: array-like of str/numeric The complete set of possible classes/labels. weighting: 'uniform' or callable From 05a07cfbcd4779c85834d80e73193b269a11b3fa Mon Sep 17 00:00:00 2001 From: Edwin Onuonga Date: Thu, 7 Jan 2021 01:26:32 +0400 Subject: [PATCH 3/7] [add:lib] Add support for dependent feature warping (#135) * Add option for DTWD * Add tests for independent/dependent warping * Add DTWD+DTWI to readme --- README.md | 4 +- .../classifiers/knn/knn_classifier.py | 24 +++++-- .../classifiers/knn/test_knn_classifier.py | 45 +++++++++--- .../Pen-Tip Trajectories (Example).ipynb | 72 +++++++++---------- 4 files changed, 93 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 935fcb0b..7372c7c2 100644 --- a/README.md +++ b/README.md @@ -58,12 +58,12 @@ The following algorithms provided within Sequentia support the use of multivaria ### Classification algorithms -- [x] Hidden Markov Models (via [`hmmlearn`](https://github.com/hmmlearn/hmmlearn))
Learning with the Baum-Welch algorithm [[1]](#references) +- [x] Hidden Markov Models (via [`hmmlearn`](https://github.com/hmmlearn/hmmlearn))
Learning with the Baum-Welch algorithm [[1]](#references) - [x] Gaussian Mixture Model emissions - [x] Linear, left-right and ergodic topologies - [x] Dynamic Time Warping k-Nearest Neighbors (via [`dtaidistance`](https://github.com/wannesm/dtaidistance)) - [x] Sakoe–Chiba band global warping constraint - - [x] Feature-independent warping (DTWI) + - [x] Dependent and independent feature warping (DTWD & DTWI) - [x] Custom distance-weighted predictions - [x] Multi-processed predictions diff --git a/lib/sequentia/classifiers/knn/knn_classifier.py b/lib/sequentia/classifiers/knn/knn_classifier.py index cac511c0..cc4cf934 100644 --- a/lib/sequentia/classifiers/knn/knn_classifier.py +++ b/lib/sequentia/classifiers/knn/knn_classifier.py @@ -1,7 +1,7 @@ import warnings, tqdm, tqdm.auto, numpy as np, types, pickle, marshal from joblib import Parallel, delayed from multiprocessing import cpu_count -from dtaidistance import dtw +from dtaidistance import dtw, dtw_ndim from sklearn.metrics import confusion_matrix from sklearn.preprocessing import LabelEncoder from ...internals import _Validator @@ -60,6 +60,9 @@ class KNNClassifier: pip install -vvv --upgrade --no-cache-dir --force-reinstall dtaidistance + independent: bool + Whether or not to allow features to be warped independently from each other. See `here `_ for a good overview of both approaches. + random_state: numpy.random.RandomState, int, optional A random state object or seed for reproducible randomness. @@ -84,7 +87,7 @@ class KNNClassifier: The complete set of possible classes/labels. """ - def __init__(self, k, classes, weighting='uniform', window=1., use_c=False, random_state=None): + def __init__(self, k, classes, weighting='uniform', window=1., use_c=False, independent=False, random_state=None): self._val = _Validator() self._k = self._val.restricted_integer( k, lambda x: x > 0, desc='number of neighbors', expected='greater than zero') @@ -116,6 +119,9 @@ def __init__(self, k, classes, weighting='uniform', window=1., use_c=False, rand warnings.warn('DTAIDistance C library not available – using Python implementation', ImportWarning) self._use_c = False + self._independent = self._val.boolean(independent, 'independent') + self._dtw = self._dtwi if independent else self._dtwd + def fit(self, X, y): """Fits the classifier by adding labeled training observation sequences. @@ -238,6 +244,7 @@ def save(self, path): 'weighting': marshal.dumps((self._weighting.__code__, self._weighting.__name__)), 'window': self._window, 'use_c': self._use_c, + 'independent': self._independent, 'random_state': self._random_state, 'X': self._X, 'y': self._y, @@ -262,7 +269,7 @@ def load(cls, path): data = pickle.load(file) # Check deserialized object dictionary and keys - keys = set(('k', 'classes', 'weighting', 'window', 'use_c', 'random_state', 'X', 'y', 'n_features')) + keys = set(('k', 'classes', 'weighting', 'window', 'use_c', 'independent', 'random_state', 'X', 'y', 'n_features')) if not isinstance(data, dict): raise TypeError('Expected deserialized object to be a dictionary - make sure the object was serialized with the save() function') else: @@ -280,6 +287,7 @@ def load(cls, path): weighting=weighting, window=data['window'], use_c=data['use_c'], + independent=data['independent'], random_state=data['random_state'] ) @@ -293,11 +301,16 @@ def _dtw_1d(self, a, b, window): # Requires fit """Computes the DTW distance between two univariate sequences.""" return dtw.distance(a, b, use_c=self._use_c, window=window) - def _dtw(self, A, B): # Requires fit - """Computes the multivariate DTW distance as the sum of the pairwise per-feature DTW distances.""" + def _dtwi(self, A, B): # Requires fit + """Computes the multivariate DTW distance as the sum of the pairwise per-feature DTW distances, allowing each feature to be warped independently.""" window = max(1, int(self._window * max(len(A), len(B)))) return np.sum([self._dtw_1d(A[:, i], B[:, i], window=window) for i in range(self._n_features)]) + def _dtwd(self, A, B): # Requires fit + """Computes the multivariate DTW distance so that the warping of the features depends on each other, by modifying the local distance measure.""" + window = max(1, int(self._window * max(len(A), len(B)))) + return dtw_ndim.distance(A, B, use_c=self._use_c, window=window) + def _argmax(self, a): """Same as numpy.argmax but returns all occurrences of the maximum, and is O(n) instead of O(2n). From: https://stackoverflow.com/a/58652335 @@ -394,6 +407,7 @@ def __repr__(self): ('k', repr(self._k)), ('window', repr(self._window)), ('use_c', repr(self._use_c)), + ('independent', repr(self._independent)), ('classes', repr(list(self._encoder.classes_))) ] try: diff --git a/lib/test/lib/classifiers/knn/test_knn_classifier.py b/lib/test/lib/classifiers/knn/test_knn_classifier.py index 43057888..a1a9c987 100644 --- a/lib/test/lib/classifiers/knn/test_knn_classifier.py +++ b/lib/test/lib/classifiers/knn/test_knn_classifier.py @@ -20,7 +20,8 @@ 'k=1': KNNClassifier(k=1, classes=classes, random_state=rng), 'k=2': KNNClassifier(k=2, classes=classes, random_state=rng), 'k=3': KNNClassifier(k=3, classes=classes, random_state=rng), - 'weighted': KNNClassifier(k=3, classes=classes, weighting=(lambda x: np.exp(-x)), random_state=rng) + 'weighted': KNNClassifier(k=3, classes=classes, weighting=(lambda x: np.exp(-x)), random_state=rng), + 'independent': KNNClassifier(k=1, classes=classes, independent=True, random_state=rng) } for _, clf in clfs.items(): @@ -96,6 +97,18 @@ def test_predict_single_weighted_no_verbose(capsys): assert 'Calculating distances' not in capsys.readouterr().err assert prediction == 'c1' +def test_predict_single_independent_verbose(capsys): + """Verbosely predict a single observation sequence with independent warping""" + prediction = clfs['independent'].predict(x, verbose=True) + assert 'Calculating distances' in capsys.readouterr().err + assert prediction == 'c1' + +def test_predict_single_k1_no_verbose(capsys): + """Silently predict a single observation sequence with independent warping""" + prediction = clfs['independent'].predict(x, verbose=False) + assert 'Calculating distances' not in capsys.readouterr().err + assert prediction == 'c1' + def test_predict_multiple_k1_verbose(capsys): """Verbosely predict multiple observation sequences (k=1)""" predictions = clfs['k=1'].predict(X, verbose=True) @@ -124,25 +137,37 @@ def test_predict_multiple_k3_verbose(capsys): """Verbosely predict multiple observation sequences (k=3)""" predictions = clfs['k=3'].predict(X, verbose=True) assert 'Classifying examples' in capsys.readouterr().err - assert list(predictions) == ['c1', 'c1', 'c1', 'c0', 'c0', 'c0'] + assert list(predictions) == ['c1', 'c1', 'c1', 'c1', 'c0', 'c1'] def test_predict_multiple_k3_no_verbose(capsys): """Silently predict multiple observation sequences (k=3)""" predictions = clfs['k=3'].predict(X, verbose=False) assert 'Classifying examples' not in capsys.readouterr().err - assert list(predictions) == ['c1', 'c1', 'c1', 'c0', 'c0', 'c0'] + assert list(predictions) == ['c1', 'c1', 'c1', 'c1', 'c0', 'c1'] def test_predict_multiple_weighted_verbose(capsys): """Verbosely predict multiple observation sequences (weighted)""" predictions = clfs['weighted'].predict(X, verbose=True) assert 'Classifying examples' in capsys.readouterr().err - assert list(predictions) == ['c1', 'c1', 'c0', 'c0', 'c0', 'c1'] + assert list(predictions) == ['c1', 'c1', 'c0', 'c1', 'c0', 'c1'] def test_predict_multiple_weighted_no_verbose(capsys): """Silently predict multiple observation sequences (weighted)""" predictions = clfs['weighted'].predict(X, verbose=False) assert 'Classifying examples' not in capsys.readouterr().err - assert list(predictions) == ['c1', 'c1', 'c0', 'c0', 'c0', 'c1'] + assert list(predictions) == ['c1', 'c1', 'c0', 'c1', 'c0', 'c1'] + +def test_predict_multiple_independent_verbose(capsys): + """Verbosely predict multiple observation sequences with independent warping""" + predictions = clfs['independent'].predict(X, verbose=True) + assert 'Classifying examples' in capsys.readouterr().err + assert list(predictions) == ['c1', 'c1', 'c0', 'c1', 'c1', 'c0'] + +def test_predict_multiple_independent_no_verbose(capsys): + """Silently predict multiple observation sequences with independent warping""" + predictions = clfs['independent'].predict(X, verbose=False) + assert 'Classifying examples' not in capsys.readouterr().err + assert list(predictions) == ['c1', 'c1', 'c0', 'c1', 'c1', 'c0'] def test_predict_single(): """Predict a single observation sequence and don't return the original labels""" @@ -157,12 +182,12 @@ def test_predict_single_original_labels(): def test_predict_multiple(): """Predict multiple observation sequences and don't return the original labels""" predictions = clfs['k=3'].predict(X, verbose=False, original_labels=False) - assert list(predictions) == [1, 1, 1, 0, 0, 0] + assert list(predictions) == [1, 1, 1, 1, 0, 1] def test_predict_multiple_original_labels(): """Predict multiple observation sequences and return the original labels""" predictions = clfs['k=3'].predict(X, verbose=False, original_labels=True) - assert list(predictions) == ['c1', 'c1', 'c1', 'c0', 'c0', 'c0'] + assert list(predictions) == ['c1', 'c1', 'c1', 'c1', 'c0', 'c1'] # ======================== # # KNNClassifier.evaluate() # @@ -173,8 +198,8 @@ def test_evaluate(): acc, cm = clfs['k=3'].evaluate(X, y) assert acc == 0.5 assert_equal(cm, np.array([ - [1, 1, 0, 0, 0], - [2, 2, 0, 0, 0], + [0, 2, 0, 0, 0], + [1, 3, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0] @@ -249,6 +274,7 @@ def test_load_valid_no_weighting(): assert list(clf._encoder.classes_) == classes assert clf._window == 1. assert clf._use_c == False + assert clf._independent == False assert deepcopy(clf._random_state).normal() == deepcopy(rng).normal() assert_all_equal(clf._X, X) assert_equal(clf._y, clf._encoder.transform(y)) @@ -271,6 +297,7 @@ def test_load_valid_weighting(): assert list(clf._encoder.classes_) == classes assert clf._window == 1. assert clf._use_c == False + assert clf._independent == False assert deepcopy(clf._random_state).normal() == deepcopy(rng).normal() assert_all_equal(clf._X, X) assert_equal(clf._y, clf._encoder.transform(y)) diff --git a/notebooks/Pen-Tip Trajectories (Example).ipynb b/notebooks/Pen-Tip Trajectories (Example).ipynb index 5e8aadb7..19e97d15 100644 --- a/notebooks/Pen-Tip Trajectories (Example).ipynb +++ b/notebooks/Pen-Tip Trajectories (Example).ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 3, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -56,7 +56,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -93,7 +93,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -120,7 +120,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -142,7 +142,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -184,7 +184,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -218,7 +218,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -263,7 +263,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -293,7 +293,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -323,7 +323,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -344,7 +344,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -383,7 +383,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -403,13 +403,13 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "0f3bf4349086417dbba1fba33f5fff2a", + "model_id": "9036fa23214f4727b2ad661a19a549c4", "version_major": 2, "version_minor": 0 }, @@ -426,7 +426,7 @@ "'w'" ] }, - "execution_count": 15, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -438,13 +438,13 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "2be0f98157f84e43af99b6895902c651", + "model_id": "1a3610f874c4400cab88d38292c49468", "version_major": 2, "version_minor": 0 }, @@ -461,8 +461,8 @@ "text": [ "w c d e a e b h s v c y w e v v w v v b o e l c d c p n h p y p m h d a y d b n m m a g o g c n l y\n", "\n", - "CPU times: user 5.16 s, sys: 229 ms, total: 5.39 s\n", - "Wall time: 5.43 s\n" + "CPU times: user 1.75 s, sys: 108 ms, total: 1.86 s\n", + "Wall time: 2.2 s\n" ] } ], @@ -482,7 +482,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -491,8 +491,8 @@ "text": [ "w c d e a e b h s v c y w e v v w v v b o e l c d c p n h p y p m h d a y d b n m m a g o g c n l y\n", "\n", - "CPU times: user 705 ms, sys: 85.8 ms, total: 791 ms\n", - "Wall time: 5.1 s\n" + "CPU times: user 699 ms, sys: 80.5 ms, total: 779 ms\n", + "Wall time: 3.73 s\n" ] } ], @@ -512,7 +512,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 16, "metadata": { "scrolled": true }, @@ -521,8 +521,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 576 ms, sys: 20 ms, total: 596 ms\n", - "Wall time: 40.4 s\n" + "CPU times: user 542 ms, sys: 17.1 ms, total: 559 ms\n", + "Wall time: 21.9 s\n" ] } ], @@ -533,12 +533,12 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 17, "metadata": {}, "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -552,7 +552,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Accuracy: 97.03%\n" + "Accuracy: 97.73%\n" ] } ], @@ -568,7 +568,7 @@ "\n", "While the fast C compiled functions in the [`dtaidistance`](https://github.com/wannesm/dtaidistance) package (along with the multiprocessing capabilities of Sequentia's `KNNClassifier`) help to speed up classification **a lot**, the practical use of $k$-NN becomes more limited as the dataset grows larger. \n", "\n", - "In this case, since our dataset is relatively small, classifying all test examples was completed in $\\approx40s$, which is even faster than the HMM classifier that we show below. " + "In this case, since our dataset is relatively small, classifying all test examples was completed in $\\approx22s$, which is even faster than the HMM classifier that we show below. " ] }, { @@ -599,13 +599,13 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "6daa1c40652d4c539f9fda73cebb3076", + "model_id": "0fbffb1d3bbc44b5b6356b54a89a61b2", "version_major": 2, "version_minor": 0 }, @@ -638,7 +638,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -648,15 +648,15 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 3min 20s, sys: 16.2 s, total: 3min 36s\n", - "Wall time: 2min 2s\n" + "CPU times: user 3min 33s, sys: 18 s, total: 3min 51s\n", + "Wall time: 2min 34s\n" ] } ], @@ -667,7 +667,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 21, "metadata": {}, "outputs": [ { From 73c5834cc486f7166934deca3c4a72115d8c5309 Mon Sep 17 00:00:00 2001 From: Edwin Onuonga Date: Thu, 7 Jan 2021 03:21:11 +0400 Subject: [PATCH 4/7] [add:lib] Add multi-processed predictions for HMMClassifier (#136) --- README.md | 1 + .../classifiers/hmm/hmm_classifier.py | 53 ++++++++++++++++--- .../classifiers/knn/knn_classifier.py | 5 +- .../Pen-Tip Trajectories (Example).ipynb | 26 ++++----- 4 files changed, 64 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 7372c7c2..2b096231 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ The following algorithms provided within Sequentia support the use of multivaria - [x] Hidden Markov Models (via [`hmmlearn`](https://github.com/hmmlearn/hmmlearn))
Learning with the Baum-Welch algorithm [[1]](#references) - [x] Gaussian Mixture Model emissions - [x] Linear, left-right and ergodic topologies + - [x] Multi-processed predictions - [x] Dynamic Time Warping k-Nearest Neighbors (via [`dtaidistance`](https://github.com/wannesm/dtaidistance)) - [x] Sakoe–Chiba band global warping constraint - [x] Dependent and independent feature warping (DTWD & DTWI) diff --git a/lib/sequentia/classifiers/hmm/hmm_classifier.py b/lib/sequentia/classifiers/hmm/hmm_classifier.py index 8f2ef552..08f2cfa3 100644 --- a/lib/sequentia/classifiers/hmm/hmm_classifier.py +++ b/lib/sequentia/classifiers/hmm/hmm_classifier.py @@ -1,4 +1,6 @@ -import numpy as np, pickle +import tqdm, tqdm.auto, numpy as np, pickle +from joblib import Parallel, delayed +from multiprocessing import cpu_count from .gmmhmm import GMMHMM from sklearn.metrics import confusion_matrix from sklearn.preprocessing import LabelEncoder @@ -43,7 +45,7 @@ def fit(self, models): self._encoder = LabelEncoder() self._encoder.fit([model.label for model in models]) - def predict(self, X, prior='frequency', return_scores=False, original_labels=True): + def predict(self, X, prior='frequency', verbose=True, return_scores=False, original_labels=True, n_jobs=1): """Predicts the label for an observation sequence (or multiple sequences) according to maximum likelihood or posterior scores. Parameters @@ -60,12 +62,24 @@ def predict(self, X, prior='frequency', return_scores=False, original_labels=Tru Alternatively, class prior probabilities can be specified in an iterable of floats, e.g. `[0.1, 0.3, 0.6]`. + verbose: bool + Whether to display a progress bar or not. + + .. note:: + If both ``verbose=True`` and ``n_jobs > 1``, then the progress bars for each process + are always displayed in the console, regardless of where you are running this function from + (e.g. a Jupyter notebook). + return_scores: bool Whether to return the scores of each model on the observation sequence(s). original_labels: bool Whether to inverse-transform the labels to their original encoding. + n_jobs: int > 0 or -1 + | The number of jobs to run in parallel. + | Setting this to -1 will use all available CPU cores. + Returns ------- prediction(s): str/numeric or :class:`numpy:numpy.ndarray` (str/numeric) @@ -91,7 +105,10 @@ def predict(self, X, prior='frequency', return_scores=False, original_labels=Tru assert np.isclose(sum(prior), 1.), 'Class priors must form a probability distribution by summing to one' else: self._val.one_of(prior, ['frequency', 'uniform'], desc='prior') + self._val.boolean(verbose, desc='verbose') self._val.boolean(return_scores, desc='return_scores') + self._val.boolean(original_labels, desc='original_labels') + self._val.restricted_integer(n_jobs, lambda x: x == -1 or x > 0, 'number of jobs', '-1 or greater than zero') # Create look-up for prior probabilities if prior == 'frequency': @@ -105,10 +122,15 @@ def predict(self, X, prior='frequency', return_scores=False, original_labels=Tru # Convert single observation sequence to a singleton list X = [X] if isinstance(X, np.ndarray) else X + # Lambda for calculating the log un-normalized posteriors as a sum of the log forward probabilities (likelihoods) and log priors + posteriors = lambda x: np.array([model.forward(x) + np.log(prior[model.label]) for model in self._models]) + # Calculate log un-normalized posteriors as a sum of the log forward probabilities (likelihoods) and log priors # Perform the MAP classification rule and return labels to original encoding if necessary - posteriors = lambda x: np.array([model.forward(x) + np.log(prior[model.label]) for model in self._models]) - scores = np.array([posteriors(x) for x in X]) + n_jobs = min(cpu_count() if n_jobs == -1 else n_jobs, len(X)) + X_chunks = [list(chunk) for chunk in np.array_split(np.array(X, dtype=object), n_jobs)] + scores = Parallel(n_jobs=n_jobs)(delayed(self._chunk_predict)(i+1, posteriors, chunk, verbose) for i, chunk in enumerate(X_chunks)) + scores = np.concatenate(scores) best_idxs = np.atleast_1d(scores.argmax(axis=1)) labels = self._encoder.inverse_transform(best_idxs) if original_labels else best_idxs @@ -117,7 +139,7 @@ def predict(self, X, prior='frequency', return_scores=False, original_labels=Tru else: return (labels, scores) if return_scores else labels - def evaluate(self, X, y, prior='frequency'): + def evaluate(self, X, y, prior='frequency', verbose=True, n_jobs=1): """Evaluates the performance of the classifier on a batch of observation sequences and their labels. Parameters @@ -137,6 +159,18 @@ def evaluate(self, X, y, prior='frequency'): Alternatively, class prior probabilities can be specified in an iterable of floats, e.g. `[0.1, 0.3, 0.6]`. + verbose: bool + Whether to display a progress bar or not. + + .. note:: + If both ``verbose=True`` and ``n_jobs > 1``, then the progress bars for each process + are always displayed in the console, regardless of where you are running this function from + (e.g. a Jupyter notebook). + + n_jobs: int > 0 or -1 + | The number of jobs to run in parallel. + | Setting this to -1 will use all available CPU cores. + Returns ------- accuracy: float @@ -146,7 +180,7 @@ def evaluate(self, X, y, prior='frequency'): The confusion matrix representing the discrepancy between predicted and actual labels. """ X, y = self._val.observation_sequences_and_labels(X, y) - predictions = self.predict(X, prior=prior, return_scores=False, original_labels=False) + predictions = self.predict(X, prior=prior, return_scores=False, original_labels=False, verbose=verbose, n_jobs=n_jobs) cm = confusion_matrix(self._encoder.transform(y), predictions, labels=self._encoder.transform(self._encoder.classes_)) return np.sum(np.diag(cm)) / np.sum(cm), cm @@ -183,6 +217,13 @@ def load(cls, path): with open(path, 'rb') as file: return pickle.load(file) + def _chunk_predict(self, process, posteriors, chunk, verbose): # Requires fit + """Makes predictions (scores) for a chunk of the observation sequences, for a given subprocess.""" + return np.array([posteriors(x) for x in tqdm.auto.tqdm( + chunk, desc='Classifying examples (process {})'.format(process), + disable=not(verbose), position=process-1 + )]) + @property def models(self): try: diff --git a/lib/sequentia/classifiers/knn/knn_classifier.py b/lib/sequentia/classifiers/knn/knn_classifier.py index cc4cf934..7ac38e2b 100644 --- a/lib/sequentia/classifiers/knn/knn_classifier.py +++ b/lib/sequentia/classifiers/knn/knn_classifier.py @@ -175,15 +175,16 @@ def predict(self, X, verbose=True, original_labels=True, n_jobs=1): X = self._val.observation_sequences(X, allow_single=True) self._val.boolean(verbose, desc='verbose') + self._val.boolean(original_labels, desc='original_labels') self._val.restricted_integer(n_jobs, lambda x: x == -1 or x > 0, 'number of jobs', '-1 or greater than zero') if isinstance(X, np.ndarray): distances = np.array([self._dtw(X, x) for x in tqdm.auto.tqdm(self._X, desc='Calculating distances', disable=not(verbose))]) return self._output(self._find_nearest(distances), original_labels) else: - n_jobs = cpu_count() if n_jobs == -1 else n_jobs + n_jobs = min(cpu_count() if n_jobs == -1 else n_jobs, len(X)) X_chunks = [list(chunk) for chunk in np.array_split(np.array(X, dtype=object), n_jobs)] - labels = Parallel(n_jobs=min(n_jobs, len(X)))(delayed(self._chunk_predict)(i+1, chunk, verbose) for i, chunk in enumerate(X_chunks)) + labels = Parallel(n_jobs=n_jobs)(delayed(self._chunk_predict)(i+1, chunk, verbose) for i, chunk in enumerate(X_chunks)) return self._output(np.concatenate(labels), original_labels) # Flatten the resulting array def evaluate(self, X, y, verbose=True, n_jobs=1): diff --git a/notebooks/Pen-Tip Trajectories (Example).ipynb b/notebooks/Pen-Tip Trajectories (Example).ipynb index 19e97d15..cf1e023a 100644 --- a/notebooks/Pen-Tip Trajectories (Example).ipynb +++ b/notebooks/Pen-Tip Trajectories (Example).ipynb @@ -409,7 +409,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "9036fa23214f4727b2ad661a19a549c4", + "model_id": "3ddd5bf499294bf0bc0963ab4dbb6ece", "version_major": 2, "version_minor": 0 }, @@ -444,7 +444,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "1a3610f874c4400cab88d38292c49468", + "model_id": "c2f3a92e26114f1fbb4d45058698b747", "version_major": 2, "version_minor": 0 }, @@ -461,8 +461,8 @@ "text": [ "w c d e a e b h s v c y w e v v w v v b o e l c d c p n h p y p m h d a y d b n m m a g o g c n l y\n", "\n", - "CPU times: user 1.75 s, sys: 108 ms, total: 1.86 s\n", - "Wall time: 2.2 s\n" + "CPU times: user 1.68 s, sys: 195 ms, total: 1.88 s\n", + "Wall time: 1.98 s\n" ] } ], @@ -491,8 +491,8 @@ "text": [ "w c d e a e b h s v c y w e v v w v v b o e l c d c p n h p y p m h d a y d b n m m a g o g c n l y\n", "\n", - "CPU times: user 699 ms, sys: 80.5 ms, total: 779 ms\n", - "Wall time: 3.73 s\n" + "CPU times: user 721 ms, sys: 90.5 ms, total: 811 ms\n", + "Wall time: 3.24 s\n" ] } ], @@ -521,8 +521,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 542 ms, sys: 17.1 ms, total: 559 ms\n", - "Wall time: 21.9 s\n" + "CPU times: user 556 ms, sys: 17.3 ms, total: 573 ms\n", + "Wall time: 10.1 s\n" ] } ], @@ -568,7 +568,7 @@ "\n", "While the fast C compiled functions in the [`dtaidistance`](https://github.com/wannesm/dtaidistance) package (along with the multiprocessing capabilities of Sequentia's `KNNClassifier`) help to speed up classification **a lot**, the practical use of $k$-NN becomes more limited as the dataset grows larger. \n", "\n", - "In this case, since our dataset is relatively small, classifying all test examples was completed in $\\approx22s$, which is even faster than the HMM classifier that we show below. " + "In this case, since our dataset is relatively small, classifying all test examples was completed in $\\approx10s$, which is even faster than the HMM classifier that we show below. " ] }, { @@ -605,7 +605,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "0fbffb1d3bbc44b5b6356b54a89a61b2", + "model_id": "30052beed780408db9e7b1f1212b6404", "version_major": 2, "version_minor": 0 }, @@ -655,14 +655,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 3min 33s, sys: 18 s, total: 3min 51s\n", - "Wall time: 2min 34s\n" + "CPU times: user 197 ms, sys: 13.5 ms, total: 210 ms\n", + "Wall time: 55.4 s\n" ] } ], "source": [ "%%time\n", - "acc, cm = clf.evaluate(X_test, y_test)" + "acc, cm = clf.evaluate(X_test, y_test, n_jobs=-1)" ] }, { From a7c6c4fc134a380e572a6f87d3332e887a872d90 Mon Sep 17 00:00:00 2001 From: Edwin Onuonga Date: Thu, 7 Jan 2021 03:38:26 +0400 Subject: [PATCH 5/7] [patch:docs] Fix posterior comment in classifier.svg (#137) --- docs/_static/classifier.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_static/classifier.svg b/docs/_static/classifier.svg index 6538744b..a802b033 100644 --- a/docs/_static/classifier.svg +++ b/docs/_static/classifier.svg @@ -1,3 +1,3 @@ -
Class

Class 1...
Class

Class 2...
Class

Class C...
Train a HMM for each class
(Baum-Welch algorithm)
Train a HMM for each class...
Calculate un-normalized posterior (likelihood prior)
for a new observation sequence
(Forward algorithm)
Calculate un-normalized posterior...
\lamb...
Find the HMM that maximizes the posterior probability of given
Find the HMM that maximizes the post...
\math...
c^{(n...
c^{(n...
c^{(n...
Split training data by class
Split training data by class
\mathb...
\mathb...
\mathb...
c'
Assign to the class represented by the model with the highest posterior
Assign O' to the class c' represente...
Viewer does not support full SVG 1.1
\ No newline at end of file +
Class

Class 1...
Class

Class 2...
Class

Class C...
Train a HMM for each class
(Baum-Welch algorithm)
Train a HMM for each class...
Calculate un-normalized posterior (likelihood prior)
for a new observation sequence
(Forward algorithm)
Calculate un-normalized posterior...
\lamb...
Find the HMM that maximizes the posterior probability of given
Find the HMM that maximizes the post...
\math...
c^{(n...
c^{(n...
c^{(n...
Split training data by class
Split training data by class
\mathb...
\mathb...
\mathb...
c'
Assign to the class represented by the model with the highest posterior
Assign O' to the class c' represente...
Viewer does not support full SVG 1.1
\ No newline at end of file From 7704bb1484ba48e279b1b433aac4414b8e79afc1 Mon Sep 17 00:00:00 2001 From: Edwin Onuonga Date: Thu, 7 Jan 2021 03:58:50 +0400 Subject: [PATCH 6/7] [patch:lib] Re-order predict() and evaluate() arguments (#138) * Update KNNClassifier argument order * Reorder HMMClassifier arguments --- lib/sequentia/classifiers/hmm/hmm_classifier.py | 16 ++++++++-------- lib/sequentia/classifiers/knn/knn_classifier.py | 12 ++++++------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/sequentia/classifiers/hmm/hmm_classifier.py b/lib/sequentia/classifiers/hmm/hmm_classifier.py index 08f2cfa3..96d1a6c8 100644 --- a/lib/sequentia/classifiers/hmm/hmm_classifier.py +++ b/lib/sequentia/classifiers/hmm/hmm_classifier.py @@ -45,7 +45,7 @@ def fit(self, models): self._encoder = LabelEncoder() self._encoder.fit([model.label for model in models]) - def predict(self, X, prior='frequency', verbose=True, return_scores=False, original_labels=True, n_jobs=1): + def predict(self, X, prior='frequency', return_scores=False, original_labels=True, verbose=True, n_jobs=1): """Predicts the label for an observation sequence (or multiple sequences) according to maximum likelihood or posterior scores. Parameters @@ -62,6 +62,12 @@ def predict(self, X, prior='frequency', verbose=True, return_scores=False, origi Alternatively, class prior probabilities can be specified in an iterable of floats, e.g. `[0.1, 0.3, 0.6]`. + return_scores: bool + Whether to return the scores of each model on the observation sequence(s). + + original_labels: bool + Whether to inverse-transform the labels to their original encoding. + verbose: bool Whether to display a progress bar or not. @@ -70,12 +76,6 @@ def predict(self, X, prior='frequency', verbose=True, return_scores=False, origi are always displayed in the console, regardless of where you are running this function from (e.g. a Jupyter notebook). - return_scores: bool - Whether to return the scores of each model on the observation sequence(s). - - original_labels: bool - Whether to inverse-transform the labels to their original encoding. - n_jobs: int > 0 or -1 | The number of jobs to run in parallel. | Setting this to -1 will use all available CPU cores. @@ -105,9 +105,9 @@ def predict(self, X, prior='frequency', verbose=True, return_scores=False, origi assert np.isclose(sum(prior), 1.), 'Class priors must form a probability distribution by summing to one' else: self._val.one_of(prior, ['frequency', 'uniform'], desc='prior') - self._val.boolean(verbose, desc='verbose') self._val.boolean(return_scores, desc='return_scores') self._val.boolean(original_labels, desc='original_labels') + self._val.boolean(verbose, desc='verbose') self._val.restricted_integer(n_jobs, lambda x: x == -1 or x > 0, 'number of jobs', '-1 or greater than zero') # Create look-up for prior probabilities diff --git a/lib/sequentia/classifiers/knn/knn_classifier.py b/lib/sequentia/classifiers/knn/knn_classifier.py index 7ac38e2b..882eace1 100644 --- a/lib/sequentia/classifiers/knn/knn_classifier.py +++ b/lib/sequentia/classifiers/knn/knn_classifier.py @@ -137,7 +137,7 @@ def fit(self, X, y): self._X, self._y = X, self._encoder.transform(y) self._n_features = X[0].shape[1] - def predict(self, X, verbose=True, original_labels=True, n_jobs=1): + def predict(self, X, original_labels=True, verbose=True, n_jobs=1): """Predicts the label for an observation sequence (or multiple sequences). Parameters @@ -145,6 +145,9 @@ def predict(self, X, verbose=True, original_labels=True, n_jobs=1): X: numpy.ndarray (float) or list of numpy.ndarray (float) An individual observation sequence or a list of multiple observation sequences. + original_labels: bool + Whether to inverse-transform the labels to their original encoding. + verbose: bool Whether to display a progress bar or not. @@ -153,9 +156,6 @@ def predict(self, X, verbose=True, original_labels=True, n_jobs=1): are always displayed in the console, regardless of where you are running this function from (e.g. a Jupyter notebook). - original_labels: bool - Whether to inverse-transform the labels to their original encoding. - n_jobs: int > 0 or -1 | The number of jobs to run in parallel. | Setting this to -1 will use all available CPU cores. @@ -174,8 +174,8 @@ def predict(self, X, verbose=True, original_labels=True, n_jobs=1): raise RuntimeError('The classifier needs to be fitted before predictions are made') X = self._val.observation_sequences(X, allow_single=True) - self._val.boolean(verbose, desc='verbose') self._val.boolean(original_labels, desc='original_labels') + self._val.boolean(verbose, desc='verbose') self._val.restricted_integer(n_jobs, lambda x: x == -1 or x > 0, 'number of jobs', '-1 or greater than zero') if isinstance(X, np.ndarray): @@ -215,7 +215,7 @@ def evaluate(self, X, y, verbose=True, n_jobs=1): """ X, y = self._val.observation_sequences_and_labels(X, y) self._val.boolean(verbose, desc='verbose') - predictions = self.predict(X, verbose=verbose, original_labels=False, n_jobs=n_jobs) + predictions = self.predict(X, original_labels=False, verbose=verbose, n_jobs=n_jobs) cm = confusion_matrix(self._encoder.transform(y), predictions, labels=self._encoder.transform(self._encoder.classes_)) return np.sum(np.diag(cm)) / np.sum(cm), cm From f1a80ae0e3c627e7d0236ad1be6dfabd6f9e0eea Mon Sep 17 00:00:00 2001 From: Edwin Onuonga Date: Thu, 7 Jan 2021 15:12:41 +0400 Subject: [PATCH 7/7] =?UTF-8?q?[release]=200.10.2=20=F0=9F=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 14 ++++++++++++++ lib/sequentia/__init__.py | 2 +- setup.py | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ace06b6e..e292e9a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +## [0.10.2](https://github.com/eonu/sequentia/releases/tag/v0.10.2) + +#### Major changes + +- Add support for dependent feature warping (addresses [#124](https://github.com/eonu/sequentia/pull/124)). ([#135](https://github.com/eonu/sequentia/pull/135)) +- Add multi-processed predictions for `HMMClassifier` (addresses [#121](https://github.com/eonu/sequentia/pull/121)). ([#136](https://github.com/eonu/sequentia/pull/136)) +- Re-order `predict()` and `evaluate()` arguments. ([#138](https://github.com/eonu/sequentia/pull/138)) + +#### Minor changes + +- Add `original_labels` documentation to `KNNClassifier`. ([#133](https://github.com/eonu/sequentia/pull/133)) +- Simplify `GMMHMM` documentation. ([#134](https://github.com/eonu/sequentia/pull/134)) +- Fix posterior comment in `classifier.svg`. ([#137](https://github.com/eonu/sequentia/pull/137)) + ## [0.10.1](https://github.com/eonu/sequentia/releases/tag/v0.10.1) #### Minor changes diff --git a/lib/sequentia/__init__.py b/lib/sequentia/__init__.py index e7c389e7..8a295646 100644 --- a/lib/sequentia/__init__.py +++ b/lib/sequentia/__init__.py @@ -1,4 +1,4 @@ -__version__ = '0.10.1' +__version__ = '0.10.2' from .classifiers import * from .preprocessing import * \ No newline at end of file diff --git a/setup.py b/setup.py index aa862eae..063e68bc 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ 'joblib>=0.14,<1' ] -VERSION = '0.10.1' +VERSION = '0.10.2' with open('README.md', 'r') as fh: long_description = fh.read()