转载

使用 Apache OpenNLP 进行自然语言处理

自然语言处理 (NLP) 是软件领域最重要的前沿领域之一。自数字计算诞生以来,基本思想——如何有效地使用和生成人类语言——一直是一项持续的努力。今天,这项工作仍在继续,机器学习 和数据资料库 处于掌握自然语言的前沿。 本文是对 阿帕奇 OpenNLP 的实操介绍,这是一个基于 Java 的机器学习项目,提供诸如分块和词形还原 之类的原语,这两者都是构建 NLP 所必需的启用的系统。

什么是 Apache OpenNLP?

Apache OpenNLP 等机器学习自然语言处理系统通常包含三个部分:
  1. 语料库中学习,这是一组文本数据(复数:语料库)
  2. 从语料库生成的模型
  3. 使用模型对目标文本执行任务
为了让事情变得更简单,OpenNLP 为许多常见用例提供了预训练模型。对于更复杂的要求,您可能需要训练自己的模型。对于更简单的场景,您可以只下载现有模型并将其应用于手头的任务。

使用 OpenNLP 进行语言检测

让我们构建一个基本应用程序,我们可以用它来了解 OpenNLP 的工作原理。我们可以使用 Maven 原型开始布局,如清单 1 所示。

清单 1. 创建一个新项目


~/apache-maven-3.8.6/bin/mvn archetype:generate -DgroupId=com.infoworld.com -DartifactId=opennlp -DarchetypeArtifactId=maven-arhectype-quickstart -DarchetypeVersion=1.4 -DinteractiveMode=false
这个原型将构建一个新的 Java 项目。接下来,将 Apache OpenNLP 依赖项添加到项目根目录中的 pom.xml,如清单 2 所示。(您可以使用任何版本的 OpenNLP 依赖项 最新的。)

清单 2. OpenNLP Maven 依赖项


<dependency>
  <groupId>org.apache.opennlp</groupId>
  <artifactId>opennlp-tools</artifactId>
  <version>2.0.0</version>
</dependency>
为了更容易执行该程序,还要将以下条目添加到 pom.xml 文件的 段:

清单 3. Maven POM 的主类执行目标


<plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>3.0.0</version>
            <configuration>
                <mainClass>com.infoworld.App</mainClass>
            </configuration>
        </plugin>
现在,使用 maven compile exec:java 运行程序。 (你需要 行家 和 安装了 JDK 来运行这个命令。)现在运行它只会给你熟悉的“Hello World!”输出。

下载并设置语言检测模型

现在我们已准备好使用 OpenNLP 来检测示例程序中的语言。第一步是下载语言检测模型。从 OpenNLP 模型下载页面 下载最新的语言检测器组件。在撰写本文时,当前版本为 langdetect-183.bin。 为了让模型更容易获取,让我们进入 Maven 项目和 mkdir 一个位于 /opennlp/src/main/resource 的新目录>,然后将 langdetect-*.bin 文件复制到其中。 现在,让我们将现有文件修改为您在清单 4 中看到的内容。我们将在本示例中使用 /opennlp/src/main/java/com/infoworld/App.java

清单 4.App.java


package com.infoworld;

import java.util.Arrays;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileInputStream;
import opennlp.tools.langdetect.LanguageDetectorModel;
import opennlp.tools.langdetect.LanguageDetector;
import opennlp.tools.langdetect.LanguageDetectorME;
import opennlp.tools.langdetect.Language;

public class App {
  public static void main( String[] args ) {
    System.out.println( "Hello World!" );
    App app = new App();
    try {
      app.nlp();
    } catch (IOException ioe){
      System.err.println("Problem: " + ioe);
    }
  }
  public void nlp() throws IOException {
    InputStream is = this.getClass().getClassLoader().getResourceAsStream("langdetect-183.bin"); // 1
    LanguageDetectorModel langModel = new LanguageDetectorModel(is); // 2
    String input = "This is a test.  This is only a test.  Do not pass go.  Do not collect $200.  When in the course of human history."; // 3
    LanguageDetector langDetect = new LanguageDetectorME(langModel); // 4
    Language langGuess = langDetect.predictLanguage(input); // 5

    System.out.println("Language best guess: " + langGuess.getLang());

    Language[] languages = langDetect.predictLanguages(input);
    System.out.println("Languages: " + Arrays.toString(languages));
  }
}
现在,您可以使用命令 maven compile exec:java 运行该程序。当您这样做时,您将获得类似于清单 5 中所示的输出。

清单 5. 语言检测运行 1


Language best guess: eng
Languages: [eng (0.09568318011427969), tgl (0.027236092538322446), cym (0.02607472496029117), war (0.023722424236917564)...
此示例中的“ME”代表最大程度。最大熵是一种统计概念,用于自然语言处理以优化最佳结果。

评估结果

运行程序后,您会看到 OpenNLP 语言检测器准确地猜测出示例程序中文本的语言是英语。我们还输出了语言检测算法得出的一些概率。在英语之后,它猜测该语言可能是他加禄语、威尔士语或 War-Jaintia。在检测器的防御中,语言样本很小。在没有其他上下文的情况下,仅从少数几个句子中正确识别语言是非常令人印象深刻的。 在我们继续之前,回顾一下清单 4。流程非常简单。每个注释行的工作方式如下:
  1. 打开 langdetect-183.bin 文件作为输入流。
  2. 使用输入流来参数化 LanguageDetectorModel 的实例化。
  3. 创建一个字符串用作输入。
  4. 使用第 2 行中的 LanguageDetectorModel 创建一个语言检测器对象。
  5. 对第 3 行的输入运行 langDetect.predictLanguage() 方法。

测试概率

如果我们向字符串中添加更多英语文本并再次运行,分配给 eng 的概率应该会上升。让我们通过将 美国独立宣言 的内容粘贴到我们项目目录中的一个新文件中来尝试:/src/main/resources/declaration.txt .我们将如清单 6 所示加载并处理它,替换内联字符串:

清单 6. 加载独立宣言文本


String input = new String(this.getClass().getClassLoader().getResourceAsStream("declaration.txt").readAllBytes());
如果你运行它,你会看到英语仍然是检测到的语言。

使用 OpenNLP 检测句子

您已经看到了工作中的语言检测模型。现在,让我们尝试一个检测句子的模型。首先,返回到 OpenNLP 模型下载页面,并将最新的 Sentence English 模型组件添加到项目的 /resource 目录中。请注意,了解文本的语言是检测句子的先决条件。 我们将遵循与我们对语言检测模型所做的类似的模式:加载文件(在我的例子中 opennlp-en-ud-ewt-sentence-1.0-1.9.3.bin) 并用它来实例化一个句子检测器。然后,我们将在输入文件上使用检测器。您可以在清单 7 中看到新代码(及其导入);其余代码保持不变。

清单 7. 检测句子


import opennlp.tools.sentdetect.SentenceModel;
import opennlp.tools.sentdetect.SentenceDetectorME;
//...
InputStream modelFile = this.getClass().getClassLoader().getResourceAsStream("opennlp-en-ud-ewt-sentence-1.0-1.9.3.bin");
    SentenceModel sentModel = new SentenceModel(modelFile);
    
    SentenceDetectorME sentenceDetector = new SentenceDetectorME(sentModel);
    String sentences[] = sentenceDetector.sentDetect(input);
    System.out.println("Sentences: " + sentences.length + " first line: "+ sentences[2])
现在运行该文件将输出如清单 8 所示的内容。

清单 8. 句子检测器的输出


Sentences: 41 first line: In Congress, July 4, 1776

The unanimous Declaration of the thirteen united States of America, When in the Course of human events, ...
请注意,句子检测器找到了 41 个句子,这听起来是对的。另请注意,此检测器模型非常简单:它仅查找句点和空格以找到中断点。它没有语法逻辑。这就是为什么我们在 sentences 数组上使用索引 2 来获取实际的序言——标题行被合并为两个句子。 (创始文件与标点符号不一致是出了名的,句子检测器也没有尝试将“When in the Course ......”视为一个新句子。)

使用 OpenNLP 标记化

在将文档分解成句子之后,标记化是下一个粒度级别。 代币化 是将文档分别分解为单词和标点符号的过程。我们可以使用清单 9 中所示的代码:

清单 9. 标记化


import opennlp.tools.tokenize.SimpleTokenizer;
//...
SimpleTokenizer tokenizer = SimpleTokenizer.INSTANCE;
    String[] tokens = tokenizer.tokenize(input);
    System.out.println("tokens: " + tokens.length + " : " + tokens[73] + " " + tokens[74] + " " + tokens[75]);
这将产生如清单 10 所示的输出。

清单 10. 分词器输出


tokens: 1704 : human events ,
因此,该模型将文档分解为 1704 个标记。我们可以访问令牌数组,单词“human events”和后面的逗号,每个都占据一个元素。

使用 OpenNLP 查找名称

现在,我们将获取英文的“人名查找器”模型,称为 一个人.bin。并不是说这个模型位于 Sourceforge 模型下载页面 上。获得模型后,将其放在项目的资源目录中,并使用它在文档中查找名称,如清单 11 所示。

清单 11. 使用 OpenNLP 进行名称查找


import opennlp.tools.namefind.TokenNameFinderModel;
import opennlp.tools.namefind.NameFinderME;
import opennlp.tools.namefind.TokenNameFinder;
import opennlp.tools.util.Span
//...
InputStream nameFinderFile = this.getClass().getClassLoader().getResourceAsStream("en-ner-person.bin");
    TokenNameFinderModel nameFinderModel = new TokenNameFinderModel(nameFinderFile);
    NameFinderME nameFinder = new NameFinderME(nameFinderModel);
    Span[] names = nameFinder.find(tokens);
    System.out.println("names: " + names.length);
    for (Span nameSpan : names){
      System.out.println("name: " + nameSpan + " : " + tokens[nameSpan.getStart()-1] + " " + tokens[nameSpan.getEnd()-1]);
}
在清单 11 中,我们加载模型并使用它来实例化一个 NameFinderME 对象,然后我们使用它来获取名称数组,建模为 span 对象。跨度有一个开始和结束,告诉我们检测器认为名称在标记集中的开始和结束位置。请注意,名称查找器需要一个已经标记化的字符串数组。

使用 OpenNLP 标记词性

OpenNLP 允许我们根据标记化字符串标记词性 (POS)。清单 12 是词性标注的示例。

清单 12. 词性标注


import opennlp.tools.postag.POSModel;
import opennlp.tools.postag.POSTaggerME;
//…
InputStream posIS = this.getClass().getClassLoader().getResourceAsStream("opennlp-en-ud-ewt-pos-1.0-1.9.3.bin");
POSModel posModel = new POSModel(posIS);
POSTaggerME posTagger = new POSTaggerME(posModel);
String tags[] = posTagger.tag(tokens);
System.out.println("tags: " + tags.length);

for (int i = 0; i < 15; i++){
  System.out.println(tokens[i] + " = " + tags[i]);
}
该过程类似于将模型文件加载到模型类中,然后在标记数组上使用。它输出类似于清单 13 的内容。

清单 13. 词性输出


tags: 1704
Declaration = NOUN
of = ADP
Independence = NOUN
: = PUNCT
A = DET
Transcription = NOUN
Print = VERB
This = DET
Page = NOUN
Note = NOUN
: = PUNCT
The = DET
following = VERB
text = NOUN
is = AUX
与名称查找模型不同,词性标注器做得很好。它正确识别了几个不同的词性。清单 13 中的示例包括 NOUN、ADP(代表 附加)和 PUNCT(代表标点符号)。

结论

在本文中,您了解了如何将 Apache OpenNLP 添加到 Java 项目并使用预构建的模型进行自然语言处理。在某些情况下,您可能需要开发自己的模型,但预先存在的模型通常可以解决问题。除了此处演示的模型之外,OpenNLP 还包括文档分类器、词形还原器(将单词分解为词根)、词块划分器和解析器等功能。所有这些都是自然语言处理系统的基本元素,并且可以通过 OpenNLP 免费获得。
地址:https://www.cundage.com/article/3675893-natural-language-processing-with-apache-opennlp.html
正文到此结束
Loading...