From db24460c91e76e357d7e4892493b873e504d8ee4 Mon Sep 17 00:00:00 2001 From: Edwin Onuonga Date: Sun, 29 Dec 2019 19:16:15 +0400 Subject: [PATCH] [add:docs] Add proper documentation (#40) * Add proper documentation * Remove and ignore docs/_build directory --- .gitignore | 3 + CHANGELOG.md | 8 +- README.md | 39 +- docs/Makefile | 20 + docs/_includes/examples/classifiers/dtwknn.py | 13 + docs/_includes/examples/classifiers/hmm.py | 11 + .../examples/classifiers/hmm_classifier.py | 21 + .../examples/preprocessing/downsample.py | 8 + docs/_includes/examples/preprocessing/fft.py | 8 + .../examples/preprocessing/normalize.py | 8 + .../examples/preprocessing/preprocess.py | 14 + docs/changelog.rst | 6 + docs/conf.py | 69 ++ docs/index.rst | 58 ++ docs/sections/classifiers/dtwknn.rst | 45 ++ docs/sections/classifiers/hmm.rst | 123 ++++ docs/sections/preprocessing/downsample.rst | 25 + docs/sections/preprocessing/fft.rst | 26 + docs/sections/preprocessing/normalize.rst | 38 + docs/sections/preprocessing/preprocessing.rst | 22 + examples/1 - Input Format (Tutorial).ipynb | 32 - examples/2 - Preprocessing (Tutorial).ipynb | 32 - examples/Pen-Tip Trajectories (Example).ipynb | 654 ------------------ lib/sequentia/classifiers/dtwknn/dtwknn.py | 112 +-- lib/sequentia/classifiers/hmm/hmm.py | 174 ++--- .../classifiers/hmm/hmm_classifier.py | 104 +-- lib/sequentia/preprocessing/methods.py | 60 +- lib/sequentia/preprocessing/preprocess.py | 55 +- 28 files changed, 820 insertions(+), 968 deletions(-) create mode 100644 docs/Makefile create mode 100644 docs/_includes/examples/classifiers/dtwknn.py create mode 100644 docs/_includes/examples/classifiers/hmm.py create mode 100644 docs/_includes/examples/classifiers/hmm_classifier.py create mode 100644 docs/_includes/examples/preprocessing/downsample.py create mode 100644 docs/_includes/examples/preprocessing/fft.py create mode 100644 docs/_includes/examples/preprocessing/normalize.py create mode 100644 docs/_includes/examples/preprocessing/preprocess.py create mode 100644 docs/changelog.rst create mode 100644 docs/conf.py create mode 100644 docs/index.rst create mode 100644 docs/sections/classifiers/dtwknn.rst create mode 100644 docs/sections/classifiers/hmm.rst create mode 100644 docs/sections/preprocessing/downsample.rst create mode 100644 docs/sections/preprocessing/fft.rst create mode 100644 docs/sections/preprocessing/normalize.rst create mode 100644 docs/sections/preprocessing/preprocessing.rst delete mode 100644 examples/1 - Input Format (Tutorial).ipynb delete mode 100644 examples/2 - Preprocessing (Tutorial).ipynb delete mode 100644 examples/Pen-Tip Trajectories (Example).ipynb diff --git a/.gitignore b/.gitignore index 2f5fd2b5..abce4b0d 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,9 @@ coverage.xml .hypothesis/ .pytest_cache/ +# Documentation +docs/_build + # Jupyter Notebook .ipynb_checkpoints diff --git a/CHANGELOG.md b/CHANGELOG.md index 6876fee8..2577a73f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,9 @@ -# [0.2.0](https://github.com/eonu/sequentia/releases/tag/v0.2.0) +## [0.2.0](https://github.com/eonu/sequentia/releases/tag/v0.2.0) #### Major changes - Add multi-processing support for `DTWKNN` predictions. ([#29](https://github.com/eonu/sequentia/pull/29)) - Rename the `fit_transform()` function in `Preprocess` to `transform()` since there is nothing being fitted. ([#35](https://github.com/eonu/sequentia/pull/35)) -- Modify package classifiers in `setup.py`: ([#31](https://github.com/eonu/sequentia/pull/31)) +- Modify package classifiers in `setup.py` ([#31](https://github.com/eonu/sequentia/pull/31)): - Set development status classifier to `Pre-Alpha`. - Add Python version classifiers for v3.5+. - Specify UNIX and macOS operating system classifiers. @@ -11,11 +11,11 @@ #### Minor changes - Finish tutorial and example notebooks. ([#35](https://github.com/eonu/sequentia/pull/35)) - Rename `examples` directory to `notebooks`. ([#32](https://github.com/eonu/sequentia/pull/32)) -- Host notebooks statically on [`nbviewer`](https://github.com/jupyter/nbviewer). ([#32](https://github.com/eonu/sequentia/pull/32)) +- Host notebooks statically on [nbviewer](https://github.com/jupyter/nbviewer). ([#32](https://github.com/eonu/sequentia/pull/32)) - Add reference to Pomegranate [paper](http://jmlr.org/papers/volume18/17-636/17-636.pdf) and [repository](https://github.com/jmschrei/pomegranate). ([#30](https://github.com/eonu/sequentia/pull/30)) - Add badges to `README.md`. ([#28](https://github.com/eonu/sequentia/pull/28)) -# [0.1.0](https://github.com/eonu/sequentia/releases/tag/v0.1.0) +## [0.1.0](https://github.com/eonu/sequentia/releases/tag/v0.1.0) #### Major changes diff --git a/README.md b/README.md index bd058bba..8c4346df 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,26 @@ +

+ Sequentia +

+ +

+ A machine learning interface for isolated temporal sequence classification algorithms in Python. +

+
- + PyPI - + PyPI - Python Version PyPI - License + + Read The Docs - Documentation +
-# Sequentia - -_A machine learning interface for isolated temporal sequence classification algorithms in Python._ - ## Introduction @@ -44,7 +51,7 @@ Sequentia offers the use of **multivariate observation sequences with differing ### Preprocessing methods -- [x] Normalization (centering observation sequences) +- [x] Normalization - [x] Downsampling (by decimation and averaging) - [x] Discrete (Fast) Fourier Transform @@ -60,6 +67,10 @@ Sequentia offers the use of **multivariate observation sequences with differing pip install sequentia ``` +## Documentation + +Documentation for the package is available on [Read The Docs](https://sequentia.readthedocs.io/en/latest). + ## Tutorials and examples For tutorials and examples on the usage of Sequentia, [look at the notebooks here](https://nbviewer.jupyter.org/github/eonu/sequentia/tree/master/notebooks/)! @@ -91,13 +102,13 @@ All contributions to this repository are greatly appreciated. Contribution guide - - Edwin Onuonga -
Edwin Onuonga -
-
- βœ‰οΈ - 🌍 + + Edwin Onuonga +
Edwin Onuonga +
+
+ βœ‰οΈ + 🌍 diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..d4bb2cbb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_includes/examples/classifiers/dtwknn.py b/docs/_includes/examples/classifiers/dtwknn.py new file mode 100644 index 00000000..2754ec24 --- /dev/null +++ b/docs/_includes/examples/classifiers/dtwknn.py @@ -0,0 +1,13 @@ +import numpy as np +from sequentia.classifiers import DTWKNN + +# Create some sample data +X = [np.random.random((10 * i, 3)) for i in range(1, 4)] +y = ['class0', 'class1', 'class1'] + +# Create and fit the classifier +clf = DTWKNN(k=1, radius=5) +clf.fit(X, y) + +# Predict labels for the training data (just as an example) +clf.predict(X) \ No newline at end of file diff --git a/docs/_includes/examples/classifiers/hmm.py b/docs/_includes/examples/classifiers/hmm.py new file mode 100644 index 00000000..510b9582 --- /dev/null +++ b/docs/_includes/examples/classifiers/hmm.py @@ -0,0 +1,11 @@ +import numpy as np +from sequentia.classifiers import HMM + +# Create some sample data +X = [np.random.random((10 * i, 3)) for i in range(1, 4)] + +# Create and fit a left-right HMM with random transitions and initial state distribution +hmm = HMM(label='class1', n_states=5, topology='left-right') +hmm.set_random_initial() +hmm.set_random_transitions() +hmm.fit(X) \ No newline at end of file diff --git a/docs/_includes/examples/classifiers/hmm_classifier.py b/docs/_includes/examples/classifiers/hmm_classifier.py new file mode 100644 index 00000000..9a85a7d0 --- /dev/null +++ b/docs/_includes/examples/classifiers/hmm_classifier.py @@ -0,0 +1,21 @@ +import numpy as np +from sequentia.classifiers import HMM, HMMClassifier + +# Create and fit some sample HMMs +hmms = [] +for i in range(5): + hmm = HMM(label=f'class{i}', n_states=(i + 3), topology='left-right') + hmm.set_random_initial() + hmm.set_random_transitions() + hmm.fit([np.arange((i + j * 20) * 30).reshape(-1, 3) for j in range(1, 4)]) + hmms.append(hmm) + +# Create some sample test data and labels +X = [np.random.random((10 * i, 3)) for i in range(1, 4)] +y = ['class0', 'class1', 'class1'] + +# Create a classifier and calculate predictions and evaluations +clf = HMMClassifier() +clf.fit(hmms) +predictions = clf.predict(X) +accuracy, confusion = clf.evaluate(X, y) \ No newline at end of file diff --git a/docs/_includes/examples/preprocessing/downsample.py b/docs/_includes/examples/preprocessing/downsample.py new file mode 100644 index 00000000..87ee97cf --- /dev/null +++ b/docs/_includes/examples/preprocessing/downsample.py @@ -0,0 +1,8 @@ +import numpy as np +from sequentia.preprocessing import downsample + +# Create some sample data +X = [np.random.random((10 * i, 3)) for i in range(1, 4)] + +# Downsample the data with downsample factor 5 and decimation +X = downsample(X, n=5, method='decimate') \ No newline at end of file diff --git a/docs/_includes/examples/preprocessing/fft.py b/docs/_includes/examples/preprocessing/fft.py new file mode 100644 index 00000000..b31b9180 --- /dev/null +++ b/docs/_includes/examples/preprocessing/fft.py @@ -0,0 +1,8 @@ +import numpy as np +from sequentia.preprocessing import fft + +# Create some sample data +X = [np.random.random((10 * i, 3)) for i in range(1, 4)] + +# Transform the data +X = fft(X) \ No newline at end of file diff --git a/docs/_includes/examples/preprocessing/normalize.py b/docs/_includes/examples/preprocessing/normalize.py new file mode 100644 index 00000000..2dd811ba --- /dev/null +++ b/docs/_includes/examples/preprocessing/normalize.py @@ -0,0 +1,8 @@ +import numpy as np +from sequentia.preprocessing import normalize + +# Create some sample data +X = [np.random.random((10 * i, 3)) for i in range(1, 4)] + +# Normalize the data +X = normalize(X) \ No newline at end of file diff --git a/docs/_includes/examples/preprocessing/preprocess.py b/docs/_includes/examples/preprocessing/preprocess.py new file mode 100644 index 00000000..aa4ee39c --- /dev/null +++ b/docs/_includes/examples/preprocessing/preprocess.py @@ -0,0 +1,14 @@ +import numpy as np +from sequentia.preprocessing import Preprocess + +# Create some sample data +X = [np.random.random((10 * i, 3)) for i in range(1, 4)] + +# Create the Preprocess object +pre = Preprocess() +pre.normalize() +pre.downsample(10, method='average') +pre.fft() + +# Transform the data applying transformations in order +X = pre.transform(X) \ No newline at end of file diff --git a/docs/changelog.rst b/docs/changelog.rst new file mode 100644 index 00000000..28d0cbf1 --- /dev/null +++ b/docs/changelog.rst @@ -0,0 +1,6 @@ +.. _changelog: + +Changelog +========= + +.. mdinclude:: ../CHANGELOG.md \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 00000000..356eaef9 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,69 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +import sys +import os +import subprocess + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath('..')) + +subprocess.call('pip install numpydoc sphinx_rtd_theme m2r', shell=True) + +# -- Project information ----------------------------------------------------- + +project = 'sequentia' +copyright = '2019-2020, Edwin Onuonga' +author = 'Edwin Onuonga' + +# The full version, including alpha/beta/rc tags +release = '0.2.0' + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.autosummary', + 'sphinx.ext.mathjax', + 'sphinx.ext.viewcode', + 'numpydoc', + 'm2r' +] + +autodoc_member_order = 'bysource' +autosummary_generate = True +numpydoc_show_class_members = False + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +source_suffix = ['.rst', '.md'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['_build'] + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 00000000..3bbd1b13 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,58 @@ +.. Sequentia documentation master file, created by + sphinx-quickstart on Sat Dec 28 19:22:34 2019. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +.. image:: https://i.ibb.co/42GkhfR/sequentia.png + :alt: Sequentia + :width: 275 + :target: https://github.com/eonu/sequentia + +About +===== + +Sequentia is a collection of machine learning algorithms for performing the classification of isolated temporal sequences. + +Each isolated sequence is generally modeled as a section of a longer multivariate time series +that represents the entire sequence. Naturally, this fits the description of many types of problems such as: + +- isolated word utterance frequencies in speech audio signals, +- isolated hand-written character pen-tip trajectories, +- isolated hand or head gestures positions in a video or motion-capture recording. + +Most modern machine learning algorithms won't work directly out of the box when applied to such +sequential data – mostly due to the fact that the dependencies between observations at different +time frames must be considered, and also because each isolated sequence generally has a different duration. + +Sequentia offers some appropriate classification algorithms for these kinds of tasks. + +.. toctree:: + :maxdepth: 1 + :hidden: + :caption: Sequentia + + self + changelog.rst + +.. toctree:: + :maxdepth: 1 + :caption: Classifiers and Models + + sections/classifiers/hmm.rst + sections/classifiers/dtwknn.rst + +.. toctree:: + :maxdepth: 1 + :caption: Preprocessing Methods + + sections/preprocessing/normalize.rst + sections/preprocessing/downsample.rst + sections/preprocessing/fft.rst + sections/preprocessing/preprocessing.rst + +Documentation Search and Index +============================== + +* :ref:`search` +* :ref:`genindex` +* :ref:`modindex` \ No newline at end of file diff --git a/docs/sections/classifiers/dtwknn.rst b/docs/sections/classifiers/dtwknn.rst new file mode 100644 index 00000000..19a67117 --- /dev/null +++ b/docs/sections/classifiers/dtwknn.rst @@ -0,0 +1,45 @@ +.. _dtwknn: + +Dynamic Time Warping `k`-Nearest Neighbors Classifier (``DTWKNN``) +================================================================== + +| Recall that the isolated sequences we are dealing with are represented as + multivariate time series of different durations. +| Suppose that our sequences are all :math:`D`-dimensional. The main requirement of + `k-Nearest Neighbor `_ + (:math:`k`-NN) classifiers is that each example must have the same number of + dimensions – and hence, be in the same feature space. This is indeed the case with + our :math:`D`-dimensional sequences. However, we can't use :math:`k`-NN with simple + distance metrics such as Euclidean distance because we are comparing sequences + (which represent an ordered collection of points in :math:`D`-dimensional space) + rather than individual points in :math:`D`-dimensional space. + +One distance metric that allows us to compare multivariate sequences of different length +is `Dynamic Time Warping `_. Coupling this metric +with :math:`k`-NN creates a powerful classifier that assigns the class of a new +observation sequence by looking at the classes of observation sequences with similar patterns. + +However, :math:`k`-NN classifiers suffer from the fact that they are non-parametric, +which means that when predicting the class for a new observation sequence, +we must look back at every observation sequence that was used to fit the model. +To speed up prediction times, we have chosen to use a constrained DTW algorithm that +sacrifices accuracy by calculating an approximate distance, but saves **a lot** of time. +This is the `FastDTW `_ +implementation, which has a `radius` parameter for controlling the imposed constraint on the distance calculation. + +This approximate DTW :math:`k`-NN classifier is implemented by the :class:`~DTWKNN` class. + +Example +------- + +.. literalinclude:: ../../_includes/examples/classifiers/dtwknn.py + :language: python + :linenos: + +For more elaborate examples, please have a look at the `example notebooks `_. + +API reference +------------- + +.. autoclass:: sequentia.classifiers.dtwknn.DTWKNN + :members: \ No newline at end of file diff --git a/docs/sections/classifiers/hmm.rst b/docs/sections/classifiers/hmm.rst new file mode 100644 index 00000000..e27173e7 --- /dev/null +++ b/docs/sections/classifiers/hmm.rst @@ -0,0 +1,123 @@ +.. _hmm: + +Hidden Markov Model (``HMM``) +============================= + +The `Hidden Markov Model `_ (HMM) +is a state-based statistical model that can be used to represent an individual +observation sequence class :math:`c`. As seen in the diagram below, the rough idea is that +each state should correspond to one 'section' of the sequence. + +.. image:: https://i.ibb.co/GFtV46t/HMM.jpg + :alt: HMM + :width: 350 + +A single HMM is modeled by the :class:`~HMM` class. + +Parameters and Training +----------------------- + +The 'sections' in the image above are determined by the parameters of the HMM, explained below. + +- | **Initial state distribution** :math:`\boldsymbol{\pi}`: + | A discrete probability distribution that dictates the probability of the HMM starting in each state. + +- | **Transition probability matrix** :math:`A`: + | A matrix whose rows represent a discrete probability distribution that dictates how likely the HMM is + to transition to each state, given some current state. + +- | **Emission probability distributions** :math:`B`: + | A collection of :math:`N` continuous multivariate probability distributions (one for each state) + that each dictate the probability of the HMM generating an observation :math:`\mathbf{o}`, given some current state. + Recall that we are generally considering multivariate observation sequences – that is, + at time :math:`t`, we have an observation :math:`\mathbf{o}^{(t)}=\left(o_1^{(t)}, o_2^{(t)}, \ldots, o_D^{(t)}\right)`. + The fact that the observations are multivariate necessitates a multivariate emission distribution. + Sequentia uses the `multivariate Gaussian distribution `_. + +In order to learn these parameters, we must train the HMM on examples that are labeled +with the class :math:`c` that the HMM models. Denote the HMM that models class :math:`c` as +:math:`\lambda_c=(\boldsymbol{\pi}_c, A_c, B_c)`. We can use the `Baum-Welch algorithm `_ +(an application of the `Expectation-Maximization algorithm `_) +to fit :math:`\lambda_c` and learn its parameters. This fitting is implemented by the :func:`~HMM.fit` function. + +Model Topologies +^^^^^^^^^^^^^^^^ + +As we usually wish to preserve the natural ordering of time, we normally want to prevent our HMM +from transitioning to previous states (this is shown in the figure above). This restriction leads +to what known as a **left-right** HMM, and is the most commonly used type of HMM for sequential +modeling. Mathematically, a left-right HMM is defined by an upper-triangular transition matrix. + +If we allow transitions to any state at any time, this HMM topology is known as **ergodic**. + +**Note**: Ergodicity is mathematically defined as having a transition matrix with no non-zero entries. +Using the ergodic topology in Sequentia will still permit zero entries in the transition matrix, +but will issue a warning stating that those probabilities will not be learned. + +Sequentia offers both topologies, specified by a string parameter ``topology`` in the +:class:`~HMM` constructor that takes values `'left-right'` or `'ergodic'`. + +Making Predictions +------------------ + +A score for how likely a HMM is to generate an observation sequence is given by the +`Forward algorithm `_. It calculates the likelihood +:math:`\mathbb{P}(O|\lambda_c)` of the HMM :math:`\lambda_c` generating the observation sequence :math:`O`. + +**Note**: The likelihood does not account for the fact that a particular observation class +may occur more or less frequently than other observation classes. Once an ensemble of :class:`~HMM` objects +(represented by a :class:`~HMMClassifier`) is created and configured, this can be accounted for by +calculating the joint probability (or un-normalized posterior) +:math:`\mathbb{P}(O, \lambda_c)=\mathbb{P}(O|\lambda_c)\mathbb{P}(\lambda_c)` +and using this score to classify instead. The addition of the prior term :math:`\mathbb{P}(\lambda_c)` +accounts for some classes occuring more frequently than others. + +Example +------- + +.. literalinclude:: ../../_includes/examples/classifiers/hmm.py + :language: python + :linenos: + +For more elaborate examples, please have a look at the +`example notebooks `_. + +API reference +------------- + +.. autoclass:: sequentia.classifiers.hmm.HMM + :members: + +Ensemble Hidden Markov Model Classifier (``HMMClassifier``) +=========================================================== + +Multiple HMMs can be combined to form an ensemble multi-class classifier. +To classify a new observation sequence :math:`O'`, this works by: + +1. | Creating and training the HMMs :math:`\lambda_1, \lambda_2, \ldots, \lambda_N`. + +2. | Calculating the likelihoods :math:`\mathbb{P}(O'|\lambda_1), \mathbb{P}(O'|\lambda_2), \ldots, \mathbb{P}(O'|\lambda_N)` of each model generating :math:`O'`. + | **Note**: You can also used the un-normalized posterior :math:`\mathbb{P}(O'|\lambda_c)\mathbb{P}(\lambda_c)` instead of the likelihood. + +3. | Choose the class represented by the HMM with the highest likelihood – that is, :math:`c^*=\mathop{\arg\max}_{c\in\{1,\ldots,N\}}{\mathbb{P}(O'|\lambda_c)}`. + +These steps are summarized in the diagram below. + +.. image:: https://i.ibb.co/gPymgs4/classifier.png + :alt: Ensemble HMM Classifier System + :width: 400 + +Example +------- + +.. literalinclude:: ../../_includes/examples/classifiers/hmm_classifier.py + :language: python + :linenos: + +For more elaborate examples, please have a look at the `example notebooks `_. + +API reference +------------- + +.. autoclass:: sequentia.classifiers.hmm.HMMClassifier + :members: \ No newline at end of file diff --git a/docs/sections/preprocessing/downsample.rst b/docs/sections/preprocessing/downsample.rst new file mode 100644 index 00000000..03d79e01 --- /dev/null +++ b/docs/sections/preprocessing/downsample.rst @@ -0,0 +1,25 @@ +.. _downsample: + +Downsampling (``downsample``) +============================= + +Downsampling reduces the number of frames in an observation sequence according +to a specified downsample factor and one of two methods: **averaging** and **decimation**. + +This is an especially helpful preprocessing method for speeding up classification times. + +For further information, please see the `preprocessing tutorial notebook `_. + +Example +------- + +.. literalinclude:: ../../_includes/examples/preprocessing/downsample.py + :language: python + :linenos: + +API reference +------------- + +.. automodule:: sequentia.preprocessing + :noindex: +.. autofunction:: downsample \ No newline at end of file diff --git a/docs/sections/preprocessing/fft.rst b/docs/sections/preprocessing/fft.rst new file mode 100644 index 00000000..65656348 --- /dev/null +++ b/docs/sections/preprocessing/fft.rst @@ -0,0 +1,26 @@ +.. _fft: + +Discrete Fourier Transform (``fft``) +==================================== + +The Discrete Fourier Transform (DFT) converts the observation sequence into a real-valued, +same-length sequence of equally-spaced samples of the +`discrete-time Fourier transform `_. + +The popular `Fast Fourier Transform `_ (FFT) implementation is used to efficiently compute the DFT. + +For further information, please see the `preprocessing tutorial notebook `_. + +Example +------- + +.. literalinclude:: ../../_includes/examples/preprocessing/fft.py + :language: python + :linenos: + +API reference +------------- + +.. automodule:: sequentia.preprocessing + :noindex: +.. autofunction:: fft \ No newline at end of file diff --git a/docs/sections/preprocessing/normalize.rst b/docs/sections/preprocessing/normalize.rst new file mode 100644 index 00000000..923f6781 --- /dev/null +++ b/docs/sections/preprocessing/normalize.rst @@ -0,0 +1,38 @@ +.. _normalize: + +Normalization (``normalize``) +============================= + +Normalizing centers an observation sequence about the mean of its observations – that is, given: + +.. math:: + + O=\begin{pmatrix} + o_1^{(1)} & o_2^{(1)} & \cdots & o_D^{(1)} \\ + o_1^{(2)} & o_2^{(2)} & \cdots & o_D^{(2)} \\ + \vdots & \vdots & \ddots & \vdots \\ + o_1^{(T)} & o_2^{(T)} & \cdots & o_D^{(T)} + \end{pmatrix} + \qquad + \boldsymbol{\mu}=\begin{pmatrix} + \overline{o_1} & \overline{o_2} & \cdots & \overline{o_D} + \end{pmatrix} + +Where :math:`\overline{o_d}` represents the mean of the :math:`d^\text{th}` feature of :math:`O`. + +We subtract :math:`\boldsymbol{\mu}` from each observation, or row in :math:`O`. This centers the observations. + +For further information, please see the `preprocessing tutorial notebook `_. + +Example +------- + +.. literalinclude:: ../../_includes/examples/preprocessing/normalize.py + :language: python + :linenos: + +API reference +------------- + +.. automodule:: sequentia.preprocessing +.. autofunction:: normalize \ No newline at end of file diff --git a/docs/sections/preprocessing/preprocessing.rst b/docs/sections/preprocessing/preprocessing.rst new file mode 100644 index 00000000..893f898e --- /dev/null +++ b/docs/sections/preprocessing/preprocessing.rst @@ -0,0 +1,22 @@ +.. _combined: + +Combined Preprocessing (``Preprocess``) +======================================= + +The :class:`~Preprocess` class provides a way of efficiently applying multiple +preprocessing transformations to provided input observation sequences. + +For further information, please see the `preprocessing tutorial notebook `_. + +Example +------- + +.. literalinclude:: ../../_includes/examples/preprocessing/preprocess.py + :language: python + :linenos: + +API reference +------------- + +.. autoclass:: sequentia.preprocessing.Preprocess + :members: \ No newline at end of file diff --git a/examples/1 - Input Format (Tutorial).ipynb b/examples/1 - Input Format (Tutorial).ipynb deleted file mode 100644 index 8d151e13..00000000 --- a/examples/1 - Input Format (Tutorial).ipynb +++ /dev/null @@ -1,32 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/2 - Preprocessing (Tutorial).ipynb b/examples/2 - Preprocessing (Tutorial).ipynb deleted file mode 100644 index 8d151e13..00000000 --- a/examples/2 - Preprocessing (Tutorial).ipynb +++ /dev/null @@ -1,32 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/Pen-Tip Trajectories (Example).ipynb b/examples/Pen-Tip Trajectories (Example).ipynb deleted file mode 100644 index b660941d..00000000 --- a/examples/Pen-Tip Trajectories (Example).ipynb +++ /dev/null @@ -1,654 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import requests\n", - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "import pandas as pd\n", - "import seaborn as sns\n", - "from tqdm.auto import tqdm\n", - "from scipy.io import loadmat\n", - "from sklearn.model_selection import train_test_split\n", - "\n", - "# Set seed for reproducible randomness\n", - "seed = 101\n", - "np.random.seed(seed)\n", - "rng = np.random.RandomState(seed)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Pen-Tip Trajectories (Example)\n", - "\n", - "This notebook aims to showcase some of the preprocessing methods and classification algorithms offered by Sequentia on the [Character Trajectories Data Set](https://archive.ics.uci.edu/ml/datasets/Character+Trajectories). This dataset consists of pen-tip trajectories generated by writing English letters on a [WACOM tablet](https://www.wacom.com/en-us). \n", - "\n", - "The **$x$-velocity**, **$y$-velocity** and **pen-tip force** were recorded.\n", - "\n", - "Some more specific details about the dataset:\n", - "\n", - "- The data consists of 2858 character samples\n", - "- The data has been numerically differentiated and Gaussian smoothed\n", - "- Only characters with a single 'PEN-DOWN' segment were considered (these characters are shown later)\n", - "- Characters have been shifted so that their velocity profiles best match the mean of the set\n", - "\n", - "Each character sample is a 3-dimensional pen tip velocity (and force) trajectory. This is contained in matrix format, with 3 rows and $T$ columns where $T$ is the length of the character sample, which represents our observation sequence.\n", - "\n", - "---\n", - "\n", - "First, we will download the dataset and extract the samples and labels, then convert them so that they are in a format compatible with Sequentia (see the [_Input Format_](https://nbviewer.jupyter.org/github/eonu/sequentia/blob/master/examples/1%20-%20Input%20Format%20(Tutorial).ipynb) notebook for more information): " - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "data = None\n", - "url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/character-trajectories/mixoutALL_shifted.mat'\n", - "\n", - "try:\n", - " path = os.path.join(os.getcwd(), 'temp.mat')\n", - " response = requests.get(url)\n", - "except:\n", - " raise\n", - "else:\n", - " with open(path, 'wb') as file:\n", - " file.write(response.content)\n", - " data = loadmat(path)\n", - "finally:\n", - " os.remove(path)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Number of trajectories: 2858\n" - ] - } - ], - "source": [ - "# Load the trajectories\n", - "X = [x.T for x in data['mixout'][0]] # Transpose from 3xT to Tx3\n", - "print('Number of trajectories: {}'.format(len(X)))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Only lowercase characters with a single pen-down segment were considered in this dataset. In total, there were 20 of these characters as shown below:" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Labels: ['a', 'b', 'c', 'd', 'e', 'g', 'h', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 'u', 'v', 'w', 'y', 'z']\n", - "Number of labels: 20\n" - ] - } - ], - "source": [ - "# Retrieve the set of unique labels and report the number of labels\n", - "labels = [label[0] for label in data['consts'][0][0][3][0]]\n", - "n_labels = len(labels)\n", - "print('Labels: {}'.format(str(labels)))\n", - "print('Number of labels: {}'.format(n_labels))" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "# View distribution of observation sequence lengths\n", - "plt.title('Histogram of observation sequence lengths')\n", - "plt.xlabel('Number of time frames')\n", - "plt.ylabel('Count')\n", - "plt.hist([len(x) for x in X], bins=n_labels)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The sample rate of each trajectory recording was 200hz–meaning that in every second, 200 pen-tip trajectories were recorded!\n", - "\n", - "As seen in the histogram above, most characters can be drawn in less than 200 frames, or in less than one second.\n", - "\n", - "Although keeping all of these frames/data-points might result in a more accurate classifier, it also significantly increases the time required for training or prediction. This is especially the case for $k$-NN, since it is a non-parametric classifier that requires going through each training example during prediction time.\n", - "\n", - "---\n", - "\n", - "There are three features offered by Sequentia that can help to reduce the time taken for predictions:\n", - "\n", - "- Downsampling (representing each trajectory in a fewer number of frames) through two different methods:\n", - " - **Decimation**: Only keeping the observation at every every $n$th time frame.\n", - " - **Averaging**: Averaging every group of $n$ observations to form a single observation.\n", - "- Using a faster, restricted distance measure that can handle sequences of different length (see [FastDTW](https://pdfs.semanticscholar.org/05a2/0cde15e172fc82f32774dd0cf4fe5827cad2.pdf))\n", - "- Parallelization (only supported in the `DTWKNN` class)\n", - "\n", - "The `DTWKNN` class always uses the FastDTW algorithm to calculate distances. Downsampling is offered as one of the preprocessing methods in Sequentia, and is used as follows (see the [_Preprocessing_](https://nbviewer.jupyter.org/github/eonu/sequentia/blob/master/examples/2%20-%20Preprocessing%20(Tutorial).ipynb) notebook for more information):" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "from sequentia.preprocessing import downsample\n", - "\n", - "# Pick an example trajectory for visualization\n", - "x = X[0]\n", - "# Downsample the example trajectory, using a downsample factor of n=10\n", - "x_down = downsample(x, n=10, method='average')\n", - "\n", - "# Create the plot to visualize the downsampling\n", - "fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5), sharey=True)\n", - "ax1.plot(x)\n", - "ax1.set_title('Original velocity and force pen-tip trajectory sample')\n", - "ax1.legend(labels=['$x$ velocity', '$y$ velocity', 'pen-tip force'])\n", - "ax2.plot(x_down)\n", - "ax2.set_title('Downsampled ($n=10$) velocity and force pen-tip trajectory sample')\n", - "ax2.legend(labels=['$x$ velocity', '$y$ velocity', 'pen-tip force'])\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "# Downsample the entire dataset\n", - "X = downsample(X, n=10, method='average')" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "# Extract the labels\n", - "y = [labels[idx - 1] for idx in data['consts'][0][0][4][0]]\n", - "\n", - "# Plot a histogram of the labels for each class\n", - "plt.title('Histogram of the dataset label counts')\n", - "plt.xlabel('Label (character)')\n", - "plt.ylabel('Count')\n", - "plt.hist(y, bins=n_labels)\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Training set size: 2286\n", - "Test set size: 572\n" - ] - } - ], - "source": [ - "# Shuffle and split the dataset into a training and test set\n", - "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=rng, shuffle=True)\n", - "print('Training set size: {}'.format(len(X_train)))\n", - "print('Test set size: {}'.format(len(X_test)))" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "# Create a function for displaying results (accuracy and confusion matrix)\n", - "def show_results(acc, cm, dataset):\n", - " df = pd.DataFrame(cm, index=labels, columns=labels)\n", - " plt.figure(figsize=(10, 7))\n", - " sns.heatmap(df, annot=True)\n", - " plt.title('Confusion matrix for {} set predictions'.format(dataset), fontsize=14)\n", - " plt.xlabel('Predicted')\n", - " plt.ylabel('Actual')\n", - " # Fix for matplotlib bug that cuts off top/bottom of seaborn visualizations\n", - " b, t = plt.ylim()\n", - " plt.ylim(b + 0.5, t - 0.5)\n", - " plt.show()\n", - " print('Accuracy: {:.2f}%'.format(acc * 100))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Dynamic Time Warping $k$-NN\n", - "\n", - "The $k$-Nearest Neighbor ($k$-NN) classifier is a conceptually simple machine learning algorithm that is also easy to implement. As a result, it is often used as a baseline, despite often being able to perform much better than more complex algorithms.\n", - "\n", - "However, applying $k$-NN to isolated temporal observation sequences is not so straightforward since different observation sequences may have different durations, making it difficult to come up with a distance measure that can be used to compare the two sequences. \n", - "\n", - "One such appropriate distance measure is [Dynamic Time Warping](https://en.wikipedia.org/wiki/Dynamic_time_warping). However, due to the non-parametric nature of $k$-NN, it may take very long to predict new observation sequences. In an effort to reduce this wait, Sequentia uses the [FastDTW](https://github.com/slaypni/fastdtw) implementation of the Dynamic Time Warping algorithm, which allows for faster, configurable approximatations to the DTW distance calculations which can save memory and time. \n", - "\n", - "---\n", - "\n", - "Importing, creating and fitting the classifier:" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "from sequentia.classifiers import DTWKNN\n", - "\n", - "# Create and fit a DTWKNN classifier using the single nearest neighbor and a radius of 1\n", - "# NOTE: The radius parameter is a parameter that constrains the FastDTW algorithm.\n", - "clf = DTWKNN(k=1, radius=1)\n", - "clf.fit(X_train, y_train)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To predict single or multiple examples, we can use the `predict` function:" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "c7cd63f3f6db45f8bd9558575733161a", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "HBox(children=(IntProgress(value=0, description='Calculating distances', max=2286, style=ProgressStyle(descrip…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - }, - { - "data": { - "text/plain": [ - "'r'" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Predict the first test example\n", - "clf.predict(X_test[0])" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "331e1528ac3b40e0aea36cd70cec7dbc", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "HBox(children=(IntProgress(value=0, description='Classifying examples', max=5, style=ProgressStyle(description…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "CPU times: user 56.1 s, sys: 759 ms, total: 56.8 s\n", - "Wall time: 58.7 s\n" - ] - }, - { - "data": { - "text/plain": [ - "['r', 'w', 'l', 'w', 'y']" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "%%time\n", - "# Predict the first 5 test examples\n", - "clf.predict(X_test[:5])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This can be sped up a bit by using multiple jobs, as specified by `n_jobs`. By default this is set to 1. A setting of -1 will use all available cores:" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 561 ms, sys: 81.6 ms, total: 642 ms\n", - "Wall time: 41.4 s\n" - ] - }, - { - "data": { - "text/plain": [ - "['r', 'w', 'l', 'w', 'y']" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "%%time\n", - "# NOTE: Progress bars for predict() and evaluate() are only displayed in the console if multiple jobs are used\n", - "clf.predict(X_test[:5], n_jobs=-1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To calculate the model's accuracy and confusion matrix on some data, we can use the `evaluate` function:" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "CPU times: user 616 ms, sys: 183 ms, total: 799 ms\n", - "Wall time: 1h 3min 34s\n" - ] - } - ], - "source": [ - "%%time\n", - "acc, cm = clf.evaluate(X_test, y_test, labels=labels, n_jobs=-1)" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Accuracy: 98.25%\n" - ] - } - ], - "source": [ - "show_results(acc, cm, dataset='test')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As you can see, Dynamic Time Warping $k$-NN classification often works with near perfect performance, but suffers due to the fact that $k$-NN is a non-parametric machine learning algorithm. \n", - "\n", - "This means that we have to look through every training example when we make a single prediction. Even with FastDTW, downsampling and multi-processing, the example classification on the test set consisting of 572 examples **took just over an hour**!\n", - "\n", - "---\n", - "\n", - "As a result, parametric methods such as Hidden Markov Models are often more feasible to use–but also generally perform worse." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Ensemble Hidden Markov Models\n", - "\n", - "An ensemble of HMMs can be a good classifier for isolated sequences. The main idea behind using ensemble HMMs for classification is as follows:\n", - "\n", - "1. Create $N$ HMMs $\\lambda_1,\\lambda_2,\\ldots,\\lambda_N$, each representing a different class (character in this case).\n", - "2. Fit each of these HMMs only using the training examples labeled with the class that the HMM represents. _The Baum-Welch algorithm is used for training here_.\n", - "3. For a new observation sequence $O$, calculate the likelihood of each HMM generating $O$–that is, calculate $\\mathbb{P}(O|\\lambda_c) \\quad \\forall c\\in\\{1, 2, \\ldots, N\\}$. _This is done using the Forward algorithm_.\n", - "4. Then $O$ is then classified as the class corresponding to the HMM that was most likely to generate $O$, giving a classification rule of: \n", - "\n", - "$$c^*=\\mathop{\\arg\\max}_{c\\in\\{1,2,\\ldots,N\\}}\\mathbb{P}(O|\\lambda_c)$$\n", - "\n", - "**Note**: In order to account for some classes naturally occurring more frequently than others, we can instead introduce a prior by using the Maximum A Posterior (MAP) classification rule:\n", - "\n", - "$$c^*=\\mathop{\\arg\\max}_{c\\in\\{1,2,\\ldots,N\\}}\\mathbb{P}(O|\\lambda_c)\\mathbb{P}(\\lambda_c)$$\n", - "\n", - "---\n", - "\n", - "Creating the individual `HMM` objects and fitting each one on the training examples corresponding to the label (character) that it represents:\n", - "\n", - "**Note**: Here we naively set the number of states for all HMMs to 10. In reality, you will probably want to have different numbers of states for HMMs that represent more complex or more simple characters." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "c03a0b387cfd4389befc6a3d0a039d03", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "HBox(children=(IntProgress(value=0, description='Training HMMs', max=20, style=ProgressStyle(description_width…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - } - ], - "source": [ - "from sequentia.classifiers import HMM, HMMClassifier\n", - "\n", - "hmms = []\n", - "for label in tqdm(labels, desc='Training HMMs'):\n", - " hmm = HMM(label=label, n_states=10, random_state=rng)\n", - " hmm.set_random_initial()\n", - " hmm.set_random_transitions()\n", - " hmm.fit([X_train[i] for i, y_i in enumerate(y_train) if y_i == label])\n", - " hmms.append(hmm)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A `HMMClassifier` object collects each of the individual `HMM` objects in order to create the ensemble classifier:" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [], - "source": [ - "clf = HMMClassifier()\n", - "clf.fit(hmms)" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Accuracy: 88.46%\n" - ] - } - ], - "source": [ - "acc, cm = clf.evaluate(X_test, y_test, labels=labels)\n", - "show_results(acc, cm, dataset='test')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/lib/sequentia/classifiers/dtwknn/dtwknn.py b/lib/sequentia/classifiers/dtwknn/dtwknn.py index 07e73099..1bab5380 100644 --- a/lib/sequentia/classifiers/dtwknn/dtwknn.py +++ b/lib/sequentia/classifiers/dtwknn/dtwknn.py @@ -8,37 +8,26 @@ from collections import Counter from scipy.spatial.distance import euclidean from sklearn.metrics import confusion_matrix -from typing import Callable, Union, List, Tuple from ...internals import Validator class DTWKNN: - """A k-Nearest Neighbor classifier that compares differing length observation sequences - using the efficient FastDTW dynamic time warping algorithm. - - Example: - >>> import numpy as np - >>> from sequentia.classifiers import DTWKNN - >>> ​ - >>> # Create some sample data - >>> X = [np.random.random((10 * i, 3)) for i in range(1, 4)] - >>> y = ['class0', 'class1', 'class1'] - >>> ​ - >>> # Create and fit the classifier - >>> clf = DTWKNN(k=1, radius=5) - >>> clf.fit(X, y) - >>> ​ - >>> # Predict labels for the training data (just as an example) - >>> clf.predict(X) + """A k-Nearest Neighbor classifier that compares differing length observation sequences using the efficient FastDTW dynamic time warping algorithm. + + Parameters + ---------- + k: int + Number of neighbors. + + radius: int + Radius parameter for FastDTW. + + See: `Stan Salvador, and Philip Chan. "FastDTW: Toward accurate dynamic time warping in linear time and space." Intelligent Data Analysis 11.5 (2007), 561-580. `_ + + metric: callable + Distance metric for FastDTW. """ - def __init__(self, k: int, radius: int = 10, metric: Callable = euclidean): - """ - Parameters: - k {int} - Number of neighbors. - radius {int} - Radius parameter for FastDTW. - See: https://pdfs.semanticscholar.org/05a2/0cde15e172fc82f32774dd0cf4fe5827cad2.pdf - metric {Callable} - Distance metric for FastDTW. - """ + def __init__(self, k, radius, metric: euclidean): self._val = Validator() self._k = self._val.restricted_integer( k, lambda x: x > 0, desc='number of neighbors', expected='greater than zero') @@ -46,26 +35,38 @@ def __init__(self, k: int, radius: int = 10, metric: Callable = euclidean): radius, lambda x: x > 0, desc='radius parameter', expected='greater than zero') self._metric = metric - def fit(self, X: List[np.ndarray], y: List[str]) -> None: + def fit(self, X, y): """Fits the classifier by adding labeled training observation sequences. - Parameters: - X {list(numpy.ndarray)} - A list of multiple observation sequences. - y {list(str)} - A list of labels for the observation sequences. + Parameters + ---------- + X: List[numpy.ndarray] + A list of multiple observation sequences. + + y: List[str] + A list of labels for the observation sequences. """ self._X, self._y = self._val.observation_sequences_and_labels(X, y) - def predict(self, X: Union[np.ndarray, List[np.ndarray]], verbose=True, n_jobs=1) -> Union[str, List[str]]: + def predict(self, X, verbose=True, n_jobs=1): """Predicts the label for an observation sequence (or multiple sequences). - Parameters: - X {numpy.ndarray, list(numpy.ndarray)} - An individual observation sequence or - a list of multiple observation sequences. - verbose {bool} - Whether to display a progress bar or not. - n_jobs {int} - The number of jobs to run in parallel. + Parameters + ---------- + X: numpy.ndarray or List[numpy.ndarray] + An individual observation sequence or a list of multiple observation sequences. - Returns {numpy.ndarray, list(numpy.ndarray)}: - The predicted labels for the observation sequence(s). + verbose: bool + Whether to display a progress bar or not. + + n_jobs: int + | The number of jobs to run in parallel. + | Setting this to -1 will use all available CPU cores. + + Returns + ------- + prediction(s): str or List[str] + The predicted label(s) for the observation sequence(s). """ try: (self._X, self._y) @@ -115,19 +116,34 @@ def parallel_predict(process, X_chunk): labels = Parallel(n_jobs=n_jobs)(delayed(parallel_predict)(i+1, chunk) for i, chunk in enumerate(X_chunks)) return [label for sublist in labels for label in sublist] # Flatten the resulting array - def evaluate(self, X: List[np.ndarray], y: List[str], labels=None, verbose=True, n_jobs=1) -> Tuple[float, np.ndarray]: + def evaluate(self, X, y, labels=None, verbose=True, n_jobs=1): """Evaluates the performance of the classifier on a batch of observation sequences and their labels. - Parameters: - X {list(numpy.ndarray)} - A list of multiple observation sequences. - y {list(str)} - A list of labels for the observation sequences. - labels {list(str)} - A list of labels for ordering the axes of the confusion matrix. - verbose {bool} - Whether to display a progress bar for predictions or not. - n_jobs {int} - The number of jobs to run in parallel. + Parameters + ---------- + X: List[numpy.ndarray] + A list of multiple observation sequences. + + y: List[str] + A list of labels for the observation sequences. + + labels: List[str] + A list of labels for ordering the axes of the confusion matrix. + + verbose: bool + Whether to display a progress bar for predictions or not. + + n_jobs: int + | The number of jobs to run in parallel. + | Setting this to -1 will use all available CPU cores. + + Returns + ------- + accuracy: float + The categorical accuracy of the classifier on the observation sequences. - Return: {tuple(float, numpy.ndarray)} - - The categorical accuracy of the classifier on the observation sequences. - - A confusion matrix representing the discrepancy between predicted and actual labels. + confusion: numpy.ndarray + The confusion matrix representing the discrepancy between predicted and actual labels. """ self._val.observation_sequences_and_labels(X, y) self._val.boolean(verbose, desc='verbose') diff --git a/lib/sequentia/classifiers/hmm/hmm.py b/lib/sequentia/classifiers/hmm/hmm.py index 7f7d196b..274559c4 100644 --- a/lib/sequentia/classifiers/hmm/hmm.py +++ b/lib/sequentia/classifiers/hmm/hmm.py @@ -2,41 +2,44 @@ import pomegranate as pg from .topologies.ergodic import ErgodicTopology from .topologies.left_right import LeftRightTopology -from typing import List from ...internals import Validator class HMM: """A hidden Markov model representing an isolated temporal sequence class. - Example: - >>> import numpy as np - >>> from sequentia.classifiers import HMM - >>> ​ - >>> # Create some sample data - >>> X = [np.random.random((10 * i, 3)) for i in range(1, 4)] - >>> ​ - >>> # Create and fit a left-right HMM with random transitions and initial state distribution - >>> hmm1 = HMM(label='class1', n_states=5, topology='left-right') - >>> hmm1.set_random_initial() - >>> hmm1.set_random_transitions() - >>> hmm1.fit(X) - - Attributes: - label (getter) - The label for the model. - n_states (getter) - The number of states for the model. - n_seqs (getter) - The number of observation sequences use to train the model. - initial (setter/getter) - The initial state distribution of the model. - transitions (setter/getter) - The transition matrix of the model. + Parameters + ---------- + label: str + A label for the model, corresponding to the class being represented. + + n_states: int + The number of states for the model. + + topology: {'ergodic', 'left-right'} + The topology for the model. + + random_state: numpy.random.RandomState, int, optional + A random state object or seed for reproducible randomness. + + Attributes + ---------- + label: str + The label for the model. + + n_states: int + The number of states for the model. + + n_seqs: int + The number of observation sequences use to train the model. + + initial: numpy.ndarray + The initial state distribution of the model. + + transitions: numpy.ndarray + The transition matrix of the model. """ - def __init__(self, label: str, n_states: int, topology='left-right', random_state=None): - """ - Parameters: - label {str} - A label for the model (should ideally correspond to the class label). - n_states {int} - The number of states for the model. - topology {str} - The topology ('ergodic' or 'left-right') for the model. - random_state {numpy.random.RandomState, int} - A random state object or seed for reproducible randomness. - """ + def __init__(self, label, n_states, topology='left-right', random_state=None): self._val = Validator() self._label = self._val.string(label, 'model label') self._n_states = self._val.restricted_integer( @@ -57,65 +60,34 @@ def __init__(self, label: str, n_states: int, topology='left-right', random_stat elif topology == 'left-right': self._topology = LeftRightTopology(self._n_states, self._random_state) - @property - def label(self) -> str: - return self._label - - @property - def n_states(self) -> int: - return self._n_states - - @property - def n_seqs(self) -> int: - """Number of observation sequences used to train the model.""" - try: - return self._n_seqs - except AttributeError as e: - raise AttributeError('The model has not been fitted and has not seen any observation sequences') from e - - @property - def initial(self) -> np.ndarray: - try: - return self._initial - except AttributeError as e: - raise AttributeError('No initial state distribution has been defined') from e - - @initial.setter - def initial(self, probabilities: np.ndarray): - self._topology.validate_initial(probabilities) - self._initial = probabilities - - @property - def transitions(self) -> np.ndarray: - try: - return self._transitions - except AttributeError as e: - raise AttributeError('No transition matrix has been defined') from e - - @transitions.setter - def transitions(self, probabilities: np.ndarray): - self._topology.validate_transitions(probabilities) - self._transitions = probabilities - def set_uniform_initial(self): + """Sets a uniform initial state distribution.""" self._initial = self._topology.uniform_initial() def set_random_initial(self): + """Sets a random initial state distribution.""" self._initial = self._topology.random_initial() def set_uniform_transitions(self): + """Sets a uniform transition matrix according to the topology.""" self._transitions = self._topology.uniform_transitions() def set_random_transitions(self): + """Sets a random transition matrix according to the topology.""" self._transitions = self._topology.random_transitions() - def fit(self, X: List[np.ndarray], n_jobs=1): + def fit(self, X, n_jobs=1): """Fits the HMM to observation sequences assumed to be labeled as the class that the model represents. - Parameters: - X {list(numpy.ndarray)} - Collection of multivariate observation sequences, each of shape (T, D) - where T may vary per observation sequence. - n_jobs {int} - The number of jobs to run in parallel. + Parameters + ---------- + X: List[numpy.ndarray] + Collection of multivariate observation sequences, each of shape :math:`(T \\times D)` where + :math:`T` may vary per observation sequence. + + n_jobs: int + | The number of jobs to run in parallel. + | Setting this to -1 will use all available CPU cores. """ self._val.observation_sequences(X) self._val.restricted_integer(n_jobs, lambda x: x == -1 or x > 0, 'number of jobs', '-1 or greater than zero') @@ -147,14 +119,19 @@ def fit(self, X: List[np.ndarray], n_jobs=1): self._initial = inner_tx[self._n_states] self._transitions = inner_tx[:self._n_states] - def forward(self, sequence: np.ndarray) -> float: + def forward(self, sequence): """Runs the forward algorithm to calculate the (negative log) likelihood of the model generating an observation sequence. - Parameters: - sequence {numpy.ndarray} - An individual sequence of observations of size (T, D) where: - T is the number of time frames (or observations) and D is the number of features. + Parameters + ---------- + sequence: numpy.ndarray + An individual sequence of observations of size :math:`(T \\times D)` where + :math:`T` is the number of time frames (or observations) and + :math:`D` is the number of features. - Returns {float}: + Returns + ------- + negative log-likelihood: float The negative log-likelihood of the model generating the observation sequence. """ if not isinstance(sequence, np.ndarray): @@ -164,4 +141,43 @@ def forward(self, sequence: np.ndarray) -> float: if not sequence.shape[1] == self._n_features: raise ValueError('Number of observation features must match the dimensionality of the original data used to fit the model') - return -self._model.log_probability(sequence) \ No newline at end of file + return -self._model.log_probability(sequence) + + @property + def label(self): + return self._label + + @property + def n_states(self): + return self._n_states + + @property + def n_seqs(self): + try: + return self._n_seqs + except AttributeError as e: + raise AttributeError('The model has not been fitted and has not seen any observation sequences') from e + + @property + def initial(self): + try: + return self._initial + except AttributeError as e: + raise AttributeError('No initial state distribution has been defined') from e + + @initial.setter + def initial(self, probabilities): + self._topology.validate_initial(probabilities) + self._initial = probabilities + + @property + def transitions(self): + try: + return self._transitions + except AttributeError as e: + raise AttributeError('No transition matrix has been defined') from e + + @transitions.setter + def transitions(self, probabilities): + self._topology.validate_transitions(probabilities) + self._transitions = probabilities \ No newline at end of file diff --git a/lib/sequentia/classifiers/hmm/hmm_classifier.py b/lib/sequentia/classifiers/hmm/hmm_classifier.py index a7b47fb9..101b5701 100644 --- a/lib/sequentia/classifiers/hmm/hmm_classifier.py +++ b/lib/sequentia/classifiers/hmm/hmm_classifier.py @@ -1,43 +1,21 @@ import numpy as np from .hmm import HMM from sklearn.metrics import confusion_matrix -from typing import Dict, Union, List, Tuple, Any from ...internals import Validator class HMMClassifier: - """An ensemble classifier that combines individual HMMs which model isolated sequences from different classes. - - Example: - >>> import numpy as np - >>> from sequentia.classifiers import HMM, HMMClassifier - >>> ​ - >>> # Create and fit some sample HMMs - >>> hmms = [] - >>> for i in range(5): - >>> hmm = HMM(label=f'class{i}', n_states=(i + 3), topology='left-right') - >>> hmm.set_random_initial() - >>> hmm.set_random_transitions() - >>> hmm.fit([np.arange((i + j * 20) * 30).reshape(-1, 3) for j in range(1, 4)]) - >>> hmms.append(hmm) - >>> ​ - >>> # Create some sample test data and labels - >>> X = [np.random.random((10 * i, 3)) for i in range(1, 4)] - >>> y = ['class0', 'class1', 'class1'] - >>> ​ - >>> # Create a classifier and calculate predictions and evaluations - >>> clf = HMMClassifier() - >>> clf.fit(hmms) - >>> predictions = clf.predict(X) - >>> f1, confusion = clf.evaluate(X, y) - """ + """An ensemble classifier that combines individual :class:`~HMM` objects, which model isolated sequences from different classes.""" def __init__(self): self._val = Validator() - def fit(self, models: Union[List[HMM], Dict[Any, HMM]]): - """ - Parameters: - models {list(HMM),dict(HMM)} - A collection of HMM objects to use for classification. + def fit(self, models): + """Fits the ensemble classifier with a collection of :class:`~HMM` objects. + + Parameters + ---------- + models: List[HMM] or Dict[Any, HMM] + A collection of :class:`~HMM` objects to use for classification. """ if isinstance(models, list): if not all(isinstance(model, HMM) for model in models): @@ -55,18 +33,31 @@ def fit(self, models: Union[List[HMM], Dict[Any, HMM]]): else: raise RuntimeError('Must fit the classifier with at least one HMM') - def predict(self, X: Union[np.ndarray, List[np.ndarray]], prior=True, return_scores=False) -> Union[str, List[str]]: + def predict(self, X, prior=True, return_scores=False): """Predicts the label for an observation sequence (or multiple sequences) according to maximum likelihood or posterior scores. - Parameters: - X {numpy.ndarray, list(numpy.ndarray)} - An individual observation sequence or - a list of multiple observation sequences. - prior {bool} - Whether to calculate a prior and perform MAP estimation. If this parameter is set - to False, then the negative log likelihoods generated from the models' `forward` function are used. - return_scores {bool} - Whether to return the scores of each model on the observation sequence(s). + Parameters + ---------- + X: numpy.ndarray or List[numpy.ndarray] + An individual observation sequence or a list of multiple observation sequences. + + prior: bool + Whether to calculate a prior for each model and perform MAP estimation by scoring with + the joint probability (or un-normalized posterior) :math:`\mathbb{P}(O, \lambda_c)=\mathbb{P}(O|\lambda_c)\mathbb{P}(\lambda_c)`. + + If this parameter is set to false, then the negative log likelihoods + :math:`\mathbb{P}(O|\lambda_c)` generated from the models' :func:`~HMM.forward` function are used. + + return_scores: bool + Whether to return the scores of each model on the observation sequence(s). + + Returns + ------- + prediction(s): str or List[str] + The predicted label(s) for the observation sequence(s). - Returns {str, list(str)}: - The predicted labels for the observation sequence(s). + If ``return_scores`` is true, then for each observation sequence, a tuple `(label, scores)` is returned for each label, + consisting of the `scores` of each HMM and the `label` of the HMM with the best score. """ self._val.boolean(prior, desc='prior') self._val.boolean(return_scores, desc='return_scores') @@ -91,19 +82,34 @@ def predict(self, X: Union[np.ndarray, List[np.ndarray]], prior=True, return_sco predictions.append((best[0], scores) if return_scores else best[0]) return predictions - def evaluate(self, X: List[np.ndarray], y: List[str], prior=True, labels=None) -> Tuple[float, np.ndarray]: + def evaluate(self, X, y, prior=True, labels=None): """Evaluates the performance of the classifier on a batch of observation sequences and their labels. - Parameters: - X {list(numpy.ndarray)} - A list of multiple observation sequences. - y {list(str)} - A list of labels for the observation sequences. - prior {bool} - Whether to calculate a prior and perform MAP estimation. If this parameter is set - to False, then the negative log likelihoods generated from the models' `forward` function are used. - labels {list(str)} - A list of labels for ordering the axes of the confusion matrix. + Parameters + ---------- + X: List[numpy.ndarray] + A list of multiple observation sequences. + + y: List[str] + A list of labels for the observation sequences. + + prior: bool + Whether to calculate a prior for each model and perform MAP estimation by scoring with + the joint probability (or un-normalized posterior) :math:`\mathbb{P}(O, \lambda_c)=\mathbb{P}(O|\lambda_c)\mathbb{P}(\lambda_c)`. + + If this parameter is set to false, then the negative log likelihoods + :math:`\mathbb{P}(O|\lambda_c)` generated from the models' :func:`~HMM.forward` function are used. + + labels: List[str] + A list of labels for ordering the axes of the confusion matrix. + + Returns + ------- + accuracy: float + The categorical accuracy of the classifier on the observation sequences. - Return: {tuple(float, numpy.ndarray)} - - The categorical accuracy of the classifier on the observation sequences. - - A confusion matrix representing the discrepancy between predicted and actual labels. + confusion: numpy.ndarray + The confusion matrix representing the discrepancy between predicted and actual labels. """ self._val.observation_sequences_and_labels(X, y) self._val.boolean(prior, desc='prior') diff --git a/lib/sequentia/preprocessing/methods.py b/lib/sequentia/preprocessing/methods.py index 9c7e17ea..d5d11794 100644 --- a/lib/sequentia/preprocessing/methods.py +++ b/lib/sequentia/preprocessing/methods.py @@ -1,23 +1,25 @@ import scipy.fftpack import numpy as np -from typing import Union, List from ..internals import Validator -def normalize(X: Union[np.ndarray, List[np.ndarray]]) -> Union[np.ndarray, List[np.ndarray]]: +def normalize(X): """Normalizes an observation sequence (or multiple sequences) by centering observations around the mean. - Parameters: - X {numpy.ndarray, list(numpy.ndarray)} - An individual observation sequence or - a list of multiple observation sequences. + Parameters + ---------- + X: numpy.ndarray or List[numpy.ndarray] + An individual observation sequence or a list of multiple observation sequences. - Returns {numpy.ndarray, list(numpy.ndarray)}: + Returns + ------- + normalized: numpy.ndarray or List[numpy.ndarray] The normalized input observation sequence(s). """ val = Validator() val.observation_sequences(X, allow_single=True) return _normalize(X) -def _normalize(X: Union[np.ndarray, List[np.ndarray]]) -> Union[np.ndarray, List[np.ndarray]]: +def _normalize(X): def transform(x): return x - x.mean(axis=0) @@ -26,20 +28,25 @@ def transform(x): elif isinstance(X, np.ndarray): return transform(X) -def downsample(X: Union[np.ndarray, List[np.ndarray]], n: int, method='decimate') -> Union[np.ndarray, List[np.ndarray]]: - """Downsamples an observation sequence (or multiple sequences) by: - - Decimating the next n-1 observations - - Averaging the current observation with the next n-1 observations +def downsample(X, n, method='decimate'): + """Downsamples an observation sequence (or multiple sequences) by either: + - Decimating the next :math:`n-1` observations + - Averaging the current observation with the next :math:`n-1` observations - Parameters: - X {numpy.ndarray, list(numpy.ndarray)} - An individual observation sequence or - a list of multiple observation sequences. - n {int} - Downsample factor. - NOTE: This downsamples the current observation by either decimating the next n-1 - observations or computing an average with them. - method {str} - The downsampling method, either 'decimate' or 'average'. + Parameters + ---------- + X: numpy.ndarray or List[numpy.ndarray] + An individual observation sequence or a list of multiple observation sequences. - Returns {numpy.ndarray, list(numpy.ndarray)}: + n: int + Downsample factor. + + method: {'decimate', 'average'} + The downsampling method. + + Returns + ------- + downsampled: numpy.ndarray or List[numpy.ndarray] The downsampled input observation sequence(s). """ val = Validator() @@ -48,7 +55,7 @@ def downsample(X: Union[np.ndarray, List[np.ndarray]], n: int, method='decimate' val.one_of(method, ['decimate', 'average'], desc='downsampling method') return _downsample(X, n, method) -def _downsample(X: Union[np.ndarray, List[np.ndarray]], n: int, method: str) -> Union[np.ndarray, List[np.ndarray]]: +def _downsample(X, n, method): def transform(x): N, D = x.shape if method == 'decimate': @@ -63,17 +70,24 @@ def transform(x): elif isinstance(X, np.ndarray): return transform(X) -def fft(X: Union[np.ndarray, List[np.ndarray]]) -> Union[np.ndarray, List[np.ndarray]]: +def fft(X): """Applies a Discrete Fourier Transform to the input observation sequence(s). - Returns {numpy.ndarray, list(numpy.ndarray)}: + Parameters + ---------- + X: numpy.ndarray or List[numpy.ndarray] + An individual observation sequence or a list of multiple observation sequences. + + Returns + ------- + transformed: numpy.ndarray or List[numpy.ndarray] The transformed input observation sequence(s). """ val = Validator() val.observation_sequences(X, allow_single=True) return _fft(X) -def _fft(X: Union[np.ndarray, List[np.ndarray]]) -> Union[np.ndarray, List[np.ndarray]]: +def _fft(X): def transform(x): return scipy.fftpack.rfft(x, axis=0) diff --git a/lib/sequentia/preprocessing/preprocess.py b/lib/sequentia/preprocessing/preprocess.py index c0eb24da..819fd0df 100644 --- a/lib/sequentia/preprocessing/preprocess.py +++ b/lib/sequentia/preprocessing/preprocess.py @@ -1,61 +1,50 @@ import numpy as np -from typing import Union, List from .methods import _normalize, _downsample, _fft from ..internals import Validator class Preprocess: - """Efficiently applies multiple preprocessing transformations to provided input observation sequences. - - Example: - >>> import numpy as np - >>> from sequentia.preprocessing import Preprocess - >>> ​ - >>> # Create some sample data - >>> X = [np.random.random((10 * i, 3)) for i in range(1, 4)] - >>> ​ - >>> # Create the Preprocess object - >>> pre = Preprocess() - >>> pre.normalize() - >>> pre.downsample(10, method='average') - >>> pre.fft() - >>> ​ - >>> # Transform the data applying transformations in order - >>> X = pre.transform(X) - """ + """Efficiently applies multiple preprocessing transformations to provided input observation sequences.""" def __init__(self): self._transforms = [] self._val = Validator() - def normalize(self) -> None: + def normalize(self): """Normalizes an observation sequence (or multiple sequences) by centering observations around the mean.""" self._transforms.append((_normalize, {})) - def downsample(self, n: int, method='decimate') -> None: - """Downsamples an observation sequence (or multiple sequences) by: - - Decimating the next n-1 observations - - Averaging the current observation with the next n-1 observations + def downsample(self, n, method='decimate'): + """Downsamples an observation sequence (or multiple sequences) by either: + - Decimating the next :math:`n-1` observations + - Averaging the current observation with the next :math:`n-1` observations + + Parameters + ---------- + n: int + Downsample factor. - Parameters: - n {int} - Downsample factor. This downsamples the current observation - by either decimating the next n-1 observations or computing an average with them. - method {str} - The downsamplimg method, either 'decimate' or 'average'. + method: {'decimate', 'average'} + The downsampling method. """ self._val.restricted_integer(n, lambda x: x > 1, desc='downsample factor', expected='greater than one') self._val.one_of(method, ['decimate', 'average'], desc='downsampling method') self._transforms.append((_downsample, {'n': n, 'method': method})) - def fft(self) -> None: + def fft(self): """Applies a Discrete Fourier Transform to the input observation sequence(s).""" self._transforms.append((_fft, {})) - def transform(self, X: List[np.ndarray]) -> List[np.ndarray]: + def transform(self, X): """Applies the preprocessing transformations to the provided input observation sequence(s). - Parameters: - X {list(numpy.ndarray)} - A list of multiple observation sequences. + Parameters + ---------- + X: List[numpy.ndarray] + A list of multiple observation sequences. - Returns {list(numpy.ndarray)}: + Returns + ------- + transformed: List[numpy.ndarray] The input observation sequences with preprocessing transformations applied in order. """ self._val.observation_sequences(X)