#!/usr/bin/env python
# Created by "Thieu" at 22:19, 10/04/2025 ----------%
# Email: nguyenthieu2102@gmail.com %
# Github: https://github.com/thieu1995 %
# --------------------------------------------------%
import numpy as np
import torch
from sklearn.metrics import accuracy_score, r2_score
from sklearn.base import ClassifierMixin, RegressorMixin
from permetrics import ClassificationMetric, RegressionMetric
from xanfis.models.base_anfis import BaseBioAnfis
[docs]class BioAnfisClassifier(BaseBioAnfis, ClassifierMixin):
"""
A Bio-based ANFIS Classifier that extends the BaseBioAnfis class and implements
the ClassifierMixin interface from Scikit-Learn for classification tasks.
This class integrates bio-inspired optimization algorithms for training an Adaptive
Neuro-Fuzzy Inference System (ANFIS) model, supporting both binary and multi-class
classification tasks.
Attributes
----------
classes_ : np.ndarray
Unique classes found in the target variable.
metric_class : type
The metric class used for evaluating classification performance.
Parameters
----------
num_rules : int, optional
Number of fuzzy rules (default is 10).
mf_class : str, optional
Membership function class (default is "Gaussian").
vanishing_strategy : str or None, optional
Strategy for calculating rule strengths (default is 'prod').
act_output : any, optional
Activation function for the output layer (default is None).
reg_lambda : float or None, optional
Regularization parameter (default is None). This is used for regularizing the model.
optim : str, optional
The optimization algorithm to use (default is "BaseGA").
optim_params : dict, optional
Parameters for the optimizer (default is None).
obj_name : str, optional
The objective name for the optimization (default is "F1S").
seed : int, optional
Random seed for reproducibility (default is 42).
verbose : bool, optional
Whether to print detailed logs during fitting (default is True).
lb : int, float, list, tuple, np.ndarray, optional.
Lower bounds for optimization (default is (-1.0,)).
ub : int, float, list, tuple, np.ndarray, optional.
Upper bounds for optimization (default is (1.0,)).
mode : str, optional
Mode for optimization (default is 'single').
n_workers : int, optional
Number of workers for parallel processing (default is None).
termination : any, optional
Termination criteria for optimization (default is None).
Methods
-------
fit(X, y, lb=(-1.0,), ub=(1.0,), mode='single', n_workers=None, termination=None, save_population=False, **kwargs):
Fits the model to the training data using the specified bio-inspired optimizer.
predict(X):
Predicts the class labels for the provided input data.
score(X, y):
Computes the accuracy score of the model based on predictions.
predict_proba(X):
Computes the probability estimates for each class (for classification tasks only).
evaluate(y_true, y_pred, list_metrics=("AS", "RS")):
Returns the list of performance metrics on the given test data and labels.
"""
def __init__(self, num_rules=10, mf_class="Gaussian", vanishing_strategy="prod", act_output=None, reg_lambda=None,
optim="BaseGA", optim_params=None, obj_name="F1S", seed=42, verbose=True,
lb=None, ub=None, mode='single', n_workers=None, termination=None):
super().__init__(num_rules, mf_class, vanishing_strategy, act_output, reg_lambda,
optim, optim_params, obj_name, seed, verbose,
lb, ub, mode, n_workers, termination)
self.classes_ = None # Initialize classes to None
self.metric_class = ClassificationMetric # Set the metric class for evaluation
[docs] def fit(self, X, y):
"""
Fits the model to the training data.
Parameters
----------
X : array-like, shape (n_samples, n_features)
Training data.
y : array-like, shape (n_samples,)
Target values.
Returns
-------
self : BioAnfisClassifier
Returns the instance of the fitted model.
"""
## Check the parameters
self.size_input = X.shape[1] # Number of features
y = np.squeeze(np.array(y)) # Convert y to a numpy array and squeeze dimensions
if y.ndim != 1:
y = np.argmax(y, axis=1) # Convert to 1D if it’s not already
self.classes_ = np.unique(y) # Get unique classes from y
if len(self.classes_) == 2:
self.task = "binary_classification" # Set task for binary classification
self.size_output = 1 # Output size for binary classification
else:
self.task = "classification" # Set task for multi-class classification
self.size_output = len(self.classes_) # Output size for multi-class
## Process data
X_tensor = torch.tensor(X, dtype=torch.float32) # Convert input data to tensor
y = torch.tensor(y, dtype=torch.float32) # Convert target data to tensor
## Build model
self.build_model() # Build the model architecture
## Fit the data
self._fit(X_tensor, y) # Fit the model
return self # Return the fitted model
[docs] def predict(self, X):
"""
Predicts the class labels for the provided input data.
Parameters
----------
X : array-like, shape (n_samples, n_features)
Input data for prediction.
Returns
-------
np.ndarray
Predicted class labels for each sample.
"""
X_tensor = torch.tensor(X, dtype=torch.float32) # Convert input data to tensor
self.network.eval() # Set model to evaluation mode
with torch.no_grad():
output = self.network(X_tensor) # Get model predictions
if self.task =="classification": # Multi-class classification
_, predicted = torch.max(output, 1)
else: # Binary classification
predicted = (output > 0.5).int().squeeze()
return predicted.numpy() # Return as a numpy array
[docs] def score(self, X, y):
"""
Computes the accuracy score of the model based on predictions.
Parameters
----------
X : array-like, shape (n_samples, n_features)
Input data for scoring.
y : array-like, shape (n_samples,)
True labels for comparison.
Returns
-------
float
Accuracy score of the model.
"""
y_pred = self.predict(X) # Get predictions
return accuracy_score(y, y_pred) # Calculate and return accuracy score
[docs] def predict_proba(self, X):
"""
Computes the probability estimates for each class (for classification tasks only).
Parameters
----------
X : array-like, shape (n_samples, n_features)
Input data for which to predict probabilities.
Returns
-------
np.ndarray
Probability predictions for each class.
Raises
------
ValueError
If the task is not a classification task.
"""
X_tensor = torch.tensor(X, dtype=torch.float32) # Convert input data to tensor
if self.task not in ["classification", "binary_classification"]:
raise ValueError(
"predict_proba is only available for classification tasks.") # Raise error if task is invalid
self.network.eval() # Ensure model is in evaluation mode
with torch.no_grad():
probs = self.network.forward(X_tensor) # Get the output from forward pass
return probs.numpy() # Return probabilities as a numpy array
[docs] def evaluate(self, y_true, y_pred, list_metrics=("AS", "RS")):
"""
Return the list of performance metrics on the given test data and labels.
Parameters
----------
y_true : array-like of shape (n_samples,) or (n_samples, n_outputs)
True values for `X`.
y_pred : array-like of shape (n_samples,) or (n_samples, n_outputs)
Predicted values for `X`.
list_metrics : list, default=("AS", "RS")
List of metrics to compute using Permetrics library:
https://github.com/thieu1995/permetrics
Returns
-------
results : dict
A dictionary containing the results of the requested metrics.
"""
return self._BaseAnfis__evaluate_cls(y_true, y_pred, list_metrics) # Call evaluation method
[docs]class BioAnfisRegressor(BaseBioAnfis, RegressorMixin):
"""
A Bio-based ANFIS Regressor that extends the BaseBioAnfis class and implements
the RegressorMixin interface from Scikit-Learn for regression tasks.
This class integrates bio-inspired optimization algorithms for training an Adaptive
Neuro-Fuzzy Inference System (ANFIS) model, supporting both single-output and multi-output
regression tasks.
Attributes
----------
metric_class : type
The metric class used for evaluating regression performance.
size_input : int
Number of input features (set during training).
size_output : int
Number of output features (set during training).
task : str
The type of regression task ("regression" or "multi_regression").
network : nn.Module
The ANFIS model instance.
Parameters
----------
num_rules : int, optional
Number of fuzzy rules (default is 10).
mf_class : str, optional
Membership function class (default is "Gaussian").
vanishing_strategy : str or None, optional
Strategy for calculating rule strengths (default is 'prod').
act_output : any, optional
Activation function for the output layer (default is None).
reg_lambda : float or None, optional
Regularization parameter (default is None). This is used for regularizing the model.
optim : str, optional
The optimization algorithm to use (default is "BaseGA").
optim_params : dict, optional
Parameters for the optimizer (default is None).
obj_name : str, optional
The objective name for the optimization (default is "MSE").
seed : int, optional
Random seed for reproducibility (default is 42).
verbose : bool, optional
Whether to print detailed logs during fitting (default is True).
lb : int, float, list, tuple, np.ndarray, optional.
Lower bounds for optimization (default is (-1.0,)).
ub : int, float, list, tuple, np.ndarray, optional.
Upper bounds for optimization (default is (1.0,)).
mode : str, optional
Mode for optimization (default is 'single').
n_workers : int, optional
Number of workers for parallel processing (default is None).
termination : any, optional
Termination criteria for optimization (default is None).
Methods
-------
fit(X, y, lb=(-1.0,), ub=(1.0,), mode='single', n_workers=None, termination=None, save_population=False, **kwargs):
Fits the model to the training data using the specified bio-inspired optimizer.
predict(X):
Predicts the output values for the provided input data.
score(X, y):
Computes the R2 score of the model based on predictions.
evaluate(y_true, y_pred, list_metrics=("AS", "RS")):
Returns the list of performance metrics on the given test data and labels.
"""
def __init__(self, num_rules=10, mf_class="Gaussian", vanishing_strategy="prod", act_output=None, reg_lambda=None,
optim="BaseGA", optim_params=None, obj_name="MSE", seed=42, verbose=True,
lb=None, ub=None, mode='single', n_workers=None, termination=None):
super().__init__(num_rules, mf_class, vanishing_strategy, act_output, reg_lambda,
optim, optim_params, obj_name, seed, verbose,
lb, ub, mode, n_workers, termination)
self.metric_class = RegressionMetric # Set the metric class for evaluation
[docs] def fit(self, X, y):
"""
Fits the model to the training data.
Parameters
----------
X : array-like, shape (n_samples, n_features)
Training data.
y : array-like, shape (n_samples,) or (n_samples, n_outputs)
Target values.
Returns
-------
self : BioAnfisRegressor
Returns the instance of the fitted model.
"""
## Check the parameters
self.size_input = X.shape[1] # Number of input features
y = np.squeeze(np.array(y)) # Convert y to a numpy array and squeeze dimensions
self.size_output = 1 # Default output size for single-output regression
self.task = "regression" # Default task is regression
if y.ndim == 2:
self.task = "multi_regression" # Set task for multi-output regression
self.size_output = y.shape[1] # Update output size for multi-output
## Process data
X_tensor = torch.tensor(X, dtype=torch.float32) # Convert input data to tensor
y = torch.tensor(y, dtype=torch.float32) # Convert target data to tensor
## Build model
self.build_model() # Build the model architecture
## Fit the data
self._fit(X_tensor, y)
return self # Return the fitted model
[docs] def predict(self, X):
"""
Predicts the output values for the provided input data.
Parameters
----------
X : array-like, shape (n_samples, n_features)
Input data for prediction.
Returns
-------
np.ndarray
Predicted output values for each sample.
"""
X_tensor = torch.tensor(X, dtype=torch.float32) # Convert input data to tensor
self.network.eval() # Set model to evaluation mode
with torch.no_grad():
predicted = self.network(X_tensor) # Get model predictions
return predicted.numpy() # Return predictions as a numpy array
[docs] def score(self, X, y):
"""
Computes the R2 score of the model based on predictions.
Parameters
----------
X : array-like, shape (n_samples, n_features)
Input data for scoring.
y : array-like, shape (n_samples,)
True labels for comparison.
Returns
-------
float
R2 score of the model.
"""
y_pred = self.predict(X) # Get predictions
return r2_score(y, y_pred) # Calculate and return R^2 score
[docs] def evaluate(self, y_true, y_pred, list_metrics=("AS", "RS")):
"""
Return the list of performance metrics on the given test data and labels.
Parameters
----------
y_true : array-like of shape (n_samples,) or (n_samples, n_outputs)
True values for `X`.
y_pred : array-like of shape (n_samples,) or (n_samples, n_outputs)
Predicted values for `X`.
list_metrics : list, default=("AS", "RS")
List of metrics to compute using Permetrics library:
https://github.com/thieu1995/permetrics
Returns
-------
results : dict
A dictionary containing the results of the requested metrics.
"""
return self._BaseAnfis__evaluate_reg(y_true, y_pred, list_metrics) # Call evaluation method