懒惰解析斯坦福CoreNLP只能获得特定句子的情感

时间:2015-06-08 16:40:11

标签: java performance parsing stanford-nlp sentiment-analysis

我正在寻找优化斯坦福CoreNLP情绪管道性能的方法。因此,想要获得句子的情感,但只想包含作为输入的特定关键词。

我尝试了两种方法:

方法1:StanfordCoreNLP管道用情绪注释整个文本

我已经定义了一个注释器管道:tokenize,ssplit,parse,sentiment。我在整篇文章中运行它,然后在每个句子中查找关键字,如果它们存在,则运行返回关键字值的方法。虽然处理需要几秒钟,但我并不满意。

这是代码:

List<String> keywords = ...;
String text = ...;
Map<Integer,Integer> sentenceSentiment = new HashMap<>();

Properties props = new Properties();
props.setProperty("annotators", "tokenize, ssplit, parse, sentiment");
props.setProperty("parse.maxlen", "20");
props.setProperty("tokenize.options", "untokenizable=noneDelete");
StanfordCoreNLP pipeline = new StanfordCoreNLP(props);

Annotation annotation = pipeline.process(text); // takes 2 seconds!!!!
List<CoreMap> sentences = annotation.get(CoreAnnotations.SentencesAnnotation.class);
for (int i=0; i<sentences.size(); i++) {
    CoreMap sentence = sentences.get(i);
    if(sentenceContainsKeywords(sentence,keywords) {
        int sentiment = RNNCoreAnnotations.getPredictedClass(sentence.get(SentimentCoreAnnotations.SentimentAnnotatedTree.class));
        sentenceSentiment.put(sentence,sentiment);
    }
}

方法2:StanfordCoreNLP管道用句子注释整个文本,分离在感兴趣的句子上运行的注释器

由于第一个解决方案的性能较弱,我已经定义了第二个解决方案。我已经使用注释器定义了一个管道:tokenize,ssplit。我在每个句子中查找了关键字,如果它们存在,我只为这个句子创建了一个注释并在其上运行下一个注释器:ParserAnnotator,BinarizerAnnotator和SentimentAnnotator。

由于ParserAnnotator,结果真的不令人满意。即使我用相同的属性初始化它。有时,方法1中的文档运行时间比整个管道花费的时间更多。

List<String> keywords = ...;
String text = ...;
Map<Integer,Integer> sentenceSentiment = new HashMap<>();

Properties props = new Properties();
props.setProperty("annotators", "tokenize, ssplit"); // parsing, sentiment removed
props.setProperty("parse.maxlen", "20");
props.setProperty("tokenize.options", "untokenizable=noneDelete");
StanfordCoreNLP pipeline = new StanfordCoreNLP(props);

// initiation of annotators to be run on sentences
ParserAnnotator parserAnnotator = new ParserAnnotator("pa", props);
BinarizerAnnotator  binarizerAnnotator = new BinarizerAnnotator("ba", props);
SentimentAnnotator sentimentAnnotator = new SentimentAnnotator("sa", props);

Annotation annotation = pipeline.process(text); // takes <100 ms
List<CoreMap> sentences = annotation.get(CoreAnnotations.SentencesAnnotation.class);
for (int i=0; i<sentences.size(); i++) {
    CoreMap sentence = sentences.get(i);
    if(sentenceContainsKeywords(sentence,keywords) {
        // code required to perform annotation on one sentence
        List<CoreMap> listWithSentence = new ArrayList<CoreMap>();
        listWithSentence.add(sentence);
        Annotation sentenceAnnotation  = new Annotation(listWithSentence);

        parserAnnotator.annotate(sentenceAnnotation); // takes 50 ms up to 2 seconds!!!!
        binarizerAnnotator.annotate(sentenceAnnotation);
        sentimentAnnotator.annotate(sentenceAnnotation);
        sentence = sentenceAnnotation.get(CoreAnnotations.SentencesAnnotation.class).get(0);

        int sentiment = RNNCoreAnnotations.getPredictedClass(sentence.get(SentimentCoreAnnotations.SentimentAnnotatedTree.class));
        sentenceSentiment.put(sentence,sentiment);
    }
}

问题

  1. 我想知道为什么在CoreNLP中解析不是“懒惰”? (在我的例子中,这意味着:只有在调用句子的情绪时才会执行)。这是出于业绩原因吗?

  2. 为什么一个句子的解析器几乎和整个文章的解析器一样长(我的文章有7个句子)?是否可以以更快的方式配置它?

1 个答案:

答案 0 :(得分:5)

如果您希望加快选区解析,唯一最好的改进是使用新的shift-reduce constituency parser。它比默认的PCFG解析器快几个数量级。

以后问题的答案:

  1. 为什么CoreNLP解析不是懒惰的?这当然是可能的,但不是我们在管道中实现的东西。在必要的情况下,我们可能没有在内部看到很多用例。如果你有兴趣制作一个“lazy annotator wrapper”,我们将很乐意接受这个贡献!
  2. 为什么一个句子的解析器几乎和整个文章的解析器一样长?默认的Stanford PCFG解析器的句子长度为cubic time complexity。这就是为什么我们通常建议出于性能原因限制最大句子长度。另一方面,shift-reduce解析器相对于句子长度以线性时间运行。