Quanteda和caret中的朴素贝叶斯:结果截然不同

5
我想使用quantedacaret包一起对基于训练样本的文本进行分类。作为测试运行,我想比较quanteda中内置的朴素贝叶斯分类器与caret中的分类器。然而,我似乎不能使caret正常工作。
以下是一些可复现的代码,首先来看quanteda方面的代码:
library(quanteda)
library(quanteda.corpora)
library(caret)
corp <- data_corpus_movies
set.seed(300)
id_train <- sample(docnames(corp), size = 1500, replace = FALSE)

# get training set
training_dfm <- corpus_subset(corp, docnames(corp) %in% id_train) %>%
  dfm(stem = TRUE)

# get test set (documents not in id_train, make features equal)
test_dfm <- corpus_subset(corp, !docnames(corp) %in% id_train) %>%
  dfm(stem = TRUE) %>% 
  dfm_select(pattern = training_dfm, 
             selection = "keep")

# train model on sentiment
nb_quanteda <- textmodel_nb(training_dfm, docvars(training_dfm, "Sentiment"))

# predict and evaluate
actual_class <- docvars(test_dfm, "Sentiment")
predicted_class <- predict(nb_quanteda, newdata = test_dfm)
class_table_quanteda <- table(actual_class, predicted_class)
class_table_quanteda
#>             predicted_class
#> actual_class neg pos
#>          neg 202  47
#>          pos  49 202

不错。准确率为80.8%,无需调整。现在相同的内容(据我所知)在caret中。

training_m <- convert(training_dfm, to = "matrix")
test_m <- convert(test_dfm, to = "matrix")
nb_caret <- train(x = training_m,
                  y = as.factor(docvars(training_dfm, "Sentiment")),
                  method = "naive_bayes",
                  trControl = trainControl(method = "none"),
                  tuneGrid = data.frame(laplace = 1,
                                        usekernel = FALSE,
                                        adjust = FALSE),
                  verbose = TRUE)

predicted_class_caret <- predict(nb_caret, newdata = test_m)
class_table_caret <- table(actual_class, predicted_class_caret)
class_table_caret
#>             predicted_class_caret
#> actual_class neg pos
#>          neg 246   3
#>          pos 249   2

这里的准确率不仅非常低(49.6% - 大约是机会),而且正类别几乎没有被预测!所以我相信我在这里缺少了一些关键的东西,因为我认为实现应该是相当相似的,但我不确定是什么。
我已经查看了quanteda函数的源代码(希望它可能是建立在caret或底层包之上),并看到有一些加权和平滑处理。如果我在训练之前将同样的内容应用于我的dfm(稍后设置laplace = 0),准确性会稍微好一些。但也只有53%。
2个回答

8
答案是,caret(使用来自naivebayes包的naive_bayes)假设高斯分布,而quanteda::textmodel_nb()基于更适合文本的多项式分布(还可以选择伯努利分布)。textmodel_nb()的文档复制了IIR书籍(Manning、Raghavan和Schütze 2008)中的示例,并引用了Jurafsky和Martin(2018)的另一个示例。请参见:另一个包e1071也基于高斯分布,产生了与您发现的相同结果。
library("e1071")
nb_e1071 <- naiveBayes(x = training_m,
                       y = as.factor(docvars(training_dfm, "Sentiment")))
nb_e1071_pred <- predict(nb_e1071, newdata = test_m)
table(actual_class, nb_e1071_pred)
##             nb_e1071_pred
## actual_class neg pos
##          neg 246   3
##          pos 249   2

然而,carete1071都适用于密集矩阵,这也是它们与基于稀疏dfm操作的quanteda相比如此缓慢的原因之一。因此,从适当性、效率和分类器性能(根据您的结果)的角度来看,应该很清楚哪种方法更受欢迎!
library("rbenchmark")
benchmark(
    quanteda = { 
        nb_quanteda <- textmodel_nb(training_dfm, docvars(training_dfm, "Sentiment"))
        predicted_class <- predict(nb_quanteda, newdata = test_dfm)
    },
    caret = {
        nb_caret <- train(x = training_m,
                          y = as.factor(docvars(training_dfm, "Sentiment")),
                          method = "naive_bayes",
                          trControl = trainControl(method = "none"),
                          tuneGrid = data.frame(laplace = 1,
                                                usekernel = FALSE,
                                                adjust = FALSE),
                          verbose = FALSE)
        predicted_class_caret <- predict(nb_caret, newdata = test_m)
    },
    e1071 = {
        nb_e1071 <- naiveBayes(x = training_m,
                       y = as.factor(docvars(training_dfm, "Sentiment")))
        nb_e1071_pred <- predict(nb_e1071, newdata = test_m)
    },
    replications = 1
)
##       test replications elapsed relative user.self sys.self user.child sys.child
## 2    caret            1  29.042  123.583    25.896    3.095          0         0
## 3    e1071            1 217.177  924.157   215.587    1.169          0         0
## 1 quanteda            1   0.235    1.000     0.213    0.023          0         0

1
感谢您提供这个惊人的答案!这一切都很有道理。还要感谢您提供的文献!我现在明白为什么您费心将nb实现到“quanteda”中了。我还检查了“caret”中哪些算法/包只能处理密集矩阵(如果提供了稀疏矩阵,我认为它们都可以处理),我很惊讶还有这么多。 - JBGruber
默认情况下,naive_bayes 假设每个连续特征(数值)都服从高斯分布。对于离散特征(字符/因子/逻辑),自动使用分类分布。可以将核密度估计应用于连续预测变量。它很快就能处理具有泊松分布的计数数据。该函数非常通用,因此速度较慢...但是相当合理。矩阵超过90%是稀疏的。naive_bayes 适用于不同类型的问题。 - Michal Majka

2
以上答案是正确的,我只想补充一下,您可以将变量转换为因子,并使用Bernoulli分布在“naivebayes”和“e1071”包中。这些的输出应该与具有Bernoulli分布的“quanteda”文本模型相匹配。
此外,您可以查看:https://cran.r-project.org/web/packages/fastNaiveBayes/index.html。它实现了Bernoulli、Multinomial和Gaussian分布,适用于稀疏矩阵,并且速度非常快(目前在CRAN上最快)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接