Úvod do Apache OpenNLP

1. Prehľad

Apache OpenNLP je open source knižnica Java pre spracovanie prirodzeného jazyka.

Obsahuje rozhranie API pre prípady použitia, ako je rozpoznávanie pomenovaných entít, detekcia vety, označovanie POS a tokenizácia.

V tomto výučbe sa pozrieme na to, ako používať toto API na rôzne prípady použitia.

2. Nastavenie Maven

Najskôr je potrebné pridať k našej hlavnej závislosti pom.xml:

 org.apache.opennlp opennlp-tools 1.8.4 

Najnovšiu stabilnú verziu nájdete na serveri Maven Central.

Niektoré prípady použitia vyžadujú trénované modely. Tu si môžete stiahnuť preddefinované modely a podrobné informácie o týchto modeloch tu.

3. Detekcia vety

Začnime tým, že pochopíme, čo je to veta.

Detekcia vety je o identifikácii začiatku a konca vety, čo zvyčajne závisí od použitého jazyka. Toto sa tiež nazýva „Sentence Boundary Disambiguation“ (SBD).

V niektorých prípadoch, detekcia vety je dosť náročná z dôvodu nejednoznačnosti dobového znaku. Bodka zvyčajne označuje koniec vety, ale môže sa tiež zobraziť v e-mailovej adrese, skratke, desatinnej čiarke a na mnohých ďalších miestach.

Pokiaľ ide o väčšinu úloh NLP, na detekciu viet potrebujeme ako vstup vyškolený model, ktorý by podľa očakávania mal byť umiestnený v / zdroje priečinok.

Na implementáciu detekcie viet načítame model a odovzdáme ho do inštancie SentenceDetectorME. Potom jednoducho odovzdáme text do sentDetect () metóda na rozdelenie na hranici vety:

@Test public void givenEnglishModel_whenDetect_thenSentencesAreDetected () vyvolá výnimku {String paragraph = "Toto je vyhlásenie. Toto je ďalšie vyhlásenie." + "Teraz je abstraktné slovo pre čas," + "ktoré neustále letí. A moja e-mailová adresa je [chránený e-mailom]"; InputStream je = getClass (). GetResourceAsStream ("/ models / en-sent.bin"); SentenceModel model = nový SentenceModel (je); SentenceDetectorME sdetector = nový SentenceDetectorME (model); Reťazce viet [] = sdetector.sentDetect (odsek); assertThat (vety) .obsahuje („Toto je vyhlásenie.“, „Toto je ďalšie vyhlásenie.“, „Teraz je abstraktné slovo pre čas, ktoré vždy letí.“, „A moja e-mailová adresa je [chránená e-mailom]“) ); }

Poznámka:prípona „ME“ sa používa v mnohých názvoch tried v Apache OpenNLP a predstavuje algoritmus založený na „maximálnej entropii“.

4. Tokenizácia

Teraz, keď môžeme rozdeliť korpus textu na vety, môžeme začať analyzovať vetu podrobnejšie.

Cieľom tokenizácie je rozdelenie vety na menšie časti, ktoré sa nazývajú tokeny. Zvyčajne sú to tokeny slová, čísla alebo interpunkčné znamienka.

V OpenNLP sú k dispozícii tri typy tokenizérov.

4.1. Použitím TokenizerME

V takom prípade musíme najskôr načítať model. Odtiaľto si môžeme stiahnuť modelový súbor a vložiť ho do / zdroje priečinok a odtiaľ ho načítajte.

Ďalej vytvoríme inštanciu TokenizerME pomocou načítaného modelu a použite tokenize () metóda na vykonanie tokenizácie na ľubovoľnom Reťazec:

@Test public void givenEnglishModel_whenTokenize_thenTokensAreDetected () vyvolá výnimku {InputStream inputStream = getClass () .getResourceAsStream ("/ models / en-token.bin"); TokenizerModel model = nový TokenizerModel (inputStream); TokenizerME tokenizer = nový TokenizerME (model); String [] tokens = tokenizer.tokenize ("Baeldung je jarný zdroj."); assertThat (tokeny) .obsahuje („Baeldung“, „je“, „a“, „jar“, „zdroj“, „.“); }

Ako vidíme, tokenizer identifikoval všetky slová a bodku ako samostatné tokeny. Tento tokenizer je možné použiť aj s prispôsobeným trénovaným modelom.

4.2. WhitespaceTokenizer

Ako naznačuje názov, tento tokenizer jednoducho rozdelí vetu na tokeny pomocou medzery ako oddeľovača:

@Test public void givenWhitespaceTokenizer_whenTokenize_thenTokensAreDetected () vyvolá výnimku {WhitespaceTokenizer tokenizer = WhitespaceTokenizer.INSTANCE; String [] tokens = tokenizer.tokenize ("Baeldung je jarný zdroj."); assertThat (tokeny) .obsahuje („Baeldung“, „je“, „a“, „jar“, „zdroj“); }

Vidíme, že veta bola rozdelená na biele medzery, a preto dostaneme „Zdroj“. (s bodkou na konci) ako jeden token namiesto dvoch rôznych tokenov pre slovo „Zdroj“ a bodku.

4.3. SimpleTokenizer

Tento tokenizer je trochu sofistikovanejší ako WhitespaceTokenizer a vetu rozdelí na slová, čísla a interpunkčné znamienka. Je to predvolené správanie a nevyžaduje žiadny model:

@Test public void givenSimpleTokenizer_whenTokenize_thenTokensAreDetected () vyvolá výnimku {SimpleTokenizer tokenizer = SimpleTokenizer.INSTANCE; Reťazec [] tokens = tokenizer .tokenize ("Baeldung je jarný zdroj."); assertThat (tokeny) .obsahuje („Baeldung“, „je“, „a“, „jar“, „zdroj“, „.“); }

5. Rozpoznanie pomenovanej entity

Teraz, keď sme pochopili tokenizáciu, poďme sa pozrieť na prvý prípad použitia, ktorý je založený na úspešnej tokenizácii: rozpoznávanie pomenovaných entít (NER).

Cieľom NER je nájsť pomenované entity ako ľudia, miesta, organizácie a ďalšie pomenované veci v danom texte.

OpenNLP používa preddefinované modely pre mená osôb, dátum a čas, miesta a organizácie. Musíme model načítať pomocou TokenNameFinderModel aodovzdať do inštancie NameFinderME. Potom môžeme použiť Nájsť() metóda na vyhľadanie pomenovaných entít v danom texte:

@Test public void givenEnglishPersonModel_whenNER_thenPersonsAreDetected () vyvolá výnimku {SimpleTokenizer tokenizer = SimpleTokenizer.INSTANCE; Reťazec [] tokens = tokenizer .tokenize ("John má 26 rokov. Jeho + alebo jeho najlepší priateľ je meno Leonard. Má sestru menom Penny."); InputStream inputStreamNameFinder = getClass () .getResourceAsStream ("/ models / en-ner-person.bin"); TokenNameFinderModel model = nový TokenNameFinderModel (inputStreamNameFinder); NameFinderME nameFinderME = nový NameFinderME (model); Zoznam rozpätí = Arrays.asList (nameFinderME.find (tokeny)); assertThat (spans.toString ()) .isEqualTo ("[[0..1) osoba, [13..14) osoba, [20..21) osoba]"); }

Ako vidíme v tvrdení, výsledkom je zoznam Rozpätie objekty obsahujúce začiatočný a konečný index tokenov, ktoré v texte tvoria pomenované entity.

6. Označovanie časti reči

Ďalším prípadom použitia, ktorý ako vstup vyžaduje zoznam tokenov, je označovanie časti reči.

Časť reči (POS) identifikuje typ slova. OpenNLP používa nasledujúce značky pre rôzne slovné druhy:

  • NN - podstatné meno, jednotné číslo alebo omša
  • DT - determinátor
  • VB - sloveso, základný tvar
  • VBD - sloveso, minulý čas
  • VBZ - sloveso, tretia osoba jednotného čísla prítomný
  • IN - predložka alebo podraďovacia spojka
  • NNP - vlastné podstatné meno, jednotné číslo
  • DO - slovo „do“
  • J J - prídavné meno

Jedná sa o rovnaké značky, ako sú definované v banke Penn Tree Bank. Celý zoznam nájdete v tomto zozname.

Podobne ako v príklade NER, načítame príslušný model a potom použijeme POSTaggerME a jeho metóda značka () na množine žetónov na označenie vety:

@ Test public void givenPOSModel_whenPOSTagging_thenPOSAreDetected () vyvolá výnimku {SimpleTokenizer tokenizer = SimpleTokenizer.INSTANCE; Reťazec [] tokens = tokenizer.tokenize ("John má sestru menom Penny."); InputStream inputStreamPOSTagger = getClass () .getResourceAsStream ("/ models / en-pos-maxent.bin"); POSModel posModel = nový POSModel (inputStreamPOSTagger); POSTaggerME posTagger = nový POSTaggerME (posModel); Reťazcové značky [] = posTagger.tag (tokens); assertThat (tagy) .obsahuje ("NNP", "VBZ", "DT", "NN", "VBN", "NNP", "."); }

The značka () metóda mapuje tokeny do zoznamu POS tagov. Výsledok v príklade je:

  1. „John“ - NNP (vlastné meno)
  2. “Has” - VBZ (sloveso)
  3. „A“ - DT (determinátor)
  4. “Sister” - NN (podstatné meno)
  5. „Pomenované“ - VBZ (sloveso)
  6. „Penny“ -NNP (vlastné meno)
  7. „.“ - obdobie

7. Lemmatizácia

Teraz, keď máme vo vete informácie o časti prejavu tokenov, môžeme text analyzovať ešte ďalej.

Lemmatizácia je proces mapovania slovnej formy ktoré môžu mať napäté pohlavie, pohlavie, náladu alebo iné informácie k základnému tvaru slova - tiež sa nazýva jeho „lemma“.

Lemmatizátor vezme na vstup token a jeho časť slovného spojenia a vráti lemmu slova. Pred Lemmatizáciou by preto mala byť veta odovzdaná prostredníctvom tokenizéra a označovača POS.

Apache OpenNLP poskytuje dva typy lemmatizácie:

  • Štatistické - potrebuje model lemmatizátora zostavený pomocou tréningových údajov na nájdenie lemmy daného slova
  • Založené na slovníku - vyžaduje slovník, ktorý obsahuje všetky platné kombinácie slova, POS tagy a príslušnú lemmu

Pre štatistickú lemmatizáciu musíme trénovať model, zatiaľ čo pre slovnú lemmatizáciu potrebujeme iba slovníkový súbor, ako je tento.

Pozrime sa na príklad kódu pomocou súboru so slovníkom:

@Test public void givenEnglishDictionary_whenLemmatize_thenLemmasAreDetected () vyvolá výnimku {SimpleTokenizer tokenizer = SimpleTokenizer.INSTANCE; Reťazec [] tokens = tokenizer.tokenize ("John má sestru menom Penny."); InputStream inputStreamPOSTagger = getClass () .getResourceAsStream ("/ models / en-pos-maxent.bin"); POSModel posModel = nový POSModel (inputStreamPOSTagger); POSTaggerME posTagger = nový POSTaggerME (posModel); Reťazcové značky [] = posTagger.tag (tokens); InputStream dictLemmatizer = getClass () .getResourceAsStream ("/ models / en-lemmatizer.dict"); DictionaryLemmatizer lemmatizer = nový slovník DictionaryLemmatizer (dictLemmatizer); Reťazec [] lemmas = lemmatizer.lemmatize (tokeny, značky); assertThat (lemmy) .obsahuje ("O", "mať", "a", "sestra", "meno", "O", "O"); }

Ako vidíme, pre každý žetón dostaneme lemmu. „O“ označuje, že lemmu nebolo možné určiť, pretože slovo je vlastné podstatné meno. Takže nemáme lemmu pre „Johna“ a „Penny“.

Ale identifikovali sme lemmy pre ďalšie slová vety:

  • mal
  • a - a
  • sestra - sestra
  • pomenovaný - meno

8. Chunking

Informácie o časti reči sú nevyhnutné aj pri chunkingu - rozdelení viet do gramaticky významných slovných skupín, ako sú podstatné mená alebo slovesné skupiny.

Podobne ako predtým, aj predtým, ako zavoláme, tokenizujeme vetu a na tokenoch použijeme značkovanie časti reči kus () metóda:

@Test public void givenChunkerModel_whenChunk_thenChunksAreDetected () vyvolá výnimku {SimpleTokenizer tokenizer = SimpleTokenizer.INSTANCE; String [] tokens = tokenizer.tokenize ("Ráta sa s tým, že deficit bežného účtu sa zníži na iba 8 miliárd."); InputStream inputStreamPOSTagger = getClass () .getResourceAsStream ("/ models / en-pos-maxent.bin"); POSModel posModel = nový POSModel (inputStreamPOSTagger); POSTaggerME posTagger = nový POSTaggerME (posModel); Reťazcové značky [] = posTagger.tag (tokens); InputStream inputStreamChunker = getClass () .getResourceAsStream ("/ models / en-chunker.bin"); ChunkerModel chunkerModel = nový ChunkerModel (inputStreamChunker); ChunkerME chunker = nový ChunkerME (chunkerModel); Reťazec [] chunks = chunker.chunk (tokeny, značky); assertThat (chunks) .obsahuje („B-NP“, „B-VP“, „B-NP“, „I-NP“, „I-NP“, „I-NP“, „B-VP“, „ I-VP "," B-PP "," B-NP "," I-NP "," I-NP "," O "); }

Ako vidíme, z chunkera dostaneme výstup pre každý token. „B“ predstavuje začiatok bloku, „I“ predstavuje pokračovanie bloku a „O“ žiadny blok.

Pri analýze výstupu z nášho príkladu dostaneme 6 blokov:

  1. „On“ - podstatné meno
  2. „Počíta“ - slovesná fráza
  3. „Deficit bežného účtu“ - podstatné meno
  4. „Sa zúži“ - slovesná fráza
  5. „To“ - predložková fráza
  6. „Iba 8 miliárd“ - podstatné meno

9. Detekcia jazyka

Okrem už diskutovaných prípadov použitia, OpenNLP poskytuje aj rozhranie API na detekciu jazykov, ktoré umožňuje identifikovať jazyk určitého textu.

Na detekciu jazyka potrebujeme školiaci dátový súbor. Takýto súbor obsahuje riadky s vetami v určitom jazyku. Každý riadok je označený správnym jazykom, ktorý poskytuje vstup do algoritmov strojového učenia.

Vzorový školiaci dátový súbor na zisťovanie jazykov si môžete stiahnuť tu.

Môžeme načítať dátový súbor tréningu do a LanguageDetectorSampleStream, definujte niektoré parametre tréningových údajov, vytvorte model a potom pomocou modelu zistite jazyk textu:

@Test public void givenLanguageDictionary_whenLanguageDetect_thenLanguageIsDetected () vyvolá FileNotFoundException, IOException {InputStreamFactory dataIn = new MarkableFileInputStreamFactory (new File ("src / main / resources / models / DoccatSample.txt")) ObjectStream lineStream = nový PlainTextByLineStream (dataIn, "UTF-8"); LanguageDetectorSampleStream sampleStream = nový LanguageDetectorSampleStream (lineStream); Parametre TrainingParameters = nové TrainingParameters (); params.put (TrainingParameters.ITERATIONS_PARAM, 100); params.put (TrainingParameters.CUTOFF_PARAM, 5); params.put ("DataIndexer", "TwoPass"); params.put (TrainingParameters.ALGORITHM_PARAM, "NAIVEBAYES"); LanguageDetectorModel model = LanguageDetectorME .train (sampleStream, params, new LanguageDetectorFactory ()); LanguageDetector ld = nový LanguageDetectorME (model); Jazyk [] languages ​​= ld .predictLanguages ​​("estava em uma marcenaria na Rua Bruno"); assertThat (Arrays.asList (jazyky)) .extracting ("lang", "confidence") .contains (tuple ("pob", 0.9999999950605625), n-tice ("ita", 4.939427661577956E-9), n-tice ("spa", 9,665954064665144E-15), n-tica ("fra", 8,250349924885834E-25))); }

Výsledkom je zoznam najpravdepodobnejších jazykov spolu so skóre spoľahlivosti.

A s bohatými modelmi môžeme pomocou tohto typu detekcie dosiahnuť veľmi vyššiu presnosť.

5. Záver

Veľa sme tu preskúmali, zo zaujímavých schopností OpenNLP. Zamerali sme sa na niekoľko zaujímavých funkcií na vykonávanie úloh NLP, ako je lemmatizácia, označovanie POS, tokenizácia, detekcia vety, detekcia jazyka a ďalšie.

Kompletnú implementáciu všetkých vyššie uvedených riešení nájdete ako vždy na GitHub.


$config[zx-auto] not found$config[zx-overlay] not found