NLTK NaiveBayesClassifier在Python中速度极慢?

时间:2014-08-31 19:07:21

标签: python machine-learning nltk

我使用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秒钟。我希望< 1秒(我在网站上运行这个)。

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

3 个答案:

答案 0 :(得分: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包装代码中写了许多天真的贝叶斯。

答案 1 :(得分:0)

我想pickle save格式只保存训练数据,并且每次加载时都会重新计算模型。

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

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

答案 2 :(得分:0)

如果您真的要使用 400万 15,000 功能来分析可能的十几个单词,那么大多数功能都不会被使用。这表明使用某种基于磁盘的数据库来代替这些功能,并且仅提供您需要的数据库。即使对于长句和低效率的数据库,4寻求x 50个单词仍然比你现在看到的少 - 在最坏的情况下可能是几百毫秒,但肯定不是多秒。

使用NDBM或GDBM后端查看anydbm作为开始,然后根据熟悉程度和可用性考虑其他后端。


您的后续评论似乎暗示了对您正在做的事情和/或事情应该如何运作的基本误解。让我们在词典中用五个单词做一个简单的例子。

# 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基础设施,而且它比为每个句子加载整个模型的愚蠢版本更有效。

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