Как более эффективно аннотировать несколько Stanford CoreNLP CoreDocuments? - PullRequest
0 голосов
/ 29 апреля 2019

Я аннотирую огромное количество строк как CoreDocuments через Stanford Corenlp. Конвейеры StanfordCoreNLP имеют внутреннюю функцию для многопоточного аннотирования для оптимизации процесса, однако, насколько я вижу, объекты CoreDocument не могут использовать эту функцию в версии, которую я запускаю, - это stanford-corenlp-full-2018-10-05.

Поскольку я не мог создавать конвейерные аннотации коллекций CoreDocuments, я вместо этого попытался оптимизировать отдельные аннотации, помещая их в многопоточные методы. У меня нет проблем с многопоточным окружением. Я получаю все результаты обратно, как и ожидалось, мой единственный недостаток - это потребление времени. Я попробовал около 7 различных реализаций, и это были 3 самых быстрых:

//ForkJoinPool is initialized in the main method in my application
private static ForkJoinPool executor = new ForkJoinPool(Runtime.getRuntime().availableProcessors(), ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, false);

   public static ConcurrentMap<String, CoreDocument> getMultipleCoreDocumentsWay1(Collection<String> str) {
        ConcurrentMap<String, CoreDocument> pipelineCoreDocumentAnnotations = new MapMaker().concurrencyLevel(2).makeMap();
        str.parallelStream().forEach((str1) -> {
            CoreDocument coreDocument = new CoreDocument(str1);
            pipeline.annotate(coreDocument);
            pipelineCoreDocumentAnnotations.put(str1, coreDocument);
            System.out.println("pipelineCoreDocumentAnnotations size1: " + pipelineCoreDocumentAnnotations.size() + "\nstr size: " + str.size() + "\n");
        });
        return pipelineCoreDocumentAnnotations;
    }


     public static ConcurrentMap<String, CoreDocument> getMultipleCoreDocumentsWay4(Collection<String> str) {
        ConcurrentMap<String, CoreDocument> pipelineCoreDocumentAnnotations = new MapMaker().concurrencyLevel(2).makeMap();
        str.parallelStream().forEach((str1) -> {
            try {
                ForkJoinTask<CoreDocument> forkCD = new RecursiveTask() {
                    @Override
                    protected CoreDocument compute() {
                        CoreDocument coreDocument = new CoreDocument(str1);
                        pipeline.annotate(coreDocument);
                        return coreDocument;
                    }
                };
                forkCD.invoke();
                pipelineCoreDocumentAnnotations.put(str1, forkCD.get());
                System.out.println("pipelineCoreDocumentAnnotations2 size: " + pipelineCoreDocumentAnnotations.size() + "\nstr size: " + str.size() + "\n");
            } catch (InterruptedException | ExecutionException ex) {
                Logger.getLogger(Parsertest.class.getName()).log(Level.SEVERE, null, ex);
            }
        });
        return pipelineCoreDocumentAnnotations;
    }

    public static ConcurrentMap<String, CoreDocument> getMultipleCoreDocumentsWay7(ConcurrentMap<Integer, String> hlstatsSTR) {
        RecursiveDocumentAnnotation recursiveAnnotation = new RecursiveDocumentAnnotation(hlstatsSTR, pipeline);
        ConcurrentMap<String, CoreDocument> returnMap = new MapMaker().concurrencyLevel(2).makeMap();
        executor.execute(recursiveAnnotation);
        try {
            returnMap = recursiveAnnotation.get();
        } catch (InterruptedException | ExecutionException ex) {
            Logger.getLogger(Parsertest.class.getName()).log(Level.SEVERE, null, ex);
        }
        System.out.println("reached end\n");
        return returnMap;
    }
RecursiveDocumentAnnotation class:

    public class RecursiveDocumentAnnotation extends RecursiveTask<ConcurrentMap<String, CoreDocument>> {

    private String str;
    private StanfordCoreNLP nlp;
    private static ConcurrentMap<String, CoreDocument> pipelineCoreDocumentAnnotations;
    private static ConcurrentMap<Integer, String> hlstatsStrMap;

    public static ConcurrentMap<String, CoreDocument> getPipelineCoreDocumentAnnotations() {
        return pipelineCoreDocumentAnnotations;
    }

    public RecursiveDocumentAnnotation(ConcurrentMap<Integer, String> hlstatsStrMap, StanfordCoreNLP pipeline) {
        this.pipelineCoreDocumentAnnotations = new MapMaker().concurrencyLevel(2).makeMap();
        this.str = hlstatsStrMap.get(0);
        this.nlp = pipeline;
        this.hlstatsStrMap = hlstatsStrMap;
    }

    public RecursiveDocumentAnnotation(ConcurrentMap<Integer, String> hlstatsStrMap, StanfordCoreNLP pipeline,
            ConcurrentMap<String, CoreDocument> returnMap) {
        this.str = hlstatsStrMap.get(returnMap.size());
        this.nlp = pipeline;
        this.hlstatsStrMap = hlstatsStrMap;
        this.pipelineCoreDocumentAnnotations = returnMap;
    }

    @Override
    protected ConcurrentMap<String, CoreDocument> compute() {
        CoreDocument coreDocument = new CoreDocument(str);
        nlp.annotate(coreDocument);
        pipelineCoreDocumentAnnotations.put(str, coreDocument);
        System.out.println("hlstatsStrMap size: " + hlstatsStrMap.size() + "\npipelineCoreDocumentAnnotations size: " + pipelineCoreDocumentAnnotations.size()
                + "\n");
        if (pipelineCoreDocumentAnnotations.size() >= hlstatsStrMap.size()) {
            return pipelineCoreDocumentAnnotations;
        }
        RecursiveDocumentAnnotation recursiveAnnotation = new RecursiveDocumentAnnotation(hlstatsStrMap, nlp, pipelineCoreDocumentAnnotations);
        recursiveAnnotation.fork();
        return recursiveAnnotation.join();
    }    } 

Параллельное время1: 336562 мс.

Параллельное время4: 391556 мс.

Параллельное время7: 491639 мс.

Честно говоря, было бы величайшим, если бы сам конвейер мог каким-либо образом выполнять мультианнотацию, однако, пока я не знаю, как этого добиться, я надеюсь, что кто-то может объяснить мне, как оптимизировать аннотации CoreDocument по отдельности. PS: Объединение всех строк в единый документ с сердцевиной для аннотации также не будет тем, что я хочу, так как мне нужны индивидуальные документы с сердцевиной для последующих сравнений.

1 Ответ

0 голосов
/ 02 мая 2019

Я не рассчитал время, но вы можете попробовать этот пример кода (добавить тестовые строки в список строк) ... он должен работать с 4 документами одновременно:

package edu.stanford.nlp.examples;

import edu.stanford.nlp.pipeline.*;

import java.util.*;
import java.util.function.*;
import java.util.stream.*;


public class MultiThreadStringExample {

    public static class AnnotationCollector<T> implements Consumer<T> {

        List<T> annotations = new ArrayList<T>();

        public void accept(T ann) {
            annotations.add(ann);
        }
    }

    public static void main(String[] args) throws Exception {
        Properties props = new Properties();
        props.setProperty("annotators", "tokenize,ssplit,pos,lemma,ner,depparse");
        props.setProperty("threads", "4");
        StanfordCoreNLP pipeline = new StanfordCoreNLP(props);
        AnnotationCollector<Annotation> annCollector = new AnnotationCollector<Annotation>();
        List<String> exampleStrings = new ArrayList<String>();
        for (String exampleString : exampleStrings) {
            pipeline.annotate(new Annotation(exampleString), annCollector);
        }
        Thread.sleep(10000);
        List<CoreDocument> coreDocs =
                annCollector.annotations.stream().map(ann -> new CoreDocument(ann)).collect(Collectors.toList());
        for (CoreDocument coreDoc : coreDocs) {
            System.out.println(coreDoc.tokens());
        }
    }

}
...