2012-12-07 94 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());   
      } 
     } 
    } 

但是,我發現的問題是識別名稱和姓氏。如果識別器遇到「Joe Smith」,則分別返回「Joe」和「Smith」。我真的很喜歡它將「喬史密斯」作爲一個任期。

這可以通過識別器通過配置來實現嗎?到目前爲止我沒有在javadoc中找到任何東西。

謝謝!

回答

18

這是因爲你的內循環遍歷個別令牌(單詞)並分別添加它們。你需要改變事物來一次添加全名。

的一種方式是,用於與在其內部while循環這需要同一類的相鄰的非-O的東西,並增加了它們作爲一個單一的實體循環定期更換內部for循環。*

的另一種方式將使用CRFClassifier方法調用:

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

,這將給你整個的實體,您可以通過原始輸入使用substring提取的字符串形式。

*我們發佈的模型使用簡單的原始IO標籤方案,其中東西標記爲PERSON或LOCATION,而要做的恰當事情就是簡單地將具有相同標籤的相鄰標記合併。許多NER系統使用更復雜的標籤,例如IOB標籤,其中像B-PERS這樣的代碼指示個人實體的起始位置。 CRFClassifier類和功能工廠支持這些標籤,但它們不在我們當前分發的模型中使用(截至2012年)。

+5

2016年有沒有關於'CROBlassifier'中的'IOB'模型的消息? –

+1

2017.仍在'CRFClassifier'中尋找'IOB'模型。 – NightFury13

+0

是否有一些我們可以在多個術語實體中使用的標識來知道它是同一個實體? –

5

classifyToCharacterOffsets方法的副本是(AFAIK)不能訪問實體的標籤。

正如克里斯托弗提出的,這裏是一個組裝「鄰近非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

我有同樣的問題,所以我也查了它。克里斯托弗曼寧提出的方法是有效的,但微妙的一點是要知道如何決定哪一種分離器是合適的。可以說只有一個空間應該被允許,例如, 「約翰佐恩」>>一個實體。但是,我可能會發現「J.Zorn」的形式,所以我還應該允許使用某些標點符號。但是「傑克,詹姆斯和喬」呢?我可能會得到2個實體而不是3個(「傑克詹姆斯」和「喬」)。

通過在斯坦福大學NER課程中挖掘一下,我發現了這個想法的正確實現。他們用它來以單個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(類「O」)標記。他們還使用屬性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; 
} 

進口:

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); 
} 
相關問題