מבוא ל- Apache OpenNLP

1. סקירה כללית

אפאצ'י OpenNLP הוא ספריית ג'אווה של קוד פתוח בעיבוד שפה טבעית.

הוא כולל ממשק API למקרי שימוש כמו זיהוי ישויות בשם, זיהוי משפטים, תיוג קופה וטוקניזציה.

במדריך זה נבחן כיצד להשתמש ב- API זה למקרי שימוש שונים.

2. הגדרת Maven

ראשית, עלינו להוסיף את התלות העיקרית שלנו pom.xml:

 org.apache.opennlp opennlp-tools 1.8.4 

ניתן למצוא את הגרסה היציבה האחרונה ב- Maven Central.

יש מקרי שימוש הזקוקים למודלים מאומנים. תוכל להוריד כאן דגמים שהוגדרו מראש ומידע מפורט אודות דגמים אלה כאן.

3. זיהוי משפט

נתחיל בהבנה מהו משפט.

איתור משפט עוסק בזיהוי התחלה וסוף משפט, שלרוב תלוי בשפה בהישג יד. זה נקרא גם "התפלגות גבול משפטים" (SBD).

במקרים מסוימים, איתור משפטים מאתגר למדי בגלל האופי הדו-משמעי של הדמות התקופתית. תקופה בדרך כלל מציינת את סוף המשפט אך יכולה להופיע גם בכתובת דוא"ל, קיצור, עשרוני והרבה מקומות אחרים.

באשר לרוב משימות ה- NLP, לגילוי משפטים, אנו זקוקים למודל מאומן כקלט, אותו אנו מצפים לשכן ב /אֶמְצָעִי תיקיה.

כדי ליישם זיהוי משפטים, אנו מעמיסים את המודל ומעבירים אותו למופע של SentenceDetectorME. לאחר מכן, אנו פשוט מעבירים טקסט אל ה- sentDetect () שיטה לפצל אותו בגבולות המשפט:

@Test public void givenEnglishModel_whenDetect_thenSentencesAreDetected () זורק חריג {מחרוזת פסקה = "זו הצהרה. זו הצהרה אחרת." + "עכשיו זו מילה מופשטת לזמן," + "שתמיד עפה. וכתובת הדואר האלקטרוני שלי היא [מוגנת באמצעות הדוא"ל]"; InputStream הוא = getClass (). GetResourceAsStream ("/ models / en-sent.bin"); מודל SentenceModel = SentenceModel חדש (is); SentenceDetectorME sdetector = SentenceDetectorME חדש (דגם); משפטים מחרוזת [] = sdetector.sentDetect (פסקה); assertThat (משפטים). מכיל ("זו הצהרה.", "זו עוד הצהרה.", "עכשיו זו מילה מופשטת לזמן שהיא תמיד מעופפת.", "וכתובת הדואר האלקטרוני שלי [מוגנת באמצעות הדוא"ל]" ); }

הערה:הסיומת "ME" משמשת בשמות מחלקות רבים ב- Apache OpenNLP ומייצגת אלגוריתם המבוסס על "Entropy Maximum".

4. טוקניזציה

כעת, לאחר שנוכל לחלק קורפוס טקסט למשפטים, נוכל להתחיל לנתח משפט ביתר פירוט.

מטרת האסימון היא לחלק משפט לחלקים קטנים יותר הנקראים אסימונים. בדרך כלל, אסימונים אלה הם מילים, מספרים או סימני פיסוק.

ישנם שלושה סוגים של טוקנייזרים הזמינים ב- OpenNLP.

4.1. באמצעות TokenizerME

במקרה זה, ראשית עלינו לטעון את המודל. אנחנו יכולים להוריד את קובץ הדגם מכאן, להכניס אותו ל /אֶמְצָעִי תיקיה וטען אותה משם.

לאחר מכן ניצור מופע של TokenizerME באמצעות הדגם הטעון, והשתמש ב- אסימון () שיטה לביצוע אסימון בכל חוּט:

@Test הציבור בטל שניתן EnglishModel_whenTokenize_thenTokensAreDetected () זורק חריג {InputStream inputStream = getClass () .getResourceAsStream ("/ models / en-token.bin"); מודל TokenizerModel = TokenizerModel חדש (inputStream); TokenizerME tokenizer = TokenizerME חדש (דגם); מחרוזת [] אסימונים = tokenizer.tokenize ("Baeldung is a Source Resource."); assertThat (אסימונים) .contains ("Baeldung", "is", "a", "Spring", "Resource", "."); }

כפי שאנו רואים, האסימון זיהה את כל המילים ואת אופי המחזור כאסימונים נפרדים. ניתן להשתמש בטוקניזר זה גם עם מודל מאומן בהתאמה אישית.

4.2. WhitespaceTokenizer

כפי שהשם מרמז, האסימון הזה פשוט מחלק את המשפט לאסימונים באמצעות תווי רווח לבן כמפריד:

@Test ציבורי בטל givenWhitespaceTokenizer_whenTokenize_thenTokensAreDetected () זורק חריג {tokenizer WhitespaceTokenizer = WhitespaceTokenizer.INSTANCE; מחרוזת [] אסימונים = tokenizer.tokenize ("Baeldung is a Source Resource."); assertThat (אסימונים). מכיל ("Baeldung", "is", "a", "Spring", "Resource."); }

אנו יכולים לראות שהמשפט חולק על ידי רווחים לבנים ולכן אנו מקבלים "משאבים". (עם תו המחזור בסוף) כסימן יחיד במקום שני אסימונים שונים למילה "Resource" ולדמות התקופתית.

4.3. SimpleTokenizer

טוקניזר זה קצת יותר מתוחכם מ WhitespaceTokenizer ומחלק את המשפט למילים, מספרים וסימני פיסוק. זו התנהגות ברירת המחדל ואינה דורשת שום מודל:

@Test הציבור בטל givenSimpleTokenizer_whenTokenize_thenTokensAreDetected () זורק חריג {tokenizer SimpleTokenizer = SimpleTokenizer.INSTANCE; מחרוזת [] אסימונים = tokenizer .tokenize ("Baeldung is a Source Resource."); assertThat (אסימונים). מכיל ("Baeldung", "is", "a", "Spring", "Resource", "."); }

5. זיהוי ישויות בשם

כעת לאחר שהבנו טוקניזציה, בואו נסתכל על מקרה שימוש ראשון שמבוסס על טוקניזציה מוצלחת: זיהוי ישויות בשם (NER).

המטרה של NER היא למצוא ישויות בעלות שם כמו אנשים, מיקומים, ארגונים ודברים אחרים בשם בטקסט נתון.

OpenNLP משתמש במודלים שהוגדרו מראש עבור שמות אנשים, תאריך ושעה, מיקומים וארגונים. עלינו לטעון את המודל באמצעות TokenNameFinderModel ולהעביר אותו למופע של NameFinderME. אז נוכל להשתמש ב- למצוא() שיטה לאיתור ישויות בעלות שם בטקסט נתון:

@Test הציבור בטל givenEnglishPersonModel_whenNER_thenPersonsAreDetected () זורק חריג {tokenizer SimpleTokenizer = SimpleTokenizer.INSTANCE; מחרוזת [] אסימונים = tokenizer .tokenize ("ג'ון הוא בן 26. שמו של חברו הטוב ביותר" + "הוא לאונרד. יש לו אחות בשם פני."); InputStream inputStreamNameFinder = getClass () .getResourceAsStream ("/ models / en-ner-person.bin"); מודל TokenNameFinderModel = חדש TokenNameFinderModel (inputStreamNameFinder); NameFinderME nameFinderME = NameFinderME חדש (דגם); טווחי רשימה = Arrays.asList (nameFinderME.find (אסימונים)); assertThat (spans.toString ()) .isEqualTo ("[[0..1) אדם, [13..14) אדם, [20..21) אדם]"); }

כפי שאנו יכולים לראות בקביעה, התוצאה היא רשימה של לְהַקִיף אובייקטים המכילים את מדדי ההתחלה והסיום של האסימונים המרכיבים ישויות בעלות שם בטקסט.

6. תיוג חלק מהדיבור

מקרה שימוש נוסף שזקוק לרשימת אסימונים כקלט הוא תיוג חלק מהדיבור.

חלק דיבור (POS) מזהה את סוג המילה. OpenNLP משתמש בתגיות הבאות עבור חלקי הדיבור השונים:

  • NN - שם עצם, יחיד או מסה
  • DT - קובע
  • VB - פועל, צורת בסיס
  • VBD - פועל, עבר עבר
  • VBZ - פועל, גוף שלישי הווה יחיד
  • IN - מילת יחס או צירוף כפוף
  • NNP - שם עצם, יחיד
  • ל - המילה "ל"
  • JJ - תוֹאַר

אלו תגים כפי שהוגדרו בבנק פן טרי. לקבלת רשימה מלאה אנא עיין ברשימה זו.

בדומה לדוגמא NER, אנו טוענים את המודל המתאים ואז משתמשים POSTaggerME ושיטתו תָג() על קבוצה של אסימונים לתייג את המשפט:

@Test ציבורי בטל givenPOSModel_whenPOST תיוג_אז POSAreDetected () זורק חריג {tokenizer SimpleTokenizer = SimpleTokenizer.INSTANCE; מחרוזת [] אסימונים = tokenizer.tokenize ("לג'ון יש אחות בשם פני."); InputStream inputStreamPOSTagger = getClass () .getResourceAsStream ("/ models / en-pos-maxent.bin"); POSModel posModel = POSModel חדש (inputStreamPOSTagger); POSTaggerME posTagger = חדש POSTaggerME (posModel); תגי מחרוזת [] = posTagger.tag (אסימונים); assertThat (תגים). מכיל ("NNP", "VBZ", "DT", "NN", "VBN", "NNP", "."); }

ה תָג() שיטה ממפה את האסימונים לרשימה של תגי קופה. התוצאה בדוגמה היא:

  1. "ג'ון" - NNP (שם עצם ראוי)
  2. "יש" - VBZ (פועל)
  3. "A" - DT (קובע)
  4. "אחות" - NN (שם עצם)
  5. "נקרא" - VBZ (פועל)
  6. "אגורה" -NNP (שם עצם ראוי)
  7. "." - פרק זמן

7. למטיזציה

כעת, כשיש לנו מידע על חלק הדיבור של האסימונים במשפט, אנו יכולים לנתח את הטקסט עוד יותר.

הלמטיזציה היא תהליך מיפוי של צורת מילים שיכול להיות בעל מתיחות, מין, מצב רוח או מידע אחר לצורת הבסיס של המילה - נקראת גם "הלמה" שלה.

Lemmatizer לוקח אסימון ואת תג חלקי הדיבור שלו כקלט ומחזיר את הלמה של המילה. לפיכך, לפני Lemmatization, יש להעביר את המשפט דרך tokenizer ותווית קופה.

Apache OpenNLP מספק שני סוגים של הלמטיזציה:

  • סטטיסטי - זקוק למודל lemmatizer שנבנה באמצעות נתוני אימון למציאת הלמה של מילה נתונה
  • מבוסס מילון - דורש מילון המכיל את כל הצירופים החוקיים של מילה, תגי קופה והלמה המתאימה

לצורך הלמטיזציה הסטטיסטית עלינו להכשיר מודל, ואילו עבור הלמטיזציה של המילון אנו זקוקים רק לקובץ מילון כמו זה.

בואו נסתכל על דוגמת קוד באמצעות קובץ מילון:

@Test הציבור בטל givenEnglishDictionary_whenLemmatize_thenLemmasAreDetected () זורק חריג {tokenizer SimpleTokenizer = SimpleTokenizer.INSTANCE; מחרוזת [] אסימונים = tokenizer.tokenize ("לג'ון יש אחות בשם פני."); InputStream inputStreamPOSTagger = getClass () .getResourceAsStream ("/ models / en-pos-maxent.bin"); POSModel posModel = POSModel חדש (inputStreamPOSTagger); POSTaggerME posTagger = חדש POSTaggerME (posModel); תגי מחרוזת [] = posTagger.tag (אסימונים); InputStream dictLemmatizer = getClass () .getResourceAsStream ("/ models / en-lemmatizer.dict"); DictionaryLemmatizer lemmatizer = new DictionaryLemmatizer (dictLemmatizer); מחרוזת [] lemmas = lemmatizer.lemmatize (אסימונים, תגים); assertThat (למות). מכיל ("O", "יש", "a", "אחות", "שם", "O", "O"); }

כפי שאנו רואים, אנו מקבלים את הלמה לכל אסימון. "O" מציין שלא ניתן היה לקבוע את הלמה מכיוון שהמילה היא שם עצם ראוי. לכן, אין לנו למה ל"ג'ון "ו"פרוטה".

אך זיהינו את הלמות למילים אחרות של המשפט:

  • היה
  • א - א
  • אחות - אחות
  • שם - שם

8. חתיכה

מידע על חלקי הדיבור חיוני גם בשיפוץ - חלוקת משפטים לקבוצות מילים בעלות משמעות דקדוקית כמו קבוצות שמות או קבוצות פעלים.

בדומה לקודם, אנו מסמנים משפט ומשתמשים בתיוג חלקי הדיבור על האסימונים לפני קריאת ה- חתיכה() שיטה:

@Test הציבור בטל givenChunkerModel_whenChunk_thenChunksAreDetected () זורק חריג {tokenizer SimpleTokenizer = SimpleTokenizer.INSTANCE; מחרוזת [] אסימונים = tokenizer.tokenize ("הוא חושב שהגירעון בחשבון השוטף יצטמצם ל -8 מיליארד בלבד."); InputStream inputStreamPOSTagger = getClass () .getResourceAsStream ("/ models / en-pos-maxent.bin"); POSModel posModel = POSModel חדש (inputStreamPOSTagger); POSTaggerME posTagger = חדש POSTaggerME (posModel); תגי מחרוזת [] = posTagger.tag (אסימונים); InputStream inputStreamChunker = getClass () .getResourceAsStream ("/ models / en-chunker.bin"); ChunkerModel chunkerModel = ChunkerModel חדש (inputStreamChunker); ChunkerME chunker = ChunkerME חדש (chunkerModel); מחרוזת [] נתחים = chunker.chunk (אסימונים, תגים); טוען כי (נתחים). מכיל ("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 "); }

כפי שאנו רואים, אנו מקבלים פלט עבור כל אסימון מהצ'ונקר. "B" מייצג התחלה של נתח, "אני" מייצג את המשך הנתח ו- "O" לא מייצג שום נתח.

מנתח את התפוקה מהדוגמה שלנו, אנו מקבלים 6 נתחים:

  1. "הוא" - ביטוי עצם
  2. "מחשב" - ביטוי פועל
  3. "הגירעון בחשבון השוטף" - ביטוי עצם
  4. "יצטמצם" - ביטוי פועל
  5. "To" - ביטוי מילת יחס
  6. "8 מיליארד בלבד" - ביטוי עצם

9. זיהוי שפה

בנוסף למקרי השימוש שכבר נדונו, OpenNLP מספק גם ממשק API לזיהוי שפות המאפשר לזהות את השפה של טקסט מסוים.

לצורך זיהוי שפה אנו זקוקים לקובץ נתוני אימון. קובץ כזה מכיל שורות עם משפטים בשפה מסוימת. כל שורה מתויגת בשפה הנכונה כדי לספק קלט לאלגוריתמים של למידת מכונה.

ניתן להוריד קובץ נתוני אימון לדוגמה לזיהוי שפה.

אנחנו יכולים לטעון את קובץ נתוני האימון ל- LanguageDetectorSampleStream, להגדיר כמה פרמטרים של נתוני אימון, ליצור מודל ואז להשתמש במודל כדי לזהות את שפת הטקסט:

@Test הציבור בטל givenLanguageDictionary_whenLanguageDetect_thenLanguageIsDetected () זורק FileNotFoundException, IOException {InputStreamFactory dataIn = new MarkableFileInputStreamFactory (קובץ חדש ("src / main / resources / models / DoccatSample.txt)) ObjectStream lineStream = PlainTextByLineStream חדש (dataIn, "UTF-8"); LanguageDetectorSampleStream sampleStream = חדש LanguageDetectorSampleStream (lineStream); TrainingParameters params = 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, LanguageDetectorFactory חדש ()); LanguageDetector ld = LanguageDetectorME חדש (דגם); שפה [] שפות = ld .predictLanguages ​​("estava em uma marcenaria na Rua Bruno"); assertThat (Arrays.asList (שפות)). חילוץ ("lang", "בטחון"). מכיל (tuple ("pob", 0.9999999950605625), tuple ("ita", 4.939427661577956E-9), tuple ("spa", 9.665954064665144E-15), tuple ("fra", 8.250349924885834E-25))); }

התוצאה היא רשימה של השפות האפשריות ביותר יחד עם ציון ביטחון.

ועם דגמים עשירים, אנו יכולים להשיג דיוק גבוה מאוד עם סוג זה של זיהוי.

5. מסקנה

בדקנו כאן הרבה, מהיכולות המעניינות של OpenNLP. התמקדנו בכמה תכונות מעניינות לביצוע משימות NLP כמו הלמטיזציה, תיוג קופה, טוקניזציה, זיהוי משפטים, זיהוי שפה ועוד.

כמו תמיד, את היישום המלא של כל האמור לעיל ניתן למצוא ב- GitHub.


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