Uvod u Apache OpenNLP

1. Pregled

Apache OpenNLP je Java biblioteka za obradu prirodnog jezika otvorenog koda.

Sadrži API za slučajeve upotrebe poput prepoznavanja imenovanih entiteta, otkrivanja rečenice, označavanja POS-a i tokenizacije.

U ovom ćemo uputstvu pogledati kako koristiti ovaj API za različite slučajeve upotrebe.

2. Postavljanje Mavena

Prvo, moramo dodati glavnu ovisnost o našoj pom.xml:

 org.apache.opennlp opennlp-tools 1.8.4 

Najnoviju stabilnu verziju možete pronaći na Maven Central.

Neki slučajevi upotrebe trebaju obučene modele. Unaprijed definirane modele možete preuzeti ovdje, a detaljne informacije o tim modelima ovdje.

3. Otkrivanje rečenice

Počnimo s razumijevanjem što je rečenica.

Otkrivanje rečenice odnosi se na prepoznavanje početka i kraja rečenice, što obično ovisi o dotičnom jeziku. To se također naziva „višeznačna razgraničenja kazne“ (SBD).

U nekim slučajevima, otkrivanje rečenica prilično je izazovno zbog dvosmislene prirode karaktera razdoblja. Razdoblje obično označava kraj rečenice, ali se može pojaviti i u adresi e-pošte, kratici, decimalnom broju i mnogim drugim mjestima.

Kao i za većinu NLP zadataka, za otkrivanje rečenica potreban nam je istrenirani model kao ulaz, za koji očekujemo da će boraviti u /resursi mapu.

Da bismo implementirali otkrivanje rečenica, učitavamo model i prosljeđujemo ga u primjerak SentenceDetectorME. Zatim jednostavno prenosimo tekst u sentDetect () metoda za razdvajanje na rečenicama:

@Test javna praznina givenEnglishModel_whenDetect_thenSentencesAreDetected () baca iznimku {String paragraph = "Ovo je izjava. Ovo je druga izjava." + "Sad je apstraktna riječ za vrijeme," + "koja uvijek leti. A moja adresa e-pošte je [zaštićena e-poštom]"; Ulazni tok je = getClass (). GetResourceAsStream ("/ models / en-sent.bin"); Model SentenceModel = novi SentenceModel (je); SentenceDetectorME sdetector = novi SentenceDetectorME (model); Niz rečenice [] = sdetector.sentDetect (odlomak); assertThat (rečenice) .contains ("Ovo je izjava.", "Ovo je još jedna izjava.", "Sad je apstraktna riječ za vrijeme, koja uvijek leti.", "A moja adresa e-pošte je [zaštićena e-poštom]" ); }

Bilješka:sufiks "ME" koristi se u mnogim imenima klasa u Apache OpenNLP-u i predstavlja algoritam koji se temelji na "Maximum Entropy".

4. Tokeniziranje

Sad kad korpus teksta možemo podijeliti na rečenice, možemo početi detaljnije analizirati rečenicu.

Cilj tokenizacije je podijeliti rečenicu na manje dijelove koji se nazivaju žetoni. Obično su to znakovi riječi, brojevi ili interpunkcijski znakovi.

U OpenNLP-u su dostupne tri vrste tokenizatora.

4.1. Koristeći TokenizerME

U ovom slučaju, prvo moramo učitati model. Datoteku modela možemo preuzeti odavde i staviti je u /resursi mapu i učitajte je od tamo.

Dalje ćemo stvoriti instancu TokenizerME koristeći učitani model i koristite tokenizirati () metoda za izvođenje tokenizacije na bilo kojem Niz:

@Test javna praznina givenEnglishModel_whenTokenize_thenTokensAreDetected () baca izuzetak {InputStream inputStream = getClass () .getResourceAsStream ("/ models / en-token.bin"); TokenizerModel model = novi TokenizerModel (inputStream); TokenizerME tokenizer = novi TokenizerME (model); String [] tokens = tokenizer.tokenize ("Baeldung je proljetni resurs."); assertThat (tokeni) .contens ("Baeldung", "is", "a", "Spring", "Resource", "."); }

Kao što vidimo, tokenizer je identificirao sve riječi i znak razdoblja kao zasebne znakove. Ovaj tokenizer može se koristiti i s prilagođenim obučenim modelom.

4.2. WhitespaceTokenizer

Kao što i samo ime govori, ovaj tokenizer jednostavno dijeli rečenicu na tokene koristeći razmake kao razgraničenja:

@Test javna praznina givenWhitespaceTokenizer_whenTokenize_thenTokensAreDetected () baca izuzetak {WhitespaceTokenizer tokenizer = WhitespaceTokenizer.INSTANCE; String [] tokens = tokenizer.tokenize ("Baeldung je proljetni resurs."); assertThat (tokeni) .contens ("Baeldung", "is", "a", "Spring", "Resource."); }

Možemo vidjeti da su rečenice razdvojene razmacima i tako dobivamo "Resurs". (s znakom razdoblja na kraju) kao jedan žeton umjesto dva različita tokena za riječ "Resurs" i znak razdoblja.

4.3. SimpleTokenizer

Ovaj je tokenizer malo sofisticiraniji od WhitespaceTokenizer i dijeli rečenicu na riječi, brojeve i interpunkcijske znakove. To je zadano ponašanje i ne zahtijeva nijedan model:

@Test javna praznina givenSimpleTokenizer_whenTokenize_thenTokensAreDetected () baca iznimku {SimpleTokenizer tokenizer = SimpleTokenizer.INSTANCE; String [] tokens = tokenizer .tokenize ("Baeldung je proljetni resurs."); assertThat (tokeni) .contens ("Baeldung", "is", "a", "Spring", "Resource", "."); }

5. Priznanje imenovanog entiteta

Sad kad smo razumjeli tokenizaciju, pogledajmo prvi slučaj korištenja koji se temelji na uspješnoj tokenizaciji: prepoznavanje imenovanog entiteta (NER).

Cilj NER-a je pronaći imenovane entitete kao što su ljudi, lokacije, organizacije i druge imenovane stvari u zadanom tekstu.

OpenNLP koristi unaprijed definirane modele za imena osoba, datum i vrijeme, lokacije i organizacije. Moramo učitati model pomoću TokenNameFinderModel iprenesite ga u instancu NameFinderME. Tada možemo koristiti pronaći() metoda za pronalaženje imenovanih entiteta u zadanom tekstu:

@Test javna praznina givenEnglishPersonModel_whenNER_thenPersonsAreDetected () baca iznimku {SimpleTokenizer tokenizer = SimpleTokenizer.INSTANCE; String [] tokens = tokenizer .tokenize ("John ima 26 godina. Ime njegovog najboljeg prijatelja" + "je Leonard. Ima sestru koja se zove Penny."); InputStream inputStreamNameFinder = getClass () .getResourceAsStream ("/ models / en-ner-person.bin"); TokenNameFinderModel model = novi TokenNameFinderModel (inputStreamNameFinder); NameFinderME nameFinderME = novi NameFinderME (model); Rasponi popisa = Arrays.asList (nameFinderME.find (tokeni)); assertThat (spans.toString ()) .isEqualTo ("[[0..1) osoba, [13..14) osoba, [20..21) osoba]"); }

Kao što možemo vidjeti u tvrdnji, rezultat je popis Raspon objekti koji sadrže indekse početka i kraja žetona koji čine imenovane cjeline u tekstu.

6. Označavanje dijela govora

Još jedan slučaj upotrebe kojem je popis tokena potreban kao ulaz je označavanje dijela govora.

Dio govora (POS) identificira vrstu riječi. OpenNLP koristi sljedeće oznake za različite dijelove govora:

  • NN - imenica, jednina ili masa
  • DT - odrednik
  • VB - glagol, osnovni oblik
  • VBD - glagol, prošlo vrijeme
  • VBZ - glagol, treće lice jednine prezenta
  • U - prijedlog ili podređeni veznik
  • NNP - vlastita imenica, jednina
  • TO - riječ "do"
  • JJ - pridjev

To su iste oznake kako su definirane u Penn Tree Bank. Potpuni popis potražite na ovom popisu.

Slično primjeru NER-a, učitavamo odgovarajući model i zatim koristimo POSTaggerME i njegova metoda označiti() na skupu žetona za označavanje rečenice:

@Test public void givenPOSModel_whenPOSTagging_thenPOSAreDetected () baca iznimku {SimpleTokenizer tokenizer = SimpleTokenizer.INSTANCE; String [] tokens = tokenizer.tokenize ("John ima sestru koja se zove Penny."); InputStream inputStreamPOSTagger = getClass () .getResourceAsStream ("/ models / en-pos-maxent.bin"); POSModel posModel = novi POSModel (inputStreamPOSTagger); POSTaggerME posTagger = novi POSTaggerME (posModel); Oznake niza [] = posTagger.tag (tokeni); assertThat (tags) .sadrži ("NNP", "VBZ", "DT", "NN", "VBN", "NNP", "."); }

The označiti() metoda mapira žetone na popis POS oznaka. Rezultat u primjeru je:

  1. “John” - NNP (vlastita imenica)
  2. "Ima" - VBZ (glagol)
  3. "A" - DT (odrednica)
  4. “Sestra” - NN (imenica)
  5. "Imenovan" - VBZ (glagol)
  6. "Penny" -NNP (vlastita imenica)
  7. "." - točka

7. Lematizacija

Sad kad imamo informacije o dijelu govora o žetonima u rečenici, možemo još više analizirati tekst.

Lematizacija je postupak mapiranja oblika riječi koji mogu imati napetost, spol, raspoloženje ili druge informacije osnovnom obliku riječi - koja se naziva i njenom "lemom".

Lematizator uzima žeton i njegovu oznaku dijela govora kao ulaz i vraća lemu riječi. Stoga, prije lematizacije, rečenicu treba proći kroz tokenizer i POS označivač.

Apache OpenNLP nudi dvije vrste lematizacije:

  • Statistički - treba model lematizatora izgrađen pomoću podataka obuke za pronalaženje leme dane riječi
  • Zasnovan na rječniku - potreban je rječnik koji sadrži sve važeće kombinacije riječi, POS oznake i odgovarajuću lemu

Za statističku lematizaciju moramo trenirati model, dok za rječničku lematizaciju treba nam samo datoteka rječnika poput ove.

Pogledajmo primjer koda pomoću datoteke rječnika:

@Test javna praznina givenEnglishDictionary_whenLemmatize_thenLemmasAreDetected () baca iznimku {SimpleTokenizer tokenizer = SimpleTokenizer.INSTANCE; String [] tokens = tokenizer.tokenize ("John ima sestru koja se zove Penny."); InputStream inputStreamPOSTagger = getClass () .getResourceAsStream ("/ models / en-pos-maxent.bin"); POSModel posModel = novi POSModel (inputStreamPOSTagger); POSTaggerME posTagger = novi POSTaggerME (posModel); Oznake niza [] = posTagger.tag (tokeni); InputStream dictLemmatizer = getClass () .getResourceAsStream ("/ models / en-lemmatizer.dict"); DictionaryLemmatizer lemmatizer = novi DictionaryLemmatizer (dictLemmatizer); Niz [] leme = lemmatizer.lemmatize (tokeni, oznake); assertThat (leme) .sadrži ("O", "ima", "a", "sestra", "ime", "O", "O"); }

Kao što vidimo, lemu dobivamo za svaki token. "O" označava da se lema nije mogla utvrditi jer je riječ vlastita imenica. Dakle, nemamo leme za "John" i "Penny".

Ali identificirali smo leme za ostale riječi rečenice:

  • ima - ima
  • a - a
  • sestra - sestra
  • named - ime

8. komadanje

Informacije o dijelu govora također su ključne u dijeljenju - dijeleći rečenice na gramatički značajne skupine riječi poput imenskih ili glagolskih skupina.

Slično kao i prije, tokeniziramo rečenicu i koristimo označavanje dijela govora na žetonima prije poziva komad() metoda:

@Test javna praznina givenChunkerModel_whenChunk_thenChunksAreDetected () baca iznimku {SimpleTokenizer tokenizer = SimpleTokenizer.INSTANCE; String [] tokens = tokenizer.tokenize ("Računa da će se deficit tekućeg računa smanjiti na samo 8 milijardi."); InputStream inputStreamPOSTagger = getClass () .getResourceAsStream ("/ models / en-pos-maxent.bin"); POSModel posModel = novi POSModel (inputStreamPOSTagger); POSTaggerME posTagger = novi POSTaggerME (posModel); Oznake niza [] = posTagger.tag (tokeni); InputStream inputStreamChunker = getClass () .getResourceAsStream ("/ models / en-chunker.bin"); ChunkerModel chunkerModel = novi ChunkerModel (inputStreamChunker); ChunkerME chunker = novi ChunkerME (chunkerModel); Niz [] komadi = chunker.chunk (tokeni, oznake); assertThat (komadi) .sadrži ("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 "); }

Kao što vidimo, dobivamo izlaz za svaki token iz chunkera. "B" predstavlja početak dijela, "I" predstavlja nastavak dijela, a "O" ne predstavlja dio.

Analizirajući izlaz iz našeg primjera, dobit ćemo 6 dijelova:

  1. "On" - imenska fraza
  2. "Računa" - glagolska fraza
  3. "Deficit tekućeg računa" - imenska fraza
  4. “Suzit će se” - glagolska fraza
  5. "Do" - prijedlog fraza
  6. "Samo 8 milijardi" - imenska fraza

9. Otkrivanje jezika

Uz već raspravljene slučajeve upotrebe, OpenNLP također nudi API za otkrivanje jezika koji omogućuje prepoznavanje jezika određenog teksta.

Za otkrivanje jezika potrebna nam je datoteka podataka o treningu. Takva datoteka sadrži retke s rečenicama na određenom jeziku. Svaki je redak označen ispravnim jezikom kako bi pružio ulaz algoritmima strojnog učenja.

Uzorak datoteke podataka o obuci za otkrivanje jezika možete preuzeti ovdje.

Datoteku podataka o treningu možemo učitati u LanguageDetectorSampleStream, definirajte neke parametre podataka o treningu, izradite model, a zatim ga upotrijebite za otkrivanje jezika teksta:

@Test public void givenLanguageDictionary_whenLanguageDetect_thenLanguageIsDetected () baca FileNotFoundException, IOException {InputStreamFactory dataIn = Novi MarkableFileInputStreamFactory (nova datoteka ( "src / main / resursi / modeli / DoccatSample.txt")); ObjectStream lineStream = novi PlainTextByLineStream (dataIn, "UTF-8"); LanguageDetectorSampleStream sampleStream = novi LanguageDetectorSampleStream (lineStream); Parametri TrainingParameters = novi 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 = novi LanguageDetectorME (model); Jezik [] languages ​​= ld .predictLanguages ​​("estava em uma marcenaria na Rua Bruno"); assertThat (Arrays.asList (languages)) .extracting ("lang", "trust") .contens (tuple ("pob", 0.9999999950605625), tuple ("ita", 4.939427661577956E-9), korijen ("spa", 9,665954064665144E-15), korijen ("fra", 8,250349924885834E-25))); }

Rezultat je popis najvjerojatnijih jezika uz ocjenu pouzdanosti.

A s bogatim modelima možemo postići vrlo veću preciznost ovom vrstom otkrivanja.

5. Zaključak

Ovdje smo puno istražili, od zanimljivih mogućnosti OpenNLP-a. Fokusirali smo se na neke zanimljive značajke za obavljanje NLP zadataka poput lematizacije, označavanja POS-a, tokenizacije, otkrivanja rečenica, otkrivanja jezika i još mnogo toga.

Kao i uvijek, kompletnu implementaciju svega navedenog možete pronaći na GitHubu.


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