【问题标题】:Multi-term named entities in Stanford Named Entity Recognizer斯坦福命名实体识别器中的多术语命名实体
【发布时间】:2012-11-25 18:21:28
【问题描述】:

我正在使用斯坦福命名实体识别器http://nlp.stanford.edu/software/CRF-NER.shtml,它工作正常。这是

    List<List<CoreLabel>> out = classifier.classify(text);
    for (List<CoreLabel> sentence : out) {
        for (CoreLabel word : sentence) {
            if (!StringUtils.equals(word.get(AnswerAnnotation.class), "O")) {
                namedEntities.add(word.word().trim());           
            }
        }
    }

但是我发现的问题是识别姓名和姓氏。如果识别器遇到“Joe Smith”,它会分别返回“Joe”和“Smith”。我真的很希望将“乔·史密斯”作为一个词返回。

这是否可以通过识别器或通过配置来实现?到目前为止,我没有在 javadoc 中找到任何内容。

谢谢!

【问题讨论】:

    标签: nlp stanford-nlp named-entity-recognition


    【解决方案1】:

    这是因为您的内部 for 循环正在迭代各个标记(单词)并分别添加它们。您需要进行更改以立即添加全名。

    一种方法是将内部 for 循环替换为常规 for 循环,其中包含一个 while 循环,该循环采用同一类的相邻非 O 事物并将它们添加为单个实体。*

    另一种方法是使用 CRFClassifier 方法调用:

    List<Triple<String,Integer,Integer>> classifyToCharacterOffsets(String sentences)
    

    这将为您提供整个实体,您可以通过在原始输入上使用 substring 来提取字符串形式。

    *我们分发的模型使用简单的原始 IO 标签方案,其中事物被标记为 PERSON 或 LOCATION,而适当的做法是简单地将具有相同标签的相邻标记合并。许多 NER 系统使用更复杂的标签,例如 IOB 标签,其中 B-PERS 等代码指示人员实体的起点。 CRFClassifier 类和特征工厂支持此类标签,但它们并未用于我们当前分发的模型中(截至 2012 年)。

    【讨论】:

    • CRFClassifier 中的 IOB 模型是否在 2016 年有任何消息?
    • 2017.仍在CRFClassifier 中寻找IOB 模型。
    • 是否有一些 id 可以跨多个术语实体使用以知道它是同一个实体?
    • 能否提供完整的代码示例?我不知道如何实例化这些分类器之一。
    【解决方案2】:

    classifyToCharacterOffsets 方法的对应物是(AFAIK)您无法访问实体的标签。

    正如 Christopher 所提议的,这里是一个组装“相邻非 O 事物”的循环示例。此示例还计算出现次数。

    public HashMap<String, HashMap<String, Integer>> extractEntities(String text){
    
        HashMap<String, HashMap<String, Integer>> entities =
                new HashMap<String, HashMap<String, Integer>>();
    
        for (List<CoreLabel> lcl : classifier.classify(text)) {
    
            Iterator<CoreLabel> iterator = lcl.iterator();
    
            if (!iterator.hasNext())
                continue;
    
            CoreLabel cl = iterator.next();
    
            while (iterator.hasNext()) {
                String answer =
                        cl.getString(CoreAnnotations.AnswerAnnotation.class);
    
                if (answer.equals("O")) {
                    cl = iterator.next();
                    continue;
                }
    
                if (!entities.containsKey(answer))
                    entities.put(answer, new HashMap<String, Integer>());
    
                String value = cl.getString(CoreAnnotations.ValueAnnotation.class);
    
                while (iterator.hasNext()) {
                    cl = iterator.next();
                    if (answer.equals(
                            cl.getString(CoreAnnotations.AnswerAnnotation.class)))
                        value = value + " " +
                               cl.getString(CoreAnnotations.ValueAnnotation.class);
                    else {
                        if (!entities.get(answer).containsKey(value))
                            entities.get(answer).put(value, 0);
    
                        entities.get(answer).put(value,
                                entities.get(answer).get(value) + 1);
    
                        break;
                    }
                }
    
                if (!iterator.hasNext())
                    break;
            }
        }
    
        return entities;
    }
    

    【讨论】:

      【解决方案3】:

      我遇到了同样的问题,所以我也查了一下。 Christopher Manning 提出的方法是有效的,但微妙的一点是要知道如何决定哪种分离器是合适的。可以说只允许一个空格,例如“约翰佐恩”>>一个实体。但是,我可能会找到“J.Zorn”的形式,所以我也应该允许某些标点符号。但是“杰克、詹姆斯和乔”呢?我可能会得到 2 个实体而不是 3 个(“Jack James”和“Joe”)。

      通过深入研究斯坦福 NER 课程,我实际上找到了这个想法的正确实现。他们使用它以单个 String 对象的形式导出实体。例如,在方法@​​987654322@ 中,我们有:

       private void printAnswersInlineXML(List<IN> doc, PrintWriter out) {
          final String background = flags.backgroundSymbol;
          String prevTag = background;
          for (Iterator<IN> wordIter = doc.iterator(); wordIter.hasNext();) {
            IN wi = wordIter.next();
            String tag = StringUtils.getNotNullString(wi.get(AnswerAnnotation.class));
      
            String before = StringUtils.getNotNullString(wi.get(BeforeAnnotation.class));
      
            String current = StringUtils.getNotNullString(wi.get(CoreAnnotations.OriginalTextAnnotation.class));
            if (!tag.equals(prevTag)) {
              if (!prevTag.equals(background) && !tag.equals(background)) {
                out.print("</");
                out.print(prevTag);
                out.print('>');
                out.print(before);
                out.print('<');
                out.print(tag);
                out.print('>');
              } else if (!prevTag.equals(background)) {
                out.print("</");
                out.print(prevTag);
                out.print('>');
                out.print(before);
              } else if (!tag.equals(background)) {
                out.print(before);
                out.print('<');
                out.print(tag);
                out.print('>');
              }
            } else {
              out.print(before);
            }
            out.print(current);
            String afterWS = StringUtils.getNotNullString(wi.get(AfterAnnotation.class));
      
            if (!tag.equals(background) && !wordIter.hasNext()) {
              out.print("</");
              out.print(tag);
              out.print('>');
              prevTag = background;
            } else {
              prevTag = tag;
            }
            out.print(afterWS);
          }
        }
      

      他们遍历每个单词,检查它是否具有与前一个相同的类(答案),如前所述。为此,他们利用了被认为不是实体的事实表达式,使用所谓的backgroundSymbol(“O”类)进行标记。他们还使用属性BeforeAnnotation,它表示将当前单词与前一个单词分开的字符串。最后一点可以解决我最初提出的关于选择合适分隔符的问题。

      【讨论】:

        【解决方案4】:

        以上代码:

        <List> result = classifier.classifyToCharacterOffsets(text);
        
        for (Triple<String, Integer, Integer> triple : result)
        {
            System.out.println(triple.first + " : " + text.substring(triple.second, triple.third));
        }
        

        【讨论】:

          【解决方案5】:
          List<List<CoreLabel>> out = classifier.classify(text);
          for (List<CoreLabel> sentence : out) {
              String s = "";
              String prevLabel = null;
              for (CoreLabel word : sentence) {
                if(prevLabel == null  || prevLabel.equals(word.get(CoreAnnotations.AnswerAnnotation.class)) ) {
                   s = s + " " + word;
                   prevLabel = word.get(CoreAnnotations.AnswerAnnotation.class);
                }
                else {
                  if(!prevLabel.equals("O"))
                     System.out.println(s.trim() + '/' + prevLabel + ' ');
                  s = " " + word;
                  prevLabel = word.get(CoreAnnotations.AnswerAnnotation.class);
                }
              }
              if(!prevLabel.equals("O"))
                  System.out.println(s + '/' + prevLabel + ' ');
          }
          

          我刚刚写了一个小逻辑,它工作正常。我所做的是将相邻的单词用相同的标签分组。

          【讨论】:

            【解决方案6】:

            利用已经提供给您的分类器。我相信这就是您正在寻找的:

                private static String combineNERSequence(String text) {
            
                String serializedClassifier = "edu/stanford/nlp/models/ner/english.all.3class.distsim.crf.ser.gz";      
                AbstractSequenceClassifier<CoreLabel> classifier = null;
                try {
                    classifier = CRFClassifier
                            .getClassifier(serializedClassifier);
                } catch (ClassCastException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            
                System.out.println(classifier.classifyWithInlineXML(text));
            
                //  FOR TSV FORMAT  //
                //System.out.print(classifier.classifyToString(text, "tsv", false));
            
                return classifier.classifyWithInlineXML(text);
            }
            

            【讨论】:

              【解决方案7】:

              这是我的完整代码,我使用斯坦福核心 NLP 并编写算法来连接多术语名称。

              import edu.stanford.nlp.ling.CoreAnnotations;
              import edu.stanford.nlp.ling.CoreLabel;
              import edu.stanford.nlp.pipeline.Annotation;
              import edu.stanford.nlp.pipeline.StanfordCoreNLP;
              import edu.stanford.nlp.util.CoreMap;
              import org.apache.log4j.Logger;
              
              import java.util.ArrayList;
              import java.util.List;
              import java.util.Properties;
              
              /**
               * Created by Chanuka on 8/28/14 AD.
               */
              public class FindNameEntityTypeExecutor {
              
              private static Logger logger = Logger.getLogger(FindNameEntityTypeExecutor.class);
              
              private StanfordCoreNLP pipeline;
              
              public FindNameEntityTypeExecutor() {
                  logger.info("Initializing Annotator pipeline ...");
              
                  Properties props = new Properties();
              
                  props.setProperty("annotators", "tokenize, ssplit, pos, lemma, ner");
              
                  pipeline = new StanfordCoreNLP(props);
              
                  logger.info("Annotator pipeline initialized");
              }
              
              List<String> findNameEntityType(String text, String entity) {
                  logger.info("Finding entity type matches in the " + text + " for entity type, " + entity);
              
                  // create an empty Annotation just with the given text
                  Annotation document = new Annotation(text);
              
                  // run all Annotators on this text
                  pipeline.annotate(document);
                  List<CoreMap> sentences = document.get(CoreAnnotations.SentencesAnnotation.class);
                  List<String> matches = new ArrayList<String>();
              
                  for (CoreMap sentence : sentences) {
              
                      int previousCount = 0;
                      int count = 0;
                      // traversing the words in the current sentence
                      // a CoreLabel is a CoreMap with additional token-specific methods
              
                      for (CoreLabel token : sentence.get(CoreAnnotations.TokensAnnotation.class)) {
                          String word = token.get(CoreAnnotations.TextAnnotation.class);
              
                          int previousWordIndex;
                          if (entity.equals(token.get(CoreAnnotations.NamedEntityTagAnnotation.class))) {
                              count++;
                              if (previousCount != 0 && (previousCount + 1) == count) {
                                  previousWordIndex = matches.size() - 1;
                                  String previousWord = matches.get(previousWordIndex);
                                  matches.remove(previousWordIndex);
                                  previousWord = previousWord.concat(" " + word);
                                  matches.add(previousWordIndex, previousWord);
              
                              } else {
                                  matches.add(word);
                              }
                              previousCount = count;
                          }
                          else
                          {
                              count=0;
                              previousCount=0;
                          }
              
              
                      }
              
                  }
                  return matches;
              }
              }
              

              【讨论】:

                【解决方案8】:

                另一种处理多词实体的方法。 如果多个标记具有相同的注释并且连续出现,则此代码将它们组合在一起。

                限制:
                如果同一个token有两个不同的注解,则保存最后一个。

                private Document getEntities(String fullText) {
                
                    Document entitiesList = new Document();
                    NERClassifierCombiner nerCombClassifier = loadNERClassifiers();
                
                    if (nerCombClassifier != null) {
                
                        List<List<CoreLabel>> results = nerCombClassifier.classify(fullText);
                
                        for (List<CoreLabel> coreLabels : results) {
                
                            String prevLabel = null;
                            String prevToken = null;
                
                            for (CoreLabel coreLabel : coreLabels) {
                
                                String word = coreLabel.word();
                                String annotation = coreLabel.get(CoreAnnotations.AnswerAnnotation.class);
                
                                if (!"O".equals(annotation)) {
                
                                    if (prevLabel == null) {
                                        prevLabel = annotation;
                                        prevToken = word;
                                    } else {
                
                                        if (prevLabel.equals(annotation)) {
                                            prevToken += " " + word;
                                        } else {
                                            prevLabel = annotation;
                                            prevToken = word;
                                        }
                                    }
                                } else {
                
                                    if (prevLabel != null) {
                                        entitiesList.put(prevToken, prevLabel);
                                        prevLabel = null;
                                    }
                                }
                            }
                        }
                    }
                
                    return entitiesList;
                }
                

                进口:

                Document: org.bson.Document;
                NERClassifierCombiner: edu.stanford.nlp.ie.NERClassifierCombiner;
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2017-12-09
                  • 2019-06-26
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多