在使用朴素贝叶斯对非常长的文本进行分类时,避免获得无意义的值(例如 -0.0)

Avoid getting nonsensical values (eg. -0.0) when classifying very long texts with Naive Bayes

我已经实现了多项式朴素贝叶斯分类器的相当有效的实现,它的效果非常好。直到分类器遇到非常长的消息(大约 10k 个词),其中预测结果是无意义的(例如 -0.0)并且我在使用 Python 时得到 math domain error math.log ] 功能。我使用对数的原因是,当处理非常小的浮点数时,如果将它们相乘得到的是非常小的浮点数,对数有助于避免无限小的数字导致预测失败。

一些上下文

我使用的是没有任何向量化的词袋方法(比如 TF-IDF,因为我不知道如何正确地实现它并平衡出现 0 的词。一个片段是也很感激 ;) ) 并且我正在使用频率计数和拉普拉斯加法平滑(基本上是为每个频率计数加 1,因此它永远不会为 0)。

我可以只删除日志,但这意味着在如此长的消息的情况下,引擎无论如何都无法正确检测到它们,所以这不是重点。

如果应用 log-sum-exp,朴素贝叶斯中没有乘法,只有加法,因此不太可能出现下溢。如果你使用平滑(就像你说的那样),你永远不会得到 log.

的未定义行为

This stats stackexchange answer describes the underlying math. For reference implementation I have a snippet of mine lying around for MultinomialNaiveBayes (analogous to sklearn's sklearn.naive_bayes.MultinomialNB 和类似 API):

import numpy as np
import scipy


class MultinomialNaiveBayes:
    def __init__(self, alpha: float = 1.0):
        self.alpha = alpha

    def fit(self, X, y):
        # Calculate priors from data
        self.log_priors_ = np.log(np.bincount(y) / y.shape[0])

        # Get indices where data belongs to separate class, creating a slicing mask.
        class_indices = np.array(
            np.ma.make_mask([y == current for current in range(len(self.log_priors_))])
        )
        # Divide dataset based on class indices
        class_datasets = np.array([X[indices] for indices in class_indices])

        # Calculate how often each class occurs and add alpha smoothing.
        # Reshape into [n_classes, features]
        classes_metrics = (
            np.array([dataset.sum(axis=0) for dataset in class_datasets]).reshape(
                len(self.log_priors_), -1
            )
            + self.alpha
        )

        # Calculate log likelihoods
        self.log_likelihoods_ = np.log(
            classes_metrics / classes_metrics.sum(axis=1)[:, np.newaxis]
        )

        return self

    def predict(self, X):
        # Return most likely class
        return np.argmax(
            scipy.sparse.csr_matrix.dot(X, self.log_likelihoods_.T) + self.log_priors_,
            axis=1,
        )

顺便说一句。 -0.00.0 完全相同,是有意义的值。