spaCy-保存和加载
文档说明
版本
✔️ ver_1.0.1
概述
如果你修改了管道、词汇、向量和实体,或者对组件模型进行了更新,你会希望保存你的进度——例如,你的nlp
对象中的所有内容。这意味着你必须将其内容和结构转换为可以保存的格式,例如文件或字节字符串。这个过程称为序列化。spaCy
带有内置的序列化方法并支持Pickle
协议。
什么是
Pickle
?Pickle
是Python
内置的对象持久化系统。它允许你在进程之间传输任意Python
对象。这通常用于将对象加载到磁盘或从磁盘加载对象,有时也用于分布式计算,例如使用PySpark
或Dask
。当你反序列化一个对象时,你就同意了执行它包含的任何代码。这就像一个字符串调用eval()
——所以不要从不受信任的来源反序列化对象。
所有容器类,如:Language
(nlp), Doc
, Vocab
和StringStore
都有以下方法:
方法 | 返回 | 例子 |
---|---|---|
to_bytes | bytes | data = nlp.to_bytes() |
from_bytes | object | nlp.from_bytes(data) |
to_disk | - | nlp.to_disk("/path") |
from_disk | object | nlp.from_disk("/path") |
序列化管道
序列化管道时,请记住,这只会为哥哥组件单独保存为二进制数据,以允许spaCy
恢复它们——而不是整个对象。这是一件好事,因为它使序列化变得安全。但这也意味着你必须小心地存储配置,其中包含管道配置和所有相关设置。
保存元数据和配置
nlp.meta
属性是一个可序列化的JSON
字典,包含所有管道的元信息,如作者和许可证信息。nlp.config
属性是包含训练配置、管道组件工厂以及其他设置的字典。它通过管道保存为config.cfg
。
#序列化
config = nlp.config
bytes_data = nlp.to_bytes()
#序列化
config = nlp.config
bytes_data = nlp.to_bytes()
#反序列化
lang_cls = spacy.util.get_lang_class(config["nlp"]["lang"])
nlp = lang_cls.from_config(config)
nlp.from_bytes(bytes_data)
#反序列化
lang_cls = spacy.util.get_lang_class(config["nlp"]["lang"])
nlp = lang_cls.from_config(config)
nlp.from_bytes(bytes_data)
这也是spaCy
在加载管道时的底层工作方式:它加载包含语言和管道信息的config.cfg
文件,初始化语言类,基于配置创建和添加管道组件,然后加载二进制数据。你可以在此处阅读有关此过程的更多信息。
序列化Doc
对象
如果你正在处理大量数据,你可能需要在机器之间传递分析,比如使用Dask
、Spark
之类的东西,或者只是将工作保存到磁盘时。通常比较有效的方式是使用Doc.to_array
方法,只需序列化numpy
数组 - 但有时你可能需要一种更通用的方法来保存和恢复Doc
对象。
DocBin
类使得序列化和反序列化Doc
对象集合变得容易,并且比在每个单独的Doc
对象上调用Doc.to_bytes
方法更有效率。你还可以控制要保存哪些数据,并且可以将托盘合并在一起以轻松进行map/reduce
式的处理过程。
import spacy
from spacy.tokens import DocBin
doc_bin = DocBin(attrs=["LEMMA", "ENT_IOB", "ENT_TYPE"], store_user_data=True)
texts = ["Some text", "Lots of texts...", "..."]
nlp = spacy.load("en_core_web_sm")
for doc in nlp.pipe(texts):
doc_bin.add(doc)
bytes_data = doc_bin.to_bytes()
# Deserialize later, e.g. in a new process
nlp = spacy.blank("en")
doc_bin = DocBin().from_bytes(bytes_data)
docs = list(doc_bin.get_docs(nlp.vocab))
import spacy
from spacy.tokens import DocBin
doc_bin = DocBin(attrs=["LEMMA", "ENT_IOB", "ENT_TYPE"], store_user_data=True)
texts = ["Some text", "Lots of texts...", "..."]
nlp = spacy.load("en_core_web_sm")
for doc in nlp.pipe(texts):
doc_bin.add(doc)
bytes_data = doc_bin.to_bytes()
# Deserialize later, e.g. in a new process
nlp = spacy.blank("en")
doc_bin = DocBin().from_bytes(bytes_data)
docs = list(doc_bin.get_docs(nlp.vocab))
如果store_user_data
设置为True
,则Doc.user_data
也将被序列化,其中包括扩展属性的值 (如果它们使用msgpack
序列化)。
关于序列化扩展属性的重要说明
Doc.user_data
和扩展属性只会序列化属性的值。要恢复值并访问它们可以通过doc._.
属性,你需要再次在Doc
上注册全局属性。
docs = list(doc_bin.get_docs(nlp.vocab))
Doc.set_extension("my_custom_attr", default=None)
print([doc._.my_custom_attr for doc in docs])
docs = list(doc_bin.get_docs(nlp.vocab))
Doc.set_extension("my_custom_attr", default=None)
print([doc._.my_custom_attr for doc in docs])
使用Pickle
# 例子
doc = nlp("This is a text.")
data = pickle.dumps(doc)
# 例子
doc = nlp("This is a text.")
data = pickle.dumps(doc)
当pickling``spaCy
的对象时,例如Doc
或者EntityRecognizer
,请记住,它们都需要共享Vocab
(包括字符串到哈希映射、标签方案和可选向量)。这意味着它们pickled
的表示可能会变得非常大,尤其是当你加载了词向量时,因为它不仅包括对象本身,还包括它所依赖的整个共享词汇。
如果你需要pickle
多个对象,请尝试将它们pickle
在一起而不是单独pickle
。例如,不是pickle
所有管道组件,而是pickle
整个管道一次。不要单独pickle
多个Doc
对象,直接pickle
一个Doc
对象列表。由于它们都共享对同一个Vocab
对象的引用,因此它只会被包含一次。
# 使用共享数据picking对象
doc1 = nlp("Hello world")
doc2 = nlp("This is a test")
doc1_data = pickle.dumps(doc1)
doc2_data = pickle.dumps(doc2)
print(len(doc1_data) + len(doc2_data)) # 6636116 😞
# 整合pickle后,文件小了一半
doc_data = pickle.dumps([doc1, doc2])
print(len(doc_data)) # 3319761 😃
# 使用共享数据picking对象
doc1 = nlp("Hello world")
doc2 = nlp("This is a test")
doc1_data = pickle.dumps(doc1)
doc2_data = pickle.dumps(doc2)
print(len(doc1_data) + len(doc2_data)) # 6636116 😞
# 整合pickle后,文件小了一半
doc_data = pickle.dumps([doc1, doc2])
print(len(doc_data)) # 3319761 😃
picking
spans
和tokens
不支持pickling
spans
和tokens
对象。他们只是对Doc
对象的一个角度的看法,并不能单独存在。pickling
它们意味着拉入父文档及其词汇表,实际上和直接pickling
父Doc
相比并没有优势。
# 不要仅仅pickle一个span
- data = pickle.dumps(doc[10:20])
+ data = pickle.dumps(doc)
# 不要仅仅pickle一个span
- data = pickle.dumps(doc[10:20])
+ data = pickle.dumps(doc)
如果你真的只需要一个spans
——例如,一个特定的句子——你可以使用Span.as_doc
复制它并将其转换为Doc
对象。但是,请注意,这不会让你恢复spans
之外上下文信息。
+ span_doc = doc[10:20].as_doc()
data = pickle.dumps(span_doc)
+ span_doc = doc[10:20].as_doc()
data = pickle.dumps(span_doc)
实现序列化方法
当你调用nlp.to_disk
, nlp.from_disk
方法或加载管道包时,spaCy
将遍历管道中的组件,检查它们是否公开了to_disk
或者from_disk
方法,如果已公开,则使用管道目录的路径加上组件的字符串名称来调用它。例如,如果你正在调用nlp.to_disk("/path")
,则命名实体识别器的数据将保存在/path/ner
。
如果你使用依赖于外部数据的自定义管道组件(例如,模型权重或术语列表),可以公开(暴露)你的自定义组件的from_disk
和to_disk
或to_bytes
和from_bytes
方法来使用spaCy
的内置组件序列化方法。当一个nlp
对象保存或加载管道中包含的组件对象时,组件将能够对其自身进行序列化和反序列化。
📖自定义组件和数据
有关如何使用依赖于数据资源的管道组件以及在训练和运行时管理数据加载和初始化的更多详细信息,请参阅初始化和序列化 组件数据的使用指南。
以下示例显示了一个自定义组件,该组件保有任意JSON
(可序列化数据),允许用户添加到该数据,并通过JSON
文件保存和加载数据。
真实世界的例子
要查看实际的自定义序列化方法,请查看新的EntityRuler
组件及其源码。如果管道被序列化到磁盘,添加到组件的模式(Patterns)将保存到.jsonl
文件中,如果管道被序列化为字节,则保存到字节串。这允许使用基于规则的实体识别器保存管道,并将所有规则包含在组件数据中。
from spacy.util import ensure_path
@Language.factory("my_component")
class CustomComponent:
def __init__(self):
self.data = []
def __call__(self, doc):
# Do something to the doc here
return doc
def add(self, data):
# Add something to the component's data
self.data.append(data)
def to_disk(self, path, exclude=tuple()):
# This will receive the directory path + /my_component
path = ensure_path(path)
if not path.exists():
path.mkdir()
data_path = path / "data.json"
with data_path.open("w", encoding="utf8") as f:
f.write(json.dumps(self.data))
def from_disk(self, path, exclude=tuple()):
# This will receive the directory path + /my_component
data_path = path / "data.json"
with data_path.open("r", encoding="utf8") as f:
self.data = json.loads(f)
return self
from spacy.util import ensure_path
@Language.factory("my_component")
class CustomComponent:
def __init__(self):
self.data = []
def __call__(self, doc):
# Do something to the doc here
return doc
def add(self, data):
# Add something to the component's data
self.data.append(data)
def to_disk(self, path, exclude=tuple()):
# This will receive the directory path + /my_component
path = ensure_path(path)
if not path.exists():
path.mkdir()
data_path = path / "data.json"
with data_path.open("w", encoding="utf8") as f:
f.write(json.dumps(self.data))
def from_disk(self, path, exclude=tuple()):
# This will receive the directory path + /my_component
data_path = path / "data.json"
with data_path.open("r", encoding="utf8") as f:
self.data = json.loads(f)
return self
将组件添加到管道并添加数据后,我们可以将nlp
对象序列化到一个目录,该目录将调用自定义组件的to_disk
方法。
nlp = spacy.load("en_core_web_sm")
my_component = nlp.add_pipe("my_component")
my_component.add({"hello": "world"})
nlp.to_disk("/path/to/pipeline")
nlp = spacy.load("en_core_web_sm")
my_component = nlp.add_pipe("my_component")
my_component.add({"hello": "world"})
nlp.to_disk("/path/to/pipeline")
目录的内容将如下所示。CustomComponent.to_disk
将数据转换为JSON
字符串并将其保存到其子目录下data.json
文件中:
# 目录结构
└── /path/to/pipeline
├── my_component # data serialized by "my_component"
│ └── data.json
├── ner # data for "ner" component
├── parser # data for "parser" component
├── tagger # data for "tagger" component
├── vocab # pipeline vocabulary
├── meta.json # pipeline meta.json
├── config.cfg # pipeline config
└── tokenizer # tokenization rules
# 目录结构
└── /path/to/pipeline
├── my_component # data serialized by "my_component"
│ └── data.json
├── ner # data for "ner" component
├── parser # data for "parser" component
├── tagger # data for "tagger" component
├── vocab # pipeline vocabulary
├── meta.json # pipeline meta.json
├── config.cfg # pipeline config
└── tokenizer # tokenization rules
当你加载数据时,spaCy
将通过给定的文件路径调用自定义组件的from_disk
方法,然后组件就会加载data.json
文件的内容,将它们转换为Python
对象并恢复组件状态。当然,这同样适用于其他类型的数据——例如,你可以为不同库(如TensorFlow
或PyTorch
)训练的模型添加包装器,并在加载管道包时让spaCy
自动加载其权重。
!关于加载自定义组件的重要说明
当你加载有自定义组件的管道时,请确保组件可用并且@Language.component
或者@Language.factory
装饰器在你的管道被加载之前已经执行。否则,spaCy
将不知道如何将组件工厂的字符串名称(如"my_component")解析回函数。有关更多详细信息,请参阅有关 [添加工厂]或使用入口点(entry points)使你的扩展包自动向spaCy
公开你的自定义组件。
使用入口点(entry points)
入口点让你可以将你编写的Python
包的某些部分公开(暴露)给其他Python
包。这允许一个应用程序通过在其setup.py
文件中暴露一个入口点来简单的自定义另一个程序的行为。有关Python
入口点的快速而有趣的介绍,请查看 这篇出色的博客文章。spaCy
可以从几个不同的入口点加载自定义函数,以添加管道组件工厂、语言类和其他设置。为了让spaCy
可以使用你的入口点,你的包需要暴露这些入口点并且它需要安装在同一个环境中——就是这样。
入口点 | 描述 |
---|---|
spacy_factories | 管道组件工厂的一组入口点,以组件名称为键。可用于公开由另一个包定义的自定义组件。 |
spacy_languages | 自定义Language subclass 的入口点组,由语言的shotcut为键。 |
spacy_lookups | 自定义Lookups 的入口点组,包括词形还原(lemmatizer)数据。使用spaCy 的spacy-lookups-data 包。 |
spacy_displacy_colors | displaCy 可视化器的自定义标签颜色入口点组。键名无关紧要,但它应该指向标签和颜色值的字典。对于预测不同实体类型的自定义模型很有用。 |
通过入口点自定义组件
当你加载管道时,spaCy
通常会使用它的config.cfg
来设置语言类并构建管道。管道被指定为一个字符串列表,例如pipeline = ["tagger", "parser", "ner"]
。对于其中的每一个字符串,spaCy
将调用nlp.add_pipe
并在装饰器@Language.component
和 @Language.factory
定义的所有工厂中查找该名称。这意味着你必须在加载管道之前导入自定义组件。
使用入口点,管道包和扩展包可以自定义"spacy_factories"
,在Language
类初始化时会在后台自动加载。因此,如果用户安装了你的软件包,他们将能够使用你的组件——即使没有导入它们!
为了理解入口点博客文章的主题,请考虑以下自定义的spaCy
管道组件,该组件在调用时会打印一条蛇:
# 包目录结构
├── snek.py # the extension code
└── setup.py # setup file for pip installation
# 包目录结构
├── snek.py # the extension code
└── setup.py # setup file for pip installation
SNEK.PY
from spacy.language import Language
snek = """
--..,_ _,.--.
`'.'. .'`__ o `;__. {text}
'.'. .'.'` '---'` `
'.`'--....--'`.'
`'--....--'`
"""
@Language.component("snek")
def snek_component(doc):
print(snek.format(text=doc.text))
return doc
SNEK.PY
from spacy.language import Language
snek = """
--..,_ _,.--.
`'.'. .'`__ o `;__. {text}
'.'. .'.'` '---'` `
'.`'--....--'`.'
`'--....--'`
"""
@Language.component("snek")
def snek_component(doc):
print(snek.format(text=doc.text))
return doc
由于它是一个非常复杂和精密的模块,因此你希望将其拆分为它自己的包,以便你可以对其进行版本控制并将其上传到PyPi
。你还希望你的自定义包能够在config.cfg
中定义pipeline = ["snek"]
。 为此,你需要能够告诉spaCy
在哪里可以找到"snek"
组件。如果你不这样做,当你尝试加载管道时spaCy
将引发错误,因为没有内置的"snek"
组件。,现在你可以编辑setup.py
中的entry_points
字典,来向工厂添加一个入口:
入口点语法 组的
Python
入口点组是一个字符串列表,每个字符串都遵循name = module:object
的语法结构。 在这个例子中,入口点被命名为snek
并指向snek
模块中的函数snek_component
,即snek.py
。
# SETUP.PY
from setuptools import setup
setup(
name="snek",
entry_points={
"spacy_factories": ["snek = snek:snek_component"]
}
)
# SETUP.PY
from setuptools import setup
setup(
name="snek",
entry_points={
"spacy_factories": ["snek = snek:snek_component"]
}
)
顺便说一句,同一个包可以暴露多个入口点。要使它们在spaCy
中可用,你唯一需要做的就是在你的环境中安装该软件包:
python setup.py develop
python setup.py develop
spaCy
现在能够创建管道组件"snek"
——即使你从未导入过snek_component
。当你保存nlp.config
到磁盘,它包含一个"snek"
组件的入口,你 使用此配置训练的任何管道都将包含该组件。并且只要你的snek
包已安装,管道就知道如何加载它 - 。
# CONFIG.CFG(摘录)
[nlp]
lang = "en"
+ pipeline = ["snek"]
[components]
+ [components.snek]
+ factory = "snek"
# CONFIG.CFG(摘录)
[nlp]
lang = "en"
+ pipeline = ["snek"]
[components]
+ [components.snek]
+ factory = "snek"
>>> from spacy.lang.en import English
>>> nlp = English()
>>> nlp.add_pipe("snek") # this now works! 🐍🎉
>>> doc = nlp("I am snek")
--..,_ _,.--.
`'.'. .'`__ o `;__. I am snek
'.'. .'.'` '---'` `
'.`'--....--'`.'
`'--....--'`
>>> from spacy.lang.en import English
>>> nlp = English()
>>> nlp.add_pipe("snek") # this now works! 🐍🎉
>>> doc = nlp("I am snek")
--..,_ _,.--.
`'.'. .'`__ o `;__. I am snek
'.'. .'.'` '---'` `
'.`'--....--'`.'
`'--....--'`
除了让你的snek
组件成为一个简单的 无状态组件,你还可以把它变成一个接受设置的工厂。用户可以在添加你的组件时加载一个可选的config
并自定义它的表现(appearance)--例如,snek_style
。
CONFIG.CFG(摘录)
[components.snek]
factory = "snek"
+ snek_style = "basic"
[components.snek]
factory = "snek"
+ snek_style = "basic"
SNEKS = {"basic": snek, "cute": cute_snek} # collection of sneks
@Language.factory("snek", default_config={"snek_style": "basic"})
class SnekFactory:
def __init__(self, nlp: Language, name: str, snek_style: str):
self.nlp = nlp
self.snek_style = snek_style
self.snek = SNEKS[self.snek_style]
def __call__(self, doc):
print(self.snek)
return doc
SNEKS = {"basic": snek, "cute": cute_snek} # collection of sneks
@Language.factory("snek", default_config={"snek_style": "basic"})
class SnekFactory:
def __init__(self, nlp: Language, name: str, snek_style: str):
self.nlp = nlp
self.snek_style = snek_style
self.snek = SNEKS[self.snek_style]
def __call__(self, doc):
print(self.snek)
return doc
# SETUP.PY
entry_points={
- "spacy_factories": ["snek = snek:snek_component"]
+ "spacy_factories": ["snek = snek:SnekFactory"]
}
# SETUP.PY
entry_points={
- "spacy_factories": ["snek = snek:snek_component"]
+ "spacy_factories": ["snek = snek:SnekFactory"]
}
工厂(factory
)还可以实现其他管道组件方法,例如,序列化相关的to_disk
和from_disk
方法,甚至是update
方法使组件可训练。如果组件公开了一个from_disk
方法并包含在管道中,spaCy
将在加载时调用它。这使你可以使用管道包发送自定义数据。当你使用nlp.to_disk
方法保存管道并且组件暴露to_disk
方法时,它将使用磁盘路径调用。
from spacy.util import ensure_path
def to_disk(self, path, exclude=tuple()):
path = ensure_path(path)
if not path.exists():
path.mkdir()
snek_path = path / "snek.txt"
with snek_path.open("w", encoding="utf8") as snek_file:
snek_file.write(self.snek)
def from_disk(self, path, exclude=tuple()):
snek_path = path / "snek.txt"
with snek_path.open("r", encoding="utf8") as snek_file:
self.snek = snek_file.read()
return self
from spacy.util import ensure_path
def to_disk(self, path, exclude=tuple()):
path = ensure_path(path)
if not path.exists():
path.mkdir()
snek_path = path / "snek.txt"
with snek_path.open("w", encoding="utf8") as snek_file:
snek_file.write(self.snek)
def from_disk(self, path, exclude=tuple()):
snek_path = path / "snek.txt"
with snek_path.open("r", encoding="utf8") as snek_file:
self.snek = snek_file.read()
return self
上面的示例将当前的蛇序列化到数据目录下的snek.txt
文件中。当管道使用snek
组件并且已经载入,它将打开snek.txt
文件并且使其可用于该组件。
通过入口点自定义语言类(language classes)
为了保持上一个示例的主题和 这篇关于入口点的博客文章,让我们假设你想为你的自定义管道实现自己的SnekLanguage类 - 但你不一定想修改 spaCy 的代码以添加一种语言。在你的包中,你可以实现以下 自定义语言子类:
# SNEK.PY
from spacy.language import Language
class SnekDefaults(Language.Defaults):
stop_words = set(["sss", "hiss"])
class SnekLanguage(Language):
lang = "snk"
Defaults = SnekDefaults
# SNEK.PY
from spacy.language import Language
class SnekDefaults(Language.Defaults):
stop_words = set(["sss", "hiss"])
class SnekLanguage(Language):
lang = "snk"
Defaults = SnekDefaults
除了spacy_factories
,还有一个入口点选项spacy_languages
,它将语言代码(language codes)映射到特定于语言的Language
子类:
# SETUP.PY
from setuptools import setup
setup(
name="snek",
entry_points={
"spacy_factories": ["snek = snek:SnekFactory"],
+ "spacy_languages": ["snk = snek:SnekLanguage"]
}
)
# SETUP.PY
from setuptools import setup
setup(
name="snek",
entry_points={
"spacy_factories": ["snek = snek:SnekFactory"],
+ "spacy_languages": ["snk = snek:SnekLanguage"]
}
)
在spaCy
中,你可以加载自定义snk
语言,它将通过自定义入口点解析SnekLanguage
。这与你训练的管道包密切相关,它将在config.cfg
文件中指定lang = snk
从而使spaCy
不会引发错误,因为该语言在核心库中不可用。
通过入口点自定义显示颜色
如果你正在为自定义域训练命名实体识别模型,你最终可能会在可视化工具中训练没有预定义颜色的新标签 。spacy_displacy_colors
入口点允许你定义其颜色值映射到实体标签的字典。它被添加到预定义的颜色中,也可以覆盖现有值。
特定领域的
NER
标签 具有特定领域标签方案的管道的良好示例如scispaCy
和Blackstone
。
# SNEK.PY
# 绿色和紫色
displacy_colors = {"SNEK": "#3dff74", "HUMAN": "#cfc5ff"}
# SNEK.PY
# 绿色和紫色
displacy_colors = {"SNEK": "#3dff74", "HUMAN": "#cfc5ff"}
鉴于上述颜色,入口点可以定义如下。入口点需要有一个名字,所以我们使用键colors
。名称无关紧要,将使用入口点组中定义的任何内容。
# SETUP.PY
from setuptools import setup
setup(
name="snek",
entry_points={
+ "spacy_displacy_colors": ["colors = snek:displacy_colors"]
}
)
# SETUP.PY
from setuptools import setup
setup(
name="snek",
entry_points={
+ "spacy_displacy_colors": ["colors = snek:displacy_colors"]
}
)
安装包后,自定义颜色将在通过displacy
进行文本可视化时使用. 每当分配SNEK
标签时,它将显示颜色为#3dff74
(绿色)。
🌱🌿 🐍 SNEK ____ 🌳🌲 ____ 👨🌾 HUMAN 🏘️
保存、加载和分发训练有素的管道
在完成管道训练之后,你通常想要保存它的状态,然后再加载回来。你可以使用Language.to_disk
方法:
nlp.to_disk("./en_example_pipeline")
nlp.to_disk("./en_example_pipeline")
如果该目录不存在,则会创建该目录,整个管道数据、元数据和配置将被写入该目录。为了更方便管道部署,我们建议将其包装为Python
包。
config.cfg 和 meta.json 有什么区别?
当你在spaCy v3.0+
中保存管道时,将导出两个文件:基于nlp.config
的config.cfg
和一个基于nlp.meta
文件的meta.json
。
config:用于创建当前
nlp
对象,其管道组件和模型以及训练设置和超参数的配置。可以包括对注册函数的引用,如管道组件或模型架构。给定一个配置,spaCy
能够重建整个对象树和nlp
对象。导出的配置也可用于使用相同设置的训练管道。
meta:有关管道和
Python
包的元信息,例如作者信息、许可证、版本、数据源和标签方案。这主要用于生成文档和打包管道。它对nlp
对象的功能没有影响。
🪐开始使用项目模板:
pipelines/tagger_parser_ud </>
开始端到端工作流的最简单方法是克隆一个项目模板并运行它——例如,这个模板可以让你在通用依赖树库上训练一个词性标注器和依赖解析器并生成一个可安装的Python
包。
$ python -m spacy project clone pipelines/tagger_parser_ud
$ python -m spacy project clone pipelines/tagger_parser_ud
生成管道包
重要的提示
管道包通常不适合公共pypi.python.org
目录,该目录并不是为超过50 MB
的二进制数据和文件设计的。但是,如果你的公司正在运行PyPi
的内部安装,那么在其中发布你的管道包可能是一种与你的团队共享它们的便捷方式。
spaCy
自带有一个方便的CLI
命令,它将创建所有必需的文件,并引导你生成元数据。你还可以手动创建meta.json
并将其放置在数据目录中,或使用--meta
选项提供它的路径。有关这方面的更多信息,请参阅package
文档。
META.JSON(示例)
{
"name": "example_pipeline",
"lang": "en",
"version": "1.0.0",
"spacy_version": ">=2.0.0,<3.0.0",
"description": "Example pipeline for spaCy",
"author": "You",
"email": "[email protected]",
"license": "CC BY-SA 3.0"
}
{
"name": "example_pipeline",
"lang": "en",
"version": "1.0.0",
"spacy_version": ">=2.0.0,<3.0.0",
"description": "Example pipeline for spaCy",
"author": "You",
"email": "[email protected]",
"license": "CC BY-SA 3.0"
}
$ python -m spacy package ./en_example_pipeline ./packages
$ python -m spacy package ./en_example_pipeline ./packages
此命令将创建一个管道包目录 ,并将在该目录运行python setup.py sdist
以为你的包创建二进制.whl
或者.tar.gz
,这样你就可以使用pip install
安装。这极大的提高了安装效率。
目录结构
└── /
├── MANIFEST.in # to include meta.json
├── meta.json # pipeline meta data
├── setup.py # setup file for pip installation
├── en_example_pipeline # pipeline directory
│ ├── __init__.py # init for pip installation
│ └── en_example_pipeline-1.0.0 # pipeline data
│ ├── config.cfg # pipeline config
│ ├── meta.json # pipeline meta
│ └── ... # directories with component data
└── dist
└── en_example_pipeline-1.0.0.tar.gz # installable package
目录结构
└── /
├── MANIFEST.in # to include meta.json
├── meta.json # pipeline meta data
├── setup.py # setup file for pip installation
├── en_example_pipeline # pipeline directory
│ ├── __init__.py # init for pip installation
│ └── en_example_pipeline-1.0.0 # pipeline data
│ ├── config.cfg # pipeline config
│ ├── meta.json # pipeline meta
│ └── ... # directories with component data
└── dist
└── en_example_pipeline-1.0.0.tar.gz # installable package
你还可以在cli/package.py
源码中得到所有的模版文件。如果你手动创建包,请记住目录需要根据lang_name
和lang_name-version
的命名约定来命名 。
使用自定义函数和组件
如果你的管道包含自定义组件、模型架构或其他代码,则需要在加载管道之前注册这些功能。否则,spaCy
将不知道如何创建配置中引用的对象。spacy package
命令允许你使用--code
参数提供包含自定义注册函数的一个或多个Python
文件的路径。
# __INIT__.PY(摘录)
from . import functions
def load(**overrides):
...
# __INIT__.PY(摘录)
from . import functions
def load(**overrides):
...
python -m spacy package ./en_example_pipeline ./packages --code functions.py
python -m spacy package ./en_example_pipeline ./packages --code functions.py
Python
文件将被复制到包的根目录中,并且包的__init__.py
文件会将它们作为模块导入。这可确保在导入管道时完成函数注册,例如,当你调用spacy.load
时。只需一个简单的导入即可使已注册的函数可用。
确保包含所有引用了自定义代码的Python
文件,包括其他人导入的模块。如果你的自定义代码依赖于外部包,请确保它们在你meta.json
文件的"requirements"
已经列出。对于大多数用例,注册函数应该为你提供所需的所有自定义,从自定义组件到自定义模型架构和生命周期钩子。但是,如果你确实想更详细地自定义设置,你可以编辑包的__init__.py
文件和spacy.load
调用的包的load
函数。
! 关于进行手动编辑的重要说明 虽然编辑包代码或元信息没有问题,但必须避免对训练完成后的
config.cfg
进行编辑,因为这很容易导致数据不兼容。例如,更改架构或超参数可能意味着训练的权重现在不兼容。如果你想进行调整,可以在训练前进行。否则,你应该始终相信spaCy
可以通过nlp.config
导出其nlp
对象的当前状态.
加载自定义管道包
要从数据目录加载管道,你可以使用spacy.load()
方法结合本地路径。它将在目录中查找config.cfg
并使用lang
和pipeline
设置来初始化一个Language
类。
nlp = spacy.load("/path/to/pipeline")
nlp = spacy.load("/path/to/pipeline")
如果你只想加载二进制数据,则必须创建一个Language
类并调用from_disk
方法。
nlp = spacy.blank("en").from_disk("/path/to/data")
nlp = spacy.blank("en").from_disk("/path/to/data")