提交 e25a5cb2 编写于 作者: W wizardforcel

2020-12-29 17:43:23

上级 015cd801
此差异已折叠。
......@@ -34,7 +34,7 @@ g. There are two ways to do this, AFAIK :smile: (internet discussion archive)
第二个发展是符号逻辑。它提供一个捕捉选定的自然语言的表达的逻辑证明的有关方面的形式化方法。符号逻辑中的形式化演算提供一种语言的句法和推理规则,并可能在一套理论模型中对规则进行解释;例子是命题逻辑和一阶逻辑。给定这样的演算和一个明确的句法和语义,通过将自然语言的表达翻译成形式化演算的表达式,联系语义与自然语言的表达成为可能。例如,如果我们翻译 John saw Mary 为公式`saw(j,m)`,我们(或明或暗地)将英语动词 saw 解释为一个二元关系,而 John 和 Mary 表示个体元素。更多的一般性的表达式如 All birds fly 需要量词,在这个例子中是∀,意思是对所有的:∀x (bird(x) → fly(x))。逻辑的使用提供了技术性的机制处理推理,而推理是语言理解的重要组成部分。
另一个密切相关的发展是组合原理,即一个复杂表达式的意思由它的各个部分的意思和它们的组合模式组成(10)。这一原理提供了句法和语义之间的有用的对应,即一个复杂的表达式的含义可以递归的计算。考虑句子 It is not true that p,其中 p 是一个命题。我们可以表示这个句子的意思为 not(p)。同样,我们可以表示 John saw Mary 的意思为 saw(j, m)。现在,我们可以使用上述信息递归地计算 It is not true that John saw Mary 的表示,得到 not(saw(j,m))。
另一个密切相关的发展是组合原理,即一个复杂表达式的意思由它的各个部分的意思和它们的组合模式组成`(10)`。这一原理提供了句法和语义之间的有用的对应,即一个复杂的表达式的含义可以递归的计算。考虑句子 It is not true that p,其中 p 是一个命题。我们可以表示这个句子的意思为 not(p)。同样,我们可以表示 John saw Mary 的意思为 saw(j, m)。现在,我们可以使用上述信息递归地计算 It is not true that John saw Mary 的表示,得到 not(saw(j,m))。
刚刚简要介绍的方法都有一个前提,自然语言计算关键依赖于操纵符号表示的规则。NLP 发展的一个特定时期,特别是 1980 年代,这个前提为语言学家和 NLP 从业人员提供了一个共同的起点,导致一种被称为基于归一(基于特征)语法的形式化语法家族(参见 9),也导致了在 Prolog 编程语言上实现 NLP 应用。虽然基于语法的自然语言处理仍然是一个研究的重要领域,由于多种因素在过去的 15-20 年它已经有些黯然失色。一个显著的影响因素来自于自动语音识别。虽然早期的语音处理采用一个模拟一类基于规则的音韵处理的模型,典型的如 *Sound Pattern of English* (Chomsky & Halle, 1968),结果远远不能够解决实时的识别实际的讲话这样困难的问题。相比之下,包含从大量语音数据中学习的模式的系统明显更准确、高效和稳健的。此外,言语社区发现建立对常见的测试数据的性能的定量测量的共享资源对建立更好的系统的过程有巨大帮助。最终,大部分的 NLP 社区拥抱面向数据密集型的语言处理,配合机器学习技术和评价为主导的方法的越来越多地使用。
......
......@@ -532,7 +532,7 @@ TypeError: can only concatenate list (not "str") to list
['John', 'Paul', 'George', 'Ringo', 'Brian']
```
当我们在一个 Python 程序中打开并读入一个文件,我们得到一个对应整个文件内容的字符串。如果我们使用一个`for`循环来处理这个字符串元素,所有我们可以挑选出的只是单个的字符——我们不选择粒度。相比之下,列表中的元素可以很大也可以很小,只要我们喜欢:例如,它们可能是段落、句子、短语、单词、字符。所以,列表的优势是我们可以灵活的决定它包含的元素,相应的后续的处理也变得灵活。因此,我们在一段 NLP 代码中可能做的第一件事情就是将一个字符串分词放入一个字符串列表中(3.7)。相反,当我们要将结果写入到一个文件或终端,我们通常会将它们格式化为一个字符串(3.9)
当我们在一个 Python 程序中打开并读入一个文件,我们得到一个对应整个文件内容的字符串。如果我们使用一个`for`循环来处理这个字符串元素,所有我们可以挑选出的只是单个的字符——我们不选择粒度。相比之下,列表中的元素可以很大也可以很小,只要我们喜欢:例如,它们可能是段落、句子、短语、单词、字符。所以,列表的优势是我们可以灵活的决定它包含的元素,相应的后续的处理也变得灵活。因此,我们在一段 NLP 代码中可能做的第一件事情就是将一个字符串分词放入一个字符串列表中`(3.7)`。相反,当我们要将结果写入到一个文件或终端,我们通常会将它们格式化为一个字符串`(3.9)`
列表与字符串没有完全相同的功能。列表具有增强的能力使你可以改变其中的元素:
......@@ -1512,7 +1512,7 @@ humor 16 30 8 8 9 13
### 将结果写入文件
我们已经看到了如何读取文本文件(3.1)。将输出写入文件往往也很有用。下面的代码打开可写文件`output.txt`,将程序的输出保存到文件。
我们已经看到了如何读取文本文件`(3.1)`。将输出写入文件往往也很有用。下面的代码打开可写文件`output.txt`,将程序的输出保存到文件。
```py
>>> output_file = open('output.txt', 'w')
......
......@@ -40,7 +40,7 @@
图 4.1:列表赋值与计算机内存:两个列表对象`foo``bar`引用计算机内存中的相同的位置;更新`foo`将同样修改`bar`,反之亦然。
`bar = foo`❶行并不会复制变量的内容,只有它的“引用对象”。要了解这里发生了什么事,我们需要知道列表是如何存储在计算机内存的。在 4.1 中,我们看到一个列表`foo`是对存储在位置 3133 处的一个对象的引用(它自身是一个指针序列,其中的指针指向其它保存字符串的位置)。当我们赋值`bar = foo`时,仅仅是 3133 位置处的引用被复制。这种行为延伸到语言的其他方面,如参数传递(4.4)
`bar = foo`❶行并不会复制变量的内容,只有它的“引用对象”。要了解这里发生了什么事,我们需要知道列表是如何存储在计算机内存的。在 4.1 中,我们看到一个列表`foo`是对存储在位置 3133 处的一个对象的引用(它自身是一个指针序列,其中的指针指向其它保存字符串的位置)。当我们赋值`bar = foo`时,仅仅是 3133 位置处的引用被复制。这种行为延伸到语言的其他方面,如参数传递`(4.4)`
让我们做更多的实验,通过创建一个持有空列表的变量`empty`,然后在下一行使用它三次。
......
# 6\. 学习分类文本
模式识别是自然语言处理的一个核心部分。以`-ed`结尾的词往往是过去时态动词(5)。频繁使用`will`是新闻文本的暗示`(3)`。这些可观察到的模式——词的结构和词频——恰好与特定方面的含义关联,如时态和主题。但我们怎么知道从哪里开始寻找,形式的哪一方面关联含义的哪一方面?
模式识别是自然语言处理的一个核心部分。以`-ed`结尾的词往往是过去时态动词`(5)`。频繁使用`will`是新闻文本的暗示`(3)`。这些可观察到的模式——词的结构和词频——恰好与特定方面的含义关联,如时态和主题。但我们怎么知道从哪里开始寻找,形式的哪一方面关联含义的哪一方面?
本章的目的是要回答下列问题:
......@@ -223,7 +223,7 @@ correct=male guess=female name=Rich
>>> random.shuffle(documents)
```
接下来,我们为文档定义一个特征提取器,这样分类器就会知道哪些方面的数据应注意(1.4)。对于文档主题识别,我们可以为每个词定义一个特性表示该文档是否包含这个词。为了限制分类器需要处理的特征的数目,我们一开始构建一个整个语料库中前 2000 个最频繁词的列表❶。然后,定义一个特征提取器❷,简单地检查这些词是否在一个给定的文档中。
接下来,我们为文档定义一个特征提取器,这样分类器就会知道哪些方面的数据应注意`(1.4)`。对于文档主题识别,我们可以为每个词定义一个特性表示该文档是否包含这个词。为了限制分类器需要处理的特征的数目,我们一开始构建一个整个语料库中前 2000 个最频繁词的列表❶。然后,定义一个特征提取器❷,简单地检查这些词是否在一个给定的文档中。
```py
all_words = nltk.FreqDist(w.lower() for w in movie_reviews.words())
......@@ -239,9 +239,9 @@ def document_features(document): ❷
注意
在❸中我们计算文档的所有词的集合,而不仅仅检查是否`word in document`,因为检查一个词是否在一个集合中出现比检查它是否在一个列表中出现要快的多(4.7)
在❸中我们计算文档的所有词的集合,而不仅仅检查是否`word in document`,因为检查一个词是否在一个集合中出现比检查它是否在一个列表中出现要快的多`(4.7)`
现在,我们已经定义了我们的特征提取器,可以用它来训练一个分类器,为新的电影评论加标签(1.5)。为了检查产生的分类器可靠性如何,我们在测试集上计算其准确性❶。再一次的,我们可以使用`show_most_informative_features()`来找出哪些特征是分类器发现最有信息量的❷。
现在,我们已经定义了我们的特征提取器,可以用它来训练一个分类器,为新的电影评论加标签`(1.5)`。为了检查产生的分类器可靠性如何,我们在测试集上计算其准确性❶。再一次的,我们可以使用`show_most_informative_features()`来找出哪些特征是分类器发现最有信息量的❷。
```py
featuresets = [(document_features(d), c) for (d,c) in documents]
......@@ -396,7 +396,7 @@ class ConsecutivePosTagger(nltk.TaggerI): ❷
这种方法的一个缺点是我们的决定一旦做出无法更改。例如,如果我们决定将一个词标注为名词,但后来发现的证据表明应该是一个动词,就没有办法回去修复我们的错误。这个问题的一个解决方案是采取转型策略。转型联合分类的工作原理是为输入的标签创建一个初始值,然后反复提炼那个值,尝试修复相关输入之间的不一致。Brill 标注器,`(1)`中描述的,是这种策略的一个很好的例子。
另一种方案是为词性标记所有可能的序列打分,选择总得分最高的序列。隐马尔可夫模型就采取这种方法。隐马尔可夫模型类似于连续分类器,它不光看输入也看已预测标记的历史。然而,不是简单地找出一个给定的词的单个最好的标签,而是为标记产生一个概率分布。然后将这些概率结合起来计算标记序列的概率得分,最高概率的标记序列会被选中。不幸的是,可能的标签序列的数量相当大。给定 30 个标签的标记集,有大约 600 万亿(`30^10`)种方式来标记一个 10 个词的句子。为了避免单独考虑所有这些可能的序列,隐马尔可夫模型要求特征提取器只看最近的标记(或最近的`n`个标记,其中`n`是相当小的)。由于这种限制,它可以使用动态规划(4.7),有效地找出最有可能的标记序列。特别是,对每个连续的词索引`i`,每个可能的当前及以前的标记都被计算得分。这种同样基础的方法被两个更先进的模型所采用,它们被称为最大熵马尔可夫模型和线性链条件随机场模型;但为标记序列打分用的是不同的算法。
另一种方案是为词性标记所有可能的序列打分,选择总得分最高的序列。隐马尔可夫模型就采取这种方法。隐马尔可夫模型类似于连续分类器,它不光看输入也看已预测标记的历史。然而,不是简单地找出一个给定的词的单个最好的标签,而是为标记产生一个概率分布。然后将这些概率结合起来计算标记序列的概率得分,最高概率的标记序列会被选中。不幸的是,可能的标签序列的数量相当大。给定 30 个标签的标记集,有大约 600 万亿(`30^10`)种方式来标记一个 10 个词的句子。为了避免单独考虑所有这些可能的序列,隐马尔可夫模型要求特征提取器只看最近的标记(或最近的`n`个标记,其中`n`是相当小的)。由于这种限制,它可以使用动态规划`(4.7)`,有效地找出最有可能的标记序列。特别是,对每个连续的词索引`i`,每个可能的当前及以前的标记都被计算得分。这种同样基础的方法被两个更先进的模型所采用,它们被称为最大熵马尔可夫模型和线性链条件随机场模型;但为标记序列打分用的是不同的算法。
## 2 有监督分类的更多例子
......@@ -512,7 +512,7 @@ NPS 聊天语料库,在 1 中的展示过,包括超过 10,000 个来自即
我们可以把 RTE 当作一个分类任务,尝试为每一对预测`True/False`标签。虽然这项任务的成功做法似乎看上去涉及语法分析、语义和现实世界的知识的组合,RTE 的许多早期的尝试使用粗浅的分析基于文字和假设之间的在词级别的相似性取得了相当不错的结果。在理想情况下,我们希望如果有一个蕴涵那么假设所表示的所有信息也应该在文本中表示。相反,如果假设中有的资料文本中没有,那么就没有蕴涵。
在我们的 RTE 特征探测器(2.2)中,我们让词(即词类型)作为信息的代理,我们的特征计数词重叠的程度和假设中有而文本中没有的词的程度(由`hyp_extra()`方法获取)。不是所有的词都是同样重要的——命名实体,如人、组织和地方的名称,可能会更为重要,这促使我们分别为`words``nes`(命名实体)提取不同的信息。此外,一些高频虚词作为“停用词”被过滤掉。
在我们的 RTE 特征探测器`(2.2)`中,我们让词(即词类型)作为信息的代理,我们的特征计数词重叠的程度和假设中有而文本中没有的词的程度(由`hyp_extra()`方法获取)。不是所有的词都是同样重要的——命名实体,如人、组织和地方的名称,可能会更为重要,这促使我们分别为`words``nes`(命名实体)提取不同的信息。此外,一些高频虚词作为“停用词”被过滤掉。
```py
def rte_features(rtepair):
......
......@@ -81,7 +81,7 @@
## 2.2 标记模式
组成一个词块语法的规则使用标记模式来描述已标注的词的序列。一个标记模式是一个词性标记序列,用尖括号分隔,如`<DT&gt;?&lt;JJ&gt;*&lt;NN>`。标记模式类似于正则表达式模式(3.4)。现在,思考下面的来自《华尔街日报》的名词短语:
组成一个词块语法的规则使用标记模式来描述已标注的词的序列。一个标记模式是一个词性标记序列,用尖括号分隔,如`<DT&gt;?&lt;JJ&gt;*&lt;NN>`。标记模式类似于正则表达式模式`(3.4)`。现在,思考下面的来自《华尔街日报》的名词短语:
```py
another/DT sharp/JJ dive/NN
......
......@@ -128,7 +128,7 @@ grammar2 = nltk.CFG.fromstring("""
一种简单的自下而上分析器是移进-归约分析器。与所有自下而上的分析器一样,移进-归约分析器尝试找到对应文法生产式*右侧*的词和短语的序列,用左侧的替换它们,直到整个句子归约为一个`S`
移位-规约分析器反复将下一个输入词推到堆栈(4.1);这是移位操作。如果堆栈上的前 *n* 项,匹配一些产生式的右侧的`n`个项目,那么就把它们弹出栈,并把产生式左边的项目压入栈。这种替换前`n`项为一项的操作就是规约操作。此操作只适用于堆栈的顶部;规约栈中的项目必须在后面的项目被压入栈之前做。当所有的输入都使用过,堆栈中只剩余一个项目,也就是一颗分析树作为它的根的`S`节点时,分析器完成。移位-规约分析器通过上述过程建立一颗分析树。每次弹出堆栈`n`个项目,它就将它们组合成部分的分析树,然后将这压回推栈。我们可以使用图形化示范`nltk.app.srparser()`看到移位-规约分析算法步骤。执行此分析器的六个阶段,如 4.2 所示。
移位-规约分析器反复将下一个输入词推到堆栈`(4.1)`;这是移位操作。如果堆栈上的前 *n* 项,匹配一些产生式的右侧的`n`个项目,那么就把它们弹出栈,并把产生式左边的项目压入栈。这种替换前`n`项为一项的操作就是规约操作。此操作只适用于堆栈的顶部;规约栈中的项目必须在后面的项目被压入栈之前做。当所有的输入都使用过,堆栈中只剩余一个项目,也就是一颗分析树作为它的根的`S`节点时,分析器完成。移位-规约分析器通过上述过程建立一颗分析树。每次弹出堆栈`n`个项目,它就将它们组合成部分的分析树,然后将这压回推栈。我们可以使用图形化示范`nltk.app.srparser()`看到移位-规约分析算法步骤。执行此分析器的六个阶段,如 4.2 所示。
![Images/srparser1-6.png](Images/56cee123595482cf3edaef089cb9a6a7.jpg)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册