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

Naive Gaussian predict probability only returns 0 or 1

我从 scikit sklearn 训练了 GaussianNB 模型。当我调用方法 classifier.predict_proba 时,它仅对新数据 returns 1 或 0。预期 return 预测正确与否的置信度百分比。我怀疑它能否对以前从未见过的新数据有 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 上,我已经在其他输入上对其进行了测试,它仍然 return 相同。如有任何帮助,我们将不胜感激!

编辑评论: .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]]

让我在 public 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,但大多数是。预测的平均预测概率 class 为 99.94%,因此该模型平均而言对其预测非常有信心。

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

而且,像往常一样,我们可以使用正则化 来对抗过度拟合。使用 GaussianNB,正则化模型的最简单方法是将参数 var_smoothing 设置为某个相对较大的正值(默认情况下,它是 10^-8)。根据我的经验,我建议值在 0.01 到 1 的范围内。这里我将其设置为 0.3。这意味着最多样化特征(即在 class 之间分布最均匀的词)的 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 射线的概率”,模型认为“我在有关无神论的文本中只看到过‘死亡’这个词 ,所以这个 必须 是一篇关于无神论的文章”。但这是一篇关于 space 的文本,更正则化的模型在其结论中不会那么确定,并且仍然会保留一些小但非零的概率,即带有“垂死”一词的文本是关于其他主题的比无神论。

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