Source code for secml.ml.classifiers.c_classifier_ridge

"""
.. module:: CClassifierRidge
   :synopsis: Ridge classifier

.. moduleauthor:: Ambra Demontis <ambra.demontis@unica.it>
.. moduleauthor:: Marco Melis <marco.melis@unica.it>

"""
from sklearn.linear_model import RidgeClassifier

from secml.ml.classifiers import CClassifierLinear
from secml.array import CArray
from secml.ml.kernel import CKernel
from secml.ml.classifiers.gradients import CClassifierGradientRidgeMixin
from secml.ml.classifiers.loss import CLossSquare
from secml.ml.classifiers.regularizer import CRegularizerL2
from secml.utils.mixed_utils import check_is_fitted


[docs]class CClassifierRidge(CClassifierLinear, CClassifierGradientRidgeMixin): """Ridge Classifier. Parameters ---------- preprocess : CPreProcess or str or None, optional Features preprocess to be applied to input data. Can be a CPreProcess subclass or a string with the type of the desired preprocessor. If None, input data is used as is. Attributes ---------- class_type : 'ridge' """ __class_type = 'ridge' _loss = CLossSquare() _reg = CRegularizerL2() def __init__(self, alpha=1.0, kernel=None, max_iter=1e5, class_weight=None, tol=1e-4, fit_intercept=True, preprocess=None): # Calling the superclass init CClassifierLinear.__init__(self, preprocess=preprocess) # Classifier parameters self.alpha = alpha self.max_iter = max_iter self.tol = tol self.class_weight = class_weight self.fit_intercept = fit_intercept # Similarity function (bound) to use for computing features # Keep private (not a param of RIDGE) self._kernel = kernel if kernel is None else CKernel.create(kernel) self._tr = None # slot for the training data
[docs] def is_linear(self): """Return True if the classifier is linear.""" if super(CClassifierRidge, self).is_linear() and self.is_kernel_linear(): return True return False
[docs] def is_kernel_linear(self): """Return True if the kernel is None or linear.""" if self.kernel is None or self.kernel.class_type == 'linear': return True return False
def _check_is_fitted(self): """Check if the classifier is trained (fitted). Raises ------ NotFittedError If the classifier is not fitted. """ if self._kernel is not None: check_is_fitted(self, '_tr') super(CClassifierRidge, self)._check_is_fitted() @property def kernel(self): """Kernel function.""" return self._kernel @kernel.setter def kernel(self, kernel): """Setting up the Kernel function (None if a linear classifier).""" self._kernel = kernel @property def alpha(self): """Returns the Constant that multiplies the regularization term.""" return self._alpha @alpha.setter def alpha(self, value): """Sets the Constant that multiplies the regularization term.""" self._alpha = float(value) @property def C(self): """Constant that multiplies the regularization term. Equal to 1 / alpha. """ return 1.0 / self.alpha @property def class_weight(self): """Weight of each training class.""" return self._class_weight @class_weight.setter def class_weight(self, value): """Sets the weight of each training class.""" if isinstance(value, dict) and len(value) != 2: raise ValueError("weight of positive (+1) and negative (0) " "classes only must be specified.") self._class_weight = value @property def n_tr_samples(self): """Returns the number of training samples.""" return self._tr.shape[0] if self._tr is not None else None def _fit(self, dataset): """Trains the One-Vs-All Ridge classifier. The following is a private method computing one single binary (2-classes) classifier of the OVA schema. Representation of each classifier attribute for the multiclass case is explained in corresponding property description. Parameters ---------- dataset : CDataset Binary (2-classes) training set. Must be a :class:`.CDataset` instance with patterns data and corresponding labels. Returns ------- trained_cls : classifier Instance of the used solver trained using input dataset. """ if dataset.num_classes != 2: raise ValueError("training can be performed on binary " "(2-classes) datasets only.") # Setting up classifier parameters ridge = RidgeClassifier(alpha=self.alpha, fit_intercept=self.fit_intercept, tol=self.tol, max_iter=self.max_iter, class_weight=self.class_weight, solver='auto') # Storing training dataset (only if required by kernel) self._tr = dataset.X if self._kernel is not None else None # Storing the training matrix for kernel mapping if self.is_kernel_linear(): # Training classifier ridge.fit(dataset.X.get_data(), dataset.Y.tondarray()) else: # Training SGD classifier with kernel mapping ridge.fit(CArray( self.kernel.k(dataset.X)).get_data(), dataset.Y.tondarray()) # Updating global classifier parameters self._w = CArray(ridge.coef_, tosparse=dataset.issparse).ravel() self._b = CArray(ridge.intercept_)[0] if self.fit_intercept else 0 # TODO: this function can be removed when removing kernel support def _decision_function(self, x, y=None): """Computes the distance from the separating hyperplane for each pattern in x. The scores are computed in kernel space if kernel is defined. Parameters ---------- x : CArray Array with new patterns to classify, 2-Dimensional of shape (n_patterns, n_features). y : {0, 1, None} The label of the class wrt the function should be calculated. If None, return the output for all classes. Returns ------- score : CArray Value of the decision function for each test pattern. Dense flat array of shape (n_samples,) if `y` is not None, otherwise a (n_samples, n_classes) array. """ # Compute decision function in kernel space if necessary k = x if self.is_kernel_linear() else \ CArray(self.kernel.k(x, self._tr)) # Scores are given by the linear model return CClassifierLinear._decision_function(self, k, y=y)