spaCy 如何通过 entry points 机制注册自定义组件与语言类?
【免费下载链接】spaCy💫 Industrial-strength Natural Language Processing (NLP) in Python项目地址: https://gitcode.com/GitHub_Trending/sp/spaCy
写出自定义 pipeline 组件或自定义Language子类后,你默认会遇到一个问题:spaCy 只能通过已注册的工厂函数把"my_component"这样的字符串名解析成真实对象,而注册发生在装饰器@Language.component/@Language.factory被执行(即模块被导入)之后。如果你把组件拆成独立的扩展包,又不想要求用户每次手动import你的模块,就可以利用 Python 的 entry points 机制:扩展包在setup.py中声明spacy_factories/spacy_languages入口点,spaCy 会自动加载它们。前提只有一个——包需要暴露这些 entry points 并安装在同一个环境中。
这篇文章按 spaCy 官方文档(保存与加载、处理流水线、语言数据)中的示例走一遍完整路径:写组件 → 在setup.py声明 entry point → 安装包 → 验证无需 import 即可使用。
spaCy 可识别的 entry point 组
| Entry point | 用途 |
|---|---|
spacy_factories | pipeline 组件工厂的入口点组,key 为组件名,用于暴露其他包定义的自定义组件 |
spacy_languages | 自定义Language子类的入口点组,key 为语言代码 |
spacy_lookups | 自定义 Lookups(含词形还原数据),spacy-lookups-data包使用 |
spacy_displacy_colors | displaCy 可视化器的自定义标签颜色,key 名随意,应指向「标签 → 颜色值」的字典 |
声明语法:setup.py中entry_points字典里的某一组是一组字符串列表,每个字符串遵循name = module:object。例如"snek = snek:snek_component"创建一个名为snek的入口点,指向模块snek(即snek.py)中的函数snek_component。同一个包可以暴露多个入口点,安装后全部对 spaCy 可用。
用 spacy_factories 注册自定义组件
第 1 步:写组件并用装饰器注册
文档示例的包结构:
├── snek.py # the extension code └── setup.py # setup file for pip installationsnek.py定义一个收到Doc时打印一条蛇的组件(示例代码,直接可用):
from spacy.language import Language snek = """ --..,_ _,.--. `'.'. .'`__ o `;__. {text} '.'. .'.'` '---'` ` '.`'--....--'`.' `'--....--'` """ @Language.component("snek") def snek_component(doc): print(snek.format(text=doc.text)) return doc这里的关键是@Language.component("snek"):从 v3.0 起,组件必须用@Language.component或@Language.factory装饰器注册,nlp.add_pipe接收的是组件工厂的字符串名而不是函数对象。装饰器把组件注册到Language的工厂注册表中,之后add_pipe("snek")才能解析这个名字。
第 2 步:在 setup.py 声明 entry point
from setuptools import setup setup( name="snek", entry_points={ "spacy_factories": ["snek = snek:snek_component"] } )等号左边的snek是入口点名,也就是 pipeline 配置里使用的组件名;右边指向被装饰的函数。不声明它的话,加载pipeline = ["snek"]的配置时 spaCy 会因为找不到内置组件"snek"而报错。
第 3 步:安装包
在包含setup.py的包目录下执行:
$ python -m pip install .这一步把当前目录的包安装进当前环境;entry point 声明只有在包被安装后才生效。
第 4 步:验证无需 import 即可使用
>>> from spacy.lang.en import English >>> nlp = English() >>> nlp.add_pipe("snek") # this now works! >>> doc = nlp("I am snek")以上代码从未导入snek_component,但nlp.add_pipe("snek")成功——说明 entry point 已生效。doc = nlp("I am snek")的输出(文档示例):
--..,_ _,.--. `'.'. .'`__ o `;__. I am snek '.'. .'.'` '---'` ` '.`'--....--'`.' `'--....--'`机制说明:加载 pipeline 时 spaCy 读取config.cfg来设置语言类并构造 pipeline,pipeline是字符串列表(如["tagger", "parser", "ner"]),每个字符串经nlp.add_pipe到所有已注册工厂中查找。声明了spacy_factories后,这些函数会在Language类初始化时在后台自动加载,因此用户无需 import你的组件。此外,把nlp.config保存到磁盘时会包含"snek"组件的条目,任何用这份配置训练的 pipeline 都会包含该组件并知道如何加载它——前提是snek包已安装。对应的配置片段(文档示例):
[nlp] lang = "en" pipeline = ["snek"] [components.snek] factory = "snek"可选:改成工厂以支持配置与序列化
如果组件需要状态或可调参数,改用@Language.factory,用户就能在添加组件时传入config。文档示例(cute_snek是文档中未定义的第二种蛇图案变量,实际使用需自行定义):
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 docsetup.py中入口点改为指向工厂类:
entry_points={ "spacy_factories": ["snek = snek:SnekFactory"] }配置中即可增加设置:
[components.snek] factory = "snek" snek_style = "basic"工厂还可以实现to_disk/from_disk做序列化,或实现update让组件可训练。组件暴露from_disk并被包含在 pipeline 中时,加载 pipeline 会调用它;用nlp.to_disk保存且组件暴露to_disk时则会调用后者——这样可以把自定义数据随 pipeline 包一起分发。
用 spacy_languages 注册自定义语言类
如果要为自己的 pipeline 实现一个语言类但不想改 spaCy 源码,在包中实现自定义语言子类(必须定义lang语言代码和Defaults语言数据):
from spacy.language import Language class SnekDefaults(Language.Defaults): stop_words = set(["sss", "hiss"]) class SnekLanguage(Language): lang = "snk" Defaults = SnekDefaults在setup.py的entry_points中追加spacy_languages组,把语言代码映射到子类:
setup( name="snek", entry_points={ "spacy_factories": ["snek = snek:SnekFactory"], "spacy_languages": ["snk = snek:SnekLanguage"] } )安装后,spaCy 加载snk语言时会经由该 entry point 解析到SnekLanguage(语言类查找逻辑见 spacy/util.py,其中明确会处理经由 entry point 提供的自定义子类)。对你训练的 pipeline 包尤其重要:其config.cfg可以写lang = "snk",而不会因该语言不在核心库中而报错。
验证方式:安装包后执行spacy.blank("snk"),确认返回对象的lang为"snk"且由SnekLanguage实例化即可。
替代路径:不打包时用 registry 装饰器注册语言类
如果不想把代码拆成独立包,文档给出了另一条路径:用@spacy.registry.languages装饰器注册自定义语言类并赋一个字符串名,之后可以直接spacy.blank("custom_en"),也能在训练配置中引用:
import spacy from spacy.lang.en import English class CustomEnglishDefaults(English.Defaults): stop_words = set(["custom", "stop"]) @spacy.registry.languages("custom_en") class CustomEnglish(English): lang = "custom_en" Defaults = CustomEnglishDefaults # This now works! nlp = spacy.blank("custom_en")训练配置中:
[nlp] lang = "custom_en"注意这条路径与 entry point 的差别:注册函数必须在训练时可用,可以用--code参数加载包含注册代码的 Python 文件:
python -m spacy train config.cfg --code code.py加载时的限制与排查
- 包必须已安装:entry point 只在包暴露入口点并安装到当前环境后生效,只放在源码目录里不会自动被发现。
- 装饰器必须在加载 pipeline 前执行:加载含自定义组件的 pipeline 时,
@Language.component/@Language.factory装饰器必须先于 pipeline 加载执行,否则 spaCy 无法把"my_component"这类字符串名解析回函数。entry point 机制通过安装后的自动导入保证了这一点;没有 entry point 时就需要手动 import 模块再加载。 spacy_displacy_colors的 key 名随意:入口点名不影响行为(文档示例用colors),组内定义的「标签 → 颜色」字典会被合并进预定义颜色,也可覆盖已有值。- 配置中的组件依赖包:
config.cfg引用了你的自定义组件后,使用该配置的每个环境都必须安装对应包,否则加载失败。
完成注册后,文档建议把训练好的 pipeline 用spacy package命令封装成 Python 包再分发,这样下游环境安装模型包时即可通过 entry points 自动解析其中的自定义组件与语言类。
【免费下载链接】spaCy💫 Industrial-strength Natural Language Processing (NLP) in Python项目地址: https://gitcode.com/GitHub_Trending/sp/spaCy
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考