'''Super class for the feature mapping functions.'''
import numpy as np
from sklearn.utils import check_array, check_X_y
from sklearn.utils.validation import check_is_fitted
[docs]class BasePhi():
'''
Base class for feature mappings
The class provides a base for different feature mapping functions
that can be used with the MRC.
This class provides definition for some utility functions that are
used by the MRCs in the library. It corresponds
to the usual identity feature map referred to as Linear feature map.
To see an example of how to extend the class `BasePhi` to implement
yout own feature mapping see :ref:`this example <ex_phi>`.
.. note:: This is a base class for all the feature mappings.
To create a new feature mapping that can be used with MRC objects,
the user can extend this class and then implement the functions -
1) `fit` - learns the required parameters for feature
transformation
2) `transform` - transforms the input instances to the features
The above functions are principal components
for different feature transformation.
Apart from these functions, the users can also re-define other
functions
in this class according to their need.
The definition of `fit` and `transform` in this class correspond to the
usual identity feature map referred to as **Linear feature map**.
The `transform` function is only used by the `eval_xy` function
of this class to get the one-hot encoded features.
If the user defines his own `eval_xy` function
that returns the features directly without the need
of `transform` function, then the `transform` function can be omitted.
.. seealso:: For more information about MRC, one can refer to the following
resources:
[1] `Mazuelas, S., Zanoni, A., & Pérez, A. (2020).
Minimax Classification with 0-1 Loss and Performance
Guarantees. Advances in Neural Information Processing
Systems, 33, 302-312. <https://arxiv.org/abs/2010.07964>`_
[2] `Mazuelas, S., Shen, Y., & Pérez, A. (2020).
Generalized Maximum Entropy for Supervised Classification.
arXiv preprint arXiv:2007.05447.
<https://arxiv.org/abs/2007.05447>`_
[3] `Bondugula, K., Mazuelas, S., & Pérez, A. (2021).
MRCpy: A Library for Minimax Risk Classifiers.
arXiv preprint arXiv:2108.01952.
<https://arxiv.org/abs/2108.01952>`_
Parameters
----------
n_classes : `int`
Number of classes in the dataset
fit_intercept : `bool`, default = `True`
Whether to calculate the intercept.
If set to false, no intercept will be used in calculations
(i.e. data is expected to be already centered)
one_hot : `bool`, default = `False`
Controls the method used for evaluating the features of the
given instances in the binary case.
Only applies in the **binary case**, namely, only when there are two
classes. When set to true, one-hot-encoding will be used. If set to
false a more efficient shorcut will be performed.
Attributes
----------
is_fitted_ : `bool`
Whether the feature mapping has learned its hyperparameters (if any)
and the length of the feature mapping is set.
len_ : `int`
Length of the feature mapping vector.
'''
[docs] def __init__(self, n_classes, fit_intercept=True, one_hot=False):
self.n_classes = n_classes
self.fit_intercept = fit_intercept
self.one_hot = one_hot
[docs] def fit(self, X, Y=None):
'''
Performs training stage.
Learns the required hyperparameters
for the feature mapping transformation
from the training instances
and set the length of the feature mapping (one-hot encoded)
obtained from the `eval_xy` function.
.. note:: If a user implements `fit` function in his own feature
mapping, then it is recommended to call this `fit` function
at the end of his own function to automatically set
the length of the feature mapping.
This `fit` function can be called in a subclass as follows -
`super().fit(X,Y)`
Feature mappings implemented in this library follow this
same approach.
Parameters
----------
X : `array`-like of shape (`n_samples`, `n_dimensions`)
Unlabeled training instances
used to learn the feature configurations
Y : `array`-like of shape (`n_samples`)
Labels corresponding to the unlabeled instances.
Returns
-------
self :
Fitted estimator
'''
X = check_array(X, accept_sparse=True)
self.is_fitted_ = True
# Defining the length of the phi
self.len_ = self.eval_xy(X[0:1, :], np.asarray([0])).shape[1]
return self
[docs] def eval_xy(self, X, Y):
'''
Evaluates the one-hot encoded features of the given instances i.e.,
X, :math:`\phi(x,y)`, x
:math:`\in` X and y :math:`\in` Y.
The encodings are calculated,
corresponding to the given labels, which is used by the learning stage
for estimating the expectation of :math:`\phi(x,y)`.
Parameters
----------
X : `array`-like of shape (`n_samples`, `n_dimensions`)
Unlabeled training instances for developing the feature matrix
Y : `array`-like of shape (`n_samples`)
Labels corresponding to the unlabeled training instances
Returns
-------
phi : `array`-like of shape
(`n_samples`, `n_features` * `n_classes`)
Matrix containing the one-hot encoding
with respect to the labels given for all the instances.
'''
# Get the features
X_feat = self.transform(X)
X_feat, Y = check_X_y(X_feat, Y, accept_sparse=True)
n = X_feat.shape[0]
# Adding intercept
if self.fit_intercept:
X_feat = np.hstack(([[1]] * n, X_feat))
# Efficient configuration in case of binary classification.
if self.n_classes == 2 and not self.one_hot:
X_feat[Y == 1, :] = X_feat[Y == 1, :] * -1
phi = X_feat
# One-hot encoding for multi-class classification.
else:
phi = np.zeros((n, self.n_classes * X_feat.shape[1]))
tweaked_eye_mat = (np.eye(self.n_classes))[Y, :]
for i in range(n):
phi[i, :] = np.kron(tweaked_eye_mat[i], X_feat[i, :])
return phi
[docs] def eval_x(self, X):
'''
Evaluates the one-hot encoded features of the given instances i.e.,
X, :math:`\phi(x,y)`, x
:math:`\in` X and all the labels. The output is 3D matrix
that is composed of 2D matrices corresponding to each of the instance.
These 2D matrices are the one-hot encodings of the instances' features
corresponding to all the possible labels in the data.
Parameters
----------
X : `array`-like of shape (`n_samples`, `n_dimensions`)
Unlabeled training instances for developing the feature matrix.
Returns
-------
phi : `array`-like of shape
(`n_samples`, `n_classes`, `n_features` * `n_classes`)
Matrix containing the one-hot encoding for all the classes
for each of the instances given.
'''
n = X.shape[0]
# Compute the one-hot encodings
phi = self.eval_xy(np.repeat(X, self.n_classes, axis=0),
np.tile(np.arange(self.n_classes), n))
m = phi.shape[1]
# Reshape to 3D matrix for easy multiplication in the MRC optimization
phi = np.reshape(phi, (n, self.n_classes, m))
return phi
[docs] def est_exp(self, X, Y):
'''
Average value of :math:`\phi(x,y)` in the supervised dataset (X,Y).
Used in the learning stage to estimate
the expectation of :math:`\phi(x,y)`, denoted by :math:`{\\tau}`
Parameters
----------
X : `array`-like of shape (`n_samples`, `n_dimensions`)
Unlabeled training instances.
Y : `array`-like of shape (`n_samples`,)
Labels corresponding to the unlabeled training instances
Returns
-------
tau_ : `array`-like of shape (`n_features` * `n_classes`)
Average value of `phi`
'''
X, Y = check_X_y(X, Y, accept_sparse=True)
return np.average(self.eval_xy(X, Y), axis=0)
[docs] def est_std(self, X, Y):
'''
Standard deviation of :math:`\phi(x,y)`
in the supervised dataset (X,Y).
Used in the learning stage
to estimate the variance in the expectation of
:math:`\phi(x,y)`, denoted by :math:`\lambda`
Parameters
----------
X : `array`-like of shape (`n_samples`, `n_dimensions`)
Unlabeled training instances.
Y : `array`-like of shape (`n_samples`,)
Labels corresponding to the unlabeled training instances
Returns
-------
lambda_ : `array`-like of shape (`n_features` * `n_classes`)
Standard deviation of `phi`
'''
X, Y = check_X_y(X, Y, accept_sparse=True)
return np.std(self.eval_xy(X, Y), axis=0)