训练流水线与模型
spaCy 的词性标注器、句法分析器、文本分类器以及许多其他组件都由 统计模型 驱动。这些组件所做的每一个“决策”——例如,分配哪个词性标签,或者一个词是否是命名实体——都是基于模型当前 权重值 的 预测。权重值是根据模型在 训练 期间看到的示例估算的。要训练一个模型,首先需要训练数据——文本示例以及您希望模型预测的标签。这可能是一个词性标签、一个命名实体或任何其他信息。
训练是一个迭代过程,其中模型的预测与参考标注进行比较,以估算 损失的梯度。然后使用损失的梯度通过 反向传播 计算权重的梯度。梯度指示应如何更改权重值,以便模型的预测随着时间的推移更接近参考标签。
在训练模型时,我们不希望它仅仅记住我们的示例——我们希望它提出一个可以 推广到未见数据 的理论。毕竟,我们不希望模型仅仅学习到这个“Amazon”实例是公司——我们希望它学习到“Amazon”在类似这种上下文中,很可能是一个公司。因此,训练数据应始终代表我们想要处理的数据。在 Wikipedia 上训练的模型,其中第一人称的句子非常罕见,很可能在 Twitter 上表现不佳。同样,在浪漫小说上训练的模型很可能在法律文本上表现不佳。
这也意味着为了知道模型的表现如何,以及它是否在学习正确的东西,您不仅需要 训练数据——您还需要 评估数据。如果您只使用训练数据测试模型,您将不知道它推广得如何。如果您想从头开始训练一个模型,通常至少需要几百个用于训练和评估的示例。
快速入门 新
训练 spaCy 流水线的推荐方法是通过命令行中的 spacy train 命令。它只需要一个 config.cfg 配置文件,其中包含所有设置和超参数。您可以选择性地 覆盖 命令行中的设置,并加载一个 Python 文件以注册 自定义函数 和架构。此快速入门小部件可帮助您为您的特定用例生成一个带有 推荐设置 的启动配置文件。它也作为 spaCy 中的 init config 命令提供。
# This is an auto-generated partial config. To use it with 'spacy train'
# you can run spacy init fill-config to auto-fill all default settings:
# python -m spacy init fill-config ./base_config.cfg ./config.cfg
[paths]
train = null
dev = null
vectors = null
[system]
gpu_allocator = null
[nlp]
lang = "en"
pipeline = []
batch_size = 1000
[components]
[corpora]
[corpora.train]
@readers = "spacy.Corpus.v1"
path = ${paths.train}
max_length = 0
[corpora.dev]
@readers = "spacy.Corpus.v1"
path = ${paths.dev}
max_length = 0
[training]
dev_corpus = "corpora.dev"
train_corpus = "corpora.train"
[training.optimizer]
@optimizers = "Adam.v1"
[training.batcher]
@batchers = "spacy.batch_by_words.v1"
discard_oversize = false
tolerance = 0.2
[training.batcher.size]
@schedules = "compounding.v1"
start = 100
stop = 1000
compound = 1.001
[initialize]
vectors = ${paths.vectors}在将启动配置文件保存到文件 base_config.cfg 后,您可以使用 init fill-config 命令填充剩余的默认值。训练配置文件应始终 完整且没有隐藏的默认值,以保持实验的可重复性。
与其从快速入门小部件导出启动配置文件并自动填充它,您还可以使用 init config 命令,并指定您的需求和设置作为 CLI 参数。现在您可以添加数据并运行 train 与您的配置。有关如何将数据转换为 spaCy 的二进制 .spacy 格式的详细信息,请参阅 convert 命令。您可以将数据路径包含在配置的 [paths] 部分,也可以通过命令行传递它们。
快速入门小部件和 init config 命令生成的推荐配置设置基于一些通用的 最佳实践 以及我们在实验中发现有效的方法。目标是为您提供最 有用的默认值。
在底层,quickstart_training.jinja 模板定义了不同的组合——例如,如果流水线应优化效率与准确性,应更改哪些参数。文件 quickstart_training_recommendations.yml 收集了每种语言的推荐设置和可用资源,包括不同的 transformer 权重。对于某些语言,我们包括不同的 transformer 推荐,具体取决于您是希望模型更高效还是更准确。这些推荐将随着我们进行更多实验而 不断发展。
训练配置系统
训练配置文件包含训练流水线的 所有设置和超参数。您无需在命令行上提供大量参数,只需将您的 config.cfg 文件传递给 spacy train。在底层,训练配置使用我们的机器学习库 Thinc 提供的 配置系统。这也有助于集成用您选择的框架编写的自定义模型和架构。spaCy 训练配置的一些主要优点和功能是
- 结构化的部分。 配置分为不同的部分,嵌套部分使用
.符号定义。例如,[components.ner]定义了流水线的命名实体识别器的设置。可以将配置加载为 Python 字典。 - 对已注册函数的引用。 部分可以引用已注册的函数,例如 模型架构、优化器 或 调度器,并定义传递给它们的参数。您还可以 注册您自己的函数 以定义自定义架构或方法,在配置中引用它们并调整其参数。
- 插值。 如果您有多个组件使用的超参数或其他设置,请一次定义它们并将其引用为 变量。
- 可重现性,无隐藏默认值。 配置文件是“单一事实来源”,包含所有设置。
- 自动化检查和验证。 当你加载一个配置文件时,spaCy 会检查设置是否完整,以及所有值是否具有正确的类型。这让你可以在早期发现潜在的错误。在你的自定义架构中,你可以使用 Python 类型提示 来告诉配置文件期望哪种类型的数据。
explosion/spaCy/master/spacy/default_config.cfg
在底层,配置文件被解析成一个字典。它被划分为带有方括号和点符号表示的章节和子章节。例如,[training] 是一个章节,而 [training.batcher] 是一个子章节。子章节可以定义值,就像一个字典一样,或者使用 @ 语法来引用 已注册的函数。这使得配置文件不仅可以定义静态设置,还可以构建对象,例如架构、调度器、优化器或任何其他自定义组件。配置文件的主要顶级章节是
| 章节 | 描述 |
|---|---|
nlp | 定义 nlp 对象、其分词器和 处理流水线 组件名称。 |
components | 定义 流水线组件 及其模型。 |
paths | 数据和其他资源的文件路径。在配置中作为变量重用,例如 ${paths.train},并且可以在 CLI 上被 覆盖。 |
system | 与系统和硬件相关的设置。在配置中作为变量重用,例如 ${system.seed},并且可以在 CLI 上被 覆盖。 |
training | 训练和评估过程的设置和控制。 |
pretraining | 可选的 语言模型预训练 设置和控制。 |
initialize | 在训练之前(但不在运行时)传递给组件的资源和参数,当调用 nlp.initialize 时。 |
运行时和训练时的配置生命周期
流水线的 config.cfg 在 训练 和 运行时 均被视为“单一事实来源”。在底层,Language.from_config 负责使用配置文件中定义的设置构建 nlp 对象。一个 nlp 对象 的配置可通过 nlp.config 访问,它包含有关流水线的全部信息,以及用于训练和初始化它的设置。
在运行时,spaCy 将仅使用配置的 [nlp] 和 [components] 块,并从流水线目录加载所有数据,包括分词规则、模型权重和其他资源。 [training] 块包含训练模型的设置,仅在训练期间使用。 类似地,[initialize] 块定义了在训练之前如何设置初始 nlp 对象,以及是否应该用向量或预训练的 tok2vec 权重或其他组件所需的数据进行初始化。
初始化设置仅在调用 nlp.initialize 时加载和使用(通常在训练之前)。这允许你使用本地数据资源和自定义函数设置流水线,并在你的配置中保留信息——而无需在运行时使其可用。你还可以使用此机制为自定义流水线组件和自定义分词器提供数据路径——有关详细信息,请参阅关于 自定义初始化 的部分。
在命令行上覆盖配置设置
配置系统意味着你可以将所有设置定义在一个地方,并采用一致的格式。没有需要在命令行上设置的参数,也没有隐藏的默认值。但是,仍然可能存在你希望在运行 spacy train 时覆盖配置设置的情况。这包括不应硬编码在配置文件中的文件路径,或与系统相关的设置。
对于这种情况,你可以设置以 -- 开头的附加命令行选项,这些选项对应于要覆盖的配置部分和值。例如,--paths.train ./corpus/train.spacy 设置 [paths] 块中的 train 值。
只能覆盖配置中现有的部分和值。在训练结束时,最终填充的 config.cfg 会与你的流水线一起导出,因此你将始终记录所使用的设置,包括你的覆盖。顺便说一句,覆盖是在 变量 解析之前添加的——因此,如果你需要在多个地方使用一个值,请在你的配置中引用它,并在 CLI 上一次性覆盖它。
通过环境变量添加覆盖
与其将覆盖定义为 CLI 参数,你还可以使用相同的参数语法使用 SPACY_CONFIG_OVERRIDES 环境变量。这对于将模型训练作为自动化过程的一部分特别有用。环境变量优先于 CLI 覆盖和配置文件中的值。
从标准输入读取
在命令行上将配置路径设置为 - 允许你从标准输入读取配置并从其他进程(例如 init config 或你自己的自定义脚本)将其传递过来。这对于快速实验特别有用,因为它允许你在不保存和加载到磁盘的情况下动态生成配置。
使用变量插值
配置系统的另一个非常有用的功能是,它支持值和章节的变量插值。这意味着你只需要定义一次设置,就可以使用 ${section.value} 语法在你的配置中引用它。在此示例中,seed 的值在 [training] 块中重用,并且整个 [training.optimizer] 块在 [pretraining] 中重用,并将变为 pretraining.optimizer。
config.cfg (excerpt)
你也可以在字符串中使用变量。在这种情况下,它的工作方式就像 Python 中的 f 字符串一样。如果变量的值不是字符串,则会将其转换为字符串。
准备训练数据
NLP 项目的训练数据有许多不同的格式。对于一些常见格式,例如 CoNLL,spaCy 提供了你可以从命令行使用的 转换器。在其他情况下,你必须自己准备训练数据。
在将训练数据转换为在 spaCy 中使用时,主要的事情是创建类似于你希望从流水线输出的 Doc 对象。例如,如果你正在创建一个 NER 流水线,加载你的注释并将它们设置为 Doc 上的 .ents 属性就是你所需要担心的全部。在磁盘上,注释将保存为 DocBin 在 .spacy 格式 中,但细节会自动处理。
这是一个从一些 NER 注释创建 .spacy 文件的示例。
preprocess.py
有关如何将训练数据从各种格式转换为与 spaCy 配合使用的更多示例,请查看 教程项目 中的预处理步骤。
在 spaCy v2 中,存储训练数据的推荐方法是使用 特定的 JSON 格式,但在 v3 中,此格式已被弃用。将其作为可读存储格式是可以的,但无需在创建 .spacy 文件之前将数据转换为 JSON 格式。
自定义管道和训练
定义管道组件
您通常会训练一个 管道,该管道由一个或多个组件组成。配置中的 [components] 块定义了可用的管道组件以及它们应该如何创建——通过内置或自定义 工厂,或从现有的训练管道 获取。例如,[components.parser] 定义了管道中名为 "parser" 的组件。您可能希望在训练期间以不同的方式处理组件,最常见的场景是
- 从头开始使用您的数据训练一个新组件。
- 使用更多示例更新现有的训练组件。
- 包含现有的训练组件,而不对其进行更新。
- 包含不可训练的组件,例如基于规则的
EntityRuler或Sentencizer,或完全 自定义组件。
如果组件块定义了 factory,spaCy 将在 内置 或 自定义 组件中查找它,并从头开始创建一个新组件。配置块中定义的所有设置都将作为参数传递给组件工厂。这使您可以配置模型设置和超参数。如果组件块定义了 source,则该组件将从现有的训练管道复制过来,并带有其现有的权重。这使您可以将已经训练好的组件包含在您的管道中,或使用特定于您的用例的数据更新训练好的组件。
config.cfg (excerpt)
配置中的 pipeline 设置在 [nlp] 块中定义了添加到管道中的管道组件的顺序。例如,"parser" 在此处引用 [components.parser]。默认情况下,spaCy 将更新所有可以更新的组件。从头开始创建的可训练组件将使用随机权重进行初始化。对于获取的组件,spaCy 将保留现有权重并 恢复训练。
如果您不希望更新组件,可以通过将其添加到 [training] 块中的 frozen_components 列表中来冻结它。冻结的组件在训练期间不会更新,并在最终训练的管道中按原样包含。它们在调用 nlp.initialize 时也会被排除在外。
使用来自先前组件的预测 v3.1
默认情况下,组件在训练期间是孤立地更新的,这意味着它们看不到管道中任何早期组件的预测结果。组件接收 Example.predicted 作为输入,并将预测结果与 Example.reference 进行比较,而不会将其注释保存在 predicted doc 中。
相反,如果某些组件应该在训练期间设置其注释,请使用 [training] 块中的 annotating_components 设置来指定组件列表。例如,可以将 parser 包含在 annotating_components 中,以便将解析器的特征 DEP 用作标记器的特征。
config.cfg (excerpt)
管道中的任何组件都可以包含为注释组件,包括冻结的组件。冻结的组件可以在训练期间设置注释,就像它们在评估期间或最终管道运行时设置注释一样。下面的配置摘录显示了如何冻结 ner 组件和 sentencizer,以便在训练期间为实体链接器提供所需的 doc.sents 和 doc.ents
config.cfg (excerpt)
类似地,可以冻结预训练的 tok2vec 层,并将其指定在 annotating_components 列表中,以确保下游组件可以使用嵌入层而无需更新它。
使用注册函数
配置文件中定义的训练配置不必仅由静态值组成。某些设置也可以是函数。例如,批大小可以是不会更改的数字,也可以是序列,例如复合值的序列,这已被证明是一种有效的技巧(参见 Smith 等人,2017)。
With static value
要引用函数,可以将 [training.batcher.size] 设为自己的部分,并使用 @ 语法来指定函数及其参数——在本例中是 compounding.v1,在 函数注册表 中定义。定义块中的所有其他值都作为关键字参数传递给函数,并在其初始化时传递。您还可以使用此机制注册 自定义实现和架构,并从您的配置中引用它们。
With registered function
模型架构
模型架构是一个函数,它将 Thinc Model 实例连接起来,然后你可以在组件中使用它,或者将其作为更大的网络的层。你可以将 Thinc 作为围绕 PyTorch、TensorFlow 或 MXNet 等框架的薄包装器,或者你可以在 Thinc 直接实现你的逻辑。有关更多详细信息和示例,请参阅关于层和架构的使用指南。
spaCy 的内置组件绝不会自行构建它们的 Model 实例,因此你无需子类化组件来更改其模型架构。你只需更新配置,使其引用不同的已注册函数即可。一旦组件被创建,它的 Model 实例就已经被分配,因此你无法更改其模型架构。架构就像网络的配方,一旦菜肴准备好,你就无法更改配方。你必须制作一个新的。spaCy 包含各种用于不同任务的内置架构。例如
| 架构 | 描述 |
|---|---|
| HashEmbedCNN | 构建 spaCy 的“标准”嵌入层,该层使用带有子词特征的哈希嵌入和具有层归一化 maxout 的 CNN。 Model[List[Doc], List[Floats2d]] |
| TransitionBasedParser | 构建一个基于转移的解析器模型,该模型用于默认的 EntityRecognizer 和 DependencyParser。 Model[List[Docs], List[List[Floats2d]]] |
| TextCatEnsemble | 一个词袋模型和一个具有内部 CNN 嵌入层的神经网络模型的堆叠集成。用于默认的 TextCategorizer。 Model[List[Doc],Floats2d] |
指标、训练输出和加权分数
当你使用 spacy train 命令训练管道时,你将看到一个表格,显示每次遍历数据后的指标。可用的指标取决于管道组件。管道组件还定义了显示哪些分数以及它们应该如何加权到最终分数,从而决定最佳模型。
你的 config.cfg 中的 training.score_weights 设置允许你自定义表格中显示的分数以及它们的加权方式。在此示例中,标记依赖关系准确率和 NER F 分数对最终分数贡献 40% 各自,而标记准确率占剩余的 20%。分词准确率和速度都显示在表格中,但不计入分数。
score_weights 不一定需要加起来等于 1.0——但建议这样做。当你为给定的管道生成配置时,分数权重是通过组合和标准化管道组件的默认分数权重生成的。默认分数权重由每个管道组件通过 @Language.factory 装饰器上的 default_score_weights 设置定义。默认情况下,所有管道组件的权重相等。如果将分数权重设置为 null,则它将从日志中排除,并且分数不会被加权。
| 名称 | 描述 |
|---|---|
| 损失 | 训练损失,表示优化器需要完成的工作量。应该减少,但通常不会减少到 0。 |
| 精确率 (P) | 预测注释中正确注释的百分比。应该增加。 |
| 召回率 (R) | 恢复的参考注释的百分比。应该增加。 |
| F 分数 (F) | 精确率和召回率的调和平均值。应该增加。 |
| UAS / LAS | 依赖关系解析器的未标记和标记附件分数,即正确弧的百分比。应该增加。 |
| 速度 | 预测速度,以每秒单词数 (WPS) 为单位。应该保持稳定。 |
请注意,如果开发数据具有原始文本,则一些黄金标准的实体可能与预测的分词不一致。这些分词错误被排除在 NER 评估之外。如果你的分词使得模型无法预测 50% 的实体,你的 NER F 分数可能仍然看起来不错。
自定义函数
训练配置文件中的已注册函数可以引用内置实现,但你也可以插入完全自定义实现。你所需要做的就是使用 @spacy.registry 装饰器注册你的函数,并指定相应的 注册表,例如 @spacy.registry.architectures,以及要分配给你的函数的字符串名称。注册自定义函数允许你插入在 PyTorch 或 TensorFlow 中定义的模型,对 nlp 对象进行自定义修改,创建自定义优化器或调度器,或流式传输数据并在训练时动态地预处理它。
每个自定义函数可以有任意数量的参数,这些参数通过 配置传入,就像内置函数一样。如果你的函数定义了默认参数值,spaCy 能够在你运行 init fill-config 时自动填充你的配置。如果你想确保在配置中始终显式设置给定的参数,请避免为其设置默认值。
使用自定义代码进行训练
spacy train 配方允许你指定一个可选参数 --code,该参数指向一个 Python 文件。该文件在训练之前被导入,并允许你将自定义函数和架构添加到函数注册表中,然后可以从你的 config.cfg 中引用它们。这使你能够使用自定义组件训练 spaCy 管道,而无需重新实现整个训练工作流程。稍后使用 spacy package 封装你的训练管道时,你可以提供一个或多个要包含在包中并在其 __init__.py 中导入的 Python 文件。这意味着任何自定义架构、函数或 组件 都将与你的管道一起发布,并在加载时注册。有关详细信息,请参阅关于保存和加载管道的文档。
示例:修改 nlp 对象
对于许多用例,你不一定想实现整个 Language 子类和语言数据从头开始——通常足以进行一些小的修改,例如调整 分词规则或 语言默认值,如停用词。配置允许你提供五个可选的回调函数,这些函数让你可以在生命周期的不同点访问语言类和 nlp 对象
| 回调 | 描述 |
|---|---|
nlp.before_creation | 在创建 nlp 对象之前调用,并接收语言子类,如 English(不是实例)。对于写入 Language.Defaults(除了分词器设置)很有用。 |
nlp.after_creation | 在创建 nlp 对象后立即调用,但在将管道组件添加到管道之前,并接收 nlp 对象。 |
nlp.after_pipeline_creation | 在创建并添加管道组件后立即调用,并接收 nlp 对象。对于修改管道组件很有用。 |
initialize.before_init | 在初始化管道组件之前调用,并接收 nlp 对象进行就地修改。对于修改分词器设置很有用,类似于 v2 基本模型选项。 |
initialize.after_init | 在初始化管道组件后调用,并接收 nlp 对象进行就地修改。 |
@spacy.registry.callbacks 装饰器允许您在 callbacks 注册表 中注册您的自定义函数,并赋予它一个名称。然后,您可以使用 @callbacks 键在配置块中引用该函数。如果一个块包含以 @ 开头的键,则将其解释为对函数的引用。由于您已经注册了该函数,spaCy 在您在配置中引用 "customize_language_data" 时会知道如何创建它。这是一个在 nlp 对象创建之前运行的回调示例,它会将一个自定义停用词添加到默认停用词列表中
functions.py
任何注册的函数 – 在这种情况下 create_callback – 也可以接受 参数,这些参数可以由 配置设置。这使您能够实现和跟踪不同的配置,而无需修改您的代码。您可以选择任何对您的用例有意义的参数。在这个例子中,我们添加了参数 extra_stop_words(一个字符串列表)和 debug(布尔值),用于在函数运行时打印附加信息。
functions.py
有了定义附加代码的 functions.py 和更新后的 config.cfg,您现在可以运行 spacy train 并将参数 --code 指向您的 Python 文件。在加载配置之前,spaCy 将导入 functions.py 模块,您的自定义函数将被注册。
示例:修改分词器设置
使用 initialize.before_init 回调来修改训练新管道时的分词器设置。编写一个修改分词器设置的注册回调,并在您的配置中指定此回调
functions.py
在训练时,通过 --code 选项提供上述函数
由于此回调仅在训练前的单次初始化步骤中调用,因此回调代码不需要与最终的管道包一起打包。但是,为了方便其他人复制您的训练设置,您可以选择将初始化回调与管道包一起打包或单独发布。
示例:自定义日志记录函数
在训练期间,每个步骤的结果都会传递给一个日志记录函数。默认情况下,这些结果会使用 ConsoleLogger 写入控制台。还内置了对使用 Weights & Biases 的 WandbLogger 将日志文件写入。在每个步骤中,日志记录函数都会接收一个包含以下键的 字典
| 键 | 值 |
|---|---|
epoch | 已完成的数据遍历次数。 int |
step | 已完成的步骤数。 int |
score | 从上次评估中获得的主要分数,在开发集上衡量。 float |
other_scores | 从上次评估中获得的其他分数,在开发集上衡量。 Dict[str, Any] |
losses | 累积的训练损失,按组件名称键入。 Dict[str, float] |
checkpoints | 一个包含先前结果的列表,其中每个结果都是一个 (score, step) 元组。 List[Tuple[float, int]] |
您可以轻松地实现并插入您自己的记录器,以自定义方式记录训练结果,或将其发送到您选择的实验管理跟踪器。在这个例子中,函数 my_custom_logger.v1 将表格结果写入文件
functions.py
示例:自定义批大小调度
您还可以实现自己的批大小调度,在训练期间使用。 @spacy.registry.schedules 装饰器允许您在 schedules 注册表 中注册该函数,并赋予它一个字符串名称
functions.py
在您的配置中,您现在可以通过 @schedules 在 [training.batcher.size] 块中引用该调度。如果一个块包含以 @ 开头的键,则将其解释为对函数的引用。块中的所有其他设置都将作为关键字参数传递给该函数。请记住,配置不应有任何隐藏的默认值,并且函数上的所有参数都需要在配置中表示。
config.cfg (excerpt)
定义自定义架构
内置的管道组件,例如词性标注器或命名实体识别器,使用默认神经网络 模型 构建。您可以通过实现自己的自定义模型并在创建管道组件时在配置中提供这些模型来完全更改模型架构。有关更多详细信息,请参阅有关 层和模型架构 的文档。
functions.py
自定义初始化
当您从头开始训练新模型时,spacy train 将调用 nlp.initialize 来初始化管道并加载所需的数据。所有这些设置都定义在配置的 [initialize] 块中,因此您可以跟踪初始 nlp 对象是如何创建的。初始化过程通常包括以下内容
- 加载在
[initialize]配置中定义的数据资源,包括 词向量 和 预训练 tok2vec 权重。 - 调用分词器(如果已实现,例如对于 中文)和管道组件的
initialize方法,并提供一个回调来访问训练数据、当前的nlp对象和在[initialize]配置中定义的任何 自定义参数。 - 在 管道组件 中:如果需要,使用数据来 推断缺失的形状 并设置标签方案(如果未提供标签)。组件还可以加载其他数据,例如查找表或字典。
初始化步骤允许配置定义管道所需的所有设置,同时保持设置与仅应在训练前用于设置初始管道的函数之间的分离,以及需要在运行时可用的逻辑和配置。如果没有这种分离,使用相同的、可重现的配置文件将非常困难,因为训练所需的组件设置(从外部文件加载数据)与运行时所需的组件设置(加载保存的 nlp 对象中包含的内容,不依赖于外部文件)不匹配。
初始化标签
像 EntityRecognizer 或 DependencyParser 这样的内置管道组件需要知道它们可用的标签以及相关的内部元信息,才能初始化它们的模型权重。通过在初始化时提供的 get_examples 回调,它们能够自动从训练数据中读取标签,这非常方便——但它也可能减慢训练过程,因为每次运行都需要计算这些信息。
init labels 命令允许您自动生成包含所有受支持组件的标签数据的 JSON 文件。然后,您可以将标签传递到各个组件的 [initialize] 设置中,以便它们可以更快地初始化。
在底层,该命令委托给管道组件的 label_data 属性,例如 EntityRecognizer.label_data。
数据实用工具
spaCy 包含各种功能和实用工具,可以轻松地使用自己的数据训练模型,管理训练和评估语料库,转换现有的注释并配置数据增强策略,以获得更强大的模型。
转换现有的语料库和注释
如果您在标准格式(如 .conll 或 .conllu)中拥有训练数据,那么将其转换为与 spaCy 配合使用最简单的方法是运行 spacy convert 并传递一个文件和一个输出目录。默认情况下,该命令将根据文件扩展名选择转换器。
二进制 .spacy 格式是序列化的 DocBin,包含一个或多个 Doc 对象。它在存储方面非常高效,尤其是在打包多个文档在一起时。您还可以手动创建 Doc 对象,因此您可以编写自己的自定义逻辑来转换和存储现有的注释,以供 spaCy 使用。
Training data from Doc objects
使用语料库
配置中的 [corpora] 块允许您定义用于训练、评估、预训练或任何其他自定义工作流程的数据资源。 corpora.train 和 corpora.dev 在 spaCy 的默认配置中用作约定,但您也可以定义任何其他自定义块。配置中的每个部分都应解析为 Corpus – 例如,使用 spaCy 的内置 语料库读取器,它接受指向二进制 .spacy 文件的路径。 [training] 块中的 train_corpus 和 dev_corpus 字段指定在您的配置中找到语料库的位置。这使得仅通过更改单个配置设置即可更换不同的语料库变得容易。
与其使 [corpora] 成为包含数据每个部分多个子部分的块,您还可以使用单个函数返回一个字典,其中包含以语料库名称(例如 "train" 和 "dev")为键的语料库。如果需要将单个文件拆分为用于训练和评估的语料库,而无需两次加载相同的文件,这将非常有用。
默认情况下,训练数据加载到内存中并在每个 epoch 之前进行洗牌。如果语料库太大而无法放入内存进行训练,请使用自定义读取器进行流式传输,如下一节所述。
自定义数据读取和批处理
某些用例需要流式传输数据或动态操作数据集,而不是预先生成所有数据并将其存储到磁盘。与其使用内置的 Corpus 读取器(它使用静态文件路径),您可以创建一个并注册一个自定义函数,该函数生成 Example 对象。
在下面的示例中,我们假设一个自定义函数 read_custom_data,它加载或生成带有相关文本分类注释的文本。然后,在生成最终的 Example 对象之前,会创建输入文本的少量词汇变化。 @spacy.registry.readers 装饰器允许您在 readers 注册表 中注册创建自定义读取器的函数,并为其分配一个字符串名称,以便在您的配置中使用。注册函数上的所有参数都将作为配置设置提供——在本例中,source。
functions.py
如果语料库太大而无法加载到内存中或语料库读取器是无限生成器,请使用设置 max_epochs = -1 来指示应流式传输训练语料库。使用此设置,训练语料库仅被流式传输和批处理,不被洗牌,因此任何洗牌都需要在语料库读取器本身中实现。在下面的示例中,一个生成包含偶数或奇数句子语料库的读取器被用于训练语料库,并使用有限数量的示例用于开发语料库。开发语料库应始终是有限的,并在评估步骤期间适合内存。 max_steps 和/或 patience 用于确定何时停止训练。
functions.py
如果训练语料库是流式传输的,则初始化步骤会查看语料库中的前 100 个示例,以查找每个组件的标签。如果这不够,您需要在 [initialize] 块中提供每个组件的标签。 init labels 可用于生成正确格式的 JSON 文件,您可以扩展这些文件以包含完整的标签集。
我们还可以通过在 batchers 注册表 中注册一个新的批处理函数来自定义批处理策略。批处理器将项目流转换为批处理流。spaCy 具有几个有用的内置 批处理策略,具有可自定义的大小,但也可以轻松实现自己的批处理策略。例如,以下函数采用生成的 Example 对象流,并删除那些具有相同的底层原始文本的对象,以避免每个批次中的重复项。请注意,在更实际的实现中,您还需要检查注释是否相同。
functions.py
数据增强
数据增强是应用对训练数据进行小的修改的过程。对于标点符号和大小写替换特别有用——例如,如果您的语料库仅使用智能引号,并且您想包含使用普通引号的变化,或者通过包含混合大小写示例来使模型对大小写不敏感。
在训练期间使用数据增强的最简单方法是在配置的 [corpora.train] 部分中为训练语料库提供一个 augmenter。内置的 orth_variants 增强器创建一个数据增强回调,该回调使用正交变体替换。
config.cfg (excerpt)
orth_variants 参数允许您传递一个替换规则字典,通常从 JSON 文件加载。有两种类型的正交变体规则:"single" 用于应替换的单个标记(例如连字符),"paired" 用于标记对(例如引号)。
orth_variants.json
编写自定义数据增强器
使用 @spacy.augmenters 注册表,你也可以注册自己的数据增强回调函数。回调函数应该是一个函数,它接收当前的 nlp 对象和一个训练 Example,并产生 Example 对象。请记住,增强器应该产生你想要在语料库中使用的所有示例,而不仅仅是增强后的示例(除非你想要增强所有示例)。
这是一个自定义增强回调函数的示例,它在 “SpOnGeBoB cAsE” 中产生文本变体。注册的函数接收一个参数 randomize,该参数可以通过配置设置,并决定是否随机应用大小写转换。增强器产生两个 Example 对象:原始示例和增强示例。
创建修改后的 Example 对象的一个简单方法是使用 Example.from_dict 方法,并使用从修改后的文本创建的新引用 Doc。在这种情况下,只有大小写发生了变化,因此原始示例和增强示例之间的 ORTH 值才会有所不同。
请注意,如果你的数据增强策略涉及更改分词(例如,删除或添加标记),并且你的训练示例包含基于标记的注释,例如依存关系解析或实体标签,则需要注意调整 Example 对象,使其注释匹配并保持有效。
内部训练 API
从 Python 脚本训练 v3.2
如果你想从 Python 脚本而不是使用 spacy train CLI 命令来运行训练,你可以直接调用 train 辅助函数。它接收配置文件的路径、可选的输出目录和可选的 配置覆盖字典。
内部训练循环 API
Example 对象包含带注释的训练数据,也称为黄金标准。它使用一个 Doc 对象初始化,该对象将保存预测结果,以及另一个包含黄金标准注释的 Doc 对象。如果它们在分词上不同,它还包括这两个文档之间的对齐关系。 Example 类确保 spaCy 可以依赖于一个标准化的格式,该格式通过管道传递。例如,假设我们想定义黄金标准的词性标签
由于这非常冗长,因此还有另一种创建带有黄金标准注释的参考 Doc 的方法。函数 Example.from_dict 接受一个字典,其中包含指定注释的关键字参数,例如 tags 或 entities。使用生成的 Example 对象及其黄金标准注释,可以更新模型以学习一个包含三个单词及其分配的词性标签的句子。
这里还有一个例子,展示了如何定义黄金标准的命名实体。添加到标签前的字母指的是 BILUO 方案的标签 – O 是实体外的标记,U 是单个实体单元,B 是实体的开头,I 是实体内的标记,L 是实体中的最后一个标记。
当然,仅仅向模型展示一个示例一次是不够的。特别是如果你只有很少的示例,你希望训练多次迭代。在每次迭代中,训练数据都会被洗牌,以确保模型不会基于示例的顺序进行任何概括。提高学习效果的另一种技术是设置一个dropout 率,即随机“删除”单个特征和表示的速率。这使得模型更难记住训练数据。例如,0.25 dropout 意味着每个特征或内部表示都有 1/4 的可能性被删除。
Example training loop
nlp.update 方法接受以下参数
| 名称 | 描述 |
|---|---|
示例 | Example 对象。 update 方法接受一个序列,因此你可以批量处理你的训练示例。 |
dropout | Dropout 率。使模型更难记住数据。 |
sgd | 一个 Optimizer 对象,它更新模型的权重。如果未设置,spaCy 将创建一个新的对象并将其保存以供进一步使用。 |