gpt4 book ai didi

r - 如何优化 R 中词干提取和拼写检查的性能?

转载 作者:行者123 更新时间:2023-12-03 18:05:26 26 4
gpt4 key购买 nike

我有大约 1,400 万个文档,每个文档的平均字符数为(中位数:250 和平均值:470)。

我想在对它们进行分类之前执行拼写检查和词干提取。

模拟文档:

sentence <- "We aree drivng as fast as we drove yestrday or evven fastter zysxzw" %>%
rep(times = 6) %>%
paste(collapse = " ")

nchar(sentence)
[1] 407

函数首先执行拼写检查,然后进行词干提取
library(hunspell)
library(magrittr)

spellAndStem <- function(sent, language = "en_US"){
words <- sentence %>%
strsplit(split = " ") %>%
unlist

# spelling
correct <- hunspell_check(
words = words,
dict = dictionary(language)
)

words[!correct] %<>%
hunspell_suggest(dict = language) %>%
sapply(FUN = "[", 1)

# stemming
words %>%
hunspell_stem(dict = dictionary(language)) %>%
unlist %>%
paste(collapse = " ")
}

我看了 hunspell()为了提高性能而放弃整个文档的功能,但我不知道如何按该顺序进行拼写检查和词干提取。

时间测量:
> library(microbenchmark)
> microbenchmark(spellAndStem(sentence), times = 100)
Unit: milliseconds
expr min lq mean median uq max neval
spellAndStem(sentence) 680.3601 689.8842 700.7957 694.3781 702.7493 798.9544 100

每个文档 0.7 秒,计算需要 0.7*1400000/3600/24 = 11.3 天。

问题:

如何优化此性能?

最后备注:

目标语言为 98% 的德语和 2% 的英语。不确定信息是否重要,只是为了完整性。

最佳答案

您可以通过对词汇表而不是文档中的所有单词执行昂贵的步骤来显着优化您的代码。 quanteda包提供了一个非常有用的对象类或称为 tokens :

toks <- quanteda::tokens(sentence)
unclass(toks)
#> $text1
#> [1] 1 2 3 4 5 4 6 7 8 9 10 11 12 1 2 3 4 5 4 6 7 8 9 10 11
#> [26] 12 1 2 3 4 5 4 6 7 8 9 10 11 12 1 2 3 4 5 4 6 7 8 9 10
#> [51] 11 12 1 2 3 4 5 4 6 7 8 9 10 11 12 1 2 3 4 5 4 6 7 8 9
#> [76] 10 11 12
#>
#> attr(,"types")
#> [1] "We" "aree" "drivng" "as" "fast" "we"
#> [7] "drove" "yestrday" "or" "evven" "fastter" "zysxzw"
#> attr(,"padding")
#> [1] FALSE
#> attr(,"what")
#> [1] "word"
#> attr(,"ngrams")
#> [1] 1
#> attr(,"skip")
#> [1] 0
#> attr(,"concatenator")
#> [1] "_"
#> attr(,"docvars")
#> data frame with 0 columns and 1 row

如您所见,文本被拆分为词汇( types )和单词的位置。我们可以通过在 types 上执行所有步骤来使用它来优化您的代码。而不是整个文本:
spellAndStem_tokens <- function(sent, language = "en_US") {

sent_t <- quanteda::tokens(sent)

# extract types to only work on them
types <- quanteda::types(sent_t)

# spelling
correct <- hunspell_check(
words = as.character(types),
dict = hunspell::dictionary(language)
)

pattern <- types[!correct]
replacement <- sapply(hunspell_suggest(pattern, dict = language), FUN = "[", 1)

types <- stringi::stri_replace_all_fixed(
types,
pattern,
replacement,
vectorize_all = FALSE
)

# stemming
types <- hunspell_stem(types, dict = dictionary(language))


# replace original tokens
sent_t_new <- quanteda::tokens_replace(sent_t, quanteda::types(sent_t), as.character(types))

sent_t_new <- quanteda::tokens_remove(sent_t_new, pattern = "NULL", valuetype = "fixed")

paste(as.character(sent_t_new), collapse = " ")
}

我正在使用 bench包来进行基准测试,因为它还检查两个函数的结果是否相同,而且我发现它通常更舒适:
res <- bench::mark(
spellAndStem(sentence),
spellAndStem_tokens(sentence)
)

res
#> # A tibble: 2 x 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl>
#> 1 spellAndStem(sentence) 807ms 807ms 1.24 259KB 0
#> 2 spellAndStem_tokens(sentence) 148ms 150ms 6.61 289KB 0

summary(res, relative = TRUE)
#> # A tibble: 2 x 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 spellAndStem(sentence) 5.44 5.37 1 1 NaN
#> 2 spellAndStem_tokens(sentence) 1 1 5.33 1.11 NaN

新功能比原始功能快 5.44 倍。请注意,输入文本越大,差异变得越明显:
sentence <- "We aree drivng as fast as we drove yestrday or evven fastter zysxzw" %>%
rep(times = 600) %>%
paste(collapse = " ")

res_big <- bench::mark(
spellAndStem(sentence),
spellAndStem_tokens(sentence)
)

res_big
#> # A tibble: 2 x 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl>
#> 1 spellAndStem(sentence) 1.27m 1.27m 0.0131 749.81KB 0
#> 2 spellAndStem_tokens(sentence) 178.26ms 182.12ms 5.51 1.94MB 0
summary(res_big, relative = TRUE)
#> # A tibble: 2 x 6
#> expression min median `itr/sec` mem_alloc `gc/sec`
#> <bch:expr> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 spellAndStem(sentence) 428. 419. 1 1 NaN
#> 2 spellAndStem_tokens(sentence) 1 1 420. 2.65 NaN

如您所见,处理 100 倍大样本所需的时间与处理较小样本所需的时间几乎相同。这是因为两者之间的词汇量完全相同。假设这个更大的样本代表您的 100 个文档,我们可以从这个结果推断到您的整个数据集。该函数应该需要不到一个小时(0.17826 * 14000/3600 = 0.69),但计算确实不完美,因为在真实数据上运行它所需的实际时间几乎完全取决于词汇量的大小。

除了编程/性能方面,我还有一些可能不适用于您的特定情况的问题:
  • 我建议将函数中的最后一行更改为 sapply(as.list(sent_t_new), paste, collapse = " ")因为这不会将所有文档折叠成一个长字符串,而是将它们分开。
  • 目前,您的设置会删除 hunspell 处的字词。找不到任何建议。我复制了这种方法(请参阅 tokens_remove 命令),但您可能需要考虑至少输出丢弃的单词而不是静默删除它们。
  • 如果上面的函数是为其他一些文本分析做准备,那么在执行词干提取和拼写检查之前将数据直接转换为文档术语矩阵会更有意义。
  • Stemming 只是词形还原的一种近似,词形还原是实际找到单词基本形式的过程。此外,词干提取在德语中的效果通常很差。根据您在做什么,您可能想要改为进行词形还原(例如,使用 spacyr )或干脆将其关闭,因为词干提取很少能提高德语的结果。
  • 关于r - 如何优化 R 中词干提取和拼写检查的性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60319594/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com