---
type: slides
---

# 训练过程

Notes: 其它软件包可能会为你包装好某一种训练模型的方法，但spaCy可以让你完全
掌控整个训练过程。

---

# 训练过程的步骤

1. **循环**几次
2. **随机化**训练数据的顺序
3. **切分**数据成为数个批次
4. **更新**模型基于每一批数据
5. **保存**更新好的模型

Notes: 整个训练过程有一系列的步骤来训练和更新一个模型。

我们通常需要跑很多次这些步骤，重复数次使得模型可以有效从训练数据中学习。如果我们
需要重复训练10次那么程序就要循环10次。

为了防止模型在一个次优化的结果上卡住，我们需要在每一次循环中对数据顺序做随机化。
这是随机梯度下降的一个常见技巧。

接下来我们要将训练数据切分为数个批次，每个批次含有数个训练例子。这样我们可以做
小批次模型训练，可以增强梯度估计的可靠性。

最后我们要对每一批训练数据更新模型，重复这个过程，直到我们到达最后一个循环。

这样我们就可以把模型保存在一个路径下，然后在spaCy中使用它了。

---

# 回顾：如何训练模型

<img src="/training_zh.png" alt="训练过程图表" />

- **训练数据：**例子及其标注
- **文本：**输入文本，模型会在这些文本上做标签预测。
- **标签：**模型需要预测的标签
- **梯度：**权重如何微调

Notes: 回顾一下：

训练数据是我们想要用来更新模型的输入例子。

文本应该是一个句子、一个段落或者更长的文档。要得到最好的结果，这些文本应该
尽可能与模型将要在生产环境中预测的文本格式相似。

标签是我们想让模型预测的结果。标签可以是一个文本类别，或者是一个实体的跨度span及其
类别。

梯度决定了我们应该如何微调模型来减少当前的错误。我们通过比较模型预测标签和真实标签
来计算梯度。

---

# 循环示例

```python
TRAINING_DATA = [
    ("如何预定iPhone X", {"entities": [(4, 10, "GADGET")]})
    # 还有很多很多类似的数据...
]
```

```python
# 循环重复10次
for i in range(10):
    # 随机化训练数据的顺序
    random.shuffle(TRAINING_DATA)
    # 创建批次并遍历
    for batch in spacy.util.minibatch(TRAINING_DATA):
	# 在该批数据中分离文本和标注
        texts = [text for text, annotation in batch]
        annotations = [annotation for text, annotation in batch]
        # 更新模型
        nlp.update(texts, annotations)

# 保存模型
nlp.to_disk(path_to_model)
```

Notes: 我们来看一个例子。

假如我们有一个训练例子的列表，包含了文本和实体标注。

我们想要循环10次，所以我们在10的`range`上做遍历。

接下来，我们用`random`模块来随机化训练数据中的顺序。

然后，我们用spaCy的`minibatch`实用函数把列子切分为不同的批次。

对每一批数据，我们读取文本和标注，然后调用`nlp.update`方法来更新模型。

最后，我们调用`nlp.to_disk`方法将训练好的数据保存在指定路径中。

---

# 已有模型的更新

- 优化新数据上的预测能力
- 对改进已有类别的预测非常有用，比如对`"PERSON"`的抽取。
- 增加新类别也是可能的
- 要小心确定模型没有"忘掉"之前的预测目标

Notes: spaCy允许我们用更多的数据来更新一个现有的预训练模型，比如改进其在不同
文本上的预测能力。

当我们想改进模型在已有类别上的表现，如"person"或者"organization"，已有模型更新就
会特别有用。

我们也可以给模型更新增加一些新的预测类别。

我们要确保总是用新类别的例子 _加上_ 之前预测正确的其它类别上的例子来一起更新模型，
否则改进模型对新类别的预测表现可能会降低其对其它旧类别的预测能力。

---

# 从头设置一个新的流程

```python
# 从空的中文模型开始
nlp = spacy.blank("zh")
# 创建一个空的实体识别器加入到流程中
ner = nlp.create_pipe("ner")
nlp.add_pipe(ner)
# 添加一个新的标签
ner.add_label("GADGET")

# 开始训练
nlp.begin_training()
# 训练10个循环
for itn in range(10):
    random.shuffle(examples)
    # 将例子切分为一系列批次
    for batch in spacy.util.minibatch(examples, size=2):
        texts = [text for text, annotation in batch]
        annotations = [annotation for text, annotation in batch]
        # 更新模型
        nlp.update(texts, annotations)
```

Notes: 在这个例子中，我们先用`spacy.blank`方法创建一个空的中文模型。这个空模型
没有任何的模型组件，只有语种信息和分词规则。

我们然后创建一个新的实体识别器加入流程中。

使用`add_label`方法我们可以将新的字符串标签加入到模型中。

现在我们可以调用`nlp.begin_training`来用一些随机权重初始化模型。

要得到更好的准确率，我们需要在所有训练例子上面循环多次，每一次迭代需要随机化
数据的顺序。

每一次迭代中，我们用spaCy的`minibatch`实用函数将例子切分成为不同的批次。每一个例子
包含了一段文本和其标注数据。

最后，我们用这些文本和标注更新模型，然后继续迭代循环。

---

# 上手练习吧！

Notes: 是练习的时间了！现在我们已经学习了训练的循环迭代过程，我们用上一个练习
中创建的数据来更新一个模型。
