2012-12-07 18 views
19

में मल्टी-टर्म नामित इकाइयां मैं स्टैनफोर्ड नामांकित इकाई पहचानकर्ता 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());   
      } 
     } 
    } 

हालांकि मुझे जो समस्या मिल रही है वह नाम और उपनामों की पहचान कर रहा है। यदि पहचानकर्ता "जो स्मिथ" से मुकाबला करता है, तो यह अलग-अलग "जो" और "स्मिथ" लौट रहा है। मैं वास्तव में इसे "जो स्मिथ" को एक शब्द के रूप में वापस करना चाहूंगा।

क्या यह कॉन्फ़िगरेशन के माध्यम से पहचानकर्ता के माध्यम से प्राप्त किया जा सकता है? मुझे अभी तक जावाडोक में कुछ भी नहीं मिला।

धन्यवाद!

उत्तर

18

ऐसा इसलिए है क्योंकि आपका आंतरिक लूप व्यक्तिगत टोकन (शब्दों) पर फिर से चल रहा है और उन्हें अलग से जोड़ रहा है। आपको एक ही समय में पूरे नाम जोड़ने के लिए चीजों को बदलने की जरूरत है।

एक तरह से इसके अंदर एक समय पाश जो एक ही कक्षा के आसन्न गैर हे बातें लेता है और उन्हें एक इकाई के रूप कहते हैं के साथ पाश के लिए एक नियमित के साथ पाश के लिए आंतरिक को बदलने के लिए है। *

एक और तरीका है

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

जो आप पूरे संस्थाओं, जो आप मूल इनपुट पर substring का उपयोग करके की स्ट्रिंग प्रपत्र निकाल सकते हैं दे देंगे: CRFClassifier विधि कॉल उपयोग करने के लिए किया जाएगा।

* हमारे द्वारा वितरित किए जाने वाले मॉडल एक साधारण कच्चे आईओ लेबल योजना का उपयोग करते हैं, जहां चीजों को व्यक्ति या स्थान लेबल किया जाता है, और करने के लिए उपयुक्त चीज केवल उसी लेबल के साथ आसन्न टोकन को जोड़ना है। कई एनईआर सिस्टम आईओबी लेबल जैसे अधिक जटिल लेबल का उपयोग करते हैं, जहां बी-पीईआर जैसे कोड इंगित करते हैं कि एक व्यक्ति इकाई कहां से शुरू होती है। सीआरएफसीलासिफायर क्लास और फीचर फैक्ट्रियां ऐसे लेबल का समर्थन करती हैं, लेकिन इन मॉडलों में उनका उपयोग नहीं किया जाता है जिन्हें हम वर्तमान में वितरित करते हैं (2012 तक)।

+5

क्या 'सीआरएफसीलासिफायर' में 'आईओबी 'मॉडल के संबंध में 2016 तक कोई खबर है? –

+1

2017. फिर भी 'सीआरएफसीलासिफायर' में 'आईओबी 'मॉडल की तलाश है। – NightFury13

+0

क्या कोई आईडी है जिसे हम बहु टर्म इकाइयों में उपयोग कर सकते हैं यह जानकर कि यह वही इकाई है? –

5

वर्गीकृत ToCharacterOffsets विधि का समकक्ष यह है कि (AFAIK) आप संस्थाओं के लेबल तक नहीं पहुंच सकते हैं।

क्रिस्टोफर द्वारा प्रस्तावित अनुसार, यहां एक लूप का एक उदाहरण है जो "आसन्न गैर-ओ चीजें" को इकट्ठा करता है। यह उदाहरण घटनाओं की संख्या की भी गणना करता है।

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

मुझे भी यही समस्या थी, इसलिए मैंने इसे भी देखा। क्रिस्टोफर मैनिंग द्वारा प्रस्तावित विधि कुशल है, लेकिन नाज़ुक बिंदु यह जानना है कि किस प्रकार का विभाजक उचित है। कोई कह सकता है कि केवल एक जगह की अनुमति होनी चाहिए, उदा। "जॉन जोर्न" >> एक इकाई। हालांकि, मुझे फॉर्म "जे। जेर्न" मिल सकता है, इसलिए मुझे कुछ विराम चिह्नों को भी अनुमति देना चाहिए। लेकिन "जैक, जेम्स और जो" के बारे में क्या? मुझे 3 ("जैक जेम्स" और "जो") की बजाय 2 इकाइयां मिल सकती हैं।

स्टैनफोर्ड एनईआर कक्षाओं में थोड़ा सा खोदकर, मुझे वास्तव में इस विचार का उचित कार्यान्वयन मिला। वे इसे एकल String ऑब्जेक्ट्स के रूप में निर्यात इकाइयों को निर्यात करने के लिए उपयोग करते हैं। उदाहरण के लिए, विधि PlainTextDocumentReaderAndWriter.printAnswersTokenizedInlineXML में, हमने:

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 (कक्षा "ओ") का उपयोग करके ध्वजांकित किया गया है। वे संपत्ति BeforeAnnotation का भी उपयोग करते हैं, जो मौजूदा शब्द को पिछले एक से अलग करने वाली स्ट्रिंग का प्रतिनिधित्व करता है।यह अंतिम बिंदु उचित विभाजक की पसंद के संबंध में शुरू की गई समस्या को हल करने की अनुमति देता है।

1

ऊपर के लिए कोड:

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

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

यहाँ मेरा पूरा कोड, मैं स्टैनफोर्ड कोर 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; 
} 
} 
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 + ' '); 
} 

मैं सिर्फ एक छोटा सा तर्क लिखा था और यह ठीक काम कर रहा है। मैंने जो किया वह समूह लेबल शब्द समान लेबल के साथ है यदि वे निकट हैं।

0

बहु शब्द इकाइयों से निपटने के लिए एक और दृष्टिकोण। यह कोड एकाधिक टोकन को एक साथ जोड़ता है यदि उनके पास समान एनोटेशन है और एक पंक्ति में जाएं।

प्रतिबंध:
इसी दो अलग-अलग टिप्पणियां हैं, तो पिछले एक सहेज लिया जाएगा।

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

आयात: classifiers के

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

उपयोग करें पहले से ही आप के लिए प्रदान की है। मेरा मानना ​​है कि आप यही देख रहे हैं:

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); 
} 
संबंधित मुद्दे