CoolFace
Apppublic

Aluode/PerceptionLabPortable

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
naive_bayes.py1541 linesDownload Raw Back to sklearn
1"""Naive Bayes algorithms.
2
3These are supervised learning methods based on applying Bayes' theorem with strong
4(naive) feature independence assumptions.
5"""
6
7# Authors: The scikit-learn developers
8# SPDX-License-Identifier: BSD-3-Clause
9
10import warnings
11from abc import ABCMeta, abstractmethod
12from numbers import Integral, Real
13
14import numpy as np
15from scipy.special import logsumexp
16
17from .base import (
18    BaseEstimator,
19    ClassifierMixin,
20    _fit_context,
21)
22from .preprocessing import LabelBinarizer, binarize, label_binarize
23from .utils._param_validation import Interval
24from .utils.extmath import safe_sparse_dot
25from .utils.multiclass import _check_partial_fit_first_call
26from .utils.validation import (
27    _check_n_features,
28    _check_sample_weight,
29    check_is_fitted,
30    check_non_negative,
31    validate_data,
32)
33
34__all__ = [
35    "BernoulliNB",
36    "CategoricalNB",
37    "ComplementNB",
38    "GaussianNB",
39    "MultinomialNB",
40]
41
42
43class _BaseNB(ClassifierMixin, BaseEstimator, metaclass=ABCMeta):
44    """Abstract base class for naive Bayes estimators"""
45
46    @abstractmethod
47    def _joint_log_likelihood(self, X):
48        """Compute the unnormalized posterior log probability of X
49
50        I.e. ``log P(c) + log P(x|c)`` for all rows x of X, as an array-like of
51        shape (n_samples, n_classes).
52
53        Public methods predict, predict_proba, predict_log_proba, and
54        predict_joint_log_proba pass the input through _check_X before handing it
55        over to _joint_log_likelihood. The term "joint log likelihood" is used
56        interchangibly with "joint log probability".
57        """
58
59    @abstractmethod
60    def _check_X(self, X):
61        """To be overridden in subclasses with the actual checks.
62
63        Only used in predict* methods.
64        """
65
66    def predict_joint_log_proba(self, X):
67        """Return joint log probability estimates for the test vector X.
68
69        For each row x of X and class y, the joint log probability is given by
70        ``log P(x, y) = log P(y) + log P(x|y),``
71        where ``log P(y)`` is the class prior probability and ``log P(x|y)`` is
72        the class-conditional probability.
73
74        Parameters
75        ----------
76        X : array-like of shape (n_samples, n_features)
77            The input samples.
78
79        Returns
80        -------
81        C : ndarray of shape (n_samples, n_classes)
82            Returns the joint log-probability of the samples for each class in
83            the model. The columns correspond to the classes in sorted
84            order, as they appear in the attribute :term:`classes_`.
85        """
86        check_is_fitted(self)
87        X = self._check_X(X)
88        return self._joint_log_likelihood(X)
89
90    def predict(self, X):
91        """
92        Perform classification on an array of test vectors X.
93
94        Parameters
95        ----------
96        X : array-like of shape (n_samples, n_features)
97            The input samples.
98
99        Returns
100        -------
101        C : ndarray of shape (n_samples,)
102            Predicted target values for X.
103        """
104        check_is_fitted(self)
105        X = self._check_X(X)
106        jll = self._joint_log_likelihood(X)
107        return self.classes_[np.argmax(jll, axis=1)]
108
109    def predict_log_proba(self, X):
110        """
111        Return log-probability estimates for the test vector X.
112
113        Parameters
114        ----------
115        X : array-like of shape (n_samples, n_features)
116            The input samples.
117
118        Returns
119        -------
120        C : array-like of shape (n_samples, n_classes)
121            Returns the log-probability of the samples for each class in
122            the model. The columns correspond to the classes in sorted
123            order, as they appear in the attribute :term:`classes_`.
124        """
125        check_is_fitted(self)
126        X = self._check_X(X)
127        jll = self._joint_log_likelihood(X)
128        # normalize by P(x) = P(f_1, ..., f_n)
129        log_prob_x = logsumexp(jll, axis=1)
130        return jll - np.atleast_2d(log_prob_x).T
131
132    def predict_proba(self, X):
133        """
134        Return probability estimates for the test vector X.
135
136        Parameters
137        ----------
138        X : array-like of shape (n_samples, n_features)
139            The input samples.
140
141        Returns
142        -------
143        C : array-like of shape (n_samples, n_classes)
144            Returns the probability of the samples for each class in
145            the model. The columns correspond to the classes in sorted
146            order, as they appear in the attribute :term:`classes_`.
147        """
148        return np.exp(self.predict_log_proba(X))
149
150
151class GaussianNB(_BaseNB):
152    """
153    Gaussian Naive Bayes (GaussianNB).
154
155    Can perform online updates to model parameters via :meth:`partial_fit`.
156    For details on algorithm used to update feature means and variance online,
157    see `Stanford CS tech report STAN-CS-79-773 by Chan, Golub, and LeVeque
158    <http://i.stanford.edu/pub/cstr/reports/cs/tr/79/773/CS-TR-79-773.pdf>`_.
159
160    Read more in the :ref:`User Guide <gaussian_naive_bayes>`.
161
162    Parameters
163    ----------
164    priors : array-like of shape (n_classes,), default=None
165        Prior probabilities of the classes. If specified, the priors are not
166        adjusted according to the data.
167
168    var_smoothing : float, default=1e-9
169        Portion of the largest variance of all features that is added to
170        variances for calculation stability.
171
172        .. versionadded:: 0.20
173
174    Attributes
175    ----------
176    class_count_ : ndarray of shape (n_classes,)
177        number of training samples observed in each class.
178
179    class_prior_ : ndarray of shape (n_classes,)
180        probability of each class.
181
182    classes_ : ndarray of shape (n_classes,)
183        class labels known to the classifier.
184
185    epsilon_ : float
186        absolute additive value to variances.
187
188    n_features_in_ : int
189        Number of features seen during :term:`fit`.
190
191        .. versionadded:: 0.24
192
193    feature_names_in_ : ndarray of shape (`n_features_in_`,)
194        Names of features seen during :term:`fit`. Defined only when `X`
195        has feature names that are all strings.
196
197        .. versionadded:: 1.0
198
199    var_ : ndarray of shape (n_classes, n_features)
200        Variance of each feature per class.
201
202        .. versionadded:: 1.0
203
204    theta_ : ndarray of shape (n_classes, n_features)
205        mean of each feature per class.
206
207    See Also
208    --------
209    BernoulliNB : Naive Bayes classifier for multivariate Bernoulli models.
210    CategoricalNB : Naive Bayes classifier for categorical features.
211    ComplementNB : Complement Naive Bayes classifier.
212    MultinomialNB : Naive Bayes classifier for multinomial models.
213
214    Examples
215    --------
216    >>> import numpy as np
217    >>> X = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])
218    >>> Y = np.array([1, 1, 1, 2, 2, 2])
219    >>> from sklearn.naive_bayes import GaussianNB
220    >>> clf = GaussianNB()
221    >>> clf.fit(X, Y)
222    GaussianNB()
223    >>> print(clf.predict([[-0.8, -1]]))
224    [1]
225    >>> clf_pf = GaussianNB()
226    >>> clf_pf.partial_fit(X, Y, np.unique(Y))
227    GaussianNB()
228    >>> print(clf_pf.predict([[-0.8, -1]]))
229    [1]
230    """
231
232    _parameter_constraints: dict = {
233        "priors": ["array-like", None],
234        "var_smoothing": [Interval(Real, 0, None, closed="left")],
235    }
236
237    def __init__(self, *, priors=None, var_smoothing=1e-9):
238        self.priors = priors
239        self.var_smoothing = var_smoothing
240
241    @_fit_context(prefer_skip_nested_validation=True)
242    def fit(self, X, y, sample_weight=None):
243        """Fit Gaussian Naive Bayes according to X, y.
244
245        Parameters
246        ----------
247        X : array-like of shape (n_samples, n_features)
248            Training vectors, where `n_samples` is the number of samples
249            and `n_features` is the number of features.
250
251        y : array-like of shape (n_samples,)
252            Target values.
253
254        sample_weight : array-like of shape (n_samples,), default=None
255            Weights applied to individual samples (1. for unweighted).
256
257            .. versionadded:: 0.17
258               Gaussian Naive Bayes supports fitting with *sample_weight*.
259
260        Returns
261        -------
262        self : object
263            Returns the instance itself.
264        """
265        y = validate_data(self, y=y)
266        return self._partial_fit(
267            X, y, np.unique(y), _refit=True, sample_weight=sample_weight
268        )
269
270    def _check_X(self, X):
271        """Validate X, used only in predict* methods."""
272        return validate_data(self, X, reset=False)
273
274    @staticmethod
275    def _update_mean_variance(n_past, mu, var, X, sample_weight=None):
276        """Compute online update of Gaussian mean and variance.
277
278        Given starting sample count, mean, and variance, a new set of
279        points X, and optionally sample weights, return the updated mean and
280        variance. (NB - each dimension (column) in X is treated as independent
281        -- you get variance, not covariance).
282
283        Can take scalar mean and variance, or vector mean and variance to
284        simultaneously update a number of independent Gaussians.
285
286        See Stanford CS tech report STAN-CS-79-773 by Chan, Golub, and LeVeque:
287
288        http://i.stanford.edu/pub/cstr/reports/cs/tr/79/773/CS-TR-79-773.pdf
289
290        Parameters
291        ----------
292        n_past : int
293            Number of samples represented in old mean and variance. If sample
294            weights were given, this should contain the sum of sample
295            weights represented in old mean and variance.
296
297        mu : array-like of shape (number of Gaussians,)
298            Means for Gaussians in original set.
299
300        var : array-like of shape (number of Gaussians,)
301            Variances for Gaussians in original set.
302
303        sample_weight : array-like of shape (n_samples,), default=None
304            Weights applied to individual samples (1. for unweighted).
305
306        Returns
307        -------
308        total_mu : array-like of shape (number of Gaussians,)
309            Updated mean for each Gaussian over the combined set.
310
311        total_var : array-like of shape (number of Gaussians,)
312            Updated variance for each Gaussian over the combined set.
313        """
314        if X.shape[0] == 0:
315            return mu, var
316
317        # Compute (potentially weighted) mean and variance of new datapoints
318        if sample_weight is not None:
319            n_new = float(sample_weight.sum())
320            if np.isclose(n_new, 0.0):
321                return mu, var
322            new_mu = np.average(X, axis=0, weights=sample_weight)
323            new_var = np.average((X - new_mu) ** 2, axis=0, weights=sample_weight)
324        else:
325            n_new = X.shape[0]
326            new_var = np.var(X, axis=0)
327            new_mu = np.mean(X, axis=0)
328
329        if n_past == 0:
330            return new_mu, new_var
331
332        n_total = float(n_past + n_new)
333
334        # Combine mean of old and new data, taking into consideration
335        # (weighted) number of observations
336        total_mu = (n_new * new_mu + n_past * mu) / n_total
337
338        # Combine variance of old and new data, taking into consideration
339        # (weighted) number of observations. This is achieved by combining
340        # the sum-of-squared-differences (ssd)
341        old_ssd = n_past * var
342        new_ssd = n_new * new_var
343        total_ssd = old_ssd + new_ssd + (n_new * n_past / n_total) * (mu - new_mu) ** 2
344        total_var = total_ssd / n_total
345
346        return total_mu, total_var
347
348    @_fit_context(prefer_skip_nested_validation=True)
349    def partial_fit(self, X, y, classes=None, sample_weight=None):
350        """Incremental fit on a batch of samples.
351
352        This method is expected to be called several times consecutively
353        on different chunks of a dataset so as to implement out-of-core
354        or online learning.
355
356        This is especially useful when the whole dataset is too big to fit in
357        memory at once.
358
359        This method has some performance and numerical stability overhead,
360        hence it is better to call partial_fit on chunks of data that are
361        as large as possible (as long as fitting in the memory budget) to
362        hide the overhead.
363
364        Parameters
365        ----------
366        X : array-like of shape (n_samples, n_features)
367            Training vectors, where `n_samples` is the number of samples and
368            `n_features` is the number of features.
369
370        y : array-like of shape (n_samples,)
371            Target values.
372
373        classes : array-like of shape (n_classes,), default=None
374            List of all the classes that can possibly appear in the y vector.
375
376            Must be provided at the first call to partial_fit, can be omitted
377            in subsequent calls.
378
379        sample_weight : array-like of shape (n_samples,), default=None
380            Weights applied to individual samples (1. for unweighted).
381
382            .. versionadded:: 0.17
383
384        Returns
385        -------
386        self : object
387            Returns the instance itself.
388        """
389        return self._partial_fit(
390            X, y, classes, _refit=False, sample_weight=sample_weight
391        )
392
393    def _partial_fit(self, X, y, classes=None, _refit=False, sample_weight=None):
394        """Actual implementation of Gaussian NB fitting.
395
396        Parameters
397        ----------
398        X : array-like of shape (n_samples, n_features)
399            Training vectors, where `n_samples` is the number of samples and
400            `n_features` is the number of features.
401
402        y : array-like of shape (n_samples,)
403            Target values.
404
405        classes : array-like of shape (n_classes,), default=None
406            List of all the classes that can possibly appear in the y vector.
407
408            Must be provided at the first call to partial_fit, can be omitted
409            in subsequent calls.
410
411        _refit : bool, default=False
412            If true, act as though this were the first time we called
413            _partial_fit (ie, throw away any past fitting and start over).
414
415        sample_weight : array-like of shape (n_samples,), default=None
416            Weights applied to individual samples (1. for unweighted).
417
418        Returns
419        -------
420        self : object
421        """
422        if _refit:
423            self.classes_ = None
424
425        first_call = _check_partial_fit_first_call(self, classes)
426        X, y = validate_data(self, X, y, reset=first_call)
427        if sample_weight is not None:
428            sample_weight = _check_sample_weight(sample_weight, X)
429
430        # If the ratio of data variance between dimensions is too small, it
431        # will cause numerical errors. To address this, we artificially
432        # boost the variance by epsilon, a small fraction of the standard
433        # deviation of the largest dimension.
434        self.epsilon_ = self.var_smoothing * np.var(X, axis=0).max()
435
436        if first_call:
437            # This is the first call to partial_fit:
438            # initialize various cumulative counters
439            n_features = X.shape[1]
440            n_classes = len(self.classes_)
441            self.theta_ = np.zeros((n_classes, n_features))
442            self.var_ = np.zeros((n_classes, n_features))
443
444            self.class_count_ = np.zeros(n_classes, dtype=np.float64)
445
446            # Initialise the class prior
447            # Take into account the priors
448            if self.priors is not None:
449                priors = np.asarray(self.priors)
450                # Check that the provided prior matches the number of classes
451                if len(priors) != n_classes:
452                    raise ValueError("Number of priors must match number of classes.")
453                # Check that the sum is 1
454                if not np.isclose(priors.sum(), 1.0):
455                    raise ValueError("The sum of the priors should be 1.")
456                # Check that the priors are non-negative
457                if (priors < 0).any():
458                    raise ValueError("Priors must be non-negative.")
459                self.class_prior_ = priors
460            else:
461                # Initialize the priors to zeros for each class
462                self.class_prior_ = np.zeros(len(self.classes_), dtype=np.float64)
463        else:
464            if X.shape[1] != self.theta_.shape[1]:
465                msg = "Number of features %d does not match previous data %d."
466                raise ValueError(msg % (X.shape[1], self.theta_.shape[1]))
467            # Put epsilon back in each time
468            self.var_[:, :] -= self.epsilon_
469
470        classes = self.classes_
471
472        unique_y = np.unique(y)
473        unique_y_in_classes = np.isin(unique_y, classes)
474
475        if not np.all(unique_y_in_classes):
476            raise ValueError(
477                "The target label(s) %s in y do not exist in the initial classes %s"
478                % (unique_y[~unique_y_in_classes], classes)
479            )
480
481        for y_i in unique_y:
482            i = classes.searchsorted(y_i)
483            X_i = X[y == y_i, :]
484
485            if sample_weight is not None:
486                sw_i = sample_weight[y == y_i]
487                N_i = sw_i.sum()
488            else:
489                sw_i = None
490                N_i = X_i.shape[0]
491
492            new_theta, new_sigma = self._update_mean_variance(
493                self.class_count_[i], self.theta_[i, :], self.var_[i, :], X_i, sw_i
494            )
495
496            self.theta_[i, :] = new_theta
497            self.var_[i, :] = new_sigma
498            self.class_count_[i] += N_i
499
500        self.var_[:, :] += self.epsilon_
501
502        # Update if only no priors is provided
503        if self.priors is None:
504            # Empirical prior, with sample_weight taken into account
505            self.class_prior_ = self.class_count_ / self.class_count_.sum()
506
507        return self
508
509    def _joint_log_likelihood(self, X):
510        joint_log_likelihood = []
511        for i in range(np.size(self.classes_)):
512            jointi = np.log(self.class_prior_[i])
513            n_ij = -0.5 * np.sum(np.log(2.0 * np.pi * self.var_[i, :]))
514            n_ij -= 0.5 * np.sum(((X - self.theta_[i, :]) ** 2) / (self.var_[i, :]), 1)
515            joint_log_likelihood.append(jointi + n_ij)
516
517        joint_log_likelihood = np.array(joint_log_likelihood).T
518        return joint_log_likelihood
519
520
521class _BaseDiscreteNB(_BaseNB):
522    """Abstract base class for naive Bayes on discrete/categorical data
523
524    Any estimator based on this class should provide:
525
526    __init__
527    _joint_log_likelihood(X) as per _BaseNB
528    _update_feature_log_prob(alpha)
529    _count(X, Y)
530    """
531
532    _parameter_constraints: dict = {
533        "alpha": [Interval(Real, 0, None, closed="left"), "array-like"],
534        "fit_prior": ["boolean"],
535        "class_prior": ["array-like", None],
536        "force_alpha": ["boolean"],
537    }
538
539    def __init__(self, alpha=1.0, fit_prior=True, class_prior=None, force_alpha=True):
540        self.alpha = alpha
541        self.fit_prior = fit_prior
542        self.class_prior = class_prior
543        self.force_alpha = force_alpha
544
545    @abstractmethod
546    def _count(self, X, Y):
547        """Update counts that are used to calculate probabilities.
548
549        The counts make up a sufficient statistic extracted from the data.
550        Accordingly, this method is called each time `fit` or `partial_fit`
551        update the model. `class_count_` and `feature_count_` must be updated
552        here along with any model specific counts.
553
554        Parameters
555        ----------
556        X : {ndarray, sparse matrix} of shape (n_samples, n_features)
557            The input samples.
558        Y : ndarray of shape (n_samples, n_classes)
559            Binarized class labels.
560        """
561
562    @abstractmethod
563    def _update_feature_log_prob(self, alpha):
564        """Update feature log probabilities based on counts.
565
566        This method is called each time `fit` or `partial_fit` update the
567        model.
568
569        Parameters
570        ----------
571        alpha : float
572            smoothing parameter. See :meth:`_check_alpha`.
573        """
574
575    def _check_X(self, X):
576        """Validate X, used only in predict* methods."""
577        return validate_data(self, X, accept_sparse="csr", reset=False)
578
579    def _check_X_y(self, X, y, reset=True):
580        """Validate X and y in fit methods."""
581        return validate_data(self, X, y, accept_sparse="csr", reset=reset)
582
583    def _update_class_log_prior(self, class_prior=None):
584        """Update class log priors.
585
586        The class log priors are based on `class_prior`, class count or the
587        number of classes. This method is called each time `fit` or
588        `partial_fit` update the model.
589        """
590        n_classes = len(self.classes_)
591        if class_prior is not None:
592            if len(class_prior) != n_classes:
593                raise ValueError("Number of priors must match number of classes.")
594            self.class_log_prior_ = np.log(class_prior)
595        elif self.fit_prior:
596            with warnings.catch_warnings():
597                # silence the warning when count is 0 because class was not yet
598                # observed
599                warnings.simplefilter("ignore", RuntimeWarning)
600                log_class_count = np.log(self.class_count_)
601
602            # empirical prior, with sample_weight taken into account
603            self.class_log_prior_ = log_class_count - np.log(self.class_count_.sum())
604        else:
605            self.class_log_prior_ = np.full(n_classes, -np.log(n_classes))
606
607    def _check_alpha(self):
608        alpha = (
609            np.asarray(self.alpha) if not isinstance(self.alpha, Real) else self.alpha
610        )
611        alpha_min = np.min(alpha)
612        if isinstance(alpha, np.ndarray):
613            if not alpha.shape[0] == self.n_features_in_:
614                raise ValueError(
615                    "When alpha is an array, it should contains `n_features`. "
616                    f"Got {alpha.shape[0]} elements instead of {self.n_features_in_}."
617                )
618            # check that all alpha are positive
619            if alpha_min < 0:
620                raise ValueError("All values in alpha must be greater than 0.")
621        alpha_lower_bound = 1e-10
622        if alpha_min < alpha_lower_bound and not self.force_alpha:
623            warnings.warn(
624                "alpha too small will result in numeric errors, setting alpha ="
625                f" {alpha_lower_bound:.1e}. Use `force_alpha=True` to keep alpha"
626                " unchanged."
627            )
628            return np.maximum(alpha, alpha_lower_bound)
629        return alpha
630
631    @_fit_context(prefer_skip_nested_validation=True)
632    def partial_fit(self, X, y, classes=None, sample_weight=None):
633        """Incremental fit on a batch of samples.
634
635        This method is expected to be called several times consecutively
636        on different chunks of a dataset so as to implement out-of-core
637        or online learning.
638
639        This is especially useful when the whole dataset is too big to fit in
640        memory at once.
641
642        This method has some performance overhead hence it is better to call
643        partial_fit on chunks of data that are as large as possible
644        (as long as fitting in the memory budget) to hide the overhead.
645
646        Parameters
647        ----------
648        X : {array-like, sparse matrix} of shape (n_samples, n_features)
649            Training vectors, where `n_samples` is the number of samples and
650            `n_features` is the number of features.
651
652        y : array-like of shape (n_samples,)
653            Target values.
654
655        classes : array-like of shape (n_classes,), default=None
656            List of all the classes that can possibly appear in the y vector.
657
658            Must be provided at the first call to partial_fit, can be omitted
659            in subsequent calls.
660
661        sample_weight : array-like of shape (n_samples,), default=None
662            Weights applied to individual samples (1. for unweighted).
663
664        Returns
665        -------
666        self : object
667            Returns the instance itself.
668        """
669        first_call = not hasattr(self, "classes_")
670
671        X, y = self._check_X_y(X, y, reset=first_call)
672        _, n_features = X.shape
673
674        if _check_partial_fit_first_call(self, classes):
675            # This is the first call to partial_fit:
676            # initialize various cumulative counters
677            n_classes = len(classes)
678            self._init_counters(n_classes, n_features)
679
680        Y = label_binarize(y, classes=self.classes_)
681        if Y.shape[1] == 1:
682            if len(self.classes_) == 2:
683                Y = np.concatenate((1 - Y, Y), axis=1)
684            else:  # degenerate case: just one class
685                Y = np.ones_like(Y)
686
687        if X.shape[0] != Y.shape[0]:
688            msg = "X.shape[0]=%d and y.shape[0]=%d are incompatible."
689            raise ValueError(msg % (X.shape[0], y.shape[0]))
690
691        # label_binarize() returns arrays with dtype=np.int64.
692        # We convert it to np.float64 to support sample_weight consistently
693        Y = Y.astype(np.float64, copy=False)
694        if sample_weight is not None:
695            sample_weight = _check_sample_weight(sample_weight, X)
696            sample_weight = np.atleast_2d(sample_weight)
697            Y *= sample_weight.T
698
699        class_prior = self.class_prior
700
701        # Count raw events from data before updating the class log prior
702        # and feature log probas
703        self._count(X, Y)
704
705        # XXX: OPTIM: we could introduce a public finalization method to
706        # be called by the user explicitly just once after several consecutive
707        # calls to partial_fit and prior any call to predict[_[log_]proba]
708        # to avoid computing the smooth log probas at each call to partial fit
709        alpha = self._check_alpha()
710        self._update_feature_log_prob(alpha)
711        self._update_class_log_prior(class_prior=class_prior)
712        return self
713
714    @_fit_context(prefer_skip_nested_validation=True)
715    def fit(self, X, y, sample_weight=None):
716        """Fit Naive Bayes classifier according to X, y.
717
718        Parameters
719        ----------
720        X : {array-like, sparse matrix} of shape (n_samples, n_features)
721            Training vectors, where `n_samples` is the number of samples and
722            `n_features` is the number of features.
723
724        y : array-like of shape (n_samples,)
725            Target values.
726
727        sample_weight : array-like of shape (n_samples,), default=None
728            Weights applied to individual samples (1. for unweighted).
729
730        Returns
731        -------
732        self : object
733            Returns the instance itself.
734        """
735        X, y = self._check_X_y(X, y)
736        _, n_features = X.shape
737
738        labelbin = LabelBinarizer()
739        Y = labelbin.fit_transform(y)
740        self.classes_ = labelbin.classes_
741        if Y.shape[1] == 1:
742            if len(self.classes_) == 2:
743                Y = np.concatenate((1 - Y, Y), axis=1)
744            else:  # degenerate case: just one class
745                Y = np.ones_like(Y)
746
747        # LabelBinarizer().fit_transform() returns arrays with dtype=np.int64.
748        # We convert it to np.float64 to support sample_weight consistently;
749        # this means we also don't have to cast X to floating point
750        if sample_weight is not None:
751            Y = Y.astype(np.float64, copy=False)
752            sample_weight = _check_sample_weight(sample_weight, X)
753            sample_weight = np.atleast_2d(sample_weight)
754            Y *= sample_weight.T
755
756        class_prior = self.class_prior
757
758        # Count raw events from data before updating the class log prior
759        # and feature log probas
760        n_classes = Y.shape[1]
761        self._init_counters(n_classes, n_features)
762        self._count(X, Y)
763        alpha = self._check_alpha()
764        self._update_feature_log_prob(alpha)
765        self._update_class_log_prior(class_prior=class_prior)
766        return self
767
768    def _init_counters(self, n_classes, n_features):
769        self.class_count_ = np.zeros(n_classes, dtype=np.float64)
770        self.feature_count_ = np.zeros((n_classes, n_features), dtype=np.float64)
771
772    def __sklearn_tags__(self):
773        tags = super().__sklearn_tags__()
774        tags.input_tags.sparse = True
775        tags.classifier_tags.poor_score = True
776        return tags
777
778
779class MultinomialNB(_BaseDiscreteNB):
780    """
781    Naive Bayes classifier for multinomial models.
782
783    The multinomial Naive Bayes classifier is suitable for classification with
784    discrete features (e.g., word counts for text classification). The
785    multinomial distribution normally requires integer feature counts. However,
786    in practice, fractional counts such as tf-idf may also work.
787
788    Read more in the :ref:`User Guide <multinomial_naive_bayes>`.
789
790    Parameters
791    ----------
792    alpha : float or array-like of shape (n_features,), default=1.0
793        Additive (Laplace/Lidstone) smoothing parameter
794        (set alpha=0 and force_alpha=True, for no smoothing).
795
796    force_alpha : bool, default=True
797        If False and alpha is less than 1e-10, it will set alpha to
798        1e-10. If True, alpha will remain unchanged. This may cause
799        numerical errors if alpha is too close to 0.
800
801        .. versionadded:: 1.2
802        .. versionchanged:: 1.4
803           The default value of `force_alpha` changed to `True`.
804
805    fit_prior : bool, default=True
806        Whether to learn class prior probabilities or not.
807        If false, a uniform prior will be used.
808
809    class_prior : array-like of shape (n_classes,), default=None
810        Prior probabilities of the classes. If specified, the priors are not
811        adjusted according to the data.
812
813    Attributes
814    ----------
815    class_count_ : ndarray of shape (n_classes,)
816        Number of samples encountered for each class during fitting. This
817        value is weighted by the sample weight when provided.
818
819    class_log_prior_ : ndarray of shape (n_classes,)
820        Smoothed empirical log probability for each class.
821
822    classes_ : ndarray of shape (n_classes,)
823        Class labels known to the classifier
824
825    feature_count_ : ndarray of shape (n_classes, n_features)
826        Number of samples encountered for each (class, feature)
827        during fitting. This value is weighted by the sample weight when
828        provided.
829
830    feature_log_prob_ : ndarray of shape (n_classes, n_features)
831        Empirical log probability of features
832        given a class, ``P(x_i|y)``.
833
834    n_features_in_ : int
835        Number of features seen during :term:`fit`.
836
837        .. versionadded:: 0.24
838
839    feature_names_in_ : ndarray of shape (`n_features_in_`,)
840        Names of features seen during :term:`fit`. Defined only when `X`
841        has feature names that are all strings.
842
843        .. versionadded:: 1.0
844
845    See Also
846    --------
847    BernoulliNB : Naive Bayes classifier for multivariate Bernoulli models.
848    CategoricalNB : Naive Bayes classifier for categorical features.
849    ComplementNB : Complement Naive Bayes classifier.
850    GaussianNB : Gaussian Naive Bayes.
851
852    References
853    ----------
854    C.D. Manning, P. Raghavan and H. Schuetze (2008). Introduction to
855    Information Retrieval. Cambridge University Press, pp. 234-265.
856    https://nlp.stanford.edu/IR-book/html/htmledition/naive-bayes-text-classification-1.html
857
858    Examples
859    --------
860    >>> import numpy as np
861    >>> rng = np.random.RandomState(1)
862    >>> X = rng.randint(5, size=(6, 100))
863    >>> y = np.array([1, 2, 3, 4, 5, 6])
864    >>> from sklearn.naive_bayes import MultinomialNB
865    >>> clf = MultinomialNB()
866    >>> clf.fit(X, y)
867    MultinomialNB()
868    >>> print(clf.predict(X[2:3]))
869    [3]
870    """
871
872    def __init__(
873        self, *, alpha=1.0, force_alpha=True, fit_prior=True, class_prior=None
874    ):
875        super().__init__(
876            alpha=alpha,
877            fit_prior=fit_prior,
878            class_prior=class_prior,
879            force_alpha=force_alpha,
880        )
881
882    def __sklearn_tags__(self):
883        tags = super().__sklearn_tags__()
884        tags.input_tags.positive_only = True
885        return tags
886
887    def _count(self, X, Y):
888        """Count and smooth feature occurrences."""
889        check_non_negative(X, "MultinomialNB (input X)")
890        self.feature_count_ += safe_sparse_dot(Y.T, X)
891        self.class_count_ += Y.sum(axis=0)
892
893    def _update_feature_log_prob(self, alpha):
894        """Apply smoothing to raw counts and recompute log probabilities"""
895        smoothed_fc = self.feature_count_ + alpha
896        smoothed_cc = smoothed_fc.sum(axis=1)
897
898        self.feature_log_prob_ = np.log(smoothed_fc) - np.log(
899            smoothed_cc.reshape(-1, 1)
900        )
901
902    def _joint_log_likelihood(self, X):
903        """Calculate the posterior log probability of the samples X"""
904        return safe_sparse_dot(X, self.feature_log_prob_.T) + self.class_log_prior_
905
906
907class ComplementNB(_BaseDiscreteNB):
908    """The Complement Naive Bayes classifier described in Rennie et al. (2003).
909
910    The Complement Naive Bayes classifier was designed to correct the "severe
911    assumptions" made by the standard Multinomial Naive Bayes classifier. It is
912    particularly suited for imbalanced data sets.
913
914    Read more in the :ref:`User Guide <complement_naive_bayes>`.
915
916    .. versionadded:: 0.20
917
918    Parameters
919    ----------
920    alpha : float or array-like of shape (n_features,), default=1.0
921        Additive (Laplace/Lidstone) smoothing parameter
922        (set alpha=0 and force_alpha=True, for no smoothing).
923
924    force_alpha : bool, default=True
925        If False and alpha is less than 1e-10, it will set alpha to
926        1e-10. If True, alpha will remain unchanged. This may cause
927        numerical errors if alpha is too close to 0.
928
929        .. versionadded:: 1.2
930        .. versionchanged:: 1.4
931           The default value of `force_alpha` changed to `True`.
932
933    fit_prior : bool, default=True
934        Only used in edge case with a single class in the training set.
935
936    class_prior : array-like of shape (n_classes,), default=None
937        Prior probabilities of the classes. Not used.
938
939    norm : bool, default=False
940        Whether or not a second normalization of the weights is performed. The
941        default behavior mirrors the implementations found in Mahout and Weka,
942        which do not follow the full algorithm described in Table 9 of the
943        paper.
944
945    Attributes
946    ----------
947    class_count_ : ndarray of shape (n_classes,)
948        Number of samples encountered for each class during fitting. This
949        value is weighted by the sample weight when provided.
950
951    class_log_prior_ : ndarray of shape (n_classes,)
952        Smoothed empirical log probability for each class. Only used in edge
953        case with a single class in the training set.
954
955    classes_ : ndarray of shape (n_classes,)
956        Class labels known to the classifier
957
958    feature_all_ : ndarray of shape (n_features,)
959        Number of samples encountered for each feature during fitting. This
960        value is weighted by the sample weight when provided.
961
962    feature_count_ : ndarray of shape (n_classes, n_features)
963        Number of samples encountered for each (class, feature) during fitting.
964        This value is weighted by the sample weight when provided.
965
966    feature_log_prob_ : ndarray of shape (n_classes, n_features)
967        Empirical weights for class complements.
968
969    n_features_in_ : int
970        Number of features seen during :term:`fit`.
971
972        .. versionadded:: 0.24
973
974    feature_names_in_ : ndarray of shape (`n_features_in_`,)
975        Names of features seen during :term:`fit`. Defined only when `X`
976        has feature names that are all strings.
977
978        .. versionadded:: 1.0
979
980    See Also
981    --------
982    BernoulliNB : Naive Bayes classifier for multivariate Bernoulli models.
983    CategoricalNB : Naive Bayes classifier for categorical features.
984    GaussianNB : Gaussian Naive Bayes.
985    MultinomialNB : Naive Bayes classifier for multinomial models.
986
987    References
988    ----------
989    Rennie, J. D., Shih, L., Teevan, J., & Karger, D. R. (2003).
990    Tackling the poor assumptions of naive bayes text classifiers. In ICML
991    (Vol. 3, pp. 616-623).
992    https://people.csail.mit.edu/jrennie/papers/icml03-nb.pdf
993
994    Examples
995    --------
996    >>> import numpy as np
997    >>> rng = np.random.RandomState(1)
998    >>> X = rng.randint(5, size=(6, 100))
999    >>> y = np.array([1, 2, 3, 4, 5, 6])
1000    >>> from sklearn.naive_bayes import ComplementNB
1001    >>> clf = ComplementNB()
1002    >>> clf.fit(X, y)
1003    ComplementNB()
1004    >>> print(clf.predict(X[2:3]))
1005    [3]
1006    """
1007
1008    _parameter_constraints: dict = {
1009        **_BaseDiscreteNB._parameter_constraints,
1010        "norm": ["boolean"],
1011    }
1012
1013    def __init__(
1014        self,
1015        *,
1016        alpha=1.0,
1017        force_alpha=True,
1018        fit_prior=True,
1019        class_prior=None,
1020        norm=False,
1021    ):
1022        super().__init__(
1023            alpha=alpha,
1024            force_alpha=force_alpha,
1025            fit_prior=fit_prior,
1026            class_prior=class_prior,
1027        )
1028        self.norm = norm
1029
1030    def __sklearn_tags__(self):
1031        tags = super().__sklearn_tags__()
1032        tags.input_tags.positive_only = True
1033        return tags
1034
1035    def _count(self, X, Y):
1036        """Count feature occurrences."""
1037        check_non_negative(X, "ComplementNB (input X)")
1038        self.feature_count_ += safe_sparse_dot(Y.T, X)
1039        self.class_count_ += Y.sum(axis=0)
1040        self.feature_all_ = self.feature_count_.sum(axis=0)
1041
1042    def _update_feature_log_prob(self, alpha):
1043        """Apply smoothing to raw counts and compute the weights."""
1044        comp_count = self.feature_all_ + alpha - self.feature_count_
1045        logged = np.log(comp_count / comp_count.sum(axis=1, keepdims=True))
1046        # _BaseNB.predict uses argmax, but ComplementNB operates with argmin.
1047        if self.norm:
1048            summed = logged.sum(axis=1, keepdims=True)
1049            feature_log_prob = logged / summed
1050        else:
1051            feature_log_prob = -logged
1052        self.feature_log_prob_ = feature_log_prob
1053
1054    def _joint_log_likelihood(self, X):
1055        """Calculate the class scores for the samples in X."""
1056        jll = safe_sparse_dot(X, self.feature_log_prob_.T)
1057        if len(self.classes_) == 1:
1058            jll += self.class_log_prior_
1059        return jll
1060
1061
1062class BernoulliNB(_BaseDiscreteNB):
1063    """Naive Bayes classifier for multivariate Bernoulli models.
1064
1065    Like MultinomialNB, this classifier is suitable for discrete data. The
1066    difference is that while MultinomialNB works with occurrence counts,
1067    BernoulliNB is designed for binary/boolean features.
1068
1069    Read more in the :ref:`User Guide <bernoulli_naive_bayes>`.
1070
1071    Parameters
1072    ----------
1073    alpha : float or array-like of shape (n_features,), default=1.0
1074        Additive (Laplace/Lidstone) smoothing parameter
1075        (set alpha=0 and force_alpha=True, for no smoothing).
1076
1077    force_alpha : bool, default=True
1078        If False and alpha is less than 1e-10, it will set alpha to
1079        1e-10. If True, alpha will remain unchanged. This may cause
1080        numerical errors if alpha is too close to 0.
1081
1082        .. versionadded:: 1.2
1083        .. versionchanged:: 1.4
1084           The default value of `force_alpha` changed to `True`.
1085
1086    binarize : float or None, default=0.0
1087        Threshold for binarizing (mapping to booleans) of sample features.
1088        If None, input is presumed to already consist of binary vectors.
1089
1090    fit_prior : bool, default=True
1091        Whether to learn class prior probabilities or not.
1092        If false, a uniform prior will be used.
1093
1094    class_prior : array-like of shape (n_classes,), default=None
1095        Prior probabilities of the classes. If specified, the priors are not
1096        adjusted according to the data.
1097
1098    Attributes
1099    ----------
1100    class_count_ : ndarray of shape (n_classes,)
1101        Number of samples encountered for each class during fitting. This
1102        value is weighted by the sample weight when provided.
1103
1104    class_log_prior_ : ndarray of shape (n_classes,)
1105        Log probability of each class (smoothed).
1106
1107    classes_ : ndarray of shape (n_classes,)
1108        Class labels known to the classifier
1109
1110    feature_count_ : ndarray of shape (n_classes, n_features)
1111        Number of samples encountered for each (class, feature)
1112        during fitting. This value is weighted by the sample weight when
1113        provided.
1114
1115    feature_log_prob_ : ndarray of shape (n_classes, n_features)
1116        Empirical log probability of features given a class, P(x_i|y).
1117
1118    n_features_in_ : int
1119        Number of features seen during :term:`fit`.
1120
1121        .. versionadded:: 0.24
1122
1123    feature_names_in_ : ndarray of shape (`n_features_in_`,)
1124        Names of features seen during :term:`fit`. Defined only when `X`
1125        has feature names that are all strings.
1126
1127        .. versionadded:: 1.0
1128
1129    See Also
1130    --------
1131    CategoricalNB : Naive Bayes classifier for categorical features.
1132    ComplementNB : The Complement Naive Bayes classifier
1133        described in Rennie et al. (2003).
1134    GaussianNB : Gaussian Naive Bayes (GaussianNB).
1135    MultinomialNB : Naive Bayes classifier for multinomial models.
1136
1137    References
1138    ----------
1139    C.D. Manning, P. Raghavan and H. Schuetze (2008). Introduction to
1140    Information Retrieval. Cambridge University Press, pp. 234-265.
1141    https://nlp.stanford.edu/IR-book/html/htmledition/the-bernoulli-model-1.html
1142
1143    A. McCallum and K. Nigam (1998). A comparison of event models for naive
1144    Bayes text classification. Proc. AAAI/ICML-98 Workshop on Learning for
1145    Text Categorization, pp. 41-48.
1146
1147    V. Metsis, I. Androutsopoulos and G. Paliouras (2006). Spam filtering with
1148    naive Bayes -- Which naive Bayes? 3rd Conf. on Email and Anti-Spam (CEAS).
1149
1150    Examples
1151    --------
1152    >>> import numpy as np
1153    >>> rng = np.random.RandomState(1)
1154    >>> X = rng.randint(5, size=(6, 100))
1155    >>> Y = np.array([1, 2, 3, 4, 4, 5])
1156    >>> from sklearn.naive_bayes import BernoulliNB
1157    >>> clf = BernoulliNB()
1158    >>> clf.fit(X, Y)
1159    BernoulliNB()
1160    >>> print(clf.predict(X[2:3]))
1161    [3]
1162    """
1163
1164    _parameter_constraints: dict = {
1165        **_BaseDiscreteNB._parameter_constraints,
1166        "binarize": [None, Interval(Real, 0, None, closed="left")],
1167    }
1168
1169    def __init__(
1170        self,
1171        *,
1172        alpha=1.0,
1173        force_alpha=True,
1174        binarize=0.0,
1175        fit_prior=True,
1176        class_prior=None,
1177    ):
1178        super().__init__(
1179            alpha=alpha,
1180            fit_prior=fit_prior,
1181            class_prior=class_prior,
1182            force_alpha=force_alpha,
1183        )
1184        self.binarize = binarize
1185
1186    def _check_X(self, X):
1187        """Validate X, used only in predict* methods."""
1188        X = super()._check_X(X)
1189        if self.binarize is not None:
1190            X = binarize(X, threshold=self.binarize)
1191        return X
1192
1193    def _check_X_y(self, X, y, reset=True):
1194        X, y = super()._check_X_y(X, y, reset=reset)
1195        if self.binarize is not None:
1196            X = binarize(X, threshold=self.binarize)
1197        return X, y
1198
1199    def _count(self, X, Y):
1200        """Count and smooth feature occurrences."""

Showing the first 1,200 of 1541 lines. Download the file for the rest.