
构建自定义语料库
|
35
无论是常规摄取文档还是部分获取固定集合,都必须考虑如何管理数据,并
为分析处理和模型计算做好准备。在下一节,我们将讨论如何监控语料库,
因为我们的摄取例程会继续进行,数据会增长变化。
语料库数据管理
假设即将处理的语料库是非常庞大的,可能包含成千上万篇文档,而最后的
大小可能有好几
GB
。另外还假设,语言数据将来自数据结构需要清洗和处理
才能进行分析的数据源。前一个假设需要可扩展的计算方法(在第
11
章中更
全面地探索),后者意味着我们将对数据进行不可逆的转换(正如我们在第
3
章中看到的那样)。
数据产品通常用一次写入、多次读取(
WORM
)的存储设施,作为摄取和预
处理之间的过渡数据管理层,如图
2-2
所示。
WORM
存储(有时也称为数据湖)
以可重复、可扩展的方式提供对原始数据的流式访问,满足高性能计算的要求。
通过将数据保存在
WORM
存储中,预处理数据可以直接重新分析,无需重新
摄取,从而允许在原始数据格式上轻松开始新的探索过程。
流式数据集读取器 预处理转换器
数据摄取(去重)
WORM存储器
(原始HTML/JSON/
CSV格式数据)
词条化
图
2
-
2
:WORM 存储对过渡整合提供支持
在数据提取流水线中加入
WORM
存储,意味着要把数据保存在两个地方(原
始语料库和预处理语料库),这就引出了一个问题:数据存在哪?
考虑数据
管理时,我们通常首先考虑数据库。数据库无疑是构建语言感知数据产品的

36
|
第
2
章
好工具,许多数据库都提供了全文搜索功能和其他类索引功能。但是,大多
数数据库被设计成每个事务只能检索或者更新几行数据。
而对文本语料库的
访问,主要是对每个完整文档的读取,不会对文档进行本地更新,也不会搜
索或查询单个文档。因此,在这里用数据库往往会增加计算开销,实际上并
没什么好处。
关系数据库管理系统非常适用一次对少数几行数据进行操作的事务,特别是
当这些行经常更新时。文本语料库上的机器学习需求则有所不同:主要是对
整个数据集的多次顺序读取。因此,通常优先考虑将语料库存储在磁盘(或
文档数据库)中。
要管理文本数据,最佳选择通常是将数据存到
NoSQL
文档数据库中,这类数
据库能以最小开销读取文档,或直接把每个文档写进磁盘。尽管
NoSQL
数据
库在大型应用程序中很合适,但基于文件的方法也有其优点:文件目录上的
压缩技术很适合用于文本数据,还能用文件同步服务完成自动复制。用数据
库构建语料库超出了本书范围,尽管后面我们会在本章简要介绍一个
Sqlite
语料库。接下来,我们会介绍在磁盘上有效组织数据的方法,来支持系统对
语料库的访问。
语料库磁盘结构
组织和管理基于文本的语料库最简单、最常用的方法,是把单独的文档保存
在磁盘的文件系统。通过将语料库组织成子目录,可以对语料库进行分类或
根据元信息(如日期)进行有意义的划分。将每个文档保存成自己对应的文件,
语料库读取器可以快速搜索不同的子文档集,还可以并行处理,每个进程处
理不同的子文档集。
我们将在下一节探讨如何利用
NLTK CorpusReader
对象从目录或
Zip
文件中
读取数据。

构建自定义语料库
|
37
文本文件也是最容易压缩的格式,压缩得到的
Zip
文件可以保留磁盘上的目
录结构,是理想的数据分发、存储格式。另外,存在磁盘上的语料库通常是
静态的,可作为整体进行处理,满足上一节介绍的
WORM
存储的要求。
但是,每个文档单独存成一个文件,可能会带来一些挑战。太小的文档(如
电子邮件或推文)作为单个文件存储没什么意义。另外,电子邮件通常以
MBox
格式存储,这是一种用分隔符来分隔其中的文本、
HTML
、图像、附件
的多部分
MIME
消息,通常可以按电子邮件服务(收件箱、加星标、存档等)
包含的类别进行组织。推文通常是小型
JSON
数据结构,不仅包括推文的文本,
还包括其他元数据,如用户或位置信息,存储多个推文的典型方法,是以换
行符分隔的
JSON
内容,有时也叫做
JSON
行格式。这种格式一次解析一行,
就能得到一条推文,也可以在文件中通过搜索找到不同的推文。包含推文的
单个文件可能会很大,因此按用户名、位置或日期来组织推文文件,可以减
少单个文件大小,创建多文件的、更有意义的磁盘结构。
将数据存储在某个逻辑结构中的另一种方法,是简单写入具有最大容量限制
的文件。例如,我们可以持续写入数据,直到达到某个大小限制(例如,
128
MB
),然后打开一个新文件并继续写。
磁盘上的语料库必然包含许多文件,每个文件包含语料库中的一个或多个文
档,有时划分为子目录,这些子目录表示有意义的分组,如类别。语料库和
文档元信息也必须与对应文档保存在一起。磁盘上语料库按标准结构进行组
织,对确保
Python
程序有效读取这些数据至关重要。
无论文档是聚合成多文档文件,还是每篇文档保存成独立的文件,语料库表
示需要组织起来的许多文件。如果随着时间推移,语料库摄取了新的数据,
一种有意义的组织方式是按年、月、日组织子目录,文档分别放置在对应文
件夹中。如果文档按情绪分类,无论是正面还是负面,每种类型的文档都可
以组合在一起,放在自己的类别子目录下。如果系统中有多个用户,生成了
他们自己的特定写作子集,例如评论或推文,那么每个用户都可以有自己的
子目录。所有子目录需要并存在一个语料库根目录下。还有一点很重要,诸

38
|
第
2
章
如许可证、清单、自述文件
(README)
或引用之类的语料库元信息,也必须
和文档一起保存,这样语料库看起来才是一个独立的整体。
Baleen
文件结构
磁盘上组织方式的选择对
CorpusReader
对象读取文档的方式有很大影响,我
们将在下一节介绍。
Baleen
语料库摄取引擎将
HTML
语料库写入磁盘,如下
所示:
corpus
├──
citation.bib
├──
feeds.json
├──
LICENSE.md
├──
manifest.json
├──
README.md
└──
books
|
├──
56d629e7c1808113ffb87eaf.html
|
├──
56d629e7c1808113ffb87eb3.html
|
└──
56d629ebc1808113ffb87ed0.html
└──
business
|
├──
56d625d5c1808113ffb87730.html
|
├──
56d625d6c1808113ffb87736.html
|
└──
56d625ddc1808113ffb87752.html
└──
cinema
|
├──
56d629b5c1808113ffb87d8f.html
|
├──
56d629b5c1808113ffb87d93.html
|
└──
56d629b6c1808113ffb87d9a.html
└──
cooking
├──
56d62af2c1808113ffb880ec.html
├──
56d62af2c1808113ffb880ee.html
└──
56d62af2c1808113ffb880fa.html
这里有几点需要注意。首先,所有文档都存为
HTML
文件,根据其
MD5
哈
希值进行命名(为了防止重复),每个文件都存在对应类别子目录下。通过
目录结构和每个文件的文件名,可以简单识别哪些文件是文档,哪些文件是
元文件。元信息方面,
citation.bib
文件提供语料库的属性,
LICENSE.md
文件
指定其他人使用此语料库必需的权限。虽然这两条信息通常是为公共语料库
保留的,但将它们包含在内,以便明确如何使用语料库是有帮助的,这与将
此类信息添加到私有软件仓库是一个道理。
feeds.json
和
manifest.json
是两个
特定于语料库的文件,分别用于标识有关类别和每个特定文件的信息。最后,
README.md
文件是语料库的自然语言描述。
Get 基于Python的智能文本分析 now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.