首页 > 解决方案 > 使用朴素贝叶斯对很长的文本进行分类时,避免得到无意义的值(例如 -0.0)

问题描述

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

一些上下文

我正在使用没有任何矢量化的词袋方法(如 TF-IDF,因为我无法弄清楚如何正确实现它并平衡 0 出现的词。对此的片段也将不胜感激;))我正在使用频率计数和拉普拉斯加法平滑(基本上每个频率计数加 1,所以它永远不会为 0)。

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

标签: pythonmachine-learningnaivebayes

解决方案


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

这个 stats stackexchange 答案描述了基础数学。对于参考实现,我有一个我的片段MultinomialNaiveBayes(类似于 sklearnsklearn.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.0是完全一样的0.0,是感性的价值。


推荐阅读