【问题标题】:NLTK NaiveBayesClassifier is extremely slow in Python?NLTK NaiveBayesClassifier 在 Python 中非常慢?
【发布时间】:2014-10-25 01:30:11
【问题描述】:

我正在使用 NLTK NaiveBayesClassifier 进行情绪分析。整个过程非常缓慢。我什至尝试保存我的教练数据,这样我就不必每次都重新训练,我发现速度/时间没有区别..

保存:

import cPickle
f = open('my_classifier.pickle', 'wb')
pickle.dump(classifier, f)
f.close()

稍后加载:

import cPickle
f = open('my_classifier.pickle')
classifier = pickle.load(f)
f.close()

我还能做些什么来提高速度?分析一个句子需要 6 秒。我想要

*现在我已经改为使用 cPickle 而不是 pickle 来保存/加载,性能下降到 3 秒!

【问题讨论】:

  • 模型很大吗?您使用什么功能?
  • 模型相当大,4MB。 pos 和 neg 表示特征。
  • 正负什么的?单个单词形式?词干?
  • 我将大约 15,000 个单词的列表分为正面或负面。

标签: python machine-learning nltk


【解决方案1】:

我猜pickle保存格式只是保存了训练数据,每次加载它都会重新计算模型。

您不应该在每次对句子进行分类时都重新加载分类器。您能否以这样一种方式编写 Web 服务,使其一次可以处理多个请求?

我从未使用过 Asp.net 和 IIS。我环顾四周,似乎可以通过安装this extensionhere 配置说明)将 IIS 配置为使用 FastCGI。 here 解释了如何编写 Python 脚本以使其与 FastCGI 兼容。

【讨论】:

  • 这真是太聪明了!问题是,我需要在不同的时间段访问文件。所以有一次我会找到“这是最好的!”的情绪。然后说.. 3分钟后我会找到“这是最糟糕的!”的情绪。我怎么能用 C# asp.net 和 Python 做到这一点?
  • 取决于你如何从 C# 中调用它。传统的修复方法类似于 FastCGI,但我不知道它是否适用于 asp.net。
  • @user3912889 我添加了有关 FastCGI 的信息。如果您每次不明白某些事情时都停止讽刺的 cmets,那真的会有所帮助(您会得到帮助)。如果您每次需要处理请求时都做一些愚蠢的事情,例如从序列化格式重新加载模型,则无需抱怨 python 速度慢。如果我一直关闭计算机,我无法在几秒钟内回复电子邮件也就不足为奇了。
【解决方案2】:

如果你真的要引入 400 万个15,000 个特征来分析大概十几个词,那么大部分特征都不会被使用。这建议使用某种基于磁盘的数据库来代替功能,并仅提取您需要的那些。即使对于一个长句子和一个低效的数据库,4 次搜索 x 50 个单词仍然比您现在看到的要少得多——在最坏的情况下可能是数百毫秒,但肯定不会是多秒。

首先查看带有 NDBM 或 GDBM 后端的 anydbm,然后根据熟悉程度和可用性考虑其他后端。


您的后续 cmet 似乎表明对您正在做什么和/或事情应该如何运作存在基本的误解。让我们用词典中的五个单词做一个简单的例子。

# training
d = { 'good': 1, 'bad': -1, 'excellent': 1, 'poor': -1, 'great': 1 }
c = classifier(d)
with open(f, "classifier.pickle", "w") as f:
    pickle.dump(c, f)


sentences = ['I took a good look', 'Even his bad examples were stunning']

# classifying, stupid version
for sentence in sentences:
    with open(f, "classifier.pickle", "r") as f:
        c = pickle.load(f)
    sentiment = c(sentence)
    # basically,  for word in sentence.split(): if word in d: sentiment += d[word]
    print sentiment, sentence

# classifying, slightly less stupid version
with open(f, "classifier.pickle", "r") as f:
    c = pickle.load(f)
# FastCGI init_end here
for sentence in sentences:
    sentiment = c(sentence)
    print sentiment, sentence

愚蠢的版本似乎是你目前正在经历的。稍微不那么愚蠢的版本加载分类器一次,然后在每个输入句子上运行它。这就是 FastCGI 将为您做的事情:您可以在进程启动中执行一次加载部分,然后运行一个服务,在输入语句进入时运行它。这很节省资源,但需要做一些工作,因为将您的脚本转换为 FastCGI 并设置服务器基础架构很麻烦。如果您期望大量使用,那绝对是您的最佳选择。

但请注意,模型中的五个特征中实际上只需要两个特征。句子中的大多数单词没有情感分数,并且情感数据库中的大多数单词不需要为这些输入计算分数。因此,数据库实现看起来会类似于(DBM 部分的粗略伪代码)

with opendbm("sentiments.db") as d:
    for sentence in sentences:
        sentiment = 0
        for word in sentence.split():
            try:
                sentiment += d[word]
            except KeyError:
                 pass
         print sentiment, sentence

每笔交易的成本较高,因此不如 FastCGI 版本优化,后者仅在启动时将整个模型加载到内存中;但它不需要你保持状态或设置 FastCGI 基础设施,而且它比为每个句子加载整个模型的愚蠢版本要高效得多。

(实际上,对于没有 FastCGI 的 Web 服务,您可以在 for 中有效地使用 opendbm,而不是相反。)

【讨论】:

  • stackoverflow.com/a/9713818/874188 建议 dbhash 如果你在 Wintendo 上(祝福你的灵魂)。
  • 你是个天才三人组!这说得通。我不知道为什么大多数 NLTK 教程都有这种想法。
  • 另外,我不认为我使用了近 400 万个特征。我只使用了 600kb 的肯定句文本文件和另一个 500kb 的否定句文件。我有 2 秒的延迟。我还没有尝试过你的数据库想法,但我很可能会在未来实现它。我只是不认为这就是我现在延迟 2 秒的原因。python 真的这么糟糕吗?
  • 另外,为什么我不能使用我的所有功能?从逻辑上讲,这不会破坏准确性吗?所以现在我为了效率而牺牲了准确性。而且我仍然需要搜索特征数据库,看看是否有与我正在检查的句子匹配的单词。因此,如果句子有 6 个单词。我必须在数据库特征的每个句子中搜索 6 个不同的单词。最明智的决定似乎是改变编程语言。
  • @tripleee Wintendo 已超过 10 年不受支持。你真的会用一个 10 年前的操作系统运行一个 Web 服务器吗?
【解决方案3】:

NLTK 是一个教学工具包;它并没有真正针对速度进行优化。如果您想要一个快速的朴素贝叶斯分类器,请使用来自scikit-learn 的分类器。在 NLTK 中有一个 wrapper (虽然直接 scikit-learn 仍然会更快)。

此外,如果您使用内存映射,scikit-learn 模型可以快速加载。首先,训练模型并将其存储在

# Let "clf" be your classifier, usually a Pipeline of CountVectorizer
# and MultinomialNB
from sklearn.externals import joblib
joblib.dump(clf, SOME_PATH, compress=0)  # turn off compression

并加载它

clf = joblib.load(SOME_PATH, mmap_mode='r')

这也允许在工作进程之间廉价地共享模型。

如果仍然太慢,请确保您处理成批的文档,而不是一次处理一个。这可以快几个数量级。

免责声明:我在 scikit-learn 和 NLTK scikit-learn 包装器代码中编写了许多朴素贝叶斯。

【讨论】:

  • 嗯,如果这个 scikit NaiveBayesClassifier 更快,为什么它不是 NLTK 的默认值?我哭了!
  • @user3912889 因为它比较老,如果你不熟悉线性代数和 SciPy,它的可读性会降低,并且它会引入 scikit-learn 依赖项。正如我所说,NLTK 是一种教学工具。它从未打算用于生产。
猜你喜欢
  • 2014-04-21
  • 1970-01-01
  • 1970-01-01
  • 2019-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-16
  • 2012-12-21
相关资源
最近更新 更多