Skip to content

Using different Models

Although mother is build around catboost it basically supports other models from the ML community (like all sklearn estimators). For example, RandomForest is already supported. Furthermore, own models can be provided.

Tip

Python
from mother import ml
print(ml.get_available_algorithms())

['randomforest', 'lasso', 'catboost']

Python
from mother import ml
print(ml.get_supported_models())

['RandomForestClassifierMother', 'RandomForestRegressorMother', 'LassoClassifierBinaryMother', 'LassoClassifierMulticlassMother', 'LassoRegressorMother', 'CatboostClassifierMother', 'CatboostGaussianProcessRegressorMother', 'CatboostRankerMother', 'CatboostRegressorMother']

Python
from mother import ml
print(ml.describe_model("RandomForestClassifierMother"))

RandomForestClassifierMother

A RandomForest classifier pipeline for the MOTHER framework, integrating hyperparameter optimization via Optuna and providing default parameter management. Inherits from both scikit-learn's RandomForestClassifier and the AbstractMotherPipeline for seamless integration with the MOTHER machine learning workflow.

get_hyperparameter_space

Defines the hyperparameter search space for RandomForestClassifier using Optuna.

Args: X: Feature matrix for training data. y: Target vector for training data. trial (Trial): Optuna trial object for suggesting hyperparameters. prefix (str, optional): Prefix to add to hyperparameter names. Defaults to "".

Returns: dict: Dictionary of hyperparameter names (with prefix) and their suggested values.

default_parameters

Returns the default hyperparameters for the RandomForestClassifier.

Args: prefix (str, optional): Prefix to add to hyperparameter names. Defaults to "".

Returns: dict: Dictionary of default hyperparameter names (with prefix) and their values.

For more information on the parent class just use 'help(ml.get_model_class("RandomForestClassifierMother")'

Using Lasso with Hyperparameter Tuning

Providing your own Model

To provide your own model and make this step as easy as possible, we provide the AbstractMotherPipelineClass.

Bases: ABC

The abstract Mother pipeline is a conventional sklearn estimator / transformer etc. but adds methods for hyperparameter definition. Furthermore, it ensures for non sklearn classes and derived classes that they are compatible to the sklearn pipeline interface. This is done by implementing the get_params and set_params methods.

Source code in mother/ml/core.py
Python
class AbstractMotherPipeline(ABC):
    """
    The abstract Mother pipeline is a conventional sklearn estimator / transformer etc. but adds methods for
    hyperparameter definition. Furthermore, it ensures for non sklearn classes and derived classes that they are
    compatible to the sklearn pipeline interface. This is done by implementing the get_params and set_params methods.
    """

    @abstractmethod
    def get_hyperparameter_space(self, X, y, trial: Trial, prefix: str = "") -> dict:
        raise NotImplementedError

    def default_parameters(self, prefix: str = "") -> dict:
        return {}

    def get_all_params(self) -> dict:
        if hasattr(super, "get_all_params"):
            return super.get_all_params()  # type: ignore
        module_logger.warning("Could not find get_all_params method in the parent class (%s).", self.__class__.__name__)
        return {}

    @abstractmethod
    def set_params(self, **params):
        raise NotImplementedError(
            "set_params method is not implemented. Please implement this method in the derived class."
        )

    @abstractmethod
    def get_params(self, deep=True) -> dict:
        raise NotImplementedError(
            "get_params method is not implemented. Please implement this method in the derived class."
        )

    def predict_uncertainty(self, X: pd.DataFrame, **kwargs):
        """
        This method needs to be implemented for the models that supports uncertainty.
        Otherwise, the model will run self.predict(X).

        Args:
            X (pd.DataFrame): input features to predict target value
        """
        module_logger.warning(
            f"Uncertainty quantification is not implemented for {self.__class__.__name__}."
            "predict() function will be run."
        )
        pred_res = self.predict(X)

        # make a standardised output data frame
        # this only works for single-task output (not supporting multi-task learning)
        if len(pred_res.shape) == 1:
            # single dim output
            pred_res = pd.DataFrame(
                {
                    "mean_predictions": pred_res,
                    "knowledge_uncertainty": None,
                    "data_uncertainty": None,
                    "total_uncertainty": None,
                },
            )
        elif (len(pred_res.shape) == 2) and (pred_res.shape[-1] == 1):
            # 2D output with one col
            pred_res = pd.DataFrame(
                {
                    "mean_predictions": pred_res.iloc[:, -1] if isinstance(pred_res, pd.DataFrame) else pred_res[:, -1],
                    "knowledge_uncertainty": None,
                    "data_uncertainty": None,
                    "total_uncertainty": None,
                },
            )

        if isinstance(X, pd.DataFrame) and isinstance(pred_res, pd.DataFrame):
            pred_res.set_index(X.index, inplace=True)

        return pred_res

predict_uncertainty(X, **kwargs)

This method needs to be implemented for the models that supports uncertainty. Otherwise, the model will run self.predict(X).

Parameters:

Name Type Description Default
X DataFrame

input features to predict target value

required
Source code in mother/ml/core.py
Python
def predict_uncertainty(self, X: pd.DataFrame, **kwargs):
    """
    This method needs to be implemented for the models that supports uncertainty.
    Otherwise, the model will run self.predict(X).

    Args:
        X (pd.DataFrame): input features to predict target value
    """
    module_logger.warning(
        f"Uncertainty quantification is not implemented for {self.__class__.__name__}."
        "predict() function will be run."
    )
    pred_res = self.predict(X)

    # make a standardised output data frame
    # this only works for single-task output (not supporting multi-task learning)
    if len(pred_res.shape) == 1:
        # single dim output
        pred_res = pd.DataFrame(
            {
                "mean_predictions": pred_res,
                "knowledge_uncertainty": None,
                "data_uncertainty": None,
                "total_uncertainty": None,
            },
        )
    elif (len(pred_res.shape) == 2) and (pred_res.shape[-1] == 1):
        # 2D output with one col
        pred_res = pd.DataFrame(
            {
                "mean_predictions": pred_res.iloc[:, -1] if isinstance(pred_res, pd.DataFrame) else pred_res[:, -1],
                "knowledge_uncertainty": None,
                "data_uncertainty": None,
                "total_uncertainty": None,
            },
        )

    if isinstance(X, pd.DataFrame) and isinstance(pred_res, pd.DataFrame):
        pred_res.set_index(X.index, inplace=True)

    return pred_res

Your own model just has to inherit from that class and implement the required functions that provide the hyperparameters you want to tune. For example, see the implementation of the Lasso model. Since lasso basically has one parameter to be tuned, the implementation is fairly easy.

Lasso with Hyperparameter Tuning
import logging
from typing import Literal, Mapping, Optional, Union

from optuna.trial import Trial
from sklearn.linear_model import Lasso, LogisticRegression

from mother.ml.core import AbstractMotherPipeline
from mother.ml.models import utils

module_logger: logging.Logger = logging.getLogger(__name__)


class LassoRegressorMother(Lasso, AbstractMotherPipeline):
    """
    MOTHER class for a LASSO regression including hyperparameter optimization
    """

    def get_hyperparameter_space(self, X, y, trial: Trial, prefix: str = "") -> dict:
        """
        Define the hyperparameter search space for Lasso regression.

        Parameters:
            X: array-like
                Feature matrix.
            y: array-like
                Target vector.
            trial: optuna.trial.Trial
                Optuna trial object for suggesting hyperparameters.
            prefix: str, optional
                Prefix to add to hyperparameter names.

        Returns:
            dict: Dictionary containing hyperparameter names and their suggested values.
        """
        return utils.add_prefix_to_dict_keys(
            {"alpha": trial.suggest_float(prefix + "alpha", 1e-6, 1e1, log=True)},
            prefix=prefix,
        )

    def default_parameters(self, prefix: str = "") -> dict:
        """
        Return the default hyperparameters for the Lasso model.

        Parameters:
            prefix: str, optional
                Prefix to add to hyperparameter names.

        Returns:
            dict: Dictionary containing default hyperparameter values.
        """
        return utils.add_prefix_to_dict_keys({"alpha": 1e-3}, prefix=prefix)

    def set_params(self, **params):
        """
        Set the parameters of the Lasso model.

        Parameters:
        **params: Keyword arguments for the parameters to set.
        """
        return super().set_params(**params)

    def get_params(self, deep=True) -> dict:
        return super().get_params(deep=deep)


class LassoClassifierBinaryMother(LogisticRegression, AbstractMotherPipeline):
    """
    MOTHER class for a LASSO classification including hyperparameter optimization
    """

    def __init__(
        self,
        penalty: Literal["l1"] = "l1",  # Lasso uses L1 penalty
        *,
        dual: bool = False,
        tol: float = 0.0001,
        C: float = 1,
        fit_intercept: bool = True,
        intercept_scaling: float = 1,
        class_weight: Optional[Union[Mapping, str]] = "balanced",
        random_state: int = 42,
        solver: str = "liblinear",  # 'liblinear' can be used for L1 penalty and is less complex
        max_iter: int = 3000,
        verbose: int = 0,
        warm_start: bool = False,
        n_jobs: Optional[int] = None,
    ) -> None:
        super().__init__(
            penalty,
            dual=dual,
            tol=tol,
            C=C,
            fit_intercept=fit_intercept,
            intercept_scaling=intercept_scaling,
            class_weight=class_weight,
            random_state=random_state,
            solver=solver,  # type: ignore
            max_iter=max_iter,
            verbose=verbose,
            warm_start=warm_start,
            n_jobs=n_jobs,
        )

    def get_hyperparameter_space(self, X, y, trial: Trial, prefix: str = "") -> dict:
        """
        Define the hyperparameter search space for Lasso classification.

        Parameters:
            X: array-like
                Feature matrix.
            y: array-like
                Target vector.
            trial: optuna.trial.Trial
                Optuna trial object for suggesting hyperparameters.
            prefix: str, optional
                Prefix to add to hyperparameter names.

        Returns:
            dict: Dictionary containing hyperparameter names and their suggested values.
        """
        return utils.add_prefix_to_dict_keys(
            {
                "C": trial.suggest_float(prefix + "C", 1e-6, 1e1, log=True),
            },
            prefix=prefix,
        )

    def default_parameters(self, prefix: str = "") -> dict:
        """
        Return the default hyperparameters for the Lasso model.

        Parameters:
            prefix: str, optional
                Prefix to add to hyperparameter names.

        Returns:
            dict: Dictionary containing default hyperparameter values.
        """
        return utils.add_prefix_to_dict_keys({"C": 1e0}, prefix=prefix)

    def set_params(self, **params):
        """
        Set the parameters of the Lasso model.

        Parameters:
        **params: Keyword arguments for the parameters to set.
        """
        return super().set_params(**params)

    def get_params(self, deep=True) -> dict:
        return super().get_params(deep=deep)


class LassoClassifierMulticlassMother(LassoClassifierBinaryMother):
    """
    MOTHER class for a LASSO classification with multiclass support.
    Inherits from LassoClassifierBinaryMother.
    """

    def __init__(self, **kwargs):
        module_logger.warning(
            """LassoClassifierMother selected. 'Saga' is used as solver.
            Scale input features beforehand to improve convergence."""
        )
        if "solver" in kwargs:
            module_logger.warning(
                "LassoClassifierMulticlassMother selected. 'Saga' is used as solver for multiclass problems."
            )
            # Use 'saga' solver for multiclass support
            # 'saga' supports L1 penalty and is suitable for large datasets
        kwargs["solver"] = "saga"
        super().__init__(**kwargs)

Registering your model using MotherModelRegistry

To register your own model and use it easily within the mother framework you can register your model with the available decorator.

MotherModelRegistry

Singleton registry for dynamically discovering and managing model classes in the 'mother.ml.models' package.

This class scans the models directory for Python files matching the pattern 'm_*.py', imports them, and registers all classes that inherit from AbstractMotherPipeline. It provides mappings for model class names, lower-case lookups, and a list of supported algorithms. The registry is used to facilitate model discovery, retrieval, and algorithm support checks throughout the mother.ml package.

Attributes:

Name Type Description
models_dir Path

Path to the directory containing model modules.

model_classes dict

Mapping of model class names to their class objects.

model_classes_lower dict

Mapping of lower-case model class names to their canonical names.

supported_algorithms list

List of supported algorithm names discovered from model files.

Source code in mother/ml/__init__.py
Python
class MotherModelRegistry:
    """
    Singleton registry for dynamically discovering and managing model classes in the 'mother.ml.models' package.

    This class scans the models directory for Python files matching the pattern 'm_*.py', imports them,
    and registers all classes that inherit from AbstractMotherPipeline. It provides mappings for model class names,
    lower-case lookups, and a list of supported algorithms. The registry is used to facilitate model discovery,
    retrieval, and algorithm support checks throughout the mother.ml package.

    Attributes:
        models_dir (Path): Path to the directory containing model modules.
        model_classes (dict): Mapping of model class names to their class objects.
        model_classes_lower (dict): Mapping of lower-case model class names to their canonical names.
        supported_algorithms (list): List of supported algorithm names discovered from model files.
    """

    _instance = None
    _initialized: bool = False

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self):
        if not self._initialized:
            module_logger.debug("Initializing MotherModelRegistry")
            self.models_dir: Path = Path(__file__).parent.joinpath("models")
            self.model_classes: dict[str, type[AbstractMotherPipeline]] = {}
            self.model_classes_lower: dict[str, str] = {}
            self.supported_algorithms: dict[str, set[str]] = {}

    def _load_models(self) -> None:
        """Dynamically load model classes from the models directory."""
        if self._initialized:
            return  # Prevent re-initialization
        importlib.invalidate_caches()  # Ensure fresh import cache
        module_logger.debug("Loading models from directory: %s", self.models_dir)
        # Get all Python files in the models directory (excluding __init__.py)
        model_files: list = [f.stem for f in self.models_dir.glob("m_*.py")]

        # Import each model module and collect AbstractMotherPipeline implementations
        for model_file in model_files:
            try:
                module_logger.debug(f"Loading model module: {model_file}")
                module = importlib.import_module(f"mother.ml.models.{model_file}")
                # Ensure the module is loaded correctly
                if not hasattr(module, "__name__"):
                    module_logger.error(f"Module {model_file} does not have a __name__ attribute.")
                    continue
                # Find all classes in the module that inherit from AbstractMotherPipeline
                for name, obj in inspect.getmembers(module, inspect.isclass):
                    if (
                        issubclass(obj, AbstractMotherPipeline)
                        and obj.__module__ == module.__name__
                        and obj != AbstractMotherPipeline
                        and not name.startswith("_")
                    ):
                        self.model_classes[name] = obj
                        self.model_classes_lower[name.lower()] = name  # Add lower-case mapping

                        algo: str = model_file.lower().lstrip("m_")

                        if algo not in self.supported_algorithms:
                            self.supported_algorithms[algo] = set()
                        self.supported_algorithms[algo].add(name)
            except Exception as e:
                module_logger.warning(
                    (
                        f"Warning: Failed to load model from {model_file}: {str(e)} "
                        f"(Please, ignore this message if you don't need {model_file.split('m_')[1]})"
                    )
                )
        module_logger.debug("Model loading complete.")
        module_logger.info(f"Loaded {len(self.model_classes)} model classes: {', '.join(self.model_classes.keys())}")
        self._initialized = True

    def register_model(self, model_class: Type[AbstractMotherPipeline], algorithm: Optional[str] = None) -> None:
        """Register a model class manually.

        Args:
            model_class: The model class to register
            algorithm: Optional algorithm name. If not provided, will be derived from class name
        """
        if not issubclass(model_class, AbstractMotherPipeline):
            raise ValueError(f"Model class {model_class.__name__} must inherit from AbstractMotherPipeline")

        class_name = model_class.__name__

        # Add to model classes
        self.model_classes[class_name] = model_class
        self.model_classes_lower[class_name.lower()] = class_name

        # Determine algorithm name
        if algorithm is None:
            # Try to extract algorithm from class name
            if class_name.lower().startswith("mother"):
                algorithm = class_name[6:].lower()  # Remove "Mother" prefix
            elif class_name.lower().endswith("mother"):
                algorithm = class_name[:-6].lower()  # Remove "Mother" suffix
            else:
                algorithm = class_name.lower()

        # Add algorithm if not already present
        if algorithm not in self.supported_algorithms:
            self.supported_algorithms[algorithm] = set()
        self.supported_algorithms[algorithm].add(class_name)

        module_logger.info(f"Registered model class: {class_name} with algorithm: {algorithm}")

    def unregister_model(self, model_class_name: str) -> None:
        """Unregister a model class.

        Args:
            model_class_name: Name of the model class to unregister
        """
        if model_class_name in self.model_classes:
            del self.model_classes[model_class_name]
            if model_class_name.lower() in self.model_classes_lower:
                del self.model_classes_lower[model_class_name.lower()]
            module_logger.info(f"Unregistered model class: {model_class_name}")
        else:
            module_logger.warning(f"Model class {model_class_name} not found for unregistration")

    def list_registered_models(self) -> dict:
        """List all registered models with their algorithms."""
        register_models = {}
        for name, classes in self.supported_algorithms.items():
            for cls in classes:
                register_models[cls] = {
                    "class": self.model_classes[cls],
                    "algorithm": name,
                }
        # return {
        #     name: {
        #         "class": cls,
        #         "algorithm": next(
        #             (algo for algo in self.supported_algorithms.keys() if name.lower().startswith(algo.lower())),
        #             "unknown",
        #         ),
        #     }
        #     for name, classes in self.supported_algorithms.items()
        # }
        return register_models

list_registered_models()

List all registered models with their algorithms.

Source code in mother/ml/__init__.py
Python
def list_registered_models(self) -> dict:
    """List all registered models with their algorithms."""
    register_models = {}
    for name, classes in self.supported_algorithms.items():
        for cls in classes:
            register_models[cls] = {
                "class": self.model_classes[cls],
                "algorithm": name,
            }
    # return {
    #     name: {
    #         "class": cls,
    #         "algorithm": next(
    #             (algo for algo in self.supported_algorithms.keys() if name.lower().startswith(algo.lower())),
    #             "unknown",
    #         ),
    #     }
    #     for name, classes in self.supported_algorithms.items()
    # }
    return register_models

register_model(model_class, algorithm=None)

Register a model class manually.

Parameters:

Name Type Description Default
model_class Type[AbstractMotherPipeline]

The model class to register

required
algorithm Optional[str]

Optional algorithm name. If not provided, will be derived from class name

None
Source code in mother/ml/__init__.py
Python
def register_model(self, model_class: Type[AbstractMotherPipeline], algorithm: Optional[str] = None) -> None:
    """Register a model class manually.

    Args:
        model_class: The model class to register
        algorithm: Optional algorithm name. If not provided, will be derived from class name
    """
    if not issubclass(model_class, AbstractMotherPipeline):
        raise ValueError(f"Model class {model_class.__name__} must inherit from AbstractMotherPipeline")

    class_name = model_class.__name__

    # Add to model classes
    self.model_classes[class_name] = model_class
    self.model_classes_lower[class_name.lower()] = class_name

    # Determine algorithm name
    if algorithm is None:
        # Try to extract algorithm from class name
        if class_name.lower().startswith("mother"):
            algorithm = class_name[6:].lower()  # Remove "Mother" prefix
        elif class_name.lower().endswith("mother"):
            algorithm = class_name[:-6].lower()  # Remove "Mother" suffix
        else:
            algorithm = class_name.lower()

    # Add algorithm if not already present
    if algorithm not in self.supported_algorithms:
        self.supported_algorithms[algorithm] = set()
    self.supported_algorithms[algorithm].add(class_name)

    module_logger.info(f"Registered model class: {class_name} with algorithm: {algorithm}")

unregister_model(model_class_name)

Unregister a model class.

Parameters:

Name Type Description Default
model_class_name str

Name of the model class to unregister

required
Source code in mother/ml/__init__.py
Python
def unregister_model(self, model_class_name: str) -> None:
    """Unregister a model class.

    Args:
        model_class_name: Name of the model class to unregister
    """
    if model_class_name in self.model_classes:
        del self.model_classes[model_class_name]
        if model_class_name.lower() in self.model_classes_lower:
            del self.model_classes_lower[model_class_name.lower()]
        module_logger.info(f"Unregistered model class: {model_class_name}")
    else:
        module_logger.warning(f"Model class {model_class_name} not found for unregistration")

algo_is_supported(algorithm)

Check if the specified algorithm is supported.

Parameters:

Name Type Description Default
algorithm str

Name of the algorithm to check

required

Returns:

Name Type Description
bool bool

True if supported, False otherwise

Source code in mother/ml/__init__.py
Python
def algo_is_supported(algorithm: str) -> bool:
    """Check if the specified algorithm is supported.

    Args:
        algorithm: Name of the algorithm to check

    Returns:
        bool: True if supported, False otherwise
    """
    if _registry._initialized is False:
        _registry._load_models()
    return algorithm.lower() in _registry.supported_algorithms

describe_model(name)

Get help text for a model class.

Parameters:

Name Type Description Default
name str

Name of the model class

required

Returns:

Name Type Description
str str

Help text for the model class

Source code in mother/ml/__init__.py
Python
def describe_model(name: str) -> str:
    """Get help text for a model class.

    Args:
        name: Name of the model class

    Returns:
        str: Help text for the model class
    """
    if _registry._initialized is False:
        _registry._load_models()
    # Instead of calling help() directly (which prints to stdout)
    # Use the inspect module to get the docstring
    if name in _registry.model_classes:
        model_class = _registry.model_classes[name]
        import inspect

        docstring = inspect.getdoc(model_class) or "No documentation available"

        # Format for better display
        result = f"## {name}\n\n{docstring}\n\n"

        # Add method documentation if desired
        methods = ["get_hyperparameter_space", "default_parameters"]
        for method_name in methods:
            if hasattr(model_class, method_name):
                method = getattr(model_class, method_name)
                method_doc = inspect.getdoc(method) or "No documentation available"
                result += f"### {method_name}\n\n{method_doc}\n\n"

        result += f"For more information on the parent class just use 'help(ml.get_model_class(\"{name}\")'"
        return result
    raise KeyError(f"Model class '{name}' not found")

get_available_algorithms()

Get a list of all supported algorithms.

Returns:

Type Description
List[str]

List[str]: Names of supported algorithms

Source code in mother/ml/__init__.py
Python
def get_available_algorithms() -> List[str]:
    """Get a list of all supported algorithms.

    Returns:
        List[str]: Names of supported algorithms
    """
    if _registry._initialized is False:
        _registry._load_models()
    return list(_registry.supported_algorithms.keys())

get_model_class(name)

Get a model class by name.

Parameters:

Name Type Description Default
name str

Name of the model class

required

Returns:

Name Type Description
Type type[AbstractMotherPipeline]

The model class

Raises:

Type Description
KeyError

If the model class is not found

Source code in mother/ml/__init__.py
Python
def get_model_class(name: str) -> type[AbstractMotherPipeline]:
    """Get a model class by name.

    Args:
        name: Name of the model class

    Returns:
        Type: The model class

    Raises:
        KeyError: If the model class is not found
    """
    if _registry._initialized is False:
        _registry._load_models()
    if name in _registry.model_classes:
        return _registry.model_classes[name]
    # Try lower-case mapping
    lower_name = name.lower()
    if lower_name in _registry.model_classes_lower:
        return _registry.model_classes[_registry.model_classes_lower[lower_name]]
    raise KeyError(f"Model class '{name}' not found")

get_model_class_by_algorithm(algorithm)

Get model classes by algorithm name.

Parameters:

Name Type Description Default
algorithm str

Name of the algorithm

required

Returns:

Name Type Description
Type list[Type[AbstractMotherPipeline]]

The model class

Source code in mother/ml/__init__.py
Python
def get_model_class_by_algorithm(algorithm: str) -> list[Type[AbstractMotherPipeline]]:
    """Get model classes by algorithm name.

    Args:
        algorithm: Name of the algorithm

    Returns:
        Type: The model class

    """
    if _registry._initialized is False:
        _registry._load_models()
    return [
        _registry.model_classes[class_]
        for name, class_set in _registry.supported_algorithms.items()
        if name.lower().startswith(algorithm.lower())
        for class_ in class_set
    ]

get_model_class_by_algorithm_and_type(algorithm, model_type)

Returns the appropriate model class based on the algorithm and model type.

Source code in mother/ml/__init__.py
Python
def get_model_class_by_algorithm_and_type(algorithm: str, model_type: str) -> type[AbstractMotherPipeline]:
    """
    Returns the appropriate model class based on the algorithm and model type.
    """
    models: List[str] = [m.lower() for m in get_supported_models()]
    __estimator_type: str
    __model_type: Optional[str] = None

    res = model_type.split("_", 1)
    if len(res) == 2:
        __estimator_type, __model_type = res
    else:
        __estimator_type = res[0]

    if __estimator_type == "classification":
        __estimator_type = "classifier"
    elif __estimator_type == "regression":
        __estimator_type = "regressor"
    elif __estimator_type == "ranking":
        __estimator_type = "ranker"
    else:
        raise ValueError(f"Unsupported model type: {model_type}. Must be 'classification' or 'regression'.")

    selected_models: List[str] = [m for m in models if m.startswith(algorithm.lower()) and __estimator_type in m]
    if __model_type is not None:
        if len(selected_models) != 1:
            selected_models = [m for m in selected_models if __model_type in m]
        elif __model_type not in ["binary", "multiclass"]:
            selected_models = []
    elif algorithm == "catboost":
        # exclude gaussian process regression
        selected_models = [m for m in selected_models if "gaussian" not in m]

    if len(selected_models) != 1:
        raise ValueError(f"Unsupported algorithm '{algorithm}' or model type '{model_type}'. ")
    return get_model_class(name=selected_models[0])

get_supported_models()

Get a list of all supported model class names.

Returns:

Type Description
List[str]

List[str]: Names of supported model classes

Source code in mother/ml/__init__.py
Python
def get_supported_models() -> List[str]:
    """Get a list of all supported model class names.

    Returns:
        List[str]: Names of supported model classes
    """
    if _registry._initialized is False:
        _registry._load_models()
    return list(_registry.model_classes.keys())

register_model(algorithm=None)

Decorator to register a model class.

Parameters:

Name Type Description Default
algorithm Optional[str]

Optional algorithm name

None
Example

@register_model("my_algorithm") class MyCustomMother(AbstractMotherPipeline): pass

Source code in mother/ml/__init__.py
Python
def register_model(algorithm: Optional[str] = None):
    """Decorator to register a model class.

    Args:
        algorithm: Optional algorithm name

    Example:
        @register_model("my_algorithm")
        class MyCustomMother(AbstractMotherPipeline):
            pass
    """

    def decorator(model_class: Type[AbstractMotherPipeline]):
        _registry.register_model(model_class, algorithm)
        return model_class

    return decorator

See the following example how to implement your custom RandomForest Classifier.

Example

Python
from mother import ml
from sklearn.ensemble import RandomForestClassifier

@ml.register_model("custom_rf")
class CustomRandomForestMother(RandomForestClassifier, ml.AbstractMotherPipeline):
    def get_hyperparameter_space(self, X, y, trial, prefix=""):
        return {
            f"{prefix}n_estimators": trial.suggest_int("n_estimators", 10, 100),
            f"{prefix}max_depth": trial.suggest_int("max_depth", 3, 10),
        }

    def default_parameters(self, prefix=""):
        return {f"{prefix}n_estimators": 50, f"{prefix}max_depth": 5}

print(ml.get_model_class_by_algorithm("custom_rf"))