Skip to content

Commit

Permalink
Chapter recurrent neural networks/text preprocessing (d2l-ai#877)
Browse files Browse the repository at this point in the history
* fix errors in 8.2. Text Preprocessing
fix errors in 8.2.1. Reading the Dataset

* fix errors in 8.2.2. Tokenization

* fix errors in 8.2.3. Vocabulary

* fix errors in 8.2.4. Put All Things Together
the end of fix errors in 8.2.

* Update text-preprocessing.md

* Update text-preprocessing.md

* fix errors in 8.2. Text Preprocessing

* fix errors in 8.2.1. Reading the Dataset

* fix errors in 8.2.2. Tokenization

* fix errors in 8.2.3. Vocabulary

* fix errors in 8.2.4. Put All Things Together

* end of fix errors in 8.2.

* enhanced reader's understanding about translation

* Polish

* Polish done

Co-authored-by: goldmermaid <[email protected]>
  • Loading branch information
zhuyuanxiang and goldmermaid authored Jul 16, 2021
1 parent 24ecfb0 commit 1e99daf
Showing 1 changed file with 32 additions and 19 deletions.
51 changes: 32 additions & 19 deletions chapter_recurrent-neural-networks/text-preprocessing.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
# 文本预处理
:label:`sec_text_preprocessing`

我们回顾和评估了处理序列数据时,使用的统计工具和预测时面临的挑战。
正如我们将在本书的许多章节中重点介绍的那样,文本是序列数据最常见例子。
例如,一篇文章可以简单地看作是一个单词序列,甚至是一个字符序列。
为了方便将来在实验中使用序列数据,我们将在本节中专门解释文本的常见预处理步骤。通常,这些步骤包括:
对于序列数据处理问题,我们回顾和评估了使用的统计工具和预测时面临的挑战。
这样的数据存在许多种形式,文本是最常见例子之一。
例如,一篇文章可以简单地看作是一串单词序列,甚至是一串字符序列。
本节中,我们将专门解释文本的常见预处理步骤。
这些步骤通常包括:

1. 将文本作为字符串加载到内存中。
1. 将字符串拆分为标记(如,单词和字符)。
1. 将字符串拆分为标记(如单词和字符)。
1. 建立一个词汇表,将拆分的标记映射到数字索引。
1. 将文本转换为数字索引序列,以便模型可以轻松地对其进行操作
1. 将文本转换为数字索引序列,方便模型操作

```{.python .input}
import collections
Expand All @@ -33,7 +34,10 @@ import re

## 读取数据集

我们从H.G.Well的 [*时光机器*](http://www.gutenberg.org/ebooks/35) 中加载文本作为开始。这是一个相当小的语料库,只有30000多个单词,但足够实现我们的目标,即介绍文本预处理。现实中的文档集合可能会包含数十亿个单词。下面的函数(**将数据集读取到由文本行组成的列表中**),其中每行都是一个字符串。为简单起见,我们在这里忽略了标点符号和字母大写。
首先,我们从 H.G.Well 的[时光机器](http://www.gutenberg.org/ebooks/35)中加载文本。
这是一个相当小的语料库,只有30000多个单词,但足够我们小试牛刀,而现实中的文档集合可能会包含数十亿个单词。
下面的函数(**将数据集读取到由多条文本行组成的列表中**),其中每条文本行都是一个字符串。
为简单起见,我们在这里忽略了标点符号和字母大写。

```{.python .input}
#@tab all
Expand All @@ -55,7 +59,9 @@ print(lines[10])

## 词元化

以下 `tokenize` 函数将列表作为输入,列表中的每个元素是一个文本序列(如,一条文本行)。[**每个文本序列被拆分成一个标记列表**]*标记*(token)是文本的基本单位。最后返回一个标记列表,其中每个标记都是一个字符串(string)。
下面的 `tokenize` 函数将文本行列表作为输入,列表中的每个元素是一个文本序列(如一条文本行)。
[**每个文本序列又被拆分成一个标记列表**]*标记*(token)是文本的基本单位。
最后,返回一个由标记列表组成的列表,其中的每个标记都是一个字符串(string)。

```{.python .input}
#@tab all
Expand All @@ -73,14 +79,20 @@ for i in range(11):
print(tokens[i])
```

## 词汇
## 词汇表

标记的字符串类型不方便模型使用,因为模型需要的输入是数字。现在,让我们[**构建一个字典,通常也叫做*词表*(vocabulary),用来将字符串标记映射到从$0$开始的数字索引中**]。为此,我们首先统计训练集中所有文档中唯一的标记,称之为 *语料*(corpus),然后根据每个唯一标记的出现频率为其分配一个数字索引。很少出现的标记通常被移除,这可以降低复杂性。语料库中不存在或已删除的任何标记都将映射到一个特定的未知标记 “&lt;unk&gt;” 。我们可以选择增加一个列表,用于保存保留的标记,例如“&lt;pad&gt;”表示填充;“&lt;bos&gt;”表示序列的开始;“&lt;eos&gt;”表示序列的结束。
标记的类型是字符串,而模型需要的输入是数字,因此这种类型不方便模型使用。
现在,让我们[**构建一个字典,通常也叫做*词汇表*(vocabulary),用来将字符串类型的标记映射到从$0$开始的数字索引中**]
我们先将训练集中的所有文档合并在一起,对它们的唯一标记进行统计,得到的统计结果称之为*语料*(corpus)。
然后根据每个唯一标记的出现频率,为其分配一个数字索引。
很少出现的标记通常被移除,这可以降低复杂性。
另外,语料库中不存在或已删除的任何标记都将映射到一个特定的未知标记 “&lt;unk&gt;” 。
我们可以选择增加一个列表,用于保存那些被保留的标记,例如:填充标记(“&lt;pad&gt;”);序列开始标记(“&lt;bos&gt;”);序列结束标记(“&lt;eos&gt;”)。

```{.python .input}
#@tab all
class Vocab: #@save
"""文本词表"""
"""文本词汇表"""
def __init__(self, tokens=None, min_freq=0, reserved_tokens=None):
if tokens is None:
tokens = []
Expand Down Expand Up @@ -121,15 +133,15 @@ def count_corpus(tokens): #@save
return collections.Counter(tokens)
```

我们使用时光机器数据集作为语料库来[**构建词汇表**]。然后,我们打印前几个常见标记及其索引
我们首先使用时光机器数据集作为语料库来[**构建词汇表**],然后打印前几个高频标记及其索引

```{.python .input}
#@tab all
vocab = Vocab(tokens)
print(list(vocab.token_to_idx.items())[:10])
```

现在我们可以(**将每一行文本转换成一个数字索引列表**)。
现在,我们可以(**将每一条文本行转换成一个数字索引列表**)。

```{.python .input}
#@tab all
Expand All @@ -138,11 +150,12 @@ for i in [0, 10]:
print('indices:', vocab[tokens[i]])
```

## 把所有的东西放在一起
## 整合所有功能

使用上述函数,我们[**将所有内容打包到`load_corpus_time_machine`函数中**],该函数返回 `corpus`(标记索引列表)和 `vocab`(时光机器语料库的词汇表)。我们在这里所做的修改是:
- 1、我们将文本词元化为字符,而不是单词,以便简化后面章节中的训练;
- 2、`corpus`是单个列表,而不是使用标记列表构成的一个列表,因为时光机器数据集中的每个文本行不一定是一个句子或一个段落。
在使用上述函数时,我们[**将所有功能打包到`load_corpus_time_machine`函数中**],该函数返回 `corpus`(标记索引列表)和 `vocab`(时光机器语料库的词汇表)。
我们在这里所做的改变是:
1. 为了简化后面章节中的训练,我们使用字符(而不是单词)实现文本标记化;
2. 时光机器数据集中的每个文本行不一定是一个句子或一个段落,还可能是一个单词,因此返回的 `corpus` 仅处理为单个列表,而不是使用多个标记列表构成的一个列表。

```{.python .input}
#@tab all
Expand All @@ -151,7 +164,7 @@ def load_corpus_time_machine(max_tokens=-1): #@save
lines = read_time_machine()
tokens = tokenize(lines, 'char')
vocab = Vocab(tokens)
# 因为时光机器数据集中的每一个文本行,不一定是一个句子或一个段落
# 因为时光机器数据集中的每个文本行不一定是一个句子或一个段落
# 所以将所有文本行展平到一个列表中
corpus = [vocab[token] for line in tokens for token in line]
if max_tokens > 0:
Expand All @@ -169,7 +182,7 @@ len(corpus), len(vocab)

## 练习

1. 词元化是一个关键的预处理步骤。它因语言而异。尝试找到另外三种常用的文本标记方法
1. 标记化是一个关键的预处理步骤,它因语言而异,尝试找到另外三种常用的标记化文本的方法
1. 在本节的实验中,将文本标记为单词和更改 `Vocab` 实例的 `min_freq` 参数。这对词汇量有何影响?

:begin_tab:`mxnet`
Expand Down

0 comments on commit 1e99daf

Please sign in to comment.