From d340e83ef6ba5668bae8591a1d0c8e4ad67b90df Mon Sep 17 00:00:00 2001 From: crownpku Date: Wed, 29 Dec 2021 09:17:52 +0000 Subject: [PATCH] upgrade zh course to spaCy v3 --- chapters/zh/chapter1.md | 55 +++-- chapters/zh/chapter2.md | 15 +- chapters/zh/chapter3.md | 28 +-- chapters/zh/chapter4.md | 207 +++++++++-------- .../chapter1_01_introduction-to-spacy.md | 18 +- .../slides/chapter1_02_statistical-models.md | 35 +-- .../slides/chapter1_03_rule-based-matching.md | 9 +- .../slides/chapter2_02_data-structures-2.md | 4 +- .../chapter2_03_word-vectors-similarity.md | 28 +-- .../zh/slides/chapter2_04_models-rules.md | 16 +- .../chapter3_01_processing-pipelines.md | 23 +- .../chapter3_02_custom-pipeline-components.md | 45 ++-- .../slides/chapter3_04_scaling-performance.md | 8 +- .../chapter4_01_training-updating-models.md | 140 ++++++++++-- .../zh/slides/chapter4_02_running-training.md | 210 ++++++++++++++++++ .../chapter4_03_training-best-practices.md | 23 +- chapters/zh/slides/chapter4_04_wrapping-up.md | 6 +- exercises/zh/config_gadget.cfg | 148 ++++++++++++ exercises/zh/dev_gadget.spacy | Bin 0 -> 118 bytes exercises/zh/exc_01_02_01.py | 6 +- exercises/zh/exc_01_02_02.py | 6 +- exercises/zh/exc_01_02_03.py | 6 +- exercises/zh/exc_01_02_04.py | 6 +- exercises/zh/exc_01_03_01.py | 4 +- exercises/zh/exc_01_03_02.py | 4 +- exercises/zh/exc_01_04.py | 4 +- exercises/zh/exc_01_07.py | 2 +- exercises/zh/exc_01_11.py | 2 +- exercises/zh/exc_01_12_01.py | 2 +- exercises/zh/exc_01_12_02.py | 2 +- exercises/zh/exc_01_12_03.py | 2 +- exercises/zh/exc_02_05_01.py | 4 +- exercises/zh/exc_02_05_02.py | 4 +- exercises/zh/exc_02_05_03.py | 4 +- exercises/zh/exc_02_06.py | 4 +- exercises/zh/exc_02_09.py | 2 +- exercises/zh/exc_02_13.py | 4 +- exercises/zh/exc_02_14.py | 6 +- exercises/zh/exc_02_15.py | 2 +- exercises/zh/exc_03_03.py | 2 +- exercises/zh/exc_03_06.py | 8 +- exercises/zh/exc_03_07.py | 6 +- exercises/zh/exc_03_09_01.py | 4 +- exercises/zh/exc_03_09_02.py | 4 +- exercises/zh/exc_03_10_01.py | 4 +- exercises/zh/exc_03_10_02.py | 4 +- exercises/zh/exc_03_12.py | 10 +- exercises/zh/exc_03_14_03.py | 4 +- exercises/zh/exc_03_15.py | 4 +- exercises/zh/exc_03_16_02.py | 2 +- exercises/zh/exc_04_03.py | 17 +- exercises/zh/exc_04_04.py | 30 ++- exercises/zh/exc_04_06.py | 11 - exercises/zh/exc_04_07.py | 29 --- exercises/zh/exc_04_07_01.sh | 1 + exercises/zh/exc_04_07_02.sh | 1 + exercises/zh/exc_04_08.sh | 1 + exercises/zh/exc_04_10.py | 18 -- exercises/zh/exc_04_11.py | 16 ++ exercises/zh/exc_04_11_01.py | 12 - exercises/zh/exc_04_11_02.py | 12 - exercises/zh/exc_04_12_01.py | 18 ++ exercises/zh/exc_04_12_02.py | 18 ++ exercises/zh/solution_01_02_01.py | 8 +- exercises/zh/solution_01_02_02.py | 8 +- exercises/zh/solution_01_02_03.py | 6 +- exercises/zh/solution_01_02_04.py | 6 +- exercises/zh/solution_01_03_01.py | 6 +- exercises/zh/solution_01_03_02.py | 6 +- exercises/zh/solution_01_04.py | 4 +- exercises/zh/solution_01_07.py | 2 +- exercises/zh/solution_01_11.py | 2 +- exercises/zh/solution_01_12_01.py | 2 +- exercises/zh/solution_01_12_02.py | 2 +- exercises/zh/solution_01_12_03.py | 2 +- exercises/zh/solution_02_05_01.py | 4 +- exercises/zh/solution_02_05_02.py | 4 +- exercises/zh/solution_02_05_03.py | 4 +- exercises/zh/solution_02_06.py | 4 +- exercises/zh/solution_02_09.py | 2 +- exercises/zh/solution_02_13.py | 4 +- exercises/zh/solution_02_14.py | 6 +- exercises/zh/solution_02_15.py | 2 +- exercises/zh/solution_03_03.py | 2 +- exercises/zh/solution_03_06.py | 8 +- exercises/zh/solution_03_07.py | 8 +- exercises/zh/solution_03_09_01.py | 4 +- exercises/zh/solution_03_09_02.py | 4 +- exercises/zh/solution_03_10_01.py | 4 +- exercises/zh/solution_03_10_02.py | 4 +- exercises/zh/solution_03_12.py | 12 +- exercises/zh/solution_03_14_03.py | 4 +- exercises/zh/solution_03_15.py | 4 +- exercises/zh/solution_03_16_02.py | 4 +- exercises/zh/solution_04_03.py | 16 +- exercises/zh/solution_04_04.py | 28 +-- exercises/zh/solution_04_06.py | 11 - exercises/zh/solution_04_07.py | 29 --- exercises/zh/solution_04_07_01.sh | 1 + exercises/zh/solution_04_07_02.sh | 1 + exercises/zh/solution_04_08.sh | 1 + exercises/zh/solution_04_10.py | 18 -- exercises/zh/solution_04_11.py | 16 ++ exercises/zh/solution_04_11_01.py | 12 - exercises/zh/solution_04_11_02.py | 15 -- exercises/zh/solution_04_12_01.py | 18 ++ exercises/zh/solution_04_12_02.py | 18 ++ exercises/zh/test_01_03_01.py | 1 + exercises/zh/test_01_03_02.py | 1 + exercises/zh/test_01_04.py | 3 + exercises/zh/test_01_07.py | 4 +- exercises/zh/test_02_06.py | 2 +- exercises/zh/test_02_07.py | 2 +- exercises/zh/test_02_09.py | 2 +- exercises/zh/test_02_10_01.py | 2 +- exercises/zh/test_02_10_02.py | 2 +- exercises/zh/test_02_10_03.py | 2 +- exercises/zh/test_03_03.py | 3 +- exercises/zh/test_03_06.py | 8 +- exercises/zh/test_03_07.py | 2 +- exercises/zh/test_03_16_02.py | 6 +- exercises/zh/test_04_04.py | 42 +--- exercises/zh/test_04_06.py | 14 -- exercises/zh/test_04_07.py | 18 -- exercises/zh/test_04_10.py | 27 --- exercises/zh/test_04_11.py | 20 ++ exercises/zh/test_04_11_01.py | 17 -- exercises/zh/test_04_11_02.py | 20 -- exercises/zh/test_04_12_01.py | 18 ++ exercises/zh/test_04_12_02.py | 15 ++ exercises/zh/train_gadget.spacy | Bin 0 -> 118 bytes 131 files changed, 1132 insertions(+), 755 deletions(-) create mode 100644 chapters/zh/slides/chapter4_02_running-training.md create mode 100644 exercises/zh/config_gadget.cfg create mode 100644 exercises/zh/dev_gadget.spacy delete mode 100644 exercises/zh/exc_04_06.py delete mode 100644 exercises/zh/exc_04_07.py create mode 100644 exercises/zh/exc_04_07_01.sh create mode 100644 exercises/zh/exc_04_07_02.sh create mode 100644 exercises/zh/exc_04_08.sh delete mode 100644 exercises/zh/exc_04_10.py create mode 100644 exercises/zh/exc_04_11.py delete mode 100644 exercises/zh/exc_04_11_01.py delete mode 100644 exercises/zh/exc_04_11_02.py create mode 100644 exercises/zh/exc_04_12_01.py create mode 100644 exercises/zh/exc_04_12_02.py delete mode 100644 exercises/zh/solution_04_06.py delete mode 100644 exercises/zh/solution_04_07.py create mode 100644 exercises/zh/solution_04_07_01.sh create mode 100644 exercises/zh/solution_04_07_02.sh create mode 100644 exercises/zh/solution_04_08.sh delete mode 100644 exercises/zh/solution_04_10.py create mode 100644 exercises/zh/solution_04_11.py delete mode 100644 exercises/zh/solution_04_11_01.py delete mode 100644 exercises/zh/solution_04_11_02.py create mode 100644 exercises/zh/solution_04_12_01.py create mode 100644 exercises/zh/solution_04_12_02.py delete mode 100644 exercises/zh/test_04_06.py delete mode 100644 exercises/zh/test_04_07.py delete mode 100644 exercises/zh/test_04_10.py create mode 100644 exercises/zh/test_04_11.py delete mode 100644 exercises/zh/test_04_11_01.py delete mode 100644 exercises/zh/test_04_11_02.py create mode 100644 exercises/zh/test_04_12_01.py create mode 100644 exercises/zh/test_04_12_02.py create mode 100644 exercises/zh/train_gadget.spacy diff --git a/chapters/zh/chapter1.md b/chapters/zh/chapter1.md index 88d310b7e..7b852805b 100644 --- a/chapters/zh/chapter1.md +++ b/chapters/zh/chapter1.md @@ -2,7 +2,7 @@ title: '第一章: 词语、短语、名字和概念的检索' description: "本章介绍spaCy文本处理的基础知识。 - 你将会学习到数据结构、构造统计模型以及如何用它们来抽取文本中的语言学特征。" + 你将会学习到数据结构、模型训练以及如何用它们来抽取文本中的语言学特征。" prev: null next: /chapter2 type: chapter @@ -18,25 +18,25 @@ id: 1 -让我们来一起开始使用spaCy吧!这个例子中我们会尝试用到55+个[spaCy支持的语言](https://spacy.io/usage/models#languages)中的一些。 +让我们来一起开始使用spaCy吧!这个例子中我们会尝试用到60+个[spaCy支持的语言](https://spacy.io/usage/models#languages)中的一些。 ### 第一部分: 中文 -- 从`spacy.lang.zh`中导入`Chinese`类然后创建`nlp`对象。 +- 使用`spacy.blank`来创建一个空白的中文(`"zh"`)的`nlp`对象。 - 创建`doc`并打印其中的文本。 ### 第二部分: 英语 -- 从`spacy.lang.en`中导入`English`类然后创建`nlp`对象。 +- 使用`spacy.blank`来创建一个空白的英语(`"en"`)的`nlp`对象。 - 创建`doc`并打印其中的文本。 ### 第三部分: 德语 -- 从`spacy.lang.de`中导入`German`类然后创建`nlp`对象。 +- 使用`spacy.blank`来创建一个空白的德语(`"de"`)的`nlp`对象。 - 创建`doc`并打印其中的文本。 @@ -50,7 +50,7 @@ id: 1 ### 第一步 -- 导入`Chinese`的语言类然后创建`nlp`的对象 +- 使用`spacy.blank`来创建一个中文的`nlp`对象。 - 处理文本,然后在`doc`变量中创建一个`Doc`对象的实例。 - 选取`Doc`中的第一个词符并打印出它的`text`。 @@ -64,7 +64,7 @@ id: 1 ### 第二步 -- 导入`Chinese`的语言类然后创建`nlp`的对象 +- 使用`spacy.blank`来创建一个中文的`nlp`对象。 - 处理文本,然后在`doc`变量中创建一个`Doc`对象的实例。 - 从`Doc`中截取其部分的词符"老虎"和"老虎和狮子"。 @@ -96,39 +96,38 @@ id: 1 - + - + -spaCy可以读入的模型包中**不包含**以下哪项? +spaCy可以读入的流水线包中**不包含**以下哪项? - + -所以模型都包含一个`meta.json`,定义了启用的语言、调用的流程组件名字以及一些其它的信息, -比如模型名字、版本、许可证书、数据源、作者以及准确率的图标(如果有的话)。 +所有流程都包含一个`config.cfg`,定义了初始化的语言、调用的流程组件以及该流程的训练和配置的细节信息。 -用来做语义标注如词性标注、依存分析或者命名实体识别的模型,包含了二进制的权重 +用来做语义标注如词性标注、依存分析或者命名实体识别的流程,包含了二进制的权重。 - + -统计模型可以在训练数据的基础上泛化。 -训练好之后模型就可以直接用二进制的权重做预测。 -这也是为什么我们不需要把模型当初训练时使用的训练数据也一并包含进来。 +训练好的流程可以在训练数据的基础上泛化。 +训练好之后流程就可以直接用二进制的权重做预测。 +这也是为什么我们不需要把流程当初训练时使用的训练数据也一并包含进来。 - + -模型包包含了`string.json`,该文件存储了模型的词典及其对应的哈希值。 +流程包含有`string.json`,该文件存储了流程的词典及其对应的哈希值。 这样spaCy在需要的时候可以直接调用哈希值来搜索到对应的词典字符串。 @@ -136,19 +135,19 @@ spaCy可以读入的模型包中**不包含**以下哪项? - + -这门课中我们需要使用的模型都已经预装好了。 -如果你想了解更多关于spaCy的统计模型以及如何在自己的电脑上安装这些模型, +这门课中我们需要使用的流程都已经预装好了。 +如果你想了解更多关于spaCy的预训练流程以及如何在自己的电脑上安装这些模型, 可以参考[这份文档](https://spacy.io/usage/models)。 -- 使用`spacy.load`来调用一个比较小的中文模型`"zh_core_web_sm"`。 +- 使用`spacy.load`来调用一个比较小的中文流程`"zh_core_web_sm"`。 - 处理文档并打印出文档中的文字。 -要调用一个模型,我们可以调用`spacy.load`加上模型对应名字的字符串。 -模型的名字会因为语言和训练数据的不同而有所变化,所以请确保使用了正确的名字。 +要调用一个流程,我们可以调用`spacy.load`加上流程对应名字的字符串。 +流程的名字会因为语言和训练数据的不同而有所变化,所以请确保使用了正确的名字。 @@ -156,7 +155,7 @@ spaCy可以读入的模型包中**不包含**以下哪项? -我们现在来试试spaCy的一个已经预训练好的模型包看看它在实际预测中的表现。 +我们现在来试试spaCy的一个已经训练好的流程包看看它在实际预测中的表现。 你完全可以在自己设定的文本中做测试! 如果你对某一个标注或者标签不清楚,可以在代码中调用`spacy.explain`。 举个例子,`spacy.explain("PROPN")`或者`spacy.explain("GPE")`. @@ -190,7 +189,7 @@ spaCy可以读入的模型包中**不包含**以下哪项? -模型是基于统计学的但并不是_永远_正确。模型的预测是否正确取决于训练数据和我们要处理的文本。 +模型是基于统计学的但并不是 _永远_ 正确。模型的预测是否正确取决于训练数据和我们要处理的文本。 我们来看一个例子: - 使用`nlp`对象来处理文本 diff --git a/chapters/zh/chapter2.md b/chapters/zh/chapter2.md index f30ca8cd4..7429cc3ce 100644 --- a/chapters/zh/chapter2.md +++ b/chapters/zh/chapter2.md @@ -51,12 +51,11 @@ id: 2 为什么这段代码抛出了错误信息? ```python -from spacy.lang.en import English -from spacy.lang.de import German +import spacy # 创建一个英文和德文的nlp实例 -nlp = English() -nlp_de = German() +nlp = spacy.blank("en") +nlp_de = spacy.blank("de") # 获取字符串'Bowie'的ID bowie_id = nlp.vocab.strings["Bowie"] @@ -244,15 +243,15 @@ for index, pos in enumerate(pos_tags): -在这个练习中我们要用到一个更大的[中文模型](https://spacy.io/models/zh), +在这个练习中我们要用到一个更大的[中文流程](https://spacy.io/models/zh), 该模型有大概两万个词向量。这里模型已经提前安装好了。 -- 读取中等大小的`"zh_core_web_md"`模型,该模型含有词向量 +- 读取中等大小的`"zh_core_web_md"`流程,该流程含有词向量 - 用`token.vector`属性来打印`"老虎"`的向量。 -- 用`spacy.load`调用模型名字来读取统计模型。 +- 用`spacy.load`调用流程名字来读取训练好的流程。 - 可以直接用索引来读取doc中的一个词符token,比如`doc[4]`。 @@ -293,7 +292,7 @@ for index, pos in enumerate(pos_tags): - + diff --git a/chapters/zh/chapter3.md b/chapters/zh/chapter3.md index af096ebca..d0cd3fcea 100644 --- a/chapters/zh/chapter3.md +++ b/chapters/zh/chapter3.md @@ -49,8 +49,8 @@ spaCy的所有运算都在本机,并不需要连接任何远端服务器。 -当我们调用`spacy.load()`来读取模型时,spaCy会初始化相应语言,加入流程并读取 -模型的二进制权重。当我们在文本上调取`nlp`之前模型就已经被读取了。 +当我们调用`spacy.load()`来读取流程时,spaCy会初始化相应语言,加入流程并读取 +模型的二进制权重。当我们在文本上调取`nlp`之前流程就已经被读取了。 @@ -58,9 +58,9 @@ spaCy的所有运算都在本机,并不需要连接任何远端服务器。 -让我们一起检查一下中文小模型的流程。 +让我们一起检查一下小规模的中文流程。 -- 读取`zh_core_web_sm`模型并创建`nlp`实例。 +- 读取`zh_core_web_sm`流程并创建`nlp`实例。 - 用`nlp.pipe_names`来打印流程组件的名字。 - 用`nlp.pipeline`来打印`(name, component)`元组的完整流程。 @@ -84,7 +84,7 @@ spaCy的所有运算都在本机,并不需要连接任何远端服务器。 下面这些问题中那些可以用定制化组件来解决?请选择所有正确的答案。 -1. 更新预训练模型来改进其性能 +1. 更新训练好的流程来改进其性能 2. 基于词符及其属性来计算我们自己定义的变量 3. 基于比如一个词典来增加新的命名实体 4. 编写对某种新语种的支持 @@ -142,13 +142,13 @@ spaCy的所有运算都在本机,并不需要连接任何远端服务器。 你能完成这段代码吗? - 用`doc`长度来完成组件函数。 -- 加入`length_component`到现有的流程中,作为其**第一个**组件。 +- 加入`"length_component"`到现有的流程中,作为其**第一个**组件。 - 试用这个新的流程,用`nlp`实例来处理一段任意的文本,比如"这是一个句子。"。 - 我们可以调用Python原生的`len()`方法来获取`Doc`实例的长度。 -- 使用`nlp.add_pipe`方法把组件加入到流程中。记住要把`first`关键词参数设置为 +- 使用`nlp.add_pipe`方法把组件加入到流程中。记住要使用组件的字符串名字并且把`first`关键词参数设置为 `True`来保证这个组件是被添加到其它所有组件之前的。 - 调用`nlp`实例来处理一段本文。 @@ -172,6 +172,7 @@ spaCy的所有运算都在本机,并不需要连接任何远端服务器。 - 注意所有的匹配结果是在一个`(match_id, start, end)`元组的列表中。 - `Span`类有四个参数:产生它的原始`doc`、起始索引、终止索引和标签。 - 在`nlp.add_pipe`上使用`after`关键词参数来把组件添加到另一个组件后面。 + 记住要使用组件的字符串名字。 @@ -246,7 +247,7 @@ spaCy的所有运算都在本机,并不需要连接任何远端服务器。 -在这个练习中,我们要结合定制化属性扩展和模型的预测结果,创建一个属性取值函数,当 +在这个练习中,我们要结合定制化属性扩展和统计预测结果,创建一个属性取值函数,当 span为一个人、组织或者位置时返回其维基百科的查询URL。 - 完成`get_wikipedia_url`这个取值函数,使其只有在span的标签在标签列表中时 @@ -272,7 +273,7 @@ span为一个人、组织或者位置时返回其维基百科的查询URL。 `matcher`变量中已经有一个匹配所有国家的短语匹配器。`CAPITALS`变量中则有一个把国家名 映射到其首都城市的字典。 -- 完成`countries_component`,为所有匹配结果创建一个含有标签`"GPE"`(地理政治实体) +- 完成`countries_component_function`,为所有匹配结果创建一个含有标签`"GPE"`(地理政治实体) 的`Span`。 - 把组件加入到流程中。 - 使用取值函数`get_capital`注册Span的扩展属性`"capital"`。 @@ -354,7 +355,7 @@ span为一个人、组织或者位置时返回其维基百科的查询URL。 -在这个练习中,我们使用`nlp.make_doc`和`nlp.disable_pipes`方法只运行我们选择的 +在这个练习中,我们使用`nlp.make_doc`和`nlp.select_pipes`方法只运行我们选择的 组件来处理文本。 ### 第一部分 @@ -369,13 +370,14 @@ span为一个人、组织或者位置时返回其维基百科的查询URL。 ### 第二部分 -- 用`nlp.disable_pipes`方法关闭词性标注和依存关系分析的组件。 +- 用`nlp.select_pipes`方法关闭词性标注(tagger)和词性还原(lemmatizer)的组件。 - 处理文本,将所有`doc`中的结果实体打印出来。 -`nlp.disable_pipes`方法有一些可变数目的参数:那些想要关闭的流程组件的字符串名。 -比如`nlp.disable_pipes("ner")`就会把命名实体识别器关掉。 +`nlp.select_pipes`方法可以接收关键字参数`enbale`或者`disable`, +读入一个组件名的列表来启用或者关闭相应的流程组件。 +比如`nlp.select_pipes(disable="ner")`就会把命名实体识别器关掉。 diff --git a/chapters/zh/chapter4.md b/chapters/zh/chapter4.md index 286066a75..d11231e3c 100644 --- a/chapters/zh/chapter4.md +++ b/chapters/zh/chapter4.md @@ -1,6 +1,6 @@ --- title: '第四章:训练神经网络模型' -description: '本章中,我们要学习更新spaCy的统计模型使其能够为特定的使用场景做出定制化。一个例子是我们想要在网络上的评论中抽取一种新的实体。我们将会学到如何从头编码自己的模型训练流程,了解模型训练的基本工作原理,以及一些技巧使得我们自己的定制化自然语言处理项目能够更加成功。' +description: '本章中,我们要学习更新spaCy的统计模型使其能够为特定的使用场景做出定制化。一个例子是我们想要在网络上的评论中抽取一种新的实体。我们将会学到如何从头训练自己的模型,了解模型训练的基本工作原理,以及一些技巧使得我们自己的定制化自然语言处理项目能够更加成功。' prev: /chapter3 next: null type: chapter @@ -14,31 +14,30 @@ id: 4 - + -spaCy已经预装了一系列预训练好的模型来抽取各种语言学标签,然后我们几乎 _总是_ -想要用更多的新例子来优化模型。我们可以用更多的标注数据来训练模型达到这个目的。 - -模型训练不能达到哪个目标? +要训练一个模型,我们通常需要训练数据 _和_ 用来评估模型的开发数据。这个评估数据是用来做什么的? - + -如果预训练模型在特定数据上面效果不好,用特定的例子再去训练模型会是个好方法。 +训练过程中,模型只能由训练数据来进行更新。开发数据只是用来将模型在未见过的数据上预测的结果与真实标注做对比, +来评估模型表现的。准确度分数由此计算而来。 - + -我们可以通过训练来教会模型新的标签、实体类别或是其它分类目标。 +开发数据只是用来将模型在未见过的数据上预测的结果与真实标注做对比, +来评估模型表现的。准确度分数由此计算而来。 - + -spaCy的组件都是用来做文本标注的监督学习模型,这意味着这些模型只能学习标注过的例子, -而不能从原始文本中估计出新的标签。 +开发数据只是用来将模型在未见过的数据上预测的结果与真实标注做对比, +来评估模型表现的。准确度分数由此计算而来。 @@ -67,81 +66,118 @@ spaCy的基于规则的`Matcher`可以很好地被用来快速创建一些命名 -我们现在用上一个练习中创建的匹配模板来引出一系列的训练例子。`TEXTS`变量中存有句子的 -列表。 +在为我们的语料创建数据之后,我们需要将其存放在一个后缀为`.spacy`的文件中。可以参见上一个例子中的代码。 -- 使用`nlp.pipe`对每一个文本创建一个doc。 -- 在`doc`上做匹配创建出一个匹配结果的span列表。 -- 获取匹配结果span的`(start character, end character, label)`元组。 -- 把每个例子的格式写为一个文本和一个词典的元组,把`"entities"`映射到实体元组上。 -- 把例子附加到`TRAINING_DATA`中,检查打印的数据。 +- 使用`docs`的列表初始化`DocBin`。 +- 将`DocBin`存储到一个名为`train.spacy`的文件中。 -- 要得到匹配结果,在`doc`上面调用`matcher`。 -- 返回的匹配结果在`(match_id, start, end)`元组里。 -- 我们可以用`TRAINING_DATA.append()`把一个例子添加到训练示例列表中。 +- 我们可以把一个含有多个文档的列表传入关键字参数`docs`中来初始化`DocBin`。 +- `DocBin`的`to_disk`方法需要一个参数:二进制文件存储的路径。 + 要确保文件的后缀名是`.spacy`. - + - + - + -这个练习中,我们来搭建一个spaCy的流程,训练实体识别器识别文本中的`"GADGET"`实体, -比如"iPhone X"。 +`config.cfg`文件是使用spaCy训练流程的“唯一真理来源”。下列关于配置的说法哪个是 **错误** 的? -- 创建一个空的`"en"`模型,我们可以用`spacy.blank`方法。 -- 用`nlp.create_pipe`创建一个新的实体识别器,加入到流程中。 -- 我们可以在流程组件上调用`add_label`方法, 把新的标签`"GADGET"`加入到实体识别器中。 + - + -- 要创建一个空的实体识别器,我们可以调用`nlp.create_pipe`,返回给名字是`"ner"`的变量。 -- 要把组件加入到流程中,我们可以用`nlp.add_pipe`方法。 -- `add_label`方法是实体识别器流程组件的一个方法,这个组件我们已经存储在了变量`ner`里面。 - 要给它增加一个标签,我们可以调用`ner.add_label`加上标签的字符串名, - 比如`ner.add_label("SOME_LABEL")`。 +配置文件含有训练流程的所有设定,包括超参数。 - + + + + +配置文件含有 _所有_ 设定,也没有隐藏的默认值,所以可以帮助我们的训练实验更加容易复现。 +其他人可以轻松通过相同设定重新跑通我们的实验。 + + + + + +配置文件含有和训练与流程相关的所有设定,但并不能为流程打包。 +要创建可安装的Python包,我们可以使用`spacy package`命令。 + + + + + +配置文件中的'[components]'包含了所有流程组件和各自的设定,包括所使用的模型实现。 + + + + - + -我们现在来从头写一个简单的训练过程。 +[`init config`命令](https://spacy.io/api/cli#init-config) 自动生成一个使用默认设定的训练配置文件。 +我们想要训练一个命名实体识别器,所以我们要生成一个含有一个流程组件`ner`的配置文件。 +因为我们在本课程中是在Jupyter环境中运行命令,所以加上前缀`!`。 +如果是在本地终端中运行则不需要加这个前缀。 -我们在上一个练习中创建的流程存储在`nlp`实例中,里面已经含有了实体识别器和我们 -新增的标签`"GADGET"`。 +### 第一部分 + +- 使用spaCy的`init config`命令来自动生成一个中文流程的配置。 +- 将配置保存到文件`config.cfg`中。 +- 使用`--pipeline`参数指明一个流程组件`ner`。 -我们之前创建的一小组标注例子存储在`TRAINING_DATA`中。想要看这些例子的话我们可以在 -代码中把它们打印出来。 + -- 调用`nlp.begin_training`,创建一个有10个循环的训练过程并把训练数据的顺序随机化。 -- 使用`spacy.util.minibatch`创建几批训练数据,然后在每一批数据上作遍历。 -- 把每一批数据里的`(text, annotations)`元组转变为`texts`和`annotations`的列表。 -- 对每一批数据,调用`nlp.update`方法用这些文本text和标注annotation去更新模型。 +- `--lang`参数定义了语言类,比如`zh`指中文。 - + + +### 第二部分 + +我们来看看spaCy刚刚生成的配置文件! +我们可以运行下面的命令将配置打印到屏幕上。 + + + + -- 调用`nlp.begin_training()`方法来重置模型参数并开始训练。 -- 在训练例子的列表上调用`spacy.util.minibatch`函数来将训练数据分为一系列批次。 + + +让我们用前面练习中生成的配置文件和训练语料来训练一个命名实体识别器! + +使用[`train`](https://spacy.io/api/cli#train) 命令来调取训练配置文件来训练一个模型。 +一个名为`config_gadget.cfg`的文件已经在`exercise/zh`中了, +同时还有一个名为`train_gadget.spacy`的文件包含了一些训练数据,`dec_gadget.spacy`文件包含了测试数据。 +因为我们在本课程中是在Jupyter环境中运行命令,所以加上前缀`!`。 +如果是在本地终端中运行则不需要加这个前缀。 + +- 在文件`exercises/zh/config_gadget.cfg`上面运行`train`命令。 +- 将训练好的流程保存在`output`文件夹中。 +- 传入路径`exercises/zh/train_gadget.spacy` 和 `exercises/zh/dev_gadget.spacy` + + + +- 命令`spacy train`的第一个参数是配置文件的路径。 - + 让我们来看看模型在未出现过的新数据上表现如何!为了节省时间,我们已经在一些文本上面 -训练好了一个带有标签`"GADGET"`的模型。这里是一些结果: +训练好了一个带有标签`"GADGET"`的流程。这里是一些结果: | 文本 | 实体 | @@ -188,37 +224,30 @@ spaCy的基于规则的`Matcher`可以很好地被用来快速创建一些命名 - + - + 这是一段摘抄,来自于一个训练集试图在旅行者的评论中标注实体类型 `TOURIST_DESTINATION`(游客目的地)。 ```python -TRAINING_DATA = [ - ( - "我去年去了西安,那里的城墙很壮观!", - {"entities": [(4, 5, "TOURIST_DESTINATION")]}, - ), - ( - "人一辈子一定要去一趟爸黎,但那里的埃菲尔铁塔有点无趣。", - {"entities": [(5, 6, "TOURIST_DESTINATION")]}, - ), - ( - "深圳也有个巴黎的埃菲尔铁塔,哈哈哈", - {"entities": []} - ), - ( - "北京很适合暑假去:长城、故宫,还有各种好吃的小吃!", - {"entities": [(0, 1, "TOURIST_DESTINATION")]}, - ), -] +doc1 = nlp("我去年去了西安,那里的城墙很壮观!") +doc1.ents = [Span(doc1, 4, 5, label="TOURIST_DESTINATION")] + +doc2 = nlp("人一辈子一定要去一趟爸黎,但那里的埃菲尔铁塔有点无聊。") +doc2.ents = [Span(doc2, 5, 6, label="TOURIST_DESTINATION")] + +doc3 = nlp("深圳也有个巴黎的埃菲尔铁塔,哈哈哈") +doc3.ents = [] + +doc4 = nlp("北京很适合暑假去:长城、故宫,还有各种好吃的小吃!") +doc4.ents = [Span(doc4, 0, 1, label="TOURIST_DESTINATION")] ``` ### 第一部分 @@ -253,20 +282,22 @@ TRAINING_DATA = [ ### 第二部分 -- 重写`TRAINING_DATA`使其标签为`"GPE"`(城市、州省、国家)而非`"TOURIST_DESTINATION"`。 -- 别忘了添加那些数据中本来未被标注为`"GPE"`的实体的元组。 +- 重写`doc.ents`使其跨度span的标签为`"GPE"`(城市、州省、国家)而非`"TOURIST_DESTINATION"`。 +- 别忘了添加那些数据中本来未被标注为`"GPE"`的实体的跨度span。 - + - 对于那些已经标注过的span,我们只需要将其标签名从`"TOURIST_DESTINATION"`换为`"GPE"`。 -- 有一段文本包含了城市和州省实体但还没有被标注。要加入实体的跨度span,我们需要数一数字符 - 来找出实体的span是从哪里开始和从哪里结束。然后把`(start, end, label)`元组加到实体中。 +- 有一段文本包含了城市和州省实体但还没有被标注。要加入实体的跨度span,我们需要数一数词符token + 来找出实体的span是从哪里开始和从哪里结束。注意最后一个词符的索引是 _不包含的_! + 然后把新的`Span`加入到`doc.ents`中。 +- 注意分词!如果不确定可以把`Doc`中的词符打印出来。 - + 这里是某个数据集的一个样品,我们创建它来训练一个新的实体种类`"WEBSITE"`。 原始的数据集包含了几千个句子。这个练习中我们要手动做标注。实际工作中我们 @@ -276,14 +307,12 @@ TRAINING_DATA = [ ### 第一部分 -- 完成数据中所有`"WEBSITE"`实体的位置参数。如果不想手动数字符数目的话我们可以随时调 -用`len()`。 +- 完成数据中所有`"WEBSITE"`实体的位置参数。 - + -- 实体span的起始和终止位置就是其对应字符在文本中的位置。比如一个从位置5开始的实体, - 其起始位置就是`5`。记住终止位置是 _不包含_ 实体的,所以`10`意味着一直到字符10 - _之前_。 +- 要注意终止词符的span是不包含的。 + 所以如果一个实体从位置2开始而从位置3结束,那么start就是`2`,而end是`4`. @@ -320,15 +349,17 @@ TRAINING_DATA = [ - 更新训练数据,加入对`"PERSON"`实体"李子柒"和"马云"的标注。 - + -- 要添加更多的实体,给列表后面加入另一个`(start, end, label)`元组就行。 +- 要添加更多的实体,给`doc.ents`增加一个`Span`就行。 +- 要注意终止词符的span是不包含的。 + 所以如果一个实体从位置2开始而从位置3结束,那么start就是`2`,而end是`4`. - + diff --git a/chapters/zh/slides/chapter1_01_introduction-to-spacy.md b/chapters/zh/slides/chapter1_01_introduction-to-spacy.md index 416d3eff3..e617c7e1d 100644 --- a/chapters/zh/slides/chapter1_01_introduction-to-spacy.md +++ b/chapters/zh/slides/chapter1_01_introduction-to-spacy.md @@ -14,11 +14,11 @@ spaCy是一个先进且广受欢迎的自然语言处理Python库。 # nlp对象 ```python -# 导入中文的语言类 -from spacy.lang.zh import Chinese +# 导入spaCy +import spacy -# 创建nlp对象 -nlp = Chinese() +# 创建一个空白的中文nlp对象 +nlp = spacy.blank("zh") ``` - 包含了自然语言处理的流程 @@ -26,13 +26,13 @@ nlp = Chinese() Notes: spaCy的核心就是包含了自然语言处理流程的对象。我们通常把这个变量叫做`nlp`。 -举个例子,要创造一个中文的`nlp`的对象,我们要从`spacy.lang.zh`中导入`Chinese`这个语言类 -并创建一个实例。我们可以像一个函数一样使用nlp对象来分析文本。 +举个例子,要创造一个中文的`nlp`的对象,我们要导入`spacy`然后使用`spacy.blank`方法来创建一个空的中文流程。 +我们可以像一个函数一样使用`nlp`对象来分析文本。 这个nlp对象包含了流程中的所有不同组件。 它还包含了一些特定语言相关的规则,用来将文本分词成为单个的词汇和标点符号。 -spaCy支持多种不同语言,包含在`spacy.lang`中。 +spaCy支持多种不同语言。 --- @@ -64,7 +64,7 @@ Doc用起来就像一个正常的Python序列,我们可以遍历它的词符 # Token对象 -一个含有四个词符的Doc实例 +一个含有三个词符的Doc实例 ```python doc = nlp("这是一个句子。") @@ -92,7 +92,7 @@ Notes: `Token`实例代表了一个文本中的词符,比如一个词或者一 # Span对象 -一个含有四个词符且其中三个被包装成一个跨度的Doc实例 +一个含有三个词符且其中两个被包装成一个跨度的Doc实例 ```python doc = nlp("这是一个句子。") diff --git a/chapters/zh/slides/chapter1_02_statistical-models.md b/chapters/zh/slides/chapter1_02_statistical-models.md index 313bf86b9..99bc796a1 100644 --- a/chapters/zh/slides/chapter1_02_statistical-models.md +++ b/chapters/zh/slides/chapter1_02_statistical-models.md @@ -2,17 +2,17 @@ type: slides --- -# 统计模型 +# 训练流程 Notes: 让我们来看看`nlp`对象更多强大的功能。 -这门课中我们会学习spaCy的统计模型。 +这门课中我们会学习spaCy的训练流程。 --- -# 什么是统计模型? +# 什么是训练流程? -- 使spaCy可以_从语境中_抽取到语言学属性 +- 使spaCy可以_从语境中_抽取到语言学属性的模型 - 词性标注 - 依存关系解析 - 命名实体识别 @@ -22,15 +22,15 @@ Notes: 让我们来看看`nlp`对象更多强大的功能。 Notes: 很多非常有趣的分析是基于语境的: 比如一个词是否是动词,或者文本的一段跨度是否是人名。 -统计模型让spaCy可以通过语境来做抽取。抽取结果通常包括了词性标注、依存关系和命名实体。 +训练好的流程组件所包含的统计模型让spaCy可以通过语境来做抽取。抽取结果通常包括了词性标注、依存关系和命名实体。 -模型是由大量标注过的文本例子训练而成。 +流程是由大量标注过的文本例子训练而成。 -模型可以输入更多的标注数据来优化结果,常见的应用是用特定数据优化用户需要的特定场景。 +流程可以输入更多的标注数据来优化结果,常见的应用是用特定数据优化用户需要的特定场景。 --- -# 模型包 +# 流程包 名字为zh_core_web_sm的模型包 @@ -46,16 +46,17 @@ nlp = spacy.load("zh_core_web_sm") - 二进制权重 - 词汇表 -- 元信息 (语言、流程) +- 元信息 +- 配置文件 -Notes: spaCy提供了很多预训练好的模型包,我们可以用`spacy download`命令来下载。 -比如"zh_core_web_sm"这个模型包就是一个小的中文模型,它有所有核心功能,是从网上的文本训练而来。 +Notes: spaCy提供了很多训练好的流程包,我们可以用`spacy download`命令来下载。 +比如"zh_core_web_sm"这个流程包就是一个小的中文模型,它有所有核心功能,是从网上的文本训练而来。 -`spacy.load`方法可以通过包名读取一个模型包并返回一个`nlp`实例。 +`spacy.load`方法可以通过包名读取一个流程包并返回一个`nlp`实例。 模型包含有二进制权重,spaCy用这些权重可以做出模型预测实现信息抽取。 -模型包也含有词汇表以及一些元信息,配置了spaCy的语言类以及相应的处理流程组件。 +模型包也含有词汇表以及关于流程和训练配置文件的元信息,配置了spaCy的语言类以及相应的处理流程组件。 --- @@ -64,7 +65,7 @@ Notes: spaCy提供了很多预训练好的模型包,我们可以用`spacy down ```python import spacy -# 读取小版本的中文模型 +# 读取小版本的中文流程 nlp = spacy.load("zh_core_web_sm") # 处理文本 @@ -87,7 +88,7 @@ for token in doc: Notes: 我们来看下模型的预测结果。这个例子中我们使用spaCy来获得词性标注的结果, 为每个词在其所在语境中标注种类。 -首先我们读入小版本的中文模型得到一个`nlp`的实例。 +首先我们读入小版本的中文流程得到一个`nlp`的实例。 然后我们处理"我吃了个肉夹馍"这个文本。 @@ -161,7 +162,7 @@ for ent in doc.ents: Notes: 命名实体是那些被赋予了名字的真实世界的物体,比如一个人、一个组织或者一个国家。 -从`doc.ents`中可以读取模型预测出的所有命名实体。 +从`doc.ents`中可以读取命名实体识别模型预测出的所有命名实体。 它会返回一个`Span`实例的遍历器,我们可以打印出实体文本和用`.label_`属性来打印出实体标注。 @@ -211,4 +212,4 @@ Notes: 一个小诀窍是可以用`spacy.explain`这个帮手函数 # 上手练习吧! -Notes: 轮到你自己来试试spaCy的统计模型和用它来预测一些信息了。 +Notes: 轮到你自己来试试spaCy的训练好的流程和用它来预测一些信息了。 diff --git a/chapters/zh/slides/chapter1_03_rule-based-matching.md b/chapters/zh/slides/chapter1_03_rule-based-matching.md index aca751d1d..939685f9e 100644 --- a/chapters/zh/slides/chapter1_03_rule-based-matching.md +++ b/chapters/zh/slides/chapter1_03_rule-based-matching.md @@ -68,7 +68,7 @@ import spacy # 导入Matcher from spacy.matcher import Matcher -# 读取一个模型,创建nlp实例 +# 读取一个流程,创建nlp实例 nlp = spacy.load("zh_core_web_sm") # 用模型分享出的vocab初始化matcher @@ -76,7 +76,7 @@ matcher = Matcher(nlp.vocab) # 给matcher加入模板 pattern = [{"TEXT": "iPhone"}, {"TEXT": "X"}] -matcher.add("IPHONE_PATTERN", None, pattern) +matcher.add("IPHONE_PATTERN", [pattern]) # 处理文本 doc = nlp("即将上市的iPhone X发布日期被泄露了") @@ -87,14 +87,13 @@ matches = matcher(doc) Notes: 要使用模板我们首先从`spacy.matcher`中导入matcher。 -我们还要读取一个模型创界`nlp`实例。 +我们还要读取一个流程创建`nlp`实例。 用模型分享出来的词汇表`nlp.vocab`来初始化matcher。 我们后面会详细介绍这一块,现在只要记得一定要传入这个词汇表就好了。 `matcher.add`方法可以用来添加一个模板。第一个参数是唯一的ID用来识别匹配的是哪一个模板。 -第二个参数是一个可选的回调参数,这里我们不需要所以设置其为`None`。 -第三个参数是模板本身。 +第二个参数是一个模板的列表。 要在文本中匹配模板,我们可以在任何doc中调用matcher。 diff --git a/chapters/zh/slides/chapter2_02_data-structures-2.md b/chapters/zh/slides/chapter2_02_data-structures-2.md index e3bf1f75e..01fe8b05b 100644 --- a/chapters/zh/slides/chapter2_02_data-structures-2.md +++ b/chapters/zh/slides/chapter2_02_data-structures-2.md @@ -13,8 +13,8 @@ Notes: 我们已经学习了词汇表和字符串库,现在我们可以看下 ```python # 创建一个nlp实例 -from spacy.lang.en import English -nlp = English() +import spacy +nlp = spacy.blank("en") # 导入Doc类 from spacy.tokens import Doc diff --git a/chapters/zh/slides/chapter2_03_word-vectors-similarity.md b/chapters/zh/slides/chapter2_03_word-vectors-similarity.md index 3e8d838a7..3d28ce4d5 100644 --- a/chapters/zh/slides/chapter2_03_word-vectors-similarity.md +++ b/chapters/zh/slides/chapter2_03_word-vectors-similarity.md @@ -16,10 +16,10 @@ Notes: 本节课中我们要学习如何用spaCy来判断 - `spaCy`可以对比两个实例来判断它们之间的相似度 - `Doc.similarity()`、`Span.similarity()`和`Token.similarity()` - 使用另一个实例作为参数返回一个相似度分数(在`0`和`1`之间) -- **注意:**我们需要一个含有词向量的模型,比如: - - ✅ `en_core_web_md` (中等模型) - - ✅ `en_core_web_lg` (大模型) - - 🚫 **而不是** `en_core_web_sm` (小模型) +- **注意:**我们需要一个含有词向量的流程,比如: + - ✅ `en_core_web_md` (中等) + - ✅ `en_core_web_lg` (大) + - 🚫 **而不是** `en_core_web_sm` (小) Notes: spaCy能够比较两个实例然后判断它们有多相似, 这包括了文档document、跨度span或者单个的词符token。 @@ -27,18 +27,18 @@ Notes: spaCy能够比较两个实例然后判断它们有多相似, `Doc`、`Token`和`Span`实例都有一个`.similarity`的方法可以传进另一个实例作为参数 然后返回一个0到1之间的浮点数,这个浮点数代表了两个实例之间的相似度。 -非常重要的一点:要计算相似度,我们必须需要一个比较大的含有词向量的spaCy模型。 +非常重要的一点:要计算相似度,我们必须需要一个比较大的含有词向量的spaCy流程。 -比如,我们可以用中等或者大的英文模型,但**不能用**小模型。 -所以如果我们一旦要用到词向量就需要寻找那些名字后面是"md"或者"lg"的模型。 -更多资料可以参考[模型文档](https://spacy.io/models)。 +比如,我们可以用中等或者大的英文流程,但**不能用**小流程。 +所以如果我们一旦要用到词向量就需要寻找那些名字后面是"md"或者"lg"的流程。 +更多资料可以参考[文档](https://spacy.io/models)。 --- # 相似度举例(1) ```python -# 读取一个有词向量的较大模型 +# 读取一个有词向量的较大流程 nlp = spacy.load("en_core_web_md") # 比较两个文档 @@ -65,7 +65,7 @@ print(token1.similarity(token2)) Notes: 我们来看看这个例子。我们想要找到两篇文档是否相似。 -首先我们读取中等规模的英文模型"en_core_web_md", +首先我们读取中等规模的英文流程"en_core_web_md", 然后可以创建两个doc实例,用第一个doc的`similarity`方法来与第二个doc实例做比较。 @@ -123,7 +123,7 @@ Notes: 我们还可以用`similarity`方法来计算不同种类的实例之间 - 词向量是一个词汇的多维度的语义表示 - 词向量是用诸如[Word2Vec](https://en.wikipedia.org/wiki/Word2vec) 这样的算法在大规模语料上面生成的 -- 词向量可以是spaCy的统计模型的一部分 +- 词向量可以是spaCy流程的一部分 - 默认我们使用余弦相似度,但也有其它计算相似度的方法 - `Doc`和`Span`的向量默认是由其词符向量的平均值计算得出的 - 短语的向量表示要优于长篇文档,因为后者含有很多不相关的词 @@ -134,7 +134,7 @@ Notes: spaCy在后台到底是怎么做到相似度计算的? 可能你听说过Word2Vec,这就是常常被用来从原始语料中训练出词向量的其中一种算法。 -词向量可以是spaCy的统计模型的一部分。 +词向量可以是spaCy流程的一部分。 默认spaCy会返回两个向量的余弦相似度,但有需要时我们也可以替换为其它计算相似度的方法。 @@ -148,7 +148,7 @@ Notes: spaCy在后台到底是怎么做到相似度计算的? # spaCy中的词向量 ```python -# 导入一个含有词向量的较大的模型 +# 导入一个含有词向量的较大的流程 nlp = spacy.load("en_core_web_md") doc = nlp("I have a banana") @@ -173,7 +173,7 @@ print(doc[3].vector) Notes: 这个例子能让我们大致了解这些向量长什么样。 -首先我们再次读取中等大小的模型,这个模型含有词向量。 +首先我们再次读取中等大小的流程,这个流程含有词向量。 然后我们处理一段文本,用`.vector`属性来查找一个词符的向量。 diff --git a/chapters/zh/slides/chapter2_04_models-rules.md b/chapters/zh/slides/chapter2_04_models-rules.md index 2ebec5a40..7b87fbbf3 100644 --- a/chapters/zh/slides/chapter2_04_models-rules.md +++ b/chapters/zh/slides/chapter2_04_models-rules.md @@ -2,9 +2,9 @@ type: slides --- -# 模型和规则的结合 +# 流程和规则的结合 -Notes: 将统计模型与规则系统结合使用,是自然语言处理工具箱里面最强大的方法之一。 +Notes: 将统计模型的预测结果与规则系统结合使用,是自然语言处理工具箱里面最强大的方法之一。 本节课中我们看下如何用spaCy来做这件事。 @@ -20,7 +20,7 @@ Notes: 将统计模型与规则系统结合使用,是自然语言处理工具 Notes: 如果你的应用需要能够根据一些例子而进行泛化,那么统计模型会很有用。 -举个例子,统计模型通常可以优化产品和人名的识别。 +举个例子,训练好的流程通常可以优化产品和人名的识别。 相比于给出一个所有曾经出现过的人名库,你的应用可以判断一段文本中的几个词符是否是人名。 相类似的你也可以预测依存关系标签从而得到主宾关系。 @@ -51,11 +51,11 @@ matcher = Matcher(nlp.vocab) # 模板是一个代表词符的字典组成的列表 pattern = [{"LEMMA": "love", "POS": "VERB"}, {"LOWER": "cats"}] -matcher.add("LOVE_CATS", None, pattern) +matcher.add("LOVE_CATS", [pattern]) # 运算符可以定义一个词符应该被匹配多少次 pattern = [{"TEXT": "very", "OP": "+"}, {"TEXT": "happy"}] -matcher.add("VERY_HAPPY", None, pattern) +matcher.add("VERY_HAPPY", [pattern]) # 在doc上面调用matcher来返回一个(match_id, start, end)元组的列表 doc = nlp("I love cats and I'm very very happy") @@ -81,7 +81,7 @@ matcher由一个共享词汇表(通常是`nlp.vocab`)来初始化。 ```python matcher = Matcher(nlp.vocab) -matcher.add("DOG", None, [{"LOWER": "golden"}, {"LOWER": "retriever"}]) +matcher.add("DOG", [[{"LOWER": "golden"}, {"LOWER": "retriever"}]]) doc = nlp("I have a Golden Retriever") for match_id, start, end in matcher(doc): @@ -141,7 +141,7 @@ from spacy.matcher import PhraseMatcher matcher = PhraseMatcher(nlp.vocab) pattern = nlp("Golden Retriever") -matcher.add("DOG", None, pattern) +matcher.add("DOG", [pattern]) doc = nlp("I have a Golden Retriever") # 遍历匹配结果 @@ -168,4 +168,4 @@ Notes: 让我们来看这个例子。 # 上手练习吧! -Notes: 让我们来试试这些结合了规则和统计模型的新技术。 +Notes: 让我们来试试这些结合了规则和预测结果的新技术。 diff --git a/chapters/zh/slides/chapter3_01_processing-pipelines.md b/chapters/zh/slides/chapter3_01_processing-pipelines.md index fac9c10a2..6e8438cd0 100644 --- a/chapters/zh/slides/chapter3_01_processing-pipelines.md +++ b/chapters/zh/slides/chapter3_01_processing-pipelines.md @@ -41,7 +41,7 @@ Notes: 我们到现在已经写过很多遍了:给`nlp`实例传入一个文 | **ner** | 命名实体识别器 | `Doc.ents`, `Token.ent_iob`, `Token.ent_type` | | **textcat** | 文本分类器 | `Doc.cats` | -Notes: spaCy原生提供了下面的流程组件: +Notes: spaCy原生提供了很多不同的流程组件。下面是一般项目中最常用的一些组件: 词性标注器设定了`token.tag`和`token.pos`这两个属性。 @@ -53,23 +53,23 @@ Notes: spaCy原生提供了下面的流程组件: 最后,文本分类器设定适用于整个文本的类别,将其加入`doc.cats`属性中。 -因为文本的类别往往是特定的,所以默认文本分类器不包含在任何一个预训练好的模型里面。 +因为文本的类别往往是特定的,所以默认文本分类器不包含在任何一个训练好的流程里面。 但我们可以用它来训练自己的系统。 --- # 解构后台 -标注为zh_core_web_sm的包、文件夹、文件及meta.json的图示 +标注为zh_core_web_sm的包、文件夹、文件及config.cfg的图示 -- 流程是依次定义在模型的`meta.json`文件里。 +- 流程是依次定义在模型的`config.cfg`文件里。 - 原生组件需要二进制数据来做预测。 -Notes: 所有我们能读进spaCy的模型都包含了一些文件和一个`meta.json`。 +Notes: 所有我们能读进spaCy的流程包都包含了一些文件和一个`config.cfg`。 -这个元数据定义了语种和流程等等,告诉spaCy应该去初始化那些组件。 +这个配置文件定义了语种和流程等等,告诉spaCy应该去初始化和配置那些组件。 -原生的组件如果要做预测也要需要二进制数据。这些数据都保存在模型包中,当我们读取模型 +原生的组件如果要做预测也要需要二进制数据。这些数据都保存在流程包中,当我们读取流程 的时候这些数据就被读取到组件中。 --- @@ -83,7 +83,7 @@ print(nlp.pipe_names) ``` ```out -['tagger', 'parser', 'ner'] +['tok2vec', 'tagger', 'parser', 'ner', 'attribute_ruler', 'lemmatizer'] ``` - `nlp.pipeline`: `(name, component)`元组的列表 @@ -93,9 +93,12 @@ print(nlp.pipeline) ``` ```out -[('tagger', ), +[('tok2vec', ), + ('tagger', ), ('parser', ), - ('ner', )] + ('ner', ), + ('attribute_ruler', ), + ('lemmatizer', )] ``` Notes: 我们可以使用`nlp.pipe_names`属性来读取当前nlp实例中流程组件的名字。 diff --git a/chapters/zh/slides/chapter3_02_custom-pipeline-components.md b/chapters/zh/slides/chapter3_02_custom-pipeline-components.md index a5bec36e2..61b56e08a 100644 --- a/chapters/zh/slides/chapter3_02_custom-pipeline-components.md +++ b/chapters/zh/slides/chapter3_02_custom-pipeline-components.md @@ -34,39 +34,48 @@ spaCy支持一系列的原生组件,但也允许我们定义自己的组件。 # 解构组件(1) - 函数用来读取一个`doc`,修改和返回它。 +- 用`Language.component`装饰器来注册。 - 我们可以用`nlp.add_pipe`来添加组件。 ```python -def custom_component(doc): +from spacy.language import Language + +@Language.component("custom_component") +def custom_component_function(doc): # 对doc做一些处理 return doc -nlp.add_pipe(custom_component) +nlp.add_pipe("custom_component") ``` Notes: 根本上来讲,一个流程组件就是一个函数或者callable,它读取一个doc,修改 和返回这个doc,作为下一个流程组件的输入。 -我们可以用`nlp.add_pipe`方法来为流程添加组件。这个方法需要至少一个参数:组件函数。 +要让spaCy找到我们的定制组件并调用,我们需要用`@Language.component`装饰器来装饰这个组件。 +只需要将其放在函数定义的前一行即可。 + +一旦组件被注册后,我们就可以用`nlp.add_pipe`来将其加入到流程中。 +这个方法需要至少一个参数:组件的字符串名。 --- # 解构组件(2) ```python -def custom_component(doc): +@Language.component("custom_component") +def custom_component_function(doc): # 对doc做一些处理 return doc -nlp.add_pipe(custom_component) +nlp.add_pipe("custom_component") ``` | 参数 | 说明 | 例子 | | -------- | -------------------- | ----------------------------------------- | -| `last` | 如果为`True`则加在最后面 | `nlp.add_pipe(component, last=True)` | -| `first` | 如果为`True`则加在最前面 | `nlp.add_pipe(component, first=True)` | -| `before` | 加在指定组件之前 | `nlp.add_pipe(component, before="ner")` | -| `after` | 加在指定组件之后 | `nlp.add_pipe(component, after="tagger")` | +| `last` | 如果为`True`则加在最后面 | `nlp.add_pipe("component", last=True)` | +| `first` | 如果为`True`则加在最前面 | `nlp.add_pipe("component", first=True)` | +| `before` | 加在指定组件之前 | `nlp.add_pipe("component", before="ner")` | +| `after` | 加在指定组件之后 | `nlp.add_pipe("component", after="tagger")` | Notes: 我们可以用下面这些关键字参数来指定在流程的 _什么位置_ 添加组件: @@ -88,26 +97,27 @@ Notes: 我们可以用下面这些关键字参数来指定在流程的 _什么 nlp = spacy.load("zh_core_web_sm") # 定义一个定制化组件 -def custom_component(doc): +@Language.component("custom_component") +def custom_component_function(doc): # 打印doc的长度 print("Doc length:", len(doc)) # 返回doc return doc # 把组件添加到流程的最前面 -nlp.add_pipe(custom_component, first=True) +nlp.add_pipe("custom_component", first=True) # 打印流程的组件名 print("Pipeline:", nlp.pipe_names) ``` ```out -Pipeline: ['custom_component', 'tagger', 'parser', 'ner'] +Pipeline: ['custom_component', 'tok2vec', 'tagger', 'parser', 'ner', 'attribute_ruler', 'lemmatizer'] ``` Notes: 我们来看看一个简单的流程组件的例子。 -我们从一个小的中文模型开始。 +我们从一个小的中文流程开始。 然后定义组件,也就是一个函数,读取`Doc`实例然后再把它返回出来。 @@ -116,6 +126,8 @@ Notes: 我们来看看一个简单的流程组件的例子。 别忘了把这个doc返回出来,因为它还要被流程后面的组件处理! 分词器创建的doc会走完全部的流程组件,所以每个组件都一定要返回其处理过的doc,这点很重要。 +要让spaCy知道新的组件,我们用`@Language.component`装饰器将其注册,起名为"custom_component". + 我们现在可以把组件加入到流程中了。我们设置`first=True`把它加到流程的最前面,紧跟着分词器。 我们打印流程组件名,可以看到定制化组件现在出现在起始位置。这意味着我们处理一个doc @@ -130,16 +142,15 @@ Notes: 我们来看看一个简单的流程组件的例子。 nlp = spacy.load("zh_core_web_sm") # 定义一个定制化组件 -def custom_component(doc): - +@Language.component("custom_component") +def custom_component_function(doc): # 打印doc的长度 print("Doc length:", len(doc)) - # 返回doc return doc # 把组件添加到流程的最前面 -nlp.add_pipe(custom_component, first=True) +nlp.add_pipe("custom_component", first=True) # 处理一段文本 doc = nlp("这是一个句子。") diff --git a/chapters/zh/slides/chapter3_04_scaling-performance.md b/chapters/zh/slides/chapter3_04_scaling-performance.md index c5630dc4f..1907cc4a3 100644 --- a/chapters/zh/slides/chapter3_04_scaling-performance.md +++ b/chapters/zh/slides/chapter3_04_scaling-performance.md @@ -134,11 +134,11 @@ Notes: 如果我们只是需要一个分词过的`Doc`实例,我们可以用`n # 关闭流程组件 -- 使用`nlp.disable_pipes`来暂时关闭一个或多个流程组件。 +- 使用`nlp.select_pipes`来暂时关闭一个或多个流程组件。 ```python # 关闭词性标注器tagger和依存关系标注器parser -with nlp.disable_pipes("tagger", "parser"): +with nlp.select_pipes(disable=["tagger", "parser"]): # 处理文本并打印实体结果 doc = nlp(text) print(doc.ents) @@ -147,10 +147,10 @@ with nlp.disable_pipes("tagger", "parser"): - `with`代码块之后这些组件会重新启用 - 这些组件关闭后spaCy流程只会跑剩余的未被关闭的组件 -Notes: spaCy允许我们暂时关闭一些流程组件,方法是用`nlp.disable_pipes` +Notes: spaCy允许我们暂时关闭一些流程组件,方法是用`nlp.select_pipes` 这个管理器。 -这个方法需要一个可变长的参数,包含了需要关闭的一个或多个流程组件的名字。 +这个方法需要一个关键词参数`enable`或者`disable`,可以定义一个包含了需要关闭的一个或多个流程组件的名字的列表。 比如我们只想要用实体识别器来处理文档,我们就可以暂时关闭词性标注器tagger 和依存关系标注器parser。 diff --git a/chapters/zh/slides/chapter4_01_training-updating-models.md b/chapters/zh/slides/chapter4_01_training-updating-models.md index 8e22ece42..feb496932 100644 --- a/chapters/zh/slides/chapter4_01_training-updating-models.md +++ b/chapters/zh/slides/chapter4_01_training-updating-models.md @@ -7,8 +7,8 @@ type: slides Notes: 欢迎来到最后一章,我们来学习现代自然语言处理最激动人心的部分: 训练我们自己的模型! -本节课中,我们要学习训练和更新spaCy的神经网络模型及其相应的数据,我们主要专注于命名 -实体识别器这一部分。 +本节课中,我们要学习训练和更新spaCy的流程组件和其中的神经网络模型,以及其相应的数据, +我们主要专注于命名实体识别器这一部分。 --- @@ -21,7 +21,7 @@ Notes: 欢迎来到最后一章,我们来学习现代自然语言处理最激 - 对词性标注和依存关系识别不是很关键 Notes: 我们介绍 _如何_ 更新模型之前先花一点时间问问我们自己:为什么我们想要 -用我们自己的例子来更新模型?我们是不是只用预训练好的模型就可以了? +用我们自己的例子来更新模型?我们是不是只用预训练好的流程就可以了? 统计模型可以基于相应的训练数据来做预测。 @@ -36,25 +36,24 @@ Notes: 我们介绍 _如何_ 更新模型之前先花一点时间问问我们自 # 如何训练(1) -1. **初始化**模型权重使之变为随机值:调用`nlp.begin_training`方法 -2. **预测**几个例子,看看当前权重的表现:调用`nlp.update`方法 +1. **初始化**模型权重使之变为随机值 +2. **预测**几个例子,看看当前权重的表现 3. **比较**预测结果和真实标注的标签 4. **计算**如何调整权重来改善预测结果 5. **微调**模型权重 6. 重复步骤2。 -Notes: spaCy支持用更多的例子来更新现有模型训练新模型。 +Notes: spaCy支持用更多的例子来更新现有模型训练新模型。如果我们不是用一个预训练模型做初始模型的话,我们首先需要随机化所有的权重。 -如果我们不是用一个预训练模型做初始模型的话,我们首先需要随机化所有的权重。 - -然后我们调用`nlp.update`,这个方法使用当前权重来预测一批次的例子。 +然后spaCy调用`nlp.update`,这个方法使用当前权重来预测一批次的例子。 模型然后把预测结果和正确答案做比较,决定下一步应该如何改变模型权重来使得下一次的 预测结果表现更好。 最后我们对当前权重做出微调,然后在下一个批次的例子上做预测。 -我们对数据中的每一批例子调用`nlp.update`。 +spaCy然后对数据中的每一批例子调用`nlp.update`。在训练过程中, +我们通常要多次遍历数据进行训练,直到模型不再变得更好。 --- @@ -91,27 +90,28 @@ Notes: 我们来看看模型训练的过程。 - 训练例子需要带有语境 ```python -("iPhone X就要来了", {"entities": [(0, 8, "GADGET")]}) +doc = nlp("iPhone X就要来了") +doc.ents = [Span(doc, 0, 8, label="GADGET")] ``` - 并非实体的文本部分也非常重要 ```python -("我急需一部新手机,给点建议吧!", {"entities": []}) +doc = nlp("我急需一部新手机,给点建议吧!") +doc.ents = [] ``` - **目标:**让模型学会泛化 Notes: 我们来看一个例子,专注于一个特定的组件:实体识别器。 -实体识别器读入一个文档,预测其中的短语及其标签。这意味着训练数据需要有文本、 +实体识别器读入一个文档,预测其中的短语及其 _语境中_ 的标签。这意味着训练数据需要有文本、 包含的实体以及实体的标签。 实体之间不能重叠,所以一个词符只能属于一个实体。 -因为实体识别器要 _在语境中_ 预测实体,我们要用实体 _加上_ 实体周围的语境来训练它。 - -最简单的方法就是给模型输入一段文本和一个字符位置的列表。比如"iPhone X"是一个电子产品, +最简单的方法就是给模型输入一段文本和一个字符位置的列表。spaCy可以通过标准的 +含有标注为`doc.ents`的实体`Doc`对象来进行更新。比如"iPhone X"是一个电子产品, 是从字符0开始到字符8结束的。 让模型知道哪些词 _并不是_ 实体也是非常重要的。 @@ -133,13 +133,13 @@ Notes: 我们来看一个例子,专注于一个特定的组件:实体识别 - 也可以是半自动的,比如用spaCy的模板匹配器`Matcher`! Notes: 训练数据告诉模型我们想要预测什么。预测目标可能是想要识别的文本和命名 -实体,或者是词符及其正确的词性标签。 +实体,或者是词符及其正确的词性标签,或是任何模型可以预测的结果。 要更新一个现有的模型,我们可以先试试几百到几千数据量的例子。 要训练一个新的类别我们可能需要百万级别的训练数据。 -spaCy的预训练中文模型是在200万个词汇的语料上训练的,这些文本都已经标注了词性标签、 +spaCy的训练好的中文流程是在200万个词汇的语料上训练的,这些文本都已经标注了词性标签、 依存关系和命名实体。 训练数据通常是由训练师手动标注文本创建的。 @@ -148,7 +148,109 @@ spaCy的预训练中文模型是在200万个词汇的语料上训练的,这些 --- +# 训练数据 vs 测试数据 + +- **训练数据:**用来更新模型 +- **测试数据:** + - 模型在训练过程中未见到的数据 + - 用来计算模型的准确度 + - 代表了模型在生产环境中会遇到的真实数据 + +Notes: 训练模型中的重要一环是了解模型的表现,模型学习的方向是否正确。方法是让模型在一些 +训练中 _尚未_ 见过的数据上进行预测,然后对比我们已经知道的正确答案。因此在训练数据之外, +我们还需要测试数据,也被称作为开发数据。 + +测试数据用来计算模型的准确度。举个例子,一个准确度为90%的模型意味着模型在测试数据上的 +预测结果中90%都是正确的。 + +这意味着测试数据需要能够代表模型在生产环境中会遇到的数据,否则准确度也将失去意义, +因为这不能告诉我们模型 _真正_ 表现得有多好。 + +--- + +# 生成训练语料(1) + +```python +import spacy +nlp = spacy.blank("zh") +# 创建一个含有实体span的Doc +doc1 = nlp("iPhone X就要来了") +doc1.ents = [Span(doc1, 0, 8, label="GADGET")] +# 创建另一个没有实体span的Doc +doc2 = nlp("我急需一部新手机,给点建议吧!") +docs = [doc1, doc2] # 以此类推... +``` + +Notes: spaCy可以用与其创建的`Doc`对象相同格式的数据来更新。我们已经在第二章中学到了 +很多如何创建`Doc`和`Span`对象的知识。 + +在这个例子中,我们为语料创建了两个`Doc`对象:一个中含有一个实体而另一个不含有实体。 +要为`Doc`设置实体,我们需要把`Span`加入到`doc.ents`中。 + +当然了,我们还需要更多的训练数据来有效地训练出一个可以泛化和在语境中预测类似实体的模型。 +根据任务的不同,我们通常希望有至少几百到上千个有代表性的数据。 + +--- + +# 生成训练语料(2) + +- 将数据分割成两份: + - **训练数据:**用来更新模型 + - **开发数据:**用来测试模型 + +```python +random.shuffle(docs) +train_docs = docs[:len(docs) // 2)] +dev_docs = docs[len(docs) // 2):] +``` + +Notes: 如前面所提到的,我们不仅需要数据来训练模型,还需要在模型训练中未见过的数据上面 +测试模型准确度。我们通常对数据随机排序,然后把数据分成两份:一份作为训练数据,一份作为 +测试数据。这里我们只是简单的50/50对半分。 + +--- + +# 生成训练语料(3) + +- `DocBin`:用来有效存储`Doc`对象的容器 +- 可以保存为二进制文件 +- 二进制文件可以用来训练模型 + +```python +# 创建和保存一系列的训练文档 +train_docbin = DocBin(docs=train_docs) +train_docbin.to_disk("./train.spacy") +# 创建和保存一系列的测试文档 +dev_docbin = DocBin(docs=dev_docs) +dev_docbin.to_disk("./dev.spacy") +``` + +Notes: 我们一般希望可以将训练和测试数据保存为硬盘上的文件,这样我们就可以读入到spaCy的训练流程中。 + +`DocBin`是用来有效存储和序列化`Doc`对象的容器。我们可以用一个`Doc`对象的列表来初始化它,然后调用 +`to_disk`方法将其存储为一个二进制文件。这些文件我们一般使用`.spacy`作为后缀。 + +相比起其它如`pickle`的二进制序列化规制,我们的`DocBin`会更加快,生成的文件更小,因为其仅对于 +共享的词汇表仅存储一次。关于`DocBin`运作的更多相关信息可以查询[文档](https://spacy.io/api/docbin). + +--- + +# 小经验:数据转换 + +- `spacy convert`可以将语料转换为常见的格式 +- 支持`.conll`, `.conllu`, `.iob`以及spaCy之前的JSON格式。 + +```bash +$ python -m spacy convert ./train.gold.conll ./corpus +``` + +Notes: 有时候我们的训练和测试数据可能已经是常见的格式 - 比如 CoNLL 或者 IOB. +spaCy的`convert`命令可以自动将这些文件转换为spaCy的二进制格式。它也可以转换spaCy v2 +中使用旧格式的JSON文件。 + +--- + # 上手练习吧! -Notes: 是时候开始准备一些训练数据了。我们来看看几个例子,为一个新的实体类别 +Notes: 是时候开始准备一些训练语料了。我们来看看几个例子,为一个新的实体类别 创建一个小的训练数据集。 diff --git a/chapters/zh/slides/chapter4_02_running-training.md b/chapters/zh/slides/chapter4_02_running-training.md new file mode 100644 index 000000000..978dd1a48 --- /dev/null +++ b/chapters/zh/slides/chapter4_02_running-training.md @@ -0,0 +1,210 @@ +--- +type: slides +--- + +# 配置和运行训练流程 + +Notes: 我们现在已经学会了如何创建训练数据,我们来看看如何配置和训练流程。本节课中我们会 +学习到spaCy的训练配置系统,如何生成我们自己的训练配置,如果使用CLI来训练模型,以及如何 +在训练结束后测试我们的流程。 + +--- + +# 训练配置(1) + +- 所有设定的**唯一真理来源** +- 通常被叫做`config.cfg` +- 定义了如何初始化`nlp`对象 +- 包含了关于流程组件和模型实现的所有设定 +- 配置了训练过程和超参数 +- 使我们的训练过程可复现 + +Notes: spaCy使用的配置文件通常被叫做`config.cfg`,是所有设定的“唯一真理来源”。这个 +配置文件决定了如何初始化`nlp`对象,哪些流程组件被添加,以及如何配置组件内部的模型实现。 +配置文件还包含了训练过程的所有设定,包括如何读取数据和超参数等。 + +由此我们再不需要在命令行提供大量的的参数,或是在代码中记着定义每一个设定。我们只需要把 +配置文件传给spaCy的训练指令即可。 + +配置文件也帮助我们可以更好复现训练过程:所有的设定都在同一个地方,流程训练一目了然。 +我们甚至可以将配置文件放到Git仓库中,加入版本控制分享给其他人,这样其他人也可以 +用同样的设定训练同样的流程。 + +--- + +# 训练配置(2) + +```ini +[nlp] +lang = "zh" +pipeline = ["tok2vec", "ner"] +batch_size = 1000 +[nlp.tokenizer] +@tokenizers = "spacy.zh.ChineseTokenizer" +segmenter = "char" +[components] +[components.ner] +factory = "ner" +[components.ner.model] +@architectures = "spacy.TransitionBasedParser.v2" +hidden_width = 64 +# 以此类推 +``` + +Notes: 这是从训练一个命名实体识别器的流程配置文件中摘取的片段。配置文件分为几个部分, +嵌套部分用一个点来定义。比如,`[components.ner.model]`定义了命名实体识别器 +的模型实现的设定。 + +配置文件也可以用`@`标记来引用Python函数。比如,分词器定义了一个注册过的分词函数。我们 +可以用它来定制化`nlp`对象和训练的不同部分 - 从嵌入我们自己的分词器到实现我们自己的模型 +架构。但是我们现在先不用担心 - 本章节中我们只是简单使用spaCy提供的开箱可用的默认配置。 + +--- + +# 生成一个配置文件 + +- spaCy可以自动生成一个默认的配置文件 +- 文档中有可交互的[快速上手插件](https://spacy.io/usage/training#quickstart) +- 作用于CLI的[`init config`](https://spacy.io/api/cli#init-config)命令 + +```bash +$ python -m spacy init config ./config.cfg --lang zh --pipeline ner +``` + +- `init config`: 要运行的命令 +- `config.cfg`: 生成的配置文档的输出路径 +- `--lang`: 流程的语言类,比如中文是 `zh` +- `--pipeline`: 用逗号分隔的流程组件名称 + +Notes: 当然了,我们往往不需要手写配置文件,很多情况下我们甚至不需要定制化配置文件。 +spaCy会自动帮我们生成。 + +文档中的快速上手插件可以交互式地帮我们生成配置文件,让我们选择需要的语言和流程组件以及 +可选的硬件和优化设定。 + +另外,我们也可以使用spaCy内建的`init config`命令。该命令的第一个参数是输出文件,我们通常 +起名为`config.cfg`. 参数`--lang`定义了流程的语言类,比如`zh`就是中文。`--pipeline`参数 +让我们指定一个或多个用逗号分隔的流程组件来加入流程之中。这个例子中,我们创建了一个配置文件, +含有一个命名实体识别的流程组件。 + +--- + +# 训练流程(1) + +- 我们需要的只是`config.cfg`和训练与测试数据 +- 配置的设定可以在命令行中被覆盖 + +```bash +$ python -m spacy train ./config.cfg --output ./output --paths.train train.spacy --paths.dev dev.spacy +``` + +- `train`: 要运行的命令 +- `config.cfg`: 配置文档的路径 +- `--output`: 保存训练流程的输出路径 +- `--paths.train`: 覆盖训练数据的路径 +- `--paths.dev`: 覆盖测试数据的路径 + +Notes: 要训练一个流程,我们需要的只是`config.cfg`和训练与测试数据。这些数据都是在之前练习中 +我们见到过的`.spacy`文件。 + +`spaCy train`的第一个参数是配置文件的路径。`--output`参数可以指定保存最终训练好的流程的输出路径。 + +我们还可以在命令行中覆盖不同的配置设定。在这个例子里面,我们用`train.spacy`文件的路径 +覆盖了`paths.train`,用`dev.spacy`文件的路径覆盖了`paths.dev`. + +--- + +# 训练流程(2) + +``` +============================ Training pipeline ============================ +ℹ Pipeline: ['tok2vec', 'ner'] +ℹ Initial learn rate: 0.001 +E # LOSS TOK2VEC LOSS NER ENTS_F ENTS_P ENTS_R SCORE +--- ------ ------------ -------- ------ ------ ------ ------ + 0 0 0.00 26.50 0.73 0.39 5.43 0.01 + 0 200 33.58 847.68 10.88 44.44 6.20 0.11 + 1 400 70.88 267.65 33.50 45.95 26.36 0.33 + 2 600 67.56 156.63 45.32 62.16 35.66 0.45 + 3 800 138.28 134.12 48.17 74.19 35.66 0.48 + 4 1000 177.95 109.77 51.43 66.67 41.86 0.51 + 6 1200 94.95 52.13 54.63 67.82 45.74 0.55 + 8 1400 126.85 66.19 56.00 65.62 48.84 0.56 + 10 1600 38.34 24.16 51.96 70.67 41.09 0.52 + 13 1800 105.14 23.23 56.88 69.66 48.06 0.57 +✔ Saved pipeline to output directory +/path/to/output/model-last +``` + +Notes: 这是训练过程中和结束时我们会看到的一个屏幕输出的例子。我们还记得之前提到过,我们 +通常希望在训练过程中遍历数据多次。每一次遍历数据被叫做一个"epoch"。这就是表中的第一列。 + +每一个epoch中,spaCy会在每200个数据后输出准确度分数。这是第二列中显示的步骤。我们可以 +在配置文件中修改这个频率。每一行显示了训练中的这一步模型损失和计算得到的准确度分数。 + +我们要留意的最有趣的分数时最后一列的合成分数。这反映了我们的模型在测试数据中预测正确的准确度。 + +训练过程会一直进行直到模型没有进一步的改进空间了,这时程序就会自动退出。 + +--- + +# 读取已经训练好的流程 + +- 训练后的输出是一个正常的可读取的spaCy流程 + - `model-last`: 最后训练出的流程 + - `model-best`: 表现最好的训练流程 +- 用`spacy.load`读取流程 + +```python +import spacy +nlp = spacy.load("/path/to/output/model-best") +doc = nlp("iPhone 11 vs iPhone 8: 到底有什么区别?") +print(doc.ents) +``` + +Notes: 训练结束后存储的流程是一个正常的可读取的spaCy流程 - 就像其它spaCy提供的训练好的流程一样, +比如 `zh_core_web_sm`. 最终,最后训练出的流程和最高分的流程都会被存储在输出路径中。 + +我们可以把路径传给`spacy.load`来读取已经训练好的流程。我们接下来就可以用它来处理和分析文本了。 + +--- + +# 小经验:将流程打包 + + + +- [`spacy package`](https://spacy.io/api/cli#package): 创建一个包含我们流程的可安装的Python包 +- 方便版本控制和部署 + +```bash +$ python -m spacy package /path/to/output/model-best ./packages --name my_pipeline --version 1.0.0 +``` + +```bash +$ cd ./packages/zh_my_pipeline-1.0.0 +$ pip install dist/zh_my_pipeline-1.0.0.tar.gz +``` + +安装后读取和使用流程: + +```python +nlp = spacy.load("zh_my_pipeline") +``` + + +Notes: 为了更方便地部署我们的流程,spaCy提供了一系列趁手的命令来将流程打包成Python包。 +`spacy package`读取的参数包括我们生成的流程路径和输出路径,然后生成一个含有我们流程的 +Python包。这个Python包是`.tar.gz`格式的文件,可以安装到环境中。 + +我们还可以在命令中提供可选的名字和版本号,这样我们就可以管理同一个流程的多个不同版本, +比如我们想继续定制化我们的流程或者用更多的数据训练它。 + +使用这个包和使用其它Python包是一样的。安装完后,我们可以用包名来读取流程。注意spaCy会自动把 +语言代码加到名字中,所以我们的流程`my_pipeline`最后就成了`zh_my_pipeline`. + +--- + +# 上手练习吧! + +Notes: 让我们上手来训练我们的第一个流程!我们会练习生成一个命名实体识别器的配置文件, +然后用之前练习中生成的数据来训练这个流程。 \ No newline at end of file diff --git a/chapters/zh/slides/chapter4_03_training-best-practices.md b/chapters/zh/slides/chapter4_03_training-best-practices.md index 4128d7975..31a628509 100644 --- a/chapters/zh/slides/chapter4_03_training-best-practices.md +++ b/chapters/zh/slides/chapter4_03_training-best-practices.md @@ -22,13 +22,13 @@ Notes: 当我们开始跑自己实验的时候,我们可能发现很多东西 之前`"PERSON"`这个类别。 - 这也被称为“灾难性遗忘”的问题。 -Notes: 统计模型可以学会很多东西,但这不代表它不会忘记东西。 +Notes: 统计模型可以学会很多东西,但它们也会忘记东西。 如果我们用新的数据更新已有模型,特别是更新一些新的标注,模型可能会过拟合,针对新的 例子做出了 _过多_ 调整。 -举个例子,我们想用"website"的例子来更新模型,结果模型就可能“忘记”了之前本来可以预测 -正确的诸如"person"这样的类别。 +举个例子,我们想用"WEBSITE"的例子来更新模型,结果模型就可能“忘记”了之前本来可以预测 +正确的诸如"PERSON"这样的类别。 这也被称作“灾难性遗忘”的问题。 @@ -39,23 +39,6 @@ Notes: 统计模型可以学会很多东西,但这不代表它不会忘记东 - 举个例子,我们要训练`"WEBSITE"`,但我们也把`"PERSON"`的例子加进来。 - 在数据上跑已有的spaCy模型然后抽取所有其它相关的实体。 -**不好的做法:** - -```python -TRAINING_DATA = [ - ("阿里巴巴是一个网站", {"entities": [(0, 1, "WEBSITE")]}) -] -``` - -**好的做法:** - -```python -TRAINING_DATA = [ - ("阿里巴巴是一个网站", {"entities": [(0, 1, "WEBSITE")]}), - ("马云是一个人", {"entities": [(0, 1, "PERSON")]}) -] -``` - Note: 要预防灾难性遗忘问题,我们要确保总是在训练数据中混入一些之前模型预测 正确的例子。 diff --git a/chapters/zh/slides/chapter4_04_wrapping-up.md b/chapters/zh/slides/chapter4_04_wrapping-up.md index 97193381d..30fecf730 100644 --- a/chapters/zh/slides/chapter4_04_wrapping-up.md +++ b/chapters/zh/slides/chapter4_04_wrapping-up.md @@ -11,7 +11,7 @@ Notes: 恭喜你!我们已经学习到这门课程的尾声了! # 你的新spaCy技能 - 抽取**语言学特征**:词性识别,依存关系,命名实体 -- 学会使用预训练好的的**统计模型** +- 学会使用训练好的的**流程** - 用`Matcher`和`PhraseMatcher`来**匹配规则**寻找目标词汇和短语 - 使用**数据结构**`Doc`、`Token`、`Span`、`Vocab`和`Lexeme`的最佳实践 - 使用**词向量**来计算**语义相似度** @@ -23,7 +23,7 @@ Notes: 恭喜你!我们已经学习到这门课程的尾声了! Notes: 这里我们总结一下我们已经学过的所有新技能: 第一章中我们学习了如何抽取语言学特征,比如词性标注的标签、依存句法关系和命名实体等, -以及如何使用预训练好的统计模型。 +以及如何使用训练好的流程。 我们还学习了书写一些非常强大的匹配模板来用spaCy的匹配器`Matcher`和`PhraseMatcher` 抽取目标词汇和短语。 @@ -66,7 +66,7 @@ Notes: 当然spaCy还能做很多其它的东西,我们这门课还没有讲 - [定制化分词器](https://spacy.io/usage/linguistic-features#tokenization) - 增加规则和异常来用不同的方法分割文本 - [增加或者改进对其它语种的支持](https://spacy.io/usage/adding-languages) - - 现在支持55+种语言 + - 现在支持60+种语言 - 已有语种的支持还有很大改进空间,还有更多语种有待添加支持 - 允许对其它语种训练模型 diff --git a/exercises/zh/config_gadget.cfg b/exercises/zh/config_gadget.cfg new file mode 100644 index 000000000..dde112eb4 --- /dev/null +++ b/exercises/zh/config_gadget.cfg @@ -0,0 +1,148 @@ +[paths] +train = null +dev = null +vectors = null +init_tok2vec = null + +[system] +gpu_allocator = null +seed = 0 + +[nlp] +lang = "zh" +pipeline = ["tok2vec","ner"] +batch_size = 1000 +disabled = [] +before_creation = null +after_creation = null +after_pipeline_creation = null + +[nlp.tokenizer] +@tokenizers = "spacy.zh.ChineseTokenizer" +segmenter = "char" + +[components] + +[components.ner] +factory = "ner" +incorrect_spans_key = null +moves = null +scorer = {"@scorers":"spacy.ner_scorer.v1"} +update_with_oracle_cut_size = 100 + +[components.ner.model] +@architectures = "spacy.TransitionBasedParser.v2" +state_type = "ner" +extra_state_tokens = false +hidden_width = 64 +maxout_pieces = 2 +use_upper = true +nO = null + +[components.ner.model.tok2vec] +@architectures = "spacy.Tok2VecListener.v1" +width = ${components.tok2vec.model.encode.width} +upstream = "*" + +[components.tok2vec] +factory = "tok2vec" + +[components.tok2vec.model] +@architectures = "spacy.Tok2Vec.v2" + +[components.tok2vec.model.embed] +@architectures = "spacy.MultiHashEmbed.v2" +width = ${components.tok2vec.model.encode.width} +attrs = ["ORTH","SHAPE"] +rows = [5000,2500] +include_static_vectors = false + +[components.tok2vec.model.encode] +@architectures = "spacy.MaxoutWindowEncoder.v2" +width = 96 +depth = 4 +window_size = 1 +maxout_pieces = 3 + +[corpora] + +[corpora.dev] +@readers = "spacy.Corpus.v1" +path = ${paths.dev} +max_length = 0 +gold_preproc = false +limit = 0 +augmenter = null + +[corpora.train] +@readers = "spacy.Corpus.v1" +path = ${paths.train} +max_length = 0 +gold_preproc = false +limit = 0 +augmenter = null + +[training] +dev_corpus = "corpora.dev" +train_corpus = "corpora.train" +seed = ${system.seed} +gpu_allocator = ${system.gpu_allocator} +dropout = 0.1 +accumulate_gradient = 1 +patience = 1600 +max_epochs = 0 +max_steps = 20000 +eval_frequency = 200 +frozen_components = [] +annotating_components = [] +before_to_disk = null + +[training.batcher] +@batchers = "spacy.batch_by_words.v1" +discard_oversize = false +tolerance = 0.2 +get_length = null + +[training.batcher.size] +@schedules = "compounding.v1" +start = 100 +stop = 1000 +compound = 1.001 +t = 0.0 + +[training.logger] +@loggers = "spacy.ConsoleLogger.v1" +progress_bar = false + +[training.optimizer] +@optimizers = "Adam.v1" +beta1 = 0.9 +beta2 = 0.999 +L2_is_weight_decay = true +L2 = 0.01 +grad_clip = 1.0 +use_averages = false +eps = 0.00000001 +learn_rate = 0.001 + +[training.score_weights] +ents_f = 1.0 +ents_p = 0.0 +ents_r = 0.0 +ents_per_type = null + +[pretraining] + +[initialize] +vectors = ${paths.vectors} +init_tok2vec = ${paths.init_tok2vec} +vocab_data = null +lookups = null +before_init = null +after_init = null + +[initialize.components] + +[initialize.tokenizer] +pkuseg_model = null +pkuseg_user_dict = "default" \ No newline at end of file diff --git a/exercises/zh/dev_gadget.spacy b/exercises/zh/dev_gadget.spacy new file mode 100644 index 0000000000000000000000000000000000000000..42c63bc1905b3ce508a12443adb038560a674c70 GIT binary patch literal 118 zcmV-+0Ez#2oa