Ленивый разбор с Stanford CoreNLP, чтобы получить чувство только определенных предложений
Я ищу способы оптимизировать производительность моего конвейера настроений Stanford CoreNLP. В результате возникает желание получить сентиментальные предложения, но только те, которые содержат конкретные ключевые слова, приведенные в качестве входных данных.
Я попробовал два подхода:
Подход 1: конвейер StanfordCoreNLP, аннотирующий весь текст с настроением
Я определил конвейер аннотаторов: токенизировать, ssplit, анализировать, настроения. Я запустил его для всей статьи, затем посмотрел ключевые слова в каждом предложении и, если они присутствовали, запустил метод, возвращающий значение ключевого слова. Я не был удовлетворен, хотя, что обработка занимает пару секунд.
Это код:
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);
}
}
Вопросы
Интересно, почему парсинг в CoreNLP не "ленивый"? (В моем примере это означало бы: выполняется только тогда, когда вызывается мнение о предложении). Это из соображений производительности?
Почему парсер для одного предложения может работать почти столько же, сколько парсер для всей статьи (в моей статье было 7 предложений)? Можно ли настроить его так, чтобы он работал быстрее?