Source code for pyoselm.elm

"""Module to build Extreme Learning Machine (ELM) models"""

# ===================================================
# Acknowledgement:
# Author: David C. Lambert [dcl -at- panix -dot- com]
# Copyright(c) 2013
# License: Simple BSD
# ===================================================

from abc import ABCMeta, abstractmethod

import numpy as np
from scipy.special import softmax
from sklearn.base import BaseEstimator, ClassifierMixin, RegressorMixin
from sklearn.linear_model import LinearRegression
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import LabelBinarizer
from sklearn.utils import as_float_array

from pyoselm.layer import RandomLayer, MLPRandomLayer


__all__ = [
    "ELMRegressor",
    "ELMClassifier",
    "GenELMRegressor",
    "GenELMClassifier",
]


class BaseELM(BaseEstimator):
    """Abstract Base class for ELMs"""
    __metaclass__ = ABCMeta

    def __init__(self, hidden_layer, regressor):
        self.regressor = regressor
        self.hidden_layer = hidden_layer

    @abstractmethod
    def fit(self, X, y):
        """
        Fit the model using X, y as training data.

        Parameters
        ----------
        X : {array-like, sparse matrix} of shape [n_samples, n_features]
            Training vectors, where n_samples is the number of samples
            and n_features is the number of features.

        y : array-like of shape [n_samples, n_outputs]
            Target values (class labels in classification, real numbers in
            regression)

        Returns
        -------
        self : object
            Returns an instance of self.
        """

    @abstractmethod
    def predict(self, X):
        """
        Predict values using the model

        Parameters
        ----------
        X : {array-like, sparse matrix} of shape [n_samples, n_features]

        Returns
        -------
        C : numpy array of shape [n_samples, n_outputs]
            Predicted values.
        """


[docs]class GenELMRegressor(BaseELM, RegressorMixin): """ Regression model based on Extreme Learning Machine. Parameters ---------- `hidden_layer` : random_layer instance, optional (default=MLPRandomLayer(random_state=0)) `regressor` : regressor instance, optional (default=sklearn.linear_model.LinearRegression()) Attributes ---------- `coefs_` : numpy array Fitted regression coefficients if no regressor supplied. `fitted_` : bool Flag set when fit has been called already. `hidden_activations_` : numpy array of shape [n_samples, n_hidden] Hidden layer activations for last input. See Also -------- ELMRegressor, MLPRandomLayer """
[docs] def __init__(self, hidden_layer=None, regressor=None): if hidden_layer is None: # Default value hidden_layer = MLPRandomLayer(random_state=0) elif not isinstance(hidden_layer, RandomLayer): raise ValueError("Argument 'hidden_layer' must be a RandomLayer instance") if regressor is None: # Default value regressor = LinearRegression() elif not isinstance(regressor, RegressorMixin): raise ValueError("Argument 'regressor' must be a RegressorMixin instance") super(GenELMRegressor, self).__init__(hidden_layer, regressor) self.coefs_ = None self.fitted_ = False self.hidden_activations_ = None
def _fit_regression(self, y): """Fit regression with the supplied regressor""" self.regressor.fit(self.hidden_activations_, y) self.fitted_ = True @property def is_fitted(self): """Check if model was fitted Returns ------- boolean, True if model is fitted """ return self.fitted_
[docs] def fit(self, X, y): """ Fit the model using X, y as training data. Parameters ---------- X : {array-like, sparse matrix} of shape [n_samples, n_features] Training vectors, where n_samples is the number of samples and n_features is the number of features. y : array-like of shape [n_samples, n_outputs] Target values (class labels in classification, real numbers in regression) Returns ------- self : object Returns an instance of self. """ # fit random hidden layer and compute the hidden layer activations self.hidden_activations_ = self.hidden_layer.fit_transform(X) # solve the regression from hidden activations to outputs self._fit_regression(as_float_array(y, copy=True)) return self
[docs] def predict(self, X): """ Predict values using the model Parameters ---------- X : {array-like, sparse matrix} of shape [n_samples, n_features] Returns ------- C : numpy array of shape [n_samples, n_outputs] Predicted values. """ if not self.is_fitted: raise ValueError("GenELMRegressor not fitted") # compute hidden layer activations self.hidden_activations_ = self.hidden_layer.transform(X) # compute output predictions for new hidden activations predictions = self.regressor.predict(self.hidden_activations_) return predictions
[docs]class GenELMClassifier(BaseELM, ClassifierMixin): """ Classification model based on Extreme Learning Machine. Internally, it uses a GenELMRegressor. Parameters ---------- `hidden_layer` : random_layer instance, optional (default=MLPRandomLayer(random_state=0)) `binarizer` : LabelBinarizer, optional (default=sklearn.preprocessing.LabelBinarizer(-1, 1)) `regressor` : regressor instance, optional (default=LinearRegression()) Used to perform the regression from hidden unit activations to the outputs and subsequent predictions. Attributes ---------- `classes_` : numpy array of shape [n_classes] Array of class labels `genelm_regressor_` : ELMRegressor instance Performs actual fit of binarized values See Also -------- GenELMRegressor, ELMClassifier, MLPRandomLayer """
[docs] def __init__(self, hidden_layer=None, binarizer=None, regressor=None): # Default values if hidden_layer is None: hidden_layer = MLPRandomLayer(random_state=0) if binarizer is None: binarizer = LabelBinarizer(neg_label=-1, pos_label=1) super(GenELMClassifier, self).__init__(hidden_layer, regressor) self.binarizer = binarizer self.classes_ = None self._genelm_regressor = GenELMRegressor(hidden_layer, regressor)
[docs] def fit(self, X, y): """ Fit the model using X, y as training data. Parameters ---------- X : {array-like, sparse matrix} of shape [n_samples, n_features] Training vectors, where n_samples is the number of samples and n_features is the number of features. y : array-like of shape [n_samples, n_outputs] Target values (class labels in classification, real numbers in regression) Returns ------- self : object Returns an instance of self. """ self.classes_ = np.unique(y) y_bin = self.binarizer.fit_transform(y) self._genelm_regressor.fit(X, y_bin) return self
@property def is_fitted(self): """Check if model was fitted Returns ------- boolean, True if model is fitted """ return self._genelm_regressor is not None and self._genelm_regressor.is_fitted
[docs] def decision_function(self, X): """ This function return the decision function values related to each class on an array of test vectors X. Parameters ---------- X : array-like of shape [n_samples, n_features] Returns ------- C : array of shape [n_samples, n_classes] or [n_samples,] Decision function values related to each class, per sample. In the two-class case, the shape is [n_samples,] """ if not self.is_fitted: raise ValueError("GenELMClassifier not fitted") return self._genelm_regressor.predict(X)
[docs] def predict(self, X): """Predict values using the model Parameters ---------- X : {array-like, sparse matrix} of shape [n_samples, n_features] Returns ------- C : numpy array of shape [n_samples, n_outputs] Predicted values. """ if not self.is_fitted: raise ValueError("GenELMClassifier not fitted") raw_predictions = self.decision_function(X) class_predictions = self.binarizer.inverse_transform(raw_predictions) return class_predictions
[docs]class ELMRegressor(BaseEstimator, RegressorMixin): """ Regression model based on Extreme Learning Machine. An Extreme Learning Machine (ELM) is a single layer feedforward network with a random hidden layer components and ordinary linear least squares fitting of the hidden->output weights by default. [1][2] ELMRegressor is a wrapper for an GenELMRegressor that creates a RandomLayer based on the given parameters. Parameters ---------- `n_hidden` : int, optional (default=20) Number of units to generate in the SimpleRandomLayer `alpha` : float, optional (default=0.5) Mixing coefficient for distance and dot product input activations: activation = alpha*mlp_activation + (1-alpha)*rbf_width*rbf_activation `rbf_width` : float, optional (default=1.0) multiplier on rbf_activation `activation_func` : {callable, string} optional (default='sigmoid') Function used to transform input activation It must be one of 'tanh', 'sine', 'tribas', 'inv_tribase', 'sigmoid', 'hardlim', 'softlim', 'gaussian', 'multiquadric', 'inv_multiquadric' or a callable. If none is given, 'tanh' will be used. If a callable is given, it will be used to compute the hidden unit activations. `activation_args` : dictionary, optional (default=None) Supplies keyword arguments for a callable activation_func `user_components`: dictionary, optional (default=None) dictionary containing values for components that woud otherwise be randomly generated. Valid key/value pairs are as follows: 'radii' : array-like of shape [n_hidden] 'centers': array-like of shape [n_hidden, n_features] 'biases' : array-like of shape [n_hidden] 'weights': array-like of shape [n_hidden, n_features] `regressor` : regressor instance, optional (default=sklearn.linear_model.LinearRegression()) Used to perform the regression from hidden unit activations to the outputs and subsequent predictions. `random_state` : int, RandomState instance or None (default=None) Control the pseudo random number generator used to generate the hidden unit weights at fit time. Attributes ---------- `genelm_regressor_` : GenELMRegressor object Wrapped object that actually performs the fit. Examples -------- >>> from pyoselm import ELMRegressor >>> from sklearn.datasets import make_regression >>> X, y = make_regression(n_samples=100, n_targets=1, n_features=10) >>> model = ELMRegressor(n_hidden=20, ... activation_func="tanh", ... random_state=123) >>> model.fit(X, y) ELMRegressor(random_state=123) >>> model.score(X, y) 0.8600650083210614 See Also -------- GenELMRegressor, RandomLayer, MLPRandomLayer, References ---------- .. [1] http://www.extreme-learning-machines.org .. [2] G.-B. Huang, Q.-Y. Zhu and C.-K. Siew, "Extreme Learning Machine: Theory and Applications", Neurocomputing, vol. 70, pp. 489-501, 2006. """
[docs] def __init__(self, n_hidden=20, alpha=0.5, rbf_width=1.0, activation_func='sigmoid', activation_args=None, user_components=None, regressor=None, random_state=None,): self.n_hidden = n_hidden self.alpha = alpha self.random_state = random_state self.activation_func = activation_func self.activation_args = activation_args self.user_components = user_components self.rbf_width = rbf_width self.regressor = regressor # Just to validate input arguments self._create_random_layer() self._genelm_regressor = None
def _create_random_layer(self): """Pass init params to RandomLayer""" return RandomLayer(n_hidden=self.n_hidden, alpha=self.alpha, random_state=self.random_state, activation_func=self.activation_func, activation_args=self.activation_args, user_components=self.user_components, rbf_width=self.rbf_width)
[docs] def fit(self, X, y): """ Fit the model using X, y as training data. Parameters ---------- X : {array-like, sparse matrix} of shape [n_samples, n_features] Training vectors, where n_samples is the number of samples and n_features is the number of features. y : array-like of shape [n_samples, n_outputs] Target values (class labels in classification, real numbers in regression) Returns ------- self : object Returns an instance of self. """ rhl = self._create_random_layer() self._genelm_regressor = GenELMRegressor(hidden_layer=rhl, regressor=self.regressor) self._genelm_regressor.fit(X, y) return self
@property def is_fitted(self): """Check if model was fitted Returns ------- boolean, True if model is fitted """ return self._genelm_regressor is not None and self._genelm_regressor.is_fitted
[docs] def predict(self, X): """ Predict values using the model Parameters ---------- X : {array-like, sparse matrix} of shape [n_samples, n_features] Returns ------- C : numpy array of shape [n_samples, n_outputs] Predicted values. """ if not self.is_fitted: raise ValueError("ELMRegressor is not fitted") return self._genelm_regressor.predict(X)
# TODO: inherit from BaseELMClassifier
[docs]class ELMClassifier(ELMRegressor): """ Classification model based on Extreme Learning Machine. An Extreme Learning Machine (ELM) is a single layer feedforward network with a random hidden layer components and ordinary linear least squares fitting of the hidden->output weights by default. [1][2] ELMClassifier is an ELMRegressor subclass that first binarizes the data, then uses the superclass to compute the decision function that is then unbinarized to yield the prediction. The params for the RandomLayer used in the input transform are exposed in the ELMClassifier constructor. Parameters ---------- `n_hidden` : int, optional (default=20) Number of units to generate in the SimpleRandomLayer `activation_func` : {callable, string} optional (default='sigmoid') Function used to transform input activation It must be one of 'tanh', 'sine', 'tribas', 'inv_tribase', 'sigmoid', 'hardlim', 'softlim', 'gaussian', 'multiquadric', 'inv_multiquadric' or a callable. If a callable is given, it will be used to compute the hidden unit activations. `activation_args` : dictionary, optional (default=None) Supplies keyword arguments for a callable activation_func `random_state` : int, RandomState instance or None (default=None) Control the pseudo random number generator used to generate the hidden unit weights at fit time. Attributes ---------- `classes_` : numpy array of shape [n_classes] Array of class labels Examples -------- >>> from pyoselm import ELMClassifier >>> from sklearn.datasets import load_digits >>> X, y = load_digits(n_class=10, return_X_y=True) >>> model = ELMClassifier(n_hidden=50, ... activation_func="sigmoid", ... random_state=123) >>> model.fit(X, y) ELMClassifier(activation_func='sigmoid', n_hidden=50, random_state=123) >>> model.score(X, y) 0.8241513633834168 See Also -------- RandomLayer, RBFRandomLayer, MLPRandomLayer, GenELMRegressor, GenELMClassifier, ELMClassifier References ---------- .. [1] http://www.extreme-learning-machines.org .. [2] G.-B. Huang, Q.-Y. Zhu and C.-K. Siew, "Extreme Learning Machine: Theory and Applications", Neurocomputing, vol. 70, pp. 489-501, 2006. """
[docs] def __init__(self, n_hidden=20, alpha=0.5, rbf_width=1.0, activation_func='sigmoid', activation_args=None, user_components=None, regressor=None, binarizer=LabelBinarizer(neg_label=-1, pos_label=1), random_state=None,): super(ELMClassifier, self).__init__(n_hidden=n_hidden, alpha=alpha, random_state=random_state, activation_func=activation_func, activation_args=activation_args, user_components=user_components, rbf_width=rbf_width, regressor=regressor) self.classes_ = None self.binarizer = binarizer
[docs] def fit(self, X, y): """ Fit the model using X, y as training data. Parameters ---------- X : {array-like, sparse matrix} of shape [n_samples, n_features] Training vectors, where n_samples is the number of samples and n_features is the number of features. y : array-like of shape [n_samples, n_outputs] Target values (class labels in classification, real numbers in regression) Returns ------- self : object Returns an instance of self. """ self.classes_ = np.unique(y) y_bin = self.binarizer.fit_transform(y) super(ELMClassifier, self).fit(X, y_bin) return self
[docs] def decision_function(self, X): """ This function return the decision function values related to each class on an array of test vectors X. Parameters ---------- X : array-like of shape [n_samples, n_features] Returns ------- C : array of shape [n_samples, n_classes] or [n_samples,] Decision function values related to each class, per sample. In the two-class case, the shape is [n_samples,] """ if not self.is_fitted: raise ValueError("ELMClassifier is not fitted") return super(ELMClassifier, self).predict(X)
[docs] def predict(self, X): """ Predict values using the model Parameters ---------- X : {array-like, sparse matrix} of shape [n_samples, n_features] Returns ------- C : numpy array of shape [n_samples, n_outputs] Predicted values. """ if not self.is_fitted: raise ValueError("ELMClassifier is not fitted") raw_predictions = self.decision_function(X) class_predictions = self.binarizer.inverse_transform(raw_predictions) return class_predictions
[docs] def predict_proba(self, X): """ Predict probability values using the model Parameters ---------- X : {array-like, sparse matrix} of shape [n_samples, n_features] Returns ------- P : numpy array of shape [n_samples, n_outputs] Predicted probability values. """ if not self.is_fitted: raise ValueError("ELMClassifier not fitted") raw_predictions = self.decision_function(X) # using softmax to translate raw predictions into probability values proba_predictions = softmax(raw_predictions) return proba_predictions
[docs] def score(self, X, y, **kwargs): """Force use of accuracy score since it doesn't inherit from ClassifierMixin""" return accuracy_score(y, self.predict(X))