Skip to content

API: modeling.classification

skyulf.modeling.classification

Classification models.

AdaBoostClassifierApplier

Bases: SklearnApplier

AdaBoost Classifier Applier.

Source code in skyulf-core/skyulf/modeling/classification.py
582
583
class AdaBoostClassifierApplier(SklearnApplier):
    """AdaBoost Classifier Applier."""

AdaBoostClassifierCalculator

Bases: SklearnCalculator

AdaBoost Classifier Calculator.

Source code in skyulf-core/skyulf/modeling/classification.py
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
@NodeRegistry.register("adaboost_classifier", AdaBoostClassifierApplier)
@node_meta(
    id="adaboost_classifier",
    name="AdaBoost Classifier",
    category="Modeling",
    description="An AdaBoost classifier.",
    params={"n_estimators": 50, "learning_rate": 1.0},
    tags=["classification"],
    learns_from_data=True,
)
class AdaBoostClassifierCalculator(SklearnCalculator):
    """AdaBoost Classifier Calculator."""

    def __init__(self):
        """Bind ``AdaBoostClassifier`` with 50 weak learners at rate 1.0.

        Both are sklearn's defaults. No base estimator is pinned here, so
        ``fit``'s config may supply one; sklearn's depth-1 decision stump is
        used when it does not.
        """
        super().__init__(
            model_class=AdaBoostClassifier,
            default_params={
                "n_estimators": 50,
                "learning_rate": 1.0,
            },
            problem_type="classification",
        )

__init__()

Bind AdaBoostClassifier with 50 weak learners at rate 1.0.

Both are sklearn's defaults. No base estimator is pinned here, so fit's config may supply one; sklearn's depth-1 decision stump is used when it does not.

Source code in skyulf-core/skyulf/modeling/classification.py
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
def __init__(self):
    """Bind ``AdaBoostClassifier`` with 50 weak learners at rate 1.0.

    Both are sklearn's defaults. No base estimator is pinned here, so
    ``fit``'s config may supply one; sklearn's depth-1 decision stump is
    used when it does not.
    """
    super().__init__(
        model_class=AdaBoostClassifier,
        default_params={
            "n_estimators": 50,
            "learning_rate": 1.0,
        },
        problem_type="classification",
    )

CalibratedClassifierApplier

Bases: SklearnApplier

Calibrated Classifier Applier (well-calibrated predict_proba).

Source code in skyulf-core/skyulf/modeling/classification.py
268
269
class CalibratedClassifierApplier(SklearnApplier):
    """Calibrated Classifier Applier (well-calibrated predict_proba)."""

CalibratedClassifierCalculator

Bases: SklearnCalculator

Calibrated Classifier Calculator with a selectable base estimator.

The frontend sends base_estimator as a string key (e.g. "random_forest"); it is resolved here into a fresh estimator instance before CalibratedClassifierCV is constructed. Defaults to logistic regression for backward compatibility. random_state seeds supported base estimators, defaults to 42, and accepts None for unseeded fits. It does not shuffle calibration CV folds or affect deterministic bases. A tuning search space can select one or more base_estimator keys; each candidate and the final refit use the selected classifier family.

Source code in skyulf-core/skyulf/modeling/classification.py
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
@NodeRegistry.register("calibrated_classifier", CalibratedClassifierApplier)
@node_meta(
    id="calibrated_classifier",
    name="Calibrated Classifier",
    category="Modeling",
    description=(
        "Wraps a base classifier with CalibratedClassifierCV so predicted "
        "probabilities are well-calibrated (Platt/sigmoid or isotonic)."
    ),
    params={"base_estimator": "logistic_regression", "method": "sigmoid", "cv": 5},
    tags=["requires_scaling", "classification"],
    learns_from_data=True,
)
class CalibratedClassifierCalculator(SklearnCalculator):
    """Calibrated Classifier Calculator with a selectable base estimator.

    The frontend sends ``base_estimator`` as a string key (e.g.
    ``"random_forest"``); it is resolved here into a fresh estimator instance
    before ``CalibratedClassifierCV`` is constructed. Defaults to logistic
    regression for backward compatibility. ``random_state`` seeds supported
    base estimators, defaults to 42, and accepts ``None`` for unseeded fits.
    It does not shuffle calibration CV folds or affect deterministic bases.
    A tuning search space can select one or more ``base_estimator`` keys;
    each candidate and the final refit use the selected classifier family.
    """

    # Map of selectable base estimators → factory. Each must support
    # ``predict_proba`` (or ``decision_function``) so calibration is meaningful.
    BASE_ESTIMATORS: ClassVar[dict[str, Callable[[], BaseEstimator]]] = _CALIBRATED_BASE_ESTIMATORS

    STRUCTURAL_TUNING_KEYS: tuple[str, ...] = ("base_estimator",)

    def __init__(self):
        """Bind ``CalibratedClassifierCV`` over a logistic-regression base estimator.

        Fixes ``method="sigmoid"`` and ``cv=5`` alongside that base estimator as
        overridable defaults, and starts ``_tuning_base_config`` empty so
        :attr:`default_params` yields the bound estimator until
        :meth:`prepare_tuning_params` records a different selection.
        """
        super().__init__(
            model_class=_SeededCalibratedClassifierCV,
            default_params={
                "estimator": LogisticRegression(max_iter=1000),
                "method": "sigmoid",
                "cv": 5,
            },
            problem_type="classification",
        )
        self._tuning_base_config: dict[str, Any] = {}

    @property
    def default_params(self) -> dict[str, Any]:
        """Return the defaults with any tuning-selected base estimator resolved in.

        An unrecognized ``base_estimator`` key warns and falls back to logistic
        regression rather than raising. The factory runs on every access, so
        separate tuning trials never share one estimator instance.
        """
        params = dict(self._default_params)
        if self._tuning_base_config:
            key = self._tuning_base_config.get("base_estimator")
            if isinstance(key, str):
                params["estimator"] = _make_calibrated_base_estimator(key)
        return params

    def prepare_tuning_params(self, config: dict[str, Any]) -> None:
        """Absorb ``base_estimator`` so it selects an estimator instead of being tuned.

        Only keys named in :attr:`STRUCTURAL_TUNING_KEYS` are taken, read from
        either the nested ``params`` dict or the flat config.
        """
        src = config.get("params") if isinstance(config.get("params"), dict) else config
        src = src or {}
        self._tuning_base_config = {k: src[k] for k in self.STRUCTURAL_TUNING_KEYS if k in src}

    def fit(
        self,
        X: Any,
        y: Any,
        config: dict[str, Any],
        progress_callback: Callable[..., Any] | None = None,
        log_callback: Callable[..., Any] | None = None,
        validation_data: Any = None,
        iteration_callback: Callable[..., Any] | None = None,
        *,
        sample_weight: Any = None,
    ) -> Any:
        """Resolve the base estimator in ``config``, then delegate to the base fit."""
        config = self._resolve_base_estimator(config)
        return super().fit(
            X,
            y,
            config,
            progress_callback,
            log_callback,
            validation_data,
            iteration_callback=iteration_callback,
            sample_weight=sample_weight,
        )

    @classmethod
    def _resolve_base_estimator(cls, config: dict[str, Any] | None) -> dict[str, Any]:
        """Translate a ``base_estimator`` string key into an estimator instance.

        Supports both the flat config shape and the nested ``{"params": {...}}``
        shape used by the model-training payload. Unknown keys fall back to
        logistic regression with a warning.
        """
        if not config:
            return config or {}
        resolved = dict(config)
        nested = isinstance(resolved.get("params"), dict)
        bucket = dict(resolved["params"]) if nested else resolved
        key = bucket.pop("base_estimator", None)
        if isinstance(key, str):
            bucket["estimator"] = _make_calibrated_base_estimator(key)
        if nested:
            resolved["params"] = bucket
            return resolved
        return bucket

default_params property

Return the defaults with any tuning-selected base estimator resolved in.

An unrecognized base_estimator key warns and falls back to logistic regression rather than raising. The factory runs on every access, so separate tuning trials never share one estimator instance.

__init__()

Bind CalibratedClassifierCV over a logistic-regression base estimator.

Fixes method="sigmoid" and cv=5 alongside that base estimator as overridable defaults, and starts _tuning_base_config empty so :attr:default_params yields the bound estimator until :meth:prepare_tuning_params records a different selection.

Source code in skyulf-core/skyulf/modeling/classification.py
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
def __init__(self):
    """Bind ``CalibratedClassifierCV`` over a logistic-regression base estimator.

    Fixes ``method="sigmoid"`` and ``cv=5`` alongside that base estimator as
    overridable defaults, and starts ``_tuning_base_config`` empty so
    :attr:`default_params` yields the bound estimator until
    :meth:`prepare_tuning_params` records a different selection.
    """
    super().__init__(
        model_class=_SeededCalibratedClassifierCV,
        default_params={
            "estimator": LogisticRegression(max_iter=1000),
            "method": "sigmoid",
            "cv": 5,
        },
        problem_type="classification",
    )
    self._tuning_base_config: dict[str, Any] = {}

fit(X, y, config, progress_callback=None, log_callback=None, validation_data=None, iteration_callback=None, *, sample_weight=None)

Resolve the base estimator in config, then delegate to the base fit.

Source code in skyulf-core/skyulf/modeling/classification.py
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
def fit(
    self,
    X: Any,
    y: Any,
    config: dict[str, Any],
    progress_callback: Callable[..., Any] | None = None,
    log_callback: Callable[..., Any] | None = None,
    validation_data: Any = None,
    iteration_callback: Callable[..., Any] | None = None,
    *,
    sample_weight: Any = None,
) -> Any:
    """Resolve the base estimator in ``config``, then delegate to the base fit."""
    config = self._resolve_base_estimator(config)
    return super().fit(
        X,
        y,
        config,
        progress_callback,
        log_callback,
        validation_data,
        iteration_callback=iteration_callback,
        sample_weight=sample_weight,
    )

prepare_tuning_params(config)

Absorb base_estimator so it selects an estimator instead of being tuned.

Only keys named in :attr:STRUCTURAL_TUNING_KEYS are taken, read from either the nested params dict or the flat config.

Source code in skyulf-core/skyulf/modeling/classification.py
338
339
340
341
342
343
344
345
346
def prepare_tuning_params(self, config: dict[str, Any]) -> None:
    """Absorb ``base_estimator`` so it selects an estimator instead of being tuned.

    Only keys named in :attr:`STRUCTURAL_TUNING_KEYS` are taken, read from
    either the nested ``params`` dict or the flat config.
    """
    src = config.get("params") if isinstance(config.get("params"), dict) else config
    src = src or {}
    self._tuning_base_config = {k: src[k] for k in self.STRUCTURAL_TUNING_KEYS if k in src}

DecisionTreeClassifierApplier

Bases: SklearnApplier

Decision Tree Classifier Applier.

Source code in skyulf-core/skyulf/modeling/classification.py
511
512
class DecisionTreeClassifierApplier(SklearnApplier):
    """Decision Tree Classifier Applier."""

DecisionTreeClassifierCalculator

Bases: SklearnCalculator

Decision Tree Classifier Calculator.

Source code in skyulf-core/skyulf/modeling/classification.py
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
@NodeRegistry.register("decision_tree_classifier", DecisionTreeClassifierApplier)
@node_meta(
    id="decision_tree_classifier",
    name="Decision Tree Classifier",
    category="Modeling",
    description="A non-parametric supervised learning method used for classification.",
    params={"max_depth": None, "min_samples_split": 2, "criterion": "gini"},
    tags=["classification"],
    learns_from_data=True,
)
class DecisionTreeClassifierCalculator(SklearnCalculator):
    """Decision Tree Classifier Calculator."""

    def __init__(self):
        """Bind ``DecisionTreeClassifier`` with sklearn's own unpruned defaults.

        ``max_depth=None`` grows the tree until its leaves are pure or fall
        below ``min_samples_split=2``, so unlike the forest and boosting nodes
        in this module this one ships no regularization of its own.
        """
        super().__init__(
            model_class=DecisionTreeClassifier,
            default_params={
                "max_depth": None,
                "min_samples_split": 2,
                "criterion": "gini",
            },
            problem_type="classification",
        )

__init__()

Bind DecisionTreeClassifier with sklearn's own unpruned defaults.

max_depth=None grows the tree until its leaves are pure or fall below min_samples_split=2, so unlike the forest and boosting nodes in this module this one ships no regularization of its own.

Source code in skyulf-core/skyulf/modeling/classification.py
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
def __init__(self):
    """Bind ``DecisionTreeClassifier`` with sklearn's own unpruned defaults.

    ``max_depth=None`` grows the tree until its leaves are pure or fall
    below ``min_samples_split=2``, so unlike the forest and boosting nodes
    in this module this one ships no regularization of its own.
    """
    super().__init__(
        model_class=DecisionTreeClassifier,
        default_params={
            "max_depth": None,
            "min_samples_split": 2,
            "criterion": "gini",
        },
        problem_type="classification",
    )

ExtraTreesClassifierApplier

Bases: SklearnApplier

Extra Trees Classifier Applier.

Source code in skyulf-core/skyulf/modeling/classification.py
675
676
class ExtraTreesClassifierApplier(SklearnApplier):
    """Extra Trees Classifier Applier."""

ExtraTreesClassifierCalculator

Bases: SklearnCalculator

Extra Trees Classifier Calculator.

Source code in skyulf-core/skyulf/modeling/classification.py
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
@NodeRegistry.register("extra_trees_classifier", ExtraTreesClassifierApplier)
@node_meta(
    id="extra_trees_classifier",
    name="Extra Trees Classifier",
    category="Modeling",
    description="Extremely randomised trees — faster than Random Forest, often comparably accurate.",
    params={"n_estimators": 100, "max_depth": None, "min_samples_split": 2},
    tags=["classification"],
    learns_from_data=True,
)
class ExtraTreesClassifierCalculator(SklearnCalculator):
    """Extra Trees Classifier Calculator."""

    def __init__(self):
        """Bind ``ExtraTreesClassifier`` with 100 unpruned, non-bootstrapped trees.

        ``bootstrap=False`` is the distinguishing default: extra-trees randomizes
        split thresholds instead of resampling rows, so every tree sees the whole
        sample. The remaining settings match sklearn's defaults, and
        ``n_jobs=-1`` fits across every core.
        """
        super().__init__(
            model_class=ExtraTreesClassifier,
            default_params={
                "n_estimators": 100,
                "max_depth": None,
                "min_samples_split": 2,
                "min_samples_leaf": 1,
                "criterion": "gini",
                "bootstrap": False,
                "n_jobs": -1,
            },
            problem_type="classification",
        )

__init__()

Bind ExtraTreesClassifier with 100 unpruned, non-bootstrapped trees.

bootstrap=False is the distinguishing default: extra-trees randomizes split thresholds instead of resampling rows, so every tree sees the whole sample. The remaining settings match sklearn's defaults, and n_jobs=-1 fits across every core.

Source code in skyulf-core/skyulf/modeling/classification.py
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
def __init__(self):
    """Bind ``ExtraTreesClassifier`` with 100 unpruned, non-bootstrapped trees.

    ``bootstrap=False`` is the distinguishing default: extra-trees randomizes
    split thresholds instead of resampling rows, so every tree sees the whole
    sample. The remaining settings match sklearn's defaults, and
    ``n_jobs=-1`` fits across every core.
    """
    super().__init__(
        model_class=ExtraTreesClassifier,
        default_params={
            "n_estimators": 100,
            "max_depth": None,
            "min_samples_split": 2,
            "min_samples_leaf": 1,
            "criterion": "gini",
            "bootstrap": False,
            "n_jobs": -1,
        },
        problem_type="classification",
    )

GaussianNBApplier

Bases: SklearnApplier

Gaussian Naive Bayes Applier.

Source code in skyulf-core/skyulf/modeling/classification.py
871
872
class GaussianNBApplier(SklearnApplier):
    """Gaussian Naive Bayes Applier."""

GaussianNBCalculator

Bases: SklearnCalculator

Gaussian Naive Bayes Calculator.

Source code in skyulf-core/skyulf/modeling/classification.py
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
@NodeRegistry.register("gaussian_nb", GaussianNBApplier)
@node_meta(
    id="gaussian_nb",
    name="Gaussian Naive Bayes",
    category="Modeling",
    description="Gaussian Naive Bayes (GaussianNB).",
    params={"var_smoothing": 1e-9},
    tags=["classification"],
    learns_from_data=True,
)
class GaussianNBCalculator(SklearnCalculator):
    """Gaussian Naive Bayes Calculator."""

    def __init__(self):
        """Bind ``GaussianNB``, whose only knob is ``var_smoothing=1e-9``.

        Naive Bayes estimates each feature's variance from the training data, so
        there is little to configure up front. ``var_smoothing`` is sklearn's own
        default: the portion of the largest variance of all features added to
        every variance for numerical stability.
        """
        super().__init__(
            model_class=GaussianNB,
            default_params={"var_smoothing": 1e-9},
            problem_type="classification",
        )

__init__()

Bind GaussianNB, whose only knob is var_smoothing=1e-9.

Naive Bayes estimates each feature's variance from the training data, so there is little to configure up front. var_smoothing is sklearn's own default: the portion of the largest variance of all features added to every variance for numerical stability.

Source code in skyulf-core/skyulf/modeling/classification.py
888
889
890
891
892
893
894
895
896
897
898
899
900
def __init__(self):
    """Bind ``GaussianNB``, whose only knob is ``var_smoothing=1e-9``.

    Naive Bayes estimates each feature's variance from the training data, so
    there is little to configure up front. ``var_smoothing`` is sklearn's own
    default: the portion of the largest variance of all features added to
    every variance for numerical stability.
    """
    super().__init__(
        model_class=GaussianNB,
        default_params={"var_smoothing": 1e-9},
        problem_type="classification",
    )

GradientBoostingClassifierApplier

Bases: SklearnApplier

Gradient Boosting Classifier Applier.

Source code in skyulf-core/skyulf/modeling/classification.py
547
548
class GradientBoostingClassifierApplier(SklearnApplier):
    """Gradient Boosting Classifier Applier."""

GradientBoostingClassifierCalculator

Bases: SklearnCalculator

Gradient Boosting Classifier Calculator.

Source code in skyulf-core/skyulf/modeling/classification.py
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
@NodeRegistry.register("gradient_boosting_classifier", GradientBoostingClassifierApplier)
@node_meta(
    id="gradient_boosting_classifier",
    name="Gradient Boosting Classifier",
    category="Modeling",
    description="Gradient Boosting for classification.",
    params={"n_estimators": 100, "learning_rate": 0.1, "max_depth": 3},
    tags=["classification"],
    learns_from_data=True,
)
class GradientBoostingClassifierCalculator(SklearnCalculator):
    """Gradient Boosting Classifier Calculator."""

    def __init__(self):
        """Bind ``GradientBoostingClassifier`` with 100 depth-3 trees at rate 0.1.

        All three are exactly sklearn's own defaults, restated so the node's
        tunable surface is explicit; ``fit``'s config overrides any of them.
        """
        super().__init__(
            model_class=GradientBoostingClassifier,
            default_params={
                "n_estimators": 100,
                "learning_rate": 0.1,
                "max_depth": 3,
            },
            problem_type="classification",
        )

__init__()

Bind GradientBoostingClassifier with 100 depth-3 trees at rate 0.1.

All three are exactly sklearn's own defaults, restated so the node's tunable surface is explicit; fit's config overrides any of them.

Source code in skyulf-core/skyulf/modeling/classification.py
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
def __init__(self):
    """Bind ``GradientBoostingClassifier`` with 100 depth-3 trees at rate 0.1.

    All three are exactly sklearn's own defaults, restated so the node's
    tunable surface is explicit; ``fit``'s config overrides any of them.
    """
    super().__init__(
        model_class=GradientBoostingClassifier,
        default_params={
            "n_estimators": 100,
            "learning_rate": 0.1,
            "max_depth": 3,
        },
        problem_type="classification",
    )

HistGradientBoostingClassifierApplier

Bases: SklearnApplier

HistGradientBoosting Classifier Applier.

Source code in skyulf-core/skyulf/modeling/classification.py
716
717
class HistGradientBoostingClassifierApplier(SklearnApplier):
    """HistGradientBoosting Classifier Applier."""

HistGradientBoostingClassifierCalculator

Bases: SklearnCalculator

HistGradientBoosting Classifier Calculator.

Source code in skyulf-core/skyulf/modeling/classification.py
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
@NodeRegistry.register("hist_gradient_boosting_classifier", HistGradientBoostingClassifierApplier)
@node_meta(
    id="hist_gradient_boosting_classifier",
    name="Hist Gradient Boosting Classifier",
    category="Modeling",
    description="Histogram-based gradient boosting — sklearn's fast LightGBM-style implementation.",
    params={"max_iter": 100, "learning_rate": 0.1, "max_leaf_nodes": 31},
    tags=["classification"],
    learns_from_data=True,
)
class HistGradientBoostingClassifierCalculator(SklearnCalculator):
    """HistGradientBoosting Classifier Calculator."""

    def __init__(self):
        """Bind ``HistGradientBoostingClassifier`` with sklearn's own defaults.

        Tree shape is governed leaf-wise — ``max_leaf_nodes=31`` with
        ``max_depth=None`` — because this estimator bins features into
        ``max_bins=255`` buckets and grows by best leaf rather than level by
        level. That is the opposite of the level-wise boosting nodes elsewhere
        in this module.
        """
        super().__init__(
            model_class=HistGradientBoostingClassifier,
            default_params={
                "max_iter": 100,
                "learning_rate": 0.1,
                "max_leaf_nodes": 31,
                "max_depth": None,
                "min_samples_leaf": 20,
                "l2_regularization": 0.0,
                "max_bins": 255,
            },
            problem_type="classification",
        )

__init__()

Bind HistGradientBoostingClassifier with sklearn's own defaults.

Tree shape is governed leaf-wise — max_leaf_nodes=31 with max_depth=None — because this estimator bins features into max_bins=255 buckets and grows by best leaf rather than level by level. That is the opposite of the level-wise boosting nodes elsewhere in this module.

Source code in skyulf-core/skyulf/modeling/classification.py
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
def __init__(self):
    """Bind ``HistGradientBoostingClassifier`` with sklearn's own defaults.

    Tree shape is governed leaf-wise — ``max_leaf_nodes=31`` with
    ``max_depth=None`` — because this estimator bins features into
    ``max_bins=255`` buckets and grows by best leaf rather than level by
    level. That is the opposite of the level-wise boosting nodes elsewhere
    in this module.
    """
    super().__init__(
        model_class=HistGradientBoostingClassifier,
        default_params={
            "max_iter": 100,
            "learning_rate": 0.1,
            "max_leaf_nodes": 31,
            "max_depth": None,
            "min_samples_leaf": 20,
            "l2_regularization": 0.0,
            "max_bins": 255,
        },
        problem_type="classification",
    )

KNeighborsClassifierApplier

Bases: SklearnApplier

K-Neighbors Classifier Applier.

Source code in skyulf-core/skyulf/modeling/classification.py
474
475
class KNeighborsClassifierApplier(SklearnApplier):
    """K-Neighbors Classifier Applier."""

KNeighborsClassifierCalculator

Bases: SklearnCalculator

K-Neighbors Classifier Calculator.

Source code in skyulf-core/skyulf/modeling/classification.py
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
@NodeRegistry.register("k_neighbors_classifier", KNeighborsClassifierApplier)
@node_meta(
    id="k_neighbors_classifier",
    name="K-Neighbors Classifier",
    category="Modeling",
    description="Classifier implementing the k-nearest neighbors vote.",
    params={"n_neighbors": 5, "weights": "uniform", "algorithm": "auto"},
    tags=["requires_scaling", "classification"],
    learns_from_data=True,
)
class KNeighborsClassifierCalculator(SklearnCalculator):
    """K-Neighbors Classifier Calculator."""

    def __init__(self):
        """Bind ``KNeighborsClassifier`` with an unweighted 5-neighbour vote.

        ``algorithm="auto"`` leaves sklearn to pick the neighbour-search
        structure from the data's shape and size, and ``n_jobs=-1`` parallelizes
        the queries.
        """
        super().__init__(
            model_class=KNeighborsClassifier,
            default_params={
                "n_neighbors": 5,
                "weights": "uniform",
                "algorithm": "auto",
                "n_jobs": -1,
            },
            problem_type="classification",
        )

__init__()

Bind KNeighborsClassifier with an unweighted 5-neighbour vote.

algorithm="auto" leaves sklearn to pick the neighbour-search structure from the data's shape and size, and n_jobs=-1 parallelizes the queries.

Source code in skyulf-core/skyulf/modeling/classification.py
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
def __init__(self):
    """Bind ``KNeighborsClassifier`` with an unweighted 5-neighbour vote.

    ``algorithm="auto"`` leaves sklearn to pick the neighbour-search
    structure from the data's shape and size, and ``n_jobs=-1`` parallelizes
    the queries.
    """
    super().__init__(
        model_class=KNeighborsClassifier,
        default_params={
            "n_neighbors": 5,
            "weights": "uniform",
            "algorithm": "auto",
            "n_jobs": -1,
        },
        problem_type="classification",
    )

LGBMClassifierApplier

Bases: SklearnApplier

LightGBM Classifier Applier.

LightGBM 4.x sets feature_names_in_ to auto-generated names (Column_0, Column_1...) even when fit with numpy arrays, and the property's deleter is intentionally a no-op (see upstream source). That triggers sklearn's UserWarning: X does not have valid feature names on every predict call. We suppress it locally here so the warning never leaks out of the applier boundary.

Source code in skyulf-core/skyulf/modeling/classification.py
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
class LGBMClassifierApplier(SklearnApplier):
    """LightGBM Classifier Applier.

    LightGBM 4.x sets ``feature_names_in_`` to auto-generated names
    (``Column_0``, ``Column_1``...) even when fit with numpy arrays, and the
    property's deleter is intentionally a no-op (see upstream source). That
    triggers sklearn's ``UserWarning: X does not have valid feature names``
    on every predict call. We suppress it locally here so the warning never
    leaks out of the applier boundary.
    """

    def predict(self, df, model_artifact):
        """Delegate to the base predict with the feature-name warning suppressed."""
        with warnings.catch_warnings():
            warnings.filterwarnings("ignore", message=".*valid feature names.*")
            return super().predict(df, model_artifact)

    def predict_proba(self, df, model_artifact):
        """Delegate to the base predict_proba with the same warning suppressed."""
        with warnings.catch_warnings():
            warnings.filterwarnings("ignore", message=".*valid feature names.*")
            return super().predict_proba(df, model_artifact)

predict(df, model_artifact)

Delegate to the base predict with the feature-name warning suppressed.

Source code in skyulf-core/skyulf/modeling/classification.py
778
779
780
781
782
def predict(self, df, model_artifact):
    """Delegate to the base predict with the feature-name warning suppressed."""
    with warnings.catch_warnings():
        warnings.filterwarnings("ignore", message=".*valid feature names.*")
        return super().predict(df, model_artifact)

predict_proba(df, model_artifact)

Delegate to the base predict_proba with the same warning suppressed.

Source code in skyulf-core/skyulf/modeling/classification.py
784
785
786
787
788
def predict_proba(self, df, model_artifact):
    """Delegate to the base predict_proba with the same warning suppressed."""
    with warnings.catch_warnings():
        warnings.filterwarnings("ignore", message=".*valid feature names.*")
        return super().predict_proba(df, model_artifact)

LGBMClassifierCalculator

Bases: SklearnCalculator

LightGBM Classifier Calculator.

Source code in skyulf-core/skyulf/modeling/classification.py
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
@NodeRegistry.register("lgbm_classifier", LGBMClassifierApplier)
@node_meta(
    id="lgbm_classifier",
    name="LightGBM Classifier",
    category="Modeling",
    description="LightGBM: leaf-wise gradient boosting for numeric features; encode categorical inputs first.",
    params={"n_estimators": 100, "num_leaves": 31, "learning_rate": 0.1},
    tags=["classification"],
    learns_from_data=True,
)
class LGBMClassifierCalculator(SklearnCalculator):
    """LightGBM Classifier Calculator."""

    def __init__(self):
        """Bind ``LGBMClassifier`` with an unregularized leaf-wise configuration.

        ``max_depth=-1`` leaves depth unbounded, so ``num_leaves=31`` alone
        governs tree shape, and both subsampling rates and both L1/L2
        penalties start at their neutral values. Automatic ``subsample_freq``
        enables per-round bagging when the fraction is below one, except
        for GOSS, which keeps its gradient sampling. ``verbose`` and
        ``verbosity`` are pinned to -1 to quiet LightGBM's native logging
        alongside the no-op logger registered at import time.
        """
        super().__init__(
            model_class=_SamplingLGBMClassifier,
            default_params={
                "n_estimators": 100,
                "num_leaves": 31,
                "learning_rate": 0.1,
                "max_depth": -1,
                "min_child_samples": 20,
                "subsample": 1.0,
                "subsample_freq": None,
                "colsample_bytree": 1.0,
                "reg_alpha": 0.0,
                "reg_lambda": 0.0,
                "boosting_type": "gbdt",
                "n_jobs": -1,
                "verbose": -1,
                "verbosity": -1,
            },
            problem_type="classification",
        )

    def fit(
        self,
        X,
        y,
        config,
        progress_callback=None,
        log_callback=None,
        validation_data=None,
        iteration_callback=None,
        *,
        sample_weight: Any = None,
    ):
        """Delegate to the base fit with the feature-name warning suppressed."""
        with warnings.catch_warnings():
            warnings.filterwarnings("ignore", message=".*valid feature names.*")
            return super().fit(
                X,
                y,
                config,
                progress_callback=progress_callback,
                log_callback=log_callback,
                validation_data=validation_data,
                iteration_callback=iteration_callback,
                sample_weight=sample_weight,
            )

    def _boosting_fit_kwargs(self, model, X_np, y_np, iteration_callback):
        if iteration_callback is None:
            return {}
        return {
            "eval_set": [(X_np, y_np)],
            "callbacks": [LightGBMIterationAdapter(iteration_callback)],
        }

__init__()

Bind LGBMClassifier with an unregularized leaf-wise configuration.

max_depth=-1 leaves depth unbounded, so num_leaves=31 alone governs tree shape, and both subsampling rates and both L1/L2 penalties start at their neutral values. Automatic subsample_freq enables per-round bagging when the fraction is below one, except for GOSS, which keeps its gradient sampling. verbose and verbosity are pinned to -1 to quiet LightGBM's native logging alongside the no-op logger registered at import time.

Source code in skyulf-core/skyulf/modeling/classification.py
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
def __init__(self):
    """Bind ``LGBMClassifier`` with an unregularized leaf-wise configuration.

    ``max_depth=-1`` leaves depth unbounded, so ``num_leaves=31`` alone
    governs tree shape, and both subsampling rates and both L1/L2
    penalties start at their neutral values. Automatic ``subsample_freq``
    enables per-round bagging when the fraction is below one, except
    for GOSS, which keeps its gradient sampling. ``verbose`` and
    ``verbosity`` are pinned to -1 to quiet LightGBM's native logging
    alongside the no-op logger registered at import time.
    """
    super().__init__(
        model_class=_SamplingLGBMClassifier,
        default_params={
            "n_estimators": 100,
            "num_leaves": 31,
            "learning_rate": 0.1,
            "max_depth": -1,
            "min_child_samples": 20,
            "subsample": 1.0,
            "subsample_freq": None,
            "colsample_bytree": 1.0,
            "reg_alpha": 0.0,
            "reg_lambda": 0.0,
            "boosting_type": "gbdt",
            "n_jobs": -1,
            "verbose": -1,
            "verbosity": -1,
        },
        problem_type="classification",
    )

fit(X, y, config, progress_callback=None, log_callback=None, validation_data=None, iteration_callback=None, *, sample_weight=None)

Delegate to the base fit with the feature-name warning suppressed.

Source code in skyulf-core/skyulf/modeling/classification.py
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
def fit(
    self,
    X,
    y,
    config,
    progress_callback=None,
    log_callback=None,
    validation_data=None,
    iteration_callback=None,
    *,
    sample_weight: Any = None,
):
    """Delegate to the base fit with the feature-name warning suppressed."""
    with warnings.catch_warnings():
        warnings.filterwarnings("ignore", message=".*valid feature names.*")
        return super().fit(
            X,
            y,
            config,
            progress_callback=progress_callback,
            log_callback=log_callback,
            validation_data=validation_data,
            iteration_callback=iteration_callback,
            sample_weight=sample_weight,
        )

LogisticRegressionApplier

Bases: SklearnApplier

Logistic Regression Applier.

Source code in skyulf-core/skyulf/modeling/classification.py
66
67
class LogisticRegressionApplier(SklearnApplier):
    """Logistic Regression Applier."""

LogisticRegressionCalculator

Bases: SklearnCalculator

Logistic Regression Calculator.

Source code in skyulf-core/skyulf/modeling/classification.py
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
@NodeRegistry.register("logistic_regression", LogisticRegressionApplier)
@node_meta(
    id="logistic_regression",
    name="Logistic Regression",
    category="Modeling",
    description="Linear model for classification.",
    params={"max_iter": 1000, "solver": "lbfgs"},
    # "text": logistic regression on TF-IDF/vectorized features is a common,
    # well-performing baseline for text classification (alongside Naive Bayes
    # and the SGD-based linear SVM approximation below).
    tags=["requires_scaling", "classification", "text", "nlp"],
    learns_from_data=True,
)
class LogisticRegressionCalculator(SklearnCalculator):
    """Logistic Regression Calculator."""

    # sklearn solver -> penalties it actually supports. Manual/UI configuration
    # allows selecting solver and penalty independently (unlike the tuner's own
    # search space, which restricts solver to "saga" whenever penalty is
    # varied), so an incompatible combination reaches `fit()` unchecked and
    # would otherwise surface as an opaque sklearn ValueError at model-fit time.
    _SOLVER_PENALTIES: ClassVar[dict[str, set[Any]]] = {
        "lbfgs": {"l2", None},
        "liblinear": {"l1", "l2"},
        "newton-cg": {"l2", None},
        "newton-cholesky": {"l2", None},
        "sag": {"l2", None},
        "saga": {"l1", "l2", "elasticnet", None},
    }

    def __init__(self):
        """Bind ``LogisticRegression`` with ``max_iter=1000`` and ``solver="lbfgs"``.

        Both are defaults, not fixtures: ``fit``'s config may override either,
        and the resulting pair is then checked against ``_SOLVER_PENALTIES``.
        """
        super().__init__(
            model_class=LogisticRegression,
            default_params={
                "max_iter": 1000,
                "solver": "lbfgs",
            },
            problem_type="classification",
        )

    def fit(
        self,
        X: Any,
        y: Any,
        config: dict[str, Any],
        progress_callback: Callable[..., Any] | None = None,
        log_callback: Callable[..., Any] | None = None,
        validation_data: Any = None,
        iteration_callback: Callable[..., Any] | None = None,
        *,
        sample_weight: Any = None,
    ) -> Any:
        """Reject an unsupported solver/penalty pair, then delegate to the base fit."""
        self._validate_solver_penalty(config)
        return super().fit(
            X,
            y,
            config,
            progress_callback,
            log_callback,
            validation_data,
            iteration_callback=iteration_callback,
            sample_weight=sample_weight,
        )

    def _resolve_fit_params(self, config: dict[str, Any]) -> dict[str, Any]:
        """Merges fit params, then normalizes ``penalty`` for sklearn >=1.8.

        sklearn >=1.8 deprecates the ``penalty`` constructor arg entirely (in
        favor of ``l1_ratio``/``C``) and will remove it in sklearn 1.10. We
        keep ``penalty`` ("l1"/"l2"/"elasticnet"/None) as our own public
        config/UI field unchanged. Translate it for the newer API while
        retaining the native penalty argument on older sklearn versions.
        """
        params = super()._resolve_fit_params(config)
        return normalize_logistic_regression_params(params)

    @classmethod
    def _extract_solver_penalty_params(cls, config: dict[str, Any] | None) -> dict[str, Any] | None:
        """Returns the params dict from config, or None if unavailable/not a dict."""
        if not config:
            return None
        params = config.get("params", config)
        if not isinstance(params, dict):
            return None
        return params

    @classmethod
    def _raise_incompatible_solver_penalty(cls, solver: Any, penalty: Any) -> None:
        """Raises a ValueError listing solvers compatible with the requested penalty."""
        compatible_solvers = sorted(
            s for s, penalties in cls._SOLVER_PENALTIES.items() if penalty in penalties
        )
        raise ValueError(
            f"Logistic Regression: solver={solver!r} does not support "
            f"penalty={penalty!r}. Solvers compatible with this penalty: "
            f"{compatible_solvers or 'none'}."
        )

    def _validate_solver_penalty(self, config: dict[str, Any] | None) -> None:
        """Raise a clear, actionable error for an invalid solver/penalty pair.

        sklearn's own error for this (e.g. "Solver lbfgs supports only 'l2' or
        None penalties") is only raised deep inside `LogisticRegression.fit`,
        after data has already been split/validated upstream. Failing fast
        here with the full list of compatible solvers is more actionable.

        Validates against the *merged* effective params (``default_params``
        overlaid with the config's overrides), not just the raw config —
        otherwise overriding only ``penalty`` (very common; ``solver``
        defaults to ``"lbfgs"``) would skip validation entirely since
        ``solver`` never appears in the raw override dict, letting an
        incompatible combo reach sklearn's own opaque error at fit time.
        """
        overrides = self._extract_solver_penalty_params(config) or {}
        params = {**self.default_params, **overrides}
        solver = params.get("solver")
        if solver is None or "penalty" not in params:
            return
        penalty = params.get("penalty")
        compatible = self._SOLVER_PENALTIES.get(solver)
        if compatible is not None and penalty not in compatible:
            self._raise_incompatible_solver_penalty(solver, penalty)

__init__()

Bind LogisticRegression with max_iter=1000 and solver="lbfgs".

Both are defaults, not fixtures: fit's config may override either, and the resulting pair is then checked against _SOLVER_PENALTIES.

Source code in skyulf-core/skyulf/modeling/classification.py
100
101
102
103
104
105
106
107
108
109
110
111
112
113
def __init__(self):
    """Bind ``LogisticRegression`` with ``max_iter=1000`` and ``solver="lbfgs"``.

    Both are defaults, not fixtures: ``fit``'s config may override either,
    and the resulting pair is then checked against ``_SOLVER_PENALTIES``.
    """
    super().__init__(
        model_class=LogisticRegression,
        default_params={
            "max_iter": 1000,
            "solver": "lbfgs",
        },
        problem_type="classification",
    )

fit(X, y, config, progress_callback=None, log_callback=None, validation_data=None, iteration_callback=None, *, sample_weight=None)

Reject an unsupported solver/penalty pair, then delegate to the base fit.

Source code in skyulf-core/skyulf/modeling/classification.py
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
def fit(
    self,
    X: Any,
    y: Any,
    config: dict[str, Any],
    progress_callback: Callable[..., Any] | None = None,
    log_callback: Callable[..., Any] | None = None,
    validation_data: Any = None,
    iteration_callback: Callable[..., Any] | None = None,
    *,
    sample_weight: Any = None,
) -> Any:
    """Reject an unsupported solver/penalty pair, then delegate to the base fit."""
    self._validate_solver_penalty(config)
    return super().fit(
        X,
        y,
        config,
        progress_callback,
        log_callback,
        validation_data,
        iteration_callback=iteration_callback,
        sample_weight=sample_weight,
    )

RandomForestClassifierApplier

Bases: SklearnApplier

Random Forest Classifier Applier.

Source code in skyulf-core/skyulf/modeling/classification.py
396
397
class RandomForestClassifierApplier(SklearnApplier):
    """Random Forest Classifier Applier."""

RandomForestClassifierCalculator

Bases: SklearnCalculator

Random Forest Classifier Calculator.

Source code in skyulf-core/skyulf/modeling/classification.py
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
@NodeRegistry.register("random_forest_classifier", RandomForestClassifierApplier)
@node_meta(
    id="random_forest_classifier",
    name="Random Forest Classifier",
    category="Modeling",
    description="Ensemble of decision trees.",
    params={"n_estimators": 50, "max_depth": 10, "min_samples_split": 5},
    tags=["classification"],
    learns_from_data=True,
)
class RandomForestClassifierCalculator(SklearnCalculator):
    """Random Forest Classifier Calculator."""

    def __init__(self):
        """Bind ``RandomForestClassifier`` with 50 shallow, constrained trees.

        ``max_depth=10``, ``min_samples_split=5`` and ``min_samples_leaf=2``
        restrict tree growth that sklearn leaves open by default, and the forest
        is half sklearn's default size. ``n_jobs=-1`` fits across every core.
        All are overridable from ``fit``'s config.
        """
        super().__init__(
            model_class=RandomForestClassifier,
            default_params={
                "n_estimators": 50,
                "max_depth": 10,
                "min_samples_split": 5,
                "min_samples_leaf": 2,
                "n_jobs": -1,
            },
            problem_type="classification",
        )

__init__()

Bind RandomForestClassifier with 50 shallow, constrained trees.

max_depth=10, min_samples_split=5 and min_samples_leaf=2 restrict tree growth that sklearn leaves open by default, and the forest is half sklearn's default size. n_jobs=-1 fits across every core. All are overridable from fit's config.

Source code in skyulf-core/skyulf/modeling/classification.py
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
def __init__(self):
    """Bind ``RandomForestClassifier`` with 50 shallow, constrained trees.

    ``max_depth=10``, ``min_samples_split=5`` and ``min_samples_leaf=2``
    restrict tree growth that sklearn leaves open by default, and the forest
    is half sklearn's default size. ``n_jobs=-1`` fits across every core.
    All are overridable from ``fit``'s config.
    """
    super().__init__(
        model_class=RandomForestClassifier,
        default_params={
            "n_estimators": 50,
            "max_depth": 10,
            "min_samples_split": 5,
            "min_samples_leaf": 2,
            "n_jobs": -1,
        },
        problem_type="classification",
    )

SGDClassifierApplier

Bases: SklearnApplier

Stochastic Gradient Descent Classifier Applier.

Source code in skyulf-core/skyulf/modeling/classification.py
904
905
class SGDClassifierApplier(SklearnApplier):
    """Stochastic Gradient Descent Classifier Applier."""

SGDClassifierCalculator

Bases: SklearnCalculator

SGD Classifier Calculator.

Source code in skyulf-core/skyulf/modeling/classification.py
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
@NodeRegistry.register("sgd_classifier", SGDClassifierApplier)
@node_meta(
    id="sgd_classifier",
    name="SGD Classifier (text / linear)",
    category="Modeling",
    description=(
        "Linear classifiers (SVM, logistic regression, etc.) with SGD training. "
        "Highly efficient for high-dimensional sparse/dense text representations "
        "and large datasets."
    ),
    params={
        "loss": "log_loss",
        "penalty": "l2",
        "alpha": 0.0001,
        "l1_ratio": 0.15,
        "max_iter": 1000,
    },
    # Text-classification-scoped only (no "classification" tag): SGD with
    # hinge/log loss is a fast linear-SVM/logistic-regression approximation
    # that excels on sparse high-dimensional text features (TF-IDF/counts),
    # so it's offered via the Text Classification node rather than the
    # general Classification node, which already has logistic_regression and
    # other dense-feature-friendly linear models covering that role.
    tags=["text", "nlp", "linear", "requires_scaling"],
    learns_from_data=True,
)
class SGDClassifierCalculator(SklearnCalculator):
    """SGD Classifier Calculator."""

    def __init__(self):
        """Bind ``SGDClassifier`` as a logistic-regression approximation.

        ``loss="log_loss"`` replaces sklearn's default ``"hinge"``, and that is
        what makes ``predict_proba`` available to the applier — a hinge loss
        yields a linear SVM with no probability output. ``l1_ratio=0.15`` stays
        inert unless ``penalty`` is switched to ``"elasticnet"``.
        """
        super().__init__(
            model_class=SGDClassifier,
            default_params={
                "loss": "log_loss",
                "penalty": "l2",
                "alpha": 0.0001,
                "l1_ratio": 0.15,
                "max_iter": 1000,
            },
            problem_type="classification",
        )

__init__()

Bind SGDClassifier as a logistic-regression approximation.

loss="log_loss" replaces sklearn's default "hinge", and that is what makes predict_proba available to the applier — a hinge loss yields a linear SVM with no probability output. l1_ratio=0.15 stays inert unless penalty is switched to "elasticnet".

Source code in skyulf-core/skyulf/modeling/classification.py
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
def __init__(self):
    """Bind ``SGDClassifier`` as a logistic-regression approximation.

    ``loss="log_loss"`` replaces sklearn's default ``"hinge"``, and that is
    what makes ``predict_proba`` available to the applier — a hinge loss
    yields a linear SVM with no probability output. ``l1_ratio=0.15`` stays
    inert unless ``penalty`` is switched to ``"elasticnet"``.
    """
    super().__init__(
        model_class=SGDClassifier,
        default_params={
            "loss": "log_loss",
            "penalty": "l2",
            "alpha": 0.0001,
            "l1_ratio": 0.15,
            "max_iter": 1000,
        },
        problem_type="classification",
    )

SVCApplier

Bases: SklearnApplier

SVC Applier.

Source code in skyulf-core/skyulf/modeling/classification.py
435
436
class SVCApplier(SklearnApplier):
    """SVC Applier."""

SVCCalculator

Bases: SklearnCalculator

SVC Calculator.

Source code in skyulf-core/skyulf/modeling/classification.py
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
@NodeRegistry.register("svc", SVCApplier)
@node_meta(
    id="svc",
    name="Support Vector Classifier",
    category="Modeling",
    description="C-Support Vector Classification.",
    params={"C": 1.0, "kernel": "rbf", "gamma": "scale"},
    tags=["requires_scaling", "classification"],
    learns_from_data=True,
)
class SVCCalculator(SklearnCalculator):
    """SVC Calculator."""

    def __init__(self):
        """Bind ``SVC`` with an RBF kernel and probability estimates switched on.

        ``probability=True`` is the load-bearing default — without it the
        applier would have no ``predict_proba`` to call — and it costs extra
        cross-validation at fit time, because Platt scaling is fitted on top of
        the margins. ``C=1.0``, ``kernel="rbf"`` and ``gamma="scale"`` are
        sklearn's own defaults.
        """
        super().__init__(
            model_class=SVC,
            default_params={
                "C": 1.0,
                "kernel": "rbf",
                "gamma": "scale",
                "probability": True,
            },
            problem_type="classification",
        )

__init__()

Bind SVC with an RBF kernel and probability estimates switched on.

probability=True is the load-bearing default — without it the applier would have no predict_proba to call — and it costs extra cross-validation at fit time, because Platt scaling is fitted on top of the margins. C=1.0, kernel="rbf" and gamma="scale" are sklearn's own defaults.

Source code in skyulf-core/skyulf/modeling/classification.py
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
def __init__(self):
    """Bind ``SVC`` with an RBF kernel and probability estimates switched on.

    ``probability=True`` is the load-bearing default — without it the
    applier would have no ``predict_proba`` to call — and it costs extra
    cross-validation at fit time, because Platt scaling is fitted on top of
    the margins. ``C=1.0``, ``kernel="rbf"`` and ``gamma="scale"`` are
    sklearn's own defaults.
    """
    super().__init__(
        model_class=SVC,
        default_params={
            "C": 1.0,
            "kernel": "rbf",
            "gamma": "scale",
            "probability": True,
        },
        problem_type="classification",
    )

XGBClassifierApplier

Bases: SklearnApplier

XGBoost Classifier Applier.

Source code in skyulf-core/skyulf/modeling/classification.py
619
620
class XGBClassifierApplier(SklearnApplier):
    """XGBoost Classifier Applier."""

XGBClassifierCalculator

Bases: SklearnCalculator

XGBoost Classifier Calculator.

Source code in skyulf-core/skyulf/modeling/classification.py
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
@NodeRegistry.register("xgboost_classifier", XGBClassifierApplier)
@node_meta(
    id="xgboost_classifier",
    name="XGBoost Classifier",
    category="Modeling",
    description="Extreme Gradient Boosting classifier.",
    params={"n_estimators": 100, "max_depth": 6, "learning_rate": 0.3},
    tags=["classification"],
    learns_from_data=True,
)
class XGBClassifierCalculator(SklearnCalculator):
    """XGBoost Classifier Calculator."""

    def __init__(self):
        """Bind ``XGBClassifier`` with XGBoost's defaults plus row/column subsampling.

        ``n_estimators=100``, ``max_depth=6`` and ``learning_rate=0.3`` are
        XGBoost's own. ``subsample`` and ``colsample_bytree`` are pulled
        down from XGBoost's 1.0 to 0.8, and neither appears in the node's
        ``params`` metadata, so they are only reachable through ``fit``'s
        config.
        """
        super().__init__(
            model_class=XGBClassifier,
            default_params={
                "n_estimators": 100,
                "max_depth": 6,
                "learning_rate": 0.3,
                "subsample": 0.8,
                "colsample_bytree": 0.8,
                "n_jobs": -1,
            },
            problem_type="classification",
        )

    def _boosting_fit_kwargs(self, model, X_np, y_np, iteration_callback):
        if iteration_callback is None or XgboostIterationAdapter is None:
            return {}
        # XGBoost 3.x reads callbacks from the estimator itself (they were
        # removed from fit()). eval_set is display-only (no early
        # stopping), so the trained model is identical to a plain fit — it
        # just streams per-round training loss for the live chart.
        model.callbacks = [
            XgboostIterationAdapter(iteration_callback, total=int(model.n_estimators))
        ]
        return {
            "eval_set": [(X_np, y_np)],
            "verbose": False,
            "_detach_callbacks": True,
        }

__init__()

Bind XGBClassifier with XGBoost's defaults plus row/column subsampling.

n_estimators=100, max_depth=6 and learning_rate=0.3 are XGBoost's own. subsample and colsample_bytree are pulled down from XGBoost's 1.0 to 0.8, and neither appears in the node's params metadata, so they are only reachable through fit's config.

Source code in skyulf-core/skyulf/modeling/classification.py
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
def __init__(self):
    """Bind ``XGBClassifier`` with XGBoost's defaults plus row/column subsampling.

    ``n_estimators=100``, ``max_depth=6`` and ``learning_rate=0.3`` are
    XGBoost's own. ``subsample`` and ``colsample_bytree`` are pulled
    down from XGBoost's 1.0 to 0.8, and neither appears in the node's
    ``params`` metadata, so they are only reachable through ``fit``'s
    config.
    """
    super().__init__(
        model_class=XGBClassifier,
        default_params={
            "n_estimators": 100,
            "max_depth": 6,
            "learning_rate": 0.3,
            "subsample": 0.8,
            "colsample_bytree": 0.8,
            "n_jobs": -1,
        },
        problem_type="classification",
    )