2012-12-07 3 views
19

Ich benutze die Stanford Name Entity Recognizer http://nlp.stanford.edu/software/CRF-NER.shtml und es funktioniert gut. Dies istMehr-Namen-Einheiten in Stanford namens Entity Recognizer

Allerdings das Problem, das ich finde, ist die Identifizierung von Namen und Nachnamen. Wenn der Erkenner auf "Joe Smith" trifft, gibt er "Joe" und "Smith" getrennt zurück. Ich würde es wirklich gerne "Joe Smith" als einen Begriff zurückgeben.

Konnte dies durch den Erkenner vielleicht durch eine Konfiguration erreicht werden? Ich habe bis jetzt nichts im Javadoc gefunden.

Danke!

Antwort

18

Dies liegt daran, dass Ihre innere for-Schleife über einzelne Token (Wörter) iteriert und separat hinzugefügt wird. Sie müssen Dinge ändern, um ganze Namen auf einmal hinzuzufügen.

Eine Möglichkeit ist, die innere for-Schleife mit einem regelmäßigen for-Schleife mit einer while-Schleife im Innern zu ersetzen, die neben nicht-O Sachen der gleichen Klasse nimmt und fügt sie als eine einzige Einheit. *

Einen anderen Weg, wäre die CRFClassifier Methodenaufruf zu verwenden:

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

, die Sie ganze Einheiten geben, die Ihnen die String Form unter Verwendung substring auf dem ursprünglichen Eingangs extrahieren kann.

* Die Modelle, die wir vertreiben, verwenden ein einfaches RAW-IO-Label-Schema, wo die Dinge PERSON oder LOCATION heißen, und die passende Sache ist einfach, benachbarte Token mit demselben Label zu verschmelzen. Viele NER-Systeme verwenden komplexere Bezeichnungen wie IOB-Etiketten, wobei Codes wie B-PERS angeben, wo eine Personenentität startet. Die CRFClassifier-Klassen- und -Feature-Factorys unterstützen solche Labels, sie werden jedoch in den Modellen, die wir derzeit verteilen, nicht verwendet (Stand 2012).

+5

Gibt es irgendwelche Neuigkeiten ab 2016 in Bezug auf 'IOB' Modelle in' CRFClassifier'? –

+1

2017. Suche noch nach 'IOB' Modellen in' CRFClassifier'. – NightFury13

+0

Gibt es eine ID, die wir für mehrere Entitäten verwenden können, um zu wissen, dass es sich um dieselbe Entität handelt? –

5

Das Gegenstück der classifyToCharacterOffsets-Methode ist, dass (AFAIK) Sie nicht auf die Bezeichnung der Entitäten zugreifen können.

Wie von Christopher vorgeschlagen, hier ist ein Beispiel für eine Schleife, die "benachbarte Nicht-O-Dinge" zusammenstellt. In diesem Beispiel wird auch die Anzahl der Vorkommen gezählt.

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

Ich hatte das gleiche Problem, also habe ich auch nachgesehen. Die von Christopher Manning vorgeschlagene Methode ist effizient, aber der heikle Punkt ist zu wissen, wie man entscheidet, welche Art von Separator geeignet ist. Man könnte sagen, dass nur ein Raum erlaubt sein sollte, z. "John Zorn" >> eine Einheit. Ich kann jedoch das Formular "J.Zorn" finden, daher sollte ich auch bestimmte Satzzeichen zulassen. Aber was ist mit "Jack, James und Joe"? Ich könnte 2 Einheiten anstelle von 3 bekommen ("Jack James" und "Joe").

Durch ein wenig in den Stanford NER-Klassen zu graben, fand ich tatsächlich eine ordnungsgemäße Umsetzung dieser Idee. Sie verwenden es, um Objekte in Form von einzelnen String Objekten zu exportieren. Zum Beispiel wird in dem Verfahren PlainTextDocumentReaderAndWriter.printAnswersTokenizedInlineXML, haben wir:

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); 
    } 
    } 

Sie iterieren jedem Wort, zu überprüfen, ob es die gleiche Klasse (Antwort) als die vorherige hat, wie zuvor erläutert. Dabei nutzen sie die Tatsache aus, dass Ausdrücke, die als Nicht-Entitäten betrachtet werden, mit der sogenannten backgroundSymbol (Klasse "O") gekennzeichnet werden. Sie verwenden auch die Eigenschaft BeforeAnnotation, die die Zeichenfolge darstellt, die das aktuelle Wort vom vorherigen trennt.Dieser letzte Punkt ermöglicht die Lösung des Problems, das ich eingangs angesprochen habe, was die Wahl eines geeigneten Separators betrifft.

1

-Code für die oben:

<List> result = classifier.classifyToCharacterOffsets(text); 

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

Hier ist mein vollständiger Code verwende ich Kern NLP Stanford und schreibe Algorithmus Multi Zeit Namen verketten.

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; 
} 
} 
2
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 + ' '); 
} 

Ich schrieb nur eine kleine Logik und es funktioniert gut. Was ich gemacht habe, ist Wörter mit demselben Label zu gruppieren, wenn sie benachbart sind.

0

Ein anderer Ansatz, um mit Multi-Wörter-Entitäten umzugehen. Dieser Code kombiniert mehrere Token zusammen, wenn sie die gleiche Annotation haben und hintereinander gehen.

Einschränkung:
Wenn das gleiche Token zwei verschiedene Anmerkungen hat, wird die letzte gespeichert werden.

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; 
} 

Importe:

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

Nutzen Sie die Klassifizierer bereits zur Verfügung gestellt. Ich glaube, das ist, was Sie suchen:

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); 
}