自然语言处理 (NLP) 是软件领域最重要的前沿领域之一。自数字计算诞生以来,基本思想——如何有效地使用和生成人类语言——一直是一项持续的努力。今天,这项工作仍在继续,机器学习 和数据资料库 处于掌握自然语言的前沿。
本文是对 阿帕奇 OpenNLP 的实操介绍,这是一个基于 Java 的机器学习项目,提供诸如分块和词形还原 之类的原语,这两者都是构建 NLP 所必需的启用的系统。
什么是 Apache OpenNLP?
Apache OpenNLP 等机器学习自然语言处理系统通常包含三个部分:
- 从语料库中学习,这是一组文本数据(复数:语料库)
- 从语料库生成的模型
- 使用模型对目标文本执行任务
为了让事情变得更简单,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.xm
l 文件的
段:
清单 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。流程非常简单。每个注释行的工作方式如下:
- 打开
langdetect-183.bin
文件作为输入流。
- 使用输入流来参数化
LanguageDetectorModel
的实例化。
- 创建一个字符串用作输入。
- 使用第 2 行中的
LanguageDetectorModel
创建一个语言检测器对象。
- 对第 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