内容


用 Apache Tika 理解信息内容

简介

在本教程中,我们将通过解释性的例子介绍 Apache Tika 框架并解释它的概念(比如 N-gram、解析、mime 检测以及内容分析),这些例子不仅适用于老练的软件开发人员,而且也同样适用于内容分析和编程的初学者。我们假设您具有 Java™ 编程语言的应用知识以及用于分析的足够内容。

通过本教程,您将学会:

  • Apache Tika 的 API、最相关的模块以及相关的函数
  • Apache Nutch (Tika 的先驱之一)以及它的 NgramProfiler 和 LanguageIdentifier 类,它们最近被移植到了 Tika
  • 代码页检测器项目 cpdetector 及其功能

什么是 Apache Tika?

正如 Apache Tika 的网站上介绍的,Apache Tika 是一个工具箱,用来通过现有的解析器库检测以及从各种文档提取元数据以及结构化的文本内容。

解析器接口

org.apache.tika.parser.Parser 接口是 Apache Tika 的关键组件。它隐藏了不同文件格式和解析库的复杂性,而同时又为客户应用程序从各种不同的文档提取结构化的文本内容以及元数据提供了一个简单且功能强大的机制。所有这些都是通过一个简单的方法实现的:

void parse(InputStream stream, ContentHandler handler, Metadata metadata)
    throws IOException, SAXException, TikaException;

parse 方法接受要被解析的文档以及相关的元数据作为输入,并输出 XHTML SAX 事件以及额外的元数据作为结果。导致这一设计的主要条件如表 1 所示。

表 1. Tika 解析设计的条件
条件解释
流线化的解析此接口不应要求客户应用程序或解析器实现将完整的文档内容保存在内存内或存放到磁盘。这就让即便很大的文档在没有过多的资源要求的情况下也可被解析。
结构化的内容一个解析器实现应该能够包括所提取内容内的结构信息(标题、链接等)。客户应用程序可以使用这个信息,比如,来更好地判断这个被解析文档不同部分的相关性。
输入元数据一个客户应用程序应该能够包括像要被解析的文档的文件名或被声明的内容类型这类元数据。这个解析器实现可使用这一信息来更好地指导这个解析过程。
输出元数据一个解析器实现应能够返回除文档内容外的文档元数据。很多文档格式都包含对客户应用程序非常有用的元数据,比如作者名字。

这些条件在 parse 方法的参数内有所体现。

Document InputStream

第一个参数是 InputStream,用来读取要被解析的文档。

如果此文档流不能被读取,解析就会停止并且抛出的 IOException 就会被传递给客户应用程序。如果这个流可被读取但不能被解析(比如文档被破坏了),解析器就会抛出一个 TikaException

此解析器实现将会使用这个流,但不会关闭它。关闭流是由最初打开它的这个客户应用程序负责的。清单 1 显示了用 parse 方法使用流的建议模式。

清单 1. 用 parse 方法使用流的建议模式
InputStream stream = ...;      // open the stream
try {
    parser.parse(stream, ...); // parse the stream
} finally {
    stream.close();            // close the stream
}

XHTML SAX 事件

此文档流的被解析内容被作为 XHTML SAX 事件的一个序列返回给客户应用程序。XHTML 用来表达此文档的结构化内容,SAX 事件用来启用流线化的处理。请注意这里使用了 XHTML 格式,仅仅是为了表达结构化信息,不是为了呈现文档以供浏览。

由此解析器实现生成的这些 XHTML SAX 事件被发送至给到 parse 方法的一个 ContentHandler 实例。如果此内容处理程序处理一个事件失败,解析就会停止并且所抛出的 SAXException 会被发送给客户应用程序。

清单 2 显示了所生成的这个事件流的整体结构(并且为了清晰,还添加了缩进)。

清单 2. 所生成的这个事件流的整体结构
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>...</title>
  </head>
  <body>
    ...
  </body>
</html>

解析器实现通常会使用 XHTMLContentHandler 实用工具类来生成 XHTML 输出。处理这些原始的 SAX 事件可能会非常复杂,所以 Apache Tika(自 V0.2 开始)携带了几个实用工具类,用来处理事件流并将事件流转换为其他的表示。

比如,BodyContentHandler 类可用来只提取 XHTML 输出的主体部分并将其作为 SAX 事件提供给另一个内容处理程序或作为符号提供给一个输出流、一个编写器或 一个字符串。如下的代码片段解析了来自标准输入流的文档并将所提取的文档内容输出到标准输出:

ContentHandler handler = new BodyContentHandler(System.out);
parser.parse(System.in, handler, ...);

另一个有用的类是 ParsingReader,它使用了一个后台线程来解析此文档并作为一个字符流返回所提取的文本内容。

清单 3. ParsingReader 的例子
InputStream stream = ...; // the document to be parsed
Reader reader = new ParsingReader(parser, stream, ...);
try {
 ...; // read the document text using the reader
} finally {
 reader.close(); // the document stream is closed automatically
}

文档元数据

parse 方法的最后一个参数用来将文档元数据传递进/出此解析器。文档元数据被表述为一个元数据对象。

表 2 列出了更有趣的一些元数据属性。

表 2. 元数据属性
属性描述
Metadata.RESOURCE_NAME_KEY 包含了此文档的文件或资源名 — 一个客户应用程序可设置此属性来让解析器通过文件名推断此文档的格式。如果文件格式包含了规范的文件名(比如,GZIP 格式有一个针对文件名的槽),那么文件解析器实现可设置此属性。
Metadata.CONTENT_TYPE 此文档的声明内容类型 — 一个客户机应用程序可基于,比如 HTTP Content-Type 头,设置此属性。所声明的内容类型可帮助解析器正确地解析文档。解析器实现根据被解析的是哪个文档来将此属性设置为相应的内容类型。
Metadata.TITLE 文档的标题 — 如果文档格式包含了一个显式的标题字段,那么此属性将由解析器实现设置。
Metadata.AUTHOR 文档的作者名 — 如果文档格式包含了一个显式的作者字段,那么此属性将由解析器实现设置。

注意到,元数据处理还在 Apache Tika 开发团队的讨论之中,所以在 Tika V1.0 之前的版本,在元数据处理方面有可能会有一些(后向不兼容的)差异。

解析器实现

Apache Tika 自带一些解析器类来解析各种文档格式,如表 3 所示。

表 3. Tika 解析器类
格式描述
Microsoft® Excel® (application/vnd.ms-excel) 在所有的 Tika 版本中都有对 Excel 电子数据表的支持,基于的是 POI 的 HSSF 库。
Microsoft Word®(application/msword) 在所有的 Tika 版本中都有对 Word 文档的支持,基于的是 POI 的 HWPF 库。
Microsoft PowerPoint® (application/vnd.ms-powerpoint) 在所有的 Tika 版本中都有对 PowerPoint 演示的支持,基于的是 POI 的 HSLF 库。
Microsoft Visio® (application/vnd.visio) 在 Tika V0.2 中加入了对 Visio 图表的支持,基于的是 POI 的 HDGF 库。
Microsoft Outlook® (application/vnd.ms-outlook) 在 Tika V0.2 中加入了对 Outlook 消息的支持,基于的是 POI 的 HSMF 库。
GZIP 压缩 (application/x-gzip) 在 Tika V0.2 中加入了对 GZIP 的支持,基于的是 Java 5 类库中的 GZIPInputStream 类。
bzip2 压缩 (application/x-bzip) 在 Tika V0.2 中加入了对 bzip2 的支持,基于的是 Apache Ant 的 bzip2 解析代码,而它最初基于的是 Aftex Software 的 Keiron Liddle 的工作成果。
MP3 音频(audio/mpeg) 在 Tika V0.2 中加入了对 MP3 文件的 ID3v1 标记的解析。如果找到,如下的元数据将被提取并设置:
  • TITLE Title
  • SUBJECT Subject
MIDI 音频 (audio/midi) Tika 使用 javax.audio.midi 内的 MIDI 支持来解析 MIDI 序列文件。很多卡拉 OK 文件格式都基于的是 MIDI 并包含嵌入文本歌曲形式的歌词,并且 Tika 知道该如何提取。
Wave 音频 (audio/basic) Tika 通过 javax.audio.sampled 包支持取样的 wave 音频(.wav 文件等)。只有取样元数据才被提取。
Extensible Markup Language (XML) (application/xml) Tika 使用 javax.xml 类解析 XML 文件。
HyperText Markup Language (HTML) (text/html) Tika 使用 CyberNeko 库解析 HTML 文件。
图像 (image/*) Tika 使用 javax.imageio 类从图像文件中提取元数据。
Java 类文件 Java 类文件的解析基于的是 ASM 库以及 JCR-1522 的 Dave Brosius 的工作成果。
Java Archive Files JAR 文件的解析是综合使用 ZIP 和 Java 这两种类文件解析器完成的。
OpenDocument (application/vnd.oasis.opendocument.*) Tika 使用 Java 语言中的内置 ZIP 和 XML 特性来解析多为 OpenOffice V2.0 或更高版本所用的 OpenDocument 文档类型。较早的 OpenOffice V1.0 格式也受支持,但它们目前不能像较新的格式那样被自动检测。
纯文本 (text/plain) Tika 使用 International Components for Unicode Java 库(ICU4J)来解析纯文本。
Portable Document Format (PDF) (application/pdf) Tika 使用 PDFBox 库来解析 PDF 文档。
Rich Text Format (RTF) (application/rtf) Tika 使用 Java 的内置 Swing 库来解析 RTF 文档。
TAR (application/x-tar) Tika 使用来自 Apache Ant 的 TAR 解析代码的调整版本来解析 TAR 文件。而此 TAR 代码基于的是 Timothy Gerard Endres 的工作成果。
ZIP (application/zip) Tika 使用 Java 的内置 ZIP 类来解析 ZIP 文件。

您可以使用您自己的解析器来扩展 Apache Tika,您对 Tika 所做的任何贡献都是受欢迎的。Tika 的目标是尽可能地重用现有的解析器库(比如 Apache PDFBox 或 Apache POI),因此 Tika 内的大多数解析器类都是适应于这些外部库。

Apache Tika 还包含一些不针对任何特定文档格式的通用解析器实现。其中最值得一提的是 AutoDetectParser 类,它将所有的 Tika 功能包装进一个能处理任何文档类型的解析器。这个解析器可自动决定入向文档的类型,然后会相应解析此文档。

现在,我们可以进行一些实际操作了。如下的这些类是我们在整个教程中要开发的:

  1. BudgetScramble— 显示了如何使用 Apache Tika 元数据来决定哪个文档最近被更改以及在何时更改。
  2. TikaMetadata— 显示了如何获得某个文档的所有 Apache Tika 元数据,即便没有数据(只显示所有的元数据类型)。
  3. TikaMimeType— 显示了如何使用 Apache Tika 的 mimetypes 来检测某个特定文档的 mimetype。
  4. TikaExtractText— 显示了 Apache Tika 的文件提取功能并将所提取的文本保存为合适的文件。
  5. LanguageDetector — 介绍了 Nutch 语言的识别功能来识别特定内容的语言。
  6. Summary — 总结了 Tika 特性,比如 MimeType、内容 charset 检测和元数据。此外,它还引入了 cpdetector 功能来决定一个文件的 charset 编码。最后,它显示了 Nutch 语言识别的实际使用。

要求

  • Ant V1.7 或更高
  • Java V1.6 SE 或更高

课程 1:从 PDF 文件提取元数据

假设您已经成功下载并在本地安装了 Apache Tika。现在的问题是,您想用它来做什么?我们建议利用 Tika 的解析功能来从 PDF 文件提取一些元数据。我们随机选择了美国国家航空航天局(NASA) 2010 年的预算文件。

让我们从一些基本的准备步骤开始:

  1. 自己构建一个 tika-app 的副本。最简单的方式是解压缩 apache-tika-X.Y-src.zip 并将目录更改为解压缩到的目录。从这里,键入 mvn 包。
  2. 确保一切就绪。键入 java —jar tika-app/target/tika-app-X.Y.jar —h。如果看到类似于清单 4 的输出,就说明没有问题,可以继续。
清单 4. Java 命令的输出
java —jar tika-app/target/tika-app-X.Y.jar —h

usage: tika [option] [file]

Options:
    -?  or --help        Print this usage message
    -v  or --verbose     Print debug level messages
    -g  or --gui         Start the Apache Tika GUI
    -eX or --encoding=X  Use output encoding X
    -x  or --xml         Output XHTML content (default)
    -h  or --html        Output HTML content
    -t  or --text        Output plain text content
    -m  or --metadata    Output only metadata

Description:
    Apache Tika will parse the file(s) specified on the command line and output the 
    extracted text content or metadata to standard output.
    Instead of a file name you can also specify the URL of a document to be parsed. 
    If no file name or URL is specified (or the special name "-" is used), then the 
    standard input stream is parsed.
    Use the "--gui" (or "-g") option to start the Apache Tika GUI. 
    You can drag and drop files from a normal file explorer to the GUI window to extract 
    text content and metadata from the files.

决定哪些元数据可用

在深入研究 Apache Tika 的富 Java API 之前,要做的第一步是弄清楚此 PDF 文件有哪些以及有多少元数据可用。元数据 这一术语指的是“有关数据的数据”,是对某个特定的内容条目的描述(在本例中,即 PDF 文件),通常包含一组命名了的字段,而每一个字段包含元数据值。比方说,一个 PDF 文件可以具有一个元数据描述,其中包含一个作者字段,而字段的值为 Barack Obama。我们可以使用之前提到的 Tika 的命令行实用工具来决定 PDF 文件有哪些元数据可用,如清单 5 所示。

清单 5. 读取 PDF 元文件数据
java —jar tika-app/target/tika-app-X.Y.jar —m \
    ./National_Aeronautics_and_Space_Administration.pdf

Content-Type: application/pdf
Last-Modified: Tue Feb 24 04:56:17 PST 2009
created: Sat Feb 21 07:38:41 PST 2009
creator: Adobe InDesign CS4 (6.0)
producer: Adobe PDF Library 9.0
resourceName: National_Aeronautics_and_Space_Administration.pdf

上述输出让我们可以大致了解所下载的整个 PDF 文件中有哪些元数据可用。不过,除了最后一次的修改时间和创建时间外,没有多少有趣的元数据可用。如果看一下白宫预算站点(http://www.whitehouse.gov/omb/budget/Overview/)上可用的 PDF 文件,我们不禁要问自己:“哪些预算是最近才上载(或修改的)?” 是因为其上有太多不确定的因素么?或许是因为在最后一分钟有一些预算的增加(或减少)需要被考虑进去。在任何情况下,通过调制出一个基于 Tika 的简捷 Java 程序,这类问题都可迎刃而解。(没错 — Java 程序并不能给出预算提高背后的合理解释,但那超出了我们的讨论范围。)

课程 1 可帮助您判断最近已经更改或修改的文档。文档均远程位于 web,记住这一点很重要。

清单 6. determineLast.java
public void determineLast() throws Exception {
 Tika tika = new Tika();
 Date lastDate = new Date();
 lastDate.setYear(lastDate.getYear() - 10);
 String lastUrl = null;
 for (String budgetUrl : URLs) {
 Metadata met = new Metadata();
 try {
 tika.parse(new URL(budgetUrl).openStream(), met);
 Date docDate = BudgetScramble.toDate(met.get(LAST_MODIFIED));
 log.info(System.getProperty("line.separator") + "Metadata:\t" +met);
 if (docDate.after(lastDate)) {
 lastDate = docDate;
 lastUrl = budgetUrl;
 }
 } catch (Exception e) {
	 log.error(e.getLocalizedMessage() + " " + e.getCause());
 }
    }

您可以通过键入 ant budgetscramble 运行这个示例。清单 7 显示了此程序的运行结果。

清单 7. Ant 命令的结果
budgetscramble:
     [java] 09/12/23 09:29:08 INFO example.BudgetScramble: The last budget to be
 finished is...[http://www.whitehouse.gov/omb/asset.aspx?AssetId=807] on: Thu Fe
b 26 15:55:07 IST 2009

清单 8 显示了 Apache Tika 的另一种使用,其中我们可以找到映射到的文档。

清单 8. Tika 映射示例
java -jar tika-app/target/tika-app-X.Y.jar \
    -x "http://www.whitehouse.gov/omb/budget/Overview/" | grep Asset | grep 807
<a href="http://www.whitehouse.gov/omb/asset.aspx?AssetId=807">Presentation and 
Technical Changes</a>

有趣的是,在本财政年度的预算中的确有一组新的变化和拨款。

上述例子充分展示了使用 Apache Tika 可以从内容提取哪些元数据。当然,您的情况可能不同,而 Tika 则力求提取尽量多的元数据。当然,这并不是说 Tika 只有提取元数据的能力。org.apache.tika.metadata.Metadata class 提供了一个丰富结构,可用来合并和轻松添加新的元数据键以及替换和修改已提取的元数据键。到目前为止,Tika 支持包括 Microsoft Word 和 Excel、ZIP/GZIP 在内的 20 余种常见格式。建议您查阅 http://lucene.apache.org/tika/formats.html 获得最新列表(或查阅本文之前给出过这些信息的部分)。

正如课程 1 所展示的,Tika 是访问 Tika 功能的一个外观类。这个类隐藏了低层 Tika 类的背后复杂性并提供了可用于诸多常见解析和类型检测操作的简单方法。

Metadata 是一个多值的元数据容器。最有趣的是一个解析可有两个参数 InputStreammetadata

课程 2:从任何文件类型进行自动元数据提取

除了课程 1 中涉及到的 PDF 文件外,Apache Tika 还具有从任何文件类型任意提取元数据的能力。在接下来的课程中,您将了解它是如何做到这一点的。如果您按捺不住,可直接跳转到课程 3 和 4,但为了阐明这一点,我们将以一个任意的 Open Office Document Template(.odt 文件)为例,将它的一些元数据自动打印到控制台。这个过程总地来说可以针对任何文件或内容类型执行,不管 Tika 是否真正理解它是哪种类型。Tika 的目标是从底层的文件类型提取尽量多的极小元数据信息,如清单 9 所示。

清单 9. 用 Tika 提取数据
List<File> list = 
Utils.getFiles(new File(Messages.getProperty("m001")), new ArrayList<File>());
for (File f : list) {
try {
	TikaMetadata tm = new TikaMetadata(f);
	tm.showMe();
 } catch (Exception e) {
	log.error(e.getLocalizedMessage() + " " + e.getCause());
		} 
}

首先,我们获得我们将要使用的文件的列表。其次,对于每个文件,我们定义 TikaMetadata 对象并显示此文件的元数据。

键入如下内容并查看结果:ant tikametadata。部分输出如清单 10 所示。

清单 10. TikaMetaData 的 ant 列表
     [java] thai_odt.odt
     [java] nbObject=0 nbPara=5 nbImg=0 nbTab=0 generator=OpenOffice.org/3.1$Lin
ux OpenOffice.org_project/310m19$Build-9420 date=2009-12-13T08:29:31 nbWord=1516
 nbPage=1 Content-Type=application/vnd.oasis.opendocument.text creator=Oleg  nbC
haracter=2031

注意到上述结果呈现了一组元数据键(出现于上述输出的 = 符号之前)以及与此文件类型相关的值(出现于上述输出的 = 符号之后)。由于 Tika 具有检测并解析.od 文件的能力,所以它能够提取更为复杂的元数据(比如,generator、 nbWord(s)、nbPage(s) 等)。

课程 3:理解 mimetype

那么,Apache Tika 怎么才能知道该如何从课程 1 的 PDF 预算文件中提取文本和元数据?Tika 自带一个全面的 mimetype 存储库。一个 mimetype 存储库是一组标准的 Internet Assigned Numbers Authority (IANA) mimetype 的定义,其中,对于每个所定义的 mimetype,所记录的每个条目都包含:

  • 它的名字(包括别名)
  • 它的父和子 mimetype
  • Mime MAGIC,一组控制字节,用来比较文件的前 1,024 KB 以便检测。
  • URL 模式,与被分析文件的文件扩展名或文件名相匹配
  • XML 根字符和名称空间

Apache Tika 使用 mimetype 存储库和一组模式(mime MAGIC、 URL 模式、XML 根字符或文件扩展名的任意组合)来决定某个文件、URL 或内容块是否与其已知类型中的一个相匹配。如果内容匹配,就说明 Tika 已经检测了它的 mimetype 并可进而选择适当的解析器。在本课程中,我们将研究某个文件的 mimetype 的一些属性并打印出这些属性。通常,在处理文件时,我们需要知道什么是文件 mimetype (比如,一个 TXT 文件、HTML 或 PDF),以及如何按原样读取它或像一个二进制一样读取它。简单的二进制输出不能提供任何信息。根据 mimetype,应该选择一个适当的解析器或与您的业务相关的东西。

清单 11. 处理 mimetype
public static void main(String[] args) {
Metadata metadata = new Metadata();
MimeTypes mimeTypes = TikaConfig.getDefaultConfig().getMimeRepository();
List<File> list = Utils.getFiles(new File(Messages.getProperty("m001")), 
new ArrayList<File>());
String mime = null;
for (File f : list) {
 URL url;
 try {
	url = new URL("file:" + f.getAbsolutePath());
	InputStream in = url.openStream();
 mime = mimeTypes.detect(in, metadata).toString();
 log.info("Mime: " + mime + " for file: " + f.getName());
 } catch (Exception e) {
	 log.error(e.getLocalizedMessage() + " " + e.getCause());
 }//try-catch
 }//foreach
}//function
ant tikamimetype

课程 4:从任何文件类型进行自动文本提取

除了能够提取元数据之外,Apache Tika 还力求能提供任何文件类型的文本内容,这些文本内容独立于其他的额外信息(打包、头、二进制 garble 以及其他通常随文件打包的杂项信息),只要它能够解析。Tika 的解析器都必须能够实现从某个它能解析的文件类型提取文本的基本功能。文本内容很有用,因为它可以被发送至搜索引擎,在内容管理系统内被索引以及用来显示针对特定内容块的总结性信息。在如下的例子中,我们通过几个步骤展示了在 Tika 内从任何文件类型提取文本内容是多么地容易。并且,与通常在电视上看到的免责声明不同,我们鼓励您在家里自己尝试。

清单 12. 从一个文件类型提取文本内容
TikaConfig tc = TikaConfig.getDefaultConfig();
List<File> list = Utils.getFiles(new File(Messages.getProperty("m001")), 
new ArrayList<File>());
Utils.deleteFiles(new File(Messages.getProperty("m002")));
for (File f : list) {
 try {
 String txt = ParseUtils.getStringContent(f, tc);
      Utils.writeTxtFile(new File(Messages.getProperty("m002") 
+ File.separator + Utils.getFileNameNoExtension(f) + ".txt"),txt);
      log.info(Messages.getProperty("m003") + f.getName() 
+ Messages.getProperty("m004"));
 } catch (TikaException e) {
	log.error(e.getLocalizedMessage() + " " + f.getName());
 } catch (IOException e) {
	log.error(e.getLocalizedMessage() + " " + f.getName());
 }catch (Exception e) {
	log.error(e.getLocalizedMessage() + " " + f.getName());
 }
}

上述魔法是由 ParseUtils getStringContent(...) 函数完成的。ParseUtils 包含了一些用来解析文档的实用工具方法。它旨在提供进入 Tika 框架的简单入口。其中的一个参数是文件,第二个参数是 TikaConfig,用来解析 XML 配置文件。它非常简单但却功能强大。

您是不是已经被好奇心煎熬,急切地想要知道这个魔术是如何完成的?键入 ant tikaextracttext

课程 5:语言识别

有了内容是一个很好的开始,但还不充分。还缺少语言的知识。那么,该如何识别内容的语言呢?通常,这类问题由 Natural Language Processing 方式负责处理。可不幸地是,您必须对它十分熟悉才行。不过,事情没有那么糟糕。Nutch 已经开发了一个模块,名为 LanguageIdentifier,我们将要使用的就是这个模块。让我们看看它是如何工作的。

清单 13. 使用 LanguageIdentifier 的示例
List<File> list = Utils.getFiles(new File(Messages.getProperty("m001")), 
new ArrayList<File>());
LanguageDetector ld = null;
for (Iterator<File> iterator = list.iterator(); iterator.hasNext();) {
 File file = (File) iterator.next();
 ld = new LanguageDetector(file);
    log.info(Messages.getProperty("m072") + ld.getLanguage() +
Messages.getProperty("m073") + ld.getFile().getName());
}//for

看到它是多么简单了吧?只需调用 getLanguage() 函数就万事大吉了!

尽请运行这个例子:ant languagedetector

好了,就是这些了。如果您有兴趣了解语言识别的工作原理、如何添加一个新的语言分析器等,那么请进一步阅读。

所有语言都可被很好地识别出来,除中文之外。这是为什么呢?因为我们的系统没有识别盒外语言的能力。因此,让我们开始创建一个 N-gram 分析器。在您深入钻研之前,我们先来解释一下什么是 N-gram,它如何工作以及如何构建一个培训集。

什么是 N-gram?

N-gram 是从文本或文档中提取的字符或单词序列,可被分成两组:基于字符的或基于单词的。一个 N-gram 是提取自一个单词(在我们例子中是一个字符串)的一组 N 个连续字符。其后的动机是类似的单词将具有高比例的 N-gram。最常见的 N 值是 2 和 3,分别称为 bigram 和 trigram。比如,单词 TIKA 导致生成的 bigram 为 *T、TI、IK、KA、A*,导致生成的 trigram 为 **T、*TI、TIK、IKA、KA*、 A**。“*” 代表的是一个补充空间。基于字符的 N-gram 被用来量度字符字符串的相似性。使用基于字符的 N-gram 有些应用程序有拼写检查程序、 stemming 和 OCR。

正如您猜想的那样,单词 N-gram 是提取自文本的 N 个连续单词的序列。它也独立于语言。基于两个字符串间的相似性的 N-gram 是由 Dice 的系数衡量的(非正式的说就是相似性衡量)。s = (2|X /\ Y|)/(|X| + |Y|),其中 XY 是这个字符集。/\ 表示两个集间的一个交集。如果我们采用的是字符串相似性衡量的方法,那么这个系数会从两个字符串或单词中计算得出,xy 使用 bigram: s = (2Nt)/(Nx + Ny),这里的 Nt 表示两个字符串中字符 bigram 的数量,Nx 表示字符串 x 中 bigram 的数量,Ny 表示字符串 y 中 bigram 的数量。例如,要计算 TIKA 和 TECA 间的相似性,我们要找到每个单词中的这组 bigram ,即(TI,IK,KA)及(TE,EC,CA)。每组有三个元素,并且这两组的交集是零。下面把这个放入公式并计算得出 s = (2x0)/(3+3) = 0。那么对于 bigram 真是完全不同吗?对于 1-gram 您将得到其他的结果。

一个大的文本 corpus(培训 corpus)被用于评估 N-gram 的概率。在 Nutch 的语言识别中,如果文件有一个 N-Gram Profile (NGP) 扩展,那么这个文件包含 N-grams 及其分数。例如,_dé17376 是一个得分为 17376 的 trigram。

N-gram 建模的一个主要问题是它的大小。幸好,我们只需完成这个进程一次。使用 N-gram 的另一个有趣的例子是提取特性来集群大量的地球卫星图片集和判断某张特定图片来自于地球的哪个部分。

语言的识别是怎样实现的?

总的来说,当要判断一个新的文档是用的什么语言时,我们首先要创建文档的 N-gram 概要文件并算出这个新文档概要文档与语言概要文件之间的距离。这个距离的计算根据的是两个概要文件之间的 “out-of-place measure”。 选择最短的距离,它表示此特定的文档属于该语言。这里要引入一个阈值,它的作用是当出现任何超过阈值的距离时,系统就会报告这个文档的语言不能被判定或判定有误。在我们创建 zh.ngp 前,我们的系统会把中文文档确定为德文文档。

通过添加一个新的 N-gram 语言概要文件,我们可以正确地识别该语言。Apache Tika V0.5 在框架中插入了一个 LanguageIdentifier 模块。它可以运行得很好,除非是一个文档包含了 LanguageIdentifier 不能识别的文本。因此,我们将它分成了不同的包。现在,您可以随意操作了,您可以添加任何还不受支持的语言,并从您的代码中调用 identify(...) 函数。

这个 NgramProfiler 主函数所要获取的参数之一就是一个 TXT 文件。在我们的示例中,它将是一个包含了从 Wikipedia 获取的中文文本的文本文件。文本的数量应该很大,以便能创建一个可以高效地预测文件的内容所用的语言的 N-gram 概要文件。此外,这里还需要从各种 wiki 主题中获取文本。主题可以涉及地理、数学、天文学等。它对于降噪也很重要(比如排除链接、图像名称和特殊字符)。数据必须冗余,以防阻碍识别的准确性。

创建一个 TXT 文件,比如 chines4ngram.txt。访问 Wikipedia,然后复制并粘贴一些文本到 chines4ngr.txt。尽量避免留空白行。爬过链接并收集东西。在本例中,越多越好。这是一个枯燥乏味的过程,但却很重要;5,000-6,000 行文本就足够了。

注意:这个过程可以使用 Nutch crawler 自动完成。

NgramProfiler 的主函数预期参数如下:-create <name_of_gram_profile> <text_file>

使用 Ant,键入:

ant createngram -Dngpname=/home/olegt/tutorial/zh \
-Dfile="/home/olegt/ chines4ngram.txt -Dencoding=utf-8

稍后,将 zh.ngp 复制到 org.apache.analysis.lang 包并通过键入 ant TikaLanguageIdentifier 重新运行 TikaLanguageIdentifier。查看输出。所有中文文件的内容均被正确地识别为 zh(中文)。

在本教程中,我们使用一个名为 cpdetector 的额外框架来判断一个文件的 charset 编码。名字 cpdetector 是页面检测程序的缩写,与 Java 类路径毫无关系。cpdetector 是一个用于文档的可配置代码页面检测的框架。它可用来检测检索自远程主机的代码页面。若文档属于何种编码不可知,就需要进行代码页面检测。因此,在信息挖掘或信息检索领域,这是对应用程序的一个核心要求。


相关主题

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Open source
ArticleID=500050
ArticleTitle=用 Apache Tika 理解信息内容
publish-date=07122010