I'm trying to integrate a custom PhraseMatcher()
component into my nlp pipeline in a way that will allow me to load the custom Spacy model without having to re-add my custom components to a generic model on each load.
How can I load a Spacy model containing custom pipeline components?
I create the component, add it to my pipeline and save it with the following:
import requests
from spacy.lang.en import English
from spacy.matcher import PhraseMatcher
from spacy.tokens import Doc, Span, Token
class RESTCountriesComponent(object):
name = 'countries'
def __init__(self, nlp, label='GPE'):
self.countries = [u'MyCountry', u'MyOtherCountry']
self.label = nlp.vocab.strings[label]
patterns = [nlp(c) for c in self.countries]
self.matcher = PhraseMatcher(nlp.vocab)
self.matcher.add('COUNTRIES', None, *patterns)
def __call__(self, doc):
matches = self.matcher(doc)
spans = []
for _, start, end in matches:
entity = Span(doc, start, end, label=self.label)
spans.append(entity)
doc.ents = list(doc.ents) + spans
for span in spans:
span.merge()
return doc
nlp = English()
rest_countries = RESTCountriesComponent(nlp)
nlp.add_pipe(rest_countries)
nlp.to_disk('myNlp')
I then attempt to load my model with,
nlp = spacy.load('myNlp')
But get this error message:
KeyError: u"[E002] Can't find factory for 'countries'. This usually happens when spaCy calls
nlp.create_pipe
with a component name that's not built in - for example, when constructing the pipeline from a model's meta.json. If you're using a custom component, you can write toLanguage.factories['countries']
or remove it from the model meta and add it vianlp.add_pipe
instead."
I can't just add my custom components to a generic pipeline in my programming environment. How can I do what I'm trying to do?
Loading a custom pipeline package To load a pipeline from a data directory, you can use spacy. load() with the local path. This will look for a config. cfg in the directory and use the lang and pipeline settings to initialize a Language class with a processing pipeline and load in the model data.
Custom components can be added to the pipeline using the add_pipe method. Optionally, you can either specify a component to add it before or after, tell spaCy to add it first or last in the pipeline, or define a custom name.
Typically, the extension for these binary files is . spacy , and they are used as input format for specifying a training corpus and for spaCy's CLI train command. The built-in convert command helps you convert spaCy's previous JSON format to the new binary format.
While NLTK provides access to many algorithms to get something done, spaCy provides the best way to do it. It provides the fastest and most accurate syntactic analysis of any NLP library released to date. It also offers access to larger word vectors that are easier to customize.
When you save out your model, spaCy will serialize all data and store a reference to your pipeline in the model's meta.json
. For example: ["ner", "countries"]
. When you load your model back in, spaCy will check out the meta and initialise each pipeline component by looking it up in the so-called "factories": functions that tell spaCy how to construct a pipeline component. (The reason for that is that you usually don't want your model to store and eval arbitrary code when you load it back in – at least not by default.)
In your case, spaCy is trying to look up the component name 'countries'
in the factories and fails, because it's not built-in. The Language.factories
are a simple dictionary, though, so you can customise it and add your own entries:
from spacy.language import Language
Language.factories['countries'] = lambda nlp, **cfg: RESTCountriesComponent(nlp, **cfg)
A factory is a function that receives the shared nlp
object and optional keyword arguments (config parameters). It then initialises the component and returns it. If you add the above code before you load your model, it should load as expected.
If you want this taken care of automatically, you could also ship your component with your model. This requires wrapping it as a Python package using the spacy package
command, which creates all required Python files. By default, the __init__.py
only includes a function to load your model – but you can also add custom functions to it or use it to add entries to spaCy's factories.
As of v2.1.0
(currently available as a nightly version for testing), spaCy will also support providing pipeline component factories via Python entry points. This is especially useful for production setups and/or if you want to modularise your individual components and split them into their own packages. For example, you could create a Python package for your countries component and its factory, upload it to PyPi, version it and test it separately. In its setup.py
, your package can define the spaCy factories it exposes and where to find them. spaCy will be able to detect them automatically – all you need to do is install the package in the same environment. Your model package could even require your component package as a dependency so it's installed automatically when you install your model.
This same issue came up for me and these are the steps I used:
python setup.py sdist
in this directory.__init__.py
file of package as instructed aboveIf you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With