朴素高斯预测概率仅返回 0 或 1

Posted

技术标签:

【中文标题】朴素高斯预测概率仅返回 0 或 1【英文标题】:Naive Gaussian predict probability only returns 0 or 1 【发布时间】:2021-09-01 16:20:20 【问题描述】:

我从 scikit sklearn 训练了 GaussianNB 模型。当我调用classifier.predict_proba 方法时,它只对新数据返回 1 或 0。预计会返回预测正确与否的置信百分比。我怀疑它对以前从未见过的新数据有 100% 的信心。我已经在多个不同的输入上对其进行了测试。我使用 CountVectorizer 和 TfidfTransformer 进行文本编码。

编码:

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer

count_vect = CountVectorizer()
tfidf_transformer = TfidfTransformer()

X_train_counts = count_vect.fit_transform(X_train_word)
X_train = tfidf_transformer.fit_transform(X_train_counts).toarray()
print(X_train)

X_test_counts = count_vect.transform(X_test_word)
X_test = tfidf_transformer.transform(X_test_counts).toarray()
print(X_test)

模型:(我得到了 91% 的准确率)

from sklearn.naive_bayes import GaussianNB
classifier = GaussianNB()
classifier.fit(X_train, y_train)

# Predict Class
y_pred = classifier.predict(X_test)

# Accuracy 
from sklearn.metrics import accuracy_score
accuracy = accuracy_score(y_test, y_pred)
print(accuracy)

最后,当我使用 predict_proba 方法时:

y_pred = classifier.predict_proba(X_test)
print(y_pred)

我得到如下输出:

[[0. 1.]
 [1. 0.]
 [0. 1.]
 ...
 [1. 0.]
 [1. 0.]
 [1. 0.]]

对新数据保持 100% 的准确率没有多大意义。除了y_test,我已经在其他输入上对其进行了测试,但它仍然返回相同的结果。任何帮助将不胜感激!

为 cmets 编辑: .predict_log_proba()的回复就更奇怪了:

[[ 0.00000000e+00 -6.95947375e+09]
 [-4.83948755e+09  0.00000000e+00]
 [ 0.00000000e+00 -1.26497690e+10]
 ...
 [ 0.00000000e+00 -6.97191054e+09]
 [ 0.00000000e+00 -2.25589894e+09]
 [ 0.00000000e+00 -2.93089863e+09]]

【问题讨论】:

只是一种预感,因为我不熟悉上下文;是不是你在浮点精度方面遇到了麻烦? classifier.predict_log_proba 输出什么? 你有两个变量名为y_pred;另一个将以您看到的形式输出数据。很可能这是错误(特别是如果这是在笔记本中完成的)。为什么不试试y_probs @anon01 谢谢回复,但输出还是一样 @jrbergen 我已经用.predict_log_proba的结果更新了问题 @Dani 这些结果确实令人困惑。在某个地方,某些东西似乎被放大了很多。我希望我能帮助你,但我对 sk-learn 或其内部结构一点也不熟悉,也不知道在这种情况下会发生什么。我只是希望对数概率的事情能暗示一个解决方案,就像它曾经在不同的情况下对我所做的那样。 【参考方案1】:

让我在公共20 newsgroups dataset 上重现您的结果。为简单起见,我将只使用两组和 30 个观察值:

from sklearn.datasets import fetch_20newsgroups
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.preprocessing import FunctionTransformer
from sklearn.naive_bayes import GaussianNB
from sklearn.pipeline import make_pipeline

cats = ['alt.atheism', 'sci.space']
newsgroups_train = fetch_20newsgroups(subset='train', categories=cats)
newsgroups_test = fetch_20newsgroups(subset='test', categories=cats)
# deliberately create a very small training set
X_small, y_small = newsgroups_train['data'][:30], newsgroups_train['target'][:30]
print(y_small)
# [0 1 1 1 0 1 1 0 0 0 1 1 1 1 1 0 0 1 1 0 1 1 0 0 0 0 0 1 0 1]

现在让我们训练一个模型。我将使用管道将所有算法堆叠在一个处理器中:

model = make_pipeline(
    CountVectorizer(), 
    TfidfTransformer(), 
    FunctionTransformer(lambda x: x.todense(), accept_sparse=True), 
    GaussianNB()
)
model.fit(X_small, y_small);
print(model.predict_proba(newsgroups_test['data']))
# [[1. 0.]
#  [0. 1.]
#  [1. 0.]
print((model.predict(X_small) == y_small).mean())
# 1.0
print((model.predict(newsgroups_test['data']) == newsgroups_test['target']).mean())
# 0.847124824684432
print(model.predict_proba(newsgroups_test['data']).max(axis=1).mean())
# 0.9994305488454233

事实上,并非所有预测的概率都是 0 或 1,但大多数都是。预测类别的平均预测概率为 99.94%,因此模型平均对其预测非常有信心。

我们看到在训练集上的准确率是完美的,但在测试集上的准确率只有 84.7%。所以看起来我们的 GaussianNB 是 overfitting - 也就是说,它过于依赖训练数据集。是的,如果特征空间很大,即使使用像 NB 这样简单的算法也是可能的。并且使用 CountVectorizer,词汇表中的每个单词都是一个单独的特征,所有可能的单词的数量都非常大。所以我们的模型是过拟合的,这就是为什么它会产生由零和一组成的过度自信的预测。

而且,像往常一样,我们可以使用正则化来对抗过拟合。使用 GaussianNB,正则化模型的最简单方法是将参数 var_smoothing 设置为某个相对较大的正值(默认为 10^-8)。根据我的经验,我建议的值在 0.01 到 1 的范围内。这里我将其设置为 0.3。这意味着最多样化特征(即在类之间分布最均匀的词)的 30% 的方差将被添加到所有其他特征中。

model2 = make_pipeline(
    CountVectorizer(), 
    TfidfTransformer(), 
    FunctionTransformer(lambda x: x.todense(), accept_sparse=True), 
    GaussianNB(var_smoothing=0.3)
)
model2.fit(X_small, y_small);
print(model2.predict_proba(newsgroups_test['data']))
# [[1.00000000e+00 6.95414544e-11]
#  [2.55262953e-02 9.74473705e-01]
#  [9.97333826e-01 2.66617361e-03]
print((model2.predict(X_small) == y_small).mean())
# 1.0
print((model2.predict(newsgroups_test['data']) == newsgroups_test['target']).mean())
# 0.8821879382889201
print(model2.predict_proba(newsgroups_test['data']).max(axis=1).mean())
# 0.9657781853646639

我们可以看到,添加正则化后,我们模型的预测变得不那么自信了:平均置信度为 96.57%,而不是 99.94%。此外,在测试集上的准确率有所提高,因为这种过度自信导致模型做出了一些不正确的预测。

这些错误预测的逻辑可以说明如下。在没有正则化的情况下,模型完全依赖于训练集中单词的频率。当它看起来例如一个文本“死于 X 射线的概率”,模型认为“我在关于无神论的文本中看到过‘死亡’这个词,所以这个必须关于无神论的文本”。但这是一个关于空间的文本,一个更正则化的模型在其结论中不会那么肯定,并且仍然会保留一些小但非零的概率,即带有“死亡”这个词的文本是关于无神论以外的某个主题。

所以这里的教训是:无论你使用什么学习算法,找出如何对其进行正则化,并仔细调整正则化参数

【讨论】:

以上是关于朴素高斯预测概率仅返回 0 或 1的主要内容,如果未能解决你的问题,请参考以下文章

生成模型学习笔记:从高斯判别分析到朴素贝叶斯

在数字分类数据上实现朴素贝叶斯高斯分类器

朴素贝叶斯算法之鸢尾花特征分类机器学习伯努利分布,多项式分布,高斯分布

分类-朴素贝叶斯(高斯多项式伯努利)

使用高斯朴素贝叶斯的多类分类

三种常用的朴素贝叶斯实现算法——高斯朴素贝叶斯伯努利朴素贝叶斯多项式朴素贝叶斯