提交 544f90aa 编写于 作者: W wizardforcel

2020-12-29 14:16:09

上级 88a8a89b
......@@ -61,7 +61,7 @@ SyntaxError: invalid syntax
在进一步深入之前,应先安装 NLTK 3.0,可以从`http://nltk.org/`免费下载。按照说明下载适合你的操作系统的版本。
安装完 NLTK 之后,像前面那样启动 Python 解释器,在 Python 提示符后面输入下面两个命令来安装本书所需的数据,然后选择`book`集合,如[1.1](http://www.nltk.org/book/ch01.html#fig-nltk-downloader)所示。
安装完 NLTK 之后,像前面那样启动 Python 解释器,在 Python 提示符后面输入下面两个命令来安装本书所需的数据,然后选择`book`集合,如 1.1 所示。
```py
>>> import nltk
......@@ -70,7 +70,7 @@ SyntaxError: invalid syntax
![Images/nltk-downloader.png](Images/7f27bfe5324e4d9573ddd210531a8126.jpg)
图 1.1:下载 NLTK Book 集:使用`nltk.download()`浏览可用的软件包.下载器上`Collections`选项卡显示软件包如何被打包分组,选择`book`标记所在行,可以获取本书的例子和练习所需的全部数据。这些数据包括约 30 个压缩文件,需要 100MB 硬盘空间。完整的数据集(即下载器中的`all`)在本书写作期间大约是这个大小的 10 倍,还在不断扩充。
图 1.1:下载 NLTK 图书集:使用`nltk.download()`浏览可用的软件包.下载器上`Collections`选项卡显示软件包如何被打包分组,选择`book`标记所在行,可以获取本书的例子和练习所需的全部数据。这些数据包括约 30 个压缩文件,需要 100MB 硬盘空间。完整的数据集(即下载器中的`all`)在本书写作期间大约是这个大小的 10 倍,还在不断扩充。
一旦数据被下载到你的机器,你就可以使用 Python 解释器加载其中一些。第一步是在 Python 提示符后输入一个特殊的命令,告诉解释器去加载一些我们要用的文本:`from nltk.book import *` 。这条语句是说“从 NLTK 的`book`模块加载所有的东西”。这个`book`模块包含你阅读本章所需的所有数据。。在输出欢迎信息之后,将会加载几本书的文本(这将需要几秒钟)。下面连同你将看到的输出一起再次列出这条命令。注意拼写和标点符号的正确性,记住不要输入`>>>`
......@@ -106,7 +106,7 @@ text9: The Man Who Was Thursday by G . K . Chesterton 1908
## 1.3 搜索文本
除了阅读文本之外,还有很多方法可以用来研究文本内容。词语索引视角显示一个指定单词的每一次出现,连同一些上下文一起显示。下面我们输入`text1`后面跟一个点,再输入函数名`concordance`,然后将`"monstrous"`放在括号里,来查一下 *Moby Dick*《白鲸记》中的词 monstrous
除了阅读文本之外,还有很多方法可以用来研究文本内容。词语索引视角显示一个指定单词的每一次出现,连同一些上下文一起显示。下面我们输入`text1`后面跟一个点,再输入函数名`concordance`,然后将`"monstrous"`放在括号里,来查一下 *Moby Dick*《白鲸记》中的词`monstrous`
```py
>>> text1.concordance("monstrous")
......@@ -125,15 +125,15 @@ of Whale - Bones ; for Whales of a monstrous size are oftentimes cast up dead u
>>>
```
在一段特定的文本上第一次使用 concordance 会花费一点时间来构建索引,因此接下来的搜索会很快。
在一段特定的文本上第一次使用`concordance`会花费一点时间来构建索引,因此接下来的搜索会很快。
注意
**轮到你来:** 尝试搜索其他词;为了方便重复输入,你也许会用到上箭头,Ctrl-上箭头或者 Alt-p 获取之前输入的命令,然后修改要搜索的词。你也可以在我们包含的其他文本上搜索。例如, 使用`text2.concordance("affection")`,搜索 *Sense and Sensibility*《理智与情感》中的 affection。使用`text3.concordance("lived")`搜索 Genesis《创世纪》找出某人活了多久。你也可以看看`text4`,_Inaugural Address Corpus_《就职演说语料》,回到 1789 年看看那时英语的例子,搜索如 nation, terror,god 这样的词,看看随着时间推移这些词的使用如何不同。我们也包括了`text5`,_NPS Chat Corpus_《NPS 聊天语料库》:你可以在里面搜索一些网络词,如 im ur,lol。(注意这个语料库未经审查!)
**轮到你来:** 尝试搜索其他词;为了方便重复输入,你也许会用到上箭头,`Ctrl+↑`或者`Alt+p`获取之前输入的命令,然后修改要搜索的词。你也可以在我们包含的其他文本上搜索。例如, 使用`text2.concordance("affection")`,搜索《理智与情感》(*Sense and Sensibility*)中的`affection`。使用`text3.concordance("lived")`搜索《创世纪》(Genesis)找出某人活了多久。你也可以看看`text4`,《就职演说语料》(*Inaugural Address Corpus*),回到 1789 年看看那时英语的例子,搜索如`nation`, `terror``god`这样的词,看看随着时间推移这些词的使用如何不同。我们也包括了`text5`,《NPS 聊天语料库》(NPS Chat Corpus):你可以在里面搜索一些网络词,如`im ur``lol`。(注意这个语料库未经审查!)
在你花了一小会儿研究这些文本之后,我们希望你对语言的丰富性和多样性有一个新的认识。在下一章中,你将学习获取更广泛的文本,包括英语以外其他语言的文本。
词语索引使我们看到词的上下文。例如,我们看到 monstrous 出现的上下文, the ___ pictures 和 a ___ size。还有哪些词出现在相似的上下文中?我们可以通过在被查询的文本名后添加函数名`similar`,然后在括号中插入相关的词来查找到:
词语索引使我们看到词的上下文。例如,我们看到`monstrous`出现的上下文, `the ___ pictures``a ___ size`。还有哪些词出现在相似的上下文中?我们可以通过在被查询的文本名后添加函数名`similar`,然后在括号中插入相关的词来查找到:
```py
>>> text1.similar("monstrous")
......@@ -146,9 +146,9 @@ extremely good sweet
>>>
```
观察我们从不同的文本中得到的不同结果。Austen 使用这些词与 Melville 完全不同;在她那里,monstrous 是正面的意思,有时它的功能像词 very 一样作强调成分。
观察我们从不同的文本中得到的不同结果。Austen 使用这些词与 Melville 完全不同;在她那里,`monstrous`是正面的意思,有时它的功能像词`very`一样作强调成分。
函数`common_contexts`允许我们研究两个或两个以上的词共同的上下文,如 monstrous 和 very。我们必须用方括号和圆括号把这些词括起来,中间用逗号分割:
函数`common_contexts`允许我们研究两个或两个以上的词共同的上下文,如`monstrous``very`。我们必须用方括号和圆括号把这些词括起来,中间用逗号分割:
```py
>>> text2.common_contexts(["monstrous", "very"])
......@@ -160,7 +160,7 @@ a_pretty is_pretty am_glad be_glad a_lucky
**轮到你来:** 挑选另一对词,使用`similar()``common_contexts()`函数比较它们在两个不同文本中的用法。
自动检测出现在文本中的特定的词,并显示同样上下文中出现的一些词,这只是一个方面。我们也可以判断词在文本中的*位置*:从文本开头算起在它前面有多少词。这个位置信息可以用离散图表示。每一个竖线代表一个单词,每一行代表整个文本。在[1.2](http://www.nltk.org/book/ch01.html#fig-inaugural) 中,我们看到在过去 220 年中的一些显著的词语用法模式(在一个由就职演说语料首尾相连的人为组合的文本中)。可以用下面的方法画出这幅图。你也许会想尝试更多的词(如,liberty,constitution)和不同的文本。你能在看到这幅图之前预测一个词的分布吗?跟以前一样,请保证引号、逗号、中括号及小括号的使用完全正确。
自动检测出现在文本中的特定的词,并显示同样上下文中出现的一些词,这只是一个方面。我们也可以判断词在文本中的*位置*:从文本开头算起在它前面有多少词。这个位置信息可以用离散图表示。每一个竖线代表一个单词,每一行代表整个文本。在 1.2 中,我们看到在过去 220 年中的一些显著的词语用法模式(在一个由就职演说语料首尾相连的人为组合的文本中)。可以用下面的方法画出这幅图。你也许会想尝试更多的词(如,`liberty``constitution`)和不同的文本。你能在看到这幅图之前预测一个词的分布吗?跟以前一样,请保证引号、逗号、中括号及小括号的使用完全正确。
```py
>>> text4.dispersion_plot(["citizens", "democracy", "freedom", "duties", "America"])
......@@ -279,7 +279,7 @@ Note
在本章中你已经遇到了几个函数,如`len()`, `set()``sorted()`。通常我们会在函数名后面加一对空括号,像`len()`中的那样,这只是为了表明这是一个函数而不是其他的 Python 表达式。函数是编程中的一个重要概念,我们在一开始提到它们,是为了让新同学体会编程的强大和富有创造力。如果你现在觉得有点混乱,请不要担心。
稍后我们将看到如何使用函数列表显示数据,像表[1.1](http://www.nltk.org/book/ch01.html#tab-brown-types)显示的那样。表的每一行将包含不同数据的相同的计算,我们用函数来做这种重复性的工作。
稍后我们将看到如何使用函数列表显示数据,像表 1.1 显示的那样。表的每一行将包含不同数据的相同的计算,我们用函数来做这种重复性的工作。
表 1.1:
......@@ -571,13 +571,13 @@ what output do you expect here?
## 3.1 频率分布
我们如何能自动识别文本中最能体现文本的主题和风格的词汇?试想一下,要找到一本书中使用最频繁的 50 个词你会怎么做?一种方法是为每个词项设置一个计数器,如图[3.1](http://www.nltk.org/book/ch01.html#fig-tally)显示的那样。计数器可能需要几千行,这将是一个极其繁琐的过程——如此繁琐以至于我们宁愿把任务交给机器来做。
我们如何能自动识别文本中最能体现文本的主题和风格的词汇?试想一下,要找到一本书中使用最频繁的 50 个词你会怎么做?一种方法是为每个词项设置一个计数器,如图 3.1 显示的那样。计数器可能需要几千行,这将是一个极其繁琐的过程——如此繁琐以至于我们宁愿把任务交给机器来做。
![Images/tally.png](Images/de0715649664a49a5ab2e2b61ae2675a.jpg)
图 3.1:计数一个文本中出现的词(频率分布)
[3.1](http://www.nltk.org/book/ch01.html#fig-tally) 中的表被称为频率分布,它告诉我们在文本中的每一个词项的频率。(一般情况下,它能计数任何观察得到的事件。)这是一个“分布”因为它告诉我们文本中单词词符的总数是如何分布在词项中的。因为我们经常需要在语言处理中使用频率分布,NLTK 中内置了它们。让我们使用`FreqDist`寻找《白鲸记》中最常见的 50 个词:
3.1 中的表被称为频率分布,它告诉我们在文本中的每一个词项的频率。(一般情况下,它能计数任何观察得到的事件。)这是一个“分布”因为它告诉我们文本中单词词符的总数是如何分布在词项中的。因为我们经常需要在语言处理中使用频率分布,NLTK 中内置了它们。让我们使用`FreqDist`寻找《白鲸记》中最常见的 50 个词:
```py
>>> fdist1 = FreqDist(text1)
......@@ -605,7 +605,7 @@ what output do you expect here?
**轮到你来:**使用`text2`尝试前面的频率分布的例子。注意正确使用括号和大写字母。如果你得到一个错误消息`NameError: name 'FreqDist' is not defined`,你需要在一开始输入`from nltk.book import *`
上一个例子中是否有什么词有助于我们把握这个文本的主题或风格呢?只有一个词,whale,稍微有些信息量!它出现了超过 900 次。其余的词没有告诉我们关于文本的信息;它们只是“管道”英语。这些词在文本中占多少比例?我们可以产生一个这些词汇的累积频率图,使用`fdist1.plot(50, cumulative=True)`来生成[3.2](http://www.nltk.org/book/ch01.html#fig-fdist-moby) 中的图。这 50 个词占了书的将近一半!
上一个例子中是否有什么词有助于我们把握这个文本的主题或风格呢?只有一个词,whale,稍微有些信息量!它出现了超过 900 次。其余的词没有告诉我们关于文本的信息;它们只是“管道”英语。这些词在文本中占多少比例?我们可以产生一个这些词汇的累积频率图,使用`fdist1.plot(50, cumulative=True)`来生成 3.2 中的图。这 50 个词占了书的将近一半!
![Images/fdist-moby.png](Images/513df73dfd52feca2c96a86dcc261c8b.jpg)
......@@ -720,7 +720,7 @@ FreqDist({3: 50223, 1: 47933, 4: 42345, 2: 38513, 5: 26597, 6: 17111, 7: 14399,
由此我们看到,最频繁的词长度是 3,长度为 3 的词有 50,000 多个(约占书中全部词汇的 20%)。虽然我们不会在这里追究它,关于词长的进一步分析可能帮助我们了解作者、文体或语言之间的差异。
[3.1](http://www.nltk.org/book/ch01.html#tab-freqdist) 总结了 NLTK 频率分布类中定义的函数。
3.1 总结了 NLTK 频率分布类中定义的函数。
表 3.1:
......@@ -742,7 +742,7 @@ NLTK 频率分布类中定义的函数
>>>
```
所有这些例子都有一个共同的模式:`[w for w in text if *condition* ]`,其中 *condition* 是 Python 中的一个“测试”,得到真或者假。在前面的代码例子所示的情况中,条件始终是数值比较。然而,我们也可以使用表[4.2](http://www.nltk.org/book/ch01.html#tab-word-tests) 中列出的函数测试词汇的各种属性。
所有这些例子都有一个共同的模式:`[w for w in text if *condition* ]`,其中 *condition* 是 Python 中的一个“测试”,得到真或者假。在前面的代码例子所示的情况中,条件始终是数值比较。然而,我们也可以使用表 4.2 中列出的函数测试词汇的各种属性。
表 4.2:
......
......@@ -74,7 +74,7 @@ equivalence <->
从命题符号和布尔运算符,我们可以建立命题逻辑的规范公式(或简称公式)的无限集合。首先,每个命题字母是一个公式。然后,如果φ是一个公式,那么`-`φ也是一个公式。如果φ和ψ是公式,那么`(`φ `&` ψ`)` `(`φ `|` ψ`)` `(`φ `->` ψ`)` `(`φ `<->` ψ`)`也是公式。
[2.1](./ch10.html#tab-boolean-tcs)指定了包含这些运算符的公式为真的条件。和以前一样,我们使用φ和ψ作为句子中的变量,iff 作为 if and only if(当且仅当)的缩写。
2.1 指定了包含这些运算符的公式为真的条件。和以前一样,我们使用φ和ψ作为句子中的变量,iff 作为 if and only if(当且仅当)的缩写。
表 2.1:
......@@ -106,7 +106,7 @@ True
这里有另一种方式可以看到结论如何得出。`SnF -&gt; -FnS`在语义上等价于`-SnF | -FnS`,其中 "`|`"是对应于 or 的二元运算符。在一般情况下,φ`|`ψ在条件 *s* 中为真,要么φ在 *s* 中为真,要么ψ在 *s* 中为真。现在,假设`SnF``-SnF | -FnS`都在 *s* 中为真。如果`SnF`为真,那么`-SnF`不可能也为真;经典逻辑的一个基本假设是:一个句子在一种情况下不能同时为真和为假。因此,`-FnS`必须为真。
回想一下,我们解释相对于一个模型的一种逻辑语言的句子,它们是这个世界的一个非常简化的版本。一个命题逻辑的模型需要为每个可能的公式分配值`True``False`。我们一步步的来做这个:首先,为每个命题符号分配一个值,然后确定布尔运算符的含义(即[2.1](./ch10.html#tab-boolean-tcs))和运用它们到这些公式的组件的值,来计算复杂的公式的值。`估值`是从逻辑的基本符号映射到它们的值。下面是一个例子:
回想一下,我们解释相对于一个模型的一种逻辑语言的句子,它们是这个世界的一个非常简化的版本。一个命题逻辑的模型需要为每个可能的公式分配值`True``False`。我们一步步的来做这个:首先,为每个命题符号分配一个值,然后确定布尔运算符的含义(即 2.1)和运用它们到这些公式的组件的值,来计算复杂的公式的值。`估值`是从逻辑的基本符号映射到它们的值。下面是一个例子:
```py
>>> val = nltk.Valuation([('P', True), ('Q', True), ('R', False)])
......@@ -221,7 +221,7 @@ False
> 4. If φ and ψ are of type t, then so are (φ `&` ψ), (φ `|` ψ), (φ `->` ψ) and (φ `<->` ψ).
> 5. If φ is of type t, and x is a variable of type e, then `exists x.`φ and `all x.`φ are of type t.
[3.1](./ch10.html#tab-nltk-logic)总结了`logic`模块的新的逻辑常量,以及`Expression`模块的两个方法。
3.1 总结了`logic`模块的新的逻辑常量,以及`Expression`模块的两个方法。
表 3.1:
......@@ -260,7 +260,7 @@ False
注意
**轮到你来:**模仿[1.2](./ch10.html#fig-model-kids)绘制一个图,描述域`m`和相应的每个一元谓词的集合。
**轮到你来:**模仿 1.2 绘制一个图,描述域`m`和相应的每个一元谓词的集合。
你可能已经注意到,我们的一元谓词(即`boy``girl``dog`)也是以单个元组的集合而不是个体的集合出现的。这使我们能够方便的统一处理任何元数的关系。一个形式为 P(τ[1], ... τ&lt;sub&gt;n&lt;/sub&gt;)的谓词,其中 P 是 n 元的,为真的条件是对应于(τ[1], ... τ&lt;sub&gt;n&lt;/sub&gt;) 的值的元组属于 P 的值的元组的集合。
......@@ -679,7 +679,7 @@ all x.(girl(x) -> exists z4.(dog(z4) & chase(x,z4)))
```
我们可以使用`draw()`方法[](./ch10.html#draw-drs)可视化结果,如[5.2](./ch10.html#fig-drs-screenshot)所示。
我们可以使用`draw()`方法[](./ch10.html#draw-drs)可视化结果,如 5.2 所示。
```py
>>> drs1.draw()
......@@ -689,7 +689,7 @@ all x.(girl(x) -> exists z4.(dog(z4) & chase(x,z4)))
图 5.2:DRS 截图
我们讨论[5.1](./ch10.html#fig-drs1)中 DRS 的真值条件时,假设最上面的段落指称被解释为存在量词,而条件也进行了解释,虽然它们是联合的。事实上,每一个 DRS 都可以转化为一阶逻辑公式,`fol()`方法实现这种转换。
我们讨论 5.1 中 DRS 的真值条件时,假设最上面的段落指称被解释为存在量词,而条件也进行了解释,虽然它们是联合的。事实上,每一个 DRS 都可以转化为一阶逻辑公式,`fol()`方法实现这种转换。
```py
>>> print(drs1.fol())
......
......@@ -56,7 +56,7 @@ TIMIT 演示了语料库设计中的几个主要特点。首先,语料库包
图 1.2:发布的 TIMIT 语料库的结构:CD-ROM 包含文档、顶层的训练和测试目录;训练和测试目录都有 8 子目录,每个方言区一个;这些目录又包含更多子目录,每个说话者一个;列出的目录是女性说话者`aks0`的目录的内容,显示 10 个`wav`文件配以一个录音文本文件、一个录音文本词对齐文件和一个音标文件。
TIMIT 的第四个特点是语料库的层次结构。每个句子 4 个文件,500 个说话者每人 10 个句子,有 20,000 个文件。这些被组织成一个树状结构,示意图如[1.2](./ch11.html#fig-timit-structure)所示。在顶层分成训练集和测试集,用于开发和评估统计模型。
TIMIT 的第四个特点是语料库的层次结构。每个句子 4 个文件,500 个说话者每人 10 个句子,有 20,000 个文件。这些被组织成一个树状结构,示意图如 1.2 所示。在顶层分成训练集和测试集,用于开发和评估统计模型。
最后,请注意虽然 TIMIT 是语音语料库,它的录音文本和相关数据只是文本,可以使用程序处理了,就像任何其他的文本语料库那样。因此,许多在这本书中所描述的计算方法都适用。此外,注意 TIMIT 语料库包含的所有数据类型分为词汇和文字两个基本类别,我们将在下面讨论。说话者人口学统计数据只不过是词汇数据类型的另一个实例。
......@@ -68,9 +68,9 @@ TIMIT 的第四个特点是语料库的层次结构。每个句子 4 个文件
图 1.3:基本语言数据类型——词汇和文本:它们的多样性中,词汇具有记录结构,而已标注文本具有时间组织。
不考虑它的复杂性,TIMIT 语料库只包含两种基本数据类型,词典和文本。正如我们在[2.](./ch02.html#chap-corpora)中所看到的,大多数词典资源都可以使用记录结构表示,即一个关键字加一个或多个字段,如[1.3](./ch11.html#fig-datatypes)所示。词典资源可能是一个传统字典或比较词表,如下所示。它也可以是一个短语词典,其中的关键字是一个短语而不是一个词。词典还包括记录结构化的数据,我们可以通过对应主题的非关键字字段来查找条目。我们也可以构造特殊的表格(称为范例)来进行对比和说明系统性的变化,[1.3](./ch11.html#fig-datatypes)显示了三个动词。TIMIT 的说话者表也是一种词典资源。
不考虑它的复杂性,TIMIT 语料库只包含两种基本数据类型,词典和文本。正如我们在[2.](./ch02.html#chap-corpora)中所看到的,大多数词典资源都可以使用记录结构表示,即一个关键字加一个或多个字段,如 1.3 所示。词典资源可能是一个传统字典或比较词表,如下所示。它也可以是一个短语词典,其中的关键字是一个短语而不是一个词。词典还包括记录结构化的数据,我们可以通过对应主题的非关键字字段来查找条目。我们也可以构造特殊的表格(称为范例)来进行对比和说明系统性的变化,1.3 显示了三个动词。TIMIT 的说话者表也是一种词典资源。
在最抽象的层面上,文本是一个真实的或虚构的讲话事件的表示,该事件的时间过程也在文本本身存在。一个文本可以是一个小单位,如一个词或句子,也可以是一个完整的叙述或对话。它可能会有标注如词性标记、形态分析、话语结构等。正如我们在 IOB 标注([7.](./ch07.html#chap-chunk))中所看到的可以使用单个词的标记表示更高层次的成分。因此,[1.3](./ch11.html#fig-datatypes)所示的文本的抽象就足够了。
在最抽象的层面上,文本是一个真实的或虚构的讲话事件的表示,该事件的时间过程也在文本本身存在。一个文本可以是一个小单位,如一个词或句子,也可以是一个完整的叙述或对话。它可能会有标注如词性标记、形态分析、话语结构等。正如我们在 IOB 标注([7.](./ch07.html#chap-chunk))中所看到的可以使用单个词的标记表示更高层次的成分。因此,1.3 所示的文本的抽象就足够了。
不考虑单独的语料库的复杂性和特质,最基本的,它们是带有记录结构化数据的文本集合。语料库的内容往往偏重于这些类型中的一种或多种。例如:布朗语料库包含 500 个文本文件,但我们仍然可以使用表将这些文件与 15 种不同风格关联。在事情的另一面,WordNet 包含 117659 个同义词集记录,也包含许多例子句子(小文本)来说明词的用法。TIMIT 处在中间,含有大量的独立的文本和词汇类型的材料。
......@@ -104,7 +104,7 @@ Kappa 系数 K 测量两个人判断类别和修正预期的期望一致性的
图 2.1:一个序列的三种分割:小矩形代表字、词、句,总之,任何可能被分为语言单位的序列;S[1]和 S[2]是接近一致的,两者都与 S[3]显著不同。
我们还可以测量语言输入的两个独立分割的一致性,例如分词、句子分割、命名实体识别。在[2.1](./ch11.html#fig-windowdiff)中,我们看到三种可能的由标注者(或程序)产生的项目序列的分割。虽然没有一个完全一致,S[1]和 S[2]是接近一致的,我们想要一个合适的测量。Windowdiff 是评估两个分割一致性的一个简单的算法,通过在数据上移动一个滑动窗口计算近似差错的部分得分。如果我们将词符预处理成 0 和 1 的序列,当词符后面跟着边界符号时记录下来,我们就可以用字符串表示分割,应用 windowdiff 打分器。
我们还可以测量语言输入的两个独立分割的一致性,例如分词、句子分割、命名实体识别。在 2.1 中,我们看到三种可能的由标注者(或程序)产生的项目序列的分割。虽然没有一个完全一致,S[1]和 S[2]是接近一致的,我们想要一个合适的测量。Windowdiff 是评估两个分割一致性的一个简单的算法,通过在数据上移动一个滑动窗口计算近似差错的部分得分。如果我们将词符预处理成 0 和 1 的序列,当词符后面跟着边界符号时记录下来,我们就可以用字符串表示分割,应用 windowdiff 打分器。
```py
>>> s1 = "00000010000000001000000"
......@@ -124,7 +124,7 @@ Kappa 系数 K 测量两个人判断类别和修正预期的期望一致性的
随着大型语料库的发布,研究人员立足于均衡的从为完全不同的目的而创建的语料库中派生出的子集进行调查的可能性越来越大。例如,Switchboard 数据库,最初是为识别说话人的研究而收集的,已被用作语音识别、单词发音、口吃、句法、语调和段落结构研究的基础。重用语言语料库的动机包括希望节省时间和精力,希望在别人可以复制的材料上工作,有时希望研究语言行为的更加自然的形式。为这样的研究选择子集的过程本身可视为一个不平凡的贡献。
除了选择语料库的适当的子集,这个新的工作可能包括重新格式化文本文件(如转换为 XML),重命名文件,重新为文本分词,选择数据的一个子集来充实等等。多个研究小组可以独立的做这项工作,如[2.2](./ch11.html#fig-evolution)所示。在以后的日子,应该有人想要组合不同的版本的源数据,这项任务可能会非常繁重。
除了选择语料库的适当的子集,这个新的工作可能包括重新格式化文本文件(如转换为 XML),重命名文件,重新为文本分词,选择数据的一个子集来充实等等。多个研究小组可以独立的做这项工作,如 2.2 所示。在以后的日子,应该有人想要组合不同的版本的源数据,这项任务可能会非常繁重。
![Images/evolution.png](Images/e33fb540f11c5ea9a07441be8a407d43.jpg)
......@@ -146,7 +146,7 @@ Kappa 系数 K 测量两个人判断类别和修正预期的期望一致性的
## 3.1 从网上获取数据
网络是语言分析的一个丰富的数据源。我们已经讨论了访问单个文件,如 RSS 订阅、搜索引擎的结果(见[3.1](./ch03.html#sec-accessing-text))的方法。然而,在某些情况下,我们要获得大量的 Web 文本。
网络是语言分析的一个丰富的数据源。我们已经讨论了访问单个文件,如 RSS 订阅、搜索引擎的结果(见 3.1)的方法。然而,在某些情况下,我们要获得大量的 Web 文本。
最简单的方法是获得出版的网页文本的文集。Web 语料库 ACL 特别兴趣组(SIGWAC)在`http://www.sigwac.org.uk/`维护一个资源列表。使用定义好的 Web 语料库的优点是它们有文档、稳定并允许重复性实验。
......@@ -176,7 +176,7 @@ Kappa 系数 K 测量两个人判断类别和修正预期的期望一致性的
这个简单的程序只是冰山一角。我们可以开发复杂的工具来检查字处理器文件的一致性,并报告错误,使字典的维护者可以*使用原来的文字处理器*纠正的原始文件。
只要我们知道数据的正确格式,就可以编写其他程序将数据转换成不同格式。[3.1](./ch11.html#code-html2csv)中的程序使用`nltk.clean_html()`剥离 HTML 标记,提取词和它们的发音,以“逗号分隔值”(CSV)格式生成输出。
只要我们知道数据的正确格式,就可以编写其他程序将数据转换成不同格式。3.1 中的程序使用`nltk.clean_html()`剥离 HTML 标记,提取词和它们的发音,以“逗号分隔值”(CSV)格式生成输出。
```py
from bs4 import BeautifulSoup
......@@ -219,11 +219,11 @@ f_out.write(bytes(s, 'UTF-8'))
## 3.4 转换数据格式
已标注语言数据很少以最方便的格式保存,往往需要进行各种格式转换。字符编码之间的转换已经讨论过(见[3.3](./ch03.html#sec-unicode))。在这里,我们专注于数据结构。
已标注语言数据很少以最方便的格式保存,往往需要进行各种格式转换。字符编码之间的转换已经讨论过(见 3.3)。在这里,我们专注于数据结构。
最简单的情况,输入和输出格式是同构的。例如,我们可能要将词汇数据从 Toolbox 格式转换为 XML,可以直接一次一个的转换词条([4](./ch11.html#sec-working-with-xml))。数据结构反映在所需的程序的结构中:一个`for`循环,每次循环处理一个词条。
另一种常见的情况,输出是输入的摘要形式,如一个倒置的文件索引。有必要在内存中建立索引结构(见[4.8](./ch04.html#code-search-documents)),然后把它以所需的格式写入一个文件。下面的例子构造一个索引,映射字典定义的词汇到相应的每个词条[](./ch11.html#map-word-lexeme)的语意[](./ch11.html#lexical-entry),已经对定义文本分词[](./ch11.html#definition-text),并丢弃短词[](./ch11.html#short-words)。一旦该索引建成,我们打开一个文件,然后遍历索引项,以所需的格式输出行[](./ch11.html#required-format)
另一种常见的情况,输出是输入的摘要形式,如一个倒置的文件索引。有必要在内存中建立索引结构(见 4.8),然后把它以所需的格式写入一个文件。下面的例子构造一个索引,映射字典定义的词汇到相应的每个词条[](./ch11.html#map-word-lexeme)的语意[](./ch11.html#lexical-entry),已经对定义文本分词[](./ch11.html#definition-text),并丢弃短词[](./ch11.html#short-words)。一旦该索引建成,我们打开一个文件,然后遍历索引项,以所需的格式输出行[](./ch11.html#required-format)
```py
>>> idx = nltk.Index((defn_word, lexeme)
......@@ -280,7 +280,7 @@ sleep: wake
图 3.2:通用格式对比通用接口
不是集中在一种共同的格式,我们认为更有希望开发一种共同的接口(参见`nltk.corpus`)。思考 NLP 中的一个重要的语料类型 treebanks 的情况。将短语结构树存储在一个文件中的方法很多。我们可以使用嵌套的括号、或嵌套的 XML 元素、或每行带有一个(child-id,parent-id)对的依赖符号、或一个 XML 版本的依赖符号等。然而,每种情况中的逻辑结构几乎是相同的。很容易设计一种共同的接口,使应用程序员编写代码使用如`children()``leaves()``depth()`等方法来访问树数据。注意这种做法来自计算机科学中已经接受的做法,即即抽象数据类型、面向对象设计、三层结构([3.2](./ch11.html#fig-three-layer-arch))。其中的最后一个——来自关系数据库领域——允许终端用户应用程序使用通用的模型(“关系模型”)和通用的语言(SQL)抽象出文件存储的特质,并允许新的文件系统技术的出现,而不会干扰到终端用户的应用。以同样的方式,一个通用的语料库接口将应用程序从数据格式隔离。
不是集中在一种共同的格式,我们认为更有希望开发一种共同的接口(参见`nltk.corpus`)。思考 NLP 中的一个重要的语料类型 treebanks 的情况。将短语结构树存储在一个文件中的方法很多。我们可以使用嵌套的括号、或嵌套的 XML 元素、或每行带有一个(child-id,parent-id)对的依赖符号、或一个 XML 版本的依赖符号等。然而,每种情况中的逻辑结构几乎是相同的。很容易设计一种共同的接口,使应用程序员编写代码使用如`children()``leaves()``depth()`等方法来访问树数据。注意这种做法来自计算机科学中已经接受的做法,即即抽象数据类型、面向对象设计、三层结构(3.2)。其中的最后一个——来自关系数据库领域——允许终端用户应用程序使用通用的模型(“关系模型”)和通用的语言(SQL)抽象出文件存储的特质,并允许新的文件系统技术的出现,而不会干扰到终端用户的应用。以同样的方式,一个通用的语料库接口将应用程序从数据格式隔离。
在此背景下,创建和发布一个新的语料库时,尽可能使用现有广泛使用的格式是权宜之计。如果这样不可能,语料库可以带有一些软件——如`nltk.corpus`模块——支持现有的接口方法。
......@@ -594,7 +594,7 @@ SHYL 15 15 2 26 21 0
## 5.1 为每个条目添加一个字段
添加一个自动从现有字段派生出的新的字段往往是方便的。这些字段经常使搜索和分析更加便捷。例如,在[5.1](./ch11.html#code-add-cv-field)中我们定义了一个函数`cv()`,将辅音和元音的字符串映射到相应的 CV 序列,即`kakapua`将映射到`CVCVCVV`。这种映射有四个步骤。首先,将字符串转换为小写,然后将所有非字母字符`[^a-z]`用下划线代替。下一步,将所有元音替换为`V`。最后,所有不是`V`或下划线的必定是一个辅音,所以我们将它替换为`C`。现在,我们可以扫描词汇,在每个`lx`字段后面添加一个新的`cv`字段。[5.1](./ch11.html#code-add-cv-field)显示了它对一个特定条目上做的内容;注意输出的最后一行表示新的`cv`字段。
添加一个自动从现有字段派生出的新的字段往往是方便的。这些字段经常使搜索和分析更加便捷。例如,在 5.1 中我们定义了一个函数`cv()`,将辅音和元音的字符串映射到相应的 CV 序列,即`kakapua`将映射到`CVCVCVV`。这种映射有四个步骤。首先,将字符串转换为小写,然后将所有非字母字符`[^a-z]`用下划线代替。下一步,将所有元音替换为`V`。最后,所有不是`V`或下划线的必定是一个辅音,所以我们将它替换为`C`。现在,我们可以扫描词汇,在每个`lx`字段后面添加一个新的`cv`字段。5.1 显示了它对一个特定条目上做的内容;注意输出的最后一行表示新的`cv`字段。
```py
from xml.etree.ElementTree import SubElement
......@@ -629,7 +629,7 @@ Toolbox 格式的许多词汇不符合任何特定的模式。有些条目可能
('lx:rt:ps:pt:ge:tkp:dt:ex:xp:xe:ex:xp:xe', 27), ('lx:ps:pt:ge:tkp:nt:dt:ex:xp:xe', 20), ...]
```
检查完高频字段序列后,我们可以设计一个词汇条目的上下文无关语法。在[5.2](./ch11.html#code-toolbox-validation)中的语法使用我们在[8.](./ch08.html#chap-parse)看到的 CFG 格式。这样的语法模型隐含 Toolbox 条目的嵌套结构,建立一个树状结构,树的叶子是单独的字段名。最后,我们遍历条目并报告它们与语法的一致性,如[5.2](./ch11.html#code-toolbox-validation)所示。那些被语法接受的在前面加一个`'+'` [](./ch11.html#accepted-entries),那些被语法拒绝的在前面加一个`'-'` [](./ch11.html#rejected-entries)。在开发这样一个文法的过程中,它可以帮助过滤掉一些标签[](./ch11.html#ignored-tags)
检查完高频字段序列后,我们可以设计一个词汇条目的上下文无关语法。在 5.2 中的语法使用我们在[8.](./ch08.html#chap-parse)看到的 CFG 格式。这样的语法模型隐含 Toolbox 条目的嵌套结构,建立一个树状结构,树的叶子是单独的字段名。最后,我们遍历条目并报告它们与语法的一致性,如 5.2 所示。那些被语法接受的在前面加一个`'+'` [](./ch11.html#accepted-entries),那些被语法拒绝的在前面加一个`'-'` [](./ch11.html#rejected-entries)。在开发这样一个文法的过程中,它可以帮助过滤掉一些标签[](./ch11.html#ignored-tags)
```py
grammar = nltk.CFG.fromstring('''
......@@ -659,7 +659,7 @@ def validate_lexicon(grammar, lexicon, ignored_tags):
print("-", ':'.join(marker_list))
```
另一种方法是用一个词块分析器([7.](./ch07.html#chap-chunk)),因为它能识别局部结构并报告已确定的局部结构,会更加有效。在[5.3](./ch11.html#code-chunk-toolbox)中我们为词汇条目建立一个词块语法,然后解析每个条目。这个程序的输出的一个示例如[5.4](./ch11.html#fig-iu-mien)所示。
另一种方法是用一个词块分析器([7.](./ch07.html#chap-chunk)),因为它能识别局部结构并报告已确定的局部结构,会更加有效。在 5.3 中我们为词汇条目建立一个词块语法,然后解析每个条目。这个程序的输出的一个示例如 5.4 所示。
```py
grammar = r"""
......@@ -752,7 +752,7 @@ Rotokas 数据由 Stuart Robinson 提供,勉方言数据由 Greg Aumann 提供
## 9 练习
1. ◑ 在[5.1](./ch11.html#code-add-cv-field)中新字段出现在条目底部。修改这个程序使它就在`lx`字段后面插入新的子元素。(提示:使用`Element('cv')`创建新的`cv`字段,分配给它一个文本值,然后使用父元素的`insert()`方法。)
1. ◑ 在 5.1 中新字段出现在条目底部。修改这个程序使它就在`lx`字段后面插入新的子元素。(提示:使用`Element('cv')`创建新的`cv`字段,分配给它一个文本值,然后使用父元素的`insert()`方法。)
2. ◑ 编写一个函数,从一个词汇条目删除指定的字段。(我们可以在把数据给别人之前用它做些清洁,如删除包含无关或不确定的内容的字段。)
......
此差异已折叠。
此差异已折叠。
......@@ -40,7 +40,7 @@
图 4.1:列表赋值与计算机内存:两个列表对象`foo``bar`引用计算机内存中的相同的位置;更新`foo`将同样修改`bar`,反之亦然。
`bar = foo`[](./ch04.html#assignment3)行并不会复制变量的内容,只有它的“引用对象”。要了解这里发生了什么事,我们需要知道列表是如何存储在计算机内存的。在[4.1](./ch04.html#fig-array-memory)中,我们看到一个列表`foo`是对存储在位置 3133 处的一个对象的引用(它自身是一个指针序列,其中的指针指向其它保存字符串的位置)。当我们赋值`bar = foo`时,仅仅是 3133 位置处的引用被复制。这种行为延伸到语言的其他方面,如参数传递([4.4](./ch04.html#sec-functions))。
`bar = foo`[](./ch04.html#assignment3)行并不会复制变量的内容,只有它的“引用对象”。要了解这里发生了什么事,我们需要知道列表是如何存储在计算机内存的。在 4.1 中,我们看到一个列表`foo`是对存储在位置 3133 处的一个对象的引用(它自身是一个指针序列,其中的指针指向其它保存字符串的位置)。当我们赋值`bar = foo`时,仅仅是 3133 位置处的引用被复制。这种行为延伸到语言的其他方面,如参数传递(4.4)。
让我们做更多的实验,通过创建一个持有空列表的变量`empty`,然后在下一行使用它三次。
......@@ -193,7 +193,7 @@ True
### 序列类型上的操作
我们可以用多种有用的方式遍历一个序列`s`中的项目,如[4.1](./ch04.html#tab-python-sequence)所示。
我们可以用多种有用的方式遍历一个序列`s`中的项目,如 4.1 所示。
表 4.1:
......@@ -480,7 +480,7 @@ True
## 4.4 函数:结构化编程的基础
函数提供了程序代码打包和重用的有效途径,已经在[3](./ch02.html#sec-reusing-code)中解释过。例如,假设我们发现我们经常要从 HTML 文件读取文本。这包括以下几个步骤,打开文件,将它读入,规范化空白符号,剥离 HTML 标记。我们可以将这些步骤收集到一个函数中,并给它一个名字,如`get_text()`,如[4.2](./ch04.html#code-get-text)所示。
函数提供了程序代码打包和重用的有效途径,已经在[3](./ch02.html#sec-reusing-code)中解释过。例如,假设我们发现我们经常要从 HTML 文件读取文本。这包括以下几个步骤,打开文件,将它读入,规范化空白符号,剥离 HTML 标记。我们可以将这些步骤收集到一个函数中,并给它一个名字,如`get_text()`,如 4.2 所示。
```py
import re
......@@ -543,7 +543,7 @@ def get_text(file):
### 参数传递
早在[4.1](./ch04.html#sec-back-to-the-basics)节中,你就已经看到了赋值操作,而一个结构化对象的值是该对象的引用。函数也是一样的。Python 按它的值来解释函数的参数(这被称为按值调用)。在下面的代码中,`set_up()`有两个参数,都在函数内部被修改。我们一开始将一个空字符串分配给`w`,将一个空列表分配给`p`。调用该函数后,`w`没有变,而`p`改变了:
早在 4.1 节中,你就已经看到了赋值操作,而一个结构化对象的值是该对象的引用。函数也是一样的。Python 按它的值来解释函数的参数(这被称为按值调用)。在下面的代码中,`set_up()`有两个参数,都在函数内部被修改。我们一开始将一个空字符串分配给`w`,将一个空列表分配给`p`。调用该函数后,`w`没有变,而`p`改变了:
```py
>>> def set_up(word, properties):
......@@ -641,7 +641,7 @@ def get_text(file):
适当使用函数使程序更具可读性和可维护性。另外,重新实现一个函数已成为可能——使用更高效的代码替换函数体——不需要关心程序的其余部分。
思考[4.3](./ch04.html#code-freq-words1)`freq_words`函数。它更新一个作为参数传递进来的频率分布的内容,并输出前 n 个最频繁的词的列表。
思考 4.3 `freq_words`函数。它更新一个作为参数传递进来的频率分布的内容,并输出前 n 个最频繁的词的列表。
```py
from urllib import request
......@@ -658,7 +658,7 @@ def freq_words(url, freqdist, n):
print(result)
```
这个函数有几个问题。该函数有两个副作用:它修改了第二个参数的内容,并输出它已计算的结果的经过选择的子集。如果我们在函数内部初始化`FreqDist()`对象(在它被处理的同一个地方),并且去掉选择集而将结果显示给调用程序的话,函数会更容易理解和更容易在其他地方重用。考虑到它的任务是找出频繁的一个词,它应该只应该返回一个列表,而不是整个频率分布。在[4.4](./ch04.html#code-freq-words2)中,我们重构此函数,并通过去掉`freqdist`参数简化其接口。
这个函数有几个问题。该函数有两个副作用:它修改了第二个参数的内容,并输出它已计算的结果的经过选择的子集。如果我们在函数内部初始化`FreqDist()`对象(在它被处理的同一个地方),并且去掉选择集而将结果显示给调用程序的话,函数会更容易理解和更容易在其他地方重用。考虑到它的任务是找出频繁的一个词,它应该只应该返回一个列表,而不是整个频率分布。在 4.4 中,我们重构此函数,并通过去掉`freqdist`参数简化其接口。
```py
from urllib import request
......@@ -681,9 +681,9 @@ def freq_words(url, n):
如果我们已经将工作分解成函数分解的很好了,那么应该很容易使用通俗易懂的语言描述每个函数的目的,并且在函数的定义顶部的文档字符串中提供这些描述。这个说明不应该解释函数是如何实现的;实际上,应该能够不改变这个说明,使用不同的方法,重新实现这个函数。
对于最简单的函数,一个单行的文档字符串通常就足够了(见[4.2](./ch04.html#code-get-text))。你应该提供一个在一行中包含一个完整的句子的三重引号引起来的字符串。对于不寻常的函数,你还是应该在第一行提供一个一句话总结,因为很多的文档字符串处理工具会索引这个字符串。它后面应该有一个空行,然后是更详细的功能说明(见`http://www.python.org/dev/peps/pep-0257/`的文档字符串约定的更多信息)。
对于最简单的函数,一个单行的文档字符串通常就足够了(见 4.2)。你应该提供一个在一行中包含一个完整的句子的三重引号引起来的字符串。对于不寻常的函数,你还是应该在第一行提供一个一句话总结,因为很多的文档字符串处理工具会索引这个字符串。它后面应该有一个空行,然后是更详细的功能说明(见`http://www.python.org/dev/peps/pep-0257/`的文档字符串约定的更多信息)。
文档字符串可以包括一个 doctest 块,说明使用的函数和预期的输出。这些都可以使用 Python 的`docutils`模块自动测试。文档字符串应当记录函数的每个参数的类型和返回类型。至少可以用纯文本来做这些。然而,请注意,NLTK 使用 Sphinx 标记语言来记录参数。这种格式可以自动转换成富结构化的 API 文档(见`http://nltk.org/`),并包含某些“字段”的特殊处理,例如`param`,允许清楚地记录函数的输入和输出。[4.5](./ch04.html#code-sphinx)演示了一个完整的文档字符串。
文档字符串可以包括一个 doctest 块,说明使用的函数和预期的输出。这些都可以使用 Python 的`docutils`模块自动测试。文档字符串应当记录函数的每个参数的类型和返回类型。至少可以用纯文本来做这些。然而,请注意,NLTK 使用 Sphinx 标记语言来记录参数。这种格式可以自动转换成富结构化的 API 文档(见`http://nltk.org/`),并包含某些“字段”的特殊处理,例如`param`,允许清楚地记录函数的输入和输出。4.5 演示了一个完整的文档字符串。
```py
def accuracy(reference, test):
......@@ -764,7 +764,7 @@ Python 提供了更多的方式来定义函数作为其他函数的参数,即
### 累计函数
这些函数以初始化一些存储开始,迭代和处理输入的数据,最后返回一些最终的对象(一个大的结构或汇总的结果)。做到这一点的一个标准的方式是初始化一个空链表,累计材料,然后返回这个链表,如[4.6](./ch04.html#code-search-examples)中所示函数`search1()`
这些函数以初始化一些存储开始,迭代和处理输入的数据,最后返回一些最终的对象(一个大的结构或汇总的结果)。做到这一点的一个标准的方式是初始化一个空链表,累计材料,然后返回这个链表,如 4.6 中所示函数`search1()`
```py
def search1(substring, words):
......@@ -801,7 +801,7 @@ def search2(substring, words):
注意
`permutations`函数使用了一种技术叫递归,将在下面[4.7](./ch04.html#sec-algorithm-design)讨论。产生一组词的排列对于创建测试一个语法的数据十分有用([8.](./ch08.html#chap-parse))。
`permutations`函数使用了一种技术叫递归,将在下面 4.7 讨论。产生一组词的排列对于创建测试一个语法的数据十分有用([8.](./ch08.html#chap-parse))。
### 高阶函数
......@@ -820,7 +820,7 @@ Python 提供一些具有函数式编程语言如 Haskell 标准特征的高阶
['Take', 'care', 'sense', 'sounds', 'take', 'care', 'themselves']
```
另一个高阶函数是`map()`,将一个函数运用到一个序列中的每一项。它是我们在[4.5](./ch04.html#sec-doing-more-with-functions)看到的函数`extract_property()`的一个通用版本。这里是一个简单的方法找出布朗语料库新闻部分中的句子的平均长度,后面跟着的是使用列表推导计算的等效版本:
另一个高阶函数是`map()`,将一个函数运用到一个序列中的每一项。它是我们在 4.5 看到的函数`extract_property()`的一个通用版本。这里是一个简单的方法找出布朗语料库新闻部分中的句子的平均长度,后面跟着的是使用列表推导计算的等效版本:
```py
>>> lengths = list(map(len, nltk.corpus.brown.sents(categories='news')))
......@@ -973,7 +973,7 @@ As metrics, they must satisfy the following three requirements:
### 多模块程序
一些程序汇集多种任务,例如从语料库加载数据、对数据进行一些分析、然后将其可视化。我们可能已经有了稳定的模块来加载数据和实现数据可视化。我们的工作可能会涉及到那些分析任务的编码,只是从现有的模块调用一些函数。[4.7](./ch04.html#fig-multi-module)描述了这种情景。
一些程序汇集多种任务,例如从语料库加载数据、对数据进行一些分析、然后将其可视化。我们可能已经有了稳定的模块来加载数据和实现数据可视化。我们的工作可能会涉及到那些分析任务的编码,只是从现有的模块调用一些函数。4.7 描述了这种情景。
![Images/multi-module.png](Images/e685801a8cec4515b47e1bda95deb59d.jpg)
......@@ -1061,7 +1061,7 @@ result = ['cat']
解决算法问题的一个重要部分是为手头的问题选择或改造一个合适的算法。有时会有几种选择,能否选择最好的一个取决于对每个选择随数据增长如何执行的知识。关于这个话题的书很多,我们只介绍一些关键概念和精心描述在自然语言处理中最普遍的做法。
最有名的策略被称为分而治之。我们解决一个大小为 *n* 的问题通过将其分成两个大小为 *n/2* 的问题,解决这些问题,组合它们的结果成为原问题的结果。例如,假设我们有一堆卡片,每张卡片上写了一个词。我们可以排序这一堆卡片,通过将它分成两半分别给另外两个人来排序(他们又可以做同样的事情)。然后,得到两个排好序的卡片堆,将它们并成一个单一的排序堆就是一项容易的任务了。参见[4.8](./ch04.html#fig-mergesort)这个过程的说明。
最有名的策略被称为分而治之。我们解决一个大小为 *n* 的问题通过将其分成两个大小为 *n/2* 的问题,解决这些问题,组合它们的结果成为原问题的结果。例如,假设我们有一堆卡片,每张卡片上写了一个词。我们可以排序这一堆卡片,通过将它分成两半分别给另外两个人来排序(他们又可以做同样的事情)。然后,得到两个排好序的卡片堆,将它们并成一个单一的排序堆就是一项容易的任务了。参见 4.8 这个过程的说明。
![Images/mergesort.png](Images/1094084b61ac3f0e4416e92869c52ccd.jpg)
......@@ -1125,7 +1125,7 @@ result = ['cat']
190
```
作为递归的最后一个例子,让我们用它来构建一个深嵌套的对象。一个字母查找树是一种可以用来索引词汇的数据结构,一次一个字母。(这个名字来自于单词 retrieval)。例如,如果`trie`包含一个字母的查找树,那么`trie['c']`是一个较小的查找树,包含所有以 c 开头的词。[4.9](./ch04.html#code-trie)演示了使用 Python 字典([3](./ch05.html#sec-dictionaries))构建查找树的递归过程。若要插入词 chien(dog 的法语),我们将 c 分类,递归的掺入 hien 到`trie['c']`子查找树中。递归继续直到词中没有剩余的字母,于是我们存储的了预期值(本例中是词 dog)。
作为递归的最后一个例子,让我们用它来构建一个深嵌套的对象。一个字母查找树是一种可以用来索引词汇的数据结构,一次一个字母。(这个名字来自于单词 retrieval)。例如,如果`trie`包含一个字母的查找树,那么`trie['c']`是一个较小的查找树,包含所有以 c 开头的词。4.9 演示了使用 Python 字典([3](./ch05.html#sec-dictionaries))构建查找树的递归过程。若要插入词 chien(dog 的法语),我们将 c 分类,递归的掺入 hien 到`trie['c']`子查找树中。递归继续直到词中没有剩余的字母,于是我们存储的了预期值(本例中是词 dog)。
```py
def insert(trie, key, value):
......@@ -1144,7 +1144,7 @@ def insert(trie, key, value):
### 权衡空间与时间
我们有时可以显著的加快程序的执行,通过建设一个辅助的数据结构,例如索引。[4.10](./ch04.html#code-search-documents)实现一个简单的电影评论语料库的全文检索系统。通过索引文档集合,它提供更快的查找。
我们有时可以显著的加快程序的执行,通过建设一个辅助的数据结构,例如索引。4.10 实现一个简单的电影评论语料库的全文检索系统。通过索引文档集合,它提供更快的查找。
```py
def raw(file):
......@@ -1172,7 +1172,7 @@ while query != "quit":
print("Not found")
```
一个更微妙的空间与时间折中的例子涉及使用整数标识符替换一个语料库的词符。我们为语料库创建一个词汇表,每个词都被存储一次的列表,然后转化这个列表以便我们能通过查找任意词来找到它的标识符。每个文档都进行预处理,使一个词列表变成一个整数列表。现在所有的语言模型都可以使用整数。见[4.11](./ch04.html#code-strings-to-ints)中的内容,如何为一个已标注的语料库做这个的例子的列表。
一个更微妙的空间与时间折中的例子涉及使用整数标识符替换一个语料库的词符。我们为语料库创建一个词汇表,每个词都被存储一次的列表,然后转化这个列表以便我们能通过查找任意词来找到它的标识符。每个文档都进行预处理,使一个词列表变成一个整数列表。现在所有的语言模型都可以使用整数。见 4.11 中的内容,如何为一个已标注的语料库做这个的例子的列表。
```py
def preprocess(tagged_corpus):
......@@ -1220,7 +1220,7 @@ V4 =
```
有了这个观察结果,我们可以写一个小的递归函数称为`virahanka1()`来计算这些旋律,如[4.12](./ch04.html#code-virahanka)所示。请注意,要计算 *V*[4],我们先要计算 *V*[3]和 *V*[2]。但要计算 *V*[3],我们先要计算 *V*[2]和 *V*[1]。在[(2)](./ch04.html#ex-call-structure)中描述了这种调用结构。
有了这个观察结果,我们可以写一个小的递归函数称为`virahanka1()`来计算这些旋律,如 4.12 所示。请注意,要计算 *V*[4],我们先要计算 *V*[3]和 *V*[2]。但要计算 *V*[3],我们先要计算 *V*[2]和 *V*[1]。在[(2)](./ch04.html#ex-call-structure)中描述了这种调用结构。
```py
from numpy import arange
......@@ -1246,7 +1246,7 @@ def bar_chart(categories, words, counts):
![Images/modal_genre.png](Images/2ce816f11fd01927802253d100780b0a.jpg)
图 4.14:条形图显示布朗语料库中不同部分的情态动词频率:这个可视化图形由[4.13](./ch04.html#code-modal-plot)中的程序产生。
图 4.14:条形图显示布朗语料库中不同部分的情态动词频率:这个可视化图形由 4.13 中的程序产生。
从该柱状图可以立即看出 may 和 must 有几乎相同的相对频率。could 和 might 也一样。
......@@ -1265,7 +1265,7 @@ def bar_chart(categories, words, counts):
### NetworkX
NetworkX 包定义和操作被称为图的由节点和边组成的结构。它可以从`https://networkx.lanl.gov/`得到。NetworkX 可以和 Matplotlib 结合使用可视化如 WordNet 的网络结构(语义网络,我们在[5](./ch02.html#sec-wordnet)介绍过)。[4.15](./ch04.html#code-networkx)中的程序初始化一个空的图[](./ch04.html#define-graph),然后遍历 WordNet 上位词层次为图添加边[](./ch04.html#add-edge)。请注意,遍历是递归的[](./ch04.html#recursive-traversal),使用在[4.7](./ch04.html#sec-algorithm-design)讨论的编程技术。结果显示在[4.16](./ch04.html#fig-dog-graph)
NetworkX 包定义和操作被称为图的由节点和边组成的结构。它可以从`https://networkx.lanl.gov/`得到。NetworkX 可以和 Matplotlib 结合使用可视化如 WordNet 的网络结构(语义网络,我们在[5](./ch02.html#sec-wordnet)介绍过)。4.15 中的程序初始化一个空的图[](./ch04.html#define-graph),然后遍历 WordNet 上位词层次为图添加边[](./ch04.html#add-edge)。请注意,遍历是递归的[](./ch04.html#recursive-traversal),使用在 4.7 讨论的编程技术。结果显示在 4.16
```py
import networkx as nx
......@@ -1294,7 +1294,7 @@ def graph_draw(graph):
![Images/dog-graph.png](Images/8cb61a943f3d34f94596e77065410cd3.jpg)
图 4.16:使用 NetworkX 和 Matplotlib 可视化数据:WordNet 的上位词层次的部分显示,开始于`dog.n.01`(中间最黑的节点);节点的大小对应节点的孩子的数目,颜色对应节点到`dog.n.01`的距离;此可视化图形由[4.15](./ch04.html#code-networkx)中的程序产生。
图 4.16:使用 NetworkX 和 Matplotlib 可视化数据:WordNet 的上位词层次的部分显示,开始于`dog.n.01`(中间最黑的节点);节点的大小对应节点的孩子的数目,颜色对应节点到`dog.n.01`的距离;此可视化图形由 4.15 中的程序产生。
### csv
......
......@@ -147,7 +147,7 @@ NLTK 中还有其他几种语言的已标注语料库,包括中文,印地语
[('El', 'da0ms0'), ('Tribunal_Suprem', 'np0000o'), ...]
```
如果你的环境设置正确,有适合的编辑器和字体,你应该能够以人可读的方式显示单个字符串。例如,[2.1](./ch05.html#fig-tag-indian)显示的使用`nltk.corpus.indian`访问的数据。
如果你的环境设置正确,有适合的编辑器和字体,你应该能够以人可读的方式显示单个字符串。例如,2.1 显示的使用`nltk.corpus.indian`访问的数据。
![Images/tag-indian.png](Images/1c54b3124863d24d17b2edec4f1d47e5.jpg)
......@@ -157,7 +157,7 @@ NLTK 中还有其他几种语言的已标注语料库,包括中文,印地语
## 2.3 通用词性标记集
已标注的语料库使用许多不同的标记集约定来标注词汇。为了帮助我们开始,我们将看一看一个简化的标记集([2.1](./ch05.html#tab-universal-tagset)中所示)。
已标注的语料库使用许多不同的标记集约定来标注词汇。为了帮助我们开始,我们将看一看一个简化的标记集(2.1 中所示)。
表 2.1:
......@@ -181,7 +181,7 @@ NLTK 中还有其他几种语言的已标注语料库,包括中文,印地语
## 2.4 名词
名词一般指的是人、地点、事情或概念,例如: woman, Scotland, book, intelligence。名词可能出现在限定词和形容词之后,可以是动词的主语或宾语,如[2.2](./ch05.html#tab-syntax-nouns)所示。
名词一般指的是人、地点、事情或概念,例如: woman, Scotland, book, intelligence。名词可能出现在限定词和形容词之后,可以是动词的主语或宾语,如 2.2 所示。
表 2.2:
......@@ -199,7 +199,7 @@ NLTK 中还有其他几种语言的已标注语料库,包括中文,印地语
## 2.5 动词
动词是用来描述事件和行动的词,例如[2.3](./ch05.html#tab-syntax-verbs)中的 fall, eat。在一个句子中,动词通常表示涉及一个或多个名词短语所指示物的关系。
动词是用来描述事件和行动的词,例如 2.3 中的 fall, eat。在一个句子中,动词通常表示涉及一个或多个名词短语所指示物的关系。
表 2.3:
......@@ -267,7 +267,7 @@ NLTK 中还有其他几种语言的已标注语料库,包括中文,印地语
## 2.7 未简化的标记
让我们找出每个名词类型中最频繁的名词。[2.2](./ch05.html#code-findtags)中的程序找出所有以`NN`开始的标记,并为每个标记提供了几个示例单词。你会看到有许多`NN`的变种;最重要有`此外,大多数的标记都有后缀修饰符:`-NC`表示引用,`-HL`表示标题中的词,`-TL`表示标题(布朗标记的特征)。
让我们找出每个名词类型中最频繁的名词。2.2 中的程序找出所有以`NN`开始的标记,并为每个标记提供了几个示例单词。你会看到有许多`NN`的变种;最重要有`此外,大多数的标记都有后缀修饰符:`-NC`表示引用,`-HL`表示标题中的词,`-TL`表示标题(布朗标记的特征)。
```py
def findtags(tag_prefix, tagged_text):
......@@ -387,19 +387,19 @@ that CNJ V WH DET
## 3.1 索引列表 VS 字典
我们已经看到,文本在 Python 中被视为一个词列表。链表的一个重要的属性是我们可以通过给出其索引来“看”特定项目,例如`text1[100]`。请注意我们如何指定一个数字,然后取回一个词。我们可以把链表看作一种简单的表格,如[3.1](./ch05.html#fig-maps01)所示。
我们已经看到,文本在 Python 中被视为一个词列表。链表的一个重要的属性是我们可以通过给出其索引来“看”特定项目,例如`text1[100]`。请注意我们如何指定一个数字,然后取回一个词。我们可以把链表看作一种简单的表格,如 3.1 所示。
![Images/maps01.png](Images/e9d9a0887996a6bac6c52bb0bfaf9fdf.jpg)
图 3.1:列表查找:一个整数索引帮助我们访问 Python 列表的内容。
对比这种情况与频率分布([3](./ch01.html#sec-computing-with-language-simple-statistics)),在那里我们指定一个词然后取回一个数字,如`fdist['monstrous']`,它告诉我们一个给定的词在文本中出现的次数。用词查询对任何使用过字典的人都很熟悉。[3.2](./ch05.html#fig-maps02)展示一些更多的例子。
对比这种情况与频率分布([3](./ch01.html#sec-computing-with-language-simple-statistics)),在那里我们指定一个词然后取回一个数字,如`fdist['monstrous']`,它告诉我们一个给定的词在文本中出现的次数。用词查询对任何使用过字典的人都很熟悉。3.2 展示一些更多的例子。
![Images/maps02.png](Images/484180fc6abc244116b30e57cb6c0cf5.jpg)
图 3.2:字典查询:我们使用一个关键字,如某人的名字、一个域名或一个英文单词,访问一个字典的条目;字典的其他名字有映射、哈希表、哈希和关联数组。
在电话簿中,我们用名字查找一个条目得到一个数字。当我们在浏览器中输入一个域名,计算机查找它得到一个 IP 地址。一个词频表允许我们查一个词找出它在一个文本集合中的频率。在所有这些情况中,我们都是从名称映射到数字,而不是其他如列表那样的方式。总之,我们希望能够在任意类型的信息之间映射。[3.1](./ch05.html#tab-linguistic-objects)列出了各种语言学对象以及它们的映射。
在电话簿中,我们用名字查找一个条目得到一个数字。当我们在浏览器中输入一个域名,计算机查找它得到一个 IP 地址。一个词频表允许我们查一个词找出它在一个文本集合中的频率。在所有这些情况中,我们都是从名称映射到数字,而不是其他如列表那样的方式。总之,我们希望能够在任意类型的信息之间映射。3.1 列出了各种语言学对象以及它们的映射。
表 3.1:
......@@ -437,7 +437,7 @@ Traceback (most recent call last):
KeyError: 'green'
```
这就提出了一个重要的问题。与列表和字符串不同,我们可以用`len()`算出哪些整数是合法索引,我们如何算出一个字典的合法键?如果字典不是太大,我们可以简单地通过查看变量`pos`检查它的内容。正如在前面([](./ch05.html#pos-inspect)行)所看到,这为我们提供了键-值对。请注意它们的顺序与最初放入它们的顺序不同;这是因为字典不是序列而是映射(参见[3.2](./ch05.html#fig-maps02)),键没有固定地排序。
这就提出了一个重要的问题。与列表和字符串不同,我们可以用`len()`算出哪些整数是合法索引,我们如何算出一个字典的合法键?如果字典不是太大,我们可以简单地通过查看变量`pos`检查它的内容。正如在前面([❷](./ch05.html#pos-inspect)行)所看到,这为我们提供了键-值对。请注意它们的顺序与最初放入它们的顺序不同;这是因为字典不是序列而是映射(参见 3.2),键没有固定地排序。
换种方式,要找到键,我们可以将字典转换成一个列表[❶](./ch05.html#dict-to-list)——要么在期望列表的上下文中使用字典,如作为`sorted()`的参数[❷](./ch05.html#dict-sorted),要么在`for`循环中[❸](./ch05.html#dict-for-loop)。
......@@ -548,7 +548,7 @@ TypeError: list objects are unhashable
注意
上面的例子使用一个 lambda 表达式,在[4.4](./ch04.html#sec-functions)介绍过。这个 lambda 表达式没有指定参数,所以我们用不带参数的括号调用它。因此,下面的`f``g`的定义是等价的:
上面的例子使用一个 lambda 表达式,在 4.4 介绍过。这个 lambda 表达式没有指定参数,所以我们用不带参数的括号调用它。因此,下面的`f``g`的定义是等价的:
```py
>>> f = lambda: 'NOUN'
......@@ -610,7 +610,7 @@ TypeError: list objects are unhashable
['NOUN', 'VERB', 'ADP', '.', 'DET', 'ADJ', 'ADV', 'CONJ', 'PRON', 'PRT', 'NUM', 'X']
```
[3.3](./ch05.html#code-dictionary)中的列表演示了一个重要的按值排序一个字典的习惯用法,来按频率递减顺序显示词汇。`sorted()`的第一个参数是要排序的项目,它是由一个词性标记和一个频率组成的元组的列表。第二个参数使用函数`itemgetter()`指定排序的键。在一般情况下,`itemgetter(n)`返回一个函数,这个函数可以在一些其他序列对象上被调用获得这个序列的第 n 个元素,例如:
3.3 中的列表演示了一个重要的按值排序一个字典的习惯用法,来按频率递减顺序显示词汇。`sorted()`的第一个参数是要排序的项目,它是由一个词性标记和一个频率组成的元组的列表。第二个参数使用函数`itemgetter()`指定排序的键。在一般情况下,`itemgetter(n)`返回一个函数,这个函数可以在一些其他序列对象上被调用获得这个序列的第 n 个元素,例如:
```py
>>> pair = ('NP', 8336)
......@@ -622,7 +622,7 @@ TypeError: list objects are unhashable
`sorted()`的最后一个参数指定项目是否应被按相反的顺序返回,即频率值递减。
[3.3](./ch05.html#code-dictionary)的开头还有第二个有用的习惯用法,那里我们初始化一个`defaultdict`,然后使用`for`循环来更新其值。下面是一个示意版本:
3.3 的开头还有第二个有用的习惯用法,那里我们初始化一个`defaultdict`,然后使用`for`循环来更新其值。下面是一个示意版本:
```py
>>> my_dictionary = defaultdict(_function to create default value_)
......@@ -729,7 +729,7 @@ defaultdict(<class 'int'>, {'ADJ': 11, 'NOUN': 5})
['peacefully', 'furiously']
```
[3.2](./ch05.html#tab-dict)给出 Python 字典方法的总结。
3.2 给出 Python 字典方法的总结。
表 3.2:
......@@ -846,7 +846,7 @@ Python 字典方法:常用的方法与字典相关习惯用法的总结。
... backoff=nltk.DefaultTagger('NN'))
```
让我们把所有这些放在一起,写一个程序来创建和评估具有一定范围的查找标注器 ,[4.1](./ch05.html#code-baseline-tagger)
让我们把所有这些放在一起,写一个程序来创建和评估具有一定范围的查找标注器 ,4.1
```py
def performance(cfd, wordlist):
......@@ -872,7 +872,7 @@ def display():
图 4.2:查找标注器
可以观察到,随着模型规模的增长,最初的性能增加迅速,最终达到一个稳定水平,这时模型的规模大量增加性能的提高很小。(这个例子使用`pylab`绘图软件包,在[4.8](./ch04.html#sec-libraries)讨论过)。
可以观察到,随着模型规模的增长,最初的性能增加迅速,最终达到一个稳定水平,这时模型的规模大量增加性能的提高很小。(这个例子使用`pylab`绘图软件包,在 4.8 讨论过)。
## 4.4 评估
......@@ -930,7 +930,7 @@ def display():
在基于一元处理一个语言处理任务时,我们使用上下文中的一个项目。标注的时候,我们只考虑当前的词符,与更大的上下文隔离。给定一个模型,我们能做的最好的是为每个词标注其*先验的*最可能的标记。这意味着我们将使用相同的标记标注一个词,如 wind,不论它出现的上下文是 the wind 还是 to wind。
一个 n-gram tagger 标注器是一个一元标注器的一般化,它的上下文是当前词和它前面 *n*-1 个标识符的词性标记,如图[5.1](./ch05.html#fig-tag-context)所示。要选择的标记是圆圈里的 *t*&lt;sub&gt;n&lt;/sub&gt;,灰色阴影的是上下文。在[5.1](./ch05.html#fig-tag-context)所示的 n-gram 标注器的例子中,我们让 *n*=3;也就是说,我们考虑当前词的前两个词的标记。一个 n-gram 标注器挑选在给定的上下文中最有可能的标记。
一个 n-gram tagger 标注器是一个一元标注器的一般化,它的上下文是当前词和它前面 *n*-1 个标识符的词性标记,如图 5.1 所示。要选择的标记是圆圈里的 *t*&lt;sub&gt;n&lt;/sub&gt;,灰色阴影的是上下文。在 5.1 所示的 n-gram 标注器的例子中,我们让 *n*=3;也就是说,我们考虑当前词的前两个词的标记。一个 n-gram 标注器挑选在给定的上下文中最有可能的标记。
![Images/tag-context.png](Images/12573c3a9015654728fe798e170a3c50.jpg)
......@@ -1147,7 +1147,7 @@ Statement User121 18/m pm me if u tryin to chat
1. ☼ 网上搜索“spoof newspaper headlines”,找到这种宝贝:British Left Waffles on Falkland Islands 和 Juvenile Court to Try Shooting Defendant。手工标注这些头条,看看词性标记的知识是否可以消除歧义。
2. ☼ 和别人一起,轮流挑选一个既可以是名词也可以是动词的词(如 contest);让对方预测哪一个可能是布朗语料库中频率最高的;检查对方的预测,为几个回合打分。
3. ☼ 分词和标注下面的句子:They wind back the clock, while we chase after the wind。涉及哪些不同的发音和词类?
4. ☼ 回顾[3.1](./ch05.html#tab-linguistic-objects)中的映射。讨论你能想到的映射的其他的例子。它们从什么类型的信息映射到什么类型的信息?
4. ☼ 回顾 3.1 中的映射。讨论你能想到的映射的其他的例子。它们从什么类型的信息映射到什么类型的信息?
5. ☼ 在交互模式下使用 Python 解释器,实验本章中字典的例子。创建一个字典`d`,添加一些条目。如果你尝试访问一个不存在的条目会发生什么,如`d['xyz']`
6. ☼ 尝试从字典`d`删除一个元素,使用语法`del d['abc']`。检查被删除的项目。
7. ☼ 创建两个字典,`d1``d2`,为每个添加一些条目。现在发出命令`d1.update(d2)`。这做了什么?它可能是有什么用?
......@@ -1165,7 +1165,7 @@ Statement User121 18/m pm me if u tryin to chat
4. 名词后面最常见的是哪些标记?这些标记代表什么?
16. ◑ 探索有关查找标注器的以下问题:
1. 回退标注器被省略时,模型大小变化,标注器的准确性会发生什么?
2. 思考[4.2](./ch05.html#fig-tag-lookup)的曲线;为查找标注器推荐一个平衡内存和准确性的好的规模。你能想出在什么情况下应该尽量减少内存使用,什么情况下性能最大化而不必考虑内存使用?
2. 思考 4.2 的曲线;为查找标注器推荐一个平衡内存和准确性的好的规模。你能想出在什么情况下应该尽量减少内存使用,什么情况下性能最大化而不必考虑内存使用?
17. ◑ 查找标注器的准确性上限是什么,假设其表的大小没有限制?(提示:写一个程序算出被分配了最有可能的标记的词的词符的平均百分比。)
18. ◑ 生成已标注数据的一些统计数据,回答下列问题:
1. 总是被分配相同词性的词类的比例是多少?
......@@ -1180,17 +1180,17 @@ Statement User121 18/m pm me if u tryin to chat
2. 识别可能是复数名词或第三人称单数动词的词(如 deals, flies)。
3. 识别三个词的介词短语形式 IN + DET + NN(如 in the lab)。
4. 男性与女性代词的比例是多少?
21. ◑ 在[3.1](./ch03.html#tab-absolutely)中我们看到动词 adore, love, like, prefer 及前面的限定符 absolutely 和 definitely 的频率计数的表格。探讨这四个动词前出现的所有限定符。
21. ◑ 在 3.1 中我们看到动词 adore, love, like, prefer 及前面的限定符 absolutely 和 definitely 的频率计数的表格。探讨这四个动词前出现的所有限定符。
22. ◑ 我们定义可以用来做生词的回退标注器的`regexp_tagger`。这个标注器只检查基数词。通过特定的前缀或后缀字符串进行测试,它应该能够猜测其他标记。例如,我们可以标注所有-s 结尾的词为复数名词。定义一个正则表达式标注器(使用`RegexpTagger()`),测试至少 5 个单词拼写的其他模式。(使用内联文档解释规则。)
23. ◑ 考虑上一练习中开发的正则表达式标注器。使用它的`accuracy()`方法评估标注器,尝试想办法提高其性能。讨论你的发现。客观的评估如何帮助开发过程?
24. ◑ 数据稀疏问题有多严重?调查 n-gram 标注器当 n 从 1 增加到 6 时的准确性。为准确性得分制表。估计这些标注器需要的训练数据,假设词汇量大小为 10&lt;sup&gt;5&lt;/sup&gt;而标记集的大小为 10&lt;sup&gt;2&lt;/sup&gt;
25. ◑ 获取另一种语言的一些已标注数据,在其上测试和评估各种标注器。如果这种语言是形态复杂的,或者有词类的任何字形线索(如),可以考虑为它开发一个正则表达式标注器(排在一元标注器之后,默认标注器之前)。对比同样的运行在英文数据上的标注器,你的标注器的准确性如何?讨论你在运用这些方法到这种语言时遇到的问题。
26.[4.1](./ch05.html#code-baseline-tagger)绘制曲线显示查找标注器的性能随模型的大小增加的变化。绘制当训练数据量变化时一元标注器的性能曲线。
26.4.1 绘制曲线显示查找标注器的性能随模型的大小增加的变化。绘制当训练数据量变化时一元标注器的性能曲线。
27. ◑ 检查[5](./ch05.html#sec-n-gram-tagging)中定义的二元标注器`t2`的混淆矩阵,确定简化的一套或多套标记。定义字典做映射,在简化的数据上评估标注器。
28. ◑ 使用简化的标记集测试标注器(或制作一个你自己的,通过丢弃每个标记名中除第一个字母外所有的字母)。这种标注器需要做的区分更少,但由它获得的信息也更少。讨论你的发现。
29. ◑ 回顾一个二元标注器训练过程中遇到生词,标注句子的其余部分为`None`的例子。一个二元标注器可能只处理了句子的一部分就失败了,即使句子中没有包含生词(即使句子在训练过程中使用过)。在什么情况下会出现这种情况呢?你可以写一个程序,找到一些这方面的例子吗?
30. ◑ 预处理布朗新闻数据,替换低频词为 UNK,但留下标记不变。在这些数据上训练和评估一个二元标注器。这样有多少帮助?一元标注器和默认标注器的贡献是什么?
31. ◑ 修改[4.1](./ch05.html#code-baseline-tagger)中的程序,通过将`pylab.plot()`替换为`pylab.semilogx()`,在 *x* 轴上使用对数刻度。关于结果图形的形状,你注意到了什么?梯度告诉你什么呢?
31. ◑ 修改 4.1 中的程序,通过将`pylab.plot()`替换为`pylab.semilogx()`,在 *x* 轴上使用对数刻度。关于结果图形的形状,你注意到了什么?梯度告诉你什么呢?
32. ◑ 使用`help(nltk.tag.brill.demo)`阅读 Brill 标注器演示函数的文档。通过设置不同的参数值试验这个标注器。是否有任何训练时间(语料库大小)和性能之间的权衡?
33. ◑ 写代码构建一个集合的字典的字典。用它来存储一套可以跟在具有给定词性标记的给定词后面的词性标记,例如 word&lt;sub&gt;i&lt;/sub&gt; → tag&lt;sub&gt;i&lt;/sub&gt; → tag&lt;sub&gt;i+1&lt;/sub&gt;
34. ★ 布朗语料库中有 264 个不同的词有 3 种可能的标签。
......
此差异已折叠。
......@@ -10,7 +10,7 @@
## 1 信息提取
信息有很多种形状和大小。一个重要的形式是结构化数据:实体和关系的可预测的规范的结构。例如,我们可能对公司和地点之间的关系感兴趣。给定一个公司,我们希望能够确定它做业务的位置;反过来,给定位置,我们会想发现哪些公司在该位置做业务。如果我们的数据是表格形式,如[1.1](./ch07.html#tab-db-locations)中的例子,那么回答这些问题就很简单了。
信息有很多种形状和大小。一个重要的形式是结构化数据:实体和关系的可预测的规范的结构。例如,我们可能对公司和地点之间的关系感兴趣。给定一个公司,我们希望能够确定它做业务的位置;反过来,给定位置,我们会想发现哪些公司在该位置做业务。如果我们的数据是表格形式,如 1.1 中的例子,那么回答这些问题就很简单了。
表 1.1:
......@@ -48,7 +48,7 @@
## 2 词块划分
我们将用于实体识别的基本技术是词块划分,它分割和标注多词符的序列,如[2.1](./ch07.html#fig-chunk-segmentation)所示。小框显示词级分词和词性标注,大框显示高级别的词块划分。每个这种较大的框叫做一个词块。就像分词忽略空白符,词块划分通常选择词符的一个子集。同样像分词一样,词块划分器生成的片段在源文本中不能重叠。
我们将用于实体识别的基本技术是词块划分,它分割和标注多词符的序列,如 2.1 所示。小框显示词级分词和词性标注,大框显示高级别的词块划分。每个这种较大的框叫做一个词块。就像分词忽略空白符,词块划分通常选择词符的一个子集。同样像分词一样,词块划分器生成的片段在源文本中不能重叠。
![Images/chunk-segmentation.png](Images/0e768e8c4378c2b0b3290aab46dc770e.jpg)
......@@ -81,7 +81,7 @@
## 2.2 标记模式
组成一个词块语法的规则使用标记模式来描述已标注的词的序列。一个标记模式是一个词性标记序列,用尖括号分隔,如`<DT&gt;?&lt;JJ&gt;*&lt;NN>`。标记模式类似于正则表达式模式([3.4](./ch03.html#sec-regular-expressions-word-patterns))。现在,思考下面的来自《华尔街日报》的名词短语:
组成一个词块语法的规则使用标记模式来描述已标注的词的序列。一个标记模式是一个词性标记序列,用尖括号分隔,如`<DT&gt;?&lt;JJ&gt;*&lt;NN>`。标记模式类似于正则表达式模式(3.4)。现在,思考下面的来自《华尔街日报》的名词短语:
```py
another/DT sharp/JJ dive/NN
......@@ -96,7 +96,7 @@ Panamanian/JJ dictator/NN Manuel/NNP Noriega/NNP
要找到一个给定的句子的词块结构,`RegexpParser`词块划分器以一个没有词符被划分的平面结构开始。词块划分规则轮流应用,依次更新词块结构。一旦所有的规则都被调用,返回生成的词块结构。
[2.3](./ch07.html#code-chunker1)显示了一个由 2 个规则组成的简单的词块语法。第一条规则匹配一个可选的限定词或所有格代名词,零个或多个形容词,然后跟一个名词。第二条规则匹配一个或多个专有名词。我们还定义了一个进行词块划分的例句[](./ch07.html#code-chunker1-ex),并在此输入上运行这个词块划分器[](./ch07.html#code-chunker1-run)
2.3 显示了一个由 2 个规则组成的简单的词块语法。第一条规则匹配一个可选的限定词或所有格代名词,零个或多个形容词,然后跟一个名词。第二条规则匹配一个或多个专有名词。我们还定义了一个进行词块划分的例句[](./ch07.html#code-chunker1-ex),并在此输入上运行这个词块划分器[](./ch07.html#code-chunker1-run)
```py
grammar = r"""
......@@ -166,13 +166,13 @@ sentence = [("Rapunzel", "NNP"), ("let", "VBD"), ("down", "RP"), ❶
## 2.6 词块的表示:标记与树
作为标注和分析之间的中间状态([8.](./ch08.html#chap-parse),词块结构可以使用标记或树来表示。最广泛的文件表示使用 IOB 标记。在这个方案中,每个词符被三个特殊的词块标记之一标注,`I`(内部),`O`(外部)或`B`(开始)。一个词符被标注为`B`,如果它标志着一个词块的开始。块内的词符子序列被标注为`I`。所有其他的词符被标注为`O`。`B`和`I`标记后面跟着词块类型,如`B-NP`, `I-NP`。当然,没有必要指定出现在词块外的词符类型,所以这些都只标注为`O`。这个方案的例子如[2.5](./ch07.html#fig-chunk-tagrep)所示。
作为标注和分析之间的中间状态([8.](./ch08.html#chap-parse),词块结构可以使用标记或树来表示。最广泛的文件表示使用 IOB 标记。在这个方案中,每个词符被三个特殊的词块标记之一标注,`I`(内部),`O`(外部)或`B`(开始)。一个词符被标注为`B`,如果它标志着一个词块的开始。块内的词符子序列被标注为`I`。所有其他的词符被标注为`O`。`B`和`I`标记后面跟着词块类型,如`B-NP`, `I-NP`。当然,没有必要指定出现在词块外的词符类型,所以这些都只标注为`O`。这个方案的例子如 2.5 所示。
![Images/chunk-tagrep.png](Images/542fee25c56235c899312bed3d5ee9ba.jpg)
图 2.5:词块结构的标记表示形式
IOB 标记已成为文件中表示词块结构的标准方式,我们也将使用这种格式。下面是[2.5](./ch07.html#fig-chunk-tagrep)中的信息如何出现在一个文件中的:
IOB 标记已成为文件中表示词块结构的标准方式,我们也将使用这种格式。下面是 2.5 中的信息如何出现在一个文件中的:
```py
We PRP B-NP
......@@ -270,7 +270,7 @@ ChunkParse score:
正如你看到的,这种方法达到相当好的结果。但是,我们可以采用更多数据驱动的方法改善它,在这里我们使用训练语料找到对每个词性标记最有可能的块标记(`I`, `O`或`B`)。换句话说,我们可以使用*一元标注器*([4](./ch05.html#sec-automatic-tagging))建立一个词块划分器。但不是尝试确定每个词的正确的词性标记,而是根据每个词的词性标记,尝试确定正确的词块标记。
[3.1](./ch07.html#code-unigram-chunker)中,我们定义了`UnigramChunker`类,使用一元标注器给句子加词块标记。这个类的大部分代码只是用来在 NLTK 的`ChunkParserI`接口使用的词块树表示和嵌入式标注器使用的 IOB 表示之间镜像转换。类定义了两个方法:一个构造函数[❶](./ch07.html#code-unigram-chunker-constructor),当我们建立一个新的 UnigramChunker 时调用;以及`parse`方法[❸](./ch07.html#code-unigram-chunker-parse),用来给新句子划分词块。
3.1 中,我们定义了`UnigramChunker`类,使用一元标注器给句子加词块标记。这个类的大部分代码只是用来在 NLTK 的`ChunkParserI`接口使用的词块树表示和嵌入式标注器使用的 IOB 表示之间镜像转换。类定义了两个方法:一个构造函数[❶](./ch07.html#code-unigram-chunker-constructor),当我们建立一个新的 UnigramChunker 时调用;以及`parse`方法[❸](./ch07.html#code-unigram-chunker-parse),用来给新句子划分词块。
```py
class UnigramChunker(nltk.ChunkParserI):
......@@ -326,7 +326,7 @@ ChunkParse score:
它已经发现大多数标点符号出现在 NP 词块外,除了两种货币符号`#`和`它也发现限定词(`DT`)和所有格(`PRP
建立了一个一元分块器,很容易建立一个二元分块器:我们只需要改变类的名称为`BigramChunker`,修改[3.1](./ch07.html#code-unigram-chunker)行[❷](./ch07.html#code-unigram-chunker-buildit)构造一个`BigramTagger`而不是`UnigramTagger`。由此产生的词块划分器的性能略高于一元词块划分器:
建立了一个一元分块器,很容易建立一个二元分块器:我们只需要改变类的名称为`BigramChunker`,修改 3.1 行[❷](./ch07.html#code-unigram-chunker-buildit)构造一个`BigramTagger`而不是`UnigramTagger`。由此产生的词块划分器的性能略高于一元词块划分器:
```py
>>> bigram_chunker = BigramChunker(train_sents)
......@@ -482,7 +482,7 @@ ChunkParse score:
## 4.1 用级联词块划分器构建嵌套结构
到目前为止,我们的词块结构一直是相对平的。已标注词符组成的树在如`NP`这样的词块节点下任意组合。然而,只需创建一个包含递归规则的多级的词块语法,就可以建立任意深度的词块结构。[4.1](./ch07.html#code-cascaded-chunker)是名词短语、介词短语、动词短语和句子的模式。这是一个四级词块语法器,可以用来创建深度最多为 4 的结构。
到目前为止,我们的词块结构一直是相对平的。已标注词符组成的树在如`NP`这样的词块节点下任意组合。然而,只需创建一个包含递归规则的多级的词块语法,就可以建立任意深度的词块结构。4.1 是名词短语、介词短语、动词短语和句子的模式。这是一个四级词块语法器,可以用来创建深度最多为 4 的结构。
```py
grammar = r"""
......@@ -593,7 +593,7 @@ tree 是一组连接的加标签节点,从一个特殊的根节点沿一条唯
## 4.3 树遍历
使用递归函数来遍历树是标准的做法。[4.2](./ch07.html#code-traverse)中的内容进行了演示。
使用递归函数来遍历树是标准的做法。4.2 中的内容进行了演示。
```py
def traverse(t):
......@@ -619,7 +619,7 @@ def traverse(t):
## 5 命名实体识别
在本章开头,我们简要介绍了命名实体(NE)。命名实体是确切的名词短语,指示特定类型的个体,如组织、人、日期等。[5.1](./ch07.html#tab-ne-types)列出了一些较常用的 NE 类型。这些应该是不言自明的,除了“FACILITY”:建筑和土木工程领域的人造产品;以及“GPE”:地缘政治实体,如城市、州/省、国家。
在本章开头,我们简要介绍了命名实体(NE)。命名实体是确切的名词短语,指示特定类型的个体,如组织、人、日期等。5.1 列出了一些较常用的 NE 类型。这些应该是不言自明的,除了“FACILITY”:建筑和土木工程领域的人造产品;以及“GPE”:地缘政治实体,如城市、州/省、国家。
表 5.1:
......
......@@ -68,7 +68,7 @@ grammar1 = nltk.CFG.fromstring("""
""")
```
[3.1](./ch08.html#code-cfg1)中的语法包含涉及各种句法类型的产生式,如在[3.1](./ch08.html#tab-syncat)中所列出的。
3.1 中的语法包含涉及各种句法类型的产生式,如在 3.1 中所列出的。
表 3.1:
......@@ -88,7 +88,7 @@ grammar1 = nltk.CFG.fromstring("""
## 3.3 句法结构中的递归
一个语法被认为是递归的,如果语法类型出现在产生式左侧也出现在右侧,如[3.3](./ch08.html#code-cfg2)所示。产生式`Nom -&gt; Adj Nom`(其中`Nom`是名词性的类别)包含`Nom`类型的直接递归,而`S`上的间接递归来自于两个产生式的组合`S -&gt; NP VP``VP -&gt; V S`
一个语法被认为是递归的,如果语法类型出现在产生式左侧也出现在右侧,如 3.3 所示。产生式`Nom -&gt; Adj Nom`(其中`Nom`是名词性的类别)包含`Nom`类型的直接递归,而`S`上的间接递归来自于两个产生式的组合`S -&gt; NP VP``VP -&gt; V S`
```py
grammar2 = nltk.CFG.fromstring("""
......@@ -128,7 +128,7 @@ grammar2 = nltk.CFG.fromstring("""
一种简单的自下而上分析器是移进-归约分析器。与所有自下而上的分析器一样,移进-归约分析器尝试找到对应文法生产式*右侧*的词和短语的序列,用左侧的替换它们,直到整个句子归约为一个`S`
移位-规约分析器反复将下一个输入词推到堆栈([4.1](./ch04.html#sec-back-to-the-basics));这是移位操作。如果堆栈上的前 *n* 项,匹配一些产生式的右侧的 *n* 个项目,那么就把它们弹出栈,并把产生式左边的项目压入栈。这种替换前 *n* 项为一项的操作就是规约操作。此操作只适用于堆栈的顶部;规约栈中的项目必须在后面的项目被压入栈之前做。当所有的输入都使用过,堆栈中只剩余一个项目,也就是一颗分析树作为它的根的`S`节点时,分析器完成。移位-规约分析器通过上述过程建立一颗分析树。每次弹出堆栈 *n* 个项目,它就将它们组合成部分的分析树,然后将这压回推栈。我们可以使用图形化示范`nltk.app.srparser()`看到移位-规约分析算法步骤。执行此分析器的六个阶段,如[4.2](./ch08.html#fig-srparser1-6)所示。
移位-规约分析器反复将下一个输入词推到堆栈(4.1);这是移位操作。如果堆栈上的前 *n* 项,匹配一些产生式的右侧的 *n* 个项目,那么就把它们弹出栈,并把产生式左边的项目压入栈。这种替换前 *n* 项为一项的操作就是规约操作。此操作只适用于堆栈的顶部;规约栈中的项目必须在后面的项目被压入栈之前做。当所有的输入都使用过,堆栈中只剩余一个项目,也就是一颗分析树作为它的根的`S`节点时,分析器完成。移位-规约分析器通过上述过程建立一颗分析树。每次弹出堆栈 *n* 个项目,它就将它们组合成部分的分析树,然后将这压回推栈。我们可以使用图形化示范`nltk.app.srparser()`看到移位-规约分析算法步骤。执行此分析器的六个阶段,如 4.2 所示。
![Images/srparser1-6.png](Images/56cee123595482cf3edaef089cb9a6a7.jpg)
......@@ -166,7 +166,7 @@ NLTK 中提供`ShiftReduceParser()`,移进-归约分析器的一个简单的
[V -> 'shot']
```
对于我们的 WFST,我们用 Python 中的列表的咧表创建一个(n-1) × (n-1)的矩阵,在[4.4](./ch08.html#code-wfst)中的函数`init_wfst()`中用每个标识符的词汇类型初始化它。我们还定义一个实用的函数`display()`来为我们精美的输出 WFST。正如预期的那样,`V`在(1, 2)单元中。
对于我们的 WFST,我们用 Python 中的列表的咧表创建一个(n-1) × (n-1)的矩阵,在 4.4 中的函数`init_wfst()`中用每个标识符的词汇类型初始化它。我们还定义一个实用的函数`display()`来为我们精美的输出 WFST。正如预期的那样,`V`在(1, 2)单元中。
```py
def init_wfst(tokens, grammar):
......@@ -224,7 +224,7 @@ WFST 1 2 3 4 5 6 7
回到我们的表格表示,假设对于词 an 我们有`Det`在(2, 3)单元,对以词 elephant 有`N`在(3, 4)单元,对于 an elephant 我们应该在(2, 4)放入什么?我们需要找到一个形如 *A*`Det N`的产生式。查询了文法,我们知道我们可以输入(0, 2)单元的`NP`
更一般的,我们可以在(i, j)输入 *A*,如果有一个产生式 *A**B* *C*,并且我们在(i, k)中找到非终结符 *B*,在(k, j)中找到非终结符 *C*[4.4](./ch08.html#code-wfst)中的程序使用此规则完成 WFST。通过调用函数`complete_wfst()`时设置`trace``True`,我们看到了显示 WFST 正在被创建的跟踪输出:
更一般的,我们可以在(i, j)输入 *A*,如果有一个产生式 *A**B* *C*,并且我们在(i, k)中找到非终结符 *B*,在(k, j)中找到非终结符 *C*4.4 中的程序使用此规则完成 WFST。通过调用函数`complete_wfst()`时设置`trace``True`,我们看到了显示 WFST 正在被创建的跟踪输出:
```py
>>> wfst1 = complete_wfst(wfst0, tokens, groucho_grammar, trace=True)
......@@ -247,7 +247,7 @@ WFST 1 2 3 4 5 6 7
图 4.5:图数据结构:图中额外的边表示非终结符。
我们得出结论,只要我们已经在(0, 7)单元构建了一个`S`节点,表明我们已经找到了一个涵盖了整个输入的句子,我们就为整个输入字符串找到了一个解析。最后的 WFST 状态如[4.5](./ch08.html#fig-chart-positions2)所示。
我们得出结论,只要我们已经在(0, 7)单元构建了一个`S`节点,表明我们已经找到了一个涵盖了整个输入的句子,我们就为整个输入字符串找到了一个解析。最后的 WFST 状态如 4.5 所示。
请注意,在这里我们没有使用任何内置的分析函数。我们已经从零开始实现了一个完整的初级图表分析器!
......@@ -263,13 +263,13 @@ WFST 有几个缺点。首先,正如你可以看到的,WFST 本身不是一
短语结构文法是关于词和词序列如何结合起来形成句子成分的。一个独特的和互补的方式,依存语法,集中关注的是词与其他词之间的关系。依存关系是一个中心词与它的依赖之间的二元对称关系。一个句子的中心词通常是动词,所有其他词要么依赖于中心词,要么依赖路径与它联通。
一个句子的中心词通常是动词,所有其他词要么依赖于中心词,要么依赖路径与它联通。[5.1](./ch08.html#fig-depgraph0)显示一个依存关系图,箭头从中心词指出它们的依赖。
一个句子的中心词通常是动词,所有其他词要么依赖于中心词,要么依赖路径与它联通。5.1 显示一个依存关系图,箭头从中心词指出它们的依赖。
![Images/depgraph0.png](Images/ff868af58b8c1843c38287717b137f7c.jpg)
图 5.1:依存结构:箭头从中心词指向它们的依赖;标签表示依赖的语法功能如:主语、宾语或修饰语。
[5.1](./ch08.html#fig-depgraph0)中的弧加了依赖与它的中心词之间的语法功能标签。例如,I 是 shot(这是整个句子的中心词)的`SBJ`(主语),in 是一个`NMOD`(elephant 的名词修饰语)。与短语结构语法相比,依存语法可以作为一种依存关系直接用来表示语法功能。
5.1 中的弧加了依赖与它的中心词之间的语法功能标签。例如,I 是 shot(这是整个句子的中心词)的`SBJ`(主语),in 是一个`NMOD`(elephant 的名词修饰语)。与短语结构语法相比,依存语法可以作为一种依存关系直接用来表示语法功能。
下面是 NLTK 为依存语法编码的一种方式——注意它只能捕捉依存关系信息,不能指定依存关系类型:
......@@ -291,7 +291,7 @@ Dependency grammar with 7 productions
'pajamas' -> 'my'
```
依存关系图是一个投影,当所有的词都按线性顺序书写,边可以在词上绘制而不会交叉。这等于是说一个词及其所有后代依赖(依赖及其依赖的依赖,等等)在句子中形成一个连续的词序列。[5.1](./ch08.html#fig-depgraph0)是一个投影,我们可以使用投影依存关系分析器分析很多英语句子。下面的例子演示`groucho_dep_grammar`如何提供了一种替代的方法来捕捉附着歧义,我们之前在研究短语结构语法中遇到的。
依存关系图是一个投影,当所有的词都按线性顺序书写,边可以在词上绘制而不会交叉。这等于是说一个词及其所有后代依赖(依赖及其依赖的依赖,等等)在句子中形成一个连续的词序列。5.1 是一个投影,我们可以使用投影依存关系分析器分析很多英语句子。下面的例子演示`groucho_dep_grammar`如何提供了一种替代的方法来捕捉附着歧义,我们之前在研究短语结构语法中遇到的。
```py
>>> pdp = nltk.ProjectiveDependencyParser(groucho_dep_grammar)
......@@ -347,7 +347,7 @@ TV -> 'chased' | 'saw'
(. .))
```
我们可以利用这些数据来帮助开发一个语法。例如,[6.1](./ch08.html#code-sentential-complement)中的程序使用一个简单的过滤器找出带句子补语的动词。假设我们已经有一个形如`VP -&gt; Vs S`的产生式,这个信息使我们能够识别那些包括在`Vs`的扩张中的特别的动词。
我们可以利用这些数据来帮助开发一个语法。例如,6.1 中的程序使用一个简单的过滤器找出带句子补语的动词。假设我们已经有一个形如`VP -&gt; Vs S`的产生式,这个信息使我们能够识别那些包括在`Vs`的扩张中的特别的动词。
```py
def filter(tree):
......@@ -433,7 +433,7 @@ def print_node(t, width):
概率上下文无关语法(或 *PCFG*)是一种上下文无关语法,它的每一个产生式关联一个概率。它会产生与相应的上下文无关语法相同的文本解析,并给每个解析分配一个概率。PCFG 产生的一个解析的概率仅仅是它用到的产生式的概率的乘积。
最简单的方法定义一个 PCFG 是从一个加权产生式序列组成的特殊格式的字符串加载它,其中权值出现在括号里,如[6.4](./ch08.html#code-pcfg1)所示。
最简单的方法定义一个 PCFG 是从一个加权产生式序列组成的特殊格式的字符串加载它,其中权值出现在括号里,如 6.4 所示。
```py
grammar = nltk.PCFG.fromstring("""
......@@ -449,7 +449,7 @@ grammar = nltk.PCFG.fromstring("""
""")
```
有时可以很方便的将多个产生式组合成一行,如`VP -&gt; TV NP [0.4] | IV [0.3] | DatV NP NP [0.3]`。为了确保由文法生成的树能形成概率分布,PCFG 语法强加了约束,产生式所有给定的左侧的概率之和必须为 1。[6.4](./ch08.html#code-pcfg1)中的语法符合这个约束:对`S`只有一个产生式,它的概率是 1.0;对于`VP`,0.4+0.3+0.3=1.0;对于`NP`,0.8+0.2=1.0。`parse()`返回的分析树包含概率:
有时可以很方便的将多个产生式组合成一行,如`VP -&gt; TV NP [0.4] | IV [0.3] | DatV NP NP [0.3]`。为了确保由文法生成的树能形成概率分布,PCFG 语法强加了约束,产生式所有给定的左侧的概率之和必须为 1。6.4 中的语法符合这个约束:对`S`只有一个产生式,它的概率是 1.0;对于`VP`,0.4+0.3+0.3=1.0;对于`NP`,0.8+0.2=1.0。`parse()`返回的分析树包含概率:
```py
>>> viterbi_parser = nltk.ViterbiParser(grammar)
......@@ -523,7 +523,7 @@ grammar = nltk.PCFG.fromstring("""
1. 写一个程序在 PP 附着语料库`nltk.corpus.ppattach`找到那些动词。找出任何这样的情况,相同的动词有两种不同的附着,其中第一个是名词,或者第二个是名词,或者介词保持不变(正如我们在[2](./ch08.html#sec-whats-the-use-of-syntax)句法歧义中讨论过的)。
2. 制定 CFG 语法产生式涵盖其中一些情况。
17. ◑ 写一个程序,比较自上而下的图表分析器与递归下降分析器的效率([4](./ch08.html#sec-parsing))。使用相同的语法和输入的句子。使用`timeit`模块比较它们的性能(见[4.7](./ch04.html#sec-algorithm-design),如何做到这一点的一个例子)。
17. ◑ 写一个程序,比较自上而下的图表分析器与递归下降分析器的效率([4](./ch08.html#sec-parsing))。使用相同的语法和输入的句子。使用`timeit`模块比较它们的性能(见 4.7,如何做到这一点的一个例子)。
18. 比较自上而下、自下而上和左角落分析器的性能,使用相同的语法和 3 个符合语法的测试句子。使用`timeit`记录每个分析器在同一个句子上花费的时间。写一个函数,在这三句话上运行这三个分析器,输出 3×3 格的时间,以及行和列的总计。讨论你的发现。
......@@ -549,13 +549,13 @@ grammar = nltk.PCFG.fromstring("""
26. ◑ 修改函数`init_wfst()`和`complete_wfst()`,使 WFST 中每个单元的内容是一组非终端符而不是一个单独的非终结符。
27. ◑ 思考[4.4](./ch08.html#code-wfst)中的算法。你能解释为什么分析上下文无关语法是与&lt;cite&gt;n&lt;/cite&gt;&lt;sup&gt;3&lt;/sup&gt;成正比的,其中 *n* 是输入句子的长度。
27. ◑ 思考 4.4 中的算法。你能解释为什么分析上下文无关语法是与&lt;cite&gt;n&lt;/cite&gt;&lt;sup&gt;3&lt;/sup&gt;成正比的,其中 *n* 是输入句子的长度。
28. ◑ 处理宾州树库语料库样本`nltk.corpus.treebank`中的每棵树,在`Tree.productions()`的帮助下提取产生式。丢弃只出现一次的产生式。具有相同的左侧和类似的右侧的产生式可以被折叠,产生一个等价的却更紧凑的规则集。编写代码输出一个紧凑的语法。
29. ★ 英语中定义句子`S`的主语的一种常见的方法是作为`S`*的名词短语孩子*和`VP`的*兄弟*。写一个函数,以一句话的树为参数,返回句子主语对应的子树。如果传递给这个函数的树的根节点不是`S`或它缺少一个主语,应该怎么做?
30. ★ 写一个函数,以一个语法(如[3.1](./ch08.html#code-cfg1)定义的语法)为参数,返回由这个语法随机产生的一个句子。(使用`grammar.start()`找出语法的开始符号;`grammar.productions(lhs)`得到具有指定左侧的语法的产生式的列表;`production.rhs()`得到一个产生式的右侧。)
30. ★ 写一个函数,以一个语法(如 3.1 定义的语法)为参数,返回由这个语法随机产生的一个句子。(使用`grammar.start()`找出语法的开始符号;`grammar.productions(lhs)`得到具有指定左侧的语法的产生式的列表;`production.rhs()`得到一个产生式的右侧。)
31. ★ 使用回溯实现移位-规约分析器的一个版本,使它找出一个句子所有可能的解析,它可以被称为“递归上升分析器”。咨询维基百科关于回溯的条目`http://en.wikipedia.org/wiki/Backtracking`
......
......@@ -86,11 +86,11 @@ N[NUM=pl]
注意一个句法类别可以有多个特征,例如`V[TENSE=pres, NUM=pl]`。在一般情况下,我们喜欢多少特征就可以添加多少。
关于[1.1](./ch09.html#code-feat0cfg)的最后的细节是语句`%start S`。这个“指令”告诉分析器以`S`作为文法的开始符号。
关于 1.1 的最后的细节是语句`%start S`。这个“指令”告诉分析器以`S`作为文法的开始符号。
一般情况下,即使我们正在尝试开发很小的语法,把产生式放在一个文件中我们可以编辑、测试和修改是很方便的。我们将[1.1](./ch09.html#code-feat0cfg)以 NLTK 的数据格式保存为文件`'feat0.fcfg'`。你可以使用`nltk.data.load()`制作你自己的副本进行进一步的实验。
一般情况下,即使我们正在尝试开发很小的语法,把产生式放在一个文件中我们可以编辑、测试和修改是很方便的。我们将 1.1 以 NLTK 的数据格式保存为文件`'feat0.fcfg'`。你可以使用`nltk.data.load()`制作你自己的副本进行进一步的实验。
[1.2](./ch09.html#code-featurecharttrace) 说明了基于特征的语法图表解析的操作。为输入分词之后,我们导入`load_parser`函数[](./ch09.html#load_parser1),以语法文件名为输入,返回一个图表分析器`cp` [](./ch09.html#load_parser2)。调用分析器的`parse()`方法将迭代生成的分析树;如果文法无法分析输入,`trees`将为空,并将会包含一个或多个分析树,取决于输入是否有句法歧义。
1.2 说明了基于特征的语法图表解析的操作。为输入分词之后,我们导入`load_parser`函数[](./ch09.html#load_parser1),以语法文件名为输入,返回一个图表分析器`cp` [](./ch09.html#load_parser2)。调用分析器的`parse()`方法将迭代生成的分析树;如果文法无法分析输入,`trees`将为空,并将会包含一个或多个分析树,取决于输入是否有句法歧义。
```py
>>> tokens = 'Kim likes children'.split()
......@@ -157,7 +157,7 @@ V[TENSE=pres, -AUX] -> 'likes'
```
在传递中,我们应该指出有显示 AVM 的替代方法;[1.3](./ch09.html#fig-avm1)显示了一个例子。虽然特征结构呈现的[(16)](./ch09.html#ex-agr0)中的风格不太悦目,我们将坚持用这种格式,因为它对应我们将会从 NLTK 得到的输出。
在传递中,我们应该指出有显示 AVM 的替代方法;1.3 显示了一个例子。虽然特征结构呈现的[(16)](./ch09.html#ex-agr0)中的风格不太悦目,我们将坚持用这种格式,因为它对应我们将会从 NLTK 得到的输出。
关于表示,我们也注意到特征结构,像字典,对特征的*顺序*没有指定特别的意义。所以[(16)](./ch09.html#ex-agr0)等同于︰
......@@ -436,7 +436,7 @@ NP/NP ->
Comp -> 'that'
```
[3.1](./ch09.html#code-slashcfg)中的语法包含一个“缺口引进”产生式,即`S[-INV] -&gt; NP S/NP`。为了正确的预填充斜线特征,我们需要为扩展`S``VP``NP`的产生式中箭头两侧的斜线添加变量值。例如,`VP/?x -&gt; V SBar/?x``VP -&gt; V SBar`的斜线版本,也就是说,可以为一个成分的父母`VP`指定斜线值,只要也为孩子`SBar`指定同样的值。最后,`NP/NP ->`允许`NP`上的斜线信息为空字符串。使用[3.1](./ch09.html#code-slashcfg)中的语法,我们可以分析序列 who do you claim that you like
3.1 中的语法包含一个“缺口引进”产生式,即`S[-INV] -&gt; NP S/NP`。为了正确的预填充斜线特征,我们需要为扩展`S``VP``NP`的产生式中箭头两侧的斜线添加变量值。例如,`VP/?x -&gt; V SBar/?x``VP -&gt; V SBar`的斜线版本,也就是说,可以为一个成分的父母`VP`指定斜线值,只要也为孩子`SBar`指定同样的值。最后,`NP/NP ->`允许`NP`上的斜线信息为空字符串。使用 3.1 中的语法,我们可以分析序列 who do you claim that you like
```py
>>> tokens = 'who do you claim that you like'.split()
......@@ -491,7 +491,7 @@ Comp -> 'that'
## 3.5 德语中的格和性别
与英语相比,德语的协议具有相对丰富的形态。例如,在德语中定冠词根据格、性别和数量变化,如[3.1](./ch09.html#tab-german-def-art)所示。
与英语相比,德语的协议具有相对丰富的形态。例如,在德语中定冠词根据格、性别和数量变化,如 3.1 所示。
表 3.1:
......@@ -611,7 +611,7 @@ Feature Bottom Up Predict Combine Rule:
|. . . [---]| [3:4] N[AGR=[GND='fem', NUM='sg', PER=3]] -> 'Katze' *
```
跟踪中的最后两个`Scanner`行显示 den 被识别为两个可能的类别:`Det[AGR=[GND='masc', NUM='sg', PER=3], CASE='acc']``Det[AGR=[NUM='pl', PER=3], CASE='dat']`。我们从[3.2](./ch09.html#code-germancfg)中的语法知道`Katze`的类别是`N[AGR=[GND=fem, NUM=sg, PER=3]]`。因而,产生式`NP[CASE=?c, AGR=?a] -&gt; Det[CASE=?c, AGR=?a] N[CASE=?c, AGR=?a]`中没有变量`?a`的绑定,这将满足这些限制,因为`Katze``AGR`值将不与 den 的任何一个`AGR`值统一,也就是`[GND='masc', NUM='sg', PER=3]``[NUM='pl', PER=3]`
跟踪中的最后两个`Scanner`行显示 den 被识别为两个可能的类别:`Det[AGR=[GND='masc', NUM='sg', PER=3], CASE='acc']``Det[AGR=[NUM='pl', PER=3], CASE='dat']`。我们从 3.2 中的语法知道`Katze`的类别是`N[AGR=[GND=fem, NUM=sg, PER=3]]`。因而,产生式`NP[CASE=?c, AGR=?a] -&gt; Det[CASE=?c, AGR=?a] N[CASE=?c, AGR=?a]`中没有变量`?a`的绑定,这将满足这些限制,因为`Katze``AGR`值将不与 den 的任何一个`AGR`值统一,也就是`[GND='masc', NUM='sg', PER=3]``[NUM='pl', PER=3]`
## 4 小结
......@@ -653,7 +653,7 @@ In the case of complex values, we say that feature structures are themselves typ
1. ☼ 需要什么样的限制才能正确分析词序列,如 I am happy 和 she is happy 而不是*you is happy 或*they am happy?实现英语中动词 be 的现在时态范例的两个解决方案,首先以语法[(6)](./ch09.html#ex-agcfg1)作为起点,然后以语法 [(18)](./ch09.html#ex-agr2)为起点。
2. ☼ 开发[1.1](./ch09.html#code-feat0cfg)中语法的变体,使用特征&lt;cite&gt;count&lt;/cite&gt;来区分下面显示的句子:
2. ☼ 开发 1.1 中语法的变体,使用特征&lt;cite&gt;count&lt;/cite&gt;来区分下面显示的句子:
```py
fs1 = nltk.FeatStruct("[A = ?x, B= [C = ?x]]")
......@@ -684,7 +684,7 @@ In the case of complex values, we say that feature structures are themselves typ
4. ◑ 忽略结构共享,给出一个统一两个特征结构的非正式算法。
5. ◑ 扩展[3.2](./ch09.html#code-germancfg)中的德语语法,使它能处理所谓的动词第二顺位结构,如下所示:
5. ◑ 扩展 3.2 中的德语语法,使它能处理所谓的动词第二顺位结构,如下所示:
| (58) | | Heute sieht der Hund die Katze. |
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册