朴素高斯预测概率仅返回 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的主要内容,如果未能解决你的问题,请参考以下文章