如何在 Python 中加速 Stanford NLP?

How to speedup Stanford NLP in Python?

import numpy as np
from nltk.tag import StanfordNERTagger
from nltk.tokenize import word_tokenize
    #english.all.3class.distsim.crf.ser.gz
st = StanfordNERTagger('/media/sf_codebase/modules/stanford-ner-2018-10-16/classifiers/english.all.3class.distsim.crf.ser.gz',
                           '/media/sf_codebase/modules/stanford-ner-2018-10-16/stanford-ner.jar',
                           encoding='utf-8')

初始化上面的代码后,Stanford NLP 下面的代码需要 10 秒来标记文本,如下所示。如何加速?

%%time
text="My name is John Doe"
tokenized_text = word_tokenize(text)
classified_text = st.tag(tokenized_text)
print (classified_text)

输出

[('My', 'O'), ('name', 'O'), ('is', 'O'), ('John', 'PERSON'), ('Doe', 'PERSON')]
CPU times: user 4 ms, sys: 20 ms, total: 24 ms
Wall time: 10.9 s

找到答案。

在 Stanford NLP 解压文件夹中后台启动 Stanford NLP 服务器。

java -Djava.ext.dirs=./lib -cp stanford-ner.jar edu.stanford.nlp.ie.NERServer -port 9199 -loadClassifier ./classifiers/english.all.3class.distsim.crf.ser.gz

然后使用 sner 库在 Python 中启动 Stanford NLP Server 标注器。

from sner import Ner
tagger = Ner(host='localhost',port=9199)

然后 运行 标注器。

%%time
classified_text=tagger.get_entities(text)
print (classified_text)

输出:

    [('My', 'O'), ('name', 'O'), ('is', 'O'), ('John', 'PERSON'), ('Doe', 'PERSON')]
CPU times: user 4 ms, sys: 0 ns, total: 4 ms
Wall time: 18.2 ms

在计时方面的性能提高了近 300 倍!哇!

NLTK 中的另一个解决方案是 不使用 旧的 nltk.tag.StanfordNERTagger 而是使用更新的 nltk.parse.CoreNLPParser 。参见,例如,https://github.com/nltk/nltk/wiki/Stanford-CoreNLP-API-in-NLTK .

更一般地说,获得良好性能的秘诀确实是在 Java 端使用服务器,您可以重复调用它,而不必为每个处理的句子启动新的子进程。如果您只需要 NER,您可以使用 NERServer,或者对所有 CoreNLP 功能使用 StanfordCoreNLPServer。它有许多 Python 接口,请参阅:https://stanfordnlp.github.io/CoreNLP/other-languages.html#python

尝试了几个选项后,我喜欢Stanza。它由 Stanford 开发,实现起来非常简单,我不必自己弄清楚如何正确启动服务器,它极大地提高了我的程序速度。它实现了 18 种不同的对象分类。

我通过 link provided in Christopher Manning's answer.

找到了 Stanza

下载: pip install stanza

然后在 Python:

import stanza
stanza.download('en') # download English model
nlp = stanza.Pipeline('en') # initialize English neural pipeline
doc = nlp("My name is John Doe.") # run annotation over a sentence or multiple sentences

如果你只想要一个特定的工具(NER),你可以用 processors 指定为: nlp = stanza.Pipeline('en',processors='tokenize,ner')

对于类似于 OP 生成的输出:

classified_text = [(token.text,token.ner) for i, sentence in enumerate(doc.sentences) for token in sentence.tokens]
print(classified_text)
[('My', 'O'), ('name', 'O'), ('is', 'O'), ('John', 'B-PERSON'), ('Doe', 'E-PERSON')]

但要生成仅包含可识别实体的单词的列表:

classified_text = [(ent.text,ent.type) for ent in doc.ents]
[('John Doe', 'PERSON')]

它产生了一些我非常喜欢的特性:

  1. 它不是将每个单词归类为一个单独的人实体,而是将 John Doe 合并为一个 'PERSON' 对象。
  2. 如果您确实需要每个单独的词,您可以提取这些词并识别它是对象的哪一部分('B' 表示对象中的第一个词,'I' 表示中间词, 'E' 对象中的最后一个单词)